1 /* GStreamer
2 * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library 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 GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19 /*
20 * Unless otherwise indicated, Source Code is licensed under MIT license.
21 * See further explanation attached in License Statement (distributed in the file
22 * LICENSE).
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy of
25 * this software and associated documentation files (the "Software"), to deal in
26 * the Software without restriction, including without limitation the rights to
27 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
28 * of the Software, and to permit persons to whom the Software is furnished to do
29 * so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in all
32 * copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40 * SOFTWARE.
41 */
42
43 /**
44 * SECTION:gstsdpmessage
45 * @title: GstSDPMessage
46 * @short_description: Helper methods for dealing with SDP messages
47 *
48 * The GstSDPMessage helper functions makes it easy to parse and create SDP
49 * messages.
50 *
51 */
52
53 #ifdef HAVE_CONFIG_H
54 #include "config.h"
55 #endif
56
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60
61 #include <gio/gio.h>
62
63 #include <gst/rtp/gstrtppayloads.h>
64 #include "gstsdpmessage.h"
65
66 #define FREE_STRING(field) g_free (field); (field) = NULL
67 #define REPLACE_STRING(field, val) FREE_STRING(field); (field) = g_strdup (val)
68
69 static void
free_string(gchar ** str)70 free_string (gchar ** str)
71 {
72 FREE_STRING (*str);
73 }
74
75 #define INIT_ARRAY(field, type, init_func) \
76 G_STMT_START { \
77 if (field) { \
78 guint i; \
79 for(i = 0; i < (field)->len; i++) \
80 init_func (&g_array_index ((field), type, i)); \
81 g_array_set_size ((field), 0); \
82 } \
83 else \
84 (field) = g_array_new (FALSE, TRUE, sizeof (type)); \
85 } G_STMT_END
86
87 #define FREE_ARRAY(field) \
88 G_STMT_START { \
89 if (field) \
90 g_array_free ((field), TRUE); \
91 (field) = NULL; \
92 } G_STMT_END
93
94 #define DEFINE_STRING_SETTER(field) \
95 GstSDPResult gst_sdp_message_set_##field (GstSDPMessage *msg, const gchar *val) { \
96 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \
97 g_free (msg->field); \
98 msg->field = g_strdup (val); \
99 return GST_SDP_OK; \
100 }
101 #define DEFINE_STRING_GETTER(field) \
102 const gchar* gst_sdp_message_get_##field (const GstSDPMessage *msg) { \
103 g_return_val_if_fail (msg != NULL, NULL); \
104 return msg->field; \
105 }
106
107 #define DEFINE_ARRAY_LEN(field) \
108 guint gst_sdp_message_##field##_len (const GstSDPMessage *msg) { \
109 g_return_val_if_fail (msg != NULL, 0); \
110 return msg->field->len; \
111 }
112 #define DEFINE_ARRAY_GETTER(method, field, type) \
113 const type * gst_sdp_message_get_##method (const GstSDPMessage *msg, guint idx) { \
114 g_return_val_if_fail (msg != NULL, NULL); \
115 return &g_array_index (msg->field, type, idx); \
116 }
117 #define DEFINE_PTR_ARRAY_GETTER(method, field, type) \
118 const type gst_sdp_message_get_##method (const GstSDPMessage *msg, guint idx) { \
119 g_return_val_if_fail (msg != NULL, (type) 0); \
120 return g_array_index (msg->field, type, idx); \
121 }
122 #define DEFINE_ARRAY_INSERT(method, field, intype, dup_method, type) \
123 GstSDPResult gst_sdp_message_insert_##method (GstSDPMessage *msg, gint idx, intype val) { \
124 type vt; \
125 type* v = &vt; \
126 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \
127 dup_method (v, val); \
128 if (idx == -1) \
129 g_array_append_val (msg->field, vt); \
130 else \
131 g_array_insert_val (msg->field, idx, vt); \
132 return GST_SDP_OK; \
133 }
134
135 #define DEFINE_ARRAY_REPLACE(method, field, intype, free_method, dup_method, type) \
136 GstSDPResult gst_sdp_message_replace_##method (GstSDPMessage *msg, guint idx, intype val) { \
137 type *v; \
138 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \
139 v = &g_array_index (msg->field, type, idx); \
140 free_method (v); \
141 dup_method (v, val); \
142 return GST_SDP_OK; \
143 }
144 #define DEFINE_ARRAY_REMOVE(method, field, type, free_method) \
145 GstSDPResult gst_sdp_message_remove_##method (GstSDPMessage *msg, guint idx) { \
146 type *v; \
147 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \
148 v = &g_array_index (msg->field, type, idx); \
149 free_method (v); \
150 g_array_remove_index (msg->field, idx); \
151 return GST_SDP_OK; \
152 }
153 #define DEFINE_ARRAY_ADDER(method, type) \
154 GstSDPResult gst_sdp_message_add_##method (GstSDPMessage *msg, const type val) { \
155 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \
156 return gst_sdp_message_insert_##method (msg, -1, val); \
157 }
158
159 #define dup_string(v,val) ((*v) = g_strdup (val))
160 #define INIT_STR_ARRAY(field) \
161 INIT_ARRAY (field, gchar *, free_string)
162 #define DEFINE_STR_ARRAY_GETTER(method, field) \
163 DEFINE_PTR_ARRAY_GETTER(method, field, gchar *)
164 #define DEFINE_STR_ARRAY_INSERT(method, field) \
165 DEFINE_ARRAY_INSERT (method, field, const gchar *, dup_string, gchar *)
166 #define DEFINE_STR_ARRAY_ADDER(method, field) \
167 DEFINE_ARRAY_ADDER (method, gchar *)
168 #define DEFINE_STR_ARRAY_REPLACE(method, field) \
169 DEFINE_ARRAY_REPLACE (method, field, const gchar *, free_string, dup_string, gchar *)
170 #define DEFINE_STR_ARRAY_REMOVE(method, field) \
171 DEFINE_ARRAY_REMOVE (method, field, gchar *, free_string)
172
173 static GstSDPMessage *gst_sdp_message_boxed_copy (GstSDPMessage * orig);
174 static void gst_sdp_message_boxed_free (GstSDPMessage * msg);
175
176 G_DEFINE_BOXED_TYPE (GstSDPMessage, gst_sdp_message, gst_sdp_message_boxed_copy,
177 gst_sdp_message_boxed_free);
178
179 static GstSDPMessage *
gst_sdp_message_boxed_copy(GstSDPMessage * orig)180 gst_sdp_message_boxed_copy (GstSDPMessage * orig)
181 {
182 GstSDPMessage *copy;
183
184 if (!orig)
185 return NULL;
186
187 if (gst_sdp_message_copy (orig, ©) == GST_SDP_OK)
188 return copy;
189
190 return NULL;
191 }
192
193 static void
gst_sdp_message_boxed_free(GstSDPMessage * msg)194 gst_sdp_message_boxed_free (GstSDPMessage * msg)
195 {
196 gst_sdp_message_free (msg);
197 }
198
199 static void
gst_sdp_origin_init(GstSDPOrigin * origin)200 gst_sdp_origin_init (GstSDPOrigin * origin)
201 {
202 FREE_STRING (origin->username);
203 FREE_STRING (origin->sess_id);
204 FREE_STRING (origin->sess_version);
205 FREE_STRING (origin->nettype);
206 FREE_STRING (origin->addrtype);
207 FREE_STRING (origin->addr);
208 }
209
210 static void
gst_sdp_key_init(GstSDPKey * key)211 gst_sdp_key_init (GstSDPKey * key)
212 {
213 FREE_STRING (key->type);
214 FREE_STRING (key->data);
215 }
216
217 /**
218 * gst_sdp_message_new:
219 * @msg: (out) (transfer full): pointer to new #GstSDPMessage
220 *
221 * Allocate a new GstSDPMessage and store the result in @msg.
222 *
223 * Returns: a #GstSDPResult.
224 */
225 GstSDPResult
gst_sdp_message_new(GstSDPMessage ** msg)226 gst_sdp_message_new (GstSDPMessage ** msg)
227 {
228 GstSDPMessage *newmsg;
229
230 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
231
232 newmsg = g_new0 (GstSDPMessage, 1);
233
234 *msg = newmsg;
235
236 return gst_sdp_message_init (newmsg);
237 }
238
239 /**
240 * gst_sdp_message_new_from_text:
241 * @msg: (out) (transfer full): pointer to new #GstSDPMessage
242 * @text: A dynamically allocated string representing the SDP description
243 *
244 * Parse @text and create a new SDPMessage from these.
245 *
246 * Returns: a #GstSDPResult.
247 * Since: 1.16
248 */
249 GstSDPResult
gst_sdp_message_new_from_text(const gchar * text,GstSDPMessage ** msg)250 gst_sdp_message_new_from_text (const gchar * text, GstSDPMessage ** msg)
251 {
252 GstSDPResult res;
253
254 if ((res = gst_sdp_message_new (msg)) != GST_SDP_OK)
255 return res;
256
257 res =
258 gst_sdp_message_parse_buffer ((const guint8 *) text, strlen (text), *msg);
259
260 return res;
261 }
262
263 /**
264 * gst_sdp_message_init:
265 * @msg: a #GstSDPMessage
266 *
267 * Initialize @msg so that its contents are as if it was freshly allocated
268 * with gst_sdp_message_new(). This function is mostly used to initialize a message
269 * allocated on the stack. gst_sdp_message_uninit() undoes this operation.
270 *
271 * When this function is invoked on newly allocated data (with malloc or on the
272 * stack), its contents should be set to 0 before calling this function.
273 *
274 * Returns: a #GstSDPResult.
275 */
276 GstSDPResult
gst_sdp_message_init(GstSDPMessage * msg)277 gst_sdp_message_init (GstSDPMessage * msg)
278 {
279 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
280
281 FREE_STRING (msg->version);
282 gst_sdp_origin_init (&msg->origin);
283 FREE_STRING (msg->session_name);
284 FREE_STRING (msg->information);
285 FREE_STRING (msg->uri);
286 INIT_STR_ARRAY (msg->emails);
287 INIT_STR_ARRAY (msg->phones);
288 gst_sdp_connection_clear (&msg->connection);
289 INIT_ARRAY (msg->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_clear);
290 INIT_ARRAY (msg->times, GstSDPTime, gst_sdp_time_clear);
291 INIT_ARRAY (msg->zones, GstSDPZone, gst_sdp_zone_clear);
292 gst_sdp_key_init (&msg->key);
293 INIT_ARRAY (msg->attributes, GstSDPAttribute, gst_sdp_attribute_clear);
294 INIT_ARRAY (msg->medias, GstSDPMedia, gst_sdp_media_uninit);
295
296 return GST_SDP_OK;
297 }
298
299 /**
300 * gst_sdp_message_uninit:
301 * @msg: a #GstSDPMessage
302 *
303 * Free all resources allocated in @msg. @msg should not be used anymore after
304 * this function. This function should be used when @msg was allocated on the
305 * stack and initialized with gst_sdp_message_init().
306 *
307 * Returns: a #GstSDPResult.
308 */
309 GstSDPResult
gst_sdp_message_uninit(GstSDPMessage * msg)310 gst_sdp_message_uninit (GstSDPMessage * msg)
311 {
312 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
313
314 gst_sdp_message_init (msg);
315
316 FREE_ARRAY (msg->emails);
317 FREE_ARRAY (msg->phones);
318 FREE_ARRAY (msg->bandwidths);
319 FREE_ARRAY (msg->times);
320 FREE_ARRAY (msg->zones);
321 FREE_ARRAY (msg->attributes);
322 FREE_ARRAY (msg->medias);
323
324 return GST_SDP_OK;
325 }
326
327 /**
328 * gst_sdp_message_copy:
329 * @msg: a #GstSDPMessage
330 * @copy: (out) (transfer full): pointer to new #GstSDPMessage
331 *
332 * Allocate a new copy of @msg and store the result in @copy. The value in
333 * @copy should be release with gst_sdp_message_free function.
334 *
335 * Returns: a #GstSDPResult
336 *
337 * Since: 1.2
338 */
339 GstSDPResult
gst_sdp_message_copy(const GstSDPMessage * msg,GstSDPMessage ** copy)340 gst_sdp_message_copy (const GstSDPMessage * msg, GstSDPMessage ** copy)
341 {
342 GstSDPResult ret;
343 GstSDPMessage *cp;
344 guint i, len;
345
346 if (msg == NULL)
347 return GST_SDP_EINVAL;
348
349 ret = gst_sdp_message_new (copy);
350 if (ret != GST_SDP_OK)
351 return ret;
352
353 cp = *copy;
354
355 REPLACE_STRING (cp->version, msg->version);
356 gst_sdp_message_set_origin (cp, msg->origin.username, msg->origin.sess_id,
357 msg->origin.sess_version, msg->origin.nettype, msg->origin.addrtype,
358 msg->origin.addr);
359 REPLACE_STRING (cp->session_name, msg->session_name);
360 REPLACE_STRING (cp->information, msg->information);
361 REPLACE_STRING (cp->uri, msg->uri);
362
363 len = gst_sdp_message_emails_len (msg);
364 for (i = 0; i < len; i++) {
365 gst_sdp_message_add_email (cp, gst_sdp_message_get_email (msg, i));
366 }
367
368 len = gst_sdp_message_phones_len (msg);
369 for (i = 0; i < len; i++) {
370 gst_sdp_message_add_phone (cp, gst_sdp_message_get_phone (msg, i));
371 }
372
373 gst_sdp_message_set_connection (cp, msg->connection.nettype,
374 msg->connection.addrtype, msg->connection.address, msg->connection.ttl,
375 msg->connection.addr_number);
376
377 len = gst_sdp_message_bandwidths_len (msg);
378 for (i = 0; i < len; i++) {
379 const GstSDPBandwidth *bw = gst_sdp_message_get_bandwidth (msg, i);
380 gst_sdp_message_add_bandwidth (cp, bw->bwtype, bw->bandwidth);
381 }
382
383 len = gst_sdp_message_times_len (msg);
384 for (i = 0; i < len; i++) {
385 const gchar **repeat = NULL;
386 const GstSDPTime *time = gst_sdp_message_get_time (msg, i);
387
388 if (time->repeat != NULL) {
389 guint j;
390
391 repeat = g_malloc0 ((time->repeat->len + 1) * sizeof (gchar *));
392 for (j = 0; j < time->repeat->len; j++) {
393 repeat[j] = g_array_index (time->repeat, char *, j);
394 }
395 repeat[j] = NULL;
396 }
397
398 gst_sdp_message_add_time (cp, time->start, time->stop, repeat);
399
400 g_free ((gchar **) repeat);
401 }
402
403 len = gst_sdp_message_zones_len (msg);
404 for (i = 0; i < len; i++) {
405 const GstSDPZone *zone = gst_sdp_message_get_zone (msg, i);
406 gst_sdp_message_add_zone (cp, zone->time, zone->typed_time);
407 }
408
409 gst_sdp_message_set_key (cp, msg->key.type, msg->key.data);
410
411 len = gst_sdp_message_attributes_len (msg);
412 for (i = 0; i < len; i++) {
413 const GstSDPAttribute *attr = gst_sdp_message_get_attribute (msg, i);
414 gst_sdp_message_add_attribute (cp, attr->key, attr->value);
415 }
416
417 len = gst_sdp_message_medias_len (msg);
418 for (i = 0; i < len; i++) {
419 GstSDPMedia *media_copy;
420 const GstSDPMedia *media = gst_sdp_message_get_media (msg, i);
421
422 if (gst_sdp_media_copy (media, &media_copy) == GST_SDP_OK) {
423 gst_sdp_message_add_media (cp, media_copy);
424 gst_sdp_media_free (media_copy);
425 }
426 }
427
428 return GST_SDP_OK;
429 }
430
431 /**
432 * gst_sdp_message_free:
433 * @msg: a #GstSDPMessage
434 *
435 * Free all resources allocated by @msg. @msg should not be used anymore after
436 * this function. This function should be used when @msg was dynamically
437 * allocated with gst_sdp_message_new().
438 *
439 * Returns: a #GstSDPResult.
440 */
441 GstSDPResult
gst_sdp_message_free(GstSDPMessage * msg)442 gst_sdp_message_free (GstSDPMessage * msg)
443 {
444 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
445
446 gst_sdp_message_uninit (msg);
447 g_free (msg);
448
449 return GST_SDP_OK;
450 }
451
452 /**
453 * gst_sdp_address_is_multicast:
454 * @nettype: a network type
455 * @addrtype: an address type
456 * @addr: an address
457 *
458 * Check if the given @addr is a multicast address.
459 *
460 * Returns: TRUE when @addr is multicast.
461 */
462 gboolean
gst_sdp_address_is_multicast(const gchar * nettype,const gchar * addrtype,const gchar * addr)463 gst_sdp_address_is_multicast (const gchar * nettype, const gchar * addrtype,
464 const gchar * addr)
465 {
466 gboolean ret = FALSE;
467 GInetAddress *iaddr;
468
469 g_return_val_if_fail (addr, FALSE);
470
471 /* we only support IN */
472 if (nettype && strcmp (nettype, "IN") != 0)
473 return FALSE;
474
475 /* guard against parse failures */
476 if ((iaddr = g_inet_address_new_from_string (addr)) == NULL)
477 return FALSE;
478
479 ret = g_inet_address_get_is_multicast (iaddr);
480 g_object_unref (iaddr);
481
482 return ret;
483 }
484
485 /**
486 * gst_sdp_message_as_text:
487 * @msg: a #GstSDPMessage
488 *
489 * Convert the contents of @msg to a text string.
490 *
491 * Returns: A dynamically allocated string representing the SDP description.
492 */
493 gchar *
gst_sdp_message_as_text(const GstSDPMessage * msg)494 gst_sdp_message_as_text (const GstSDPMessage * msg)
495 {
496 /* change all vars so they match rfc? */
497 GString *lines;
498 guint i;
499
500 g_return_val_if_fail (msg != NULL, NULL);
501
502 lines = g_string_new ("");
503
504 if (msg->version)
505 g_string_append_printf (lines, "v=%s\r\n", msg->version);
506
507 if (msg->origin.sess_id && msg->origin.sess_version && msg->origin.nettype &&
508 msg->origin.addrtype && msg->origin.addr)
509 g_string_append_printf (lines, "o=%s %s %s %s %s %s\r\n",
510 msg->origin.username ? msg->origin.username : "-", msg->origin.sess_id,
511 msg->origin.sess_version, msg->origin.nettype, msg->origin.addrtype,
512 msg->origin.addr);
513
514 if (msg->session_name)
515 g_string_append_printf (lines, "s=%s\r\n", msg->session_name);
516
517 if (msg->information)
518 g_string_append_printf (lines, "i=%s\r\n", msg->information);
519
520 if (msg->uri)
521 g_string_append_printf (lines, "u=%s\r\n", msg->uri);
522
523 for (i = 0; i < gst_sdp_message_emails_len (msg); i++)
524 g_string_append_printf (lines, "e=%s\r\n",
525 gst_sdp_message_get_email (msg, i));
526
527 for (i = 0; i < gst_sdp_message_phones_len (msg); i++)
528 g_string_append_printf (lines, "p=%s\r\n",
529 gst_sdp_message_get_phone (msg, i));
530
531 if (msg->connection.nettype && msg->connection.addrtype &&
532 msg->connection.address) {
533 g_string_append_printf (lines, "c=%s %s %s", msg->connection.nettype,
534 msg->connection.addrtype, msg->connection.address);
535 if (gst_sdp_address_is_multicast (msg->connection.nettype,
536 msg->connection.addrtype, msg->connection.address)) {
537 /* only add ttl for IP4 */
538 if (strcmp (msg->connection.addrtype, "IP4") == 0)
539 g_string_append_printf (lines, "/%u", msg->connection.ttl);
540 if (msg->connection.addr_number > 1)
541 g_string_append_printf (lines, "/%u", msg->connection.addr_number);
542 }
543 g_string_append_printf (lines, "\r\n");
544 }
545
546 for (i = 0; i < gst_sdp_message_bandwidths_len (msg); i++) {
547 const GstSDPBandwidth *bandwidth = gst_sdp_message_get_bandwidth (msg, i);
548
549 g_string_append_printf (lines, "b=%s:%u\r\n", bandwidth->bwtype,
550 bandwidth->bandwidth);
551 }
552
553 if (gst_sdp_message_times_len (msg) == 0) {
554 g_string_append_printf (lines, "t=0 0\r\n");
555 } else {
556 for (i = 0; i < gst_sdp_message_times_len (msg); i++) {
557 const GstSDPTime *times = gst_sdp_message_get_time (msg, i);
558
559 g_string_append_printf (lines, "t=%s %s\r\n", times->start, times->stop);
560
561 if (times->repeat != NULL) {
562 guint j;
563
564 g_string_append_printf (lines, "r=%s",
565 g_array_index (times->repeat, gchar *, 0));
566 for (j = 1; j < times->repeat->len; j++)
567 g_string_append_printf (lines, " %s",
568 g_array_index (times->repeat, gchar *, j));
569 g_string_append_printf (lines, "\r\n");
570 }
571 }
572 }
573
574 if (gst_sdp_message_zones_len (msg) > 0) {
575 const GstSDPZone *zone = gst_sdp_message_get_zone (msg, 0);
576
577 g_string_append_printf (lines, "z=%s %s", zone->time, zone->typed_time);
578 for (i = 1; i < gst_sdp_message_zones_len (msg); i++) {
579 zone = gst_sdp_message_get_zone (msg, i);
580 g_string_append_printf (lines, " %s %s", zone->time, zone->typed_time);
581 }
582 g_string_append_printf (lines, "\r\n");
583 }
584
585 if (msg->key.type) {
586 g_string_append_printf (lines, "k=%s", msg->key.type);
587 if (msg->key.data)
588 g_string_append_printf (lines, ":%s", msg->key.data);
589 g_string_append_printf (lines, "\r\n");
590 }
591
592 for (i = 0; i < gst_sdp_message_attributes_len (msg); i++) {
593 const GstSDPAttribute *attr = gst_sdp_message_get_attribute (msg, i);
594
595 if (attr->key) {
596 g_string_append_printf (lines, "a=%s", attr->key);
597 if (attr->value && attr->value[0] != '\0')
598 g_string_append_printf (lines, ":%s", attr->value);
599 g_string_append_printf (lines, "\r\n");
600 }
601 }
602
603 for (i = 0; i < gst_sdp_message_medias_len (msg); i++) {
604 const GstSDPMedia *media = gst_sdp_message_get_media (msg, i);
605 gchar *sdp_media_str;
606
607 sdp_media_str = gst_sdp_media_as_text (media);
608 g_string_append_printf (lines, "%s", sdp_media_str);
609 g_free (sdp_media_str);
610 }
611
612 return g_string_free (lines, FALSE);
613 }
614
615 static int
hex_to_int(gchar c)616 hex_to_int (gchar c)
617 {
618 return c >= '0' && c <= '9' ? c - '0'
619 : c >= 'A' && c <= 'F' ? c - 'A' + 10
620 : c >= 'a' && c <= 'f' ? c - 'a' + 10 : 0;
621 }
622
623 /**
624 * gst_sdp_message_parse_uri:
625 * @uri: the start of the uri
626 * @msg: the result #GstSDPMessage
627 *
628 * Parse the null-terminated @uri and store the result in @msg.
629 *
630 * The uri should be of the form:
631 *
632 * scheme://[address[:ttl=ttl][:noa=noa]]/[sessionname]
633 * [#type=value *[&type=value]]
634 *
635 * where value is url encoded. This looslely resembles
636 * http://tools.ietf.org/html/draft-fujikawa-sdp-url-01
637 *
638 * Returns: #GST_SDP_OK on success.
639 */
640 GstSDPResult
gst_sdp_message_parse_uri(const gchar * uri,GstSDPMessage * msg)641 gst_sdp_message_parse_uri (const gchar * uri, GstSDPMessage * msg)
642 {
643 GstSDPResult res;
644 gchar *message;
645 const gchar *colon, *slash, *hash, *p;
646 GString *lines;
647
648 g_return_val_if_fail (uri != NULL, GST_SDP_EINVAL);
649 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
650
651 colon = strstr (uri, "://");
652 if (!colon)
653 goto no_colon;
654
655 /* FIXME connection info goes here */
656
657 slash = strstr (colon + 3, "/");
658 if (!slash)
659 goto no_slash;
660
661 /* FIXME session name goes here */
662
663 hash = strstr (slash + 1, "#");
664 if (!hash)
665 goto no_hash;
666
667 lines = g_string_new ("");
668
669 /* unescape */
670 for (p = hash + 1; *p; p++) {
671 if (*p == '&')
672 g_string_append_printf (lines, "\r\n");
673 else if (*p == '+')
674 g_string_append_c (lines, ' ');
675 else if (*p == '%') {
676 gchar a, b;
677
678 if ((a = p[1])) {
679 if ((b = p[2])) {
680 g_string_append_c (lines, (hex_to_int (a) << 4) | hex_to_int (b));
681 p += 2;
682 }
683 } else {
684 p++;
685 }
686 } else
687 g_string_append_c (lines, *p);
688 }
689
690 message = g_string_free (lines, FALSE);
691 res =
692 gst_sdp_message_parse_buffer ((const guint8 *) message, strlen (message),
693 msg);
694 g_free (message);
695
696 return res;
697
698 /* ERRORS */
699 no_colon:
700 {
701 return GST_SDP_EINVAL;
702 }
703 no_slash:
704 {
705 return GST_SDP_EINVAL;
706 }
707 no_hash:
708 {
709 return GST_SDP_EINVAL;
710 }
711 }
712
713 static const guchar acceptable[96] = {
714 /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */
715 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, /* 2X !"#$%&'()*+,-./ */
716 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 3X 0123456789:;<=>? */
717 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 4X @ABCDEFGHIJKLMNO */
718 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, /* 5X PQRSTUVWXYZ[\]^_ */
719 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 6X `abcdefghijklmno */
720 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 /* 7X pqrstuvwxyz{|}~DEL */
721 };
722
723 static const gchar hex[16] = "0123456789ABCDEF";
724
725 #define ACCEPTABLE_CHAR(a) (((guchar)(a))>=32 && ((guchar)(a))<128 && acceptable[(((guchar)a))-32])
726
727 /**
728 * gst_sdp_message_as_uri:
729 * @scheme: the uri scheme
730 * @msg: the #GstSDPMessage
731 *
732 * Creates a uri from @msg with the given @scheme. The uri has the format:
733 *
734 * \@scheme:///[#type=value *[&type=value]]
735 *
736 * Where each value is url encoded.
737 *
738 * Returns: a uri for @msg.
739 */
740 gchar *
gst_sdp_message_as_uri(const gchar * scheme,const GstSDPMessage * msg)741 gst_sdp_message_as_uri (const gchar * scheme, const GstSDPMessage * msg)
742 {
743 gchar *serialized, *p;
744 gchar *res;
745 GString *lines;
746 gboolean first;
747
748 g_return_val_if_fail (scheme != NULL, NULL);
749 g_return_val_if_fail (msg != NULL, NULL);
750
751 serialized = gst_sdp_message_as_text (msg);
752
753 lines = g_string_new ("");
754 g_string_append_printf (lines, "%s:///#", scheme);
755
756 /* now escape */
757 first = TRUE;
758 for (p = serialized; *p; p++) {
759 if (first) {
760 g_string_append_printf (lines, "%c=", *p);
761 if (*(p + 1))
762 p++;
763 first = FALSE;
764 continue;
765 }
766 if (*p == '\r')
767 continue;
768 else if (*p == '\n') {
769 if (*(p + 1))
770 g_string_append_c (lines, '&');
771 first = TRUE;
772 } else if (*p == ' ')
773 g_string_append_c (lines, '+');
774 else if (ACCEPTABLE_CHAR (*p))
775 g_string_append_c (lines, *p);
776 else {
777 /* escape */
778 g_string_append_printf (lines, "%%%c%c", hex[*p >> 4], hex[*p & 0xf]);
779 }
780 }
781
782 res = g_string_free (lines, FALSE);
783 g_free (serialized);
784
785 return res;
786 }
787
788 /**
789 * gst_sdp_message_set_version:
790 * @msg: a #GstSDPMessage
791 * @version: the version
792 *
793 * Set the version in @msg.
794 *
795 * Returns: a #GstSDPResult.
796 */
797 DEFINE_STRING_SETTER (version);
798 /**
799 * gst_sdp_message_get_version:
800 * @msg: a #GstSDPMessage
801 *
802 * Get the version in @msg.
803 *
804 * Returns: a #GstSDPResult.
805 */
806 DEFINE_STRING_GETTER (version);
807
808 /**
809 * gst_sdp_message_set_origin:
810 * @msg: a #GstSDPMessage
811 * @username: the user name
812 * @sess_id: a session id
813 * @sess_version: a session version
814 * @nettype: a network type
815 * @addrtype: an address type
816 * @addr: an address
817 *
818 * Configure the SDP origin in @msg with the given parameters.
819 *
820 * Returns: #GST_SDP_OK.
821 */
822 GstSDPResult
gst_sdp_message_set_origin(GstSDPMessage * msg,const gchar * username,const gchar * sess_id,const gchar * sess_version,const gchar * nettype,const gchar * addrtype,const gchar * addr)823 gst_sdp_message_set_origin (GstSDPMessage * msg, const gchar * username,
824 const gchar * sess_id, const gchar * sess_version, const gchar * nettype,
825 const gchar * addrtype, const gchar * addr)
826 {
827 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
828
829 REPLACE_STRING (msg->origin.username, username);
830 REPLACE_STRING (msg->origin.sess_id, sess_id);
831 REPLACE_STRING (msg->origin.sess_version, sess_version);
832 REPLACE_STRING (msg->origin.nettype, nettype);
833 REPLACE_STRING (msg->origin.addrtype, addrtype);
834 REPLACE_STRING (msg->origin.addr, addr);
835
836 return GST_SDP_OK;
837 }
838
839 /**
840 * gst_sdp_message_get_origin:
841 * @msg: a #GstSDPMessage
842 *
843 * Get the origin of @msg.
844 *
845 * Returns: a #GstSDPOrigin. The result remains valid as long as @msg is valid.
846 */
847 const GstSDPOrigin *
gst_sdp_message_get_origin(const GstSDPMessage * msg)848 gst_sdp_message_get_origin (const GstSDPMessage * msg)
849 {
850 g_return_val_if_fail (msg != NULL, NULL);
851
852 return &msg->origin;
853 }
854
855 /**
856 * gst_sdp_message_set_session_name:
857 * @msg: a #GstSDPMessage
858 * @session_name: the session name
859 *
860 * Set the session name in @msg.
861 *
862 * Returns: a #GstSDPResult.
863 */
864 DEFINE_STRING_SETTER (session_name);
865 /**
866 * gst_sdp_message_get_session_name:
867 * @msg: a #GstSDPMessage
868 *
869 * Get the session name in @msg.
870 *
871 * Returns: a #GstSDPResult.
872 */
873 DEFINE_STRING_GETTER (session_name);
874 /**
875 * gst_sdp_message_set_information:
876 * @msg: a #GstSDPMessage
877 * @information: the information
878 *
879 * Set the information in @msg.
880 *
881 * Returns: a #GstSDPResult.
882 */
883 DEFINE_STRING_SETTER (information);
884 /**
885 * gst_sdp_message_get_information:
886 * @msg: a #GstSDPMessage
887 *
888 * Get the information in @msg.
889 *
890 * Returns: a #GstSDPResult.
891 */
892 DEFINE_STRING_GETTER (information);
893 /**
894 * gst_sdp_message_set_uri:
895 * @msg: a #GstSDPMessage
896 * @uri: the URI
897 *
898 * Set the URI in @msg.
899 *
900 * Returns: a #GstSDPResult.
901 */
902 DEFINE_STRING_SETTER (uri);
903 /**
904 * gst_sdp_message_get_uri:
905 * @msg: a #GstSDPMessage
906 *
907 * Get the URI in @msg.
908 *
909 * Returns: a #GstSDPResult.
910 */
911 DEFINE_STRING_GETTER (uri);
912
913 /**
914 * gst_sdp_message_emails_len:
915 * @msg: a #GstSDPMessage
916 *
917 * Get the number of emails in @msg.
918 *
919 * Returns: the number of emails in @msg.
920 */
921 DEFINE_ARRAY_LEN (emails);
922 /**
923 * gst_sdp_message_get_email:
924 * @msg: a #GstSDPMessage
925 * @idx: an email index
926 *
927 * Get the email with number @idx from @msg.
928 *
929 * Returns: the email at position @idx.
930 */
931 DEFINE_STR_ARRAY_GETTER (email, emails);
932
933 /**
934 * gst_sdp_message_insert_email:
935 * @msg: a #GstSDPMessage
936 * @idx: an index
937 * @email: an email
938 *
939 * Insert @email into the array of emails in @msg at index @idx.
940 * When -1 is given as @idx, the email is inserted at the end.
941 *
942 * Returns: a #GstSDPResult.
943 *
944 * Since: 1.2
945 */
946 DEFINE_STR_ARRAY_INSERT (email, emails);
947
948 /**
949 * gst_sdp_message_replace_email:
950 * @msg: a #GstSDPMessage
951 * @idx: an email index
952 * @email: an email
953 *
954 * Replace the email in @msg at index @idx with @email.
955 *
956 * Returns: a #GstSDPResult.
957 *
958 * Since: 1.2
959 */
960 DEFINE_STR_ARRAY_REPLACE (email, emails);
961
962 /**
963 * gst_sdp_message_remove_email:
964 * @msg: a #GstSDPMessage
965 * @idx: an email index
966 *
967 * Remove the email in @msg at index @idx.
968 *
969 * Returns: a #GstSDPResult.
970 *
971 * Since: 1.2
972 */
973 DEFINE_STR_ARRAY_REMOVE (email, emails);
974
975 /**
976 * gst_sdp_message_add_email:
977 * @msg: a #GstSDPMessage
978 * @email: an email
979 *
980 * Add @email to the list of emails in @msg.
981 *
982 * Returns: a #GstSDPResult.
983 */
984 DEFINE_STR_ARRAY_ADDER (email, emails);
985
986 /**
987 * gst_sdp_message_phones_len:
988 * @msg: a #GstSDPMessage
989 *
990 * Get the number of phones in @msg.
991 *
992 * Returns: the number of phones in @msg.
993 */
994 DEFINE_ARRAY_LEN (phones);
995 /**
996 * gst_sdp_message_get_phone:
997 * @msg: a #GstSDPMessage
998 * @idx: a phone index
999 *
1000 * Get the phone with number @idx from @msg.
1001 *
1002 * Returns: the phone at position @idx.
1003 */
1004 DEFINE_STR_ARRAY_GETTER (phone, phones);
1005
1006 /**
1007 * gst_sdp_message_insert_phone:
1008 * @msg: a #GstSDPMessage
1009 * @idx: a phone index
1010 * @phone: a phone
1011 *
1012 * Insert @phone into the array of phone numbers in @msg at index @idx.
1013 * When -1 is given as @idx, the phone is inserted at the end.
1014 *
1015 * Returns: a #GstSDPResult.
1016 *
1017 * Since: 1.2
1018 */
1019 DEFINE_STR_ARRAY_INSERT (phone, phones);
1020
1021 /**
1022 * gst_sdp_message_replace_phone:
1023 * @msg: a #GstSDPMessage
1024 * @idx: a phone index
1025 * @phone: a phone
1026 *
1027 * Replace the phone number in @msg at index @idx with @phone.
1028 *
1029 * Returns: a #GstSDPResult.
1030 *
1031 * Since: 1.2
1032 */
1033 DEFINE_STR_ARRAY_REPLACE (phone, phones);
1034
1035 /**
1036 * gst_sdp_message_remove_phone:
1037 * @msg: a #GstSDPMessage
1038 * @idx: a phone index
1039 *
1040 * Remove the phone number in @msg at index @idx.
1041 *
1042 * Returns: a #GstSDPResult.
1043 *
1044 * Since: 1.2
1045 */
1046 DEFINE_STR_ARRAY_REMOVE (phone, phones);
1047
1048 /**
1049 * gst_sdp_message_add_phone:
1050 * @msg: a #GstSDPMessage
1051 * @phone: a phone
1052 *
1053 * Add @phone to the list of phones in @msg.
1054 *
1055 * Returns: a #GstSDPResult.
1056 */
1057 DEFINE_STR_ARRAY_ADDER (phone, phones);
1058
1059
1060 /**
1061 * gst_sdp_message_set_connection:
1062 * @msg: a #GstSDPMessage
1063 * @nettype: the type of network. "IN" is defined to have the meaning
1064 * "Internet".
1065 * @addrtype: the type of address.
1066 * @address: the address
1067 * @ttl: the time to live of the address
1068 * @addr_number: the number of layers
1069 *
1070 * Configure the SDP connection in @msg with the given parameters.
1071 *
1072 * Returns: a #GstSDPResult.
1073 */
1074 GstSDPResult
gst_sdp_message_set_connection(GstSDPMessage * msg,const gchar * nettype,const gchar * addrtype,const gchar * address,guint ttl,guint addr_number)1075 gst_sdp_message_set_connection (GstSDPMessage * msg, const gchar * nettype,
1076 const gchar * addrtype, const gchar * address, guint ttl, guint addr_number)
1077 {
1078 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
1079
1080 REPLACE_STRING (msg->connection.nettype, nettype);
1081 REPLACE_STRING (msg->connection.addrtype, addrtype);
1082 REPLACE_STRING (msg->connection.address, address);
1083 msg->connection.ttl = ttl;
1084 msg->connection.addr_number = addr_number;
1085
1086 return GST_SDP_OK;
1087 }
1088
1089 /**
1090 * gst_sdp_message_get_connection:
1091 * @msg: a #GstSDPMessage
1092 *
1093 * Get the connection of @msg.
1094 *
1095 * Returns: a #GstSDPConnection. The result remains valid as long as @msg is valid.
1096 */
1097 const GstSDPConnection *
gst_sdp_message_get_connection(const GstSDPMessage * msg)1098 gst_sdp_message_get_connection (const GstSDPMessage * msg)
1099 {
1100 g_return_val_if_fail (msg != NULL, NULL);
1101
1102 return &msg->connection;
1103 }
1104
1105 /**
1106 * gst_sdp_bandwidth_set:
1107 * @bw: a #GstSDPBandwidth
1108 * @bwtype: the bandwidth modifier type
1109 * @bandwidth: the bandwidth in kilobits per second
1110 *
1111 * Set bandwidth information in @bw.
1112 *
1113 * Returns: a #GstSDPResult.
1114 *
1115 * Since: 1.2
1116 */
1117 GstSDPResult
gst_sdp_bandwidth_set(GstSDPBandwidth * bw,const gchar * bwtype,guint bandwidth)1118 gst_sdp_bandwidth_set (GstSDPBandwidth * bw, const gchar * bwtype,
1119 guint bandwidth)
1120 {
1121 g_return_val_if_fail (bw != NULL, GST_SDP_EINVAL);
1122
1123 bw->bwtype = g_strdup (bwtype);
1124 bw->bandwidth = bandwidth;
1125 return GST_SDP_OK;
1126 }
1127
1128 /**
1129 * gst_sdp_bandwidth_clear:
1130 * @bw: a #GstSDPBandwidth
1131 *
1132 * Reset the bandwidth information in @bw.
1133 *
1134 * Returns: a #GstSDPResult.
1135 *
1136 * Since: 1.2
1137 */
1138 GstSDPResult
gst_sdp_bandwidth_clear(GstSDPBandwidth * bw)1139 gst_sdp_bandwidth_clear (GstSDPBandwidth * bw)
1140 {
1141 g_return_val_if_fail (bw != NULL, GST_SDP_EINVAL);
1142
1143 FREE_STRING (bw->bwtype);
1144 bw->bandwidth = 0;
1145 return GST_SDP_OK;
1146 }
1147
1148 /**
1149 * gst_sdp_message_bandwidths_len:
1150 * @msg: a #GstSDPMessage
1151 *
1152 * Get the number of bandwidth information in @msg.
1153 *
1154 * Returns: the number of bandwidth information in @msg.
1155 */
1156 DEFINE_ARRAY_LEN (bandwidths);
1157 /**
1158 * gst_sdp_message_get_bandwidth:
1159 * @msg: a #GstSDPMessage
1160 * @idx: the bandwidth index
1161 *
1162 * Get the bandwidth at index @idx from @msg.
1163 *
1164 * Returns: a #GstSDPBandwidth.
1165 */
1166 DEFINE_ARRAY_GETTER (bandwidth, bandwidths, GstSDPBandwidth);
1167
1168 #define DUP_BANDWIDTH(v, val) memcpy (v, val, sizeof (GstSDPBandwidth))
1169 #define FREE_BANDWIDTH(v) gst_sdp_bandwidth_clear(v)
1170
1171 /**
1172 * gst_sdp_message_insert_bandwidth:
1173 * @msg: a #GstSDPMessage
1174 * @idx: an index
1175 * @bw: the bandwidth
1176 *
1177 * Insert bandwidth parameters into the array of bandwidths in @msg
1178 * at index @idx.
1179 * When -1 is given as @idx, the bandwidth is inserted at the end.
1180 *
1181 * Returns: a #GstSDPResult.
1182 *
1183 * Since: 1.2
1184 */
1185 DEFINE_ARRAY_INSERT (bandwidth, bandwidths, GstSDPBandwidth *, DUP_BANDWIDTH,
1186 GstSDPBandwidth);
1187
1188 /**
1189 * gst_sdp_message_replace_bandwidth:
1190 * @msg: a #GstSDPMessage
1191 * @idx: the bandwidth index
1192 * @bw: the bandwidth
1193 *
1194 * Replace the bandwidth information in @msg at index @idx with @bw.
1195 *
1196 * Returns: a #GstSDPResult.
1197 *
1198 * Since: 1.2
1199 */
1200 DEFINE_ARRAY_REPLACE (bandwidth, bandwidths, GstSDPBandwidth *, FREE_BANDWIDTH,
1201 DUP_BANDWIDTH, GstSDPBandwidth);
1202
1203 /**
1204 * gst_sdp_message_remove_bandwidth:
1205 * @msg: a #GstSDPMessage
1206 * @idx: the bandwidth index
1207 *
1208 * Remove the bandwidth information in @msg at index @idx.
1209 *
1210 * Returns: a #GstSDPResult.
1211 *
1212 * Since: 1.2
1213 */
1214 DEFINE_ARRAY_REMOVE (bandwidth, bandwidths, GstSDPBandwidth, FREE_BANDWIDTH);
1215
1216 /**
1217 * gst_sdp_message_add_bandwidth:
1218 * @msg: a #GstSDPMessage
1219 * @bwtype: the bandwidth modifier type
1220 * @bandwidth: the bandwidth in kilobits per second
1221 *
1222 * Add the specified bandwidth information to @msg.
1223 *
1224 * Returns: a #GstSDPResult.
1225 */
1226 GstSDPResult
gst_sdp_message_add_bandwidth(GstSDPMessage * msg,const gchar * bwtype,guint bandwidth)1227 gst_sdp_message_add_bandwidth (GstSDPMessage * msg, const gchar * bwtype,
1228 guint bandwidth)
1229 {
1230 GstSDPBandwidth bw;
1231
1232 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
1233
1234 gst_sdp_bandwidth_set (&bw, bwtype, bandwidth);
1235 return gst_sdp_message_insert_bandwidth (msg, -1, &bw);
1236 }
1237
1238 /**
1239 * gst_sdp_time_set:
1240 * @t: a #GstSDPTime
1241 * @start: the start time
1242 * @stop: the stop time
1243 * @repeat: (array zero-terminated=1): the repeat times
1244 *
1245 * Set time information @start, @stop and @repeat in @t.
1246 *
1247 * Returns: a #GstSDPResult.
1248 *
1249 * Since: 1.2
1250 */
1251 GstSDPResult
gst_sdp_time_set(GstSDPTime * t,const gchar * start,const gchar * stop,const gchar ** repeat)1252 gst_sdp_time_set (GstSDPTime * t, const gchar * start,
1253 const gchar * stop, const gchar ** repeat)
1254 {
1255 g_return_val_if_fail (t != NULL, GST_SDP_EINVAL);
1256
1257 t->start = g_strdup (start);
1258 t->stop = g_strdup (stop);
1259 if (repeat) {
1260 t->repeat = g_array_new (FALSE, TRUE, sizeof (gchar *));
1261 for (; *repeat; repeat++) {
1262 gchar *r = g_strdup (*repeat);
1263
1264 g_array_append_val (t->repeat, r);
1265 }
1266 } else
1267 t->repeat = NULL;
1268
1269 return GST_SDP_OK;
1270 }
1271
1272 /**
1273 * gst_sdp_time_clear:
1274 * @t: a #GstSDPTime
1275 *
1276 * Reset the time information in @t.
1277 *
1278 * Returns: a #GstSDPResult.
1279 *
1280 * Since: 1.2
1281 */
1282 GstSDPResult
gst_sdp_time_clear(GstSDPTime * t)1283 gst_sdp_time_clear (GstSDPTime * t)
1284 {
1285 g_return_val_if_fail (t != NULL, GST_SDP_EINVAL);
1286
1287 FREE_STRING (t->start);
1288 FREE_STRING (t->stop);
1289 INIT_STR_ARRAY (t->repeat);
1290 FREE_ARRAY (t->repeat);
1291
1292 return GST_SDP_OK;
1293 }
1294
1295 /**
1296 * gst_sdp_message_times_len:
1297 * @msg: a #GstSDPMessage
1298 *
1299 * Get the number of time information entries in @msg.
1300 *
1301 * Returns: the number of time information entries in @msg.
1302 */
1303 DEFINE_ARRAY_LEN (times);
1304
1305 /**
1306 * gst_sdp_message_get_time:
1307 * @msg: a #GstSDPMessage
1308 * @idx: the time index
1309 *
1310 * Get time information with index @idx from @msg.
1311 *
1312 * Returns: a #GstSDPTime.
1313 */
1314 DEFINE_ARRAY_GETTER (time, times, GstSDPTime);
1315
1316 #define DUP_TIME(v, val) memcpy (v, val, sizeof (GstSDPTime))
1317 #define FREE_TIME(v) gst_sdp_time_clear(v)
1318
1319 /**
1320 * gst_sdp_message_insert_time:
1321 * @msg: a #GstSDPMessage
1322 * @idx: an index
1323 * @t: a #GstSDPTime
1324 *
1325 * Insert time parameters into the array of times in @msg
1326 * at index @idx.
1327 * When -1 is given as @idx, the times are inserted at the end.
1328 *
1329 * Returns: a #GstSDPResult.
1330 *
1331 * Since: 1.2
1332 */
1333 DEFINE_ARRAY_INSERT (time, times, GstSDPTime *, DUP_TIME, GstSDPTime);
1334
1335 /**
1336 * gst_sdp_message_replace_time:
1337 * @msg: a #GstSDPMessage
1338 * @idx: the index
1339 * @t: a #GstSDPTime
1340 *
1341 * Replace the time information in @msg at index @idx with @t.
1342 *
1343 * Returns: a #GstSDPResult.
1344 *
1345 * Since: 1.2
1346 */
1347 DEFINE_ARRAY_REPLACE (time, times, GstSDPTime *, FREE_TIME,
1348 DUP_TIME, GstSDPTime);
1349
1350 /**
1351 * gst_sdp_message_remove_time:
1352 * @msg: a #GstSDPMessage
1353 * @idx: the index
1354 *
1355 * Remove the time information in @msg at index @idx.
1356 *
1357 * Returns: a #GstSDPResult.
1358 *
1359 * Since: 1.2
1360 */
1361 DEFINE_ARRAY_REMOVE (time, times, GstSDPTime, FREE_TIME);
1362
1363 /**
1364 * gst_sdp_message_add_time:
1365 * @msg: a #GstSDPMessage
1366 * @start: the start time
1367 * @stop: the stop time
1368 * @repeat: (array zero-terminated=1): the repeat times
1369 *
1370 * Add time information @start and @stop to @msg.
1371 *
1372 * Returns: a #GstSDPResult.
1373 */
1374 GstSDPResult
gst_sdp_message_add_time(GstSDPMessage * msg,const gchar * start,const gchar * stop,const gchar ** repeat)1375 gst_sdp_message_add_time (GstSDPMessage * msg, const gchar * start,
1376 const gchar * stop, const gchar ** repeat)
1377 {
1378 GstSDPTime times;
1379
1380 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
1381
1382 gst_sdp_time_set (×, start, stop, repeat);
1383 g_array_append_val (msg->times, times);
1384
1385 return GST_SDP_OK;
1386 }
1387
1388 /**
1389 * gst_sdp_zone_set:
1390 * @zone: a #GstSDPZone
1391 * @adj_time: the NTP time that a time zone adjustment happens
1392 * @typed_time: the offset from the time when the session was first scheduled
1393 *
1394 * Set zone information in @zone.
1395 *
1396 * Returns: a #GstSDPResult.
1397 *
1398 * Since: 1.2
1399 */
1400 GstSDPResult
gst_sdp_zone_set(GstSDPZone * zone,const gchar * adj_time,const gchar * typed_time)1401 gst_sdp_zone_set (GstSDPZone * zone, const gchar * adj_time,
1402 const gchar * typed_time)
1403 {
1404 g_return_val_if_fail (zone != NULL, GST_SDP_EINVAL);
1405
1406 zone->time = g_strdup (adj_time);
1407 zone->typed_time = g_strdup (typed_time);
1408 return GST_SDP_OK;
1409 }
1410
1411 /**
1412 * gst_sdp_zone_clear:
1413 * @zone: a #GstSDPZone
1414 *
1415 * Reset the zone information in @zone.
1416 *
1417 * Returns: a #GstSDPResult.
1418 *
1419 * Since: 1.2
1420 */
1421 GstSDPResult
gst_sdp_zone_clear(GstSDPZone * zone)1422 gst_sdp_zone_clear (GstSDPZone * zone)
1423 {
1424 g_return_val_if_fail (zone != NULL, GST_SDP_EINVAL);
1425
1426 FREE_STRING (zone->time);
1427 FREE_STRING (zone->typed_time);
1428 return GST_SDP_OK;
1429 }
1430
1431 /**
1432 * gst_sdp_message_zones_len:
1433 * @msg: a #GstSDPMessage
1434 *
1435 * Get the number of time zone information entries in @msg.
1436 *
1437 * Returns: the number of time zone information entries in @msg.
1438 */
1439 DEFINE_ARRAY_LEN (zones);
1440 /**
1441 * gst_sdp_message_get_zone:
1442 * @msg: a #GstSDPMessage
1443 * @idx: the zone index
1444 *
1445 * Get time zone information with index @idx from @msg.
1446 *
1447 * Returns: a #GstSDPZone.
1448 */
1449 DEFINE_ARRAY_GETTER (zone, zones, GstSDPZone);
1450
1451 #define DUP_ZONE(v, val) memcpy (v, val, sizeof (GstSDPZone))
1452 #define FREE_ZONE(v) gst_sdp_zone_clear(v)
1453
1454 /**
1455 * gst_sdp_message_insert_zone:
1456 * @msg: a #GstSDPMessage
1457 * @idx: an index
1458 * @zone: a #GstSDPZone
1459 *
1460 * Insert zone parameters into the array of zones in @msg
1461 * at index @idx.
1462 * When -1 is given as @idx, the zone is inserted at the end.
1463 *
1464 * Returns: a #GstSDPResult.
1465 *
1466 * Since: 1.2
1467 */
1468 DEFINE_ARRAY_INSERT (zone, zones, GstSDPZone *, DUP_ZONE, GstSDPZone);
1469
1470 /**
1471 * gst_sdp_message_replace_zone:
1472 * @msg: a #GstSDPMessage
1473 * @idx: the index
1474 * @zone: a #GstSDPZone
1475 *
1476 * Replace the zone information in @msg at index @idx with @zone.
1477 *
1478 * Returns: a #GstSDPResult.
1479 *
1480 * Since: 1.2
1481 */
1482 DEFINE_ARRAY_REPLACE (zone, zones, GstSDPZone *, FREE_ZONE,
1483 DUP_ZONE, GstSDPZone);
1484
1485 /**
1486 * gst_sdp_message_remove_zone:
1487 * @msg: a #GstSDPMessage
1488 * @idx: the index
1489 *
1490 * Remove the zone information in @msg at index @idx.
1491 *
1492 * Returns: a #GstSDPResult.
1493 *
1494 * Since: 1.2
1495 */
1496 DEFINE_ARRAY_REMOVE (zone, zones, GstSDPZone, FREE_ZONE);
1497
1498 /**
1499 * gst_sdp_message_add_zone:
1500 * @msg: a #GstSDPMessage
1501 * @adj_time: the NTP time that a time zone adjustment happens
1502 * @typed_time: the offset from the time when the session was first scheduled
1503 *
1504 * Add time zone information to @msg.
1505 *
1506 * Returns: a #GstSDPResult.
1507 */
1508 GstSDPResult
gst_sdp_message_add_zone(GstSDPMessage * msg,const gchar * adj_time,const gchar * typed_time)1509 gst_sdp_message_add_zone (GstSDPMessage * msg, const gchar * adj_time,
1510 const gchar * typed_time)
1511 {
1512 GstSDPZone zone;
1513
1514 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
1515
1516 gst_sdp_zone_set (&zone, adj_time, typed_time);
1517 g_array_append_val (msg->zones, zone);
1518
1519 return GST_SDP_OK;
1520 }
1521
1522 /**
1523 * gst_sdp_message_set_key:
1524 * @msg: a #GstSDPMessage
1525 * @type: the encryption type
1526 * @data: the encryption data
1527 *
1528 * Adds the encryption information to @msg.
1529 *
1530 * Returns: a #GstSDPResult.
1531 */
1532 GstSDPResult
gst_sdp_message_set_key(GstSDPMessage * msg,const gchar * type,const gchar * data)1533 gst_sdp_message_set_key (GstSDPMessage * msg, const gchar * type,
1534 const gchar * data)
1535 {
1536 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
1537
1538 REPLACE_STRING (msg->key.type, type);
1539 REPLACE_STRING (msg->key.data, data);
1540
1541 return GST_SDP_OK;
1542 }
1543
1544 /**
1545 * gst_sdp_message_get_key:
1546 * @msg: a #GstSDPMessage
1547 *
1548 * Get the encryption information from @msg.
1549 *
1550 * Returns: a #GstSDPKey.
1551 */
1552 const GstSDPKey *
gst_sdp_message_get_key(const GstSDPMessage * msg)1553 gst_sdp_message_get_key (const GstSDPMessage * msg)
1554 {
1555 g_return_val_if_fail (msg != NULL, NULL);
1556
1557 return &msg->key;
1558 }
1559
1560 /**
1561 * gst_sdp_attribute_set:
1562 * @attr: a #GstSDPAttribute
1563 * @key: the key
1564 * @value: (nullable): the value
1565 *
1566 * Set the attribute with @key and @value.
1567 *
1568 * Returns: @GST_SDP_OK.
1569 *
1570 * Since: 1.2
1571 */
1572 GstSDPResult
gst_sdp_attribute_set(GstSDPAttribute * attr,const gchar * key,const gchar * value)1573 gst_sdp_attribute_set (GstSDPAttribute * attr, const gchar * key,
1574 const gchar * value)
1575 {
1576 g_return_val_if_fail (attr != NULL, GST_SDP_EINVAL);
1577 g_return_val_if_fail (key != NULL, GST_SDP_EINVAL);
1578
1579 attr->key = g_strdup (key);
1580 attr->value = g_strdup (value);
1581 return GST_SDP_OK;
1582 }
1583
1584 /**
1585 * gst_sdp_attribute_clear:
1586 * @attr: a #GstSDPAttribute
1587 *
1588 * Clear the attribute.
1589 *
1590 * Returns: @GST_SDP_OK.
1591 *
1592 * Since: 1.2
1593 */
1594 GstSDPResult
gst_sdp_attribute_clear(GstSDPAttribute * attr)1595 gst_sdp_attribute_clear (GstSDPAttribute * attr)
1596 {
1597 g_return_val_if_fail (attr != NULL, GST_SDP_EINVAL);
1598
1599 FREE_STRING (attr->key);
1600 FREE_STRING (attr->value);
1601 return GST_SDP_OK;
1602 }
1603
1604 /**
1605 * gst_sdp_message_attributes_len:
1606 * @msg: a #GstSDPMessage
1607 *
1608 * Get the number of attributes in @msg.
1609 *
1610 * Returns: the number of attributes in @msg.
1611 */
1612 DEFINE_ARRAY_LEN (attributes);
1613
1614 /**
1615 * gst_sdp_message_get_attribute:
1616 * @msg: a #GstSDPMessage
1617 * @idx: the index
1618 *
1619 * Get the attribute at position @idx in @msg.
1620 *
1621 * Returns: the #GstSDPAttribute at position @idx.
1622 */
1623 DEFINE_ARRAY_GETTER (attribute, attributes, GstSDPAttribute);
1624
1625 /**
1626 * gst_sdp_message_get_attribute_val_n:
1627 * @msg: a #GstSDPMessage
1628 * @key: the key
1629 * @nth: the index
1630 *
1631 * Get the @nth attribute with key @key in @msg.
1632 *
1633 * Returns: the attribute value of the @nth attribute with @key.
1634 */
1635 const gchar *
gst_sdp_message_get_attribute_val_n(const GstSDPMessage * msg,const gchar * key,guint nth)1636 gst_sdp_message_get_attribute_val_n (const GstSDPMessage * msg,
1637 const gchar * key, guint nth)
1638 {
1639 guint i;
1640
1641 g_return_val_if_fail (msg != NULL, NULL);
1642 g_return_val_if_fail (key != NULL, NULL);
1643
1644 for (i = 0; i < msg->attributes->len; i++) {
1645 GstSDPAttribute *attr;
1646
1647 attr = &g_array_index (msg->attributes, GstSDPAttribute, i);
1648 if (!strcmp (attr->key, key)) {
1649 if (nth == 0)
1650 return attr->value;
1651 else
1652 nth--;
1653 }
1654 }
1655 return NULL;
1656 }
1657
1658 /**
1659 * gst_sdp_message_get_attribute_val:
1660 * @msg: a #GstSDPMessage
1661 * @key: the key
1662 *
1663 * Get the first attribute with key @key in @msg.
1664 *
1665 * Returns: the attribute value of the first attribute with @key.
1666 */
1667 const gchar *
gst_sdp_message_get_attribute_val(const GstSDPMessage * msg,const gchar * key)1668 gst_sdp_message_get_attribute_val (const GstSDPMessage * msg, const gchar * key)
1669 {
1670 return gst_sdp_message_get_attribute_val_n (msg, key, 0);
1671 }
1672
1673 #define DUP_ATTRIBUTE(v, val) memcpy (v, val, sizeof (GstSDPAttribute))
1674 #define FREE_ATTRIBUTE(v) gst_sdp_attribute_clear(v)
1675
1676 /**
1677 * gst_sdp_message_insert_attribute:
1678 * @msg: a #GstSDPMessage
1679 * @idx: an index
1680 * @attr: a #GstSDPAttribute
1681 *
1682 * Insert attribute into the array of attributes in @msg
1683 * at index @idx.
1684 * When -1 is given as @idx, the attribute is inserted at the end.
1685 *
1686 * Returns: a #GstSDPResult.
1687 *
1688 * Since: 1.2
1689 */
1690 DEFINE_ARRAY_INSERT (attribute, attributes, GstSDPAttribute *, DUP_ATTRIBUTE,
1691 GstSDPAttribute);
1692
1693 /**
1694 * gst_sdp_message_replace_attribute:
1695 * @msg: a #GstSDPMessage
1696 * @idx: the index
1697 * @attr: a #GstSDPAttribute
1698 *
1699 * Replace the attribute in @msg at index @idx with @attr.
1700 *
1701 * Returns: a #GstSDPResult.
1702 *
1703 * Since: 1.2
1704 */
1705 DEFINE_ARRAY_REPLACE (attribute, attributes, GstSDPAttribute *, FREE_ATTRIBUTE,
1706 DUP_ATTRIBUTE, GstSDPAttribute);
1707
1708 /**
1709 * gst_sdp_message_remove_attribute:
1710 * @msg: a #GstSDPMessage
1711 * @idx: the index
1712 *
1713 * Remove the attribute in @msg at index @idx.
1714 *
1715 * Returns: a #GstSDPResult.
1716 *
1717 * Since: 1.2
1718 */
1719 DEFINE_ARRAY_REMOVE (attribute, attributes, GstSDPAttribute, FREE_ATTRIBUTE);
1720
1721 /**
1722 * gst_sdp_message_add_attribute:
1723 * @msg: a #GstSDPMessage
1724 * @key: the key
1725 * @value: (nullable): the value
1726 *
1727 * Add the attribute with @key and @value to @msg.
1728 *
1729 * Returns: @GST_SDP_OK.
1730 */
1731 GstSDPResult
gst_sdp_message_add_attribute(GstSDPMessage * msg,const gchar * key,const gchar * value)1732 gst_sdp_message_add_attribute (GstSDPMessage * msg, const gchar * key,
1733 const gchar * value)
1734 {
1735 GstSDPAttribute attr;
1736
1737 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
1738 g_return_val_if_fail (key != NULL, GST_SDP_EINVAL);
1739
1740 gst_sdp_attribute_set (&attr, key, value);
1741 g_array_append_val (msg->attributes, attr);
1742
1743 return GST_SDP_OK;
1744 }
1745
1746 /**
1747 * gst_sdp_message_medias_len:
1748 * @msg: a #GstSDPMessage
1749 *
1750 * Get the number of media descriptions in @msg.
1751 *
1752 * Returns: the number of media descriptions in @msg.
1753 */
1754 DEFINE_ARRAY_LEN (medias);
1755 /**
1756 * gst_sdp_message_get_media:
1757 * @msg: a #GstSDPMessage
1758 * @idx: the index
1759 *
1760 * Get the media description at index @idx in @msg.
1761 *
1762 * Returns: a #GstSDPMedia.
1763 */
1764 DEFINE_ARRAY_GETTER (media, medias, GstSDPMedia);
1765
1766 /**
1767 * gst_sdp_message_add_media:
1768 * @msg: a #GstSDPMessage
1769 * @media: a #GstSDPMedia to add
1770 *
1771 * Adds @media to the array of medias in @msg. This function takes ownership of
1772 * the contents of @media so that @media will have to be reinitialized with
1773 * gst_sdp_media_init() before it can be used again.
1774 *
1775 * Returns: a #GstSDPResult.
1776 */
1777 GstSDPResult
gst_sdp_message_add_media(GstSDPMessage * msg,GstSDPMedia * media)1778 gst_sdp_message_add_media (GstSDPMessage * msg, GstSDPMedia * media)
1779 {
1780 guint len;
1781 GstSDPMedia *nmedia;
1782
1783 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
1784 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
1785
1786 len = msg->medias->len;
1787 g_array_set_size (msg->medias, len + 1);
1788 nmedia = &g_array_index (msg->medias, GstSDPMedia, len);
1789
1790 memcpy (nmedia, media, sizeof (GstSDPMedia));
1791 memset (media, 0, sizeof (GstSDPMedia));
1792
1793 return GST_SDP_OK;
1794 }
1795
1796 /* media access */
1797
1798 /**
1799 * gst_sdp_media_new:
1800 * @media: (out) (transfer full): pointer to new #GstSDPMedia
1801 *
1802 * Allocate a new GstSDPMedia and store the result in @media.
1803 *
1804 * Returns: a #GstSDPResult.
1805 */
1806 GstSDPResult
gst_sdp_media_new(GstSDPMedia ** media)1807 gst_sdp_media_new (GstSDPMedia ** media)
1808 {
1809 GstSDPMedia *newmedia;
1810
1811 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
1812
1813 newmedia = g_new0 (GstSDPMedia, 1);
1814
1815 *media = newmedia;
1816
1817 return gst_sdp_media_init (newmedia);
1818 }
1819
1820 /**
1821 * gst_sdp_media_init:
1822 * @media: a #GstSDPMedia
1823 *
1824 * Initialize @media so that its contents are as if it was freshly allocated
1825 * with gst_sdp_media_new(). This function is mostly used to initialize a media
1826 * allocated on the stack. gst_sdp_media_uninit() undoes this operation.
1827 *
1828 * When this function is invoked on newly allocated data (with malloc or on the
1829 * stack), its contents should be set to 0 before calling this function.
1830 *
1831 * Returns: a #GstSDPResult.
1832 */
1833 GstSDPResult
gst_sdp_media_init(GstSDPMedia * media)1834 gst_sdp_media_init (GstSDPMedia * media)
1835 {
1836 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
1837
1838 FREE_STRING (media->media);
1839 media->port = 0;
1840 media->num_ports = 0;
1841 FREE_STRING (media->proto);
1842 INIT_STR_ARRAY (media->fmts);
1843 FREE_STRING (media->information);
1844 INIT_ARRAY (media->connections, GstSDPConnection, gst_sdp_connection_clear);
1845 INIT_ARRAY (media->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_clear);
1846 gst_sdp_key_init (&media->key);
1847 INIT_ARRAY (media->attributes, GstSDPAttribute, gst_sdp_attribute_clear);
1848
1849 return GST_SDP_OK;
1850 }
1851
1852 /**
1853 * gst_sdp_media_uninit:
1854 * @media: a #GstSDPMedia
1855 *
1856 * Free all resources allocated in @media. @media should not be used anymore after
1857 * this function. This function should be used when @media was allocated on the
1858 * stack and initialized with gst_sdp_media_init().
1859 *
1860 * Returns: a #GstSDPResult.
1861 */
1862 GstSDPResult
gst_sdp_media_uninit(GstSDPMedia * media)1863 gst_sdp_media_uninit (GstSDPMedia * media)
1864 {
1865 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
1866
1867 gst_sdp_media_init (media);
1868 FREE_ARRAY (media->fmts);
1869 FREE_ARRAY (media->connections);
1870 FREE_ARRAY (media->bandwidths);
1871 FREE_ARRAY (media->attributes);
1872
1873 return GST_SDP_OK;
1874 }
1875
1876 /**
1877 * gst_sdp_media_free:
1878 * @media: a #GstSDPMedia
1879 *
1880 * Free all resources allocated by @media. @media should not be used anymore after
1881 * this function. This function should be used when @media was dynamically
1882 * allocated with gst_sdp_media_new().
1883 *
1884 * Returns: a #GstSDPResult.
1885 */
1886 GstSDPResult
gst_sdp_media_free(GstSDPMedia * media)1887 gst_sdp_media_free (GstSDPMedia * media)
1888 {
1889 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
1890
1891 gst_sdp_media_uninit (media);
1892 g_free (media);
1893
1894 return GST_SDP_OK;
1895 }
1896
1897 /**
1898 * gst_sdp_media_copy:
1899 * @media: a #GstSDPMedia
1900 * @copy: (out) (transfer full): pointer to new #GstSDPMedia
1901 *
1902 * Allocate a new copy of @media and store the result in @copy. The value in
1903 * @copy should be release with gst_sdp_media_free function.
1904 *
1905 * Returns: a #GstSDPResult
1906 *
1907 * Since: 1.2
1908 */
1909 GstSDPResult
gst_sdp_media_copy(const GstSDPMedia * media,GstSDPMedia ** copy)1910 gst_sdp_media_copy (const GstSDPMedia * media, GstSDPMedia ** copy)
1911 {
1912 GstSDPResult ret;
1913 GstSDPMedia *cp;
1914 guint i, len;
1915
1916 if (media == NULL)
1917 return GST_SDP_EINVAL;
1918
1919 ret = gst_sdp_media_new (copy);
1920 if (ret != GST_SDP_OK)
1921 return ret;
1922
1923 cp = *copy;
1924
1925 REPLACE_STRING (cp->media, media->media);
1926 cp->port = media->port;
1927 cp->num_ports = media->num_ports;
1928 REPLACE_STRING (cp->proto, media->proto);
1929
1930 len = gst_sdp_media_formats_len (media);
1931 for (i = 0; i < len; i++) {
1932 gst_sdp_media_add_format (cp, gst_sdp_media_get_format (media, i));
1933 }
1934
1935 REPLACE_STRING (cp->information, media->information);
1936
1937 len = gst_sdp_media_connections_len (media);
1938 for (i = 0; i < len; i++) {
1939 const GstSDPConnection *connection =
1940 gst_sdp_media_get_connection (media, i);
1941 gst_sdp_media_add_connection (cp, connection->nettype, connection->addrtype,
1942 connection->address, connection->ttl, connection->addr_number);
1943 }
1944
1945 len = gst_sdp_media_bandwidths_len (media);
1946 for (i = 0; i < len; i++) {
1947 const GstSDPBandwidth *bw = gst_sdp_media_get_bandwidth (media, i);
1948 gst_sdp_media_add_bandwidth (cp, bw->bwtype, bw->bandwidth);
1949 }
1950
1951 gst_sdp_media_set_key (cp, media->key.type, media->key.data);
1952
1953 len = gst_sdp_media_attributes_len (media);
1954 for (i = 0; i < len; i++) {
1955 const GstSDPAttribute *att = gst_sdp_media_get_attribute (media, i);
1956 gst_sdp_media_add_attribute (cp, att->key, att->value);
1957 }
1958
1959 return GST_SDP_OK;
1960 }
1961
1962 /**
1963 * gst_sdp_media_as_text:
1964 * @media: a #GstSDPMedia
1965 *
1966 * Convert the contents of @media to a text string.
1967 *
1968 * Returns: A dynamically allocated string representing the media.
1969 */
1970 gchar *
gst_sdp_media_as_text(const GstSDPMedia * media)1971 gst_sdp_media_as_text (const GstSDPMedia * media)
1972 {
1973 GString *lines;
1974 guint i;
1975
1976 g_return_val_if_fail (media != NULL, NULL);
1977
1978 lines = g_string_new ("");
1979
1980 if (media->media)
1981 g_string_append_printf (lines, "m=%s", media->media);
1982
1983 g_string_append_printf (lines, " %u", media->port);
1984
1985 if (media->num_ports > 1)
1986 g_string_append_printf (lines, "/%u", media->num_ports);
1987
1988 g_string_append_printf (lines, " %s", media->proto);
1989
1990 for (i = 0; i < gst_sdp_media_formats_len (media); i++)
1991 g_string_append_printf (lines, " %s", gst_sdp_media_get_format (media, i));
1992 g_string_append_printf (lines, "\r\n");
1993
1994 if (media->information)
1995 g_string_append_printf (lines, "i=%s", media->information);
1996
1997 for (i = 0; i < gst_sdp_media_connections_len (media); i++) {
1998 const GstSDPConnection *conn = gst_sdp_media_get_connection (media, i);
1999
2000 if (conn->nettype && conn->addrtype && conn->address) {
2001 g_string_append_printf (lines, "c=%s %s %s", conn->nettype,
2002 conn->addrtype, conn->address);
2003 if (gst_sdp_address_is_multicast (conn->nettype, conn->addrtype,
2004 conn->address)) {
2005 /* only add TTL for IP4 multicast */
2006 if (strcmp (conn->addrtype, "IP4") == 0)
2007 g_string_append_printf (lines, "/%u", conn->ttl);
2008 if (conn->addr_number > 1)
2009 g_string_append_printf (lines, "/%u", conn->addr_number);
2010 }
2011 g_string_append_printf (lines, "\r\n");
2012 }
2013 }
2014
2015 for (i = 0; i < gst_sdp_media_bandwidths_len (media); i++) {
2016 const GstSDPBandwidth *bandwidth = gst_sdp_media_get_bandwidth (media, i);
2017
2018 g_string_append_printf (lines, "b=%s:%u\r\n", bandwidth->bwtype,
2019 bandwidth->bandwidth);
2020 }
2021
2022 if (media->key.type) {
2023 g_string_append_printf (lines, "k=%s", media->key.type);
2024 if (media->key.data)
2025 g_string_append_printf (lines, ":%s", media->key.data);
2026 g_string_append_printf (lines, "\r\n");
2027 }
2028
2029 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
2030 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
2031
2032 if (attr->key) {
2033 g_string_append_printf (lines, "a=%s", attr->key);
2034 if (attr->value && attr->value[0] != '\0')
2035 g_string_append_printf (lines, ":%s", attr->value);
2036 g_string_append_printf (lines, "\r\n");
2037 }
2038 }
2039
2040 return g_string_free (lines, FALSE);
2041 }
2042
2043 /**
2044 * gst_sdp_media_get_media:
2045 * @media: a #GstSDPMedia
2046 *
2047 * Get the media description of @media.
2048 *
2049 * Returns: the media description.
2050 */
2051 const gchar *
gst_sdp_media_get_media(const GstSDPMedia * media)2052 gst_sdp_media_get_media (const GstSDPMedia * media)
2053 {
2054 g_return_val_if_fail (media != NULL, NULL);
2055
2056 return media->media;
2057 }
2058
2059 /**
2060 * gst_sdp_media_set_media:
2061 * @media: a #GstSDPMedia
2062 * @med: the media description
2063 *
2064 * Set the media description of @media to @med.
2065 *
2066 * Returns: #GST_SDP_OK.
2067 */
2068 GstSDPResult
gst_sdp_media_set_media(GstSDPMedia * media,const gchar * med)2069 gst_sdp_media_set_media (GstSDPMedia * media, const gchar * med)
2070 {
2071 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2072 g_return_val_if_fail (med != NULL, GST_SDP_EINVAL);
2073
2074 g_free (media->media);
2075 media->media = g_strdup (med);
2076
2077 return GST_SDP_OK;
2078 }
2079
2080 /**
2081 * gst_sdp_media_get_port:
2082 * @media: a #GstSDPMedia
2083 *
2084 * Get the port number for @media.
2085 *
2086 * Returns: the port number of @media.
2087 */
2088 guint
gst_sdp_media_get_port(const GstSDPMedia * media)2089 gst_sdp_media_get_port (const GstSDPMedia * media)
2090 {
2091 g_return_val_if_fail (media != NULL, 0);
2092
2093 return media->port;
2094 }
2095
2096 /**
2097 * gst_sdp_media_get_num_ports:
2098 * @media: a #GstSDPMedia
2099 *
2100 * Get the number of ports for @media.
2101 *
2102 * Returns: the number of ports for @media.
2103 */
2104 guint
gst_sdp_media_get_num_ports(const GstSDPMedia * media)2105 gst_sdp_media_get_num_ports (const GstSDPMedia * media)
2106 {
2107 g_return_val_if_fail (media != NULL, 0);
2108
2109 return media->num_ports;
2110 }
2111
2112 /**
2113 * gst_sdp_media_set_port_info:
2114 * @media: a #GstSDPMedia
2115 * @port: the port number
2116 * @num_ports: the number of ports
2117 *
2118 * Set the port information in @media.
2119 *
2120 * Returns: #GST_SDP_OK.
2121 */
2122 GstSDPResult
gst_sdp_media_set_port_info(GstSDPMedia * media,guint port,guint num_ports)2123 gst_sdp_media_set_port_info (GstSDPMedia * media, guint port, guint num_ports)
2124 {
2125 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2126
2127 media->port = port;
2128 media->num_ports = num_ports;
2129
2130 return GST_SDP_OK;
2131 }
2132
2133 /**
2134 * gst_sdp_media_get_proto:
2135 * @media: a #GstSDPMedia
2136 *
2137 * Get the transport protocol of @media
2138 *
2139 * Returns: the transport protocol of @media.
2140 */
2141 const gchar *
gst_sdp_media_get_proto(const GstSDPMedia * media)2142 gst_sdp_media_get_proto (const GstSDPMedia * media)
2143 {
2144 g_return_val_if_fail (media != NULL, NULL);
2145
2146 return media->proto;
2147 }
2148
2149 /**
2150 * gst_sdp_media_set_proto:
2151 * @media: a #GstSDPMedia
2152 * @proto: the media transport protocol
2153 *
2154 * Set the media transport protocol of @media to @proto.
2155 *
2156 * Returns: #GST_SDP_OK.
2157 */
2158 GstSDPResult
gst_sdp_media_set_proto(GstSDPMedia * media,const gchar * proto)2159 gst_sdp_media_set_proto (GstSDPMedia * media, const gchar * proto)
2160 {
2161 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2162
2163 g_free (media->proto);
2164 media->proto = g_strdup (proto);
2165
2166 return GST_SDP_OK;
2167 }
2168
2169 /**
2170 * gst_sdp_media_formats_len:
2171 * @media: a #GstSDPMedia
2172 *
2173 * Get the number of formats in @media.
2174 *
2175 * Returns: the number of formats in @media.
2176 */
2177 guint
gst_sdp_media_formats_len(const GstSDPMedia * media)2178 gst_sdp_media_formats_len (const GstSDPMedia * media)
2179 {
2180 g_return_val_if_fail (media != NULL, 0);
2181
2182 return media->fmts->len;
2183 }
2184
2185 /**
2186 * gst_sdp_media_get_format:
2187 * @media: a #GstSDPMedia
2188 * @idx: an index
2189 *
2190 * Get the format information at position @idx in @media.
2191 *
2192 * Returns: the format at position @idx.
2193 */
2194 const gchar *
gst_sdp_media_get_format(const GstSDPMedia * media,guint idx)2195 gst_sdp_media_get_format (const GstSDPMedia * media, guint idx)
2196 {
2197 g_return_val_if_fail (media != NULL, NULL);
2198
2199 if (idx >= media->fmts->len)
2200 return NULL;
2201 return g_array_index (media->fmts, gchar *, idx);
2202 }
2203
2204 /**
2205 * gst_sdp_media_insert_format:
2206 * @media: a #GstSDPMedia
2207 * @idx: an index
2208 * @format: the format
2209 *
2210 * Insert the format information to @media at @idx. When @idx is -1,
2211 * the format is appended.
2212 *
2213 * Returns: #GST_SDP_OK.
2214 *
2215 * Since: 1.2
2216 */
2217 GstSDPResult
gst_sdp_media_insert_format(GstSDPMedia * media,gint idx,const gchar * format)2218 gst_sdp_media_insert_format (GstSDPMedia * media, gint idx,
2219 const gchar * format)
2220 {
2221 gchar *fmt;
2222
2223 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2224 g_return_val_if_fail (format != NULL, GST_SDP_EINVAL);
2225
2226 fmt = g_strdup (format);
2227
2228 if (idx == -1)
2229 g_array_append_val (media->fmts, fmt);
2230 else
2231 g_array_insert_val (media->fmts, idx, fmt);
2232
2233 return GST_SDP_OK;
2234 }
2235
2236 /**
2237 * gst_sdp_media_replace_format:
2238 * @media: a #GstSDPMedia
2239 * @idx: an index
2240 * @format: the format
2241 *
2242 * Replace the format information in @media at @idx with @format.
2243 *
2244 * Returns: #GST_SDP_OK.
2245 *
2246 * Since: 1.2
2247 */
2248 GstSDPResult
gst_sdp_media_replace_format(GstSDPMedia * media,guint idx,const gchar * format)2249 gst_sdp_media_replace_format (GstSDPMedia * media, guint idx,
2250 const gchar * format)
2251 {
2252 gchar **old;
2253
2254 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2255 g_return_val_if_fail (format != NULL, GST_SDP_EINVAL);
2256
2257 old = &g_array_index (media->fmts, gchar *, idx);
2258 g_free (*old);
2259 *old = g_strdup (format);
2260
2261 return GST_SDP_OK;
2262 }
2263
2264 /**
2265 * gst_sdp_media_remove_format:
2266 * @media: a #GstSDPMedia
2267 * @idx: an index
2268 *
2269 * Remove the format information in @media at @idx.
2270 *
2271 * Returns: #GST_SDP_OK.
2272 *
2273 * Since: 1.2
2274 */
2275 GstSDPResult
gst_sdp_media_remove_format(GstSDPMedia * media,guint idx)2276 gst_sdp_media_remove_format (GstSDPMedia * media, guint idx)
2277 {
2278 gchar **old;
2279
2280 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2281
2282 old = &g_array_index (media->fmts, gchar *, idx);
2283 g_free (*old);
2284 g_array_remove_index (media->fmts, idx);
2285
2286 return GST_SDP_OK;
2287 }
2288
2289 /**
2290 * gst_sdp_media_add_format:
2291 * @media: a #GstSDPMedia
2292 * @format: the format
2293 *
2294 * Add the format information to @media.
2295 *
2296 * Returns: #GST_SDP_OK.
2297 */
2298 GstSDPResult
gst_sdp_media_add_format(GstSDPMedia * media,const gchar * format)2299 gst_sdp_media_add_format (GstSDPMedia * media, const gchar * format)
2300 {
2301 gchar *fmt;
2302
2303 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2304 g_return_val_if_fail (format != NULL, GST_SDP_EINVAL);
2305
2306 fmt = g_strdup (format);
2307
2308 g_array_append_val (media->fmts, fmt);
2309
2310 return GST_SDP_OK;
2311 }
2312
2313 /**
2314 * gst_sdp_media_get_information:
2315 * @media: a #GstSDPMedia
2316 *
2317 * Get the information of @media
2318 *
2319 * Returns: the information of @media.
2320 */
2321 const gchar *
gst_sdp_media_get_information(const GstSDPMedia * media)2322 gst_sdp_media_get_information (const GstSDPMedia * media)
2323 {
2324 g_return_val_if_fail (media != NULL, NULL);
2325
2326 return media->information;
2327 }
2328
2329 /**
2330 * gst_sdp_media_set_information:
2331 * @media: a #GstSDPMedia
2332 * @information: the media information
2333 *
2334 * Set the media information of @media to @information.
2335 *
2336 * Returns: #GST_SDP_OK.
2337 */
2338 GstSDPResult
gst_sdp_media_set_information(GstSDPMedia * media,const gchar * information)2339 gst_sdp_media_set_information (GstSDPMedia * media, const gchar * information)
2340 {
2341 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2342
2343 g_free (media->information);
2344 media->information = g_strdup (information);
2345
2346 return GST_SDP_OK;
2347 }
2348
2349 /**
2350 * gst_sdp_connection_set:
2351 * @conn: a #GstSDPConnection
2352 * @nettype: the type of network. "IN" is defined to have the meaning
2353 * "Internet".
2354 * @addrtype: the type of address.
2355 * @address: the address
2356 * @ttl: the time to live of the address
2357 * @addr_number: the number of layers
2358 *
2359 * Set the connection with the given parameters.
2360 *
2361 * Returns: @GST_SDP_OK.
2362 *
2363 * Since: 1.2
2364 */
2365 GstSDPResult
gst_sdp_connection_set(GstSDPConnection * conn,const gchar * nettype,const gchar * addrtype,const gchar * address,guint ttl,guint addr_number)2366 gst_sdp_connection_set (GstSDPConnection * conn, const gchar * nettype,
2367 const gchar * addrtype, const gchar * address, guint ttl, guint addr_number)
2368 {
2369 g_return_val_if_fail (conn != NULL, GST_SDP_EINVAL);
2370 g_return_val_if_fail (nettype != NULL, GST_SDP_EINVAL);
2371 g_return_val_if_fail (addrtype != NULL, GST_SDP_EINVAL);
2372 g_return_val_if_fail (address != NULL, GST_SDP_EINVAL);
2373
2374 conn->nettype = g_strdup (nettype);
2375 conn->addrtype = g_strdup (addrtype);
2376 conn->address = g_strdup (address);
2377 conn->ttl = ttl;
2378 conn->addr_number = addr_number;
2379 return GST_SDP_OK;
2380 }
2381
2382 /**
2383 * gst_sdp_connection_clear:
2384 * @conn: a #GstSDPConnection
2385 *
2386 * Clear the connection.
2387 *
2388 * Returns: @GST_SDP_OK.
2389 *
2390 * Since: 1.2
2391 */
2392 GstSDPResult
gst_sdp_connection_clear(GstSDPConnection * conn)2393 gst_sdp_connection_clear (GstSDPConnection * conn)
2394 {
2395 g_return_val_if_fail (conn != NULL, GST_SDP_EINVAL);
2396
2397 FREE_STRING (conn->nettype);
2398 FREE_STRING (conn->addrtype);
2399 FREE_STRING (conn->address);
2400 conn->ttl = 0;
2401 conn->addr_number = 0;
2402 return GST_SDP_OK;
2403 }
2404
2405
2406 /**
2407 * gst_sdp_media_connections_len:
2408 * @media: a #GstSDPMedia
2409 *
2410 * Get the number of connection fields in @media.
2411 *
2412 * Returns: the number of connections in @media.
2413 */
2414 guint
gst_sdp_media_connections_len(const GstSDPMedia * media)2415 gst_sdp_media_connections_len (const GstSDPMedia * media)
2416 {
2417 g_return_val_if_fail (media != NULL, 0);
2418
2419 return media->connections->len;
2420 }
2421
2422 /**
2423 * gst_sdp_media_get_connection:
2424 * @media: a #GstSDPMedia
2425 * @idx: an index
2426 *
2427 * Get the connection at position @idx in @media.
2428 *
2429 * Returns: the #GstSDPConnection at position @idx.
2430 */
2431 const GstSDPConnection *
gst_sdp_media_get_connection(const GstSDPMedia * media,guint idx)2432 gst_sdp_media_get_connection (const GstSDPMedia * media, guint idx)
2433 {
2434 g_return_val_if_fail (media != NULL, NULL);
2435 g_return_val_if_fail (idx < media->connections->len, NULL);
2436
2437 return &g_array_index (media->connections, GstSDPConnection, idx);
2438 }
2439
2440 /**
2441 * gst_sdp_media_insert_connection:
2442 * @media: a #GstSDPMedia
2443 * @idx: an index
2444 * @conn: a #GstSDPConnection
2445 *
2446 * Insert the connection information to @media at @idx. When @idx is -1,
2447 * the connection is appended.
2448 *
2449 * Returns: #GST_SDP_OK.
2450 *
2451 * Since: 1.2
2452 */
2453 GstSDPResult
gst_sdp_media_insert_connection(GstSDPMedia * media,gint idx,GstSDPConnection * conn)2454 gst_sdp_media_insert_connection (GstSDPMedia * media, gint idx,
2455 GstSDPConnection * conn)
2456 {
2457 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2458 g_return_val_if_fail (conn != NULL, GST_SDP_EINVAL);
2459 g_return_val_if_fail (idx == -1
2460 || idx < media->connections->len, GST_SDP_EINVAL);
2461
2462 if (idx == -1)
2463 g_array_append_val (media->connections, *conn);
2464 else
2465 g_array_insert_val (media->connections, idx, *conn);
2466
2467 return GST_SDP_OK;
2468 }
2469
2470 /**
2471 * gst_sdp_media_replace_connection:
2472 * @media: a #GstSDPMedia
2473 * @idx: an index
2474 * @conn: a #GstSDPConnection
2475 *
2476 * Replace the connection information in @media at @idx with @conn.
2477 *
2478 * Returns: #GST_SDP_OK.
2479 *
2480 * Since: 1.2
2481 */
2482 GstSDPResult
gst_sdp_media_replace_connection(GstSDPMedia * media,guint idx,GstSDPConnection * conn)2483 gst_sdp_media_replace_connection (GstSDPMedia * media, guint idx,
2484 GstSDPConnection * conn)
2485 {
2486 GstSDPConnection *old;
2487
2488 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2489 g_return_val_if_fail (conn != NULL, GST_SDP_EINVAL);
2490 g_return_val_if_fail (idx < media->connections->len, GST_SDP_EINVAL);
2491
2492 old = &g_array_index (media->connections, GstSDPConnection, idx);
2493 gst_sdp_connection_clear (old);
2494 *old = *conn;
2495
2496 return GST_SDP_OK;
2497 }
2498
2499 /**
2500 * gst_sdp_media_remove_connection:
2501 * @media: a #GstSDPMedia
2502 * @idx: an index
2503 *
2504 * Remove the connection information in @media at @idx.
2505 *
2506 * Returns: #GST_SDP_OK.
2507 *
2508 * Since: 1.2
2509 */
2510 GstSDPResult
gst_sdp_media_remove_connection(GstSDPMedia * media,guint idx)2511 gst_sdp_media_remove_connection (GstSDPMedia * media, guint idx)
2512 {
2513 GstSDPConnection *old;
2514
2515 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2516 g_return_val_if_fail (idx < media->connections->len, GST_SDP_EINVAL);
2517
2518 old = &g_array_index (media->connections, GstSDPConnection, idx);
2519 gst_sdp_connection_clear (old);
2520 g_array_remove_index (media->connections, idx);
2521
2522 return GST_SDP_OK;
2523 }
2524
2525 /**
2526 * gst_sdp_media_add_connection:
2527 * @media: a #GstSDPMedia
2528 * @nettype: the type of network. "IN" is defined to have the meaning
2529 * "Internet".
2530 * @addrtype: the type of address.
2531 * @address: the address
2532 * @ttl: the time to live of the address
2533 * @addr_number: the number of layers
2534 *
2535 * Add the given connection parameters to @media.
2536 *
2537 * Returns: a #GstSDPResult.
2538 */
2539 GstSDPResult
gst_sdp_media_add_connection(GstSDPMedia * media,const gchar * nettype,const gchar * addrtype,const gchar * address,guint ttl,guint addr_number)2540 gst_sdp_media_add_connection (GstSDPMedia * media, const gchar * nettype,
2541 const gchar * addrtype, const gchar * address, guint ttl, guint addr_number)
2542 {
2543 GstSDPConnection conn;
2544
2545 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2546 g_return_val_if_fail (nettype != NULL, GST_SDP_EINVAL);
2547 g_return_val_if_fail (addrtype != NULL, GST_SDP_EINVAL);
2548 g_return_val_if_fail (address != NULL, GST_SDP_EINVAL);
2549
2550 gst_sdp_connection_set (&conn, nettype, addrtype, address, ttl, addr_number);
2551 g_array_append_val (media->connections, conn);
2552
2553 return GST_SDP_OK;
2554 }
2555
2556 /**
2557 * gst_sdp_media_bandwidths_len:
2558 * @media: a #GstSDPMedia
2559 *
2560 * Get the number of bandwidth fields in @media.
2561 *
2562 * Returns: the number of bandwidths in @media.
2563 */
2564 guint
gst_sdp_media_bandwidths_len(const GstSDPMedia * media)2565 gst_sdp_media_bandwidths_len (const GstSDPMedia * media)
2566 {
2567 g_return_val_if_fail (media != NULL, 0);
2568
2569 return media->bandwidths->len;
2570 }
2571
2572 /**
2573 * gst_sdp_media_get_bandwidth:
2574 * @media: a #GstSDPMedia
2575 * @idx: an index
2576 *
2577 * Get the bandwidth at position @idx in @media.
2578 *
2579 * Returns: the #GstSDPBandwidth at position @idx.
2580 */
2581 const GstSDPBandwidth *
gst_sdp_media_get_bandwidth(const GstSDPMedia * media,guint idx)2582 gst_sdp_media_get_bandwidth (const GstSDPMedia * media, guint idx)
2583 {
2584 g_return_val_if_fail (media != NULL, NULL);
2585
2586 return &g_array_index (media->bandwidths, GstSDPBandwidth, idx);
2587 }
2588
2589 /**
2590 * gst_sdp_media_insert_bandwidth:
2591 * @media: a #GstSDPMedia
2592 * @idx: an index
2593 * @bw: a #GstSDPBandwidth
2594 *
2595 * Insert the bandwidth information to @media at @idx. When @idx is -1,
2596 * the bandwidth is appended.
2597 *
2598 * Returns: #GST_SDP_OK.
2599 *
2600 * Since: 1.2
2601 */
2602 GstSDPResult
gst_sdp_media_insert_bandwidth(GstSDPMedia * media,gint idx,GstSDPBandwidth * bw)2603 gst_sdp_media_insert_bandwidth (GstSDPMedia * media, gint idx,
2604 GstSDPBandwidth * bw)
2605 {
2606 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2607 g_return_val_if_fail (bw != NULL, GST_SDP_EINVAL);
2608 g_return_val_if_fail (idx == -1
2609 || idx < media->bandwidths->len, GST_SDP_EINVAL);
2610
2611 if (idx == -1)
2612 g_array_append_val (media->bandwidths, *bw);
2613 else
2614 g_array_insert_val (media->bandwidths, idx, *bw);
2615
2616 return GST_SDP_OK;
2617 }
2618
2619 /**
2620 * gst_sdp_media_replace_bandwidth:
2621 * @media: a #GstSDPMedia
2622 * @idx: an index
2623 * @bw: a #GstSDPBandwidth
2624 *
2625 * Replace the bandwidth information in @media at @idx with @bw.
2626 *
2627 * Returns: #GST_SDP_OK.
2628 *
2629 * Since: 1.2
2630 */
2631 GstSDPResult
gst_sdp_media_replace_bandwidth(GstSDPMedia * media,guint idx,GstSDPBandwidth * bw)2632 gst_sdp_media_replace_bandwidth (GstSDPMedia * media, guint idx,
2633 GstSDPBandwidth * bw)
2634 {
2635 GstSDPBandwidth *old;
2636 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2637 g_return_val_if_fail (bw != NULL, GST_SDP_EINVAL);
2638 g_return_val_if_fail (idx < media->bandwidths->len, GST_SDP_EINVAL);
2639
2640 old = &g_array_index (media->bandwidths, GstSDPBandwidth, idx);
2641 gst_sdp_bandwidth_clear (old);
2642 *old = *bw;
2643
2644 return GST_SDP_OK;
2645 }
2646
2647 /**
2648 * gst_sdp_media_remove_bandwidth:
2649 * @media: a #GstSDPMedia
2650 * @idx: an index
2651 *
2652 * Remove the bandwidth information in @media at @idx.
2653 *
2654 * Returns: #GST_SDP_OK.
2655 *
2656 * Since: 1.2
2657 */
2658 GstSDPResult
gst_sdp_media_remove_bandwidth(GstSDPMedia * media,guint idx)2659 gst_sdp_media_remove_bandwidth (GstSDPMedia * media, guint idx)
2660 {
2661 GstSDPBandwidth *old;
2662
2663 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2664 g_return_val_if_fail (idx < media->bandwidths->len, GST_SDP_EINVAL);
2665
2666 old = &g_array_index (media->bandwidths, GstSDPBandwidth, idx);
2667 gst_sdp_bandwidth_clear (old);
2668 g_array_remove_index (media->bandwidths, idx);
2669
2670 return GST_SDP_OK;
2671 }
2672
2673 /**
2674 * gst_sdp_media_add_bandwidth:
2675 * @media: a #GstSDPMedia
2676 * @bwtype: the bandwidth modifier type
2677 * @bandwidth: the bandwidth in kilobits per second
2678 *
2679 * Add the bandwidth information with @bwtype and @bandwidth to @media.
2680 *
2681 * Returns: #GST_SDP_OK.
2682 */
2683 GstSDPResult
gst_sdp_media_add_bandwidth(GstSDPMedia * media,const gchar * bwtype,guint bandwidth)2684 gst_sdp_media_add_bandwidth (GstSDPMedia * media, const gchar * bwtype,
2685 guint bandwidth)
2686 {
2687 GstSDPBandwidth bw;
2688
2689 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2690 g_return_val_if_fail (bwtype != NULL, GST_SDP_EINVAL);
2691
2692 gst_sdp_bandwidth_set (&bw, bwtype, bandwidth);
2693 g_array_append_val (media->bandwidths, bw);
2694
2695 return GST_SDP_OK;
2696 }
2697
2698 /**
2699 * gst_sdp_media_set_key:
2700 * @media: a #GstSDPMedia
2701 * @type: the encryption type
2702 * @data: the encryption data
2703 *
2704 * Adds the encryption information to @media.
2705 *
2706 * Returns: a #GstSDPResult.
2707 */
2708 GstSDPResult
gst_sdp_media_set_key(GstSDPMedia * media,const gchar * type,const gchar * data)2709 gst_sdp_media_set_key (GstSDPMedia * media, const gchar * type,
2710 const gchar * data)
2711 {
2712 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2713
2714 g_free (media->key.type);
2715 media->key.type = g_strdup (type);
2716 g_free (media->key.data);
2717 media->key.data = g_strdup (data);
2718
2719 return GST_SDP_OK;
2720 }
2721
2722 /**
2723 * gst_sdp_media_get_key:
2724 * @media: a #GstSDPMedia
2725 *
2726 * Get the encryption information from @media.
2727 *
2728 * Returns: a #GstSDPKey.
2729 */
2730 const GstSDPKey *
gst_sdp_media_get_key(const GstSDPMedia * media)2731 gst_sdp_media_get_key (const GstSDPMedia * media)
2732 {
2733 g_return_val_if_fail (media != NULL, NULL);
2734
2735 return &media->key;
2736 }
2737
2738 /**
2739 * gst_sdp_media_attributes_len:
2740 * @media: a #GstSDPMedia
2741 *
2742 * Get the number of attribute fields in @media.
2743 *
2744 * Returns: the number of attributes in @media.
2745 */
2746 guint
gst_sdp_media_attributes_len(const GstSDPMedia * media)2747 gst_sdp_media_attributes_len (const GstSDPMedia * media)
2748 {
2749 g_return_val_if_fail (media != NULL, 0);
2750
2751 return media->attributes->len;
2752 }
2753
2754 /**
2755 * gst_sdp_media_add_attribute:
2756 * @media: a #GstSDPMedia
2757 * @key: a key
2758 * @value: (nullable): a value
2759 *
2760 * Add the attribute with @key and @value to @media.
2761 *
2762 * Returns: #GST_SDP_OK.
2763 */
2764 GstSDPResult
gst_sdp_media_add_attribute(GstSDPMedia * media,const gchar * key,const gchar * value)2765 gst_sdp_media_add_attribute (GstSDPMedia * media, const gchar * key,
2766 const gchar * value)
2767 {
2768 GstSDPAttribute attr;
2769
2770 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2771 g_return_val_if_fail (key != NULL, GST_SDP_EINVAL);
2772
2773 gst_sdp_attribute_set (&attr, key, value);
2774 g_array_append_val (media->attributes, attr);
2775
2776 return GST_SDP_OK;
2777 }
2778
2779 /**
2780 * gst_sdp_media_get_attribute:
2781 * @media: a #GstSDPMedia
2782 * @idx: an index
2783 *
2784 * Get the attribute at position @idx in @media.
2785 *
2786 * Returns: the #GstSDPAttribute at position @idx.
2787 */
2788 const GstSDPAttribute *
gst_sdp_media_get_attribute(const GstSDPMedia * media,guint idx)2789 gst_sdp_media_get_attribute (const GstSDPMedia * media, guint idx)
2790 {
2791 g_return_val_if_fail (media != NULL, NULL);
2792 g_return_val_if_fail (idx < media->attributes->len, NULL);
2793
2794 return &g_array_index (media->attributes, GstSDPAttribute, idx);
2795 }
2796
2797 /**
2798 * gst_sdp_media_get_attribute_val_n:
2799 * @media: a #GstSDPMedia
2800 * @key: a key
2801 * @nth: an index
2802 *
2803 * Get the @nth attribute value for @key in @media.
2804 *
2805 * Returns: the @nth attribute value.
2806 */
2807 const gchar *
gst_sdp_media_get_attribute_val_n(const GstSDPMedia * media,const gchar * key,guint nth)2808 gst_sdp_media_get_attribute_val_n (const GstSDPMedia * media, const gchar * key,
2809 guint nth)
2810 {
2811 guint i;
2812
2813 g_return_val_if_fail (media != NULL, NULL);
2814 g_return_val_if_fail (key != NULL, NULL);
2815
2816 for (i = 0; i < media->attributes->len; i++) {
2817 GstSDPAttribute *attr;
2818
2819 attr = &g_array_index (media->attributes, GstSDPAttribute, i);
2820 if (!strcmp (attr->key, key)) {
2821 if (nth == 0)
2822 return attr->value;
2823 else
2824 nth--;
2825 }
2826 }
2827 return NULL;
2828 }
2829
2830 /**
2831 * gst_sdp_media_get_attribute_val:
2832 * @media: a #GstSDPMedia
2833 * @key: a key
2834 *
2835 * Get the first attribute value for @key in @media.
2836 *
2837 * Returns: the first attribute value for @key.
2838 */
2839 const gchar *
gst_sdp_media_get_attribute_val(const GstSDPMedia * media,const gchar * key)2840 gst_sdp_media_get_attribute_val (const GstSDPMedia * media, const gchar * key)
2841 {
2842 g_return_val_if_fail (media != NULL, NULL);
2843 g_return_val_if_fail (key != NULL, NULL);
2844
2845 return gst_sdp_media_get_attribute_val_n (media, key, 0);
2846 }
2847
2848 /**
2849 * gst_sdp_media_insert_attribute:
2850 * @media: a #GstSDPMedia
2851 * @idx: an index
2852 * @attr: a #GstSDPAttribute
2853 *
2854 * Insert the attribute to @media at @idx. When @idx is -1,
2855 * the attribute is appended.
2856 *
2857 * Returns: #GST_SDP_OK.
2858 *
2859 * Since: 1.2
2860 */
2861 GstSDPResult
gst_sdp_media_insert_attribute(GstSDPMedia * media,gint idx,GstSDPAttribute * attr)2862 gst_sdp_media_insert_attribute (GstSDPMedia * media, gint idx,
2863 GstSDPAttribute * attr)
2864 {
2865 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2866 g_return_val_if_fail (attr != NULL, GST_SDP_EINVAL);
2867 g_return_val_if_fail (idx == -1
2868 || idx < media->attributes->len, GST_SDP_EINVAL);
2869
2870 if (idx == -1)
2871 g_array_append_val (media->attributes, *attr);
2872 else
2873 g_array_insert_val (media->attributes, idx, *attr);
2874
2875 return GST_SDP_OK;
2876 }
2877
2878 /**
2879 * gst_sdp_media_replace_attribute:
2880 * @media: a #GstSDPMedia
2881 * @idx: an index
2882 * @attr: a #GstSDPAttribute
2883 *
2884 * Replace the attribute in @media at @idx with @attr.
2885 *
2886 * Returns: #GST_SDP_OK.
2887 *
2888 * Since: 1.2
2889 */
2890 GstSDPResult
gst_sdp_media_replace_attribute(GstSDPMedia * media,guint idx,GstSDPAttribute * attr)2891 gst_sdp_media_replace_attribute (GstSDPMedia * media, guint idx,
2892 GstSDPAttribute * attr)
2893 {
2894 GstSDPAttribute *old;
2895
2896 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2897 g_return_val_if_fail (attr != NULL, GST_SDP_EINVAL);
2898 g_return_val_if_fail (idx < media->attributes->len, GST_SDP_EINVAL);
2899
2900 old = &g_array_index (media->attributes, GstSDPAttribute, idx);
2901 gst_sdp_attribute_clear (old);
2902 *old = *attr;
2903
2904 return GST_SDP_OK;
2905 }
2906
2907 /**
2908 * gst_sdp_media_remove_attribute:
2909 * @media: a #GstSDPMedia
2910 * @idx: an index
2911 *
2912 * Remove the attribute in @media at @idx.
2913 *
2914 * Returns: #GST_SDP_OK.
2915 *
2916 * Since: 1.2
2917 */
2918 GstSDPResult
gst_sdp_media_remove_attribute(GstSDPMedia * media,guint idx)2919 gst_sdp_media_remove_attribute (GstSDPMedia * media, guint idx)
2920 {
2921 GstSDPAttribute *old;
2922 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
2923 g_return_val_if_fail (idx < media->attributes->len, GST_SDP_EINVAL);
2924
2925 old = &g_array_index (media->attributes, GstSDPAttribute, idx);
2926 gst_sdp_attribute_clear (old);
2927 g_array_remove_index (media->attributes, idx);
2928
2929 return GST_SDP_OK;
2930 }
2931
2932 static void
read_string(gchar * dest,guint size,gchar ** src)2933 read_string (gchar * dest, guint size, gchar ** src)
2934 {
2935 guint idx;
2936
2937 idx = 0;
2938 /* skip spaces */
2939 while (g_ascii_isspace (**src))
2940 (*src)++;
2941
2942 while (!g_ascii_isspace (**src) && **src != '\0') {
2943 if (idx < size - 1)
2944 dest[idx++] = **src;
2945 (*src)++;
2946 }
2947 if (size > 0)
2948 dest[idx] = '\0';
2949 }
2950
2951 static void
read_string_del(gchar * dest,guint size,gchar del,gchar ** src)2952 read_string_del (gchar * dest, guint size, gchar del, gchar ** src)
2953 {
2954 guint idx;
2955
2956 idx = 0;
2957 /* skip spaces */
2958 while (g_ascii_isspace (**src))
2959 (*src)++;
2960
2961 while (**src != del && **src != '\0') {
2962 if (idx < size - 1)
2963 dest[idx++] = **src;
2964 (*src)++;
2965 }
2966 if (size > 0)
2967 dest[idx] = '\0';
2968 }
2969
2970 enum
2971 {
2972 SDP_SESSION,
2973 SDP_MEDIA,
2974 };
2975
2976 typedef struct
2977 {
2978 guint state;
2979 GstSDPMessage *msg;
2980 GstSDPMedia *media;
2981 } SDPContext;
2982
2983 static gboolean
gst_sdp_parse_line(SDPContext * c,gchar type,gchar * buffer)2984 gst_sdp_parse_line (SDPContext * c, gchar type, gchar * buffer)
2985 {
2986 gchar str[8192];
2987 gchar *p = buffer;
2988
2989 #define READ_STRING(field) \
2990 do { read_string (str, sizeof (str), &p); REPLACE_STRING (field, str); } while (0)
2991 #define READ_UINT(field) \
2992 do { read_string (str, sizeof (str), &p); field = strtoul (str, NULL, 10); } while (0)
2993
2994 switch (type) {
2995 case 'v':
2996 if (buffer[0] != '0')
2997 GST_WARNING ("wrong SDP version");
2998 gst_sdp_message_set_version (c->msg, buffer);
2999 break;
3000 case 'o':
3001 READ_STRING (c->msg->origin.username);
3002 READ_STRING (c->msg->origin.sess_id);
3003 READ_STRING (c->msg->origin.sess_version);
3004 READ_STRING (c->msg->origin.nettype);
3005 READ_STRING (c->msg->origin.addrtype);
3006 READ_STRING (c->msg->origin.addr);
3007 break;
3008 case 's':
3009 REPLACE_STRING (c->msg->session_name, buffer);
3010 break;
3011 case 'i':
3012 if (c->state == SDP_SESSION) {
3013 REPLACE_STRING (c->msg->information, buffer);
3014 } else {
3015 REPLACE_STRING (c->media->information, buffer);
3016 }
3017 break;
3018 case 'u':
3019 REPLACE_STRING (c->msg->uri, buffer);
3020 break;
3021 case 'e':
3022 gst_sdp_message_add_email (c->msg, buffer);
3023 break;
3024 case 'p':
3025 gst_sdp_message_add_phone (c->msg, buffer);
3026 break;
3027 case 'c':
3028 {
3029 GstSDPConnection conn;
3030 gchar *str2;
3031
3032 memset (&conn, 0, sizeof (conn));
3033
3034 str2 = p;
3035 while ((str2 = strchr (str2, '/')))
3036 *str2++ = ' ';
3037 READ_STRING (conn.nettype);
3038 READ_STRING (conn.addrtype);
3039 READ_STRING (conn.address);
3040 /* only read TTL for IP4 */
3041 if (strcmp (conn.addrtype, "IP4") == 0)
3042 READ_UINT (conn.ttl);
3043 READ_UINT (conn.addr_number);
3044
3045 if (c->state == SDP_SESSION) {
3046 gst_sdp_message_set_connection (c->msg, conn.nettype, conn.addrtype,
3047 conn.address, conn.ttl, conn.addr_number);
3048 } else {
3049 gst_sdp_media_add_connection (c->media, conn.nettype, conn.addrtype,
3050 conn.address, conn.ttl, conn.addr_number);
3051 }
3052 gst_sdp_connection_clear (&conn);
3053 break;
3054 }
3055 case 'b':
3056 {
3057 gchar str2[32];
3058
3059 read_string_del (str, sizeof (str), ':', &p);
3060 if (*p != '\0')
3061 p++;
3062 read_string (str2, sizeof (str2), &p);
3063 if (c->state == SDP_SESSION)
3064 gst_sdp_message_add_bandwidth (c->msg, str, atoi (str2));
3065 else
3066 gst_sdp_media_add_bandwidth (c->media, str, atoi (str2));
3067 break;
3068 }
3069 case 't':
3070 break;
3071 case 'k':
3072 read_string_del (str, sizeof (str), ':', &p);
3073 if (*p != '\0')
3074 p++;
3075 if (c->state == SDP_SESSION)
3076 gst_sdp_message_set_key (c->msg, str, p);
3077 else
3078 gst_sdp_media_set_key (c->media, str, p);
3079 break;
3080 case 'a':
3081 read_string_del (str, sizeof (str), ':', &p);
3082 if (*p != '\0')
3083 p++;
3084 if (c->state == SDP_SESSION)
3085 gst_sdp_message_add_attribute (c->msg, str, p);
3086 else
3087 gst_sdp_media_add_attribute (c->media, str, p);
3088 break;
3089 case 'm':
3090 {
3091 gchar *slash;
3092 GstSDPMedia nmedia;
3093
3094 c->state = SDP_MEDIA;
3095 memset (&nmedia, 0, sizeof (nmedia));
3096 gst_sdp_media_init (&nmedia);
3097
3098 /* m=<media> <port>/<number of ports> <proto> <fmt> ... */
3099 READ_STRING (nmedia.media);
3100 read_string (str, sizeof (str), &p);
3101 slash = g_strrstr (str, "/");
3102 if (slash) {
3103 *slash = '\0';
3104 nmedia.port = atoi (str);
3105 nmedia.num_ports = atoi (slash + 1);
3106 } else {
3107 nmedia.port = atoi (str);
3108 nmedia.num_ports = 0;
3109 }
3110 READ_STRING (nmedia.proto);
3111 do {
3112 read_string (str, sizeof (str), &p);
3113 gst_sdp_media_add_format (&nmedia, str);
3114 } while (*p != '\0');
3115
3116 gst_sdp_message_add_media (c->msg, &nmedia);
3117 c->media =
3118 &g_array_index (c->msg->medias, GstSDPMedia, c->msg->medias->len - 1);
3119 break;
3120 }
3121 default:
3122 break;
3123 }
3124 return TRUE;
3125 }
3126
3127 /**
3128 * gst_sdp_message_parse_buffer:
3129 * @data: (array length=size): the start of the buffer
3130 * @size: the size of the buffer
3131 * @msg: the result #GstSDPMessage
3132 *
3133 * Parse the contents of @size bytes pointed to by @data and store the result in
3134 * @msg.
3135 *
3136 * Returns: #GST_SDP_OK on success.
3137 */
3138 GstSDPResult
gst_sdp_message_parse_buffer(const guint8 * data,guint size,GstSDPMessage * msg)3139 gst_sdp_message_parse_buffer (const guint8 * data, guint size,
3140 GstSDPMessage * msg)
3141 {
3142 gchar *p, *s;
3143 SDPContext c;
3144 gchar type;
3145 gchar *buffer = NULL;
3146 guint bufsize = 0;
3147 guint len = 0;
3148
3149 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
3150 g_return_val_if_fail (data != NULL, GST_SDP_EINVAL);
3151 g_return_val_if_fail (size != 0, GST_SDP_EINVAL);
3152
3153 c.state = SDP_SESSION;
3154 c.msg = msg;
3155 c.media = NULL;
3156
3157 #define SIZE_CHECK_GUARD \
3158 G_STMT_START { \
3159 if (p - (gchar *) data >= size) \
3160 goto out; \
3161 } G_STMT_END
3162
3163 p = (gchar *) data;
3164 while (TRUE) {
3165 while (p - (gchar *) data < size && g_ascii_isspace (*p))
3166 p++;
3167
3168 SIZE_CHECK_GUARD;
3169
3170 type = *p++;
3171 if (type == '\0')
3172 break;
3173
3174 SIZE_CHECK_GUARD;
3175
3176 if (*p != '=')
3177 goto line_done;
3178 p++;
3179
3180 SIZE_CHECK_GUARD;
3181
3182 s = p;
3183 while (p - (gchar *) data < size && *p != '\n' && *p != '\r' && *p != '\0')
3184 p++;
3185
3186 len = p - s;
3187 if (bufsize <= len) {
3188 buffer = g_realloc (buffer, len + 1);
3189 bufsize = len + 1;
3190 }
3191 memcpy (buffer, s, len);
3192 buffer[len] = '\0';
3193
3194 gst_sdp_parse_line (&c, type, buffer);
3195
3196 SIZE_CHECK_GUARD;
3197
3198 line_done:
3199 while (p - (gchar *) data < size && *p != '\n' && *p != '\0')
3200 p++;
3201
3202 SIZE_CHECK_GUARD;
3203
3204 if (*p == '\n')
3205 p++;
3206 }
3207
3208 #undef SIZE_CHECK_GUARD
3209
3210 out:
3211 g_free (buffer);
3212
3213 return GST_SDP_OK;
3214 }
3215
3216 static void
print_media(GstSDPMedia * media)3217 print_media (GstSDPMedia * media)
3218 {
3219 g_print (" media: '%s'\n", GST_STR_NULL (media->media));
3220 g_print (" port: '%u'\n", media->port);
3221 g_print (" num_ports: '%u'\n", media->num_ports);
3222 g_print (" proto: '%s'\n", GST_STR_NULL (media->proto));
3223 if (media->fmts->len > 0) {
3224 guint i;
3225
3226 g_print (" formats:\n");
3227 for (i = 0; i < media->fmts->len; i++) {
3228 g_print (" format '%s'\n", g_array_index (media->fmts, gchar *, i));
3229 }
3230 }
3231 g_print (" information: '%s'\n", GST_STR_NULL (media->information));
3232 if (media->connections->len > 0) {
3233 guint i;
3234
3235 g_print (" connections:\n");
3236 for (i = 0; i < media->connections->len; i++) {
3237 GstSDPConnection *conn =
3238 &g_array_index (media->connections, GstSDPConnection, i);
3239
3240 g_print (" nettype: '%s'\n", GST_STR_NULL (conn->nettype));
3241 g_print (" addrtype: '%s'\n", GST_STR_NULL (conn->addrtype));
3242 g_print (" address: '%s'\n", GST_STR_NULL (conn->address));
3243 g_print (" ttl: '%u'\n", conn->ttl);
3244 g_print (" addr_number: '%u'\n", conn->addr_number);
3245 }
3246 }
3247 if (media->bandwidths->len > 0) {
3248 guint i;
3249
3250 g_print (" bandwidths:\n");
3251 for (i = 0; i < media->bandwidths->len; i++) {
3252 GstSDPBandwidth *bw =
3253 &g_array_index (media->bandwidths, GstSDPBandwidth, i);
3254
3255 g_print (" type: '%s'\n", GST_STR_NULL (bw->bwtype));
3256 g_print (" bandwidth: '%u'\n", bw->bandwidth);
3257 }
3258 }
3259 g_print (" key:\n");
3260 g_print (" type: '%s'\n", GST_STR_NULL (media->key.type));
3261 g_print (" data: '%s'\n", GST_STR_NULL (media->key.data));
3262 if (media->attributes->len > 0) {
3263 guint i;
3264
3265 g_print (" attributes:\n");
3266 for (i = 0; i < media->attributes->len; i++) {
3267 GstSDPAttribute *attr =
3268 &g_array_index (media->attributes, GstSDPAttribute, i);
3269
3270 g_print (" attribute '%s' : '%s'\n", attr->key, attr->value);
3271 }
3272 }
3273 }
3274
3275 /**
3276 * gst_sdp_message_dump:
3277 * @msg: a #GstSDPMessage
3278 *
3279 * Dump the parsed contents of @msg to stdout.
3280 *
3281 * Returns: a #GstSDPResult.
3282 */
3283 GstSDPResult
gst_sdp_message_dump(const GstSDPMessage * msg)3284 gst_sdp_message_dump (const GstSDPMessage * msg)
3285 {
3286 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
3287
3288 g_print ("sdp packet %p:\n", msg);
3289 g_print (" version: '%s'\n", GST_STR_NULL (msg->version));
3290 g_print (" origin:\n");
3291 g_print (" username: '%s'\n", GST_STR_NULL (msg->origin.username));
3292 g_print (" sess_id: '%s'\n", GST_STR_NULL (msg->origin.sess_id));
3293 g_print (" sess_version: '%s'\n", GST_STR_NULL (msg->origin.sess_version));
3294 g_print (" nettype: '%s'\n", GST_STR_NULL (msg->origin.nettype));
3295 g_print (" addrtype: '%s'\n", GST_STR_NULL (msg->origin.addrtype));
3296 g_print (" addr: '%s'\n", GST_STR_NULL (msg->origin.addr));
3297 g_print (" session_name: '%s'\n", GST_STR_NULL (msg->session_name));
3298 g_print (" information: '%s'\n", GST_STR_NULL (msg->information));
3299 g_print (" uri: '%s'\n", GST_STR_NULL (msg->uri));
3300
3301 if (msg->emails->len > 0) {
3302 guint i;
3303
3304 g_print (" emails:\n");
3305 for (i = 0; i < msg->emails->len; i++) {
3306 g_print (" email '%s'\n", g_array_index (msg->emails, gchar *, i));
3307 }
3308 }
3309 if (msg->phones->len > 0) {
3310 guint i;
3311
3312 g_print (" phones:\n");
3313 for (i = 0; i < msg->phones->len; i++) {
3314 g_print (" phone '%s'\n", g_array_index (msg->phones, gchar *, i));
3315 }
3316 }
3317 g_print (" connection:\n");
3318 g_print (" nettype: '%s'\n", GST_STR_NULL (msg->connection.nettype));
3319 g_print (" addrtype: '%s'\n", GST_STR_NULL (msg->connection.addrtype));
3320 g_print (" address: '%s'\n", GST_STR_NULL (msg->connection.address));
3321 g_print (" ttl: '%u'\n", msg->connection.ttl);
3322 g_print (" addr_number: '%u'\n", msg->connection.addr_number);
3323 if (msg->bandwidths->len > 0) {
3324 guint i;
3325
3326 g_print (" bandwidths:\n");
3327 for (i = 0; i < msg->bandwidths->len; i++) {
3328 GstSDPBandwidth *bw =
3329 &g_array_index (msg->bandwidths, GstSDPBandwidth, i);
3330
3331 g_print (" type: '%s'\n", GST_STR_NULL (bw->bwtype));
3332 g_print (" bandwidth: '%u'\n", bw->bandwidth);
3333 }
3334 }
3335 g_print (" key:\n");
3336 g_print (" type: '%s'\n", GST_STR_NULL (msg->key.type));
3337 g_print (" data: '%s'\n", GST_STR_NULL (msg->key.data));
3338 if (msg->attributes->len > 0) {
3339 guint i;
3340
3341 g_print (" attributes:\n");
3342 for (i = 0; i < msg->attributes->len; i++) {
3343 GstSDPAttribute *attr =
3344 &g_array_index (msg->attributes, GstSDPAttribute, i);
3345
3346 g_print (" attribute '%s' : '%s'\n", attr->key, attr->value);
3347 }
3348 }
3349 if (msg->medias->len > 0) {
3350 guint i;
3351
3352 g_print (" medias:\n");
3353 for (i = 0; i < msg->medias->len; i++) {
3354 g_print (" media %u:\n", i);
3355 print_media (&g_array_index (msg->medias, GstSDPMedia, i));
3356 }
3357 }
3358 return GST_SDP_OK;
3359 }
3360
3361 static const gchar *
gst_sdp_get_attribute_for_pt(const GstSDPMedia * media,const gchar * name,gint pt)3362 gst_sdp_get_attribute_for_pt (const GstSDPMedia * media, const gchar * name,
3363 gint pt)
3364 {
3365 guint i;
3366
3367 for (i = 0;; i++) {
3368 const gchar *attr;
3369 gint val;
3370
3371 if ((attr = gst_sdp_media_get_attribute_val_n (media, name, i)) == NULL)
3372 break;
3373
3374 if (sscanf (attr, "%d ", &val) != 1)
3375 continue;
3376
3377 if (val == pt)
3378 return attr;
3379 }
3380 return NULL;
3381 }
3382
3383 /* this may modify the input string (and resets) */
3384 #define PARSE_INT(p, del, res) \
3385 G_STMT_START { \
3386 gchar *t = p; \
3387 p = strstr (p, del); \
3388 if (p == NULL) \
3389 res = -1; \
3390 else { \
3391 char prev = *p; \
3392 *p = '\0'; \
3393 res = atoi (t); \
3394 *p = prev; \
3395 p++; \
3396 } \
3397 } G_STMT_END
3398
3399 /* this may modify the string without reset */
3400 #define PARSE_STRING(p, del, res) \
3401 G_STMT_START { \
3402 gchar *t = p; \
3403 p = strstr (p, del); \
3404 if (p == NULL) { \
3405 res = NULL; \
3406 p = t; \
3407 } \
3408 else { \
3409 *p = '\0'; \
3410 p++; \
3411 res = t; \
3412 } \
3413 } G_STMT_END
3414
3415 #define SKIP_SPACES(p) \
3416 while (*p && g_ascii_isspace (*p)) \
3417 p++;
3418
3419 /* rtpmap contains:
3420 *
3421 * <payload> <encoding_name>/<clock_rate>[/<encoding_params>]
3422 */
3423 static gboolean
gst_sdp_parse_rtpmap(const gchar * rtpmap,gint * payload,gchar ** name,gint * rate,gchar ** params)3424 gst_sdp_parse_rtpmap (const gchar * rtpmap, gint * payload, gchar ** name,
3425 gint * rate, gchar ** params)
3426 {
3427 gchar *p, *t, *orig_value;
3428
3429 p = orig_value = g_strdup (rtpmap);
3430
3431 PARSE_INT (p, " ", *payload);
3432 if (*payload == -1)
3433 goto fail;
3434
3435 SKIP_SPACES (p);
3436 if (*p == '\0')
3437 goto fail;
3438
3439 PARSE_STRING (p, "/", *name);
3440 if (*name == NULL) {
3441 GST_DEBUG ("no rate, name %s", p);
3442 /* no rate, assume -1 then, this is not supposed to happen but RealMedia
3443 * streams seem to omit the rate. */
3444 *name = g_strdup (p);
3445 *rate = -1;
3446 *params = NULL;
3447 goto out;
3448 } else {
3449 *name = strdup (*name);
3450 }
3451
3452 t = p;
3453 p = strstr (p, "/");
3454 if (p == NULL) {
3455 *rate = atoi (t);
3456 *params = NULL;
3457 goto out;
3458 }
3459 p++;
3460 *rate = atoi (t);
3461
3462 if (*p == '\0')
3463 *params = NULL;
3464 else
3465 *params = g_strdup (p);
3466
3467 out:
3468 g_free (orig_value);
3469 return TRUE;
3470
3471 fail:
3472 g_free (orig_value);
3473 return FALSE;
3474 }
3475
3476 /**
3477 * gst_sdp_media_add_rtcp_fb_attributes_from_media:
3478 * @media: a #GstSDPMedia
3479 * @pt: payload type
3480 * @caps: a #GstCaps
3481 *
3482 * Parse given @media for "rtcp-fb" attributes and add it to the @caps.
3483 *
3484 * Mapping of caps from SDP fields:
3485 *
3486 * a=rtcp-fb:(payload) (param1) [param2]...
3487 *
3488 * Returns: a #GstSDPResult.
3489 */
3490 static GstSDPResult
gst_sdp_media_add_rtcp_fb_attributes_from_media(const GstSDPMedia * media,gint pt,GstCaps * caps)3491 gst_sdp_media_add_rtcp_fb_attributes_from_media (const GstSDPMedia * media,
3492 gint pt, GstCaps * caps)
3493 {
3494 const gchar *rtcp_fb;
3495 gchar *p, *to_free;
3496 gint payload, i;
3497 GstStructure *s;
3498
3499 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
3500 g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL);
3501
3502 s = gst_caps_get_structure (caps, 0);
3503
3504 for (i = 0;; i++) {
3505 gboolean all_formats = FALSE;
3506
3507 if ((rtcp_fb = gst_sdp_media_get_attribute_val_n (media,
3508 "rtcp-fb", i)) == NULL)
3509 break;
3510
3511 /* p is now of the format <payload> attr... */
3512 to_free = p = g_strdup (rtcp_fb);
3513
3514 /* check if it applies to all formats */
3515 if (*p == '*') {
3516 p++;
3517 all_formats = TRUE;
3518 } else {
3519 PARSE_INT (p, " ", payload);
3520 }
3521
3522 if (all_formats || (payload != -1 && payload == pt)) {
3523 gchar *tmp, *key;
3524
3525 SKIP_SPACES (p);
3526
3527 /* replace space with '-' */
3528 key = g_strdup_printf ("rtcp-fb-%s", p);
3529
3530 for (tmp = key; (tmp = strchr (tmp, ' ')); ++tmp) {
3531 *tmp = '-';
3532 }
3533
3534 gst_structure_set (s, key, G_TYPE_BOOLEAN, TRUE, NULL);
3535 GST_DEBUG ("adding caps: %s=TRUE", key);
3536 g_free (key);
3537 }
3538 g_free (to_free);
3539 }
3540 return GST_SDP_OK;
3541 }
3542
3543 /**
3544 * gst_sdp_media_get_caps_from_media:
3545 * @media: a #GstSDPMedia
3546 * @pt: a payload type
3547 *
3548 * Mapping of caps from SDP fields:
3549 *
3550 * a=rtpmap:(payload) (encoding_name)/(clock_rate)[/(encoding_params)]
3551 *
3552 * a=framesize:(payload) (width)-(height)
3553 *
3554 * a=fmtp:(payload) (param)[=(value)];...
3555 *
3556 * Returns: a #GstCaps, or %NULL if an error happened
3557 *
3558 * Since: 1.8
3559 */
3560 GstCaps *
gst_sdp_media_get_caps_from_media(const GstSDPMedia * media,gint pt)3561 gst_sdp_media_get_caps_from_media (const GstSDPMedia * media, gint pt)
3562 {
3563 GstCaps *caps;
3564 const gchar *rtpmap;
3565 gchar *fmtp = NULL;
3566 gchar *framesize = NULL;
3567 gchar *name = NULL;
3568 gint rate = -1;
3569 gchar *params = NULL;
3570 gchar *tmp;
3571 GstStructure *s;
3572 gint payload = 0;
3573 gboolean ret;
3574
3575 g_return_val_if_fail (media != NULL, NULL);
3576
3577 /* get and parse rtpmap */
3578 rtpmap = gst_sdp_get_attribute_for_pt (media, "rtpmap", pt);
3579
3580 if (rtpmap) {
3581 ret = gst_sdp_parse_rtpmap (rtpmap, &payload, &name, &rate, ¶ms);
3582 if (!ret) {
3583 GST_ERROR ("error parsing rtpmap, ignoring");
3584 rtpmap = NULL;
3585 }
3586 }
3587 /* dynamic payloads need rtpmap or we fail */
3588 if (rtpmap == NULL && pt >= 96)
3589 goto no_rtpmap;
3590
3591 /* check if we have a rate, if not, we need to look up the rate from the
3592 * default rates based on the payload types. */
3593 if (rate == -1) {
3594 const GstRTPPayloadInfo *info;
3595
3596 if (GST_RTP_PAYLOAD_IS_DYNAMIC (pt)) {
3597 /* dynamic types, use media and encoding_name */
3598 tmp = g_ascii_strdown (media->media, -1);
3599 info = gst_rtp_payload_info_for_name (tmp, name);
3600 g_free (tmp);
3601 } else {
3602 /* static types, use payload type */
3603 info = gst_rtp_payload_info_for_pt (pt);
3604 }
3605
3606 if (info) {
3607 if ((rate = info->clock_rate) == 0)
3608 rate = -1;
3609 }
3610 /* we fail if we cannot find one */
3611 if (rate == -1)
3612 goto no_rate;
3613 }
3614
3615 tmp = g_ascii_strdown (media->media, -1);
3616 caps = gst_caps_new_simple ("application/x-unknown",
3617 "media", G_TYPE_STRING, tmp, "payload", G_TYPE_INT, pt, NULL);
3618 g_free (tmp);
3619 s = gst_caps_get_structure (caps, 0);
3620
3621 gst_structure_set (s, "clock-rate", G_TYPE_INT, rate, NULL);
3622
3623 /* encoding name must be upper case */
3624 if (name != NULL) {
3625 tmp = g_ascii_strup (name, -1);
3626 gst_structure_set (s, "encoding-name", G_TYPE_STRING, tmp, NULL);
3627 g_free (tmp);
3628 }
3629
3630 /* params must be lower case */
3631 if (params != NULL) {
3632 tmp = g_ascii_strdown (params, -1);
3633 gst_structure_set (s, "encoding-params", G_TYPE_STRING, tmp, NULL);
3634 g_free (tmp);
3635 }
3636
3637 /* parse optional fmtp: field */
3638 if ((fmtp = g_strdup (gst_sdp_get_attribute_for_pt (media, "fmtp", pt)))) {
3639 gchar *p;
3640 gint payload = 0;
3641
3642 p = fmtp;
3643
3644 /* p is now of the format <payload> <param>[=<value>];... */
3645 PARSE_INT (p, " ", payload);
3646 if (payload != -1 && payload == pt) {
3647 gchar **pairs;
3648 gint i;
3649
3650 /* <param>[=<value>] are separated with ';' */
3651 pairs = g_strsplit (p, ";", 0);
3652 for (i = 0; pairs[i]; i++) {
3653 gchar *valpos;
3654 const gchar *val, *key;
3655 gint j;
3656 const gchar *reserved_keys[] =
3657 { "media", "payload", "clock-rate", "encoding-name",
3658 "encoding-params"
3659 };
3660
3661 /* the key may not have a '=', the value can have other '='s */
3662 valpos = strstr (pairs[i], "=");
3663 if (valpos) {
3664 /* we have a '=' and thus a value, remove the '=' with \0 */
3665 *valpos = '\0';
3666 /* value is everything between '=' and ';'. We split the pairs at ;
3667 * boundaries so we can take the remainder of the value. Some servers
3668 * put spaces around the value which we strip off here. Alternatively
3669 * we could strip those spaces in the depayloaders should these spaces
3670 * actually carry any meaning in the future. */
3671 val = g_strstrip (valpos + 1);
3672 } else {
3673 /* simple <param>;.. is translated into <param>=1;... */
3674 val = "1";
3675 }
3676 /* strip the key of spaces, convert key to lowercase but not the value. */
3677 key = g_strstrip (pairs[i]);
3678
3679 /* skip keys from the fmtp, which we already use ourselves for the
3680 * caps. Some software is adding random things like clock-rate into
3681 * the fmtp, and we would otherwise here set a string-typed clock-rate
3682 * in the caps... and thus fail to create valid RTP caps
3683 */
3684 for (j = 0; j < G_N_ELEMENTS (reserved_keys); j++) {
3685 if (g_ascii_strcasecmp (reserved_keys[j], key) == 0) {
3686 key = "";
3687 break;
3688 }
3689 }
3690
3691 if (strlen (key) > 1) {
3692 tmp = g_ascii_strdown (key, -1);
3693 gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL);
3694 g_free (tmp);
3695 }
3696 }
3697 g_strfreev (pairs);
3698 }
3699 }
3700
3701 /* parse framesize: field */
3702 if ((framesize =
3703 g_strdup (gst_sdp_media_get_attribute_val (media, "framesize")))) {
3704 gchar *p;
3705
3706 /* p is now of the format <payload> <width>-<height> */
3707 p = framesize;
3708
3709 PARSE_INT (p, " ", payload);
3710 if (payload != -1 && payload == pt) {
3711 gst_structure_set (s, "a-framesize", G_TYPE_STRING, p, NULL);
3712 }
3713 }
3714
3715 /* parse rtcp-fb: field */
3716 gst_sdp_media_add_rtcp_fb_attributes_from_media (media, pt, caps);
3717
3718 out:
3719 g_free (framesize);
3720 g_free (fmtp);
3721 g_free (name);
3722 g_free (params);
3723 return caps;
3724
3725 /* ERRORS */
3726 no_rtpmap:
3727 {
3728 GST_ERROR ("rtpmap type not given for dynamic payload %d", pt);
3729 caps = NULL;
3730 goto out;
3731 }
3732 no_rate:
3733 {
3734 GST_ERROR ("rate unknown for payload type %d", pt);
3735 caps = NULL;
3736 goto out;
3737 }
3738 }
3739
3740 /**
3741 * gst_sdp_media_set_media_from_caps:
3742 * @caps: a #GstCaps
3743 * @media: a #GstSDPMedia
3744 *
3745 * Mapping of caps to SDP fields:
3746 *
3747 * a=rtpmap:(payload) (encoding_name) or (clock_rate)[or (encoding_params)]
3748 *
3749 * a=framesize:(payload) (width)-(height)
3750 *
3751 * a=fmtp:(payload) (param)[=(value)];...
3752 *
3753 * a=rtcp-fb:(payload) (param1) [param2]...
3754 *
3755 * Returns: a #GstSDPResult.
3756 *
3757 * Since: 1.8
3758 */
3759 GstSDPResult
gst_sdp_media_set_media_from_caps(const GstCaps * caps,GstSDPMedia * media)3760 gst_sdp_media_set_media_from_caps (const GstCaps * caps, GstSDPMedia * media)
3761 {
3762 const gchar *caps_str, *caps_enc, *caps_params;
3763 gchar *tmp;
3764 gint caps_pt, caps_rate;
3765 guint n_fields, j;
3766 gboolean first, nack, nack_pli, ccm_fir;
3767 GString *fmtp;
3768 GstStructure *s;
3769
3770 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
3771 g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL);
3772
3773 s = gst_caps_get_structure (caps, 0);
3774 if (s == NULL) {
3775 GST_ERROR ("ignoring stream without media type");
3776 goto error;
3777 }
3778
3779 /* get media type and payload for the m= line */
3780 caps_str = gst_structure_get_string (s, "media");
3781 gst_sdp_media_set_media (media, caps_str);
3782
3783 gst_structure_get_int (s, "payload", &caps_pt);
3784 tmp = g_strdup_printf ("%d", caps_pt);
3785 gst_sdp_media_add_format (media, tmp);
3786 g_free (tmp);
3787
3788 /* get clock-rate, media type and params for the rtpmap attribute */
3789 gst_structure_get_int (s, "clock-rate", &caps_rate);
3790 caps_enc = gst_structure_get_string (s, "encoding-name");
3791 caps_params = gst_structure_get_string (s, "encoding-params");
3792
3793 if (caps_enc) {
3794 if (caps_params)
3795 tmp = g_strdup_printf ("%d %s/%d/%s", caps_pt, caps_enc, caps_rate,
3796 caps_params);
3797 else
3798 tmp = g_strdup_printf ("%d %s/%d", caps_pt, caps_enc, caps_rate);
3799
3800 gst_sdp_media_add_attribute (media, "rtpmap", tmp);
3801 g_free (tmp);
3802 }
3803
3804 /* get rtcp-fb attributes */
3805 if (gst_structure_get_boolean (s, "rtcp-fb-nack", &nack)) {
3806 if (nack) {
3807 tmp = g_strdup_printf ("%d nack", caps_pt);
3808 gst_sdp_media_add_attribute (media, "rtcp-fb", tmp);
3809 g_free (tmp);
3810 GST_DEBUG ("adding rtcp-fb-nack to pt=%d", caps_pt);
3811 }
3812 }
3813
3814 if (gst_structure_get_boolean (s, "rtcp-fb-nack-pli", &nack_pli)) {
3815 if (nack_pli) {
3816 tmp = g_strdup_printf ("%d nack pli", caps_pt);
3817 gst_sdp_media_add_attribute (media, "rtcp-fb", tmp);
3818 g_free (tmp);
3819 GST_DEBUG ("adding rtcp-fb-nack-pli to pt=%d", caps_pt);
3820 }
3821 }
3822
3823 if (gst_structure_get_boolean (s, "rtcp-fb-ccm-fir", &ccm_fir)) {
3824 if (ccm_fir) {
3825 tmp = g_strdup_printf ("%d ccm fir", caps_pt);
3826 gst_sdp_media_add_attribute (media, "rtcp-fb", tmp);
3827 g_free (tmp);
3828 GST_DEBUG ("adding rtcp-fb-ccm-fir to pt=%d", caps_pt);
3829 }
3830 }
3831
3832 /* collect all other properties and add them to fmtp or attributes */
3833 fmtp = g_string_new ("");
3834 g_string_append_printf (fmtp, "%d ", caps_pt);
3835 first = TRUE;
3836 n_fields = gst_structure_n_fields (s);
3837 for (j = 0; j < n_fields; j++) {
3838 const gchar *fname, *fval;
3839
3840 fname = gst_structure_nth_field_name (s, j);
3841
3842 /* filter out standard properties */
3843 if (!strcmp (fname, "media"))
3844 continue;
3845 if (!strcmp (fname, "payload"))
3846 continue;
3847 if (!strcmp (fname, "clock-rate"))
3848 continue;
3849 if (!strcmp (fname, "encoding-name"))
3850 continue;
3851 if (!strcmp (fname, "encoding-params"))
3852 continue;
3853 if (!strcmp (fname, "ssrc"))
3854 continue;
3855 if (!strcmp (fname, "timestamp-offset"))
3856 continue;
3857 if (!strcmp (fname, "seqnum-offset"))
3858 continue;
3859 if (g_str_has_prefix (fname, "srtp-"))
3860 continue;
3861 if (g_str_has_prefix (fname, "srtcp-"))
3862 continue;
3863 /* handled later */
3864 if (g_str_has_prefix (fname, "x-gst-rtsp-server-rtx-time"))
3865 continue;
3866 if (g_str_has_prefix (fname, "rtcp-fb-"))
3867 continue;
3868
3869 if (!strcmp (fname, "a-framesize")) {
3870 /* a-framesize attribute */
3871 if ((fval = gst_structure_get_string (s, fname))) {
3872 tmp = g_strdup_printf ("%d %s", caps_pt, fval);
3873 gst_sdp_media_add_attribute (media, fname + 2, tmp);
3874 g_free (tmp);
3875 }
3876 continue;
3877 }
3878
3879 if (g_str_has_prefix (fname, "a-")) {
3880 /* attribute */
3881 if ((fval = gst_structure_get_string (s, fname)))
3882 gst_sdp_media_add_attribute (media, fname + 2, fval);
3883 continue;
3884 }
3885 if (g_str_has_prefix (fname, "x-")) {
3886 /* attribute */
3887 if ((fval = gst_structure_get_string (s, fname)))
3888 gst_sdp_media_add_attribute (media, fname, fval);
3889 continue;
3890 }
3891
3892 if ((fval = gst_structure_get_string (s, fname))) {
3893 g_string_append_printf (fmtp, "%s%s=%s", first ? "" : ";", fname, fval);
3894 first = FALSE;
3895 }
3896 }
3897
3898 if (!first) {
3899 tmp = g_string_free (fmtp, FALSE);
3900 gst_sdp_media_add_attribute (media, "fmtp", tmp);
3901 g_free (tmp);
3902 } else {
3903 g_string_free (fmtp, TRUE);
3904 }
3905
3906 return GST_SDP_OK;
3907
3908 /* ERRORS */
3909 error:
3910 {
3911 GST_DEBUG ("ignoring stream");
3912 return GST_SDP_EINVAL;
3913 }
3914 }
3915
3916 /**
3917 * gst_sdp_make_keymgmt:
3918 * @uri: a #gchar URI
3919 * @base64: a #gchar base64-encoded key data
3920 *
3921 * Makes key management data
3922 *
3923 * Returns: (transfer full): a #gchar key-mgmt data,
3924 *
3925 * Since: 1.8
3926 */
3927 gchar *
gst_sdp_make_keymgmt(const gchar * uri,const gchar * base64)3928 gst_sdp_make_keymgmt (const gchar * uri, const gchar * base64)
3929 {
3930 g_return_val_if_fail (uri != NULL, NULL);
3931 g_return_val_if_fail (base64 != NULL, NULL);
3932
3933 return g_strdup_printf ("prot=mikey;uri=\"%s\";data=\"%s\"", uri, base64);
3934 }
3935
3936 static gboolean
gst_sdp_parse_keymgmt(const gchar * keymgmt,GstMIKEYMessage ** mikey)3937 gst_sdp_parse_keymgmt (const gchar * keymgmt, GstMIKEYMessage ** mikey)
3938 {
3939 gsize size;
3940 guchar *data;
3941 gchar *orig_value;
3942 gchar *p, *kmpid;
3943
3944 p = orig_value = g_strdup (keymgmt);
3945
3946 SKIP_SPACES (p);
3947 if (*p == '\0') {
3948 g_free (orig_value);
3949 return FALSE;
3950 }
3951
3952 PARSE_STRING (p, " ", kmpid);
3953 if (kmpid == NULL || !g_str_equal (kmpid, "mikey")) {
3954 g_free (orig_value);
3955 return FALSE;
3956 }
3957 data = g_base64_decode (p, &size);
3958 g_free (orig_value); /* Don't need this any more */
3959
3960 if (data == NULL)
3961 return FALSE;
3962
3963 *mikey = gst_mikey_message_new_from_data (data, size, NULL, NULL);
3964 g_free (data);
3965
3966 return (*mikey != NULL);
3967 }
3968
3969 static GstSDPResult
sdp_add_attributes_to_keymgmt(GArray * attributes,GstMIKEYMessage ** mikey)3970 sdp_add_attributes_to_keymgmt (GArray * attributes, GstMIKEYMessage ** mikey)
3971 {
3972 GstSDPResult res = GST_SDP_OK;
3973
3974 if (attributes->len > 0) {
3975 guint i;
3976 for (i = 0; i < attributes->len; i++) {
3977 GstSDPAttribute *attr = &g_array_index (attributes, GstSDPAttribute, i);
3978
3979 if (g_str_equal (attr->key, "key-mgmt")) {
3980 res = gst_sdp_parse_keymgmt (attr->value, mikey);
3981 break;
3982 }
3983 }
3984 }
3985
3986 return res;
3987 }
3988
3989 /**
3990 * gst_sdp_message_parse_keymgmt:
3991 * @msg: a #GstSDPMessage
3992 * @mikey: (out) (transfer full): pointer to new #GstMIKEYMessage
3993 *
3994 * Creates a new #GstMIKEYMessage after parsing the key-mgmt attribute
3995 * from a #GstSDPMessage.
3996 *
3997 * Returns: a #GstSDPResult.
3998 *
3999 * Since: 1.8.1
4000 */
4001 GstSDPResult
gst_sdp_message_parse_keymgmt(const GstSDPMessage * msg,GstMIKEYMessage ** mikey)4002 gst_sdp_message_parse_keymgmt (const GstSDPMessage * msg,
4003 GstMIKEYMessage ** mikey)
4004 {
4005 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
4006
4007 return sdp_add_attributes_to_keymgmt (msg->attributes, mikey);
4008 }
4009
4010 /**
4011 * gst_sdp_media_parse_keymgmt:
4012 * @media: a #GstSDPMedia
4013 * @mikey: (out) (transfer full): pointer to new #GstMIKEYMessage
4014 *
4015 * Creates a new #GstMIKEYMessage after parsing the key-mgmt attribute
4016 * from a #GstSDPMedia.
4017 *
4018 * Returns: a #GstSDPResult.
4019 *
4020 * Since: 1.8.1
4021 */
4022 GstSDPResult
gst_sdp_media_parse_keymgmt(const GstSDPMedia * media,GstMIKEYMessage ** mikey)4023 gst_sdp_media_parse_keymgmt (const GstSDPMedia * media,
4024 GstMIKEYMessage ** mikey)
4025 {
4026 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
4027
4028 return sdp_add_attributes_to_keymgmt (media->attributes, mikey);
4029 }
4030
4031 static GstSDPResult
sdp_add_attributes_to_caps(GArray * attributes,GstCaps * caps)4032 sdp_add_attributes_to_caps (GArray * attributes, GstCaps * caps)
4033 {
4034 if (attributes->len > 0) {
4035 GstStructure *s;
4036 guint i;
4037
4038 s = gst_caps_get_structure (caps, 0);
4039
4040 for (i = 0; i < attributes->len; i++) {
4041 GstSDPAttribute *attr = &g_array_index (attributes, GstSDPAttribute, i);
4042 gchar *tofree, *key;
4043
4044 key = attr->key;
4045
4046 /* skip some of the attribute we already handle */
4047 if (!strcmp (key, "fmtp"))
4048 continue;
4049 if (!strcmp (key, "rtpmap"))
4050 continue;
4051 if (!strcmp (key, "control"))
4052 continue;
4053 if (!strcmp (key, "range"))
4054 continue;
4055 if (!strcmp (key, "framesize"))
4056 continue;
4057 if (!strcmp (key, "key-mgmt"))
4058 continue;
4059
4060 /* string must be valid UTF8 */
4061 if (!g_utf8_validate (attr->value, -1, NULL))
4062 continue;
4063
4064 if (!g_str_has_prefix (key, "x-"))
4065 tofree = key = g_strdup_printf ("a-%s", key);
4066 else
4067 tofree = NULL;
4068
4069 GST_DEBUG ("adding caps: %s=%s", key, attr->value);
4070 gst_structure_set (s, key, G_TYPE_STRING, attr->value, NULL);
4071 g_free (tofree);
4072 }
4073 }
4074
4075 return GST_SDP_OK;
4076 }
4077
4078 /**
4079 * gst_sdp_message_attributes_to_caps:
4080 * @msg: a #GstSDPMessage
4081 * @caps: a #GstCaps
4082 *
4083 * Mapping of attributes of #GstSDPMessage to #GstCaps
4084 *
4085 * Returns: a #GstSDPResult.
4086 *
4087 * Since: 1.8
4088 */
4089 GstSDPResult
gst_sdp_message_attributes_to_caps(const GstSDPMessage * msg,GstCaps * caps)4090 gst_sdp_message_attributes_to_caps (const GstSDPMessage * msg, GstCaps * caps)
4091 {
4092 GstSDPResult res;
4093 GstMIKEYMessage *mikey = NULL;
4094
4095 g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
4096 g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL);
4097
4098 gst_sdp_message_parse_keymgmt (msg, &mikey);
4099 if (mikey) {
4100 if (gst_mikey_message_to_caps (mikey, caps)) {
4101 res = GST_SDP_EINVAL;
4102 goto done;
4103 }
4104 }
4105
4106 res = sdp_add_attributes_to_caps (msg->attributes, caps);
4107
4108 done:
4109 if (mikey)
4110 gst_mikey_message_unref (mikey);
4111 return res;
4112 }
4113
4114 /**
4115 * gst_sdp_media_attributes_to_caps:
4116 * @media: a #GstSDPMedia
4117 * @caps: a #GstCaps
4118 *
4119 * Mapping of attributes of #GstSDPMedia to #GstCaps
4120 *
4121 * Returns: a #GstSDPResult.
4122 *
4123 * Since: 1.8
4124 */
4125 GstSDPResult
gst_sdp_media_attributes_to_caps(const GstSDPMedia * media,GstCaps * caps)4126 gst_sdp_media_attributes_to_caps (const GstSDPMedia * media, GstCaps * caps)
4127 {
4128 GstSDPResult res;
4129 GstMIKEYMessage *mikey = NULL;
4130
4131 g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
4132 g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL);
4133
4134 gst_sdp_media_parse_keymgmt (media, &mikey);
4135 if (mikey) {
4136 if (!gst_mikey_message_to_caps (mikey, caps)) {
4137 res = GST_SDP_EINVAL;
4138 goto done;
4139 }
4140 }
4141
4142 res = sdp_add_attributes_to_caps (media->attributes, caps);
4143
4144 done:
4145 if (mikey)
4146 gst_mikey_message_unref (mikey);
4147 return res;
4148 }
4149