mxfdec: Speed up metadata and index parsing.
[ffmpeg.git] / libavformat / mxfdec.c
index 7c45b01..2ab2efe 100644 (file)
@@ -187,6 +187,10 @@ typedef struct {
     int64_t first_essence_length;
     KLVPacket current_klv_data;
     int current_klv_index;
+    int run_in;
+    MXFPartition *current_partition;
+    int parsing_backward;
+    int64_t last_forward_tell;
 } MXFContext;
 
 enum MXFWrappingScheme {
@@ -443,7 +447,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size
     if (!mxf->partitions)
         return AVERROR(ENOMEM);
 
-    partition = &mxf->partitions[mxf->partitions_count++];
+    partition = mxf->current_partition = &mxf->partitions[mxf->partitions_count++];
 
     switch(uid[13]) {
     case 2:
@@ -1341,35 +1345,134 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF
     return ctx_size ? mxf_add_metadata_set(mxf, ctx) : 0;
 }
 
+/**
+ * Seeks to the previous partition, if possible
+ * @return <= 0 if we should stop parsing, > 0 if we should keep going
+ */
+static int mxf_seek_to_previous_partition(MXFContext *mxf)
+{
+    AVIOContext *pb = mxf->fc->pb;
+
+    if (!mxf->current_partition ||
+        mxf->run_in + mxf->current_partition->previous_partition <= mxf->last_forward_tell)
+        return 0;   /* we've parsed all partitions */
+
+    /* seek to previous partition */
+    avio_seek(pb, mxf->run_in + mxf->current_partition->previous_partition, SEEK_SET);
+    mxf->current_partition = NULL;
+
+    av_dlog(mxf->fc, "seeking to previous partition\n");
+
+    return 1;
+}
+
+/**
+ * Called when essence is encountered
+ * @return <= 0 if we should stop parsing, > 0 if we should keep going
+ */
+static int mxf_parse_handle_essence(MXFContext *mxf)
+{
+    AVIOContext *pb = mxf->fc->pb;
+    int64_t ret;
+
+    if (!mxf->current_partition) {
+        av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to PartitionPack\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (mxf->parsing_backward) {
+        return mxf_seek_to_previous_partition(mxf);
+    } else {
+        if (!mxf->footer_partition) {
+            av_dlog(mxf->fc, "no footer\n");
+            return 0;
+        }
+
+        av_dlog(mxf->fc, "seeking to footer\n");
+
+        /* remember where we were so we don't end up seeking further back than this */
+        mxf->last_forward_tell = avio_tell(pb);
+
+        if (!pb->seekable) {
+            av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing footer\n");
+            return -1;
+        }
+
+        /* seek to footer partition and parse backward */
+        if ((ret = avio_seek(pb, mxf->run_in + mxf->footer_partition, SEEK_SET)) < 0) {
+            av_log(mxf->fc, AV_LOG_ERROR, "failed to seek to footer @ 0x%"PRIx64" (%"PRId64") - partial file?\n",
+                   mxf->run_in + mxf->footer_partition, ret);
+            return ret;
+        }
+
+        mxf->current_partition = NULL;
+        mxf->parsing_backward = 1;
+    }
+
+    return 1;
+}
+
+/**
+ * Called when the next partition or EOF is encountered
+ * @return <= 0 if we should stop parsing, > 0 if we should keep going
+ */
+static int mxf_parse_handle_partition_or_eof(MXFContext *mxf)
+{
+    return mxf->parsing_backward ? mxf_seek_to_previous_partition(mxf) : 1;
+}
+
 static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap)
 {
     MXFContext *mxf = s->priv_data;
     KLVPacket klv;
 
+    mxf->last_forward_tell = INT64_MAX;
+
     if (!mxf_read_sync(s->pb, mxf_header_partition_pack_key, 14)) {
         av_log(s, AV_LOG_ERROR, "could not find header partition pack key\n");
         return AVERROR_INVALIDDATA;
     }
     avio_seek(s->pb, -14, SEEK_CUR);
     mxf->fc = s;
+    mxf->run_in = avio_tell(s->pb);
+
     while (!s->pb->eof_reached) {
-        int ret;
         const MXFMetadataReadTableEntry *metadata;
 
-        if ((ret = klv_read_packet(&klv, s->pb)) < 0)
-            return ret;
+        if (klv_read_packet(&klv, s->pb) < 0) {
+            /* EOF - seek to previous partition or stop */
+            if(mxf_parse_handle_partition_or_eof(mxf) <= 0)
+                break;
+            else
+                continue;
+        }
+
         PRINT_KEY(s, "read header", klv.key);
         av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset);
         if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key) ||
-            IS_KLV_KEY(klv.key, mxf_essence_element_key)) {
-            /* FIXME avoid seek */
-            avio_seek(s->pb, klv.offset, SEEK_SET);
-            break;
-        }
+            IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
+            IS_KLV_KEY(klv.key, mxf_system_item_key)) {
         if (IS_KLV_KEY(klv.key, mxf_system_item_key)) {
             mxf->system_item = 1;
-            avio_skip(s->pb, klv.length);
+        }
+
+            if (!mxf->essence_offset)
+                mxf->essence_offset = klv.offset;
+
+            if (!mxf->first_essence_kl_length && IS_KLV_KEY(klv.key, mxf_essence_element_key)) {
+                mxf->first_essence_kl_length = avio_tell(s->pb) - klv.offset;
+                mxf->first_essence_length = klv.length;
+            }
+
+            /* seek to footer, previous partition or stop */
+            if (mxf_parse_handle_essence(mxf) <= 0)
+                break;
             continue;
+        } else if (!memcmp(klv.key, mxf_header_partition_pack_key, 13) &&
+                   klv.key[13] >= 2 && klv.key[13] <= 4 && mxf->current_partition) {
+            /* next partition pack - keep going, seek to previous partition or stop */
+            if(mxf_parse_handle_partition_or_eof(mxf) <= 0)
+                break;
         }
 
         for (metadata = mxf_metadata_read_table; metadata->read; metadata++) {
@@ -1392,6 +1495,12 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap)
         if (!metadata->read)
             avio_skip(s->pb, klv.length);
     }
+    /* FIXME avoid seek */
+    if (!mxf->essence_offset)  {
+        av_log(s, AV_LOG_ERROR, "no essence\n");
+        return AVERROR_INVALIDDATA;
+    }
+    avio_seek(s->pb, mxf->essence_offset, SEEK_SET);
     return mxf_parse_structural_metadata(mxf);
 }