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