From 726ea62d25a55d0e964157358fcfefa8d84a94a6 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Thu, 15 Jul 2021 10:59:39 +0200 Subject: [PATCH] mxfdemux: Handle non-frame wrapping * If we have an index table for non-framed essence, we can handle it * The demuxer has a state which indicates whether it will next fetch a KLV or data contained *within* a KLV. * The position on Essence Tracks always correspond to the next entry to fetch, demuxer offset will be skipped accordingly whenever we switch between partitions (in case of resyncs). A copy of the main clip/custom KLV for that partition is kept to track the position within the essence of that partition. * For clip/custom-wrapped raw audio, if the edit rate is too small (and would cause plenty of tiny buffers to be outputted), specify a minimum number of edit units per buffer. Part-of: --- gst/mxf/mxfdemux.c | 420 +++++++++++++++++++++++++++++++++------------ gst/mxf/mxfdemux.h | 26 ++- 2 files changed, 333 insertions(+), 113 deletions(-) diff --git a/gst/mxf/mxfdemux.c b/gst/mxf/mxfdemux.c index 0d0b9f102b..3bf56320e7 100644 --- a/gst/mxf/mxfdemux.c +++ b/gst/mxf/mxfdemux.c @@ -34,7 +34,6 @@ * - Handle timecode tracks correctly (where is this documented?) * - Handle drop-frame field of timecode tracks * - Handle Generic container system items - * - Implement correct support for clip-wrapped essence elements. * - Post structural metadata and descriptive metadata trees as a message on the bus * and send them downstream as event. * - Multichannel audio needs channel layouts, define them (SMPTE S320M?). @@ -89,6 +88,9 @@ static GstFlowReturn gst_mxf_demux_handle_index_table_segment (GstMXFDemux * demux, GstMXFKLV * klv); static void collect_index_table_segments (GstMXFDemux * demux); +static gboolean find_entry_for_offset (GstMXFDemux * demux, + GstMXFDemuxEssenceTrack * etrack, guint64 offset, + GstMXFDemuxIndex * retentry); GType gst_mxf_demux_pad_get_type (void); G_DEFINE_TYPE (GstMXFDemuxPad, gst_mxf_demux_pad, GST_TYPE_PAD); @@ -256,6 +258,8 @@ gst_mxf_demux_reset (GstMXFDemux * demux) demux->flushing = FALSE; + demux->state = GST_MXF_DEMUX_STATE_UNKNOWN; + demux->footer_partition_pack_offset = 0; demux->offset = 0; @@ -397,6 +401,66 @@ gst_mxf_demux_partition_compare (GstMXFDemuxPartition * a, return 0; } +/* Final checks and variable calculation for tracks and partition. This function + * can be called repeatedly without any side-effect. + */ +static void +gst_mxf_demux_partition_postcheck (GstMXFDemux * demux, + GstMXFDemuxPartition * partition) +{ + guint i; + GstMXFDemuxPartition *old_partition = demux->current_partition; + + /* If we already handled this partition or it doesn't contain any essence, skip */ + if (partition->single_track || !partition->partition.body_sid) + return; + + for (i = 0; i < demux->essence_tracks->len; i++) { + GstMXFDemuxEssenceTrack *cand = + &g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack, i); + + if (cand->body_sid != partition->partition.body_sid) + continue; + + if (!cand->source_package->is_interleaved) { + GST_DEBUG_OBJECT (demux, + "Assigning single track %d (0x%08x) to partition at offset %" + G_GUINT64_FORMAT, cand->track_id, cand->track_number, + partition->partition.this_partition); + + partition->single_track = cand; + + if (partition->essence_container_offset != 0 + && cand->wrapping != MXF_ESSENCE_WRAPPING_FRAME_WRAPPING) { + GstMXFKLV essence_klv; + GstMXFDemuxIndex entry; + /* Update the essence container offset for the fact that the index + * stream offset is relative to the essence position and not to the + * KLV position */ + if (gst_mxf_demux_peek_klv_packet (demux, + partition->partition.this_partition + + partition->essence_container_offset, + &essence_klv) == GST_FLOW_OK) { + partition->essence_container_offset += essence_klv.data_offset; + /* And keep a copy of the clip/custom klv for this partition */ + partition->clip_klv = essence_klv; + GST_DEBUG_OBJECT (demux, + "Non-frame wrapping, updated essence_container_offset to %" + G_GUINT64_FORMAT, partition->essence_container_offset); + + /* And match it against index table, this will also update the track delta_id (if needed) */ + demux->current_partition = partition; + find_entry_for_offset (demux, cand, + essence_klv.offset + essence_klv.data_offset, &entry); + demux->current_partition = old_partition; + } + } + + break; + } + } +} + static GstFlowReturn gst_mxf_demux_handle_partition_pack (GstMXFDemux * demux, GstMXFKLV * klv) { @@ -465,6 +529,8 @@ gst_mxf_demux_handle_partition_pack (GstMXFDemux * demux, GstMXFKLV * klv) (GCompareFunc) gst_mxf_demux_partition_compare); } + gst_mxf_demux_partition_postcheck (demux, p); + for (l = demux->partitions; l; l = l->next) { GstMXFDemuxPartition *a, *b; @@ -746,12 +812,22 @@ gst_mxf_demux_update_essence_tracks (GstMXFDemux * demux) gboolean new = FALSE; if (!package->parent.tracks[j] - || !MXF_IS_METADATA_TIMELINE_TRACK (package->parent.tracks[j])) + || !MXF_IS_METADATA_TIMELINE_TRACK (package->parent.tracks[j])) { + GST_DEBUG_OBJECT (demux, + "Skipping non-timeline track (id:%d number:0x%08x)", + package->parent.tracks[j]->track_id, + package->parent.tracks[j]->track_number); continue; + } track = MXF_METADATA_TIMELINE_TRACK (package->parent.tracks[j]); - if ((track->parent.type & 0xf0) != 0x30) + if ((track->parent.type & 0xf0) != 0x30) { + GST_DEBUG_OBJECT (demux, + "Skipping track of type 0x%02x (id:%d number:0x%08x)", + track->parent.type, track->parent.track_id, + track->parent.track_number); continue; + } if (track->edit_rate.n <= 0 || track->edit_rate.d <= 0) { GST_WARNING_OBJECT (demux, "Invalid edit rate"); @@ -931,18 +1007,18 @@ gst_mxf_demux_update_essence_tracks (GstMXFDemux * demux) caps = NULL; } - if (etrack->handler != NULL) { - MXFEssenceWrapping track_wrapping; - - track_wrapping = etrack->handler->get_track_wrapping (track); - if (track_wrapping == MXF_ESSENCE_WRAPPING_CLIP_WRAPPING) { - GST_ELEMENT_ERROR (demux, STREAM, NOT_IMPLEMENTED, (NULL), - ("Clip essence wrapping is not implemented yet.")); - return GST_FLOW_ERROR; - } else if (track_wrapping == MXF_ESSENCE_WRAPPING_CUSTOM_WRAPPING) { - GST_ELEMENT_ERROR (demux, STREAM, NOT_IMPLEMENTED, (NULL), - ("Custom essence wrappings are not supported.")); - return GST_FLOW_ERROR; + etrack->min_edit_units = 1; + /* Ensure we don't output one buffer per sample for audio */ + if (gst_util_uint64_scale (GST_SECOND, track->edit_rate.d, + track->edit_rate.n) < 10 * GST_MSECOND) { + GstStructure *s = gst_caps_get_structure (etrack->caps, 0); + const gchar *name = gst_structure_get_name (s); + if (g_str_has_prefix (name, "audio/x-raw")) { + etrack->min_edit_units = + gst_util_uint64_scale (25 * GST_MSECOND, track->edit_rate.n, + track->edit_rate.d * GST_SECOND); + GST_DEBUG_OBJECT (demux, "Seting miminum number of edit units to %u", + etrack->min_edit_units); } } @@ -1199,7 +1275,7 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux) } if (!MXF_IS_METADATA_TIMELINE_TRACK (current_package->tracks[i])) { - GST_DEBUG_OBJECT (demux, "No timeline track"); + GST_DEBUG_OBJECT (demux, "Skipping Non-timeline track"); continue; } @@ -1334,7 +1410,10 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux) } if (track->parent.type && (track->parent.type & 0xf0) != 0x30) { - GST_DEBUG_OBJECT (demux, "No essence track"); + GST_DEBUG_OBJECT (demux, + "No essence track. type:0x%02x track_id:%d track_number:0x%08x", + track->parent.type, track->parent.track_id, + track->parent.track_number); if (!pad) { continue; } else { @@ -1572,6 +1651,11 @@ gst_mxf_demux_update_tracks (GstMXFDemux * demux) if (first_run) gst_element_no_more_pads (GST_ELEMENT_CAST (demux)); + /* Re-check all existing partitions for source package linking in case the + * header partition contains data (allowed in early MXF versions) */ + for (l = demux->partitions; l; l = l->next) + gst_mxf_demux_partition_postcheck (demux, (GstMXFDemuxPartition *) l->data); + return GST_FLOW_OK; error: @@ -1955,6 +2039,11 @@ get_partition_for_stream_offset (GstMXFDemux * demux, && stream_offset < offset_partition->partition.body_offset) return NULL; + GST_DEBUG_OBJECT (demux, + "Found this_partition:%" G_GUINT64_FORMAT " body_offset:%" + G_GUINT64_FORMAT, offset_partition->partition.this_partition, + offset_partition->partition.body_offset); + /* Are we overriding into the next partition ? */ if (next_partition) { guint64 partition_essence_size = @@ -1963,6 +2052,11 @@ get_partition_for_stream_offset (GstMXFDemux * demux, offset_partition->essence_container_offset; guint64 in_partition = stream_offset - offset_partition->partition.body_offset; + GST_DEBUG_OBJECT (demux, + "Followed by this_partition:%" G_GUINT64_FORMAT " body_offset:%" + G_GUINT64_FORMAT, next_partition->partition.this_partition, + next_partition->partition.body_offset); + if (in_partition >= partition_essence_size) { GST_WARNING_OBJECT (demux, "stream_offset %" G_GUINT64_FORMAT @@ -2218,6 +2312,14 @@ search_in_segment: etrack->delta_id, delta_entry->pos_table_index, delta_entry->slice, delta_entry->element_delta); stream_offset += delta_entry->element_delta; + } else if (etrack->min_edit_units != 1) { + GST_LOG_OBJECT (demux, "Handling minimum edit unit %u", + etrack->min_edit_units); + entry->duration = + MIN (etrack->min_edit_units, + (segment->index_start_position + segment->index_duration) - position); + entry->size = segment->edit_unit_byte_count * entry->duration; + } else { entry->size = segment->edit_unit_byte_count; } } else if (segment->n_index_entries) { @@ -2285,6 +2387,9 @@ search_in_segment: "Couldn't find matching partition for stream offset %" G_GUINT64_FORMAT, stream_offset); return FALSE; + } else { + GST_DEBUG_OBJECT (demux, "Entry is in partition %" G_GUINT64_FORMAT, + offset_partition->partition.this_partition); } /* Convert stream offset to absolute offset using matching partition */ @@ -2424,7 +2529,14 @@ find_entry_for_offset (GstMXFDemux * demux, GstMXFDemuxEssenceTrack * etrack, "Invalid offset, exceeds table segment limits"); return FALSE; } + if (etrack->min_edit_units != 1) { + retentry->duration = MIN (etrack->min_edit_units, + (index_segment->index_start_position + + index_segment->index_duration) - position); + retentry->size = index_segment->edit_unit_byte_count * retentry->duration; + } else { retentry->size = index_segment->edit_unit_byte_count; + } } else { /* Find the content package entry containing this offset */ guint cpidx; @@ -2559,10 +2671,22 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, GST_DEBUG_OBJECT (demux, " essence element type = 0x%02x", klv->key.u[14]); GST_DEBUG_OBJECT (demux, " essence element number = 0x%02x", klv->key.u[15]); - if (demux->current_partition->essence_container_offset == 0) + if (demux->current_partition->essence_container_offset == 0) { demux->current_partition->essence_container_offset = demux->offset - demux->current_partition->partition.this_partition - demux->run_in; + if (demux->current_partition->single_track + && demux->current_partition->single_track->wrapping != + MXF_ESSENCE_WRAPPING_FRAME_WRAPPING) { + demux->current_partition->essence_container_offset += klv->data_offset; + demux->current_partition->clip_klv = *klv; + /* "consume" the initial bytes of the KLV */ + klv->consumed = klv->data_offset; + GST_DEBUG_OBJECT (demux, + "Non-frame wrapping, updated essence_container_offset to %" + G_GUINT64_FORMAT, demux->current_partition->essence_container_offset); + } + } if (!demux->current_package) { GST_ERROR_OBJECT (demux, "No package selected yet"); @@ -2582,26 +2706,31 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, /* Identify and fetch the essence track */ track_number = GST_READ_UINT32_BE (&klv->key.u[12]); - for (i = 0; i < demux->essence_tracks->len; i++) { - GstMXFDemuxEssenceTrack *tmp = - &g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack, i); + etrack = demux->current_partition->single_track; + if (!etrack) { + for (i = 0; i < demux->essence_tracks->len; i++) { + GstMXFDemuxEssenceTrack *tmp = + &g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack, i); - if (tmp->body_sid == demux->current_partition->partition.body_sid && - (tmp->track_number == track_number || tmp->track_number == 0)) { - etrack = tmp; - break; + if (tmp->body_sid == demux->current_partition->partition.body_sid && + (tmp->track_number == track_number || tmp->track_number == 0)) { + etrack = tmp; + break; + } + } + + if (!etrack) { + GST_WARNING_OBJECT (demux, + "No essence track for this essence element found"); + return GST_FLOW_OK; } } - if (!etrack) { - GST_WARNING_OBJECT (demux, - "No essence track for this essence element found"); - return GST_FLOW_OK; - } - GST_DEBUG_OBJECT (demux, - "Handling generic container essence (track %d , number: 0x%08x)", - etrack->track_id, track_number); + "Handling generic container essence (track %d , position:%" + G_GINT64_FORMAT ", number: 0x%08x , frame-wrapped:%d)", etrack->track_id, + etrack->position, track_number, + etrack->wrapping == MXF_ESSENCE_WRAPPING_FRAME_WRAPPING); /* Fetch the current entry. * @@ -2641,18 +2770,67 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, 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 */ + if (!find_edit_entry (demux, etrack, etrack->position, FALSE, &index_entry)) { GST_DEBUG_OBJECT (demux, "Couldn't find entry"); - } else if (index_entry.offset != demux->offset) { + } else if (etrack->wrapping == MXF_ESSENCE_WRAPPING_FRAME_WRAPPING) { + if (etrack->delta_id != MXF_INDEX_DELTA_ID_IGNORE + && index_entry.offset != offset) { + GST_ERROR_OBJECT (demux, + "demux offset doesn't match %" G_GINT64_FORMAT " entry offset %" + G_GUINT64_FORMAT, offset, index_entry.offset); + return GST_FLOW_ERROR; + } + } else if (index_entry.offset != klv->offset + klv->consumed && + index_entry.offset != klv->offset + klv->data_offset) { GST_ERROR_OBJECT (demux, - "demux offset doesn't match %" G_GINT64_FORMAT " entry offset %" - G_GUINT64_FORMAT, demux->offset, index_entry.offset); + "KLV offset doesn't match %" G_GINT64_FORMAT " entry offset %" + G_GUINT64_FORMAT, klv->offset + klv->consumed, index_entry.offset); return GST_FLOW_ERROR; } } + if (etrack->wrapping != MXF_ESSENCE_WRAPPING_FRAME_WRAPPING) { + /* We need entry information to deal with non-frame-wrapped content */ + if (!index_entry.initialized) { + GST_ELEMENT_ERROR (demux, STREAM, WRONG_TYPE, (NULL), + ("Essence with non-frame-wrapping require an index table to be present")); + return GST_FLOW_ERROR; + } + /* We cannot deal with non-frame-wrapping in push mode for now */ + if (!demux->random_access) { + GST_ELEMENT_ERROR (demux, STREAM, WRONG_TYPE, (NULL), + ("Non-frame-wrapping is not support in push mode")); + return GST_FLOW_ERROR; + } + } + + /* FIXME : If we're peeking and don't need to actually parse the data, we + * should avoid pulling the content from upstream */ + if (etrack->wrapping != MXF_ESSENCE_WRAPPING_FRAME_WRAPPING) { + g_assert (index_entry.size); + GST_DEBUG_OBJECT (demux, "Should only grab %" G_GUINT64_FORMAT " bytes", + index_entry.size); + ret = + gst_mxf_demux_pull_range (demux, index_entry.offset, index_entry.size, + &inbuf); + if (ret != GST_FLOW_OK) + return ret; + if (klv->consumed == 0) + klv->consumed = klv->data_offset + index_entry.size; + else + klv->consumed += index_entry.size; + if (klv != &demux->current_partition->clip_klv) + demux->current_partition->clip_klv = *klv; + GST_LOG_OBJECT (demux, + "klv data_offset:%" G_GUINT64_FORMAT " length:%" G_GSIZE_FORMAT + " consumed:%" G_GUINT64_FORMAT, klv->data_offset, klv->length, + klv->consumed); + /* Switch back to KLV mode if we're done with this one */ + if (klv->length + klv->data_offset == klv->consumed) + demux->state = GST_MXF_DEMUX_STATE_KLV; + else + demux->state = GST_MXF_DEMUX_STATE_ESSENCE; + } else { ret = gst_mxf_demux_fill_klv (demux, klv); if (ret != GST_FLOW_OK) @@ -2740,7 +2918,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, continue; } - if (etrack->position != pad->current_essence_track_position) { + if (etrack->position < pad->current_essence_track_position) { GST_DEBUG_OBJECT (pad, "Not at current component's position (track:%" G_GINT64_FORMAT " essence:%" G_GINT64_FORMAT ")", etrack->position, @@ -2753,7 +2931,7 @@ 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 (pad, + GST_DEBUG_OBJECT (earliest, "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)); @@ -2793,6 +2971,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale (GST_SECOND, + index_entry.duration * pad->current_essence_track->source_track->edit_rate.d, pad->current_essence_track->source_track->edit_rate.n); GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE; @@ -2864,6 +3043,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, e = gst_event_new_segment (&shift_segment); } else e = gst_event_new_segment (&demux->segment); + GST_DEBUG_OBJECT (pad, "Sending segment %" GST_PTR_FORMAT, e); gst_event_set_seqnum (e, demux->seqnum); gst_pad_push_event (GST_PAD_CAST (pad), e); pad->need_segment = FALSE; @@ -2875,17 +3055,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 (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), - pad->material_track->parent.track_id, - GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)), - GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), - pad->current_essence_track_position); + pad->current_material_track_position += index_entry.duration; if (pad->discont) { GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); @@ -2904,6 +3074,16 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, "Replacing empty gap buffer with gap event %" GST_PTR_FORMAT, gap); gst_pad_push_event (GST_PAD_CAST (pad), gap); } else { + 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), + pad->material_track->parent.track_id, + GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)), + GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), + pad->current_essence_track_position); + ret = gst_pad_push (GST_PAD_CAST (pad), outbuf); } outbuf = NULL; @@ -2916,7 +3096,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, if (ret != GST_FLOW_OK) goto out; - pad->current_essence_track_position++; + pad->current_essence_track_position += index_entry.duration; if (pad->current_component) { if (pad->current_component_duration > 0 && @@ -2929,6 +3109,9 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, pad->current_component_index + 1); if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS) { GST_ERROR_OBJECT (demux, "Switching component failed"); + } else { + pad->current_essence_track->position = + pad->current_essence_track_position; } } else if (etrack->duration > 0 && pad->current_essence_track_position >= etrack->duration) { @@ -2964,7 +3147,7 @@ out: if (outbuf) gst_buffer_unref (outbuf); - etrack->position++; + etrack->position += index_entry.duration; return ret; } @@ -3581,25 +3764,6 @@ gst_mxf_demux_handle_klv_packet (GstMXFDemux * demux, GstMXFKLV * klv, G_GUINT64_FORMAT ", key: %s", klv->length, demux->offset, mxf_ul_to_string (key, key_str)); } else if (mxf_is_partition_pack (key)) { - - /* If this partition contains the start of an essence container - * set the positions of all essence streams to 0 - */ - if (ret == GST_FLOW_OK && demux->current_partition - && demux->current_partition->partition.body_sid != 0 - && demux->current_partition->partition.body_offset == 0) { - guint i; - - for (i = 0; i < demux->essence_tracks->len; i++) { - GstMXFDemuxEssenceTrack *etrack = - &g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack, i); - - if (etrack->body_sid != demux->current_partition->partition.body_sid) - continue; - - etrack->position = 0; - } - } ret = gst_mxf_demux_handle_partition_pack (demux, klv); } else if (mxf_is_primer_pack (key)) { ret = gst_mxf_demux_handle_primer_pack (demux, klv); @@ -3639,34 +3803,6 @@ gst_mxf_demux_handle_klv_packet (GstMXFDemux * demux, GstMXFKLV * klv, demux->offset, mxf_ul_to_string (key, key_str)); } - /* In pull mode try to get the last metadata */ - if (mxf_is_partition_pack (key) && ret == GST_FLOW_OK - && demux->pull_footer_metadata - && demux->random_access && demux->current_partition - && demux->current_partition->partition.type == MXF_PARTITION_PACK_HEADER - && (!demux->current_partition->partition.closed - || !demux->current_partition->partition.complete) - && (demux->footer_partition_pack_offset != 0 || demux->random_index_pack)) { - GST_DEBUG_OBJECT (demux, - "Open or incomplete header partition, trying to get final metadata from the last partitions"); - gst_mxf_demux_parse_footer_metadata (demux); - demux->pull_footer_metadata = FALSE; - - if (demux->current_partition->partition.body_sid != 0 && - demux->current_partition->partition.body_offset == 0) { - guint i; - for (i = 0; i < demux->essence_tracks->len; i++) { - GstMXFDemuxEssenceTrack *etrack = - &g_array_index (demux->essence_tracks, GstMXFDemuxEssenceTrack, i); - - if (etrack->body_sid != demux->current_partition->partition.body_sid) - continue; - - etrack->position = 0; - } - } - } - beach: return ret; } @@ -3676,6 +3812,8 @@ gst_mxf_demux_set_partition_for_offset (GstMXFDemux * demux, guint64 offset) { GList *l; + GST_LOG_OBJECT (demux, "offset %" G_GUINT64_FORMAT, offset); + /* This partition will already be parsed, otherwise * the position wouldn't be in the index */ for (l = demux->partitions; l; l = l->next) { @@ -3684,6 +3822,15 @@ gst_mxf_demux_set_partition_for_offset (GstMXFDemux * demux, guint64 offset) if (p->partition.this_partition + demux->run_in <= offset) demux->current_partition = p; } + if (demux->current_partition) + GST_DEBUG_OBJECT (demux, + "Current partition now %p (body_sid:%d index_sid:%d this_partition:%" + G_GUINT64_FORMAT ")", demux->current_partition, + demux->current_partition->partition.body_sid, + demux->current_partition->partition.index_sid, + demux->current_partition->partition.this_partition); + else + GST_DEBUG_OBJECT (demux, "Haven't found partition for offset yet"); } static guint64 @@ -3887,6 +4034,16 @@ gst_mxf_demux_pull_and_handle_klv_packet (GstMXFDemux * demux) } } + if (demux->state == GST_MXF_DEMUX_STATE_ESSENCE) { + g_assert (demux->current_partition->single_track + && demux->current_partition->single_track->wrapping != + MXF_ESSENCE_WRAPPING_FRAME_WRAPPING); + /* Feeding essence directly (i.e. in the middle of a custom/clip KLV) */ + ret = + gst_mxf_demux_handle_generic_container_essence_element (demux, + &demux->current_partition->clip_klv, FALSE); + gst_mxf_demux_consume_klv (demux, &demux->current_partition->clip_klv); + } else { ret = gst_mxf_demux_peek_klv_packet (demux, demux->offset, &klv); @@ -3945,6 +4102,14 @@ gst_mxf_demux_pull_and_handle_klv_packet (GstMXFDemux * demux) demux->offset = offset + demux->run_in; gst_mxf_demux_set_partition_for_offset (demux, demux->offset); + if (p->current_essence_track->wrapping != + MXF_ESSENCE_WRAPPING_FRAME_WRAPPING) { + demux->state = GST_MXF_DEMUX_STATE_ESSENCE; + demux->current_partition->clip_klv.consumed = + offset - demux->current_partition->clip_klv.offset; + } else + demux->state = GST_MXF_DEMUX_STATE_KLV; + p->current_essence_track->position = position; ret = GST_FLOW_OK; goto beach; @@ -4102,8 +4267,17 @@ gst_mxf_demux_pull_and_handle_klv_packet (GstMXFDemux * demux) demux->offset, position, earliest->current_essence_track->track_id, earliest->current_essence_track->body_sid, earliest->current_essence_track->index_sid); + if (demux->current_partition->single_track + && demux->current_partition->single_track->wrapping != + MXF_ESSENCE_WRAPPING_FRAME_WRAPPING) { + demux->state = GST_MXF_DEMUX_STATE_ESSENCE; + demux->current_partition->clip_klv.consumed = + offset - demux->current_partition->clip_klv.offset; + } else + demux->state = GST_MXF_DEMUX_STATE_KLV; earliest->current_essence_track->position = position; + GST_DEBUG_OBJECT (earliest, "Switching to this pad"); break; } } @@ -4120,7 +4294,7 @@ gst_mxf_demux_loop (GstPad * pad) demux = GST_MXF_DEMUX (gst_pad_get_parent (pad)); - if (demux->run_in == -1) { + if (demux->state == GST_MXF_DEMUX_STATE_UNKNOWN) { GstMXFKLV klv; /* Skip run-in, which is at most 64K and is finished @@ -4135,6 +4309,7 @@ gst_mxf_demux_loop (GstPad * pad) GST_DEBUG_OBJECT (demux, "Found header partition pack at offset %" G_GUINT64_FORMAT, demux->offset); + demux->state = GST_MXF_DEMUX_STATE_KLV; demux->run_in = demux->offset; break; } @@ -4147,7 +4322,7 @@ gst_mxf_demux_loop (GstPad * pad) goto pause; } - /* First of all pull&parse the random index pack at EOF */ + /* Grab the RIP at the end of the file (if present) */ gst_mxf_demux_pull_random_index_pack (demux); } @@ -4268,6 +4443,7 @@ gst_mxf_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) GST_DEBUG_OBJECT (demux, "beginning of file, expect header"); demux->run_in = -1; demux->offset = 0; + demux->state = GST_MXF_DEMUX_STATE_UNKNOWN; } if (G_UNLIKELY (demux->offset == 0 && GST_BUFFER_OFFSET (inbuf) != 0)) { @@ -4292,7 +4468,7 @@ gst_mxf_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) if (gst_adapter_available (demux->adapter) < 16) break; - if (demux->run_in == -1) { + if (demux->state == GST_MXF_DEMUX_STATE_UNKNOWN) { /* Skip run-in, which is at most 64K and is finished * by a header partition pack */ @@ -4307,6 +4483,7 @@ gst_mxf_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) "Found header partition pack at offset %" G_GUINT64_FORMAT, demux->offset); demux->run_in = demux->offset; + demux->state = GST_MXF_DEMUX_STATE_KLV; break; } gst_adapter_flush (demux->adapter, 1); @@ -4320,11 +4497,11 @@ gst_mxf_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) continue; } - /* Need more data */ - if (demux->run_in == -1 && demux->offset < 64 * 1024) - break; + if (demux->state == GST_MXF_DEMUX_STATE_UNKNOWN) { + /* Need more data */ + if (demux->offset < 64 * 1024) + break; - if (G_UNLIKELY (demux->run_in == -1)) { GST_ERROR_OBJECT (demux, "No valid header partition pack found"); ret = GST_FLOW_ERROR; break; @@ -4333,6 +4510,9 @@ gst_mxf_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) if (gst_adapter_available (demux->adapter) < 17) break; + /* FIXME : Handle non-klv state */ + g_assert (demux->state == GST_MXF_DEMUX_STATE_KLV); + /* Now actually do something */ memset (&klv, 0, sizeof (GstMXFKLV)); @@ -4644,8 +4824,8 @@ gst_mxf_demux_seek_push (GstMXFDemux * demux, GstEvent * event) if (format != GST_FORMAT_TIME) goto wrong_format; - flush = ! !(flags & GST_SEEK_FLAG_FLUSH); - keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT); + flush = !!(flags & GST_SEEK_FLAG_FLUSH); + keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT); /* Work on a copy until we are sure the seek succeeded. */ memcpy (&seeksegment, &demux->segment, sizeof (GstSegment)); @@ -4935,8 +5115,8 @@ gst_mxf_demux_seek_pull (GstMXFDemux * demux, GstEvent * event) if (rate <= 0.0) goto wrong_rate; - flush = ! !(flags & GST_SEEK_FLAG_FLUSH); - keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT); + flush = !!(flags & GST_SEEK_FLAG_FLUSH); + keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT); if (!demux->index_table_segments_collected) { collect_index_table_segments (demux); @@ -4984,6 +5164,7 @@ gst_mxf_demux_seek_pull (GstMXFDemux * demux, GstEvent * event) /* Initialize and reset ourselves if needed */ if (flush || seeksegment.position != demux->segment.position) { + GList *tmp; if (!demux->metadata_resolved || demux->update_metadata) { if (gst_mxf_demux_resolve_references (demux) != GST_FLOW_OK || gst_mxf_demux_update_tracks (demux) != GST_FLOW_OK) { @@ -4991,6 +5172,13 @@ gst_mxf_demux_seek_pull (GstMXFDemux * demux, GstEvent * event) } } + /* Reset all single-track KLV tracking */ + for (tmp = demux->partitions; tmp; tmp = tmp->next) { + GstMXFDemuxPartition *partition = (GstMXFDemuxPartition *) tmp->data; + if (partition->single_track) { + partition->clip_klv.consumed = 0; + } + } } keyunit_ts = seeksegment.position; @@ -5084,6 +5272,7 @@ gst_mxf_demux_seek_pull (GstMXFDemux * demux, GstEvent * event) } p->current_essence_track_position = position; } + p->current_essence_track->position = p->current_essence_track_position; p->discont = TRUE; } gst_flow_combiner_reset (demux->flowcombiner); @@ -5094,6 +5283,13 @@ gst_mxf_demux_seek_pull (GstMXFDemux * demux, GstEvent * event) demux->offset = new_offset + demux->run_in; } gst_mxf_demux_set_partition_for_offset (demux, demux->offset); + /* Reset the state accordingly */ + if (demux->current_partition->single_track + && demux->current_partition->single_track->wrapping != + MXF_ESSENCE_WRAPPING_FRAME_WRAPPING) + demux->state = GST_MXF_DEMUX_STATE_ESSENCE; + else + demux->state = GST_MXF_DEMUX_STATE_KLV; } if (G_UNLIKELY (demux->close_seg_event)) { diff --git a/gst/mxf/mxfdemux.h b/gst/mxf/mxfdemux.h index 2936ccb4be..d079a1de1a 100644 --- a/gst/mxf/mxfdemux.h +++ b/gst/mxf/mxfdemux.h @@ -67,6 +67,13 @@ typedef struct { guint64 consumed; } GstMXFKLV; + +typedef enum { + GST_MXF_DEMUX_STATE_UNKNOWN, /* Still looking for run-in/klv */ + GST_MXF_DEMUX_STATE_KLV, /* Next read/fetch is a KLV */ + GST_MXF_DEMUX_STATE_ESSENCE /* Next read/fetch is within a KLV (i.e. non-frame-wrapped) */ +} GstMXFDemuxState; + typedef struct _GstMXFDemuxPartition GstMXFDemuxPartition; typedef struct _GstMXFDemuxEssenceTrack GstMXFDemuxEssenceTrack; @@ -75,9 +82,19 @@ struct _GstMXFDemuxPartition MXFPartitionPack partition; MXFPrimerPack primer; gboolean parsed_metadata; - /* Relative offset at which essence starts within this partition.*/ + + /* Relative offset at which essence starts within this partition. + * + * For Frame wrapping, the position of the first KLV + * For Clip/Custom wrapping, the position of the first byte of essence in the KLV + **/ guint64 essence_container_offset; + /* If the partition contains a single essence track, point to it */ + GstMXFDemuxEssenceTrack *single_track; + + /* For clip-based wrapping, the essence KLV */ + GstMXFKLV clip_klv; }; #define MXF_INDEX_DELTA_ID_UNKNOWN -1 @@ -121,6 +138,10 @@ struct _GstMXFDemuxEssenceTrack MXFEssenceWrapping wrapping; + /* Minimum number of edit unit to send in one go. + * Default : 1 + * Used for raw audio track */ + guint min_edit_units; }; typedef struct @@ -217,6 +238,8 @@ struct _GstMXFDemux GPtrArray *src; /* < private > */ + GstMXFDemuxState state; + gboolean have_group_id; guint group_id; @@ -260,6 +283,7 @@ struct _GstMXFDemux MXFMetadataPreface *preface; GHashTable *metadata; + /* Current Material Package */ MXFUMID current_package_uid; MXFMetadataGenericPackage *current_package; gchar *current_package_string;