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