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-06-13 09:18:46 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          371 : Sdp::Sdp(const std::string& id)
      55          742 :     : memPool_(nullptr, [](pj_pool_t* pool) { pj_pool_release(pool); })
      56          371 :     , publishedIpAddr_()
      57          371 :     , publishedIpAddrType_()
      58          371 :     , telephoneEventPayload_(101) // same as asterisk
      59          742 :     , sessionName_("Call ID " + id)
      60              : {
      61          371 :     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          371 :     if (not memPool_)
      67            0 :         throw std::runtime_error("pj_pool_create() failed");
      68          371 : }
      69              : 
      70          371 : Sdp::~Sdp()
      71              : {
      72          371 :     SIPAccount::releasePort(localAudioRtpPort_);
      73              : #ifdef ENABLE_VIDEO
      74          371 :     SIPAccount::releasePort(localVideoRtpPort_);
      75              : #endif
      76          371 : }
      77              : 
      78              : std::shared_ptr<SystemCodecInfo>
      79          734 : Sdp::findCodecBySpec(std::string_view codec, const unsigned clockrate) const
      80              : {
      81              :     // TODO : only manage a list?
      82         1218 :     for (const auto& accountCodec : audio_codec_list_) {
      83          888 :         auto audioCodecInfo = std::static_pointer_cast<SystemAudioCodecInfo>(accountCodec);
      84          888 :         if (audioCodecInfo->name == codec
      85         1292 :             and (audioCodecInfo->isPCMG722() ? (clockrate == 8000)
      86          404 :                                              : (audioCodecInfo->audioformat.sample_rate == clockrate)))
      87          404 :             return accountCodec;
      88          888 :     }
      89              : 
      90          330 :     for (const auto& accountCodec : video_codec_list_) {
      91          330 :         if (accountCodec->name == codec)
      92          330 :             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          437 : randomFill(std::vector<uint8_t>& dest)
     115              : {
     116          437 :     std::uniform_int_distribution<int> rand_byte {0, std::numeric_limits<uint8_t>::max()};
     117          437 :     std::random_device rdev;
     118          437 :     std::generate(dest.begin(), dest.end(), std::bind(rand_byte, std::ref(rdev)));
     119          437 : }
     120              : 
     121              : void
     122          661 : Sdp::setActiveLocalSdpSession(const pjmedia_sdp_session* sdp)
     123              : {
     124          661 :     if (activeLocalSession_ != sdp)
     125         1896 :         JAMI_LOG("Set active local session to [{}]. Was [{}]", fmt::ptr(sdp), fmt::ptr(activeLocalSession_));
     126          661 :     activeLocalSession_ = sdp;
     127          661 : }
     128              : 
     129              : void
     130          661 : Sdp::setActiveRemoteSdpSession(const pjmedia_sdp_session* sdp)
     131              : {
     132          661 :     if (activeLocalSession_ != sdp)
     133         1192 :         JAMI_LOG("Set active remote session to [{}]. Was [{}]", fmt::ptr(sdp), fmt::ptr(activeRemoteSession_));
     134          661 :     activeRemoteSession_ = sdp;
     135          661 : }
     136              : 
     137              : pjmedia_sdp_attr*
     138          437 : Sdp::generateSdesAttribute()
     139              : {
     140              :     static constexpr const unsigned cryptoSuite = 0;
     141          437 :     std::vector<uint8_t> keyAndSalt;
     142          437 :     keyAndSalt.resize(jami::CryptoSuites[cryptoSuite].masterKeyLength / 8
     143          437 :                       + jami::CryptoSuites[cryptoSuite].masterSaltLength / 8);
     144              :     // generate keys
     145          437 :     randomFill(keyAndSalt);
     146              : 
     147          437 :     std::string crypto_attr = "1 "s + jami::CryptoSuites[cryptoSuite].name + " inline:" + base64::encode(keyAndSalt);
     148          437 :     pj_str_t val {sip_utils::CONST_PJ_STR(crypto_attr)};
     149          874 :     return pjmedia_sdp_attr_create(memPool_.get(), "crypto", &val);
     150          437 : }
     151              : 
     152              : char const*
     153          438 : Sdp::mediaDirection(const MediaAttribute& mediaAttr)
     154              : {
     155          438 :     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          438 :     if (mediaAttr.type_ == MediaType::MEDIA_AUDIO) {
     163          243 :         return DIRECTION_STR[MediaDirection::SENDRECV];
     164              :     }
     165              : 
     166          195 :     if (mediaAttr.muted_) {
     167            8 :         if (mediaAttr.hold_) {
     168            0 :             return DIRECTION_STR[MediaDirection::INACTIVE];
     169              :         }
     170            8 :         return DIRECTION_STR[MediaDirection::RECVONLY];
     171              :     }
     172              : 
     173          187 :     if (mediaAttr.hold_) {
     174            5 :         return DIRECTION_STR[MediaDirection::SENDONLY];
     175              :     }
     176              : 
     177          182 :     return DIRECTION_STR[MediaDirection::SENDRECV];
     178              : }
     179              : 
     180              : MediaDirection
     181         1284 : Sdp::getMediaDirection(pjmedia_sdp_media* media)
     182              : {
     183         1284 :     if (pjmedia_sdp_attr_find2(media->attr_count, media->attr, DIRECTION_STR[MediaDirection::SENDONLY], nullptr)
     184         1284 :         != nullptr) {
     185           19 :         return MediaDirection::SENDONLY;
     186              :     }
     187              : 
     188         1265 :     if (pjmedia_sdp_attr_find2(media->attr_count, media->attr, DIRECTION_STR[MediaDirection::RECVONLY], nullptr)
     189         1265 :         != nullptr) {
     190           25 :         return MediaDirection::RECVONLY;
     191              :     }
     192              : 
     193         1240 :     if (pjmedia_sdp_attr_find2(media->attr_count, media->attr, DIRECTION_STR[MediaDirection::INACTIVE], nullptr)
     194         1240 :         != nullptr) {
     195            6 :         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         1234 :     return MediaDirection::SENDRECV;
     201              : }
     202              : 
     203              : MediaTransport
     204          550 : Sdp::getMediaTransport(pjmedia_sdp_media* media)
     205              : {
     206          550 :     if (pj_stricmp2(&media->desc.transport, "RTP/SAVP") == 0)
     207          549 :         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          549 : Sdp::getCrypto(pjmedia_sdp_media* media)
     216              : {
     217          549 :     std::vector<std::string> crypto;
     218        10921 :     for (unsigned j = 0; j < media->attr_count; j++) {
     219        10372 :         auto* const attribute = media->attr[j];
     220        10372 :         if (pj_stricmp2(&attribute->name, "crypto") == 0)
     221          547 :             crypto.emplace_back(attribute->value.ptr, attribute->value.slen);
     222              :     }
     223              : 
     224          549 :     return crypto;
     225            0 : }
     226              : 
     227              : pjmedia_sdp_media*
     228          438 : Sdp::addMediaDescription(const MediaAttribute& mediaAttr)
     229              : {
     230          438 :     auto type = mediaAttr.type_;
     231          438 :     auto secure = mediaAttr.secure_;
     232              : 
     233         1752 :     JAMI_LOG("Add media description [{}]", mediaAttr.toString(true));
     234              : 
     235          438 :     pjmedia_sdp_media* med = PJ_POOL_ZALLOC_T(memPool_.get(), pjmedia_sdp_media);
     236              : 
     237          438 :     switch (type) {
     238          243 :     case MediaType::MEDIA_AUDIO:
     239          243 :         med->desc.media = sip_utils::CONST_PJ_STR("audio");
     240          243 :         med->desc.port = mediaAttr.enabled_ ? localAudioRtpPort_ : 0;
     241          243 :         med->desc.fmt_count = audio_codec_list_.size();
     242          243 :         break;
     243          195 :     case MediaType::MEDIA_VIDEO:
     244          195 :         med->desc.media = sip_utils::CONST_PJ_STR("video");
     245          195 :         med->desc.port = mediaAttr.enabled_ ? localVideoRtpPort_ : 0;
     246          195 :         med->desc.fmt_count = video_codec_list_.size();
     247          195 :         break;
     248            0 :     default:
     249            0 :         throw SdpException("Unsupported media type! Only audio and video are supported");
     250              :         break;
     251              :     }
     252              : 
     253          438 :     med->desc.port_count = 1;
     254              : 
     255              :     // Set the transport protocol of the media
     256          438 :     med->desc.transport = secure ? sip_utils::CONST_PJ_STR("RTP/SAVP") : sip_utils::CONST_PJ_STR("RTP/AVP");
     257              : 
     258          438 :     unsigned dynamic_payload = 96;
     259              : 
     260         1268 :     for (unsigned i = 0; i < med->desc.fmt_count; i++) {
     261              :         pjmedia_sdp_rtpmap rtpmap;
     262          830 :         rtpmap.param.slen = 0;
     263              : 
     264          830 :         std::string channels; // must have the lifetime of rtpmap
     265          830 :         std::string enc_name;
     266              :         unsigned payload;
     267              : 
     268          830 :         if (type == MediaType::MEDIA_AUDIO) {
     269          418 :             auto accountAudioCodec = std::static_pointer_cast<SystemAudioCodecInfo>(audio_codec_list_[i]);
     270          418 :             payload = accountAudioCodec->payloadType;
     271          418 :             enc_name = accountAudioCodec->name;
     272              : 
     273          418 :             if (accountAudioCodec->audioformat.nb_channels > 1) {
     274          243 :                 channels = std::to_string(accountAudioCodec->audioformat.nb_channels);
     275          243 :                 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          418 :             if (accountAudioCodec->isPCMG722())
     280           25 :                 rtpmap.clock_rate = 8000;
     281              :             else
     282          393 :                 rtpmap.clock_rate = accountAudioCodec->audioformat.sample_rate;
     283              : 
     284          418 :         } else {
     285              :             // FIXME: get this key from header
     286          412 :             payload = dynamic_payload++;
     287          412 :             enc_name = video_codec_list_[i]->name;
     288          412 :             rtpmap.clock_rate = 90000;
     289              :         }
     290              : 
     291          830 :         auto payloadStr = std::to_string(payload);
     292          830 :         auto pjPayload = sip_utils::CONST_PJ_STR(payloadStr);
     293          830 :         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          830 :         rtpmap.pt = med->desc.fmt[i];
     299          830 :         rtpmap.enc_name = sip_utils::CONST_PJ_STR(enc_name);
     300              : 
     301              :         pjmedia_sdp_attr* attr;
     302          830 :         pjmedia_sdp_rtpmap_to_attr(memPool_.get(), &rtpmap, &attr);
     303          830 :         med->attr[med->attr_count++] = attr;
     304              : 
     305              : #ifdef ENABLE_VIDEO
     306          830 :         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          195 :             const auto accountVideoCodec = std::static_pointer_cast<SystemVideoCodecInfo>(video_codec_list_[i]);
     310          195 :             const auto& profileLevelID = accountVideoCodec->parameters.empty()
     311          195 :                                              ? libav_utils::DEFAULT_H264_PROFILE_LEVEL_ID
     312          390 :                                              : accountVideoCodec->parameters;
     313          195 :             auto value = fmt::format("fmtp:{} {}", payload, profileLevelID);
     314          195 :             med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_.get(), value.c_str(), NULL);
     315          195 :         }
     316              : #endif
     317          830 :     }
     318              : 
     319          438 :     if (type == MediaType::MEDIA_AUDIO) {
     320          243 :         setTelephoneEventRtpmap(med);
     321          243 :         if (localAudioRtcpPort_) {
     322          243 :             addRTCPAttribute(med, localAudioRtcpPort_);
     323              :         }
     324          195 :     } else if (type == MediaType::MEDIA_VIDEO and localVideoRtcpPort_) {
     325          195 :         addRTCPAttribute(med, localVideoRtcpPort_);
     326              :     }
     327              : 
     328          438 :     char const* direction = mediaDirection(mediaAttr);
     329              : 
     330          438 :     med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_.get(), direction, NULL);
     331              : 
     332          438 :     if (secure) {
     333          437 :         if (pjmedia_sdp_media_add_attr(med, generateSdesAttribute()) != PJ_SUCCESS)
     334            0 :             throw SdpException("Unable to add sdes attribute to media");
     335              :     }
     336              : 
     337          438 :     return med;
     338              : }
     339              : 
     340              : void
     341          438 : Sdp::addRTCPAttribute(pjmedia_sdp_media* med, uint16_t port)
     342              : {
     343          438 :     dhtnet::IpAddr addr {publishedIpAddr_};
     344          438 :     addr.setPort(port);
     345          438 :     pjmedia_sdp_attr* attr = pjmedia_sdp_attr_create_rtcp(memPool_.get(), addr.pjPtr());
     346          438 :     if (attr)
     347          438 :         pjmedia_sdp_attr_add(&med->attr_count, med->attr, attr);
     348          438 : }
     349              : 
     350              : void
     351          202 : Sdp::setPublishedIP(const std::string& addr, pj_uint16_t addr_type)
     352              : {
     353          202 :     publishedIpAddr_ = addr;
     354          202 :     publishedIpAddrType_ = addr_type;
     355          202 :     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          202 : }
     366              : 
     367              : void
     368          202 : Sdp::setPublishedIP(const dhtnet::IpAddr& ip_addr)
     369              : {
     370          202 :     setPublishedIP(ip_addr, ip_addr.getFamily());
     371          202 : }
     372              : 
     373              : void
     374          243 : Sdp::setTelephoneEventRtpmap(pjmedia_sdp_media* med)
     375              : {
     376          243 :     ++med->desc.fmt_count;
     377          243 :     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          243 :         pj_pool_zalloc(memPool_.get(), sizeof(pjmedia_sdp_attr)));
     381          243 :     attr_rtpmap->name = sip_utils::CONST_PJ_STR("rtpmap");
     382          243 :     attr_rtpmap->value = sip_utils::CONST_PJ_STR("101 telephone-event/8000");
     383              : 
     384          243 :     med->attr[med->attr_count++] = attr_rtpmap;
     385              : 
     386              :     pjmedia_sdp_attr* attr_fmtp = static_cast<pjmedia_sdp_attr*>(
     387          243 :         pj_pool_zalloc(memPool_.get(), sizeof(pjmedia_sdp_attr)));
     388          243 :     attr_fmtp->name = sip_utils::CONST_PJ_STR("fmtp");
     389          243 :     attr_fmtp->value = sip_utils::CONST_PJ_STR("101 0-15");
     390              : 
     391          243 :     med->attr[med->attr_count++] = attr_fmtp;
     392          243 : }
     393              : 
     394              : void
     395          742 : Sdp::setLocalMediaCapabilities(MediaType type, const std::vector<std::shared_ptr<SystemCodecInfo>>& selectedCodecs)
     396              : {
     397          742 :     switch (type) {
     398          371 :     case MediaType::MEDIA_AUDIO:
     399          371 :         audio_codec_list_ = selectedCodecs;
     400          371 :         break;
     401              : 
     402          371 :     case MediaType::MEDIA_VIDEO:
     403              : #ifdef ENABLE_VIDEO
     404          371 :         video_codec_list_ = selectedCodecs;
     405              :         // Do not expose H265 if accel is disactivated
     406          371 :         if (not jami::Manager::instance().videoPreferences.getEncodingAccelerated()) {
     407          371 :             video_codec_list_.erase(std::remove_if(video_codec_list_.begin(),
     408              :                                                    video_codec_list_.end(),
     409          782 :                                                    [](const std::shared_ptr<SystemCodecInfo>& i) {
     410          782 :                                                        return i->name == "H265";
     411              :                                                    }),
     412          742 :                                     video_codec_list_.end());
     413              :         }
     414              : #else
     415              :         (void) selectedCodecs;
     416              : #endif
     417          371 :         break;
     418              : 
     419            0 :     default:
     420            0 :         throw SdpException("Unsupported media type");
     421              :         break;
     422              :     }
     423          742 : }
     424              : 
     425              : constexpr std::string_view
     426          878 : Sdp::getSdpDirectionStr(SdpDirection direction)
     427              : {
     428          878 :     if (direction == SdpDirection::OFFER)
     429          485 :         return "OFFER"sv;
     430          393 :     if (direction == SdpDirection::ANSWER)
     431          393 :         return "ANSWER"sv;
     432            0 :     return "NONE"sv;
     433              : }
     434              : 
     435              : void
     436          878 : Sdp::printSession(const pjmedia_sdp_session* session, const char* header, SdpDirection direction)
     437              : {
     438              :     static constexpr size_t BUF_SZ = 4095;
     439          878 :     sip_utils::PoolPtr tmpPool_(pj_pool_create(&Manager::instance().sipVoIPLink().getCachingPool()->factory,
     440              :                                                "printSdp",
     441              :                                                BUF_SZ,
     442              :                                                BUF_SZ,
     443          878 :                                                nullptr));
     444              : 
     445          878 :     auto* cloned_session = pjmedia_sdp_session_clone(tmpPool_.get(), session);
     446          878 :     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         2502 :     for (unsigned i = 0; i < cloned_session->media_count; ++i) {
     453         1624 :         pjmedia_sdp_media_remove_all_attr(cloned_session->media[i], "crypto");
     454              :     }
     455              : 
     456              :     std::array<char, BUF_SZ + 1> buffer;
     457          878 :     auto size = pjmedia_sdp_print(cloned_session, buffer.data(), BUF_SZ);
     458          878 :     if (size < 0) {
     459            0 :         JAMI_ERROR("SDP too big for dump: {}", header);
     460            0 :         return;
     461              :     }
     462              : 
     463         4390 :     JAMI_LOG("[SDP {}] {}\n{:s}", getSdpDirectionStr(direction), header, std::string_view(buffer.data(), size));
     464          878 : }
     465              : 
     466              : void
     467          241 : Sdp::createLocalSession(SdpDirection direction)
     468              : {
     469          241 :     sdpDirection_ = direction;
     470          241 :     localSession_ = PJ_POOL_ZALLOC_T(memPool_.get(), pjmedia_sdp_session);
     471          241 :     localSession_->conn = PJ_POOL_ZALLOC_T(memPool_.get(), pjmedia_sdp_conn);
     472              : 
     473              :     /* Initialize the fields of the struct */
     474          241 :     localSession_->origin.version = 0;
     475              :     pj_time_val tv;
     476          241 :     pj_gettimeofday(&tv);
     477              : 
     478          241 :     localSession_->origin.user = *pj_gethostname();
     479              : 
     480              :     // Use Network Time Protocol format timestamp to ensure uniqueness.
     481          241 :     localSession_->origin.id = tv.sec + 2208988800UL;
     482          241 :     localSession_->origin.net_type = sip_utils::CONST_PJ_STR("IN");
     483          241 :     if (publishedIpAddrType_ == pj_AF_INET6())
     484            0 :         localSession_->origin.addr_type = sip_utils::CONST_PJ_STR("IP6");
     485              :     else
     486          241 :         localSession_->origin.addr_type = sip_utils::CONST_PJ_STR("IP4");
     487          241 :     localSession_->origin.addr = sip_utils::CONST_PJ_STR(publishedIpAddr_);
     488              : 
     489              :     // Use the call IDs for s= line
     490          241 :     localSession_->name = sip_utils::CONST_PJ_STR(sessionName_);
     491              : 
     492          241 :     localSession_->conn->net_type = localSession_->origin.net_type;
     493          241 :     localSession_->conn->addr_type = localSession_->origin.addr_type;
     494          241 :     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          241 :     localSession_->time.start = 0;
     500          241 :     localSession_->time.stop = 0;
     501          241 : }
     502              : 
     503              : int
     504          482 : Sdp::validateSession() const
     505              : {
     506          482 :     return pjmedia_sdp_validate(localSession_);
     507              : }
     508              : 
     509              : bool
     510          126 : Sdp::createOffer(const std::vector<MediaAttribute>& mediaList)
     511              : {
     512          126 :     if (mediaList.size() >= PJMEDIA_MAX_SDP_MEDIA) {
     513            0 :         throw SdpException("Media list size exceeds SDP media maximum size");
     514              :     }
     515          504 :     JAMI_DEBUG("Creating SDP offer with {} media", mediaList.size());
     516              : 
     517          126 :     createLocalSession(SdpDirection::OFFER);
     518              : 
     519          126 :     if (validateSession() != PJ_SUCCESS) {
     520            0 :         JAMI_ERROR("Failed to create initial offer");
     521            0 :         return false;
     522              :     }
     523              : 
     524          126 :     localSession_->media_count = 0;
     525              : 
     526          356 :     for (auto const& media : mediaList) {
     527          230 :         if (media.enabled_) {
     528          230 :             localSession_->media[localSession_->media_count++] = addMediaDescription(media);
     529              :         }
     530              :     }
     531              : 
     532          126 :     if (validateSession() != PJ_SUCCESS) {
     533            0 :         JAMI_ERROR("Failed to add medias");
     534            0 :         return false;
     535              :     }
     536              : 
     537          126 :     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          126 :     printSession(localSession_, "Local session (initial):", sdpDirection_);
     543              : 
     544          126 :     return true;
     545              : }
     546              : 
     547              : void
     548          125 : Sdp::setReceivedOffer(const pjmedia_sdp_session* remote)
     549              : {
     550          125 :     if (remote == nullptr) {
     551            0 :         JAMI_ERROR("Remote session is NULL");
     552            0 :         return;
     553              :     }
     554          125 :     remoteSession_ = pjmedia_sdp_session_clone(memPool_.get(), remote);
     555              : }
     556              : 
     557              : bool
     558          115 : Sdp::processIncomingOffer(const std::vector<MediaAttribute>& mediaList)
     559              : {
     560          115 :     if (not remoteSession_)
     561            0 :         return false;
     562              : 
     563          460 :     JAMI_DEBUG("Processing received offer for [{:s}] with {:d} media", sessionName_, mediaList.size());
     564              : 
     565          115 :     printSession(remoteSession_, "Remote session:", SdpDirection::OFFER);
     566              : 
     567          115 :     createLocalSession(SdpDirection::ANSWER);
     568          115 :     if (validateSession() != PJ_SUCCESS) {
     569            0 :         JAMI_ERROR("Failed to create local session");
     570            0 :         return false;
     571              :     }
     572              : 
     573          115 :     localSession_->media_count = 0;
     574              : 
     575          326 :     for (auto const& media : mediaList) {
     576          211 :         if (media.enabled_) {
     577          208 :             localSession_->media[localSession_->media_count++] = addMediaDescription(media);
     578              :         }
     579              :     }
     580              : 
     581          115 :     printSession(localSession_, "Local session:\n", sdpDirection_);
     582              : 
     583          115 :     if (validateSession() != PJ_SUCCESS) {
     584            0 :         JAMI_ERROR("Failed to add medias");
     585            0 :         return false;
     586              :     }
     587              : 
     588          115 :     if (pjmedia_sdp_neg_create_w_remote_offer(memPool_.get(), localSession_, remoteSession_, &negotiator_)
     589          115 :         != PJ_SUCCESS) {
     590            0 :         JAMI_ERROR("Failed to initialize media negotiation");
     591            0 :         return false;
     592              :     }
     593              : 
     594          115 :     return true;
     595              : }
     596              : 
     597              : bool
     598           24 : Sdp::startNegotiation()
     599              : {
     600           96 :     JAMI_LOG("Starting media negotiation for [{}]", sessionName_);
     601              : 
     602           24 :     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           24 :     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           24 :     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           24 :     if (pjmedia_sdp_neg_get_active_local(negotiator_, &active_local) != PJ_SUCCESS)
     621            0 :         JAMI_ERROR("Unable to retrieve local active session");
     622              : 
     623           24 :     setActiveLocalSdpSession(active_local);
     624              : 
     625           24 :     if (active_local != nullptr) {
     626           24 :         printSession(active_local, "Local active session:", sdpDirection_);
     627              :     }
     628              : 
     629           24 :     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           24 :     setActiveRemoteSdpSession(active_remote);
     635              : 
     636           24 :     printSession(active_remote, "Remote active session:", sdpDirection_);
     637              : 
     638           24 :     return true;
     639              : }
     640              : 
     641              : std::string
     642          384 : 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          384 :         pj_pool_create(&Manager::instance().sipVoIPLink().getCachingPool()->factory, "tmpSdp", BUF_SZ, BUF_SZ, nullptr));
     647          384 :     auto* cloned = pjmedia_sdp_session_clone(tmpPool_.get(), session);
     648          384 :     if (!cloned) {
     649            0 :         JAMI_ERROR("Unable to clone SDP");
     650            0 :         return "";
     651              :     }
     652              : 
     653              :     // deactivate non-video media
     654          384 :     bool hasKeep = false;
     655         1134 :     for (unsigned i = 0; i < cloned->media_count; i++)
     656          750 :         if (i != media_keep) {
     657          366 :             if (pjmedia_sdp_media_deactivate(tmpPool_.get(), cloned->media[i]) != PJ_SUCCESS)
     658            0 :                 JAMI_ERROR("Unable to deactivate media");
     659              :         } else {
     660          384 :             hasKeep = true;
     661              :         }
     662              : 
     663          384 :     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         1134 :     for (unsigned i = 0; i < cloned->media_count; i++)
     670          750 :         if (cloned->media[i]->desc.port == 0) {
     671          366 :             std::move(cloned->media + i + 1, cloned->media + cloned->media_count, cloned->media + i);
     672          366 :             cloned->media_count--;
     673          366 :             i--;
     674              :         }
     675              : 
     676          768 :     for (unsigned i = 0; i < cloned->media_count; i++) {
     677          384 :         auto* media = cloned->media[i];
     678              : 
     679              :         // filter other codecs
     680          980 :         for (unsigned c = 0; c < media->desc.fmt_count; c++) {
     681          596 :             auto& pt = media->desc.fmt[c];
     682          596 :             if (pj_strtoul(&pt) == pt_keep)
     683          384 :                 continue;
     684              : 
     685          424 :             while (auto* attr = pjmedia_sdp_attr_find2(media->attr_count, media->attr, "rtpmap", &pt))
     686          212 :                 pjmedia_sdp_attr_remove(&media->attr_count, media->attr, attr);
     687              : 
     688          212 :             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          212 :             std::move(media->desc.fmt + c + 1, media->desc.fmt + media->desc.fmt_count, media->desc.fmt + c);
     692          212 :             media->desc.fmt_count--;
     693          212 :             c--;
     694              :         }
     695              : 
     696              :         // we handle crypto ourselfs, don't tell libav about it
     697          384 :         pjmedia_sdp_media_remove_all_attr(media, "crypto");
     698              :     }
     699              : 
     700              :     char buffer[BUF_SZ];
     701          384 :     size_t size = pjmedia_sdp_print(cloned, buffer, sizeof(buffer));
     702          384 :     string sessionStr(buffer, std::min(size, sizeof(buffer)));
     703              : 
     704          384 :     return sessionStr;
     705          384 : }
     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          396 : Sdp::getMediaDescriptions(const pjmedia_sdp_session* session, bool remote) const
     718              : {
     719          396 :     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          396 :     std::vector<MediaDescription> ret;
     725         1136 :     for (unsigned i = 0; i < session->media_count; i++) {
     726          740 :         auto* media = session->media[i];
     727          740 :         ret.emplace_back(MediaDescription());
     728          740 :         MediaDescription& descr = ret.back();
     729          740 :         if (!pj_stricmp2(&media->desc.media, "audio"))
     730          404 :             descr.type = MEDIA_AUDIO;
     731          336 :         else if (!pj_stricmp2(&media->desc.media, "video"))
     732          336 :             descr.type = MEDIA_VIDEO;
     733              :         else
     734            0 :             continue;
     735              : 
     736          740 :         descr.enabled = media->desc.port;
     737          740 :         if (!descr.enabled)
     738            6 :             continue;
     739              : 
     740              :         // get connection info
     741          734 :         pjmedia_sdp_conn* conn = media->conn ? media->conn : session->conn;
     742          734 :         if (not conn) {
     743            0 :             JAMI_ERROR("Unable to find connection information for media");
     744            0 :             continue;
     745            0 :         }
     746          734 :         descr.addr = std::string_view(conn->addr.ptr, conn->addr.slen);
     747          734 :         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          734 :         auto* attr = pjmedia_sdp_attr_find2(media->attr_count, media->attr, "rtcp", NULL);
     752          734 :         if (attr) {
     753              :             pjmedia_sdp_rtcp_attr rtcp;
     754          734 :             auto status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
     755          734 :             if (status == PJ_SUCCESS && rtcp.addr.slen) {
     756          734 :                 descr.rtcp_addr = std::string_view(rtcp.addr.ptr, rtcp.addr.slen);
     757          734 :                 descr.rtcp_addr.setPort(rtcp.port);
     758              :             }
     759              :         }
     760              : 
     761         2202 :         descr.hold = pjmedia_sdp_attr_find2(media->attr_count,
     762          734 :                                             media->attr,
     763          734 :                                             DIRECTION_STR[MediaDirection::SENDONLY],
     764              :                                             nullptr)
     765         1456 :                      || pjmedia_sdp_attr_find2(media->attr_count,
     766          722 :                                                media->attr,
     767          722 :                                                DIRECTION_STR[MediaDirection::INACTIVE],
     768              :                                                nullptr);
     769              : 
     770          734 :         descr.direction_ = getMediaDirection(media);
     771              : 
     772              :         // get codecs infos
     773          734 :         for (unsigned j = 0; j < media->desc.fmt_count; j++) {
     774          734 :             auto* const rtpMapAttribute = pjmedia_sdp_media_find_attr(media, &STR_RTPMAP, &media->desc.fmt[j]);
     775          734 :             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          734 :             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          734 :             auto codec_raw = sip_utils::as_view(rtpmap.enc_name);
     787          734 :             descr.rtp_clockrate = rtpmap.clock_rate;
     788          734 :             descr.codec = findCodecBySpec(codec_raw, rtpmap.clock_rate);
     789          734 :             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          734 :             descr.payload_type = pj_strtoul(&rtpmap.pt);
     795          734 :             if (descr.type == MEDIA_VIDEO) {
     796          330 :                 auto* const fmtpAttr = pjmedia_sdp_media_find_attr(media, &STR_FMTP, &media->desc.fmt[j]);
     797              :                 // descr.bitrate = getOutgoingVideoField(codec, "bitrate");
     798          330 :                 if (fmtpAttr && fmtpAttr->value.ptr && fmtpAttr->value.slen) {
     799          158 :                     const auto& v = fmtpAttr->value;
     800          315 :                     descr.parameters = std::string(v.ptr, v.ptr + v.slen);
     801              :                 }
     802              :             }
     803              :             // for now, just keep the first codec only
     804          733 :             descr.enabled = true;
     805          733 :             break;
     806              :         }
     807              : 
     808          733 :         if (not remote)
     809          384 :             descr.receiving_sdp = getFilteredSdp(session, i, descr.payload_type);
     810              : 
     811              :         // get crypto info
     812          733 :         std::vector<std::string> crypto;
     813        14020 :         for (unsigned j = 0; j < media->attr_count; j++) {
     814        13287 :             auto* const attribute = media->attr[j];
     815        13287 :             if (pj_stricmp2(&attribute->name, "crypto") == 0)
     816          734 :                 crypto.emplace_back(attribute->value.ptr, attribute->value.slen);
     817              :         }
     818          733 :         descr.crypto = SdesNegotiator::negotiate(crypto);
     819          734 :     }
     820          396 :     return ret;
     821          396 : }
     822              : 
     823              : std::vector<Sdp::MediaSlot>
     824          181 : Sdp::getMediaSlots() const
     825              : {
     826          181 :     auto loc = getMediaDescriptions(activeLocalSession_, false);
     827          181 :     auto rem = getMediaDescriptions(activeRemoteSession_, true);
     828          181 :     size_t slot_n = std::min(loc.size(), rem.size());
     829          181 :     std::vector<MediaSlot> s;
     830          181 :     s.reserve(slot_n);
     831          518 :     for (decltype(slot_n) i = 0; i < slot_n; i++)
     832          337 :         s.emplace_back(std::move(loc[i]), std::move(rem[i]));
     833          362 :     return s;
     834          181 : }
     835              : 
     836              : void
     837          824 : Sdp::addIceCandidates(unsigned media_index, const std::vector<std::string>& cands)
     838              : {
     839          824 :     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          824 :     auto* media = localSession_->media[media_index];
     845              : 
     846         6082 :     for (const auto& item : cands) {
     847         5258 :         const pj_str_t val = sip_utils::CONST_PJ_STR(item);
     848         5258 :         pjmedia_sdp_attr* attr = pjmedia_sdp_attr_create(memPool_.get(), "candidate", &val);
     849              : 
     850         5258 :         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          387 : Sdp::getIceCandidates(unsigned media_index) const
     857              : {
     858          387 :     const auto* remoteSession = activeRemoteSession_ ? activeRemoteSession_ : remoteSession_;
     859          387 :     const auto* localSession = activeLocalSession_ ? activeLocalSession_ : localSession_;
     860          387 :     if (not remoteSession) {
     861            0 :         JAMI_ERROR("getIceCandidates failed: no remote session");
     862            0 :         return {};
     863              :     }
     864          387 :     if (not localSession) {
     865            0 :         JAMI_ERROR("getIceCandidates failed: no local session");
     866            0 :         return {};
     867              :     }
     868          387 :     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          387 :     auto* media = remoteSession->media[media_index];
     873          387 :     auto* localMedia = localSession->media[media_index];
     874          387 :     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          382 :     std::vector<std::string> candidates;
     883              : 
     884         7556 :     for (unsigned i = 0; i < media->attr_count; i++) {
     885         7174 :         pjmedia_sdp_attr* attribute = media->attr[i];
     886         7174 :         if (pj_stricmp2(&attribute->name, "candidate") == 0)
     887         9796 :             candidates.push_back(std::string(attribute->value.ptr, attribute->value.slen));
     888              :     }
     889              : 
     890          382 :     return candidates;
     891          382 : }
     892              : 
     893              : void
     894          225 : Sdp::addIceAttributes(const dhtnet::IceTransport::Attribute&& ice_attrs)
     895              : {
     896          225 :     pj_str_t value = sip_utils::CONST_PJ_STR(ice_attrs.ufrag);
     897          225 :     pjmedia_sdp_attr* attr = pjmedia_sdp_attr_create(memPool_.get(), "ice-ufrag", &value);
     898              : 
     899          225 :     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          225 :     value = sip_utils::CONST_PJ_STR(ice_attrs.pwd);
     903          225 :     attr = pjmedia_sdp_attr_create(memPool_.get(), "ice-pwd", &value);
     904              : 
     905          225 :     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          225 : }
     908              : 
     909              : dhtnet::IceTransport::Attribute
     910          539 : Sdp::getIceAttributes() const
     911              : {
     912          539 :     if (const auto* session = activeRemoteSession_ ? activeRemoteSession_ : remoteSession_)
     913          539 :         return getIceAttributes(session);
     914            0 :     return {};
     915            0 : }
     916              : 
     917              : dhtnet::IceTransport::Attribute
     918          539 : Sdp::getIceAttributes(const pjmedia_sdp_session* session)
     919              : {
     920          539 :     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         1064 :     for (unsigned i = 0; i < session->attr_count; i++) {
     925         1050 :         pjmedia_sdp_attr* attribute = session->attr[i];
     926         1050 :         if (pj_stricmp2(&attribute->name, "ice-ufrag") == 0)
     927          525 :             ice_attrs.ufrag.assign(attribute->value.ptr, attribute->value.slen);
     928          525 :         else if (pj_stricmp2(&attribute->name, "ice-pwd") == 0)
     929          525 :             ice_attrs.pwd.assign(attribute->value.ptr, attribute->value.slen);
     930         1050 :         if (!ice_attrs.ufrag.empty() && !ice_attrs.pwd.empty())
     931          525 :             return ice_attrs;
     932              :     }
     933           39 :     for (unsigned i = 0; i < session->media_count; i++) {
     934           25 :         auto* media = session->media[i];
     935          190 :         for (unsigned j = 0; j < media->attr_count; j++) {
     936          165 :             pjmedia_sdp_attr* attribute = media->attr[j];
     937          165 :             if (pj_stricmp2(&attribute->name, "ice-ufrag") == 0)
     938            0 :                 ice_attrs.ufrag.assign(attribute->value.ptr, attribute->value.slen);
     939          165 :             else if (pj_stricmp2(&attribute->name, "ice-pwd") == 0)
     940            0 :                 ice_attrs.pwd.assign(attribute->value.ptr, attribute->value.slen);
     941          165 :             if (!ice_attrs.ufrag.empty() && !ice_attrs.pwd.empty())
     942            0 :                 return ice_attrs;
     943              :         }
     944              :     }
     945              : 
     946           14 :     return ice_attrs;
     947            0 : }
     948              : 
     949              : void
     950           49 : Sdp::clearIce()
     951              : {
     952           49 :     clearIce(localSession_);
     953           49 :     clearIce(remoteSession_);
     954           49 :     setActiveRemoteSdpSession(nullptr);
     955           49 :     setActiveLocalSdpSession(nullptr);
     956           49 : }
     957              : 
     958              : void
     959           98 : Sdp::clearIce(pjmedia_sdp_session* session)
     960              : {
     961           98 :     if (not session)
     962           24 :         return;
     963           74 :     pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "ice-ufrag");
     964           74 :     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           74 :     pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "candidate");
     967          210 :     for (unsigned i = 0; i < session->media_count; i++) {
     968          136 :         auto* media = session->media[i];
     969          136 :         pjmedia_sdp_attr_remove_all(&media->attr_count, media->attr, "candidate");
     970              :     }
     971              : }
     972              : 
     973              : std::vector<MediaAttribute>
     974          297 : Sdp::getMediaAttributeListFromSdp(const pjmedia_sdp_session* sdpSession, bool ignoreDisabled)
     975              : {
     976          297 :     if (sdpSession == nullptr) {
     977            0 :         return {};
     978              :     }
     979              : 
     980          297 :     std::vector<MediaAttribute> mediaList;
     981          297 :     unsigned audioIdx = 0;
     982          297 :     unsigned videoIdx = 0;
     983          848 :     for (unsigned idx = 0; idx < sdpSession->media_count; idx++) {
     984          551 :         mediaList.emplace_back(MediaAttribute {});
     985          551 :         auto& mediaAttr = mediaList.back();
     986              : 
     987          551 :         auto const& media = sdpSession->media[idx];
     988              : 
     989              :         // Get media type.
     990          551 :         if (!pj_stricmp2(&media->desc.media, "audio"))
     991          302 :             mediaAttr.type_ = MediaType::MEDIA_AUDIO;
     992          249 :         else if (!pj_stricmp2(&media->desc.media, "video"))
     993          249 :             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          551 :         mediaAttr.enabled_ = media->desc.port > 0;
    1003              : 
    1004          551 :         if (!mediaAttr.enabled_ && ignoreDisabled) {
    1005            1 :             mediaList.pop_back();
    1006            1 :             continue;
    1007              :         }
    1008              : 
    1009              :         // Get mute state.
    1010          550 :         auto direction = getMediaDirection(media);
    1011          550 :         mediaAttr.muted_ = direction != MediaDirection::SENDRECV and direction != MediaDirection::SENDONLY;
    1012              : 
    1013              :         // Get transport.
    1014          550 :         auto transp = getMediaTransport(media);
    1015          550 :         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          550 :         mediaAttr.secure_ = transp == MediaTransport::RTP_SAVP and not getCrypto(media).empty();
    1022              : 
    1023          550 :         if (mediaAttr.type_ == MediaType::MEDIA_AUDIO) {
    1024          302 :             mediaAttr.label_ = "audio_" + std::to_string(audioIdx++);
    1025          248 :         } else if (mediaAttr.type_ == MediaType::MEDIA_VIDEO) {
    1026          248 :             mediaAttr.label_ = "video_" + std::to_string(videoIdx++);
    1027              :         }
    1028              :     }
    1029              : 
    1030          297 :     return mediaList;
    1031          297 : }
    1032              : 
    1033              : } // namespace jami
        

Generated by: LCOV version 2.0-1