src/jlibrtp/RTPSession.java
changeset 13 e684f11070d5
equal deleted inserted replaced
12:c9ff263c29ad 13:e684f11070d5
       
     1 /**
       
     2  * Java RTP Library (jlibrtp)
       
     3  * Copyright (C) 2006 Arne Kepp
       
     4  * 
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Lesser General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2.1 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Lesser General Public License for more details.
       
    14  * 
       
    15  * You should have received a copy of the GNU Lesser General Public
       
    16  * License along with this library; if not, write to the Free Software
       
    17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
       
    18  */
       
    19 package jlibrtp;
       
    20 
       
    21 import java.net.DatagramSocket;
       
    22 import java.net.MulticastSocket;
       
    23 import java.net.DatagramPacket;
       
    24 import java.net.InetAddress;
       
    25 import java.net.InetSocketAddress;
       
    26 import java.util.Iterator;
       
    27 import java.util.concurrent.locks.*;
       
    28 import java.util.Random;
       
    29 import java.util.Enumeration;
       
    30 /**
       
    31  * The RTPSession object is the core of jlibrtp. 
       
    32  * 
       
    33  * One should be instantiated for every communication channel, i.e. if you send voice and video, you should create one for each.
       
    34  * 
       
    35  * 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.
       
    36  * 
       
    37  * RTP Packets are sent synchronously, all other operations are asynchronous.
       
    38  * 
       
    39  * @author Arne Kepp
       
    40  */
       
    41 public class RTPSession {
       
    42 	 /**
       
    43 	  * The debug level is final to avoid compilation of if-statements.</br>
       
    44 	  * 0 provides no debugging information, 20 provides everything </br>
       
    45 	  * Debug output is written to System.out</br>
       
    46 	  * Debug level for RTP related things.
       
    47 	  */
       
    48 	 final static public int rtpDebugLevel = 0;
       
    49 	 /**
       
    50 	  * The debug level is final to avoid compilation of if-statements.</br>
       
    51 	  * 0 provides no debugging information, 20 provides everything </br>
       
    52 	  * Debug output is written to System.out</br>
       
    53 	  * Debug level for RTCP related things.
       
    54 	  */
       
    55 	 final static public int rtcpDebugLevel = 0;
       
    56 	 
       
    57 	 /** RTP unicast socket */
       
    58 	 protected DatagramSocket rtpSock = null;
       
    59 	 /** RTP multicast socket */
       
    60 	 protected MulticastSocket rtpMCSock = null;
       
    61 	 /** RTP multicast group */
       
    62 	 protected InetAddress mcGroup = null;
       
    63 	 
       
    64 	 // Internal state
       
    65 	 /** Whether this session is a multicast session or not */
       
    66 	 protected boolean mcSession = false;
       
    67 	 /** Current payload type, can be changed by application */
       
    68 	 protected int payloadType = 0;
       
    69 	 /** SSRC of this session */
       
    70 	 protected long ssrc;
       
    71 	 /** The last timestamp when we sent something */
       
    72 	 protected long lastTimestamp = 0;
       
    73 	 /** Current sequence number */
       
    74 	 protected int seqNum = 0;
       
    75 	 /** Number of packets sent by this session */
       
    76 	 protected int sentPktCount = 0;
       
    77 	 /** Number of octets sent by this session */
       
    78 	 protected int sentOctetCount = 0;
       
    79 	 
       
    80 	 /** The random seed */
       
    81 	 protected Random random = null;
       
    82 	 
       
    83 	 /** Session bandwidth in BYTES per second */
       
    84 	 protected int bandwidth = 8000;
       
    85 	 
       
    86 	 /** By default we do not return packets from strangers in unicast mode */
       
    87 	 protected boolean naiveReception = false;
       
    88 	 
       
    89 	 /** Should the library attempt frame reconstruction? */
       
    90 	 protected boolean frameReconstruction = true;
       
    91 	 
       
    92 	 /** Maximum number of packets used for reordering */
       
    93 	 protected int pktBufBehavior = 3;
       
    94 	 
       
    95 	 /** Participant database */
       
    96 	 protected ParticipantDatabase partDb = new ParticipantDatabase(this); 
       
    97 	 /** Handle to application interface for RTP */
       
    98 	 protected RTPAppIntf appIntf = null;
       
    99 	 /** Handle to application interface for RTCP (optional) */
       
   100 	 protected RTCPAppIntf rtcpAppIntf = null;
       
   101 	 /** Handle to application interface for AVPF, RFC 4585 (optional) */
       
   102 	 protected RTCPAVPFIntf rtcpAVPFIntf = null;
       
   103 	 /** Handle to application interface for debugging */
       
   104 	 protected DebugAppIntf debugAppIntf = null;
       
   105 	 
       
   106 	 /** The RTCP session associated with this RTP Session */
       
   107 	 protected RTCPSession rtcpSession = null;
       
   108 	 /** The thread for receiving RTP packets */
       
   109 	 protected RTPReceiverThread recvThrd = null;
       
   110 	 /** The thread for invoking callbacks for RTP packets */
       
   111 	 protected AppCallerThread appCallerThrd = null;
       
   112 	 
       
   113 	 /** Lock to protect the packet buffers */
       
   114 	 final protected Lock pktBufLock = new ReentrantLock();
       
   115 	 /** Condition variable, to tell the  */
       
   116 	 final protected Condition pktBufDataReady = pktBufLock.newCondition();
       
   117 	 
       
   118 	 /** Enough is enough, set to true when you want to quit. */
       
   119 	 protected boolean endSession = false;
       
   120 	 /** Only one registered application, please */
       
   121 	 protected boolean registered = false;
       
   122 	 /** We're busy resolving a SSRC conflict, please try again later */
       
   123 	 protected boolean conflict = false;
       
   124 	 /** Number of conflicts observed, exessive number suggests loop in network */
       
   125 	 protected int conflictCount = 0;
       
   126 	 
       
   127 	 /** SDES CNAME */
       
   128 	 protected String cname = null;
       
   129 	 /** SDES The participant's real name */
       
   130 	 public String name = null;
       
   131 	 /** SDES The participant's email */
       
   132 	 public String email = null;
       
   133 	 /** SDES The participant's phone number */
       
   134 	 public String phone = null;
       
   135 	 /** SDES The participant's location*/
       
   136 	 public String loc = null;
       
   137 	 /** SDES The tool the participants is using */
       
   138 	 public String tool = null;
       
   139 	 /** SDES A note */
       
   140 	 public String note = null;
       
   141 	 /** SDES A priv string, loosely defined */
       
   142 	 public String priv = null;
       
   143 		
       
   144 	 // RFC 4585 stuff. This should live on RTCPSession, but we need to have this
       
   145 	 // infromation ready by the time the RTCP Session starts
       
   146 	 // 0 = RFC 3550 , -1 = ACK , 1 = Immediate feedback, 2 = Early RTCP,  
       
   147 	 protected int rtcpMode = 0;
       
   148 	 protected int fbEarlyThreshold = -1;		// group size, immediate -> early transition point
       
   149 	 protected int fbRegularThreshold = -1;	// group size, early -> regular transition point
       
   150 	 protected int minInterval = 5000;		// minimum interval
       
   151 	 protected int fbMaxDelay = 1000;			// how long the information is useful
       
   152 	 // RTCP bandwidth
       
   153 	 protected int rtcpBandwidth = -1;
       
   154 	 
       
   155 	 
       
   156 	 /**
       
   157 	  * Returns an instance of a <b>unicast</b> RTP session. 
       
   158 	  * Following this you should adjust any settings and then register your application.
       
   159 	  * 
       
   160 	  * The sockets should have external ip addresses, else your CNAME automatically
       
   161 	  * generated CNAMe will be bad.
       
   162 	  * 
       
   163 	  * @param	rtpSocket UDP socket to receive RTP communication on
       
   164 	  * @param	rtcpSocket UDP socket to receive RTCP communication on, null if none.
       
   165 	  */
       
   166 	 public RTPSession(DatagramSocket rtpSocket, DatagramSocket rtcpSocket) {
       
   167 		 mcSession = false;
       
   168 		 rtpSock = rtpSocket;
       
   169 		 this.generateCNAME();
       
   170 		 this.generateSsrc();
       
   171 		 this.rtcpSession = new RTCPSession(this,rtcpSocket);
       
   172 		 
       
   173 		 // The sockets are not always imediately available?
       
   174 		 try { Thread.sleep(1); } catch (InterruptedException e) { System.out.println("RTPSession sleep failed"); }
       
   175 	 }
       
   176 	 
       
   177 	 /**
       
   178 	  * Returns an instance of a <b>multicast</b> RTP session. 
       
   179 	  * Following this you should register your application.
       
   180 	  * 
       
   181 	  * The sockets should have external ip addresses, else your CNAME automatically
       
   182 	  * generated CNAMe will be bad.
       
   183 	  * 
       
   184 	  * @param	rtpSock a multicast socket to receive RTP communication on
       
   185 	  * @param	rtcpSock a multicast socket to receive RTP communication on
       
   186 	  * @param	multicastGroup the multicast group that we want to communicate with.
       
   187 	  */
       
   188 	 public RTPSession(MulticastSocket rtpSock, MulticastSocket rtcpSock, InetAddress multicastGroup) throws Exception {
       
   189 		 mcSession = true;
       
   190 		 rtpMCSock =rtpSock;
       
   191 		 mcGroup = multicastGroup;
       
   192 		 rtpMCSock.joinGroup(mcGroup);
       
   193 		 rtcpSock.joinGroup(mcGroup);
       
   194 		 this.generateCNAME();
       
   195 		 this.generateSsrc();
       
   196 		 this.rtcpSession = new RTCPSession(this,rtcpSock,mcGroup);
       
   197 		 
       
   198 		 // The sockets are not always imediately available?
       
   199 		 try { Thread.sleep(1); } catch (InterruptedException e) { System.out.println("RTPSession sleep failed"); }
       
   200 	 }
       
   201 	 
       
   202 	 /**
       
   203 	  * Registers an application (RTPAppIntf) with the RTP session.
       
   204 	  * The session will call receiveData() on the supplied instance whenever data has been received.
       
   205 	  * 
       
   206 	  * Following this you should set the payload type and add participants to the session.
       
   207 	  * 
       
   208 	  * @param	rtpApp an object that implements the RTPAppIntf-interface
       
   209 	  * @param	rtcpApp an object that implements the RTCPAppIntf-interface (optional)
       
   210 	  * @return	-1 if this RTPSession-instance already has an application registered.
       
   211 	  */
       
   212 	 public int RTPSessionRegister(RTPAppIntf rtpApp, RTCPAppIntf rtcpApp, DebugAppIntf debugApp) {
       
   213 		if(registered) {
       
   214 			System.out.println("RTPSessionRegister(): Can\'t register another application!");
       
   215 			return -1;
       
   216 		} else {
       
   217 			registered = true;
       
   218 			generateSeqNum();
       
   219 			if(RTPSession.rtpDebugLevel > 0) {
       
   220 				System.out.println("-> RTPSessionRegister");
       
   221 			}  
       
   222 			this.appIntf = rtpApp;
       
   223 			this.rtcpAppIntf = rtcpApp;
       
   224 			this.debugAppIntf = debugApp;
       
   225 			
       
   226 			recvThrd = new RTPReceiverThread(this);
       
   227 			appCallerThrd = new AppCallerThread(this, rtpApp);
       
   228 			recvThrd.start();
       
   229 		 	appCallerThrd.start();
       
   230 		 	rtcpSession.start();
       
   231 		 	return 0;
       
   232 		}
       
   233 	}
       
   234 	
       
   235 	 /**
       
   236 	  * Send data to all participants registered as receivers, using the current timeStamp,
       
   237 	  * dynamic sequence number and the current payload type specified for the session.
       
   238 	  * 
       
   239 	  * @param buf A buffer of bytes, less than 1496 bytes
       
   240 	  * @return	null if there was a problem, {RTP Timestamp, Sequence number} otherwise
       
   241 	  */
       
   242 	 public long[] sendData(byte[] buf) {
       
   243 		 byte[][] tmp = {buf}; 
       
   244 		 long[][] ret = this.sendData(tmp, null, null, -1, null);
       
   245 		 
       
   246 		 if(ret != null)
       
   247 			 return ret[0];
       
   248 		 
       
   249 		 return null;
       
   250 	 }
       
   251 	 
       
   252 	 /**
       
   253 	  * Send data to all participants registered as receivers, using the specified timeStamp,
       
   254 	  * sequence number and the current payload type specified for the session.
       
   255 	  * 
       
   256 	  * @param buf A buffer of bytes, less than 1496 bytes
       
   257 	  * @param rtpTimestamp the RTP timestamp to be used in the packet
       
   258 	  * @param seqNum the sequence number to be used in the packet
       
   259 	  * @return null if there was a problem, {RTP Timestamp, Sequence number} otherwise
       
   260 	  */
       
   261 	 public long[] sendData(byte[] buf, long rtpTimestamp, long seqNum) {
       
   262 		 byte[][] tmp = {buf};
       
   263 		 long[][] ret = this.sendData(tmp, null, null, -1, null);
       
   264 		 
       
   265 		 if(ret != null)
       
   266 			 return ret[0];
       
   267 		 
       
   268 		 return null;
       
   269 	 }
       
   270 	 
       
   271 	 /**
       
   272 	  * Send data to all participants registered as receivers, using the current timeStamp and
       
   273 	  * payload type. The RTP timestamp will be the same for all the packets.
       
   274 	  * 
       
   275 	  * @param buffers A buffer of bytes, should not bed padded and less than 1500 bytes on most networks.
       
   276 	  * @param csrcArray an array with the SSRCs of contributing sources
       
   277 	  * @param markers An array indicating what packets should be marked. Rarely anything but the first one
       
   278 	  * @param rtpTimestamp The RTP timestamp to be applied to all packets
       
   279 	  * @param seqNumbers An array with the sequence number associated with each byte[]
       
   280 	  * @return	null if there was a problem sending the packets, 2-dim array with {RTP Timestamp, Sequence number}
       
   281 	  */
       
   282 	 public long[][] sendData(byte[][] buffers, long[] csrcArray, boolean[] markers, long rtpTimestamp, long[] seqNumbers) {
       
   283 		 if(RTPSession.rtpDebugLevel > 5) {
       
   284 			 System.out.println("-> RTPSession.sendData(byte[])");
       
   285 		 }
       
   286 
       
   287 		 // Same RTP timestamp for all
       
   288 		 if(rtpTimestamp < 0)
       
   289 			 rtpTimestamp = System.currentTimeMillis();
       
   290 		 
       
   291 		 // Return values
       
   292 		 long[][] ret = new long[buffers.length][2];
       
   293 
       
   294 		 for(int i=0; i<buffers.length; i++) {
       
   295 			 byte[] buf = buffers[i];
       
   296 			 
       
   297 			 boolean marker = false;
       
   298 			 if(markers != null)
       
   299 				  marker = markers[i];
       
   300 			 
       
   301 			 if(buf.length > 1500) {
       
   302 				 System.out.println("RTPSession.sendData() called with buffer exceeding 1500 bytes ("+buf.length+")");
       
   303 			 }
       
   304 
       
   305 			 // Get the return values
       
   306 			 ret[i][0] = rtpTimestamp;
       
   307 			 if(seqNumbers == null) {
       
   308 				 ret[i][1] = getNextSeqNum();
       
   309 			 } else {
       
   310 				 ret[i][1] = seqNumbers[i];
       
   311 			 }
       
   312 			 // Create a new RTP Packet
       
   313 			 RtpPkt pkt = new RtpPkt(rtpTimestamp,this.ssrc,(int) ret[i][1],this.payloadType,buf);
       
   314 
       
   315 			 if(csrcArray != null)
       
   316 				 pkt.setCsrcs(csrcArray);
       
   317 
       
   318 			 pkt.setMarked(marker);
       
   319 
       
   320 			 // Creates a raw packet
       
   321 			 byte[] pktBytes = pkt.encode();
       
   322 			 
       
   323 			 //System.out.println(Integer.toString(StaticProcs.bytesToUIntInt(pktBytes, 2)));
       
   324 
       
   325 			 // Pre-flight check, are resolving an SSRC conflict?
       
   326 			 if(this.conflict) {
       
   327 				 System.out.println("RTPSession.sendData() called while trying to resolve conflict.");
       
   328 				 return null;
       
   329 			 }
       
   330 
       
   331 
       
   332 			 if(this.mcSession) {
       
   333 				 DatagramPacket packet = null;
       
   334 
       
   335 
       
   336 				 try {
       
   337 					 packet = new DatagramPacket(pktBytes,pktBytes.length,this.mcGroup,this.rtpMCSock.getPort());
       
   338 				 } catch (Exception e) {
       
   339 					 System.out.println("RTPSession.sendData() packet creation failed.");
       
   340 					 e.printStackTrace();
       
   341 					 return null;
       
   342 				 }
       
   343 
       
   344 				 try {
       
   345 					 rtpMCSock.send(packet);
       
   346 					 //Debug
       
   347 					 if(this.debugAppIntf != null) {
       
   348 						 this.debugAppIntf.packetSent(1, (InetSocketAddress) packet.getSocketAddress(), 
       
   349 								 new String("Sent multicast RTP packet of size " + packet.getLength() + 
       
   350 										 " to " + packet.getSocketAddress().toString() + " via " 
       
   351 										 + rtpMCSock.getLocalSocketAddress().toString()));
       
   352 					 }
       
   353 				 } catch (Exception e) {
       
   354 					 System.out.println("RTPSession.sendData() multicast failed.");
       
   355 					 e.printStackTrace();
       
   356 					 return null;
       
   357 				 }		
       
   358 
       
   359 			 } else {
       
   360 				 // Loop over recipients
       
   361 				 Iterator<Participant> iter = partDb.getUnicastReceivers();
       
   362 				 while(iter.hasNext()) {			
       
   363 					 InetSocketAddress receiver = iter.next().rtpAddress;
       
   364 					 DatagramPacket packet = null;
       
   365 
       
   366 					 if(RTPSession.rtpDebugLevel > 15) {
       
   367 						 System.out.println("   Sending to " + receiver.toString());
       
   368 					 }
       
   369 
       
   370 					 try {
       
   371 						 packet = new DatagramPacket(pktBytes,pktBytes.length,receiver);
       
   372 					 } catch (Exception e) {
       
   373 						 System.out.println("RTPSession.sendData() packet creation failed.");
       
   374 						 e.printStackTrace();
       
   375 						 return null;
       
   376 					 }
       
   377 
       
   378 					 //Actually send the packet
       
   379 					 try {
       
   380 						 rtpSock.send(packet);
       
   381 						 //Debug
       
   382 						 if(this.debugAppIntf != null) {
       
   383 							 this.debugAppIntf.packetSent(0, (InetSocketAddress) packet.getSocketAddress(), 
       
   384 									 new String("Sent unicast RTP packet of size " + packet.getLength() + 
       
   385 											 " to " + packet.getSocketAddress().toString() + " via " 
       
   386 											 + rtpSock.getLocalSocketAddress().toString()));
       
   387 						 }
       
   388 					 } catch (Exception e) {
       
   389 						 System.out.println("RTPSession.sendData() unicast failed.");
       
   390 						 e.printStackTrace();
       
   391 						 return null;
       
   392 					 }
       
   393 				 }
       
   394 			 }
       
   395 
       
   396 			 //Update our stats
       
   397 			 this.sentPktCount++;
       
   398 			 this.sentOctetCount++;
       
   399 
       
   400 			 if(RTPSession.rtpDebugLevel > 5) {
       
   401 				 System.out.println("<- RTPSession.sendData(byte[]) " + pkt.getSeqNumber());
       
   402 			 }  
       
   403 		 }
       
   404 
       
   405 		 return ret;
       
   406 	 }
       
   407 	 
       
   408 	 /**
       
   409 	  * Send RTCP App packet to receiver specified by ssrc
       
   410 	  * 
       
   411 	  * 
       
   412 	  * 
       
   413 	  * Return values:
       
   414 	  *  0 okay
       
   415 	  * -1 no RTCP session established
       
   416 	  * -2 name is not byte[4];
       
   417 	  * -3 data is not byte[x], where x = 4*y for syme y
       
   418 	  * -4 type is not a 5 bit unsigned integer
       
   419 	  * 
       
   420 	  * Note that a return value of 0 does not guarantee delivery.
       
   421 	  * The participant must also exist in the participant database,
       
   422 	  * otherwise the message will eventually be deleted.
       
   423 	  * 
       
   424 	  * @param ssrc of the participant you want to reach
       
   425 	  * @param type the RTCP App packet subtype, default 0
       
   426 	  * @param name the ASCII (in byte[4]) representation
       
   427 	  * @param data the data itself 
       
   428 	  * @return 0 if okay, negative value otherwise (see above)
       
   429 	  */
       
   430 	
       
   431 	 public int sendRTCPAppPacket(long ssrc, int type, byte[] name, byte[] data) {
       
   432 		 if(this.rtcpSession == null)
       
   433 			 return -1;
       
   434 		 
       
   435 		 if(name.length != 4)
       
   436 			 return -2;
       
   437 		 
       
   438 		 if(data.length % 4 != 0)
       
   439 			 return -3;
       
   440 		 
       
   441 		 if(type > 63 || type < 0 )
       
   442 			 return -4;
       
   443 		
       
   444 		RtcpPktAPP pkt = new RtcpPktAPP(ssrc, type, name, data);
       
   445 		this.rtcpSession.addToAppQueue(ssrc, pkt);
       
   446 		
       
   447 		return 0;
       
   448 	 }
       
   449 	 /**
       
   450 	  * Add a participant object to the participant database.
       
   451 	  * 
       
   452 	  * If packets have already been received from this user, we will try to update the automatically inserted participant with the information provided here.
       
   453 	  *
       
   454 	  * @param p A participant.
       
   455 	  */
       
   456 	public int addParticipant(Participant p) {
       
   457 		//For now we make all participants added this way persistent
       
   458 		p.unexpected = false;
       
   459 		return this.partDb.addParticipant(0, p);
       
   460 	}
       
   461 	
       
   462 	 /**
       
   463 	  * Remove a participant from the database. All buffered packets will be destroyed.
       
   464 	  *
       
   465 	  * @param p A participant.
       
   466 	  */
       
   467 	 public void removeParticipant(Participant p) {
       
   468 		partDb.removeParticipant(p);
       
   469 	 }
       
   470 	 
       
   471 	 public Iterator<Participant> getUnicastReceivers() {
       
   472 		 return partDb.getUnicastReceivers();
       
   473 	 }
       
   474 	 
       
   475 	 public Enumeration<Participant> getParticipants() {
       
   476 		 return partDb.getParticipants();
       
   477 	 }
       
   478 	 
       
   479 	 /**
       
   480 	  * End the RTP Session. This will halt all threads and send bye-messages to other participants.
       
   481 	  * 
       
   482 	  * RTCP related threads may require several seconds to wake up and terminate.
       
   483 	  */
       
   484 	public void endSession() {
       
   485 		this.endSession = true;
       
   486 		
       
   487 		// No more RTP packets, please
       
   488 		if(this.mcSession) {
       
   489 			this.rtpMCSock.close();
       
   490 		} else {
       
   491 			this.rtpSock.close();
       
   492 		}
       
   493 		
       
   494 		// Signal the thread that pushes data to application
       
   495 		this.pktBufLock.lock();
       
   496 		try { this.pktBufDataReady.signalAll(); } finally {
       
   497 			this.pktBufLock.unlock();
       
   498 		}
       
   499 		// Interrupt what may be sleeping
       
   500 		this.rtcpSession.senderThrd.interrupt();
       
   501 		
       
   502 		// Give things a chance to cool down.
       
   503 		try { Thread.sleep(50); } catch (Exception e){ };
       
   504 		
       
   505 		this.appCallerThrd.interrupt();
       
   506 
       
   507 		// Give things a chance to cool down.
       
   508 		try { Thread.sleep(50); } catch (Exception e){ };
       
   509 		
       
   510 		if(this.rtcpSession != null) {		
       
   511 			// No more RTP packets, please
       
   512 			if(this.mcSession) {
       
   513 				this.rtcpSession.rtcpMCSock.close();
       
   514 			} else {
       
   515 				this.rtcpSession.rtcpSock.close();
       
   516 			}
       
   517 		}
       
   518 	}
       
   519 
       
   520 	
       
   521 	 /**
       
   522 	  * Check whether this session is ending.
       
   523 	  * 
       
   524 	  * @return true if session and associated threads are terminating.
       
   525 	  */
       
   526 	boolean isEnding() {
       
   527 		return this.endSession;
       
   528 	}
       
   529 
       
   530 	/**
       
   531 	 * Overrides CNAME, used for outgoing RTCP packets.
       
   532 	 * 
       
   533 	 * @param cname a string, e.g. username@hostname. Must be unique for session.
       
   534 	 */
       
   535 	public void CNAME(String cname) {
       
   536 		this.cname = cname;
       
   537 	}
       
   538 	
       
   539 	/**
       
   540 	 * Get the current CNAME, used for outgoing SDES packets
       
   541 	 */
       
   542 	public String CNAME() {
       
   543 		return this.cname;
       
   544 	}
       
   545 	
       
   546 	public long getSsrc() {
       
   547 		return this.ssrc;
       
   548 	}
       
   549 	
       
   550 	private void generateCNAME() {
       
   551 		String hostname;
       
   552 		
       
   553 		if(this.mcSession) {
       
   554 			hostname = this.rtpMCSock.getLocalAddress().getCanonicalHostName();
       
   555 		} else {
       
   556 			hostname = this.rtpSock.getLocalAddress().getCanonicalHostName();
       
   557 		}
       
   558 		
       
   559 		//if(hostname.equals("0.0.0.0") && System.getenv("HOSTNAME") != null) {
       
   560 		//	hostname = System.getenv("HOSTNAME");
       
   561 		//}
       
   562 		
       
   563 		cname = System.getProperty("user.name") + "@" + hostname;
       
   564 	}
       
   565 	
       
   566 	/**
       
   567 	 * Change the RTP socket of the session. 
       
   568 	 * Peers must be notified through SIP or other signalling protocol.
       
   569 	 * Only valid if this is a unicast session to begin with.
       
   570 	 * 
       
   571 	 * @param newSock integer for new port number, check it is free first.
       
   572 	 */
       
   573 	public int updateRTPSock(DatagramSocket newSock) {
       
   574 		if(!mcSession) {
       
   575 			 rtpSock = newSock;
       
   576 			 return 0;
       
   577 		} else {
       
   578 			System.out.println("Can't switch from multicast to unicast.");
       
   579 			return -1;
       
   580 		}
       
   581 	}
       
   582 	
       
   583 	/**
       
   584 	 * Change the RTCP socket of the session. 
       
   585 	 * Peers must be notified through SIP or other signalling protocol.
       
   586 	 * Only valid if this is a unicast session to begin with.
       
   587 	 * 
       
   588 	 * @param newSock the new unicast socket for RTP communication.
       
   589 	 */
       
   590 	public int updateRTCPSock(DatagramSocket newSock) {
       
   591 		if(!mcSession) {
       
   592 			this.rtcpSession.rtcpSock = newSock;
       
   593 			return 0;
       
   594 		} else {
       
   595 			System.out.println("Can't switch from multicast to unicast.");
       
   596 			return -1;
       
   597 		}
       
   598 	}
       
   599 	
       
   600 	/**
       
   601 	 * Change the RTP multicast socket of the session. 
       
   602 	 * Peers must be notified through SIP or other signalling protocol.
       
   603 	 * Only valid if this is a multicast session to begin with.
       
   604 	 * 
       
   605 	 * @param newSock the new multicast socket for RTP communication.
       
   606 	 */
       
   607 	public int updateRTPSock(MulticastSocket newSock) {
       
   608 		if(mcSession) {
       
   609 			 this.rtpMCSock = newSock;
       
   610 			 return 0;
       
   611 		} else {
       
   612 			System.out.println("Can't switch from unicast to multicast.");
       
   613 			return -1;
       
   614 		}
       
   615 	}
       
   616 	
       
   617 	/**
       
   618 	 * Change the RTCP multicast socket of the session. 
       
   619 	 * Peers must be notified through SIP or other signalling protocol.
       
   620 	 * Only valid if this is a multicast session to begin with.
       
   621 	 * 
       
   622 	 * @param newSock the new multicast socket for RTCP communication.
       
   623 	 */
       
   624 	public int updateRTCPSock(MulticastSocket newSock) {
       
   625 		if(mcSession) {
       
   626 			this.rtcpSession.rtcpMCSock = newSock;
       
   627 			return 0;
       
   628 		} else {
       
   629 			System.out.println("Can't switch from unicast to multicast.");
       
   630 			return -1;
       
   631 		}
       
   632 	}
       
   633 	
       
   634 	/**
       
   635 	 * 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 <a href="http://www.ietf.org/rfc/rfc3550.txt">3550</a> and <a href="http://www.ietf.org/rfc/rfc3550.txt">3551</a>)
       
   636 	 * 
       
   637 	 * @param payloadT an integer representing the payload type of any subsequent packets that are sent.
       
   638 	 */
       
   639 	public int payloadType(int payloadT) {
       
   640 		if(payloadT > 128 || payloadT < 0) {
       
   641 			return -1;
       
   642 		} else {
       
   643 			this.payloadType = payloadT;
       
   644 			return this.payloadType;
       
   645 		}
       
   646 	}
       
   647 	
       
   648 	/**
       
   649 	 * Get the payload type that is currently used for outgoing RTP packets.
       
   650 	 * 
       
   651 	 * @return payload type as integer
       
   652 	 */
       
   653 	public int payloadType() {
       
   654 		return this.payloadType;
       
   655 	}
       
   656 	
       
   657 	/**
       
   658 	 * Should packets from unknown participants be returned to the application? This can be dangerous.
       
   659 	 * 
       
   660 	 * @param doAccept packets from participants not added by the application.
       
   661 	 */
       
   662 	public void naivePktReception(boolean doAccept) {
       
   663 		naiveReception = doAccept;
       
   664 	}
       
   665 	
       
   666 	/**
       
   667 	 * Are packets from unknown participants returned to the application?
       
   668 	 * 
       
   669 	 * @return whether we accept packets from participants not added by the application.
       
   670 	 */
       
   671 	public boolean naivePktReception() {
       
   672 		return naiveReception;
       
   673 	}
       
   674 	
       
   675 	/**
       
   676 	 * Set the number of RTP packets that should be buffered when a packet is
       
   677 	 * missing or received out of order. Setting this number high increases
       
   678 	 * the chance of correctly reordering packets, but increases latency when
       
   679 	 * a packet is dropped by the network.
       
   680 	 * 
       
   681 	 * Packets that arrive in order are not affected, they are passed straight
       
   682 	 * to the application.
       
   683 	 * 
       
   684 	 * The maximum delay is numberofPackets * packet rate , where the packet rate
       
   685 	 * depends on the codec and profile used by the sender.
       
   686 	 * 
       
   687 	 * Valid values:
       
   688 	 *  >0 - The maximum number of packets (based on RTP Timestamp) that may accumulate
       
   689 	 *  0 - All valid packets received in order will be given to the application
       
   690 	 * -1 - All valid packets will be given to the application
       
   691 	 * 
       
   692 	 * @param behavior the be
       
   693 	 * @return the behavior set, unchanged in the case of a erroneous value
       
   694 	 */
       
   695 	public int packetBufferBehavior(int behavior) {
       
   696 		if(behavior > -2) {
       
   697 			this.pktBufBehavior = behavior; 
       
   698 			// Signal the thread that pushes data to application
       
   699 			this.pktBufLock.lock();
       
   700 			try { this.pktBufDataReady.signalAll(); } finally {
       
   701 				this.pktBufLock.unlock();
       
   702 			}
       
   703 			return this.pktBufBehavior;
       
   704 		} else {
       
   705 			return this.pktBufBehavior;
       
   706 		}
       
   707 	}
       
   708 	
       
   709 	/**
       
   710 	 * The number of RTP packets that should be buffered when a packet is
       
   711 	 * missing or received out of order. A high number  increases the chance 
       
   712 	 * of correctly reordering packets, but increases latency when a packet is 
       
   713 	 * dropped by the network.
       
   714 	 * 
       
   715 	 * A negative value disables the buffering, out of order packets will simply be dropped.
       
   716 	 * 
       
   717 	 * @return the maximum number of packets that can accumulate before the first is returned
       
   718 	 */
       
   719 	public int packetBufferBehavior() {
       
   720 		return this.pktBufBehavior;
       
   721 	}
       
   722 	
       
   723 	/**
       
   724 	 * Set whether the stack should operate in RFC 4585 mode.
       
   725 	 * 
       
   726 	 * This will automatically call adjustPacketBufferBehavior(-1),
       
   727 	 * i.e. disable all RTP packet buffering in jlibrtp,
       
   728 	 * and disable frame reconstruction 
       
   729 	 * 
       
   730 	 * @param rtcpAVPFIntf the in
       
   731 	 */
       
   732 	public int registerAVPFIntf(RTCPAVPFIntf rtcpAVPFIntf, int maxDelay, int earlyThreshold, int regularThreshold ) {
       
   733 		if(this.rtcpSession != null) {
       
   734 			this.packetBufferBehavior(-1);
       
   735 			this.frameReconstruction = false;
       
   736 			this.rtcpAVPFIntf = rtcpAVPFIntf;
       
   737 			this.fbEarlyThreshold = earlyThreshold;
       
   738 			this.fbRegularThreshold = regularThreshold;	
       
   739 			return 0;
       
   740 		} else {
       
   741 			return -1;
       
   742 		}
       
   743 	}
       
   744 	
       
   745 	/**
       
   746 	 * Unregisters the RTCP AVPF interface, thereby going from
       
   747 	 * RFC 4585 mode to RFC 3550
       
   748 	 * 
       
   749 	 * You still have to adjust packetBufferBehavior() and
       
   750 	 * frameReconstruction.
       
   751 	 * 	
       
   752 	 */
       
   753 	public void unregisterAVPFIntf() {
       
   754 		this.fbEarlyThreshold = -1;
       
   755 		this.fbRegularThreshold = -1;	
       
   756 		this.rtcpAVPFIntf = null;
       
   757 	}
       
   758 	
       
   759 	/**
       
   760 	 * Enable / disable frame reconstruction in the packet buffers.
       
   761 	 * This is only relevant if getPacketBufferBehavior > 0;
       
   762 	 * 
       
   763 	 * Default is true.
       
   764 	 */
       
   765 	public void frameReconstruction(boolean toggle) {
       
   766 		this.frameReconstruction = toggle;
       
   767 	}
       
   768 	
       
   769 	/**
       
   770 	 * Whether the packet buffer will attempt to reconstruct
       
   771 	 * packet automatically.  
       
   772 	 * 
       
   773 	 * @return the status
       
   774 	 */
       
   775 	public boolean frameReconstruction() {
       
   776 		return this.frameReconstruction;
       
   777 	}
       
   778 	
       
   779 	/**
       
   780 	 * The bandwidth currently allocated to the session,
       
   781 	 * in bytes per second. The default is 8000.
       
   782 	 * 
       
   783 	 * This value is not enforced and currently only
       
   784 	 * used to calculate the RTCP interval to ensure the
       
   785 	 * control messages do not exceed 5% of the total bandwidth
       
   786 	 * described here.
       
   787 	 * 
       
   788 	 * Since the actual value may change a conservative
       
   789 	 * estimate should be used to avoid RTCP flooding.
       
   790 	 * 
       
   791 	 * see rtcpBandwidth(void)
       
   792 	 * 
       
   793 	 * @return current bandwidth setting
       
   794 	 */
       
   795 	public int sessionBandwidth() {
       
   796 		return this.bandwidth;
       
   797 	}
       
   798 	
       
   799 	/**
       
   800 	 * Set the bandwidth of the session.
       
   801 	 * 
       
   802 	 * See sessionBandwidth(void) for details. 
       
   803 	 * 
       
   804 	 * @param bandwidth the new value requested, in bytes per second
       
   805 	 * @return the actual value set
       
   806  	 */
       
   807 	public int sessionBandwidth(int bandwidth) {
       
   808 		if(bandwidth < 1) {
       
   809 			this.bandwidth = 8000;
       
   810 		} else {
       
   811 			this.bandwidth = bandwidth;
       
   812 		}
       
   813 		return this.bandwidth;
       
   814 	}
       
   815 	
       
   816 	
       
   817 	/**
       
   818 	 * RFC 3550 dictates that 5% of the total bandwidth,
       
   819 	 * as set by sessionBandwidth, should be dedicated
       
   820 	 * to RTCP traffic. This 
       
   821 	 * 
       
   822 	 * This should normally not be done, but is permissible in 
       
   823 	 * conjunction with feedback (RFC 4585) and possibly
       
   824 	 * other profiles. 
       
   825 	 * 
       
   826 	 * Also see sessionBandwidth(void)
       
   827 	 * 
       
   828 	 * @return current RTCP bandwidth setting, -1 means not in use
       
   829 	 */
       
   830 	public int rtcpBandwidth() {
       
   831 		return this.rtcpBandwidth;
       
   832 	}
       
   833 	
       
   834 	/**
       
   835 	 * Set the RTCP bandwidth, see rtcpBandwidth(void) for details. 
       
   836 	 * 
       
   837 	 * This function must be
       
   838 	 * 
       
   839 	 * @param bandwidth the new value requested, in bytes per second or -1 to disable
       
   840 	 * @return the actual value set
       
   841  	 */
       
   842 	public int rtcpBandwidth(int bandwidth) {
       
   843 		if(bandwidth < -1) {
       
   844 			this.rtcpBandwidth = -1;
       
   845 		} else {
       
   846 			this.rtcpBandwidth = bandwidth;
       
   847 		}
       
   848 		return this.rtcpBandwidth;
       
   849 	}
       
   850 	
       
   851 	/********************************************* Feedback message stuff ***************************************/
       
   852 	
       
   853 	/**
       
   854 	 * Adds a Picture Loss Indication to the feedback queue
       
   855 	 * 
       
   856 	 * @param ssrcMediaSource
       
   857 	 * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant
       
   858 	 */
       
   859 	public int fbPictureLossIndication(long ssrcMediaSource) {
       
   860 		int ret = 0;
       
   861 		
       
   862 		if(this.rtcpAVPFIntf == null)
       
   863 			return -1;
       
   864 		
       
   865 		RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource);
       
   866 		pkt.makePictureLossIndication();
       
   867 		ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt);
       
   868 		if(ret == 0)
       
   869 			this.rtcpSession.wakeSenderThread(ssrcMediaSource);
       
   870 		return ret; 
       
   871 	}
       
   872 	
       
   873 	/**
       
   874 	 * Adds a Slice Loss Indication to the feedback queue
       
   875 	 * 
       
   876 	 * @param ssrcMediaSource
       
   877 	 * @param sliFirst macroblock (MB) address of the first lost macroblock
       
   878 	 * @param sliNumber number of lost macroblocks
       
   879 	 * @param sliPictureId six least significant bits of the codec-specific identif
       
   880 	 * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant
       
   881 	 */
       
   882 	public int fbSlicLossIndication(long ssrcMediaSource, int[] sliFirst, int[] sliNumber, int[] sliPictureId) {
       
   883 		int ret = 0;
       
   884 		if(this.rtcpAVPFIntf == null)
       
   885 			return -1;
       
   886 		
       
   887 		RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource);
       
   888 		pkt.makeSliceLossIndication(sliFirst, sliNumber, sliPictureId);
       
   889 		
       
   890 		ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt);
       
   891 		if(ret == 0)
       
   892 			this.rtcpSession.wakeSenderThread(ssrcMediaSource);
       
   893 		return ret; 
       
   894 	}
       
   895 	
       
   896 	/**
       
   897 	 * Adds a Reference Picture Selection Indication to the feedback queue
       
   898 	 * 
       
   899 	 * @param ssrcMediaSource
       
   900 	 * @param bitPadding number of padded bits at end of bitString
       
   901 	 * @param payloadType RTP payload type for codec
       
   902 	 * @param bitString RPSI information as natively defined by the video codec
       
   903 	 * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant
       
   904 	 */
       
   905 	public int fbRefPictureSelIndic(long ssrcMediaSource, int bitPadding, int payloadType, byte[] bitString) {
       
   906 		int ret = 0;
       
   907 		
       
   908 		if(this.rtcpAVPFIntf == null)
       
   909 			return -1;
       
   910 		
       
   911 		RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource);
       
   912 		pkt.makeRefPictureSelIndic(bitPadding, payloadType, bitString);
       
   913 		ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt);
       
   914 		if(ret == 0)
       
   915 			this.rtcpSession.wakeSenderThread(ssrcMediaSource);
       
   916 		return ret; 
       
   917 	}
       
   918 	
       
   919 	/**
       
   920 	 * Adds a Picture Loss Indication to the feedback queue
       
   921 	 * 
       
   922 	 * @param ssrcMediaSource
       
   923 	 * @param bitString the original application message
       
   924 	 * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant
       
   925 	 */
       
   926 	public int fbAppLayerFeedback(long ssrcMediaSource, byte[] bitString) {
       
   927 		int ret = 0;
       
   928 		
       
   929 		if(this.rtcpAVPFIntf == null)
       
   930 			return -1;
       
   931 		
       
   932 		RtcpPktPSFB pkt = new RtcpPktPSFB(this.ssrc, ssrcMediaSource);
       
   933 		pkt.makeAppLayerFeedback(bitString);
       
   934 		ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt);
       
   935 		if(ret == 0)
       
   936 			this.rtcpSession.wakeSenderThread(ssrcMediaSource);
       
   937 		return ret; 
       
   938 	}
       
   939 	
       
   940 	
       
   941 	/**
       
   942 	 * Adds a RTP Feedback packet to the feedback queue.
       
   943 	 * 
       
   944 	 * These are mostly used for NACKs.
       
   945 	 * 
       
   946 	 * @param ssrcMediaSource
       
   947 	 * @param FMT the Feedback Message Subtype
       
   948 	 * @param PID RTP sequence numbers of lost packets
       
   949 	 * @param BLP bitmask of following lost packets, shared index with PID 
       
   950 	 * @return 0 if packet was queued, -1 if no feedback support, 1 if redundant
       
   951 	 */
       
   952 	public int fbPictureLossIndication(long ssrcMediaSource, int FMT, int[] PID, int[] BLP) {
       
   953 		int ret = 0;
       
   954 		
       
   955 		if(this.rtcpAVPFIntf == null)
       
   956 			return -1;
       
   957 		
       
   958 		RtcpPktRTPFB pkt = new RtcpPktRTPFB(this.ssrc, ssrcMediaSource, FMT, PID, BLP);
       
   959 		ret = this.rtcpSession.addToFbQueue(ssrcMediaSource, pkt);
       
   960 		if(ret == 0)
       
   961 			this.rtcpSession.wakeSenderThread(ssrcMediaSource);
       
   962 		return ret; 
       
   963 	}
       
   964 		
       
   965 	/**
       
   966 	 * Fetches the next sequence number for RTP packets.
       
   967 	 * @return the next sequence number 
       
   968 	 */
       
   969 	private int getNextSeqNum() {
       
   970 		seqNum++;
       
   971 		// 16 bit number
       
   972 		if(seqNum > 65536) { 
       
   973 			seqNum = 0;
       
   974 		}
       
   975 		return seqNum;
       
   976 	}
       
   977 	
       
   978 	/** 
       
   979 	 * Initializes a random variable
       
   980 	 *
       
   981 	 */
       
   982 	private void createRandom() {
       
   983 		this.random = new Random(System.currentTimeMillis() + Thread.currentThread().getId() 
       
   984 				- Thread.currentThread().hashCode() + this.cname.hashCode());
       
   985 	}
       
   986 	
       
   987 	
       
   988 	/** 
       
   989 	 * Generates a random sequence number
       
   990 	 */
       
   991 	private void generateSeqNum() {
       
   992 		if(this.random == null)
       
   993 			createRandom();
       
   994 		
       
   995 		seqNum = this.random.nextInt();
       
   996 		if(seqNum < 0)
       
   997 			seqNum = -seqNum;
       
   998 		while(seqNum > 65535) {
       
   999 			seqNum = seqNum / 10;
       
  1000 		}
       
  1001 	}
       
  1002 	
       
  1003 	/**
       
  1004 	 * Generates a random SSRC
       
  1005 	 */
       
  1006 	private void generateSsrc() {
       
  1007 		if(this.random == null)
       
  1008 			createRandom();
       
  1009 		
       
  1010 		// Set an SSRC
       
  1011 		this.ssrc = this.random.nextInt();
       
  1012 		if(this.ssrc < 0) {
       
  1013 			this.ssrc = this.ssrc * -1;
       
  1014 		}	
       
  1015 	}
       
  1016 	
       
  1017 	/**
       
  1018 	 * Resolve an SSRC conflict.
       
  1019 	 * 
       
  1020 	 * Also increments the SSRC conflict counter, after 5 conflicts
       
  1021 	 * it is assumed there is a loop somewhere and the session will
       
  1022 	 * terminate. 
       
  1023 	 *
       
  1024 	 */
       
  1025 	protected void resolveSsrcConflict() {
       
  1026 		System.out.println("!!!!!!! Beginning SSRC conflict resolution !!!!!!!!!");
       
  1027 		this.conflictCount++;
       
  1028 		
       
  1029 		if(this.conflictCount < 5) {
       
  1030 			//Don't send any more regular packets out until we have this sorted out.
       
  1031 			this.conflict = true;
       
  1032 		
       
  1033 			//Send byes
       
  1034 			rtcpSession.sendByes();
       
  1035 		
       
  1036 			//Calculate the next delay
       
  1037 			rtcpSession.calculateDelay();
       
  1038 			
       
  1039 			//Generate a new Ssrc for ourselves
       
  1040 			generateSsrc();
       
  1041 			
       
  1042 			//Get the SDES packets out faster
       
  1043 			rtcpSession.initial = true;
       
  1044 			
       
  1045 			this.conflict = false;
       
  1046 			System.out.println("SSRC conflict resolution complete");
       
  1047 			
       
  1048 		} else {
       
  1049 			System.out.println("Too many conflicts. There is probably a loop in the network.");
       
  1050 			this.endSession();
       
  1051 		}
       
  1052 	}
       
  1053 }