},
"rank": "none"
},
+ "rtphdrextrfc6464": {
+ "RTP-Header-Extension-URI": "urn:ietf:params:rtp-hdrext:ssrc-audio-level",
+ "author": "Guillaume Desmottes <guillaume.desmottes@collabora.com>",
+ "description": "Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
+ "hierarchy": [
+ "GstRTPHeaderExtensionRfc6464",
+ "GstRTPHeaderExtension",
+ "GstElement",
+ "GstObject",
+ "GInitiallyUnowned",
+ "GObject"
+ ],
+ "klass": "Network/Extension/RTPHeader",
+ "long-name": "Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
+ "properties": {
+ "vad": {
+ "blurb": "If the vad extension attribute is enabled or not",
+ "conditionally-available": false,
+ "construct": false,
+ "construct-only": false,
+ "controllable": false,
+ "default": "true",
+ "mutable": "null",
+ "readable": true,
+ "type": "gboolean",
+ "writable": false
+ }
+ },
+ "rank": "marginal"
+ },
"rtphdrexttwcc": {
"RTP-Header-Extension-URI": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
"author": "Matthew Waters <matthew@centricular.com>",
--- /dev/null
+/* GStreamer
+ * Copyright (C) <2018> Havard Graff <havard.graff@gmail.com>
+ * Copyright (C) <2020-2021> Guillaume Desmottes <guillaume.desmottes@collabora.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
+ */
+
+/**
+ * SECTION:element-rtphdrextrfc6464
+ * @title: rtphdrextrfc6464
+ * @short_description: Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension
+ *
+ * Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension.
+ * The extension should be automatically created by payloader and depayloaders,
+ * if their `auto-header-extension` property is enabled, if the extension
+ * is part of the RTP caps.
+ *
+ * Since: 1.20
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstrtphdrext-rfc6464.h"
+
+#include <gst/audio/audio.h>
+
+#define RFC6464_HDR_EXT_URI GST_RTP_HDREXT_BASE"ssrc-audio-level"
+
+GST_DEBUG_CATEGORY_STATIC (rtphdrrfc6464_twcc_debug);
+#define GST_CAT_DEFAULT (rtphdrrfc6464_twcc_debug)
+
+#define DEFAULT_VAD TRUE
+
+enum
+{
+ PROP_0,
+ PROP_VAD,
+};
+
+struct _GstRTPHeaderExtensionRfc6464
+{
+ GstRTPHeaderExtension parent;
+
+ gboolean vad;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GstRTPHeaderExtensionRfc6464,
+ gst_rtp_header_extension_rfc6464, GST_TYPE_RTP_HEADER_EXTENSION,
+ GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtphdrextrfc6464", 0,
+ "RTP RFC 6464 Header Extensions");
+ )
+ static void
+ gst_rtp_header_extension_rfc6464_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ GstRTPHeaderExtensionRfc6464 *self =
+ GST_RTP_HEADER_EXTENSION_RFC6464 (object);
+
+ switch (prop_id) {
+ case PROP_VAD:
+ g_value_set_boolean (value, self->vad);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstRTPHeaderExtensionFlags
+gst_rtp_header_extension_rfc6464_get_supported_flags (GstRTPHeaderExtension *
+ ext)
+{
+ return GST_RTP_HEADER_EXTENSION_ONE_BYTE | GST_RTP_HEADER_EXTENSION_TWO_BYTE;
+}
+
+static gsize
+gst_rtp_header_extension_rfc6464_get_max_size (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta)
+{
+ return 2;
+}
+
+static void
+set_vad (GstRTPHeaderExtension * ext, gboolean vad)
+{
+ GstRTPHeaderExtensionRfc6464 *self = GST_RTP_HEADER_EXTENSION_RFC6464 (ext);
+
+ if (self->vad == vad)
+ return;
+
+ GST_DEBUG_OBJECT (ext, "vad: %d", vad);
+ self->vad = vad;
+ g_object_notify (G_OBJECT (self), "vad");
+}
+
+static gboolean
+gst_rtp_header_extension_rfc6464_set_attributes_from_caps (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;
+ 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;
+ }
+ set_vad (ext, DEFAULT_VAD);
+ } 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;
+ const gchar *vad_attr;
+
+ 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, 2);
+ if (!G_VALUE_HOLDS_STRING (val))
+ goto error;
+
+ vad_attr = g_value_get_string (val);
+
+ if (g_str_equal (vad_attr, "vad=on"))
+ set_vad (ext, TRUE);
+ else if (g_str_equal (vad_attr, "vad=off"))
+ set_vad (ext, FALSE);
+ else {
+ GST_WARNING_OBJECT (ext, "Invalid attribute: %s", vad_attr);
+ goto error;
+ }
+ } else {
+ /* unknown caps format */
+ goto error;
+ }
+
+ g_free (field_name);
+ return TRUE;
+
+error:
+ g_free (field_name);
+ return FALSE;
+}
+
+static gboolean
+gst_rtp_header_extension_rfc6464_set_caps_from_attributes (GstRTPHeaderExtension
+ * ext, GstCaps * caps)
+{
+ GstRTPHeaderExtensionRfc6464 *self = GST_RTP_HEADER_EXTENSION_RFC6464 (ext);
+ gchar *field_name = gst_rtp_header_extension_get_sdp_caps_field_name (ext);
+ GstStructure *s = gst_caps_get_structure (caps, 0);
+ GValue arr = G_VALUE_INIT;
+ GValue val = G_VALUE_INIT;
+
+ if (!field_name)
+ return FALSE;
+
+ g_value_init (&arr, GST_TYPE_ARRAY);
+ g_value_init (&val, G_TYPE_STRING);
+
+ /* direction */
+ g_value_set_string (&val, "");
+ 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 */
+ if (self->vad)
+ g_value_set_string (&val, "vad=on");
+ else
+ g_value_set_string (&val, "vad=off");
+ gst_value_array_append_value (&arr, &val);
+
+ gst_structure_set_value (s, field_name, &arr);
+
+ GST_DEBUG_OBJECT (self, "%" GST_PTR_FORMAT, caps);
+
+ g_value_unset (&val);
+ g_value_unset (&arr);
+
+ g_free (field_name);
+ return TRUE;
+}
+
+static gsize
+gst_rtp_header_extension_rfc6464_write (GstRTPHeaderExtension * ext,
+ const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags,
+ GstBuffer * output, guint8 * data, gsize size)
+{
+ GstAudioLevelMeta *meta;
+
+ g_return_val_if_fail (size >=
+ gst_rtp_header_extension_rfc6464_get_max_size (ext, NULL), -1);
+ g_return_val_if_fail (write_flags &
+ gst_rtp_header_extension_rfc6464_get_supported_flags (ext), -1);
+
+ meta = gst_buffer_get_audio_level_meta ((GstBuffer *) input_meta);
+ if (!meta) {
+ GST_LOG_OBJECT (ext, "no meta");
+ return 0;
+ }
+
+ if (meta->level > 127) {
+ GST_WARNING_OBJECT (ext, "level from meta is higher than 127: %d",
+ meta->level);
+ return -1;
+ }
+
+ GST_LOG_OBJECT (ext, "writing ext (level: %d voice: %d)", meta->level,
+ meta->voice_activity);
+
+ if (write_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE) {
+ *data = (meta->level & 0x7F) | (meta->voice_activity << 7);
+ return 1;
+ } else {
+ guint16 payload;
+
+ payload = ((meta->level & 0x7F) | (meta->voice_activity << 7)) << 8;
+ GST_WRITE_UINT16_LE (data, payload);
+ return 2;
+ }
+}
+
+static gboolean
+gst_rtp_header_extension_rfc6464_read (GstRTPHeaderExtension * ext,
+ GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size,
+ GstBuffer * buffer)
+{
+ guint8 val;
+ guint8 level;
+ gboolean voice_activity;
+
+ g_return_val_if_fail (read_flags &
+ gst_rtp_header_extension_rfc6464_get_supported_flags (ext), -1);
+
+ if (read_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE) {
+ val = data[0];
+ } else {
+ val = *((guint16 *) data) >> 8;
+ }
+
+ level = val & 0x7F;
+ voice_activity = (val & 0x80) >> 7;
+
+ GST_LOG_OBJECT (ext, "reading ext (level: %d voice: %d)", level,
+ voice_activity);
+
+ gst_buffer_add_audio_level_meta (buffer, level, voice_activity);
+
+ return TRUE;
+}
+
+static void
+gst_rtp_header_extension_rfc6464_class_init (GstRTPHeaderExtensionRfc6464Class *
+ klass)
+{
+ GstRTPHeaderExtensionClass *rtp_hdr_class;
+ GstElementClass *gstelement_class;
+ GObjectClass *gobject_class;
+
+ rtp_hdr_class = GST_RTP_HEADER_EXTENSION_CLASS (klass);
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = GST_ELEMENT_CLASS (klass);
+
+ gobject_class->get_property = gst_rtp_header_extension_rfc6464_get_property;
+
+ /**
+ * rtphdrextrfc6464:vad:
+ *
+ * If the vad extension attribute is enabled or not, default to %FALSE.
+ *
+ * Since: 1.20
+ */
+ g_object_class_install_property (gobject_class, PROP_VAD,
+ g_param_spec_boolean ("vad", "vad",
+ "If the vad extension attribute is enabled or not",
+ DEFAULT_VAD, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ rtp_hdr_class->get_supported_flags =
+ gst_rtp_header_extension_rfc6464_get_supported_flags;
+ rtp_hdr_class->get_max_size = gst_rtp_header_extension_rfc6464_get_max_size;
+ rtp_hdr_class->set_attributes_from_caps =
+ gst_rtp_header_extension_rfc6464_set_attributes_from_caps;
+ rtp_hdr_class->set_caps_from_attributes =
+ gst_rtp_header_extension_rfc6464_set_caps_from_attributes;
+ rtp_hdr_class->write = gst_rtp_header_extension_rfc6464_write;
+ rtp_hdr_class->read = gst_rtp_header_extension_rfc6464_read;
+
+ gst_element_class_set_static_metadata (gstelement_class,
+ "Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
+ GST_RTP_HDREXT_ELEMENT_CLASS,
+ "Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
+ "Guillaume Desmottes <guillaume.desmottes@collabora.com>");
+ gst_rtp_header_extension_class_set_uri (rtp_hdr_class, RFC6464_HDR_EXT_URI);
+}
+
+static void
+gst_rtp_header_extension_rfc6464_init (GstRTPHeaderExtensionRfc6464 * self)
+{
+ GST_DEBUG_OBJECT (self, "creating element");
+ self->vad = DEFAULT_VAD;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) <2018> Havard Graff <havard.graff@gmail.com>
+ * Copyright (C) <2020-2021> Guillaume Desmottes <guillaume.desmottes@collabora.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
+ */
+
+#ifndef __GST_RTPHDREXT_RFC6464_H__
+#define __GST_RTPHDREXT_RFC6464_H__
+
+#include <gst/gst.h>
+#include <gst/rtp/gstrtphdrext.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_RTP_HEADER_EXTENSION_RFC6464 (gst_rtp_header_extension_rfc6464_get_type())
+
+G_DECLARE_FINAL_TYPE (GstRTPHeaderExtensionRfc6464, gst_rtp_header_extension_rfc6464, GST, RTP_HEADER_EXTENSION_RFC6464, GstRTPHeaderExtension)
+
+G_END_DECLS
+
+#endif /* __GST_RTPHDREXT_RFC6464_H__ */
#include "gstrtpst2022-1-fecdec.h"
#include "gstrtpst2022-1-fecenc.h"
#include "gstrtphdrext-twcc.h"
+#include "gstrtphdrext-rfc6464.h"
static gboolean
plugin_init (GstPlugin * plugin)
GST_TYPE_RTP_HEADER_EXTENSION_TWCC))
return FALSE;
+ if (!gst_element_register (plugin, "rtphdrextrfc6464", GST_RANK_MARGINAL,
+ GST_TYPE_RTP_HEADER_EXTENSION_RFC6464))
+ return FALSE;
+
return TRUE;
}
'gstrtpdtmfmux.c',
'gstrtpjitterbuffer.c',
'gstrtphdrext-twcc.c',
+ 'gstrtphdrext-rfc6464.c',
'gstrtpmux.c',
'gstrtpptdemux.c',
'gstrtprtxqueue.c',
rtpmanager_sources,
c_args : gst_plugins_good_args,
include_directories : [configinc, libsinc],
- dependencies : [gstbase_dep, gstnet_dep, gstrtp_dep, gio_dep],
+ dependencies : [gstbase_dep, gstnet_dep, gstrtp_dep, gstaudio_dep, gio_dep],
install : true,
install_dir : plugins_install_dir,
)
--- /dev/null
+/* GStreamer
+ *
+ * unit test for RTP RFC 6464 Header Extensions
+ *
+ * Copyright (C) <2020-2021> Guillaume Desmottes <guillaume.desmottes@collabora.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/check/gstcheck.h>
+#include <gst/rtp/rtp.h>
+#include <gst/sdp/gstsdpmessage.h>
+#include <gst/audio/audio.h>
+
+#define URN "urn:ietf:params:rtp-hdrext:ssrc-audio-level"
+
+#define SDP "v=0\r\n" \
+ "o=- 123456 2 IN IP4 127.0.0.1 \r\n" \
+ "s=-\r\n" \
+ "t=0 0\r\n" \
+ "a=maxptime:60\r\n" \
+ "a=sendrecv\r\n" \
+ "m=audio 55815 RTP/SAVPF 100\r\n" \
+ "c=IN IP4 1.1.1.1\r\n" \
+ "a=rtpmap:100 opus/48000/2\r\n"
+
+#define SDP_NO_VAD SDP \
+ "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
+#define SDP_VAD_ON SDP \
+ "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level vad=on\r\n"
+#define SDP_VAD_OFF SDP \
+ "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level vad=off\r\n"
+#define SDP_VAD_WRONG SDP \
+ "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level vad=badger\r\n"
+
+static GstCaps *
+create_caps (const gchar * sdp)
+{
+ GstSDPMessage *message;
+ glong length = -1;
+ const GstSDPMedia *media;
+ GstCaps *caps;
+
+ gst_sdp_message_new (&message);
+ gst_sdp_message_parse_buffer ((guint8 *) sdp, length, message);
+ media = gst_sdp_message_get_media (message, 0);
+ fail_unless (media != NULL);
+
+ caps = gst_sdp_media_get_caps_from_media (media, 100);
+ gst_sdp_media_attributes_to_caps (media, caps);
+ gst_sdp_message_free (message);
+ return caps;
+}
+
+static void
+check_caps (GstRTPHeaderExtension * ext, gboolean vad)
+{
+ GstCaps *caps;
+ GstStructure *s;
+ const GValue *arr, *val;
+
+ caps = gst_caps_new_empty_simple ("application/x-rtp");
+ fail_unless (gst_rtp_header_extension_set_caps_from_attributes (ext, caps));
+ s = gst_caps_get_structure (caps, 0);
+
+ arr = gst_structure_get_value (s, "extmap-1");
+ fail_unless (arr != NULL);
+ fail_unless (GST_VALUE_HOLDS_ARRAY (arr));
+ fail_unless (gst_value_array_get_size (arr) == 3);
+
+ val = gst_value_array_get_value (arr, 0);
+ fail_unless_equals_string (g_value_get_string (val), "");
+
+ val = gst_value_array_get_value (arr, 1);
+ fail_unless_equals_string (g_value_get_string (val), URN);
+
+ val = gst_value_array_get_value (arr, 2);
+ if (vad) {
+ fail_unless_equals_string (g_value_get_string (val), "vad=on");
+ } else {
+ fail_unless_equals_string (g_value_get_string (val), "vad=off");
+ }
+
+ gst_caps_unref (caps);
+}
+
+GST_START_TEST (rtprfc6464_sdp)
+{
+ GstRTPHeaderExtension *ext;
+ GstCaps *caps;
+ gboolean vad = FALSE;
+
+ ext = gst_rtp_header_extension_create_from_uri (URN);
+ fail_unless (ext != NULL);
+ gst_rtp_header_extension_set_id (ext, 1);
+
+ /* vad default to on */
+ caps = create_caps (SDP_NO_VAD);
+ fail_unless (gst_rtp_header_extension_set_attributes_from_caps (ext, caps));
+ gst_caps_unref (caps);
+ g_object_get (ext, "vad", &vad, NULL);
+ fail_unless (vad);
+ check_caps (ext, TRUE);
+
+ /* vad is disabled */
+ caps = create_caps (SDP_VAD_OFF);
+ fail_unless (gst_rtp_header_extension_set_attributes_from_caps (ext, caps));
+ gst_caps_unref (caps);
+ g_object_get (ext, "vad", &vad, NULL);
+ fail_if (vad);
+
+ /* vad is enabled */
+ caps = create_caps (SDP_VAD_ON);
+ fail_unless (gst_rtp_header_extension_set_attributes_from_caps (ext, caps));
+ gst_caps_unref (caps);
+ g_object_get (ext, "vad", &vad, NULL);
+ fail_unless (vad);
+
+ /* invalid vad */
+ caps = create_caps (SDP_VAD_WRONG);
+ fail_if (gst_rtp_header_extension_set_attributes_from_caps (ext, caps));
+ gst_caps_unref (caps);
+
+ gst_object_unref (ext);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtprfc6464_one_byte)
+{
+ GstRTPHeaderExtension *ext;
+ GstRTPHeaderExtensionFlags flags;
+ GstBuffer *buffer;
+ guint8 *data;
+ gsize size, written;
+ GstAudioLevelMeta *meta;
+ guint8 level = 12;
+ gboolean voice = TRUE;
+
+ ext = gst_rtp_header_extension_create_from_uri (URN);
+ fail_unless (ext != NULL);
+ gst_rtp_header_extension_set_id (ext, 1);
+
+ flags = gst_rtp_header_extension_get_supported_flags (ext);
+ fail_unless (flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE);
+
+ buffer = gst_buffer_new ();
+ meta = gst_buffer_add_audio_level_meta (buffer, level, voice);
+
+ size = gst_rtp_header_extension_get_max_size (ext, buffer);
+ fail_unless (size > 0);
+ data = g_malloc0 (size);
+ fail_unless (data != NULL);
+
+ /* Write extension */
+ written =
+ gst_rtp_header_extension_write (ext, buffer,
+ GST_RTP_HEADER_EXTENSION_ONE_BYTE, buffer, data, size);
+ fail_unless (written == 1);
+
+ /* Read it back */
+ fail_unless (gst_buffer_remove_meta (buffer, (GstMeta *) meta));
+ fail_unless (gst_rtp_header_extension_read (ext,
+ GST_RTP_HEADER_EXTENSION_ONE_BYTE, data, size, buffer));
+ meta = gst_buffer_get_audio_level_meta (buffer);
+ fail_unless (meta != NULL);
+ fail_unless_equals_int (meta->level, level);
+ fail_unless (meta->voice_activity == voice);
+
+ g_free (data);
+ gst_buffer_unref (buffer);
+ gst_object_unref (ext);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtprfc6464_two_bytes)
+{
+ GstRTPHeaderExtension *ext;
+ GstRTPHeaderExtensionFlags flags;
+ GstBuffer *buffer;
+ guint8 *data;
+ gsize size, written;
+ GstAudioLevelMeta *meta;
+ guint8 level = 12;
+ gboolean voice = TRUE;
+
+ ext = gst_rtp_header_extension_create_from_uri (URN);
+ fail_unless (ext != NULL);
+ gst_rtp_header_extension_set_id (ext, 1);
+
+ flags = gst_rtp_header_extension_get_supported_flags (ext);
+ fail_unless (flags & GST_RTP_HEADER_EXTENSION_TWO_BYTE);
+
+ buffer = gst_buffer_new ();
+ meta = gst_buffer_add_audio_level_meta (buffer, level, voice);
+
+ size = gst_rtp_header_extension_get_max_size (ext, buffer);
+ fail_unless (size > 0);
+ data = g_malloc0 (size);
+ fail_unless (data != NULL);
+
+ /* Write extension */
+ written =
+ gst_rtp_header_extension_write (ext, buffer,
+ GST_RTP_HEADER_EXTENSION_TWO_BYTE, buffer, data, size);
+ fail_unless (written == 2);
+
+ /* Read it back */
+ fail_unless (gst_buffer_remove_meta (buffer, (GstMeta *) meta));
+ fail_unless (gst_rtp_header_extension_read (ext,
+ GST_RTP_HEADER_EXTENSION_TWO_BYTE, data, size, buffer));
+ meta = gst_buffer_get_audio_level_meta (buffer);
+ fail_unless (meta != NULL);
+ fail_unless_equals_int (meta->level, level);
+ fail_unless (meta->voice_activity == voice);
+
+ g_free (data);
+ gst_buffer_unref (buffer);
+ gst_object_unref (ext);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtprfc6464_no_meta)
+{
+ GstRTPHeaderExtension *ext;
+ GstBuffer *buffer;
+ guint8 *data;
+ gsize size, written;
+
+ ext = gst_rtp_header_extension_create_from_uri (URN);
+ fail_unless (ext != NULL);
+ gst_rtp_header_extension_set_id (ext, 1);
+
+ buffer = gst_buffer_new ();
+
+ size = gst_rtp_header_extension_get_max_size (ext, buffer);
+ fail_unless (size > 0);
+ data = g_malloc0 (size);
+ fail_unless (data != NULL);
+
+ written =
+ gst_rtp_header_extension_write (ext, buffer,
+ GST_RTP_HEADER_EXTENSION_ONE_BYTE, buffer, data, size);
+ fail_unless (written == 0);
+
+ written =
+ gst_rtp_header_extension_write (ext, buffer,
+ GST_RTP_HEADER_EXTENSION_TWO_BYTE, buffer, data, size);
+ fail_unless (written == 0);
+
+ g_free (data);
+ gst_buffer_unref (buffer);
+ gst_object_unref (ext);
+}
+
+GST_END_TEST;
+
+static Suite *
+rtprfc6464_suite (void)
+{
+ Suite *s = suite_create ("rtprfc6464");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, rtprfc6464_sdp);
+ tcase_add_test (tc_chain, rtprfc6464_one_byte);
+ tcase_add_test (tc_chain, rtprfc6464_two_bytes);
+ tcase_add_test (tc_chain, rtprfc6464_no_meta);
+
+ return s;
+}
+
+GST_CHECK_MAIN (rtprfc6464)
[ 'elements/rtpbin_buffer_list' ],
[ 'elements/rtpcollision' ],
[ 'elements/rtpfunnel' ],
+ [ 'elements/rtphdrextrfc6464', false, [gstsdp_dep, gstaudio_dep] ],
[ 'elements/rtpjitterbuffer' ],
[ 'elements/rtpjpeg' ],