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