rtphdrext: Print warnings when trying to parse caps
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / gst-libs / gst / rtp / gstrtphdrext.c
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
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
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
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
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
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_static_metadata (element_class,
184       GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY, uri);
185 }
186
187 static void
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
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 *
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
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
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
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
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
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
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 /**
391  * gst_rtp_header_extension_set_attributes_from_caps:
392  * @ext: a #GstRTPHeaderExtension
393  * @caps: the #GstCaps to configure this extension with
394  *
395  * gst_rtp_header_extension_set_id() must have been called with a valid
396  * extension id that is contained in these caps.
397  *
398  * The only current known caps format is based on the SDP standard as produced
399  * by gst_sdp_media_attributes_to_caps().
400  *
401  * Returns: whether the @caps could be successfully set on @ext.
402  *
403  * Since: 1.20
404  */
405 gboolean
406 gst_rtp_header_extension_set_attributes_from_caps (GstRTPHeaderExtension * ext,
407     const GstCaps * caps)
408 {
409   GstRTPHeaderExtensionPrivate *priv =
410       gst_rtp_header_extension_get_instance_private (ext);
411   GstRTPHeaderExtensionClass *klass;
412   GstStructure *structure;
413   gchar *field_name;
414   const GValue *val;
415   GstRTPHeaderExtensionDirection direction =
416       GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT;
417   const gchar *attributes = "";
418   gboolean ret = FALSE;
419
420   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
421   g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
422   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
423   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, FALSE);
424   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
425
426   structure = gst_caps_get_structure (caps, 0);
427   g_return_val_if_fail (structure != NULL, FALSE);
428   field_name = g_strdup_printf ("extmap-%u", priv->ext_id);
429   g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE);
430
431   val = gst_structure_get_value (structure, field_name);
432
433   if (G_VALUE_HOLDS_STRING (val)) {
434     const gchar *ext_uri = g_value_get_string (val);
435
436     if (g_strcmp0 (ext_uri, gst_rtp_header_extension_get_uri (ext)) != 0) {
437       /* incompatible extension uri for this instance */
438       GST_WARNING_OBJECT (ext, "Field %s, URI doesn't match RTP Header"
439           " extension:  \"%s\", expected \"%s\"", field_name, ext_uri,
440           gst_rtp_header_extension_get_uri (ext));
441       goto done;
442     }
443   } else if (GST_VALUE_HOLDS_ARRAY (val)
444       && gst_value_array_get_size (val) == 3) {
445     const GValue *inner_val;
446
447     inner_val = gst_value_array_get_value (val, 0);
448     if (G_VALUE_HOLDS_STRING (inner_val)) {
449       const gchar *dir = g_value_get_string (inner_val);
450
451       if (!strcmp (dir, ""))
452         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT;
453       else if (!strcmp (dir, "sendrecv"))
454         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_SENDRECV;
455       else if (!strcmp (dir, "sendonly"))
456         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_SENDONLY;
457       else if (!strcmp (dir, "recvonly"))
458         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_RECVONLY;
459       else if (!strcmp (dir, "inactive"))
460         direction = GST_RTP_HEADER_EXTENSION_DIRECTION_INACTIVE;
461       else {
462         GST_WARNING_OBJECT (ext, "Unexpected direction \"%s\", expected one"
463             " of: sendrecv, sendonly, recvonly or inactive", dir);
464         goto done;
465       }
466     } else {
467       GST_WARNING_OBJECT (ext, "Caps should hold an array of 3 strings, "
468           "but first member is %s instead", G_VALUE_TYPE_NAME (inner_val));
469       goto done;
470     }
471
472     inner_val = gst_value_array_get_value (val, 1);
473     if (!G_VALUE_HOLDS_STRING (inner_val)) {
474       GST_WARNING_OBJECT (ext, "Caps should hold an array of 3 strings, "
475           "but second member is %s instead", G_VALUE_TYPE_NAME (inner_val));
476
477       goto done;
478     }
479     if (g_strcmp0 (g_value_get_string (inner_val),
480             gst_rtp_header_extension_get_uri (ext)) != 0) {
481       GST_WARNING_OBJECT (ext, "URI doesn't match RTP Header extension:"
482           " \"%s\", expected \"%s\"", g_value_get_string (inner_val),
483           gst_rtp_header_extension_get_uri (ext));
484       goto done;
485     }
486
487     inner_val = gst_value_array_get_value (val, 2);
488     if (!G_VALUE_HOLDS_STRING (inner_val)) {
489       GST_WARNING_OBJECT (ext, "Caps should hold an array of 3 strings, "
490           "but third member is %s instead", G_VALUE_TYPE_NAME (inner_val));
491       goto done;
492     }
493
494     attributes = g_value_get_string (inner_val);
495   } else {
496     GST_WARNING_OBJECT (ext, "Caps field %s should be either a string"
497         " containing the URI or an array of 3 strings containing the"
498         " direction, URI and attributes, but contains %s", field_name,
499         G_VALUE_TYPE_NAME (val));
500     goto done;
501   }
502
503   if (klass->set_attributes)
504     ret = klass->set_attributes (ext, direction, attributes);
505   else
506     ret = TRUE;
507
508   if (ret)
509     priv->direction = direction;
510
511 done:
512
513   g_free (field_name);
514   return ret;
515 }
516
517 /**
518  * gst_rtp_header_extension_wants_update_non_rtp_src_caps:
519  * @ext: a #GstRTPHeaderExtension
520  *
521  * Call this function after gst_rtp_header_extension_read() to check if
522  * the depayloader's src caps need updating with data received in the last RTP
523  * packet.
524  *
525  * Returns: Whether @ext wants to update depayloader's src caps.
526  *
527  * Since: 1.20
528  */
529 gboolean
530 gst_rtp_header_extension_wants_update_non_rtp_src_caps (GstRTPHeaderExtension *
531     ext)
532 {
533   GstRTPHeaderExtensionPrivate *priv =
534       gst_rtp_header_extension_get_instance_private (ext);
535
536   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
537
538   return priv->wants_update_non_rtp_src_caps;
539 }
540
541 /**
542  * gst_rtp_header_extension_set_wants_update_non_rtp_src_caps:
543  * @ext: a #GstRTPHeaderExtension
544  * @state: TRUE if caps update is needed
545  *
546  * Call this function in a subclass from #GstRTPHeaderExtensionClass::read to
547  * tell the depayloader whether the data just parsed from RTP packet require
548  * updating its src (non-RTP) caps. If @state is TRUE, #GstRTPBaseDepayload will
549  * eventually invoke gst_rtp_header_extension_update_non_rtp_src_caps() to
550  * have the caps update applied. Applying the update also flips the internal
551  * "wants update" flag back to FALSE.
552  *
553  * Since: 1.20
554  */
555 void gst_rtp_header_extension_set_wants_update_non_rtp_src_caps
556     (GstRTPHeaderExtension * ext, gboolean state)
557 {
558   GstRTPHeaderExtensionPrivate *priv =
559       gst_rtp_header_extension_get_instance_private (ext);
560
561   g_return_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext));
562
563   priv->wants_update_non_rtp_src_caps = state;
564 }
565
566 /**
567  * gst_rtp_header_extension_set_non_rtp_sink_caps:
568  * @ext: a #GstRTPHeaderExtension
569  * @caps: sink #GstCaps
570  *
571  * Passes RTP payloader's sink (i.e. not payloaded) @caps to the header
572  * extension.
573  *
574  * Returns: Whether @caps could be read successfully
575  *
576  * Since: 1.20
577  */
578 gboolean
579 gst_rtp_header_extension_set_non_rtp_sink_caps (GstRTPHeaderExtension * ext,
580     const GstCaps * caps)
581 {
582   GstRTPHeaderExtensionPrivate *priv =
583       gst_rtp_header_extension_get_instance_private (ext);
584   GstRTPHeaderExtensionClass *klass;
585
586   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
587   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
588   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, FALSE);
589   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
590
591   if (klass->set_non_rtp_sink_caps) {
592     return klass->set_non_rtp_sink_caps (ext, caps);
593   }
594
595   return TRUE;
596 }
597
598 /**
599  * gst_rtp_header_extension_update_non_rtp_src_caps:
600  * @ext: a #GstRTPHeaderExtension
601  * @caps: src #GstCaps to modify
602  *
603  * Updates depayloader src caps based on the information received in RTP header.
604  * @caps must be writable as this function may modify them.
605  *
606  * Returns: whether @caps were modified successfully
607  *
608  * Since: 1.20
609  */
610 gboolean
611 gst_rtp_header_extension_update_non_rtp_src_caps (GstRTPHeaderExtension * ext,
612     GstCaps * caps)
613 {
614   GstRTPHeaderExtensionPrivate *priv =
615       gst_rtp_header_extension_get_instance_private (ext);
616   GstRTPHeaderExtensionClass *klass;
617
618   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
619   g_return_val_if_fail (gst_caps_is_writable (caps), FALSE);
620   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
621   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, FALSE);
622   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
623
624   priv->wants_update_non_rtp_src_caps = FALSE;
625
626   if (klass->update_non_rtp_src_caps) {
627     return klass->update_non_rtp_src_caps (ext, caps);
628   }
629
630   return TRUE;
631 }
632
633 /**
634  * gst_rtp_header_extension_set_caps_from_attributes:
635  * @ext: a #GstRTPHeaderExtension
636  * @caps: writable #GstCaps to modify
637  *
638  * gst_rtp_header_extension_set_id() must have been called with a valid
639  * extension id that is contained in these caps.
640  *
641  * The only current known caps format is based on the SDP standard as produced
642  * by gst_sdp_media_attributes_to_caps().
643  *
644  * Returns: whether the configured attributes on @ext can successfully be set on
645  *      @caps
646  *
647  * Since: 1.20
648  */
649 gboolean
650 gst_rtp_header_extension_set_caps_from_attributes (GstRTPHeaderExtension * ext,
651     GstCaps * caps)
652 {
653   GstRTPHeaderExtensionPrivate *priv =
654       gst_rtp_header_extension_get_instance_private (ext);
655   GstRTPHeaderExtensionClass *klass;
656
657   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
658   g_return_val_if_fail (gst_caps_is_writable (caps), FALSE);
659   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
660   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, FALSE);
661   klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
662   g_return_val_if_fail (klass->set_caps_from_attributes != NULL, FALSE);
663
664   return klass->set_caps_from_attributes (ext, caps);
665 }
666
667 /**
668  * gst_rtp_header_extension_get_sdp_caps_field_name:
669  * @ext: the #GstRTPHeaderExtension
670  *
671  * Returns: (transfer full): the #GstStructure field name used in SDP-like #GstCaps for this @ext configuration
672  *
673  * Since: 1.20
674  */
675 gchar *
676 gst_rtp_header_extension_get_sdp_caps_field_name (GstRTPHeaderExtension * ext)
677 {
678   GstRTPHeaderExtensionPrivate *priv =
679       gst_rtp_header_extension_get_instance_private (ext);
680
681   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), NULL);
682   g_return_val_if_fail (priv->ext_id <= MAX_RTP_EXT_ID, NULL);
683
684   return g_strdup_printf ("extmap-%u", priv->ext_id);
685 }
686
687 /**
688  * gst_rtp_header_extension_set_caps_from_attributes_helper:
689  * @ext: the #GstRTPHeaderExtension
690  * @caps: #GstCaps to write fields into
691  *
692  * Helper implementation for GstRTPExtensionClass::set_caps_from_attributes
693  * that sets the @ext uri on caps with the specified extension id as required
694  * for sdp #GstCaps.
695  *
696  * Requires that the extension does not have any attributes or direction
697  * advertised in @caps.
698  *
699  * Returns: whether the @ext attributes could be set on @caps.
700  *
701  * Since: 1.20
702  */
703 gboolean
704 gst_rtp_header_extension_set_caps_from_attributes_helper (GstRTPHeaderExtension
705     * ext, GstCaps * caps, const gchar * attributes)
706 {
707   GstRTPHeaderExtensionPrivate *priv =
708       gst_rtp_header_extension_get_instance_private (ext);
709   gchar *field_name = gst_rtp_header_extension_get_sdp_caps_field_name (ext);
710   GstStructure *s = gst_caps_get_structure (caps, 0);
711
712   if (priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_INHERITED &&
713       (attributes == NULL || attributes[0] == 0)) {
714     gst_structure_set (s, field_name, G_TYPE_STRING,
715         gst_rtp_header_extension_get_uri (ext), NULL);
716   } else {
717     GValue arr = G_VALUE_INIT;
718     GValue val = G_VALUE_INIT;
719
720     g_value_init (&arr, GST_TYPE_ARRAY);
721     g_value_init (&val, G_TYPE_STRING);
722
723     if (priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_INHERITED) {
724       g_value_set_string (&val, "");
725     } else {
726       if ((priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_SENDRECV) ==
727           GST_RTP_HEADER_EXTENSION_DIRECTION_SENDRECV)
728         g_value_set_string (&val, "sendrecv");
729       else if (priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_SENDONLY)
730         g_value_set_string (&val, "sendonly");
731       else if (priv->direction & GST_RTP_HEADER_EXTENSION_DIRECTION_RECVONLY)
732         g_value_set_string (&val, "recvonly");
733       else
734         g_value_set_string (&val, "inactive");
735     }
736     gst_value_array_append_value (&arr, &val);
737
738     /* uri */
739     g_value_set_string (&val, gst_rtp_header_extension_get_uri (ext));
740     gst_value_array_append_value (&arr, &val);
741
742     /* attributes */
743     g_value_set_string (&val, attributes);
744     gst_value_array_append_value (&arr, &val);
745
746     gst_structure_set_value (s, field_name, &arr);
747
748     GST_DEBUG_OBJECT (ext, "%" GST_PTR_FORMAT, caps);
749
750     g_value_unset (&val);
751     g_value_unset (&arr);
752   }
753
754   g_free (field_name);
755   return TRUE;
756 }
757
758 static gboolean
759 gst_rtp_header_extension_set_caps_from_attributes_default (GstRTPHeaderExtension
760     * ext, GstCaps * caps)
761 {
762   return gst_rtp_header_extension_set_caps_from_attributes_helper (ext, caps,
763       NULL);
764 }
765
766 static gboolean
767 gst_rtp_ext_list_filter (GstPluginFeature * feature, gpointer user_data)
768 {
769   GstElementFactory *factory;
770   gchar *uri = user_data;
771   const gchar *klass, *factory_uri;
772   guint rank;
773
774   /* we only care about element factories */
775   if (!GST_IS_ELEMENT_FACTORY (feature))
776     return FALSE;
777
778   factory = GST_ELEMENT_FACTORY (feature);
779
780   /* only select elements with autoplugging rank */
781   rank = gst_plugin_feature_get_rank (feature);
782   if (rank < GST_RANK_MARGINAL)
783     return FALSE;
784
785   klass =
786       gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
787   if (!strstr (klass, "Network") || !strstr (klass, "Extension") ||
788       !strstr (klass, "RTPHeader"))
789     return FALSE;
790
791   factory_uri =
792       gst_element_factory_get_metadata (factory,
793       GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY);
794   if (!factory_uri)
795     return FALSE;
796
797   if (uri && g_strcmp0 (uri, factory_uri) != 0)
798     return FALSE;
799
800   return TRUE;
801 }
802
803 /**
804  * gst_rtp_get_header_extension_list:
805  *
806  * Retrieve all the factories of the currently registered RTP header
807  * extensions.  Call gst_element_factory_create() with each factory to create
808  * the associated #GstRTPHeaderExtension.
809  *
810  * Returns: (transfer full) (element-type GstElementFactory): a #GList of
811  *     #GstElementFactory's. Use gst_plugin_feature_list_free() after use
812  *
813  * Since: 1.20
814  */
815 GList *
816 gst_rtp_get_header_extension_list (void)
817 {
818   return gst_registry_feature_filter (gst_registry_get (),
819       (GstPluginFeatureFilter) gst_rtp_ext_list_filter, FALSE, NULL);
820 }
821
822 /**
823  * gst_rtp_header_extension_create_from_uri:
824  * @uri: the rtp header extension URI to search for
825  *
826  * Returns: (transfer full) (nullable): the #GstRTPHeaderExtension for @uri or %NULL
827  *
828  * Since: 1.20
829  */
830 GstRTPHeaderExtension *
831 gst_rtp_header_extension_create_from_uri (const gchar * uri)
832 {
833   GList *l;
834
835   l = gst_registry_feature_filter (gst_registry_get (),
836       (GstPluginFeatureFilter) gst_rtp_ext_list_filter, TRUE, (gpointer) uri);
837   if (l) {
838     GstElementFactory *factory = GST_ELEMENT_FACTORY (l->data);
839     GstElement *element = gst_element_factory_create (factory, NULL);
840
841     g_list_free_full (l, (GDestroyNotify) gst_object_unref);
842
843     return GST_RTP_HEADER_EXTENSION (element);
844   }
845
846   return NULL;
847 }
848
849 /**
850  * gst_rtp_header_extension_set_direction:
851  * @ext: the #GstRTPHeaderExtension
852  * @direction: The direction
853  *
854  * Set the direction that this header extension should be used in.
855  * If #GST_RTP_HEADER_EXTENSION_DIRECTION_INHERITED is included, the
856  * direction will not be included in the caps (as it shouldn't be in the
857  * extmap line in the SDP).
858  *
859  * Since: 1.20
860  */
861
862 void
863 gst_rtp_header_extension_set_direction (GstRTPHeaderExtension * ext,
864     GstRTPHeaderExtensionDirection direction)
865 {
866   GstRTPHeaderExtensionPrivate *priv =
867       gst_rtp_header_extension_get_instance_private (ext);
868
869   g_return_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext));
870   g_return_if_fail (direction <= GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT);
871
872   priv->direction = direction;
873 }
874
875 /**
876  * gst_rtp_header_extension_get_direction:
877  * @ext: the #GstRTPHeaderExtension
878  *
879  * Retrieve the direction
880  *
881  * Returns: The direction
882  *
883  * Since: 1.20
884  */
885
886 GstRTPHeaderExtensionDirection
887 gst_rtp_header_extension_get_direction (GstRTPHeaderExtension * ext)
888 {
889   GstRTPHeaderExtensionPrivate *priv =
890       gst_rtp_header_extension_get_instance_private (ext);
891
892   g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext),
893       GST_RTP_HEADER_EXTENSION_DIRECTION_DEFAULT);
894
895   return priv->direction;
896 }