audiorate: accumulate offset by time diff
authorJustin Kim <justin.kim@collabora.com>
Fri, 5 Jan 2018 07:07:54 +0000 (16:07 +0900)
committerOlivier CrĂȘte <olivier.crete@collabora.com>
Thu, 19 Jul 2018 22:19:19 +0000 (18:19 -0400)
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

gst/audiorate/gstaudiorate.c
gst/audiorate/gstaudiorate.h
tests/check/Makefile.am
tests/check/elements/audiorate.c

index eceeed2..5b981bb 100644 (file)
@@ -499,9 +499,21 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
   audiorate->in += in_samples;
 
   /* 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;
 
+  audiorate->prev_in_offset = in_offset;
+  audiorate->prev_in_time = in_time;
+
   GST_LOG_OBJECT (audiorate,
       "in_time:%" GST_TIME_FORMAT ", in_duration:%" GST_TIME_FORMAT
       ", 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' 
        * streams */
       GST_BUFFER_TIMESTAMP (fill) = audiorate->next_ts;
-      audiorate->next_ts =
-          gst_util_uint64_scale_int_round (audiorate->next_offset, GST_SECOND,
-          rate);
+      audiorate->next_ts +=
+          gst_util_uint64_scale_int_round (cursamples, GST_SECOND, rate);
       GST_BUFFER_DURATION (fill) =
           audiorate->next_ts - GST_BUFFER_TIMESTAMP (fill);
 
@@ -641,7 +652,8 @@ send:
   GST_BUFFER_OFFSET_END (buf) = in_offset_end;
 
   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_BUFFER_DURATION (buf) = audiorate->next_ts - GST_BUFFER_TIMESTAMP (buf);
 
index c7dfd23..81eb87f 100644 (file)
@@ -63,6 +63,9 @@ struct _GstAudioRate
   guint64 next_offset;
   guint64 next_ts;
 
+  GstClockTime prev_in_time;
+  guint64 prev_in_offset;
+
   gboolean discont;
 
   gboolean new_segment;
index 7f46b91..2a95f15 100644 (file)
@@ -679,6 +679,7 @@ elements_audiointerleave_CFLAGS = \
 elements_audiorate_LDADD = \
        $(top_builddir)/gst-libs/gst/audio/libgstaudio-@GST_API_VERSION@.la \
        $(GST_BASE_LIBS) \
+       $(top_builddir)/gst-libs/gst/app/libgstapp-@GST_API_VERSION@.la \
        $(LDADD)
 elements_audiorate_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(CFLAGS) $(AM_CFLAGS)
 
index 7664cb0..0fabf93 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <gst/check/gstcheck.h>
 #include <gst/audio/audio.h>
+#include <gst/app/gstappsrc.h>
 
 /* helper element to insert additional buffers overlapping with previous ones */
 static gdouble injector_inject_probability = 0.0;
@@ -451,6 +452,116 @@ GST_START_TEST (test_large_discont)
 
 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 *
 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_drop45_inject25);
   tcase_add_test (tc_chain, test_large_discont);
+  tcase_add_test (tc_chain, test_rate_change_down);
 
   return s;
 }