Document the SDP library.
[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., 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
58 #include <stdlib.h>
59 #include <string.h>
60
61 #include "gstsdpmessage.h"
62
63 /* FIXME, is currently allocated on the stack */
64 #define MAX_LINE_LEN    1024 * 16
65
66 #define FREE_STRING(field)      g_free ((field)); (field) = NULL;
67 #define FREE_ARRAY(field)       \
68 G_STMT_START {                  \
69   if (field)                    \
70     g_array_free (field, TRUE); \
71   field = NULL;                 \
72 } G_STMT_END
73 #define REPLACE_STRING(field,val)       FREE_STRING(field);field=g_strdup (val);
74
75 #define INIT_ARRAY(field,type,init_func)                \
76 G_STMT_START {                                          \
77   if (field) {                                          \
78     guint i;                                            \
79     for(i=0; i<field->len; i++)                         \
80       init_func (&g_array_index(field, type, i));       \
81     g_array_set_size (field,0);                         \
82   }                                                     \
83   else                                                  \
84     field = g_array_new (FALSE, TRUE, sizeof(type));    \
85 } G_STMT_END
86
87 #define DEFINE_STRING_SETTER(field)                                     \
88 GstSDPResult gst_sdp_message_set_##field (GstSDPMessage *msg, const gchar *val) {      \
89   g_free (msg->field);                                                  \
90   msg->field = g_strdup (val);                                          \
91   return GST_SDP_OK;                                                       \
92 }
93 #define DEFINE_STRING_GETTER(field)                                     \
94 const gchar* gst_sdp_message_get_##field (GstSDPMessage *msg) {                       \
95   return msg->field;                                                    \
96 }
97
98 #define DEFINE_ARRAY_LEN(field)                                         \
99 gint gst_sdp_message_##field##_len (GstSDPMessage *msg) {                      \
100   return ((msg)->field->len);                                           \
101 }
102 #define DEFINE_ARRAY_GETTER(method,field,type)                          \
103 type gst_sdp_message_get_##method (GstSDPMessage *msg, guint idx) {            \
104   return g_array_index ((msg)->field, type, idx);                       \
105 }
106 #define DEFINE_ARRAY_P_GETTER(method,field,type)                        \
107 type * gst_sdp_message_get_##method (GstSDPMessage *msg, guint idx) {          \
108   return &g_array_index ((msg)->field, type, idx);                      \
109 }
110 #define DEFINE_ARRAY_ADDER(method,field,type,dup_method)                \
111 GstSDPResult gst_sdp_message_add_##method (GstSDPMessage *msg, type val) {       \
112   type v = dup_method(val);                                             \
113   g_array_append_val((msg)->field, v);                                  \
114   return GST_SDP_OK;                                                       \
115 }
116
117 static void
118 gst_sdp_origin_init (GstSDPOrigin * origin)
119 {
120   FREE_STRING (origin->username);
121   FREE_STRING (origin->sess_id);
122   FREE_STRING (origin->sess_version);
123   FREE_STRING (origin->nettype);
124   FREE_STRING (origin->addrtype);
125   FREE_STRING (origin->addr);
126 }
127
128 static void
129 gst_sdp_connection_init (GstSDPConnection * connection)
130 {
131   FREE_STRING (connection->nettype);
132   FREE_STRING (connection->addrtype);
133   FREE_STRING (connection->address);
134   connection->ttl = 0;
135   connection->addr_number = 0;
136 }
137
138 static void
139 gst_sdp_bandwidth_init (GstSDPBandwidth * bandwidth)
140 {
141   FREE_STRING (bandwidth->bwtype);
142   bandwidth->bandwidth = 0;
143 }
144
145 static void
146 gst_sdp_time_init (GstSDPTime * time)
147 {
148   FREE_STRING (time->start);
149   FREE_STRING (time->stop);
150   time->n_repeat = 0;
151 }
152
153 static void
154 gst_sdp_zone_init (GstSDPZone * zone)
155 {
156   FREE_STRING (zone->time);
157   FREE_STRING (zone->typed_time);
158 }
159
160 static void
161 gst_sdp_key_init (GstSDPKey * key)
162 {
163   FREE_STRING (key->type);
164   FREE_STRING (key->data);
165 }
166
167 static void
168 gst_sdp_attribute_init (GstSDPAttribute * attr)
169 {
170   FREE_STRING (attr->key);
171   FREE_STRING (attr->value);
172 }
173
174 /**
175  * gst_sdp_message_new:
176  * @msg: pointer to new #GstSDPMessage
177  *
178  * Allocate a new GstSDPMessage and store the result in @msg.
179  *
180  * Returns: a #GstSDPResult.
181  */
182 GstSDPResult
183 gst_sdp_message_new (GstSDPMessage ** msg)
184 {
185   GstSDPMessage *newmsg;
186
187   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
188
189   newmsg = g_new0 (GstSDPMessage, 1);
190
191   *msg = newmsg;
192
193   return gst_sdp_message_init (newmsg);
194 }
195
196 /**
197  * gst_sdp_message_init:
198  * @msg: a #GstSDPMessage
199  *
200  * Initialize @msg so that its contents are as if it was freshly allocated
201  * with gst_sdp_message_new(). This function is mostly used to initialize a message
202  * allocated on the stack. gst_sdp_message_uninit() undoes this operation.
203  *
204  * When this function is invoked on newly allocated data (with malloc or on the
205  * stack), its contents should be set to 0 before calling this function.
206  *
207  * Returns: a #GstSDPResult.
208  */
209 GstSDPResult
210 gst_sdp_message_init (GstSDPMessage * msg)
211 {
212   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
213
214   FREE_STRING (msg->version);
215   gst_sdp_origin_init (&msg->origin);
216   FREE_STRING (msg->session_name);
217   FREE_STRING (msg->information);
218   FREE_STRING (msg->uri);
219   INIT_ARRAY (msg->emails, gchar *, g_free);
220   INIT_ARRAY (msg->phones, gchar *, g_free);
221   gst_sdp_connection_init (&msg->connection);
222   INIT_ARRAY (msg->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_init);
223   INIT_ARRAY (msg->times, GstSDPTime, gst_sdp_time_init);
224   INIT_ARRAY (msg->zones, GstSDPZone, gst_sdp_zone_init);
225   gst_sdp_key_init (&msg->key);
226   INIT_ARRAY (msg->attributes, GstSDPAttribute, gst_sdp_attribute_init);
227   INIT_ARRAY (msg->medias, GstSDPMedia, gst_sdp_media_uninit);
228
229   return GST_SDP_OK;
230 }
231
232 /**
233  * gst_sdp_message_uninit:
234  * @msg: a #GstSDPMessage
235  *
236  * Free all resources allocated in @msg. @msg should not be used anymore after
237  * this function. This function should be used when @msg was allocated on the
238  * stack and initialized with gst_sdp_message_init().
239  *
240  * Returns: a #GstSDPResult.
241  */
242 GstSDPResult
243 gst_sdp_message_uninit (GstSDPMessage * msg)
244 {
245   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
246
247   gst_sdp_message_init (msg);
248
249   FREE_ARRAY (msg->emails);
250   FREE_ARRAY (msg->phones);
251   FREE_ARRAY (msg->bandwidths);
252   FREE_ARRAY (msg->times);
253   FREE_ARRAY (msg->zones);
254   FREE_ARRAY (msg->attributes);
255   FREE_ARRAY (msg->medias);
256
257   return GST_SDP_OK;
258 }
259
260 /**
261  * gst_sdp_message_free:
262  * @msg: a #GstSDPMessage
263  *
264  * Free all resources allocated by @msg. @msg should not be used anymore after
265  * this function. This function should be used when @msg was dynamically
266  * allocated with gst_sdp_message_new().
267  *
268  * Returns: a #GstSDPResult.
269  */
270 GstSDPResult
271 gst_sdp_message_free (GstSDPMessage * msg)
272 {
273   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
274
275   gst_sdp_message_uninit (msg);
276   g_free (msg);
277
278   return GST_SDP_OK;
279 }
280
281 /**
282  * gst_sdp_message_set_version:
283  * @msg: a #GstSDPMessage
284  * @version: the version
285  *
286  * Set the version in @msg.
287  *
288  * Returns: a #GstSDPResult.
289  */
290 DEFINE_STRING_SETTER (version);
291 /**
292  * gst_sdp_message_get_version:
293  * @msg: a #GstSDPMessage
294  *
295  * Get the version in @msg.
296  *
297  * Returns: a #GstSDPResult.
298  */
299 DEFINE_STRING_GETTER (version);
300
301 /**
302  * gst_sdp_message_set_origin:
303  * @msg: a #GstSDPMessage
304  * @username: the user name
305  * @sess_id: a session id
306  * @sess_version: a session version
307  * @nettype: a network type
308  * @addrtype: an address type
309  * @addr: an address
310  *
311  * Configure the SDP origin in @msg with the given parameters.
312  *
313  * Returns: #GST_SDP_OK.
314  */
315 GstSDPResult
316 gst_sdp_message_set_origin (GstSDPMessage * msg, const gchar * username,
317     const gchar * sess_id, const gchar * sess_version, const gchar * nettype,
318     const gchar * addrtype, const gchar * addr)
319 {
320   REPLACE_STRING (msg->origin.username, username);
321   REPLACE_STRING (msg->origin.sess_id, sess_id);
322   REPLACE_STRING (msg->origin.sess_version, sess_version);
323   REPLACE_STRING (msg->origin.nettype, nettype);
324   REPLACE_STRING (msg->origin.addrtype, addrtype);
325   REPLACE_STRING (msg->origin.addr, addr);
326
327   return GST_SDP_OK;
328 }
329
330 /**
331  * gst_sdp_message_get_origin:
332  * @msg: a #GstSDPMessage
333  *
334  * Get the origin of @msg.
335  *
336  * Returns: a #GstSDPOrigin. The result remains valid as long as @msg is valid.
337  */
338 const GstSDPOrigin *
339 gst_sdp_message_get_origin (GstSDPMessage * msg)
340 {
341   return &msg->origin;
342 }
343
344 /**
345  * gst_sdp_message_set_session_name:
346  * @msg: a #GstSDPMessage
347  * @session_name: the session name
348  *
349  * Set the session name in @msg.
350  *
351  * Returns: a #GstSDPResult.
352  */
353 DEFINE_STRING_SETTER (session_name);
354 /**
355  * gst_sdp_message_get_session_name:
356  * @msg: a #GstSDPMessage
357  *
358  * Get the session name in @msg.
359  *
360  * Returns: a #GstSDPResult.
361  */
362 DEFINE_STRING_GETTER (session_name);
363 /**
364  * gst_sdp_message_set_information:
365  * @msg: a #GstSDPMessage
366  * @information: the information
367  *
368  * Set the information in @msg.
369  *
370  * Returns: a #GstSDPResult.
371  */
372 DEFINE_STRING_SETTER (information);
373 /**
374  * gst_sdp_message_get_information:
375  * @msg: a #GstSDPMessage
376  *
377  * Get the information in @msg.
378  *
379  * Returns: a #GstSDPResult.
380  */
381 DEFINE_STRING_GETTER (information);
382 /**
383  * gst_sdp_message_set_uri:
384  * @msg: a #GstSDPMessage
385  * @uri: the URI
386  *
387  * Set the URI in @msg.
388  *
389  * Returns: a #GstSDPResult.
390  */
391 DEFINE_STRING_SETTER (uri);
392 /**
393  * gst_sdp_message_get_uri:
394  * @msg: a #GstSDPMessage
395  *
396  * Get the URI in @msg.
397  *
398  * Returns: a #GstSDPResult.
399  */
400 DEFINE_STRING_GETTER (uri);
401
402 /**
403  * gst_sdp_message_emails_len:
404  * @msg: a #GstSDPMessage
405  *
406  * Get the number of emails in @msg.
407  *
408  * Returns: the number of emails in @msg.
409  */
410 DEFINE_ARRAY_LEN (emails);
411 /**
412  * gst_sdp_message_get_email:
413  * @msg: a #GstSDPMessage
414  * @idx: an email index
415  *
416  * Get the email with number @idx from @msg.
417  *
418  * Returns: the email at position @idx. 
419  */
420 DEFINE_ARRAY_GETTER (email, emails, const gchar *);
421
422 /**
423  * gst_sdp_message_add_email:
424  * @msg: a #GstSDPMessage
425  * @email: an email
426  *
427  * Add @email to the list of emails in @msg.
428  *
429  * Returns: a #GstSDPResult.
430  */
431 DEFINE_ARRAY_ADDER (email, emails, const gchar *, g_strdup);
432
433 /**
434  * gst_sdp_message_phones_len:
435  * @msg: a #GstSDPMessage
436  *
437  * Get the number of phones in @msg.
438  *
439  * Returns: the number of phones in @msg.
440  */
441 DEFINE_ARRAY_LEN (phones);
442 /**
443  * gst_sdp_message_get_phone:
444  * @msg: a #GstSDPMessage
445  * @idx: a phone index
446  *
447  * Get the phone with number @idx from @msg.
448  *
449  * Returns: the phone at position @idx. 
450  */
451 DEFINE_ARRAY_GETTER (phone, phones, const gchar *);
452
453 /**
454  * gst_sdp_message_add_phone:
455  * @msg: a #GstSDPMessage
456  * @phone: a phone
457  *
458  * Add @phone to the list of phones in @msg.
459  *
460  * Returns: a #GstSDPResult.
461  */
462 DEFINE_ARRAY_ADDER (phone, phones, const gchar *, g_strdup);
463
464 /**
465  * gst_sdp_message_set_connection:
466  * @msg: a #GstSDPMessage
467  * @nettype: the type of network. "IN" is defined to have the meaning
468  * "Internet".
469  * @addrtype: the type of address.
470  * @address: the address
471  * @ttl: the time to live of the address
472  * @addr_number: the number of layers
473  *
474  * Configure the SDP connection in @msg with the given parameters.
475  *
476  * Returns: a #GstSDPResult.
477  */
478 GstSDPResult
479 gst_sdp_message_set_connection (GstSDPMessage * msg, const gchar * nettype,
480     const gchar * addrtype, const gchar * address, gint ttl, gint addr_number)
481 {
482   REPLACE_STRING (msg->connection.nettype, nettype);
483   REPLACE_STRING (msg->connection.addrtype, addrtype);
484   REPLACE_STRING (msg->connection.address, address);
485   msg->connection.ttl = ttl;
486   msg->connection.addr_number = addr_number;
487
488   return GST_SDP_OK;
489 }
490
491 /**
492  * gst_sdp_message_get_connection:
493  * @msg: a #GstSDPMessage
494  *
495  * Get the connection of @msg.
496  *
497  * Returns: a #GstSDPConnection. The result remains valid as long as @msg is valid.
498  */
499 const GstSDPConnection *
500 gst_sdp_message_get_connection (GstSDPMessage * msg)
501 {
502   return &msg->connection;
503 }
504
505 /**
506  * gst_sdp_message_bandwidths_len:
507  * @msg: a #GstSDPMessage
508  *
509  * Get the number of bandwidth information in @msg.
510  *
511  * Returns: the number of bandwidth information in @msg.
512  */
513 DEFINE_ARRAY_LEN (bandwidths);
514 /**
515  * gst_sdp_message_get_bandwidth:
516  * @msg: a #GstSDPMessage
517  * @idx: the bandwidth index
518  *
519  * Get the bandwidth at index @idx from @msg.
520  *
521  * Returns: a #GstSDPBandwidth.
522  */
523 DEFINE_ARRAY_P_GETTER (bandwidth, bandwidths, const GstSDPBandwidth);
524
525 /**
526  * gst_sdp_message_add_bandwidth:
527  * @msg: a #GstSDPMessage
528  * @bwtype: the bandwidth modifier type 
529  * @bandwidth: the bandwidth in kilobits per second
530  *
531  * Add the specified bandwidth information to @msg.
532  *
533  * Returns: a #GstSDPResult.
534  */
535 GstSDPResult
536 gst_sdp_message_add_bandwidth (GstSDPMessage * msg, const gchar * bwtype,
537     gint bandwidth)
538 {
539   GstSDPBandwidth bw;
540
541   bw.bwtype = g_strdup (bwtype);
542   bw.bandwidth = bandwidth;
543
544   g_array_append_val (msg->bandwidths, bw);
545
546   return GST_SDP_OK;
547 }
548
549 /**
550  * gst_sdp_message_times_len:
551  * @msg: a #GstSDPMessage
552  *
553  * Get the number of time information entries in @msg.
554  *
555  * Returns: the number of time information entries in @msg.
556  */
557 DEFINE_ARRAY_LEN (times);
558
559 /**
560  * gst_sdp_message_get_time:
561  * @msg: a #GstSDPMessage
562  * @idx: the time index
563  *
564  * Get time information with index @idx from @msg.
565  *
566  * Returns: a #GstSDPTime.
567  */
568 DEFINE_ARRAY_P_GETTER (time, times, const GstSDPTime);
569
570 /**
571  * gst_sdp_message_add_time:
572  * @msg: a #GstSDPMessage
573  * @time: a time information
574  *
575  * Add time information @time to @msg.
576  *
577  * Returns: a #GstSDPResult.
578  */
579 GstSDPResult
580 gst_sdp_message_add_time (GstSDPMessage * msg, const gchar * time)
581 {
582   return GST_SDP_OK;
583 }
584
585 /**
586  * gst_sdp_message_zones_len:
587  * @msg: a #GstSDPMessage
588  *
589  * Get the number of time zone information entries in @msg.
590  *
591  * Returns: the number of time zone information entries in @msg.
592  */
593 DEFINE_ARRAY_LEN (zones);
594 /**
595  * gst_sdp_message_get_zone:
596  * @msg: a #GstSDPMessage
597  * @idx: the zone index
598  *
599  * Get time zone information with index @idx from @msg.
600  *
601  * Returns: a #GstSDPZone.
602  */
603 DEFINE_ARRAY_P_GETTER (zone, zones, const GstSDPZone);
604
605 /**
606  * gst_sdp_message_add_zone:
607  * @msg: a #GstSDPMessage
608  * @time: the NTP time that a time zone adjustment happens
609  * @typed_time: the offset from the time when the session was first scheduled 
610  *
611  * Add time zone information to @msg.
612  *
613  * Returns: a #GstSDPResult.
614  */
615 GstSDPResult
616 gst_sdp_message_add_zone (GstSDPMessage * msg, const gchar * time,
617     const gchar * typed_time)
618 {
619   GstSDPZone zone;
620
621   zone.time = g_strdup (time);
622   zone.typed_time = g_strdup (typed_time);
623
624   g_array_append_val (msg->zones, zone);
625
626   return GST_SDP_OK;
627 }
628
629 /**
630  * gst_sdp_message_set_key:
631  * @msg: a #GstSDPMessage
632  * @type: the encryption type
633  * @data: the encryption data
634  *
635  * Adds the encryption information to @msg.
636  *
637  * Returns: a #GstSDPResult.
638  */
639 GstSDPResult
640 gst_sdp_message_set_key (GstSDPMessage * msg, const gchar * type,
641     const gchar * data)
642 {
643   REPLACE_STRING (msg->key.type, type);
644   REPLACE_STRING (msg->key.data, data);
645
646   return GST_SDP_OK;
647 }
648
649 /**
650  * gst_sdp_message_get_key:
651  * @msg: a #GstSDPMessage
652  *
653  * Get the encryption information from @msg.
654  *
655  * Returns: a #GstSDPKey.
656  */
657 const GstSDPKey *
658 gst_sdp_message_get_key (GstSDPMessage * msg)
659 {
660   return &msg->key;
661 }
662
663 /**
664  * gst_sdp_message_attributes_len:
665  * @msg: a #GstSDPMessage
666  *
667  * Get the number of attributes in @msg.
668  *
669  * Returns: the number of attributes in @msg.
670  */
671 DEFINE_ARRAY_LEN (attributes);
672 /**
673  * gst_sdp_message_get_attribute:
674  * @msg: a #GstSDPMessage
675  * @idx: the index
676  *
677  * Get the attribute at position @idx in @msg.
678  *
679  * Returns: the #GstSDPAttribute at position @idx.
680  */
681 DEFINE_ARRAY_P_GETTER (attribute, attributes, const GstSDPAttribute);
682 /**
683  * gst_sdp_message_get_attribute_val_n:
684  * @msg: a #GstSDPMessage
685  * @key: the key
686  * @nth: the index
687  *
688  * Get the @nth attribute with key @key in @msg.
689  *
690  * Returns: the attribute value of the @nth attribute with @key.
691  */
692 const gchar *
693 gst_sdp_message_get_attribute_val_n (GstSDPMessage * msg, const gchar * key,
694     guint nth)
695 {
696   guint i;
697
698   for (i = 0; i < msg->attributes->len; i++) {
699     GstSDPAttribute *attr;
700
701     attr = &g_array_index (msg->attributes, GstSDPAttribute, i);
702     if (!strcmp (attr->key, key)) {
703       if (nth == 0)
704         return attr->value;
705       else
706         nth--;
707     }
708   }
709   return NULL;
710 }
711
712 /**
713  * gst_sdp_message_get_attribute_val:
714  * @msg: a #GstSDPMessage
715  * @key: the key
716  *
717  * Get the first attribute with key @key in @msg.
718  *
719  * Returns: the attribute value of the first attribute with @key.
720  */
721 const gchar *
722 gst_sdp_message_get_attribute_val (GstSDPMessage * msg, const gchar * key)
723 {
724   return gst_sdp_message_get_attribute_val_n (msg, key, 0);
725 }
726
727 /**
728  * gst_sdp_message_add_attribute:
729  * @msg: a #GstSDPMessage
730  * @key: the key
731  * @value: the value
732  *
733  * Add the attribute with @key and @value to @msg.
734  *
735  * Returns: @GST_SDP_OK.
736  */
737 GstSDPResult
738 gst_sdp_message_add_attribute (GstSDPMessage * msg, const gchar * key,
739     const gchar * value)
740 {
741   GstSDPAttribute attr;
742
743   attr.key = g_strdup (key);
744   attr.value = g_strdup (value);
745
746   g_array_append_val (msg->attributes, attr);
747
748   return GST_SDP_OK;
749 }
750
751 /**
752  * gst_sdp_message_medias_len:
753  * @msg: a #GstSDPMessage
754  *
755  * Get the number of media descriptions in @msg.
756  *
757  * Returns: the number of media descriptions in @msg.
758  */
759 DEFINE_ARRAY_LEN (medias);
760 /**
761  * gst_sdp_message_get_media:
762  * @msg: a #GstSDPMessage
763  * @idx: the index
764  *
765  * Get the media description at index @idx in @msg.
766  *
767  * Returns: a #GstSDPMedia.
768  */
769 DEFINE_ARRAY_P_GETTER (media, medias, const GstSDPMedia);
770
771 /**
772  * gst_sdp_message_add_media:
773  * @msg: a #GstSDPMessage
774  * @media: a #GstSDPMedia to add
775  *
776  * Adds @media to the array of medias in @msg. This function takes ownership of
777  * the contents of @media so that @media will have to be reinitialized with
778  * gst_media_init() before it can be used again.
779  *
780  * Returns: a #GstSDPResult.
781  */
782 GstSDPResult
783 gst_sdp_message_add_media (GstSDPMessage * msg, GstSDPMedia * media)
784 {
785   gint len;
786   GstSDPMedia *nmedia;
787
788   len = msg->medias->len;
789   g_array_set_size (msg->medias, len + 1);
790   nmedia = &g_array_index (msg->medias, GstSDPMedia, len);
791
792   memcpy (nmedia, media, sizeof (GstSDPMedia));
793   memset (media, 0, sizeof (GstSDPMedia));
794
795   return GST_SDP_OK;
796 }
797
798 /* media access */
799
800 /**
801  * gst_sdp_media_new:
802  * @media: pointer to new #GstSDPMedia
803  *
804  * Allocate a new GstSDPMedia and store the result in @media.
805  *
806  * Returns: a #GstSDPResult.
807  */
808 GstSDPResult
809 gst_sdp_media_new (GstSDPMedia ** media)
810 {
811   GstSDPMedia *newmedia;
812
813   g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
814
815   newmedia = g_new0 (GstSDPMedia, 1);
816
817   *media = newmedia;
818
819   return gst_sdp_media_init (newmedia);
820 }
821
822 /**
823  * gst_sdp_media_init:
824  * @media: a #GstSDPMedia
825  *
826  * Initialize @media so that its contents are as if it was freshly allocated
827  * with gst_sdp_media_new(). This function is mostly used to initialize a media
828  * allocated on the stack. gst_sdp_media_uninit() undoes this operation.
829  *
830  * When this function is invoked on newly allocated data (with malloc or on the
831  * stack), its contents should be set to 0 before calling this function.
832  *
833  * Returns: a #GstSDPResult.
834  */
835 GstSDPResult
836 gst_sdp_media_init (GstSDPMedia * media)
837 {
838   g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
839
840   FREE_STRING (media->media);
841   media->port = 0;
842   media->num_ports = 0;
843   FREE_STRING (media->proto);
844   INIT_ARRAY (media->fmts, gchar *, g_free);
845   FREE_STRING (media->information);
846   INIT_ARRAY (media->connections, GstSDPConnection, gst_sdp_connection_init);
847   INIT_ARRAY (media->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_init);
848   gst_sdp_key_init (&media->key);
849   INIT_ARRAY (media->attributes, GstSDPAttribute, gst_sdp_attribute_init);
850
851   return GST_SDP_OK;
852 }
853
854 /**
855  * gst_sdp_media_uninit:
856  * @media: a #GstSDPMedia
857  *
858  * Free all resources allocated in @media. @media should not be used anymore after
859  * this function. This function should be used when @media was allocated on the
860  * stack and initialized with gst_sdp_media_init().
861  *
862  * Returns: a #GstSDPResult.
863  */
864 GstSDPResult
865 gst_sdp_media_uninit (GstSDPMedia * media)
866 {
867   g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
868
869   gst_sdp_media_init (media);
870   FREE_ARRAY (media->fmts);
871   FREE_ARRAY (media->connections);
872   FREE_ARRAY (media->bandwidths);
873   FREE_ARRAY (media->attributes);
874
875   return GST_SDP_OK;
876 }
877
878 /**
879  * gst_sdp_media_free:
880  * @media: a #GstSDPMedia
881  *
882  * Free all resources allocated by @media. @media should not be used anymore after
883  * this function. This function should be used when @media was dynamically
884  * allocated with gst_sdp_media_new().
885  *
886  * Returns: a #GstSDPResult.
887  */
888 GstSDPResult
889 gst_sdp_media_free (GstSDPMedia * media)
890 {
891   g_return_val_if_fail (media != NULL, GST_SDP_EINVAL);
892
893   gst_sdp_media_uninit (media);
894   g_free (media);
895
896   return GST_SDP_OK;
897 }
898
899 /**
900  * gst_sdp_media_get_media:
901  * @media: a #GstSDPMedia
902  *
903  * Get the media description of @media.
904  *
905  * Returns: the media description.
906  */
907 const gchar *
908 gst_sdp_media_get_media (GstSDPMedia * media)
909 {
910   return media->media;
911 }
912
913 /**
914  * gst_sdp_media_set_media:
915  * @media: a #GstSDPMedia
916  * @med: the media description
917  *
918  * Set the media description of @media to @med.
919  *
920  * Returns: #GST_SDP_OK.
921  */
922 GstSDPResult
923 gst_sdp_media_set_media (GstSDPMedia * media, const gchar * med)
924 {
925   g_free (media->media);
926   media->media = g_strdup (med);
927
928   return GST_SDP_OK;
929 }
930
931 /**
932  * gst_sdp_media_get_port:
933  * @media: a #GstSDPMedia
934  *
935  * Get the port number for @media.
936  *
937  * Returns: the port number of @media.
938  */
939 gint
940 gst_sdp_media_get_port (GstSDPMedia * media)
941 {
942   return media->port;
943 }
944
945 /**
946  * gst_sdp_media_get_num_ports:
947  * @media: a #GstSDPMedia
948  *
949  * Get the number of ports for @media.
950  *
951  * Returns: the number of ports for @media.
952  */
953 gint
954 gst_sdp_media_get_num_ports (GstSDPMedia * media)
955 {
956   return media->num_ports;
957 }
958
959 /**
960  * gst_sdp_media_set_port_info:
961  * @media: a #GstSDPMedia
962  * @port: the port number
963  * @num_ports: the number of ports
964  *
965  * Set the port information in @media.
966  *
967  * Returns: #GST_SDP_OK.
968  */
969 GstSDPResult
970 gst_sdp_media_set_port_info (GstSDPMedia * media, gint port, gint num_ports)
971 {
972   media->port = port;
973   media->num_ports = num_ports;
974
975   return GST_SDP_OK;
976 }
977
978 /**
979  * gst_sdp_media_get_proto:
980  * @media: a #GstSDPMedia
981  *
982  * Get the transport protocol of @media
983  *
984  * Returns: the transport protocol of @media.
985  */
986 const gchar *
987 gst_sdp_media_get_proto (GstSDPMedia * media)
988 {
989   return media->proto;
990 }
991
992 /**
993  * gst_sdp_media_set_proto:
994  * @media: a #GstSDPMedia
995  * @proto: the media transport protocol
996  *
997  * Set the media transport protocol of @media to @proto.
998  *
999  * Returns: #GST_SDP_OK.
1000  */
1001 GstSDPResult
1002 gst_sdp_media_set_proto (GstSDPMedia * media, const gchar * proto)
1003 {
1004   g_free (media->proto);
1005   media->proto = g_strdup (proto);
1006
1007   return GST_SDP_OK;
1008 }
1009
1010 /**
1011  * gst_sdp_media_formats_len:
1012  * @media: a #GstSDPMedia
1013  *
1014  * Get the number of formats in @media.
1015  *
1016  * Returns: the number of formats in @media.
1017  */
1018 gint
1019 gst_sdp_media_formats_len (GstSDPMedia * media)
1020 {
1021   return media->fmts->len;
1022 }
1023
1024 /**
1025  * gst_sdp_media_get_format:
1026  * @media: a #GstSDPMedia
1027  * @idx: an index
1028  *
1029  * Get the format information at position @idx in @media.
1030  *
1031  * Returns: the format at position @idx.
1032  */
1033 const gchar *
1034 gst_sdp_media_get_format (GstSDPMedia * media, guint idx)
1035 {
1036   if (idx >= media->fmts->len)
1037     return NULL;
1038   return g_array_index (media->fmts, gchar *, idx);
1039 }
1040
1041 /**
1042  * gst_sdp_media_add_format:
1043  * @media: a #GstSDPMedia
1044  * @format: the format
1045  *
1046  * Add the format information to @media.
1047  *
1048  * Returns: #GST_SDP_OK.
1049  */
1050 GstSDPResult
1051 gst_sdp_media_add_format (GstSDPMedia * media, const gchar * format)
1052 {
1053   gchar *fmt;
1054
1055   fmt = g_strdup (format);
1056
1057   g_array_append_val (media->fmts, fmt);
1058
1059   return GST_SDP_OK;
1060 }
1061
1062 /**
1063  * gst_sdp_media_get_information:
1064  * @media: a #GstSDPMedia
1065  *
1066  * Get the information of @media
1067  *
1068  * Returns: the information of @media.
1069  */
1070 const gchar *
1071 gst_sdp_media_get_information (GstSDPMedia * media)
1072 {
1073   return media->information;
1074 }
1075
1076 /**
1077  * gst_sdp_media_set_information:
1078  * @media: a #GstSDPMedia
1079  * @information: the media information
1080  *
1081  * Set the media information of @media to @information.
1082  *
1083  * Returns: #GST_SDP_OK.
1084  */
1085 GstSDPResult
1086 gst_sdp_media_set_information (GstSDPMedia * media, const gchar * information)
1087 {
1088   g_free (media->information);
1089   media->information = g_strdup (information);
1090
1091   return GST_SDP_OK;
1092 }
1093
1094 /**
1095  * gst_sdp_media_connections_len:
1096  * @media: a #GstSDPMedia
1097  *
1098  * Get the number of connection fields in @media.
1099  *
1100  * Returns: the number of connections in @media.
1101  */
1102 gint
1103 gst_sdp_media_connections_len (GstSDPMedia * media)
1104 {
1105   return (media->connections->len);
1106 }
1107
1108 /**
1109  * gst_sdp_media_get_connection:
1110  * @media: a #GstSDPMedia
1111  * @idx: an index
1112  *
1113  * Get the connection at position @idx in @media.
1114  *
1115  * Returns: the #GstSDPConnection at position @idx.
1116  */
1117 const GstSDPConnection *
1118 gst_sdp_media_get_connection (GstSDPMedia * media, guint idx)
1119 {
1120   return &g_array_index (media->connections, GstSDPConnection, idx);
1121 }
1122
1123 /**
1124  * gst_sdp_media_add_connection:
1125  * @media: a #GstSDPMedia
1126  * @nettype: the type of network. "IN" is defined to have the meaning
1127  * "Internet".
1128  * @addrtype: the type of address.
1129  * @address: the address
1130  * @ttl: the time to live of the address
1131  * @addr_number: the number of layers
1132  *
1133  * Add the given connection parameters to @media.
1134  *
1135  * Returns: a #GstSDPResult.
1136  */
1137 GstSDPResult
1138 gst_sdp_media_add_connection (GstSDPMedia * media, const gchar * nettype,
1139     const gchar * addrtype, const gchar * address, gint ttl, gint addr_number)
1140 {
1141   GstSDPConnection conn;
1142
1143   conn.nettype = g_strdup (nettype);
1144   conn.addrtype = g_strdup (addrtype);
1145   conn.address = g_strdup (address);
1146   conn.ttl = ttl;
1147   conn.addr_number = addr_number;
1148
1149   g_array_append_val (media->connections, conn);
1150
1151   return GST_SDP_OK;
1152 }
1153
1154 /**
1155  * gst_sdp_media_bandwidths_len:
1156  * @media: a #GstSDPMedia
1157  *
1158  * Get the number of bandwidth fields in @media.
1159  *
1160  * Returns: the number of bandwidths in @media.
1161  */
1162 gint
1163 gst_sdp_media_bandwidths_len (GstSDPMedia * media)
1164 {
1165   return (media->bandwidths->len);
1166 }
1167
1168 /**
1169  * gst_sdp_media_get_bandwidth:
1170  * @media: a #GstSDPMedia
1171  * @idx: an index
1172  *
1173  * Get the bandwidth at position @idx in @media.
1174  *
1175  * Returns: the #GstSDPBandwidth at position @idx.
1176  */
1177 const GstSDPBandwidth *
1178 gst_sdp_media_get_badwidth (GstSDPMedia * media, guint idx)
1179 {
1180   return &g_array_index (media->bandwidths, GstSDPBandwidth, idx);
1181 }
1182
1183 /**
1184  * gst_sdp_media_add_bandwidth:
1185  * @media: a #GstSDPMedia
1186  * @bwtype: the bandwidth modifier type
1187  * @bandwidth: the bandwidth in kilobits per second
1188  *
1189  * Add the bandwidth information with @bwtype and @bandwidth to @media.
1190  *
1191  * Returns: #GST_SDP_OK.
1192  */
1193 GstSDPResult
1194 gst_sdp_media_add_bandwidth (GstSDPMedia * media, const gchar * bwtype,
1195     gint bandwidth)
1196 {
1197   GstSDPBandwidth bw;
1198
1199   bw.bwtype = g_strdup (bwtype);
1200   bw.bandwidth = bandwidth;
1201
1202   g_array_append_val (media->bandwidths, bw);
1203
1204   return GST_SDP_OK;
1205 }
1206
1207 /**
1208  * gst_sdp_media_set_key:
1209  * @media: a #GstSDPMedia
1210  * @type: the encryption type
1211  * @data: the encryption data
1212  *
1213  * Adds the encryption information to @media.
1214  *
1215  * Returns: a #GstSDPResult.
1216  */
1217 GstSDPResult
1218 gst_sdp_media_set_key (GstSDPMedia * media, const gchar * type,
1219     const gchar * data)
1220 {
1221   g_free (media->key.type);
1222   media->key.type = g_strdup (type);
1223   g_free (media->key.data);
1224   media->key.data = g_strdup (data);
1225
1226   return GST_SDP_OK;
1227 }
1228
1229 /**
1230  * gst_sdp_media_get_key:
1231  * @media: a #GstSDPMedia
1232  *
1233  * Get the encryption information from @media.
1234  *
1235  * Returns: a #GstSDPKey.
1236  */
1237 const GstSDPKey *
1238 gst_sdp_media_get_key (GstSDPMedia * media)
1239 {
1240   return &media->key;
1241 }
1242
1243 /**
1244  * gst_sdp_media_attributes_len:
1245  * @media: a #GstSDPMedia
1246  *
1247  * Get the number of attribute fields in @media.
1248  *
1249  * Returns: the number of attributes in @media.
1250  */
1251 gint
1252 gst_sdp_media_attributes_len (GstSDPMedia * media)
1253 {
1254   return (media->attributes->len);
1255 }
1256
1257 /**
1258  * gst_sdp_media_add_attribute:
1259  * @media: a #GstSDPMedia
1260  * @key: a key
1261  * @value: a value
1262  *
1263  * Add the attribute with @key and @value to @media.
1264  *
1265  * Returns: #GST_SDP_OK.
1266  */
1267 GstSDPResult
1268 gst_sdp_media_add_attribute (GstSDPMedia * media, const gchar * key,
1269     const gchar * value)
1270 {
1271   GstSDPAttribute attr;
1272
1273   attr.key = g_strdup (key);
1274   attr.value = g_strdup (value);
1275
1276   g_array_append_val (media->attributes, attr);
1277
1278   return GST_SDP_OK;
1279 }
1280
1281 /**
1282  * gst_sdp_media_get_attribute:
1283  * @media: a #GstSDPMedia
1284  * @idx: an index
1285  *
1286  * Get the attribute at position @idx in @media.
1287  *
1288  * Returns: the #GstSDPAttribute at position @idx.
1289  */
1290 const GstSDPAttribute *
1291 gst_sdp_media_get_attribute (GstSDPMedia * media, guint idx)
1292 {
1293   return &g_array_index (media->attributes, GstSDPAttribute, idx);
1294 }
1295
1296 /**
1297  * gst_sdp_media_get_attribute_val_n:
1298  * @media: a #GstSDPMedia
1299  * @key: a key
1300  * @nth: an index
1301  *
1302  * Get the @nth attribute value for @key in @media.
1303  *
1304  * Returns: the @nth attribute value.
1305  */
1306 const gchar *
1307 gst_sdp_media_get_attribute_val_n (GstSDPMedia * media, const gchar * key,
1308     guint nth)
1309 {
1310   guint i;
1311
1312   for (i = 0; i < media->attributes->len; i++) {
1313     GstSDPAttribute *attr;
1314
1315     attr = &g_array_index (media->attributes, GstSDPAttribute, i);
1316     if (!strcmp (attr->key, key)) {
1317       if (nth == 0)
1318         return attr->value;
1319       else
1320         nth--;
1321     }
1322   }
1323   return NULL;
1324 }
1325
1326 /**
1327  * gst_sdp_media_get_attribute_val:
1328  * @media: a #GstSDPMedia
1329  * @key: a key
1330  *
1331  * Get the first attribute value for @key in @media.
1332  *
1333  * Returns: the first attribute value for @key.
1334  */
1335 const gchar *
1336 gst_sdp_media_get_attribute_val (GstSDPMedia * media, const gchar * key)
1337 {
1338   return gst_sdp_media_get_attribute_val_n (media, key, 0);
1339 }
1340
1341 static void
1342 read_string (gchar * dest, guint size, gchar ** src)
1343 {
1344   guint idx;
1345
1346   idx = 0;
1347   /* skip spaces */
1348   while (g_ascii_isspace (**src))
1349     (*src)++;
1350
1351   while (!g_ascii_isspace (**src) && **src != '\0') {
1352     if (idx < size - 1)
1353       dest[idx++] = **src;
1354     (*src)++;
1355   }
1356   if (size > 0)
1357     dest[idx] = '\0';
1358 }
1359
1360 static void
1361 read_string_del (gchar * dest, guint size, gchar del, gchar ** src)
1362 {
1363   guint idx;
1364
1365   idx = 0;
1366   /* skip spaces */
1367   while (g_ascii_isspace (**src))
1368     (*src)++;
1369
1370   while (**src != del && **src != '\0') {
1371     if (idx < size - 1)
1372       dest[idx++] = **src;
1373     (*src)++;
1374   }
1375   if (size > 0)
1376     dest[idx] = '\0';
1377 }
1378
1379 enum
1380 {
1381   SDP_SESSION,
1382   SDP_MEDIA,
1383 };
1384
1385 typedef struct
1386 {
1387   gint state;
1388   GstSDPMessage *msg;
1389   GstSDPMedia *media;
1390 } SDPContext;
1391
1392 static gboolean
1393 gst_sdp_parse_line (SDPContext * c, gchar type, gchar * buffer)
1394 {
1395   gchar str[8192];
1396   gchar *p = buffer;
1397
1398 #define READ_STRING(field) read_string (str, sizeof(str), &p);REPLACE_STRING (field, str);
1399 #define READ_INT(field) read_string (str, sizeof(str), &p);field = atoi(str);
1400
1401   switch (type) {
1402     case 'v':
1403       if (buffer[0] != '0')
1404         g_warning ("wrong SDP version");
1405       gst_sdp_message_set_version (c->msg, buffer);
1406       break;
1407     case 'o':
1408       READ_STRING (c->msg->origin.username);
1409       READ_STRING (c->msg->origin.sess_id);
1410       READ_STRING (c->msg->origin.sess_version);
1411       READ_STRING (c->msg->origin.nettype);
1412       READ_STRING (c->msg->origin.addrtype);
1413       READ_STRING (c->msg->origin.addr);
1414       break;
1415     case 's':
1416       REPLACE_STRING (c->msg->session_name, buffer);
1417       break;
1418     case 'i':
1419       if (c->state == SDP_SESSION) {
1420         REPLACE_STRING (c->msg->information, buffer);
1421       } else {
1422         REPLACE_STRING (c->media->information, buffer);
1423       }
1424       break;
1425     case 'u':
1426       REPLACE_STRING (c->msg->uri, buffer);
1427       break;
1428     case 'e':
1429       gst_sdp_message_add_email (c->msg, buffer);
1430       break;
1431     case 'p':
1432       gst_sdp_message_add_phone (c->msg, buffer);
1433       break;
1434     case 'c':
1435       READ_STRING (c->msg->connection.nettype);
1436       READ_STRING (c->msg->connection.addrtype);
1437       READ_STRING (c->msg->connection.address);
1438       READ_INT (c->msg->connection.ttl);
1439       READ_INT (c->msg->connection.addr_number);
1440       break;
1441     case 'b':
1442     {
1443       gchar str2[MAX_LINE_LEN];
1444
1445       read_string_del (str, sizeof (str), ':', &p);
1446       read_string (str2, sizeof (str2), &p);
1447       if (c->state == SDP_SESSION)
1448         gst_sdp_message_add_bandwidth (c->msg, str, atoi (str2));
1449       else
1450         gst_sdp_media_add_bandwidth (c->media, str, atoi (str2));
1451       break;
1452     }
1453     case 't':
1454       break;
1455     case 'k':
1456
1457       break;
1458     case 'a':
1459       read_string_del (str, sizeof (str), ':', &p);
1460       if (*p != '\0')
1461         p++;
1462       if (c->state == SDP_SESSION)
1463         gst_sdp_message_add_attribute (c->msg, str, p);
1464       else
1465         gst_sdp_media_add_attribute (c->media, str, p);
1466       break;
1467     case 'm':
1468     {
1469       gchar *slash;
1470       GstSDPMedia nmedia;
1471
1472       c->state = SDP_MEDIA;
1473       memset (&nmedia, 0, sizeof (nmedia));
1474       gst_sdp_media_init (&nmedia);
1475
1476       READ_STRING (nmedia.media);
1477       read_string (str, sizeof (str), &p);
1478       slash = g_strrstr (str, "/");
1479       if (slash) {
1480         *slash = '\0';
1481         nmedia.port = atoi (str);
1482         nmedia.num_ports = atoi (slash + 1);
1483       } else {
1484         nmedia.port = atoi (str);
1485         nmedia.num_ports = -1;
1486       }
1487       READ_STRING (nmedia.proto);
1488       do {
1489         read_string (str, sizeof (str), &p);
1490         gst_sdp_media_add_format (&nmedia, str);
1491       } while (*p != '\0');
1492
1493       gst_sdp_message_add_media (c->msg, &nmedia);
1494       c->media =
1495           &g_array_index (c->msg->medias, GstSDPMedia, c->msg->medias->len - 1);
1496       break;
1497     }
1498     default:
1499       break;
1500   }
1501   return TRUE;
1502 }
1503
1504 /**
1505  * gst_sdp_message_parse_buffer:
1506  * @data: the start of the buffer
1507  * @size: the size of the buffer
1508  * @msg: the result #GstSDPMessage
1509  *
1510  * Parse the contents of @size bytes pointed to by @data and store the result in
1511  * @msg.
1512  *
1513  * Returns: #GST_SDP_OK on success.
1514  */
1515 GstSDPResult
1516 gst_sdp_message_parse_buffer (const guint8 * data, guint size,
1517     GstSDPMessage * msg)
1518 {
1519   gchar *p;
1520   SDPContext c;
1521   gchar type;
1522   gchar buffer[MAX_LINE_LEN];
1523   guint idx = 0;
1524
1525   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
1526   g_return_val_if_fail (data != NULL, GST_SDP_EINVAL);
1527   g_return_val_if_fail (size != 0, GST_SDP_EINVAL);
1528
1529   c.state = SDP_SESSION;
1530   c.msg = msg;
1531   c.media = NULL;
1532
1533   p = (gchar *) data;
1534   while (TRUE) {
1535     while (g_ascii_isspace (*p))
1536       p++;
1537
1538     type = *p++;
1539     if (type == '\0')
1540       break;
1541
1542     if (*p != '=')
1543       goto line_done;
1544     p++;
1545
1546     idx = 0;
1547     while (*p != '\n' && *p != '\r' && *p != '\0') {
1548       if (idx < sizeof (buffer) - 1)
1549         buffer[idx++] = *p;
1550       p++;
1551     }
1552     buffer[idx] = '\0';
1553     gst_sdp_parse_line (&c, type, buffer);
1554
1555   line_done:
1556     while (*p != '\n' && *p != '\0')
1557       p++;
1558     if (*p == '\n')
1559       p++;
1560   }
1561
1562   return GST_SDP_OK;
1563 }
1564
1565 static void
1566 print_media (GstSDPMedia * media)
1567 {
1568   g_print ("   media:       '%s'\n", media->media);
1569   g_print ("   port:        '%d'\n", media->port);
1570   g_print ("   num_ports:   '%d'\n", media->num_ports);
1571   g_print ("   proto:       '%s'\n", media->proto);
1572   if (media->fmts->len > 0) {
1573     guint i;
1574
1575     g_print ("   formats:\n");
1576     for (i = 0; i < media->fmts->len; i++) {
1577       g_print ("    format  '%s'\n", g_array_index (media->fmts, gchar *, i));
1578     }
1579   }
1580   g_print ("   information: '%s'\n", media->information);
1581   g_print ("   key:\n");
1582   g_print ("    type:       '%s'\n", media->key.type);
1583   g_print ("    data:       '%s'\n", media->key.data);
1584   if (media->attributes->len > 0) {
1585     guint i;
1586
1587     g_print ("   attributes:\n");
1588     for (i = 0; i < media->attributes->len; i++) {
1589       GstSDPAttribute *attr =
1590           &g_array_index (media->attributes, GstSDPAttribute, i);
1591
1592       g_print ("    attribute '%s' : '%s'\n", attr->key, attr->value);
1593     }
1594   }
1595 }
1596
1597 /**
1598  * gst_sdp_message_dump:
1599  * @msg: a #GstSDPMessage
1600  *
1601  * Dump the parsed contents of @msg to stdout.
1602  *
1603  * Returns: a #GstSDPResult.
1604  */
1605 GstSDPResult
1606 gst_sdp_message_dump (GstSDPMessage * msg)
1607 {
1608   g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL);
1609
1610   g_print ("sdp packet %p:\n", msg);
1611   g_print (" version:       '%s'\n", msg->version);
1612   g_print (" origin:\n");
1613   g_print ("  username:     '%s'\n", msg->origin.username);
1614   g_print ("  sess_id:      '%s'\n", msg->origin.sess_id);
1615   g_print ("  sess_version: '%s'\n", msg->origin.sess_version);
1616   g_print ("  nettype:      '%s'\n", msg->origin.nettype);
1617   g_print ("  addrtype:     '%s'\n", msg->origin.addrtype);
1618   g_print ("  addr:         '%s'\n", msg->origin.addr);
1619   g_print (" session_name:  '%s'\n", msg->session_name);
1620   g_print (" information:   '%s'\n", msg->information);
1621   g_print (" uri:           '%s'\n", msg->uri);
1622
1623   if (msg->emails->len > 0) {
1624     guint i;
1625
1626     g_print (" emails:\n");
1627     for (i = 0; i < msg->emails->len; i++) {
1628       g_print ("  email '%s'\n", g_array_index (msg->emails, gchar *, i));
1629     }
1630   }
1631   if (msg->phones->len > 0) {
1632     guint i;
1633
1634     g_print (" phones:\n");
1635     for (i = 0; i < msg->phones->len; i++) {
1636       g_print ("  phone '%s'\n", g_array_index (msg->phones, gchar *, i));
1637     }
1638   }
1639   g_print (" connection:\n");
1640   g_print ("  nettype:      '%s'\n", msg->connection.nettype);
1641   g_print ("  addrtype:     '%s'\n", msg->connection.addrtype);
1642   g_print ("  address:      '%s'\n", msg->connection.address);
1643   g_print ("  ttl:          '%d'\n", msg->connection.ttl);
1644   g_print ("  addr_number:  '%d'\n", msg->connection.addr_number);
1645   g_print (" key:\n");
1646   g_print ("  type:         '%s'\n", msg->key.type);
1647   g_print ("  data:         '%s'\n", msg->key.data);
1648   if (msg->attributes->len > 0) {
1649     guint i;
1650
1651     g_print (" attributes:\n");
1652     for (i = 0; i < msg->attributes->len; i++) {
1653       GstSDPAttribute *attr =
1654           &g_array_index (msg->attributes, GstSDPAttribute, i);
1655
1656       g_print ("  attribute '%s' : '%s'\n", attr->key, attr->value);
1657     }
1658   }
1659   if (msg->medias->len > 0) {
1660     guint i;
1661
1662     g_print (" medias:\n");
1663     for (i = 0; i < msg->medias->len; i++) {
1664       g_print ("  media %d:\n", i);
1665       print_media (&g_array_index (msg->medias, GstSDPMedia, i));
1666     }
1667   }
1668   return GST_SDP_OK;
1669 }