• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2012> Wim Taymans <wim.taymans@gmail.com>
3  * Copyright (C) <2020> Matthew Waters <matthew@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 
21 /**
22  * SECTION:gstrtphdrext
23  * @title: GstRtphdrext
24  * @short_description: Helper methods for dealing with RTP header extensions
25  * @see_also: #GstRTPBasePayload, #GstRTPBaseDepayload, gstrtpbuffer
26  *
27  */
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include "gstrtphdrext.h"
33 
34 #include <stdlib.h>
35 #include <string.h>
36 
37 static gboolean
38 gst_rtp_header_extension_set_caps_from_attributes_default (GstRTPHeaderExtension
39     * ext, GstCaps * caps);
40 
41 GST_DEBUG_CATEGORY_STATIC (rtphderext_debug);
42 #define GST_CAT_DEFAULT (rtphderext_debug)
43 
44 #define MAX_RTP_EXT_ID 256
45 
46 #define GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT      \
47   (GST_RTP_HEADER_EXTENSION_DIRECTION_SENDRECV |        \
48       GST_RTP_HEADER_EXTENSION_DIRECTION_INHERITED)
49 
50 typedef struct
51 {
52   guint ext_id;
53   gboolean wants_update_non_rtp_src_caps;
54   GstRTPHeaderExtensionDirection direction;
55 } GstRTPHeaderExtensionPrivate;
56 
57 /**
58  * gst_rtp_hdrext_set_ntp_64:
59  * @data: the data to write to
60  * @size: the size of @data
61  * @ntptime: the NTP time
62  *
63  * Writes the NTP time in @ntptime to the format required for the NTP-64 header
64  * extension. @data must hold at least #GST_RTP_HDREXT_NTP_64_SIZE bytes.
65  *
66  * Returns: %TRUE on success.
67  */
68 gboolean
gst_rtp_hdrext_set_ntp_64(gpointer data,guint size,guint64 ntptime)69 gst_rtp_hdrext_set_ntp_64 (gpointer data, guint size, guint64 ntptime)
70 {
71   g_return_val_if_fail (data != NULL, FALSE);
72   g_return_val_if_fail (size >= GST_RTP_HDREXT_NTP_64_SIZE, FALSE);
73 
74   GST_WRITE_UINT64_BE (data, ntptime);
75 
76   return TRUE;
77 }
78 
79 /**
80  * gst_rtp_hdrext_get_ntp_64:
81  * @data: (array length=size) (element-type guint8): the data to read from
82  * @size: the size of @data
83  * @ntptime: (out): the result NTP time
84  *
85  * Reads the NTP time from the @size NTP-64 extension bytes in @data and store the
86  * result in @ntptime.
87  *
88  * Returns: %TRUE on success.
89  */
90 gboolean
gst_rtp_hdrext_get_ntp_64(gpointer data,guint size,guint64 * ntptime)91 gst_rtp_hdrext_get_ntp_64 (gpointer data, guint size, guint64 * ntptime)
92 {
93   g_return_val_if_fail (data != NULL, FALSE);
94   g_return_val_if_fail (size >= GST_RTP_HDREXT_NTP_64_SIZE, FALSE);
95 
96   if (ntptime)
97     *ntptime = GST_READ_UINT64_BE (data);
98 
99   return TRUE;
100 }
101 
102 /**
103  * gst_rtp_hdrext_set_ntp_56:
104  * @data: the data to write to
105  * @size: the size of @data
106  * @ntptime: the NTP time
107  *
108  * Writes the NTP time in @ntptime to the format required for the NTP-56 header
109  * extension. @data must hold at least #GST_RTP_HDREXT_NTP_56_SIZE bytes.
110  *
111  * Returns: %TRUE on success.
112  */
113 gboolean
gst_rtp_hdrext_set_ntp_56(gpointer data,guint size,guint64 ntptime)114 gst_rtp_hdrext_set_ntp_56 (gpointer data, guint size, guint64 ntptime)
115 {
116   guint8 *d = data;
117   gint i;
118 
119   g_return_val_if_fail (data != NULL, FALSE);
120   g_return_val_if_fail (size >= GST_RTP_HDREXT_NTP_56_SIZE, FALSE);
121 
122   for (i = 0; i < 7; i++) {
123     d[6 - i] = ntptime & 0xff;
124     ntptime >>= 8;
125   }
126   return TRUE;
127 }
128 
129 /**
130  * gst_rtp_hdrext_get_ntp_56:
131  * @data: (array length=size) (element-type guint8): the data to read from
132  * @size: the size of @data
133  * @ntptime: (out): the result NTP time
134  *
135  * Reads the NTP time from the @size NTP-56 extension bytes in @data and store the
136  * result in @ntptime.
137  *
138  * Returns: %TRUE on success.
139  */
140 gboolean
gst_rtp_hdrext_get_ntp_56(gpointer data,guint size,guint64 * ntptime)141 gst_rtp_hdrext_get_ntp_56 (gpointer data, guint size, guint64 * ntptime)
142 {
143   guint8 *d = data;
144 
145   g_return_val_if_fail (data != NULL, FALSE);
146   g_return_val_if_fail (size >= GST_RTP_HDREXT_NTP_56_SIZE, FALSE);
147 
148   if (ntptime) {
149     gint i;
150 
151     *ntptime = 0;
152     for (i = 0; i < 7; i++) {
153       *ntptime <<= 8;
154       *ntptime |= d[i];
155     }
156   }
157   return TRUE;
158 }
159 
160 #define gst_rtp_header_extension_parent_class parent_class
161 G_DEFINE_TYPE_EXTENDED (GstRTPHeaderExtension, gst_rtp_header_extension,
162     GST_TYPE_ELEMENT, G_TYPE_FLAG_ABSTRACT,
163     G_ADD_PRIVATE (GstRTPHeaderExtension)
164     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtphdrext", 0,
165         "RTP Header Extensions")
166     );
167 
168 /**
169  * gst_rtp_header_extension_class_set_uri:
170  * @klass: the #GstRTPHeaderExtensionClass
171  * @uri: the RTP Header extension uri for @klass
172  *
173  * Set the URI for this RTP header extension implementation.
174  *
175  * Since: 1.20
176  */
177 void
gst_rtp_header_extension_class_set_uri(GstRTPHeaderExtensionClass * klass,const gchar * uri)178 gst_rtp_header_extension_class_set_uri (GstRTPHeaderExtensionClass * klass,
179     const gchar * uri)
180 {
181   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
182 
183   gst_element_class_add_metadata (element_class,
184       GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY, uri);
185 }
186 
187 static void
gst_rtp_header_extension_class_init(GstRTPHeaderExtensionClass * klass)188 gst_rtp_header_extension_class_init (GstRTPHeaderExtensionClass * klass)
189 {
190   klass->set_caps_from_attributes =
191       gst_rtp_header_extension_set_caps_from_attributes_default;
192 }
193 
194 static void
gst_rtp_header_extension_init(GstRTPHeaderExtension * ext)195 gst_rtp_header_extension_init (GstRTPHeaderExtension * ext)
196 {
197   GstRTPHeaderExtensionPrivate *priv =
198       gst_rtp_header_extension_get_instance_private (ext);
199 
200   priv->ext_id = G_MAXUINT32;
201   priv->direction = GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT;
202 }
203 
204 /**
205  * gst_rtp_header_extension_get_uri:
206  * @ext: a #GstRTPHeaderExtension
207  *
208  * Returns: the RTP extension URI for this object
209  *
210  * Since: 1.20
211  */
212 const gchar *
gst_rtp_header_extension_get_uri(GstRTPHeaderExtension * ext)213 gst_rtp_header_extension_get_uri (GstRTPHeaderExtension * ext)
214 {
215   GstRTPHeaderExtensionClass *klass;
216   GstElementClass *element_class;
217 
218   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), NULL);
219   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
220   element_class = GST_ELEMENT_CLASS (klass);
221 
222   return gst_element_class_get_metadata (element_class,
223       GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY);
224 }
225 
226 /**
227  * gst_rtp_header_extension_get_supported_flags:
228  * @ext: a #GstRTPHeaderExtension
229  *
230  * Returns: the flags supported by this instance of @ext
231  *
232  * Since: 1.20
233  */
234 GstRTPHeaderExtensionFlags
gst_rtp_header_extension_get_supported_flags(GstRTPHeaderExtension * ext)235 gst_rtp_header_extension_get_supported_flags (GstRTPHeaderExtension * ext)
236 {
237   GstRTPHeaderExtensionClass *klass;
238 
239   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), 0);
240   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
241   g_return_val_if_fail (klass->get_supported_flags != NULL, 0);
242 
243   return klass->get_supported_flags (ext);
244 }
245 
246 /**
247  * gst_rtp_header_extension_get_max_size:
248  * @ext: a #GstRTPHeaderExtension
249  * @input_meta: a #GstBuffer
250  *
251  * This is used to know how much data a certain header extension will need for
252  * both allocating the resulting data, and deciding how much payload data can
253  * be generated.
254  *
255  * Implementations should return as accurate a value as is possible using the
256  * information given in the input @buffer.
257  *
258  * Returns: the maximum size of the data written by this extension
259  *
260  * Since: 1.20
261  */
262 gsize
gst_rtp_header_extension_get_max_size(GstRTPHeaderExtension * ext,const GstBuffer * input_meta)263 gst_rtp_header_extension_get_max_size (GstRTPHeaderExtension * ext,
264     const GstBuffer * input_meta)
265 {
266   GstRTPHeaderExtensionClass *klass;
267 
268   g_return_val_if_fail (GST_IS_BUFFER (input_meta), 0);
269   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), 0);
270   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
271   g_return_val_if_fail (klass->get_max_size != NULL, 0);
272 
273   return klass->get_max_size (ext, input_meta);
274 }
275 
276 /**
277  * gst_rtp_header_extension_write:
278  * @ext: a #GstRTPHeaderExtension
279  * @input_meta: the input #GstBuffer to read information from if necessary
280  * @write_flags: #GstRTPHeaderExtensionFlags for how the extension should
281  *               be written
282  * @output: output RTP #GstBuffer
283  * @data: (array length=size): location to write the rtp header extension into
284  * @size: size of @data
285  *
286  * Writes the RTP header extension to @data using information available from
287  * the @input_meta.  @data will be sized to be at least the value returned
288  * from gst_rtp_header_extension_get_max_size().
289  *
290  * Returns: the size of the data written, < 0 on failure
291  *
292  * Since: 1.20
293  */
294 gssize
gst_rtp_header_extension_write(GstRTPHeaderExtension * ext,const GstBuffer * input_meta,GstRTPHeaderExtensionFlags write_flags,GstBuffer * output,guint8 * data,gsize size)295 gst_rtp_header_extension_write (GstRTPHeaderExtension * ext,
296     const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags,
297     GstBuffer * output, guint8 * data, gsize size)
298 {
299   GstRTPHeaderExtensionPrivate *priv =
300       gst_rtp_header_extension_get_instance_private (ext);
301   GstRTPHeaderExtensionClass *klass;
302 
303   g_return_val_if_fail (GST_IS_BUFFER (input_meta), -1);
304   g_return_val_if_fail (GST_IS_BUFFER (output), -1);
305   g_return_val_if_fail (gst_buffer_is_writable (output), -1);
306   g_return_val_if_fail (data != NULL, -1);
307   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), -1);
308   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, -1);
309   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
310   g_return_val_if_fail (klass->write != NULL, -1);
311 
312   return klass->write (ext, input_meta, write_flags, output, data, size);
313 }
314 
315 /**
316  * gst_rtp_header_extension_read:
317  * @ext: a #GstRTPHeaderExtension
318  * @read_flags: #GstRTPHeaderExtensionFlags for how the extension should
319  *               be written
320  * @data: (array length=size): location to read the rtp header extension from
321  * @size: size of @data
322  * @buffer: a #GstBuffer to modify if necessary
323  *
324  * Read the RTP header extension from @data.
325  *
326  * Returns: whether the extension could be read from @data
327  *
328  * Since: 1.20
329  */
330 gboolean
gst_rtp_header_extension_read(GstRTPHeaderExtension * ext,GstRTPHeaderExtensionFlags read_flags,const guint8 * data,gsize size,GstBuffer * buffer)331 gst_rtp_header_extension_read (GstRTPHeaderExtension * ext,
332     GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size,
333     GstBuffer * buffer)
334 {
335   GstRTPHeaderExtensionPrivate *priv =
336       gst_rtp_header_extension_get_instance_private (ext);
337   GstRTPHeaderExtensionClass *klass;
338 
339   g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
340   g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE);
341   g_return_val_if_fail (data != NULL, FALSE);
342   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
343   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, FALSE);
344   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
345   g_return_val_if_fail (klass->read != NULL, FALSE);
346 
347   return klass->read (ext, read_flags, data, size, buffer);
348 }
349 
350 /**
351  * gst_rtp_header_extension_get_id:
352  * @ext: a #GstRTPHeaderExtension
353  *
354  * Returns: the RTP extension id configured on @ext
355  *
356  * Since: 1.20
357  */
358 guint
gst_rtp_header_extension_get_id(GstRTPHeaderExtension * ext)359 gst_rtp_header_extension_get_id (GstRTPHeaderExtension * ext)
360 {
361   GstRTPHeaderExtensionPrivate *priv =
362       gst_rtp_header_extension_get_instance_private (ext);
363 
364   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), 0);
365 
366   return priv->ext_id;
367 }
368 
369 /**
370  * gst_rtp_header_extension_set_id:
371  * @ext: a #GstRTPHeaderExtension
372  * @ext_id: The id of this extension
373  *
374  * sets the RTP extension id on @ext
375  *
376  * Since: 1.20
377  */
378 void
gst_rtp_header_extension_set_id(GstRTPHeaderExtension * ext,guint ext_id)379 gst_rtp_header_extension_set_id (GstRTPHeaderExtension * ext, guint ext_id)
380 {
381   GstRTPHeaderExtensionPrivate *priv =
382       gst_rtp_header_extension_get_instance_private (ext);
383 
384   g_return_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext));
385   g_return_if_fail (ext_id < MAX_RTP_EXT_ID);
386 
387   priv->ext_id = ext_id;
388 }
389 
390 static int
strcasecmp0(const gchar * str1,const gchar * str2)391 strcasecmp0 (const gchar * str1, const gchar * str2)
392 {
393   if (!str1)
394     return -(str1 != str2);
395   if (!str2)
396     return str1 != str2;
397 
398   return g_ascii_strcasecmp (str1, str2);
399 }
400 
401 /**
402  * gst_rtp_header_extension_set_attributes_from_caps:
403  * @ext: a #GstRTPHeaderExtension
404  * @caps: the #GstCaps to configure this extension with
405  *
406  * gst_rtp_header_extension_set_id() must have been called with a valid
407  * extension id that is contained in these caps.
408  *
409  * The only current known caps format is based on the SDP standard as produced
410  * by gst_sdp_media_attributes_to_caps().
411  *
412  * Returns: whether the @caps could be successfully set on @ext.
413  *
414  * Since: 1.20
415  */
416 gboolean
gst_rtp_header_extension_set_attributes_from_caps(GstRTPHeaderExtension * ext,const GstCaps * caps)417 gst_rtp_header_extension_set_attributes_from_caps (GstRTPHeaderExtension * ext,
418     const GstCaps * caps)
419 {
420   GstRTPHeaderExtensionPrivate *priv =
421       gst_rtp_header_extension_get_instance_private (ext);
422   GstRTPHeaderExtensionClass *klass;
423   GstStructure *structure;
424   gchar *field_name;
425   const GValue *val;
426   GstRTPHeaderExtensionDirection direction =
427       GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT;
428   const gchar *attributes = "";
429   gboolean ret = FALSE;
430 
431   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
432   g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
433   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
434   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, FALSE);
435   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
436 
437   structure = gst_caps_get_structure (caps, 0);
438   g_return_val_if_fail (structure != NULL, FALSE);
439   field_name = g_strdup_printf ("extmap-%u", priv->ext_id);
440   g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE);
441 
442   val = gst_structure_get_value (structure, field_name);
443 
444   if (G_VALUE_HOLDS_STRING (val)) {
445     const gchar *ext_uri = g_value_get_string (val);
446 
447     if (strcasecmp0 (ext_uri, gst_rtp_header_extension_get_uri (ext)) != 0) {
448       /* incompatible extension uri for this instance */
449       GST_WARNING_OBJECT (ext, "Field %s, URI doesn't match RTP Header"
450           " extension:  \"%s\", expected \"%s\"", field_name, ext_uri,
451           gst_rtp_header_extension_get_uri (ext));
452       goto done;
453     }
454   } else if (GST_VALUE_HOLDS_ARRAY (val)
455       && gst_value_array_get_size (val) == 3) {
456     const GValue *inner_val;
457 
458     inner_val = gst_value_array_get_value (val, 0);
459     if (G_VALUE_HOLDS_STRING (inner_val)) {
460       const gchar *dir = g_value_get_string (inner_val);
461 
462       if (!strcasecmp0 (dir, ""))
463         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT;
464       else if (!strcasecmp0 (dir, "sendrecv"))
465         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_SENDRECV;
466       else if (!strcasecmp0 (dir, "sendonly"))
467         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_SENDONLY;
468       else if (!strcasecmp0 (dir, "recvonly"))
469         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_RECVONLY;
470       else if (!strcasecmp0 (dir, "inactive"))
471         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_INACTIVE;
472       else {
473         GST_WARNING_OBJECT (ext, "Unexpected direction \"%s\", expected one"
474             " of: sendrecv, sendonly, recvonly or inactive", dir);
475         goto done;
476       }
477     } else {
478       GST_WARNING_OBJECT (ext, "Caps should hold an array of 3 strings, "
479           "but first member is %s instead", G_VALUE_TYPE_NAME (inner_val));
480       goto done;
481     }
482 
483     inner_val = gst_value_array_get_value (val, 1);
484     if (!G_VALUE_HOLDS_STRING (inner_val)) {
485       GST_WARNING_OBJECT (ext, "Caps should hold an array of 3 strings, "
486           "but second member is %s instead", G_VALUE_TYPE_NAME (inner_val));
487 
488       goto done;
489     }
490     if (strcasecmp0 (g_value_get_string (inner_val),
491             gst_rtp_header_extension_get_uri (ext)) != 0) {
492       GST_WARNING_OBJECT (ext, "URI doesn't match RTP Header extension:"
493           " \"%s\", expected \"%s\"", g_value_get_string (inner_val),
494           gst_rtp_header_extension_get_uri (ext));
495       goto done;
496     }
497 
498     inner_val = gst_value_array_get_value (val, 2);
499     if (!G_VALUE_HOLDS_STRING (inner_val)) {
500       GST_WARNING_OBJECT (ext, "Caps should hold an array of 3 strings, "
501           "but third member is %s instead", G_VALUE_TYPE_NAME (inner_val));
502       goto done;
503     }
504 
505     attributes = g_value_get_string (inner_val);
506   } else {
507     GST_WARNING_OBJECT (ext, "Caps field %s should be either a string"
508         " containing the URI or an array of 3 strings containing the"
509         " direction, URI and attributes, but contains %s", field_name,
510         G_VALUE_TYPE_NAME (val));
511     goto done;
512   }
513 
514   /* If the caps don't include directions, use the ones that were
515    * previously set by the application.
516    */
517   if (direction == GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT &&
518       priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_INHERITED)
519     direction = priv->direction;
520 
521   if (klass->set_attributes)
522     ret = klass->set_attributes (ext, direction, attributes);
523   else
524     ret = TRUE;
525 
526   if (ret)
527     priv->direction = direction;
528 
529 done:
530 
531   g_free (field_name);
532   return ret;
533 }
534 
535 /**
536  * gst_rtp_header_extension_wants_update_non_rtp_src_caps:
537  * @ext: a #GstRTPHeaderExtension
538  *
539  * Call this function after gst_rtp_header_extension_read() to check if
540  * the depayloader's src caps need updating with data received in the last RTP
541  * packet.
542  *
543  * Returns: Whether @ext wants to update depayloader's src caps.
544  *
545  * Since: 1.20
546  */
547 gboolean
gst_rtp_header_extension_wants_update_non_rtp_src_caps(GstRTPHeaderExtension * ext)548 gst_rtp_header_extension_wants_update_non_rtp_src_caps (GstRTPHeaderExtension *
549     ext)
550 {
551   GstRTPHeaderExtensionPrivate *priv =
552       gst_rtp_header_extension_get_instance_private (ext);
553 
554   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
555 
556   return priv->wants_update_non_rtp_src_caps;
557 }
558 
559 /**
560  * gst_rtp_header_extension_set_wants_update_non_rtp_src_caps:
561  * @ext: a #GstRTPHeaderExtension
562  * @state: TRUE if caps update is needed
563  *
564  * Call this function in a subclass from #GstRTPHeaderExtensionClass::read to
565  * tell the depayloader whether the data just parsed from RTP packet require
566  * updating its src (non-RTP) caps. If @state is TRUE, #GstRTPBaseDepayload will
567  * eventually invoke gst_rtp_header_extension_update_non_rtp_src_caps() to
568  * have the caps update applied. Applying the update also flips the internal
569  * "wants update" flag back to FALSE.
570  *
571  * Since: 1.20
572  */
gst_rtp_header_extension_set_wants_update_non_rtp_src_caps(GstRTPHeaderExtension * ext,gboolean state)573 void gst_rtp_header_extension_set_wants_update_non_rtp_src_caps
574     (GstRTPHeaderExtension * ext, gboolean state)
575 {
576   GstRTPHeaderExtensionPrivate *priv =
577       gst_rtp_header_extension_get_instance_private (ext);
578 
579   g_return_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext));
580 
581   priv->wants_update_non_rtp_src_caps = state;
582 }
583 
584 /**
585  * gst_rtp_header_extension_set_non_rtp_sink_caps:
586  * @ext: a #GstRTPHeaderExtension
587  * @caps: sink #GstCaps
588  *
589  * Passes RTP payloader's sink (i.e. not payloaded) @caps to the header
590  * extension.
591  *
592  * Returns: Whether @caps could be read successfully
593  *
594  * Since: 1.20
595  */
596 gboolean
gst_rtp_header_extension_set_non_rtp_sink_caps(GstRTPHeaderExtension * ext,const GstCaps * caps)597 gst_rtp_header_extension_set_non_rtp_sink_caps (GstRTPHeaderExtension * ext,
598     const GstCaps * caps)
599 {
600   GstRTPHeaderExtensionPrivate *priv =
601       gst_rtp_header_extension_get_instance_private (ext);
602   GstRTPHeaderExtensionClass *klass;
603 
604   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
605   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
606   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, FALSE);
607   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
608 
609   if (klass->set_non_rtp_sink_caps) {
610     return klass->set_non_rtp_sink_caps (ext, caps);
611   }
612 
613   return TRUE;
614 }
615 
616 /**
617  * gst_rtp_header_extension_update_non_rtp_src_caps:
618  * @ext: a #GstRTPHeaderExtension
619  * @caps: src #GstCaps to modify
620  *
621  * Updates depayloader src caps based on the information received in RTP header.
622  * @caps must be writable as this function may modify them.
623  *
624  * Returns: whether @caps were modified successfully
625  *
626  * Since: 1.20
627  */
628 gboolean
gst_rtp_header_extension_update_non_rtp_src_caps(GstRTPHeaderExtension * ext,GstCaps * caps)629 gst_rtp_header_extension_update_non_rtp_src_caps (GstRTPHeaderExtension * ext,
630     GstCaps * caps)
631 {
632   GstRTPHeaderExtensionPrivate *priv =
633       gst_rtp_header_extension_get_instance_private (ext);
634   GstRTPHeaderExtensionClass *klass;
635 
636   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
637   g_return_val_if_fail (gst_caps_is_writable (caps), FALSE);
638   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
639   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, FALSE);
640   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
641 
642   priv->wants_update_non_rtp_src_caps = FALSE;
643 
644   if (klass->update_non_rtp_src_caps) {
645     return klass->update_non_rtp_src_caps (ext, caps);
646   }
647 
648   return TRUE;
649 }
650 
651 /**
652  * gst_rtp_header_extension_set_caps_from_attributes:
653  * @ext: a #GstRTPHeaderExtension
654  * @caps: writable #GstCaps to modify
655  *
656  * gst_rtp_header_extension_set_id() must have been called with a valid
657  * extension id that is contained in these caps.
658  *
659  * The only current known caps format is based on the SDP standard as produced
660  * by gst_sdp_media_attributes_to_caps().
661  *
662  * Returns: whether the configured attributes on @ext can successfully be set on
663  * 	@caps
664  *
665  * Since: 1.20
666  */
667 gboolean
gst_rtp_header_extension_set_caps_from_attributes(GstRTPHeaderExtension * ext,GstCaps * caps)668 gst_rtp_header_extension_set_caps_from_attributes (GstRTPHeaderExtension * ext,
669     GstCaps * caps)
670 {
671   GstRTPHeaderExtensionPrivate *priv =
672       gst_rtp_header_extension_get_instance_private (ext);
673   GstRTPHeaderExtensionClass *klass;
674 
675   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
676   g_return_val_if_fail (gst_caps_is_writable (caps), FALSE);
677   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
678   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, FALSE);
679   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
680   g_return_val_if_fail (klass->set_caps_from_attributes != NULL, FALSE);
681 
682   return klass->set_caps_from_attributes (ext, caps);
683 }
684 
685 /**
686  * gst_rtp_header_extension_get_sdp_caps_field_name:
687  * @ext: the #GstRTPHeaderExtension
688  *
689  * Returns: (transfer full): the #GstStructure field name used in SDP-like #GstCaps for this @ext configuration
690  *
691  * Since: 1.20
692  */
693 gchar *
gst_rtp_header_extension_get_sdp_caps_field_name(GstRTPHeaderExtension * ext)694 gst_rtp_header_extension_get_sdp_caps_field_name (GstRTPHeaderExtension * ext)
695 {
696   GstRTPHeaderExtensionPrivate *priv =
697       gst_rtp_header_extension_get_instance_private (ext);
698 
699   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), NULL);
700   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, NULL);
701 
702   return g_strdup_printf ("extmap-%u", priv->ext_id);
703 }
704 
705 /**
706  * gst_rtp_header_extension_set_caps_from_attributes_helper:
707  * @ext: the #GstRTPHeaderExtension
708  * @caps: #GstCaps to write fields into
709  *
710  * Helper implementation for GstRTPExtensionClass::set_caps_from_attributes
711  * that sets the @ext uri on caps with the specified extension id as required
712  * for sdp #GstCaps.
713  *
714  * Requires that the extension does not have any attributes or direction
715  * advertised in @caps.
716  *
717  * Returns: whether the @ext attributes could be set on @caps.
718  *
719  * Since: 1.20
720  */
721 gboolean
gst_rtp_header_extension_set_caps_from_attributes_helper(GstRTPHeaderExtension * ext,GstCaps * caps,const gchar * attributes)722 gst_rtp_header_extension_set_caps_from_attributes_helper (GstRTPHeaderExtension
723     * ext, GstCaps * caps, const gchar * attributes)
724 {
725   GstRTPHeaderExtensionPrivate *priv =
726       gst_rtp_header_extension_get_instance_private (ext);
727   gchar *field_name = gst_rtp_header_extension_get_sdp_caps_field_name (ext);
728   GstStructure *s = gst_caps_get_structure (caps, 0);
729 
730   if (priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_INHERITED &&
731       (attributes == NULL || attributes[0] == 0)) {
732     gst_structure_set (s, field_name, G_TYPE_STRING,
733         gst_rtp_header_extension_get_uri (ext), NULL);
734   } else {
735     GValue arr = G_VALUE_INIT;
736     GValue val = G_VALUE_INIT;
737 
738     g_value_init (&arr, GST_TYPE_ARRAY);
739     g_value_init (&val, G_TYPE_STRING);
740 
741     if (priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_INHERITED) {
742       g_value_set_string (&val, "");
743     } else {
744       if ((priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_SENDRECV) ==
745           GST_RTP_HEADER_EXTENSION_DIRECTION_SENDRECV)
746         g_value_set_string (&val, "sendrecv");
747       else if (priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_SENDONLY)
748         g_value_set_string (&val, "sendonly");
749       else if (priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_RECVONLY)
750         g_value_set_string (&val, "recvonly");
751       else
752         g_value_set_string (&val, "inactive");
753     }
754     gst_value_array_append_value (&arr, &val);
755 
756     /* uri */
757     g_value_set_string (&val, gst_rtp_header_extension_get_uri (ext));
758     gst_value_array_append_value (&arr, &val);
759 
760     /* attributes */
761     g_value_set_string (&val, attributes);
762     gst_value_array_append_value (&arr, &val);
763 
764     gst_structure_set_value (s, field_name, &arr);
765 
766     GST_DEBUG_OBJECT (ext, "%" GST_PTR_FORMAT, caps);
767 
768     g_value_unset (&val);
769     g_value_unset (&arr);
770   }
771 
772   g_free (field_name);
773   return TRUE;
774 }
775 
776 static gboolean
gst_rtp_header_extension_set_caps_from_attributes_default(GstRTPHeaderExtension * ext,GstCaps * caps)777 gst_rtp_header_extension_set_caps_from_attributes_default (GstRTPHeaderExtension
778     * ext, GstCaps * caps)
779 {
780   return gst_rtp_header_extension_set_caps_from_attributes_helper (ext, caps,
781       NULL);
782 }
783 
784 static gboolean
gst_rtp_ext_list_filter(GstPluginFeature * feature,gpointer user_data)785 gst_rtp_ext_list_filter (GstPluginFeature * feature, gpointer user_data)
786 {
787   GstElementFactory *factory;
788   gchar *uri = user_data;
789   const gchar *klass, *factory_uri;
790   guint rank;
791 
792   /* we only care about element factories */
793   if (!GST_IS_ELEMENT_FACTORY (feature))
794     return FALSE;
795 
796   factory = GST_ELEMENT_FACTORY (feature);
797 
798   /* only select elements with autoplugging rank */
799   rank = gst_plugin_feature_get_rank (feature);
800   if (rank < GST_RANK_MARGINAL)
801     return FALSE;
802 
803   klass =
804       gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
805   if (!strstr (klass, "Network") || !strstr (klass, "Extension") ||
806       !strstr (klass, "RTPHeader"))
807     return FALSE;
808 
809   factory_uri =
810       gst_element_factory_get_metadata (factory,
811       GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY);
812   if (!factory_uri)
813     return FALSE;
814 
815   if (uri && g_strcmp0 (uri, factory_uri) != 0)
816     return FALSE;
817 
818   return TRUE;
819 }
820 
821 /**
822  * gst_rtp_get_header_extension_list:
823  *
824  * Retrieve all the factories of the currently registered RTP header
825  * extensions.  Call gst_element_factory_create() with each factory to create
826  * the associated #GstRTPHeaderExtension.
827  *
828  * Returns: (transfer full) (element-type GstElementFactory): a #GList of
829  *     #GstElementFactory's. Use gst_plugin_feature_list_free() after use
830  *
831  * Since: 1.20
832  */
833 GList *
gst_rtp_get_header_extension_list(void)834 gst_rtp_get_header_extension_list (void)
835 {
836   return gst_registry_feature_filter (gst_registry_get (),
837       (GstPluginFeatureFilter) gst_rtp_ext_list_filter, FALSE, NULL);
838 }
839 
840 /**
841  * gst_rtp_header_extension_create_from_uri:
842  * @uri: the rtp header extension URI to search for
843  *
844  * Returns: (transfer full) (nullable): the #GstRTPHeaderExtension for @uri or %NULL
845  *
846  * Since: 1.20
847  */
848 GstRTPHeaderExtension *
gst_rtp_header_extension_create_from_uri(const gchar * uri)849 gst_rtp_header_extension_create_from_uri (const gchar * uri)
850 {
851   GList *l;
852 
853   l = gst_registry_feature_filter (gst_registry_get (),
854       (GstPluginFeatureFilter) gst_rtp_ext_list_filter, TRUE, (gpointer) uri);
855   if (l) {
856     GstElementFactory *factory = GST_ELEMENT_FACTORY (l->data);
857     GstElement *element = gst_element_factory_create (factory, NULL);
858 
859     g_list_free_full (l, (GDestroyNotify) gst_object_unref);
860 
861     gst_object_ref_sink (element);
862 
863     return GST_RTP_HEADER_EXTENSION (element);
864   }
865 
866   return NULL;
867 }
868 
869 /**
870  * gst_rtp_header_extension_set_direction:
871  * @ext: the #GstRTPHeaderExtension
872  * @direction: The direction
873  *
874  * Set the direction that this header extension should be used in.
875  * If #GST_RTP_HEADER_EXTENSION_DIRECTION_INHERITED is included, the
876  * direction will not be included in the caps (as it shouldn't be in the
877  * extmap line in the SDP).
878  *
879  * Since: 1.20
880  */
881 
882 void
gst_rtp_header_extension_set_direction(GstRTPHeaderExtension * ext,GstRTPHeaderExtensionDirection direction)883 gst_rtp_header_extension_set_direction (GstRTPHeaderExtension * ext,
884     GstRTPHeaderExtensionDirection direction)
885 {
886   GstRTPHeaderExtensionPrivate *priv =
887       gst_rtp_header_extension_get_instance_private (ext);
888 
889   g_return_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext));
890   g_return_if_fail (direction <= GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT);
891 
892   priv->direction = direction;
893 }
894 
895 /**
896  * gst_rtp_header_extension_get_direction:
897  * @ext: the #GstRTPHeaderExtension
898  *
899  * Retrieve the direction
900  *
901  * Returns: The direction
902  *
903  * Since: 1.20
904  */
905 
906 GstRTPHeaderExtensionDirection
gst_rtp_header_extension_get_direction(GstRTPHeaderExtension * ext)907 gst_rtp_header_extension_get_direction (GstRTPHeaderExtension * ext)
908 {
909   GstRTPHeaderExtensionPrivate *priv =
910       gst_rtp_header_extension_get_instance_private (ext);
911 
912   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext),
913       GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT);
914 
915   return priv->direction;
916 }
917