From 9f9e76bc9996641bef18fadebf25e2ed79090990 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Thu, 9 Aug 2007 15:44:02 +0000 Subject: [PATCH] gst/audiorate/gstaudiorate.c: If we have a large (> 1 second) discontinuity, push a series of smaller buffers rather ... Original commit message from CVS: * gst/audiorate/gstaudiorate.c: (gst_audio_rate_chain): If we have a large (> 1 second) discontinuity, push a series of smaller buffers rather than a single very large buffer. Avoids unreasonably large single buffer allocations when encountering a large gap. * tests/check/elements/audiorate.c: (GST_START_TEST), (audiorate_suite): Add a test for this. --- ChangeLog | 11 +++++ gst/audiorate/gstaudiorate.c | 77 +++++++++++++++++++------------- tests/check/elements/audiorate.c | 63 +++++++++++++++++++++++++- 3 files changed, 119 insertions(+), 32 deletions(-) diff --git a/ChangeLog b/ChangeLog index 82b0cb26e5..068b84d1fc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2007-08-09 Michael Smith + + * gst/audiorate/gstaudiorate.c: (gst_audio_rate_chain): + If we have a large (> 1 second) discontinuity, push a series of + smaller buffers rather than a single very large buffer. Avoids + unreasonably large single buffer allocations when encountering a + large gap. + * tests/check/elements/audiorate.c: (GST_START_TEST), + (audiorate_suite): + Add a test for this. + 2007-08-09 Jan Schmidt * gst/playback/gstplaybasebin.c: (group_commit), diff --git a/gst/audiorate/gstaudiorate.c b/gst/audiorate/gstaudiorate.c index 5b8125c2f5..8662adcd31 100644 --- a/gst/audiorate/gstaudiorate.c +++ b/gst/audiorate/gstaudiorate.c @@ -543,45 +543,60 @@ gst_audio_rate_chain (GstPad * pad, GstBuffer * buf) gint fillsize; guint64 fillsamples; + /* We don't want to allocate a single unreasonably huge buffer - it might + be hundreds of megabytes. So, limit each output buffer to one second of + audio */ fillsamples = in_offset - audiorate->next_offset; - fillsize = fillsamples * audiorate->bytes_per_sample; - fill = gst_buffer_new_and_alloc (fillsize); - /* FIXME, 0 might not be the silence byte for the negotiated format. */ - memset (GST_BUFFER_DATA (fill), 0, fillsize); + while (fillsamples > 0) { + guint64 cursamples = MIN (fillsamples, audiorate->rate); - GST_DEBUG_OBJECT (audiorate, "inserting %lld samples", fillsamples); + GST_DEBUG_OBJECT (audiorate, + "inserting %lld samples of total %lld at ts %lld", cursamples, + fillsamples, audiorate->next_ts); - GST_BUFFER_OFFSET (fill) = audiorate->next_offset; - audiorate->next_offset += fillsamples; - GST_BUFFER_OFFSET_END (fill) = audiorate->next_offset; + fillsamples -= cursamples; + fillsize = cursamples * audiorate->bytes_per_sample; - /* Use next timestamp, then calculate following timestamp based on in_offset - * to get duration. Neccesary complexity to get 'perfect' streams */ - GST_BUFFER_TIMESTAMP (fill) = audiorate->next_ts; - audiorate->next_ts = gst_util_uint64_scale_int (in_offset, - GST_SECOND, audiorate->rate); - GST_BUFFER_DURATION (fill) = audiorate->next_ts - - GST_BUFFER_TIMESTAMP (fill); + fill = gst_buffer_new_and_alloc (fillsize); + /* FIXME, 0 might not be the silence byte for the negotiated format. */ + memset (GST_BUFFER_DATA (fill), 0, fillsize); - /* we created this buffer to fill a gap */ - GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_GAP); - /* set discont if it's pending, this is mostly done for the first buffer and - * after a flushing seek */ - if (audiorate->discont) { - GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_DISCONT); - audiorate->discont = FALSE; + GST_DEBUG_OBJECT (audiorate, "inserting %lld samples", cursamples); + + GST_BUFFER_OFFSET (fill) = audiorate->next_offset; + audiorate->next_offset += cursamples; + GST_BUFFER_OFFSET_END (fill) = audiorate->next_offset; + + /* Use next timestamp, then calculate following timestamp based on + * offset to get duration. Neccesary complexity to get 'perfect' + * streams */ + GST_BUFFER_TIMESTAMP (fill) = audiorate->next_ts; + audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset, + GST_SECOND, audiorate->rate); + GST_BUFFER_DURATION (fill) = audiorate->next_ts - + GST_BUFFER_TIMESTAMP (fill); + + /* we created this buffer to fill a gap */ + GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_GAP); + /* set discont if it's pending, this is mostly done for the first buffer + * and after a flushing seek */ + if (audiorate->discont) { + GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_DISCONT); + audiorate->discont = FALSE; + } + gst_buffer_set_caps (fill, GST_PAD_CAPS (audiorate->srcpad)); + + ret = gst_pad_push (audiorate->srcpad, fill); + if (ret != GST_FLOW_OK) + goto beach; + audiorate->out++; + audiorate->add += cursamples; + + if (!audiorate->silent) + g_object_notify (G_OBJECT (audiorate), "add"); } - gst_buffer_set_caps (fill, GST_PAD_CAPS (audiorate->srcpad)); - ret = gst_pad_push (audiorate->srcpad, fill); - if (ret != GST_FLOW_OK) - goto beach; - audiorate->out++; - audiorate->add += fillsamples; - - if (!audiorate->silent) - g_object_notify (G_OBJECT (audiorate), "add"); } else if (in_offset < audiorate->next_offset) { /* need to remove samples */ if (in_offset_end <= audiorate->next_offset) { diff --git a/tests/check/elements/audiorate.c b/tests/check/elements/audiorate.c index 6f35730ee2..2cfc759635 100644 --- a/tests/check/elements/audiorate.c +++ b/tests/check/elements/audiorate.c @@ -25,7 +25,6 @@ #include /* helper element to insert additional buffers overlapping with previous ones */ - static gdouble injector_inject_probability = 0.0; typedef GstElement TestInjector; @@ -386,6 +385,67 @@ GST_END_TEST; /* TODO: also do all tests with channels=1 and channels=2 */ +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float,channels=1,rate=44100,width=32") + ); + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float,channels=1,rate=44100,width=32") + ); + +GST_START_TEST (test_large_discont) +{ + GstElement *audiorate; + GstCaps *caps; + GstPad *srcpad, *sinkpad; + GstBuffer *buf; + + audiorate = gst_check_setup_element ("audiorate"); + caps = gst_caps_new_simple ("audio/x-raw-float", + "channels", G_TYPE_INT, 1, + "rate", G_TYPE_INT, 44100, "width", G_TYPE_INT, 32, NULL); + + srcpad = gst_check_setup_src_pad (audiorate, &srctemplate, caps); + sinkpad = gst_check_setup_sink_pad (audiorate, &sinktemplate, caps); + + gst_pad_set_active (srcpad, TRUE); + gst_pad_set_active (sinkpad, TRUE); + + fail_unless (gst_element_set_state (audiorate, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "failed to set audiorate playing"); + + buf = gst_buffer_new_and_alloc (4); + gst_buffer_set_caps (buf, caps); + GST_BUFFER_TIMESTAMP (buf) = 0; + gst_pad_push (srcpad, buf); + + fail_unless_equals_int (g_list_length (buffers), 1); + + buf = gst_buffer_new_and_alloc (4); + gst_buffer_set_caps (buf, caps); + GST_BUFFER_TIMESTAMP (buf) = 2 * GST_SECOND; + gst_pad_push (srcpad, buf); + /* Now we should have 3 more buffers: the one we injected, plus _two_ filler + * buffers, because the gap is > 1 second (but less than 2 seconds) */ + fail_unless_equals_int (g_list_length (buffers), 4); + + gst_element_set_state (audiorate, GST_STATE_NULL); + gst_caps_unref (caps); + + gst_check_teardown_sink_pad (audiorate); + gst_check_teardown_src_pad (audiorate); + + gst_object_unref (audiorate); +} + +GST_END_TEST; + + static Suite * audiorate_suite (void) { @@ -401,6 +461,7 @@ audiorate_suite (void) tcase_add_test (tc_chain, test_perfect_stream_inject10); tcase_add_test (tc_chain, test_perfect_stream_inject90); tcase_add_test (tc_chain, test_perfect_stream_drop45_inject25); + tcase_add_test (tc_chain, test_large_discont); return s; }