sbcdepay: Add property to ignore input timestamps
authorSebastian Dröge <sebastian@centricular.com>
Tue, 5 Sep 2017 08:41:35 +0000 (11:41 +0300)
committerSebastian Dröge <sebastian@centricular.com>
Thu, 28 Sep 2017 11:15:12 +0000 (14:15 +0300)
This then just counts samples and calculates the output timestamps based
on that and the very first observed timestamp. The timestamps on the
buffers are continued to be used to detect discontinuities that are too
big and reset the counter at that point.

When receiving data via Bluetooth, many devices put completely wrong
values into the RTP timestamp field. For example iOS seems to put a
timestamp in milliseconds in there, instead of something based on the
current sample offset (RTP clock-rate == sample rate).

https://bugzilla.gnome.org/show_bug.cgi?id=787297

gst/rtp/gstrtpsbcdepay.c
gst/rtp/gstrtpsbcdepay.h

index 4d41515..cd49c9f 100644 (file)
@@ -53,9 +53,22 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
         "encoding-name = (string) SBC")
     );
 
+enum
+{
+  PROP_0,
+  PROP_IGNORE_TIMESTAMPS,
+  PROP_LAST
+};
+
+#define DEFAULT_IGNORE_TIMESTAMPS FALSE
+
 #define gst_rtp_sbc_depay_parent_class parent_class
 G_DEFINE_TYPE (GstRtpSbcDepay, gst_rtp_sbc_depay, GST_TYPE_RTP_BASE_DEPAYLOAD);
 
+static void gst_rtp_sbc_depay_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_rtp_sbc_depay_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
 static void gst_rtp_sbc_depay_finalize (GObject * object);
 
 static gboolean gst_rtp_sbc_depay_setcaps (GstRTPBaseDepayload * base,
@@ -72,6 +85,13 @@ gst_rtp_sbc_depay_class_init (GstRtpSbcDepayClass * klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   gobject_class->finalize = gst_rtp_sbc_depay_finalize;
+  gobject_class->set_property = gst_rtp_sbc_depay_set_property;
+  gobject_class->get_property = gst_rtp_sbc_depay_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_IGNORE_TIMESTAMPS,
+      g_param_spec_boolean ("ignore-timestamps", "Ignore Timestamps",
+          "Various statistics", DEFAULT_IGNORE_TIMESTAMPS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   gstbasertpdepayload_class->set_caps = gst_rtp_sbc_depay_setcaps;
   gstbasertpdepayload_class->process_rtp_packet = gst_rtp_sbc_depay_process;
@@ -95,6 +115,9 @@ static void
 gst_rtp_sbc_depay_init (GstRtpSbcDepay * rtpsbcdepay)
 {
   rtpsbcdepay->adapter = gst_adapter_new ();
+  rtpsbcdepay->stream_align =
+      gst_audio_stream_align_new (48000, 40 * GST_MSECOND, 1 * GST_SECOND);
+  rtpsbcdepay->ignore_timestamps = DEFAULT_IGNORE_TIMESTAMPS;
 }
 
 static void
@@ -102,11 +125,44 @@ gst_rtp_sbc_depay_finalize (GObject * object)
 {
   GstRtpSbcDepay *depay = GST_RTP_SBC_DEPAY (object);
 
+  gst_audio_stream_align_free (depay->stream_align);
   gst_object_unref (depay->adapter);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+static void
+gst_rtp_sbc_depay_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstRtpSbcDepay *depay = GST_RTP_SBC_DEPAY (object);
+
+  switch (prop_id) {
+    case PROP_IGNORE_TIMESTAMPS:
+      depay->ignore_timestamps = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtp_sbc_depay_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstRtpSbcDepay *depay = GST_RTP_SBC_DEPAY (object);
+
+  switch (prop_id) {
+    case PROP_IGNORE_TIMESTAMPS:
+      g_value_set_boolean (value, depay->ignore_timestamps);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
 /* FIXME: This duplicates similar functionality rtpsbcpay, but there isn't a
  * simple way to consolidate the two. This is best done by moving the function
  * to the codec-utils library in gst-plugins-base when these elements move to
@@ -184,6 +240,9 @@ gst_rtp_sbc_depay_setcaps (GstRTPBaseDepayload * base, GstCaps * caps)
   if (oldcaps)
     gst_caps_unref (oldcaps);
 
+  /* Reset when the caps are changing */
+  gst_audio_stream_align_set_rate (depay->stream_align, depay->rate);
+
   return TRUE;
 
 bad_caps:
@@ -202,6 +261,9 @@ gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base, GstRTPBuffer * rtp)
   guint8 nframes;
   guint8 *payload;
   guint payload_len;
+  gint samples = 0;
+
+  GstClockTime timestamp;
 
   GST_LOG_OBJECT (depay, "Got %" G_GSIZE_FORMAT " bytes",
       gst_buffer_get_size (rtp->buffer));
@@ -212,6 +274,27 @@ gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base, GstRTPBuffer * rtp)
     goto bad_packet;
   }
 
+  timestamp = GST_BUFFER_DTS_OR_PTS (rtp->buffer);
+  if (depay->ignore_timestamps && timestamp == GST_CLOCK_TIME_NONE) {
+    GstClockTime initial_timestamp;
+    guint64 n_samples;
+
+    initial_timestamp =
+        gst_audio_stream_align_get_timestamp_at_discont (depay->stream_align);
+    n_samples =
+        gst_audio_stream_align_get_samples_since_discont (depay->stream_align);
+
+    if (initial_timestamp == GST_CLOCK_TIME_NONE) {
+      GST_ERROR_OBJECT (depay,
+          "Can only ignore timestamps on streams without valid initial timestamp");
+      return NULL;
+    }
+
+    timestamp =
+        initial_timestamp + gst_util_uint64_scale (n_samples, GST_SECOND,
+        depay->rate);
+  }
+
   payload = gst_rtp_buffer_get_payload (rtp);
   payload_len = gst_rtp_buffer_get_payload_len (rtp);
 
@@ -243,15 +326,25 @@ gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base, GstRTPBuffer * rtp)
     gst_adapter_push (depay->adapter, data);
 
     if (last) {
+      gint framelen, samples;
+      guint8 header[4];
+
       data = gst_adapter_take_buffer (depay->adapter,
           gst_adapter_available (depay->adapter));
       gst_rtp_drop_non_audio_meta (depay, data);
-    } else
-      data = NULL;
 
+      if (gst_buffer_extract (data, 0, &header, 4) != 4 ||
+          gst_rtp_sbc_depay_get_params (depay, header,
+              payload_len, &framelen, &samples) < 0) {
+        gst_buffer_unref (data);
+        goto bad_packet;
+      }
+    } else {
+      data = NULL;
+    }
   } else {
     /* !fragment */
-    gint framelen, samples;
+    gint framelen;
 
     GST_LOG_OBJECT (depay, "Got %d frames", nframes);
 
@@ -261,6 +354,8 @@ gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base, GstRTPBuffer * rtp)
       goto bad_packet;
     }
 
+    samples *= nframes;
+
     GST_LOG_OBJECT (depay, "Got payload of %d", payload_len);
 
     if (nframes * framelen > (gint) payload_len) {
@@ -271,6 +366,18 @@ gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base, GstRTPBuffer * rtp)
     }
   }
 
+  if (depay->ignore_timestamps) {
+    GstClockTime duration;
+
+    gst_audio_stream_align_process (depay->stream_align,
+        GST_BUFFER_IS_DISCONT (rtp->buffer), timestamp, samples, &timestamp,
+        &duration, NULL);
+
+    GST_BUFFER_PTS (data) = timestamp;
+    GST_BUFFER_DTS (data) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_DURATION (data) = duration;
+  }
+
 out:
   return data;
 
index 4147a31..9cac3ad 100644 (file)
@@ -27,6 +27,7 @@
 #include <gst/gst.h>
 #include <gst/base/gstadapter.h>
 #include <gst/rtp/gstrtpbasedepayload.h>
+#include <gst/audio/audio.h>
 
 G_BEGIN_DECLS
 #define GST_TYPE_RTP_SBC_DEPAY \
@@ -50,6 +51,10 @@ struct _GstRtpSbcDepay
 
   int rate;
   GstAdapter *adapter;
+  gboolean ignore_timestamps;
+
+  /* Timestamp tracking when ignoring input timestamps */
+  GstAudioStreamAlign *stream_align;
 };
 
 struct _GstRtpSbcDepayClass