mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
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:
parent
e2ff5587fe
commit
4eb5cd9156
4 changed files with 89 additions and 62 deletions
|
@ -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;
|
||||||
|
|
|
@ -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 ();
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue