From e3ef9cd15d1f0fc55702c29cfb60338325b54065 Mon Sep 17 00:00:00 2001 From: Julien Moutte Date: Wed, 14 Mar 2007 17:16:30 +0000 Subject: gst/audioresample/gstaudioresample.c: Handle discontinuous streams. Original commit message from CVS: 2007-03-14 Julien MOUTTE * 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 | 55 +++++++++++++++++++---- gst/audioresample/gstaudioresample.h | 2 + tests/check/elements/audioresample.c | 85 ++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 9 deletions(-) diff --git a/gst/audioresample/gstaudioresample.c b/gst/audioresample/gstaudioresample.c index d670858e..8e9e7682 100644 --- a/gst/audioresample/gstaudioresample.c +++ b/gst/audioresample/gstaudioresample.c @@ -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); diff --git a/gst/audioresample/gstaudioresample.h b/gst/audioresample/gstaudioresample.h index 8baa2c90..c969ccdb 100644 --- a/gst/audioresample/gstaudioresample.h +++ b/gst/audioresample/gstaudioresample.h @@ -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; diff --git a/tests/check/elements/audioresample.c b/tests/check/elements/audioresample.c index bd5bf424..78e46540 100644 --- a/tests/check/elements/audioresample.c +++ b/tests/check/elements/audioresample.c @@ -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; -- cgit v1.2.1