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.
This commit is contained in:
Sebastian Dröge 2015-01-28 15:48:26 +01:00
parent e2ff5587fe
commit 4eb5cd9156
4 changed files with 89 additions and 62 deletions

View file

@ -152,6 +152,7 @@ struct _GstDecklinkOutput {
gboolean audio_enabled; gboolean audio_enabled;
GstElement *videosink; GstElement *videosink;
gboolean video_enabled; gboolean video_enabled;
void (*start_scheduled_playback) (GstElement *videosink);
}; };
typedef struct _GstDecklinkInput GstDecklinkInput; typedef struct _GstDecklinkInput GstDecklinkInput;

View file

@ -363,6 +363,8 @@ gst_decklink_audio_sink_ringbuffer_acquire (GstAudioRingBuffer * rb,
g_mutex_lock (&self->output->lock); g_mutex_lock (&self->output->lock);
self->output->audio_enabled = TRUE; 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); g_mutex_unlock (&self->output->lock);
ret = ret =
@ -398,6 +400,8 @@ gst_decklink_audio_sink_ringbuffer_release (GstAudioRingBuffer * rb)
if (self->output) { if (self->output) {
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)
self->output->start_scheduled_playback (self->output->videosink);
g_mutex_unlock (&self->output->lock); g_mutex_unlock (&self->output->lock);
self->output->output->DisableAudioOutput (); self->output->output->DisableAudioOutput ();

View file

@ -145,6 +145,9 @@ static gboolean gst_decklink_video_sink_close (GstBaseSink * bsink);
static gboolean gst_decklink_video_sink_propose_allocation (GstBaseSink * bsink, static gboolean gst_decklink_video_sink_propose_allocation (GstBaseSink * bsink,
GstQuery * query); GstQuery * query);
static void
gst_decklink_video_sink_start_scheduled_playback (GstElement * element);
#define parent_class gst_decklink_video_sink_parent_class #define parent_class gst_decklink_video_sink_parent_class
G_DEFINE_TYPE (GstDecklinkVideoSink, gst_decklink_video_sink, G_DEFINE_TYPE (GstDecklinkVideoSink, gst_decklink_video_sink,
GST_TYPE_BASE_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); g_mutex_lock (&self->output->lock);
self->output->mode = mode; self->output->mode = mode;
self->output->video_enabled = TRUE; 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); g_mutex_unlock (&self->output->lock);
return TRUE; return TRUE;
@ -528,6 +533,8 @@ gst_decklink_video_sink_open (GstBaseSink * bsink)
g_mutex_lock (&self->output->lock); g_mutex_lock (&self->output->lock);
self->output->mode = mode; 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_start_time = GST_CLOCK_TIME_NONE;
self->output->clock_last_time = 0; self->output->clock_last_time = 0;
self->output->clock_offset = 0; self->output->clock_offset = 0;
@ -547,6 +554,8 @@ gst_decklink_video_sink_close (GstBaseSink * bsink)
g_mutex_lock (&self->output->lock); g_mutex_lock (&self->output->lock);
self->output->mode = NULL; self->output->mode = NULL;
self->output->video_enabled = FALSE; 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); g_mutex_unlock (&self->output->lock);
self->output->output->DisableVideoOutput (); self->output->output->DisableVideoOutput ();
@ -559,6 +568,77 @@ gst_decklink_video_sink_close (GstBaseSink * bsink)
return TRUE; 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 static GstStateChangeReturn
gst_decklink_video_sink_change_state (GstElement * element, gst_decklink_video_sink_change_state (GstElement * element,
GstStateChange transition) GstStateChange transition)
@ -652,67 +732,8 @@ gst_decklink_video_sink_change_state (GstElement * element,
break; break;
} }
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
GstClockTime start_time; if (self->output->start_scheduled_playback)
HRESULT res; self->output->start_scheduled_playback (self->output->videosink);
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);
break; break;
} }
default: default:

View file

@ -596,7 +596,8 @@ gst_decklink_video_src_start_streams (GstElement * element)
if (self->input->video_enabled && (!self->input->audiosrc if (self->input->video_enabled && (!self->input->audiosrc
|| self->input->audio_enabled) || 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"); GST_DEBUG_OBJECT (self, "Starting streams");
res = self->input->input->StartStreams (); res = self->input->input->StartStreams ();
if (res != S_OK) { if (res != S_OK) {