Initialize Tizen 2.3
[framework/multimedia/gst-plugins-base0.10.git] / wearable / 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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, 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  * Last reviewed on 2007-07-24 (0.10.14)
55  */
56
57 #ifdef HAVE_CONFIG_H
58 #include "config.h"
59 #endif
60
61 #include <stdlib.h>
62 #include <string.h>
63
64 #ifdef HAVE_SYS_TYPES_H
65 #include <sys/types.h>
66 #endif
67
68 #include <glib.h>               /* for G_OS_WIN32 */
69 #include <gst/gstinfo.h>        /* For GST_STR_NULL */
70
71 #ifdef G_OS_WIN32
72 /* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
73  * minwg32 headers check WINVER before allowing the use of these */
74 #ifndef WINVER
75 #define WINVER 0x0501
76 #endif
77 #ifdef _MSC_VER
78 #include <Winsock2.h>
79 #endif
80 #include <ws2tcpip.h>
81 #else
82 #include <sys/socket.h>
83 #include <netdb.h>
84 #include <netinet/in.h>
85 #endif
86
87 #include "gstsdpmessage.h"
88
89 /* FIXME, is currently allocated on the stack */
90 #define MAX_LINE_LEN    1024 * 16
91
92 #define FREE_STRING(field)              g_free (field); (field) = NULL
93 #define REPLACE_STRING(field, val)      FREE_STRING(field); (field) = g_strdup (val)
94
95 #define INIT_ARRAY(field, type, init_func)              \
96 G_STMT_START {                                          \
97   if (field) {                                          \
98     guint i;                                            \
99     for(i = 0; i < (field)->len; i++)                   \
100       init_func (&g_array_index ((field), type, i));    \
101     g_array_set_size ((field), 0);                      \
102   }                                                     \
103   else                                                  \
104     (field) = g_array_new (FALSE, TRUE, sizeof (type)); \
105 } G_STMT_END
106
107 #define FREE_ARRAY(field)         \
108 G_STMT_START {                    \
109   if (field)                      \
110     g_array_free ((field), TRUE); \
111   (field) = NULL;                 \
112 } G_STMT_END
113
114 #define INIT_PTR_ARRAY(field, type, init_func)          \
115 G_STMT_START {                                          \
116   if (field) {                                          \
117     guint i;                                            \
118     for(i = 0; i < (field)->len; i++)                   \
119       init_func (g_array_index ((field), type, i));     \
120     g_array_set_size ((field), 0);                      \
121   }                                                     \
122   else                                                  \
123     (field) = g_array_new (FALSE, TRUE, sizeof (type)); \
124 } G_STMT_END
125
126 #define FREE_PTR_ARRAY(field) FREE_ARRAY(field)
127
128 #define DEFINE_STRING_SETTER(field)                                     \
129 GstSDPResult gst_sdp_message_set_##field (GstSDPMessage *msg, const gchar *val) { \
130   g_free (msg->field);                                                  \
131   msg->field = g_strdup (val);                                          \
132   return GST_SDP_OK;                                                    \
133 }
134 #define DEFINE_STRING_GETTER(field)                                     \
135 const gchar* gst_sdp_message_get_##field (const GstSDPMessage *msg) {   \
136   return msg->field;                                                    \
137 }
138
139 #define DEFINE_ARRAY_LEN(field)                                         \
140 guint gst_sdp_message_##field##_len (const GstSDPMessage *msg) {        \
141   return msg->field->len;                                               \
142 }
143 #define DEFINE_ARRAY_GETTER(method, field, type)                        \
144 type * gst_sdp_message_get_##method (const GstSDPMessage *msg, guint idx) {  \
145   return &g_array_index (msg->field, type, idx);                        \
146 }
147
148 #define DEFINE_PTR_ARRAY_LEN(field) DEFINE_ARRAY_LEN(field)
149 #define DEFINE_PTR_ARRAY_GETTER(method, field, type)                    \
150 type gst_sdp_message_get_##method (const GstSDPMessage *msg, guint idx) {    \
151   return g_array_index (msg->field, type, idx);                         \
152 }
153 #define DEFINE_PTR_ARRAY_ADDER(method, field, type, dup_method)         \
154 GstSDPResult gst_sdp_message_add_##method (GstSDPMessage *msg, type val) {   \
155   type v = dup_method (val);                                            \
156   g_array_append_val (msg->field, v);                                   \
157   return GST_SDP_OK;                                                    \
158 }
159
160 static void
161 gst_sdp_origin_init (GstSDPOrigin * origin)
162 {
163   FREE_STRING (origin->username);
164   FREE_STRING (origin->sess_id);
165   FREE_STRING (origin->sess_version);
166   FREE_STRING (origin->nettype);
167   FREE_STRING (origin->addrtype);
168   FREE_STRING (origin->addr);
169 }
170
171 static void
172 gst_sdp_connection_init (GstSDPConnection * connection)
173 {
174   FREE_STRING (connection->nettype);
175   FREE_STRING (connection->addrtype);
176   FREE_STRING (connection->address);
177   connection->ttl = 0;
178   connection->addr_number = 0;
179 }
180
181 static void
182 gst_sdp_bandwidth_init (GstSDPBandwidth * bandwidth)
183 {
184   FREE_STRING (bandwidth->bwtype);
185   bandwidth->bandwidth = 0;
186 }
187
188 static void
189 gst_sdp_time_init (GstSDPTime * t)
190 {
191   FREE_STRING (t->start);
192   FREE_STRING (t->stop);
193   INIT_PTR_ARRAY (t->repeat, gchar *, g_free);
194   FREE_PTR_ARRAY (t->repeat);
195 }
196
197 static void
198 gst_sdp_zone_init (GstSDPZone * zone)
199 {
200   FREE_STRING (zone->time);
201   FREE_STRING (zone->typed_time);
202 }
203
204 static void
205 gst_sdp_key_init (GstSDPKey * key)
206 {
207   FREE_STRING (key->type);
208   FREE_STRING (key->data);
209 }
210
211 static void
212 gst_sdp_attribute_init (GstSDPAttribute * attr)
213 {
214   FREE_STRING (attr->key);
215   FREE_STRING (attr->value);
216 }
217
218 /**
219  * gst_sdp_message_new:
220  * @msg: pointer to new #GstSDPMessage
221  *
222  * Allocate a new GstSDPMessage and store the result in @msg.
223  *
224  * Returns: a #GstSDPResult.
225  */
226 GstSDPResult
227 gst_sdp_message_new (GstSDPMessage ** msg)
228 {
229   GstSDPMessage *newmsg;
230
231   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
232
233   newmsg = g_new0 (GstSDPMessage, 1);
234
235   *msg = newmsg;
236
237   return gst_sdp_message_init (newmsg);
238 }
239
240 /**
241  * gst_sdp_message_init:
242  * @msg: a #GstSDPMessage
243  *
244  * Initialize @msg so that its contents are as if it was freshly allocated
245  * with gst_sdp_message_new(). This function is mostly used to initialize a message
246  * allocated on the stack. gst_sdp_message_uninit() undoes this operation.
247  *
248  * When this function is invoked on newly allocated data (with malloc or on the
249  * stack), its contents should be set to 0 before calling this function.
250  *
251  * Returns: a #GstSDPResult.
252  */
253 GstSDPResult
254 gst_sdp_message_init (GstSDPMessage * msg)
255 {
256   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
257
258   FREE_STRING (msg->version);
259   gst_sdp_origin_init (&msg->origin);
260   FREE_STRING (msg->session_name);
261   FREE_STRING (msg->information);
262   FREE_STRING (msg->uri);
263   INIT_PTR_ARRAY (msg->emails, gchar *, g_free);
264   INIT_PTR_ARRAY (msg->phones, gchar *, g_free);
265   gst_sdp_connection_init (&msg->connection);
266   INIT_ARRAY (msg->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_init);
267   INIT_ARRAY (msg->times, GstSDPTime, gst_sdp_time_init);
268   INIT_ARRAY (msg->zones, GstSDPZone, gst_sdp_zone_init);
269   gst_sdp_key_init (&msg->key);
270   INIT_ARRAY (msg->attributes, GstSDPAttribute, gst_sdp_attribute_init);
271   INIT_ARRAY (msg->medias, GstSDPMedia, gst_sdp_media_uninit);
272
273   return GST_SDP_OK;
274 }
275
276 /**
277  * gst_sdp_message_uninit:
278  * @msg: a #GstSDPMessage
279  *
280  * Free all resources allocated in @msg. @msg should not be used anymore after
281  * this function. This function should be used when @msg was allocated on the
282  * stack and initialized with gst_sdp_message_init().
283  *
284  * Returns: a #GstSDPResult.
285  */
286 GstSDPResult
287 gst_sdp_message_uninit (GstSDPMessage * msg)
288 {
289   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
290
291   gst_sdp_message_init (msg);
292
293   FREE_PTR_ARRAY (msg->emails);
294   FREE_PTR_ARRAY (msg->phones);
295   FREE_ARRAY (msg->bandwidths);
296   FREE_ARRAY (msg->times);
297   FREE_ARRAY (msg->zones);
298   FREE_ARRAY (msg->attributes);
299   FREE_ARRAY (msg->medias);
300
301   return GST_SDP_OK;
302 }
303
304 /**
305  * gst_sdp_message_free:
306  * @msg: a #GstSDPMessage
307  *
308  * Free all resources allocated by @msg. @msg should not be used anymore after
309  * this function. This function should be used when @msg was dynamically
310  * allocated with gst_sdp_message_new().
311  *
312  * Returns: a #GstSDPResult.
313  */
314 GstSDPResult
315 gst_sdp_message_free (GstSDPMessage * msg)
316 {
317   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
318
319   gst_sdp_message_uninit (msg);
320   g_free (msg);
321
322   return GST_SDP_OK;
323 }
324
325 /**
326  * gst_sdp_address_is_multicast:
327  * @nettype: a network type
328  * @addrtype: an address type
329  * @addr: an address
330  *
331  * Check if the given @addr is a multicast address.
332  *
333  * Returns: TRUE when @addr is multicast.
334  *
335  * Since: 0.10.32
336  */
337 gboolean
338 gst_sdp_address_is_multicast (const gchar * nettype, const gchar * addrtype,
339     const gchar * addr)
340 {
341   struct addrinfo hints;
342   struct addrinfo *ai;
343   struct addrinfo *res;
344   gboolean ret = FALSE;
345
346   g_return_val_if_fail (addr, FALSE);
347
348   /* we only support IN */
349   if (nettype && strcmp (nettype, "IN") != 0)
350     return FALSE;
351
352   memset (&hints, 0, sizeof (hints));
353   hints.ai_socktype = SOCK_DGRAM;
354
355   /* set the address type as a hint */
356   if (addrtype) {
357     if (!strcmp (addrtype, "IP4"))
358       hints.ai_family = AF_INET;
359     else if (!strcmp (addrtype, "IP6"))
360       hints.ai_family = AF_INET6;
361   }
362
363   if (getaddrinfo (addr, NULL, &hints, &res) < 0)
364     return FALSE;
365
366   for (ai = res; !ret && ai; ai = ai->ai_next) {
367     if (ai->ai_family == AF_INET)
368       ret =
369           IN_MULTICAST (ntohl (((struct sockaddr_in *) ai->ai_addr)->
370               sin_addr.s_addr));
371     else
372       ret =
373           IN6_IS_ADDR_MULTICAST (&((struct sockaddr_in6 *) ai->
374               ai_addr)->sin6_addr);
375   }
376
377   freeaddrinfo (res);
378
379   return ret;
380 }
381
382 /**
383  * gst_sdp_message_as_text:
384  * @msg: a #GstSDPMessage
385  *
386  * Convert the contents of @msg to a text string.
387  *
388  * Returns: A dynamically allocated string representing the SDP description.
389  */
390 gchar *
391 gst_sdp_message_as_text (const GstSDPMessage * msg)
392 {
393   /* change all vars so they match rfc? */
394   GString *lines;
395   guint i;
396
397   g_return_val_if_fail (msg != NULL, NULL);
398
399   lines = g_string_new ("");
400
401   if (msg->version)
402     g_string_append_printf (lines, "v=%s\r\n", msg->version);
403
404   if (msg->origin.sess_id && msg->origin.sess_version && msg->origin.nettype &&
405       msg->origin.addrtype && msg->origin.addr)
406     g_string_append_printf (lines, "o=%s %s %s %s %s %s\r\n",
407         msg->origin.username ? msg->origin.username : "-", msg->origin.sess_id,
408         msg->origin.sess_version, msg->origin.nettype, msg->origin.addrtype,
409         msg->origin.addr);
410
411   if (msg->session_name)
412     g_string_append_printf (lines, "s=%s\r\n", msg->session_name);
413
414   if (msg->information)
415     g_string_append_printf (lines, "i=%s\r\n", msg->information);
416
417   if (msg->uri)
418     g_string_append_printf (lines, "u=%s\r\n", msg->uri);
419
420   for (i = 0; i < gst_sdp_message_emails_len (msg); i++)
421     g_string_append_printf (lines, "e=%s\r\n",
422         gst_sdp_message_get_email (msg, i));
423
424   for (i = 0; i < gst_sdp_message_phones_len (msg); i++)
425     g_string_append_printf (lines, "p=%s\r\n",
426         gst_sdp_message_get_phone (msg, i));
427
428   if (gst_sdp_message_emails_len (msg) == 0 &&
429       gst_sdp_message_phones_len (msg) == 0)
430     g_string_append_printf (lines, "e=NONE\r\n");
431
432   if (msg->connection.nettype && msg->connection.addrtype &&
433       msg->connection.address) {
434     g_string_append_printf (lines, "c=%s %s %s", msg->connection.nettype,
435         msg->connection.addrtype, msg->connection.address);
436     if (gst_sdp_address_is_multicast (msg->connection.nettype,
437             msg->connection.addrtype, msg->connection.address)) {
438       /* only add ttl for IP4 */
439       if (strcmp (msg->connection.addrtype, "IP4") == 0)
440         g_string_append_printf (lines, "/%u", msg->connection.ttl);
441       if (msg->connection.addr_number > 1)
442         g_string_append_printf (lines, "/%u", msg->connection.addr_number);
443     }
444     g_string_append_printf (lines, "\r\n");
445   }
446
447   for (i = 0; i < gst_sdp_message_bandwidths_len (msg); i++) {
448     const GstSDPBandwidth *bandwidth = gst_sdp_message_get_bandwidth (msg, i);
449
450     g_string_append_printf (lines, "b=%s:%u\r\n", bandwidth->bwtype,
451         bandwidth->bandwidth);
452   }
453
454   for (i = 0; i < gst_sdp_message_times_len (msg); i++) {
455     const GstSDPTime *times = gst_sdp_message_get_time (msg, i);
456
457     g_string_append_printf (lines, "t=%s %s\r\n", times->start, times->stop);
458
459     if (times->repeat != NULL) {
460       guint j;
461
462       g_string_append_printf (lines, "r=%s",
463           g_array_index (times->repeat, gchar *, 0));
464       for (j = 1; j < times->repeat->len; j++)
465         g_string_append_printf (lines, " %s",
466             g_array_index (times->repeat, gchar *, j));
467       g_string_append_printf (lines, "\r\n");
468     }
469   }
470
471   if (gst_sdp_message_zones_len (msg) > 0) {
472     const GstSDPZone *zone = gst_sdp_message_get_zone (msg, 0);
473
474     g_string_append_printf (lines, "z=%s %s", zone->time, zone->typed_time);
475     for (i = 1; i < gst_sdp_message_zones_len (msg); i++) {
476       zone = gst_sdp_message_get_zone (msg, i);
477       g_string_append_printf (lines, " %s %s", zone->time, zone->typed_time);
478     }
479     g_string_append_printf (lines, "\r\n");
480   }
481
482   if (msg->key.type) {
483     g_string_append_printf (lines, "k=%s", msg->key.type);
484     if (msg->key.data)
485       g_string_append_printf (lines, ":%s", msg->key.data);
486     g_string_append_printf (lines, "\r\n");
487   }
488
489   for (i = 0; i < gst_sdp_message_attributes_len (msg); i++) {
490     const GstSDPAttribute *attr = gst_sdp_message_get_attribute (msg, i);
491
492     if (attr->key) {
493       g_string_append_printf (lines, "a=%s", attr->key);
494       if (attr->value)
495         g_string_append_printf (lines, ":%s", attr->value);
496       g_string_append_printf (lines, "\r\n");
497     }
498   }
499
500   for (i = 0; i < gst_sdp_message_medias_len (msg); i++) {
501     const GstSDPMedia *media = gst_sdp_message_get_media (msg, i);
502     gchar *sdp_media_str;
503
504     sdp_media_str = gst_sdp_media_as_text (media);
505     g_string_append_printf (lines, "%s", sdp_media_str);
506     g_free (sdp_media_str);
507   }
508
509   return g_string_free (lines, FALSE);
510 }
511
512 static int
513 hex_to_int (gchar c)
514 {
515   return c >= '0' && c <= '9' ? c - '0'
516       : c >= 'A' && c <= 'F' ? c - 'A' + 10
517       : c >= 'a' && c <= 'f' ? c - 'a' + 10 : 0;
518 }
519
520 /**
521  * gst_sdp_message_parse_uri:
522  * @uri: the start of the uri
523  * @msg: the result #GstSDPMessage
524  *
525  * Parse the null-terminated @uri and store the result in @msg.
526  *
527  * The uri should be of the form:
528  *
529  *  scheme://[address[:ttl=ttl][:noa=noa]]/[sessionname]
530  *               [#type=value *[&type=value]]
531  *
532  *  where value is url encoded. This looslely resembles
533  *  http://tools.ietf.org/html/draft-fujikawa-sdp-url-01
534  *
535  * Returns: #GST_SDP_OK on success.
536  *
537  * Since: 0.10.31
538  */
539 GstSDPResult
540 gst_sdp_message_parse_uri (const gchar * uri, GstSDPMessage * msg)
541 {
542   GstSDPResult res;
543   gchar *message;
544   const gchar *colon, *slash, *hash, *p;
545   GString *lines;
546
547   g_return_val_if_fail (uri != NULL, GST_SDP_EINVAL);
548   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
549
550   colon = strstr (uri, "://");
551   if (!colon)
552     goto no_colon;
553
554   /* FIXME connection info goes here */
555
556   slash = strstr (colon + 3, "/");
557   if (!slash)
558     goto no_slash;
559
560   /* FIXME session name goes here */
561
562   hash = strstr (slash + 1, "#");
563   if (!hash)
564     goto no_hash;
565
566   lines = g_string_new ("");
567
568   /* unescape */
569   for (p = hash + 1; *p; p++) {
570     if (*p == '&')
571       g_string_append_printf (lines, "\r\n");
572     else if (*p == '+')
573       g_string_append_c (lines, ' ');
574     else if (*p == '%') {
575       gchar a, b;
576
577       if ((a = p[1])) {
578         if ((b = p[2])) {
579           g_string_append_c (lines, (hex_to_int (a) << 4) | hex_to_int (b));
580           p += 2;
581         }
582       } else {
583         p++;
584       }
585     } else
586       g_string_append_c (lines, *p);
587   }
588
589   message = g_string_free (lines, FALSE);
590   res =
591       gst_sdp_message_parse_buffer ((const guint8 *) message, strlen (message),
592       msg);
593   g_free (message);
594
595   return res;
596
597   /* ERRORS */
598 no_colon:
599   {
600     return GST_SDP_EINVAL;
601   }
602 no_slash:
603   {
604     return GST_SDP_EINVAL;
605   }
606 no_hash:
607   {
608     return GST_SDP_EINVAL;
609   }
610 }
611
612 static const guchar acceptable[96] = {
613   /* X0   X1    X2    X3    X4    X5    X6    X7    X8    X9    XA    XB    XC    XD    XE    XF */
614   0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00,       /* 2X  !"#$%&'()*+,-./   */
615   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,       /* 3X 0123456789:;<=>?   */
616   0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,       /* 4X @ABCDEFGHIJKLMNO   */
617   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,       /* 5X PQRSTUVWXYZ[\]^_   */
618   0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,       /* 6X `abcdefghijklmno   */
619   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00        /* 7X pqrstuvwxyz{|}~DEL */
620 };
621
622 static const gchar hex[16] = "0123456789ABCDEF";
623
624 #define ACCEPTABLE_CHAR(a) (((guchar)(a))>=32 && ((guchar)(a))<128 && acceptable[(((guchar)a))-32])
625
626 /**
627  * gst_sdp_message_as_uri:
628  * @scheme: the uri scheme
629  * @msg: the #GstSDPMessage
630  *
631  * Creates a uri from @msg with the given @scheme. The uri has the format:
632  *
633  *  @scheme:///[#type=value *[&type=value]]
634  *
635  *  Where each value is url encoded.
636  *
637  * Returns: a uri for @msg.
638  *
639  * Since: 0.10.31
640  */
641 gchar *
642 gst_sdp_message_as_uri (const gchar * scheme, const GstSDPMessage * msg)
643 {
644   gchar *serialized, *p;
645   gchar *res;
646   GString *lines;
647   gboolean first;
648
649   g_return_val_if_fail (scheme != NULL, NULL);
650   g_return_val_if_fail (msg != NULL, NULL);
651
652   p = serialized = gst_sdp_message_as_text (msg);
653
654   lines = g_string_new ("");
655   g_string_append_printf (lines, "%s:///#", scheme);
656
657   /* now escape */
658   first = TRUE;
659   for (p = serialized; *p; p++) {
660     if (first) {
661       g_string_append_printf (lines, "%c=", *p);
662       if (*(p + 1))
663         p++;
664       first = FALSE;
665       continue;
666     }
667     if (*p == '\r')
668       continue;
669     else if (*p == '\n') {
670       if (*(p + 1))
671         g_string_append_c (lines, '&');
672       first = TRUE;
673     } else if (*p == ' ')
674       g_string_append_c (lines, '+');
675     else if (ACCEPTABLE_CHAR (*p))
676       g_string_append_c (lines, *p);
677     else {
678       /* escape */
679       g_string_append_printf (lines, "%%%c%c", hex[*p >> 4], hex[*p & 0xf]);
680     }
681   }
682
683   res = g_string_free (lines, FALSE);
684   g_free (serialized);
685
686   return res;
687 }
688
689 /**
690  * gst_sdp_message_set_version:
691  * @msg: a #GstSDPMessage
692  * @version: the version
693  *
694  * Set the version in @msg.
695  *
696  * Returns: a #GstSDPResult.
697  */
698 DEFINE_STRING_SETTER (version);
699 /**
700  * gst_sdp_message_get_version:
701  * @msg: a #GstSDPMessage
702  *
703  * Get the version in @msg.
704  *
705  * Returns: a #GstSDPResult.
706  */
707 DEFINE_STRING_GETTER (version);
708
709 /**
710  * gst_sdp_message_set_origin:
711  * @msg: a #GstSDPMessage
712  * @username: the user name
713  * @sess_id: a session id
714  * @sess_version: a session version
715  * @nettype: a network type
716  * @addrtype: an address type
717  * @addr: an address
718  *
719  * Configure the SDP origin in @msg with the given parameters.
720  *
721  * Returns: #GST_SDP_OK.
722  */
723 GstSDPResult
724 gst_sdp_message_set_origin (GstSDPMessage * msg, const gchar * username,
725     const gchar * sess_id, const gchar * sess_version, const gchar * nettype,
726     const gchar * addrtype, const gchar * addr)
727 {
728   REPLACE_STRING (msg->origin.username, username);
729   REPLACE_STRING (msg->origin.sess_id, sess_id);
730   REPLACE_STRING (msg->origin.sess_version, sess_version);
731   REPLACE_STRING (msg->origin.nettype, nettype);
732   REPLACE_STRING (msg->origin.addrtype, addrtype);
733   REPLACE_STRING (msg->origin.addr, addr);
734
735   return GST_SDP_OK;
736 }
737
738 /**
739  * gst_sdp_message_get_origin:
740  * @msg: a #GstSDPMessage
741  *
742  * Get the origin of @msg.
743  *
744  * Returns: a #GstSDPOrigin. The result remains valid as long as @msg is valid.
745  */
746 const GstSDPOrigin *
747 gst_sdp_message_get_origin (const GstSDPMessage * msg)
748 {
749   return &msg->origin;
750 }
751
752 /**
753  * gst_sdp_message_set_session_name:
754  * @msg: a #GstSDPMessage
755  * @session_name: the session name
756  *
757  * Set the session name in @msg.
758  *
759  * Returns: a #GstSDPResult.
760  */
761 DEFINE_STRING_SETTER (session_name);
762 /**
763  * gst_sdp_message_get_session_name:
764  * @msg: a #GstSDPMessage
765  *
766  * Get the session name in @msg.
767  *
768  * Returns: a #GstSDPResult.
769  */
770 DEFINE_STRING_GETTER (session_name);
771 /**
772  * gst_sdp_message_set_information:
773  * @msg: a #GstSDPMessage
774  * @information: the information
775  *
776  * Set the information in @msg.
777  *
778  * Returns: a #GstSDPResult.
779  */
780 DEFINE_STRING_SETTER (information);
781 /**
782  * gst_sdp_message_get_information:
783  * @msg: a #GstSDPMessage
784  *
785  * Get the information in @msg.
786  *
787  * Returns: a #GstSDPResult.
788  */
789 DEFINE_STRING_GETTER (information);
790 /**
791  * gst_sdp_message_set_uri:
792  * @msg: a #GstSDPMessage
793  * @uri: the URI
794  *
795  * Set the URI in @msg.
796  *
797  * Returns: a #GstSDPResult.
798  */
799 DEFINE_STRING_SETTER (uri);
800 /**
801  * gst_sdp_message_get_uri:
802  * @msg: a #GstSDPMessage
803  *
804  * Get the URI in @msg.
805  *
806  * Returns: a #GstSDPResult.
807  */
808 DEFINE_STRING_GETTER (uri);
809
810 /**
811  * gst_sdp_message_emails_len:
812  * @msg: a #GstSDPMessage
813  *
814  * Get the number of emails in @msg.
815  *
816  * Returns: the number of emails in @msg.
817  */
818 DEFINE_ARRAY_LEN (emails);
819 /**
820  * gst_sdp_message_get_email:
821  * @msg: a #GstSDPMessage
822  * @idx: an email index
823  *
824  * Get the email with number @idx from @msg.
825  *
826  * Returns: the email at position @idx.
827  */
828 DEFINE_PTR_ARRAY_GETTER (email, emails, const gchar *);
829
830 /**
831  * gst_sdp_message_add_email:
832  * @msg: a #GstSDPMessage
833  * @email: an email
834  *
835  * Add @email to the list of emails in @msg.
836  *
837  * Returns: a #GstSDPResult.
838  */
839 DEFINE_PTR_ARRAY_ADDER (email, emails, const gchar *, g_strdup);
840
841 /**
842  * gst_sdp_message_phones_len:
843  * @msg: a #GstSDPMessage
844  *
845  * Get the number of phones in @msg.
846  *
847  * Returns: the number of phones in @msg.
848  */
849 DEFINE_ARRAY_LEN (phones);
850 /**
851  * gst_sdp_message_get_phone:
852  * @msg: a #GstSDPMessage
853  * @idx: a phone index
854  *
855  * Get the phone with number @idx from @msg.
856  *
857  * Returns: the phone at position @idx.
858  */
859 DEFINE_PTR_ARRAY_GETTER (phone, phones, const gchar *);
860
861 /**
862  * gst_sdp_message_add_phone:
863  * @msg: a #GstSDPMessage
864  * @phone: a phone
865  *
866  * Add @phone to the list of phones in @msg.
867  *
868  * Returns: a #GstSDPResult.
869  */
870 DEFINE_PTR_ARRAY_ADDER (phone, phones, const gchar *, g_strdup);
871
872 /**
873  * gst_sdp_message_set_connection:
874  * @msg: a #GstSDPMessage
875  * @nettype: the type of network. "IN" is defined to have the meaning
876  * "Internet".
877  * @addrtype: the type of address.
878  * @address: the address
879  * @ttl: the time to live of the address
880  * @addr_number: the number of layers
881  *
882  * Configure the SDP connection in @msg with the given parameters.
883  *
884  * Returns: a #GstSDPResult.
885  */
886 GstSDPResult
887 gst_sdp_message_set_connection (GstSDPMessage * msg, const gchar * nettype,
888     const gchar * addrtype, const gchar * address, guint ttl, guint addr_number)
889 {
890   REPLACE_STRING (msg->connection.nettype, nettype);
891   REPLACE_STRING (msg->connection.addrtype, addrtype);
892   REPLACE_STRING (msg->connection.address, address);
893   msg->connection.ttl = ttl;
894   msg->connection.addr_number = addr_number;
895
896   return GST_SDP_OK;
897 }
898
899 /**
900  * gst_sdp_message_get_connection:
901  * @msg: a #GstSDPMessage
902  *
903  * Get the connection of @msg.
904  *
905  * Returns: a #GstSDPConnection. The result remains valid as long as @msg is valid.
906  */
907 const GstSDPConnection *
908 gst_sdp_message_get_connection (const GstSDPMessage * msg)
909 {
910   return &msg->connection;
911 }
912
913 /**
914  * gst_sdp_message_bandwidths_len:
915  * @msg: a #GstSDPMessage
916  *
917  * Get the number of bandwidth information in @msg.
918  *
919  * Returns: the number of bandwidth information in @msg.
920  */
921 DEFINE_ARRAY_LEN (bandwidths);
922 /**
923  * gst_sdp_message_get_bandwidth:
924  * @msg: a #GstSDPMessage
925  * @idx: the bandwidth index
926  *
927  * Get the bandwidth at index @idx from @msg.
928  *
929  * Returns: a #GstSDPBandwidth.
930  */
931 DEFINE_ARRAY_GETTER (bandwidth, bandwidths, const GstSDPBandwidth);
932
933 /**
934  * gst_sdp_message_add_bandwidth:
935  * @msg: a #GstSDPMessage
936  * @bwtype: the bandwidth modifier type
937  * @bandwidth: the bandwidth in kilobits per second
938  *
939  * Add the specified bandwidth information to @msg.
940  *
941  * Returns: a #GstSDPResult.
942  */
943
944 GstSDPResult
945 gst_sdp_message_add_bandwidth (GstSDPMessage * msg, const gchar * bwtype,
946     guint bandwidth)
947 {
948   GstSDPBandwidth bw;
949
950   bw.bwtype = g_strdup (bwtype);
951   bw.bandwidth = bandwidth;
952
953   g_array_append_val (msg->bandwidths, bw);
954
955   return GST_SDP_OK;
956 }
957
958 /**
959  * gst_sdp_message_times_len:
960  * @msg: a #GstSDPMessage
961  *
962  * Get the number of time information entries in @msg.
963  *
964  * Returns: the number of time information entries in @msg.
965  */
966 DEFINE_ARRAY_LEN (times);
967
968 /**
969  * gst_sdp_message_get_time:
970  * @msg: a #GstSDPMessage
971  * @idx: the time index
972  *
973  * Get time information with index @idx from @msg.
974  *
975  * Returns: a #GstSDPTime.
976  */
977 DEFINE_ARRAY_GETTER (time, times, const GstSDPTime);
978
979 /**
980  * gst_sdp_message_add_time:
981  * @msg: a #GstSDPMessage
982  * @start: the start time
983  * @stop: the stop time
984  * @repeat: the repeat times
985  *
986  * Add time information @start and @stop to @msg.
987  *
988  * Returns: a #GstSDPResult.
989  */
990 GstSDPResult
991 gst_sdp_message_add_time (GstSDPMessage * msg, const gchar * start,
992     const gchar * stop, const gchar ** repeat)
993 {
994   GstSDPTime times;
995
996   times.start = g_strdup (start);
997   times.stop = g_strdup (stop);
998   if (repeat) {
999     times.repeat = g_array_new (FALSE, TRUE, sizeof (gchar *));
1000     for (; *repeat; repeat++) {
1001       gchar *r = g_strdup (*repeat);
1002
1003       g_array_append_val (times.repeat, r);
1004     }
1005   } else
1006     times.repeat = NULL;
1007   g_array_append_val (msg->times, times);
1008
1009   return GST_SDP_OK;
1010 }
1011
1012 /**
1013  * gst_sdp_message_zones_len:
1014  * @msg: a #GstSDPMessage
1015  *
1016  * Get the number of time zone information entries in @msg.
1017  *
1018  * Returns: the number of time zone information entries in @msg.
1019  */
1020 DEFINE_ARRAY_LEN (zones);
1021 /**
1022  * gst_sdp_message_get_zone:
1023  * @msg: a #GstSDPMessage
1024  * @idx: the zone index
1025  *
1026  * Get time zone information with index @idx from @msg.
1027  *
1028  * Returns: a #GstSDPZone.
1029  */
1030 DEFINE_ARRAY_GETTER (zone, zones, const GstSDPZone);
1031
1032 /**
1033  * gst_sdp_message_add_zone:
1034  * @msg: a #GstSDPMessage
1035  * @adj_time: the NTP time that a time zone adjustment happens
1036  * @typed_time: the offset from the time when the session was first scheduled
1037  *
1038  * Add time zone information to @msg.
1039  *
1040  * Returns: a #GstSDPResult.
1041  */
1042 GstSDPResult
1043 gst_sdp_message_add_zone (GstSDPMessage * msg, const gchar * adj_time,
1044     const gchar * typed_time)
1045 {
1046   GstSDPZone zone;
1047
1048   zone.time = g_strdup (adj_time);
1049   zone.typed_time = g_strdup (typed_time);
1050
1051   g_array_append_val (msg->zones, zone);
1052
1053   return GST_SDP_OK;
1054 }
1055
1056 /**
1057  * gst_sdp_message_set_key:
1058  * @msg: a #GstSDPMessage
1059  * @type: the encryption type
1060  * @data: the encryption data
1061  *
1062  * Adds the encryption information to @msg.
1063  *
1064  * Returns: a #GstSDPResult.
1065  */
1066 GstSDPResult
1067 gst_sdp_message_set_key (GstSDPMessage * msg, const gchar * type,
1068     const gchar * data)
1069 {
1070   REPLACE_STRING (msg->key.type, type);
1071   REPLACE_STRING (msg->key.data, data);
1072
1073   return GST_SDP_OK;
1074 }
1075
1076 /**
1077  * gst_sdp_message_get_key:
1078  * @msg: a #GstSDPMessage
1079  *
1080  * Get the encryption information from @msg.
1081  *
1082  * Returns: a #GstSDPKey.
1083  */
1084 const GstSDPKey *
1085 gst_sdp_message_get_key (const GstSDPMessage * msg)
1086 {
1087   return &msg->key;
1088 }
1089
1090 /**
1091  * gst_sdp_message_attributes_len:
1092  * @msg: a #GstSDPMessage
1093  *
1094  * Get the number of attributes in @msg.
1095  *
1096  * Returns: the number of attributes in @msg.
1097  */
1098 DEFINE_ARRAY_LEN (attributes);
1099
1100 /**
1101  * gst_sdp_message_get_attribute:
1102  * @msg: a #GstSDPMessage
1103  * @idx: the index
1104  *
1105  * Get the attribute at position @idx in @msg.
1106  *
1107  * Returns: the #GstSDPAttribute at position @idx.
1108  */
1109 DEFINE_ARRAY_GETTER (attribute, attributes, const GstSDPAttribute);
1110
1111 /**
1112  * gst_sdp_message_get_attribute_val_n:
1113  * @msg: a #GstSDPMessage
1114  * @key: the key
1115  * @nth: the index
1116  *
1117  * Get the @nth attribute with key @key in @msg.
1118  *
1119  * Returns: the attribute value of the @nth attribute with @key.
1120  */
1121 const gchar *
1122 gst_sdp_message_get_attribute_val_n (const GstSDPMessage * msg,
1123     const gchar * key, guint nth)
1124 {
1125   guint i;
1126
1127   for (i = 0; i < msg->attributes->len; i++) {
1128     GstSDPAttribute *attr;
1129
1130     attr = &g_array_index (msg->attributes, GstSDPAttribute, i);
1131     if (!strcmp (attr->key, key)) {
1132       if (nth == 0)
1133         return attr->value;
1134       else
1135         nth--;
1136     }
1137   }
1138   return NULL;
1139 }
1140
1141 /**
1142  * gst_sdp_message_get_attribute_val:
1143  * @msg: a #GstSDPMessage
1144  * @key: the key
1145  *
1146  * Get the first attribute with key @key in @msg.
1147  *
1148  * Returns: the attribute value of the first attribute with @key.
1149  */
1150 const gchar *
1151 gst_sdp_message_get_attribute_val (const GstSDPMessage * msg, const gchar * key)
1152 {
1153   return gst_sdp_message_get_attribute_val_n (msg, key, 0);
1154 }
1155
1156 /**
1157  * gst_sdp_message_add_attribute:
1158  * @msg: a #GstSDPMessage
1159  * @key: the key
1160  * @value: the value
1161  *
1162  * Add the attribute with @key and @value to @msg.
1163  *
1164  * Returns: @GST_SDP_OK.
1165  */
1166 GstSDPResult
1167 gst_sdp_message_add_attribute (GstSDPMessage * msg, const gchar * key,
1168     const gchar * value)
1169 {
1170   GstSDPAttribute attr;
1171
1172   attr.key = g_strdup (key);
1173   attr.value = g_strdup (value);
1174
1175   g_array_append_val (msg->attributes, attr);
1176
1177   return GST_SDP_OK;
1178 }
1179
1180 /**
1181  * gst_sdp_message_medias_len:
1182  * @msg: a #GstSDPMessage
1183  *
1184  * Get the number of media descriptions in @msg.
1185  *
1186  * Returns: the number of media descriptions in @msg.
1187  */
1188 DEFINE_ARRAY_LEN (medias);
1189 /**
1190  * gst_sdp_message_get_media:
1191  * @msg: a #GstSDPMessage
1192  * @idx: the index
1193  *
1194  * Get the media description at index @idx in @msg.
1195  *
1196  * Returns: a #GstSDPMedia.
1197  */
1198 DEFINE_ARRAY_GETTER (media, medias, const GstSDPMedia);
1199
1200 /**
1201  * gst_sdp_message_add_media:
1202  * @msg: a #GstSDPMessage
1203  * @media: a #GstSDPMedia to add
1204  *
1205  * Adds @media to the array of medias in @msg. This function takes ownership of
1206  * the contents of @media so that @media will have to be reinitialized with
1207  * gst_sdp_media_init() before it can be used again.
1208  *
1209  * Returns: a #GstSDPResult.
1210  */
1211 GstSDPResult
1212 gst_sdp_message_add_media (GstSDPMessage * msg, GstSDPMedia * media)
1213 {
1214   guint len;
1215   GstSDPMedia *nmedia;
1216
1217   len = msg->medias->len;
1218   g_array_set_size (msg->medias, len + 1);
1219   nmedia = &g_array_index (msg->medias, GstSDPMedia, len);
1220
1221   memcpy (nmedia, media, sizeof (GstSDPMedia));
1222   memset (media, 0, sizeof (GstSDPMedia));
1223
1224   return GST_SDP_OK;
1225 }
1226
1227 /* media access */
1228
1229 /**
1230  * gst_sdp_media_new:
1231  * @media: pointer to new #GstSDPMedia
1232  *
1233  * Allocate a new GstSDPMedia and store the result in @media.
1234  *
1235  * Returns: a #GstSDPResult.
1236  */
1237 GstSDPResult
1238 gst_sdp_media_new (GstSDPMedia ** media)
1239 {
1240   GstSDPMedia *newmedia;
1241
1242   g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
1243
1244   newmedia = g_new0 (GstSDPMedia, 1);
1245
1246   *media = newmedia;
1247
1248   return gst_sdp_media_init (newmedia);
1249 }
1250
1251 /**
1252  * gst_sdp_media_init:
1253  * @media: a #GstSDPMedia
1254  *
1255  * Initialize @media so that its contents are as if it was freshly allocated
1256  * with gst_sdp_media_new(). This function is mostly used to initialize a media
1257  * allocated on the stack. gst_sdp_media_uninit() undoes this operation.
1258  *
1259  * When this function is invoked on newly allocated data (with malloc or on the
1260  * stack), its contents should be set to 0 before calling this function.
1261  *
1262  * Returns: a #GstSDPResult.
1263  */
1264 GstSDPResult
1265 gst_sdp_media_init (GstSDPMedia * media)
1266 {
1267   g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
1268
1269   FREE_STRING (media->media);
1270   media->port = 0;
1271   media->num_ports = 0;
1272   FREE_STRING (media->proto);
1273   INIT_PTR_ARRAY (media->fmts, gchar *, g_free);
1274   FREE_STRING (media->information);
1275   INIT_ARRAY (media->connections, GstSDPConnection, gst_sdp_connection_init);
1276   INIT_ARRAY (media->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_init);
1277   gst_sdp_key_init (&media->key);
1278   INIT_ARRAY (media->attributes, GstSDPAttribute, gst_sdp_attribute_init);
1279
1280   return GST_SDP_OK;
1281 }
1282
1283 /**
1284  * gst_sdp_media_uninit:
1285  * @media: a #GstSDPMedia
1286  *
1287  * Free all resources allocated in @media. @media should not be used anymore after
1288  * this function. This function should be used when @media was allocated on the
1289  * stack and initialized with gst_sdp_media_init().
1290  *
1291  * Returns: a #GstSDPResult.
1292  */
1293 GstSDPResult
1294 gst_sdp_media_uninit (GstSDPMedia * media)
1295 {
1296   g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
1297
1298   gst_sdp_media_init (media);
1299   FREE_PTR_ARRAY (media->fmts);
1300   FREE_ARRAY (media->connections);
1301   FREE_ARRAY (media->bandwidths);
1302   FREE_ARRAY (media->attributes);
1303
1304   return GST_SDP_OK;
1305 }
1306
1307 /**
1308  * gst_sdp_media_free:
1309  * @media: a #GstSDPMedia
1310  *
1311  * Free all resources allocated by @media. @media should not be used anymore after
1312  * this function. This function should be used when @media was dynamically
1313  * allocated with gst_sdp_media_new().
1314  *
1315  * Returns: a #GstSDPResult.
1316  */
1317 GstSDPResult
1318 gst_sdp_media_free (GstSDPMedia * media)
1319 {
1320   g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
1321
1322   gst_sdp_media_uninit (media);
1323   g_free (media);
1324
1325   return GST_SDP_OK;
1326 }
1327
1328 /**
1329  * gst_sdp_media_as_text:
1330  * @media: a #GstSDPMedia
1331  *
1332  * Convert the contents of @media to a text string.
1333  *
1334  * Returns: A dynamically allocated string representing the media.
1335  */
1336 gchar *
1337 gst_sdp_media_as_text (const GstSDPMedia * media)
1338 {
1339   GString *lines;
1340   guint i;
1341
1342   g_return_val_if_fail (media != NULL, NULL);
1343
1344   lines = g_string_new ("");
1345
1346   if (media->media)
1347     g_string_append_printf (lines, "m=%s", media->media);
1348
1349   g_string_append_printf (lines, " %u", media->port);
1350
1351   if (media->num_ports > 1)
1352     g_string_append_printf (lines, "/%u", media->num_ports);
1353
1354   g_string_append_printf (lines, " %s", media->proto);
1355
1356   for (i = 0; i < gst_sdp_media_formats_len (media); i++)
1357     g_string_append_printf (lines, " %s", gst_sdp_media_get_format (media, i));
1358   g_string_append_printf (lines, "\r\n");
1359
1360   if (media->information)
1361     g_string_append_printf (lines, "i=%s", media->information);
1362
1363   for (i = 0; i < gst_sdp_media_connections_len (media); i++) {
1364     const GstSDPConnection *conn = gst_sdp_media_get_connection (media, i);
1365
1366     if (conn->nettype && conn->addrtype && conn->address) {
1367       g_string_append_printf (lines, "c=%s %s %s", conn->nettype,
1368           conn->addrtype, conn->address);
1369       if (gst_sdp_address_is_multicast (conn->nettype, conn->addrtype,
1370               conn->address)) {
1371         /* only add TTL for IP4 multicast */
1372         if (strcmp (conn->addrtype, "IP4") == 0)
1373           g_string_append_printf (lines, "/%u", conn->ttl);
1374         if (conn->addr_number > 1)
1375           g_string_append_printf (lines, "/%u", conn->addr_number);
1376       }
1377       g_string_append_printf (lines, "\r\n");
1378     }
1379   }
1380
1381   for (i = 0; i < gst_sdp_media_bandwidths_len (media); i++) {
1382     const GstSDPBandwidth *bandwidth = gst_sdp_media_get_bandwidth (media, i);
1383
1384     g_string_append_printf (lines, "b=%s:%u\r\n", bandwidth->bwtype,
1385         bandwidth->bandwidth);
1386   }
1387
1388   if (media->key.type) {
1389     g_string_append_printf (lines, "k=%s", media->key.type);
1390     if (media->key.data)
1391       g_string_append_printf (lines, ":%s", media->key.data);
1392     g_string_append_printf (lines, "\r\n");
1393   }
1394
1395   for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
1396     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
1397
1398     if (attr->key) {
1399       g_string_append_printf (lines, "a=%s", attr->key);
1400       if (attr->value)
1401         g_string_append_printf (lines, ":%s", attr->value);
1402       g_string_append_printf (lines, "\r\n");
1403     }
1404   }
1405
1406   return g_string_free (lines, FALSE);
1407 }
1408
1409 /**
1410  * gst_sdp_media_get_media:
1411  * @media: a #GstSDPMedia
1412  *
1413  * Get the media description of @media.
1414  *
1415  * Returns: the media description.
1416  */
1417 const gchar *
1418 gst_sdp_media_get_media (const GstSDPMedia * media)
1419 {
1420   return media->media;
1421 }
1422
1423 /**
1424  * gst_sdp_media_set_media:
1425  * @media: a #GstSDPMedia
1426  * @med: the media description
1427  *
1428  * Set the media description of @media to @med.
1429  *
1430  * Returns: #GST_SDP_OK.
1431  */
1432 GstSDPResult
1433 gst_sdp_media_set_media (GstSDPMedia * media, const gchar * med)
1434 {
1435   g_free (media->media);
1436   media->media = g_strdup (med);
1437
1438   return GST_SDP_OK;
1439 }
1440
1441 /**
1442  * gst_sdp_media_get_port:
1443  * @media: a #GstSDPMedia
1444  *
1445  * Get the port number for @media.
1446  *
1447  * Returns: the port number of @media.
1448  */
1449 guint
1450 gst_sdp_media_get_port (const GstSDPMedia * media)
1451 {
1452   return media->port;
1453 }
1454
1455 /**
1456  * gst_sdp_media_get_num_ports:
1457  * @media: a #GstSDPMedia
1458  *
1459  * Get the number of ports for @media.
1460  *
1461  * Returns: the number of ports for @media.
1462  */
1463 guint
1464 gst_sdp_media_get_num_ports (const GstSDPMedia * media)
1465 {
1466   return media->num_ports;
1467 }
1468
1469 /**
1470  * gst_sdp_media_set_port_info:
1471  * @media: a #GstSDPMedia
1472  * @port: the port number
1473  * @num_ports: the number of ports
1474  *
1475  * Set the port information in @media.
1476  *
1477  * Returns: #GST_SDP_OK.
1478  */
1479 GstSDPResult
1480 gst_sdp_media_set_port_info (GstSDPMedia * media, guint port, guint num_ports)
1481 {
1482   media->port = port;
1483   media->num_ports = num_ports;
1484
1485   return GST_SDP_OK;
1486 }
1487
1488 /**
1489  * gst_sdp_media_get_proto:
1490  * @media: a #GstSDPMedia
1491  *
1492  * Get the transport protocol of @media
1493  *
1494  * Returns: the transport protocol of @media.
1495  */
1496 const gchar *
1497 gst_sdp_media_get_proto (const GstSDPMedia * media)
1498 {
1499   return media->proto;
1500 }
1501
1502 /**
1503  * gst_sdp_media_set_proto:
1504  * @media: a #GstSDPMedia
1505  * @proto: the media transport protocol
1506  *
1507  * Set the media transport protocol of @media to @proto.
1508  *
1509  * Returns: #GST_SDP_OK.
1510  */
1511 GstSDPResult
1512 gst_sdp_media_set_proto (GstSDPMedia * media, const gchar * proto)
1513 {
1514   g_free (media->proto);
1515   media->proto = g_strdup (proto);
1516
1517   return GST_SDP_OK;
1518 }
1519
1520 /**
1521  * gst_sdp_media_formats_len:
1522  * @media: a #GstSDPMedia
1523  *
1524  * Get the number of formats in @media.
1525  *
1526  * Returns: the number of formats in @media.
1527  */
1528 guint
1529 gst_sdp_media_formats_len (const GstSDPMedia * media)
1530 {
1531   return media->fmts->len;
1532 }
1533
1534 /**
1535  * gst_sdp_media_get_format:
1536  * @media: a #GstSDPMedia
1537  * @idx: an index
1538  *
1539  * Get the format information at position @idx in @media.
1540  *
1541  * Returns: the format at position @idx.
1542  */
1543 const gchar *
1544 gst_sdp_media_get_format (const GstSDPMedia * media, guint idx)
1545 {
1546   if (idx >= media->fmts->len)
1547     return NULL;
1548   return g_array_index (media->fmts, gchar *, idx);
1549 }
1550
1551 /**
1552  * gst_sdp_media_add_format:
1553  * @media: a #GstSDPMedia
1554  * @format: the format
1555  *
1556  * Add the format information to @media.
1557  *
1558  * Returns: #GST_SDP_OK.
1559  */
1560 GstSDPResult
1561 gst_sdp_media_add_format (GstSDPMedia * media, const gchar * format)
1562 {
1563   gchar *fmt;
1564
1565   fmt = g_strdup (format);
1566
1567   g_array_append_val (media->fmts, fmt);
1568
1569   return GST_SDP_OK;
1570 }
1571
1572 /**
1573  * gst_sdp_media_get_information:
1574  * @media: a #GstSDPMedia
1575  *
1576  * Get the information of @media
1577  *
1578  * Returns: the information of @media.
1579  */
1580 const gchar *
1581 gst_sdp_media_get_information (const GstSDPMedia * media)
1582 {
1583   return media->information;
1584 }
1585
1586 /**
1587  * gst_sdp_media_set_information:
1588  * @media: a #GstSDPMedia
1589  * @information: the media information
1590  *
1591  * Set the media information of @media to @information.
1592  *
1593  * Returns: #GST_SDP_OK.
1594  */
1595 GstSDPResult
1596 gst_sdp_media_set_information (GstSDPMedia * media, const gchar * information)
1597 {
1598   g_free (media->information);
1599   media->information = g_strdup (information);
1600
1601   return GST_SDP_OK;
1602 }
1603
1604 /**
1605  * gst_sdp_media_connections_len:
1606  * @media: a #GstSDPMedia
1607  *
1608  * Get the number of connection fields in @media.
1609  *
1610  * Returns: the number of connections in @media.
1611  */
1612 guint
1613 gst_sdp_media_connections_len (const GstSDPMedia * media)
1614 {
1615   return media->connections->len;
1616 }
1617
1618 /**
1619  * gst_sdp_media_get_connection:
1620  * @media: a #GstSDPMedia
1621  * @idx: an index
1622  *
1623  * Get the connection at position @idx in @media.
1624  *
1625  * Returns: the #GstSDPConnection at position @idx.
1626  */
1627 const GstSDPConnection *
1628 gst_sdp_media_get_connection (const GstSDPMedia * media, guint idx)
1629 {
1630   return &g_array_index (media->connections, GstSDPConnection, idx);
1631 }
1632
1633 /**
1634  * gst_sdp_media_add_connection:
1635  * @media: a #GstSDPMedia
1636  * @nettype: the type of network. "IN" is defined to have the meaning
1637  * "Internet".
1638  * @addrtype: the type of address.
1639  * @address: the address
1640  * @ttl: the time to live of the address
1641  * @addr_number: the number of layers
1642  *
1643  * Add the given connection parameters to @media.
1644  *
1645  * Returns: a #GstSDPResult.
1646  */
1647 GstSDPResult
1648 gst_sdp_media_add_connection (GstSDPMedia * media, const gchar * nettype,
1649     const gchar * addrtype, const gchar * address, guint ttl, guint addr_number)
1650 {
1651   GstSDPConnection conn;
1652
1653   conn.nettype = g_strdup (nettype);
1654   conn.addrtype = g_strdup (addrtype);
1655   conn.address = g_strdup (address);
1656   conn.ttl = ttl;
1657   conn.addr_number = addr_number;
1658
1659   g_array_append_val (media->connections, conn);
1660
1661   return GST_SDP_OK;
1662 }
1663
1664 /**
1665  * gst_sdp_media_bandwidths_len:
1666  * @media: a #GstSDPMedia
1667  *
1668  * Get the number of bandwidth fields in @media.
1669  *
1670  * Returns: the number of bandwidths in @media.
1671  */
1672 guint
1673 gst_sdp_media_bandwidths_len (const GstSDPMedia * media)
1674 {
1675   return media->bandwidths->len;
1676 }
1677
1678 /**
1679  * gst_sdp_media_get_bandwidth:
1680  * @media: a #GstSDPMedia
1681  * @idx: an index
1682  *
1683  * Get the bandwidth at position @idx in @media.
1684  *
1685  * Returns: the #GstSDPBandwidth at position @idx.
1686  */
1687 const GstSDPBandwidth *
1688 gst_sdp_media_get_bandwidth (const GstSDPMedia * media, guint idx)
1689 {
1690   return &g_array_index (media->bandwidths, GstSDPBandwidth, idx);
1691 }
1692
1693 /**
1694  * gst_sdp_media_add_bandwidth:
1695  * @media: a #GstSDPMedia
1696  * @bwtype: the bandwidth modifier type
1697  * @bandwidth: the bandwidth in kilobits per second
1698  *
1699  * Add the bandwidth information with @bwtype and @bandwidth to @media.
1700  *
1701  * Returns: #GST_SDP_OK.
1702  */
1703 GstSDPResult
1704 gst_sdp_media_add_bandwidth (GstSDPMedia * media, const gchar * bwtype,
1705     guint bandwidth)
1706 {
1707   GstSDPBandwidth bw;
1708
1709   bw.bwtype = g_strdup (bwtype);
1710   bw.bandwidth = bandwidth;
1711
1712   g_array_append_val (media->bandwidths, bw);
1713
1714   return GST_SDP_OK;
1715 }
1716
1717 /**
1718  * gst_sdp_media_set_key:
1719  * @media: a #GstSDPMedia
1720  * @type: the encryption type
1721  * @data: the encryption data
1722  *
1723  * Adds the encryption information to @media.
1724  *
1725  * Returns: a #GstSDPResult.
1726  */
1727 GstSDPResult
1728 gst_sdp_media_set_key (GstSDPMedia * media, const gchar * type,
1729     const gchar * data)
1730 {
1731   g_free (media->key.type);
1732   media->key.type = g_strdup (type);
1733   g_free (media->key.data);
1734   media->key.data = g_strdup (data);
1735
1736   return GST_SDP_OK;
1737 }
1738
1739 /**
1740  * gst_sdp_media_get_key:
1741  * @media: a #GstSDPMedia
1742  *
1743  * Get the encryption information from @media.
1744  *
1745  * Returns: a #GstSDPKey.
1746  */
1747 const GstSDPKey *
1748 gst_sdp_media_get_key (const GstSDPMedia * media)
1749 {
1750   return &media->key;
1751 }
1752
1753 /**
1754  * gst_sdp_media_attributes_len:
1755  * @media: a #GstSDPMedia
1756  *
1757  * Get the number of attribute fields in @media.
1758  *
1759  * Returns: the number of attributes in @media.
1760  */
1761 guint
1762 gst_sdp_media_attributes_len (const GstSDPMedia * media)
1763 {
1764   return media->attributes->len;
1765 }
1766
1767 /**
1768  * gst_sdp_media_add_attribute:
1769  * @media: a #GstSDPMedia
1770  * @key: a key
1771  * @value: a value
1772  *
1773  * Add the attribute with @key and @value to @media.
1774  *
1775  * Returns: #GST_SDP_OK.
1776  */
1777 GstSDPResult
1778 gst_sdp_media_add_attribute (GstSDPMedia * media, const gchar * key,
1779     const gchar * value)
1780 {
1781   GstSDPAttribute attr;
1782
1783   attr.key = g_strdup (key);
1784   attr.value = g_strdup (value);
1785
1786   g_array_append_val (media->attributes, attr);
1787
1788   return GST_SDP_OK;
1789 }
1790
1791 /**
1792  * gst_sdp_media_get_attribute:
1793  * @media: a #GstSDPMedia
1794  * @idx: an index
1795  *
1796  * Get the attribute at position @idx in @media.
1797  *
1798  * Returns: the #GstSDPAttribute at position @idx.
1799  */
1800 const GstSDPAttribute *
1801 gst_sdp_media_get_attribute (const GstSDPMedia * media, guint idx)
1802 {
1803   return &g_array_index (media->attributes, GstSDPAttribute, idx);
1804 }
1805
1806 /**
1807  * gst_sdp_media_get_attribute_val_n:
1808  * @media: a #GstSDPMedia
1809  * @key: a key
1810  * @nth: an index
1811  *
1812  * Get the @nth attribute value for @key in @media.
1813  *
1814  * Returns: the @nth attribute value.
1815  */
1816 const gchar *
1817 gst_sdp_media_get_attribute_val_n (const GstSDPMedia * media, const gchar * key,
1818     guint nth)
1819 {
1820   guint i;
1821
1822   for (i = 0; i < media->attributes->len; i++) {
1823     GstSDPAttribute *attr;
1824
1825     attr = &g_array_index (media->attributes, GstSDPAttribute, i);
1826     if (!strcmp (attr->key, key)) {
1827       if (nth == 0)
1828         return attr->value;
1829       else
1830         nth--;
1831     }
1832   }
1833   return NULL;
1834 }
1835
1836 /**
1837  * gst_sdp_media_get_attribute_val:
1838  * @media: a #GstSDPMedia
1839  * @key: a key
1840  *
1841  * Get the first attribute value for @key in @media.
1842  *
1843  * Returns: the first attribute value for @key.
1844  */
1845 const gchar *
1846 gst_sdp_media_get_attribute_val (const GstSDPMedia * media, const gchar * key)
1847 {
1848   return gst_sdp_media_get_attribute_val_n (media, key, 0);
1849 }
1850
1851 static void
1852 read_string (gchar * dest, guint size, gchar ** src)
1853 {
1854   guint idx;
1855
1856   idx = 0;
1857   /* skip spaces */
1858   while (g_ascii_isspace (**src))
1859     (*src)++;
1860
1861   while (!g_ascii_isspace (**src) && **src != '\0') {
1862     if (idx < size - 1)
1863       dest[idx++] = **src;
1864     (*src)++;
1865   }
1866   if (size > 0)
1867     dest[idx] = '\0';
1868 }
1869
1870 static void
1871 read_string_del (gchar * dest, guint size, gchar del, gchar ** src)
1872 {
1873   guint idx;
1874
1875   idx = 0;
1876   /* skip spaces */
1877   while (g_ascii_isspace (**src))
1878     (*src)++;
1879
1880   while (**src != del && **src != '\0') {
1881     if (idx < size - 1)
1882       dest[idx++] = **src;
1883     (*src)++;
1884   }
1885   if (size > 0)
1886     dest[idx] = '\0';
1887 }
1888
1889 enum
1890 {
1891   SDP_SESSION,
1892   SDP_MEDIA,
1893 };
1894
1895 typedef struct
1896 {
1897   guint state;
1898   GstSDPMessage *msg;
1899   GstSDPMedia *media;
1900 } SDPContext;
1901
1902 static gboolean
1903 gst_sdp_parse_line (SDPContext * c, gchar type, gchar * buffer)
1904 {
1905   gchar str[8192];
1906   gchar *p = buffer;
1907
1908 #define READ_STRING(field) read_string (str, sizeof (str), &p); REPLACE_STRING (field, str)
1909 #define READ_UINT(field) read_string (str, sizeof (str), &p); field = strtoul (str, NULL, 10)
1910
1911   switch (type) {
1912     case 'v':
1913       if (buffer[0] != '0')
1914         g_warning ("wrong SDP version");
1915       gst_sdp_message_set_version (c->msg, buffer);
1916       break;
1917     case 'o':
1918       READ_STRING (c->msg->origin.username);
1919       READ_STRING (c->msg->origin.sess_id);
1920       READ_STRING (c->msg->origin.sess_version);
1921       READ_STRING (c->msg->origin.nettype);
1922       READ_STRING (c->msg->origin.addrtype);
1923       READ_STRING (c->msg->origin.addr);
1924       break;
1925     case 's':
1926       REPLACE_STRING (c->msg->session_name, buffer);
1927       break;
1928     case 'i':
1929       if (c->state == SDP_SESSION) {
1930         REPLACE_STRING (c->msg->information, buffer);
1931       } else {
1932         REPLACE_STRING (c->media->information, buffer);
1933       }
1934       break;
1935     case 'u':
1936       REPLACE_STRING (c->msg->uri, buffer);
1937       break;
1938     case 'e':
1939       gst_sdp_message_add_email (c->msg, buffer);
1940       break;
1941     case 'p':
1942       gst_sdp_message_add_phone (c->msg, buffer);
1943       break;
1944     case 'c':
1945     {
1946       GstSDPConnection conn;
1947       gchar *str2;
1948
1949       memset (&conn, 0, sizeof (conn));
1950
1951       str2 = p;
1952       while ((str2 = strchr (str2, '/')))
1953         *str2++ = ' ';
1954       READ_STRING (conn.nettype);
1955       READ_STRING (conn.addrtype);
1956       READ_STRING (conn.address);
1957       /* only read TTL for IP4 */
1958       if (strcmp (conn.addrtype, "IP4") == 0)
1959         READ_UINT (conn.ttl);
1960       READ_UINT (conn.addr_number);
1961
1962       if (c->state == SDP_SESSION) {
1963         gst_sdp_message_set_connection (c->msg, conn.nettype, conn.addrtype,
1964             conn.address, conn.ttl, conn.addr_number);
1965       } else {
1966         gst_sdp_media_add_connection (c->media, conn.nettype, conn.addrtype,
1967             conn.address, conn.ttl, conn.addr_number);
1968       }
1969       gst_sdp_connection_init (&conn);
1970       break;
1971     }
1972     case 'b':
1973     {
1974       gchar str2[MAX_LINE_LEN];
1975
1976       read_string_del (str, sizeof (str), ':', &p);
1977       if (*p != '\0')
1978         p++;
1979       read_string (str2, sizeof (str2), &p);
1980       if (c->state == SDP_SESSION)
1981         gst_sdp_message_add_bandwidth (c->msg, str, atoi (str2));
1982       else
1983         gst_sdp_media_add_bandwidth (c->media, str, atoi (str2));
1984       break;
1985     }
1986     case 't':
1987       break;
1988     case 'k':
1989       break;
1990     case 'a':
1991       read_string_del (str, sizeof (str), ':', &p);
1992       if (*p != '\0')
1993         p++;
1994       if (c->state == SDP_SESSION)
1995         gst_sdp_message_add_attribute (c->msg, str, p);
1996       else
1997         gst_sdp_media_add_attribute (c->media, str, p);
1998       break;
1999     case 'm':
2000     {
2001       gchar *slash;
2002       GstSDPMedia nmedia;
2003
2004       c->state = SDP_MEDIA;
2005       memset (&nmedia, 0, sizeof (nmedia));
2006       gst_sdp_media_init (&nmedia);
2007
2008       /* m=<media> <port>/<number of ports> <proto> <fmt> ... */
2009       READ_STRING (nmedia.media);
2010       read_string (str, sizeof (str), &p);
2011       slash = g_strrstr (str, "/");
2012       if (slash) {
2013         *slash = '\0';
2014         nmedia.port = atoi (str);
2015         nmedia.num_ports = atoi (slash + 1);
2016       } else {
2017         nmedia.port = atoi (str);
2018         nmedia.num_ports = -1;
2019       }
2020       READ_STRING (nmedia.proto);
2021       do {
2022         read_string (str, sizeof (str), &p);
2023         gst_sdp_media_add_format (&nmedia, str);
2024       } while (*p != '\0');
2025
2026       gst_sdp_message_add_media (c->msg, &nmedia);
2027       c->media =
2028           &g_array_index (c->msg->medias, GstSDPMedia, c->msg->medias->len - 1);
2029       break;
2030     }
2031     default:
2032       break;
2033   }
2034   return TRUE;
2035 }
2036
2037 /**
2038  * gst_sdp_message_parse_buffer:
2039  * @data: the start of the buffer
2040  * @size: the size of the buffer
2041  * @msg: the result #GstSDPMessage
2042  *
2043  * Parse the contents of @size bytes pointed to by @data and store the result in
2044  * @msg.
2045  *
2046  * Returns: #GST_SDP_OK on success.
2047  */
2048 GstSDPResult
2049 gst_sdp_message_parse_buffer (const guint8 * data, guint size,
2050     GstSDPMessage * msg)
2051 {
2052   gchar *p;
2053   SDPContext c;
2054   gchar type;
2055   gchar buffer[MAX_LINE_LEN];
2056   guint idx = 0;
2057
2058   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
2059   g_return_val_if_fail (data != NULL, GST_SDP_EINVAL);
2060   g_return_val_if_fail (size != 0, GST_SDP_EINVAL);
2061
2062   c.state = SDP_SESSION;
2063   c.msg = msg;
2064   c.media = NULL;
2065
2066   p = (gchar *) data;
2067   while (TRUE) {
2068     while (g_ascii_isspace (*p))
2069       p++;
2070
2071     type = *p++;
2072     if (type == '\0')
2073       break;
2074
2075     if (*p != '=')
2076       goto line_done;
2077     p++;
2078
2079     idx = 0;
2080     while (*p != '\n' && *p != '\r' && *p != '\0') {
2081       if (idx < sizeof (buffer) - 1)
2082         buffer[idx++] = *p;
2083       p++;
2084     }
2085     buffer[idx] = '\0';
2086     gst_sdp_parse_line (&c, type, buffer);
2087
2088   line_done:
2089     while (*p != '\n' && *p != '\0')
2090       p++;
2091     if (*p == '\n')
2092       p++;
2093   }
2094
2095   return GST_SDP_OK;
2096 }
2097
2098 static void
2099 print_media (GstSDPMedia * media)
2100 {
2101   g_print ("   media:       '%s'\n", GST_STR_NULL (media->media));
2102   g_print ("   port:        '%u'\n", media->port);
2103   g_print ("   num_ports:   '%u'\n", media->num_ports);
2104   g_print ("   proto:       '%s'\n", GST_STR_NULL (media->proto));
2105   if (media->fmts->len > 0) {
2106     guint i;
2107
2108     g_print ("   formats:\n");
2109     for (i = 0; i < media->fmts->len; i++) {
2110       g_print ("    format  '%s'\n", g_array_index (media->fmts, gchar *, i));
2111     }
2112   }
2113   g_print ("   information: '%s'\n", GST_STR_NULL (media->information));
2114   if (media->connections->len > 0) {
2115     guint i;
2116
2117     g_print ("   connections:\n");
2118     for (i = 0; i < media->connections->len; i++) {
2119       GstSDPConnection *conn =
2120           &g_array_index (media->connections, GstSDPConnection, i);
2121
2122       g_print ("    nettype:      '%s'\n", GST_STR_NULL (conn->nettype));
2123       g_print ("    addrtype:     '%s'\n", GST_STR_NULL (conn->addrtype));
2124       g_print ("    address:      '%s'\n", GST_STR_NULL (conn->address));
2125       g_print ("    ttl:          '%u'\n", conn->ttl);
2126       g_print ("    addr_number:  '%u'\n", conn->addr_number);
2127     }
2128   }
2129   if (media->bandwidths->len > 0) {
2130     guint i;
2131
2132     g_print ("   bandwidths:\n");
2133     for (i = 0; i < media->bandwidths->len; i++) {
2134       GstSDPBandwidth *bw =
2135           &g_array_index (media->bandwidths, GstSDPBandwidth, i);
2136
2137       g_print ("    type:         '%s'\n", GST_STR_NULL (bw->bwtype));
2138       g_print ("    bandwidth:    '%u'\n", bw->bandwidth);
2139     }
2140   }
2141   g_print ("   key:\n");
2142   g_print ("    type:       '%s'\n", GST_STR_NULL (media->key.type));
2143   g_print ("    data:       '%s'\n", GST_STR_NULL (media->key.data));
2144   if (media->attributes->len > 0) {
2145     guint i;
2146
2147     g_print ("   attributes:\n");
2148     for (i = 0; i < media->attributes->len; i++) {
2149       GstSDPAttribute *attr =
2150           &g_array_index (media->attributes, GstSDPAttribute, i);
2151
2152       g_print ("    attribute '%s' : '%s'\n", attr->key, attr->value);
2153     }
2154   }
2155 }
2156
2157 /**
2158  * gst_sdp_message_dump:
2159  * @msg: a #GstSDPMessage
2160  *
2161  * Dump the parsed contents of @msg to stdout.
2162  *
2163  * Returns: a #GstSDPResult.
2164  */
2165 GstSDPResult
2166 gst_sdp_message_dump (const GstSDPMessage * msg)
2167 {
2168   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
2169
2170   g_print ("sdp packet %p:\n", msg);
2171   g_print (" version:       '%s'\n", GST_STR_NULL (msg->version));
2172   g_print (" origin:\n");
2173   g_print ("  username:     '%s'\n", GST_STR_NULL (msg->origin.username));
2174   g_print ("  sess_id:      '%s'\n", GST_STR_NULL (msg->origin.sess_id));
2175   g_print ("  sess_version: '%s'\n", GST_STR_NULL (msg->origin.sess_version));
2176   g_print ("  nettype:      '%s'\n", GST_STR_NULL (msg->origin.nettype));
2177   g_print ("  addrtype:     '%s'\n", GST_STR_NULL (msg->origin.addrtype));
2178   g_print ("  addr:         '%s'\n", GST_STR_NULL (msg->origin.addr));
2179   g_print (" session_name:  '%s'\n", GST_STR_NULL (msg->session_name));
2180   g_print (" information:   '%s'\n", GST_STR_NULL (msg->information));
2181   g_print (" uri:           '%s'\n", GST_STR_NULL (msg->uri));
2182
2183   if (msg->emails->len > 0) {
2184     guint i;
2185
2186     g_print (" emails:\n");
2187     for (i = 0; i < msg->emails->len; i++) {
2188       g_print ("  email '%s'\n", g_array_index (msg->emails, gchar *, i));
2189     }
2190   }
2191   if (msg->phones->len > 0) {
2192     guint i;
2193
2194     g_print (" phones:\n");
2195     for (i = 0; i < msg->phones->len; i++) {
2196       g_print ("  phone '%s'\n", g_array_index (msg->phones, gchar *, i));
2197     }
2198   }
2199   g_print (" connection:\n");
2200   g_print ("  nettype:      '%s'\n", GST_STR_NULL (msg->connection.nettype));
2201   g_print ("  addrtype:     '%s'\n", GST_STR_NULL (msg->connection.addrtype));
2202   g_print ("  address:      '%s'\n", GST_STR_NULL (msg->connection.address));
2203   g_print ("  ttl:          '%u'\n", msg->connection.ttl);
2204   g_print ("  addr_number:  '%u'\n", msg->connection.addr_number);
2205   if (msg->bandwidths->len > 0) {
2206     guint i;
2207
2208     g_print (" bandwidths:\n");
2209     for (i = 0; i < msg->bandwidths->len; i++) {
2210       GstSDPBandwidth *bw =
2211           &g_array_index (msg->bandwidths, GstSDPBandwidth, i);
2212
2213       g_print ("  type:         '%s'\n", GST_STR_NULL (bw->bwtype));
2214       g_print ("  bandwidth:    '%u'\n", bw->bandwidth);
2215     }
2216   }
2217   g_print (" key:\n");
2218   g_print ("  type:         '%s'\n", GST_STR_NULL (msg->key.type));
2219   g_print ("  data:         '%s'\n", GST_STR_NULL (msg->key.data));
2220   if (msg->attributes->len > 0) {
2221     guint i;
2222
2223     g_print (" attributes:\n");
2224     for (i = 0; i < msg->attributes->len; i++) {
2225       GstSDPAttribute *attr =
2226           &g_array_index (msg->attributes, GstSDPAttribute, i);
2227
2228       g_print ("  attribute '%s' : '%s'\n", attr->key, attr->value);
2229     }
2230   }
2231   if (msg->medias->len > 0) {
2232     guint i;
2233
2234     g_print (" medias:\n");
2235     for (i = 0; i < msg->medias->len; i++) {
2236       g_print ("  media %u:\n", i);
2237       print_media (&g_array_index (msg->medias, GstSDPMedia, i));
2238     }
2239   }
2240   return GST_SDP_OK;
2241 }