LCOV - code coverage report
Current view: top level - src/sip - sdp.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 82.0 % 571 468
Test Date: 2026-07-06 08:25:38 Functions: 49.1 % 112 55

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

Generated by: LCOV version 2.0-1