audio: Add helper object for audio discontinuity detection and sample alignment
authorSebastian Dröge <sebastian@centricular.com>
Mon, 11 Sep 2017 19:49:32 +0000 (22:49 +0300)
committerSebastian Dröge <sebastian@centricular.com>
Thu, 28 Sep 2017 11:06:05 +0000 (14:06 +0300)
This is the same code that is in decklinkaudiosrc, audioringbuffer,
audiomixer and various other places. Have it once instead of copying it
everywhere.

https://bugzilla.gnome.org/show_bug.cgi?id=787560

docs/libs/gst-plugins-base-libs-sections.txt
gst-libs/gst/audio/Makefile.am
gst-libs/gst/audio/audio.h
gst-libs/gst/audio/gstaudiostreamalign.c [new file with mode: 0644]
gst-libs/gst/audio/gstaudiostreamalign.h [new file with mode: 0644]
gst-libs/gst/audio/meson.build
tests/check/libs/audio.c
win32/common/libgstaudio.def

index de28e4e..1e3190e 100644 (file)
@@ -244,6 +244,19 @@ GST_AUDIO_RESAMPLER_QUALITY_DEFAULT
 GST_AUDIO_RESAMPLER_QUALITY_MAX
 GST_AUDIO_RESAMPLER_QUALITY_MIN
 
+GstAudioStreamAlign
+gst_audio_stream_align_new
+gst_audio_stream_align_copy
+gst_audio_stream_align_free
+gst_audio_stream_align_mark_discont
+gst_audio_stream_align_process
+gst_audio_stream_align_get_alignment_threshold
+gst_audio_stream_align_set_alignment_threshold
+gst_audio_stream_align_get_discont_wait
+gst_audio_stream_align_set_discont_wait
+gst_audio_stream_align_get_rate
+gst_audio_stream_align_set_rate
+
 <SUBSECTION Standard>
 GST_TYPE_BUFFER_FORMAT
 GST_TYPE_BUFFER_FORMAT_TYPE
@@ -265,6 +278,7 @@ gst_audio_resampler_filter_interpolation_get_type
 gst_audio_resampler_filter_mode_get_type
 gst_audio_resampler_flags_get_type
 gst_audio_resampler_method_get_type
+gst_audio_stream_align_get_type
 <SUBSECTION Private>
 _GST_AUDIO_FORMAT_NE
 </SECTION>
index e6fcde1..100867d 100644 (file)
@@ -54,7 +54,8 @@ libgstaudio_@GST_API_VERSION@_la_SOURCES = \
        gstaudiosrc.c \
        gstaudioutilsprivate.c \
        streamvolume.c \
-       gstaudioiec61937.c
+       gstaudioiec61937.c \
+       gstaudiostreamalign.c
 
 nodist_libgstaudio_@GST_API_VERSION@_la_SOURCES = $(BUILT_SOURCES)
 
@@ -80,7 +81,8 @@ libgstaudio_@GST_API_VERSION@include_HEADERS = \
        gstaudiosink.h \
        gstaudiosrc.h \
        streamvolume.h \
-       gstaudioiec61937.h
+       gstaudioiec61937.h \
+       gstaudiostreamalign.h
 
 nodist_libgstaudio_@GST_API_VERSION@include_HEADERS = \
        audio-enumtypes.h
index edb8136..42d112d 100644 (file)
@@ -31,6 +31,7 @@
 #include <gst/audio/audio-quantize.h>
 #include <gst/audio/audio-converter.h>
 #include <gst/audio/audio-resampler.h>
+#include <gst/audio/gstaudiostreamalign.h>
 
 G_BEGIN_DECLS
 
diff --git a/gst-libs/gst/audio/gstaudiostreamalign.c b/gst-libs/gst/audio/gstaudiostreamalign.c
new file mode 100644 (file)
index 0000000..846a03c
--- /dev/null
@@ -0,0 +1,377 @@
+/* GStreamer
+ * Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
+ *
+ * gstaudiostreamalign.h:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstaudiostreamalign.h"
+
+/**
+ * SECTION:gstaudiostreamalign
+ * @title: GstAudioStreamAlign
+ * @short_description: Helper object for tracking audio stream alignment and discontinuities
+ *
+ * #GstAudioStreamAlign provides a helper object that helps tracking audio
+ * stream alignment and discontinuities, and detects discontinuities if
+ * possible.
+ *
+ * See gst_audio_stream_align_new() for a description of its parameters and
+ * gst_audio_stream_align_process() for the details of the processing.
+ */
+
+G_DEFINE_BOXED_TYPE (GstAudioStreamAlign, gst_audio_stream_align,
+    (GBoxedCopyFunc) gst_audio_stream_align_copy,
+    (GBoxedFreeFunc) gst_audio_stream_align_free);
+
+struct _GstAudioStreamAlign
+{
+  gint rate;
+  GstClockTime alignment_threshold;
+  GstClockTime discont_wait;
+
+  /* counter to keep track of timestamps */
+  guint64 next_offset;
+
+  /* Last time we noticed a discont */
+  GstClockTime discont_time;
+};
+
+/**
+ * gst_audio_stream_align_new:
+ * @rate: a sample rate
+ * @alignment_threshold: a alignment threshold in nanoseconds
+ * @discont_wait: discont wait in nanoseconds
+ *
+ * Allocate a new #GstAudioStreamAlign with the given configuration. All
+ * processing happens according to sample rate @rate, until
+ * gst_audio_discont_wait_set_rate() is called with a new @rate.
+ *
+ * @alignment_threshold gives the tolerance in nanoseconds after which a
+ * timestamp difference is considered a discontinuity. Once detected,
+ * @discont_wait nanoseconds have to pass without going below the threshold
+ * again until the output buffer is marked as a discontinuity. These can later
+ * be re-configured with gst_audio_stream_align_set_alignment_threshold() and
+ * gst_audio_stream_align_set_discont_wait().
+ *
+ * Returns: a new #GstAudioStreamAlign. free with gst_audio_stream_align_free().
+ *
+ * Since: 1.14
+ */
+GstAudioStreamAlign *
+gst_audio_stream_align_new (gint rate, GstClockTime alignment_threshold,
+    GstClockTime discont_wait)
+{
+  GstAudioStreamAlign *align;
+
+  g_return_val_if_fail (rate > 0, NULL);
+
+  align = g_new0 (GstAudioStreamAlign, 1);
+  align->rate = rate;
+  align->alignment_threshold = alignment_threshold;
+  align->discont_wait = discont_wait;
+
+  gst_audio_stream_align_mark_discont (align);
+
+  return align;
+}
+
+/**
+ * gst_audio_stream_align_copy:
+ * @align: a #GstAudioStreamAlign
+ *
+ * Copy a GstAudioStreamAlign structure.
+ *
+ * Returns: a new #GstAudioStreamAlign. free with gst_audio_stream_align_free.
+ *
+ * Since: 1.14
+ */
+GstAudioStreamAlign *
+gst_audio_stream_align_copy (const GstAudioStreamAlign * align)
+{
+  GstAudioStreamAlign *copy;
+
+  g_return_val_if_fail (align != NULL, NULL);
+
+  copy = g_new0 (GstAudioStreamAlign, 1);
+  *copy = *align;
+
+  return copy;
+}
+
+/**
+ * gst_audio_stream_align_free:
+ * @align: a #GstAudioStreamAlign
+ *
+ * Free a GstAudioStreamAlign structure previously allocated with gst_audio_stream_align_new()
+ * or gst_audio_stream_align_copy().
+ *
+ * Since: 1.14
+ */
+void
+gst_audio_stream_align_free (GstAudioStreamAlign * align)
+{
+  g_return_if_fail (align != NULL);
+  g_free (align);
+}
+
+/**
+ * gst_audio_discont_set_rate:
+ * @align: a #GstAudioStreamAlign
+ * @rate: a new sample rate
+ *
+ * Sets @rate as new sample rate for the following processing. If the sample
+ * rate differs this implicitely marks the next data as discontinuous.
+ *
+ * Since: 1.14
+ */
+void
+gst_audio_stream_align_set_rate (GstAudioStreamAlign * align, gint rate)
+{
+  g_return_if_fail (align != NULL);
+  g_return_if_fail (rate > 0);
+
+  if (align->rate == rate)
+    return;
+
+  align->rate = rate;
+  gst_audio_stream_align_mark_discont (align);
+}
+
+/**
+ * gst_audio_discont_get_rate:
+ * @align: a #GstAudioStreamAlign
+ *
+ * Gets the currently configured sample rate.
+ *
+ * Returns: The currently configured sample rate
+ *
+ * Since: 1.14
+ */
+gint
+gst_audio_stream_align_get_rate (GstAudioStreamAlign * align)
+{
+  g_return_val_if_fail (align != NULL, 0);
+
+  return align->rate;
+}
+
+/**
+ * gst_audio_discont_set_alignment_threshold:
+ * @align: a #GstAudioStreamAlign
+ * @alignment_treshold: a new alignment threshold
+ *
+ * Sets @alignment_treshold as new alignment threshold for the following processing.
+ *
+ * Since: 1.14
+ */
+void
+gst_audio_stream_align_set_alignment_threshold (GstAudioStreamAlign *
+    align, GstClockTime alignment_threshold)
+{
+  g_return_if_fail (align != NULL);
+
+  align->alignment_threshold = alignment_threshold;
+}
+
+/**
+ * gst_audio_discont_get_alignment_threshold:
+ * @align: a #GstAudioStreamAlign
+ *
+ * Gets the currently configured alignment threshold.
+ *
+ * Returns: The currently configured alignment threshold
+ *
+ * Since: 1.14
+ */
+GstClockTime
+gst_audio_stream_align_get_alignment_threshold (GstAudioStreamAlign * align)
+{
+  g_return_val_if_fail (align != NULL, 0);
+
+  return align->alignment_threshold;
+}
+
+/**
+ * gst_audio_discont_set_discont_wait:
+ * @align: a #GstAudioStreamAlign
+ * @alignment_treshold: a new discont wait
+ *
+ * Sets @alignment_treshold as new discont wait for the following processing.
+ *
+ * Since: 1.14
+ */
+void
+gst_audio_stream_align_set_discont_wait (GstAudioStreamAlign * align,
+    GstClockTime discont_wait)
+{
+  g_return_if_fail (align != NULL);
+
+  align->discont_wait = discont_wait;
+}
+
+/**
+ * gst_audio_discont_get_discont_wait:
+ * @align: a #GstAudioStreamAlign
+ *
+ * Gets the currently configured discont wait.
+ *
+ * Returns: The currently configured discont wait
+ *
+ * Since: 1.14
+ */
+GstClockTime
+gst_audio_stream_align_get_discont_wait (GstAudioStreamAlign * align)
+{
+  g_return_val_if_fail (align != NULL, 0);
+
+  return align->discont_wait;
+}
+
+/**
+ * gst_audio_stream_align_mark_discont:
+ * @align: a #GstAudioStreamAlign
+ *
+ * Marks the next buffer as discontinuous and resets timestamp tracking.
+ *
+ * Since: 1.14
+ */
+void
+gst_audio_stream_align_mark_discont (GstAudioStreamAlign * align)
+{
+  g_return_if_fail (align != NULL);
+
+  align->next_offset = -1;
+  align->discont_time = GST_CLOCK_TIME_NONE;
+}
+
+/**
+ * gst_audio_stream_align_process:
+ * @align: a #GstAudioStreamAlign
+ * @discont: if this data is considered to be discontinuous
+ * @timestamp: a #GstClockTime of the start of the data
+ * @n_samples: number of samples to process
+ * @out_timestamp: (out): output timestamp of the data
+ * @out_duration: (out): output duration of the data
+ * @out_sample_position: (out): output sample position of the start of the data
+ *
+ * Processes data with @timestamp and @n_samples, and returns the output
+ * timestamp, duration and sample position together with a boolean to signal
+ * whether a discontinuity was detected or not. All non-discontinuous data
+ * will have perfect timestamps and durations.
+ *
+ * A discontinuity is detected once the difference between the actual
+ * timestamp and the timestamp calculated from the sample count since the last
+ * discontinuity differs by more than the alignment threshold for a duration
+ * longer than discont wait.
+ *
+ * Returns: %TRUE if a discontinuity was detected, %FALSE otherwise.
+ *
+ * Since: 1.14
+ */
+gboolean
+gst_audio_stream_align_process (GstAudioStreamAlign * align,
+    gboolean discont, GstClockTime timestamp, guint n_samples,
+    GstClockTime * out_timestamp, GstClockTime * out_duration,
+    guint64 * out_sample_position)
+{
+  GstClockTime start_time, end_time, duration;
+  guint64 start_offset, end_offset;
+
+  g_return_val_if_fail (align != NULL, FALSE);
+
+  start_time = timestamp;
+  start_offset = gst_util_uint64_scale (start_time, align->rate, GST_SECOND);
+
+  end_offset = start_offset + n_samples;
+  end_time = gst_util_uint64_scale_int (end_offset, GST_SECOND, align->rate);
+
+  duration = end_time - start_time;
+
+  if (align->next_offset == (guint64) - 1 || discont) {
+    discont = TRUE;
+  } else {
+    guint64 diff, max_sample_diff;
+
+    /* Check discont */
+    if (start_offset <= align->next_offset)
+      diff = align->next_offset - start_offset;
+    else
+      diff = start_offset - align->next_offset;
+
+    max_sample_diff =
+        gst_util_uint64_scale_int (align->alignment_threshold,
+        align->rate, GST_SECOND);
+
+    /* Discont! */
+    if (G_UNLIKELY (diff >= max_sample_diff)) {
+      if (align->discont_wait > 0) {
+        if (align->discont_time == GST_CLOCK_TIME_NONE) {
+          align->discont_time = start_time;
+        } else if ((start_time >= align->discont_time
+                && start_time - align->discont_time >= align->discont_wait)
+            || (start_time < align->discont_time
+                && align->discont_time - start_time >= align->discont_wait)) {
+          discont = TRUE;
+          align->discont_time = GST_CLOCK_TIME_NONE;
+        }
+      } else {
+        discont = TRUE;
+      }
+    } else if (G_UNLIKELY (align->discont_time != GST_CLOCK_TIME_NONE)) {
+      /* we have had a discont, but are now back on track! */
+      align->discont_time = GST_CLOCK_TIME_NONE;
+    }
+  }
+
+  if (discont) {
+    /* Have discont, need resync and use the capture timestamps */
+    if (align->next_offset != (guint64) - 1)
+      GST_INFO ("Have discont. Expected %"
+          G_GUINT64_FORMAT ", got %" G_GUINT64_FORMAT,
+          align->next_offset, start_offset);
+    align->next_offset = end_offset;
+
+    /* Got a discont and adjusted, reset the discont_time marker */
+    align->discont_time = GST_CLOCK_TIME_NONE;
+  } else {
+
+    /* No discont, just keep counting */
+    timestamp =
+        gst_util_uint64_scale (align->next_offset, GST_SECOND, align->rate);
+
+    start_offset = align->next_offset;
+    align->next_offset += n_samples;
+
+    duration =
+        gst_util_uint64_scale (align->next_offset, GST_SECOND,
+        align->rate) - timestamp;
+  }
+
+  if (out_timestamp)
+    *out_timestamp = timestamp;
+  if (out_duration)
+    *out_duration = duration;
+  if (out_sample_position)
+    *out_sample_position = start_offset;
+
+  return discont;
+}
diff --git a/gst-libs/gst/audio/gstaudiostreamalign.h b/gst-libs/gst/audio/gstaudiostreamalign.h
new file mode 100644 (file)
index 0000000..541c06d
--- /dev/null
@@ -0,0 +1,74 @@
+/* GStreamer
+ * Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
+ *
+ * gstaudiostreamalign.h:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_AUDIO_STREAM_ALIGN_H__
+#define __GST_AUDIO_STREAM_ALIGN_H__
+
+#include <gst/gst.h>
+
+#define GST_TYPE_AUDIO_INFO_STREAM_ALIGN (gst_audio_stream_align_get_type ())
+
+typedef struct _GstAudioStreamAlign GstAudioStreamAlign;
+
+GST_EXPORT
+GType                   gst_audio_stream_align_get_type                (void);
+
+GST_EXPORT
+GstAudioStreamAlign *   gst_audio_stream_align_new                     (gint rate,
+                                                                        GstClockTime alignment_threshold,
+                                                                        GstClockTime discont_wait);
+GST_EXPORT
+GstAudioStreamAlign *   gst_audio_stream_align_copy                    (const GstAudioStreamAlign * align);
+GST_EXPORT
+void                    gst_audio_stream_align_free                    (GstAudioStreamAlign * align);
+
+GST_EXPORT
+void                    gst_audio_stream_align_set_rate                (GstAudioStreamAlign * align,
+                                                                        gint rate);
+GST_EXPORT
+gint                    gst_audio_stream_align_get_rate                (GstAudioStreamAlign * align);
+
+GST_EXPORT
+void                    gst_audio_stream_align_set_alignment_threshold (GstAudioStreamAlign * align,
+                                                                        GstClockTime alignment_threshold);
+GST_EXPORT
+GstClockTime            gst_audio_stream_align_get_alignment_threshold (GstAudioStreamAlign * align);
+
+GST_EXPORT
+void                    gst_audio_stream_align_set_discont_wait        (GstAudioStreamAlign * align,
+                                                                        GstClockTime discont_wait);
+GST_EXPORT
+GstClockTime            gst_audio_stream_align_get_discont_wait        (GstAudioStreamAlign * align);
+
+
+GST_EXPORT
+void                    gst_audio_stream_align_mark_discont            (GstAudioStreamAlign * align);
+
+GST_EXPORT
+gboolean                gst_audio_stream_align_process                 (GstAudioStreamAlign * align,
+                                                                        gboolean discont,
+                                                                        GstClockTime timestamp,
+                                                                        guint n_samples,
+                                                                        GstClockTime *out_timestamp,
+                                                                        GstClockTime *out_duration,
+                                                                        guint64 *out_sample_position);
+
+#endif /* __GST_AUDIO_STREAM_ALIGN_H__ */
index f225294..1f14a27 100644 (file)
@@ -21,6 +21,7 @@ audio_src= [
   'gstaudiosrc.c',
   'gstaudioutilsprivate.c',
   'streamvolume.c',
+  'gstaudiostreamalign.c',
 ]
 
 audio_mkenum_headers = [
@@ -36,6 +37,7 @@ audio_mkenum_headers = [
   'gstaudiobasesrc.h',
   'gstaudiocdsrc.h',
   'gstaudiobasesink.h',
+  'gstaudiostreamalign.h',
 ]
 
 # FIXME: check headers
index 3cbc9e9..996b888 100644 (file)
@@ -705,6 +705,184 @@ GST_START_TEST (test_fill_silence)
 
 GST_END_TEST;
 
+GST_START_TEST (test_stream_align)
+{
+  GstAudioStreamAlign *align;
+  guint i;
+  GstClockTime timestamp;
+  GstClockTime out_timestamp, out_duration;
+  gboolean discont;
+
+  align = gst_audio_stream_align_new (1000);
+
+  for (i = 0; i < 500; i++) {
+    timestamp = 10 * GST_MSECOND * i;
+    discont = i == 0;
+
+    discont =
+        gst_audio_stream_align_process (align, discont, timestamp, 10,
+        &out_timestamp, &out_duration, NULL);
+
+    fail_unless_equals_uint64 (out_timestamp, 10 * GST_MSECOND * i);
+    fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+    if (i == 0)
+      fail_unless (discont);
+    else
+      fail_unless (!discont);
+  }
+
+  /* Drift forwards by 1ms per 10ms buffer for the first 40 buffers.
+   * - after 40 buffers we're above alignment threshold
+   * - after 40 + 100 buffers we're at discont wait
+   */
+  for (i = 0; i < 500; i++) {
+    timestamp = 10 * GST_MSECOND * i;
+    discont = i == 0;
+    if (i > 0)
+      timestamp += 1 * GST_MSECOND * MIN (i, 40);
+
+    discont =
+        gst_audio_stream_align_process (align, discont, timestamp, 10,
+        &out_timestamp, &out_duration, NULL);
+
+    if (i < 140) {
+      fail_unless_equals_uint64 (out_timestamp, 10 * GST_MSECOND * i);
+      fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+      if (i == 0)
+        fail_unless (discont);
+      else
+        fail_unless (!discont);
+    } else {
+      if (i == 140)
+        fail_unless (discont);
+      else
+        fail_unless (!discont);
+      fail_unless_equals_uint64 (out_timestamp,
+          10 * GST_MSECOND * i + 40 * GST_MSECOND);
+      fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+    }
+  }
+
+  /* Drift backwards by 1ms per 10ms buffer for the first 40 buffers.
+   * - after 40 buffers we're above alignment threshold
+   * - after 40 + 100 buffers we're at discont wait
+   */
+  for (i = 0; i < 500; i++) {
+    timestamp = 10 * GST_MSECOND * i;
+    discont = i == 0;
+    if (i > 0)
+      timestamp -= 1 * GST_MSECOND * MIN (i, 40);
+
+    discont =
+        gst_audio_stream_align_process (align, discont, timestamp, 10,
+        &out_timestamp, &out_duration, NULL);
+
+    if (i < 140) {
+      fail_unless_equals_uint64 (out_timestamp, 10 * GST_MSECOND * i);
+      fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+      if (i == 0)
+        fail_unless (discont);
+      else
+        fail_unless (!discont);
+    } else {
+      if (i == 140)
+        fail_unless (discont);
+      else
+        fail_unless (!discont);
+
+      fail_unless_equals_uint64 (out_timestamp,
+          10 * GST_MSECOND * i - 40 * GST_MSECOND);
+      fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+    }
+  }
+
+  /* Shift all buffers but the first by 40ms
+   * - after 1 buffers we're above alignment threshold
+   * - after 101 buffers we're at discont wait
+   */
+  for (i = 0; i < 500; i++) {
+    timestamp = 10 * GST_MSECOND * i;
+    discont = i == 0;
+    if (i > 0)
+      timestamp += 40 * GST_MSECOND;
+
+    discont =
+        gst_audio_stream_align_process (align, discont, timestamp, 10,
+        &out_timestamp, &out_duration, NULL);
+
+    if (i < 101) {
+      fail_unless_equals_uint64 (out_timestamp, 10 * GST_MSECOND * i);
+      fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+      if (i == 0)
+        fail_unless (discont);
+      else
+        fail_unless (!discont);
+    } else {
+      if (i == 101)
+        fail_unless (discont);
+      else
+        fail_unless (!discont);
+      fail_unless_equals_uint64 (out_timestamp,
+          10 * GST_MSECOND * i + 40 * GST_MSECOND);
+      fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+    }
+  }
+
+  /* Shift every second buffer by 40ms:
+   * - never discont!
+   */
+  for (i = 0; i < 500; i++) {
+    timestamp = 10 * GST_MSECOND * i;
+    discont = i == 0;
+
+    if (i % 2 == 0 && i > 0)
+      timestamp += 40 * GST_MSECOND;
+
+    discont =
+        gst_audio_stream_align_process (align, discont, timestamp, 10,
+        &out_timestamp, &out_duration, NULL);
+
+    fail_unless_equals_uint64 (out_timestamp, 10 * GST_MSECOND * i);
+    fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+    if (i == 0)
+      fail_unless (discont);
+    else
+      fail_unless (!discont);
+  }
+
+  /* Shift every buffer 100 by 2: discont at buffer 200
+   */
+  for (i = 0; i < 500; i++) {
+    timestamp = 10 * GST_MSECOND * i;
+    discont = i == 0;
+    if (i >= 100)
+      timestamp += 2 * GST_SECOND;
+
+    discont =
+        gst_audio_stream_align_process (align, discont, timestamp, 10,
+        &out_timestamp, &out_duration, NULL);
+
+    if (i < 200) {
+      fail_unless_equals_uint64 (out_timestamp, 10 * GST_MSECOND * i);
+      fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+      if (i == 0)
+        fail_unless (discont);
+      else
+        fail_unless (!discont);
+    } else {
+      fail_unless_equals_uint64 (out_timestamp,
+          10 * GST_MSECOND * i + 2 * GST_SECOND);
+      fail_unless_equals_uint64 (out_duration, 10 * GST_MSECOND);
+      if (i == 200)
+        fail_unless (discont);
+      else
+        fail_unless (!discont);
+    }
+  }
+}
+
+GST_END_TEST;
+
 static Suite *
 audio_suite (void)
 {
@@ -732,6 +910,7 @@ audio_suite (void)
   tcase_add_test (tc_chain, test_audio_format_s8);
   tcase_add_test (tc_chain, test_audio_format_u8);
   tcase_add_test (tc_chain, test_fill_silence);
+  tcase_add_test (tc_chain, test_stream_align);
 
   return s;
 }
index 26e3038..9a47e43 100644 (file)
@@ -209,6 +209,18 @@ EXPORTS
        gst_audio_ring_buffer_stop
        gst_audio_sink_get_type
        gst_audio_src_get_type
+       gst_audio_stream_align_copy
+       gst_audio_stream_align_free
+       gst_audio_stream_align_get_alignment_threshold
+       gst_audio_stream_align_get_discont_wait
+       gst_audio_stream_align_get_rate
+       gst_audio_stream_align_get_type
+       gst_audio_stream_align_mark_discont
+       gst_audio_stream_align_new
+       gst_audio_stream_align_process
+       gst_audio_stream_align_set_alignment_threshold
+       gst_audio_stream_align_set_discont_wait
+       gst_audio_stream_align_set_rate
        gst_buffer_add_audio_clipping_meta
        gst_buffer_add_audio_downmix_meta
        gst_buffer_get_audio_downmix_meta_for_channels