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