1 // Copyright (C) 1999-2005 Open Source Telecom Corporation.
2 // Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
3 // Copyright (C) 2015 Cherokees of Idaho.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program 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
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with GNU ccRTP. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // As a special exception, you may use this file as part of a free software
19 // library without restriction. Specifically, if other files instantiate
20 // templates or use macros or inline functions from this file, or you compile
21 // this file and link it with other files to produce an executable, this
22 // file does not by itself cause the resulting executable to be covered by
23 // the GNU General Public License. This exception does not however
24 // invalidate any other reasons why the executable file might be covered by
25 // the GNU General Public License.
26 //
27 // This exception applies only to the code released under the name GNU
28 // ccRTP. If you copy code from other releases into a copy of GNU
29 // ccRTP, as the General Public License permits, the exception does
30 // not apply to the code that you add in this way. To avoid misleading
31 // anyone as to the status of such modified files, you must delete
32 // this exception notice from them.
33 //
34 // If you write modifications of your own for GNU ccRTP, it is your choice
35 // whether to permit this exception to apply to your modifications.
36 // If you do not wish that, delete this exception notice.
37 //
38
39 /**
40 * @file rtp.h
41 *
42 * @short Generic and audio/video profile specific RTP interface of
43 * ccRTP.
44 *
45 * The classes and types in this header provide general RTP
46 * functionality (following RFC 3550) as well as audio/video RTP
47 * profile specific functionality (following RFC 3551).
48 **/
49
50 #ifndef CCXX_RTP_RTP_H_
51 #define CCXX_RTP_RTP_H_
52
53 #include <ccrtp/cqueue.h>
54 #include <ccrtp/channel.h>
55
56 NAMESPACE_COMMONCPP
57
58 /**
59 * @defgroup sessions RTP sessions.
60 * @{
61 **/
62
63 /**
64 * @class RTPSessionBase
65 *
66 * Generic RTP protocol stack for exchange of realtime data. This
67 * stack uses the concept of packet send and receive queues to schedule
68 * and buffer outgoing packets and to arrange or reorder incoming packets
69 * as they arrive.
70 *
71 * This is a template class that allows customization of two aspects:
72 * the underlying network and the control protocol. The RTPDataChannel
73 * and RTCPChannel template parameters specify the socket types to
74 * use. The ServiceQueue template parameter specify which packet queue
75 * is used.
76 *
77 * RTPSessionBase objects do not have any threading policy, thus
78 * allowing to customize this aspect in derived classes (see
79 * SingleThreadRTPSession or RTPSessionPoolBase).
80 *
81 * @author David Sugar <dyfet@ostel.com>
82 * @short RTP protocol stack based on Common C++.
83 **/
84 template <class RTPDataChannel = DualRTPUDPIPv4Channel,
85 class RTCPChannel = DualRTPUDPIPv4Channel,
86 class ServiceQueue = AVPQueue>
87 class __EXPORT TRTPSessionBase : public ServiceQueue
88 {
89 public:
90 /**
91 * Builds a session waiting for packets in a host address.
92 *
93 * @param ia Network address this socket is to be bound.
94 * @param dataPort Transport port the data socket is to be bound.
95 * @param controlPort Transport port the control socket is to be bound.
96 * @param membersSize Initial size of the membership table.
97 * @param app Application this session is associated to.
98 * */
TRTPSessionBase(const InetHostAddress & ia,tpport_t dataPort,tpport_t controlPort,uint32 membersSize,RTPApplication & app)99 TRTPSessionBase(const InetHostAddress& ia, tpport_t dataPort,
100 tpport_t controlPort, uint32 membersSize,
101 RTPApplication& app) :
102 ServiceQueue(membersSize,app)
103 { build(ia,dataPort,controlPort); }
104
105 /**
106 * Builds a session with the specified ssrc identifier for the
107 * local source.
108 *
109 * @param ssrc SSRC identifier for the local source.
110 * @param ia Network address this socket is to be bound.
111 * @param dataPort Transport port the data socket is to be bound.
112 * @param controlPort Transport port the control socket is to be bound.
113 * @param membersSize Initial size of the membership table.
114 * @param app Application this session is associated to.
115 **/
TRTPSessionBase(uint32 ssrc,const InetHostAddress & ia,tpport_t dataPort,tpport_t controlPort,uint32 membersSize,RTPApplication & app)116 TRTPSessionBase(uint32 ssrc,
117 const InetHostAddress& ia,
118 tpport_t dataPort, tpport_t controlPort,
119 uint32 membersSize, RTPApplication& app):
120 ServiceQueue(ssrc,membersSize,app)
121 { build(ia,dataPort,controlPort); }
122
123 /**
124 * Builds a session waiting for packets in a multicast address.
125 * TODO: ssrc constructor for multicast!
126 *
127 * @param ia Multicast address this socket is to be bound.
128 * @param dataPort Transport port the data socket is to be bound.
129 * @param controlPort Transport port the control socket is to be bound.
130 * @param membersSize Initial size of the membership table.
131 * @param app Application this session is associated to.
132 * @param iface Index (from 0 to n) of network interface to join to
133 * multicast group.
134 **/
TRTPSessionBase(const InetMcastAddress & ia,tpport_t dataPort,tpport_t controlPort,uint32 membersSize,RTPApplication & app,uint32 iface)135 TRTPSessionBase(const InetMcastAddress& ia, tpport_t dataPort,
136 tpport_t controlPort, uint32 membersSize,
137 RTPApplication& app, uint32 iface) :
138 ServiceQueue(membersSize,app)
139 { build(ia,dataPort,controlPort,iface); }
140
141 /**
142 * Builds a session waiting for packets in a multicast
143 * address, with the specified ssrc identifier for the local
144 * source.
145 *
146 * @param ssrc SSRC identifier for the local source.
147 * @param ia Multicast address this socket is to be bound.
148 * @param dataPort Transport port the data socket is to be bound.
149 * @param controlPort Transport port the control socket is to be bound.
150 * @param membersSize Initial size of the membership table.
151 * @param app Application this session is associated to.
152 * @param iface Index (from 0 to n) of network interface to join to
153 * multicast group.
154 **/
TRTPSessionBase(uint32 ssrc,const InetMcastAddress & ia,tpport_t dataPort,tpport_t controlPort,uint32 membersSize,RTPApplication & app,uint32 iface)155 TRTPSessionBase(uint32 ssrc,
156 const InetMcastAddress& ia, tpport_t dataPort,
157 tpport_t controlPort, uint32 membersSize,
158 RTPApplication& app, uint32 iface) :
159 ServiceQueue(ssrc,membersSize,app)
160 { build(ia,dataPort,controlPort,iface); }
161
dispatchBYE(const std::string & str)162 virtual size_t dispatchBYE(const std::string &str)
163 {
164 return QueueRTCPManager::dispatchBYE(str);
165 }
166
167 /**
168 * Set the value of the TTL field in the sent packets.
169 *
170 * @param ttl Time To Live
171 * @return error code from the socket operation
172 */
173 inline Socket::Error
setMcastTTL(uint8 ttl)174 setMcastTTL(uint8 ttl)
175 {
176 Socket::Error error = dso->setMulticast(true);
177 if ( error ) return error;
178 error = dso->setTimeToLive(ttl);
179 if ( error ) return error;
180 error = cso->setMulticast(true);
181 if ( error ) return error;
182 return cso->setTimeToLive(ttl);
183 }
184
185 inline virtual
~TRTPSessionBase()186 ~TRTPSessionBase()
187 {
188 endSocket();
189 }
190
getDSO(void)191 inline RTPDataChannel *getDSO(void)
192 {return dso;}
193
194 protected:
195 /**
196 * @param timeout maximum timeout to wait, in microseconds
197 */
198 inline bool
isPendingData(microtimeout_t timeout)199 isPendingData(microtimeout_t timeout)
200 { return dso->isPendingRecv(timeout); }
201
202 InetHostAddress
203 getDataSender(tpport_t *port = NULL) const
204 { return dso->getSender(port); }
205
206 inline size_t
getNextDataPacketSize()207 getNextDataPacketSize() const
208 { return dso->getNextPacketSize(); }
209
210 /**
211 * Receive data from the data channel/socket.
212 *
213 * @param buffer Memory region to read to.
214 * @param len Maximum number of octets to get.
215 * @param na Source network address.
216 * @param tp Source transport port.
217 * @return Number of octets actually read.
218 */
219 inline size_t
recvData(unsigned char * buffer,size_t len,InetHostAddress & na,tpport_t & tp)220 recvData(unsigned char* buffer, size_t len,
221 InetHostAddress& na, tpport_t& tp)
222 { na = dso->getSender(tp); return dso->recv(buffer, len); }
223
224 inline void
setDataPeer(const InetAddress & host,tpport_t port)225 setDataPeer(const InetAddress &host, tpport_t port)
226 { dso->setPeer(host,port); }
227
228
229 /**
230 * @param buffer memory region to write from
231 * @param len number of octets to write
232 */
233 inline size_t
sendData(const unsigned char * const buffer,size_t len)234 sendData(const unsigned char* const buffer, size_t len)
235 { return dso->send(buffer, len); }
236
getDataRecvSocket()237 inline SOCKET getDataRecvSocket() const
238 { return dso->getRecvSocket(); }
239
240 /**
241 * @param timeout maximum timeout to wait, in microseconds
242 * @return whether there are packets waiting to be picked
243 */
244 inline bool
isPendingControl(microtimeout_t timeout)245 isPendingControl(microtimeout_t timeout)
246 { return cso->isPendingRecv(timeout); }
247
248 InetHostAddress
249 getControlSender(tpport_t *port = NULL) const
250 { return cso->getSender(port); }
251
252 /**
253 * Receive data from the control channel/socket.
254 *
255 * @param buffer Buffer where to get data.
256 * @param len Maximum number of octets to get.
257 * @param na Source network address.
258 * @param tp Source transport port.
259 * @return Number of octets actually read.
260 **/
261 inline size_t
recvControl(unsigned char * buffer,size_t len,InetHostAddress & na,tpport_t & tp)262 recvControl(unsigned char *buffer, size_t len,
263 InetHostAddress& na, tpport_t& tp)
264 { na = cso->getSender(tp); return cso->recv(buffer,len); }
265
266 inline void
setControlPeer(const InetAddress & host,tpport_t port)267 setControlPeer(const InetAddress &host, tpport_t port)
268 { cso->setPeer(host,port); }
269
270 /**
271 * @return number of octets actually written
272 * @param buffer
273 * @param len
274 */
275 inline size_t
sendControl(const unsigned char * const buffer,size_t len)276 sendControl(const unsigned char* const buffer, size_t len)
277 { return cso->send(buffer,len); }
278
getControlRecvSocket()279 inline SOCKET getControlRecvSocket() const
280 { return cso->getRecvSocket(); }
281
282 /**
283 * Join a multicast group.
284 *
285 * @param ia address of the multicast group
286 * @return error code from the socket operation
287 */
288 inline Socket::Error
joinGroup(const InetMcastAddress & ia,uint32 iface)289 joinGroup(const InetMcastAddress& ia, uint32 iface)
290 {
291 Socket::Error error = dso->setMulticast(true);
292 if ( error ) return error;
293 error = dso->join(ia,iface);
294 if ( error ) return error;
295 error = cso->setMulticast(true);
296 if ( error ) {
297 dso->drop(ia);
298 return error;
299 }
300 error = cso->join(ia,iface);
301 if ( error ) {
302 dso->drop(ia);
303 return error;
304 }
305 return Socket::errSuccess;
306 }
307
308 /**
309 * Leave a multicast group.
310 *
311 * @param ia address of the multicast group
312 * @return error code from the socket operation
313 */
314 inline Socket::Error
leaveGroup(const InetMcastAddress & ia)315 leaveGroup(const InetMcastAddress& ia)
316 {
317 Socket::Error error = dso->setMulticast(false);
318 if ( error ) return error;
319 error = dso->leaveGroup(ia);
320 if ( error ) return error;
321 error = cso->setMulticast(false);
322 if ( error ) return error;
323 return cso->leaveGroup(ia);
324 }
325
326 inline void
endSocket()327 endSocket()
328 {
329 if (dso) {
330 dso->endSocket();
331 delete dso;
332 }
333 dso = NULL;
334 if (cso) {
335 cso->endSocket();
336 delete cso;
337 }
338 cso = NULL;
339 }
340
341 private:
342 void
build(const InetHostAddress & ia,tpport_t dataPort,tpport_t controlPort)343 build(const InetHostAddress& ia, tpport_t dataPort,
344 tpport_t controlPort)
345 {
346 if ( 0 == controlPort ) {
347 dataBasePort = even_port(dataPort);
348 controlBasePort = dataBasePort + 1;
349 } else {
350 dataBasePort = dataPort;
351 controlBasePort = controlPort;
352 }
353 dso = new RTPDataChannel(ia,dataBasePort);
354 cso = new RTCPChannel(ia,controlBasePort);
355 }
356
357 void
build(const InetMcastAddress & ia,tpport_t dataPort,tpport_t controlPort,uint32 iface)358 build(const InetMcastAddress& ia, tpport_t dataPort,
359 tpport_t controlPort, uint32 iface)
360 {
361 if ( 0 == controlPort ) {
362 dataBasePort = even_port(dataPort);
363 controlBasePort = dataBasePort + 1;
364 } else {
365 dataBasePort = dataPort;
366 controlBasePort = controlPort;
367 }
368 dso = new RTPDataChannel(InetHostAddress("0.0.0.0"),dataBasePort);
369 cso = new RTCPChannel(InetHostAddress("0.0.0.0"),controlBasePort);
370 joinGroup(ia,iface);
371 }
372
373 /**
374 * Ensure a port number is odd. If it is an even number, return
375 * the next lower (odd) port number.
376 *
377 * @param port number to filter
378 * @return filtered (odd) port number
379 */
380 inline tpport_t
odd_port(tpport_t port)381 odd_port(tpport_t port)
382 { return (port & 0x01)? (port) : (port - 1); }
383
384 /**
385 * Ensure a port number is even. If it is an odd number, return
386 * the next lower (even) port number.
387 *
388 * @param port number to filter
389 * @return filtered (even) port number
390 */
391 inline tpport_t
even_port(tpport_t port)392 even_port(tpport_t port)
393 { return (port & 0x01)? (port - 1) : (port); }
394
395 tpport_t dataBasePort;
396 tpport_t controlBasePort;
397
398 protected:
399 RTPDataChannel* dso;
400 RTCPChannel* cso;
401 friend class RTPSessionBaseHandler;
402 };
403
404 /**
405 * @class SingleThreadRTPSession
406 *
407 * This template class adds the threading aspect to the RTPSessionBase
408 * template in one of the many possible ways. It inherits from a
409 * single execution thread that schedules sending of outgoing packets
410 * and receipt of incoming packets.
411 *
412 * @author Federico Montesino Pouzols <fedemp@altern.org>
413 **/
414 template
415 <class RTPDataChannel = DualRTPUDPIPv4Channel,
416 class RTCPChannel = DualRTPUDPIPv4Channel,
417 class ServiceQueue = AVPQueue>
418 class __EXPORT SingleThreadRTPSession :
419 protected Thread,
420 public TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>
421 {
422 public:
423 SingleThreadRTPSession(const InetHostAddress& ia,
424 tpport_t dataPort = DefaultRTPDataPort,
425 tpport_t controlPort = 0,
426 int pri = 0,
427 uint32 memberssize =
428 MembershipBookkeeping::defaultMembersHashSize,
429 RTPApplication& app = defaultApplication()
430 #if defined(_MSC_VER) && _MSC_VER >= 1300
431 );
432 #else
433 ):
434 Thread(pri),
435 TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>
436 (ia,dataPort,controlPort,memberssize,app)
437 { }
438 #endif
439
440 SingleThreadRTPSession(uint32 ssrc, const InetHostAddress& ia,
441 tpport_t dataPort = DefaultRTPDataPort,
442 tpport_t controlPort = 0,
443 int pri = 0,
444 uint32 memberssize =
445 MembershipBookkeeping::defaultMembersHashSize,
446 RTPApplication& app = defaultApplication()
447 #if defined(_MSC_VER) && _MSC_VER >= 1300
448 );
449 #else
450 ):
451 Thread(pri),
452 TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>
453 (ssrc, ia,dataPort,controlPort,memberssize,app)
454 { }
455 #endif
456
457 SingleThreadRTPSession(const InetMcastAddress& ia,
458 tpport_t dataPort = DefaultRTPDataPort,
459 tpport_t controlPort = 0,
460 int pri = 0,
461 uint32 memberssize =
462 MembershipBookkeeping::defaultMembersHashSize,
463 RTPApplication& app = defaultApplication(),
464 uint32 iface = 0
465 #if defined(_MSC_VER) && _MSC_VER >= 1300
466 );
467 #else
468 ):
469 Thread(pri),
470 TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>
471 (ia,dataPort,controlPort,memberssize,app,iface)
472 { }
473 #endif
474
475 SingleThreadRTPSession(uint32 ssrc, const InetMcastAddress& ia,
476 tpport_t dataPort = DefaultRTPDataPort,
477 tpport_t controlPort = 0,
478 int pri = 0,
479 uint32 memberssize =
480 MembershipBookkeeping::defaultMembersHashSize,
481 RTPApplication& app = defaultApplication(),
482 uint32 iface = 0
483 #if defined(_MSC_VER) && _MSC_VER >= 1300
484 );
485 #else
486 ):
487 Thread(pri),
488 TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>
489 (ssrc,ia,dataPort,controlPort,memberssize,app,iface)
490 { }
491 #endif
492
493
~SingleThreadRTPSession()494 ~SingleThreadRTPSession()
495 {
496 if (isRunning()) {
497 disableStack(); Thread::join();
498 }
499 }
500
501 #if defined(_MSC_VER) && _MSC_VER >= 1300
502 virtual void startRunning();
503 #else
504 /**
505 * Activate stack and start service thread.
506 **/
507 void
startRunning()508 startRunning()
509 { enableStack(); Thread::start(); }
510 #endif
511
512
513 protected:
disableStack(void)514 inline void disableStack(void)
515 {TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::disableStack();}
516
enableStack(void)517 inline void enableStack(void)
518 {TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::enableStack();}
519
getSchedulingTimeout(void)520 inline microtimeout_t getSchedulingTimeout(void)
521 {return TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::getSchedulingTimeout();}
522
controlReceptionService(void)523 inline void controlReceptionService(void)
524 {TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::controlReceptionService();}
525
controlTransmissionService(void)526 inline void controlTransmissionService(void)
527 {TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::controlTransmissionService();}
528
getRTCPCheckInterval(void)529 inline timeval getRTCPCheckInterval(void)
530 {return TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::getRTCPCheckInterval();}
531
dispatchDataPacket(void)532 inline size_t dispatchDataPacket(void)
533 {return TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::dispatchDataPacket();}
534
535 #if defined(_MSC_VER) && _MSC_VER >= 1300
536 virtual void run(void);
537
538 virtual void timerTick(void);
539
540 virtual bool isPendingData(microtimeout_t timeout);
541 #else
542
timerTick(void)543 virtual void timerTick(void)
544 {return;}
545
isPendingData(microtimeout_t timeout)546 virtual bool isPendingData(microtimeout_t timeout)
547 {return TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::isPendingData(timeout);}
548
549 /**
550 * Single runnable method for this RTP stacks, schedules
551 * outgoing and incoming RTP data and RTCP packets.
552 **/
run(void)553 virtual void run(void)
554 {
555 microtimeout_t timeout = 0;
556 while ( ServiceQueue::isActive() ) {
557 if ( timeout < 1000 ){ // !(timeout/1000)
558 timeout = getSchedulingTimeout();
559 }
560 controlReceptionService();
561 controlTransmissionService();
562 microtimeout_t maxWait =
563 timeval2microtimeout(getRTCPCheckInterval());
564 // make sure the scheduling timeout is
565 // <= the check interval for RTCP
566 // packets
567 timeout = (timeout > maxWait)? maxWait : timeout;
568 if ( timeout < 1000 ) { // !(timeout/1000)
569 dispatchDataPacket();
570 timerTick();
571 } else {
572 if ( isPendingData(timeout/1000) ) {
573 if (ServiceQueue::isActive()) { // take in only if active
574 takeInDataPacket();
575 }
576 }
577 timeout = 0;
578 }
579 }
580 dispatchBYE("GNU ccRTP stack finishing.");
581 // Thread::exit();
582 }
583
584 #endif
585
takeInDataPacket(void)586 inline size_t takeInDataPacket(void)
587 {return TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::takeInDataPacket();}
588
dispatchBYE(const std::string & str)589 inline size_t dispatchBYE(const std::string &str)
590 {return TRTPSessionBase<RTPDataChannel,RTCPChannel,ServiceQueue>::dispatchBYE(str);}
591 };
592
593 /**
594 * @typedef RTPSession
595 *
596 * Uses two pairs of sockets for RTP data and RTCP
597 * transmission/reception.
598 *
599 * @short UDP/IPv4 RTP Session scheduled by one thread of execution.
600 **/
601 typedef SingleThreadRTPSession<> RTPSession;
602
603 /**
604 * @typedef RTPSocket
605 *
606 * Alias for RTPSession.
607 **/
608 typedef RTPSession RTPSocket;
609
610 /**
611 * @typedef SymmetricRTPSession
612 *
613 * Uses one pair of sockets, (1) for RTP data and (2) for RTCP
614 * transmission/reception.
615 *
616 * @short Symmetric UDP/IPv4 RTP session scheduled by one thread of execution.
617 **/
618 typedef SingleThreadRTPSession<SymmetricRTPChannel,
619 SymmetricRTPChannel> SymmetricRTPSession;
620
621 #ifdef CCXX_IPV6
622
623 /**
624 * @class RTPSessionBaseIPV6
625 *
626 * Generic RTP protocol stack for exchange of realtime data. This
627 * stack uses the concept of packet send and receive queues to schedule
628 * and buffer outgoing packets and to arrange or reorder incoming packets
629 * as they arrive.
630 *
631 * This is a template class that allows customization of two aspects:
632 * the underlying network and the control protocol. The RTPDataChannel
633 * and RTCPChannel template parameters specify the socket types to
634 * use. The ServiceQueue template parameter specify which packet queue
635 * is used.
636 *
637 * RTPSessionBase objects do not have any threading policy, thus
638 * allowing to customize this aspect in derived classes (see
639 * SingleThreadRTPSession or RTPSessionPoolBase).
640 *
641 * @author David Sugar <dyfet@ostel.com>
642 * @short RTP protocol stack based on Common C++.
643 **/
644 template <class RTPDataChannel = DualRTPUDPIPv6Channel,
645 class RTCPChannel = DualRTPUDPIPv6Channel,
646 class ServiceQueue = AVPQueue>
647 class __EXPORT TRTPSessionBaseIPV6 : public ServiceQueue
648 {
649 public:
650 /**
651 * Builds a session waiting for packets in a host address.
652 *
653 * @param ia Network address this socket is to be bound.
654 * @param dataPort Transport port the data socket is to be bound.
655 * @param controlPort Transport port the control socket is to be bound.
656 * @param membersSize Initial size of the membership table.
657 * @param app Application this session is associated to.
658 * */
TRTPSessionBaseIPV6(const IPV6Host & ia,tpport_t dataPort,tpport_t controlPort,uint32 membersSize,RTPApplication & app)659 TRTPSessionBaseIPV6(const IPV6Host& ia, tpport_t dataPort,
660 tpport_t controlPort, uint32 membersSize,
661 RTPApplication& app) :
662 ServiceQueue(membersSize,app)
663 { build(ia,dataPort,controlPort); }
664
665 /**
666 * Builds a session with the specified ssrc identifier for the
667 * local source.
668 *
669 * @param ssrc SSRC identifier for the local source.
670 * @param ia Network address this socket is to be bound.
671 * @param dataPort Transport port the data socket is to be bound.
672 * @param controlPort Transport port the control socket is to be bound.
673 * @param membersSize Initial size of the membership table.
674 * @param app Application this session is associated to.
675 **/
TRTPSessionBaseIPV6(uint32 ssrc,const IPV6Host & ia,tpport_t dataPort,tpport_t controlPort,uint32 membersSize,RTPApplication & app)676 TRTPSessionBaseIPV6(uint32 ssrc,
677 const IPV6Host& ia,
678 tpport_t dataPort, tpport_t controlPort,
679 uint32 membersSize, RTPApplication& app):
680 ServiceQueue(ssrc,membersSize,app)
681 { build(ia,dataPort,controlPort); }
682
683 /**
684 * Builds a session waiting for packets in a multicast address.
685 * TODO: ssrc constructor for multicast!
686 *
687 * @param ia Multicast address this socket is to be bound.
688 * @param dataPort Transport port the data socket is to be bound.
689 * @param controlPort Transport port the control socket is to be bound.
690 * @param membersSize Initial size of the membership table.
691 * @param app Application this session is associated to.
692 * @param iface Index (from 0 to n) of network interface to join to
693 * multicast group.
694 **/
TRTPSessionBaseIPV6(const IPV6Multicast & ia,tpport_t dataPort,tpport_t controlPort,uint32 membersSize,RTPApplication & app,uint32 iface)695 TRTPSessionBaseIPV6(const IPV6Multicast& ia, tpport_t dataPort,
696 tpport_t controlPort, uint32 membersSize,
697 RTPApplication& app, uint32 iface) :
698 ServiceQueue(membersSize,app)
699 { build(ia,dataPort,controlPort,iface); }
700
701 /**
702 * Builds a session waiting for packets in a multicast
703 * address, with the specified ssrc identifier for the local
704 * source.
705 *
706 * @param ssrc SSRC identifier for the local source.
707 * @param ia Multicast address this socket is to be bound.
708 * @param dataPort Transport port the data socket is to be bound.
709 * @param controlPort Transport port the control socket is to be bound.
710 * @param membersSize Initial size of the membership table.
711 * @param app Application this session is associated to.
712 * @param iface Index (from 0 to n) of network interface to join to
713 * multicast group.
714 **/
TRTPSessionBaseIPV6(uint32 ssrc,const IPV6Multicast & ia,tpport_t dataPort,tpport_t controlPort,uint32 membersSize,RTPApplication & app,uint32 iface)715 TRTPSessionBaseIPV6(uint32 ssrc,
716 const IPV6Multicast& ia, tpport_t dataPort,
717 tpport_t controlPort, uint32 membersSize,
718 RTPApplication& app, uint32 iface) :
719 ServiceQueue(ssrc,membersSize,app)
720 { build(ia,dataPort,controlPort,iface); }
721
dispatchBYE(const std::string & str)722 virtual size_t dispatchBYE(const std::string &str)
723 {
724 return QueueRTCPManager::dispatchBYE(str);
725 }
726
727 inline virtual
~TRTPSessionBaseIPV6()728 ~TRTPSessionBaseIPV6()
729 {
730 endSocket();
731 }
732
getDSO(void)733 inline RTPDataChannel *getDSO(void)
734 {return dso;}
735
736 protected:
737 /**
738 * @param timeout maximum timeout to wait, in microseconds
739 */
740 inline bool
isPendingData(microtimeout_t timeout)741 isPendingData(microtimeout_t timeout)
742 { return dso->isPendingRecv(timeout); }
743
744 inline IPV6Host
745 getDataSender(tpport_t *port = NULL) const
746 { return dso->getSender(port); }
747
748 inline size_t
getNextDataPacketSize()749 getNextDataPacketSize() const
750 { return dso->getNextPacketSize(); }
751
752 /**
753 * Receive data from the data channel/socket.
754 *
755 * @param buffer Memory region to read to.
756 * @param len Maximum number of octets to get.
757 * @param na Source network address.
758 * @param tp Source transport port.
759 * @return Number of octets actually read.
760 */
761 inline size_t
recvData(unsigned char * buffer,size_t len,IPV6Host & na,tpport_t & tp)762 recvData(unsigned char* buffer, size_t len,
763 IPV6Host& na, tpport_t& tp)
764 { na = dso->getSender(tp); return dso->recv(buffer, len); }
765
766 inline void
setDataPeerIPV6(const IPV6Host & host,tpport_t port)767 setDataPeerIPV6(const IPV6Host &host, tpport_t port)
768 { dso->setPeer(host,port); }
769
770 /**
771 * @param buffer memory region to write from
772 * @param len number of octets to write
773 */
774 inline size_t
sendDataIPV6(const unsigned char * const buffer,size_t len)775 sendDataIPV6(const unsigned char* const buffer, size_t len)
776 { return dso->send(buffer, len); }
777
getDataRecvSocket()778 inline SOCKET getDataRecvSocket() const
779 { return dso->getRecvSocket(); }
780
781 /**
782 * @param timeout maximum timeout to wait, in microseconds
783 * @return whether there are packets waiting to be picked
784 */
785 inline bool
isPendingControl(microtimeout_t timeout)786 isPendingControl(microtimeout_t timeout)
787 { return cso->isPendingRecv(timeout); }
788
789 inline IPV6Host
790 getControlSender(tpport_t *port = NULL) const
791 { return cso->getSender(port); }
792
793 /**
794 * Receive data from the control channel/socket.
795 *
796 * @param buffer Buffer where to get data.
797 * @param len Maximum number of octets to get.
798 * @param na Source network address.
799 * @param tp Source transport port.
800 * @return Number of octets actually read.
801 **/
802 inline size_t
recvControl(unsigned char * buffer,size_t len,IPV6Host & na,tpport_t & tp)803 recvControl(unsigned char *buffer, size_t len,
804 IPV6Host& na, tpport_t& tp)
805 { na = cso->getSender(tp); return cso->recv(buffer,len); }
806
807 inline void
setControlPeerIPV6(const IPV6Host & host,tpport_t port)808 setControlPeerIPV6(const IPV6Host &host, tpport_t port)
809 { cso->setPeer(host,port); }
810
811 /**
812 * @return number of octets actually written
813 * @param buffer
814 * @param len
815 */
816 inline size_t
sendControl(const unsigned char * const buffer,size_t len)817 sendControl(const unsigned char* const buffer, size_t len)
818 { return cso->send(buffer,len); }
819
getControlRecvSocket()820 inline SOCKET getControlRecvSocket() const
821 { return cso->getRecvSocket(); }
822
823 inline void
endSocket()824 endSocket()
825 {
826 dso->endSocket();
827 cso->endSocket();
828 if (dso) delete dso;
829 dso = NULL;
830 if (cso) delete cso;
831 cso = NULL;
832 }
833
834 private:
835 void
build(const IPV6Host & ia,tpport_t dataPort,tpport_t controlPort)836 build(const IPV6Host& ia, tpport_t dataPort,
837 tpport_t controlPort)
838 {
839 if ( 0 == controlPort ) {
840 dataBasePort = even_port(dataPort);
841 controlBasePort = dataBasePort + 1;
842 } else {
843 dataBasePort = dataPort;
844 controlBasePort = controlPort;
845 }
846 dso = new RTPDataChannel(ia,dataBasePort);
847 cso = new RTCPChannel(ia,controlBasePort);
848 }
849
850 void
build(const IPV6Multicast & ia,tpport_t dataPort,tpport_t controlPort,uint32 iface)851 build(const IPV6Multicast& ia, tpport_t dataPort,
852 tpport_t controlPort, uint32 iface)
853 {
854 if ( 0 == controlPort ) {
855 dataBasePort = even_port(dataPort);
856 controlBasePort = dataBasePort + 1;
857 } else {
858 dataBasePort = dataPort;
859 controlBasePort = controlPort;
860 }
861 dso = new RTPDataChannel(IPV6Host("0.0.0.0"),dataBasePort);
862 cso = new RTCPChannel(IPV6Host("0.0.0.0"),controlBasePort);
863 joinGroup(ia,iface);
864 }
865
866 /**
867 * Join a multicast group.
868 *
869 * @param ia address of the multicast group
870 * @return error code from the socket operation
871 */
872 inline Socket::Error
joinGroup(const IPV6Multicast & ia,uint32 iface)873 joinGroup(const IPV6Multicast& ia, uint32 iface)
874 {
875 Socket::Error error = dso->setMulticast(true);
876 if ( error ) return error;
877 error = dso->join(ia,iface);
878 if ( error ) return error;
879 error = cso->setMulticast(true);
880 if ( error ) {
881 dso->drop(ia);
882 return error;
883 }
884 error = cso->join(ia,iface);
885 if ( error ) {
886 dso->drop(ia);
887 return error;
888 }
889 return Socket::errSuccess;
890 }
891
892 /**
893 * Leave a multicast group.
894 *
895 * @param ia address of the multicast group
896 * @return error code from the socket operation
897 */
898 inline Socket::Error
leaveGroup(const IPV6Multicast & ia)899 leaveGroup(const IPV6Multicast& ia)
900 {
901 Socket::Error error = dso->setMulticast(false);
902 if ( error ) return error;
903 error = dso->leaveGroup(ia);
904 if ( error ) return error;
905 error = cso->setMulticast(false);
906 if ( error ) return error;
907 return cso->leaveGroup(ia);
908 }
909
910 /**
911 * Set the value of the TTL field in the sent packets.
912 *
913 * @param ttl Time To Live
914 * @return error code from the socket operation
915 */
916 inline Socket::Error
setMcastTTL(uint8 ttl)917 setMcastTTL(uint8 ttl)
918 {
919 Socket::Error error = dso->setMulticast(true);
920 if ( error ) return error;
921 error = dso->setTimeToLive(ttl);
922 if ( error ) return error;
923 error = cso->setMulticast(true);
924 if ( error ) return error;
925 return cso->setTimeToLive(ttl);
926 }
927
928 /**
929 * Ensure a port number is odd. If it is an even number, return
930 * the next lower (odd) port number.
931 *
932 * @param port number to filter
933 * @return filtered (odd) port number
934 */
935 inline tpport_t
odd_port(tpport_t port)936 odd_port(tpport_t port)
937 { return (port & 0x01)? (port) : (port - 1); }
938
939 /**
940 * Ensure a port number is even. If it is an odd number, return
941 * the next lower (even) port number.
942 *
943 * @param port number to filter
944 * @return filtered (even) port number
945 */
946 inline tpport_t
even_port(tpport_t port)947 even_port(tpport_t port)
948 { return (port & 0x01)? (port - 1) : (port); }
949
950 tpport_t dataBasePort;
951 tpport_t controlBasePort;
952
953 protected:
954 RTPDataChannel* dso;
955 RTCPChannel* cso;
956 friend class RTPSessionBaseHandler;
957 };
958
959 /**
960 * @class SingleThreadRTPSessionIPV6
961 *
962 * This template class adds the threading aspect to the RTPSessionBase
963 * template in one of the many possible ways. It inherits from a
964 * single execution thread that schedules sending of outgoing packets
965 * and receipt of incoming packets.
966 *
967 * @author David Sugar <dyfet@gnutelephony.org>
968 **/
969 template
970 <class RTPDataChannel = DualRTPUDPIPv6Channel,
971 class RTCPChannel = DualRTPUDPIPv6Channel,
972 class ServiceQueue = AVPQueue>
973 class __EXPORT SingleThreadRTPSessionIPV6 :
974 protected Thread,
975 public TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>
976 {
977 public:
978 SingleThreadRTPSessionIPV6(const IPV6Host& ia,
979 tpport_t dataPort = DefaultRTPDataPort,
980 tpport_t controlPort = 0,
981 int pri = 0,
982 uint32 memberssize =
983 MembershipBookkeeping::defaultMembersHashSize,
984 RTPApplication& app = defaultApplication()
985 #if defined(_MSC_VER) && _MSC_VER >= 1300
986 );
987 #else
988 ):
989 Thread(pri),
990 TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>
991 (ia,dataPort,controlPort,memberssize,app)
992 { }
993 #endif
994
995 SingleThreadRTPSessionIPV6(const IPV6Multicast& ia,
996 tpport_t dataPort = DefaultRTPDataPort,
997 tpport_t controlPort = 0,
998 int pri = 0,
999 uint32 memberssize =
1000 MembershipBookkeeping::defaultMembersHashSize,
1001 RTPApplication& app = defaultApplication(),
1002 uint32 iface = 0
1003 #if defined(_MSC_VER) && _MSC_VER >= 1300
1004 );
1005 #else
1006 ):
1007 Thread(pri),
1008 TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>
1009 (ia,dataPort,controlPort,memberssize,app,iface)
1010 { }
1011 #endif
1012
~SingleThreadRTPSessionIPV6()1013 ~SingleThreadRTPSessionIPV6()
1014 {
1015 if (isRunning()) {
1016 disableStack(); Thread::join();
1017 }
1018 }
1019
1020 #if defined(_MSC_VER) && _MSC_VER >= 1300
1021 virtual void startRunning();
1022 #else
1023 /**
1024 * Activate stack and start service thread.
1025 **/
1026 void
startRunning()1027 startRunning()
1028 { enableStack(); Thread::start(); }
1029 #endif
1030
1031
1032 protected:
enableStack(void)1033 inline void enableStack(void)
1034 {TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::enableStack();}
1035
disableStack(void)1036 inline void disableStack(void)
1037 {TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::disableStack();}
1038
getSchedulingTimeout(void)1039 inline microtimeout_t getSchedulingTimeout(void)
1040 {return TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::getSchedulingTimeout();}
1041
controlReceptionService(void)1042 inline void controlReceptionService(void)
1043 {TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::controlReceptionService();}
1044
controlTransmissionService(void)1045 inline void controlTransmissionService(void)
1046 {TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::controlTransmissionService();}
1047
getRTCPCheckInterval(void)1048 inline timeval getRTCPCheckInterval(void)
1049 {return TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::getRTCPCheckInterval();}
1050
dispatchDataPacket(void)1051 inline size_t dispatchDataPacket(void)
1052 {return TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::dispatchDataPacket();}
1053
1054 #if defined(_MSC_VER) && _MSC_VER >= 1300
1055 virtual void run(void);
1056
1057 virtual void timerTick(void);
1058
1059 virtual bool isPendingData(microtimeout_t timeout);
1060 #else
1061
timerTick(void)1062 virtual void timerTick(void)
1063 {return;}
1064
isPendingData(microtimeout_t timeout)1065 virtual bool isPendingData(microtimeout_t timeout)
1066 {return TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::isPendingData(timeout);}
1067
1068 /**
1069 * Single runnable method for this RTP stacks, schedules
1070 * outgoing and incoming RTP data and RTCP packets.
1071 **/
run(void)1072 virtual void run(void)
1073 {
1074 microtimeout_t timeout = 0;
1075 while ( ServiceQueue::isActive() ) {
1076 if ( timeout < 1000 ){ // !(timeout/1000)
1077 timeout = getSchedulingTimeout();
1078 }
1079 controlReceptionService();
1080 controlTransmissionService();
1081 microtimeout_t maxWait =
1082 timeval2microtimeout(getRTCPCheckInterval());
1083 // make sure the scheduling timeout is
1084 // <= the check interval for RTCP
1085 // packets
1086 timeout = (timeout > maxWait)? maxWait : timeout;
1087 if ( timeout < 1000 ) { // !(timeout/1000)
1088 dispatchDataPacket();
1089 timerTick();
1090 } else {
1091 if ( isPendingData(timeout/1000) ) {
1092 takeInDataPacket();
1093 }
1094 timeout = 0;
1095 }
1096 }
1097 dispatchBYE("GNU ccRTP stack finishing.");
1098 Thread::exit();
1099 }
1100
1101 #endif
1102
takeInDataPacket(void)1103 inline size_t takeInDataPacket(void)
1104 {return TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::takeInDataPacket();}
1105
dispatchBYE(const std::string & str)1106 inline size_t dispatchBYE(const std::string &str)
1107 {return TRTPSessionBaseIPV6<RTPDataChannel,RTCPChannel,ServiceQueue>::dispatchBYE(str);}
1108 };
1109
1110 /**
1111 * @typedef RTPSession
1112 *
1113 * Uses two pairs of sockets for RTP data and RTCP
1114 * transmission/reception.
1115 *
1116 * @short UDP/IPv6 RTP Session scheduled by one thread of execution.
1117 **/
1118 typedef SingleThreadRTPSessionIPV6<> RTPSessionIPV6;
1119
1120 /**
1121 * @typedef RTPSocket
1122 *
1123 * Alias for RTPSession.
1124 **/
1125 typedef RTPSessionIPV6 RTPSocketIPV6;
1126
1127 /**
1128 * @typedef SymmetricRTPSession
1129 *
1130 * Uses one pair of sockets, (1) for RTP data and (2) for RTCP
1131 * transmission/reception.
1132 *
1133 * @short Symmetric UDP/IPv6 RTP session scheduled by one thread of execution.
1134 **/
1135 typedef SingleThreadRTPSessionIPV6<SymmetricRTPChannelIPV6,
1136 SymmetricRTPChannelIPV6> SymmetricRTPSessionIPV6;
1137
1138
1139 #endif
1140
1141 /** @}*/ // sessions
1142
1143 END_NAMESPACE
1144
1145 #endif //CCXX_RTP_RTP_H_
1146
1147 /** EMACS **
1148 * Local variables:
1149 * mode: c++
1150 * c-basic-offset: 8
1151 * End:
1152 */
1153