1 /*
2  * rtpconn.cxx
3  *
4  * Connection abstraction
5  *
6  * Open Phone Abstraction Library (OPAL)
7  *
8  * Copyright (C) 2007 Post Increment
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Open Phone Abstraction Library.
21  *
22  * The Initial Developer of the Original Code is Post Increment
23  *
24  * Contributor(s): ______________________________________.
25  *
26  * $Revision: 26728 $
27  * $Author: rjongbloed $
28  * $Date: 2011-12-01 22:59:14 -0600 (Thu, 01 Dec 2011) $
29  */
30 
31 #include <ptlib.h>
32 
33 #ifdef P_USE_PRAGMA
34 #pragma implementation "rtpconn.h"
35 #endif
36 
37 #include <opal/buildopts.h>
38 
39 #include <opal/rtpconn.h>
40 #include <opal/rtpep.h>
41 #include <opal/manager.h>
42 #include <codec/rfc2833.h>
43 #include <t38/t38proto.h>
44 #include <opal/patch.h>
45 
46 #if OPAL_VIDEO
47 #include <codec/vidcodec.h>
48 #endif
49 
50 
51 #define new PNEW
52 
53 
54 #ifdef OPAL_ZRTP
55 extern OpalZRTPConnectionInfo * OpalLibZRTPConnInfo_Create();
56 #endif
57 
58 
OpalRTPConnection(OpalCall & call,OpalRTPEndPoint & ep,const PString & token,unsigned int options,StringOptions * stringOptions)59 OpalRTPConnection::OpalRTPConnection(OpalCall & call,
60                              OpalRTPEndPoint  & ep,
61                                 const PString & token,
62                                    unsigned int options,
63                                 StringOptions * stringOptions)
64   : OpalConnection(call, ep, token, options, stringOptions)
65 #ifdef _MSC_VER
66 #pragma warning(disable:4355)
67 #endif
68   , m_rtpSessions(*this)
69 #ifdef _MSC_VER
70 #pragma warning(default:4355)
71 #endif
72   , remoteIsNAT(false)
73 {
74   rfc2833Handler  = new OpalRFC2833Proto(*this, PCREATE_NOTIFIER(OnUserInputInlineRFC2833), OpalRFC2833);
75 #if OPAL_T38_CAPABILITY
76   ciscoNSEHandler = new OpalRFC2833Proto(*this, PCREATE_NOTIFIER(OnUserInputInlineCiscoNSE), OpalCiscoNSE);
77 #endif
78 
79 #ifdef OPAL_ZRTP
80   zrtpEnabled = ep.GetZRTPEnabled();
81   zrtpConnInfo = NULL;
82 #endif
83 }
84 
~OpalRTPConnection()85 OpalRTPConnection::~OpalRTPConnection()
86 {
87   delete rfc2833Handler;
88 #if OPAL_T38_CAPABILITY
89   delete ciscoNSEHandler;
90 #endif
91 }
92 
93 
OnReleased()94 void OpalRTPConnection::OnReleased()
95 {
96   OpalConnection::OnReleased();
97   CloseSession(0);
98 }
99 
100 
GetNextSessionID(const OpalMediaType & mediaType,bool isSource)101 unsigned OpalRTPConnection::GetNextSessionID(const OpalMediaType & mediaType, bool isSource)
102 {
103   unsigned nextSessionId = m_rtpSessions.GetNextSessionID();
104 
105   if (GetMediaStream(mediaType, isSource) != NULL)
106     return nextSessionId;
107 
108   OpalMediaStreamPtr mediaStream = GetMediaStream(mediaType, !isSource);
109   if (mediaStream != NULL)
110     return mediaStream->GetSessionID();
111 
112   unsigned defaultSessionId = mediaType.GetDefinition()->GetDefaultSessionId();
113   if (defaultSessionId >= nextSessionId ||
114       GetMediaStream(defaultSessionId,  isSource) != NULL ||
115       GetMediaStream(defaultSessionId, !isSource) != NULL)
116     return nextSessionId;
117 
118   return defaultSessionId;
119 }
120 
121 
GetSession(unsigned sessionID) const122 RTP_Session * OpalRTPConnection::GetSession(unsigned sessionID) const
123 {
124   return m_rtpSessions.GetSession(sessionID);
125 }
126 
127 
GetMediaSession(unsigned sessionID) const128 OpalMediaSession * OpalRTPConnection::GetMediaSession(unsigned sessionID) const
129 {
130   return m_rtpSessions.GetMediaSession(sessionID);
131 }
132 
133 
UseSession(const OpalTransport & transport,unsigned sessionID,const OpalMediaType & mediaType,RTP_QOS * rtpqos)134 RTP_Session * OpalRTPConnection::UseSession(const OpalTransport & transport, unsigned sessionID, const OpalMediaType & mediaType, RTP_QOS * rtpqos)
135 {
136   RTP_Session * rtpSession = m_rtpSessions.GetSession(sessionID);
137   if (rtpSession == NULL) {
138     rtpSession = CreateSession(transport, sessionID, mediaType, rtpqos);
139     m_rtpSessions.AddSession(rtpSession, mediaType);
140   }
141 
142   return rtpSession;
143 }
144 
145 
CreateSession(const OpalTransport & transport,unsigned sessionID,const OpalMediaType & mediaType,RTP_QOS * rtpqos)146 RTP_Session * OpalRTPConnection::CreateSession(const OpalTransport & transport,
147                                                             unsigned sessionID,
148                                                const OpalMediaType & mediaType,
149                                                            RTP_QOS * rtpqos)
150 {
151   // We only support RTP over UDP at this point in time ...
152   if (!transport.IsCompatibleTransport("ip$127.0.0.1"))
153     return NULL;
154 
155   // create an RTP session
156   RTP_UDP * rtpSession = CreateRTPSession(sessionID, mediaType, remoteIsNAT);
157   if (rtpSession == NULL)
158     return NULL;
159 
160   PIPSocket::Address localAddress;
161   transport.GetLocalAddress(false).GetIpAddress(localAddress);
162 
163   PIPSocket::Address remoteAddress;
164   transport.GetRemoteAddress().GetIpAddress(remoteAddress);
165 
166   OpalManager & manager = GetEndPoint().GetManager();
167   PNatMethod * natMethod = manager.GetNatMethod(remoteAddress);
168 
169   WORD firstPort = manager.GetRtpIpPortPair();
170   WORD nextPort = firstPort;
171   while (!rtpSession->Open(localAddress, nextPort, nextPort, manager.GetMediaTypeOfService(mediaType), natMethod, rtpqos)) {
172     nextPort = manager.GetRtpIpPortPair();
173     if (nextPort == firstPort) {
174       PTRACE(1, "RTPCon\tNo ports available for RTP session " << sessionID << ","
175                 " base=" << manager.GetRtpIpPortBase() << ","
176                 " max=" << manager.GetRtpIpPortMax() << ","
177                 " bind=" << localAddress << ","
178                 " for " << *this);
179       delete rtpSession;
180       return NULL;
181     }
182   }
183 
184   localAddress = rtpSession->GetLocalAddress();
185   if (manager.TranslateIPAddress(localAddress, remoteAddress)){
186     rtpSession->SetLocalAddress(localAddress);
187   }
188 
189   return rtpSession;
190 }
191 
192 
CreateRTPSession(unsigned sessionID,const OpalMediaType & mediaType,bool remoteIsNAT)193 RTP_UDP * OpalRTPConnection::CreateRTPSession(unsigned sessionID, const OpalMediaType & mediaType, bool remoteIsNAT)
194 {
195   OpalMediaTypeDefinition * def = mediaType.GetDefinition();
196   if (def == NULL) {
197     PTRACE(1, "RTPCon\tNo definition for media type " << mediaType);
198     return NULL;
199   }
200 
201 #ifdef OPAL_ZRTP
202   // create ZRTP channel if enabled
203   {
204     PWaitAndSignal m(zrtpConnInfoMutex);
205     if (zrtpEnabled) {
206       if (zrtpConnInfo == NULL)
207         zrtpConnInfo = OpalLibZRTPConnInfo_Create();
208       if (zrtpConnInfo != NULL) {
209         RTP_UDP * rtpSession = zrtpConnInfo->CreateRTPSession(*this, sessionID, remoteIsNAT);
210         if (rtpSession != NULL)
211           return rtpSession;
212         delete zrtpConnInfo;
213         zrtpConnInfo = NULL;
214       }
215     }
216   }
217 #endif
218 
219   return def->CreateRTPSession(*this, sessionID, remoteIsNAT);
220 }
221 
222 
ChangeSessionID(unsigned fromSessionID,unsigned toSessionID)223 bool OpalRTPConnection::ChangeSessionID(unsigned fromSessionID, unsigned toSessionID)
224 {
225   PTRACE(3, "RTPCon\tChanging session ID " << fromSessionID << " to " << toSessionID);
226   if (!m_rtpSessions.ChangeSessionID(fromSessionID, toSessionID))
227     return false;
228 
229   for (OpalMediaStreamPtr stream(mediaStreams, PSafeReference); stream != NULL; ++stream) {
230     if (stream->GetSessionID() == fromSessionID) {
231       stream->SetSessionID(toSessionID);
232       OpalMediaPatch * patch = stream->GetPatch();
233       if (patch != NULL) {
234         patch->GetSource().SetSessionID(toSessionID);
235         OpalMediaStreamPtr otherStream;
236         for (PINDEX i = 0; (otherStream = patch->GetSink(i)) != NULL; ++i)
237           otherStream->SetSessionID(toSessionID);
238       }
239     }
240   }
241 
242   return true;
243 }
244 
245 
CloseSession(unsigned sessionID)246 void OpalRTPConnection::CloseSession(unsigned sessionID)
247 {
248 #ifdef HAS_LIBZRTP
249   //check is security mode ZRTP
250   if (0 == securityMode.Find("ZRTP")) {
251     RTP_Session *session = GetSession(sessionID);
252     if (NULL != session){
253       OpalZrtp_UDP *zsession = (OpalZrtp_UDP*)session;
254       if (NULL != zsession->zrtpStream){
255         ::zrtp_stop_stream(zsession->zrtpStream);
256         zsession->zrtpStream = NULL;
257       }
258     }
259   }
260 
261   printf("release session %i\n", sessionID);
262 #endif
263 
264   m_rtpSessions.CloseSession(sessionID);
265 }
266 
267 
IsRTPNATEnabled(const PIPSocket::Address & localAddr,const PIPSocket::Address & peerAddr,const PIPSocket::Address & sigAddr,PBoolean incoming)268 PBoolean OpalRTPConnection::IsRTPNATEnabled(const PIPSocket::Address & localAddr,
269                                             const PIPSocket::Address & peerAddr,
270                                             const PIPSocket::Address & sigAddr,
271                                                               PBoolean incoming)
272 {
273   return static_cast<OpalRTPEndPoint &>(endpoint).IsRTPNATEnabled(*this, localAddr, peerAddr, sigAddr, incoming);
274 }
275 
276 
OnMediaCommand(OpalMediaStream & stream,const OpalMediaCommand & command)277 bool OpalRTPConnection::OnMediaCommand(OpalMediaStream & stream, const OpalMediaCommand & command)
278 {
279   bool done = OpalConnection::OnMediaCommand(stream, command);
280 
281 #if OPAL_VIDEO
282 
283   unsigned sessionID = stream.GetSessionID();
284   RTP_Session * session = m_rtpSessions.GetSession(sessionID);
285   if (session != NULL) {
286     PCaselessString rtcp_fb;
287     OpalMediaStreamPtr videoStream = GetMediaStream(sessionID, true);
288     if (videoStream != NULL)
289       rtcp_fb = videoStream->GetMediaFormat().GetOptionString("RTCP-FB");
290 
291     if (PIsDescendant(&command, OpalVideoUpdatePicture)) {
292       bool no_AVPF_PLI = rtcp_fb.Find("pli") == P_MAX_INDEX;
293       bool no_AVPF_FIR = rtcp_fb.Find("fir") == P_MAX_INDEX;
294 
295       if (no_AVPF_PLI && no_AVPF_FIR)
296         session->SendIntraFrameRequest(true, false);  // Fall back to RFC2032
297       else if (no_AVPF_PLI)
298         session->SendIntraFrameRequest(false, false); // Unusual, but possible, use RFC5104 FIR
299       else if (no_AVPF_FIR)
300         session->SendIntraFrameRequest(false, true);  // More common, use RFC4585 PLI
301       else
302         session->SendIntraFrameRequest(false, PIsDescendant(&command, OpalVideoPictureLoss));
303 
304 #if OPAL_STATISTICS
305       m_VideoUpdateRequestsSent++;
306 #endif
307 
308       done = true;
309     }
310     else if (PIsDescendant(&command, OpalTemporalSpatialTradeOff) && rtcp_fb.Find("tstr") != P_MAX_INDEX) {
311       session->SendTemporalSpatialTradeOff(dynamic_cast<const OpalTemporalSpatialTradeOff &>(command).GetTradeOff());
312       done = true;
313     }
314   }
315 #endif // OPAL_VIDEO
316 
317   return done;
318 }
319 
320 
AttachRFC2833HandlerToPatch(PBoolean isSource,OpalMediaPatch & patch)321 void OpalRTPConnection::AttachRFC2833HandlerToPatch(PBoolean isSource, OpalMediaPatch & patch)
322 {
323   if (isSource) {
324     OpalRTPMediaStream * mediaStream = dynamic_cast<OpalRTPMediaStream *>(&patch.GetSource());
325     if (mediaStream != NULL) {
326       RTP_Session & rtpSession = mediaStream->GetRtpSession();
327       if (rfc2833Handler != NULL) {
328         PTRACE(3, "RTPCon\tAdding RFC2833 receive handler");
329         rtpSession.AddFilter(rfc2833Handler->GetReceiveHandler());
330       }
331 #if OPAL_T38_CAPABILITY
332       if (ciscoNSEHandler != NULL) {
333         PTRACE(3, "RTPCon\tAdding Cisco NSE receive handler");
334         rtpSession.AddFilter(ciscoNSEHandler->GetReceiveHandler());
335       }
336 #endif
337     }
338   }
339 }
340 
341 
SendUserInputTone(char tone,unsigned duration)342 PBoolean OpalRTPConnection::SendUserInputTone(char tone, unsigned duration)
343 {
344   if (GetRealSendUserInputMode() == SendUserInputAsRFC2833) {
345     if (
346 #if OPAL_T38_CAPABILITY
347         ciscoNSEHandler->SendToneAsync(tone, duration) ||
348 #endif
349          rfc2833Handler->SendToneAsync(tone, duration))
350       return true;
351 
352     PTRACE(2, "RTPCon\tCould not send tone '" << tone << "' via RFC2833.");
353   }
354 
355   return OpalConnection::SendUserInputTone(tone, duration);
356 }
357 
358 
GetMediaInformation(unsigned sessionID,MediaInformation & info) const359 PBoolean OpalRTPConnection::GetMediaInformation(unsigned sessionID,
360                                          MediaInformation & info) const
361 {
362   if (!mediaTransportAddresses.Contains(sessionID)) {
363     PTRACE(2, "RTPCon\tGetMediaInformation for session " << sessionID << " - no channel.");
364     return PFalse;
365   }
366 
367   OpalTransportAddress & address = mediaTransportAddresses[sessionID];
368 
369   PIPSocket::Address ip;
370   WORD port;
371   if (address.GetIpAndPort(ip, port)) {
372     info.data    = OpalTransportAddress(ip, (WORD)(port&0xfffe));
373     info.control = OpalTransportAddress(ip, (WORD)(port|0x0001));
374   }
375   else
376     info.data = info.control = address;
377 
378   info.rfc2833 = rfc2833Handler->GetRxMediaFormat().GetPayloadType();
379   PTRACE(3, "RTPCon\tGetMediaInformation for session " << sessionID
380          << " data=" << info.data << " rfc2833=" << info.rfc2833);
381   return PTrue;
382 }
383 
IsMediaBypassPossible(unsigned) const384 PBoolean OpalRTPConnection::IsMediaBypassPossible(unsigned) const
385 {
386   return true;
387 }
388 
CreateMediaStream(const OpalMediaFormat & mediaFormat,unsigned sessionID,PBoolean isSource)389 OpalMediaStream * OpalRTPConnection::CreateMediaStream(const OpalMediaFormat & mediaFormat, unsigned sessionID, PBoolean isSource)
390 {
391   if (ownerCall.IsMediaBypassPossible(*this, sessionID))
392     return new OpalNullMediaStream(*this, mediaFormat, sessionID, isSource);
393 
394   for (OpalMediaStreamPtr mediaStream(mediaStreams, PSafeReference); mediaStream != NULL; ++mediaStream) {
395     if (mediaStream->GetSessionID() == sessionID && mediaStream->IsSource() == isSource && !mediaStream->IsOpen())
396       return mediaStream;
397   }
398 
399   if (mediaFormat.GetMediaType().GetDefinition()->UsesRTP()) {
400     if (UseSession(GetTransport(), sessionID, mediaFormat.GetMediaType(), NULL) == NULL) {
401       PTRACE(1, "RTPCon\tCreateMediaStream could not find/create session " << sessionID);
402       return NULL;
403     }
404   }
405 
406   OpalMediaSession * mediaSession = GetMediaSession(sessionID);
407   if (mediaSession == NULL) {
408     PTRACE(1, "RTPCon\tUnable to create media stream for session " << sessionID);
409     return NULL;
410   }
411 
412   return PAssertNULL(mediaSession)->CreateMediaStream(mediaFormat, sessionID, isSource);
413 }
414 
415 
AdjustMediaFormats(bool local,const OpalConnection * otherConnection,OpalMediaFormatList & mediaFormats) const416 void OpalRTPConnection::AdjustMediaFormats(bool   local,
417                            const OpalConnection * otherConnection,
418                             OpalMediaFormatList & mediaFormats) const
419 {
420   if (otherConnection == NULL && local) {
421     OpalMediaFormatList::iterator fmt = mediaFormats.begin();
422     while (fmt != mediaFormats.end()) {
423       if (fmt->IsTransportable())
424         ++fmt;
425       else
426         mediaFormats -= *fmt++;
427     }
428   }
429 
430   OpalConnection::AdjustMediaFormats(local, otherConnection, mediaFormats);
431 }
432 
433 
OnPatchMediaStream(PBoolean isSource,OpalMediaPatch & patch)434 void OpalRTPConnection::OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch)
435 {
436   OpalConnection::OnPatchMediaStream(isSource, patch);
437 
438   if (patch.GetSource().GetMediaFormat().GetMediaType() == OpalMediaType::Audio())
439     AttachRFC2833HandlerToPatch(isSource, patch);
440 }
441 
OnUserInputInlineRFC2833(OpalRFC2833Info & info,INT type)442 void OpalRTPConnection::OnUserInputInlineRFC2833(OpalRFC2833Info & info, INT type)
443 {
444   // trigger on start of tone only
445   if (type == 0)
446     OnUserInputTone(info.GetTone(), info.GetDuration() > 0 ? info.GetDuration()/8 : 100);
447 }
448 
OnUserInputInlineCiscoNSE(OpalRFC2833Info & info,INT type)449 void OpalRTPConnection::OnUserInputInlineCiscoNSE(OpalRFC2833Info & info, INT type)
450 {
451   // trigger on start of tone only
452   if (type == 0)
453     OnUserInputTone(info.GetTone(), info.GetDuration() > 0 ? info.GetDuration()/8 : 100);
454 }
455 
SessionFailing(RTP_Session & session)456 void OpalRTPConnection::SessionFailing(RTP_Session & session)
457 {
458   // set this session as failed
459   session.SetFailed(true);
460 
461   // check to see if all RTP session have failed
462   // if so, clear the call
463   if (m_rtpSessions.AllSessionsFailing()) {
464     PTRACE(2, "RTPCon\tClearing call as all RTP session are failing");
465     Release();
466   }
467 }
468 
469 /////////////////////////////////////////////////////////////////////////////
470 
OpalMediaSession(OpalConnection & _conn,const OpalMediaType & _mediaType,unsigned _sessionId)471 OpalMediaSession::OpalMediaSession(OpalConnection & _conn, const OpalMediaType & _mediaType, unsigned _sessionId)
472   : connection(_conn)
473   , mediaType(_mediaType)
474   , sessionId(_sessionId)
475 {
476 }
477 
OpalMediaSession(const OpalMediaSession & _obj)478 OpalMediaSession::OpalMediaSession(const OpalMediaSession & _obj)
479   : PObject(_obj)
480   , connection(_obj.connection)
481   , mediaType(_obj.mediaType)
482   , sessionId(_obj.sessionId)
483 {
484 }
485 
486 /////////////////////////////////////////////////////////////////////////////
487 
OpalRTPMediaSession(OpalConnection & conn,const OpalMediaType & mediaType,unsigned sessionId)488 OpalRTPMediaSession::OpalRTPMediaSession(OpalConnection & conn,
489                                          const OpalMediaType & mediaType,
490                                          unsigned sessionId)
491   : OpalMediaSession(conn, mediaType, sessionId)
492   , rtpSession(NULL)
493 {
494 }
495 
496 
OpalRTPMediaSession(const OpalRTPMediaSession & obj)497 OpalRTPMediaSession::OpalRTPMediaSession(const OpalRTPMediaSession & obj)
498   : OpalMediaSession(obj)
499   , rtpSession(NULL)
500 {
501 }
502 
503 
~OpalRTPMediaSession()504 OpalRTPMediaSession::~OpalRTPMediaSession()
505 {
506   if (rtpSession != NULL) {
507     PTRACE(4, "RTP\tDeleting session " << rtpSession->GetSessionID());
508     ((OpalRTPEndPoint &)connection.GetEndPoint()).SetConnectionByRtpLocalPort(rtpSession, NULL);
509     delete rtpSession;
510   }
511 }
512 
513 
Attach(RTP_Session * rtp)514 void OpalRTPMediaSession::Attach(RTP_Session * rtp)
515 {
516   if (PAssert(rtpSession == NULL, "Cannot attach with already existing session")) {
517     rtpSession = rtp;
518     ((OpalRTPEndPoint &)connection.GetEndPoint()).SetConnectionByRtpLocalPort(rtpSession, &connection);
519   }
520 }
521 
522 
Close()523 void OpalRTPMediaSession::Close()
524 {
525   if (rtpSession != NULL) {
526     PTRACE(3, "RTP\tClosing session " << rtpSession->GetSessionID());
527     ((OpalRTPEndPoint &)connection.GetEndPoint()).SetConnectionByRtpLocalPort(rtpSession, NULL);
528     if (rtpSession->GetPacketsReceived() > 0 || rtpSession->GetPacketsSent() > 0)
529       rtpSession->SendBYE();
530     rtpSession->Close(PTrue);
531     rtpSession->SetJitterBufferSize(0, 0);
532   }
533 }
534 
GetLocalMediaAddress() const535 OpalTransportAddress OpalRTPMediaSession::GetLocalMediaAddress() const
536 {
537   PIPSocket::Address addr = ((RTP_UDP *)rtpSession)->GetLocalAddress();
538   WORD port               = ((RTP_UDP *)rtpSession)->GetLocalDataPort();
539   return OpalTransportAddress(addr, port, "udp$");
540 }
541 
542 #if OPAL_SIP
543 
CreateSDPMediaDescription(const OpalTransportAddress & sdpContactAddress)544 SDPMediaDescription * OpalRTPMediaSession::CreateSDPMediaDescription(const OpalTransportAddress & sdpContactAddress)
545 {
546   return mediaType.GetDefinition()->CreateSDPMediaDescription(sdpContactAddress);
547 }
548 
549 #endif
550 
CreateMediaStream(const OpalMediaFormat & mediaFormat,unsigned,PBoolean isSource)551 OpalMediaStream * OpalRTPMediaSession::CreateMediaStream(const OpalMediaFormat & mediaFormat,
552                                                                          unsigned /*sessionID*/,
553                                                                          PBoolean isSource)
554 {
555   mediaType = mediaFormat.GetMediaType();
556   return new OpalRTPMediaStream((OpalRTPConnection &)connection, mediaFormat, isSource, *rtpSession,
557                                 connection.GetMinAudioJitterDelay(),
558                                 connection.GetMaxAudioJitterDelay());
559 }
560 
561 
562 /////////////////////////////////////////////////////////////////////////////
563 
OpalRTPSessionManager(OpalRTPConnection & connection)564 OpalRTPSessionManager::OpalRTPSessionManager(OpalRTPConnection & connection)
565   : m_connection(connection)
566 {
567 }
568 
569 
OpalRTPSessionManager(const OpalRTPSessionManager & other)570 OpalRTPSessionManager::OpalRTPSessionManager(const OpalRTPSessionManager & other)
571   : PObject(other)
572   , m_connection(other.m_connection)
573   , sessions(other.sessions)
574 {
575 }
576 
577 
~OpalRTPSessionManager()578 OpalRTPSessionManager::~OpalRTPSessionManager()
579 {
580 }
581 
582 
GetNextSessionID()583 unsigned OpalRTPSessionManager::GetNextSessionID()
584 {
585   unsigned maxSessionID = 0;
586 
587   for (PINDEX i = 0; i < sessions.GetSize(); ++i) {
588     unsigned sessionID = sessions.GetDataAt(i).sessionId;
589     if (maxSessionID < sessionID)
590       maxSessionID = sessionID;
591   }
592 
593   return maxSessionID+1;
594 }
595 
596 
AddSession(RTP_Session * rtpSession,const OpalMediaType & mediaType)597 void OpalRTPSessionManager::AddSession(RTP_Session * rtpSession, const OpalMediaType & mediaType)
598 {
599   if (rtpSession == NULL)
600     return;
601 
602   PWaitAndSignal m(m_mutex);
603 
604   unsigned sessionID = rtpSession->GetSessionID();
605   OpalMediaSession * session = sessions.GetAt(sessionID);
606   if (session == NULL) {
607     session = new OpalRTPMediaSession(m_connection, mediaType, sessionID);
608     sessions.Insert(POrdinalKey(sessionID), session);
609     PTRACE(3, "RTP\tCreating new session " << *rtpSession);
610   }
611 
612   OpalRTPMediaSession * s = dynamic_cast<OpalRTPMediaSession *>(session);
613   if (PAssert(s != NULL, "RTP session type does not match"))
614     s->Attach(rtpSession);
615 }
616 
617 
AddMediaSession(OpalMediaSession * mediaSession,const OpalMediaType &)618 void OpalRTPSessionManager::AddMediaSession(OpalMediaSession * mediaSession, const OpalMediaType & /*mediaType*/)
619 {
620   PWaitAndSignal m(m_mutex);
621 
622   PAssert(sessions.GetAt(mediaSession->sessionId) == NULL, "Cannot add already existing session");
623   sessions.Insert(POrdinalKey(mediaSession->sessionId), mediaSession);
624 }
625 
626 
CloseSession(unsigned sessionID)627 void OpalRTPSessionManager::CloseSession(unsigned sessionID)
628 {
629   PWaitAndSignal mutex(m_mutex);
630   if (sessionID == 0) {
631     for (PINDEX i = 0; i < sessions.GetSize(); ++i) {
632       PTRACE(3, "RTP\tClosing session " << sessions.GetKeyAt(i));
633       sessions.GetDataAt(i).Close();
634     }
635   }
636   else {
637     PTRACE(3, "RTP\tClosing session " << sessionID);
638     sessions[sessionID].Close();
639   }
640 }
641 
642 
GetMediaSession(unsigned sessionID) const643 OpalMediaSession * OpalRTPSessionManager::GetMediaSession(unsigned sessionID) const
644 {
645   PWaitAndSignal wait(m_mutex);
646 
647   OpalMediaSession * session;
648   if (((session = sessions.GetAt(sessionID)) == NULL) || !session->IsActive()) {
649     PTRACE(3, "RTP\tCannot find media session " << sessionID);
650     return NULL;
651   }
652 
653   PTRACE(3, "RTP\tFound existing media session " << sessionID);
654   return session;
655 }
656 
657 
GetSession(unsigned sessionID) const658 RTP_Session * OpalRTPSessionManager::GetSession(unsigned sessionID) const
659 {
660   PWaitAndSignal wait(m_mutex);
661 
662   OpalMediaSession * session;
663   if ( ((session = sessions.GetAt(sessionID)) == NULL) ||
664        !session->IsActive() ||
665        !session->IsRTP()
666        ) {
667     PTRACE(3, "RTP\tCannot find RTP session " << sessionID);
668     return NULL;
669   }
670 
671   PTRACE(3, "RTP\tFound existing RTP session " << sessionID);
672   return ((OpalRTPMediaSession *)session)->GetSession();
673 }
674 
675 
ChangeSessionID(unsigned fromSessionID,unsigned toSessionID)676 bool OpalRTPSessionManager::ChangeSessionID(unsigned fromSessionID, unsigned toSessionID)
677 {
678   PWaitAndSignal wait(m_mutex);
679 
680   if (sessions.Contains(toSessionID)) {
681     PTRACE(2, "RTP\tAttempt to renumber session " << fromSessionID << " to existing sesion ID " << toSessionID);
682     return false;
683   }
684 
685   sessions.DisallowDeleteObjects();
686   OpalMediaSession * session = sessions.RemoveAt(fromSessionID);
687   sessions.AllowDeleteObjects();
688 
689   if (session == NULL)
690     return false;
691 
692   OpalRTPMediaSession * rtpSession = dynamic_cast<OpalRTPMediaSession *>(session);
693   if (rtpSession != NULL)
694     rtpSession->GetSession()->SetSessionID(toSessionID);
695 
696   session->sessionId = toSessionID;
697   return sessions.SetAt(POrdinalKey(toSessionID), session);
698 }
699 
700 
AllSessionsFailing()701 bool OpalRTPSessionManager::AllSessionsFailing()
702 {
703   PWaitAndSignal wait(m_mutex);
704 
705   for (PINDEX i = 0; i < sessions.GetSize(); ++i) {
706     OpalMediaSession & session = sessions.GetDataAt(i);
707     if (session.IsActive() && !session.HasFailed())
708       return false;
709   }
710 
711   return true;
712 }
713 
714 /////////////////////////////////////////////////////////////////////////////
715 
716