summaryrefslogtreecommitdiff
path: root/gst
diff options
context:
space:
mode:
authorEdward Hervey <edward@centricular.com>2021-07-01 08:35:01 +0200
committerEdward Hervey <bilboed@bilboed.com>2021-07-21 14:33:19 +0000
commitac483b238455000917f5489574e24d2c20dfc0b4 (patch)
tree049a7f20e235f31d8b4183f73ca7bc2222861019 /gst
parentc24d48765dd1236795c8c00343962a13f53885a4 (diff)
downloadgstreamer-plugins-bad-ac483b238455000917f5489574e24d2c20dfc0b4.tar.gz
mxfdemux: Refactor index table and offset handling
* Streamline offset <=> entry handling. Historically the demuxer didn't support information from index tables and stored the discovered information in an array per track. When index table support was added, a parallel system was setup for that relationship. This commit unifies this into one system with the `find_edit_entry()` and `find_entry_for_offset()` functions. * By extension, per-track offset entry tables are only created/used if no index table is present for those tracks. * Use index table information as-is. The index table system from MXF is quite complex and there are various ways to use the information contained within. Instead of converting that information we store the data from the tables as-is and extract the needed information when needed. * Handle index tables without entries (i.e. all content package units are of the same size). * Allow collecting index table segments as we go instead of only once if a random-index-pack is present. This also improves support of some files in push-mode. * When searching for keyframe entries, use the keyframe_offset if present (speeds up searching). * For interleaved content (i.e. several tracks in the sample essence container), we use a system to be able to identify the position of each track in the delta entries of index tables. * Handle temporal offset only on tracks which *do* need it (as specified in the delta entries of the index tables). If present, those offsets are stored in a pre-processed table which allows computing PTS from DTS with a simple offset. * Add a quirk for files which are known to be have wrongly stored temporal offsets. * Overall opens the way to handle more types of MXF files, especially those with non-frame-wrapping. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2371>
Diffstat (limited to 'gst')
-rw-r--r--gst/mxf/mxfdemux.c1411
-rw-r--r--gst/mxf/mxfdemux.h60
-rw-r--r--gst/mxf/mxftypes.c15
-rw-r--r--gst/mxf/mxftypes.h5
4 files changed, 1089 insertions, 402 deletions
diff --git a/gst/mxf/mxfdemux.c b/gst/mxf/mxfdemux.c
index a9fc44e4c..d40ff4c3c 100644
--- a/gst/mxf/mxfdemux.c
+++ b/gst/mxf/mxfdemux.c
@@ -206,6 +206,7 @@ gst_mxf_demux_reset_linked_metadata (GstMXFDemux * demux)
&g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack, i);
track->source_package = NULL;
+ track->delta_id = -1;
track->source_track = NULL;
}
@@ -288,7 +289,8 @@ gst_mxf_demux_reset (GstMXFDemux * demux)
for (l = demux->index_tables; l; l = l->next) {
GstMXFDemuxIndexTable *t = l->data;
- g_array_free (t->offsets, TRUE);
+ g_array_free (t->segments, TRUE);
+ g_array_free (t->reverse_temporal_offsets, TRUE);
g_free (t);
}
g_list_free (demux->index_tables);
@@ -508,6 +510,7 @@ gst_mxf_demux_resolve_references (GstMXFDemux * demux)
GHashTableIter iter;
MXFMetadataBase *m = NULL;
GstStructure *structure;
+ guint i;
g_rw_lock_writer_lock (&demux->metadata_lock);
@@ -551,6 +554,23 @@ gst_mxf_demux_resolve_references (GstMXFDemux * demux)
gst_structure_free (structure);
+ /* Check for quirks */
+ for (i = 0; i < demux->preface->n_identifications; i++) {
+ MXFMetadataIdentification *identification =
+ demux->preface->identifications[i];
+
+ GST_DEBUG_OBJECT (demux, "product:'%s' company:'%s'",
+ identification->product_name, identification->company_name);
+ if (!g_strcmp0 (identification->product_name, "MXFTk Advanced") &&
+ !g_strcmp0 (identification->company_name, "OpenCube") &&
+ identification->product_version.major <= 2 &&
+ identification->product_version.minor <= 0) {
+ GST_WARNING_OBJECT (demux,
+ "Setting up quirk for misuse of temporal_order field");
+ demux->temporal_order_misuse = TRUE;
+ }
+ }
+
g_rw_lock_writer_unlock (&demux->metadata_lock);
return ret;
@@ -788,6 +808,7 @@ gst_mxf_demux_update_essence_tracks (GstMXFDemux * demux)
etrack->source_package = NULL;
etrack->source_track = NULL;
+ etrack->delta_id = -1;
if (!track->parent.sequence) {
GST_WARNING_OBJECT (demux, "Source track has no sequence");
@@ -902,6 +923,26 @@ gst_mxf_demux_update_essence_tracks (GstMXFDemux * demux)
}
}
+ /* FIXME : We really should just abort/ignore the stream completely if we
+ * don't have a handler for it */
+ if (etrack->handler != NULL)
+ etrack->wrapping = etrack->handler->get_track_wrapping (track);
+ else
+ etrack->wrapping = MXF_ESSENCE_WRAPPING_UNKNOWN_WRAPPING;
+
+ if (package->is_interleaved) {
+ GST_DEBUG_OBJECT (demux,
+ "track comes from interleaved source package with %d track(s), setting delta_id to -1",
+ package->parent.n_tracks);
+ if (etrack->wrapping != MXF_ESSENCE_WRAPPING_FRAME_WRAPPING) {
+ GST_ELEMENT_ERROR (demux, STREAM, WRONG_TYPE, (NULL),
+ ("Non-frame-wrapping is not allowed in interleaved File Package."));
+ return GST_FLOW_ERROR;
+ }
+ etrack->delta_id = MXF_INDEX_DELTA_ID_UNKNOWN;
+ } else {
+ etrack->delta_id = MXF_INDEX_DELTA_ID_UNKNOWN;
+ }
etrack->source_package = package;
etrack->source_track = track;
continue;
@@ -1715,6 +1756,613 @@ gst_mxf_demux_pad_set_component (GstMXFDemux * demux, GstMXFDemuxPad * pad,
return ret;
}
+/*
+ * Find the partition containing the stream offset of the given track
+ * */
+static GstMXFDemuxPartition *
+get_partition_for_stream_offset (GstMXFDemux * demux,
+ GstMXFDemuxEssenceTrack * etrack, guint64 stream_offset)
+{
+ GList *tmp;
+ GstMXFDemuxPartition *offset_partition = NULL, *next_partition = NULL;
+
+ for (tmp = demux->partitions; tmp; tmp = tmp->next) {
+ GstMXFDemuxPartition *partition = tmp->data;
+
+ if (!next_partition && offset_partition)
+ next_partition = partition;
+
+ if (partition->partition.body_sid != etrack->body_sid)
+ continue;
+ if (partition->partition.body_offset > stream_offset)
+ break;
+
+ offset_partition = partition;
+ next_partition = NULL;
+ }
+
+ if (offset_partition
+ && stream_offset < offset_partition->partition.body_offset)
+ return NULL;
+
+ /* Are we overriding into the next partition ? */
+ if (next_partition) {
+ guint64 partition_essence_size =
+ next_partition->partition.this_partition -
+ offset_partition->partition.this_partition +
+ offset_partition->essence_container_offset;
+ guint64 in_partition =
+ stream_offset - offset_partition->partition.body_offset;
+ if (in_partition >= partition_essence_size) {
+ GST_WARNING_OBJECT (demux,
+ "stream_offset %" G_GUINT64_FORMAT
+ " in track body_sid:% index_sid:%d leaks into next unrelated partition (body_sid:%d / index_sid:%d)",
+ stream_offset, etrack->body_sid, etrack->index_sid,
+ next_partition->partition.body_sid,
+ next_partition->partition.index_sid);
+ return NULL;
+ }
+ }
+ return offset_partition;
+}
+
+static GstMXFDemuxIndexTable *
+get_track_index_table (GstMXFDemux * demux, GstMXFDemuxEssenceTrack * etrack)
+{
+ GList *l;
+
+ /* Look in the indextables */
+ for (l = demux->index_tables; l; l = l->next) {
+ GstMXFDemuxIndexTable *tmp = l->data;
+
+ if (tmp->body_sid == etrack->body_sid
+ && tmp->index_sid == etrack->index_sid) {
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+static guint32
+get_track_max_temporal_offset (GstMXFDemux * demux,
+ GstMXFDemuxEssenceTrack * etrack)
+{
+ GstMXFDemuxIndexTable *table;
+
+ if (etrack->intra_only)
+ return 0;
+
+ table = get_track_index_table (demux, etrack);
+
+ if (table)
+ return table->max_temporal_offset;
+ return 0;
+}
+
+static guint64
+find_offset (GArray * offsets, gint64 * position, gboolean keyframe)
+{
+ GstMXFDemuxIndex *idx;
+ guint64 current_offset = -1;
+ gint64 current_position = *position;
+
+ if (!offsets || offsets->len <= *position)
+ return -1;
+
+ idx = &g_array_index (offsets, GstMXFDemuxIndex, *position);
+ if (idx->offset != 0 && (!keyframe || idx->keyframe)) {
+ current_offset = idx->offset;
+ } else if (idx->offset != 0) {
+ current_position--;
+ while (current_position >= 0) {
+ GST_LOG ("current_position %" G_GINT64_FORMAT, current_position);
+ idx = &g_array_index (offsets, GstMXFDemuxIndex, current_position);
+ if (idx->offset == 0) {
+ GST_LOG ("breaking offset 0");
+ break;
+ } else if (!idx->keyframe) {
+ current_position--;
+ continue;
+ } else {
+ GST_LOG ("Breaking found offset");
+ current_offset = idx->offset;
+ break;
+ }
+ }
+ }
+
+ if (current_offset == -1)
+ return -1;
+
+ *position = current_position;
+ return current_offset;
+}
+
+/**
+ * find_edit_entry:
+ * @demux: The demuxer
+ * @etrack: The target essence track
+ * @position: An edit unit position
+ * @keyframe: if TRUE search for supporting keyframe
+ * @entry: (out): Will be filled with the matching entry information
+ *
+ * Finds the edit entry of @etrack for the given edit unit @position and fill
+ * @entry with the information about that edit entry. If @keyframe is TRUE, the
+ * supporting entry (i.e. keyframe) for the given position will be searched for.
+ *
+ * For frame-wrapped contents, the returned offset will be the position of the
+ * KLV of the content. For clip-wrapped content, the returned offset will be the
+ * position of the essence (i.e. without KLV header) and the entry will specify
+ * the size (in bytes).
+ *
+ * The returned entry will also specify the duration (in edit units) of the
+ * content, which can be different from 1 for special cases (such as raw audio
+ * where multiple samples could be aggregated).
+ *
+ * Returns: TRUE if the entry was found and @entry was properly filled, else
+ * FALSE.
+ */
+static gboolean
+find_edit_entry (GstMXFDemux * demux, GstMXFDemuxEssenceTrack * etrack,
+ gint64 position, gboolean keyframe, GstMXFDemuxIndex * entry)
+{
+ GstMXFDemuxIndexTable *index_table = NULL;
+ guint i;
+ MXFIndexTableSegment *segment = NULL;
+ GstMXFDemuxPartition *offset_partition = NULL;
+ guint64 stream_offset = G_MAXUINT64, absolute_offset;
+
+ GST_DEBUG_OBJECT (demux,
+ "track %d body_sid:%d index_sid:%d delta_id:%d position:%" G_GINT64_FORMAT
+ " keyframe:%d", etrack->track_id, etrack->body_sid,
+ etrack->index_sid, etrack->delta_id, position, keyframe);
+
+ /* Default values */
+ entry->duration = 1;
+ /* By default every entry is a keyframe unless specified otherwise */
+ entry->keyframe = TRUE;
+
+ /* Look in the track offsets */
+ if (etrack->offsets && etrack->offsets->len > position) {
+ if (find_offset (etrack->offsets, &position, keyframe) != -1) {
+ *entry = g_array_index (etrack->offsets, GstMXFDemuxIndex, position);
+ GST_LOG_OBJECT (demux, "Found entry in track offsets");
+ return TRUE;
+ } else
+ GST_LOG_OBJECT (demux, "Didn't find entry in track offsets");
+ }
+
+ /* Look in the indextables */
+ index_table = get_track_index_table (demux, etrack);
+
+ if (!index_table) {
+ GST_DEBUG_OBJECT (demux,
+ "Couldn't find index table for body_sid:%d index_sid:%d",
+ etrack->body_sid, etrack->index_sid);
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (demux,
+ "Looking for position %" G_GINT64_FORMAT
+ " in index table (max temporal offset %u)",
+ etrack->position, index_table->max_temporal_offset);
+
+ /* Searching for a position in index tables works in 3 steps:
+ *
+ * 1. Figure out the table segment containing that position
+ * 2. Figure out the "stream offset" (and additional flags/timing) of that
+ * position from the table segment.
+ * 3. Figure out the "absolute offset" of that "stream offset" using partitions
+ */
+
+search_in_segment:
+
+ /* Find matching index segment */
+ GST_DEBUG_OBJECT (demux, "Look for entry in %d segments",
+ index_table->segments->len);
+ for (i = 0; i < index_table->segments->len; i++) {
+ MXFIndexTableSegment *cand =
+ &g_array_index (index_table->segments, MXFIndexTableSegment, i);
+ if (position >= cand->index_start_position && (cand->index_duration == 0
+ || position <
+ (cand->index_start_position + cand->index_duration))) {
+ GST_DEBUG_OBJECT (demux,
+ "Entry is in Segment #%d , start: %" G_GINT64_FORMAT " , duration: %"
+ G_GINT64_FORMAT, i, cand->index_start_position, cand->index_duration);
+ segment = cand;
+ break;
+ }
+ }
+ if (!segment) {
+ GST_DEBUG_OBJECT (demux,
+ "Didn't find index table segment for position %" G_GINT64_FORMAT,
+ position);
+ return FALSE;
+ }
+
+ /* Were we asked for a keyframe ? */
+ if (keyframe) {
+ if (segment->edit_unit_byte_count && !segment->n_index_entries) {
+ GST_LOG_OBJECT (demux,
+ "Index table without entries, directly using requested position for keyframe search");
+ } else {
+ gint64 candidate;
+ GST_LOG_OBJECT (demux, "keyframe search");
+ /* Search backwards for keyframe */
+ for (candidate = position; candidate >= segment->index_start_position;
+ candidate--) {
+ MXFIndexEntry *segment_index_entry =
+ &segment->index_entries[candidate - segment->index_start_position];
+
+ /* Match */
+ if (segment_index_entry->flags & 0x80) {
+ GST_LOG_OBJECT (demux, "Found keyframe at position %" G_GINT64_FORMAT,
+ candidate);
+ position = candidate;
+ break;
+ }
+
+ /* If a keyframe offset is specified and valid, use that */
+ if (segment_index_entry->key_frame_offset
+ && !(segment_index_entry->flags & 0x08)) {
+ GST_DEBUG_OBJECT (demux, "Using keyframe offset %d",
+ segment_index_entry->key_frame_offset);
+ position = candidate + segment_index_entry->key_frame_offset;
+ if (position < segment->index_start_position) {
+ GST_DEBUG_OBJECT (demux, "keyframe info is in previous segment");
+ goto search_in_segment;
+ }
+ break;
+ }
+
+ /* If we reached the beginning, use that */
+ if (candidate == 0) {
+ GST_LOG_OBJECT (demux,
+ "Reached position 0 while searching for keyframe");
+ position = 0;
+ break;
+ }
+
+ /* If we looped past the beginning of this segment, go to the previous one */
+ if (candidate == segment->index_start_position) {
+ position = candidate - 1;
+ GST_LOG_OBJECT (demux, "Looping with new position %" G_GINT64_FORMAT,
+ position);
+ goto search_in_segment;
+ }
+
+ /* loop back to check previous entry */
+ }
+ }
+ }
+
+ /* Figure out the stream offset (also called "body offset" in specification) */
+ if (segment->edit_unit_byte_count && !segment->n_index_entries) {
+ /* Constant entry table. */
+ stream_offset = position * segment->edit_unit_byte_count;
+ if (etrack->delta_id >= 0) {
+ MXFDeltaEntry *delta_entry = &segment->delta_entries[etrack->delta_id];
+ GST_LOG_OBJECT (demux,
+ "Using delta %d pos_table_index:%d slice:%u element_delta:%u",
+ etrack->delta_id, delta_entry->pos_table_index, delta_entry->slice,
+ delta_entry->element_delta);
+ stream_offset += delta_entry->element_delta;
+ entry->size = segment->edit_unit_byte_count;
+ }
+ } else if (segment->n_index_entries) {
+ MXFIndexEntry *segment_index_entry;
+ MXFDeltaEntry *delta_entry = NULL;
+ g_assert (position <=
+ segment->index_start_position + segment->n_index_entries);
+ segment_index_entry =
+ &segment->index_entries[position - segment->index_start_position];
+ stream_offset = segment_index_entry->stream_offset;
+
+ if (segment->n_delta_entries > 0)
+ delta_entry = &segment->delta_entries[etrack->delta_id];
+
+ if (delta_entry) {
+ GST_LOG_OBJECT (demux,
+ "Using delta %d pos_table_index:%d slice:%u element_delta:%u",
+ etrack->delta_id, delta_entry->pos_table_index, delta_entry->slice,
+ delta_entry->element_delta);
+
+ /* Apply offset from slice/delta if needed */
+ if (delta_entry->slice)
+ stream_offset +=
+ segment_index_entry->slice_offset[delta_entry->slice - 1];
+ stream_offset += delta_entry->element_delta;
+ if (delta_entry->pos_table_index == -1) {
+ entry->keyframe = (segment_index_entry->flags & 0x80) == 0x80;
+ }
+ /* FIXME : Handle fractional offset position (delta_entry->pos_table_offset > 0) */
+ }
+
+ /* Apply reverse temporal reordering if present */
+ if (index_table->reordered_delta_entry == etrack->delta_id) {
+ if (position >= index_table->reverse_temporal_offsets->len) {
+ GST_WARNING_OBJECT (demux,
+ "Can't apply temporal offset for position %" G_GINT64_FORMAT
+ " (max:%d)", position, index_table->reverse_temporal_offsets->len);
+ }
+ if (demux->temporal_order_misuse) {
+ GST_DEBUG_OBJECT (demux, "Handling temporal order misuse");
+ entry->pts = position + segment_index_entry->temporal_offset;
+ } else {
+ entry->pts =
+ position + g_array_index (index_table->reverse_temporal_offsets,
+ gint8, position);
+ GST_LOG_OBJECT (demux,
+ "Applied temporal offset. dts:%" G_GINT64_FORMAT " pts:%"
+ G_GINT64_FORMAT, position, entry->pts);
+ }
+ } else
+ entry->pts = position;
+ } else {
+ /* Note : This should have been handled in the parser */
+ GST_WARNING_OBJECT (demux,
+ "Can't handle index tables without entries nor constant edit unit byte count");
+ return FALSE;
+ }
+
+ /* Find the partition containing the stream offset for this track */
+ offset_partition =
+ get_partition_for_stream_offset (demux, etrack, stream_offset);
+
+ if (!offset_partition) {
+ GST_WARNING_OBJECT (demux,
+ "Couldn't find matching partition for stream offset %" G_GUINT64_FORMAT,
+ stream_offset);
+ return FALSE;
+ }
+
+ /* Convert stream offset to absolute offset using matching partition */
+ absolute_offset =
+ offset_partition->partition.this_partition +
+ offset_partition->essence_container_offset + (stream_offset -
+ offset_partition->partition.body_offset);
+
+ GST_LOG_OBJECT (demux,
+ "track %d position:%" G_GINT64_FORMAT " stream_offset %" G_GUINT64_FORMAT
+ " matches to absolute offset %" G_GUINT64_FORMAT, etrack->track_id,
+ position, stream_offset, absolute_offset);
+ entry->initialized = TRUE;
+ entry->offset = absolute_offset;
+ entry->dts = position;
+
+ return TRUE;
+}
+
+/**
+ * find_entry_for_offset:
+ * @demux: The demuxer
+ * @etrack: The target essence track
+ * @offset: An absolute byte offset (excluding run_in)
+ * @entry: (out): Will be filled with the matching entry information
+ *
+ * Find the entry located at the given absolute byte offset.
+ *
+ * Note: the offset requested should be in the current partition !
+ *
+ * Returns: TRUE if the entry was found and @entry was properly filled, else
+ * FALSE.
+ */
+static gboolean
+find_entry_for_offset (GstMXFDemux * demux, GstMXFDemuxEssenceTrack * etrack,
+ guint64 offset, GstMXFDemuxIndex * retentry)
+{
+ GstMXFDemuxIndexTable *index_table = get_track_index_table (demux, etrack);
+ guint i;
+ MXFIndexTableSegment *index_segment = NULL;
+ GstMXFDemuxPartition *partition = demux->current_partition;
+ guint64 original_offset = offset;
+ guint64 cp_offset = 0; /* Offset in Content Package */
+ MXFIndexEntry *index_entry = NULL;
+ MXFDeltaEntry *delta_entry = NULL;
+ gint64 position = 0;
+
+ GST_DEBUG_OBJECT (demux,
+ "track %d body_sid:%d index_sid:%d offset:%" G_GUINT64_FORMAT,
+ etrack->track_id, etrack->body_sid, etrack->index_sid, offset);
+
+ /* Default value */
+ retentry->duration = 1;
+ retentry->keyframe = TRUE;
+
+ /* Index-less search */
+ if (etrack->offsets) {
+ for (i = 0; i < etrack->offsets->len; i++) {
+ GstMXFDemuxIndex *idx =
+ &g_array_index (etrack->offsets, GstMXFDemuxIndex, i);
+
+ if (idx->initialized && idx->offset != 0 && idx->offset == offset) {
+ *retentry = *idx;
+ GST_DEBUG_OBJECT (demux,
+ "Found in track index. Position:%" G_GINT64_FORMAT, idx->dts);
+ return TRUE;
+ }
+ }
+ }
+
+ /* Actual index search */
+ if (!index_table || !index_table->segments->len) {
+ GST_WARNING_OBJECT (demux, "No index table or entries to search in");
+ return FALSE;
+ }
+
+ if (!partition) {
+ GST_WARNING_OBJECT (demux, "No current partition for search");
+ return FALSE;
+ }
+
+ /* Searching for a stream position from an absolute offset works in 3 steps:
+ *
+ * 1. Convert the absolute offset to a "stream offset" based on the partition
+ * information.
+ * 2. Find the segment for that "stream offset"
+ * 3. Match the entry within that segment
+ */
+
+ /* Convert to stream offset */
+ GST_LOG_OBJECT (demux,
+ "offset %" G_GUINT64_FORMAT " this_partition:%" G_GUINT64_FORMAT
+ " essence_container_offset:%" G_GINT64_FORMAT " partition body offset %"
+ G_GINT64_FORMAT, offset, partition->partition.this_partition,
+ partition->essence_container_offset, partition->partition.body_offset);
+ offset =
+ offset - partition->partition.this_partition -
+ partition->essence_container_offset + partition->partition.body_offset;
+
+ GST_LOG_OBJECT (demux, "stream offset %" G_GUINT64_FORMAT, offset);
+
+ /* Find the segment that covers the given stream offset (the highest one that
+ * covers that offset) */
+ for (i = index_table->segments->len - 1; i >= 0; i--) {
+ index_segment =
+ &g_array_index (index_table->segments, MXFIndexTableSegment, i);
+ GST_DEBUG_OBJECT (demux,
+ "Checking segment #%d (essence_offset %" G_GUINT64_FORMAT ")", i,
+ index_segment->segment_start_offset);
+ /* Not in the right segment yet */
+ if (offset >= index_segment->segment_start_offset) {
+ GST_LOG_OBJECT (demux, "Found");
+ break;
+ }
+ }
+ if (!index_segment) {
+ GST_WARNING_OBJECT (demux,
+ "Couldn't find index table segment for given offset");
+ return FALSE;
+ }
+
+ /* In the right segment, figure out:
+ * * the offset in the content package,
+ * * the position in edit units
+ * * the matching entry (if the table has entries)
+ */
+ if (index_segment->edit_unit_byte_count) {
+ cp_offset = offset % index_segment->edit_unit_byte_count;
+ position = offset / index_segment->edit_unit_byte_count;
+ /* Boundary check */
+ if ((position < index_segment->index_start_position)
+ || (index_segment->index_duration
+ && position >
+ (index_segment->index_start_position +
+ index_segment->index_duration))) {
+ GST_WARNING_OBJECT (demux,
+ "Invalid offset, exceeds table segment limits");
+ return FALSE;
+ }
+ retentry->size = index_segment->edit_unit_byte_count;
+ } else {
+ /* Find the content package entry containing this offset */
+ guint cpidx;
+ for (cpidx = 0; cpidx < index_segment->n_index_entries; cpidx++) {
+ index_entry = &index_segment->index_entries[cpidx];
+ GST_DEBUG_OBJECT (demux,
+ "entry #%u offset:%" G_GUINT64_FORMAT " stream_offset:%"
+ G_GUINT64_FORMAT, cpidx, offset, index_entry->stream_offset);
+ if (index_entry->stream_offset == offset) {
+ index_entry = &index_segment->index_entries[cpidx];
+ /* exactly on the entry */
+ cp_offset = offset - index_entry->stream_offset;
+ position = index_segment->index_start_position + cpidx;
+ break;
+ }
+ if (index_entry->stream_offset > offset && cpidx > 0) {
+ index_entry = &index_segment->index_entries[cpidx - 1];
+ /* One too far, result is in previous entry */
+ cp_offset = offset - index_entry->stream_offset;
+ position = index_segment->index_start_position + cpidx - 1;
+ break;
+ }
+ }
+ if (cpidx == index_segment->n_index_entries) {
+ GST_WARNING_OBJECT (demux,
+ "offset exceeds maximum number of entries in table segment");
+ return FALSE;
+ }
+ }
+
+ /* If the track comes from an interleaved essence container and doesn't have a
+ * delta_id set, figure it out now */
+ if (G_UNLIKELY (etrack->delta_id == MXF_INDEX_DELTA_ID_UNKNOWN)) {
+ guint delta;
+ GST_DEBUG_OBJECT (demux,
+ "Unknown delta_id for track. Attempting to resolve it");
+
+ if (index_segment->n_delta_entries == 0) {
+ /* No delta entries, nothing we can do about this */
+ GST_DEBUG_OBJECT (demux, "Index table has no delta entries, ignoring");
+ etrack->delta_id = MXF_INDEX_DELTA_ID_IGNORE;
+ } else if (!index_entry) {
+ for (delta = 0; delta < index_segment->n_delta_entries; delta++) {
+ /* No entry, therefore no slices */
+ GST_LOG_OBJECT (demux,
+ "delta #%d offset %" G_GUINT64_FORMAT " cp_offs:%" G_GUINT64_FORMAT
+ " element_delta:%u", delta, offset, cp_offset,
+ index_segment->delta_entries[delta].element_delta);
+ if (cp_offset == index_segment->delta_entries[delta].element_delta) {
+ GST_DEBUG_OBJECT (demux, "Matched to delta %d", delta);
+ etrack->delta_id = delta;
+ delta_entry = &index_segment->delta_entries[delta];
+ break;
+ }
+ }
+ } else {
+ for (delta = 0; delta < index_segment->n_delta_entries; delta++) {
+ guint64 delta_offs = 0;
+ /* If we are not in the first slice, take that offset into account */
+ if (index_segment->delta_entries[delta].slice)
+ delta_offs =
+ index_entry->slice_offset[index_segment->
+ delta_entries[delta].slice - 1];
+ /* Add the offset for this delta */
+ delta_offs += index_segment->delta_entries[delta].element_delta;
+ if (cp_offset == delta_offs) {
+ GST_DEBUG_OBJECT (demux, "Matched to delta %d", delta);
+ etrack->delta_id = delta;
+ delta_entry = &index_segment->delta_entries[delta];
+ break;
+ }
+ }
+
+ }
+ /* If we didn't managed to match, ignore it from now on */
+ if (etrack->delta_id == MXF_INDEX_DELTA_ID_UNKNOWN) {
+ GST_WARNING_OBJECT (demux,
+ "Couldn't match delta id, ignoring it from now on");
+ etrack->delta_id = MXF_INDEX_DELTA_ID_IGNORE;
+ }
+ } else if (index_segment->n_delta_entries > 0) {
+ delta_entry = &index_segment->delta_entries[etrack->delta_id];
+ }
+
+ if (index_entry && delta_entry && delta_entry->pos_table_index == -1) {
+ retentry->keyframe = (index_entry->flags & 0x80) == 0x80;
+ if (!demux->temporal_order_misuse)
+ retentry->pts =
+ position + g_array_index (index_table->reverse_temporal_offsets,
+ gint8, position);
+ else
+ retentry->pts = position + index_entry->temporal_offset;
+ GST_LOG_OBJECT (demux,
+ "Applied temporal offset. dts:%" G_GINT64_FORMAT " pts:%"
+ G_GINT64_FORMAT, position, retentry->pts);
+ } else
+ retentry->pts = position;
+
+ /* FIXME : check if position and cp_offs matches the table */
+ GST_LOG_OBJECT (demux, "Found in index table. position:%" G_GINT64_FORMAT,
+ position);
+ retentry->initialized = TRUE;
+ retentry->offset = original_offset;
+ retentry->dts = position;
+
+ return TRUE;
+}
+
static GstFlowReturn
gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
const MXFUL * key, GstBuffer * buffer, gboolean peek)
@@ -1725,10 +2373,10 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
GstBuffer *inbuf = NULL;
GstBuffer *outbuf = NULL;
GstMXFDemuxEssenceTrack *etrack = NULL;
- gboolean keyframe = TRUE;
/* As in GstMXFDemuxIndex */
- guint64 pts = G_MAXUINT64, dts = G_MAXUINT64;
+ guint64 pts = G_MAXUINT64;
gint32 max_temporal_offset = 0;
+ GstMXFDemuxIndex index_entry = { 0, };
GST_DEBUG_OBJECT (demux,
"Handling generic container essence element of size %" G_GSIZE_FORMAT
@@ -1760,6 +2408,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
return GST_FLOW_ERROR;
}
+ /* Identify and fetch the essence track */
track_number = GST_READ_UINT32_BE (&key->u[12]);
for (i = 0; i < demux->essence_tracks->len; i++) {
@@ -1779,37 +2428,56 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
return GST_FLOW_OK;
}
+ GST_DEBUG_OBJECT (demux,
+ "Handling generic container essence (track %d , number: 0x%08x)",
+ etrack->track_id, track_number);
+
+ /* Fetch the current entry.
+ *
+ * 1. If we don't have a current position, use find_entry_for_offset()
+ * 2. If we do have a position, use find_edit_entry()
+ *
+ * 3. If we are dealing with frame-wrapped content, pull the corresponding
+ * data from upstream (because it wasn't provided). If we didn't find an
+ * entry, error out because we can't deal with a frame-wrapped stream
+ * without index.
+ */
+
+ /* Update the track position (in case of resyncs) */
if (etrack->position == -1) {
GST_DEBUG_OBJECT (demux,
"Unknown essence track position, looking into index");
- if (etrack->offsets) {
- for (i = 0; i < etrack->offsets->len; i++) {
- GstMXFDemuxIndex *idx =
- &g_array_index (etrack->offsets, GstMXFDemuxIndex, i);
-
- if (idx->initialized && idx->offset != 0
- && idx->offset == demux->offset - demux->run_in) {
- etrack->position = i;
- break;
- }
- }
- }
-
- if (etrack->position == -1) {
+ if (!find_entry_for_offset (demux, etrack, demux->offset - demux->run_in,
+ &index_entry)) {
GST_WARNING_OBJECT (demux, "Essence track position not in index");
return GST_FLOW_OK;
}
- }
-
- if (etrack->offsets && etrack->offsets->len > etrack->position) {
- GstMXFDemuxIndex *index =
- &g_array_index (etrack->offsets, GstMXFDemuxIndex, etrack->position);
- if (index->initialized && index->offset != 0)
- keyframe = index->keyframe;
- if (index->initialized && index->pts != G_MAXUINT64)
- pts = index->pts;
- if (index->initialized && index->dts != G_MAXUINT64)
- dts = index->dts;
+ /* Update track position */
+ etrack->position = index_entry.dts;
+ } else if (etrack->delta_id == -1) {
+ GST_DEBUG_OBJECT (demux,
+ "Unknown essence track delta_id, looking into index");
+ if (!find_entry_for_offset (demux, etrack, demux->offset - demux->run_in,
+ &index_entry)) {
+ /* Non-fatal, fallback to legacy mode */
+ GST_WARNING_OBJECT (demux, "Essence track position not in index");
+ } else if (etrack->position != index_entry.dts) {
+ GST_ERROR_OBJECT (demux,
+ "track position doesn't match %" G_GINT64_FORMAT " entry dts %"
+ G_GINT64_FORMAT, etrack->position, index_entry.dts);
+ return GST_FLOW_ERROR;
+ }
+ } else {
+ if (!find_edit_entry (demux, etrack, etrack->position, FALSE, &index_entry,
+ FALSE)) {
+ /* FIXME : Error out if using non-frame-wrapping */
+ GST_DEBUG_OBJECT (demux, "Couldn't find entry");
+ } else if (index_entry.offset != demux->offset) {
+ GST_ERROR_OBJECT (demux,
+ "demux offset doesn't match %" G_GINT64_FORMAT " entry offset %"
+ G_GUINT64_FORMAT, demux->offset, index_entry.offset);
+ return GST_FLOW_ERROR;
+ }
}
/* Create subbuffer to be able to change metadata */
@@ -1817,7 +2485,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, 0,
gst_buffer_get_size (buffer));
- if (!keyframe)
+ if (index_entry.initialized && !index_entry.keyframe)
GST_BUFFER_FLAG_SET (inbuf, GST_BUFFER_FLAG_DELTA_UNIT);
if (etrack->handle_func) {
@@ -1841,84 +2509,27 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
return ret;
}
- if (outbuf)
- keyframe = !GST_BUFFER_FLAG_IS_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
-
- /* Prefer keyframe information from index tables over everything else */
- if (demux->index_tables) {
- GList *l;
- GstMXFDemuxIndexTable *index_table = NULL;
-
- for (l = demux->index_tables; l; l = l->next) {
- GstMXFDemuxIndexTable *tmp = l->data;
-
- if (tmp->body_sid == etrack->body_sid
- && tmp->index_sid == etrack->index_sid) {
- index_table = tmp;
- break;
- }
- }
-
- if (!index_table)
- GST_DEBUG_OBJECT (demux,
- "Couldn't find index table for body_sid:%d index_sid:%d",
- etrack->body_sid, etrack->index_sid);
- else {
- GST_DEBUG_OBJECT (demux,
- "Looking for position %" G_GINT64_FORMAT
- " in index table of size %d (max temporal offset %u)",
- etrack->position, index_table->offsets->len,
- index_table->max_temporal_offset);
- max_temporal_offset = index_table->max_temporal_offset;
- }
-
- if (index_table && index_table->offsets->len > etrack->position) {
- GstMXFDemuxIndex *index =
- &g_array_index (index_table->offsets, GstMXFDemuxIndex,
- etrack->position);
- if (index->initialized && index->offset != 0) {
- keyframe = index->keyframe;
-
- if (outbuf) {
- if (keyframe)
- GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
- else
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
- }
- }
-
- if (index->initialized && index->pts != G_MAXUINT64)
- pts = index->pts;
- if (index->initialized && index->dts != G_MAXUINT64)
- dts = index->dts;
- }
- }
-
- if (!etrack->offsets)
- etrack->offsets = g_array_new (FALSE, TRUE, sizeof (GstMXFDemuxIndex));
-
- {
- if (etrack->offsets->len > etrack->position) {
- GstMXFDemuxIndex *index =
- &g_array_index (etrack->offsets, GstMXFDemuxIndex, etrack->position);
+ if (!index_entry.initialized) {
+ /* This can happen when doing scanning without entry tables */
+ index_entry.duration = 1;
+ index_entry.offset = demux->offset - demux->run_in;
+ index_entry.dts = etrack->position;
+ index_entry.pts = etrack->intra_only ? etrack->position : G_MAXUINT64;
+ index_entry.keyframe =
+ !GST_BUFFER_FLAG_IS_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
+ index_entry.initialized = TRUE;
+ GST_DEBUG_OBJECT (demux,
+ "Storing newly discovered information on track %d. dts: %"
+ G_GINT64_FORMAT " offset:%" G_GUINT64_FORMAT " keyframe:%d",
+ etrack->track_id, index_entry.dts, index_entry.offset,
+ index_entry.keyframe);
- index->offset = demux->offset - demux->run_in;
- index->initialized = TRUE;
- index->pts = pts;
- index->dts = dts;
- index->keyframe = keyframe;
- } else if (etrack->position < G_MAXINT) {
- GstMXFDemuxIndex index;
+ if (!etrack->offsets)
+ etrack->offsets = g_array_new (FALSE, TRUE, sizeof (GstMXFDemuxIndex));
- index.offset = demux->offset - demux->run_in;
- index.initialized = TRUE;
- index.pts = pts;
- index.dts = dts;
- index.keyframe = keyframe;
- if (etrack->offsets->len < etrack->position)
- g_array_set_size (etrack->offsets, etrack->position + 1);
- g_array_insert_val (etrack->offsets, etrack->position, index);
- }
+ /* We only ever append to the track offset entry. */
+ g_assert (etrack->position <= etrack->offsets->len);
+ g_array_insert_val (etrack->offsets, etrack->position, index_entry);
}
if (peek)
@@ -1932,6 +2543,8 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
inbuf = outbuf;
outbuf = NULL;
+ max_temporal_offset = get_track_max_temporal_offset (demux, etrack);
+
for (i = 0; i < demux->src->len; i++) {
GstMXFDemuxPad *pad = g_ptr_array_index (demux->src, i);
@@ -1939,12 +2552,15 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
continue;
if (pad->eos) {
- GST_DEBUG_OBJECT (demux, "Pad is already EOS");
+ GST_DEBUG_OBJECT (pad, "Pad is already EOS");
continue;
}
if (etrack->position != pad->current_essence_track_position) {
- GST_DEBUG_OBJECT (demux, "Not at current component's position");
+ GST_DEBUG_OBJECT (pad,
+ "Not at current component's position (track:%" G_GINT64_FORMAT
+ " essence:%" G_GINT64_FORMAT ")", etrack->position,
+ pad->current_essence_track_position);
continue;
}
@@ -1953,7 +2569,10 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
if (earliest && earliest != pad && earliest->position < pad->position &&
pad->position - earliest->position > demux->max_drift) {
- GST_DEBUG_OBJECT (demux, "Pad is too far ahead of time");
+ GST_DEBUG_OBJECT (pad,
+ "Pad is too far ahead of time (%" GST_TIME_FORMAT " vs earliest:%"
+ GST_TIME_FORMAT ")", GST_TIME_ARGS (earliest->position),
+ GST_TIME_ARGS (pad->position));
continue;
}
}
@@ -1963,6 +2582,8 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
gst_buffer_copy_region (inbuf, GST_BUFFER_COPY_ALL, 0,
gst_buffer_get_size (inbuf));
+ pts = index_entry.pts;
+
GST_BUFFER_DTS (outbuf) = pad->position;
if (etrack->intra_only) {
GST_BUFFER_PTS (outbuf) = pad->position;
@@ -2072,7 +2693,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
pad->position += GST_BUFFER_DURATION (outbuf);
pad->current_material_track_position++;
- GST_DEBUG_OBJECT (demux,
+ GST_DEBUG_OBJECT (pad,
"Pushing buffer of size %" G_GSIZE_FORMAT " for track %u: pts %"
GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT
" position %" G_GUINT64_FORMAT, gst_buffer_get_size (outbuf),
@@ -2103,7 +2724,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
}
outbuf = NULL;
ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret);
- GST_LOG_OBJECT (demux, "combined return %s", gst_flow_get_name (ret));
+ GST_LOG_OBJECT (pad, "combined return %s", gst_flow_get_name (ret));
if (pad->position > demux->segment.position)
demux->segment.position = pad->position;
@@ -2140,7 +2761,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux,
if (ret == GST_FLOW_EOS) {
GstEvent *e;
- GST_DEBUG_OBJECT (demux, "EOS for track");
+ GST_DEBUG_OBJECT (pad, "EOS for track");
pad->eos = TRUE;
e = gst_event_new_eos ();
gst_event_set_seqnum (e, demux->seqnum);
@@ -2164,6 +2785,16 @@ out:
return ret;
}
+/*
+ * Called when analyzing the (RIP) Random Index Pack.
+ *
+ * FIXME : If a file doesn't have a RIP, we should iterate the partition headers
+ * to collect as much information as possible.
+ *
+ * This function collects as much information as possible from the partition headers:
+ * * Store partition information in the list of partitions
+ * * Handle any index table segment present
+ */
static void
read_partition_header (GstMXFDemux * demux)
{
@@ -2329,6 +2960,37 @@ gst_mxf_demux_handle_random_index_pack (GstMXFDemux * demux, const MXFUL * key,
return GST_FLOW_OK;
}
+static gint
+compare_index_table_segment (MXFIndexTableSegment * sa,
+ MXFIndexTableSegment * sb)
+{
+ if (mxf_uuid_is_equal (&sa->instance_id, &sb->instance_id))
+ return 0;
+ if (sa->body_sid != sb->body_sid)
+ return (sa->body_sid < sb->body_sid) ? -1 : 1;
+ if (sa->index_sid != sb->index_sid)
+ return (sa->index_sid < sb->index_sid) ? -1 : 1;
+ /* Finally sort by index start position */
+ if (sa->index_start_position < sb->index_start_position)
+ return -1;
+ return (sa->index_start_position != sb->index_start_position);
+}
+
+#if !GLIB_CHECK_VERSION(2, 62, 0)
+static gboolean
+has_table_segment (GArray * segments, MXFIndexTableSegment * target)
+{
+ guint i;
+ for (i = 0; i < segments->len; i++) {
+ MXFIndexTableSegment *cand =
+ &g_array_index (segments, MXFIndexTableSegment, i);
+ if (mxf_uuid_is_equal (&cand->instance_id, &target->instance_id))
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
static GstFlowReturn
gst_mxf_demux_handle_index_table_segment (GstMXFDemux * demux,
const MXFUL * key, GstBuffer * buffer, guint64 offset)
@@ -2336,6 +2998,7 @@ gst_mxf_demux_handle_index_table_segment (GstMXFDemux * demux,
MXFIndexTableSegment *segment;
GstMapInfo map;
gboolean ret;
+ GList *tmp;
GST_DEBUG_OBJECT (demux,
"Handling index table segment of size %" G_GSIZE_FORMAT " at offset %"
@@ -2353,12 +3016,39 @@ gst_mxf_demux_handle_index_table_segment (GstMXFDemux * demux,
return GST_FLOW_ERROR;
}
+ /* Drop it if we already saw it. Ideally we should be able to do this before
+ parsing (by checking instance UID) */
+ if (g_list_find_custom (demux->pending_index_table_segments, segment,
+ (GCompareFunc) compare_index_table_segment)) {
+ GST_DEBUG_OBJECT (demux, "Already in pending list");
+ g_free (segment);
+ return GST_FLOW_OK;
+ }
+ for (tmp = demux->index_tables; tmp; tmp = tmp->next) {
+ GstMXFDemuxIndexTable *table = (GstMXFDemuxIndexTable *) tmp->data;
+#if !GLIB_CHECK_VERSION (2, 62, 0)
+ if (has_table_segment (table->segments, segment)) {
+#else
+ if (g_array_binary_search (table->segments, segment,
+ (GCompareFunc) compare_index_table_segment, NULL)) {
+#endif
+ GST_DEBUG_OBJECT (demux, "Already handled");
+ g_free (segment);
+ return GST_FLOW_OK;
+ }
+ }
+
demux->pending_index_table_segments =
- g_list_prepend (demux->pending_index_table_segments, segment);
+ g_list_insert_sorted (demux->pending_index_table_segments, segment,
+ (GCompareFunc) compare_index_table_segment);
return GST_FLOW_OK;
}
+/*
+ * FIXME : Make outbuf optional in cases where we are only interested in getting
+ * the key and length.
+ */
static GstFlowReturn
gst_mxf_demux_pull_klv_packet (GstMXFDemux * demux, guint64 offset, MXFUL * key,
GstBuffer ** outbuf, guint * read)
@@ -2757,10 +3447,14 @@ gst_mxf_demux_handle_klv_packet (GstMXFDemux * demux, const MXFUL * key,
} else if (mxf_is_descriptive_metadata (key)) {
ret = gst_mxf_demux_handle_descriptive_metadata (demux, key, buffer);
} else if (mxf_is_generic_container_system_item (key)) {
+ if (demux->pending_index_table_segments)
+ collect_index_table_segments (demux);
ret =
gst_mxf_demux_handle_generic_container_system_item (demux, key, buffer);
} else if (mxf_is_generic_container_essence_element (key) ||
mxf_is_avid_essence_container_essence_element (key)) {
+ if (demux->pending_index_table_segments)
+ collect_index_table_segments (demux);
ret =
gst_mxf_demux_handle_generic_container_essence_element (demux, key,
buffer, peek);
@@ -2835,42 +3529,6 @@ gst_mxf_demux_set_partition_for_offset (GstMXFDemux * demux, guint64 offset)
}
static guint64
-find_offset (GArray * offsets, gint64 * position, gboolean keyframe)
-{
- GstMXFDemuxIndex *idx;
- guint64 current_offset = -1;
- gint64 current_position = *position;
-
- if (!offsets || offsets->len <= *position)
- return -1;
-
- idx = &g_array_index (offsets, GstMXFDemuxIndex, *position);
- if (idx->offset != 0 && (!keyframe || idx->keyframe)) {
- current_offset = idx->offset;
- } else if (idx->offset != 0) {
- current_position--;
- while (current_position >= 0) {
- idx = &g_array_index (offsets, GstMXFDemuxIndex, current_position);
- if (idx->offset == 0) {
- break;
- } else if (!idx->keyframe) {
- current_position--;
- continue;
- } else {
- current_offset = idx->offset;
- break;
- }
- }
- }
-
- if (current_offset == -1)
- return -1;
-
- *position = current_position;
- return current_offset;
-}
-
-static guint64
find_closest_offset (GArray * offsets, gint64 * position, gboolean keyframe)
{
GstMXFDemuxIndex *idx;
@@ -2906,176 +3564,158 @@ gst_mxf_demux_find_essence_element (GstMXFDemux * demux,
GstMXFDemuxPartition *old_partition = demux->current_partition;
gint i;
guint64 offset;
- gint64 requested_position = *position;
- GstMXFDemuxIndexTable *index_table = NULL;
+ gint64 requested_position = *position, index_start_position;
+ GstMXFDemuxIndex index_entry = { 0, };
GST_DEBUG_OBJECT (demux, "Trying to find essence element %" G_GINT64_FORMAT
- " of track %u with body_sid %u (keyframe %d)", *position,
+ " of track 0x%08x with body_sid %u (keyframe %d)", *position,
etrack->track_number, etrack->body_sid, keyframe);
- if (demux->index_tables) {
- GList *l;
-
- for (l = demux->index_tables; l; l = l->next) {
- GstMXFDemuxIndexTable *tmp = l->data;
-
- if (tmp->body_sid == etrack->body_sid
- && tmp->index_sid == etrack->index_sid) {
- index_table = tmp;
- break;
- }
- }
+ /* Get entry from index table if present */
+ if (find_edit_entry (demux, etrack, *position, keyframe, &index_entry)) {
+ GST_DEBUG_OBJECT (demux,
+ "Got position %" G_GINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
+ index_entry.dts, index_entry.offset);
+ *position = index_entry.dts;
+ return index_entry.offset;
}
-from_index:
+ GST_DEBUG_OBJECT (demux, "Not found in index table");
- if (etrack->duration > 0 && *position >= etrack->duration) {
- GST_WARNING_OBJECT (demux, "Position after end of essence track");
- return -1;
- }
-
- /* First try to find an offset in our index */
- offset = find_offset (etrack->offsets, position, keyframe);
- if (offset != -1) {
- GST_DEBUG_OBJECT (demux,
- "Found edit unit %" G_GINT64_FORMAT " for %" G_GINT64_FORMAT
- " in generated index at offset %" G_GUINT64_FORMAT, *position,
- requested_position, offset);
- return offset;
- }
+ /* Fallback to track offsets */
- GST_DEBUG_OBJECT (demux, "Not found in index");
if (!demux->random_access) {
+ /* Best effort for push mode */
offset = find_closest_offset (etrack->offsets, position, keyframe);
- if (offset != -1) {
+ if (offset != -1)
GST_DEBUG_OBJECT (demux,
"Starting with edit unit %" G_GINT64_FORMAT " for %" G_GINT64_FORMAT
" in generated index at offset %" G_GUINT64_FORMAT, *position,
requested_position, offset);
- return offset;
- }
+ return offset;
+ }
- if (index_table) {
- offset = find_closest_offset (index_table->offsets, position, keyframe);
- if (offset != -1) {
- GST_DEBUG_OBJECT (demux,
- "Starting with edit unit %" G_GINT64_FORMAT " for %" G_GINT64_FORMAT
- " in index at offset %" G_GUINT64_FORMAT, *position,
- requested_position, offset);
- return offset;
- }
- }
- } else if (demux->random_access) {
- gint64 index_start_position = *position;
+ if (etrack->duration > 0 && *position >= etrack->duration) {
+ GST_WARNING_OBJECT (demux, "Position after end of essence track");
+ return -1;
+ }
- demux->offset = demux->run_in;
+from_track_offset:
- offset =
- find_closest_offset (etrack->offsets, &index_start_position, FALSE);
- if (offset != -1) {
- demux->offset = offset + demux->run_in;
- GST_DEBUG_OBJECT (demux,
- "Starting with edit unit %" G_GINT64_FORMAT " for %" G_GINT64_FORMAT
- " in generated index at offset %" G_GUINT64_FORMAT,
- index_start_position, requested_position, offset);
- } else {
- index_start_position = -1;
- }
+ index_start_position = *position;
- if (index_table) {
- gint64 tmp_position = *position;
+ demux->offset = demux->run_in;
- offset = find_closest_offset (index_table->offsets, &tmp_position, TRUE);
- if (offset != -1 && tmp_position > index_start_position) {
- demux->offset = offset + demux->run_in;
- index_start_position = tmp_position;
- GST_DEBUG_OBJECT (demux,
- "Starting with edit unit %" G_GINT64_FORMAT " for %" G_GINT64_FORMAT
- " in index at offset %" G_GUINT64_FORMAT, index_start_position,
- requested_position, offset);
- }
- }
+ offset = find_closest_offset (etrack->offsets, &index_start_position, FALSE);
+ if (offset != -1) {
+ demux->offset = offset + demux->run_in;
+ GST_DEBUG_OBJECT (demux,
+ "Starting with edit unit %" G_GINT64_FORMAT " for %" G_GINT64_FORMAT
+ " in generated index at offset %" G_GUINT64_FORMAT,
+ index_start_position, requested_position, offset);
+ } else {
+ index_start_position = -1;
+ }
- gst_mxf_demux_set_partition_for_offset (demux, demux->offset);
+ gst_mxf_demux_set_partition_for_offset (demux, demux->offset);
- for (i = 0; i < demux->essence_tracks->len; i++) {
- GstMXFDemuxEssenceTrack *t =
- &g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack, i);
+ for (i = 0; i < demux->essence_tracks->len; i++) {
+ GstMXFDemuxEssenceTrack *t =
+ &g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack, i);
- if (index_start_position != -1 && t == etrack)
- t->position = index_start_position;
- else
- t->position = (demux->offset == demux->run_in) ? 0 : -1;
- }
+ if (index_start_position != -1 && t == etrack)
+ t->position = index_start_position;
+ else
+ t->position = (demux->offset == demux->run_in) ? 0 : -1;
+ GST_LOG_OBJECT (demux, "Setting track %d position to %" G_GINT64_FORMAT,
+ t->track_id, t->position);
+ }
- /* Else peek at all essence elements and complete our
- * index until we find the requested element
- */
- while (ret == GST_FLOW_OK) {
- GstBuffer *buffer = NULL;
- MXFUL key;
- guint read = 0;
+ /* Else peek at all essence elements and complete our
+ * index until we find the requested element
+ */
+ while (ret == GST_FLOW_OK) {
+ GstBuffer *buffer = NULL;
+ MXFUL key;
+ guint read = 0;
- ret =
- gst_mxf_demux_pull_klv_packet (demux, demux->offset, &key, &buffer,
- &read);
+ GST_LOG_OBJECT (demux, "Pulling from offset %" G_GINT64_FORMAT,
+ demux->offset);
+ ret =
+ gst_mxf_demux_pull_klv_packet (demux, demux->offset, &key, &buffer,
+ &read);
- if (ret == GST_FLOW_EOS) {
- for (i = 0; i < demux->essence_tracks->len; i++) {
- GstMXFDemuxEssenceTrack *t =
- &g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack,
- i);
+ if (ret == GST_FLOW_EOS) {
+ /* Handle EOS */
+ for (i = 0; i < demux->essence_tracks->len; i++) {
+ GstMXFDemuxEssenceTrack *t =
+ &g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack,
+ i);
- if (t->position > 0)
- t->duration = t->position;
- }
- /* For the searched track this is really our position */
- etrack->duration = etrack->position;
+ if (t->position > 0)
+ t->duration = t->position;
+ }
+ /* For the searched track this is really our position */
+ etrack->duration = etrack->position;
- for (i = 0; i < demux->src->len; i++) {
- GstMXFDemuxPad *p = g_ptr_array_index (demux->src, i);
+ for (i = 0; i < demux->src->len; i++) {
+ GstMXFDemuxPad *p = g_ptr_array_index (demux->src, i);
- if (!p->eos
- && p->current_essence_track_position >=
- p->current_essence_track->duration) {
- GstEvent *e;
+ if (!p->eos
+ && p->current_essence_track_position >=
+ p->current_essence_track->duration) {
+ GstEvent *e;
- p->eos = TRUE;
- e = gst_event_new_eos ();
- gst_event_set_seqnum (e, demux->seqnum);
- gst_pad_push_event (GST_PAD_CAST (p), e);
- }
+ p->eos = TRUE;
+ e = gst_event_new_eos ();
+ gst_event_set_seqnum (e, demux->seqnum);
+ gst_pad_push_event (GST_PAD_CAST (p), e);
}
}
+ }
- if (G_UNLIKELY (ret != GST_FLOW_OK) && etrack->position <= *position) {
- demux->offset = old_offset;
- demux->current_partition = old_partition;
- break;
- } else if (G_UNLIKELY (ret == GST_FLOW_OK)) {
- ret = gst_mxf_demux_handle_klv_packet (demux, &key, buffer, TRUE);
- gst_buffer_unref (buffer);
- }
+ GST_LOG_OBJECT (demux,
+ "pulling gave flow:%s track->position:%" G_GINT64_FORMAT,
+ gst_flow_get_name (ret), etrack->position);
+ if (G_UNLIKELY (ret != GST_FLOW_OK) && etrack->position <= *position) {
+ demux->offset = old_offset;
+ demux->current_partition = old_partition;
+ break;
+ } else if (G_UNLIKELY (ret == GST_FLOW_OK)) {
+ ret = gst_mxf_demux_handle_klv_packet (demux, &key, buffer, TRUE);
+ gst_buffer_unref (buffer);
+ }
- /* If we found the position read it from the index again */
- if (((ret == GST_FLOW_OK && etrack->position == *position + 2) ||
- (ret == GST_FLOW_EOS && etrack->position == *position + 1))
- && etrack->offsets && etrack->offsets->len > *position
- && g_array_index (etrack->offsets, GstMXFDemuxIndex,
- *position).offset != 0) {
- GST_DEBUG_OBJECT (demux, "Found at offset %" G_GUINT64_FORMAT,
- demux->offset);
- demux->offset = old_offset;
- demux->current_partition = old_partition;
- goto from_index;
+ GST_LOG_OBJECT (demux,
+ "Handling gave flow:%s track->position:%" G_GINT64_FORMAT
+ " looking for %" G_GINT64_FORMAT, gst_flow_get_name (ret),
+ etrack->position, *position);
+
+ /* If we found the position read it from the index again */
+ if (((ret == GST_FLOW_OK && etrack->position == *position + 1) ||
+ (ret == GST_FLOW_EOS && etrack->position == *position + 1))
+ && etrack->offsets && etrack->offsets->len > *position
+ && g_array_index (etrack->offsets, GstMXFDemuxIndex,
+ *position).offset != 0) {
+ GST_DEBUG_OBJECT (demux, "Found at offset %" G_GUINT64_FORMAT,
+ demux->offset);
+ demux->offset = old_offset;
+ demux->current_partition = old_partition;
+ if (find_edit_entry (demux, etrack, *position, keyframe, &index_entry)) {
+ GST_DEBUG_OBJECT (demux,
+ "Got position %" G_GINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
+ index_entry.dts, index_entry.offset);
+ *position = index_entry.dts;
+ return index_entry.offset;
}
- demux->offset += read;
+ goto from_track_offset;
}
- demux->offset = old_offset;
- demux->current_partition = old_partition;
-
- GST_DEBUG_OBJECT (demux, "Not found in this file");
+ demux->offset += read;
}
+ demux->offset = old_offset;
+ demux->current_partition = old_partition;
+
+ GST_DEBUG_OBJECT (demux, "Not found in this file");
return -1;
}
@@ -3758,30 +4398,46 @@ collect_index_table_segments (GstMXFDemux * demux)
guint64 old_offset = demux->offset;
GstMXFDemuxPartition *old_partition = demux->current_partition;
- if (!demux->random_index_pack)
- return;
+ /* This function can also be called when a RIP is not present. This can happen
+ * if index table segments were discovered while scanning the file */
+ if (demux->random_index_pack) {
+ for (i = 0; i < demux->random_index_pack->len; i++) {
+ MXFRandomIndexPackEntry *e =
+ &g_array_index (demux->random_index_pack, MXFRandomIndexPackEntry, i);
- for (i = 0; i < demux->random_index_pack->len; i++) {
- MXFRandomIndexPackEntry *e =
- &g_array_index (demux->random_index_pack, MXFRandomIndexPackEntry, i);
+ if (e->offset < demux->run_in) {
+ GST_ERROR_OBJECT (demux, "Invalid random index pack entry");
+ return;
+ }
- if (e->offset < demux->run_in) {
- GST_ERROR_OBJECT (demux, "Invalid random index pack entry");
- return;
+ demux->offset = e->offset;
+ read_partition_header (demux);
}
- demux->offset = e->offset;
- read_partition_header (demux);
+ demux->offset = old_offset;
+ demux->current_partition = old_partition;
}
- demux->offset = old_offset;
- demux->current_partition = old_partition;
+ if (demux->pending_index_table_segments == NULL) {
+ GST_DEBUG_OBJECT (demux, "No pending index table segments to collect");
+ return;
+ }
+
+ GST_LOG_OBJECT (demux, "Collecting pending index table segments");
for (l = demux->pending_index_table_segments; l; l = l->next) {
MXFIndexTableSegment *segment = l->data;
GstMXFDemuxIndexTable *t = NULL;
GList *k;
- guint64 start, end;
+ guint didx;
+#ifndef GST_DISABLE_GST_DEBUG
+ gchar str[48];
+#endif
+
+ GST_LOG_OBJECT (demux,
+ "Collecting from segment bodySID:%d indexSID:%d instance_id: %s",
+ segment->body_sid, segment->index_sid,
+ mxf_uuid_to_string (&segment->instance_id, str));
for (k = demux->index_tables; k; k = k->next) {
GstMXFDemuxIndexTable *tmp = k->data;
@@ -3798,122 +4454,91 @@ collect_index_table_segments (GstMXFDemux * demux)
t->body_sid = segment->body_sid;
t->index_sid = segment->index_sid;
t->max_temporal_offset = 0;
- t->offsets = g_array_new (FALSE, TRUE, sizeof (GstMXFDemuxIndex));
+ t->segments = g_array_new (FALSE, TRUE, sizeof (MXFIndexTableSegment));
+ g_array_set_clear_func (t->segments,
+ (GDestroyNotify) mxf_index_table_segment_reset);
+ t->reordered_delta_entry = -1;
+ t->reverse_temporal_offsets = g_array_new (FALSE, TRUE, 1);
demux->index_tables = g_list_prepend (demux->index_tables, t);
}
- start = segment->index_start_position;
- end = start + segment->index_duration;
- if (end > G_MAXINT / sizeof (GstMXFDemuxIndex)) {
- demux->index_tables = g_list_remove (demux->index_tables, t);
- g_array_free (t->offsets, TRUE);
- g_free (t);
- continue;
- }
+ /* Store index segment */
+ g_array_append_val (t->segments, *segment);
- if (t->offsets->len < end)
- g_array_set_size (t->offsets, end);
-
- for (i = 0; i < segment->n_index_entries && start + i < t->offsets->len;
- i++) {
- guint64 offset = segment->index_entries[i].stream_offset;
- GList *m;
- GstMXFDemuxPartition *offset_partition = NULL, *next_partition = NULL;
-
- for (m = demux->partitions; m; m = m->next) {
- GstMXFDemuxPartition *partition = m->data;
-
- if (!next_partition && offset_partition)
- next_partition = partition;
+ /* Check if temporal reordering tables should be pre-calculated */
+ for (didx = 0; didx < segment->n_delta_entries; didx++) {
+ MXFDeltaEntry *delta = &segment->delta_entries[didx];
+ if (delta->pos_table_index == -1) {
+ if (t->reordered_delta_entry != -1 && didx != t->reordered_delta_entry)
+ GST_WARNING_OBJECT (demux,
+ "Index Table specifies more than one stream using temporal reordering (%d and %d)",
+ didx, t->reordered_delta_entry);
+ else
+ t->reordered_delta_entry = didx;
+ } else if (delta->pos_table_index > 0)
+ GST_WARNING_OBJECT (delta,
+ "Index Table uses fractional offset, please file a bug");
+ }
- if (partition->partition.body_sid != t->body_sid)
- continue;
- if (partition->partition.body_offset > offset)
- break;
+ }
- offset_partition = partition;
- next_partition = NULL;
- }
+ /* Handle temporal offset if present and needed */
+ for (l = demux->index_tables; l; l = l->next) {
+ GstMXFDemuxIndexTable *table = l->data;
+ guint segidx;
- if (offset_partition && offset >= offset_partition->partition.body_offset) {
- offset =
- offset_partition->partition.this_partition +
- offset_partition->essence_container_offset + (offset -
- offset_partition->partition.body_offset);
+ /* No reordered entries, skip */
+ if (table->reordered_delta_entry == -1)
+ continue;
- if (next_partition
- && offset >= next_partition->partition.this_partition) {
+ GST_DEBUG_OBJECT (demux,
+ "bodySID:%d indexSID:%d Calculating reverse temporal offset table",
+ table->body_sid, table->index_sid);
+
+ for (segidx = 0; segidx < table->segments->len; segidx++) {
+ MXFIndexTableSegment *s =
+ &g_array_index (table->segments, MXFIndexTableSegment, segidx);
+ guint start = s->index_start_position;
+ guint stop =
+ s->index_duration ? start + s->index_duration : start +
+ s->n_index_entries;
+ guint entidx = 0;
+
+ if (stop > table->reverse_temporal_offsets->len)
+ g_array_set_size (table->reverse_temporal_offsets, stop);
+
+ for (entidx = 0; entidx < s->n_index_entries; entidx++) {
+ MXFIndexEntry *entry = &s->index_entries[entidx];
+ gint8 offs = -entry->temporal_offset;
+ /* Check we don't exceed boundaries */
+ if ((start + entidx + entry->temporal_offset) < 0 ||
+ (start + entidx + entry->temporal_offset) >
+ table->reverse_temporal_offsets->len) {
GST_ERROR_OBJECT (demux,
- "Invalid index table segment going into next unrelated partition");
+ "Temporal offset exceeds boundaries. entry:%d offset:%d max:%d",
+ start + entidx, entry->temporal_offset,
+ table->reverse_temporal_offsets->len);
} else {
- GstMXFDemuxIndex *index;
- gint8 temporal_offset = segment->index_entries[i].temporal_offset;
- guint64 pts_i = G_MAXUINT64;
-
- /* Store the highest reordering offset (to ensure we never output
- * buffer with DTS greater than PTS) */
- if (temporal_offset > (gint) t->max_temporal_offset) {
+ /* Applying the temporal offset gives us the entry that should contain this PTS.
+ * We store the reverse temporal offset on that entry, i.e. the value it should apply
+ * to go from DTS to PTS. (i.e. entry.pts = entry.dts + rto[idx]) */
+ g_array_index (table->reverse_temporal_offsets, gint8,
+ start + entidx + entry->temporal_offset) = offs;
+ if (entry->temporal_offset > (gint) table->max_temporal_offset) {
GST_LOG_OBJECT (demux,
- "bodySID:%d indexSID:%d Stored new max temporal offset %d (was %d)",
- t->body_sid, t->index_sid, temporal_offset,
- t->max_temporal_offset);
- t->max_temporal_offset = temporal_offset;
- }
-
- /* Fetch the matching entry in presentation order to store the pts (in
- * edit units). */
- if (temporal_offset >= 0 || (start + i >= -(gint) temporal_offset)) {
- pts_i = start + i + temporal_offset;
-
- if (t->offsets->len < pts_i)
- g_array_set_size (t->offsets, pts_i + 1);
-
- index = &g_array_index (t->offsets, GstMXFDemuxIndex, pts_i);
- if (!index->initialized) {
- index->initialized = TRUE;
- index->offset = 0;
- index->pts = G_MAXUINT64;
- index->dts = G_MAXUINT64;
- index->keyframe = FALSE;
- }
-
- index->pts = start + i;
- }
-
- /* Fetch the matching entry in encoded/bitstream order to store all
- * the other values related to the actual content (offset, flag, dts,
- * ..) */
-
- index = &g_array_index (t->offsets, GstMXFDemuxIndex, start + i);
- if (!index->initialized) {
- index->initialized = TRUE;
- index->offset = 0;
- index->pts = G_MAXUINT64;
- index->dts = G_MAXUINT64;
- index->keyframe = FALSE;
+ "Updating max temporal offset to %d (was %d)",
+ entry->temporal_offset, table->max_temporal_offset);
+ table->max_temporal_offset = entry->temporal_offset;
}
-
- index->offset = offset;
- /* EG41-2004 Table 9: 0x80 = Random access */
- /* random_access is more reliable to determine if the index is
- * a key-frame than checking the keyframe_offset or the frame type flag.
- * See https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2173#note_900580
- * for more details.
- */
- index->keyframe = ! !(segment->index_entries[i].flags & 0x80);
- index->dts = pts_i;
}
}
}
}
- for (l = demux->pending_index_table_segments; l; l = l->next) {
- MXFIndexTableSegment *s = l->data;
- mxf_index_table_segment_reset (s);
- g_free (s);
- }
- g_list_free (demux->pending_index_table_segments);
+ g_list_free_full (demux->pending_index_table_segments, g_free);
demux->pending_index_table_segments = NULL;
+
+ GST_DEBUG_OBJECT (demux, "Done collecting segments");
}
static gboolean
diff --git a/gst/mxf/mxfdemux.h b/gst/mxf/mxfdemux.h
index b9efd49c4..8ddd714f3 100644
--- a/gst/mxf/mxfdemux.h
+++ b/gst/mxf/mxfdemux.h
@@ -49,23 +49,41 @@ typedef struct _GstMXFDemuxClass GstMXFDemuxClass;
typedef struct _GstMXFDemuxPad GstMXFDemuxPad;
typedef struct _GstMXFDemuxPadClass GstMXFDemuxPadClass;
-typedef struct
+typedef struct _GstMXFDemuxPartition GstMXFDemuxPartition;
+typedef struct _GstMXFDemuxEssenceTrack GstMXFDemuxEssenceTrack;
+
+struct _GstMXFDemuxPartition
{
MXFPartitionPack partition;
MXFPrimerPack primer;
gboolean parsed_metadata;
+ /* Relative offset at which essence starts within this partition.*/
guint64 essence_container_offset;
-} GstMXFDemuxPartition;
-typedef struct
+};
+
+#define MXF_INDEX_DELTA_ID_UNKNOWN -1
+#define MXF_INDEX_DELTA_ID_IGNORE -2
+
+struct _GstMXFDemuxEssenceTrack
{
guint32 body_sid;
guint32 index_sid;
guint32 track_number;
+ /* delta id, the position of this track in the container package delta table
+ * (if the track is in an interleaved essence container)
+ *
+ * Special values:
+ * * -1 Not discovered yet
+ * * -2 Ignore delta entry (if index table is not present or not complete)
+ */
+ gint32 delta_id;
+
guint32 track_id;
MXFUMID source_package_uid;
+ /* Position and duration in edit units */
gint64 position;
gint64 duration;
@@ -82,11 +100,14 @@ typedef struct
GstCaps *caps;
gboolean intra_only;
-} GstMXFDemuxEssenceTrack;
+
+ MXFEssenceWrapping wrapping;
+
+};
typedef struct
{
- /* 0 if uninitialized */
+ /* absolute byte offset excluding run_in, 0 if uninitialized */
guint64 offset;
/* PTS edit unit number or G_MAXUINT64 */
@@ -95,8 +116,14 @@ typedef struct
/* DTS edit unit number if we got here via PTS */
guint64 dts;
+ /* Duration in edit units */
+ guint64 duration;
+
gboolean keyframe;
gboolean initialized;
+
+ /* Size, used for non-frame-wrapped content */
+ guint64 size;
} GstMXFDemuxIndex;
typedef struct
@@ -104,12 +131,23 @@ typedef struct
guint32 body_sid;
guint32 index_sid;
+ /* Array of MXFIndexTableSegment, sorted by DTS
+ * Note: Can be empty and can be sparse (i.e. not cover every edit unit) */
+ GArray *segments;
+
+ /* Delta entry to which reordering should be applied (-1 == no reordering) */
+ gint reordered_delta_entry;
+
+ /* Array of gint8 reverse temporal offsets.
+ * Contains the shift to apply to an entry DTS to get the PTS
+ *
+ * Can be NULL if the content doesn't have temporal shifts (i.e. all present
+ * entries have a temporal offset of 0) */
+ GArray *reverse_temporal_offsets;
+
/* Greatest temporal offset value contained within offsets.
* Unsigned because the smallest value is 0 (no reordering) */
guint max_temporal_offset;
-
- /* offsets indexed by DTS */
- GArray *offsets;
} GstMXFDemuxIndexTable;
struct _GstMXFDemuxPad
@@ -121,7 +159,7 @@ struct _GstMXFDemuxPad
GstClockTime position;
gdouble position_accumulated_error;
- /* Current position in the material track */
+ /* Current position in the material track (in edit units) */
gint64 current_material_track_position;
gboolean eos, discont;
@@ -143,6 +181,7 @@ struct _GstMXFDemuxPad
gint64 current_component_start;
gint64 current_component_duration;
+ /* Current essence track and position (in edit units) */
GstMXFDemuxEssenceTrack *current_essence_track;
gint64 current_essence_track_position;
};
@@ -212,6 +251,9 @@ struct _GstMXFDemux
/* Properties */
gchar *requested_package_string;
GstClockTime max_drift;
+
+ /* Quirks */
+ gboolean temporal_order_misuse;
};
struct _GstMXFDemuxClass
diff --git a/gst/mxf/mxftypes.c b/gst/mxf/mxftypes.c
index 36a7401ae..f5c2852e3 100644
--- a/gst/mxf/mxftypes.c
+++ b/gst/mxf/mxftypes.c
@@ -1285,6 +1285,21 @@ mxf_index_table_segment_parse (const MXFUL * ul,
break;
}
}
+
+ /* If edit unit byte count is 0 there *must* be entries */
+ if (segment->edit_unit_byte_count == 0 && segment->n_index_entries == 0) {
+ GST_WARNING
+ ("Invalid IndexTableSegment, No entries and no specified edit unit byte count");
+ goto error;
+ }
+
+ /* Compute initial essence offset */
+ if (segment->edit_unit_byte_count)
+ segment->segment_start_offset =
+ segment->index_start_position * segment->edit_unit_byte_count;
+ else
+ segment->segment_start_offset = segment->index_entries[0].stream_offset;
+
return TRUE;
error:
diff --git a/gst/mxf/mxftypes.h b/gst/mxf/mxftypes.h
index c96590e44..c6f5c15f2 100644
--- a/gst/mxf/mxftypes.h
+++ b/gst/mxf/mxftypes.h
@@ -169,6 +169,11 @@ typedef struct {
guint32 n_index_entries;
MXFIndexEntry *index_entries;
+
+ /* Computed fields (i.e. not present in file) */
+
+ /* Initial essence offset covered by this segment */
+ guint64 segment_start_offset;
} MXFIndexTableSegment;
#define GST_TAG_MXF_UMID "mxf-umid"