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->seek_block = 0;
demux->seek_block_is_offset = FALSE;
demux->stream_start_time = GST_CLOCK_TIME_NONE;
demux->to_time = GST_CLOCK_TIME_NONE;
demux->cluster_time = GST_CLOCK_TIME_NONE;
demux->cluster_offset = 0;
demux->cluster_prefix = 0;
demux->cluster_prevsize = 0;
demux->seen_cluster_prevsize = FALSE;
demux->next_cluster_offset = 0;
@ -2256,16 +2258,19 @@ gst_matroska_demux_move_to_entry (GstMatroskaDemux * demux,
demux->next_cluster_offset = 0;
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,
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 */
gst_matroska_read_common_reset_streams (&demux->common, entry->time, TRUE);
gst_flow_combiner_reset (demux->flowcombiner);
demux->common.segment.position = entry->time;
demux->seek_block = entry->block;
demux->seek_first = TRUE;
demux->seek_block = entry->relative ? entry->offset : entry->block;
demux->seek_block_is_offset = entry->relative;
demux->seek_first = !entry->relative;
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
gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux,
GstEbmlRead * ebml)
@ -5995,9 +6022,12 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
}
demux->cluster_time = GST_CLOCK_TIME_NONE;
demux->cluster_offset = demux->common.offset;
demux->cluster_prefix = needed;
demux->cluster_prevsize = 0;
if (G_UNLIKELY (!demux->seek_first && demux->seek_block)) {
GST_DEBUG_OBJECT (demux, "seek target block %" G_GUINT64_FORMAT
if (G_UNLIKELY (!demux->seek_first && !demux->seek_block_is_offset
&& demux->seek_block)) {
GST_DEBUG_OBJECT (demux,
"seek target block %" G_GUINT64_FORMAT
" not found in Cluster, trying next Cluster's first block instead",
demux->seek_block);
demux->seek_block = 0;
@ -6043,7 +6073,9 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id,
break;
}
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;
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
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);
break;
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;
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
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->cluster_time = GST_CLOCK_TIME_NONE;
demux->cluster_offset = 0;
demux->cluster_prefix = 0;
demux->cluster_prevsize = 0;
demux->need_segment = TRUE;
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->cluster_time = GST_CLOCK_TIME_NONE;
demux->cluster_offset = 0;
demux->cluster_prefix = 0;
demux->cluster_prevsize = 0;
GST_OBJECT_UNLOCK (demux);
}

View File

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

View File

@ -216,6 +216,7 @@
#define GST_MATROSKA_ID_CUETRACK 0xF7
#define GST_MATROSKA_ID_CUECLUSTERPOSITION 0xF1
#define GST_MATROSKA_ID_CUEBLOCKNUMBER 0x5378
#define GST_MATROSKA_ID_CUERELATIVEPOSITION 0xF0
/* semi-draft */
#define GST_MATROSKA_ID_CUECODECSTATE 0xEA
/* semi-draft */
@ -687,8 +688,12 @@ typedef struct _GstMatroskaTrackSubtitleContext {
typedef struct _GstMatroskaIndex {
guint64 pos; /* of the corresponding *cluster*! */
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' */
gboolean relative; /* whether we're using relative offset or block number */
} GstMatroskaIndex;
typedef struct _Wavpack4Header {

View File

@ -1639,6 +1639,7 @@ gst_matroska_read_common_parse_index_cuetrack (GstMatroskaReadCommon * common,
idx.track = 0;
idx.time = GST_CLOCK_TIME_NONE;
idx.block = 1;
idx.relative = FALSE;
DEBUG_ELEMENT_START (common, ebml, "CueTrackPositions");
@ -1702,6 +1703,9 @@ gst_matroska_read_common_parse_index_cuetrack (GstMatroskaReadCommon * common,
break;
}
if (idx.relative)
break;
GST_DEBUG_OBJECT (common->sinkpad, "CueBlockNumber: %" G_GUINT64_FORMAT,
num);
idx.block = num;
@ -1714,6 +1718,27 @@ gst_matroska_read_common_parse_index_cuetrack (GstMatroskaReadCommon * common,
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:
ret = gst_matroska_read_common_parse_skip (common, ebml,
"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,
* which is bogus and leads to contradictory information */
if (common->index->len) {
if (common->index->len && !idx.relative) {
GstMatroskaIndex *last_idx;
last_idx = &g_array_index (common->index, GstMatroskaIndex,
@ -1818,8 +1843,10 @@ gst_matroska_read_common_parse_index_pointentry (GstMatroskaReadCommon *
idx->time = time;
GST_DEBUG_OBJECT (common->sinkpad, "Index entry: pos=%" G_GUINT64_FORMAT
", time=%" GST_TIME_FORMAT ", track=%u, block=%u", idx->pos,
GST_TIME_ARGS (idx->time), (guint) idx->track, (guint) idx->block);
", time=%" GST_TIME_FORMAT ", track=%u, %s=%u", idx->pos,
GST_TIME_ARGS (idx->time), (guint) idx->track,
idx->relative ? "offset" : "block",
(guint) (idx->relative ? idx->offset : idx->block));
}
}
} else {