reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
SipXRemoteParticipant.cxx
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
6
9
10#include <sdp/SdpCodec.h> // sipX SdpCodec
11
13#include "Conversation.hxx"
14#include "UserAgent.hxx"
15#include "DtmfEvent.hxx"
16#include "ReconSubsystem.hxx"
17
18#include <rutil/Log.hxx>
19#include <rutil/Logger.hxx>
20#include <rutil/DnsUtil.hxx>
21#include <rutil/Random.hxx>
31
32#ifdef USE_SSL
34#endif
35
37
38#include <utility>
39
40using namespace recon;
41using namespace sdpcontainer;
42using namespace resip;
43using namespace std;
44
45#define RESIPROCATE_SUBSYSTEM ReconSubsystem::RECON
46
47// Returns the hash function textual name (RFC 8122) of a media line's fingerprint if there is
48// one and reflow's DTLS-SRTP can check it, or 0 if not
49static const char*
51{
52#ifdef USE_SSL
55 {
56 return hashFunction;
57 }
58#endif
59 return 0;
60}
61
62/* Technically, there are a range of features that need to be implemented
63 to be fully (S)AVPF compliant.
64 However, it is speculated that (S)AVPF peers will communicate with legacy
65 systems that just fudge the RTP/SAVPF protocol in their SDP. Enabling
66 this define allows such behavior to be tested.
67
68 http://www.ietf.org/mail-archive/web/rtcweb/current/msg01145.html
69 "1) RTCWEB end-point will always signal AVPF or SAVPF. I signalling
70 gateway to legacy will change that by removing the F to AVP or SAVP."
71
72 http://www.ietf.org/mail-archive/web/rtcweb/current/msg04380.html
73*/
74//#define RTP_SAVPF_FUDGE
75
77
78// UAC
80 ConversationManager& conversationManager,
81 SipXMediaStackAdapter& sipXMediaStackAdapter,
83 RemoteParticipantDialogSet& remoteParticipantDialogSet)
84: Participant(partHandle, ConversationManager::ParticipantType_Remote, conversationManager),
85 RemoteParticipant(partHandle, conversationManager, dum, remoteParticipantDialogSet),
86 SipXParticipant(partHandle, ConversationManager::ParticipantType_Remote, conversationManager, sipXMediaStackAdapter)
87{
88 InfoLog(<< "SipXRemoteParticipant created (UAC), handle=" << mHandle);
89}
90
91// UAS - or forked leg
93 SipXMediaStackAdapter& sipXMediaStackAdapter,
95 RemoteParticipantDialogSet& remoteParticipantDialogSet)
96: Participant(ConversationManager::ParticipantType_Remote, conversationManager),
97 RemoteParticipant(conversationManager, dum, remoteParticipantDialogSet),
98 SipXParticipant(ConversationManager::ParticipantType_Remote, conversationManager, sipXMediaStackAdapter)
99{
100 InfoLog(<< "SipXRemoteParticipant created (UAS or forked leg), handle=" << mHandle);
101}
102
104{
105 // Note: Ideally this call would exist in the Participant Base class - but this call requires
106 // dynamic_casts and virtual methods to function correctly during destruction.
107 // If the call is placed in the base Participant class then these things will not
108 // function as desired because a classes type changes as the descructors unwind.
109 // See https://stackoverflow.com/questions/10979250/usage-of-this-in-destructor.
111
112 InfoLog(<< "SipXRemoteParticipant destroyed, handle=" << mHandle);
113}
114
115unsigned int
117{
119 if (sipXDialogSet)
120 {
121 return sipXDialogSet->getLocalRTPPort();
122 }
123 return 0;
124}
125
126//static const resip::ExtensionHeader h_AlertInfo("Alert-Info");
127
128
129int
131{
132 if(getDialogSet().getActiveRemoteParticipantHandle() == mHandle)
133 {
135 if (sipXDialogSet)
136 {
137 return sipXDialogSet->getConnectionPortOnBridge();
138 }
139 return -1;
140 }
141 else
142 {
143 // If this is not active fork leg, then we don't want to effect the bridge mixer.
144 // Note: All forked endpoints/participants have the same connection port on the bridge
145 return -1;
146 }
147}
148
149int
151{
153 if (sipXDialogSet)
154 {
155 return sipXDialogSet->getMediaConnectionId();
156 }
157 return 0;
158}
159
160void
161SipXRemoteParticipant::buildSdpOffer(bool holdSdp, CallbackSdpReady sdpReady, bool preferExistingSdp)
162{
163 std::unique_ptr<SdpContents> _offer(new SdpContents);
164 SdpContents& offer = *_offer;
165 SdpContents::Session::Medium *audioMedium = 0;
167 resip_assert(profile);
168
169 // We need a copy of the session caps, since we modify them
170 SdpContents sessionCaps = profile->sessionCaps();
171
172 // If we already have a local sdp for this sesion, then use this to form the next offer - doing so will ensure
173 // that we do not switch codecs or payload id's mid session.
174 if(getInviteSessionHandle().isValid() && getInviteSessionHandle()->getLocalSdp().session().media().size() != 0)
175 {
177
178 // Set sessionid and version for this sdp
179 uint64_t currentTime = Timer::getTimeMicroSec();
180 offer.session().origin().getSessionId() = currentTime;
181 offer.session().origin().getVersion() = currentTime;
182
183 // Find the audio medium
184 for (std::list<SdpContents::Session::Medium>::iterator mediaIt = offer.session().media().begin();
185 mediaIt != offer.session().media().end(); mediaIt++)
186 {
187 if(mediaIt->name() == "audio" &&
188 (mediaIt->protocol() == Symbols::RTP_AVP ||
189 mediaIt->protocol() == Symbols::RTP_SAVP ||
190#ifdef RTP_SAVPF_FUDGE
191 mediaIt->protocol() == Symbols::RTP_SAVPF ||
192#endif
193 mediaIt->protocol() == Symbols::UDP_TLS_RTP_SAVP))
194 {
195 audioMedium = &(*mediaIt);
196 break;
197 }
198 }
199 resip_assert(audioMedium);
200
201 // Add any codecs from our capabilities that may not be in current local sdp - since endpoint may have changed and may now be capable
202 // of handling codecs that it previously could not (common when endpoint is a B2BUA).
203
204 int highPayloadId = 96; // Note: static payload id's are in range of 0-96
205 // Iterate through codecs in session caps and check if already in offer
206 for (std::list<SdpContents::Session::Codec>::iterator codecsIt = sessionCaps.session().media().front().codecs().begin();
207 codecsIt != sessionCaps.session().media().front().codecs().end(); codecsIt++)
208 {
209 bool found=false;
210 bool payloadIdCollision=false;
211 for (std::list<SdpContents::Session::Codec>::iterator codecsIt2 = audioMedium->codecs().begin();
212 codecsIt2 != audioMedium->codecs().end(); codecsIt2++)
213 {
214 if(isEqualNoCase(codecsIt->getName(), codecsIt2->getName()) &&
215 codecsIt->getRate() == codecsIt2->getRate())
216 {
217 found = true;
218 }
219 else if(codecsIt->payloadType() == codecsIt2->payloadType())
220 {
221 payloadIdCollision = true;
222 }
223 // Keep track of highest payload id in offer - used if we need to resolve a payload id conflict
224 if(codecsIt2->payloadType() > highPayloadId)
225 {
226 highPayloadId = codecsIt2->payloadType();
227 }
228 }
229 if(!found)
230 {
231 if(payloadIdCollision)
232 {
233 highPayloadId++;
234 codecsIt->payloadType() = highPayloadId;
235 }
236 else if(codecsIt->payloadType() > highPayloadId)
237 {
238 highPayloadId = codecsIt->payloadType();
239 }
240 audioMedium->addCodec(*codecsIt);
241 }
242 }
243 }
244 else
245 {
246 // Build base offer
247 mConversationManager.buildSdpOffer(profile, offer);
248
249 // Assumes there is only 1 media stream in session caps and it the audio one
250 audioMedium = &offer.session().media().front();
251 resip_assert(audioMedium);
252
253 // Set the local RTP Port
255 if (sipXDialogSet)
256 {
257 audioMedium->port() = sipXDialogSet->getLocalRTPPort();
258 }
259 else
260 {
261 audioMedium->port() = 0;
262 }
263 }
264
265 // Add Crypto attributes (if required) - assumes there is only 1 media stream
266 audioMedium->clearAttribute("crypto");
267 audioMedium->clearAttribute("encryption");
268 audioMedium->clearAttribute("tcap");
269 audioMedium->clearAttribute("pcfg");
270 offer.session().clearAttribute("fingerprint");
271 offer.session().clearAttribute("setup");
272 if(getDialogSet().getSecureMediaMode() == ConversationProfile::Srtp)
273 {
274 // Note: We could add the crypto attribute to the "SDP Capabilties Negotiation"
275 // potential configuration if secure media is not required - but other implementations
276 // should ignore them any way if just plain RTP is used. It is thought the
277 // current implementation will increase interopability. (ie. SNOM Phones)
278
279 Data crypto;
280
282 if (sipXDialogSet)
283 {
284 switch (sipXDialogSet->getSrtpCryptoSuite())
285 {
287 crypto = "1 AES_CM_128_HMAC_SHA1_32 inline:" + sipXDialogSet->getLocalSrtpSessionKey().base64encode();
288 audioMedium->addAttribute("crypto", crypto);
289 crypto = "2 AES_CM_128_HMAC_SHA1_80 inline:" + sipXDialogSet->getLocalSrtpSessionKey().base64encode();
290 audioMedium->addAttribute("crypto", crypto);
291 break;
292 default:
293 crypto = "1 AES_CM_128_HMAC_SHA1_80 inline:" + sipXDialogSet->getLocalSrtpSessionKey().base64encode();
294 audioMedium->addAttribute("crypto", crypto);
295 crypto = "2 AES_CM_128_HMAC_SHA1_32 inline:" + sipXDialogSet->getLocalSrtpSessionKey().base64encode();
296 audioMedium->addAttribute("crypto", crypto);
297 break;
298 }
299 }
300 if(getDialogSet().getSecureMediaRequired())
301 {
302#ifdef RTP_SAVPF_FUDGE
303 if(mDialogSet.peerExpectsSAVPF())
304 audioMedium->protocol() = Symbols::RTP_SAVPF;
305 else
306#endif
307 audioMedium->protocol() = Symbols::RTP_SAVP;
308 }
309 else
310 {
311 audioMedium->protocol() = Symbols::RTP_AVP;
312 audioMedium->addAttribute("encryption", "optional"); // Used by SNOM phones?
313 audioMedium->addAttribute("tcap", "1 RTP/SAVP"); // draft-ietf-mmusic-sdp-capability-negotiation-08
314 audioMedium->addAttribute("pcfg", "1 t=1");
315 }
316 }
317#ifdef USE_SSL
318 else if(getDialogSet().getSecureMediaMode() == ConversationProfile::SrtpDtls)
319 {
320 if(mSipXMediaStackAdapter.getFlowManager().getDtlsFactory())
321 {
322 // Note: We could add the fingerprint and setup attributes to the "SDP Capabilties Negotiation"
323 // potential configuration if secure media is not required - but other implementations
324 // should ignore them any way if just plain RTP is used. It is thought the
325 // current implementation will increase interopability.
326
327 // Add fingerprint attribute
328 char fingerprint[100];
329 mSipXMediaStackAdapter.getFlowManager().getDtlsFactory()->getMyCertFingerprint(fingerprint);
330 //offer.session().addAttribute("fingerprint", "SHA-1 " + Data(fingerprint));
331 offer.session().addAttribute("fingerprint", "SHA-256 " + Data(fingerprint)); // Use SHA-256 for web-rtc compatibility
332 //offer.session().addAttribute("acap", "1 fingerprint:SHA-1 " + Data(fingerprint));
333
334 // Add setup attribute
335 offer.session().addAttribute("setup", "actpass");
336
337 if(getDialogSet().getSecureMediaRequired())
338 {
339 audioMedium->protocol() = Symbols::UDP_TLS_RTP_SAVP;
340 }
341 else
342 {
343 audioMedium->protocol() = Symbols::RTP_AVP;
344 audioMedium->addAttribute("tcap", "1 UDP/TLS/RTP/SAVP"); // draft-ietf-mmusic-sdp-capability-negotiation-08
345 audioMedium->addAttribute("pcfg", "1 t=1");
346 //audioMedium->addAttribute("pcfg", "1 t=1 a=1");
347 }
348 }
349 }
350#endif
351
352 audioMedium->clearAttribute("sendrecv");
353 audioMedium->clearAttribute("sendonly");
354 audioMedium->clearAttribute("recvonly");
355 audioMedium->clearAttribute("inactive");
356
357 if(holdSdp)
358 {
359 if(isRemoteHold())
360 {
361 audioMedium->addAttribute("inactive");
362 }
363 else
364 {
365 audioMedium->addAttribute("sendonly");
366 }
367 }
368 else
369 {
370 if(isRemoteHold())
371 {
372 audioMedium->addAttribute("recvonly");
373 }
374 else
375 {
376 audioMedium->addAttribute("sendrecv");
377 }
378 }
379 setProposedSdp(offer);
380 sdpReady(true, std::move(_offer));
381}
382
383bool
384SipXRemoteParticipant::answerMediaLine(SdpContents::Session::Medium& mediaSessionCaps, const sdpcontainer::SdpMediaLine& sdpMediaLine, SdpContents& answer, bool potential)
385{
387 bool valid = false;
388
389 // If this is a valid audio medium then process it
393#ifdef RTP_SAVPF_FUDGE
395#endif
397 sdpMediaLine.getConnections().size() != 0 &&
398 sdpMediaLine.getConnections().front().getPort() != 0)
399 {
400 SdpContents::Session::Medium medium("audio", getLocalRTPPort(), 1,
401#ifndef RTP_SAVPF_FUDGE
403#else
404 ( protocolType == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP
405 || protocolType == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVPF)
406 ? (protocolType == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVPF ? Symbols::RTP_SAVPF : Symbols::RTP_SAVP) :
407#endif
408 (protocolType == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP ? Symbols::UDP_TLS_RTP_SAVP :
409 Symbols::RTP_AVP));
410
411 // Check secure media properties and requirements
412 bool secureMediaRequired = getDialogSet().getSecureMediaRequired() || protocolType != sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_AVP;
414 if (sipXDialogSet)
415 {
417
418 if (getDialogSet().getSecureMediaMode() == ConversationProfile::Srtp ||
419#ifdef RTP_SAVPF_FUDGE
421#endif
422 protocolType == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP) // allow accepting of SAVP profiles, even if SRTP is not enabled as a SecureMedia mode
423 {
424 bool supportedCryptoSuite = false;
425 sdpcontainer::SdpMediaLine::CryptoList::const_iterator itCrypto = sdpMediaLine.getCryptos().begin();
426 for (; !supportedCryptoSuite && itCrypto != sdpMediaLine.getCryptos().end(); itCrypto++)
427 {
428 Data cryptoKeyB64(itCrypto->getCryptoKeyParams().front().getKeyValue());
429 Data cryptoKey = cryptoKeyB64.base64decode();
430
431 if (cryptoKey.size() == SRTP_MASTER_KEY_LEN)
432 {
433 switch (itCrypto->getSuite())
434 {
436 medium.addAttribute("crypto", Data(itCrypto->getTag()) + " AES_CM_128_HMAC_SHA1_80 inline:" + sipXDialogSet->getLocalSrtpSessionKey().base64encode());
437 supportedCryptoSuite = true;
438 break;
440 medium.addAttribute("crypto", Data(itCrypto->getTag()) + " AES_CM_128_HMAC_SHA1_32 inline:" + sipXDialogSet->getLocalSrtpSessionKey().base64encode());
441 supportedCryptoSuite = true;
442 break;
443 default:
444 break;
445 }
446 }
447 else
448 {
449 InfoLog(<< "SDES crypto key found in SDP, but is not of correct length after base 64 decode: " << cryptoKey.size());
450 }
451 }
452 if (!supportedCryptoSuite && secureMediaRequired)
453 {
454 InfoLog(<< "Secure media stream is required, but there is no supported crypto attributes in the offer - skipping this stream...");
455 return false;
456 }
457 }
458#ifdef USE_SSL
459 else if (mSipXMediaStackAdapter.getFlowManager().getDtlsFactory() &&
460 (getDialogSet().getSecureMediaMode() == ConversationProfile::SrtpDtls ||
461 protocolType == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP)) // allow accepting of DTLS SAVP profiles, even if DTLS-SRTP is not enabled as a SecureMedia mode
462 {
463 bool supportedFingerprint = false;
464
465 // We will only process Dtls-Srtp if we can check the fingerprint's hash function
466 if (supportedFingerprintHashFunction(sdpMediaLine))
467 {
468 answer.session().clearAttribute("fingerprint"); // ensure we don't add these twice
469 answer.session().clearAttribute("setup"); // ensure we don't add these twice
470
471 // Add fingerprint attribute to answer
472 char fingerprint[100];
473 mSipXMediaStackAdapter.getFlowManager().getDtlsFactory()->getMyCertFingerprint(fingerprint);
474 //answer.session().addAttribute("fingerprint", "SHA-1 " + Data(fingerprint));
475 answer.session().addAttribute("fingerprint", "SHA-256 " + Data(fingerprint)); // Use SHA-256 for web-rtc compatibility
476
477 // Add setup attribute
479 {
480 answer.session().addAttribute("setup", "passive");
481 }
482 else
483 {
484 answer.session().addAttribute("setup", "active");
485 }
486
487 supportedFingerprint = true;
488 }
489 if (!supportedFingerprint && secureMediaRequired)
490 {
491 InfoLog(<< "Secure media stream is required, but there is no supported fingerprint attributes in the offer - skipping this stream...");
492 return false;
493 }
494 }
495#endif
496 }
497
498 if(potential && !sdpMediaLine.getPotentialMediaViewString().empty())
499 {
500 medium.addAttribute("acfg", sdpMediaLine.getPotentialMediaViewString());
501 }
502
503 // Iterate through codecs and look for supported codecs - tag found ones by storing their payload id
504 sdpcontainer::SdpMediaLine::CodecList::const_iterator itCodec = sdpMediaLine.getCodecs().begin();
505 for(; itCodec != sdpMediaLine.getCodecs().end(); itCodec++)
506 {
507 unsigned int itCodecRate = itCodec->getRate();
508 if(itCodec->getMimeSubtype() == "G722" && itCodecRate != 8000)
509 {
510 WarningLog(<<"Peer is advertising G.722 with rate " << itCodecRate << ", overriding to 8000 (see RFC 3551 s4.5.2)");
511 itCodecRate = 8000;
512 }
513 std::list<SdpContents::Session::Codec>::iterator bestCapsCodecMatchIt = mediaSessionCaps.codecs().end();
514 bool modeInOffer = itCodec->getFormatParameters().prefix("mode=");
515
516 // Loop through allowed codec list and see if codec is supported locally
517 for (std::list<SdpContents::Session::Codec>::iterator capsCodecsIt = mediaSessionCaps.codecs().begin();
518 capsCodecsIt != mediaSessionCaps.codecs().end(); capsCodecsIt++)
519 {
520 if(isEqualNoCase(capsCodecsIt->getName(), itCodec->getMimeSubtype()) &&
521 (unsigned int)capsCodecsIt->getRate() == itCodecRate)
522 {
523 bool modeInCaps = capsCodecsIt->parameters().prefix("mode=");
524 if(!modeInOffer && !modeInCaps)
525 {
526 // If mode is not specified in either - then we have a match
527 bestCapsCodecMatchIt = capsCodecsIt;
528 break;
529 }
530 else if(modeInOffer && modeInCaps)
531 {
532 if(isEqualNoCase(capsCodecsIt->parameters(), itCodec->getFormatParameters()))
533 {
534 bestCapsCodecMatchIt = capsCodecsIt;
535 break;
536 }
537 // If mode is specified in both, and doesn't match - then we have no match
538 }
539 else
540 {
541 // Mode is specified on either offer or caps - this match is a potential candidate
542 // As a rule - use first match of this kind only
543 if(bestCapsCodecMatchIt == mediaSessionCaps.codecs().end())
544 {
545 bestCapsCodecMatchIt = capsCodecsIt;
546 }
547 }
548 }
549 }
550
551 if(bestCapsCodecMatchIt != mediaSessionCaps.codecs().end())
552 {
553 SdpContents::Session::Codec codec(*bestCapsCodecMatchIt);
554 codec.payloadType() = itCodec->getPayloadType(); // honour offered payload id - just to be nice :)
555 medium.addCodec(codec);
556 if(!valid && !isEqualNoCase(bestCapsCodecMatchIt->getName(), "telephone-event"))
557 {
558 // Consider offer valid if we see any matching codec other than telephone-event
559 valid = true;
560 }
561 }
562 }
563
564 if(valid)
565 {
566 // copy ptime attribute from session caps (if exists)
567 if(mediaSessionCaps.exists("ptime"))
568 {
569 medium.addAttribute("ptime", mediaSessionCaps.getValues("ptime").front());
570 }
571
572 // Check requested direction
573 unsigned int remoteRtpPort = sdpMediaLine.getConnections().front().getPort();
575 (isHolding() && (sdpMediaLine.getDirection() == sdpcontainer::SdpMediaLine::DIRECTION_TYPE_SENDONLY || remoteRtpPort == 0))) // If remote inactive or both sides are holding
576 {
577 medium.addAttribute("inactive");
578 }
579 else if(sdpMediaLine.getDirection() == sdpcontainer::SdpMediaLine::DIRECTION_TYPE_SENDONLY || remoteRtpPort == 0 /* old RFC 2543 hold */)
580 {
581 medium.addAttribute("recvonly");
582 }
584 {
585 medium.addAttribute("sendonly");
586 }
587 else
588 {
589 // Note: sendrecv is the default in SDP
590 medium.addAttribute("sendrecv");
591 }
592 answer.session().addMedium(medium);
593 }
594 }
595 return valid;
596}
597
598void
599SipXRemoteParticipant::buildSdpAnswer(const SdpContents& offer, CallbackSdpReady sdpReady)
600{
601 // Note: this implementation has minimal support for draft-ietf-mmusic-sdp-capabilities-negotiation
602 // for responding "best-effort" / optional SRTP (Dtls-SRTP) offers
603
604 bool valid = false;
605 std::shared_ptr<sdpcontainer::Sdp> remoteSdp(SdpHelper::createSdpFromResipSdp(offer));
606 std::unique_ptr<SdpContents> _answer(new SdpContents);
607 SdpContents& answer = *_answer;
608
609 try
610 {
611 // copy over session capabilities
613 resip_assert(profile);
614
615 answer = profile->sessionCaps();
616
617 // Set sessionid and version for this answer
618 uint64_t currentTime = Timer::getTimeMicroSec();
619 answer.session().origin().getSessionId() = currentTime;
620 answer.session().origin().getVersion() = currentTime;
621
622 // Set local port in answer
623 // for now we only allow 1 audio media
624 resip_assert(answer.session().media().size() == 1);
625 SdpContents::Session::Medium& mediaSessionCaps = profile->sessionCaps().session().media().front();
626 resip_assert(mediaSessionCaps.name() == "audio");
627 resip_assert(mediaSessionCaps.codecs().size() > 0);
628
629 // Copy t= field from sdp (RFC3264)
630 resip_assert(answer.session().getTimes().size() > 0);
631 if(offer.session().getTimes().size() >= 1)
632 {
633 answer.session().getTimes().clear();
634 answer.session().addTime(offer.session().getTimes().front());
635 }
636
637 // Clear out m= lines in answer then populate below
638 answer.session().media().clear();
639
640 // Loop through each offered m= line and provide a response
641 sdpcontainer::Sdp::MediaLineList::const_iterator itMediaLine = remoteSdp->getMediaLines().begin();
642 for(; itMediaLine != remoteSdp->getMediaLines().end(); itMediaLine++)
643 {
644 bool mediaLineValid = false;
645
646 // We only process one media stream - so if we already have a valid - just reject the rest
647 if(valid)
648 {
649 SdpContents::Session::Medium rejmedium((*itMediaLine)->getMediaTypeString(), 0, 1, // Reject medium by specifying port 0 (RFC3264)
650 (*itMediaLine)->getTransportProtocolTypeString());
651 if((*itMediaLine)->getCodecs().size() > 0)
652 {
653 rejmedium.addCodec(SdpContents::Session::Codec((*itMediaLine)->getCodecs().front().getMimeSubtype(),
654 (*itMediaLine)->getCodecs().front().getRate(),
655 (*itMediaLine)->getCodecs().front().getFormatParameters()));
656 rejmedium.codecs().front().payloadType() = (*itMediaLine)->getCodecs().front().getPayloadType();
657 }
658 answer.session().addMedium(rejmedium);
659 continue;
660 }
661
662 // Give preference to potential configuration first - if there are any
663 sdpcontainer::SdpMediaLine::SdpMediaLineList::const_iterator itPotentialMediaLine = (*itMediaLine)->getPotentialMediaViews().begin();
664 for(; itPotentialMediaLine != (*itMediaLine)->getPotentialMediaViews().end(); itPotentialMediaLine++)
665 {
666 mediaLineValid = answerMediaLine(mediaSessionCaps, *itPotentialMediaLine, answer, true);
667 if(mediaLineValid)
668 {
669 // We have a valid potential media - line - copy over normal media line to make
670 // further processing easier
671
672 // itPotentialMediaLine points into (*itMediaLine)->mPotentialMediaViews. Assigning
673 // *(*itMediaLine) = *itPotentialMediaLine would cause operator= to destroy
674 // mPotentialMediaViews (which owns itPotentialMediaLine's node) before copying
675 // from rhs, resulting in a use-after-free. Swapping the list out first transfers
676 // ownership of the nodes to 'stolen' in O(1) without copying, keeping
677 // itPotentialMediaLine valid for the assignment. 'stolen' is discarded on scope exit.
679 stolen.swap((*itMediaLine)->getPotentialMediaViews()); // empties the list, our iterator now owns the nodes
680 *(*itMediaLine) = *itPotentialMediaLine; // operator= assigns an empty mPotentialMediaViews, no aliasing
681 valid = true;
682 break;
683 }
684 }
685 if(!mediaLineValid)
686 {
687 // Process SDP normally
688 mediaLineValid = answerMediaLine(mediaSessionCaps, *(*itMediaLine), answer, false);
689 if(!mediaLineValid)
690 {
691 SdpContents::Session::Medium rejmedium((*itMediaLine)->getMediaTypeString(), 0, 1, // Reject medium by specifying port 0 (RFC3264)
692 (*itMediaLine)->getTransportProtocolTypeString());
693 if((*itMediaLine)->getCodecs().size() > 0)
694 {
695 rejmedium.addCodec(SdpContents::Session::Codec((*itMediaLine)->getCodecs().front().getMimeSubtype(),
696 (*itMediaLine)->getCodecs().front().getRate(),
697 (*itMediaLine)->getCodecs().front().getFormatParameters()));
698 rejmedium.codecs().front().payloadType() = (*itMediaLine)->getCodecs().front().getPayloadType();
699 }
700 answer.session().addMedium(rejmedium);
701 }
702 else
703 {
704 valid = true;
705 }
706 }
707 } // end loop through m= offers
708 }
709 catch(BaseException &e)
710 {
711 WarningLog( << "buildSdpAnswer: exception parsing SDP offer: " << e.getMessage());
712 valid = false;
713 }
714 catch(...)
715 {
716 WarningLog( << "buildSdpAnswer: unknown exception parsing SDP offer");
717 valid = false;
718 }
719
720 //InfoLog( << "SDPOffer: " << offer);
721 //InfoLog( << "SDPAnswer: " << answer);
722 if(valid)
723 {
724 setLocalSdp(answer);
725 setRemoteSdp(offer);
726 }
727 sdpReady(valid, std::move(_answer));
728}
729
730#ifdef OLD_CODE
731// Note: This old code used to serve 2 purposes -
732// 1 - that we do not change payload id's mid session
733// 2 - that we do not add codecs or media streams that have previously rejected
734// Purpose 2 is not correct. RFC3264 states we need purpose 1, but you are allowed to add new codecs mid-session
735//
736// Decision to comment out this code and implement purpose 1 elsewhere - leaving this code here for reference (for now)
737// as it may be useful for something in the future.
738bool
739SipXRemoteParticipant::formMidDialogSdpOfferOrAnswer(const SdpContents& localSdp, const SdpContents& remoteSdp, SdpContents& newSdp, bool offer)
740{
741 bool valid = false;
742
743 try
744 {
745 // start with current localSdp
746 newSdp = localSdp;
747
748 // Clear all m= lines are rebuild
749 newSdp.session().media().clear();
750
751 // Set sessionid and version for this sdp
752 uint64_t currentTime = Timer::getTimeMicroSec();
753 newSdp.session().origin().getSessionId() = currentTime;
754 newSdp.session().origin().getVersion() = currentTime;
755
756 // Loop through each m= line in local Sdp and remove or disable if not in remote
757 for (std::list<SdpContents::Session::Medium>::const_iterator localMediaIt = localSdp.session().media().begin();
758 localMediaIt != localSdp.session().media().end(); localMediaIt++)
759 {
760 for (std::list<SdpContents::Session::Medium>::const_iterator remoteMediaIt = remoteSdp.session().media().begin();
761 remoteMediaIt != remoteSdp.session().media().end(); remoteMediaIt++)
762 {
763 if(localMediaIt->name() == remoteMediaIt->name() && localMediaIt->protocol() == remoteMediaIt->protocol())
764 {
765 // Found an m= line match, proceed to process codecs
766 SdpContents::Session::Medium medium(localMediaIt->name(), localMediaIt->port(), localMediaIt->multicast(), localMediaIt->protocol());
767
768 // Iterate through local codecs and look for remote supported codecs
769 for (std::list<SdpContents::Session::Codec>::const_iterator localCodecsIt = localMediaIt->codecs().begin();
770 localCodecsIt != localMediaIt->codecs().end(); localCodecsIt++)
771 {
772 // Loop through remote supported codec list and see if codec is supported
773 for (std::list<SdpContents::Session::Codec>::const_iterator remoteCodecsIt = remoteMediaIt->codecs().begin();
774 remoteCodecsIt != remoteMediaIt->codecs().end(); remoteCodecsIt++)
775 {
776 if(isEqualNoCase(localCodecsIt->getName(), remoteCodecsIt->getName()) &&
777 localCodecsIt->getRate() == remoteCodecsIt->getRate())
778 {
779 // matching supported codec found - add to newSdp
780 medium.addCodec(*localCodecsIt);
781 if(!valid && !isEqualNoCase(localCodecsIt->getName(), "telephone-event"))
782 {
783 // Consider valid if we see any matching codec other than telephone-event
784 valid = true;
785 }
786 break;
787 }
788 }
789 }
790
791 // copy ptime attribute from session caps (if exists)
792 if(localMediaIt->exists("ptime"))
793 {
794 medium.addAttribute("ptime", localMediaIt->getValues("ptime").front());
795 }
796
797 if(offer)
798 {
799 if(isHolding())
800 {
801 if(remoteMediaIt->exists("inactive") ||
802 remoteMediaIt->exists("sendonly") ||
803 remoteMediaIt->port() == 0) // If remote inactive or both sides are holding
804 {
805 medium.addAttribute("inactive");
806 }
807 else
808 {
809 medium.addAttribute("sendonly");
810 }
811 }
812 else
813 {
814 if(remoteMediaIt->exists("inactive") || remoteMediaIt->exists("sendonly") || remoteMediaIt->port() == 0 /* old RFC 2543 hold */)
815 {
816 medium.addAttribute("recvonly");
817 }
818 else
819 {
820 medium.addAttribute("sendrecv");
821 }
822 }
823 }
824 else // This is an sdp answer
825 {
826 // Check requested direction
827 if(remoteMediaIt->exists("inactive") ||
828 (isHolding() && (remoteMediaIt->exists("sendonly") || remoteMediaIt->port() == 0))) // If remote inactive or both sides are holding
829 {
830 medium.addAttribute("inactive");
831 }
832 else if(remoteMediaIt->exists("sendonly") || remoteMediaIt->port() == 0 /* old RFC 2543 hold */)
833 {
834 medium.addAttribute("recvonly");
835 }
836 else if(remoteMediaIt->exists("recvonly") || isHolding())
837 {
838 medium.addAttribute("sendonly");
839 }
840 else
841 {
842 // Note: sendrecv is the default in SDP
843 medium.addAttribute("sendrecv");
844 }
845 }
846
847 newSdp.session().addMedium(medium);
848 break;
849 }
850 }
851 }
852 }
853 catch(BaseException &e)
854 {
855 WarningLog( << "formMidDialogSdpOfferOrAnswer: exception: " << e.getMessage());
856 valid = false;
857 }
858 catch(...)
859 {
860 WarningLog( << "formMidDialogSdpOfferOrAnswer: unknown exception");
861 valid = false;
862 }
863
864 return valid;
865}
866#endif
867
868void
870{
871 //if(mHandle) mConversationManager.onParticipantMediaUpdate(mHandle, localSdp, remoteSdp);
873 Data remoteIPAddress;
874 unsigned int remoteRtpPort=0;
875 unsigned int remoteRtcpPort=0;
876 std::shared_ptr<sdpcontainer::Sdp> localSdp(SdpHelper::createSdpFromResipSdp(sendingOffer ? *getDialogSet().getProposedSdp() : *getLocalSdp()));
877 std::shared_ptr<sdpcontainer::Sdp> remoteSdp(sendingOffer ? 0 : SdpHelper::createSdpFromResipSdp(*getRemoteSdp()));
878 const sdpcontainer::SdpMediaLine::CodecList* localCodecs = 0;
879 const sdpcontainer::SdpMediaLine::CodecList* remoteCodecs = 0;
880 bool supportedCryptoSuite = false;
881 bool supportedFingerprint = false;
882 bool localFingerprint = false;
883
884 resip_assert(localSdp);
885
887 if (!sipXDialogSet)
888 {
889 // This would only ever happen on object destruction
890 return;
891 }
892
893 /*
894 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", localSdp=" << localSdp);
895 if(remoteSdp)
896 {
897 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", remoteSdp=" << *remoteSdp);
898 }*/
899
901
902 sdpcontainer::Sdp::MediaLineList::const_iterator itMediaLine = localSdp->getMediaLines().begin();
903 for(; itMediaLine != localSdp->getMediaLines().end(); itMediaLine++)
904 {
905 DebugLog(<< "adjustRTPStreams: handle=" << mHandle <<
906 ", found media line in local sdp, mediaType=" << sdpcontainer::SdpMediaLine::SdpMediaTypeString[(*itMediaLine)->getMediaType()] <<
907 ", transportType=" << sdpcontainer::SdpMediaLine::SdpTransportProtocolTypeString[(*itMediaLine)->getTransportProtocolType()] <<
908 ", numConnections=" << (*itMediaLine)->getConnections().size() <<
909 ", port=" << ((*itMediaLine)->getConnections().size() > 0 ? (*itMediaLine)->getConnections().front().getPort() : 0));
910 if((*itMediaLine)->getMediaType() == sdpcontainer::SdpMediaLine::MEDIA_TYPE_AUDIO &&
911 ((*itMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_AVP ||
912 (*itMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP ||
913#ifdef RTP_SAVPF_FUDGE
914 (*itMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVPF ||
915#endif
916 (*itMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP) &&
917 (*itMediaLine)->getConnections().size() != 0 &&
918 (*itMediaLine)->getConnections().front().getPort() != 0)
919 {
920 //InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", found audio media line in local sdp");
921 localMediaDirection = (*itMediaLine)->getDirection();
922 localCodecs = &(*itMediaLine)->getCodecs();
923 localFingerprint = !(*itMediaLine)->getFingerPrint().empty();
924 break;
925 }
926 }
927
928 if(remoteSdp)
929 {
931
932 sdpcontainer::Sdp::MediaLineList::const_iterator itRemMediaLine = remoteSdp->getMediaLines().begin();
933 for(; itRemMediaLine != remoteSdp->getMediaLines().end(); itRemMediaLine++)
934 {
935 //InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", found media line in remote sdp");
936 if((*itRemMediaLine)->getMediaType() == sdpcontainer::SdpMediaLine::MEDIA_TYPE_AUDIO &&
937 ((*itRemMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_AVP ||
938 (*itRemMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP ||
939#ifdef RTP_SAVPF_FUDGE
940 (*itRemMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVPF ||
941#endif
942 (*itRemMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP) &&
943 (*itRemMediaLine)->getConnections().size() != 0 &&
944 (*itRemMediaLine)->getConnections().front().getPort() != 0)
945 {
946 //InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", found audio media line in remote sdp");
947 remoteMediaDirection = (*itRemMediaLine)->getDirection();
948 remoteRtpPort = (*itRemMediaLine)->getConnections().front().getPort();
949 remoteRtcpPort = (*itRemMediaLine)->getRtcpConnections().front().getPort();
950 remoteIPAddress = (*itRemMediaLine)->getConnections().front().getAddress();
951 remoteCodecs = &(*itRemMediaLine)->getCodecs();
952
953 // Process Crypto settings (if required) - createSRTPSession using remote key
954 // Note: Top crypto in remote sdp will always be the correct suite/key
955 if(getDialogSet().getSecureMediaMode() == ConversationProfile::Srtp ||
956#ifdef RTP_SAVPF_FUDGE
957 (*itRemMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVPF ||
958#endif
959 (*itRemMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP)
960 {
961 DebugLog(<<"adjustRTPStreams: handle=" << mHandle << ", process crypto settings");
962 sdpcontainer::SdpMediaLine::CryptoList::const_iterator itCrypto = (*itRemMediaLine)->getCryptos().begin();
963 for(; itCrypto != (*itRemMediaLine)->getCryptos().end(); itCrypto++)
964 {
965 Data cryptoKeyB64(itCrypto->getCryptoKeyParams().front().getKeyValue());
966 Data cryptoKey = cryptoKeyB64.base64decode();
967
968 if(cryptoKey.size() == SRTP_MASTER_KEY_LEN)
969 {
970 switch(itCrypto->getSuite())
971 {
973 if(!sipXDialogSet->createSRTPSession(MediaConstants::SRTP_AES_CM_128_HMAC_SHA1_80, cryptoKey.data(), cryptoKey.size()))
974 InfoLog(<<"Failed creating SRTP session");
975 supportedCryptoSuite = true;
976 break;
978 if(!sipXDialogSet->createSRTPSession(MediaConstants::SRTP_AES_CM_128_HMAC_SHA1_32, cryptoKey.data(), cryptoKey.size()))
979 InfoLog(<<"Failed creating SRTP session");
980 supportedCryptoSuite = true;
981 break;
982 default:
983 break;
984 }
985 }
986 else
987 {
988 InfoLog(<< "SDES crypto key found in SDP, but is not of correct length after base 64 decode: " << cryptoKey.size());
989 }
990 if(supportedCryptoSuite)
991 {
992 StackLog(<< "Supported crypto suite found");
993 break;
994 }
995 }
996 }
997 // Process Fingerprint and setup settings (if required): DTLS-SRTP was negotiated if the remote
998 // media line is UDP/TLS/RTP/SAVP, or if ours offered or answered it - with a fingerprint - and the
999 // remote one has a fingerprint too. The latter covers RFC 8643 (OSRTP) RTP/AVP media lines with
1000 // fingerprints, and an answer of ours that took the UDP/TLS/RTP/SAVP potential configuration
1001 // (tcap/pcfg) of an RTP/AVP offer - whose media line, here, is still RTP/AVP.
1002 else if((*itRemMediaLine)->getTransportProtocolType() == sdpcontainer::SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP ||
1003 localFingerprint)
1004 {
1005 // We will only process Dtls-Srtp if we can check the fingerprint's hash function
1006 const char* hashFunction = supportedFingerprintHashFunction(**itRemMediaLine);
1007 if(hashFunction)
1008 {
1009 InfoLog(<< "Fingerprint retrieved from remote SDP: " << hashFunction << " " << (*itRemMediaLine)->getFingerPrint());
1010 // ensure we only accept media streams with this fingerprint
1011 sipXDialogSet->setRemoteSDPFingerprint(hashFunction, (*itRemMediaLine)->getFingerPrint());
1012
1013 // If remote setup value is not active then we must be the Dtls client - ensure client DtlsSocket is create
1014 if((*itRemMediaLine)->getTcpSetupAttribute() != sdpcontainer::SdpMediaLine::TCP_SETUP_ATTRIBUTE_ACTIVE)
1015 {
1016 // If we are the active end, then kick start the DTLS handshake
1017 sipXDialogSet->startDtlsClient(remoteIPAddress.c_str(), remoteRtpPort, remoteRtcpPort);
1018 }
1019
1020 supportedFingerprint = true;
1021 }
1022 else if((*itRemMediaLine)->getFingerPrintHashFunction() != sdpcontainer::SdpMediaLine::FINGERPRINT_HASH_FUNC_NONE)
1023 {
1024 InfoLog(<< "Fingerprint found, but its hash function is not supported: "
1025 << sdpcontainer::SdpMediaLine::SdpFingerPrintHashFuncTypeString[(*itRemMediaLine)->getFingerPrintHashFunction()]);
1026 }
1027 }
1028
1029 break;
1030 }
1031 }
1032
1033 if (remoteMediaDirection == sdpcontainer::SdpMediaLine::DIRECTION_TYPE_INACTIVE ||
1035 {
1036 setRemoteHold(true);
1037 }
1038 else
1039 {
1040 setRemoteHold(false);
1041 }
1042
1043 // Check if any conversations are broadcast only - if so, then we need to send media to parties on hold
1044 bool broadcastOnly = false;
1045 ConversationMap::iterator it;
1046 for(it = mConversations.begin(); it != mConversations.end(); it++)
1047 {
1048 if(it->second->broadcastOnly())
1049 {
1050 broadcastOnly = true;
1051 break;
1052 }
1053 }
1054
1055 // Aggregate local and remote direction attributes to determine overall media direction
1056 if((isHolding() && !broadcastOnly) ||
1059 {
1061 }
1062 else if(localMediaDirection == sdpcontainer::SdpMediaLine::DIRECTION_TYPE_SENDONLY)
1063 {
1065 }
1066 else if(localMediaDirection == sdpcontainer::SdpMediaLine::DIRECTION_TYPE_RECVONLY)
1067 {
1069 }
1070 else if(remoteMediaDirection == sdpcontainer::SdpMediaLine::DIRECTION_TYPE_SENDONLY)
1071 {
1073 }
1074 else if(remoteMediaDirection == sdpcontainer::SdpMediaLine::DIRECTION_TYPE_RECVONLY)
1075 {
1077 }
1078 else
1079 {
1081 }
1082 }
1083 else
1084 {
1085 // No remote SDP info - so put direction into receive only mode (unless inactive)
1086 if(isHolding() ||
1089 {
1091 }
1092 else
1093 {
1095 }
1096 }
1097
1098 if(remoteSdp && getDialogSet().getSecureMediaRequired() && !supportedCryptoSuite && !supportedFingerprint)
1099 {
1100 InfoLog(<< "Secure media is required and no valid support found in remote sdp - ending call.");
1102 return;
1103 }
1104
1105 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", mediaDirection=" << sdpcontainer::SdpMediaLine::SdpDirectionTypeString[mediaDirection] << ", remoteIp=" << remoteIPAddress << ", remotePort=" << remoteRtpPort);
1106
1107 bool activeDestinationSet = false;
1108 if(!remoteIPAddress.empty() && remoteIPAddress != IPV4_ZERO_ADDRESS_STRING && remoteRtpPort != 0)
1109 {
1110 sipXDialogSet->setActiveDestination(remoteIPAddress.c_str(), remoteRtpPort, remoteRtcpPort);
1111 activeDestinationSet = true;
1112 }
1113
1116 activeDestinationSet && remoteCodecs && localCodecs)
1117 {
1118 // Calculate intersection of local and remote codecs, and pass remote codecs that exist locally to RTP send fn
1119 int numCodecs=0;
1120 ::SdpCodec** codecs = new ::SdpCodec*[remoteCodecs->size()];
1121 sdpcontainer::SdpMediaLine::CodecList::const_iterator itRemoteCodec = remoteCodecs->begin();
1122 for(; itRemoteCodec != remoteCodecs->end(); itRemoteCodec++)
1123 {
1124 bool modeInRemote = itRemoteCodec->getFormatParameters().prefix("mode=");
1125 sdpcontainer::SdpMediaLine::CodecList::const_iterator bestCapsCodecMatchIt = localCodecs->end();
1126 sdpcontainer::SdpMediaLine::CodecList::const_iterator itLocalCodec = localCodecs->begin();
1127 for(; itLocalCodec != localCodecs->end(); itLocalCodec++)
1128 {
1129 if(isEqualNoCase(itRemoteCodec->getMimeSubtype(), itLocalCodec->getMimeSubtype()) &&
1130 itRemoteCodec->getRate() == itLocalCodec->getRate())
1131 {
1132 bool modeInLocal = itLocalCodec->getFormatParameters().prefix("mode=");
1133 if(!modeInLocal && !modeInRemote)
1134 {
1135 // If mode is not specified in either - then we have a match
1136 bestCapsCodecMatchIt = itLocalCodec;
1137 break;
1138 }
1139 else if(modeInLocal && modeInRemote)
1140 {
1141 if(isEqualNoCase(itRemoteCodec->getFormatParameters(), itLocalCodec->getFormatParameters()))
1142 {
1143 bestCapsCodecMatchIt = itLocalCodec;
1144 break;
1145 }
1146 // If mode is specified in both, and doesn't match - then we have no match
1147 }
1148 else
1149 {
1150 // Mode is specified on either offer or caps - this match is a potential candidate
1151 // As a rule - use first match of this kind only
1152 if(bestCapsCodecMatchIt == localCodecs->end())
1153 {
1154 bestCapsCodecMatchIt = itLocalCodec;
1155 }
1156 }
1157 }
1158 }
1159 if(bestCapsCodecMatchIt != localCodecs->end())
1160 {
1161 codecs[numCodecs++] = new ::SdpCodec(itRemoteCodec->getPayloadType(),
1162 itRemoteCodec->getMimeType().c_str(),
1163 itRemoteCodec->getMimeSubtype().c_str(),
1164 itRemoteCodec->getRate(),
1165 itRemoteCodec->getPacketTime(),
1166 itRemoteCodec->getNumChannels(),
1167 itRemoteCodec->getFormatParameters().c_str());
1168
1169 UtlString codecString;
1170 codecs[numCodecs-1]->toString(codecString);
1171
1172 DebugLog(<< "adjustRTPStreams: handle=" << mHandle << ", sending codec: " << codecString.data());
1173 }
1174 }
1175
1176 if(numCodecs > 0)
1177 {
1178 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", starting to send for " << numCodecs << " codecs to destination address " << remoteIPAddress << ":" <<
1179 remoteRtpPort << " (RTCP on " << remoteRtcpPort << ")");
1180 int ret = getMediaInterface()->getInterface()->startRtpSend(sipXDialogSet->getMediaConnectionId(), numCodecs, codecs);
1181 if(ret != OS_SUCCESS)
1182 {
1183 InfoLog(<<"adjustRTPStreams: handle=" << mHandle << ", failed to start RTP send, ret = " << ret);
1184 }
1185 }
1186 else
1187 {
1188 WarningLog(<< "adjustRTPStreams: handle=" << mHandle << ", something went wrong during SDP negotiations, no common codec found.");
1189 }
1190 for(int i = 0; i < numCodecs; i++)
1191 {
1192 delete codecs[i];
1193 }
1194 delete [] codecs;
1195 }
1196 else
1197 {
1198 if(getMediaInterface()->getInterface()->isSendingRtpAudio(sipXDialogSet->getMediaConnectionId()))
1199 {
1200 getMediaInterface()->getInterface()->stopRtpSend(sipXDialogSet->getMediaConnectionId());
1201 }
1202 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", stop sending.");
1203 }
1204
1207 && localCodecs)
1208 {
1209 if(!getMediaInterface()->getInterface()->isReceivingRtpAudio(sipXDialogSet->getMediaConnectionId()))
1210 {
1211 // !SLG! - we could make this better, no need to recalculate this every time
1212 // We are always willing to receive any of our locally supported codecs
1213 int numCodecs=0;
1214 ::SdpCodec** codecs = new ::SdpCodec*[localCodecs->size()];
1215 sdpcontainer::SdpMediaLine::CodecList::const_iterator itLocalCodec = localCodecs->begin();
1216 for(; itLocalCodec != localCodecs->end(); itLocalCodec++)
1217 {
1218 codecs[numCodecs++] = new ::SdpCodec(itLocalCodec->getPayloadType(),
1219 itLocalCodec->getMimeType().c_str(),
1220 itLocalCodec->getMimeSubtype().c_str(),
1221 itLocalCodec->getRate(),
1222 itLocalCodec->getPacketTime(),
1223 itLocalCodec->getNumChannels(),
1224 itLocalCodec->getFormatParameters().c_str());
1225 UtlString codecString;
1226 codecs[numCodecs-1]->toString(codecString);
1227 DebugLog(<< "adjustRTPStreams: handle=" << mHandle << ", receiving: " << codecString.data());
1228 }
1229 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", starting to receive for " << numCodecs << " codecs");
1230
1231 getMediaInterface()->getInterface()->startRtpReceive(sipXDialogSet->getMediaConnectionId(), numCodecs, codecs);
1232 for(int i = 0; i < numCodecs; i++)
1233 {
1234 delete codecs[i];
1235 }
1236 delete [] codecs;
1237 }
1238 DebugLog(<< "adjustRTPStreams: handle=" << mHandle << ", receiving...");
1239 }
1240 else
1241 {
1242 // Never stop receiving - keep reading buffers and let mixing matrix handle supression of audio output
1243 //if(getMediaInterface()->getInterface()->isReceivingRtpAudio(getSipXDialogSet().getMediaConnectionId()))
1244 //{
1245 // getMediaInterface()->getInterface()->stopRtpReceive(getSipXDialogSet().getMediaConnectionId());
1246 //}
1247 //InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", stop receiving (mLocalHold=" << isHolding() << ").");
1248 }
1249}
1250
1251void
1253{
1254 WarningLog(<<"requestKeyframe not implemented for this media stack"); // FIXME
1255}
1256
1257bool
1262
1263
1264/* ====================================================================
1265
1266 Copyright (c) 2021-2026, SIP Spectrum, Inc.
1267 Copyright (c) 2022, Software Freedom Institute https://softwarefreedom.institute
1268 Copyright (c) 2021-2022, Daniel Pocock https://danielpocock.com
1269 Copyright (c) 2007-2008, Plantronics, Inc.
1270 All rights reserved.
1271
1272 Redistribution and use in source and binary forms, with or without
1273 modification, are permitted provided that the following conditions are
1274 met:
1275
1276 1. Redistributions of source code must retain the above copyright
1277 notice, this list of conditions and the following disclaimer.
1278
1279 2. Redistributions in binary form must reproduce the above copyright
1280 notice, this list of conditions and the following disclaimer in the
1281 documentation and/or other materials provided with the distribution.
1282
1283 3. Neither the name of Plantronics nor the names of its contributors
1284 may be used to endorse or promote products derived from this
1285 software without specific prior written permission.
1286
1287 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1288 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1289 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1290 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1291 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1292 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1293 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1294 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1295 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1296 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1297 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1298
1299 ==================================================================== */
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define resip_assert(x)
This file needs to work for both C and C++ code.
static Data IPV4_ZERO_ADDRESS_STRING("0.0.0.0")
static const char * supportedFingerprintHashFunction(const sdpcontainer::SdpMediaLine &mediaLine)
static sdpcontainer::Sdp * createSdpFromResipSdp(const resip::SdpContents &resipSdp)
Definition SdpHelper.cxx:37
void getMyCertFingerprint(char *fingerprint)
static bool isFingerprintHashFunctionSupported(const char *hashFunction)
dtls::DtlsFactory * getDtlsFactory()
This class is one of two main classes of concern to an application using the UserAgent library.
This class extends the resip UserProfile to include Conversation Manager specific settings.
virtual resip::SdpContents & sessionCaps()
Get/Set the session capabilities.
This is the base class for a RemoteParticipant, LocalParticipant and a MediaResourceParticipant.
ConversationMap mConversations
virtual void unregisterFromAllConversations()
ParticipantHandle mHandle
ConversationManager & mConversationManager
This class is used by Invite DialogSets.
std::shared_ptr< ConversationProfile > getConversationProfile()
This class represent a remote participant.
void setProposedSdp(const resip::SdpContents &sdp)
void setRemoteSdp(const resip::SdpContents &sdp, bool answer=false)
std::function< void(bool sdpOk, std::unique_ptr< resip::SdpContents > sdp)> CallbackSdpReady
void setLocalSdp(const resip::SdpContents &sdp)
std::shared_ptr< resip::SdpContents > getLocalSdp()
std::shared_ptr< resip::SdpContents > getRemoteSdp()
virtual void destroyParticipant() override
virtual resip::InviteSessionHandle & getInviteSessionHandle()
void setRemoteHold(bool remoteHold)
RemoteParticipantDialogSet & getDialogSet()
This class is one of two main classes of concern to an application using the UserAgent library.
This is the base class for a SipXRemoteParticipant, SipXLocalParticipant and a SipXMediaResourceParti...
SipXMediaStackAdapter & mSipXMediaStackAdapter
virtual std::shared_ptr< SipXMediaInterface > getMediaInterface()
This class is used by Invite DialogSets.
resip::MediaConstants::SrtpCryptoSuite getSrtpCryptoSuite()
void setRemoteSDPFingerprint(const resip::Data &hashFunction, const resip::Data &fingerprint)
void setActiveDestination(const char *address, unsigned short rtpPort, unsigned short rtcpPort)
bool createSRTPSession(resip::MediaConstants::SrtpCryptoSuite cryptoSuite, const char *remoteKey, size_t remoteKeyLen)
void startDtlsClient(const char *address, unsigned short rtpPort, unsigned short rtcpPort)
virtual void adjustRTPStreams(bool sendingOffer=false) override
virtual SipXRemoteParticipantDialogSet * getSipXDialogSet()
SipXRemoteParticipant(ParticipantHandle partHandle, ConversationManager &conversationManager, SipXMediaStackAdapter &sipXMediaStackAdapter, resip::DialogUsageManager &dum, RemoteParticipantDialogSet &remoteParticipantDialogSet)
virtual void requestKeyframe() override
virtual void buildSdpOffer(bool holdSdp, CallbackSdpReady sdpReady, bool preferExistingSdp=false) override
virtual int getConnectionPortOnBridge() override
virtual bool mediaStackPortAvailable() override
The abstract base-class for all exceptions thrown by resip code.
const Data & getMessage() const noexcept
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
Data base64decode() const
Performs RFC 3548 Base 64 decoding of the contents of this data.
Definition Data.cxx:2607
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
Data base64encode(bool useUrlSafe=false) const
Performs RFC 3548 Base 64 encoding of the contents of this data.
Definition Data.cxx:2682
bool prefix(const Data &pre) const noexcept
Returns true if this Data starts with the bytes indicated by the passed-in Data.
Definition Data.cxx:1888
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
const SdpContents & getLocalSdp() const
parameters for a specific codec are stored in this class.
process m= (media announcement) blocks
void addAttribute(const Data &key, const Data &value=Data::Empty)
add a media attribute line
const std::list< Data > & getValues(const Data &key) const
get the attribute values corresponding to the key - returns the attributes set on this medium,...
bool exists(const Data &key) const
tests if an a= key is present in the media section
int port() const
return the base port
void addCodec(const Codec &codec)
add a codec to the m= line
const CodecContainer & codecs() const
preferred codec/format interface
const Data & protocol() const
return the transport protocol
const Data & name() const
return the media type
void clearAttribute(const Data &key)
erase all attributes for a given key
const uint64_t & getVersion() const
returns the session version
const uint64_t & getSessionId() const
returns the session ID
const std::list< Time > & getTimes() const
return session Time lines
void addMedium(const Medium &medium)
add an m= (Medium) section to session
void addTime(const Time &t)
add a t= (Time) line to session
const Origin & origin() const
return session Origin line
void clearAttribute(const Data &key)
erase all attributes for a given key
const MediumContainer & media() const
return session Media lines
void addAttribute(const Data &key, const Data &value=Data::Empty)
add a session attribute line
Provides an interface to process and generate SDP bodies (MIME content-type application/sdp).
Session & session()
get the parsed SDP
static const char * RTP_AVP
Definition Symbols.hxx:97
static const char * RTP_SAVP
Definition Symbols.hxx:99
static const char * RTP_SAVPF
Definition Symbols.hxx:100
static const char * UDP_TLS_RTP_SAVP
Definition Symbols.hxx:101
static uint64_t getTimeMicroSec()
Returns the current clock time in microseconds.
Definition Timer.hxx:66
const resip::Data & getFormatParameters() const
Definition SdpCodec.hxx:42
void toString(resip::Data &sdpCodecString) const
SdpDirectionType getDirection() const
const resip::Data & getFingerPrint() const
const resip::Data & getPotentialMediaViewString() const
SdpTransportProtocolType
Transport protocol identifiers for the SDP "m=" line (proto field).
const CryptoList & getCryptos() const
static const char * SdpFingerPrintHashFuncTypeString[]
SdpTcpSetupAttribute getTcpSetupAttribute() const
std::list< SdpMediaLine > SdpMediaLineList
std::list< SdpCodec > CodecList
static const char * SdpDirectionTypeString[]
const SdpTransportProtocolType getTransportProtocolType() const
SdpFingerPrintHashFuncType getFingerPrintHashFunction() const
const SdpMediaType getMediaType() const
static const char * SdpMediaTypeString[]
static const char * SdpTransportProtocolTypeString[]
const CodecList & getCodecs() const
const ConnectionList & getConnections() const
unsigned int ParticipantHandle
dcm! – add UnusedChecking(_enum) below;
bool isEqualNoCase(const Data &left, const Data &right)
Definition Data.hxx:1116
unsigned __int64 uint64_t
Definition stdint.h:95