From 4eb5cd915614c25f6bf6eee9366d18dac5cf8bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 28 Jan 2015 15:48:26 +0100 Subject: [PATCH] decklink{audio,video}sink: Only start scheduled playback once both sources are ready and we are in PLAYING Otherwise we might start the scheduled playback before the audio or video streams are actually enabled, and then error out later because they are enabled to late. We enable the streams when getting the caps, which might be *after* we were set to PLAYING state. --- sys/decklink/gstdecklink.h | 1 + sys/decklink/gstdecklinkaudiosink.cpp | 4 + sys/decklink/gstdecklinkvideosink.cpp | 143 +++++++++++++++----------- sys/decklink/gstdecklinkvideosrc.cpp | 3 +- 4 files changed, 89 insertions(+), 62 deletions(-) diff --git a/sys/decklink/gstdecklink.h b/sys/decklink/gstdecklink.h index e58987e4b0..e1ef4e697f 100644 --- a/sys/decklink/gstdecklink.h +++ b/sys/decklink/gstdecklink.h @@ -152,6 +152,7 @@ struct _GstDecklinkOutput { gboolean audio_enabled; GstElement *videosink; gboolean video_enabled; + void (*start_scheduled_playback) (GstElement *videosink); }; typedef struct _GstDecklinkInput GstDecklinkInput; diff --git a/sys/decklink/gstdecklinkaudiosink.cpp b/sys/decklink/gstdecklinkaudiosink.cpp index 61ff6bbcac..7dcd59b222 100644 --- a/sys/decklink/gstdecklinkaudiosink.cpp +++ b/sys/decklink/gstdecklinkaudiosink.cpp @@ -363,6 +363,8 @@ gst_decklink_audio_sink_ringbuffer_acquire (GstAudioRingBuffer * rb, g_mutex_lock (&self->output->lock); self->output->audio_enabled = TRUE; + if (self->output->start_scheduled_playback) + self->output->start_scheduled_playback (self->output->videosink); g_mutex_unlock (&self->output->lock); ret = @@ -398,6 +400,8 @@ gst_decklink_audio_sink_ringbuffer_release (GstAudioRingBuffer * rb) if (self->output) { g_mutex_lock (&self->output->lock); self->output->audio_enabled = FALSE; + if (self->output->start_scheduled_playback) + self->output->start_scheduled_playback (self->output->videosink); g_mutex_unlock (&self->output->lock); self->output->output->DisableAudioOutput (); diff --git a/sys/decklink/gstdecklinkvideosink.cpp b/sys/decklink/gstdecklinkvideosink.cpp index 741c655cbb..d2d67bccc9 100644 --- a/sys/decklink/gstdecklinkvideosink.cpp +++ b/sys/decklink/gstdecklinkvideosink.cpp @@ -145,6 +145,9 @@ static gboolean gst_decklink_video_sink_close (GstBaseSink * bsink); static gboolean gst_decklink_video_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query); +static void +gst_decklink_video_sink_start_scheduled_playback (GstElement * element); + #define parent_class gst_decklink_video_sink_parent_class G_DEFINE_TYPE (GstDecklinkVideoSink, gst_decklink_video_sink, GST_TYPE_BASE_SINK); @@ -288,6 +291,8 @@ gst_decklink_video_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) g_mutex_lock (&self->output->lock); self->output->mode = mode; self->output->video_enabled = TRUE; + if (self->output->start_scheduled_playback) + self->output->start_scheduled_playback (self->output->videosink); g_mutex_unlock (&self->output->lock); return TRUE; @@ -528,6 +533,8 @@ gst_decklink_video_sink_open (GstBaseSink * bsink) g_mutex_lock (&self->output->lock); self->output->mode = mode; + self->output->start_scheduled_playback = + gst_decklink_video_sink_start_scheduled_playback; self->output->clock_start_time = GST_CLOCK_TIME_NONE; self->output->clock_last_time = 0; self->output->clock_offset = 0; @@ -547,6 +554,8 @@ gst_decklink_video_sink_close (GstBaseSink * bsink) g_mutex_lock (&self->output->lock); self->output->mode = NULL; self->output->video_enabled = FALSE; + if (self->output->start_scheduled_playback) + self->output->start_scheduled_playback (self->output->videosink); g_mutex_unlock (&self->output->lock); self->output->output->DisableVideoOutput (); @@ -559,6 +568,77 @@ gst_decklink_video_sink_close (GstBaseSink * bsink) return TRUE; } +static void +gst_decklink_video_sink_start_scheduled_playback (GstElement * element) +{ + GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (element); + GstClockTime start_time; + HRESULT res; + bool active; + + if (self->output->video_enabled && (!self->output->audiosink + || self->output->audio_enabled) + && (GST_STATE (self) == GST_STATE_PLAYING + || GST_STATE_PENDING (self) == GST_STATE_PLAYING)) { + // FIXME: start time is the same for the complete pipeline, + // but what we need here is the start time of this element! + start_time = gst_element_get_base_time (element); + if (start_time != GST_CLOCK_TIME_NONE) + start_time = gst_clock_get_time (GST_ELEMENT_CLOCK (self)) - start_time; + + // FIXME: This will probably not work + if (start_time == GST_CLOCK_TIME_NONE) + start_time = 0; + + // Current times of internal and external clock when we go to + // playing. We need this to convert the pipeline running time + // to the running time of the hardware + // + // We can't use the normal base time for the external clock + // because we might go to PLAYING later than the pipeline + self->internal_base_time = + gst_clock_get_internal_time (self->output->clock); + self->external_base_time = + gst_clock_get_internal_time (GST_ELEMENT_CLOCK (self)); + + convert_to_internal_clock (self, &start_time, NULL); + + active = false; + self->output->output->IsScheduledPlaybackRunning (&active); + if (active) { + GST_DEBUG_OBJECT (self, "Stopping scheduled playback"); + + g_mutex_lock (&self->output->lock); + self->output->started = FALSE; + g_mutex_unlock (&self->output->lock); + + res = self->output->output->StopScheduledPlayback (0, 0, 0); + if (res != S_OK) { + GST_ELEMENT_ERROR (self, STREAM, FAILED, + (NULL), ("Failed to stop scheduled playback: 0x%08x", res)); + return; + } + } + + GST_DEBUG_OBJECT (self, + "Starting scheduled playback at %" GST_TIME_FORMAT, + GST_TIME_ARGS (start_time)); + + res = + self->output->output->StartScheduledPlayback (start_time, + GST_SECOND, 1.0); + if (res != S_OK) { + GST_ELEMENT_ERROR (self, STREAM, FAILED, + (NULL), ("Failed to start scheduled playback: 0x%08x", res)); + return; + } + self->output->started = TRUE; + self->output->clock_restart = TRUE; + } else { + GST_DEBUG_OBJECT (self, "Not starting scheduled playback yet"); + } +} + static GstStateChangeReturn gst_decklink_video_sink_change_state (GstElement * element, GstStateChange transition) @@ -652,67 +732,8 @@ gst_decklink_video_sink_change_state (GstElement * element, break; } case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{ - GstClockTime start_time; - HRESULT res; - bool active; - - // FIXME: start time is the same for the complete pipeline, - // but what we need here is the start time of this element! - start_time = gst_element_get_base_time (element); - if (start_time != GST_CLOCK_TIME_NONE) - start_time = gst_clock_get_time (GST_ELEMENT_CLOCK (self)) - start_time; - - // FIXME: This will probably not work - if (start_time == GST_CLOCK_TIME_NONE) - start_time = 0; - - // Current times of internal and external clock when we go to - // playing. We need this to convert the pipeline running time - // to the running time of the hardware - // - // We can't use the normal base time for the external clock - // because we might go to PLAYING later than the pipeline - self->internal_base_time = - gst_clock_get_internal_time (self->output->clock); - self->external_base_time = - gst_clock_get_internal_time (GST_ELEMENT_CLOCK (self)); - - convert_to_internal_clock (self, &start_time, NULL); - - active = false; - self->output->output->IsScheduledPlaybackRunning (&active); - if (active) { - GST_DEBUG_OBJECT (self, "Stopping scheduled playback"); - - g_mutex_lock (&self->output->lock); - self->output->started = FALSE; - g_mutex_unlock (&self->output->lock); - - res = self->output->output->StopScheduledPlayback (0, 0, 0); - if (res != S_OK) { - GST_ELEMENT_ERROR (self, STREAM, FAILED, - (NULL), ("Failed to stop scheduled playback: 0x%08x", res)); - ret = GST_STATE_CHANGE_FAILURE; - break; - } - } - - GST_DEBUG_OBJECT (self, - "Starting scheduled playback at %" GST_TIME_FORMAT, - GST_TIME_ARGS (start_time)); - - res = - self->output->output->StartScheduledPlayback (start_time, - GST_SECOND, 1.0); - if (res != S_OK) { - GST_ELEMENT_ERROR (self, STREAM, FAILED, - (NULL), ("Failed to start scheduled playback: 0x%08x", res)); - ret = GST_STATE_CHANGE_FAILURE; - } - g_mutex_lock (&self->output->lock); - self->output->started = TRUE; - self->output->clock_restart = TRUE; - g_mutex_unlock (&self->output->lock); + if (self->output->start_scheduled_playback) + self->output->start_scheduled_playback (self->output->videosink); break; } default: diff --git a/sys/decklink/gstdecklinkvideosrc.cpp b/sys/decklink/gstdecklinkvideosrc.cpp index e015c5d32a..112f7b0785 100644 --- a/sys/decklink/gstdecklinkvideosrc.cpp +++ b/sys/decklink/gstdecklinkvideosrc.cpp @@ -596,7 +596,8 @@ gst_decklink_video_src_start_streams (GstElement * element) if (self->input->video_enabled && (!self->input->audiosrc || self->input->audio_enabled) - && GST_STATE (self) == GST_STATE_PLAYING) { + && (GST_STATE (self) == GST_STATE_PLAYING + || GST_STATE_PENDING (self) == GST_STATE_PLAYING)) { GST_DEBUG_OBJECT (self, "Starting streams"); res = self->input->input->StartStreams (); if (res != S_OK) {