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 241 : 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 24 : pjmedia_sdp_session* getRemoteSdpSession() { return remoteSession_; }
90 :
91 172 : 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 396 : void setLocalPublishedAudioPorts(uint16_t audio_port, uint16_t control_port)
147 : {
148 396 : localAudioRtpPort_ = audio_port;
149 396 : localAudioRtcpPort_ = control_port;
150 396 : }
151 :
152 396 : void setLocalPublishedVideoPorts(uint16_t video_port, uint16_t control_port)
153 : {
154 396 : localVideoRtpPort_ = video_port;
155 396 : localVideoRtcpPort_ = control_port;
156 396 : }
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 450 : 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
|