diff -r c9ff263c29ad -r e684f11070d5 src/jlibrtp/ParticipantDatabase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/ParticipantDatabase.java Sat Mar 14 22:15:41 2009 +0100 @@ -0,0 +1,256 @@ +/** + * Java RTP Library (jlibrtp) + * Copyright (C) 2006 Arne Kepp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package jlibrtp; + +import java.util.*; +import java.util.concurrent.*; + +/** + * The participant database maintains three hashtables with participants. + * + * The key issue is to be fast for operations that happen every time an + * RTP packet is sent or received. We allow linear searching in cases + * where we need to update participants with information. + * + * The keying is therefore usually the SSRC. In cases where we have the + * cname, but no SSRC is known (no SDES packet has been received), a + * simple hash i calculated based on the CNAME. The RTCP code should, + * when receiving SDES packets, check whether the participant is known + * and update the copy in this database with SSRC if needed. + * + * @author Arne Kepp + */ +public class ParticipantDatabase { + /** The parent RTP Session */ + RTPSession rtpSession = null; + /** + * A linked list to hold participants explicitly added by the application + * In unicast mode this is the list used for RTP and RTCP transmission, + * in multicast it should not be in use. + */ + LinkedList receivers = new LinkedList(); + /** + * The hashtable holds participants added through received RTP and RTCP packets, + * as well as participants that have been linked to an SSRC by ip address (in unicast mode). + */ + ConcurrentHashMap ssrcTable = new ConcurrentHashMap(); + + /** + * Simple constructor + * + * @param parent parent RTPSession + */ + protected ParticipantDatabase(RTPSession parent) { + rtpSession = parent; + } + + /** + * + * @param cameFrom 0: Application, 1: RTP packet, 2: RTCP + * @param p the participant + * @return 0 if okay, -1 if not + */ + protected int addParticipant(int cameFrom, Participant p) { + //Multicast or not? + if(this.rtpSession.mcSession) { + return this.addParticipantMulticast(cameFrom, p); + } else { + return this.addParticipantUnicast(cameFrom, p); + } + + } + + /** + * Add a multicast participant to the database + * + * @param cameFrom 0: Application, 1,2: discovered through RTP or RTCP + * @param p the participant to add + * @return 0 if okay, -2 if redundant, -1 if adding participant to multicast + */ + private int addParticipantMulticast(int cameFrom, Participant p) { + if( cameFrom == 0) { + System.out.println("ParticipantDatabase.addParticipant() doesnt expect" + + " application to add participants to multicast session."); + return -1; + } else { + // Check this one is not redundant + if(this.ssrcTable.contains(p.ssrc)) { + System.out.println("ParticipantDatabase.addParticipant() SSRC " + +"already known " + Long.toString(p.ssrc)); + return -2; + } else { + this.ssrcTable.put(p.ssrc, p); + return 0; + } + } + } + + /** + * Add a unicast participant to the database + * + * Result will be reported back through tpSession.appIntf.userEvent + * + * @param cameFrom 0: Application, 1,2: discovered through RTP or RTCP + * @param p the participant to add + * @return 0 if new, 1 if + */ + private int addParticipantUnicast(int cameFrom, Participant p) { + if(cameFrom == 0) { + //Check whether there is a match in the ssrcTable + boolean notDone = true; + + Enumeration enu = this.ssrcTable.elements(); + while(notDone && enu.hasMoreElements()) { + Participant part = enu.nextElement(); + if(part.unexpected && + (part.rtcpReceivedFromAddress.equals(part.rtcpAddress.getAddress()) + || part.rtpReceivedFromAddress.equals(part.rtpAddress.getAddress()))) { + + part.rtpAddress = p.rtpAddress; + part.rtcpAddress = p.rtcpAddress; + part.unexpected = false; + + //Report the match back to the application + Participant[] partArray = {part}; + this.rtpSession.appIntf.userEvent(5, partArray); + + notDone = false; + p = part; + } + } + + //Add to the table of people that we send packets to + this.receivers.add(p); + return 0; + + } else { + //Check whether there's a match in the receivers table + boolean notDone = true; + //System.out.println("GOT " + p.cname); + Iterator iter = this.receivers.iterator(); + + while(notDone && iter.hasNext()) { + Participant part = iter.next(); + + //System.out.println(part.rtpAddress.getAddress().toString() + // + " " + part.rtcpAddress.getAddress().toString() + // + " " + p.rtpReceivedFromAddress.getAddress().toString() + // + " " + p.rtcpReceivedFromAddress.getAddress().toString()); + + //System.out.println(" HUUHHHH? " + p.rtcpReceivedFromAddress.getAddress().equals(part.rtcpAddress.getAddress())); + if((cameFrom == 1 && p.rtpReceivedFromAddress.getAddress().equals(part.rtpAddress.getAddress())) + || (cameFrom == 2 && p.rtcpReceivedFromAddress.getAddress().equals(part.rtcpAddress.getAddress()))) { + + part.rtpReceivedFromAddress = p.rtpReceivedFromAddress; + part.rtcpReceivedFromAddress = p.rtcpReceivedFromAddress; + + // Move information + part.ssrc = p.ssrc; + part.cname = p.cname; + part.name = p.name; + part.loc = p.loc; + part.phone = p.phone; + part.email = p.email; + part.note = p.note; + part.tool = p.tool; + part.priv = p.priv; + + this.ssrcTable.put(part.ssrc, part); + + //Report the match back to the application + Participant[] partArray = {part}; + this.rtpSession.appIntf.userEvent(5, partArray); + return 0; + } + } + + // No match? ok + this.ssrcTable.put(p.ssrc, p); + return 0; + } + } + + /** + * Remove a participant from all tables + * + * @param p the participant to be removed + */ + protected void removeParticipant(Participant p) { + if(! this.rtpSession.mcSession) + this.receivers.remove(p); + + this.ssrcTable.remove(p.ssrc, p); + } + + /** + * Find a participant based on the ssrc + * + * @param ssrc of the participant to be found + * @return the participant, null if unknonw + */ + protected Participant getParticipant(long ssrc) { + Participant p = null; + p = ssrcTable.get(ssrc); + return p; + } + + /** + * Iterator for all the unicast receivers. + * + * This one is used by both RTP for sending packets, as well as RTCP. + * + * @return iterator for unicast participants + */ + protected Iterator getUnicastReceivers() { + if(! this.rtpSession.mcSession) { + return this.receivers.iterator(); + } else { + System.out.println("Request for ParticipantDatabase.getUnicastReceivers in multicast session"); + return null; + } + } + + /** + * Enumeration of all the participants with known ssrcs. + * + * This is primarily used for sending packets in multicast sessions. + * + * @return enumerator with all the participants with known SSRCs + */ + protected Enumeration getParticipants() { + return this.ssrcTable.elements(); + } + + protected void debugPrint() { + System.out.println(" ParticipantDatabase.debugPrint()"); + Participant p; + Enumeration enu = ssrcTable.elements(); + while(enu.hasMoreElements()) { + p = (Participant) enu.nextElement(); + System.out.println(" ssrcTable ssrc:"+p.ssrc+" cname:"+p.cname + +" loc:"+p.loc+" rtpAddress:"+p.rtpAddress+" rtcpAddress:"+p.rtcpAddress); + } + + Iterator iter = receivers.iterator(); + while(iter.hasNext()) { + p = iter.next(); + System.out.println(" receivers: "+p.rtpAddress.toString()); + } + } +}