From 4de7fdfa3a664a1880566e43b95a92f5ed8d8bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 9 Feb 2015 16:18:03 +0100 Subject: [PATCH] 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. --- sys/decklink/gstdecklinkaudiosink.cpp | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/sys/decklink/gstdecklinkaudiosink.cpp b/sys/decklink/gstdecklinkaudiosink.cpp index 2e6d871cdb..e4a0ec8092 100644 --- a/sys/decklink/gstdecklinkaudiosink.cpp +++ b/sys/decklink/gstdecklinkaudiosink.cpp @@ -54,6 +54,9 @@ struct _GstDecklinkAudioSinkRingBuffer GstDecklinkOutput *output; GstDecklinkAudioSink *sink; + + GMutex clock_id_lock; + GstClockID clock_id; }; struct _GstDecklinkAudioSinkRingBufferClass @@ -122,6 +125,7 @@ static void static void gst_decklink_audio_sink_ringbuffer_init (GstDecklinkAudioSinkRingBuffer * self) { + g_mutex_init (&self->clock_id_lock); } static void @@ -132,6 +136,7 @@ gst_decklink_audio_sink_ringbuffer_finalize (GObject * object) gst_object_unref (self->sink); self->sink = NULL; + g_mutex_clear (&self->clock_id_lock); G_OBJECT_CLASS (ringbuffer_parent_class)->finalize (object); } @@ -193,10 +198,60 @@ public: gint bpf; guint written, written_sum; 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)", 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 (m_ringbuffer), &seg, &ptr, &len)) { 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"); 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); self->output->audio_enabled = FALSE; if (self->output->start_scheduled_playback)