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 {
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:
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++) {
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);
}