[libav-commits] mxfdec: Speed up metadata and index parsing.

Tomas Härdin git at libav.org
Sun Jan 22 14:47:45 CET 2012


Module: libav
Branch: master
Commit: 775d3b4ab174d3654864668beb8a68657bc6da9b

Author:    Tomas Härdin <tomas.hardin at codemill.se>
Committer: Janne Grunau <janne-libav at jannau.net>
Date:      Mon Mar 14 17:00:21 2011 +0100

mxfdec: Speed up metadata and index parsing.

Specifically, this means parsing as before until we run into essence.
At that point we seek to the footer and parse until EOF. After that we start
seeking backward to the previous partition and parse that until we run into
essence or the next partition. This procedure is repeated until we encounter
the last partition we parsed in the forward direction.

The end result of all this is that large essence containers are not needlessly
parsed. This speeds up parsing large files a lot.

Signed-off-by: Janne Grunau <janne-libav at jannau.net>

---

 libavformat/mxfdec.c |  129 ++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 119 insertions(+), 10 deletions(-)

diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index 7c45b01..2ab2efe 100644
--- a/libavformat/mxfdec.c
+++ b/libavformat/mxfdec.c
@@ -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);
 }
 



More information about the libav-commits mailing list