gst/audioresample/gstaudioresample.c: Handle discontinuous streams.
authorJulien Moutte <julien@moutte.net>
Wed, 14 Mar 2007 17:16:30 +0000 (17:16 +0000)
committerJulien Moutte <julien@moutte.net>
Wed, 14 Mar 2007 17:16:30 +0000 (17:16 +0000)
Original commit message from CVS:
2007-03-14  Julien MOUTTE  <julien@moutte.net>

* gst/audioresample/gstaudioresample.c: (gst_audioresample_init),
(audioresample_transform_size), (audioresample_do_output),
(audioresample_transform), (audioresample_pushthrough): Handle
discontinuous streams.
* gst/audioresample/gstaudioresample.h:
* tests/check/elements/audioresample.c:
(test_discont_stream_instance), (GST_START_TEST),
(audioresample_suite): Add a test for discontinuous streams.
* win32/common/config.h: Updated.

gst/audioresample/gstaudioresample.c
gst/audioresample/gstaudioresample.h
tests/check/elements/audioresample.c

index d670858..8e9e768 100644 (file)
@@ -194,6 +194,8 @@ gst_audioresample_init (GstAudioresample * audioresample,
   gst_pad_set_bufferalloc_function (trans->sinkpad, NULL);
 
   audioresample->filter_length = DEFAULT_FILTERLEN;
+
+  audioresample->need_discont = FALSE;
 }
 
 /* vmethods */
@@ -371,7 +373,7 @@ audioresample_transform_size (GstBaseTransform * base,
   gboolean use_internal = FALSE;        /* whether we use the internal state */
   gboolean ret = TRUE;
 
-  GST_DEBUG_OBJECT (base, "asked to transform size %d in direction %s",
+  GST_LOG_OBJECT (base, "asked to transform size %d in direction %s",
       size, direction == GST_PAD_SINK ? "SINK" : "SRC");
   if (direction == GST_PAD_SINK) {
     sinkcaps = caps;
@@ -406,7 +408,7 @@ audioresample_transform_size (GstBaseTransform * base,
 
   /* we make room for one extra sample, given that the resampling filter
    * can output an extra one for non-integral i_rate/o_rate */
-  GST_DEBUG_OBJECT (base, "transformed size %d to %d", size, *othersize);
+  GST_LOG_OBJECT (base, "transformed size %d to %d", size, *othersize);
 
   if (!use_internal) {
     resample_free (state);
@@ -492,8 +494,7 @@ audioresample_do_output (GstAudioresample * audioresample, GstBuffer * outbuf)
   r = audioresample->resample;
 
   outsize = resample_get_output_size (r);
-  GST_DEBUG_OBJECT (audioresample, "audioresample can give me %d bytes",
-      outsize);
+  GST_LOG_OBJECT (audioresample, "audioresample can give me %d bytes", outsize);
 
   /* protect against mem corruption */
   if (outsize > GST_BUFFER_SIZE (outbuf)) {
@@ -556,6 +557,13 @@ audioresample_do_output (GstAudioresample * audioresample, GstBuffer * outbuf)
   }
   GST_BUFFER_SIZE (outbuf) = outsize;
 
+  if (G_UNLIKELY (audioresample->need_discont)) {
+    GST_DEBUG_OBJECT (audioresample,
+        "marking this buffer with the DISCONT flag");
+    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+    audioresample->need_discont = FALSE;
+  }
+
   GST_LOG_OBJECT (audioresample, "transformed to buffer of %ld bytes, ts %"
       GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
       G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
@@ -591,6 +599,25 @@ audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf,
       GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)),
       GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf));
 
+  /* check for timestamp discontinuities and flush/reset if needed */
+  if (GST_CLOCK_TIME_IS_VALID (audioresample->prev_ts) &&
+      GST_CLOCK_TIME_IS_VALID (audioresample->prev_duration)) {
+    GstClockTime ts_expected = audioresample->prev_ts +
+        audioresample->prev_duration;
+    GstClockTimeDiff ts_diff = GST_CLOCK_DIFF (ts_expected, timestamp);
+
+    if (G_UNLIKELY (ts_diff != 0)) {
+      GST_WARNING_OBJECT (audioresample,
+          "encountered timestamp discontinuity of %" G_GINT64_FORMAT, ts_diff);
+      /* Flush internal samples */
+      audioresample_pushthrough (audioresample);
+      /* Inform downstream element about discontinuity */
+      audioresample->need_discont = TRUE;
+      /* We want to recalculate the offset */
+      audioresample->ts_offset = -1;
+    }
+  }
+
   if (audioresample->ts_offset == -1) {
     /* if we don't know the initial offset yet, calculate it based on the 
      * input timestamp. */
@@ -610,6 +637,8 @@ audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf,
           gst_util_uint64_scale_int (stime, r->o_rate, GST_SECOND);
     }
   }
+  audioresample->prev_ts = timestamp;
+  audioresample->prev_duration = GST_BUFFER_DURATION (inbuf);
 
   /* need to memdup, resample takes ownership. */
   datacopy = g_memdup (data, size);
@@ -631,16 +660,24 @@ audioresample_pushthrough (GstAudioresample * audioresample)
   r = audioresample->resample;
 
   outsize = resample_get_output_size (r);
-  if (outsize == 0)
+  if (outsize == 0) {
+    GST_DEBUG_OBJECT (audioresample, "no internal buffers needing flush");
     goto done;
+  }
 
-  outbuf = gst_buffer_new_and_alloc (outsize);
+  trans = GST_BASE_TRANSFORM (audioresample);
 
-  res = audioresample_do_output (audioresample, outbuf);
-  if (res != GST_FLOW_OK)
+  res = gst_pad_alloc_buffer (trans->srcpad, GST_BUFFER_OFFSET_NONE, outsize,
+      GST_PAD_CAPS (trans->srcpad), &outbuf);
+  if (G_UNLIKELY (res != GST_FLOW_OK)) {
+    GST_WARNING_OBJECT (audioresample, "failed allocating buffer of %d bytes",
+        outsize);
     goto done;
+  }
 
-  trans = GST_BASE_TRANSFORM (audioresample);
+  res = audioresample_do_output (audioresample, outbuf);
+  if (G_UNLIKELY (res != GST_FLOW_OK))
+    goto done;
 
   res = gst_pad_push (trans->srcpad, outbuf);
 
index 8baa2c9..c969ccd 100644 (file)
@@ -53,10 +53,12 @@ struct _GstAudioresample {
   GstCaps *srccaps, *sinkcaps;
 
   gboolean passthru;
+  gboolean need_discont;
 
   guint64 offset;
   guint64 ts_offset;
   GstClockTime next_ts;
+  GstClockTime prev_ts, prev_duration;
   int channels;
 
   int i_rate;
index bd5bf42..78e4654 100644 (file)
@@ -144,6 +144,7 @@ fail_unless_perfect_stream ()
   buffers = NULL;
 }
 
+/* this tests that the output is a perfect stream if the input is */
 static void
 test_perfect_stream_instance (int inrate, int outrate, int samples,
     int numbuffers)
@@ -224,6 +225,89 @@ GST_START_TEST (test_perfect_stream)
 
 GST_END_TEST;
 
+/* this tests that the output is a correct discontinuous stream
+ * if the input is; ie input drops in time come out the same way */
+static void
+test_discont_stream_instance (int inrate, int outrate, int samples,
+    int numbuffers)
+{
+  GstElement *audioresample;
+  GstBuffer *inbuffer, *outbuffer;
+  GstCaps *caps;
+  GstClockTime ints;
+
+  int i, j;
+  gint16 *p;
+
+  audioresample = setup_audioresample (2, inrate, outrate);
+  caps = gst_pad_get_negotiated_caps (mysrcpad);
+  fail_unless (gst_caps_is_fixed (caps));
+
+  fail_unless (gst_element_set_state (audioresample,
+          GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+      "could not set to playing");
+
+  for (j = 1; j <= numbuffers; ++j) {
+
+    inbuffer = gst_buffer_new_and_alloc (samples * 4);
+    GST_BUFFER_DURATION (inbuffer) = samples * GST_SECOND / inrate;
+    /* "drop" half the buffers */
+    ints = GST_BUFFER_DURATION (inbuffer) * 2 * (j - 1);
+    GST_BUFFER_TIMESTAMP (inbuffer) = ints;
+    GST_BUFFER_OFFSET (inbuffer) = (j - 1) * 2 * samples;
+    GST_BUFFER_OFFSET_END (inbuffer) = j * 2 * samples + samples;
+
+    gst_buffer_set_caps (inbuffer, caps);
+
+    p = (gint16 *) GST_BUFFER_DATA (inbuffer);
+
+    /* create a 16 bit signed ramp */
+    for (i = 0; i < samples; ++i) {
+      *p = -32767 + i * (65535 / samples);
+      ++p;
+      *p = -32767 + i * (65535 / samples);
+      ++p;
+    }
+
+    /* pushing gives away my reference ... */
+    fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+
+    /* check if the timestamp of the pushed buffer matches the incoming one */
+    outbuffer = g_list_nth_data (buffers, g_list_length (buffers) - 1);
+    fail_if (outbuffer == NULL);
+    fail_unless_equals_uint64 (ints, GST_BUFFER_TIMESTAMP (outbuffer));
+    if (j > 1) {
+      fail_unless (GST_BUFFER_IS_DISCONT (outbuffer),
+          "expected discont buffer");
+    }
+  }
+
+  /* cleanup */
+  gst_caps_unref (caps);
+  cleanup_audioresample (audioresample);
+}
+
+GST_START_TEST (test_discont_stream)
+{
+  /* integral scalings */
+  test_discont_stream_instance (48000, 24000, 500, 20);
+  test_discont_stream_instance (48000, 12000, 500, 20);
+  test_discont_stream_instance (12000, 24000, 500, 20);
+  test_discont_stream_instance (12000, 48000, 500, 20);
+
+  /* non-integral scalings */
+  test_discont_stream_instance (44100, 8000, 500, 20);
+  test_discont_stream_instance (8000, 44100, 500, 20);
+
+  /* wacky scalings */
+  test_discont_stream_instance (12345, 54321, 500, 20);
+  test_discont_stream_instance (101, 99, 500, 20);
+}
+
+GST_END_TEST;
+
+
+
 GST_START_TEST (test_reuse)
 {
   GstElement *audioresample;
@@ -295,6 +379,7 @@ audioresample_suite (void)
 
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_perfect_stream);
+  tcase_add_test (tc_chain, test_discont_stream);
   tcase_add_test (tc_chain, test_reuse);
 
   return s;