decklink: calculate internal using values closer to the current clock times
In a previous version of the clock time conversion code, scheduled playback used to be started (from 0) when transitioning to PLAYING and stopped when transitioning PLAYING->PAUSED. This worked fine when converting running times using the internal clock. However, now the decklink clock will produce values that are monotonically increasing and do not reset to 0 at the same moments as running time anymore. This means that the clock adjustments could attempt to convert a small running time based on a large clock time e.g. after pausing for many hours. As the adjustment code is a simple linear interpolation based on the current clock times (large) using the provided value (small), the small differences in the rate could result in very large differences in the output time. Fix by instead using both internal and external clock times based on the values that gst_clock_get_calibration() will return. By doing so, small changes in the rate calculations between the internal and external clock times will not result in potentially large differences in the output internal time from gst_clock_unadjust_with_calibration(). Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4197 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9267>
This commit is contained in:
parent
d432dbd106
commit
cae2b38614
@ -1343,49 +1343,31 @@ gst_decklink_video_sink_convert_to_internal_clock (GstDecklinkVideoSink * self,
|
||||
GstClockTime * timestamp, GstClockTime * duration)
|
||||
{
|
||||
GstClock *clock;
|
||||
GstClockTime internal_base, external_base, internal_offset;
|
||||
GstClockTime internal_base, external_base, base_time;
|
||||
|
||||
g_assert (timestamp != NULL);
|
||||
|
||||
clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
|
||||
base_time = gst_element_get_base_time (GST_ELEMENT_CAST (self));
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
internal_base = self->internal_base_time;
|
||||
external_base = self->external_base_time;
|
||||
internal_offset = self->internal_time_offset;
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (!clock || clock != self->output->clock) {
|
||||
// If we don't have any initial correlation between multiple clocks
|
||||
if (external_base == GST_CLOCK_TIME_NONE ||
|
||||
internal_base == GST_CLOCK_TIME_NONE) {
|
||||
*timestamp = gst_clock_get_internal_time (self->output->clock);
|
||||
} else if (!clock || clock != self->output->clock) {
|
||||
GstClockTime internal, external, rate_n, rate_d;
|
||||
GstClockTime external_timestamp = *timestamp;
|
||||
GstClockTime base_time;
|
||||
|
||||
gst_clock_get_calibration (self->output->clock, &internal, &external,
|
||||
&rate_n, &rate_d);
|
||||
|
||||
// Convert to the running time corresponding to both clock times
|
||||
if (!GST_CLOCK_TIME_IS_VALID (internal_base) || internal < internal_base)
|
||||
internal = 0;
|
||||
else
|
||||
internal -= internal_base;
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (external_base) || external < external_base)
|
||||
external = 0;
|
||||
else
|
||||
external -= external_base;
|
||||
|
||||
// Convert timestamp to the "running time" since we started scheduled
|
||||
// playback, that is the difference between the pipeline's base time
|
||||
// and our own base time.
|
||||
base_time = gst_element_get_base_time (GST_ELEMENT_CAST (self));
|
||||
if (base_time > external_base)
|
||||
base_time = 0;
|
||||
else
|
||||
base_time = external_base - base_time;
|
||||
|
||||
if (external_timestamp < base_time)
|
||||
external_timestamp = 0;
|
||||
else
|
||||
external_timestamp = external_timestamp - base_time;
|
||||
// get the clock time for this running time
|
||||
external_timestamp += base_time;
|
||||
|
||||
// Get the difference in the external time, note
|
||||
// that the running time is external time.
|
||||
@ -1420,18 +1402,12 @@ gst_decklink_video_sink_convert_to_internal_clock (GstDecklinkVideoSink * self,
|
||||
} else {
|
||||
GST_LOG_OBJECT (self, "No clock conversion needed, same clocks: %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (*timestamp));
|
||||
*timestamp += base_time;
|
||||
}
|
||||
|
||||
if (external_base != GST_CLOCK_TIME_NONE &&
|
||||
internal_base != GST_CLOCK_TIME_NONE)
|
||||
*timestamp += internal_offset;
|
||||
else
|
||||
*timestamp = gst_clock_get_internal_time (self->output->clock);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Output timestamp %" GST_TIME_FORMAT
|
||||
" using clock epoch %" GST_TIME_FORMAT " and offset %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (*timestamp), GST_TIME_ARGS (self->output->clock_epoch),
|
||||
GST_TIME_ARGS (internal_offset));
|
||||
" using clock epoch %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (*timestamp), GST_TIME_ARGS (self->output->clock_epoch));
|
||||
|
||||
if (clock)
|
||||
gst_object_unref (clock);
|
||||
@ -2086,30 +2062,24 @@ gst_decklink_video_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->initial_sync = FALSE;
|
||||
if (clock) {
|
||||
GstClockTime external_time = gst_clock_get_internal_time (clock);
|
||||
GstClockTime internal_time = gst_clock_get_internal_time (self->output->clock);
|
||||
|
||||
if (clock != self->output->clock) {
|
||||
gst_clock_set_master (self->output->clock, clock);
|
||||
}
|
||||
|
||||
if (self->external_base_time == GST_CLOCK_TIME_NONE
|
||||
|| self->internal_base_time == GST_CLOCK_TIME_NONE) {
|
||||
self->external_base_time = gst_clock_get_internal_time (clock);
|
||||
self->internal_base_time =
|
||||
gst_clock_get_internal_time (self->output->clock);
|
||||
self->internal_time_offset = self->internal_base_time;
|
||||
} else if (GST_CLOCK_TIME_IS_VALID (self->internal_pause_time)) {
|
||||
self->internal_time_offset +=
|
||||
gst_clock_get_internal_time (self->output->clock) -
|
||||
self->internal_pause_time;
|
||||
self->internal_pause_time = GST_CLOCK_TIME_NONE;
|
||||
self->external_base_time = external_time;
|
||||
self->internal_base_time = internal_time;
|
||||
}
|
||||
|
||||
GST_INFO_OBJECT (self, "clock has been set to %" GST_PTR_FORMAT
|
||||
", updated base times - internal: %" GST_TIME_FORMAT
|
||||
" external: %" GST_TIME_FORMAT " internal offset %"
|
||||
GST_TIME_FORMAT, clock,
|
||||
" external: %" GST_TIME_FORMAT, clock,
|
||||
GST_TIME_ARGS (self->internal_base_time),
|
||||
GST_TIME_ARGS (self->external_base_time),
|
||||
GST_TIME_ARGS (self->internal_time_offset));
|
||||
GST_TIME_ARGS (self->external_base_time));
|
||||
|
||||
gst_object_unref (clock);
|
||||
} else {
|
||||
@ -2487,7 +2457,6 @@ gst_decklink_video_sink_change_state (GstElement * element,
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->internal_base_time = GST_CLOCK_TIME_NONE;
|
||||
self->external_base_time = GST_CLOCK_TIME_NONE;
|
||||
self->internal_pause_time = GST_CLOCK_TIME_NONE;
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
}
|
||||
@ -2497,8 +2466,6 @@ gst_decklink_video_sink_change_state (GstElement * element,
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
break;
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
self->internal_pause_time =
|
||||
gst_clock_get_internal_time (self->output->clock);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -2527,6 +2494,7 @@ gst_decklink_video_sink_event (GstBaseSink * bsink, GstEvent * event)
|
||||
/* force a recalculation of clock base times */
|
||||
self->external_base_time = GST_CLOCK_TIME_NONE;
|
||||
self->internal_base_time = GST_CLOCK_TIME_NONE;
|
||||
self->initial_sync = TRUE;
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
break;
|
||||
|
@ -63,10 +63,6 @@ struct _GstDecklinkVideoSink
|
||||
GstClockTime internal_base_time;
|
||||
GstClockTime external_base_time;
|
||||
|
||||
/* really an internal start time */
|
||||
GstClockTime internal_time_offset;
|
||||
GstClockTime internal_pause_time;
|
||||
|
||||
GstDecklinkOutput *output;
|
||||
|
||||
GstVideoVBIEncoder *vbiencoder;
|
||||
|
Loading…
x
Reference in New Issue
Block a user