/* GStreamer
* Copyright (C) <2012> Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C) <2020> Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
#include <stdlib.h>
#include <string.h>
+GST_DEBUG_CATEGORY_STATIC (rtphderext_debug);
+#define GST_CAT_DEFAULT (rtphderext_debug)
+
+#define MAX_RTP_EXT_ID 256
+
/**
* gst_rtp_hdrext_set_ntp_64:
* @data: the data to write to
}
return TRUE;
}
+
+#define gst_rtp_header_extension_parent_class parent_class
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstRTPHeaderExtension,
+ gst_rtp_header_extension, GST_TYPE_ELEMENT,
+ GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtphdrext", 0,
+ "RTP Header Extensions");
+ );
+
+/**
+ * gst_rtp_header_extension_class_set_uri:
+ * @klass: the #GstRTPHeaderExtensionClass
+ * @uri: the RTP Header extension uri for @klass
+ *
+ * Set the URI for this RTP header extension implementation.
+ *
+ * Since: 1.20
+ */
+void
+gst_rtp_header_extension_class_set_uri (GstRTPHeaderExtensionClass * klass,
+ const gchar * uri)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ gst_element_class_add_static_metadata (element_class,
+ GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY, uri);
+}
+
+static void
+gst_rtp_header_extension_class_init (GstRTPHeaderExtensionClass * klass)
+{
+}
+
+static void
+gst_rtp_header_extension_init (GstRTPHeaderExtension * ext)
+{
+ ext->ext_id = G_MAXUINT32;
+}
+
+/**
+ * gst_rtp_header_extension_get_uri:
+ * @ext: a #GstRTPHeaderExtension
+ *
+ * Returns: the RTP extension URI for this object
+ *
+ * Since: 1.20
+ */
+const gchar *
+gst_rtp_header_extension_get_uri (GstRTPHeaderExtension * ext)
+{
+ GstRTPHeaderExtensionClass *klass;
+ GstElementClass *element_class;
+
+ g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), NULL);
+ klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
+ element_class = GST_ELEMENT_CLASS (klass);
+
+ return gst_element_class_get_metadata (element_class,
+ GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY);
+}
+
+/**
+ * gst_rtp_header_extension_get_supported_flags:
+ * @ext: a #GstRTPHeaderExtension
+ *
+ * Returns: the flags supported by this instance of @ext
+ *
+ * Since: 1.20
+ */
+GstRTPHeaderExtensionFlags
+gst_rtp_header_extension_get_supported_flags (GstRTPHeaderExtension * ext)
+{
+ GstRTPHeaderExtensionClass *klass;
+
+ g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), 0);
+ klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
+ g_return_val_if_fail (klass->get_supported_flags != NULL, 0);
+
+ return klass->get_supported_flags (ext);
+}
+
+/**
+ * gst_rtp_header_extension_get_max_size:
+ * @ext: a #GstRTPHeaderExtension
+ * @input_meta: a #GstBuffer
+ *
+ * This is used to know how much data a certain header extension will need for
+ * both allocating the resulting data, and deciding how much payload data can
+ * be generated.
+ *
+ * Implemntations should return as accurate a value as is possible using the
+ * information given in the input @buffer.
+ *
+ * Returns: the maximum size of the data written by this extension
+ *
+ * Since: 1.20
+ */
+gsize
+gst_rtp_header_extension_get_max_size (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta)
+{
+ GstRTPHeaderExtensionClass *klass;
+
+ g_return_val_if_fail (GST_IS_BUFFER (input_meta), 0);
+ g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), 0);
+ klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
+ g_return_val_if_fail (klass->get_max_size != NULL, 0);
+
+ return klass->get_max_size (ext, input_meta);
+}
+
+/**
+ * gst_rtp_header_extension_write:
+ * @ext: a #GstRTPHeaderExtension
+ * @input_meta: the input #GstBuffer to read information from if necessary
+ * @write_flags: #GstRTPHeaderExtensionFlags for how the extension should
+ * be written
+ * @output: output RTP #GstBuffer
+ * @data: location to write the rtp header extension into
+ * @size: size of @data
+ *
+ * Writes the RTP header extension to @data using information available from
+ * the @input_meta. @data will be sized to be at least the value returned
+ * from gst_rtp_header_extension_get_max_size().
+ *
+ * Returns: the size of the data written, < 0 on failure
+ *
+ * Since: 1.20
+ */
+gsize
+gst_rtp_header_extension_write (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags,
+ GstBuffer * output, guint8 * data, gsize size)
+{
+ GstRTPHeaderExtensionClass *klass;
+
+ g_return_val_if_fail (GST_IS_BUFFER (input_meta), -1);
+ g_return_val_if_fail (GST_IS_BUFFER (output), -1);
+ g_return_val_if_fail (gst_buffer_is_writable (output), -1);
+ g_return_val_if_fail (data != NULL, -1);
+ g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), -1);
+ g_return_val_if_fail (ext->ext_id <= MAX_RTP_EXT_ID, -1);
+ klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
+ g_return_val_if_fail (klass->write != NULL, -1);
+
+ return klass->write (ext, input_meta, write_flags, output, data, size);
+}
+
+/**
+ * gst_rtp_header_extension_read:
+ * @ext: a #GstRTPHeaderExtension
+ * @read_flags: #GstRTPHeaderExtensionFlags for how the extension should
+ * be written
+ * @data: location to read the rtp header extension from
+ * @size: size of @data
+ * @buffer: a #GstBuffer to modify if necessary
+ *
+ * Read the RTP header extension from @data.
+ *
+ * Returns: whether the extension could be read from @data
+ *
+ * Since: 1.20
+ */
+gboolean
+gst_rtp_header_extension_read (GstRTPHeaderExtension * ext,
+ GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size,
+ GstBuffer * buffer)
+{
+ GstRTPHeaderExtensionClass *klass;
+
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
+ g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE);
+ g_return_val_if_fail (data != NULL, FALSE);
+ g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
+ g_return_val_if_fail (ext->ext_id <= MAX_RTP_EXT_ID, FALSE);
+ klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
+ g_return_val_if_fail (klass->read != NULL, FALSE);
+
+ return klass->read (ext, read_flags, data, size, buffer);
+}
+
+/**
+ * gst_rtp_header_extension_get_id:
+ * @ext: a #GstRTPHeaderExtension
+ *
+ * Returns: the RTP extension id configured on @ext
+ *
+ * Since: 1.20
+ */
+guint
+gst_rtp_header_extension_get_id (GstRTPHeaderExtension * ext)
+{
+ g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), 0);
+
+ return ext->ext_id;
+}
+
+/**
+ * gst_rtp_header_extension_set_id:
+ * @ext: a #GstRTPHeaderExtension
+ * @ext_id: The id of this extension
+ *
+ * sets the RTP extension id on @ext
+ *
+ * Since: 1.20
+ */
+void
+gst_rtp_header_extension_set_id (GstRTPHeaderExtension * ext, guint ext_id)
+{
+ g_return_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext));
+ g_return_if_fail (ext_id < MAX_RTP_EXT_ID);
+
+ ext->ext_id = ext_id;
+}
+
+/**
+ * gst_rtp_header_extension_set_attributes_from_caps:
+ * @ext: a #GstRTPHeaderExtension
+ * @caps: the #GstCaps to configure this extension with
+ *
+ * gst_rtp_header_extension_set_id() must have been called with a valid
+ * extension id that is contained in these caps.
+ *
+ * The only current known caps format is based on the SDP standard as produced
+ * by gst_sdp_media_attributes_to_caps().
+ *
+ * Returns: whether the @caps could be successfully set on @ext.
+ *
+ * Since: 1.20
+ */
+gboolean
+gst_rtp_header_extension_set_attributes_from_caps (GstRTPHeaderExtension * ext,
+ const GstCaps * caps)
+{
+ GstRTPHeaderExtensionClass *klass;
+ GstStructure *structure;
+ gchar *field_name;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
+ g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
+ g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
+ g_return_val_if_fail (ext->ext_id <= MAX_RTP_EXT_ID, FALSE);
+ klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
+ g_return_val_if_fail (klass->set_attributes_from_caps != NULL, FALSE);
+
+ structure = gst_caps_get_structure (caps, 0);
+ g_return_val_if_fail (structure != NULL, FALSE);
+ field_name = g_strdup_printf ("extmap-%u", ext->ext_id);
+ g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE);
+ g_free (field_name);
+
+ return klass->set_attributes_from_caps (ext, caps);
+}
+
+/**
+ * gst_rtp_header_extension_set_caps_from_attributes:
+ * @ext: a #GstRTPHeaderExtension
+ * @caps: writable #GstCaps to modify
+ *
+ * gst_rtp_header_extension_set_id() must have been called with a valid
+ * extension id that is contained in these caps.
+ *
+ * The only current known caps format is based on the SDP standard as produced
+ * by gst_sdp_media_attributes_to_caps().
+ *
+ * Returns: whether the configured attributes on @ext can successfully be set on
+ * @caps
+ *
+ * Since: 1.20
+ */
+gboolean
+gst_rtp_header_extension_set_caps_from_attributes (GstRTPHeaderExtension * ext,
+ GstCaps * caps)
+{
+ GstRTPHeaderExtensionClass *klass;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
+ g_return_val_if_fail (gst_caps_is_writable (caps), FALSE);
+ g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), FALSE);
+ g_return_val_if_fail (ext->ext_id <= MAX_RTP_EXT_ID, FALSE);
+ klass = GST_RTP_HEADER_EXTENSION_GET_CLASS (ext);
+ g_return_val_if_fail (klass->set_caps_from_attributes != NULL, FALSE);
+
+ return klass->set_caps_from_attributes (ext, caps);
+}
+
+/**
+ * gst_rtp_header_extension_get_sdp_caps_field_name:
+ * @ext: the #GstRTPHeaderExtension
+ *
+ * Returns: (transfer full): the #GstStructure field name used in SDP-like #GstCaps for this @ext configuration
+ *
+ * Since: 1.20
+ */
+gchar *
+gst_rtp_header_extension_get_sdp_caps_field_name (GstRTPHeaderExtension * ext)
+{
+ g_return_val_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext), NULL);
+ g_return_val_if_fail (ext->ext_id <= MAX_RTP_EXT_ID, NULL);
+
+ return g_strdup_printf ("extmap-%u", ext->ext_id);
+}
+
+/**
+ * gst_rtp_header_extension_set_attributes_from_caps_simple_sdp:
+ * @ext: the #GstRTPHeaderExtension
+ * @caps: #GstCaps to read attributes from
+ *
+ * Helper implementation for GstRTPExtensionClass::set_attributes_from_caps
+ * that retrieves the configured extension id and checks that the
+ * corresponding field in the sdp caps is configured for this extension uri.
+ * Requires that the extension does not have any attributes or direction
+ * advertised in the caps.
+ *
+ * Returns: whether the attributes in the @caps could be set on @ext successfully
+ *
+ * Since: 1.20
+ */
+gboolean
+ gst_rtp_header_extension_set_attributes_from_caps_simple_sdp
+ (GstRTPHeaderExtension * ext, const GstCaps * caps) {
+ gchar *field_name = gst_rtp_header_extension_get_sdp_caps_field_name (ext);
+ GstStructure *s = gst_caps_get_structure (caps, 0);
+ const gchar *ext_uri;
+
+ if (!(ext_uri = gst_structure_get_string (s, field_name)))
+ goto error;
+
+ if (g_strcmp0 (ext_uri, gst_rtp_header_extension_get_uri (ext)) != 0)
+ goto error;
+
+ g_free (field_name);
+ return TRUE;
+
+error:
+ g_free (field_name);
+ return FALSE;
+}
+
+/**
+ * gst_rtp_header_extension_set_caps_from_attributes_simple_sdp:
+ * @ext: the #GstRTPHeaderExtension
+ * @caps: #GstCaps to write fields into
+ *
+ * Helper implementation for GstRTPExtensionClass::set_caps_from_attributes
+ * that sets the @ext uri on caps with the specified extension id as required
+ * for sdp #GstCaps.
+ *
+ * Requires that the extension does not have any attributes or direction
+ * advertised in @caps.
+ *
+ * Returns: whether the @ext attributes could be set on @caps.
+ *
+ * Since: 1.20
+ */
+gboolean
+ gst_rtp_header_extension_set_caps_from_attributes_simple_sdp
+ (GstRTPHeaderExtension * ext, GstCaps * caps) {
+ gchar *field_name = gst_rtp_header_extension_get_sdp_caps_field_name (ext);
+ GstStructure *s = gst_caps_get_structure (caps, 0);
+
+ gst_structure_set (s, field_name, G_TYPE_STRING,
+ gst_rtp_header_extension_get_uri (ext), NULL);
+
+ g_free (field_name);
+ return TRUE;
+}
+
+static gboolean
+gst_rtp_ext_list_filter (GstPluginFeature * feature, gpointer user_data)
+{
+ GstElementFactory *factory;
+ gchar *uri = user_data;
+ const gchar *klass, *factory_uri;
+ guint rank;
+
+ /* we only care about element factories */
+ if (!GST_IS_ELEMENT_FACTORY (feature))
+ return FALSE;
+
+ factory = GST_ELEMENT_FACTORY (feature);
+
+ /* only select elements with autoplugging rank */
+ rank = gst_plugin_feature_get_rank (feature);
+ if (rank < GST_RANK_MARGINAL)
+ return FALSE;
+
+ klass =
+ gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
+ if (!strstr (klass, "Network") || !strstr (klass, "Extension") ||
+ !strstr (klass, "RTPHeader"))
+ return FALSE;
+
+ factory_uri =
+ gst_element_factory_get_metadata (factory,
+ GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY);
+ if (!factory_uri)
+ return FALSE;
+
+ if (uri && g_strcmp0 (uri, factory_uri) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * gst_rtp_get_header_extension_list:
+ *
+ * Retrieve all the currently registered RTP header extensions
+ *
+ * Returns: (transfer full) (element-type GstRTPHeaderExtension): a #GList of
+ * #GstRTPHeaderExtension. Use gst_plugin_feature_list_free() after use
+ *
+ * Since: 1.20
+ */
+GList *
+gst_rtp_get_header_extension_list (void)
+{
+ return gst_registry_feature_filter (gst_registry_get (),
+ (GstPluginFeatureFilter) gst_rtp_ext_list_filter, FALSE, NULL);
+}
+
+/**
+ * gst_rtp_header_extension_create_from_uri:
+ * @uri: the rtp header extension URI to search for
+ *
+ * Returns: (transfer full) (nullable): the #GstRTPHeaderExtension for @uri or %NULL
+ *
+ * Since: 1.20
+ */
+GstRTPHeaderExtension *
+gst_rtp_header_extension_create_from_uri (const gchar * uri)
+{
+ GList *l;
+
+ l = gst_registry_feature_filter (gst_registry_get (),
+ (GstPluginFeatureFilter) gst_rtp_ext_list_filter, TRUE, (gpointer) uri);
+ if (l) {
+ GstElementFactory *factory = GST_ELEMENT_FACTORY (l->data);
+ GstElement *element = gst_element_factory_create (factory, NULL);
+
+ g_list_free_full (l, (GDestroyNotify) gst_object_unref);
+
+ return GST_RTP_HEADER_EXTENSION (element);
+ }
+
+ return NULL;
+}
/* GStreamer
* Copyright (C) <2012> Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C) <2020> Matthew Waters <matthew@centricular.com>
*
* gstrtphdrext.h: RTP header extensions
*
GST_RTP_API
gboolean gst_rtp_hdrext_get_ntp_56 (gpointer data, guint size, guint64 *ntptime);
+/**
+ * GST_RTP_HDREXT_ELEMENT_CLASS:
+ *
+ * Constant string used in element classification to signal that this element
+ * is a RTP header extension.
+ *
+ * Since: 1.20
+ */
+#define GST_RTP_HDREXT_ELEMENT_CLASS "Network/Extension/RTPHeader"
+
+GST_RTP_API
+GType gst_rtp_header_extension_get_type (void);
+#define GST_TYPE_RTP_HEADER_EXTENSION (gst_rtp_header_extension_get_type())
+#define GST_RTP_HEADER_EXTENSION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_HEADER_EXTENSION,GstRTPHeaderExtension))
+#define GST_RTP_HEADER_EXTENSION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_HEADER_EXTENSION,GstRTPHeaderExtensionClass))
+#define GST_RTP_HEADER_EXTENSION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_RTP_HEADER_EXTENSION,GstRTPHeaderExtensionClass))
+#define GST_IS_RTP_HEADER_EXTENSION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_HEADER_EXTENSION))
+#define GST_IS_RTP_HEADER_EXTENSION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_HEADER_EXTENSION))
+/**
+ * GST_RTP_HEADER_EXTENSION_CAST:
+ *
+ * Since: 1.20
+ */
+#define GST_RTP_HEADER_EXTENSION_CAST(obj) ((GstRTPHeaderExtension *)(obj))
+
+typedef struct _GstRTPHeaderExtension GstRTPHeaderExtension;
+typedef struct _GstRTPHeaderExtensionClass GstRTPHeaderExtensionClass;
+
+/**
+ * GstRTPHeaderExtensionFlags:
+ * @GST_RTP_HEADER_EXTENSION_ONE_BYTE: The one byte rtp extension header.
+ * 1-16 data bytes per extension with a maximum of
+ * 14 extension ids in total.
+ * @GST_RTP_HEADER_EXTENSION_TWO_BYTE: The two byte rtp extension header.
+ * 256 data bytes per extension with a maximum of 255 (or 256
+ * including appbits) extensions in total.
+ *
+ * Flags that apply to a RTP Audio/Video header extension.
+ *
+ * Since: 1.20
+ */
+typedef enum /*< underscore_name=gst_rtp_header_extension_flags >*/
+{
+ GST_RTP_HEADER_EXTENSION_ONE_BYTE = (1 << 0),
+ GST_RTP_HEADER_EXTENSION_TWO_BYTE = (1 << 1),
+} GstRTPHeaderExtensionFlags;
+
+/**
+ * GstRTPHeaderExtension:
+ * @parent: the parent #GObject
+ * @ext_id: the configured extension id
+ *
+ * Instance struct for a RTP Audio/Video header extension.
+ *
+ * Since: 1.20
+ */
+struct _GstRTPHeaderExtension
+{
+ GstElement parent;
+
+ guint ext_id;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+/**
+ * GstRTPHeaderExtensionClass:
+ * @parent_class: the parent class
+ * @get_uri: retrieve the RTP extension uri
+ * @get_supported_flags: retrieve the supported flags
+ * @get_max_size: retrieve the maximum size for this extension based on the
+ * information available from input_meta. Implementations should attempt
+ * to provide as accurate information as possible as the returned value
+ * will be used to control the amount of possible data in the payload.
+ * Implementations must return the maximum as the allocated size for
+ * writing the extension will be at least the size of the returned value.
+ * Return the amount of data read or <0 on failure.
+ * @write: write into @data the information for this extension. Various
+ * information is provided to help writing extensions in particular cases.
+ * @read: read from a rtp payloaded buffer and extract the extension
+ * information, optionally adding some meta onto the output buffer.
+ * @set_attributes_from_caps: read the caps information to set the necesary
+ * attributes that may be signaled e.g. with an SDP.
+ * @set_caps_from_attributes: write the necessary caps field/s for the configured
+ * attributes e.g. as signalled with SDP.
+ *
+ * Base class for RTP Header extensions.
+ *
+ * Since: 1.20
+ */
+struct _GstRTPHeaderExtensionClass
+{
+ GstElementClass parent_class;
+
+ /*< public >*/
+ GstRTPHeaderExtensionFlags (*get_supported_flags) (GstRTPHeaderExtension * ext);
+
+ gsize (*get_max_size) (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta);
+
+ gsize (*write) (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta,
+ GstRTPHeaderExtensionFlags write_flags,
+ GstBuffer * output,
+ guint8 * data,
+ gsize size);
+
+ gboolean (*read) (GstRTPHeaderExtension * ext,
+ GstRTPHeaderExtensionFlags read_flags,
+ const guint8 * data,
+ gsize size,
+ GstBuffer * buffer);
+ gboolean (*set_attributes_from_caps) (GstRTPHeaderExtension * ext,
+ const GstCaps * caps);
+ gboolean (*set_caps_from_attributes) (GstRTPHeaderExtension * ext,
+ GstCaps * caps);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTPHeaderExtension, gst_object_unref)
+
+/**
+ * GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY:
+ *
+ * Since: 1.20
+ */
+#define GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY "RTP-Header-Extension-URI"
+
+GST_RTP_API
+void gst_rtp_header_extension_class_set_uri (GstRTPHeaderExtensionClass *klass,
+ const gchar * uri);
+
+GST_RTP_API
+const gchar * gst_rtp_header_extension_get_uri (GstRTPHeaderExtension * ext);
+GST_RTP_API
+gsize gst_rtp_header_extension_get_max_size (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta);
+GST_RTP_API
+GstRTPHeaderExtensionFlags gst_rtp_header_extension_get_supported_flags (GstRTPHeaderExtension * ext);
+GST_RTP_API
+guint gst_rtp_header_extension_get_id (GstRTPHeaderExtension * ext);
+GST_RTP_API
+void gst_rtp_header_extension_set_id (GstRTPHeaderExtension * ext,
+ guint ext_id);
+GST_RTP_API
+gsize gst_rtp_header_extension_write (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta,
+ GstRTPHeaderExtensionFlags write_flags,
+ GstBuffer * output,
+ guint8 * data,
+ gsize size);
+GST_RTP_API
+gboolean gst_rtp_header_extension_read (GstRTPHeaderExtension * ext,
+ GstRTPHeaderExtensionFlags read_flags,
+ const guint8 * data,
+ gsize size,
+ GstBuffer * buffer);
+GST_RTP_API
+gboolean gst_rtp_header_extension_set_caps_from_attributes (GstRTPHeaderExtension * ext,
+ GstCaps * caps);
+GST_RTP_API
+gboolean gst_rtp_header_extension_set_attributes_from_caps (GstRTPHeaderExtension * ext,
+ const GstCaps * caps);
+
+GST_RTP_API
+GList * gst_rtp_get_header_extension_list (void);
+GST_RTP_API
+GstRTPHeaderExtension * gst_rtp_header_extension_create_from_uri (const gchar * uri);
+
+GST_RTP_API
+gchar * gst_rtp_header_extension_get_sdp_caps_field_name (GstRTPHeaderExtension * ext);
+GST_RTP_API
+gboolean gst_rtp_header_extension_set_attributes_from_caps_simple_sdp (GstRTPHeaderExtension * ext, const GstCaps *caps);
+GST_RTP_API
+gboolean gst_rtp_header_extension_set_caps_from_attributes_simple_sdp (GstRTPHeaderExtension * ext, GstCaps *caps);
+
G_END_DECLS
#endif /* __GST_RTPHDREXT_H__ */
--- /dev/null
+/* GStreamer RTP header extension unit tests
+ * Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General
+ * Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "gst/gstcaps.h"
+#include "gst/gstvalue.h"
+#include "gst/rtp/gstrtphdrext.h"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/check/check.h>
+#include <gst/rtp/rtp.h>
+
+/* GstRTPDummyHdrExt shared between payloading and depayloading tests */
+
+#define GST_TYPE_RTP_DUMMY_HDR_EXT \
+ (gst_rtp_dummy_hdr_ext_get_type())
+#define GST_RTP_DUMMY_HDR_EXT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_DUMMY_HDR_EXT,GstRTPDummyHdrExt))
+#define GST_RTP_DUMMY_HDR_EXT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_DUMMY_HDR_EXT,GstRTPDummyHdrExtClass))
+#define GST_IS_RTP_DUMMY_HDR_EXT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_DUMMY_HDR_EXT))
+#define GST_IS_RTP_DUMMY_HDR_EXT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_DUMMY_HDR_EXT))
+
+#define DUMMY_HDR_EXT_URI "gst:test:uri"
+
+typedef struct _GstRTPDummyHdrExt GstRTPDummyHdrExt;
+typedef struct _GstRTPDummyHdrExtClass GstRTPDummyHdrExtClass;
+
+struct _GstRTPDummyHdrExt
+{
+ GstRTPHeaderExtension payload;
+
+ GstRTPHeaderExtensionFlags supported_flags;
+ guint read_count;
+ guint write_count;
+
+ gchar *direction;
+ gchar *attributes;
+};
+
+struct _GstRTPDummyHdrExtClass
+{
+ GstRTPHeaderExtensionClass parent_class;
+};
+
+GType gst_rtp_dummy_hdr_ext_get_type (void);
+
+G_DEFINE_TYPE (GstRTPDummyHdrExt, gst_rtp_dummy_hdr_ext,
+ GST_TYPE_RTP_HEADER_EXTENSION);
+
+static GstRTPHeaderExtensionFlags
+gst_rtp_dummy_hdr_ext_get_supported_flags (GstRTPHeaderExtension * ext);
+static gsize gst_rtp_dummy_hdr_ext_get_max_size (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta);
+static gsize gst_rtp_dummy_hdr_ext_write (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags,
+ GstBuffer * output, guint8 * data, gsize size);
+static gboolean gst_rtp_dummy_hdr_ext_read (GstRTPHeaderExtension * ext,
+ GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size,
+ GstBuffer * buffer);
+static gboolean
+gst_rtp_dummy_hdr_ext_set_caps_from_attributes (GstRTPHeaderExtension * ext,
+ GstCaps * caps);
+static gboolean
+gst_rtp_dummy_hdr_ext_set_attributes_from_caps (GstRTPHeaderExtension * ext,
+ const GstCaps * caps);
+
+static void gst_rtp_dummy_hdr_ext_finalize (GObject * object);
+
+static void
+gst_rtp_dummy_hdr_ext_class_init (GstRTPDummyHdrExtClass * klass)
+{
+ GstRTPHeaderExtensionClass *gstrtpheaderextension_class;
+ GstElementClass *gstelement_class;
+ GObjectClass *gobject_class;
+
+ gstrtpheaderextension_class = GST_RTP_HEADER_EXTENSION_CLASS (klass);
+ gstelement_class = GST_ELEMENT_CLASS (klass);
+ gobject_class = G_OBJECT_CLASS (klass);
+
+ gstrtpheaderextension_class->get_supported_flags =
+ gst_rtp_dummy_hdr_ext_get_supported_flags;
+ gstrtpheaderextension_class->get_max_size =
+ gst_rtp_dummy_hdr_ext_get_max_size;
+ gstrtpheaderextension_class->write = gst_rtp_dummy_hdr_ext_write;
+ gstrtpheaderextension_class->read = gst_rtp_dummy_hdr_ext_read;
+ gstrtpheaderextension_class->set_attributes_from_caps =
+ gst_rtp_dummy_hdr_ext_set_attributes_from_caps;
+ gstrtpheaderextension_class->set_caps_from_attributes =
+ gst_rtp_dummy_hdr_ext_set_caps_from_attributes;
+
+ gobject_class->finalize = gst_rtp_dummy_hdr_ext_finalize;
+
+ gst_element_class_set_static_metadata (gstelement_class,
+ "Dummy Test RTP Header Extension", GST_RTP_HDREXT_ELEMENT_CLASS,
+ "Dummy Test RTP Header Extension", "Author <email@example.com>");
+ gst_rtp_header_extension_class_set_uri (gstrtpheaderextension_class,
+ DUMMY_HDR_EXT_URI);
+}
+
+static void
+gst_rtp_dummy_hdr_ext_init (GstRTPDummyHdrExt * dummy)
+{
+ dummy->supported_flags =
+ GST_RTP_HEADER_EXTENSION_ONE_BYTE | GST_RTP_HEADER_EXTENSION_TWO_BYTE;
+}
+
+static void
+gst_rtp_dummy_hdr_ext_finalize (GObject * object)
+{
+ GstRTPDummyHdrExt *dummy = GST_RTP_DUMMY_HDR_EXT (object);
+
+ g_free (dummy->attributes);
+ dummy->attributes = NULL;
+ g_free (dummy->direction);
+ dummy->direction = NULL;
+
+ G_OBJECT_CLASS (gst_rtp_dummy_hdr_ext_parent_class)->finalize (object);
+}
+
+static GstRTPHeaderExtension *
+rtp_dummy_hdr_ext_new (void)
+{
+ return g_object_new (GST_TYPE_RTP_DUMMY_HDR_EXT, NULL);
+}
+
+static GstRTPHeaderExtensionFlags
+gst_rtp_dummy_hdr_ext_get_supported_flags (GstRTPHeaderExtension * ext)
+{
+ GstRTPDummyHdrExt *dummy = GST_RTP_DUMMY_HDR_EXT (ext);
+
+ return dummy->supported_flags;
+}
+
+static gsize
+gst_rtp_dummy_hdr_ext_get_max_size (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta)
+{
+ return 1;
+}
+
+#define TEST_DATA_BYTE 0x9d
+
+static gsize
+gst_rtp_dummy_hdr_ext_write (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags,
+ GstBuffer * output, guint8 * data, gsize size)
+{
+ GstRTPDummyHdrExt *dummy = GST_RTP_DUMMY_HDR_EXT (ext);
+
+ g_assert (size >= gst_rtp_dummy_hdr_ext_get_max_size (ext, NULL));
+
+ data[0] = TEST_DATA_BYTE;
+
+ dummy->write_count++;
+
+ return 1;
+}
+
+static gboolean
+gst_rtp_dummy_hdr_ext_read (GstRTPHeaderExtension * ext,
+ GstRTPHeaderExtensionFlags read_flags, const guint8 * data,
+ gsize size, GstBuffer * buffer)
+{
+ GstRTPDummyHdrExt *dummy = GST_RTP_DUMMY_HDR_EXT (ext);
+
+ fail_unless_equals_int (data[0], TEST_DATA_BYTE);
+
+ dummy->read_count++;
+
+ return TRUE;
+}
+
+static gboolean
+gst_rtp_dummy_hdr_ext_set_caps_from_attributes (GstRTPHeaderExtension * ext,
+ GstCaps * caps)
+{
+ GstRTPDummyHdrExt *dummy = GST_RTP_DUMMY_HDR_EXT (ext);
+ gchar *field_name = gst_rtp_header_extension_get_sdp_caps_field_name (ext);
+ GstStructure *s = gst_caps_get_structure (caps, 0);
+
+ if (!field_name)
+ return FALSE;
+
+ if (dummy->attributes || dummy->direction) {
+ GValue arr = G_VALUE_INIT;
+ GValue val = G_VALUE_INIT;
+
+ g_value_init (&arr, GST_TYPE_ARRAY);
+ g_value_init (&val, G_TYPE_STRING);
+
+ /* direction */
+ g_value_set_string (&val, dummy->direction);
+ gst_value_array_append_value (&arr, &val);
+
+ /* uri */
+ g_value_set_string (&val, gst_rtp_header_extension_get_uri (ext));
+ gst_value_array_append_value (&arr, &val);
+
+ /* attributes */
+ g_value_set_string (&val, dummy->attributes);
+ gst_value_array_append_value (&arr, &val);
+
+ gst_structure_set_value (s, field_name, &arr);
+
+ g_value_unset (&val);
+ g_value_unset (&arr);
+ } else {
+ gst_structure_set (s, field_name, G_TYPE_STRING,
+ gst_rtp_header_extension_get_uri (ext), NULL);
+ }
+
+ g_free (field_name);
+ return TRUE;
+}
+
+static gboolean
+gst_rtp_dummy_hdr_ext_set_attributes_from_caps (GstRTPHeaderExtension * ext,
+ const GstCaps * caps)
+{
+ GstRTPDummyHdrExt *dummy = GST_RTP_DUMMY_HDR_EXT (ext);
+ gchar *field_name = gst_rtp_header_extension_get_sdp_caps_field_name (ext);
+ GstStructure *s = gst_caps_get_structure (caps, 0);
+ gchar *new_attrs = NULL, *new_direction = NULL;
+ const gchar *ext_uri;
+ const GValue *arr;
+
+ if (!field_name)
+ return FALSE;
+
+ if ((ext_uri = gst_structure_get_string (s, field_name))) {
+ if (g_strcmp0 (ext_uri, gst_rtp_header_extension_get_uri (ext)) != 0) {
+ /* incompatible extension uri for this instance */
+ goto error;
+ }
+ } else if ((arr = gst_structure_get_value (s, field_name))
+ && GST_VALUE_HOLDS_ARRAY (arr)
+ && gst_value_array_get_size (arr) == 3) {
+ const GValue *val;
+
+ val = gst_value_array_get_value (arr, 1);
+ if (!G_VALUE_HOLDS_STRING (val))
+ goto error;
+ if (g_strcmp0 (g_value_get_string (val),
+ gst_rtp_header_extension_get_uri (ext)) != 0)
+ goto error;
+
+ val = gst_value_array_get_value (arr, 0);
+ if (!G_VALUE_HOLDS_STRING (val))
+ goto error;
+ new_direction = g_value_dup_string (val);
+
+ val = gst_value_array_get_value (arr, 2);
+ if (!G_VALUE_HOLDS_STRING (val))
+ goto error;
+ new_attrs = g_value_dup_string (val);
+ } else {
+ /* unknown caps format */
+ goto error;
+ }
+
+ g_free (dummy->attributes);
+ dummy->attributes = new_attrs;
+ g_free (dummy->direction);
+ dummy->direction = new_direction;
+
+ g_free (field_name);
+ return TRUE;
+
+error:
+ g_free (field_name);
+ g_free (new_direction);
+ g_free (new_attrs);
+ return FALSE;
+}
--- /dev/null
+/* GStreamer RTP header extension unit tests
+ * Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General
+ * Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "gst/gstcaps.h"
+#include "gst/gstvalue.h"
+#include "gst/rtp/gstrtphdrext.h"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/check/check.h>
+#include <gst/rtp/rtp.h>
+
+#include "rtpdummyhdrextimpl.c"
+
+GST_START_TEST (rtp_header_ext_write)
+{
+ GstRTPHeaderExtension *dummy;
+ GstBuffer *buffer;
+ guint8 *data;
+ gsize size, written;
+
+ dummy = rtp_dummy_hdr_ext_new ();
+ gst_rtp_header_extension_set_id (dummy, 1);
+
+ buffer = gst_buffer_new ();
+ size = gst_rtp_header_extension_get_max_size (dummy, buffer);
+ fail_unless (size > 0);
+
+ data = g_malloc0 (size);
+ fail_unless (data != NULL);
+
+ written =
+ gst_rtp_header_extension_write (dummy, buffer, 0, buffer, data, size);
+ fail_unless (written > 0 && written <= size);
+ fail_unless_equals_int (GST_RTP_DUMMY_HDR_EXT (dummy)->write_count, 1);
+
+ fail_unless (gst_rtp_header_extension_read (dummy, 0, data, size, buffer));
+ fail_unless_equals_int (GST_RTP_DUMMY_HDR_EXT (dummy)->read_count, 1);
+
+ g_free (data);
+ gst_buffer_unref (buffer);
+ g_object_unref (dummy);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtp_header_ext_create_from_uri)
+{
+ GstElementFactory *factory;
+ GstRTPHeaderExtension *dummy;
+
+ fail_unless (gst_element_register (NULL, "test-dummyrtphdrext",
+ GST_RANK_MARGINAL, GST_TYPE_RTP_DUMMY_HDR_EXT));
+
+ dummy = gst_rtp_header_extension_create_from_uri (DUMMY_HDR_EXT_URI);
+ fail_unless (GST_IS_RTP_DUMMY_HDR_EXT (dummy));
+
+ factory = gst_element_get_factory (GST_ELEMENT (dummy));
+ gst_registry_remove_feature (gst_registry_get (),
+ GST_PLUGIN_FEATURE (factory));
+ gst_object_unref (dummy);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtp_header_ext_caps_with_attributes)
+{
+ GstRTPHeaderExtension *dummy;
+ GstCaps *caps = gst_caps_new_empty_simple ("application/x-rtp");
+ GstStructure *s = gst_caps_get_structure (caps, 0);
+ const GValue *arr, *val;
+ const gchar *attributes = "attr0 attr1";
+ const gchar *direction = "recvonly";
+
+ dummy = rtp_dummy_hdr_ext_new ();
+
+ gst_rtp_header_extension_set_id (dummy, 1);
+
+ GST_RTP_DUMMY_HDR_EXT (dummy)->direction = g_strdup (direction);
+ GST_RTP_DUMMY_HDR_EXT (dummy)->attributes = g_strdup (attributes);
+
+ fail_unless (gst_rtp_header_extension_set_caps_from_attributes (dummy, caps));
+ fail_unless (gst_structure_has_field_typed (s, "extmap-1", GST_TYPE_ARRAY));
+ arr = gst_structure_get_value (s, "extmap-1");
+ fail_unless (GST_VALUE_HOLDS_ARRAY (arr));
+ fail_unless_equals_int (gst_value_array_get_size (arr), 3);
+ val = gst_value_array_get_value (arr, 0);
+ fail_unless_equals_int (g_strcmp0 (g_value_get_string (val), direction), 0);
+ val = gst_value_array_get_value (arr, 1);
+ fail_unless_equals_int (g_strcmp0 (g_value_get_string (val),
+ gst_rtp_header_extension_get_uri (dummy)), 0);
+ val = gst_value_array_get_value (arr, 2);
+ fail_unless_equals_int (g_strcmp0 (g_value_get_string (val), attributes), 0);
+
+ g_free (GST_RTP_DUMMY_HDR_EXT (dummy)->direction);
+ GST_RTP_DUMMY_HDR_EXT (dummy)->direction = NULL;
+ g_free (GST_RTP_DUMMY_HDR_EXT (dummy)->attributes);
+ GST_RTP_DUMMY_HDR_EXT (dummy)->attributes = NULL;
+
+ fail_unless (gst_rtp_header_extension_set_attributes_from_caps (dummy, caps));
+
+ fail_unless_equals_int (g_strcmp0 (GST_RTP_DUMMY_HDR_EXT (dummy)->attributes,
+ attributes), 0);
+ fail_unless_equals_int (g_strcmp0 (GST_RTP_DUMMY_HDR_EXT (dummy)->direction,
+ direction), 0);
+
+ gst_caps_unref (caps);
+ gst_object_unref (dummy);
+}
+
+GST_END_TEST;
+
+static Suite *
+rtp_header_extension_suite (void)
+{
+ Suite *s = suite_create ("rtp_header_extension_test");
+ TCase *tc_chain = tcase_create ("header extension test");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, rtp_header_ext_write);
+ tcase_add_test (tc_chain, rtp_header_ext_create_from_uri);
+ tcase_add_test (tc_chain, rtp_header_ext_caps_with_attributes);
+
+ return s;
+}
+
+GST_CHECK_MAIN (rtp_header_extension)
[ 'libs/rtp.c' ],
[ 'libs/rtpbasedepayload.c' ],
[ 'libs/rtpbasepayload.c' ],
+ [ 'libs/rtphdrext.c' ],
[ 'libs/rtpmeta.c' ],
[ 'libs/rtsp.c' ],
[ 'libs/sdp.c' ],