From 1e55e2d654070c54ea246d348fea538200f3d5cf Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 25 Aug 2021 16:58:16 +1000 Subject: [PATCH] rtpmanager: add support for RFC8852 (rid) RTP header extensions Both for regular RID and for adding on a repaired (RTX) etc stream. Part-of: --- .../docs/gst_plugins_cache.json | 60 ++++ .../gstrtphdrext-repairedstreamid.c | 307 ++++++++++++++++++ .../gstrtphdrext-repairedstreamid.h | 36 ++ .../gst/rtpmanager/gstrtphdrext-streamid.c | 297 +++++++++++++++++ .../gst/rtpmanager/gstrtphdrext-streamid.h | 36 ++ .../gst/rtpmanager/gstrtpmanager.c | 4 + .../gst/rtpmanager/meson.build | 2 + 7 files changed, 742 insertions(+) create mode 100644 subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-repairedstreamid.c create mode 100644 subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-repairedstreamid.h create mode 100644 subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-streamid.c create mode 100644 subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-streamid.h diff --git a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json index 178587398f..4e3ee275c3 100644 --- a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json +++ b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json @@ -17503,6 +17503,66 @@ }, "rank": "marginal" }, + "rtphdrextrepairedstreamid": { + "RTP-Header-Extension-URI": "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id", + "author": "Matthew Waters ", + "description": "Extends RTP packets to add or retrieve a RepairedStreamId (RID) value as specified in RFC8852", + "hierarchy": [ + "GstRTPHeaderExtensionRepairedStreamId", + "GstRTPHeaderExtension", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Network/Extension/RTPHeader", + "long-name": "RTP Header Extension Repaired RFC8852 Stream ID", + "properties": { + "rid": { + "blurb": "The RepairedRtpStreamId (RID) value last read or to write from/to RTP buffers", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + } + }, + "rank": "marginal" + }, + "rtphdrextstreamid": { + "RTP-Header-Extension-URI": "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id", + "author": "Matthew Waters ", + "description": "Extends RTP packets to add or retrieve a RtpStreamId (RID) value as specified in RFC8852", + "hierarchy": [ + "GstRTPHeaderExtensionStreamId", + "GstRTPHeaderExtension", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Network/Extension/RTPHeader", + "long-name": "RTP Header Extension RFC8852 Stream ID", + "properties": { + "rid": { + "blurb": "The RtpStreamId (RID) value last read or to write from/to RTP buffers", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + } + }, + "rank": "marginal" + }, "rtphdrexttwcc": { "RTP-Header-Extension-URI": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01", "author": "Matthew Waters ", diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-repairedstreamid.c b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-repairedstreamid.c new file mode 100644 index 0000000000..706176952a --- /dev/null +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-repairedstreamid.c @@ -0,0 +1,307 @@ +/* GStreamer + * Copyright (C) <2021> 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:element-rtphdrextrepairedstreamid + * @title: rtphdrextrepairedstreamid + * @short_description: RTP SDES Header Extension for RFC8852 RepairedRtpStreamId + * Extension + * @see_also: #GstRTPHeaderExtension, #GstRTPBasePayload, #GstRTPBaseDepayload, gstrtpbuffer + * + * Since: 1.22 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstrtphdrext-repairedstreamid.h" + +GST_DEBUG_CATEGORY_STATIC (rtphdrext_repaired_stream_id_debug); +#define GST_CAT_DEFAULT (rtphdrext_repaired_stream_id_debug) + +#define REPAIRED_RID_EXTMAP_STR GST_RTP_HDREXT_BASE "sdes:repaired-rtp-stream-id" + +enum +{ + PROP_0, + PROP_RID, +}; + +struct _GstRTPHeaderExtensionRepairedStreamId +{ + GstRTPHeaderExtension parent; + + char *rid; +}; + +#define gst_rtp_header_extension_repaired_stream_id_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstRTPHeaderExtensionRepairedStreamId, + gst_rtp_header_extension_repaired_stream_id, + GST_TYPE_RTP_HEADER_EXTENSION, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, + "rtphdrextrepairedstreamid", 0, + "RTP RFC8852 RepairedRtpStreamId Header Extensions"); + ); +GST_ELEMENT_REGISTER_DEFINE (rtphdrextrepairedstreamid, + "rtphdrextrepairedstreamid", GST_RANK_MARGINAL, + GST_TYPE_RTP_HEADER_EXTENSION_REPAIRED_STREAM_ID); + +static GstRTPHeaderExtensionFlags + gst_rtp_header_extension_repaired_stream_id_get_supported_flags + (GstRTPHeaderExtension * ext) +{ + GstRTPHeaderExtensionRepairedStreamId *self = + GST_RTP_HEADER_EXTENSION_REPAIRED_STREAM_ID (ext); + GstRTPHeaderExtensionFlags flags = + GST_RTP_HEADER_EXTENSION_ONE_BYTE | GST_RTP_HEADER_EXTENSION_TWO_BYTE; + gssize rid_len = -1; + + GST_OBJECT_LOCK (ext); + if (self->rid) + rid_len = strlen (self->rid); + GST_OBJECT_UNLOCK (ext); + /* One byte extensions only support [1, 16] bytes */ + if (rid_len > 16) + flags = GST_RTP_HEADER_EXTENSION_TWO_BYTE; + + return flags; +} + +static gsize + gst_rtp_header_extension_repaired_stream_id_get_max_size + (GstRTPHeaderExtension * ext, const GstBuffer * buffer) +{ + if (gst_rtp_header_extension_repaired_stream_id_get_supported_flags + (ext) & GST_RTP_HEADER_EXTENSION_ONE_BYTE) + return 16; + else + return 255; +} + +static gssize +gst_rtp_header_extension_repaired_stream_id_write (GstRTPHeaderExtension + * ext, const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags, + GstBuffer * output, guint8 * data, gsize size) +{ + GstRTPHeaderExtensionRepairedStreamId *self = + GST_RTP_HEADER_EXTENSION_REPAIRED_STREAM_ID (ext); + gsize len = 0; + + g_return_val_if_fail (size >= + gst_rtp_header_extension_repaired_stream_id_get_max_size (ext, NULL), -1); + g_return_val_if_fail (write_flags & + gst_rtp_header_extension_repaired_stream_id_get_supported_flags + (ext), -1); + + GST_OBJECT_LOCK (ext); + if (!self->rid) { + GST_LOG_OBJECT (self, "no rid to write"); + goto out; + } + + /* TODO: we don't need to always add rid, we can selectively omit it from e.g. + * non-video-keyframes or some percentage of the produced frames, e.g. RFC8852 + * mentions possibly using packet-loss as a indication of how often to add rid + * to packets */ + GST_LOG_OBJECT (self, "writing rid \'%s\'", self->rid); + len = strlen (self->rid); + if ((write_flags & GST_RTP_HEADER_EXTENSION_TWO_BYTE) == 0 && len > 16) { + GST_DEBUG_OBJECT (self, "cannot write a rid of size %" G_GSIZE_FORMAT + " without using the two byte extension format", len); + len = 0; + goto out; + } + if (len > 0) { + GST_LOG_OBJECT (self, "writing rid \'%s\'", self->rid); + memcpy (data, self->rid, len); + } + +out: + GST_OBJECT_UNLOCK (ext); + return len; +} + +static gboolean +gst_rtp_header_extension_repaired_stream_id_read (GstRTPHeaderExtension + * ext, GstRTPHeaderExtensionFlags read_flags, const guint8 * data, + gsize size, GstBuffer * buffer) +{ + GstRTPHeaderExtensionRepairedStreamId *self = + GST_RTP_HEADER_EXTENSION_REPAIRED_STREAM_ID (ext); + gboolean notify = FALSE; + + if (!data || size == 0) + return TRUE; + + if (read_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE && (size < 1 || size > 16)) { + GST_ERROR_OBJECT (ext, + "one-byte header extensions must be between 1 and 16 bytes inculusive"); + return FALSE; + } + + GST_OBJECT_LOCK (self); + if (!self->rid || strncmp ((const char *) data, self->rid, size) != 0) { + g_clear_pointer (&self->rid, g_free); + self->rid = g_strndup ((const char *) data, size); + notify = TRUE; + } + GST_OBJECT_UNLOCK (self); + + if (notify) + g_object_notify ((GObject *) self, "rid"); + + return TRUE; +} + +static void +gst_rtp_header_extension_repaired_stream_id_get_property (GObject * + object, guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstRTPHeaderExtensionRepairedStreamId *self = + GST_RTP_HEADER_EXTENSION_REPAIRED_STREAM_ID (object); + + switch (prop_id) { + case PROP_RID: + GST_OBJECT_LOCK (self); + g_value_set_string (value, self->rid); + GST_OBJECT_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +validate_rid (const char *rid) +{ + const char *iter; + + /* For avoidance of doubt, the only allowed byte values for + * these IDs are decimal 48 through 57, 65 through 90, and 97 through + * 122. + */ + for (iter = rid; iter && iter[0]; iter++) { + if (iter[0] < 48) + return FALSE; + if (iter[0] > 122) + return FALSE; + if (iter[0] > 57 && iter[0] < 65) + return FALSE; + if (iter[0] > 90 && iter[0] < 97) + return FALSE; + } + + return TRUE; +} + +static void +gst_rtp_header_extension_repaired_stream_id_set_property (GObject * + object, guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstRTPHeaderExtensionRepairedStreamId *self = + GST_RTP_HEADER_EXTENSION_REPAIRED_STREAM_ID (object); + + switch (prop_id) { + case PROP_RID:{ + const char *rid; + GST_OBJECT_LOCK (self); + rid = g_value_get_string (value); + if (!validate_rid (rid)) { + GST_WARNING_OBJECT (self, "Could not set rid \'%s\'. Validation failed", + rid); + } else { + g_clear_pointer (&self->rid, g_free); + self->rid = g_strdup (rid); + } + GST_OBJECT_UNLOCK (self); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_header_extension_repaired_stream_id_finalize (GObject * object) +{ + GstRTPHeaderExtensionRepairedStreamId *self = + GST_RTP_HEADER_EXTENSION_REPAIRED_STREAM_ID (object); + + g_clear_pointer (&self->rid, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void + gst_rtp_header_extension_repaired_stream_id_class_init + (GstRTPHeaderExtensionRepairedStreamIdClass * 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_repaired_stream_id_set_property; + gobject_class->get_property = + gst_rtp_header_extension_repaired_stream_id_get_property; + gobject_class->finalize = + gst_rtp_header_extension_repaired_stream_id_finalize; + + /** + * rtphdrextrepairedstreamid:rid: + * + * The RepairedRtpStreamID (RID) value either last retrieved from the RTP + * Header extension, or to set on outgoing RTP packets. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_RID, + g_param_spec_string ("rid", "rid", + "The RepairedRtpStreamId (RID) value last read or to write from/to " + "RTP buffers", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + rtp_hdr_class->get_supported_flags = + gst_rtp_header_extension_repaired_stream_id_get_supported_flags; + rtp_hdr_class->get_max_size = + gst_rtp_header_extension_repaired_stream_id_get_max_size; + rtp_hdr_class->write = gst_rtp_header_extension_repaired_stream_id_write; + rtp_hdr_class->read = gst_rtp_header_extension_repaired_stream_id_read; + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Header Extension Repaired RFC8852 Stream ID", + GST_RTP_HDREXT_ELEMENT_CLASS, + "Extends RTP packets to add or retrieve a RepairedStreamId (RID) " + "value as specified in RFC8852", + "Matthew Waters "); + gst_rtp_header_extension_class_set_uri (rtp_hdr_class, + REPAIRED_RID_EXTMAP_STR); +} + +static void + gst_rtp_header_extension_repaired_stream_id_init + (GstRTPHeaderExtensionRepairedStreamId * self) +{ +} diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-repairedstreamid.h b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-repairedstreamid.h new file mode 100644 index 0000000000..7697c537cc --- /dev/null +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-repairedstreamid.h @@ -0,0 +1,36 @@ +/* GStreamer + * Copyright (C) 2021 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. + */ + +#ifndef __GST_RTPHDREXT_REPAIRED_STREAM_ID_H__ +#define __GST_RTPHDREXT_REPAIRED_STREAM_ID_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_HEADER_EXTENSION_REPAIRED_STREAM_ID (gst_rtp_header_extension_repaired_stream_id_get_type()) + +G_DECLARE_FINAL_TYPE (GstRTPHeaderExtensionRepairedStreamId, gst_rtp_header_extension_repaired_stream_id, GST, RTP_HEADER_EXTENSION_REPAIRED_STREAM_ID, GstRTPHeaderExtension); + +GST_ELEMENT_REGISTER_DECLARE (rtphdrextrepairedstreamid); + +G_END_DECLS + +#endif /* __GST_RTPHDREXT_REPAIRED_STREAM_ID_H__ */ diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-streamid.c b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-streamid.c new file mode 100644 index 0000000000..1705e1f98b --- /dev/null +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-streamid.c @@ -0,0 +1,297 @@ +/* GStreamer + * Copyright (C) <2021> 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:element-rtphdrextstreamid + * @title: rtphdrextstreamid + * @short_description: RTP SDES Header Extension for RFC8852 RtpStreamId + * Extension + * @see_also: #GstRTPHeaderExtension, #GstRTPBasePayload, #GstRTPBaseDepayload, gstrtpbuffer + * + * Since: 1.22 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstrtphdrext-streamid.h" + +GST_DEBUG_CATEGORY_STATIC (rtphdrext_stream_id_debug); +#define GST_CAT_DEFAULT (rtphdrext_stream_id_debug) + +#define RID_EXTMAP_STR GST_RTP_HDREXT_BASE "sdes:rtp-stream-id" + +enum +{ + PROP_0, + PROP_RID, +}; + +struct _GstRTPHeaderExtensionStreamId +{ + GstRTPHeaderExtension parent; + + char *rid; +}; + +#define gst_rtp_header_extension_stream_id_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstRTPHeaderExtensionStreamId, + gst_rtp_header_extension_stream_id, GST_TYPE_RTP_HEADER_EXTENSION, + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtphdrextstreamid", 0, + "RTP RFC8852 RtpStreamId Header Extensions"); + ); +GST_ELEMENT_REGISTER_DEFINE (rtphdrextstreamid, + "rtphdrextstreamid", GST_RANK_MARGINAL, + GST_TYPE_RTP_HEADER_EXTENSION_STREAM_ID); + +static GstRTPHeaderExtensionFlags + gst_rtp_header_extension_stream_id_get_supported_flags + (GstRTPHeaderExtension * ext) +{ + GstRTPHeaderExtensionStreamId *self = + GST_RTP_HEADER_EXTENSION_STREAM_ID (ext); + GstRTPHeaderExtensionFlags flags = + GST_RTP_HEADER_EXTENSION_ONE_BYTE | GST_RTP_HEADER_EXTENSION_TWO_BYTE; + gssize rid_len = -1; + + GST_OBJECT_LOCK (ext); + if (self->rid) + rid_len = strlen (self->rid); + GST_OBJECT_UNLOCK (ext); + if (rid_len > 16) + flags = GST_RTP_HEADER_EXTENSION_TWO_BYTE; + + return flags; +} + +static gsize +gst_rtp_header_extension_stream_id_get_max_size (GstRTPHeaderExtension * + ext, const GstBuffer * buffer) +{ + if (gst_rtp_header_extension_stream_id_get_supported_flags (ext) & + GST_RTP_HEADER_EXTENSION_ONE_BYTE) + return 16; + else + return 255; +} + +static gssize +gst_rtp_header_extension_stream_id_write (GstRTPHeaderExtension * ext, + const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags, + GstBuffer * output, guint8 * data, gsize size) +{ + GstRTPHeaderExtensionStreamId *self = + GST_RTP_HEADER_EXTENSION_STREAM_ID (ext); + gsize len = 0; + + g_return_val_if_fail (size >= + gst_rtp_header_extension_stream_id_get_max_size (ext, NULL), -1); + g_return_val_if_fail (write_flags & + gst_rtp_header_extension_stream_id_get_supported_flags (ext), -1); + + GST_OBJECT_LOCK (ext); + if (!self->rid) { + GST_LOG_OBJECT (self, "no rid to write"); + goto out; + } + + /* TODO: we don't need to always add rid, we can selectively omit it from e.g. + * non-video-keyframes or some percentage of the produced frames, e.g. RFC8852 + * mentions possibly using packet-loss as a indication of how often to add mid + * to packets */ + len = strlen (self->rid); + if ((write_flags & GST_RTP_HEADER_EXTENSION_TWO_BYTE) == 0 && len > 16) { + GST_DEBUG_OBJECT (self, "cannot write a rid of size %" G_GSIZE_FORMAT + " without using the two byte extension format", len); + len = 0; + goto out; + } + if (len > 0) { + GST_LOG_OBJECT (self, "writing rid \'%s\'", self->rid); + memcpy (data, self->rid, len); + } + +out: + GST_OBJECT_UNLOCK (ext); + return len; +} + +static gboolean +gst_rtp_header_extension_stream_id_read (GstRTPHeaderExtension * ext, + GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size, + GstBuffer * buffer) +{ + GstRTPHeaderExtensionStreamId *self = + GST_RTP_HEADER_EXTENSION_STREAM_ID (ext); + gboolean notify = FALSE; + + if (!data || size == 0) + return TRUE; + + if (read_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE && (size < 1 || size > 16)) { + GST_ERROR_OBJECT (ext, + "one-byte header extensions must be between 1 and 16 bytes inculusive"); + return FALSE; + } + + GST_OBJECT_LOCK (self); + if (!self->rid || strncmp ((const char *) data, self->rid, size) != 0) { + g_clear_pointer (&self->rid, g_free); + self->rid = g_strndup ((const char *) data, size); + notify = TRUE; + } + GST_OBJECT_UNLOCK (self); + + if (notify) + g_object_notify ((GObject *) self, "rid"); + + return TRUE; +} + +static void +gst_rtp_header_extension_stream_id_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstRTPHeaderExtensionStreamId *self = + GST_RTP_HEADER_EXTENSION_STREAM_ID (object); + + switch (prop_id) { + case PROP_RID: + GST_OBJECT_LOCK (self); + g_value_set_string (value, self->rid); + GST_OBJECT_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +validate_rid (const char *rid) +{ + const char *iter; + + /* For avoidance of doubt, the only allowed byte values for + * these IDs are decimal 48 through 57, 65 through 90, and 97 through + * 122. + */ + for (iter = rid; iter && iter[0]; iter++) { + if (iter[0] < 48) + return FALSE; + if (iter[0] > 122) + return FALSE; + if (iter[0] > 57 && iter[0] < 65) + return FALSE; + if (iter[0] > 90 && iter[0] < 97) + return FALSE; + } + + return TRUE; +} + +static void +gst_rtp_header_extension_stream_id_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstRTPHeaderExtensionStreamId *self = + GST_RTP_HEADER_EXTENSION_STREAM_ID (object); + + switch (prop_id) { + case PROP_RID:{ + const char *rid; + GST_OBJECT_LOCK (self); + rid = g_value_get_string (value); + if (!validate_rid (rid)) { + GST_WARNING_OBJECT (self, "Could not set rid \'%s\'. Validation failed", + rid); + } else { + g_clear_pointer (&self->rid, g_free); + self->rid = g_strdup (rid); + } + GST_OBJECT_UNLOCK (self); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_header_extension_stream_id_finalize (GObject * object) +{ + GstRTPHeaderExtensionStreamId *self = + GST_RTP_HEADER_EXTENSION_STREAM_ID (object); + + g_clear_pointer (&self->rid, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void + gst_rtp_header_extension_stream_id_class_init + (GstRTPHeaderExtensionStreamIdClass * 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_stream_id_set_property; + gobject_class->get_property = gst_rtp_header_extension_stream_id_get_property; + gobject_class->finalize = gst_rtp_header_extension_stream_id_finalize; + + /** + * rtphdrextstreamid:rid: + * + * The RtpStreamID (RID) value either last retrieved from the RTP + * Header extension, or to set on outgoing RTP packets. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_RID, + g_param_spec_string ("rid", "rid", + "The RtpStreamId (RID) value last read or to write from/to " + "RTP buffers", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + rtp_hdr_class->get_supported_flags = + gst_rtp_header_extension_stream_id_get_supported_flags; + rtp_hdr_class->get_max_size = gst_rtp_header_extension_stream_id_get_max_size; + rtp_hdr_class->write = gst_rtp_header_extension_stream_id_write; + rtp_hdr_class->read = gst_rtp_header_extension_stream_id_read; + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Header Extension RFC8852 Stream ID", GST_RTP_HDREXT_ELEMENT_CLASS, + "Extends RTP packets to add or retrieve a RtpStreamId (RID) " + "value as specified in RFC8852", + "Matthew Waters "); + gst_rtp_header_extension_class_set_uri (rtp_hdr_class, RID_EXTMAP_STR); +} + +static void + gst_rtp_header_extension_stream_id_init + (GstRTPHeaderExtensionStreamId * self) +{ +} diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-streamid.h b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-streamid.h new file mode 100644 index 0000000000..17771321ea --- /dev/null +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-streamid.h @@ -0,0 +1,36 @@ +/* GStreamer + * Copyright (C) 2021 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. + */ + +#ifndef __GST_RTPHDREXT_STREAM_ID_H__ +#define __GST_RTPHDREXT_STREAM_ID_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_HEADER_EXTENSION_STREAM_ID (gst_rtp_header_extension_stream_id_get_type()) + +G_DECLARE_FINAL_TYPE (GstRTPHeaderExtensionStreamId, gst_rtp_header_extension_stream_id, GST, RTP_HEADER_EXTENSION_STREAM_ID, GstRTPHeaderExtension); + +GST_ELEMENT_REGISTER_DECLARE (rtphdrextstreamid); + +G_END_DECLS + +#endif /* __GST_RTPHDREXT_STREAM_ID_H__ */ diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpmanager.c b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpmanager.c index 3d291e76ee..35ccf884d3 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpmanager.c +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpmanager.c @@ -37,6 +37,8 @@ #include "gstrtphdrext-twcc.h" #include "gstrtphdrext-clientaudiolevel.h" #include "gstrtphdrext-mid.h" +#include "gstrtphdrext-repairedstreamid.h" +#include "gstrtphdrext-streamid.h" static gboolean plugin_init (GstPlugin * plugin) @@ -59,6 +61,8 @@ plugin_init (GstPlugin * plugin) ret |= GST_ELEMENT_REGISTER (rtphdrexttwcc, plugin); ret |= GST_ELEMENT_REGISTER (rtphdrextclientaudiolevel, plugin); ret |= GST_ELEMENT_REGISTER (rtphdrextmid, plugin); + ret |= GST_ELEMENT_REGISTER (rtphdrextstreamid, plugin); + ret |= GST_ELEMENT_REGISTER (rtphdrextrepairedstreamid, plugin); return ret; } diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/meson.build b/subprojects/gst-plugins-good/gst/rtpmanager/meson.build index 1414a13412..9af080975e 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/meson.build +++ b/subprojects/gst-plugins-good/gst/rtpmanager/meson.build @@ -6,6 +6,8 @@ rtpmanager_sources = [ 'gstrtphdrext-twcc.c', 'gstrtphdrext-clientaudiolevel.c', 'gstrtphdrext-mid.c', + 'gstrtphdrext-repairedstreamid.c', + 'gstrtphdrext-streamid.c', 'gstrtpmux.c', 'gstrtpptdemux.c', 'gstrtprtxqueue.c',