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