LCOV - code coverage report
Current view: top level - src/sip - sdp.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 497 592 84.0 %
Date: 2024-04-26 09:41:19 Functions: 43 44 97.7 %

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

Generated by: LCOV version 1.14