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