diff options
Diffstat (limited to 'gst/mxf/mxfdemux.c')
-rw-r--r-- | gst/mxf/mxfdemux.c | 1411 |
1 files changed, 1018 insertions, 393 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 |