1 /*! \file    sdp-utils.h
2  * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3  * \copyright GNU General Public License v3
4  * \brief    SDP utilities (headers)
5  * \details  Implementation of an internal SDP representation. Allows
6  * to parse SDP strings to an internal janus_sdp object, the manipulation
7  * of such object by playing with its properties, and a serialization
8  * to an SDP string that can be passed around. Since they don't have any
9  * core dependencies, these utilities can be used by plugins as well.
10  *
11  * \ingroup core
12  * \ref core
13  */
14 
15 #ifndef JANUS_SDP_UTILS_H
16 #define JANUS_SDP_UTILS_H
17 
18 
19 #include <inttypes.h>
20 #include <glib.h>
21 
22 #include "refcount.h"
23 
24 /*! \brief Janus SDP internal object representation */
25 typedef struct janus_sdp {
26 	/*! \brief v= */
27 	int version;
28 	/*! \brief o= name */
29 	char *o_name;
30 	/*! \brief o= session ID */
31 	guint64 o_sessid;
32 	/*! \brief o= version */
33 	guint64 o_version;
34 	/*! \brief o= protocol */
35 	gboolean o_ipv4;
36 	/*! \brief o= address */
37 	char *o_addr;
38 	/*! \brief s= */
39 	char *s_name;
40 	/*! \brief t= start */
41 	guint64 t_start;
42 	/*! \brief t= stop */
43 	guint64 t_stop;
44 	/*! \brief c= protocol (not rendered for WebRTC usage) */
45 	gboolean c_ipv4;
46 	/*! \brief c= address (not rendered for WebRTC usage) */
47 	char *c_addr;
48 	/*! \brief List of global a= attributes */
49 	GList *attributes;
50 	/*! \brief List of m= m-lines */
51 	GList *m_lines;
52 	/*! \brief Atomic flag to check if this instance has been destroyed */
53 	volatile gint destroyed;
54 	/*! \brief Reference counter for this instance */
55 	janus_refcount ref;
56 } janus_sdp;
57 
58 /*! \brief Helper enumeration to quickly identify m-line media types */
59 typedef enum janus_sdp_mtype {
60 	/*! \brief m=audio */
61 	JANUS_SDP_AUDIO,
62 	/*! \brief m=video */
63 	JANUS_SDP_VIDEO,
64 	/*! \brief m=application */
65 	JANUS_SDP_APPLICATION,
66 	/*! \brief m=whatever (we don't care, unsupported) */
67 	JANUS_SDP_OTHER
68 } janus_sdp_mtype;
69 /*! \brief Helper method to get a janus_sdp_mtype from a string
70  * @param[in] type The type to parse as a string (e.g., "audio")
71  * @returns The corresponding janus_sdp_mtype value */
72 janus_sdp_mtype janus_sdp_parse_mtype(const char *type);
73 /*! \brief Helper method to get the string associated to a janus_sdp_mtype value
74  * @param[in] type The type to stringify
75  * @returns The type as a string, if valid, or NULL otherwise */
76 const char *janus_sdp_mtype_str(janus_sdp_mtype type);
77 
78 /*! \brief Helper enumeration to quickly identify m-line directions */
79 typedef enum janus_sdp_mdirection {
80 	/*! \brief default=sendrecv */
81 	JANUS_SDP_DEFAULT,
82 	/*! \brief sendrecv */
83 	JANUS_SDP_SENDRECV,
84 	/*! \brief sendonly */
85 	JANUS_SDP_SENDONLY,
86 	/*! \brief recvonly */
87 	JANUS_SDP_RECVONLY,
88 	/*! \brief inactive */
89 	JANUS_SDP_INACTIVE,
90 	/*! \brief invalid direction (when parsing) */
91 	JANUS_SDP_INVALID
92 } janus_sdp_mdirection;
93 /*! \brief Helper method to get a janus_sdp_mdirection from a string
94  * @param[in] direction The direction to parse as a string (e.g., "sendrecv")
95  * @returns The corresponding janus_sdp_mdirection value */
96 janus_sdp_mdirection janus_sdp_parse_mdirection(const char *direction);
97 /*! \brief Helper method to get the string associated to a janus_sdp_mdirection value
98  * @param[in] direction The direction to stringify
99  * @returns The direction as a string, if valid, or NULL otherwise */
100 const char *janus_sdp_mdirection_str(janus_sdp_mdirection direction);
101 
102 /*! \brief Helper method to return the preferred audio and video codecs in an SDP offer or answer,
103  * (where by preferred we mean the codecs we prefer ourselves, and not the m-line SDP order)
104  * as long as the m-line direction is not disabled (port=0 or direction=inactive) in the SDP
105  * \note The acodec and vcodec arguments are input/output, and they'll be set to a static value
106  * in janus_preferred_audio_codecs and janus_preferred_video_codecs, so don't free them.
107  * @param[in] sdp The Janus SDP object to parse
108  * @param[out] acodec The audio codec that was found
109  * @param[out] vcodec The video codec that was found */
110 void janus_sdp_find_preferred_codecs(janus_sdp *sdp, const char **acodec, const char **vcodec);
111 /*! \brief Helper method to return the first audio and video codecs in an SDP offer or answer,
112  * (no matter whether we personally prefer them ourselves or not)
113  * as long as the m-line direction is not disabled (port=0 or direction=inactive) in the SDP
114  * \note The acodec and vcodec arguments are input/output, and they'll be set to a static value
115  * in janus_preferred_audio_codecs and janus_preferred_video_codecs, so don't free them.
116  * @param[in] sdp The Janus SDP object to parse
117  * @param[out] acodec The audio codec that was found
118  * @param[out] vcodec The video codec that was found */
119 void janus_sdp_find_first_codecs(janus_sdp *sdp, const char **acodec, const char **vcodec);
120 /*! \brief Helper method to match a codec to one of the preferred codecs
121  * \note Don't free the returned value, as it's a constant value
122  * @param[in] type The type of media to match
123  * @param[in] codec The codec to match
124  * @returns The codec, if found, or NULL otherwise */
125 const char *janus_sdp_match_preferred_codec(janus_sdp_mtype type, char *codec);
126 
127 /*! \brief SDP m-line representation */
128 typedef struct janus_sdp_mline {
129 	/*! \brief Media type as a janus_sdp_mtype enumerator */
130 	janus_sdp_mtype type;
131 	/*! \brief Media type (string) */
132 	char *type_str;
133 	/*! \brief Media port */
134 	guint16 port;
135 	/*! \brief Media protocol */
136 	char *proto;
137 	/*! \brief List of formats */
138 	GList *fmts;
139 	/*! \brief List of payload types */
140 	GList *ptypes;
141 	/*! \brief Media c= protocol */
142 	gboolean c_ipv4;
143 	/*! \brief Media c= address */
144 	char *c_addr;
145 	/*! \brief Media b= type */
146 	char *b_name;
147 	/*! \brief Media b= value */
148 	uint32_t b_value;
149 	/*! \brief Media direction */
150 	janus_sdp_mdirection direction;
151 	/*! \brief List of m-line attributes */
152 	GList *attributes;
153 	/*! \brief Atomic flag to check if this instance has been destroyed */
154 	volatile gint destroyed;
155 	/*! \brief Reference counter for this instance */
156 	janus_refcount ref;
157 } janus_sdp_mline;
158 /*! \brief Helper method to quickly create a janus_sdp_mline instance
159  * @note The \c type_str property of the new m-line is created automatically
160  * depending on the provided \c type attribute. If \c type is JANUS_SDP_OTHER,
161  * though, \c type_str will NOT we allocated, and will be up to the caller.
162  * @param[in] type Type of the media (audio/video/application) as a janus_sdp_mtype
163  * @param[in] port Port to advertise
164  * @param[in] proto Profile to advertise
165  * @param[in] direction Direction of the media as a janus_sdp_direction
166  * @returns A pointer to a valid janus_sdp_mline instance, if successfull, NULL otherwise */
167 janus_sdp_mline *janus_sdp_mline_create(janus_sdp_mtype type, guint16 port, const char *proto, janus_sdp_mdirection direction);
168 /*! \brief Helper method to free a janus_sdp_mline instance
169  * @note This method does not remove the m-line from the janus_sdp instance, that's up to the caller
170  * @param[in] mline The janus_sdp_mline instance to free */
171 void janus_sdp_mline_destroy(janus_sdp_mline *mline);
172 /*! \brief Helper method to get the janus_sdp_mline associated to a media type
173  * @note This currently returns the first m-line of the specified type it finds: in
174  * general, it shouldn't be an issue as we currently only support a single stream
175  * of the same type per session anyway... this will need to be fixed in the future.
176  * @param[in] sdp The Janus SDP object to search
177  * @param[in] type The type of media to search
178  * @returns The janus_sdp_mline instance, if found, or NULL otherwise */
179 janus_sdp_mline *janus_sdp_mline_find(janus_sdp *sdp, janus_sdp_mtype type);
180 /*! \brief Helper method to remove the janus_sdp_mline associated to a media type from the SDP
181  * @note This currently removes the first m-line of the specified type it finds: in
182  * general, it shouldn't be an issue as we currently only support a single stream
183  * of the same type per session anyway... this will need to be fixed in the future.
184  * @param[in] sdp The Janus SDP object to modify
185  * @param[in] type The type of media to remove
186  * @returns 0 if successful, a negative integer otherwise */
187 int janus_sdp_mline_remove(janus_sdp *sdp, janus_sdp_mtype type);
188 
189 /*! \brief SDP a= attribute representation */
190 typedef struct janus_sdp_attribute {
191 	/*! \brief Attribute name */
192 	char *name;
193 	/*! \brief Attribute value */
194 	char *value;
195 	/*! \brief Attribute direction (e.g., for extmap) */
196 	janus_sdp_mdirection direction;
197 	/*! \brief Atomic flag to check if this instance has been destroyed */
198 	volatile gint destroyed;
199 	/*! \brief Reference counter for this instance */
200 	janus_refcount ref;
201 } janus_sdp_attribute;
202 /*! \brief Helper method to quickly create a janus_sdp_attribute instance
203  * @param[in] name Name of the attribute
204  * @param[in] value Value of the attribute, as a printf compliant string (variable arguments)
205  * @returns A pointer to a valid janus_sdp_attribute instance, if successfull, NULL otherwise */
206 janus_sdp_attribute *janus_sdp_attribute_create(const char *name, const char *value, ...) G_GNUC_PRINTF(2, 3);
207 /*! \brief Helper method to free a janus_sdp_attribute instance
208  * @note This method does not remove the attribute from the global or m-line attributes, that's up to the caller
209  * @param[in] attr The janus_sdp_attribute instance to free */
210 void janus_sdp_attribute_destroy(janus_sdp_attribute *attr);
211 /*! \brief Helper method to add an attribute to a media line
212  * @param[in] mline The m-line to add the attribute to
213  * @param[in] attr The attribute to add
214  * @returns 0 in case of success, -1 otherwise */
215 int janus_sdp_attribute_add_to_mline(janus_sdp_mline *mline, janus_sdp_attribute *attr);
216 
217 /*! \brief Method to parse an SDP string to a janus_sdp object
218  * @param[in] sdp The SDP string to parse
219  * @param[in,out] error Buffer to receive a reason for an error, if any
220  * @param[in] errlen The length of the error buffer
221  * @returns A pointer to a janus_sdp object, if successful, NULL otherwise; in case
222  * of errors, if provided the error string is filled with a reason  */
223 janus_sdp *janus_sdp_parse(const char *sdp, char *error, size_t errlen);
224 
225 /*! \brief Helper method to quickly remove all traces (m-line, rtpmap, fmtp, etc.) of a payload type
226  * @param[in] sdp The janus_sdp object to remove the payload type from
227  * @param[in] pt The payload type to remove
228  * @returns 0 in case of success, a negative integer otherwise */
229 int janus_sdp_remove_payload_type(janus_sdp *sdp, int pt);
230 
231 /*! \brief Method to serialize a janus_sdp object to an SDP string
232  * @param[in] sdp The janus_sdp object to serialize
233  * @returns A pointer to a string with the serialized SDP, if successful, NULL otherwise */
234 char *janus_sdp_write(janus_sdp *sdp);
235 
236 /*! \brief Method to quickly generate a janus_sdp instance from a few selected fields
237  * @note This allocates the \c o_addr, \c s_name and \c c_addr properties: if you
238  * want to replace them, don't forget to \c g_free the original pointers first.
239  * @param[in] name The session name (if NULL, a default value will be set)
240  * @param[in] address The IP to set in o= and c= fields (if NULL, a default value will be set)
241  * @returns A pointer to a janus_sdp object, if successful, NULL otherwise */
242 janus_sdp *janus_sdp_new(const char *name, const char *address);
243 
244 /*! \brief Method to destroy a Janus SDP object
245  * @param[in] sdp The Janus SDP object to free */
246 void janus_sdp_destroy(janus_sdp *sdp);
247 
248 typedef enum janus_sdp_oa_type {
249 /*! \brief When generating an offer or answer automatically, accept/reject audio if offered (depends on value that follows) */
250 JANUS_SDP_OA_AUDIO = 1,
251 /*! \brief When generating an offer or answer automatically, accept/reject video if offered (depends on value that follows) */
252 JANUS_SDP_OA_VIDEO,
253 /*! \brief When generating an offer or answer automatically, accept/reject datachannels if offered (depends on value that follows) */
254 JANUS_SDP_OA_DATA,
255 /*! \brief When generating an offer or answer automatically, use this direction for audio (depends on value that follows) */
256 JANUS_SDP_OA_AUDIO_DIRECTION,
257 /*! \brief When generating an offer or answer automatically, use this direction for video (depends on value that follows) */
258 JANUS_SDP_OA_VIDEO_DIRECTION,
259 /*! \brief When generating an offer or answer automatically, use this codec for audio (depends on value that follows) */
260 JANUS_SDP_OA_AUDIO_CODEC,
261 /*! \brief When generating an offer or answer automatically, use this codec for video (depends on value that follows) */
262 JANUS_SDP_OA_VIDEO_CODEC,
263 /*! \brief When generating an offer or answer automatically, use this profile for VP9 (depends on value that follows) */
264 JANUS_SDP_OA_VP9_PROFILE,
265 /*! \brief When generating an offer or answer automatically, use this profile for H.264 (depends on value that follows) */
266 JANUS_SDP_OA_H264_PROFILE,
267 /*! \brief When generating an offer (this is ignored for answers), use this payload type for audio (depends on value that follows) */
268 JANUS_SDP_OA_AUDIO_PT,
269 /*! \brief When generating an offer (this is ignored for answers), use this payload type for video (depends on value that follows) */
270 JANUS_SDP_OA_VIDEO_PT,
271 /*! \brief When generating an offer or answer automatically, do or do not negotiate telephone events (FIXME telephone-event/8000 only) */
272 JANUS_SDP_OA_AUDIO_DTMF,
273 /*! \brief When generating an offer or answer automatically, add this custom fmtp string for audio */
274 JANUS_SDP_OA_AUDIO_FMTP,
275 /*! \brief When generating an offer or answer automatically, add this custom fmtp string for video
276  * @note This property is ignored if JANUS_SDP_OA_VP9_PROFILE or JANUS_SDP_OA_H264_PROFILE is used on a compliant codec. */
277 JANUS_SDP_OA_VIDEO_FMTP,
278 /*! \brief When generating an offer or answer automatically, do or do not add the rtcpfb attributes we typically negotiate (fir, nack, pli, remb) */
279 JANUS_SDP_OA_VIDEO_RTCPFB_DEFAULTS,
280 /*! \brief When generating an offer (this is ignored for answers), use the old "DTLS/SCTP" instead of the new "UDP/DTLS/SCTP (default=TRUE for now, depends on what follows) */
281 JANUS_SDP_OA_DATA_LEGACY,
282 /*! \brief When generating an offer (this is ignored for answers), negotiate this audio extension: needs two arguments, extmap value and extension ID; can be used multiple times) */
283 JANUS_SDP_OA_AUDIO_EXTENSION,
284 /*! \brief When generating an offer (this is ignored for answers), negotiate this video extension: needs two arguments, extmap value and extension ID; can be used multiple times) */
285 JANUS_SDP_OA_VIDEO_EXTENSION,
286 /*! \brief When generating an answer (this is ignored for offers), accept this extension (by default, we reject them all; can be used multiple times) */
287 JANUS_SDP_OA_ACCEPT_EXTMAP,
288 /*! \brief MUST be used as the last argument in janus_sdp_generate_offer and janus_sdp_generate_answer */
289 JANUS_SDP_OA_DONE = 0
290 } janus_sdp_oa_type;
291 
292 /*! \brief Method to generate a janus_sdp offer, using variable arguments to dictate
293  * what to negotiate (e.g., in terms of media to offer, directions, etc.). Variable
294  * arguments are in the form of a sequence of name-value terminated by a JANUS_SDP_OA_DONE, e.g.:
295  \verbatim
296 	janus_sdp *offer = janus_sdp_generate_offer("My session", "127.0.0.1",
297 		JANUS_SDP_OA_AUDIO, TRUE,
298 		JANUS_SDP_OA_AUDIO_PT, 100,
299 		JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_SENDONLY,
300 		JANUS_SDP_OA_AUDIO_CODEC, "opus",
301 		JANUS_SDP_OA_VIDEO, FALSE,
302 		JANUS_SDP_OA_DATA, FALSE,
303 		JANUS_SDP_OA_DONE);
304  \endverbatim
305  * to only offer a \c sendonly Opus audio stream being offered with 100 as
306  * payload type, and avoid video and datachannels. Refer to the property names in
307  * the header file for a complete list of how you can drive the offer.
308  * The default, if not specified, is to offer everything, using Opus with pt=111
309  * for audio, VP8 with pt=96 as video, and data channels, all as \c sendrecv.
310  * @param[in] name The session name (if NULL, a default value will be set)
311  * @param[in] address The IP to set in o= and c= fields (if NULL, a default value will be set)
312  * @returns A pointer to a janus_sdp object, if successful, NULL otherwise */
313 janus_sdp *janus_sdp_generate_offer(const char *name, const char *address, ...);
314 /*! \brief Method to generate a janus_sdp answer to a provided janus_sdp offer, using variable arguments
315  * to dictate how to respond (e.g., in terms of media to accept, reject, directions, etc.). Variable
316  * arguments are in the form of a sequence of name-value terminated by a JANUS_SDP_OA_DONE, e.g.:
317  \verbatim
318 	janus_sdp *answer = janus_sdp_generate_answer(offer,
319 		JANUS_SDP_OA_AUDIO, TRUE,
320 		JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_RECVONLY,
321 		JANUS_SDP_OA_AUDIO_CODEC, "opus",
322 		JANUS_SDP_OA_VIDEO, FALSE,
323 		JANUS_SDP_OA_DATA, FALSE,
324 		JANUS_SDP_OA_DONE);
325  \endverbatim
326  * to only accept the audio stream being offered, but as \c recvonly, use Opus
327  * and reject both video and datachannels. Refer to the property names in
328  * the header file for a complete list of how you can drive the answer.
329  * The default, if not specified, is to accept everything as \c sendrecv.
330  * @param[in] offer The Janus SDP offer to respond to
331  * @returns A pointer to a janus_sdp object, if successful, NULL otherwise */
332 janus_sdp *janus_sdp_generate_answer(janus_sdp *offer, ...);
333 
334 /*! \brief Helper to get the payload type associated to a specific codec
335  * @note This version doesn't involve profiles, which means that in case
336  * of multiple payload types associated to the same codec because of
337  * different profiles (e.g., VP9 and H.264), this will simply return the
338  * first payload type associated with it the codec itself.
339  * @param sdp The Janus SDP instance to process
340  * @param codec The codec to find, as a string
341  * @returns The payload type, if found, or -1 otherwise */
342 int janus_sdp_get_codec_pt(janus_sdp *sdp, const char *codec);
343 
344 /*! \brief Helper to get the payload type associated to a specific codec,
345  * taking into account a codec profile as a hint as well
346  * @note The profile will only be used if the codec supports it, and the
347  * core is aware of it: right now, this is only VP9 and H.264. If the codec
348  * is there but the profile is not found, then no payload type is returned.
349  * @param sdp The Janus SDP instance to process
350  * @param codec The codec to find, as a string
351  * @param profile The codec profile to use as a hint, as a string
352  * @returns The payload type, if found, or -1 otherwise */
353 int janus_sdp_get_codec_pt_full(janus_sdp *sdp, const char *codec, const char *profile);
354 
355 /*! \brief Helper to get the codec name associated to a specific payload type
356  * @param sdp The Janus SDP instance to process
357  * @param pt The payload type to find
358  * @returns The codec name, if found, or NULL otherwise */
359 const char *janus_sdp_get_codec_name(janus_sdp *sdp, int pt);
360 
361 /*! \brief Helper to get the rtpmap associated to a specific codec
362  * @param codec The codec name, as a string (e.g., "opus")
363  * @returns The rtpmap value, if found (e.g., "opus/48000/2"), or -1 otherwise */
364 const char *janus_sdp_get_codec_rtpmap(const char *codec);
365 
366 /*! \brief Helper to get the fmtp associated to a specific payload type
367  * @param sdp The Janus SDP instance to process
368  * @param pt The payload type to find
369  * @returns The fmtp content, if found, or NULL otherwise */
370 const char *janus_sdp_get_fmtp(janus_sdp *sdp, int pt);
371 
372 #endif
373