Ring Daemon 16.0.0
Loading...
Searching...
No Matches
sdp.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "sdp.h"
19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
24#include "sip/sipaccount.h"
25#include "sip/sipvoiplink.h"
26#include "string_utils.h"
27#include "base64.h"
28
29#include "manager.h"
30#include "logger.h"
31#include "libav_utils.h"
32
33#include "media_codec.h"
35#include "compiler_intrinsics.h" // for UNUSED
36
37#include <opendht/rng.h>
38
39#include <algorithm>
40#include <cassert>
41
42namespace jami {
43
44using std::string;
45using std::vector;
46
47static constexpr int POOL_INITIAL_SIZE = 16384;
49
50static std::map<MediaDirection, const char*> DIRECTION_STR {{MediaDirection::SENDRECV, "sendrecv"},
51 {MediaDirection::SENDONLY, "sendonly"},
52 {MediaDirection::RECVONLY, "recvonly"},
53 {MediaDirection::INACTIVE, "inactive"},
54 {MediaDirection::UNKNOWN, "unknown"}};
55
56Sdp::Sdp(const std::string& id)
57 : memPool_(nullptr, [](pj_pool_t* pool) { pj_pool_release(pool); })
58 , publishedIpAddr_()
59 , publishedIpAddrType_()
60 , telephoneEventPayload_(101) // same as asterisk
61 , sessionName_("Call ID " + id)
62{
63 memPool_.reset(pj_pool_create(&Manager::instance().sipVoIPLink().getCachingPool()->factory,
64 id.c_str(),
67 NULL));
68 if (not memPool_)
69 throw std::runtime_error("pj_pool_create() failed");
70}
71
73{
74 SIPAccount::releasePort(localAudioRtpPort_);
75#ifdef ENABLE_VIDEO
76 SIPAccount::releasePort(localVideoRtpPort_);
77#endif
78}
79
80std::shared_ptr<SystemCodecInfo>
81Sdp::findCodecBySpec(std::string_view codec, const unsigned clockrate) const
82{
83 // TODO : only manage a list?
84 for (const auto& accountCodec : audio_codec_list_) {
85 auto audioCodecInfo = std::static_pointer_cast<SystemAudioCodecInfo>(accountCodec);
86 if (audioCodecInfo->name == codec
87 and (audioCodecInfo->isPCMG722() ? (clockrate == 8000)
88 : (audioCodecInfo->audioformat.sample_rate == clockrate)))
90 }
91
92 for (const auto& accountCodec : video_codec_list_) {
93 if (accountCodec->name == codec)
94 return accountCodec;
95 }
96 return nullptr;
97}
98
99std::shared_ptr<SystemCodecInfo>
100Sdp::findCodecByPayload(const unsigned payloadType)
101{
102 // TODO : only manage a list?
103 for (const auto& accountCodec : audio_codec_list_) {
104 if (accountCodec->payloadType == payloadType)
105 return accountCodec;
106 }
107
108 for (const auto& accountCodec : video_codec_list_) {
109 if (accountCodec->payloadType == payloadType)
110 return accountCodec;
111 }
112 return nullptr;
113}
114
115static void
116randomFill(std::vector<uint8_t>& dest)
117{
118 std::uniform_int_distribution<int> rand_byte {0, std::numeric_limits<uint8_t>::max()};
119 std::random_device rdev;
120 std::generate(dest.begin(), dest.end(), std::bind(rand_byte, std::ref(rdev)));
121}
122
123void
125{
126 if (activeLocalSession_ != sdp)
127 JAMI_DBG("Set active local session to [%p]. Was [%p]", sdp, activeLocalSession_);
128 activeLocalSession_ = sdp;
129}
130
131void
133{
134 if (activeLocalSession_ != sdp)
135 JAMI_DBG("Set active remote session to [%p]. Was [%p]", sdp, activeRemoteSession_);
136 activeRemoteSession_ = sdp;
137}
138
140Sdp::generateSdesAttribute()
141{
142 static constexpr const unsigned cryptoSuite = 0;
143 std::vector<uint8_t> keyAndSalt;
144 keyAndSalt.resize(jami::CryptoSuites[cryptoSuite].masterKeyLength / 8
145 + jami::CryptoSuites[cryptoSuite].masterSaltLength / 8);
146 // generate keys
148
149 std::string crypto_attr = "1 "s + jami::CryptoSuites[cryptoSuite].name
150 + " inline:" + base64::encode(keyAndSalt);
152 return pjmedia_sdp_attr_create(memPool_.get(), "crypto", &val);
153}
154
155char const*
156Sdp::mediaDirection(const MediaAttribute& mediaAttr)
157{
158 if (not mediaAttr.enabled_) {
160 }
161
162 // Since mute/un-mute audio is only done locally (RTP packets
163 // are still sent to the peer), the media direction must be
164 // set to "sendrecv" regardless of the mute state.
165 if (mediaAttr.type_ == MediaType::MEDIA_AUDIO) {
167 }
168
169 if (mediaAttr.muted_) {
170 if (mediaAttr.onHold_) {
172 }
174 }
175
176 if (mediaAttr.onHold_) {
178 }
179
181}
182
184Sdp::getMediaDirection(pjmedia_sdp_media* media)
185{
186 if (pjmedia_sdp_attr_find2(media->attr_count,
187 media->attr,
189 nullptr)
190 != nullptr) {
192 }
193
194 if (pjmedia_sdp_attr_find2(media->attr_count,
195 media->attr,
197 nullptr)
198 != nullptr) {
200 }
201
202 if (pjmedia_sdp_attr_find2(media->attr_count,
203 media->attr,
205 nullptr)
206 != nullptr) {
208 }
209
210 if (pjmedia_sdp_attr_find2(media->attr_count,
211 media->attr,
213 nullptr)
214 != nullptr) {
216 }
217
219}
220
222Sdp::getMediaTransport(pjmedia_sdp_media* media)
223{
224 if (pj_stricmp2(&media->desc.transport, "RTP/SAVP") == 0)
226 else if (pj_stricmp2(&media->desc.transport, "RTP/AVP") == 0)
228
230}
231
232std::vector<std::string>
233Sdp::getCrypto(pjmedia_sdp_media* media)
234{
235 std::vector<std::string> crypto;
236 for (unsigned j = 0; j < media->attr_count; j++) {
237 const auto attribute = media->attr[j];
238 if (pj_stricmp2(&attribute->name, "crypto") == 0)
239 crypto.emplace_back(attribute->value.ptr, attribute->value.slen);
240 }
241
242 return crypto;
243}
244
246Sdp::addMediaDescription(const MediaAttribute& mediaAttr)
247{
248 auto type = mediaAttr.type_;
249 auto secure = mediaAttr.secure_;
250
251 JAMI_DBG("Add media description [%s]", mediaAttr.toString(true).c_str());
252
254
255 switch (type) {
257 med->desc.media = sip_utils::CONST_PJ_STR("audio");
258 med->desc.port = mediaAttr.enabled_ ? localAudioRtpPort_ : 0;
259 med->desc.fmt_count = audio_codec_list_.size();
260 break;
262 med->desc.media = sip_utils::CONST_PJ_STR("video");
263 med->desc.port = mediaAttr.enabled_ ? localVideoRtpPort_ : 0;
264 med->desc.fmt_count = video_codec_list_.size();
265 break;
266 default:
267 throw SdpException("Unsupported media type! Only audio and video are supported");
268 break;
269 }
270
271 med->desc.port_count = 1;
272
273 // Set the transport protocol of the media
274 med->desc.transport = secure ? sip_utils::CONST_PJ_STR("RTP/SAVP")
275 : sip_utils::CONST_PJ_STR("RTP/AVP");
276
277 unsigned dynamic_payload = 96;
278
279 for (unsigned i = 0; i < med->desc.fmt_count; i++) {
281 rtpmap.param.slen = 0;
282
283 std::string channels; // must have the lifetime of rtpmap
284 std::string enc_name;
285 unsigned payload;
286
287 if (type == MediaType::MEDIA_AUDIO) {
288 auto accountAudioCodec = std::static_pointer_cast<SystemAudioCodecInfo>(
289 audio_codec_list_[i]);
290 payload = accountAudioCodec->payloadType;
292
293 if (accountAudioCodec->audioformat.nb_channels > 1) {
294 channels = std::to_string(accountAudioCodec->audioformat.nb_channels);
295 rtpmap.param = sip_utils::CONST_PJ_STR(channels);
296 }
297 // G722 requires G722/8000 media description even though it's @ 16000 Hz
298 // See http://tools.ietf.org/html/rfc3551#section-4.5.2
299 if (accountAudioCodec->isPCMG722())
300 rtpmap.clock_rate = 8000;
301 else
302 rtpmap.clock_rate = accountAudioCodec->audioformat.sample_rate;
303
304 } else {
305 // FIXME: get this key from header
306 payload = dynamic_payload++;
307 enc_name = video_codec_list_[i]->name;
308 rtpmap.clock_rate = 90000;
309 }
310
311 auto payloadStr = std::to_string(payload);
313 pj_strdup(memPool_.get(), &med->desc.fmt[i], &pjPayload);
314
315 // Add a rtpmap field for each codec
316 // We could add one only for dynamic payloads because the codecs with static RTP payloads
317 // are entirely defined in the RFC 3351
318 rtpmap.pt = med->desc.fmt[i];
320
322 pjmedia_sdp_rtpmap_to_attr(memPool_.get(), &rtpmap, &attr);
323 med->attr[med->attr_count++] = attr;
324
325#ifdef ENABLE_VIDEO
326 if (enc_name == "H264") {
327 // FIXME: this should not be hardcoded, it will determine what profile and level
328 // our peer will send us
329 const auto accountVideoCodec = std::static_pointer_cast<SystemVideoCodecInfo>(
330 video_codec_list_[i]);
331 const auto& profileLevelID = accountVideoCodec->parameters.empty()
333 : accountVideoCodec->parameters;
334 auto value = fmt::format("fmtp:{} {}", payload, profileLevelID);
335 med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_.get(),
336 value.c_str(),
337 NULL);
338 }
339#endif
340 }
341
342 if (type == MediaType::MEDIA_AUDIO) {
343 setTelephoneEventRtpmap(med);
344 if (localAudioRtcpPort_) {
345 addRTCPAttribute(med, localAudioRtcpPort_);
346 }
347 } else if (type == MediaType::MEDIA_VIDEO and localVideoRtcpPort_) {
348 addRTCPAttribute(med, localVideoRtcpPort_);
349 }
350
351 char const* direction = mediaDirection(mediaAttr);
352
353 med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_.get(), direction, NULL);
354
355 if (secure) {
356 if (pjmedia_sdp_media_add_attr(med, generateSdesAttribute()) != PJ_SUCCESS)
357 throw SdpException("Unable to add sdes attribute to media");
358 }
359
360 return med;
361}
362
363void
364Sdp::addRTCPAttribute(pjmedia_sdp_media* med, uint16_t port)
365{
366 dhtnet::IpAddr addr {publishedIpAddr_};
367 addr.setPort(port);
368 pjmedia_sdp_attr* attr = pjmedia_sdp_attr_create_rtcp(memPool_.get(), addr.pjPtr());
369 if (attr)
370 pjmedia_sdp_attr_add(&med->attr_count, med->attr, attr);
371}
372
373void
375{
376 publishedIpAddr_ = addr;
377 publishedIpAddrType_ = addr_type;
378 if (localSession_) {
379 if (addr_type == pj_AF_INET6())
380 localSession_->origin.addr_type = sip_utils::CONST_PJ_STR("IP6");
381 else
382 localSession_->origin.addr_type = sip_utils::CONST_PJ_STR("IP4");
383 localSession_->origin.addr = sip_utils::CONST_PJ_STR(publishedIpAddr_);
384 localSession_->conn->addr = localSession_->origin.addr;
385 if (pjmedia_sdp_validate(localSession_) != PJ_SUCCESS)
386 JAMI_ERR("Unable to validate SDP");
387 }
388}
389
390void
391Sdp::setPublishedIP(const dhtnet::IpAddr& ip_addr)
392{
393 setPublishedIP(ip_addr, ip_addr.getFamily());
394}
395
396void
397Sdp::setTelephoneEventRtpmap(pjmedia_sdp_media* med)
398{
399 ++med->desc.fmt_count;
400 pj_strdup2(memPool_.get(),
401 &med->desc.fmt[med->desc.fmt_count - 1],
402 std::to_string(telephoneEventPayload_).c_str());
403
405 pj_pool_zalloc(memPool_.get(), sizeof(pjmedia_sdp_attr)));
406 attr_rtpmap->name = sip_utils::CONST_PJ_STR("rtpmap");
407 attr_rtpmap->value = sip_utils::CONST_PJ_STR("101 telephone-event/8000");
408
409 med->attr[med->attr_count++] = attr_rtpmap;
410
412 pj_pool_zalloc(memPool_.get(), sizeof(pjmedia_sdp_attr)));
413 attr_fmtp->name = sip_utils::CONST_PJ_STR("fmtp");
414 attr_fmtp->value = sip_utils::CONST_PJ_STR("101 0-15");
415
416 med->attr[med->attr_count++] = attr_fmtp;
417}
418
419void
421 const std::vector<std::shared_ptr<SystemCodecInfo>>& selectedCodecs)
422{
423 switch (type) {
425 audio_codec_list_ = selectedCodecs;
426 break;
427
429#ifdef ENABLE_VIDEO
430 video_codec_list_ = selectedCodecs;
431 // Do not expose H265 if accel is disactivated
432 if (not jami::Manager::instance().videoPreferences.getEncodingAccelerated()) {
433 video_codec_list_.erase(std::remove_if(video_codec_list_.begin(),
434 video_codec_list_.end(),
435 [](const std::shared_ptr<SystemCodecInfo>& i) {
436 return i->name == "H265";
437 }),
438 video_codec_list_.end());
439 }
440#else
442#endif
443 break;
444
445 default:
446 throw SdpException("Unsupported media type");
447 break;
448 }
449}
450
451constexpr std::string_view
453{
455 return "OFFER"sv;
457 return "ANSWER"sv;
458 return "NONE"sv;
459}
460
461void
463{
464 static constexpr size_t BUF_SZ = 4095;
465 std::unique_ptr<pj_pool_t, decltype(pj_pool_release)&>
466 tmpPool_(pj_pool_create(&Manager::instance().sipVoIPLink().getCachingPool()->factory,
467 "printSdp",
468 BUF_SZ,
469 BUF_SZ,
470 nullptr),
472
474 if (!cloned_session) {
475 JAMI_ERROR("Unable to clone SDP for printing");
476 return;
477 }
478
479 // Filter-out sensible data like SRTP master key.
480 for (unsigned i = 0; i < cloned_session->media_count; ++i) {
482 }
483
484 std::array<char, BUF_SZ + 1> buffer;
485 auto size = pjmedia_sdp_print(cloned_session, buffer.data(), BUF_SZ);
486 if (size < 0) {
487 JAMI_ERROR("SDP too big for dump: {}", header);
488 return;
489 }
490
491 JAMI_LOG("[SDP {}] {}\n{:s}", getSdpDirectionStr(direction), header,
492 std::string_view(buffer.data(), size));
493}
494
495void
496Sdp::createLocalSession(SdpDirection direction)
497{
498 sdpDirection_ = direction;
499 localSession_ = PJ_POOL_ZALLOC_T(memPool_.get(), pjmedia_sdp_session);
500 localSession_->conn = PJ_POOL_ZALLOC_T(memPool_.get(), pjmedia_sdp_conn);
501
502 /* Initialize the fields of the struct */
503 localSession_->origin.version = 0;
506
507 localSession_->origin.user = *pj_gethostname();
508
509 // Use Network Time Protocol format timestamp to ensure uniqueness.
510 localSession_->origin.id = tv.sec + 2208988800UL;
511 localSession_->origin.net_type = sip_utils::CONST_PJ_STR("IN");
512 if (publishedIpAddrType_ == pj_AF_INET6())
513 localSession_->origin.addr_type = sip_utils::CONST_PJ_STR("IP6");
514 else
515 localSession_->origin.addr_type = sip_utils::CONST_PJ_STR("IP4");
516 localSession_->origin.addr = sip_utils::CONST_PJ_STR(publishedIpAddr_);
517
518 // Use the call IDs for s= line
519 localSession_->name = sip_utils::CONST_PJ_STR(sessionName_);
520
521 localSession_->conn->net_type = localSession_->origin.net_type;
522 localSession_->conn->addr_type = localSession_->origin.addr_type;
523 localSession_->conn->addr = localSession_->origin.addr;
524
525 // RFC 3264: An offer/answer model session description protocol
526 // As the session is created and destroyed through an external signaling mean (SIP), the line
527 // should have a value of "0 0".
528 localSession_->time.start = 0;
529 localSession_->time.stop = 0;
530}
531
532int
533Sdp::validateSession() const
534{
535 return pjmedia_sdp_validate(localSession_);
536}
537
538bool
539Sdp::createOffer(const std::vector<MediaAttribute>& mediaList)
540{
541 if (mediaList.size() >= PJMEDIA_MAX_SDP_MEDIA) {
542 throw SdpException("Media list size exceeds SDP media maximum size");
543 }
544 JAMI_DEBUG("Creating SDP offer with {} media", mediaList.size());
545
546 createLocalSession(SdpDirection::OFFER);
547
548 if (validateSession() != PJ_SUCCESS) {
549 JAMI_ERR("Failed to create initial offer");
550 return false;
551 }
552
553 localSession_->media_count = 0;
554
555 for (auto const& media : mediaList) {
556 if (media.enabled_) {
557 localSession_->media[localSession_->media_count++] = addMediaDescription(media);
558 }
559 }
560
561 if (validateSession() != PJ_SUCCESS) {
562 JAMI_ERR("Failed to add medias");
563 return false;
564 }
565
566 if (pjmedia_sdp_neg_create_w_local_offer(memPool_.get(), localSession_, &negotiator_)
567 != PJ_SUCCESS) {
568 JAMI_ERR("Failed to create an initial SDP negotiator");
569 return false;
570 }
571
572 printSession(localSession_, "Local session (initial):", sdpDirection_);
573
574 return true;
575}
576
577void
579{
580 if (remote == nullptr) {
581 JAMI_ERR("Remote session is NULL");
582 return;
583 }
584 remoteSession_ = pjmedia_sdp_session_clone(memPool_.get(), remote);
585}
586
587bool
588Sdp::processIncomingOffer(const std::vector<MediaAttribute>& mediaList)
589{
590 if (not remoteSession_)
591 return false;
592
593 JAMI_DEBUG("Processing received offer for [{:s}] with {:d} media",
594 sessionName_,
595 mediaList.size());
596
597 printSession(remoteSession_, "Remote session:", SdpDirection::OFFER);
598
599 createLocalSession(SdpDirection::ANSWER);
600 if (validateSession() != PJ_SUCCESS) {
601 JAMI_ERR("Failed to create local session");
602 return false;
603 }
604
605 localSession_->media_count = 0;
606
607 for (auto const& media : mediaList) {
608 if (media.enabled_) {
609 localSession_->media[localSession_->media_count++] = addMediaDescription(media);
610 }
611 }
612
613 printSession(localSession_, "Local session:\n", sdpDirection_);
614
615 if (validateSession() != PJ_SUCCESS) {
616 JAMI_ERR("Failed to add medias");
617 return false;
618 }
619
620 if (pjmedia_sdp_neg_create_w_remote_offer(memPool_.get(),
621 localSession_,
622 remoteSession_,
623 &negotiator_)
624 != PJ_SUCCESS) {
625 JAMI_ERR("Failed to initialize media negotiation");
626 return false;
627 }
628
629 return true;
630}
631
632bool
634{
635 JAMI_DBG("Starting media negotiation for [%s]", sessionName_.c_str());
636
637 if (negotiator_ == NULL) {
638 JAMI_ERR("Unable to start negotiation with invalid negotiator");
639 return false;
640 }
641
644
646 JAMI_WARN("Negotiator not in right state for negotiation");
647 return false;
648 }
649
650 if (pjmedia_sdp_neg_negotiate(memPool_.get(), negotiator_, 0) != PJ_SUCCESS) {
651 JAMI_ERR("Failed to start media negotiation");
652 return false;
653 }
654
656 JAMI_ERR("Unable to retrieve local active session");
657
659
660 if (active_local != nullptr) {
661 printSession(active_local, "Local active session:", sdpDirection_);
662 }
663
665 or active_remote == nullptr) {
666 JAMI_ERR("Unable to retrieve remote active session");
667 return false;
668 }
669
671
672 printSession(active_remote, "Remote active session:", sdpDirection_);
673
674 return true;
675}
676
677std::string
678Sdp::getFilteredSdp(const pjmedia_sdp_session* session, unsigned media_keep, unsigned pt_keep)
679{
680 static constexpr size_t BUF_SZ = 4096;
681 std::unique_ptr<pj_pool_t, decltype(pj_pool_release)&>
682 tmpPool_(pj_pool_create(&Manager::instance().sipVoIPLink().getCachingPool()->factory,
683 "tmpSdp",
684 BUF_SZ,
685 BUF_SZ,
686 nullptr),
689 if (!cloned) {
690 JAMI_ERR("Unable to clone SDP");
691 return "";
692 }
693
694 // deactivate non-video media
695 bool hasKeep = false;
696 for (unsigned i = 0; i < cloned->media_count; i++)
697 if (i != media_keep) {
699 JAMI_ERR("Unable to deactivate media");
700 } else {
701 hasKeep = true;
702 }
703
704 if (not hasKeep) {
705 JAMI_DBG("No media to keep present in SDP");
706 return "";
707 }
708
709 // Leaking medias will be dropped with tmpPool_
710 for (unsigned i = 0; i < cloned->media_count; i++)
711 if (cloned->media[i]->desc.port == 0) {
712 std::move(cloned->media + i + 1, cloned->media + cloned->media_count, cloned->media + i);
713 cloned->media_count--;
714 i--;
715 }
716
717 for (unsigned i = 0; i < cloned->media_count; i++) {
718 auto media = cloned->media[i];
719
720 // filter other codecs
721 for (unsigned c = 0; c < media->desc.fmt_count; c++) {
722 auto& pt = media->desc.fmt[c];
723 if (pj_strtoul(&pt) == pt_keep)
724 continue;
725
726 while (auto attr = pjmedia_sdp_attr_find2(media->attr_count, media->attr, "rtpmap", &pt))
727 pjmedia_sdp_attr_remove(&media->attr_count, media->attr, attr);
728
729 while (auto attr = pjmedia_sdp_attr_find2(media->attr_count, media->attr, "fmt", &pt))
730 pjmedia_sdp_attr_remove(&media->attr_count, media->attr, attr);
731
732 std::move(media->desc.fmt + c + 1,
733 media->desc.fmt + media->desc.fmt_count,
734 media->desc.fmt + c);
735 media->desc.fmt_count--;
736 c--;
737 }
738
739 // we handle crypto ourselfs, don't tell libav about it
741 }
742
743 char buffer[BUF_SZ];
744 size_t size = pjmedia_sdp_print(cloned, buffer, sizeof(buffer));
745 string sessionStr(buffer, std::min(size, sizeof(buffer)));
746
747 return sessionStr;
748}
749
750std::vector<MediaDescription>
752{
753 if (remote)
754 return getMediaDescriptions(activeRemoteSession_, true);
755
756 return getMediaDescriptions(activeLocalSession_, false);
757}
758
759std::vector<MediaDescription>
761{
762 if (!session)
763 return {};
764 static constexpr pj_str_t STR_RTPMAP {sip_utils::CONST_PJ_STR("rtpmap")};
765 static constexpr pj_str_t STR_FMTP {sip_utils::CONST_PJ_STR("fmtp")};
766
767 std::vector<MediaDescription> ret;
768 for (unsigned i = 0; i < session->media_count; i++) {
769 auto media = session->media[i];
770 ret.emplace_back(MediaDescription());
771 MediaDescription& descr = ret.back();
772 if (!pj_stricmp2(&media->desc.media, "audio"))
774 else if (!pj_stricmp2(&media->desc.media, "video"))
775 descr.type = MEDIA_VIDEO;
776 else
777 continue;
778
779 descr.enabled = media->desc.port;
780 if (!descr.enabled)
781 continue;
782
783 // get connection info
784 pjmedia_sdp_conn* conn = media->conn ? media->conn : session->conn;
785 if (not conn) {
786 JAMI_ERR("Unable to find connection information for media");
787 continue;
788 }
789 descr.addr = std::string_view(conn->addr.ptr, conn->addr.slen);
790 descr.addr.setPort(media->desc.port);
791
792 // Get the "rtcp" address from the SDP if present. Otherwise,
793 // infere it from endpoint (RTP) address.
794 auto attr = pjmedia_sdp_attr_find2(media->attr_count, media->attr, "rtcp", NULL);
795 if (attr) {
797 auto status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
798 if (status == PJ_SUCCESS && rtcp.addr.slen) {
799 descr.rtcp_addr = std::string_view(rtcp.addr.ptr, rtcp.addr.slen);
800 descr.rtcp_addr.setPort(rtcp.port);
801 }
802 }
803
804 descr.onHold = pjmedia_sdp_attr_find2(media->attr_count,
805 media->attr,
807 nullptr)
808 || pjmedia_sdp_attr_find2(media->attr_count,
809 media->attr,
811 nullptr);
812
813 descr.direction_ = getMediaDirection(media);
814 if (descr.direction_ == MediaDirection::UNKNOWN) {
815 JAMI_ERR("Did not find media direction attribute in remote SDP");
816 }
817
818 // get codecs infos
819 for (unsigned j = 0; j < media->desc.fmt_count; j++) {
821 &STR_RTPMAP,
822 &media->desc.fmt[j]);
823 if (!rtpMapAttribute) {
824 JAMI_ERR("Unable to find rtpmap attribute");
825 descr.enabled = false;
826 continue;
827 }
830 || rtpmap.enc_name.slen == 0) {
831 JAMI_ERROR("Unable to find payload type {} in SDP",
832 sip_utils::as_view(media->desc.fmt[j]));
833 descr.enabled = false;
834 continue;
835 }
836 auto codec_raw = sip_utils::as_view(rtpmap.enc_name);
837 descr.rtp_clockrate = rtpmap.clock_rate;
838 descr.codec = findCodecBySpec(codec_raw, rtpmap.clock_rate);
839 if (not descr.codec) {
840 JAMI_ERROR("Unable to find codec {}", codec_raw);
841 descr.enabled = false;
842 continue;
843 }
844 descr.payload_type = pj_strtoul(&rtpmap.pt);
845 if (descr.type == MEDIA_VIDEO) {
847 &STR_FMTP,
848 &media->desc.fmt[j]);
849 // descr.bitrate = getOutgoingVideoField(codec, "bitrate");
850 if (fmtpAttr && fmtpAttr->value.ptr && fmtpAttr->value.slen) {
851 const auto& v = fmtpAttr->value;
852 descr.parameters = std::string(v.ptr, v.ptr + v.slen);
853 }
854 }
855 // for now, just keep the first codec only
856 descr.enabled = true;
857 break;
858 }
859
860 if (not remote)
861 descr.receiving_sdp = getFilteredSdp(session, i, descr.payload_type);
862
863 // get crypto info
864 std::vector<std::string> crypto;
865 for (unsigned j = 0; j < media->attr_count; j++) {
866 const auto attribute = media->attr[j];
867 if (pj_stricmp2(&attribute->name, "crypto") == 0)
868 crypto.emplace_back(attribute->value.ptr, attribute->value.slen);
869 }
870 descr.crypto = SdesNegotiator::negotiate(crypto);
871 }
872 return ret;
873}
874
875std::vector<Sdp::MediaSlot>
877{
878 auto loc = getMediaDescriptions(activeLocalSession_, false);
879 auto rem = getMediaDescriptions(activeRemoteSession_, true);
880 size_t slot_n = std::min(loc.size(), rem.size());
881 std::vector<MediaSlot> s;
882 s.reserve(slot_n);
883 for (decltype(slot_n) i = 0; i < slot_n; i++)
884 s.emplace_back(std::move(loc[i]), std::move(rem[i]));
885 return s;
886}
887
888void
889Sdp::addIceCandidates(unsigned media_index, const std::vector<std::string>& cands)
890{
891 if (media_index >= localSession_->media_count) {
892 JAMI_ERR("addIceCandidates failed: unable to access media#%u (may be deactivated)",
894 return;
895 }
896
897 auto media = localSession_->media[media_index];
898
899 for (const auto& item : cands) {
901 pjmedia_sdp_attr* attr = pjmedia_sdp_attr_create(memPool_.get(), "candidate", &val);
902
904 throw SdpException("Unable to add ICE candidates attribute to media");
905 }
906}
907
908std::vector<std::string>
910{
911 auto remoteSession = activeRemoteSession_ ? activeRemoteSession_ : remoteSession_;
912 auto localSession = activeLocalSession_ ? activeLocalSession_ : localSession_;
913 if (not remoteSession) {
914 JAMI_ERR("getIceCandidates failed: no remote session");
915 return {};
916 }
917 if (not localSession) {
918 JAMI_ERR("getIceCandidates failed: no local session");
919 return {};
920 }
921 if (media_index >= remoteSession->media_count || media_index >= localSession->media_count) {
922 JAMI_ERR("getIceCandidates failed: unable to access media#%u (may be deactivated)",
924 return {};
925 }
926 auto media = remoteSession->media[media_index];
927 auto localMedia = localSession->media[media_index];
928 if (media->desc.port == 0 || localMedia->desc.port == 0) {
929 JAMI_WARN("Media#%u is disabled. Media ports: local %u, remote %u",
931 localMedia->desc.port,
932 media->desc.port);
933 return {};
934 }
935
936 std::vector<std::string> candidates;
937
938 for (unsigned i = 0; i < media->attr_count; i++) {
940 if (pj_stricmp2(&attribute->name, "candidate") == 0)
941 candidates.push_back(std::string(attribute->value.ptr, attribute->value.slen));
942 }
943
944 return candidates;
945}
946
947void
948Sdp::addIceAttributes(const dhtnet::IceTransport::Attribute&& ice_attrs)
949{
951 pjmedia_sdp_attr* attr = pjmedia_sdp_attr_create(memPool_.get(), "ice-ufrag", &value);
952
953 if (pjmedia_sdp_attr_add(&localSession_->attr_count, localSession_->attr, attr) != PJ_SUCCESS)
954 throw SdpException("Unable to add ICE.ufrag attribute to local SDP");
955
957 attr = pjmedia_sdp_attr_create(memPool_.get(), "ice-pwd", &value);
958
959 if (pjmedia_sdp_attr_add(&localSession_->attr_count, localSession_->attr, attr) != PJ_SUCCESS)
960 throw SdpException("Unable to add ICE.pwd attribute to local SDP");
961}
962
963dhtnet::IceTransport::Attribute
965{
966 if (auto session = activeRemoteSession_ ? activeRemoteSession_ : remoteSession_)
968 return {};
969}
970
971dhtnet::IceTransport::Attribute
973{
974 dhtnet::IceTransport::Attribute ice_attrs;
975 // Per RFC8839, ice-ufrag/ice-pwd can be present either at
976 // media or session level.
977 // This seems to be the case for Asterisk servers (ICE is at media-session).
978 for (unsigned i = 0; i < session->attr_count; i++) {
980 if (pj_stricmp2(&attribute->name, "ice-ufrag") == 0)
981 ice_attrs.ufrag.assign(attribute->value.ptr, attribute->value.slen);
982 else if (pj_stricmp2(&attribute->name, "ice-pwd") == 0)
983 ice_attrs.pwd.assign(attribute->value.ptr, attribute->value.slen);
984 if (!ice_attrs.ufrag.empty() && !ice_attrs.pwd.empty())
985 return ice_attrs;
986 }
987 for (unsigned i = 0; i < session->media_count; i++) {
988 auto* media = session->media[i];
989 for (unsigned j = 0; j < media->attr_count; j++) {
991 if (pj_stricmp2(&attribute->name, "ice-ufrag") == 0)
992 ice_attrs.ufrag.assign(attribute->value.ptr, attribute->value.slen);
993 else if (pj_stricmp2(&attribute->name, "ice-pwd") == 0)
994 ice_attrs.pwd.assign(attribute->value.ptr, attribute->value.slen);
995 if (!ice_attrs.ufrag.empty() && !ice_attrs.pwd.empty())
996 return ice_attrs;
997 }
998 }
999
1000 return ice_attrs;
1001}
1002
1003void
1005{
1006 clearIce(localSession_);
1007 clearIce(remoteSession_);
1009 setActiveLocalSdpSession(nullptr);
1010}
1011
1012void
1014{
1015 if (not session)
1016 return;
1017 pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "ice-ufrag");
1018 pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "ice-pwd");
1019 // TODO. Why this? we should not have "candidate" attribute at session level.
1020 pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "candidate");
1021 for (unsigned i = 0; i < session->media_count; i++) {
1022 auto media = session->media[i];
1023 pjmedia_sdp_attr_remove_all(&media->attr_count, media->attr, "candidate");
1024 }
1025}
1026
1027std::vector<MediaAttribute>
1029{
1030 if (sdpSession == nullptr) {
1031 return {};
1032 }
1033
1034 std::vector<MediaAttribute> mediaList;
1035 unsigned audioIdx = 0;
1036 unsigned videoIdx = 0;
1037 for (unsigned idx = 0; idx < sdpSession->media_count; idx++) {
1038 mediaList.emplace_back(MediaAttribute {});
1039 auto& mediaAttr = mediaList.back();
1040
1041 auto const& media = sdpSession->media[idx];
1042
1043 // Get media type.
1044 if (!pj_stricmp2(&media->desc.media, "audio"))
1046 else if (!pj_stricmp2(&media->desc.media, "video"))
1048 else {
1049 JAMI_WARN("Media#%u only 'audio' and 'video' types are supported!", idx);
1050 // Disable the media. No need to parse the attributes.
1051 mediaAttr.enabled_ = false;
1052 continue;
1053 }
1054
1055 // Set enabled flag
1056 mediaAttr.enabled_ = media->desc.port > 0;
1057
1058 if (!mediaAttr.enabled_ && ignoreDisabled) {
1059 mediaList.pop_back();
1060 continue;
1061 }
1062
1063 // Get mute state.
1064 auto direction = getMediaDirection(media);
1067
1068 // Get transport.
1069 auto transp = getMediaTransport(media);
1071 JAMI_WARN("Media#%u is unable to determine transport type!", idx);
1072 }
1073
1074 // A media is secure if the transport is of type RTP/SAVP
1075 // and the crypto materials are present.
1076 mediaAttr.secure_ = transp == MediaTransport::RTP_SAVP and not getCrypto(media).empty();
1077
1078 if (mediaAttr.type_ == MediaType::MEDIA_AUDIO) {
1079 mediaAttr.label_ = "audio_" + std::to_string(audioIdx++);
1080 } else if (mediaAttr.type_ == MediaType::MEDIA_VIDEO) {
1081 mediaAttr.label_ = "video_" + std::to_string(videoIdx++);
1082 }
1083 }
1084
1085 return mediaList;
1086}
1087
1088} // namespace jami
static LIBJAMI_TEST_EXPORT Manager & instance()
Definition manager.cpp:676
static void releasePort(uint16_t port) noexcept
static CryptoAttribute negotiate(const std::vector< std::string > &attributes)
void setReceivedOffer(const pjmedia_sdp_session *remote)
Definition sdp.cpp:578
bool processIncomingOffer(const std::vector< MediaAttribute > &mediaList)
Build a new SDP answer using mediaList.
Definition sdp.cpp:588
std::vector< MediaDescription > getActiveMediaDescription(bool remote) const
Definition sdp.cpp:751
void addIceCandidates(unsigned media_index, const std::vector< std::string > &cands)
Definition sdp.cpp:889
bool startNegotiation()
Start the sdp negotiation.
Definition sdp.cpp:633
void setLocalMediaCapabilities(MediaType type, const std::vector< std::shared_ptr< SystemCodecInfo > > &selectedCodecs)
Set the local media capabilities.
Definition sdp.cpp:420
std::vector< MediaDescription > getMediaDescriptions(const pjmedia_sdp_session *session, bool remote) const
Definition sdp.cpp:760
bool createOffer(const std::vector< MediaAttribute > &mediaList)
Definition sdp.cpp:539
~Sdp()
Definition sdp.cpp:72
static std::vector< MediaAttribute > getMediaAttributeListFromSdp(const pjmedia_sdp_session *sdpSession, bool ignoreDisabled=false)
Definition sdp.cpp:1028
std::vector< std::string > getIceCandidates(unsigned media_index) const
Definition sdp.cpp:909
void setPublishedIP(const std::string &addr, pj_uint16_t addr_type=pj_AF_UNSPEC())
Definition sdp.cpp:374
void addIceAttributes(const dhtnet::IceTransport::Attribute &&ice_attrs)
Definition sdp.cpp:948
dhtnet::IceTransport::Attribute getIceAttributes() const
Definition sdp.cpp:964
void setActiveLocalSdpSession(const pjmedia_sdp_session *sdp)
Set the negotiated sdp offer from the sip payload.
Definition sdp.cpp:124
Sdp(const std::string &id)
Definition sdp.cpp:56
static constexpr std::string_view getSdpDirectionStr(SdpDirection direction)
Definition sdp.cpp:452
void clearIce()
Definition sdp.cpp:1004
void setActiveRemoteSdpSession(const pjmedia_sdp_session *sdp)
Retrieve the negotiated sdp offer from the sip payload.
Definition sdp.cpp:132
static void printSession(const pjmedia_sdp_session *session, const char *header, SdpDirection direction)
Log the given session.
Definition sdp.cpp:462
std::vector< MediaSlot > getMediaSlots() const
Definition sdp.cpp:876
direction
0: outgoing, 1: incoming
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_ERROR(formatstr,...)
Definition logger.h:228
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_DEBUG(formatstr,...)
Definition logger.h:226
#define JAMI_WARN(...)
Definition logger.h:217
#define JAMI_LOG(formatstr,...)
Definition logger.h:225
std::string encode(std::string_view dat)
Definition base64.cpp:28
int64_t size(const std::filesystem::path &path)
const char *const DEFAULT_H264_PROFILE_LEVEL_ID
Definition libav_utils.h:40
constexpr std::string_view as_view(const pj_str_t &str) noexcept
Definition sip_utils.h:107
constexpr const pj_str_t CONST_PJ_STR(T(&a)[N]) noexcept
Definition sip_utils.h:89
static std::map< MediaDirection, const char * > DIRECTION_STR
Definition sdp.cpp:50
void emitSignal(Args... args)
Definition ring_signal.h:64
MediaTransport
static std::vector< CryptoSuiteDefinition > CryptoSuites
List of accepted Crypto-Suites as defined in RFC4568 (6.2)
SdpDirection
Definition sdp.h:58
MediaDirection
static constexpr int POOL_INITIAL_SIZE
Definition sdp.cpp:47
static constexpr int POOL_INCREMENT_SIZE
Definition sdp.cpp:48
@ MEDIA_AUDIO
Definition media_codec.h:47
@ MEDIA_VIDEO
Definition media_codec.h:48
static void randomFill(std::vector< uint8_t > &dest)
Definition sdp.cpp:116
A SIP Account specify SIP specific functions and object = SIPCall/SIPVoIPLink)
MediaDescription Negotiated RTP media slot.
MediaType type
Audio / video.