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