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 : #pragma once 18 : 19 : #include "noncopyable.h" 20 : #include "media/media_codec.h" 21 : #include "media/media_attribute.h" 22 : 23 : #include <dhtnet/ip_utils.h> 24 : #include <dhtnet/ice_transport.h> 25 : 26 : #include <pjmedia/sdp.h> 27 : #include <pjmedia/sdp_neg.h> 28 : #include <pjsip/sip_transport.h> 29 : #include <pjlib.h> 30 : #include <pjsip_ua.h> 31 : #include <pjmedia/errno.h> 32 : #include <pj/pool.h> 33 : #include <pj/assert.h> 34 : 35 : #include <vector> 36 : #include <string> 37 : #include <stdexcept> 38 : 39 : namespace jami { 40 : 41 : namespace test { 42 : class SDPTest; 43 : } 44 : 45 : class AudioCodec; 46 : 47 : class SdpException : public std::runtime_error 48 : { 49 : public: 50 0 : SdpException(const std::string& str = "") 51 0 : : std::runtime_error("SDP: SdpException occurred: " + str) 52 0 : {} 53 : }; 54 : 55 : enum class SdpDirection : uint8_t { OFFER, ANSWER, NONE }; 56 : 57 : class Sdp 58 : { 59 : public: 60 : /* 61 : * Class Constructor. 62 : * 63 : * @param memory pool 64 : */ 65 : Sdp(const std::string& id); 66 : 67 : ~Sdp(); 68 : 69 : /** 70 : * Set the local media capabilities. 71 : * @param List of codec in preference order 72 : */ 73 : void setLocalMediaCapabilities(MediaType type, const std::vector<std::shared_ptr<SystemCodecInfo>>& selectedCodecs); 74 : 75 : /** 76 : * Read accessor. Get the local passive sdp session information before negotiation 77 : * 78 : * @return The structure that describes a SDP session 79 : */ 80 245 : pjmedia_sdp_session* getLocalSdpSession() { return localSession_; } 81 : 82 : const pjmedia_sdp_session* getActiveLocalSdpSession() const { return activeLocalSession_; } 83 : 84 : /** 85 : * Read accessor. Get the remote passive sdp session information before negotiation 86 : * 87 : * @return The structure that describe the SDP session 88 : */ 89 27 : pjmedia_sdp_session* getRemoteSdpSession() { return remoteSession_; } 90 : 91 171 : const pjmedia_sdp_session* getActiveRemoteSdpSession() const { return activeRemoteSession_; } 92 : 93 : /** 94 : * Set the negotiated sdp offer from the sip payload. 95 : * 96 : * @param sdp the negotiated offer 97 : */ 98 : void setActiveLocalSdpSession(const pjmedia_sdp_session* sdp); 99 : 100 : /** 101 : * Retrieve the negotiated sdp offer from the sip payload. 102 : * 103 : * @param sdp the negotiated offer 104 : */ 105 : void setActiveRemoteSdpSession(const pjmedia_sdp_session* sdp); 106 : 107 : /* 108 : * On building an invite outside a dialog, build the local offer and create the 109 : * SDP negotiator instance with it. 110 : * @returns true if offer was created, false otherwise 111 : */ 112 : bool createOffer(const std::vector<MediaAttribute>& mediaList); 113 : 114 : void setReceivedOffer(const pjmedia_sdp_session* remote); 115 : 116 : /** 117 : * Build a new SDP answer using mediaList. 118 : * 119 : * @param mediaList The list of media attributes to build the answer 120 : */ 121 : bool processIncomingOffer(const std::vector<MediaAttribute>& mediaList); 122 : 123 : /** 124 : * Start the sdp negotiation. 125 : */ 126 : bool startNegotiation(); 127 : 128 : /** 129 : * Remove all media in the session media vector. 130 : */ 131 : void cleanSessionMedia(); 132 : 133 : /* 134 : * Write accessor. Set the local IP address that will be used in the sdp session 135 : */ 136 : void setPublishedIP(const std::string& addr, pj_uint16_t addr_type = pj_AF_UNSPEC()); 137 : void setPublishedIP(const dhtnet::IpAddr& addr); 138 : 139 : /* 140 : * Read accessor. Get the local IP address 141 : */ 142 : dhtnet::IpAddr getPublishedIPAddr() const { return std::string_view(publishedIpAddr_); } 143 : 144 : std::string_view getPublishedIP() const { return publishedIpAddr_; } 145 : 146 395 : void setLocalPublishedAudioPorts(uint16_t audio_port, uint16_t control_port) 147 : { 148 395 : localAudioRtpPort_ = audio_port; 149 395 : localAudioRtcpPort_ = control_port; 150 395 : } 151 : 152 395 : void setLocalPublishedVideoPorts(uint16_t video_port, uint16_t control_port) 153 : { 154 395 : localVideoRtpPort_ = video_port; 155 395 : localVideoRtcpPort_ = control_port; 156 395 : } 157 : 158 0 : uint16_t getLocalVideoPort() const { return localVideoRtpPort_; } 159 : 160 0 : uint16_t getLocalVideoControlPort() const { return localVideoRtcpPort_; } 161 : 162 0 : uint16_t getLocalAudioPort() const { return localAudioRtpPort_; } 163 : 164 0 : uint16_t getLocalAudioControlPort() const { return localAudioRtcpPort_; } 165 : 166 : std::vector<MediaDescription> getActiveMediaDescription(bool remote) const; 167 : 168 : std::vector<MediaDescription> getMediaDescriptions(const pjmedia_sdp_session* session, bool remote) const; 169 : 170 : static std::vector<MediaAttribute> getMediaAttributeListFromSdp(const pjmedia_sdp_session* sdpSession, 171 : bool ignoreDisabled = false); 172 : 173 : using MediaSlot = std::pair<MediaDescription, MediaDescription>; 174 : std::vector<MediaSlot> getMediaSlots() const; 175 : 176 : unsigned int getTelephoneEventType() const { return telephoneEventPayload_; } 177 : 178 : void addIceAttributes(const dhtnet::IceTransport::Attribute&& ice_attrs); 179 : dhtnet::IceTransport::Attribute getIceAttributes() const; 180 : static dhtnet::IceTransport::Attribute getIceAttributes(const pjmedia_sdp_session* session); 181 : 182 : void addIceCandidates(unsigned media_index, const std::vector<std::string>& cands); 183 : 184 : std::vector<std::string> getIceCandidates(unsigned media_index) const; 185 : 186 : void clearIce(); 187 : 188 462 : SdpDirection getSdpDirection() const { return sdpDirection_; }; 189 : static constexpr std::string_view getSdpDirectionStr(SdpDirection direction); 190 : 191 : /// \brief Log the given session 192 : /// \note crypto lines with are removed for security 193 : static void printSession(const pjmedia_sdp_session* session, const char* header, SdpDirection direction); 194 : 195 : private: 196 : friend class test::SDPTest; 197 : 198 : NON_COPYABLE(Sdp); 199 : 200 : void getProfileLevelID(const pjmedia_sdp_session* session, std::string& dest, int payload) const; 201 : 202 : /** 203 : * Returns the printed original SDP filtered with only the specified media index and codec 204 : * remaining. 205 : */ 206 : static std::string getFilteredSdp(const pjmedia_sdp_session* session, unsigned media_keep, unsigned pt_keep); 207 : 208 : static void clearIce(pjmedia_sdp_session* session); 209 : 210 : /* 211 : * Build the sdp media section 212 : * Add rtpmap field if necessary 213 : */ 214 : pjmedia_sdp_media* addMediaDescription(const MediaAttribute& mediaAttr); 215 : 216 : // Determine media direction 217 : char const* mediaDirection(const MediaAttribute& mediaAttr); 218 : 219 : // Get media direction 220 : static MediaDirection getMediaDirection(pjmedia_sdp_media* media); 221 : 222 : // Get the transport type 223 : static MediaTransport getMediaTransport(pjmedia_sdp_media* media); 224 : 225 : // Get the crypto materials 226 : static std::vector<std::string> getCrypto(pjmedia_sdp_media* media); 227 : 228 : pjmedia_sdp_attr* generateSdesAttribute(); 229 : 230 : void setTelephoneEventRtpmap(pjmedia_sdp_media* med); 231 : 232 : /* 233 : * Create a new SDP 234 : */ 235 : void createLocalSession(SdpDirection direction); 236 : 237 : /* 238 : * Validate SDP 239 : */ 240 : int validateSession() const; 241 : 242 : /* 243 : * Adds a sdes attribute to the given media section. 244 : * 245 : * @param media The media to add the srtp attribute to 246 : * @throw SdpException 247 : */ 248 : void addSdesAttribute(const std::vector<std::string>& crypto); 249 : 250 : void addRTCPAttribute(pjmedia_sdp_media* med, uint16_t port); 251 : 252 : std::shared_ptr<SystemCodecInfo> findCodecByPayload(const unsigned payloadType); 253 : std::shared_ptr<SystemCodecInfo> findCodecBySpec(std::string_view codecName, const unsigned clockrate = 0) const; 254 : 255 : // Data members 256 : std::unique_ptr<pj_pool_t, std::function<void(pj_pool_t*)>> memPool_; 257 : pjmedia_sdp_neg* negotiator_ {nullptr}; 258 : pjmedia_sdp_session* localSession_ {nullptr}; 259 : pjmedia_sdp_session* remoteSession_ {nullptr}; 260 : 261 : /** 262 : * The negotiated SDP remote session 263 : * Explanation: each endpoint's offer is negotiated, and a new sdp offer results from this 264 : * negotiation, with the compatible media from each part 265 : */ 266 : const pjmedia_sdp_session* activeLocalSession_ {nullptr}; 267 : 268 : /** 269 : * The negotiated SDP remote session 270 : * Explanation: each endpoint's offer is negotiated, and a new sdp offer results from this 271 : * negotiation, with the compatible media from each part 272 : */ 273 : const pjmedia_sdp_session* activeRemoteSession_ {nullptr}; 274 : 275 : /** 276 : * Codec Map used for offer 277 : */ 278 : std::vector<std::shared_ptr<SystemCodecInfo>> audio_codec_list_; 279 : std::vector<std::shared_ptr<SystemCodecInfo>> video_codec_list_; 280 : 281 : std::string publishedIpAddr_; 282 : pj_uint16_t publishedIpAddrType_; 283 : 284 : uint16_t localAudioRtpPort_ {0}; 285 : uint16_t localAudioRtcpPort_ {0}; 286 : uint16_t localVideoRtpPort_ {0}; 287 : uint16_t localVideoRtcpPort_ {0}; 288 : 289 : unsigned int telephoneEventPayload_; 290 : 291 : // The call Id of the SDP owner 292 : std::string sessionName_ {}; 293 : 294 : // Offer/Answer flag. 295 : SdpDirection sdpDirection_ {SdpDirection::NONE}; 296 : }; 297 : 298 : } // namespace jami