diff --git a/subprojects/gst-plugins-good/gst/matroska/matroska-demux.c b/subprojects/gst-plugins-good/gst/matroska/matroska-demux.c index aeacfe1bb2..f3019406e3 100644 --- a/subprojects/gst-plugins-good/gst/matroska/matroska-demux.c +++ b/subprojects/gst-plugins-good/gst/matroska/matroska-demux.c @@ -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); } diff --git a/subprojects/gst-plugins-good/gst/matroska/matroska-demux.h b/subprojects/gst-plugins-good/gst/matroska/matroska-demux.h index d45c8f3e1e..389959ae25 100644 --- a/subprojects/gst-plugins-good/gst/matroska/matroska-demux.h +++ b/subprojects/gst-plugins-good/gst/matroska/matroska-demux.h @@ -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; diff --git a/subprojects/gst-plugins-good/gst/matroska/matroska-ids.h b/subprojects/gst-plugins-good/gst/matroska/matroska-ids.h index a063f40df9..bd9f1376bb 100644 --- a/subprojects/gst-plugins-good/gst/matroska/matroska-ids.h +++ b/subprojects/gst-plugins-good/gst/matroska/matroska-ids.h @@ -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 { diff --git a/subprojects/gst-plugins-good/gst/matroska/matroska-read-common.c b/subprojects/gst-plugins-good/gst/matroska/matroska-read-common.c index 6c9155178f..a314d7cb95 100644 --- a/subprojects/gst-plugins-good/gst/matroska/matroska-read-common.c +++ b/subprojects/gst-plugins-good/gst/matroska/matroska-read-common.c @@ -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 {