+/**
+ * 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->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;
+}
+
+/**
+ * Figure out the proper offset and length of the essence container
+ * in each partition
+ */
+static void mxf_compute_essence_containers(MXFContext *mxf)
+{
+ int x;
+
+ /* everything is already correct */
+ if (mxf->op == OPAtom)
+ return;
+
+ for (x = 0; x < mxf->partitions_count; x++) {
+ MXFPartition *p = &mxf->partitions[x];
+
+ if (!p->body_sid)
+ continue; /* BodySID == 0 -> no essence */
+
+ if (x >= mxf->partitions_count - 1)
+ break; /* last partition - can't compute length (and we don't need to) */
+
+ /* essence container spans to the next partition */
+ p->essence_length = mxf->partitions[x+1].this_partition - p->essence_offset;
+
+ if (p->essence_length < 0) {
+ /* next ThisPartition < essence_offset */
+ p->essence_length = 0;
+ av_log(mxf->fc, AV_LOG_ERROR,
+ "partition %i: bad ThisPartition = %"PRIX64"\n",
+ x+1, mxf->partitions[x+1].this_partition);
+ }
+ }
+}
+
+static int64_t round_to_kag(int64_t position, int kag_size)
+{
+ /* TODO: account for run-in? the spec isn't clear whether KAG should account for it */
+ /* NOTE: kag_size may be any integer between 1 - 2^10 */
+ int64_t ret = (position / kag_size) * kag_size;
+ return ret == position ? ret : ret + kag_size;
+}
+
+static inline void compute_partition_essence_offset(AVFormatContext *s,
+ MXFContext *mxf,
+ KLVPacket *klv)
+{
+ MXFPartition *cur_part = mxf->current_partition;
+ /* for OP1a we compute essence_offset
+ * for OPAtom we point essence_offset after the KL
+ * (usually op1a_essence_offset + 20 or 25)
+ * TODO: for OP1a we could eliminate this entire if statement, always
+ * stopping parsing at op1a_essence_offset
+ * for OPAtom we still need the actual essence_offset though
+ * (the KL's length can vary)
+ */
+ int64_t op1a_essence_offset =
+ round_to_kag(cur_part->this_partition + cur_part->pack_length,
+ cur_part->kag_size) +
+ round_to_kag(cur_part->header_byte_count, cur_part->kag_size) +
+ round_to_kag(cur_part->index_byte_count, cur_part->kag_size);
+
+ if (mxf->op == OPAtom) {
+ /* point essence_offset to the actual data
+ * OPAtom has all the essence in one big KLV
+ */
+ cur_part->essence_offset = avio_tell(s->pb);
+ cur_part->essence_length = klv->length;
+ } else {
+ /* NOTE: op1a_essence_offset may be less than to klv.offset
+ * (C0023S01.mxf) */
+ cur_part->essence_offset = op1a_essence_offset;
+ }
+}
+
+static int is_pcm(enum CodecID codec_id)
+{
+ /* we only care about "normal" PCM codecs until we get samples */
+ return codec_id >= CODEC_ID_PCM_S16LE && codec_id < CODEC_ID_PCM_S24DAUD;
+}
+
+/**
+ * Deal with the case where for some audio atoms EditUnitByteCount is
+ * very small (2, 4..). In those cases we should read more than one
+ * sample per call to mxf_read_packet().
+ */
+static void mxf_handle_small_eubc(AVFormatContext *s)
+{
+ MXFContext *mxf = s->priv_data;
+
+ /* assuming non-OPAtom == frame wrapped
+ * no sane writer would wrap 2 byte PCM packets with 20 byte headers.. */
+ if (mxf->op != OPAtom)
+ return;
+
+ /* expect PCM with exactly one index table segment and a small (< 32) EUBC */
+ if (s->nb_streams != 1 ||
+ s->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO ||
+ !is_pcm(s->streams[0]->codec->codec_id) ||
+ mxf->nb_index_tables != 1 ||
+ mxf->index_tables[0].nb_segments != 1 ||
+ mxf->index_tables[0].segments[0]->edit_unit_byte_count >= 32)
+ return;
+
+ /* arbitrarily default to 48 kHz PAL audio frame size */
+ /* TODO: We could compute this from the ratio between the audio
+ * and video edit rates for 48 kHz NTSC we could use the
+ * 1802-1802-1802-1802-1801 pattern. */
+ mxf->edit_units_per_packet = 1920;
+}
+
+static int mxf_read_header(AVFormatContext *s)