diff -r f5a5d9237d69 -r e8d6255306f8 src/jlibrtp/RTPSession.java --- a/src/jlibrtp/RTPSession.java Sat Jan 23 21:48:58 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1301 +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.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.MulticastSocket; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Random; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import org.sipdroid.net.tools.DataFramePool; -import org.sipdroid.net.tools.DatagramPool; -import org.sipdroid.net.tools.PktBufNodePool; -import org.sipdroid.net.tools.RtpPktPool; - -import android.util.Log; - -/** - * 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 = 1; - /** - * 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 = 1; - - /** 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); - /** First participant */ - protected Participant firstPart; - /** 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; - } - } - - public AppCallerThread getAppCallerThrd() { - return appCallerThrd; - } - - public RTPReceiverThread getRTPRecvThrd() { - return recvThrd; - } - - /** - * 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 < buffers.length; i++) { - byte[] buf = buffers[i]; - - boolean marker = false; - if (markers != null) - marker = markers[i]; - - if (buf.length > 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 = RtpPktPool.getInstance().borrowPkt(); - pkt.initPacket(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; - } - - public void sendData(DatagramPacket packet, RtpPkt pkt) { - if (RTPSession.rtpDebugLevel > 5) { - System.out.println("-> RTPSession.sendData(byte[])"); - } - - pkt.setTimeStamp(System.currentTimeMillis()); - pkt.setSsrc(ssrc); - pkt.setSeqNumber(getNextSeqNum()); - - // Creates a raw packet - pkt.writeHeader(); - - // Pre-flight check, are resolving an SSRC conflict? - if (this.conflict) { - System.out - .println("RTPSession.sendData() called while trying to resolve conflict."); - return; - } - - if (this.mcSession) { - try { - packet.setPort(this.rtpMCSock.getPort()); - packet.setAddress(this.mcGroup); - } catch (Exception e) { - System.out - .println("RTPSession.sendData() packet creation failed."); - e.printStackTrace(); - return; - } - 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; - } - - } else { - try { - packet.setSocketAddress(firstPart.rtpAddress); - } catch (Exception e) { - System.out - .println("RTPSession.sendData() packet creation failed."); - e.printStackTrace(); - return; - } - - // Actually send the packet - try { - - rtpSock.send(packet); - //Log.d("RTP", "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; - } - } - - // Update our stats - this.sentPktCount++; - this.sentOctetCount++; - if (RTPSession.rtpDebugLevel > 5) { - System.out.println("<- RTPSession.sendData(byte[]) " - + pkt.getSeqNumber()); - } - } - - /** - * 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 - firstPart = p; - 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(); - } - } - DatagramPool.removeInstance(); - PktBufNodePool.removeInstance(); - DataFramePool.removeInstance(); - RtpPktPool.removeInstance(); - } - - /** - * 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(); - } - } -}