decklink: Make sure our clock never returns NONE, always advances and does not jump when going from PAUSED to PLAYING

It basically behaves the same as the audio clocks.
This commit is contained in:
Sebastian Dröge 2015-01-28 14:21:40 +01:00
parent b3a4772834
commit 00176a1ddf
6 changed files with 162 additions and 23 deletions

View file

@ -378,8 +378,8 @@ struct _GstDecklinkClock
{
GstSystemClock clock;
IDeckLinkInput *input;
IDeckLinkOutput *output;
GstDecklinkInput *input;
GstDecklinkOutput *output;
};
struct _GstDecklinkClockClass
@ -589,7 +589,7 @@ init_devices (gpointer data)
devices[i].input.device = decklink;
devices[i].input.clock = gst_decklink_clock_new ("GstDecklinkInputClock");
GST_DECKLINK_CLOCK_CAST (devices[i].input.clock)->input =
devices[i].input.input;
&devices[i].input;
devices[i].input.
input->SetCallback (new GStreamerDecklinkInputCallback (&devices[i].
input));
@ -604,7 +604,7 @@ init_devices (gpointer data)
devices[i].output.clock =
gst_decklink_clock_new ("GstDecklinkOutputClock");
GST_DECKLINK_CLOCK_CAST (devices[i].output.clock)->output =
devices[i].output.output;
&devices[i].output;
}
ret = decklink->QueryInterface (IID_IDeckLinkConfiguration,
@ -806,30 +806,97 @@ static GstClockTime
gst_decklink_clock_get_internal_time (GstClock * clock)
{
GstDecklinkClock *self = GST_DECKLINK_CLOCK (clock);
GstClockTime result;
GstClockTime result, start_time, last_time;
GstClockTimeDiff offset;
BMDTimeValue time;
HRESULT ret;
GST_OBJECT_LOCK (clock);
if (self->input != NULL) {
ret =
self->input->GetHardwareReferenceClock (GST_SECOND, &time, NULL, NULL);
if (ret == S_OK && time >= 0)
result = time;
else
result = GST_CLOCK_TIME_NONE;
g_mutex_lock (&self->input->lock);
start_time = self->input->clock_start_time;
offset = self->input->clock_offset;
last_time = self->input->clock_last_time;
time = -1;
if (!self->input->started) {
result = last_time;
ret = -1;
} else {
ret =
self->input->input->GetHardwareReferenceClock (GST_SECOND, &time,
NULL, NULL);
if (ret == S_OK && time >= 0) {
result = time;
if (start_time == GST_CLOCK_TIME_NONE)
start_time = self->input->clock_start_time = result;
if (result > start_time)
result -= start_time;
else
result = 0;
if (self->input->clock_restart) {
self->input->clock_offset = result - last_time;
offset = self->input->clock_offset;
self->input->clock_restart = FALSE;
}
result = MAX (last_time, result);
result -= offset;
result = MAX (last_time, result);
} else {
result = last_time;
}
self->input->clock_last_time = result;
}
g_mutex_unlock (&self->input->lock);
} else if (self->output != NULL) {
ret =
self->output->GetHardwareReferenceClock (GST_SECOND, &time, NULL, NULL);
if (ret == S_OK && time >= 0)
result = time;
else
result = GST_CLOCK_TIME_NONE;
g_mutex_lock (&self->output->lock);
start_time = self->output->clock_start_time;
offset = self->output->clock_offset;
last_time = self->output->clock_last_time;
time = -1;
if (!self->output->started) {
result = last_time;
ret = -1;
} else {
ret =
self->output->output->GetHardwareReferenceClock (GST_SECOND, &time,
NULL, NULL);
if (ret == S_OK && time >= 0) {
result = time;
if (start_time == GST_CLOCK_TIME_NONE)
start_time = self->output->clock_start_time = result;
if (result > start_time)
result -= start_time;
else
result = 0;
if (self->output->clock_restart) {
self->output->clock_offset = result - last_time;
offset = self->output->clock_offset;
self->output->clock_restart = FALSE;
}
result = MAX (last_time, result);
result -= offset;
result = MAX (last_time, result);
} else {
result = last_time;
}
self->output->clock_last_time = result;
}
g_mutex_unlock (&self->output->lock);
} else {
result = GST_CLOCK_TIME_NONE;
g_assert_not_reached ();
}
GST_OBJECT_UNLOCK (clock);
GST_LOG_OBJECT (clock, "result %" GST_TIME_FORMAT, GST_TIME_ARGS (result));
GST_LOG_OBJECT (clock,
"result %" GST_TIME_FORMAT " time %" GST_TIME_FORMAT " last time %"
GST_TIME_FORMAT " offset %" GST_TIME_FORMAT " start time %"
GST_TIME_FORMAT " (ret: 0x%08x)", GST_TIME_ARGS (result),
GST_TIME_ARGS (time), GST_TIME_ARGS (last_time), GST_TIME_ARGS (offset),
GST_TIME_ARGS (start_time), ret);
return result;
}

View file

@ -134,6 +134,9 @@ struct _GstDecklinkOutput {
IDeckLink *device;
IDeckLinkOutput *output;
GstClock *clock;
GstClockTime clock_start_time, clock_last_time;
GstClockTimeDiff clock_offset;
gboolean started, clock_restart;
/* Everything below protected by mutex */
GMutex lock;
@ -147,7 +150,9 @@ struct _GstDecklinkOutput {
/* <private> */
GstElement *audiosink;
gboolean audio_enabled;
GstElement *videosink;
gboolean video_enabled;
};
typedef struct _GstDecklinkInput GstDecklinkInput;
@ -157,6 +162,8 @@ struct _GstDecklinkInput {
IDeckLinkConfiguration *config;
IDeckLinkAttributes *attributes;
GstClock *clock;
GstClockTime clock_start_time, clock_offset, clock_last_time;
gboolean started, clock_restart;
/* Everything below protected by mutex */
GMutex lock;
@ -171,7 +178,9 @@ struct _GstDecklinkInput {
/* <private> */
GstElement *audiosrc;
gboolean audio_enabled;
GstElement *videosrc;
gboolean video_enabled;
};
GstDecklinkOutput * gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio);

View file

@ -361,6 +361,10 @@ gst_decklink_audio_sink_ringbuffer_acquire (GstAudioRingBuffer * rb,
return FALSE;
}
g_mutex_lock (&self->output->lock);
self->output->audio_enabled = TRUE;
g_mutex_unlock (&self->output->lock);
ret =
self->output->
output->SetAudioCallback (new GStreamerAudioOutputCallback (self));
@ -391,9 +395,13 @@ gst_decklink_audio_sink_ringbuffer_release (GstAudioRingBuffer * rb)
GST_DEBUG_OBJECT (self->sink, "Release");
if (self->output)
self->output->output->DisableAudioOutput ();
if (self->output) {
g_mutex_lock (&self->output->lock);
self->output->audio_enabled = FALSE;
g_mutex_unlock (&self->output->lock);
self->output->output->DisableAudioOutput ();
}
// free the buffer
g_free (rb->memory);
rb->memory = NULL;

View file

@ -379,6 +379,10 @@ gst_decklink_audio_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
return FALSE;
}
g_mutex_lock (&self->input->lock);
self->input->audio_enabled = TRUE;
g_mutex_unlock (&self->input->lock);
return TRUE;
}
@ -652,6 +656,7 @@ gst_decklink_audio_src_close (GstDecklinkAudioSrc * self)
if (self->input) {
g_mutex_lock (&self->input->lock);
self->input->got_audio_packet = NULL;
self->input->audio_enabled = FALSE;
g_mutex_unlock (&self->input->lock);
self->input->input->DisableAudioInput ();

View file

@ -287,6 +287,7 @@ 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;
g_mutex_unlock (&self->output->lock);
return TRUE;
@ -527,6 +528,9 @@ gst_decklink_video_sink_open (GstBaseSink * bsink)
g_mutex_lock (&self->output->lock);
self->output->mode = mode;
self->output->clock_start_time = GST_CLOCK_TIME_NONE;
self->output->clock_last_time = 0;
self->output->clock_offset = 0;
g_mutex_unlock (&self->output->lock);
return TRUE;
@ -542,6 +546,7 @@ gst_decklink_video_sink_close (GstBaseSink * bsink)
if (self->output) {
g_mutex_lock (&self->output->lock);
self->output->mode = NULL;
self->output->video_enabled = FALSE;
g_mutex_unlock (&self->output->lock);
self->output->output->DisableVideoOutput ();
@ -563,6 +568,11 @@ gst_decklink_video_sink_change_state (GstElement * element,
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
g_mutex_lock (&self->output->lock);
self->output->clock_start_time = GST_CLOCK_TIME_NONE;
self->output->clock_last_time = 0;
self->output->clock_offset = 0;
g_mutex_unlock (&self->output->lock);
gst_element_post_message (element,
gst_message_new_clock_provide (GST_OBJECT_CAST (element),
self->output->clock, TRUE));
@ -597,6 +607,11 @@ gst_decklink_video_sink_change_state (GstElement * element,
gst_message_new_clock_lost (GST_OBJECT_CAST (element),
self->output->clock));
gst_clock_set_master (self->output->clock, NULL);
g_mutex_lock (&self->output->lock);
self->output->clock_start_time = GST_CLOCK_TIME_NONE;
self->output->clock_last_time = 0;
self->output->clock_offset = 0;
g_mutex_unlock (&self->output->lock);
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:{
GstClockTime start_time;
@ -620,6 +635,10 @@ gst_decklink_video_sink_change_state (GstElement * element,
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);
@ -665,6 +684,10 @@ gst_decklink_video_sink_change_state (GstElement * element,
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,
@ -686,6 +709,10 @@ gst_decklink_video_sink_change_state (GstElement * element,
(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;
}
default:

View file

@ -328,6 +328,7 @@ gst_decklink_video_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
g_mutex_lock (&self->input->lock);
self->input->mode = mode;
self->input->video_enabled = TRUE;
g_mutex_unlock (&self->input->lock);
return TRUE;
@ -550,6 +551,9 @@ gst_decklink_video_src_open (GstDecklinkVideoSrc * self)
g_mutex_lock (&self->input->lock);
self->input->mode = mode;
self->input->got_video_frame = gst_decklink_video_src_got_frame;
self->input->clock_start_time = GST_CLOCK_TIME_NONE;
self->input->clock_last_time = 0;
self->input->clock_offset = 0;
g_mutex_unlock (&self->input->lock);
return TRUE;
@ -565,6 +569,7 @@ gst_decklink_video_src_close (GstDecklinkVideoSrc * self)
g_mutex_lock (&self->input->lock);
self->input->got_video_frame = NULL;
self->input->mode = NULL;
self->input->video_enabled = FALSE;
g_mutex_unlock (&self->input->lock);
self->input->input->DisableVideoInput ();
@ -592,6 +597,11 @@ gst_decklink_video_src_change_state (GstElement * element,
}
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
g_mutex_lock (&self->input->lock);
self->input->clock_start_time = GST_CLOCK_TIME_NONE;
self->input->clock_last_time = 0;
self->input->clock_offset = 0;
g_mutex_unlock (&self->input->lock);
gst_element_post_message (element,
gst_message_new_clock_provide (GST_OBJECT_CAST (element),
self->input->clock, TRUE));
@ -623,6 +633,11 @@ gst_decklink_video_src_change_state (GstElement * element,
gst_message_new_clock_lost (GST_OBJECT_CAST (element),
self->input->clock));
gst_clock_set_master (self->input->clock, NULL);
g_mutex_lock (&self->input->lock);
self->input->clock_start_time = GST_CLOCK_TIME_NONE;
self->input->clock_last_time = 0;
self->input->clock_offset = 0;
g_mutex_unlock (&self->input->lock);
g_queue_foreach (&self->current_frames, (GFunc) capture_frame_free, NULL);
g_queue_clear (&self->current_frames);
@ -632,6 +647,10 @@ gst_decklink_video_src_change_state (GstElement * element,
HRESULT res;
GST_DEBUG_OBJECT (self, "Stopping streams");
g_mutex_lock (&self->input->lock);
self->input->started = FALSE;
g_mutex_unlock (&self->input->lock);
res = self->input->input->StopStreams ();
if (res != S_OK) {
GST_ELEMENT_ERROR (self, STREAM, FAILED,
@ -650,6 +669,10 @@ gst_decklink_video_src_change_state (GstElement * element,
(NULL), ("Failed to start streams: 0x%08x", res));
ret = GST_STATE_CHANGE_FAILURE;
}
g_mutex_lock (&self->input->lock);
self->input->started = TRUE;
self->input->clock_restart = TRUE;
g_mutex_unlock (&self->input->lock);
break;
}
case GST_STATE_CHANGE_READY_TO_NULL: