rtp/basedepayload: implement support for rtp header extensions

New signals are added for managing the internal list of rtp header
extension implementations read by a specific depayloader instance.

If the 'extmap-$NUM' field is present in the sink caps, then an
extension implementation will be requested but is not requited to be
able to negotiate correctly.  An extension will be requested using the
'request-extension' signal if none could be found internally.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/748>
This commit is contained in:
Matthew Waters 2020-07-10 15:30:57 +10:00 committed by GStreamer Merge Bot
parent 427c3f4442
commit 092ea647bb
2 changed files with 626 additions and 4 deletions

View file

@ -31,6 +31,7 @@
#include "gstrtpbasedepayload.h"
#include "gstrtpmeta.h"
#include "gstrtphdrext.h"
GST_DEBUG_CATEGORY_STATIC (rtpbasedepayload_debug);
#define GST_CAT_DEFAULT (rtpbasedepayload_debug)
@ -65,15 +66,23 @@ struct _GstRTPBaseDepayloadPrivate
GstBuffer *input_buffer;
GstFlowReturn process_flow_ret;
/* array of GstRTPHeaderExtension's * */
GPtrArray *header_exts;
};
/* Filter signals and args */
enum
{
/* FILL ME */
SIGNAL_0,
SIGNAL_REQUEST_EXTENSION,
SIGNAL_ADD_EXTENSION,
SIGNAL_CLEAR_EXTENSIONS,
LAST_SIGNAL
};
static guint gst_rtp_base_depayload_signals[LAST_SIGNAL] = { 0 };
#define DEFAULT_SOURCE_INFO FALSE
#define DEFAULT_MAX_REORDER 100
@ -117,6 +126,11 @@ static void gst_rtp_base_depayload_init (GstRTPBaseDepayload * rtpbasepayload,
static GstEvent *create_segment_event (GstRTPBaseDepayload * filter,
guint rtptime, GstClockTime position);
static void gst_rtp_base_depayload_add_extension (GstRTPBaseDepayload *
rtpbasepayload, GstRTPHeaderExtension * ext);
static void gst_rtp_base_depayload_clear_extensions (GstRTPBaseDepayload *
rtpbasepayload);
GType
gst_rtp_base_depayload_get_type (void)
{
@ -223,6 +237,54 @@ gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass * klass)
"Max seqnum reorder before assuming sender has restarted",
0, G_MAXINT, DEFAULT_MAX_REORDER, G_PARAM_READWRITE));
/**
* GstRTPBaseDepayload::request-extension:
* @object: the #GstRTPBaseDepayload
* @ext_id: the extension id being requested
* @ext_uri: (nullable): the extension URI being requested
*
* The returned @ext must be configured with the correct @ext_id and with the
* necessary attributes as required by the extension implementation.
*
* Returns: (transfer full): the #GstRTPHeaderExtension for @ext_id, or %NULL
*
* Since: 1.20
*/
gst_rtp_base_depayload_signals[SIGNAL_REQUEST_EXTENSION] =
g_signal_new_class_handler ("request-extension",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL,
GST_TYPE_RTP_HEADER_EXTENSION, 2, G_TYPE_UINT, G_TYPE_STRING);
/**
* GstRTPBaseDepayload::add-extension:
* @object: the #GstRTPBaseDepayload
* @ext: (transfer full): the #GstRTPHeaderExtension
*
* Add @ext as an extension for reading part of an RTP header extension from
* incoming RTP packets.
*
* Since: 1.20
*/
gst_rtp_base_depayload_signals[SIGNAL_ADD_EXTENSION] =
g_signal_new_class_handler ("add-extension", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (gst_rtp_base_depayload_add_extension), NULL, NULL, NULL,
G_TYPE_NONE, 1, GST_TYPE_RTP_HEADER_EXTENSION);
/**
* GstRTPBaseDepayload::clear-extensions:
* @object: the #GstRTPBaseDepayload
*
* Clear all RTP header extensions used by this depayloader.
*
* Since: 1.20
*/
gst_rtp_base_depayload_signals[SIGNAL_ADD_EXTENSION] =
g_signal_new_class_handler ("clear-extensions", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (gst_rtp_base_depayload_clear_extensions), NULL, NULL, NULL,
G_TYPE_NONE, 0);
gstelement_class->change_state = gst_rtp_base_depayload_change_state;
klass->packet_lost = gst_rtp_base_depayload_packet_lost;
@ -276,20 +338,46 @@ gst_rtp_base_depayload_init (GstRTPBaseDepayload * filter,
priv->max_reorder = DEFAULT_MAX_REORDER;
gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED);
priv->header_exts =
g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
}
static void
gst_rtp_base_depayload_finalize (GObject * object)
{
GstRTPBaseDepayload *rtpbasedepayload = GST_RTP_BASE_DEPAYLOAD (object);
g_ptr_array_unref (rtpbasedepayload->priv->header_exts);
rtpbasedepayload->priv->header_exts = NULL;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
add_and_ref_item (GstRTPHeaderExtension * ext, GPtrArray * ret)
{
g_ptr_array_add (ret, gst_object_ref (ext));
}
static void
remove_item_from (GstRTPHeaderExtension * ext, GPtrArray * ret)
{
g_ptr_array_remove_fast (ret, ext);
}
static void
add_item_to (GstRTPHeaderExtension * ext, GPtrArray * ret)
{
g_ptr_array_add (ret, ext);
}
static gboolean
gst_rtp_base_depayload_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps)
{
GstRTPBaseDepayloadClass *bclass;
GstRTPBaseDepayloadPrivate *priv;
gboolean res;
gboolean res = TRUE;
GstStructure *caps_struct;
const GValue *value;
@ -355,6 +443,132 @@ gst_rtp_base_depayload_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps)
else
priv->clock_base = -1;
{
/* ensure we have header extension implementations for the list in the
* caps */
guint i, j, n_fields = gst_structure_n_fields (caps_struct);
GPtrArray *header_exts = g_ptr_array_new_with_free_func (gst_object_unref);
GPtrArray *to_add = g_ptr_array_new ();
GPtrArray *to_remove = g_ptr_array_new ();
GST_OBJECT_LOCK (filter);
g_ptr_array_foreach (filter->priv->header_exts,
(GFunc) add_and_ref_item, header_exts);
GST_OBJECT_UNLOCK (filter);
for (i = 0; i < n_fields; i++) {
const gchar *field_name = gst_structure_nth_field_name (caps_struct, i);
if (g_str_has_prefix (field_name, "extmap-")) {
const GValue *val;
const gchar *uri = NULL;
gchar *nptr;
guint64 ext_id;
GstRTPHeaderExtension *ext = NULL;
errno = 0;
ext_id = g_ascii_strtoull (&field_name[strlen ("extmap-")], &nptr, 10);
if (errno != 0 || (ext_id == 0 && field_name == nptr)) {
GST_WARNING_OBJECT (filter, "could not parse id from %s", field_name);
res = FALSE;
goto ext_out;
}
val = gst_structure_get_value (caps_struct, field_name);
if (G_VALUE_HOLDS_STRING (val)) {
uri = g_value_get_string (val);
} else if (GST_VALUE_HOLDS_ARRAY (val)) {
/* the uri is the second value in the array */
const GValue *str = gst_value_array_get_value (val, 1);
if (G_VALUE_HOLDS_STRING (str)) {
uri = g_value_get_string (str);
}
}
if (!uri) {
GST_WARNING_OBJECT (filter, "could not get extmap uri for "
"field %s", field_name);
res = FALSE;
goto ext_out;
}
/* try to find if this extension mapping already exists */
for (j = 0; j < header_exts->len; j++) {
ext = g_ptr_array_index (header_exts, j);
if (gst_rtp_header_extension_get_id (ext) == ext_id) {
if (g_strcmp0 (uri, gst_rtp_header_extension_get_uri (ext)) == 0) {
/* still matching, we're good, set attributes from caps in case
* the caps have changed */
if (!gst_rtp_header_extension_set_attributes_from_caps (ext,
caps)) {
GST_WARNING_OBJECT (filter,
"Failed to configure rtp header " "extension %"
GST_PTR_FORMAT " attributes from caps %" GST_PTR_FORMAT,
ext, caps);
res = FALSE;
goto ext_out;
}
break;
} else {
GST_DEBUG_OBJECT (filter, "extension id %" G_GUINT64_FORMAT
"was replaced with a different extension uri "
"original:\'%s' vs \'%s\'", ext_id,
gst_rtp_header_extension_get_uri (ext), uri);
g_ptr_array_add (to_remove, ext);
ext = NULL;
break;
}
} else {
ext = NULL;
}
}
/* if no extension, attempt to request one */
if (!ext) {
GST_DEBUG_OBJECT (filter, "requesting extension for id %"
G_GUINT64_FORMAT " and uri %s", ext_id, uri);
g_signal_emit (filter,
gst_rtp_base_depayload_signals[SIGNAL_REQUEST_EXTENSION], 0,
ext_id, uri, &ext);
GST_DEBUG_OBJECT (filter, "request returned extension %p \'%s\' "
"for id %" G_GUINT64_FORMAT " and uri %s", ext,
ext ? GST_OBJECT_NAME (ext) : "", ext_id, uri);
if (ext && gst_rtp_header_extension_get_id (ext) != ext_id) {
g_warning ("\'request-extension\' signal provided an rtp header "
"extension for uri \'%s\' that does not match the requested "
"extension id %" G_GUINT64_FORMAT, uri, ext_id);
gst_clear_object (&ext);
}
/* it is the signal handler's responsibility to set attributes if
* required */
/* We don't create an extension implementation by default and require
* the caller to set the appropriate extension if it's required */
if (ext)
g_ptr_array_add (to_add, ext);
}
}
}
/* Note: we intentionally don't remove extensions that are not listed
* in caps */
GST_OBJECT_LOCK (filter);
g_ptr_array_foreach (to_remove, (GFunc) remove_item_from,
filter->priv->header_exts);
g_ptr_array_foreach (to_add, (GFunc) add_item_to,
filter->priv->header_exts);
GST_OBJECT_UNLOCK (filter);
ext_out:
g_ptr_array_unref (to_add);
g_ptr_array_unref (to_remove);
g_ptr_array_unref (header_exts);
if (!res)
return res;
}
if (bclass->set_caps) {
res = bclass->set_caps (filter, caps);
if (!res) {
@ -834,6 +1048,141 @@ add_rtp_source_meta (GstBuffer * outbuf, GstBuffer * rtpbuf)
gst_rtp_buffer_unmap (&rtp);
}
static void
gst_rtp_base_depayload_add_extension (GstRTPBaseDepayload * rtpbasepayload,
GstRTPHeaderExtension * ext)
{
g_return_if_fail (GST_IS_RTP_HEADER_EXTENSION (ext));
g_return_if_fail (gst_rtp_header_extension_get_id (ext) > 0);
/* XXX: check for duplicate ids? */
GST_OBJECT_LOCK (rtpbasepayload);
g_ptr_array_add (rtpbasepayload->priv->header_exts, gst_object_ref (ext));
GST_OBJECT_UNLOCK (rtpbasepayload);
}
static void
gst_rtp_base_depayload_clear_extensions (GstRTPBaseDepayload * rtpbasepayload)
{
GST_OBJECT_LOCK (rtpbasepayload);
g_ptr_array_set_size (rtpbasepayload->priv->header_exts, 0);
GST_OBJECT_UNLOCK (rtpbasepayload);
}
static void
read_rtp_header_extensions (GstRTPBaseDepayload * depayload,
GstBuffer * input, GstBuffer * output)
{
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
guint16 bit_pattern;
guint8 *pdata;
guint wordlen;
if (!input) {
GST_DEBUG_OBJECT (depayload, "no input buffer");
return;
}
if (!gst_rtp_buffer_map (input, GST_MAP_READ, &rtp)) {
GST_WARNING_OBJECT (depayload, "Failed to map buffer");
return;
}
if (gst_rtp_buffer_get_extension_data (&rtp, &bit_pattern, (gpointer) & pdata,
&wordlen)) {
GstRTPHeaderExtensionFlags ext_flags = 0;
gsize bytelen = wordlen * 4;
guint hdr_unit_bytes;
gsize offset = 0;
if (bit_pattern == 0xBEDE) {
/* one byte extensions */
hdr_unit_bytes = 1;
ext_flags |= GST_RTP_HEADER_EXTENSION_ONE_BYTE;
} else if (bit_pattern >> 4 == 0x100) {
/* two byte extensions */
hdr_unit_bytes = 2;
ext_flags |= GST_RTP_HEADER_EXTENSION_TWO_BYTE;
} else {
GST_DEBUG_OBJECT (depayload, "unknown extension bit pattern 0x%02x%02x",
bit_pattern >> 8, bit_pattern & 0xff);
goto out;
}
while (TRUE) {
guint8 read_id, read_len;
GstRTPHeaderExtension *ext = NULL;
guint i;
if (offset + hdr_unit_bytes >= bytelen)
/* not enough remaning data */
break;
if (ext_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE) {
read_id = GST_READ_UINT8 (pdata + offset) >> 4;
read_len = (GST_READ_UINT8 (pdata + offset) & 0x0F) + 1;
offset += 1;
if (read_id == 0)
/* padding */
continue;
if (read_id == 15)
/* special id for possible future expansion */
break;
} else {
read_id = GST_READ_UINT8 (pdata + offset);
offset += 1;
if (read_id == 0)
/* padding */
continue;
read_len = GST_READ_UINT8 (pdata + offset);
offset += 1;
}
GST_TRACE_OBJECT (depayload, "found rtp header extension with id %u and "
"length %u", read_id, read_len);
/* Ignore extension headers where the size does not fit */
if (offset + read_len > bytelen) {
GST_WARNING_OBJECT (depayload, "Extension length extends past the "
"size of the extension data");
break;
}
GST_OBJECT_LOCK (depayload);
for (i = 0; i < depayload->priv->header_exts->len; i++) {
ext = g_ptr_array_index (depayload->priv->header_exts, i);
if (read_id == gst_rtp_header_extension_get_id (ext)) {
gst_object_ref (ext);
break;
}
ext = NULL;
}
if (ext) {
if (!gst_rtp_header_extension_read (ext, ext_flags, &pdata[offset],
read_len, output)) {
GST_WARNING_OBJECT (depayload, "RTP header extension (%s) could "
"not read payloaded data", GST_OBJECT_NAME (ext));
GST_OBJECT_UNLOCK (ext);
gst_object_unref (ext);
goto out;
}
gst_object_unref (ext);
}
GST_OBJECT_UNLOCK (depayload);
offset += read_len;
}
}
out:
gst_rtp_buffer_unmap (&rtp);
}
static gboolean
set_headers (GstBuffer ** buffer, guint idx, GstRTPBaseDepayload * depayload)
{
@ -866,8 +1215,12 @@ set_headers (GstBuffer ** buffer, guint idx, GstRTPBaseDepayload * depayload)
priv->dts = GST_CLOCK_TIME_NONE;
priv->duration = GST_CLOCK_TIME_NONE;
if (priv->source_info && priv->input_buffer)
add_rtp_source_meta (*buffer, priv->input_buffer);
if (priv->input_buffer) {
if (priv->source_info)
add_rtp_source_meta (*buffer, priv->input_buffer);
read_rtp_header_extensions (depayload, priv->input_buffer, *buffer);
}
return TRUE;
}

View file

@ -26,6 +26,8 @@
#include <gst/check/gstharness.h>
#include <gst/rtp/rtp.h>
#include "rtpdummyhdrextimpl.c"
#define DEFAULT_CLOCK_RATE (42)
/* GstRtpDummyDepay */
@ -392,6 +394,25 @@ rtp_buffer_set_valist (GstBuffer * buf, const gchar * field, va_list var_args,
guint idx = va_arg (var_args, guint);
guint csrc = va_arg (var_args, guint);
gst_rtp_buffer_set_csrc (&rtp, idx, csrc);
} else if (g_str_has_prefix (field, "hdrext-")) {
GstRTPHeaderExtension *ext = va_arg (var_args, GstRTPHeaderExtension *);
guint id = gst_rtp_header_extension_get_id (ext);
gsize size = gst_rtp_header_extension_get_max_size (ext, buf);
guint8 *data = g_malloc0 (size);
if (!g_strcmp0 (field, "hdrext-1")) {
fail_unless (gst_rtp_header_extension_write (ext, buf,
GST_RTP_HEADER_EXTENSION_ONE_BYTE, buf, data, size) > 0);
fail_unless (gst_rtp_buffer_add_extension_onebyte_header (&rtp, id,
data, size));
} else if (!g_strcmp0 (field, "hdrext-2")) {
fail_unless (gst_rtp_header_extension_write (ext, buf,
GST_RTP_HEADER_EXTENSION_TWO_BYTE, buf, data, size) > 0);
fail_unless (gst_rtp_buffer_add_extension_twobytes_header (&rtp, 0,
id, data, size));
}
g_free (data);
} else {
fail ("test cannot set unknown buffer field '%s'", field);
}
@ -1540,6 +1561,247 @@ GST_START_TEST (rtp_base_depayload_flow_return_push_list_func)
GST_END_TEST;
GST_START_TEST (rtp_base_depayload_one_byte_hdr_ext)
{
GstRTPHeaderExtension *ext;
State *state;
state = create_depayloader ("application/x-rtp", NULL);
ext = rtp_dummy_hdr_ext_new ();
gst_rtp_header_extension_set_id (ext, 1);
GST_RTP_DUMMY_DEPAY (state->element)->push_method =
GST_RTP_DUMMY_RETURN_TO_PUSH;
g_signal_emit_by_name (state->element, "add-extension", ext);
set_state (state, GST_STATE_PLAYING);
push_rtp_buffer (state, "pts", 0 * GST_SECOND,
"rtptime", G_GUINT64_CONSTANT (0x1234), "seq", 0x4242, "hdrext-1", ext,
NULL);
set_state (state, GST_STATE_NULL);
validate_buffers_received (1);
validate_buffer (0, "pts", 0 * GST_SECOND, "discont", FALSE, NULL);
fail_unless_equals_int (GST_RTP_DUMMY_HDR_EXT (ext)->read_count, 1);
gst_object_unref (ext);
destroy_depayloader (state);
}
GST_END_TEST;
GST_START_TEST (rtp_base_depayload_two_byte_hdr_ext)
{
GstRTPHeaderExtension *ext;
State *state;
state = create_depayloader ("application/x-rtp", NULL);
ext = rtp_dummy_hdr_ext_new ();
gst_rtp_header_extension_set_id (ext, 1);
GST_RTP_DUMMY_DEPAY (state->element)->push_method =
GST_RTP_DUMMY_RETURN_TO_PUSH;
g_signal_emit_by_name (state->element, "add-extension", ext);
set_state (state, GST_STATE_PLAYING);
push_rtp_buffer (state, "pts", 0 * GST_SECOND,
"rtptime", G_GUINT64_CONSTANT (0x1234), "seq", 0x4242, "hdrext-2", ext,
NULL);
set_state (state, GST_STATE_NULL);
validate_buffers_received (1);
validate_buffer (0, "pts", 0 * GST_SECOND, "discont", FALSE, NULL);
fail_unless_equals_int (GST_RTP_DUMMY_HDR_EXT (ext)->read_count, 1);
gst_object_unref (ext);
destroy_depayloader (state);
}
GST_END_TEST;
static GstRTPHeaderExtension *
request_extension (GstRTPBaseDepayload * depayload, guint ext_id,
const gchar * ext_uri, gpointer user_data)
{
GstRTPHeaderExtension *ext = user_data;
if (ext && gst_rtp_header_extension_get_id (ext) == ext_id
&& g_strcmp0 (ext_uri, gst_rtp_header_extension_get_uri (ext)) == 0)
return gst_object_ref (ext);
return NULL;
}
GST_START_TEST (rtp_base_depayload_request_extension)
{
GstRTPHeaderExtension *ext;
State *state;
state =
create_depayloader ("application/x-rtp,extmap-3=(string)"
DUMMY_HDR_EXT_URI, NULL);
ext = rtp_dummy_hdr_ext_new ();
gst_rtp_header_extension_set_id (ext, 3);
GST_RTP_DUMMY_DEPAY (state->element)->push_method =
GST_RTP_DUMMY_RETURN_TO_PUSH;
g_signal_connect (state->element, "request-extension",
G_CALLBACK (request_extension), ext);
set_state (state, GST_STATE_PLAYING);
push_rtp_buffer (state, "pts", 0 * GST_SECOND,
"rtptime", G_GUINT64_CONSTANT (0x1234), "seq", 0x4242, "hdrext-1", ext,
NULL);
set_state (state, GST_STATE_NULL);
validate_buffers_received (1);
validate_buffer (0, "pts", 0 * GST_SECOND, "discont", FALSE, NULL);
fail_unless_equals_int (GST_RTP_DUMMY_HDR_EXT (ext)->read_count, 1);
gst_object_unref (ext);
destroy_depayloader (state);
}
GST_END_TEST;
GST_START_TEST (rtp_base_depayload_clear_extensions)
{
GstRTPHeaderExtension *ext;
State *state;
state = create_depayloader ("application/x-rtp", NULL);
ext = rtp_dummy_hdr_ext_new ();
gst_rtp_header_extension_set_id (ext, 1);
GST_RTP_DUMMY_DEPAY (state->element)->push_method =
GST_RTP_DUMMY_RETURN_TO_PUSH;
g_signal_emit_by_name (state->element, "add-extension", ext);
set_state (state, GST_STATE_PLAYING);
push_rtp_buffer (state, "pts", 0 * GST_SECOND,
"rtptime", G_GUINT64_CONSTANT (0x1234), "seq", 0x4242, "hdrext-1", ext,
NULL);
g_signal_emit_by_name (state->element, "clear-extensions");
push_rtp_buffer (state, "pts", 1 * GST_SECOND,
"rtptime", G_GUINT64_CONSTANT (0x1234) + 1 * DEFAULT_CLOCK_RATE,
"seq", 0x4242 + 1, "hdrext-1", ext, NULL);
set_state (state, GST_STATE_NULL);
validate_buffers_received (2);
validate_buffer (0, "pts", 0 * GST_SECOND, "discont", FALSE, NULL);
validate_buffer (1, "pts", 1 * GST_SECOND, "discont", FALSE, NULL);
fail_unless_equals_int (GST_RTP_DUMMY_HDR_EXT (ext)->read_count, 1);
gst_object_unref (ext);
destroy_depayloader (state);
}
GST_END_TEST;
GST_START_TEST (rtp_base_depayload_multiple_exts)
{
GstRTPHeaderExtension *ext1;
GstRTPHeaderExtension *ext2;
State *state;
state = create_depayloader ("application/x-rtp", NULL);
ext1 = rtp_dummy_hdr_ext_new ();
gst_rtp_header_extension_set_id (ext1, 1);
ext2 = rtp_dummy_hdr_ext_new ();
gst_rtp_header_extension_set_id (ext2, 2);
GST_RTP_DUMMY_DEPAY (state->element)->push_method =
GST_RTP_DUMMY_RETURN_TO_PUSH;
g_signal_emit_by_name (state->element, "add-extension", ext1);
g_signal_emit_by_name (state->element, "add-extension", ext2);
set_state (state, GST_STATE_PLAYING);
push_rtp_buffer (state, "pts", 0 * GST_SECOND,
"rtptime", G_GUINT64_CONSTANT (0x1234), "seq", 0x4242, "hdrext-1", ext1,
"hdrext-1", ext2, NULL);
set_state (state, GST_STATE_NULL);
validate_buffers_received (1);
validate_buffer (0, "pts", 0 * GST_SECOND, "discont", FALSE, NULL);
fail_unless_equals_int (GST_RTP_DUMMY_HDR_EXT (ext1)->read_count, 1);
fail_unless_equals_int (GST_RTP_DUMMY_HDR_EXT (ext2)->read_count, 1);
gst_object_unref (ext1);
gst_object_unref (ext2);
destroy_depayloader (state);
}
GST_END_TEST;
static GstRTPHeaderExtension *
request_extension_ignored (GstRTPBaseDepayload * depayload, guint ext_id,
const gchar * ext_uri, gpointer user_data)
{
guint *request_counter = user_data;
*request_counter += 1;
return NULL;
}
GST_START_TEST (rtp_base_depayload_caps_request_ignored)
{
State *state;
guint request_counter = 0;
state =
create_depayloader ("application/x-rtp,extmap-3=(string)"
DUMMY_HDR_EXT_URI, NULL);
GST_RTP_DUMMY_DEPAY (state->element)->push_method =
GST_RTP_DUMMY_RETURN_TO_PUSH;
g_signal_connect (state->element, "request-extension",
G_CALLBACK (request_extension_ignored), &request_counter);
set_state (state, GST_STATE_PLAYING);
push_rtp_buffer (state,
"pts", 0 * GST_SECOND,
"rtptime", G_GUINT64_CONSTANT (0x1234), "seq", 0x4242, NULL);
fail_unless_equals_int (request_counter, 1);
set_state (state, GST_STATE_NULL);
validate_buffers_received (1);
destroy_depayloader (state);
}
GST_END_TEST;
static Suite *
rtp_basepayloading_suite (void)
{
@ -1577,6 +1839,13 @@ rtp_basepayloading_suite (void)
tcase_add_test (tc_chain, rtp_base_depayload_flow_return_push_func);
tcase_add_test (tc_chain, rtp_base_depayload_flow_return_push_list_func);
tcase_add_test (tc_chain, rtp_base_depayload_one_byte_hdr_ext);
tcase_add_test (tc_chain, rtp_base_depayload_two_byte_hdr_ext);
tcase_add_test (tc_chain, rtp_base_depayload_request_extension);
tcase_add_test (tc_chain, rtp_base_depayload_clear_extensions);
tcase_add_test (tc_chain, rtp_base_depayload_multiple_exts);
tcase_add_test (tc_chain, rtp_base_depayload_caps_request_ignored);
return s;
}