LCOV - code coverage report
Current view: top level - foo/src/sip - sdp.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 471 571 82.5 %
Date: 2025-12-18 10:07:43 Functions: 45 54 83.3 %

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

Generated by: LCOV version 1.14