From 656af79130d65d3120c173e9f7ba1ef090d7f926 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Fri, 10 Jul 2020 15:36:54 +1000 Subject: [PATCH] rtpmanager: update for rtp header extensions Provide an implementation of the transport-wide-cc header extension and use it in rtpfunnel. Part-of: --- docs/gst_plugins_cache.json | 32 +++++ gst/rtpmanager/gstrtpfunnel.c | 74 +++++++----- gst/rtpmanager/gstrtphdrext-twcc.c | 232 +++++++++++++++++++++++++++++++++++++ gst/rtpmanager/gstrtphdrext-twcc.h | 82 +++++++++++++ gst/rtpmanager/gstrtpmanager.c | 5 + gst/rtpmanager/meson.build | 1 + 6 files changed, 394 insertions(+), 32 deletions(-) create mode 100644 gst/rtpmanager/gstrtphdrext-twcc.c create mode 100644 gst/rtpmanager/gstrtphdrext-twcc.h diff --git a/docs/gst_plugins_cache.json b/docs/gst_plugins_cache.json index a2cd19b..39017ab 100644 --- a/docs/gst_plugins_cache.json +++ b/docs/gst_plugins_cache.json @@ -17001,6 +17001,38 @@ }, "rank": "none" }, + "rtphdrexttwcc": { + "RTP-Header-Extension-URI": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01", + "author": "Matthew Waters ", + "description": "Extends RTP packets to add sequence number transport wide.", + "hierarchy": [ + "GstRTPHeaderExtensionTWCC", + "GstRTPHeaderExtension", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Network/Extension/RTPHeader", + "long-name": "Transport Wide Congestion Control", + "properties": { + "n-streams": { + "blurb": "The number of separate RTP streams this header applies to", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "1", + "max": "-1", + "min": "1", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + } + }, + "rank": "marginal" + }, "rtpjitterbuffer": { "author": "Philippe Kalaf , Wim Taymans ", "description": "A buffer that deals with network jitter and other transmission faults", diff --git a/gst/rtpmanager/gstrtpfunnel.c b/gst/rtpmanager/gstrtpfunnel.c index 6bd86b0..5e30487 100644 --- a/gst/rtpmanager/gstrtpfunnel.c +++ b/gst/rtpmanager/gstrtpfunnel.c @@ -67,14 +67,13 @@ #endif #include +#include #include "gstrtpfunnel.h" GST_DEBUG_CATEGORY_STATIC (gst_rtp_funnel_debug); #define GST_CAT_DEFAULT gst_rtp_funnel_debug -#define TWCC_EXTMAP_STR "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01" - /**************** GstRTPFunnelPad ****************/ struct _GstRtpFunnelPadClass @@ -127,9 +126,8 @@ struct _GstRtpFunnel /* The last pad data was chained on */ GstPad *current_pad; - guint8 twcc_ext_id; /* the negotiated twcc extmap id */ - guint16 twcc_seqnum; /* our internal twcc seqnum */ guint twcc_pads; /* numer of sinkpads with negotiated twcc */ + GstRTPHeaderExtension *twcc_ext; /* properties */ gint common_ts_offset; @@ -222,36 +220,40 @@ gst_rtp_funnel_set_twcc_seqnum (GstRtpFunnel * funnel, GstPad * pad, GstBuffer ** buf) { GstRtpFunnelPad *fpad = GST_RTP_FUNNEL_PAD_CAST (pad); + guint8 twcc_seq[2] = { 0, }; GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + guint ext_id = gst_rtp_header_extension_get_id (funnel->twcc_ext); + guint8 *existing; + guint size; - if (!funnel->twcc_ext_id || !fpad->has_twcc) + if (!funnel->twcc_ext || !fpad->has_twcc) return; *buf = gst_buffer_make_writable (*buf); - if (gst_rtp_buffer_map (*buf, GST_MAP_READWRITE, &rtp)) { - gpointer data; + gst_rtp_header_extension_write (funnel->twcc_ext, *buf, + GST_RTP_HEADER_EXTENSION_ONE_BYTE, *buf, twcc_seq, sizeof (twcc_seq)); - /* if there already is a twcc-seqnum inside the packet */ - if (gst_rtp_buffer_get_extension_onebyte_header (&rtp, funnel->twcc_ext_id, - 0, &data, NULL)) { + if (!gst_rtp_buffer_map (*buf, GST_MAP_READWRITE, &rtp)) + goto map_failed; - /* with only one pad, we read the twcc-seqnum instead of writing it */ - if (funnel->twcc_pads == 1) { - funnel->twcc_seqnum = GST_READ_UINT16_BE (data); - } else { - GST_WRITE_UINT16_BE (data, funnel->twcc_seqnum); - } - } else { - guint16 seq_be; - GST_WRITE_UINT16_BE (&seq_be, funnel->twcc_seqnum); - gst_rtp_buffer_add_extension_onebyte_header (&rtp, funnel->twcc_ext_id, - &seq_be, 2); + if (gst_rtp_buffer_get_extension_onebyte_header (&rtp, ext_id, + 0, (gpointer) & existing, &size)) { + if (size >= gst_rtp_header_extension_get_max_size (funnel->twcc_ext, *buf)) { + existing[0] = twcc_seq[0]; + existing[1] = twcc_seq[1]; } } + /* TODO: two-byte variant */ + gst_rtp_buffer_unmap (&rtp); - funnel->twcc_seqnum++; + return; + +map_failed: + { + GST_ERROR ("failed to map buffer %p", *buf); + } } static GstFlowReturn @@ -302,24 +304,25 @@ static void gst_rtp_funnel_set_twcc_ext_id (GstRtpFunnel * funnel, guint8 twcc_ext_id) { gchar *name; + guint current_ext_id; + + current_ext_id = gst_rtp_header_extension_get_id (funnel->twcc_ext); + g_object_set (funnel->twcc_ext, "n-streams", funnel->twcc_pads, NULL); - if (funnel->twcc_ext_id == twcc_ext_id) + if (current_ext_id == twcc_ext_id) return; name = g_strdup_printf ("extmap-%u", twcc_ext_id); - GST_OBJECT_LOCK (funnel); - gst_caps_set_simple (funnel->srccaps, name, G_TYPE_STRING, TWCC_EXTMAP_STR, - NULL); - GST_OBJECT_UNLOCK (funnel); + gst_caps_set_simple (funnel->srccaps, name, G_TYPE_STRING, + gst_rtp_header_extension_get_uri (funnel->twcc_ext), NULL); g_free (name); /* make sure we update the sticky with the new caps */ funnel->send_sticky_events = TRUE; - GST_INFO_OBJECT (funnel, "Setting twcc-ext-id to %u", twcc_ext_id); - funnel->twcc_ext_id = twcc_ext_id; + gst_rtp_header_extension_set_id (funnel->twcc_ext, twcc_ext_id); } static guint8 @@ -345,6 +348,8 @@ _get_extmap_id_for_attribute (const GstStructure * s, const gchar * ext_name) return extmap_id; } +#define TWCC_EXTMAP_STR "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01" + static gboolean gst_rtp_funnel_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { @@ -376,22 +381,25 @@ gst_rtp_funnel_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) caps); g_assert_not_reached (); } - GST_OBJECT_UNLOCK (funnel); s = gst_caps_get_structure (caps, 0); if (gst_structure_get_uint (s, "ssrc", &ssrc)) { fpad->ssrc = ssrc; GST_DEBUG_OBJECT (pad, "Got ssrc: %u", ssrc); - GST_OBJECT_LOCK (funnel); g_hash_table_insert (funnel->ssrc_to_pad, GUINT_TO_POINTER (ssrc), pad); - GST_OBJECT_UNLOCK (funnel); } + + if (!funnel->twcc_ext) + funnel->twcc_ext = + gst_rtp_header_extension_create_from_uri (TWCC_EXTMAP_STR); + ext_id = _get_extmap_id_for_attribute (s, TWCC_EXTMAP_STR); if (ext_id > 0) { fpad->has_twcc = TRUE; funnel->twcc_pads++; gst_rtp_funnel_set_twcc_ext_id (funnel, ext_id); } + GST_OBJECT_UNLOCK (funnel); forward = FALSE; break; @@ -625,6 +633,8 @@ gst_rtp_funnel_finalize (GObject * object) gst_caps_unref (funnel->srccaps); g_hash_table_destroy (funnel->ssrc_to_pad); + gst_clear_object (&funnel->twcc_ext); + G_OBJECT_CLASS (parent_class)->finalize (object); } diff --git a/gst/rtpmanager/gstrtphdrext-twcc.c b/gst/rtpmanager/gstrtphdrext-twcc.c new file mode 100644 index 0000000..8d4b2c7 --- /dev/null +++ b/gst/rtpmanager/gstrtphdrext-twcc.c @@ -0,0 +1,232 @@ +/* GStreamer + * Copyright (C) <2020> Matthew Waters + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:rtphdrexttwcc + * @title: GstRtphdrext-TWCC + * @short_description: Helper methods for dealing with RTP header extensions + * in the Audio/Video RTP Profile for transport-wide-cc + * @see_also: #GstRTPHeaderExtension, #GstRTPBasePayload, #GstRTPBaseDepayload, gstrtpbuffer + * + * Since: 1.20 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstrtphdrext-twcc.h" + +GST_DEBUG_CATEGORY_STATIC (rtphdrext_twcc_debug); +#define GST_CAT_DEFAULT (rtphdrext_twcc_debug) + +#define gst_gl_base_filter_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstRTPHeaderExtensionTWCC, + gst_rtp_header_extension_twcc, GST_TYPE_RTP_HEADER_EXTENSION, + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtphdrexttwcc", 0, + "RTP TWCC Header Extensions"); + ); + +#define TWCC_EXTMAP_STR "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01" + +static void gst_rtp_header_extension_twcc_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_rtp_header_extension_twcc_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static GstRTPHeaderExtensionFlags +gst_rtp_header_extension_twcc_get_supported_flags (GstRTPHeaderExtension * ext); +static gsize gst_rtp_header_extension_twcc_get_max_size (GstRTPHeaderExtension * + ext, const GstBuffer * buffer); +static gsize gst_rtp_header_extension_twcc_write (GstRTPHeaderExtension * ext, + const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags, + GstBuffer * output, guint8 * data, gsize size); +static gboolean gst_rtp_header_extension_twcc_read (GstRTPHeaderExtension * ext, + GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size, + GstBuffer * buffer); + +enum +{ + PROP_0, + PROP_N_STREAMS, +}; + +static void +gst_rtp_header_extension_twcc_class_init (GstRTPHeaderExtensionTWCCClass * + klass) +{ + GstRTPHeaderExtensionClass *rtp_hdr_class; + GstElementClass *gstelement_class; + GObjectClass *gobject_class; + + rtp_hdr_class = (GstRTPHeaderExtensionClass *) klass; + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->set_property = gst_rtp_header_extension_twcc_set_property; + gobject_class->get_property = gst_rtp_header_extension_twcc_get_property; + + /** + * rtphdrexttwcc:n-streams: + * + * The number of independant RTP streams that are being used for the transport + * wide counter for TWCC. If set to 1 (the default), then any existing + * transport wide counter is kept. + * + * Since: 1.20 + */ + g_object_class_install_property (gobject_class, PROP_N_STREAMS, + g_param_spec_uint ("n-streams", "N Streams", + "The number of separate RTP streams this header applies to", + 1, G_MAXUINT32, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + rtp_hdr_class->get_supported_flags = + gst_rtp_header_extension_twcc_get_supported_flags; + rtp_hdr_class->get_max_size = gst_rtp_header_extension_twcc_get_max_size; + rtp_hdr_class->write = gst_rtp_header_extension_twcc_write; + rtp_hdr_class->read = gst_rtp_header_extension_twcc_read; + rtp_hdr_class->set_attributes_from_caps = + gst_rtp_header_extension_set_attributes_from_caps_simple_sdp; + rtp_hdr_class->set_caps_from_attributes = + gst_rtp_header_extension_set_caps_from_attributes_simple_sdp; + + gst_element_class_set_static_metadata (gstelement_class, + "Transport Wide Congestion Control", GST_RTP_HDREXT_ELEMENT_CLASS, + "Extends RTP packets to add sequence number transport wide.", + "Matthew Waters "); + gst_rtp_header_extension_class_set_uri (rtp_hdr_class, TWCC_EXTMAP_STR); +} + +static void +gst_rtp_header_extension_twcc_init (GstRTPHeaderExtensionTWCC * twcc) +{ + twcc->n_streams = 1; +} + +static void +gst_rtp_header_extension_twcc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRTPHeaderExtensionTWCC *twcc = GST_RTP_HEADER_EXTENSION_TWCC (object); + + switch (prop_id) { + case PROP_N_STREAMS: + twcc->n_streams = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_header_extension_twcc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRTPHeaderExtensionTWCC *twcc = GST_RTP_HEADER_EXTENSION_TWCC (object); + + switch (prop_id) { + case PROP_N_STREAMS: + g_value_set_uint (value, twcc->n_streams); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstRTPHeaderExtensionFlags +gst_rtp_header_extension_twcc_get_supported_flags (GstRTPHeaderExtension * ext) +{ + return GST_RTP_HEADER_EXTENSION_ONE_BYTE; +} + +static gsize +gst_rtp_header_extension_twcc_get_max_size (GstRTPHeaderExtension * ext, + const GstBuffer * buffer) +{ + return 2; +} + +static gsize +gst_rtp_header_extension_twcc_write (GstRTPHeaderExtension * ext, + const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags, + GstBuffer * output, guint8 * data, gsize size) +{ + GstRTPHeaderExtensionTWCC *twcc = GST_RTP_HEADER_EXTENSION_TWCC (ext); + GstRTPBuffer rtp = { NULL, }; + gpointer ext_data; + guint ext_size; + gsize written = 0; + + g_return_val_if_fail (size >= gst_rtp_header_extension_twcc_get_max_size (ext, + NULL), -1); + g_return_val_if_fail (write_flags & + gst_rtp_header_extension_twcc_get_supported_flags (ext), -1); + + if (!gst_rtp_buffer_map (output, GST_MAP_READWRITE, &rtp)) + goto map_failed; + + /* if there already is a twcc-seqnum inside the packet */ + if (gst_rtp_buffer_get_extension_onebyte_header (&rtp, ext->ext_id, 0, + &ext_data, &ext_size)) { + if (ext_size < gst_rtp_header_extension_twcc_get_max_size (ext, NULL)) + goto existing_too_small; + + /* with only one stream, we read the twcc-seqnum */ + if (twcc->n_streams == 1) + twcc->seqnum = GST_READ_UINT16_BE (ext_data); + } else { + /* with only one stream, we read the existing seqnum */ + if (twcc->n_streams == 1) + twcc->seqnum = gst_rtp_buffer_get_seq (&rtp); + + written = 2; + } + GST_WRITE_UINT16_BE (data, twcc->seqnum); + + gst_rtp_buffer_unmap (&rtp); + + twcc->seqnum++; + + return written; + + /* ERRORS */ +map_failed: + { + GST_ERROR ("failed to map buffer %p", output); + return 0; + } + +existing_too_small: + { + GST_ERROR ("Cannot rewrite twcc data of smaller size (%u)", ext_size); + return 0; + } +} + +static gboolean +gst_rtp_header_extension_twcc_read (GstRTPHeaderExtension * ext, + GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size, + GstBuffer * buffer) +{ + /* TODO: does this need an extra GstMeta? */ + return TRUE; +} diff --git a/gst/rtpmanager/gstrtphdrext-twcc.h b/gst/rtpmanager/gstrtphdrext-twcc.h new file mode 100644 index 0000000..fd8135e --- /dev/null +++ b/gst/rtpmanager/gstrtphdrext-twcc.h @@ -0,0 +1,82 @@ +/* GStreamer + * Copyright (C) 2020 Matthew Waters + * + * gstrtphdrexttwcc.h: transport-wide-cc RTP header extensions for the + * Audio/Video RTP Profile + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_RTPHDREXT_TWCC_H__ +#define __GST_RTPHDREXT_TWCC_H__ + +#include +#include + +G_BEGIN_DECLS + +GST_RTP_API +GType gst_rtp_header_extension_twcc_get_type (void); +#define GST_TYPE_RTP_HEADER_EXTENSION_TWCC (gst_rtp_header_extension_twcc_get_type()) +#define GST_RTP_HEADER_EXTENSION_TWCC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_HEADER_EXTENSION_TWCC,GstRTPHeaderExtensionTWCC)) +#define GST_RTP_HEADER_EXTENSION_TWCC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_HEADER_EXTENSION_TWCC,GstRTPHeaderExtensionTWCCClass)) +#define GST_RTP_HEADER_EXTENSION_TWCC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_RTP_HEADER_EXTENSION_TWCC,GstRTPHeaderExtensionTWCCClass)) +#define GST_IS_RTP_HEADER_EXTENSION_TWCC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_HEADER_EXTENSION_TWCC)) +#define GST_IS_RTP_HEADER_EXTENSION_TWCC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_HEADER_EXTENSION_TWCC)) +#define GST_RTP_HEADER_EXTENSION_TWCC_CAST(obj) ((GstRTPHeaderExtensionTWCC *)(obj)) + +typedef struct _GstRTPHeaderExtensionTWCC GstRTPHeaderExtensionTWCC; +typedef struct _GstRTPHeaderExtensionTWCCClass GstRTPHeaderExtensionTWCCClass; + +/** + * GstRTPHeaderExtensionTWCC: + * @parent: the parent #GstRTPHeaderExtension + * + * Instance struct for a transport-wide-cc RTP Audio/Video header extension. + * + * http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 + */ +struct _GstRTPHeaderExtensionTWCC +{ + GstRTPHeaderExtension parent; + + guint16 seqnum; + guint n_streams; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstRTPHeaderExtensionTWCCClass: + * @parent_class: the parent class + */ +struct _GstRTPHeaderExtensionTWCCClass +{ + GstRTPHeaderExtensionClass parent_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +G_END_DECLS + +#endif /* __GST_RTPHDREXT_TWCC_H__ */ diff --git a/gst/rtpmanager/gstrtpmanager.c b/gst/rtpmanager/gstrtpmanager.c index 35f131b..aa7d374 100644 --- a/gst/rtpmanager/gstrtpmanager.c +++ b/gst/rtpmanager/gstrtpmanager.c @@ -34,6 +34,7 @@ #include "gstrtpfunnel.h" #include "gstrtpst2022-1-fecdec.h" #include "gstrtpst2022-1-fecenc.h" +#include "gstrtphdrext-twcc.h" static gboolean plugin_init (GstPlugin * plugin) @@ -84,6 +85,10 @@ plugin_init (GstPlugin * plugin) GST_TYPE_RTPST_2022_1_FECENC)) return FALSE; + if (!gst_element_register (plugin, "rtphdrexttwcc", GST_RANK_MARGINAL, + GST_TYPE_RTP_HEADER_EXTENSION_TWCC)) + return FALSE; + return TRUE; } diff --git a/gst/rtpmanager/meson.build b/gst/rtpmanager/meson.build index 334b10a..ff2c9e8 100644 --- a/gst/rtpmanager/meson.build +++ b/gst/rtpmanager/meson.build @@ -3,6 +3,7 @@ rtpmanager_sources = [ 'gstrtpbin.c', 'gstrtpdtmfmux.c', 'gstrtpjitterbuffer.c', + 'gstrtphdrext-twcc.c', 'gstrtpmux.c', 'gstrtpptdemux.c', 'gstrtprtxqueue.c', -- 2.7.4