diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 69138552d1..eaba0847a7 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 { @@ -441,7 +445,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: @@ -1304,34 +1308,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 -1; } avio_seek(s->pb, -14, SEEK_CUR); mxf->fc = s; + mxf->run_in = avio_tell(s->pb); + while (!url_feof(s->pb)) { const MXFMetadataReadTableEntry *metadata; - if (klv_read_packet(&klv, s->pb) < 0) - return -1; + 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++) { @@ -1354,6 +1458,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); }