matroskademux: Add support for relative position cues

Cueing data can contain the block number within a cluster and/or
a relative offset into the cluster.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5535>
This commit is contained in:
Doug Nazar 2019-07-27 15:15:47 -04:00
parent 3578b23ba2
commit 6e9667b65c
4 changed files with 82 additions and 12 deletions

View File

@ -339,10 +339,12 @@ gst_matroska_demux_reset (GstElement * element)
demux->last_stop_end = GST_CLOCK_TIME_NONE; demux->last_stop_end = GST_CLOCK_TIME_NONE;
demux->seek_block = 0; demux->seek_block = 0;
demux->seek_block_is_offset = FALSE;
demux->stream_start_time = GST_CLOCK_TIME_NONE; demux->stream_start_time = GST_CLOCK_TIME_NONE;
demux->to_time = GST_CLOCK_TIME_NONE; demux->to_time = GST_CLOCK_TIME_NONE;
demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_time = GST_CLOCK_TIME_NONE;
demux->cluster_offset = 0; demux->cluster_offset = 0;
demux->cluster_prefix = 0;
demux->cluster_prevsize = 0; demux->cluster_prevsize = 0;
demux->seen_cluster_prevsize = FALSE; demux->seen_cluster_prevsize = FALSE;
demux->next_cluster_offset = 0; demux->next_cluster_offset = 0;
@ -2256,16 +2258,19 @@ gst_matroska_demux_move_to_entry (GstMatroskaDemux * demux,
demux->next_cluster_offset = 0; demux->next_cluster_offset = 0;
GST_DEBUG_OBJECT (demux, GST_DEBUG_OBJECT (demux,
"Seeked to offset %" G_GUINT64_FORMAT ", block %d, " "time %" "Seeked to offset %" G_GUINT64_FORMAT ", %s %d, " "time %"
GST_TIME_FORMAT, entry->pos + demux->common.ebml_segment_start, GST_TIME_FORMAT, entry->pos + demux->common.ebml_segment_start,
entry->block, GST_TIME_ARGS (entry->time)); entry->relative ? "relative offset" : "block",
entry->relative ? entry->offset : entry->block,
GST_TIME_ARGS (entry->time));
/* update the time */ /* update the time */
gst_matroska_read_common_reset_streams (&demux->common, entry->time, TRUE); gst_matroska_read_common_reset_streams (&demux->common, entry->time, TRUE);
gst_flow_combiner_reset (demux->flowcombiner); gst_flow_combiner_reset (demux->flowcombiner);
demux->common.segment.position = entry->time; demux->common.segment.position = entry->time;
demux->seek_block = entry->block; demux->seek_block = entry->relative ? entry->offset : entry->block;
demux->seek_first = TRUE; demux->seek_block_is_offset = entry->relative;
demux->seek_first = !entry->relative;
demux->last_stop_end = GST_CLOCK_TIME_NONE; demux->last_stop_end = GST_CLOCK_TIME_NONE;
} }
@ -5402,6 +5407,28 @@ gst_matroska_demux_seek_block (GstMatroskaDemux * demux)
} }
} }
/* returns TRUE if we've seeked and should exit */
static inline gboolean
gst_matroska_demux_seek_offset (GstMatroskaDemux * demux)
{
guint64 next_off;
if (G_LIKELY (!demux->seek_block_is_offset))
return FALSE;
next_off = demux->cluster_offset + demux->cluster_prefix + demux->seek_block;
demux->seek_block = 0;
demux->seek_block_is_offset = FALSE;
if (next_off > demux->common.offset && next_off < demux->next_cluster_offset) {
GST_DEBUG_OBJECT (demux, "seeking to %" G_GUINT64_FORMAT, next_off);
demux->common.offset = next_off;
return TRUE;
}
return FALSE;
}
static GstFlowReturn static GstFlowReturn
gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux,
GstEbmlRead * ebml) GstEbmlRead * ebml)
@ -5995,9 +6022,12 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
} }
demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_time = GST_CLOCK_TIME_NONE;
demux->cluster_offset = demux->common.offset; demux->cluster_offset = demux->common.offset;
demux->cluster_prefix = needed;
demux->cluster_prevsize = 0; demux->cluster_prevsize = 0;
if (G_UNLIKELY (!demux->seek_first && demux->seek_block)) { if (G_UNLIKELY (!demux->seek_first && !demux->seek_block_is_offset
GST_DEBUG_OBJECT (demux, "seek target block %" G_GUINT64_FORMAT && demux->seek_block)) {
GST_DEBUG_OBJECT (demux,
"seek target block %" G_GUINT64_FORMAT
" not found in Cluster, trying next Cluster's first block instead", " not found in Cluster, trying next Cluster's first block instead",
demux->seek_block); demux->seek_block);
demux->seek_block = 0; demux->seek_block = 0;
@ -6043,7 +6073,9 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
break; break;
} }
case GST_MATROSKA_ID_BLOCKGROUP: case GST_MATROSKA_ID_BLOCKGROUP:
if (!gst_matroska_demux_seek_block (demux)) if (gst_matroska_demux_seek_offset (demux))
break;
else if (!gst_matroska_demux_seek_block (demux))
goto skip; goto skip;
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml)); GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
DEBUG_ELEMENT_START (demux, &ebml, "BlockGroup"); DEBUG_ELEMENT_START (demux, &ebml, "BlockGroup");
@ -6054,7 +6086,9 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
DEBUG_ELEMENT_STOP (demux, &ebml, "BlockGroup", ret); DEBUG_ELEMENT_STOP (demux, &ebml, "BlockGroup", ret);
break; break;
case GST_MATROSKA_ID_SIMPLEBLOCK: case GST_MATROSKA_ID_SIMPLEBLOCK:
if (!gst_matroska_demux_seek_block (demux)) if (gst_matroska_demux_seek_offset (demux))
break;
else if (!gst_matroska_demux_seek_block (demux))
goto skip; goto skip;
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml)); GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
DEBUG_ELEMENT_START (demux, &ebml, "SimpleBlock"); DEBUG_ELEMENT_START (demux, &ebml, "SimpleBlock");
@ -6528,6 +6562,7 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent,
demux->common.segment.position = GST_CLOCK_TIME_NONE; demux->common.segment.position = GST_CLOCK_TIME_NONE;
demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_time = GST_CLOCK_TIME_NONE;
demux->cluster_offset = 0; demux->cluster_offset = 0;
demux->cluster_prefix = 0;
demux->cluster_prevsize = 0; demux->cluster_prevsize = 0;
demux->need_segment = TRUE; demux->need_segment = TRUE;
demux->segment_seqnum = gst_event_get_seqnum (event); demux->segment_seqnum = gst_event_get_seqnum (event);
@ -6581,6 +6616,7 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent,
demux->common.segment.duration = dur; demux->common.segment.duration = dur;
demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_time = GST_CLOCK_TIME_NONE;
demux->cluster_offset = 0; demux->cluster_offset = 0;
demux->cluster_prefix = 0;
demux->cluster_prevsize = 0; demux->cluster_prevsize = 0;
GST_OBJECT_UNLOCK (demux); GST_OBJECT_UNLOCK (demux);
} }

View File

@ -66,6 +66,7 @@ typedef struct _GstMatroskaDemux {
gboolean streaming; gboolean streaming;
guint64 seek_block; guint64 seek_block;
gboolean seek_first; gboolean seek_first;
gboolean seek_block_is_offset;
/* did we parse cues/tracks/segmentinfo already? */ /* did we parse cues/tracks/segmentinfo already? */
GList *seek_parsed; GList *seek_parsed;
@ -87,6 +88,7 @@ typedef struct _GstMatroskaDemux {
/* some state saving */ /* some state saving */
GstClockTime cluster_time; GstClockTime cluster_time;
guint64 cluster_offset; guint64 cluster_offset;
guint cluster_prefix;
guint64 cluster_prevsize; /* 0 if unknown */ guint64 cluster_prevsize; /* 0 if unknown */
guint64 first_cluster_offset; guint64 first_cluster_offset;
guint64 next_cluster_offset; guint64 next_cluster_offset;

View File

@ -216,6 +216,7 @@
#define GST_MATROSKA_ID_CUETRACK 0xF7 #define GST_MATROSKA_ID_CUETRACK 0xF7
#define GST_MATROSKA_ID_CUECLUSTERPOSITION 0xF1 #define GST_MATROSKA_ID_CUECLUSTERPOSITION 0xF1
#define GST_MATROSKA_ID_CUEBLOCKNUMBER 0x5378 #define GST_MATROSKA_ID_CUEBLOCKNUMBER 0x5378
#define GST_MATROSKA_ID_CUERELATIVEPOSITION 0xF0
/* semi-draft */ /* semi-draft */
#define GST_MATROSKA_ID_CUECODECSTATE 0xEA #define GST_MATROSKA_ID_CUECODECSTATE 0xEA
/* semi-draft */ /* semi-draft */
@ -687,8 +688,12 @@ typedef struct _GstMatroskaTrackSubtitleContext {
typedef struct _GstMatroskaIndex { typedef struct _GstMatroskaIndex {
guint64 pos; /* of the corresponding *cluster*! */ guint64 pos; /* of the corresponding *cluster*! */
GstClockTime time; /* in nanoseconds */ GstClockTime time; /* in nanoseconds */
guint32 block; /* number of the block in the cluster */ union {
guint32 block; /* number of the block in the cluster */
guint32 offset; /* relative offset from start of cluster */
};
guint16 track; /* reference to 'num' */ guint16 track; /* reference to 'num' */
gboolean relative; /* whether we're using relative offset or block number */
} GstMatroskaIndex; } GstMatroskaIndex;
typedef struct _Wavpack4Header { typedef struct _Wavpack4Header {

View File

@ -1639,6 +1639,7 @@ gst_matroska_read_common_parse_index_cuetrack (GstMatroskaReadCommon * common,
idx.track = 0; idx.track = 0;
idx.time = GST_CLOCK_TIME_NONE; idx.time = GST_CLOCK_TIME_NONE;
idx.block = 1; idx.block = 1;
idx.relative = FALSE;
DEBUG_ELEMENT_START (common, ebml, "CueTrackPositions"); DEBUG_ELEMENT_START (common, ebml, "CueTrackPositions");
@ -1702,6 +1703,9 @@ gst_matroska_read_common_parse_index_cuetrack (GstMatroskaReadCommon * common,
break; break;
} }
if (idx.relative)
break;
GST_DEBUG_OBJECT (common->sinkpad, "CueBlockNumber: %" G_GUINT64_FORMAT, GST_DEBUG_OBJECT (common->sinkpad, "CueBlockNumber: %" G_GUINT64_FORMAT,
num); num);
idx.block = num; idx.block = num;
@ -1714,6 +1718,27 @@ gst_matroska_read_common_parse_index_cuetrack (GstMatroskaReadCommon * common,
break; break;
} }
/* byte offset from start of cluster */
case GST_MATROSKA_ID_CUERELATIVEPOSITION:
{
guint64 num;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
break;
GST_DEBUG_OBJECT (common->sinkpad,
"CueRelativePosition: %" G_GUINT64_FORMAT, num);
/* mild sanity check, disregard strange cases ... */
if (num > G_MAXUINT32) {
GST_DEBUG_OBJECT (common->sinkpad, "... looks suspicious, ignoring");
} else {
idx.offset = num;
idx.relative = TRUE;
}
break;
}
default: default:
ret = gst_matroska_read_common_parse_skip (common, ebml, ret = gst_matroska_read_common_parse_skip (common, ebml,
"CueTrackPositions", id); "CueTrackPositions", id);
@ -1730,7 +1755,7 @@ gst_matroska_read_common_parse_index_cuetrack (GstMatroskaReadCommon * common,
/* (e.g.) lavf typically creates entries without a block number, /* (e.g.) lavf typically creates entries without a block number,
* which is bogus and leads to contradictory information */ * which is bogus and leads to contradictory information */
if (common->index->len) { if (common->index->len && !idx.relative) {
GstMatroskaIndex *last_idx; GstMatroskaIndex *last_idx;
last_idx = &g_array_index (common->index, GstMatroskaIndex, last_idx = &g_array_index (common->index, GstMatroskaIndex,
@ -1818,8 +1843,10 @@ gst_matroska_read_common_parse_index_pointentry (GstMatroskaReadCommon *
idx->time = time; idx->time = time;
GST_DEBUG_OBJECT (common->sinkpad, "Index entry: pos=%" G_GUINT64_FORMAT GST_DEBUG_OBJECT (common->sinkpad, "Index entry: pos=%" G_GUINT64_FORMAT
", time=%" GST_TIME_FORMAT ", track=%u, block=%u", idx->pos, ", time=%" GST_TIME_FORMAT ", track=%u, %s=%u", idx->pos,
GST_TIME_ARGS (idx->time), (guint) idx->track, (guint) idx->block); GST_TIME_ARGS (idx->time), (guint) idx->track,
idx->relative ? "offset" : "block",
(guint) (idx->relative ? idx->offset : idx->block));
} }
} }
} else { } else {