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