From 8980c35efee347060a8fa6693ccf2e0224e9a5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 5 Apr 2022 20:05:57 +0300 Subject: [PATCH] rtpmanager: Add header extension implementation for the 64-bit RFC6051 NTP header extension Part-of: --- .../docs/gst_plugins_cache.json | 44 +++ .../gst/rtpmanager/gstrtphdrext-ntp.c | 269 ++++++++++++++++++ .../gst/rtpmanager/gstrtphdrext-ntp.h | 36 +++ .../gst/rtpmanager/gstrtpmanager.c | 2 + .../gst/rtpmanager/meson.build | 1 + 5 files changed, 352 insertions(+) create mode 100644 subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-ntp.c create mode 100644 subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-ntp.h diff --git a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json index 012ebab944..f2f8ff6f5e 100644 --- a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json +++ b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json @@ -17948,6 +17948,50 @@ }, "rank": "marginal" }, + "rtphdrextntp64": { + "RTP-Header-Extension-URI": "urn:ietf:params:rtp-hdrext:ntp-64", + "author": "Sebastian Dröge ", + "description": "Extends RTP packets to add or retrieve a 64-bit NTP timestamp as specified in RFC6501", + "hierarchy": [ + "GstRTPHeaderExtensionNtp64", + "GstRTPHeaderExtension", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Network/Extension/RTPHeader", + "long-name": "RTP Header Extension RFC6051 64-bit NTP timestamp", + "properties": { + "every-packet": { + "blurb": "Add the header extension to every packet", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, + "interval": { + "blurb": "Interval between consecutive packets that get the header extension added", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "0", + "max": "18446744073709551615", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint64", + "writable": true + } + }, + "rank": "marginal" + }, "rtphdrextrepairedstreamid": { "RTP-Header-Extension-URI": "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id", "author": "Matthew Waters ", diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-ntp.c b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-ntp.c new file mode 100644 index 0000000000..a027138d5b --- /dev/null +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-ntp.c @@ -0,0 +1,269 @@ +/* GStreamer + * Copyright (C) <2022> Sebastian Dröge + * + * 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-rtphdrextntp64 + * @title: rtphdrextntp64 + * @short_description: RTP Header Extension for RFC6051 64-bit NTP timestamps for rapid synchronization. + * @see_also: #GstRTPHeaderExtension, #GstRTPBasePayload, #GstRTPBaseDepayload, gstrtpbuffer + * + * Since: 1.22 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstrtphdrext-ntp.h" + +GST_DEBUG_CATEGORY_STATIC (rtphdrext_ntp_debug); +#define GST_CAT_DEFAULT (rtphdrext_ntp_debug) + +enum +{ + PROP_0, + PROP_INTERVAL, + PROP_EVERY_PACKET, +}; + +#define DEFAULT_INTERVAL 0 +#define DEFAULT_EVERY_PACKET FALSE + +static GstStaticCaps ntp_reference_timestamp_caps = +GST_STATIC_CAPS ("timestamp/x-ntp"); + +struct _GstRTPHeaderExtensionNtp64 +{ + GstRTPHeaderExtension parent; + + GstClockTime last_pts; + + GstClockTime interval; + gboolean every_packet; +}; + +G_DEFINE_TYPE_WITH_CODE (GstRTPHeaderExtensionNtp64, + gst_rtp_header_extension_ntp_64, + GST_TYPE_RTP_HEADER_EXTENSION, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, + "rtphdrextntp", 0, "RTP RFC6051 NTP Timestamps Header Extension"); + ); + +GST_ELEMENT_REGISTER_DEFINE (rtphdrextntp64, + "rtphdrextntp64", GST_RANK_MARGINAL, GST_TYPE_RTP_HEADER_EXTENSION_NTP_64); + +static GstRTPHeaderExtensionFlags + gst_rtp_header_extension_ntp_get_supported_flags + (GstRTPHeaderExtension * ext) +{ + return GST_RTP_HEADER_EXTENSION_ONE_BYTE | GST_RTP_HEADER_EXTENSION_TWO_BYTE; +} + +static gsize + gst_rtp_header_extension_ntp_64_get_max_size + (GstRTPHeaderExtension * ext, const GstBuffer * buffer) +{ + return 8; +} + +static gssize +gst_rtp_header_extension_ntp_64_write (GstRTPHeaderExtension + * ext, const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags, + GstBuffer * output, guint8 * data, gsize size) +{ + GstRTPHeaderExtensionNtp64 *self = GST_RTP_HEADER_EXTENSION_NTP_64 (ext); + + g_return_val_if_fail (size >= + gst_rtp_header_extension_ntp_64_get_max_size (ext, NULL), -1); + g_return_val_if_fail (write_flags & + gst_rtp_header_extension_ntp_get_supported_flags (ext), -1); + + if (self->every_packet + || self->last_pts == GST_CLOCK_TIME_NONE + || !GST_BUFFER_PTS_IS_VALID (input_meta) + || (self->last_pts != GST_BUFFER_PTS (input_meta) + && (GST_BUFFER_IS_DISCONT (input_meta) + || (GST_BUFFER_PTS (input_meta) >= self->last_pts + && GST_BUFFER_PTS (input_meta) - self->last_pts >= + self->interval)))) { + GstCaps *caps; + GstReferenceTimestampMeta *meta; + + caps = gst_static_caps_get (&ntp_reference_timestamp_caps); + meta = + gst_buffer_get_reference_timestamp_meta ((GstBuffer *) input_meta, + caps); + if (meta) { + guint64 ntptime = + gst_util_uint64_scale (meta->timestamp, G_GUINT64_CONSTANT (1) << 32, + GST_SECOND); + + GST_WRITE_UINT64_BE (data, ntptime); + } else { + memset (data, 0, 8); + } + gst_caps_unref (caps); + self->last_pts = GST_BUFFER_PTS (input_meta); + return 8; + } else { + return 0; + } +} + +static gboolean +gst_rtp_header_extension_ntp_64_read (GstRTPHeaderExtension + * ext, GstRTPHeaderExtensionFlags read_flags, const guint8 * data, + gsize size, GstBuffer * buffer) +{ + GstCaps *caps; + guint64 ntptime; + GstClockTime timestamp; + + if (size < 8) + return FALSE; + + caps = gst_static_caps_get (&ntp_reference_timestamp_caps); + + ntptime = GST_READ_UINT64_BE (data); + timestamp = + gst_util_uint64_scale (ntptime, GST_SECOND, G_GUINT64_CONSTANT (1) << 32); + + gst_buffer_add_reference_timestamp_meta (buffer, caps, timestamp, + GST_CLOCK_TIME_NONE); + + gst_caps_unref (caps); + + return TRUE; +} + + +static void +gst_rtp_header_extension_ntp_64_get_property (GObject * + object, guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstRTPHeaderExtensionNtp64 *self = GST_RTP_HEADER_EXTENSION_NTP_64 (object); + + switch (prop_id) { + case PROP_INTERVAL: + GST_OBJECT_LOCK (self); + g_value_set_uint64 (value, self->interval); + GST_OBJECT_UNLOCK (self); + break; + case PROP_EVERY_PACKET: + GST_OBJECT_LOCK (self); + g_value_set_boolean (value, self->every_packet); + GST_OBJECT_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_header_extension_ntp_64_set_property (GObject * + object, guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstRTPHeaderExtensionNtp64 *self = GST_RTP_HEADER_EXTENSION_NTP_64 (object); + + switch (prop_id) { + case PROP_INTERVAL: + GST_OBJECT_LOCK (self); + self->interval = g_value_get_uint64 (value); + GST_OBJECT_UNLOCK (self); + break; + case PROP_EVERY_PACKET: + GST_OBJECT_LOCK (self); + self->every_packet = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void + gst_rtp_header_extension_ntp_64_class_init + (GstRTPHeaderExtensionNtp64Class * 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_ntp_64_set_property; + gobject_class->get_property = gst_rtp_header_extension_ntp_64_get_property; + + /** + * rtphdrextntp64:interval: + * + * The minimum interval between packets that get the header extension added. + * + * On discontinuities the interval will be reset and the next packet gets + * the header extension added. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_INTERVAL, + g_param_spec_uint64 ("interval", "Interval", + "Interval between consecutive packets that get the header extension added", + 0, G_MAXUINT64, DEFAULT_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * rtphdrextntp64:every-packet: + * + * If set to %TRUE the header extension will be added to every packet, + * independent of its timestamp. By default only the first packet with a + * given timestamp will get the header extension added. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, PROP_EVERY_PACKET, + g_param_spec_boolean ("every-packet", "Every Packet", + "Add the header extension to every packet", DEFAULT_EVERY_PACKET, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + rtp_hdr_class->get_supported_flags = + gst_rtp_header_extension_ntp_get_supported_flags; + rtp_hdr_class->get_max_size = gst_rtp_header_extension_ntp_64_get_max_size; + rtp_hdr_class->write = gst_rtp_header_extension_ntp_64_write; + rtp_hdr_class->read = gst_rtp_header_extension_ntp_64_read; + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Header Extension RFC6051 64-bit NTP timestamp", + GST_RTP_HDREXT_ELEMENT_CLASS, + "Extends RTP packets to add or retrieve a 64-bit NTP " + "timestamp as specified in RFC6501", + "Sebastian Dröge "); + gst_rtp_header_extension_class_set_uri (rtp_hdr_class, + GST_RTP_HDREXT_BASE GST_RTP_HDREXT_NTP_64); +} + +static void +gst_rtp_header_extension_ntp_64_init (GstRTPHeaderExtensionNtp64 * self) +{ + self->last_pts = GST_CLOCK_TIME_NONE; + self->interval = DEFAULT_INTERVAL; + self->every_packet = DEFAULT_EVERY_PACKET; +} diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-ntp.h b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-ntp.h new file mode 100644 index 0000000000..0e468058b8 --- /dev/null +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtphdrext-ntp.h @@ -0,0 +1,36 @@ +/* GStreamer + * Copyright (C) <2022> Sebastian Dröge + * + * 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_NTP_H__ +#define __GST_RTPHDREXT_NTP_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_HEADER_EXTENSION_NTP_64 (gst_rtp_header_extension_ntp_64_get_type()) + +G_DECLARE_FINAL_TYPE (GstRTPHeaderExtensionNtp64, gst_rtp_header_extension_ntp_64, GST, RTP_HEADER_EXTENSION_NTP_64, GstRTPHeaderExtension); + +GST_ELEMENT_REGISTER_DECLARE (rtphdrextntp64); + +G_END_DECLS + +#endif /* __GST_RTPHDREXT_NTP_H__ */ diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpmanager.c b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpmanager.c index 35ccf884d3..59a9eeb3a2 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpmanager.c +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpmanager.c @@ -37,6 +37,7 @@ #include "gstrtphdrext-twcc.h" #include "gstrtphdrext-clientaudiolevel.h" #include "gstrtphdrext-mid.h" +#include "gstrtphdrext-ntp.h" #include "gstrtphdrext-repairedstreamid.h" #include "gstrtphdrext-streamid.h" @@ -61,6 +62,7 @@ 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 (rtphdrextntp64, plugin); ret |= GST_ELEMENT_REGISTER (rtphdrextstreamid, plugin); ret |= GST_ELEMENT_REGISTER (rtphdrextrepairedstreamid, plugin); diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/meson.build b/subprojects/gst-plugins-good/gst/rtpmanager/meson.build index 9af080975e..bbd2b4f288 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/meson.build +++ b/subprojects/gst-plugins-good/gst/rtpmanager/meson.build @@ -6,6 +6,7 @@ rtpmanager_sources = [ 'gstrtphdrext-twcc.c', 'gstrtphdrext-clientaudiolevel.c', 'gstrtphdrext-mid.c', + 'gstrtphdrext-ntp.c', 'gstrtphdrext-repairedstreamid.c', 'gstrtphdrext-streamid.c', 'gstrtpmux.c',