decklinkvideosink: Also stop scheduled playback when gst_element_lost_state() is called
Unfortunately this does not go through the normal state change
machinery, so we don't get notified about this in change_state().
However we need to stop scheduled playback, so that once PLAYING is
reached again we can start scheduled playback with the correct time.
Without this, flushing seeks in PLAYING will not work correctly:
decklinkvideosink will wait before showing the new frames for the amount
of time the pipeline was in PLAYING before.
diff --git a/sys/decklink/gstdecklinkvideosink.cpp b/sys/decklink/gstdecklinkvideosink.cpp
index 6fbcb34..c1c93c8 100644
--- a/sys/decklink/gstdecklinkvideosink.cpp
+++ b/sys/decklink/gstdecklinkvideosink.cpp
@@ -132,6 +132,9 @@
static GstStateChangeReturn
gst_decklink_video_sink_change_state (GstElement * element,
GstStateChange transition);
+static void
+gst_decklink_video_sink_state_changed (GstElement * element,
+ GstState old_state, GstState new_state, GstState pending_state);
static GstClock *gst_decklink_video_sink_provide_clock (GstElement * element);
static GstCaps *gst_decklink_video_sink_get_caps (GstBaseSink * bsink,
@@ -179,6 +182,8 @@
element_class->change_state =
GST_DEBUG_FUNCPTR (gst_decklink_video_sink_change_state);
+ element_class->state_changed =
+ GST_DEBUG_FUNCPTR (gst_decklink_video_sink_state_changed);
element_class->provide_clock =
GST_DEBUG_FUNCPTR (gst_decklink_video_sink_provide_clock);
@@ -831,6 +836,60 @@
}
static GstStateChangeReturn
+gst_decklink_video_sink_stop_scheduled_playback (GstDecklinkVideoSink * self)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+ GstClockTime start_time;
+ HRESULT res;
+ GstClock *clock;
+
+ if (!self->output->started)
+ return ret;
+
+ clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
+ if (clock) {
+ // 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 (GST_ELEMENT (self));
+ if (start_time != GST_CLOCK_TIME_NONE)
+ start_time = gst_clock_get_time (clock) - start_time;
+
+ // FIXME: This will probably not work
+ if (start_time == GST_CLOCK_TIME_NONE)
+ start_time = 0;
+
+ convert_to_internal_clock (self, &start_time, NULL);
+
+ // The start time is now the running time when we stopped
+ // playback
+
+ gst_object_unref (clock);
+ } else {
+ GST_WARNING_OBJECT (self,
+ "No clock, stopping scheduled playback immediately");
+ start_time = 0;
+ }
+
+ GST_DEBUG_OBJECT (self,
+ "Stopping scheduled playback at %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (start_time));
+
+ g_mutex_lock (&self->output->lock);
+ self->output->started = FALSE;
+ g_mutex_unlock (&self->output->lock);
+ res = self->output->output->StopScheduledPlayback (start_time, 0, GST_SECOND);
+ if (res != S_OK) {
+ GST_ELEMENT_ERROR (self, STREAM, FAILED,
+ (NULL), ("Failed to stop scheduled playback: 0x%08x", res));
+ ret = GST_STATE_CHANGE_FAILURE;
+ }
+ self->internal_base_time = GST_CLOCK_TIME_NONE;
+ self->external_base_time = GST_CLOCK_TIME_NONE;
+
+ return ret;
+}
+
+static GstStateChangeReturn
gst_decklink_video_sink_change_state (GstElement * element,
GstStateChange transition)
{
@@ -896,51 +955,8 @@
gst_decklink_video_sink_stop (self);
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:{
- GstClockTime start_time;
- HRESULT res;
- GstClock *clock;
-
- clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
- if (clock) {
- // 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 (clock) - start_time;
-
- // FIXME: This will probably not work
- if (start_time == GST_CLOCK_TIME_NONE)
- start_time = 0;
-
- convert_to_internal_clock (self, &start_time, NULL);
-
- // The start time is now the running time when we stopped
- // playback
-
- gst_object_unref (clock);
- } else {
- GST_WARNING_OBJECT (self,
- "No clock, stopping scheduled playback immediately");
- start_time = 0;
- }
-
- GST_DEBUG_OBJECT (self,
- "Stopping scheduled playback at %" GST_TIME_FORMAT,
- GST_TIME_ARGS (start_time));
-
- g_mutex_lock (&self->output->lock);
- self->output->started = FALSE;
- g_mutex_unlock (&self->output->lock);
- res =
- self->output->output->StopScheduledPlayback (start_time, 0,
- GST_SECOND);
- if (res != S_OK) {
- GST_ELEMENT_ERROR (self, STREAM, FAILED,
- (NULL), ("Failed to stop scheduled playback: 0x%08x", res));
+ if (gst_decklink_video_sink_stop_scheduled_playback (self) == GST_STATE_CHANGE_FAILURE)
ret = GST_STATE_CHANGE_FAILURE;
- }
- self->internal_base_time = GST_CLOCK_TIME_NONE;
- self->external_base_time = GST_CLOCK_TIME_NONE;
break;
}
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
@@ -957,6 +973,20 @@
return ret;
}
+static void
+gst_decklink_video_sink_state_changed (GstElement * element,
+ GstState old_state, GstState new_state, GstState pending_state)
+{
+ GstDecklinkVideoSink *self = GST_DECKLINK_VIDEO_SINK_CAST (element);
+
+ // Aka gst_element_lost_state()
+ if (old_state == GST_STATE_PAUSED &&
+ new_state == GST_STATE_PAUSED && pending_state == GST_STATE_PAUSED &&
+ GST_STATE_TARGET (element) == GST_STATE_PLAYING) {
+ gst_decklink_video_sink_stop_scheduled_playback (self);
+ }
+}
+
static GstClock *
gst_decklink_video_sink_provide_clock (GstElement * element)
{