LCOV - code coverage report
Current view: top level - src/sip - sdp.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 495 592 83.6 %
Date: 2024-11-15 09:04:49 Functions: 43 44 97.7 %

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

Generated by: LCOV version 1.14