mxfdemux: Handle non-frame wrapping
authorEdward Hervey <edward@centricular.com>
Thu, 15 Jul 2021 08:59:39 +0000 (10:59 +0200)
committerEdward Hervey <bilboed@bilboed.com>
Wed, 21 Jul 2021 14:33:19 +0000 (14:33 +0000)
* 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: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2371>

gst/mxf/mxfdemux.c
gst/mxf/mxfdemux.h

index 0d0b9f1..3bf5632 100644 (file)
@@ -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)) {
index 2936ccb..d079a1d 100644 (file)
@@ -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;