diff -r 9bdff6cbd120 -r 2bf440c54ca5 src/jlibrtp/RTPSession.java --- a/src/jlibrtp/RTPSession.java Thu May 28 14:26:06 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1053 +0,0 @@ -/** - * 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.net.DatagramSocket; -import java.net.MulticastSocket; -import java.net.DatagramPacket; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.Iterator; -import java.util.concurrent.locks.*; -import java.util.Random; -import java.util.Enumeration; -/** - * The RTPSession object is the core of jlibrtp. - * - * One should be instantiated for every communication channel, i.e. if you send voice and video, you should create one for each. - * - * The instance holds a participant database, as well as other information about the session. When the application registers with the session, the necessary threads for receiving and processing RTP packets are spawned. - * - * RTP Packets are sent synchronously, all other operations are asynchronous. - * - * @author Arne Kepp - */ -public class RTPSession { - /** - * The debug level is final to avoid compilation of if-statements.
- * 0 provides no debugging information, 20 provides everything
- * Debug output is written to System.out
- * Debug level for RTP related things. - */ - final static public int rtpDebugLevel = 0; - /** - * The debug level is final to avoid compilation of if-statements.
- * 0 provides no debugging information, 20 provides everything
- * Debug output is written to System.out
- * Debug level for RTCP related things. - */ - final static public int rtcpDebugLevel = 0; - - /** RTP unicast socket */ - protected DatagramSocket rtpSock = null; - /** RTP multicast socket */ - protected MulticastSocket rtpMCSock = null; - /** RTP multicast group */ - protected InetAddress mcGroup = null; - - // Internal state - /** Whether this session is a multicast session or not */ - protected boolean mcSession = false; - /** Current payload type, can be changed by application */ - protected int payloadType = 0; - /** SSRC of this session */ - protected long ssrc; - /** The last timestamp when we sent something */ - protected long lastTimestamp = 0; - /** Current sequence number */ - protected int seqNum = 0; - /** Number of packets sent by this session */ - protected int sentPktCount = 0; - /** Number of octets sent by this session */ - protected int sentOctetCount = 0; - - /** The random seed */ - protected Random random = null; - - /** Session bandwidth in BYTES per second */ - protected int bandwidth = 8000; - - /** By default we do not return packets from strangers in unicast mode */ - protected boolean naiveReception = false; - - /** Should the library attempt frame reconstruction? */ - protected boolean frameReconstruction = true; - - /** Maximum number of packets used for reordering */ - protected int pktBufBehavior = 3; - - /** Participant database */ - protected ParticipantDatabase partDb = new ParticipantDatabase(this); - /** Handle to application interface for RTP */ - protected RTPAppIntf appIntf = null; - /** Handle to application interface for RTCP (optional) */ - protected RTCPAppIntf rtcpAppIntf = null; - /** Handle to application interface for AVPF, RFC 4585 (optional) */ - protected RTCPAVPFIntf rtcpAVPFIntf = null; - /** Handle to application interface for debugging */ - protected DebugAppIntf debugAppIntf = null; - - /** The RTCP session associated with this RTP Session */ - protected RTCPSession rtcpSession = null; - /** The thread for receiving RTP packets */ - protected RTPReceiverThread recvThrd = null; - /** The thread for invoking callbacks for RTP packets */ - protected AppCallerThread appCallerThrd = null; - - /** Lock to protect the packet buffers */ - final protected Lock pktBufLock = new ReentrantLock(); - /** Condition variable, to tell the */ - final protected Condition pktBufDataReady = pktBufLock.newCondition(); - - /** Enough is enough, set to true when you want to quit. */ - protected boolean endSession = false; - /** Only one registered application, please */ - protected boolean registered = false; - /** We're busy resolving a SSRC conflict, please try again later */ - protected boolean conflict = false; - /** Number of conflicts observed, exessive number suggests loop in network */ - protected int conflictCount = 0; - - /** SDES CNAME */ - protected String cname = null; - /** SDES The participant's real name */ - public String name = null; - /** SDES The participant's email */ - public String email = null; - /** SDES The participant's phone number */ - public String phone = null; - /** SDES The participant's location*/ - public String loc = null; - /** SDES The tool the participants is using */ - public String tool = null; - /** SDES A note */ - public String note = null; - /** SDES A priv string, loosely defined */ - public String priv = null; - - // RFC 4585 stuff. This should live on RTCPSession, but we need to have this - // infromation ready by the time the RTCP Session starts - // 0 = RFC 3550 , -1 = ACK , 1 = Immediate feedback, 2 = Early RTCP, - protected int rtcpMode = 0; - protected int fbEarlyThreshold = -1; // group size, immediate -> early transition point - protected int fbRegularThreshold = -1; // group size, early -> regular transition point - protected int minInterval = 5000; // minimum interval - protected int fbMaxDelay = 1000; // how long the information is useful - // RTCP bandwidth - protected int rtcpBandwidth = -1; - - - /** - * Returns an instance of a unicast RTP session. - * Following this you should adjust any settings and then register your application. - * - * The sockets should have external ip addresses, else your CNAME automatically - * generated CNAMe will be bad. - * - * @param rtpSocket UDP socket to receive RTP communication on - * @param rtcpSocket UDP socket to receive RTCP communication on, null if none. - */ - public RTPSession(DatagramSocket rtpSocket, DatagramSocket rtcpSocket) { - mcSession = false; - rtpSock = rtpSocket; - this.generateCNAME(); - this.generateSsrc(); - this.rtcpSession = new RTCPSession(this,rtcpSocket); - - // The sockets are not always imediately available? - try { Thread.sleep(1); } catch (InterruptedException e) { System.out.println("RTPSession sleep failed"); } - } - - /** - * Returns an instance of a multicast RTP session. - * Following this you should register your application. - * - * The sockets should have external ip addresses, else your CNAME automatically - * generated CNAMe will be bad. - * - * @param rtpSock a multicast socket to receive RTP communication on - * @param rtcpSock a multicast socket to receive RTP communication on - * @param multicastGroup the multicast group that we want to communicate with. - */ - public RTPSession(MulticastSocket rtpSock, MulticastSocket rtcpSock, InetAddress multicastGroup) throws Exception { - mcSession = true; - rtpMCSock =rtpSock; - mcGroup = multicastGroup; - rtpMCSock.joinGroup(mcGroup); - rtcpSock.joinGroup(mcGroup); - this.generateCNAME(); - this.generateSsrc(); - this.rtcpSession = new RTCPSession(this,rtcpSock,mcGroup); - - // The sockets are not always imediately available? - try { Thread.sleep(1); } catch (InterruptedException e) { System.out.println("RTPSession sleep failed"); } - } - - /** - * Registers an application (RTPAppIntf) with the RTP session. - * The session will call receiveData() on the supplied instance whenever data has been received. - * - * Following this you should set the payload type and add participants to the session. - * - * @param rtpApp an object that implements the RTPAppIntf-interface - * @param rtcpApp an object that implements the RTCPAppIntf-interface (optional) - * @return -1 if this RTPSession-instance already has an application registered. - */ - public int RTPSessionRegister(RTPAppIntf rtpApp, RTCPAppIntf rtcpApp, DebugAppIntf debugApp) { - if(registered) { - System.out.println("RTPSessionRegister(): Can\'t register another application!"); - return -1; - } else { - registered = true; - generateSeqNum(); - if(RTPSession.rtpDebugLevel > 0) { - System.out.println("-> RTPSessionRegister"); - } - this.appIntf = rtpApp; - this.rtcpAppIntf = rtcpApp; - this.debugAppIntf = debugApp; - - recvThrd = new RTPReceiverThread(this); - appCallerThrd = new AppCallerThread(this, rtpApp); - recvThrd.start(); - appCallerThrd.start(); - rtcpSession.start(); - return 0; - } - } - - /** - * Send data to all participants registered as receivers, using the current timeStamp, - * dynamic sequence number and the current payload type specified for the session. - * - * @param buf A buffer of bytes, less than 1496 bytes - * @return null if there was a problem, {RTP Timestamp, Sequence number} otherwise - */ - public long[] sendData(byte[] buf) { - byte[][] tmp = {buf}; - long[][] ret = this.sendData(tmp, null, null, -1, null); - - if(ret != null) - return ret[0]; - - return null; - } - - /** - * Send data to all participants registered as receivers, using the specified timeStamp, - * sequence number and the current payload type specified for the session. - * - * @param buf A buffer of bytes, less than 1496 bytes - * @param rtpTimestamp the RTP timestamp to be used in the packet - * @param seqNum the sequence number to be used in the packet - * @return null if there was a problem, {RTP Timestamp, Sequence number} otherwise - */ - public long[] sendData(byte[] buf, long rtpTimestamp, long seqNum) { - byte[][] tmp = {buf}; - long[][] ret = this.sendData(tmp, null, null, -1, null); - - if(ret != null) - return ret[0]; - - return null; - } - - /** - * Send data to all participants registered as receivers, using the current timeStamp and - * payload type. The RTP timestamp will be the same for all the packets. - * - * @param buffers A buffer of bytes, should not bed padded and less than 1500 bytes on most networks. - * @param csrcArray an array with the SSRCs of contributing sources - * @param markers An array indicating what packets should be marked. Rarely anything but the first one - * @param rtpTimestamp The RTP timestamp to be applied to all packets - * @param seqNumbers An array with the sequence number associated with each byte[] - * @return null if there was a problem sending the packets, 2-dim array with {RTP Timestamp, Sequence number} - */ - public long[][] sendData(byte[][] buffers, long[] csrcArray, boolean[] markers, long rtpTimestamp, long[] seqNumbers) { - if(RTPSession.rtpDebugLevel > 5) { - System.out.println("-> RTPSession.sendData(byte[])"); - } - - // Same RTP timestamp for all - if(rtpTimestamp < 0) - rtpTimestamp = System.currentTimeMillis(); - - // Return values - long[][] ret = new long[buffers.length][2]; - - for(int i=0; i 1500) { - System.out.println("RTPSession.sendData() called with buffer exceeding 1500 bytes ("+buf.length+")"); - } - - // Get the return values - ret[i][0] = rtpTimestamp; - if(seqNumbers == null) { - ret[i][1] = getNextSeqNum(); - } else { - ret[i][1] = seqNumbers[i]; - } - // Create a new RTP Packet - RtpPkt pkt = new RtpPkt(rtpTimestamp,this.ssrc,(int) ret[i][1],this.payloadType,buf); - - if(csrcArray != null) - pkt.setCsrcs(csrcArray); - - pkt.setMarked(marker); - - // Creates a raw packet - byte[] pktBytes = pkt.encode(); - - //System.out.println(Integer.toString(StaticProcs.bytesToUIntInt(pktBytes, 2))); - - // Pre-flight check, are resolving an SSRC conflict? - if(this.conflict) { - System.out.println("RTPSession.sendData() called while trying to resolve conflict."); - return null; - } - - - if(this.mcSession) { - DatagramPacket packet = null; - - - try { - packet = new DatagramPacket(pktBytes,pktBytes.length,this.mcGroup,this.rtpMCSock.getPort()); - } catch (Exception e) { - System.out.println("RTPSession.sendData() packet creation failed."); - e.printStackTrace(); - return null; - } - - try { - rtpMCSock.send(packet); - //Debug - if(this.debugAppIntf != null) { - this.debugAppIntf.packetSent(1, (InetSocketAddress) packet.getSocketAddress(), - new String("Sent multicast RTP packet of size " + packet.getLength() + - " to " + packet.getSocketAddress().toString() + " via " - + rtpMCSock.getLocalSocketAddress().toString())); - } - } catch (Exception e) { - System.out.println("RTPSession.sendData() multicast failed."); - e.printStackTrace(); - return null; - } - - } else { - // Loop over recipients - Iterator iter = partDb.getUnicastReceivers(); - while(iter.hasNext()) { - InetSocketAddress receiver = iter.next().rtpAddress; - DatagramPacket packet = null; - - if(RTPSession.rtpDebugLevel > 15) { - System.out.println(" Sending to " + receiver.toString()); - } - - try { - packet = new DatagramPacket(pktBytes,pktBytes.length,receiver); - } catch (Exception e) { - System.out.println("RTPSession.sendData() packet creation failed."); - e.printStackTrace(); - return null; - } - - //Actually send the packet - try { - rtpSock.send(packet); - //Debug - if(this.debugAppIntf != null) { - this.debugAppIntf.packetSent(0, (InetSocketAddress) packet.getSocketAddress(), - new String("Sent unicast RTP packet of size " + packet.getLength() + - " to " + packet.getSocketAddress().toString() + " via " - + rtpSock.getLocalSocketAddress().toString())); - } - } catch (Exception e) { - System.out.println("RTPSession.sendData() unicast failed."); - e.printStackTrace(); - return null; - } - } - } - - //Update our stats - this.sentPktCount++; - this.sentOctetCount++; - - if(RTPSession.rtpDebugLevel > 5) { - System.out.println("<- RTPSession.sendData(byte[]) " + pkt.getSeqNumber()); - } - } - - return ret; - } - - /** - * Send RTCP App packet to receiver specified by ssrc - * - * - * - * Return values: - * 0 okay - * -1 no RTCP session established - * -2 name is not byte[4]; - * -3 data is not byte[x], where x = 4*y for syme y - * -4 type is not a 5 bit unsigned integer - * - * Note that a return value of 0 does not guarantee delivery. - * The participant must also exist in the participant database, - * otherwise the message will eventually be deleted. - * - * @param ssrc of the participant you want to reach - * @param type the RTCP App packet subtype, default 0 - * @param name the ASCII (in byte[4]) representation - * @param data the data itself - * @return 0 if okay, negative value otherwise (see above) - */ - - public int sendRTCPAppPacket(long ssrc, int type, byte[] name, byte[] data) { - if(this.rtcpSession == null) - return -1; - - if(name.length != 4) - return -2; - - if(data.length % 4 != 0) - return -3; - - if(type > 63 || type < 0 ) - return -4; - - RtcpPktAPP pkt = new RtcpPktAPP(ssrc, type, name, data); - this.rtcpSession.addToAppQueue(ssrc, pkt); - - return 0; - } - /** - * Add a participant object to the participant database. - * - * If packets have already been received from this user, we will try to update the automatically inserted participant with the information provided here. - * - * @param p A participant. - */ - public int addParticipant(Participant p) { - //For now we make all participants added this way persistent - p.unexpected = false; - return this.partDb.addParticipant(0, p); - } - - /** - * Remove a participant from the database. All buffered packets will be destroyed. - * - * @param p A participant. - */ - public void removeParticipant(Participant p) { - partDb.removeParticipant(p); - } - - public Iterator getUnicastReceivers() { - return partDb.getUnicastReceivers(); - } - - public Enumeration getParticipants() { - return partDb.getParticipants(); - } - - /** - * End the RTP Session. This will halt all threads and send bye-messages to other participants. - * - * RTCP related threads may require several seconds to wake up and terminate. - */ - public void endSession() { - this.endSession = true; - - // No more RTP packets, please - if(this.mcSession) { - this.rtpMCSock.close(); - } else { - this.rtpSock.close(); - } - - // Signal the thread that pushes data to application - this.pktBufLock.lock(); - try { this.pktBufDataReady.signalAll(); } finally { - this.pktBufLock.unlock(); - } - // Interrupt what may be sleeping - this.rtcpSession.senderThrd.interrupt(); - - // Give things a chance to cool down. - try { Thread.sleep(50); } catch (Exception e){ }; - - this.appCallerThrd.interrupt(); - - // Give things a chance to cool down. - try { Thread.sleep(50); } catch (Exception e){ }; - - if(this.rtcpSession != null) { - // No more RTP packets, please - if(this.mcSession) { - this.rtcpSession.rtcpMCSock.close(); - } else { - this.rtcpSession.rtcpSock.close(); - } - } - } - - - /** - * Check whether this session is ending. - * - * @return true if session and associated threads are terminating. - */ - boolean isEnding() { - return this.endSession; - } - - /** - * Overrides CNAME, used for outgoing RTCP packets. - * - * @param cname a string, e.g. username@hostname. Must be unique for session. - */ - public void CNAME(String cname) { - this.cname = cname; - } - - /** - * Get the current CNAME, used for outgoing SDES packets - */ - public String CNAME() { - return this.cname; - } - - public long getSsrc() { - return this.ssrc; - } - - private void generateCNAME() { - String hostname; - - if(this.mcSession) { - hostname = this.rtpMCSock.getLocalAddress().getCanonicalHostName(); - } else { - hostname = this.rtpSock.getLocalAddress().getCanonicalHostName(); - } - - //if(hostname.equals("0.0.0.0") && System.getenv("HOSTNAME") != null) { - // hostname = System.getenv("HOSTNAME"); - //} - - cname = System.getProperty("user.name") + "@" + hostname; - } - - /** - * Change the RTP socket of the session. - * Peers must be notified through SIP or other signalling protocol. - * Only valid if this is a unicast session to begin with. - * - * @param newSock integer for new port number, check it is free first. - */ - public int updateRTPSock(DatagramSocket newSock) { - if(!mcSession) { - rtpSock = newSock; - return 0; - } else { - System.out.println("Can't switch from multicast to unicast."); - return -1; - } - } - - /** - * Change the RTCP socket of the session. - * Peers must be notified through SIP or other signalling protocol. - * Only valid if this is a unicast session to begin with. - * - * @param newSock the new unicast socket for RTP communication. - */ - public int updateRTCPSock(DatagramSocket newSock) { - if(!mcSession) { - this.rtcpSession.rtcpSock = newSock; - return 0; - } else { - System.out.println("Can't switch from multicast to unicast."); - return -1; - } - } - - /** - * Change the RTP multicast socket of the session. - * Peers must be notified through SIP or other signalling protocol. - * Only valid if this is a multicast session to begin with. - * - * @param newSock the new multicast socket for RTP communication. - */ - public int updateRTPSock(MulticastSocket newSock) { - if(mcSession) { - this.rtpMCSock = newSock; - return 0; - } else { - System.out.println("Can't switch from unicast to multicast."); - return -1; - } - } - - /** - * Change the RTCP multicast socket of the session. - * Peers must be notified through SIP or other signalling protocol. - * Only valid if this is a multicast session to begin with. - * - * @param newSock the new multicast socket for RTCP communication. - */ - public int updateRTCPSock(MulticastSocket newSock) { - if(mcSession) { - this.rtcpSession.rtcpMCSock = newSock; - return 0; - } else { - System.out.println("Can't switch from unicast to multicast."); - return -1; - } - } - - /** - * Update the payload type used for the session. It is represented as a 7 bit integer, whose meaning must be negotiated elsewhere (see IETF RFCs 3550 and 3551) - * - * @param payloadT an integer representing the payload type of any subsequent packets that are sent. - */ - public int payloadType(int payloadT) { - if(payloadT > 128 || payloadT < 0) { - return -1; - } else { - this.payloadType = payloadT; - return this.payloadType; - } - } - - /** - * Get the payload type that is currently used for outgoing RTP packets. - * - * @return payload type as integer - */ - public int payloadType() { - return this.payloadType; - } - - /** - * Should packets from unknown participants be returned to the application? This can be dangerous. - * - * @param doAccept packets from participants not added by the application. - */ - public void naivePktReception(boolean doAccept) { - naiveReception = doAccept; - } - - /** - * Are packets from unknown participants returned to the application? - * - * @return whether we accept packets from participants not added by the application. - */ - public boolean naivePktReception() { - return naiveReception; - } - - /** - * Set the number of RTP packets that should be buffered when a packet is - * missing or received out of order. Setting this number high increases - * the chance of correctly reordering packets, but increases latency when - * a packet is dropped by the network. - * - * Packets that arrive in order are not affected, they are passed straight - * to the application. - * - * The maximum delay is numberofPackets * packet rate , where the packet rate - * depends on the codec and profile used by the sender. - * - * Valid values: - * >0 - The maximum number of packets (based on RTP Timestamp) that may accumulate - * 0 - All valid packets received in order will be given to the application - * -1 - All valid packets will be given to the application - * - * @param behavior the be - * @return the behavior set, unchanged in the case of a erroneous value - */ - public int packetBufferBehavior(int behavior) { - if(behavior > -2) { - this.pktBufBehavior = behavior; - // Signal the thread that pushes data to application - this.pktBufLock.lock(); - try { this.pktBufDataReady.signalAll(); } finally { - this.pktBufLock.unlock(); - } - return this.pktBufBehavior; - } else { - return this.pktBufBehavior; - } - } - - /** - * The number of RTP packets that should be buffered when a packet is - * missing or received out of order. A high number increases the chance - * of correctly reordering packets, but increases latency when a packet is - * dropped by the network. - * - * A negative value disables the buffering, out of order packets will simply be dropped. - * - * @return the maximum number of packets that can accumulate before the first is returned - */ - public int packetBufferBehavior() { - return this.pktBufBehavior; - } - - /** - * Set whether the stack should operate in RFC 4585 mode. - * - * This will automatically call adjustPacketBufferBehavior(-1), - * i.e. disable all RTP packet buffering in jlibrtp, - * and disable frame reconstruction - * - * @param rtcpAVPFIntf the in - */ - public int registerAVPFIntf(RTCPAVPFIntf rtcpAVPFIntf, int maxDelay, int earlyThreshold, int regularThreshold ) { - if(this.rtcpSession != null) { - this.packetBufferBehavior(-1); - this.frameReconstruction = false; - this.rtcpAVPFIntf = rtcpAVPFIntf; - this.fbEarlyThreshold = earlyThreshold; - this.fbRegularThreshold = regularThreshold; - return 0; - } else { - return -1; - } - } - - /** - * Unregisters the RTCP AVPF interface, thereby going from - * RFC 4585 mode to RFC 3550 - * - * You still have to adjust packetBufferBehavior() and - * frameReconstruction. - * - */ - public void unregisterAVPFIntf() { - this.fbEarlyThreshold = -1; - this.fbRegularThreshold = -1; - this.rtcpAVPFIntf = null; - } - - /** - * Enable / disable frame reconstruction in the packet buffers. - * This is only relevant if getPacketBufferBehavior > 0; - * - * Default is true. - */ - public void frameReconstruction(boolean toggle) { - this.frameReconstruction = toggle; - } - - /** - * Whether the packet buffer will attempt to reconstruct - * packet automatically. - * - * @return the status - */ - public boolean frameReconstruction() { - return this.frameReconstruction; - } - - /** - * The bandwidth currently allocated to the session, - * in bytes per second. The default is 8000. - * - * This value is not enforced and currently only - * used to calculate the RTCP interval to ensure the - * control messages do not exceed 5% of the total bandwidth - * described here. - * - * Since the actual value may change a conservative - * estimate should be used to avoid RTCP flooding. - * - * see rtcpBandwidth(void) - * - * @return current bandwidth setting - */ - public int sessionBandwidth() { - return this.bandwidth; - } - - /** - * Set the bandwidth of the session. - * - * See sessionBandwidth(void) for details. - * - * @param bandwidth the new value requested, in bytes per second - * @return the actual value set - */ - public int sessionBandwidth(int bandwidth) { - if(bandwidth < 1) { - this.bandwidth = 8000; - } else { - this.bandwidth = bandwidth; - } - return this.bandwidth; - } - - - /** - * RFC 3550 dictates that 5% of the total bandwidth, - * as set by sessionBandwidth, should be dedicated - * to RTCP traffic. This - * - * This should normally not be done, but is permissible in - * conjunction with feedback (RFC 4585) and possibly - * other profiles. - * - * Also see sessionBandwidth(void) - * - * @return current RTCP bandwidth setting, -1 means not in use - */ - public int rtcpBandwidth() { - return this.rtcpBandwidth; - } - - /** - * Set the RTCP bandwidth, see rtcpBandwidth(void) for details. - * - * This function must be - * - * @param bandwidth the new value requested, in bytes per second or -1 to disable - * @return the actual value set - */ - public int rtcpBandwidth(int bandwidth) { - if(bandwidth < -1) { - this.rtcpBandwidth = -1; - } else { - this.rtcpBandwidth = bandwidth; - } - return this.rtcpBandwidth; - } - - /********************************************* Feedback message stuff ***************************************/ - - /** - * Adds a Picture Loss Indication to the feedback queue - * - * @param ssrcMediaSource - * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant - */ - public int fbPictureLossIndication(long ssrcMediaSource) { - int ret = 0; - - if(this.rtcpAVPFIntf == null) - return -1; - - RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource); - pkt.makePictureLossIndication(); - ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); - if(ret == 0) - this.rtcpSession.wakeSenderThread(ssrcMediaSource); - return ret; - } - - /** - * Adds a Slice Loss Indication to the feedback queue - * - * @param ssrcMediaSource - * @param sliFirst macroblock (MB) address of the first lost macroblock - * @param sliNumber number of lost macroblocks - * @param sliPictureId six least significant bits of the codec-specific identif - * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant - */ - public int fbSlicLossIndication(long ssrcMediaSource, int[] sliFirst, int[] sliNumber, int[] sliPictureId) { - int ret = 0; - if(this.rtcpAVPFIntf == null) - return -1; - - RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource); - pkt.makeSliceLossIndication(sliFirst, sliNumber, sliPictureId); - - ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); - if(ret == 0) - this.rtcpSession.wakeSenderThread(ssrcMediaSource); - return ret; - } - - /** - * Adds a Reference Picture Selection Indication to the feedback queue - * - * @param ssrcMediaSource - * @param bitPadding number of padded bits at end of bitString - * @param payloadType RTP payload type for codec - * @param bitString RPSI information as natively defined by the video codec - * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant - */ - public int fbRefPictureSelIndic(long ssrcMediaSource, int bitPadding, int payloadType, byte[] bitString) { - int ret = 0; - - if(this.rtcpAVPFIntf == null) - return -1; - - RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource); - pkt.makeRefPictureSelIndic(bitPadding, payloadType, bitString); - ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); - if(ret == 0) - this.rtcpSession.wakeSenderThread(ssrcMediaSource); - return ret; - } - - /** - * Adds a Picture Loss Indication to the feedback queue - * - * @param ssrcMediaSource - * @param bitString the original application message - * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant - */ - public int fbAppLayerFeedback(long ssrcMediaSource, byte[] bitString) { - int ret = 0; - - if(this.rtcpAVPFIntf == null) - return -1; - - RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource); - pkt.makeAppLayerFeedback(bitString); - ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); - if(ret == 0) - this.rtcpSession.wakeSenderThread(ssrcMediaSource); - return ret; - } - - - /** - * Adds a RTP Feedback packet to the feedback queue. - * - * These are mostly used for NACKs. - * - * @param ssrcMediaSource - * @param FMT the Feedback Message Subtype - * @param PID RTP sequence numbers of lost packets - * @param BLP bitmask of following lost packets, shared index with PID - * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant - */ - public int fbPictureLossIndication(long ssrcMediaSource, int FMT, int[] PID, int[] BLP) { - int ret = 0; - - if(this.rtcpAVPFIntf == null) - return -1; - - RtcpPktRTPFB pkt = new RtcpPktRTPFB(this.ssrc, ssrcMediaSource, FMT, PID, BLP); - ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt); - if(ret == 0) - this.rtcpSession.wakeSenderThread(ssrcMediaSource); - return ret; - } - - /** - * Fetches the next sequence number for RTP packets. - * @return the next sequence number - */ - private int getNextSeqNum() { - seqNum++; - // 16 bit number - if(seqNum > 65536) { - seqNum = 0; - } - return seqNum; - } - - /** - * Initializes a random variable - * - */ - private void createRandom() { - this.random = new Random(System.currentTimeMillis() + Thread.currentThread().getId() - - Thread.currentThread().hashCode() + this.cname.hashCode()); - } - - - /** - * Generates a random sequence number - */ - private void generateSeqNum() { - if(this.random == null) - createRandom(); - - seqNum = this.random.nextInt(); - if(seqNum < 0) - seqNum = -seqNum; - while(seqNum > 65535) { - seqNum = seqNum / 10; - } - } - - /** - * Generates a random SSRC - */ - private void generateSsrc() { - if(this.random == null) - createRandom(); - - // Set an SSRC - this.ssrc = this.random.nextInt(); - if(this.ssrc < 0) { - this.ssrc = this.ssrc * -1; - } - } - - /** - * Resolve an SSRC conflict. - * - * Also increments the SSRC conflict counter, after 5 conflicts - * it is assumed there is a loop somewhere and the session will - * terminate. - * - */ - protected void resolveSsrcConflict() { - System.out.println("!!!!!!! Beginning SSRC conflict resolution !!!!!!!!!"); - this.conflictCount++; - - if(this.conflictCount < 5) { - //Don't send any more regular packets out until we have this sorted out. - this.conflict = true; - - //Send byes - rtcpSession.sendByes(); - - //Calculate the next delay - rtcpSession.calculateDelay(); - - //Generate a new Ssrc for ourselves - generateSsrc(); - - //Get the SDES packets out faster - rtcpSession.initial = true; - - this.conflict = false; - System.out.println("SSRC conflict resolution complete"); - - } else { - System.out.println("Too many conflicts. There is probably a loop in the network."); - this.endSession(); - } - } -}