adaptivedemux2: reverse playback running times

Account for running time moving non-monotonically in
reverse playback by tracking the highest running time
seen at each point.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2510>
This commit is contained in:
Jan Schmidt 2022-04-08 23:06:09 +10:00 committed by Tim-Philipp Müller
parent 9df7a21ec9
commit a4a805312d
4 changed files with 71 additions and 23 deletions

View file

@ -1354,7 +1354,7 @@
"construct": false, "construct": false,
"construct-only": false, "construct-only": false,
"controllable": false, "controllable": false,
"default": "3000000000", "default": "10000000000",
"max": "18446744073709551615", "max": "18446744073709551615",
"min": "0", "min": "0",
"mutable": "playing", "mutable": "playing",

View file

@ -204,6 +204,8 @@ typedef struct
GstClockTimeDiff runningtime; GstClockTimeDiff runningtime;
/* GST_CLOCK_STIME_NONE for non-timed data */ /* GST_CLOCK_STIME_NONE for non-timed data */
GstClockTimeDiff runningtime_end; GstClockTimeDiff runningtime_end;
/* running time of item for buffering tracking: GST_CLOCK_STIME_NONE for non-timed data */
GstClockTimeDiff runningtime_buffering;
} TrackQueueItem; } TrackQueueItem;
GstAdaptiveDemux2Stream *find_stream_for_track_locked (GstAdaptiveDemux * GstAdaptiveDemux2Stream *find_stream_for_track_locked (GstAdaptiveDemux *

View file

@ -51,7 +51,7 @@ gst_adaptive_demux_track_flush (GstAdaptiveDemuxTrack * track)
gst_segment_init (&track->output_segment, GST_FORMAT_TIME); gst_segment_init (&track->output_segment, GST_FORMAT_TIME);
track->gap_position = track->gap_duration = GST_CLOCK_TIME_NONE; track->gap_position = track->gap_duration = GST_CLOCK_TIME_NONE;
track->output_time = 0; track->output_time = GST_CLOCK_STIME_NONE;
track->next_position = GST_CLOCK_STIME_NONE; track->next_position = GST_CLOCK_STIME_NONE;
track->level_bytes = 0; track->level_bytes = 0;
@ -123,6 +123,7 @@ track_dequeue_data_locked (GstAdaptiveDemux * demux,
gboolean is_pending_sticky = FALSE; gboolean is_pending_sticky = FALSE;
GstEvent *event; GstEvent *event;
GstClockTimeDiff running_time; GstClockTimeDiff running_time;
GstClockTimeDiff running_time_buffering = GST_CLOCK_STIME_NONE;
GstClockTimeDiff running_time_end; GstClockTimeDiff running_time_end;
gsize item_size = 0; gsize item_size = 0;
@ -131,7 +132,8 @@ track_dequeue_data_locked (GstAdaptiveDemux * demux,
event = gst_event_store_get_next_pending (&track->sticky_events); event = gst_event_store_get_next_pending (&track->sticky_events);
if (event != NULL) { if (event != NULL) {
res = (GstMiniObject *) event; res = (GstMiniObject *) event;
running_time = running_time_end = GST_CLOCK_STIME_NONE; running_time_buffering = running_time = running_time_end =
GST_CLOCK_STIME_NONE;
GST_DEBUG_OBJECT (demux, GST_DEBUG_OBJECT (demux,
"track %s dequeued pending sticky event %" GST_PTR_FORMAT, "track %s dequeued pending sticky event %" GST_PTR_FORMAT,
track->stream_id, event); track->stream_id, event);
@ -160,9 +162,16 @@ track_dequeue_data_locked (GstAdaptiveDemux * demux,
} }
res = (GstMiniObject *) gst_event_new_gap (pos, duration); res = (GstMiniObject *) gst_event_new_gap (pos, duration);
running_time = my_segment_to_running_time (&track->output_segment, pos); if (track->output_segment.rate > 0.0) {
running_time_end = running_time = my_segment_to_running_time (&track->output_segment, pos);
my_segment_to_running_time (&track->output_segment, pos + duration); running_time_buffering = running_time_end =
my_segment_to_running_time (&track->output_segment, pos + duration);
} else {
running_time =
my_segment_to_running_time (&track->output_segment, pos + duration);
running_time_buffering = running_time_end =
my_segment_to_running_time (&track->output_segment, pos);
}
item_size = 0; item_size = 0;
break; break;
} }
@ -174,6 +183,7 @@ track_dequeue_data_locked (GstAdaptiveDemux * demux,
res = item.item; res = item.item;
running_time = item.runningtime; running_time = item.runningtime;
running_time_end = item.runningtime_end; running_time_end = item.runningtime_end;
running_time_buffering = item.runningtime_buffering;
item_size = item.size; item_size = item.size;
/* Special case for a gap event, to drain them out little-by-little. /* Special case for a gap event, to drain them out little-by-little.
@ -223,6 +233,18 @@ handle_event:
switch (GST_EVENT_TYPE (event)) { switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEGMENT: case GST_EVENT_SEGMENT:
gst_event_copy_segment (event, &track->output_segment); gst_event_copy_segment (event, &track->output_segment);
if (!GST_CLOCK_STIME_IS_VALID (track->output_time)) {
if (track->output_segment.rate > 0.0)
track->output_time =
my_segment_to_running_time (&track->output_segment,
track->output_segment.start);
else
track->output_time =
my_segment_to_running_time (&track->output_segment,
track->output_segment.stop);
}
if (track->update_next_segment) { if (track->update_next_segment) {
GstClockTimeDiff global_output_position = GstClockTimeDiff global_output_position =
demux->priv->global_output_position; demux->priv->global_output_position;
@ -261,14 +283,21 @@ handle_event:
} }
/* Update track buffering levels */ /* Update track buffering levels */
if (running_time != GST_CLOCK_STIME_NONE) { if (GST_CLOCK_STIME_IS_VALID (running_time_buffering)) {
GstClockTimeDiff output_time; GstClockTimeDiff output_time;
track->output_time = running_time; track->output_time = running_time_buffering;
if (running_time_end != GST_CLOCK_TIME_NONE)
track->output_time = running_time_end; GST_LOG_OBJECT (demux,
"track %s buffering time:%" GST_STIME_FORMAT,
track->stream_id, GST_STIME_ARGS (running_time_buffering));
if (GST_CLOCK_STIME_IS_VALID (track->output_time))
output_time =
MAX (track->output_time, demux->priv->global_output_position);
else
output_time = track->input_time;
output_time = MAX (track->output_time, demux->priv->global_output_position);
if (track->input_time >= output_time) if (track->input_time >= output_time)
track->level_time = track->input_time - output_time; track->level_time = track->input_time - output_time;
else else
@ -409,29 +438,46 @@ track_queue_data_locked (GstAdaptiveDemux * demux,
item.size = size; item.size = size;
item.runningtime = GST_CLOCK_STIME_NONE; item.runningtime = GST_CLOCK_STIME_NONE;
item.runningtime_end = GST_CLOCK_STIME_NONE; item.runningtime_end = GST_CLOCK_STIME_NONE;
item.runningtime_buffering = GST_CLOCK_STIME_NONE;
if (timestamp != GST_CLOCK_TIME_NONE) { if (timestamp != GST_CLOCK_TIME_NONE) {
GstClockTimeDiff output_time; GstClockTimeDiff output_time, input_time;
/* Set the running time of the item */ /* Set the running time of the item */
item.runningtime = input_time = item.runningtime_end = item.runningtime =
my_segment_to_running_time (&track->input_segment, timestamp); my_segment_to_running_time (&track->input_segment, timestamp);
/* Update segment position (include duration if valid) */ /* Update segment position (include duration if valid) */
track->input_segment.position = timestamp; track->input_segment.position = timestamp;
if (GST_CLOCK_TIME_IS_VALID (duration)) { if (GST_CLOCK_TIME_IS_VALID (duration)) {
track->input_segment.position += duration; /* In backward playback, each buffer is played
item.runningtime_end = * 'backward', so the segment position should
my_segment_to_running_time (&track->input_segment, * only include duration in forward playback */
track->input_segment.position); if (track->input_segment.rate > 0.0) {
track->input_segment.position += duration;
input_time = my_segment_to_running_time (&track->input_segment,
track->input_segment.position);
} else {
/* Otherwise, the end of the buffer has the smaller running time */
item.runningtime = my_segment_to_running_time (&track->input_segment,
timestamp + duration);
}
} }
/* Update track input time and level */ /* Update track input time and level */
track->input_time = if (input_time > track->input_time)
my_segment_to_running_time (&track->input_segment, track->input_time = input_time;
track->input_segment.position);
/* Store the maximum running time we've seen as
* this item's "buffering running time" */
item.runningtime_buffering = track->input_time;
if (GST_CLOCK_STIME_IS_VALID (track->output_time))
output_time =
MAX (track->output_time, demux->priv->global_output_position);
else
output_time = track->input_time;
output_time = MAX (track->output_time, demux->priv->global_output_position);
if (track->input_time >= output_time) if (track->input_time >= output_time)
track->level_time = track->input_time - output_time; track->level_time = track->input_time - output_time;
else else
@ -879,7 +925,7 @@ gst_adaptive_demux_track_new (GstAdaptiveDemux * demux,
gst_segment_init (&track->output_segment, GST_FORMAT_TIME); gst_segment_init (&track->output_segment, GST_FORMAT_TIME);
track->gap_position = track->gap_duration = GST_CLOCK_TIME_NONE; track->gap_position = track->gap_duration = GST_CLOCK_TIME_NONE;
track->output_time = 0; track->output_time = GST_CLOCK_STIME_NONE;
track->next_position = GST_CLOCK_STIME_NONE; track->next_position = GST_CLOCK_STIME_NONE;
track->update_next_segment = FALSE; track->update_next_segment = FALSE;

View file

@ -122,7 +122,7 @@ GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug);
#define DEFAULT_MAX_BUFFERING_TIME (30 * GST_SECOND) #define DEFAULT_MAX_BUFFERING_TIME (30 * GST_SECOND)
#define DEFAULT_BUFFERING_HIGH_WATERMARK_TIME (30 * GST_SECOND) #define DEFAULT_BUFFERING_HIGH_WATERMARK_TIME (30 * GST_SECOND)
#define DEFAULT_BUFFERING_LOW_WATERMARK_TIME (3 * GST_SECOND) #define DEFAULT_BUFFERING_LOW_WATERMARK_TIME (10 * GST_SECOND)
#define DEFAULT_BUFFERING_HIGH_WATERMARK_FRAGMENTS 0.0 #define DEFAULT_BUFFERING_HIGH_WATERMARK_FRAGMENTS 0.0
#define DEFAULT_BUFFERING_LOW_WATERMARK_FRAGMENTS 0.0 #define DEFAULT_BUFFERING_LOW_WATERMARK_FRAGMENTS 0.0