summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Dröge <sebastian@centricular.com>2014-12-19 14:35:03 +0100
committerSebastian Dröge <sebastian@centricular.com>2014-12-19 14:40:40 +0100
commit9c2e09cf392691013884cc69861a26c3dd2a3f4c (patch)
tree9e303285d5128f9b387f9b511a609b55a3cd685b
parentb9e17c5605596262ee386a124c367a0e9bf84179 (diff)
decklinkvideosink: Implement clock slaving
-rw-r--r--sys/decklink/gstdecklinkvideosink.cpp109
-rw-r--r--sys/decklink/gstdecklinkvideosink.h2
2 files changed, 76 insertions, 35 deletions
diff --git a/sys/decklink/gstdecklinkvideosink.cpp b/sys/decklink/gstdecklinkvideosink.cpp
index ba93a8432..5195b860d 100644
--- a/sys/decklink/gstdecklinkvideosink.cpp
+++ b/sys/decklink/gstdecklinkvideosink.cpp
@@ -191,6 +191,72 @@ gst_decklink_video_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
return GST_FLOW_OK;
}
+static void
+convert_to_internal_clock (GstDecklinkVideoSink * self,
+ GstClockTime * timestamp, GstClockTime * duration)
+{
+ GstClock *clock, *audio_clock;
+
+ g_assert (timestamp != NULL);
+
+ clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
+ audio_clock = gst_decklink_output_get_audio_clock (self->output);
+ if (clock && clock != self->output->clock && clock != audio_clock) {
+ GstClockTime internal, external, rate_n, rate_d;
+ gst_clock_get_calibration (self->output->clock, &internal, &external,
+ &rate_n, &rate_d);
+
+ if (rate_n != rate_d) {
+ GstClockTime external_timestamp = *timestamp;
+
+ // Convert to the running time corresponding to both clock times
+ internal -= self->internal_base_time;
+ external -= gst_element_get_base_time (GST_ELEMENT_CAST (self));
+
+ // Get the difference in the external time, note
+ // that the running time is external time.
+ // Then scale this difference and offset it to
+ // our internal time. Now we have the running time
+ // according to our internal clock.
+ //
+ // For the duration we just scale
+ if (external > external_timestamp) {
+ guint64 diff = external - external_timestamp;
+ diff = gst_util_uint64_scale (diff, rate_d, rate_n);
+ *timestamp = internal - diff;
+ } else {
+ guint64 diff = external_timestamp - external;
+ diff = gst_util_uint64_scale (diff, rate_d, rate_n);
+ *timestamp = internal + diff;
+ }
+
+ GST_LOG_OBJECT (self,
+ "Converted %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT " (internal: %"
+ GST_TIME_FORMAT " external %" GST_TIME_FORMAT " rate: %lf)",
+ GST_TIME_ARGS (external_timestamp), GST_TIME_ARGS (*timestamp),
+ GST_TIME_ARGS (internal), GST_TIME_ARGS (external),
+ ((gdouble) rate_n) / ((gdouble) rate_d));
+
+ if (duration) {
+ GstClockTime external_duration = *duration;
+
+ *duration = gst_util_uint64_scale (external_duration, rate_d, rate_n);
+
+ GST_LOG_OBJECT (self,
+ "Converted duration %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
+ " (internal: %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT
+ " rate: %lf)", GST_TIME_ARGS (external_duration),
+ GST_TIME_ARGS (*duration), GST_TIME_ARGS (internal),
+ GST_TIME_ARGS (external), ((gdouble) rate_n) / ((gdouble) rate_d));
+ }
+ } else {
+ GST_LOG_OBJECT (self, "No clock conversion needed, relative rate is 1.0");
+ }
+ } else {
+ GST_LOG_OBJECT (self, "No clock conversion needed, same clocks");
+ }
+}
+
static GstFlowReturn
gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
{
@@ -200,9 +266,9 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
guint8 *outdata, *indata;
GstFlowReturn flow_ret;
HRESULT ret;
- GstClockTime timestamp, duration, running_time, running_time_duration;
+ GstClockTime timestamp, duration;
+ GstClockTime running_time, running_time_duration;
gint i;
- GstClock *clock = NULL, *audio_clock = NULL;
GST_DEBUG_OBJECT (self, "Preparing buffer %p", buffer);
@@ -249,12 +315,7 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
gst_segment_to_running_time (&GST_BASE_SINK_CAST (self)->segment,
GST_FORMAT_TIME, timestamp + duration) - running_time;
- clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
- audio_clock = gst_decklink_output_get_audio_clock (self->output);
- if (clock && clock != self->output->clock && clock != audio_clock) {
- // TODO: Adjust time if pipeline clock is not our clock
- //g_assert_not_reached ();
- }
+ convert_to_internal_clock (self, &running_time, &running_time_duration);
GST_LOG_OBJECT (self, "Scheduling video frame %p at %" GST_TIME_FORMAT
" with duration %" GST_TIME_FORMAT, frame, GST_TIME_ARGS (running_time),
@@ -273,11 +334,6 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
out:
- if (clock)
- gst_object_unref (clock);
- if (audio_clock)
- gst_object_unref (audio_clock);
-
frame->Release ();
return flow_ret;
@@ -481,22 +537,12 @@ gst_decklink_video_sink_change_state (GstElement * element,
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:{
GstClockTime start_time = gst_element_get_start_time (element);
HRESULT res;
- GstClock *clock, *audio_clock;
// FIXME: This will probably not work
if (start_time == GST_CLOCK_TIME_NONE)
start_time = 0;
- clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
- audio_clock = gst_decklink_output_get_audio_clock (self->output);
- if (clock && clock != self->output->clock && clock != audio_clock) {
- // TODO: Adjust time if pipeline clock is not our clock
- //g_assert_not_reached ();
- }
- if (clock)
- gst_object_unref (clock);
- if (audio_clock)
- gst_object_unref (audio_clock);
+ convert_to_internal_clock (self, &start_time, NULL);
// The start time is now the running time when we stopped
// playback
@@ -517,22 +563,15 @@ gst_decklink_video_sink_change_state (GstElement * element,
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
GstClockTime start_time = gst_element_get_start_time (element);
HRESULT res;
- GstClock *clock, *audio_clock;
// FIXME: This will probably not work
if (start_time == GST_CLOCK_TIME_NONE)
start_time = 0;
- clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
- audio_clock = gst_decklink_output_get_audio_clock (self->output);
- if (clock && clock != self->output->clock && clock != audio_clock) {
- // TODO: Adjust time if pipeline clock is not our clock
- //g_assert_not_reached ();
- }
- if (clock)
- gst_object_unref (clock);
- if (audio_clock)
- gst_object_unref (audio_clock);
+ self->internal_base_time =
+ gst_clock_get_internal_time (self->output->clock);
+
+ convert_to_internal_clock (self, &start_time, NULL);
GST_DEBUG_OBJECT (self,
"Starting scheduled playback at %" GST_TIME_FORMAT,
diff --git a/sys/decklink/gstdecklinkvideosink.h b/sys/decklink/gstdecklinkvideosink.h
index 01abfd7f7..6052feb5a 100644
--- a/sys/decklink/gstdecklinkvideosink.h
+++ b/sys/decklink/gstdecklinkvideosink.h
@@ -54,6 +54,8 @@ struct _GstDecklinkVideoSink
GstVideoInfo info;
+ GstClockTime internal_base_time;
+
GstDecklinkOutput *output;
};