mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-02 20:42:30 +00:00
audiorate: accumulate offset by time diff
The fomula, 'offset = time / rate', is correct only if the rate is never changed. When the rate is changed, the offset should be re-calculated based on the previous offset. https://bugzilla.gnome.org/show_bug.cgi?id=791269
This commit is contained in:
parent
83c7dd2335
commit
4fa850e3e6
4 changed files with 133 additions and 5 deletions
|
@ -499,9 +499,21 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
|
||||||
audiorate->in += in_samples;
|
audiorate->in += in_samples;
|
||||||
|
|
||||||
/* calculate the buffer offset */
|
/* calculate the buffer offset */
|
||||||
in_offset = gst_util_uint64_scale_int_round (in_time, rate, GST_SECOND);
|
if (in_time < audiorate->prev_in_time) {
|
||||||
|
in_offset = audiorate->prev_in_offset -
|
||||||
|
gst_util_uint64_scale_int_round (audiorate->prev_in_time - in_time,
|
||||||
|
rate, GST_SECOND);
|
||||||
|
} else {
|
||||||
|
in_offset =
|
||||||
|
audiorate->prev_in_offset +
|
||||||
|
gst_util_uint64_scale_int_round (in_time - audiorate->prev_in_time,
|
||||||
|
rate, GST_SECOND);
|
||||||
|
}
|
||||||
in_offset_end = in_offset + in_samples;
|
in_offset_end = in_offset + in_samples;
|
||||||
|
|
||||||
|
audiorate->prev_in_offset = in_offset;
|
||||||
|
audiorate->prev_in_time = in_time;
|
||||||
|
|
||||||
GST_LOG_OBJECT (audiorate,
|
GST_LOG_OBJECT (audiorate,
|
||||||
"in_time:%" GST_TIME_FORMAT ", in_duration:%" GST_TIME_FORMAT
|
"in_time:%" GST_TIME_FORMAT ", in_duration:%" GST_TIME_FORMAT
|
||||||
", in_size:%u, in_offset:%" G_GUINT64_FORMAT ", in_offset_end:%"
|
", in_size:%u, in_offset:%" G_GUINT64_FORMAT ", in_offset_end:%"
|
||||||
|
@ -566,9 +578,8 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
|
||||||
* offset to get duration. Necessary complexity to get 'perfect'
|
* offset to get duration. Necessary complexity to get 'perfect'
|
||||||
* streams */
|
* streams */
|
||||||
GST_BUFFER_TIMESTAMP (fill) = audiorate->next_ts;
|
GST_BUFFER_TIMESTAMP (fill) = audiorate->next_ts;
|
||||||
audiorate->next_ts =
|
audiorate->next_ts +=
|
||||||
gst_util_uint64_scale_int_round (audiorate->next_offset, GST_SECOND,
|
gst_util_uint64_scale_int_round (cursamples, GST_SECOND, rate);
|
||||||
rate);
|
|
||||||
GST_BUFFER_DURATION (fill) =
|
GST_BUFFER_DURATION (fill) =
|
||||||
audiorate->next_ts - GST_BUFFER_TIMESTAMP (fill);
|
audiorate->next_ts - GST_BUFFER_TIMESTAMP (fill);
|
||||||
|
|
||||||
|
@ -641,7 +652,8 @@ send:
|
||||||
GST_BUFFER_OFFSET_END (buf) = in_offset_end;
|
GST_BUFFER_OFFSET_END (buf) = in_offset_end;
|
||||||
|
|
||||||
GST_BUFFER_TIMESTAMP (buf) = audiorate->next_ts;
|
GST_BUFFER_TIMESTAMP (buf) = audiorate->next_ts;
|
||||||
audiorate->next_ts = gst_util_uint64_scale_int_round (in_offset_end,
|
audiorate->next_ts +=
|
||||||
|
gst_util_uint64_scale_int_round (in_offset_end - audiorate->next_offset,
|
||||||
GST_SECOND, rate);
|
GST_SECOND, rate);
|
||||||
GST_BUFFER_DURATION (buf) = audiorate->next_ts - GST_BUFFER_TIMESTAMP (buf);
|
GST_BUFFER_DURATION (buf) = audiorate->next_ts - GST_BUFFER_TIMESTAMP (buf);
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,9 @@ struct _GstAudioRate
|
||||||
guint64 next_offset;
|
guint64 next_offset;
|
||||||
guint64 next_ts;
|
guint64 next_ts;
|
||||||
|
|
||||||
|
GstClockTime prev_in_time;
|
||||||
|
guint64 prev_in_offset;
|
||||||
|
|
||||||
gboolean discont;
|
gboolean discont;
|
||||||
|
|
||||||
gboolean new_segment;
|
gboolean new_segment;
|
||||||
|
|
|
@ -679,6 +679,7 @@ elements_audiointerleave_CFLAGS = \
|
||||||
elements_audiorate_LDADD = \
|
elements_audiorate_LDADD = \
|
||||||
$(top_builddir)/gst-libs/gst/audio/libgstaudio-@GST_API_VERSION@.la \
|
$(top_builddir)/gst-libs/gst/audio/libgstaudio-@GST_API_VERSION@.la \
|
||||||
$(GST_BASE_LIBS) \
|
$(GST_BASE_LIBS) \
|
||||||
|
$(top_builddir)/gst-libs/gst/app/libgstapp-@GST_API_VERSION@.la \
|
||||||
$(LDADD)
|
$(LDADD)
|
||||||
elements_audiorate_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(CFLAGS) $(AM_CFLAGS)
|
elements_audiorate_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(CFLAGS) $(AM_CFLAGS)
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include <gst/check/gstcheck.h>
|
#include <gst/check/gstcheck.h>
|
||||||
#include <gst/audio/audio.h>
|
#include <gst/audio/audio.h>
|
||||||
|
#include <gst/app/gstappsrc.h>
|
||||||
|
|
||||||
/* helper element to insert additional buffers overlapping with previous ones */
|
/* helper element to insert additional buffers overlapping with previous ones */
|
||||||
static gdouble injector_inject_probability = 0.0;
|
static gdouble injector_inject_probability = 0.0;
|
||||||
|
@ -451,6 +452,116 @@ GST_START_TEST (test_large_discont)
|
||||||
|
|
||||||
GST_END_TEST;
|
GST_END_TEST;
|
||||||
|
|
||||||
|
|
||||||
|
#define FIRST_CAPS \
|
||||||
|
"audio/x-raw,format=S16LE,layout=interleaved,rate=48000,channels=1"
|
||||||
|
#define SECOND_CAPS \
|
||||||
|
"audio/x-raw,format=S16LE,layout=interleaved,rate=8000,channels=1"
|
||||||
|
|
||||||
|
#define BUFFERS_BEFORE_CHANGE 10
|
||||||
|
#define TOTAL_BUFFERS (BUFFERS_BEFORE_CHANGE * 2)
|
||||||
|
|
||||||
|
static GList *
|
||||||
|
generate_buffers (gint from_rate, gint to_rate)
|
||||||
|
{
|
||||||
|
GQueue q = G_QUEUE_INIT;
|
||||||
|
GstBuffer *buf;
|
||||||
|
guint i;
|
||||||
|
GstClockTime pts = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < BUFFERS_BEFORE_CHANGE; i++) {
|
||||||
|
buf = gst_buffer_new_allocate (NULL, 2 * from_rate / 100, NULL);
|
||||||
|
gst_buffer_memset (buf, 0, 1, gst_buffer_get_size (buf));
|
||||||
|
GST_BUFFER_PTS (buf) = pts;
|
||||||
|
GST_BUFFER_DURATION (buf) = GST_SECOND / 100;
|
||||||
|
pts += GST_BUFFER_DURATION (buf);
|
||||||
|
g_queue_push_tail (&q, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < TOTAL_BUFFERS; i++) {
|
||||||
|
buf = gst_buffer_new_allocate (NULL, 2 * to_rate / 100, NULL);
|
||||||
|
gst_buffer_memset (buf, 0, 1, gst_buffer_get_size (buf));
|
||||||
|
GST_BUFFER_PTS (buf) = pts;
|
||||||
|
GST_BUFFER_DURATION (buf) = GST_SECOND / 100;
|
||||||
|
pts += GST_BUFFER_DURATION (buf);
|
||||||
|
g_queue_push_tail (&q, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.head;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_START_TEST (test_rate_change_down)
|
||||||
|
{
|
||||||
|
GList *l, *rbufs = NULL, *bufs = NULL;
|
||||||
|
GstElement *pipeline;
|
||||||
|
GstElement *sink;
|
||||||
|
GstElement *src;
|
||||||
|
GstElement *audiorate;
|
||||||
|
GstCaps *caps1, *caps2;
|
||||||
|
int i = 0;
|
||||||
|
gint64 drop, in, out;
|
||||||
|
GstBus *bus;
|
||||||
|
|
||||||
|
caps1 = gst_caps_from_string (FIRST_CAPS);
|
||||||
|
caps2 = gst_caps_from_string (SECOND_CAPS);
|
||||||
|
|
||||||
|
bufs = generate_buffers (48000, 8000);
|
||||||
|
|
||||||
|
pipeline =
|
||||||
|
gst_parse_launch
|
||||||
|
("appsrc name=src is-live=true format=time !"
|
||||||
|
" audiorate name=audiorate ! fakesink name=sink signal-handoffs=true",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
fail_if (pipeline == NULL);
|
||||||
|
|
||||||
|
sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
|
||||||
|
g_signal_connect (sink, "handoff", G_CALLBACK (got_buf), &rbufs);
|
||||||
|
gst_object_unref (sink);
|
||||||
|
|
||||||
|
src = gst_bin_get_by_name (GST_BIN (pipeline), "src");
|
||||||
|
gst_app_src_set_caps (GST_APP_SRC (src), caps1);
|
||||||
|
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||||
|
|
||||||
|
for (l = bufs; l != NULL; l = l->next) {
|
||||||
|
if (i++ == BUFFERS_BEFORE_CHANGE) {
|
||||||
|
gst_app_src_set_caps (GST_APP_SRC (src), caps2);
|
||||||
|
}
|
||||||
|
GST_LOG ("Position: %" GST_TIME_FORMAT " Duration: %" GST_TIME_FORMAT "\n",
|
||||||
|
GST_TIME_ARGS (GST_BUFFER_PTS (l->data)),
|
||||||
|
GST_TIME_ARGS (GST_BUFFER_DURATION (l->data)));
|
||||||
|
fail_unless_equals_int (gst_app_src_push_buffer (GST_APP_SRC (src),
|
||||||
|
GST_BUFFER (l->data)), GST_FLOW_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_app_src_end_of_stream (GST_APP_SRC (src));
|
||||||
|
gst_object_unref (src);
|
||||||
|
|
||||||
|
/* Give some time to the appsrc loop to push the buffers */
|
||||||
|
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
||||||
|
gst_message_unref (gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
|
||||||
|
GST_MESSAGE_EOS));
|
||||||
|
gst_object_unref (bus);
|
||||||
|
|
||||||
|
audiorate = gst_bin_get_by_name (GST_BIN (pipeline), "audiorate");
|
||||||
|
g_object_get (audiorate, "drop", &drop, "out", &out, "in", &in, NULL);
|
||||||
|
gst_object_unref (audiorate);
|
||||||
|
|
||||||
|
fail_unless_equals_int64 (drop, 0);
|
||||||
|
|
||||||
|
g_list_foreach (rbufs, (GFunc) gst_mini_object_unref, NULL);
|
||||||
|
g_list_free (rbufs);
|
||||||
|
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||||
|
gst_object_unref (pipeline);
|
||||||
|
|
||||||
|
gst_caps_unref (caps1);
|
||||||
|
gst_caps_unref (caps2);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
static Suite *
|
static Suite *
|
||||||
audiorate_suite (void)
|
audiorate_suite (void)
|
||||||
{
|
{
|
||||||
|
@ -467,6 +578,7 @@ audiorate_suite (void)
|
||||||
tcase_add_test (tc_chain, test_perfect_stream_inject90);
|
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_perfect_stream_drop45_inject25);
|
||||||
tcase_add_test (tc_chain, test_large_discont);
|
tcase_add_test (tc_chain, test_large_discont);
|
||||||
|
tcase_add_test (tc_chain, test_rate_change_down);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue