decklinkaudiosink: Throttle reading from the ringbuffer

The driver has an internal buffer of unspecified and unconfigurable size, and
it will pull data from our ring buffer as fast as it can until that is full.
Unfortunately that means that we pull silence from the ringbuffer unless its
size is by conincidence larger than the driver's internal ringbuffer.

The good news is that it's not required to completely fill the buffer for
proper playback. So we now throttle reading from the ringbuffer whenever
the driver has buffered more than half of our ringbuffer size by waiting
on the clock for the amount of time until it has buffered less than that
again.
This commit is contained in:
Sebastian Dröge 2015-02-09 16:18:03 +01:00
parent a6bcd09b6c
commit 4de7fdfa3a

View file

@ -54,6 +54,9 @@ struct _GstDecklinkAudioSinkRingBuffer
GstDecklinkOutput *output; GstDecklinkOutput *output;
GstDecklinkAudioSink *sink; GstDecklinkAudioSink *sink;
GMutex clock_id_lock;
GstClockID clock_id;
}; };
struct _GstDecklinkAudioSinkRingBufferClass struct _GstDecklinkAudioSinkRingBufferClass
@ -122,6 +125,7 @@ static void
static void static void
gst_decklink_audio_sink_ringbuffer_init (GstDecklinkAudioSinkRingBuffer * self) gst_decklink_audio_sink_ringbuffer_init (GstDecklinkAudioSinkRingBuffer * self)
{ {
g_mutex_init (&self->clock_id_lock);
} }
static void static void
@ -132,6 +136,7 @@ gst_decklink_audio_sink_ringbuffer_finalize (GObject * object)
gst_object_unref (self->sink); gst_object_unref (self->sink);
self->sink = NULL; self->sink = NULL;
g_mutex_clear (&self->clock_id_lock);
G_OBJECT_CLASS (ringbuffer_parent_class)->finalize (object); G_OBJECT_CLASS (ringbuffer_parent_class)->finalize (object);
} }
@ -193,10 +198,60 @@ public:
gint bpf; gint bpf;
guint written, written_sum; guint written, written_sum;
HRESULT res; HRESULT res;
const GstAudioRingBufferSpec *spec =
&GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer)->spec;
guint delay, max_delay;
GST_LOG_OBJECT (m_ringbuffer->sink, "Writing audio samples (preroll: %d)", GST_LOG_OBJECT (m_ringbuffer->sink, "Writing audio samples (preroll: %d)",
preroll); preroll);
delay =
gst_audio_ring_buffer_delay (GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer));
max_delay = MAX ((spec->segtotal * spec->segsize) / 2, spec->segsize);
max_delay /= GST_AUDIO_INFO_BPF (&spec->info);
if (delay > max_delay) {
GstClock *clock =
gst_element_get_clock (GST_ELEMENT_CAST (m_ringbuffer->sink));
GstClockTime wait_time;
GstClockID clock_id;
GstClockReturn clock_ret;
GST_DEBUG_OBJECT (m_ringbuffer->sink, "Delay %u > max delay %u", delay,
max_delay);
wait_time =
gst_util_uint64_scale (delay - max_delay, GST_SECOND,
GST_AUDIO_INFO_RATE (&spec->info));
GST_DEBUG_OBJECT (m_ringbuffer->sink, "Waiting for %" GST_TIME_FORMAT,
GST_TIME_ARGS (wait_time));
wait_time += gst_clock_get_time (clock);
g_mutex_lock (&m_ringbuffer->clock_id_lock);
if (!GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer)->acquired) {
GST_DEBUG_OBJECT (m_ringbuffer->sink,
"Ringbuffer not acquired anymore");
g_mutex_unlock (&m_ringbuffer->clock_id_lock);
gst_object_unref (clock);
return S_OK;
}
clock_id = gst_clock_new_single_shot_id (clock, wait_time);
m_ringbuffer->clock_id = clock_id;
g_mutex_unlock (&m_ringbuffer->clock_id_lock);
gst_object_unref (clock);
clock_ret = gst_clock_id_wait (clock_id, NULL);
g_mutex_lock (&m_ringbuffer->clock_id_lock);
gst_clock_id_unref (clock_id);
m_ringbuffer->clock_id = NULL;
g_mutex_unlock (&m_ringbuffer->clock_id_lock);
if (clock_ret == GST_CLOCK_UNSCHEDULED) {
GST_DEBUG_OBJECT (m_ringbuffer->sink, "Flushing");
return S_OK;
}
}
if (!gst_audio_ring_buffer_prepare_read (GST_AUDIO_RING_BUFFER_CAST if (!gst_audio_ring_buffer_prepare_read (GST_AUDIO_RING_BUFFER_CAST
(m_ringbuffer), &seg, &ptr, &len)) { (m_ringbuffer), &seg, &ptr, &len)) {
GST_WARNING_OBJECT (m_ringbuffer->sink, "No segment available"); GST_WARNING_OBJECT (m_ringbuffer->sink, "No segment available");
@ -392,6 +447,11 @@ gst_decklink_audio_sink_ringbuffer_release (GstAudioRingBuffer * rb)
GST_DEBUG_OBJECT (self->sink, "Release"); GST_DEBUG_OBJECT (self->sink, "Release");
if (self->output) { if (self->output) {
g_mutex_lock (&self->clock_id_lock);
if (self->clock_id)
gst_clock_id_unschedule (self->clock_id);
g_mutex_unlock (&self->clock_id_lock);
g_mutex_lock (&self->output->lock); g_mutex_lock (&self->output->lock);
self->output->audio_enabled = FALSE; self->output->audio_enabled = FALSE;
if (self->output->start_scheduled_playback) if (self->output->start_scheduled_playback)