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