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