[FFmpeg-cvslog] mxfdec: hybrid demuxing/seeking solution

Janne Grunau git at videolan.org
Mon Jan 23 01:18:12 CET 2012


ffmpeg | branch: master | Janne Grunau <janne-libav at jannau.net> | Thu Dec  8 20:28:49 2011 +0100| [f028d4d1c393a13c66e828d45ba8412c0b4df6da] | committer: Janne Grunau

mxfdec: hybrid demuxing/seeking solution

This uses the old demuxing code for OP1a and separate demuxing code for OPAtom.
Timestamp output is added to the old demuxing code.

The seeking code is made to seek to the start of the desired EditUnit only,
from which the normal demuxing code takes over (if OP1a). This means we
do not use delta entries or slices, only StreamOffsets. OPAtom seeking
basically works like before.

This also makes D-10 seeking behave the same way as OP1a and OPAtom. In other
words, we allow seeking before the start or past the end for D-10 too.

Based on several patches by Tomas Härdin <tomas.hardin at codemill.se> and
Reimar Döffinger <Reimar.Doeffinger at gmx.de>.

Changed av_calloc to av_mallocz, added overflow checks.

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=f028d4d1c393a13c66e828d45ba8412c0b4df6da
---

 libavformat/mxfdec.c        |  597 ++++++++++++++++++++++++++++++++-----------
 tests/ref/seek/lavf_mxf     |   59 ++---
 tests/ref/seek/lavf_mxf_d10 |   36 ++--
 3 files changed, 499 insertions(+), 193 deletions(-)

diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index 37f22a6..f192d80 100644
--- a/libavformat/mxfdec.c
+++ b/libavformat/mxfdec.c
@@ -177,6 +177,18 @@ typedef struct {
     enum MXFMetadataSetType type;
 } MXFMetadataSet;
 
+/* decoded index table */
+typedef struct {
+    int index_sid;
+    int body_sid;
+    int nb_ptses;               /* number of PTSes or total duration of index */
+    int64_t first_dts;          /* DTS = EditUnit + first_dts */
+    int64_t *ptses;             /* maps EditUnit -> PTS */
+    int nb_segments;
+    MXFIndexTableSegment **segments;    /* sorted by IndexStartPosition */
+    AVIndexEntry *fake_index;   /* used for calling ff_index_search_timestamp() */
+} MXFIndexTable;
+
 typedef struct {
     MXFPartition *partitions;
     unsigned partitions_count;
@@ -197,6 +209,9 @@ typedef struct {
     int parsing_backward;
     int64_t last_forward_tell;
     int last_forward_partition;
+    int current_edit_unit;
+    int nb_index_tables;
+    MXFIndexTable *index_tables;
 } MXFContext;
 
 enum MXFWrappingScheme {
@@ -373,54 +388,6 @@ static int mxf_decrypt_triplet(AVFormatContext *s, AVPacket *pkt, KLVPacket *klv
     return 0;
 }
 
-static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
-{
-    KLVPacket klv;
-
-    while (!s->pb->eof_reached) {
-        int ret;
-        if (klv_read_packet(&klv, s->pb) < 0)
-            return -1;
-        PRINT_KEY(s, "read packet", klv.key);
-        av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset);
-        if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
-            ret = mxf_decrypt_triplet(s, pkt, &klv);
-            if (ret < 0) {
-                av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n");
-                return AVERROR_INVALIDDATA;
-            }
-            return 0;
-        }
-        if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
-            IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) {
-            int index = mxf_get_stream_index(s, &klv);
-            if (index < 0) {
-                av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12));
-                goto skip;
-            }
-            if (s->streams[index]->discard == AVDISCARD_ALL)
-                goto skip;
-            /* check for 8 channels AES3 element */
-            if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) {
-                if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) {
-                    av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n");
-                    return AVERROR_INVALIDDATA;
-                }
-            } else {
-                ret = av_get_packet(s->pb, pkt, klv.length);
-                if (ret < 0)
-                    return ret;
-            }
-            pkt->stream_index = index;
-            pkt->pos = klv.offset;
-            return 0;
-        } else
-        skip:
-            avio_skip(s->pb, klv.length);
-    }
-    return AVERROR_EOF;
-}
-
 static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
 {
     MXFContext *mxf = arg;
@@ -1053,111 +1020,268 @@ static int mxf_absolute_bodysid_offset(MXFContext *mxf, int body_sid, int64_t of
     return AVERROR_INVALIDDATA;
 }
 
-static int mxf_parse_index(MXFContext *mxf, int track_id, AVStream *st)
+/**
+ * Returns the end position of the essence container with given BodySID, or zero if unknown
+ */
+static int64_t mxf_essence_container_end(MXFContext *mxf, int body_sid)
 {
-    int64_t accumulated_offset = 0;
-    int j, k, ret, nb_sorted_segments;
-    MXFIndexTableSegment **sorted_segments;
-    int n_delta = track_id - 1;  /* TrackID = 1-based stream index */
+    int x;
+    int64_t ret = 0;
 
-    if (track_id < 1) {
-        av_log(mxf->fc, AV_LOG_ERROR, "TrackID not positive: %i\n", track_id);
-        return AVERROR_INVALIDDATA;
+    for (x = 0; x < mxf->partitions_count; x++) {
+        MXFPartition *p = &mxf->partitions[x];
+
+        if (p->body_sid != body_sid)
+            continue;
+
+        if (!p->essence_length)
+            return 0;
+
+        ret = p->essence_offset + p->essence_length;
     }
 
-    if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments)))
-        return ret;
+    return ret;
+}
 
-    for (j = 0; j < nb_sorted_segments; j++) {
-        int duration, sample_duration = 1, last_sample_size = 0;
-        int64_t segment_size;
-        MXFIndexTableSegment *tableseg = sorted_segments[j];
+/* EditUnit -> absolute offset */
+static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_table, int64_t edit_unit, int64_t *edit_unit_out, int64_t *offset_out, int nag)
+{
+    int i;
+    int offset_temp = 0;
 
-        /* reset accumulated_offset on BodySID change */
-        if (j > 0 && tableseg->body_sid != sorted_segments[j-1]->body_sid)
-            accumulated_offset = 0;
+    for (i = 0; i < index_table->nb_segments; i++) {
+        MXFIndexTableSegment *s = index_table->segments[i];
 
-        if (n_delta >= tableseg->nb_delta_entries && st->index != 0)
-            continue;
-        duration = tableseg->index_duration > 0 ? tableseg->index_duration :
-            st->duration - st->nb_index_entries;
-        segment_size = tableseg->edit_unit_byte_count * duration;
-        /* check small EditUnitByteCount for audio */
-        if (tableseg->edit_unit_byte_count && tableseg->edit_unit_byte_count < 32
-            && !tableseg->index_duration) {
-            /* duration might be prime relative to the new sample_duration,
-             * which means we need to handle the last frame differently */
-            sample_duration = 8192;
-            last_sample_size = (duration % sample_duration) * tableseg->edit_unit_byte_count;
-            tableseg->edit_unit_byte_count *= sample_duration;
-            duration /= sample_duration;
-            if (last_sample_size) duration++;
-        }
+        edit_unit = FFMAX(edit_unit, s->index_start_position);  /* clamp if trying to seek before start */
 
-        for (k = 0; k < duration; k++) {
-            int64_t pos;
-            int size, flags = 0;
-
-            if (k < tableseg->nb_index_entries) {
-                pos = tableseg->stream_offset_entries[k];
-                if (n_delta < tableseg->nb_delta_entries) {
-                    if (n_delta < tableseg->nb_delta_entries - 1) {
-                        size =
-                            tableseg->slice_offset_entries[k][tableseg->slice[n_delta+1]-1] +
-                            tableseg->element_delta[n_delta+1] -
-                            tableseg->element_delta[n_delta];
-                        if (tableseg->slice[n_delta] > 0)
-                            size -= tableseg->slice_offset_entries[k][tableseg->slice[n_delta]-1];
-                    } else if (k < duration - 1) {
-                        size = tableseg->stream_offset_entries[k+1] -
-                            tableseg->stream_offset_entries[k] -
-                            tableseg->slice_offset_entries[k][tableseg->slice[tableseg->nb_delta_entries-1]-1] -
-                            tableseg->element_delta[tableseg->nb_delta_entries-1];
-                    } else
-                        size = 0;
-                    if (tableseg->slice[n_delta] > 0)
-                        pos += tableseg->slice_offset_entries[k][tableseg->slice[n_delta]-1];
-                    pos += tableseg->element_delta[n_delta];
-                } else
-                    size = 0;
-                flags = !(tableseg->flag_entries[k] & 0x30) ? AVINDEX_KEYFRAME : 0;
-            } else {
-                pos = (int64_t)k * tableseg->edit_unit_byte_count + accumulated_offset;
-                if (n_delta < tableseg->nb_delta_entries - 1)
-                    size = tableseg->element_delta[n_delta+1] - tableseg->element_delta[n_delta];
-                else {
-                    /* use smaller size for last sample if we should */
-                    if (last_sample_size && k == duration - 1)
-                        size = last_sample_size;
-                    else
-                        size = tableseg->edit_unit_byte_count;
-                    if (tableseg->nb_delta_entries)
-                        size -= tableseg->element_delta[tableseg->nb_delta_entries-1];
+        if (edit_unit < s->index_start_position + s->index_duration) {
+            int64_t index = edit_unit - s->index_start_position;
+
+            if (s->edit_unit_byte_count)
+                offset_temp += s->edit_unit_byte_count * index;
+            else if (s->nb_index_entries) {
+                if (s->nb_index_entries == 2 * s->index_duration + 1)
+                    index *= 2;     /* Avid index */
+
+                if (index < 0 || index > s->nb_index_entries) {
+                    av_log(mxf->fc, AV_LOG_ERROR, "IndexSID %i segment at %"PRId64" IndexEntryArray too small\n",
+                           index_table->index_sid, s->index_start_position);
+                    return AVERROR_INVALIDDATA;
                 }
-                if (n_delta < tableseg->nb_delta_entries)
-                    pos += tableseg->element_delta[n_delta];
-                flags = AVINDEX_KEYFRAME;
-            }
 
-            if (mxf_absolute_bodysid_offset(mxf, tableseg->body_sid, pos, &pos) < 0) {
-                /* probably partial file - no point going further for this stream */
-                break;
+                offset_temp = s->stream_offset_entries[index];
+            } else {
+                av_log(mxf->fc, AV_LOG_ERROR, "IndexSID %i segment at %"PRId64" missing EditUnitByteCount and IndexEntryArray\n",
+                       index_table->index_sid, s->index_start_position);
+                return AVERROR_INVALIDDATA;
             }
 
-            av_dlog(mxf->fc, "Stream %d IndexEntry %d TrackID %d Offset %"PRIx64" Timestamp %"PRId64"\n",
-                    st->index, st->nb_index_entries, track_id, pos, sample_duration * st->nb_index_entries);
+            if (edit_unit_out)
+                *edit_unit_out = edit_unit;
 
-            if ((ret = av_add_index_entry(st, pos, sample_duration * st->nb_index_entries, size, 0, flags)) < 0)
-                return ret;
+            return mxf_absolute_bodysid_offset(mxf, index_table->body_sid, offset_temp, offset_out);
+        } else {
+            /* EditUnitByteCount == 0 for VBR indexes, which is fine since they use explicit StreamOffsets */
+            offset_temp += s->edit_unit_byte_count * s->index_duration;
         }
-        accumulated_offset += segment_size;
     }
 
-    av_free(sorted_segments);
+    if (nag)
+        av_log(mxf->fc, AV_LOG_ERROR, "failed to map EditUnit %"PRId64" in IndexSID %i to an offset\n", edit_unit, index_table->index_sid);
+
+    return AVERROR_INVALIDDATA;
+}
+
+static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_table)
+{
+    int i, j, x;
+    int8_t max_temporal_offset = -128;
+
+    /* first compute how many entries we have */
+    for (i = 0; i < index_table->nb_segments; i++) {
+        MXFIndexTableSegment *s = index_table->segments[i];
+
+        if (!s->nb_index_entries)
+            return 0;                               /* no TemporalOffsets */
+
+        index_table->nb_ptses += s->index_duration;
+    }
+
+    /* paranoid check */
+    if (index_table->nb_ptses <= 0)
+        return 0;
+
+    if (index_table->nb_ptses > INT_MAX >> av_log2(sizeof(AVIndexEntry)) + 1)
+        return AVERROR(ENOMEM);
+
+    index_table->ptses      = av_mallocz(index_table->nb_ptses *
+                                         sizeof(int64_t));
+    index_table->fake_index = av_mallocz(index_table->nb_ptses *
+                                         sizeof(AVIndexEntry));
+    if (!index_table->ptses || !index_table->fake_index) {
+        av_freep(&index_table->ptses);
+        return AVERROR(ENOMEM);
+    }
+
+    /* we may have a few bad TemporalOffsets
+     * make sure the corresponding PTSes don't have the bogus value 0 */
+    for (x = 0; x < index_table->nb_ptses; x++)
+        index_table->ptses[x] = AV_NOPTS_VALUE;
+
+    /**
+     * We have this:
+     *
+     * x  TemporalOffset
+     * 0:  0
+     * 1:  1
+     * 2:  1
+     * 3: -2
+     * 4:  1
+     * 5:  1
+     * 6: -2
+     *
+     * We want to transform it into this:
+     *
+     * x  DTS PTS
+     * 0: -1   0
+     * 1:  0   3
+     * 2:  1   1
+     * 3:  2   2
+     * 4:  3   6
+     * 5:  4   4
+     * 6:  5   5
+     *
+     * We do this by bucket sorting x by x+TemporalOffset[x] into mxf->ptses,
+     * then settings mxf->first_dts = -max(TemporalOffset[x]).
+     * The latter makes DTS <= PTS.
+     */
+    for (i = x = 0; i < index_table->nb_segments; i++) {
+        MXFIndexTableSegment *s = index_table->segments[i];
+        int index_delta = 1;
+
+        if (s->nb_index_entries == 2 * s->index_duration + 1)
+            index_delta = 2;    /* Avid index */
+
+        for (j = 0; j < s->nb_index_entries; j += index_delta, x++) {
+            int offset = s->temporal_offset_entries[j] / index_delta;
+            int index  = x + offset;
+
+            index_table->fake_index[x].timestamp = x;
+            index_table->fake_index[x].flags = !(s->flag_entries[j] & 0x30) ? AVINDEX_KEYFRAME : 0;
+
+            if (index < 0 || index >= index_table->nb_ptses) {
+                av_log(mxf->fc, AV_LOG_ERROR,
+                       "index entry %i + TemporalOffset %i = %i, which is out of bounds\n",
+                       x, offset, index);
+                continue;
+            }
+
+            index_table->ptses[index] = x;
+            max_temporal_offset = FFMAX(max_temporal_offset, offset);
+        }
+    }
+
+    index_table->first_dts = -max_temporal_offset;
 
     return 0;
 }
 
+/**
+ * Sorts and collects index table segments into index tables.
+ * Also computes PTSes if possible.
+ */
+static int mxf_compute_index_tables(MXFContext *mxf)
+{
+    int i, j, k, ret, nb_sorted_segments;
+    MXFIndexTableSegment **sorted_segments = NULL;
+
+    if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments)) ||
+        nb_sorted_segments <= 0) {
+        av_log(mxf->fc, AV_LOG_WARNING, "broken or empty index\n");
+        return 0;
+    }
+
+    /* sanity check and count unique BodySIDs/IndexSIDs */
+    for (i = 0; i < nb_sorted_segments; i++) {
+        if (i == 0 || sorted_segments[i-1]->index_sid != sorted_segments[i]->index_sid)
+            mxf->nb_index_tables++;
+        else if (sorted_segments[i-1]->body_sid != sorted_segments[i]->body_sid) {
+            av_log(mxf->fc, AV_LOG_ERROR, "found inconsistent BodySID\n");
+            ret = AVERROR_INVALIDDATA;
+            goto finish_decoding_index;
+        }
+    }
+
+    if (mxf->nb_index_tables > INT_MAX >> av_log2(sizeof(MXFIndexTable)) + 1 ||
+        !(mxf->index_tables = av_mallocz(mxf->nb_index_tables *
+                                         sizeof(MXFIndexTable)))) {
+        av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate index tables\n");
+        ret = AVERROR(ENOMEM);
+        goto finish_decoding_index;
+    }
+
+    /* distribute sorted segments to index tables */
+    for (i = j = 0; i < nb_sorted_segments; i++) {
+        if (i != 0 && sorted_segments[i-1]->index_sid != sorted_segments[i]->index_sid) {
+            /* next IndexSID */
+            j++;
+        }
+
+        mxf->index_tables[j].nb_segments++;
+    }
+
+    for (i = j = 0; j < mxf->nb_index_tables; i += mxf->index_tables[j++].nb_segments) {
+        MXFIndexTable *t = &mxf->index_tables[j];
+
+        if (t->nb_segments >
+            (INT_MAX >> av_log2(sizeof(MXFIndexTableSegment *))) ||
+            !(t->segments = av_mallocz(t->nb_segments *
+                                       sizeof(MXFIndexTableSegment*)))) {
+            av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate IndexTableSegment"
+                   " pointer array\n");
+            ret = AVERROR(ENOMEM);
+            goto finish_decoding_index;
+        }
+
+        if (sorted_segments[i]->index_start_position)
+            av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i starts at EditUnit %"PRId64" - seeking may not work as expected\n",
+                   sorted_segments[i]->index_sid, sorted_segments[i]->index_start_position);
+
+        memcpy(t->segments, &sorted_segments[i], t->nb_segments * sizeof(MXFIndexTableSegment*));
+        t->index_sid = sorted_segments[i]->index_sid;
+        t->body_sid = sorted_segments[i]->body_sid;
+
+        if ((ret = mxf_compute_ptses_fake_index(mxf, t)) < 0)
+            goto finish_decoding_index;
+
+        /* fix zero IndexDurations */
+        for (k = 0; k < t->nb_segments; k++) {
+            if (t->segments[k]->index_duration)
+                continue;
+
+            if (t->nb_segments > 1)
+                av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment %i has zero IndexDuration and there's more than one segment\n",
+                       t->index_sid, k);
+
+            if (mxf->fc->nb_streams <= 0) {
+                av_log(mxf->fc, AV_LOG_WARNING, "no streams?\n");
+                break;
+            }
+
+            /* assume the first stream's duration is reasonable
+             * leave index_duration = 0 on further segments in case we have any (unlikely)
+             */
+            t->segments[k]->index_duration = mxf->fc->streams[0]->duration;
+            break;
+        }
+    }
+
+    ret = 0;
+finish_decoding_index:
+    av_free(sorted_segments);
+    return ret;
+}
+
 static int mxf_parse_structural_metadata(MXFContext *mxf)
 {
     MXFPackage *material_package = NULL;
@@ -1220,7 +1344,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
             for (k = 0; k < source_package->tracks_count; k++) {
                 if (!(temp_track = mxf_resolve_strong_ref(mxf, &source_package->tracks_refs[k], Track))) {
                     av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track strong ref\n");
-                    return AVERROR_INVALIDDATA;
+                    ret = AVERROR_INVALIDDATA;
+                    goto fail_and_free;
                 }
                 if (temp_track->track_id == component->source_track_id) {
                     source_track = temp_track;
@@ -1237,7 +1362,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
 
         if (!(source_track->sequence = mxf_resolve_strong_ref(mxf, &source_track->sequence_ref, Sequence))) {
             av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n");
-            return AVERROR_INVALIDDATA;
+            ret = AVERROR_INVALIDDATA;
+            goto fail_and_free;
         }
 
         /* 0001GL00.MXF.A1.mxf_opatom.mxf has the same SourcePackageID as 0001GL.MXF.V1.mxf_opatom.mxf
@@ -1250,7 +1376,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
         st = avformat_new_stream(mxf->fc, NULL);
         if (!st) {
             av_log(mxf->fc, AV_LOG_ERROR, "could not allocate stream\n");
-            return AVERROR(ENOMEM);
+            ret = AVERROR(ENOMEM);
+            goto fail_and_free;
         }
         st->id = source_track->track_id;
         st->priv_data = source_track;
@@ -1301,6 +1428,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
                 }
             }
         }
+
         /* TODO: drop PictureEssenceCoding and SoundEssenceCompression, only check EssenceContainer */
         codec_ul = mxf_get_codec_ul(ff_mxf_codec_uls, &descriptor->essence_codec_ul);
         st->codec->codec_id = codec_ul->id;
@@ -1340,14 +1468,14 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
             }
         }
         if (st->codec->codec_type != AVMEDIA_TYPE_DATA && (*essence_container_ul)[15] > 0x01) {
-            av_log(mxf->fc, AV_LOG_WARNING, "only frame wrapped mappings are correctly supported\n");
-            st->need_parsing = AVSTREAM_PARSE_FULL;
+            /* TODO: decode timestamps */
+            st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS;
         }
-
-        if ((ret = mxf_parse_index(mxf, material_track->track_id, st)))
-            return ret;
     }
-    return 0;
+
+    ret = 0;
+fail_and_free:
+    return ret;
 }
 
 static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = {
@@ -1577,6 +1705,7 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap)
     MXFContext *mxf = s->priv_data;
     KLVPacket klv;
     int64_t essence_offset = 0;
+    int ret;
 
     mxf->last_forward_tell = INT64_MAX;
 
@@ -1652,9 +1781,156 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap)
 
     mxf_compute_essence_containers(mxf);
 
-    return mxf_parse_structural_metadata(mxf);
+    /* we need to do this before computing the index tables
+     * to be able to fill in zero IndexDurations with st->duration */
+    if ((ret = mxf_parse_structural_metadata(mxf)) < 0)
+        return ret;
+
+    if ((ret = mxf_compute_index_tables(mxf)) < 0)
+        return ret;
+
+    if (mxf->nb_index_tables > 1) {
+        /* TODO: look up which IndexSID to use via EssenceContainerData */
+        av_log(mxf->fc, AV_LOG_INFO, "got %i index tables - only the first one (IndexSID %i) will be used\n",
+               mxf->nb_index_tables, mxf->index_tables[0].index_sid);
+    } else if (mxf->nb_index_tables == 0 && mxf->op == OPAtom) {
+        av_log(mxf->fc, AV_LOG_ERROR, "cannot demux OPAtom without an index\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    return 0;
 }
 
+/**
+ * Computes DTS and PTS for the given video packet based on its offset.
+ */
+static void mxf_packet_timestamps(MXFContext *mxf, AVPacket *pkt)
+{
+    int64_t next_ofs;
+    MXFIndexTable *t = &mxf->index_tables[0];
+
+    /* find mxf->current_edit_unit so that the next edit unit starts ahead of pkt->pos */
+    for (;;) {
+        if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, 0) < 0)
+            break;
+
+        if (next_ofs > pkt->pos)
+            break;
+
+        mxf->current_edit_unit++;
+    }
+
+    if (mxf->current_edit_unit >= t->nb_ptses)
+        return;
+
+    pkt->dts = mxf->current_edit_unit + t->first_dts;
+    pkt->pts = t->ptses[mxf->current_edit_unit];
+}
+
+static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
+{
+    KLVPacket klv;
+
+    while (!s->pb->eof_reached) {
+        if (klv_read_packet(&klv, s->pb) < 0)
+            return -1;
+        PRINT_KEY(s, "read packet", klv.key);
+        av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset);
+        if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
+            int res = mxf_decrypt_triplet(s, pkt, &klv);
+            if (res < 0) {
+                av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n");
+                return -1;
+            }
+            return 0;
+        }
+        if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
+            IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) {
+            int index = mxf_get_stream_index(s, &klv);
+            if (index < 0) {
+                av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12));
+                goto skip;
+            }
+            if (s->streams[index]->discard == AVDISCARD_ALL)
+                goto skip;
+            /* check for 8 channels AES3 element */
+            if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) {
+                if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) {
+                    av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n");
+                    return -1;
+                }
+            } else {
+                int ret = av_get_packet(s->pb, pkt, klv.length);
+                if (ret < 0)
+                    return ret;
+            }
+            pkt->stream_index = index;
+            pkt->pos = klv.offset;
+
+            if (s->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+                mxf_packet_timestamps(s->priv_data, pkt);   /* offset -> EditUnit -> DTS/PTS */
+
+            return 0;
+        } else
+        skip:
+            avio_skip(s->pb, klv.length);
+    }
+    return AVERROR_EOF;
+}
+
+static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    MXFContext *mxf = s->priv_data;
+    int ret, size;
+    int64_t ret64, pos, next_pos;
+    AVStream *st;
+    MXFIndexTable *t;
+
+    if (mxf->op != OPAtom)
+        return mxf_read_packet_old(s, pkt);
+
+    /* OPAtom - clip wrapped demuxing */
+    st = s->streams[0];
+    t = &mxf->index_tables[0];
+
+    if (mxf->current_edit_unit >= st->duration)
+        return AVERROR_EOF;
+
+    if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit, NULL, &pos, 1)) < 0)
+        return ret;
+
+    /* compute size by finding the next edit unit or the end of the essence container
+     * not pretty, but it works */
+    if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_pos, 0)) < 0 &&
+        (next_pos = mxf_essence_container_end(mxf, t->body_sid)) <= 0) {
+        av_log(s, AV_LOG_ERROR, "unable to compute the size of the last packet\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    if ((size = next_pos - pos) <= 0) {
+        av_log(s, AV_LOG_ERROR, "bad size: %i\n", size);
+        return AVERROR_INVALIDDATA;
+    }
+
+    if ((ret64 = avio_seek(s->pb, pos, SEEK_SET)) < 0)
+        return ret64;
+
+        if ((ret = av_get_packet(s->pb, pkt, size)) != size)
+            return ret < 0 ? ret : AVERROR_EOF;
+
+    if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && t->ptses &&
+        mxf->current_edit_unit >= 0 && mxf->current_edit_unit < t->nb_ptses) {
+        pkt->dts = mxf->current_edit_unit + t->first_dts;
+        pkt->pts = t->ptses[mxf->current_edit_unit];
+    }
+
+    pkt->stream_index = 0;
+    mxf->current_edit_unit++;
+
+    return 0;
+}
+
+
 static int mxf_read_close(AVFormatContext *s)
 {
     MXFContext *mxf = s->priv_data;
@@ -1699,6 +1975,13 @@ static int mxf_read_close(AVFormatContext *s)
     av_freep(&mxf->metadata_sets);
     av_freep(&mxf->aesc);
     av_freep(&mxf->local_tags);
+
+    for (i = 0; i < mxf->nb_index_tables; i++) {
+        av_freep(&mxf->index_tables[i].segments);
+        av_freep(&mxf->index_tables[i].fake_index);
+    }
+    av_freep(&mxf->index_tables);
+
     return 0;
 }
 
@@ -1724,8 +2007,12 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
 {
     AVStream *st = s->streams[stream_index];
     int64_t seconds;
+    MXFContext* mxf = s->priv_data;
+    int64_t seekpos;
     int ret;
+    MXFIndexTable *t;
 
+    if (mxf->index_tables <= 0) {
     if (!s->bit_rate)
         return AVERROR_INVALIDDATA;
     if (sample_time < 0)
@@ -1735,6 +2022,30 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
     if ((ret = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET)) < 0)
         return ret;
     ff_update_cur_dts(s, st, sample_time);
+    } else {
+        t = &mxf->index_tables[0];
+
+        /* clamp above zero, else ff_index_search_timestamp() returns negative
+         * this also means we allow seeking before the start */
+        sample_time = FFMAX(sample_time, 0);
+
+        if (t->fake_index) {
+            /* behave as if we have a proper index */
+            if ((sample_time = ff_index_search_timestamp(t->fake_index, t->nb_ptses, sample_time, flags)) < 0)
+                return sample_time;
+        } else {
+            /* no IndexEntryArray (one or more CBR segments)
+             * make sure we don't seek past the end */
+            sample_time = FFMIN(sample_time, st->duration - 1);
+        }
+
+        if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) << 0)
+            return ret;
+
+        ff_update_cur_dts(s, st, sample_time);
+        mxf->current_edit_unit = sample_time;
+        avio_seek(s->pb, seekpos, SEEK_SET);
+    }
     return 0;
 }
 
diff --git a/tests/ref/seek/lavf_mxf b/tests/ref/seek/lavf_mxf
index 4c1aecc..cc634a8 100644
--- a/tests/ref/seek/lavf_mxf
+++ b/tests/ref/seek/lavf_mxf
@@ -1,53 +1,48 @@
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: NOPTS    pos:   6144 size: 24801
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
 ret: 0         st:-1 flags:0  ts:-1.000000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: NOPTS    pos:   6144 size: 24801
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
 ret: 0         st:-1 flags:1  ts: 1.894167
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
 ret: 0         st: 0 flags:0  ts: 0.800000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
 ret: 0         st: 0 flags:1  ts:-0.320000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: NOPTS    pos:   6144 size: 24801
-ret: 0         st: 1 flags:0  ts: 2.560000
-ret:-1
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
+ret:-1         st: 1 flags:0  ts: 2.560000
 ret: 0         st: 1 flags:1  ts: 1.480000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
 ret: 0         st:-1 flags:0  ts: 0.365002
-ret: 0         st: 0 flags:1 dts: 0.360000 pts: NOPTS    pos:   6144 size: 24801
+ret: 0         st: 0 flags:1 dts: 0.360000 pts: 0.480000 pos: 211968 size: 24787
 ret: 0         st:-1 flags:1  ts:-0.740831
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: NOPTS    pos:   6144 size: 24801
-ret: 0         st: 0 flags:0  ts: 2.160000
-ret:-1
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
+ret:-1         st: 0 flags:0  ts: 2.160000
 ret: 0         st: 0 flags:1  ts: 1.040000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
 ret: 0         st: 1 flags:0  ts:-0.040000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: NOPTS    pos:   6144 size: 24801
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
 ret: 0         st: 1 flags:1  ts: 2.840000
-ret:-1
-ret: 0         st:-1 flags:0  ts: 1.730004
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
+ret:-1         st:-1 flags:0  ts: 1.730004
 ret: 0         st:-1 flags:1  ts: 0.624171
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.360000 pts: 0.480000 pos: 211968 size: 24787
 ret: 0         st: 0 flags:0  ts:-0.480000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: NOPTS    pos:   6144 size: 24801
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
 ret: 0         st: 0 flags:1  ts: 2.400000
-ret:-1
-ret: 0         st: 1 flags:0  ts: 1.320000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
+ret:-1         st: 1 flags:0  ts: 1.320000
 ret: 0         st: 1 flags:1  ts: 0.200000
-ret: 0         st: 0 flags:1 dts: 0.200000 pts: NOPTS    pos:   6144 size: 24801
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
 ret: 0         st:-1 flags:0  ts:-0.904994
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: NOPTS    pos:   6144 size: 24801
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
 ret: 0         st:-1 flags:1  ts: 1.989173
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
 ret: 0         st: 0 flags:0  ts: 0.880000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
 ret: 0         st: 0 flags:1  ts:-0.240000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: NOPTS    pos:   6144 size: 24801
-ret: 0         st: 1 flags:0  ts: 2.680000
-ret:-1
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
+ret:-1         st: 1 flags:0  ts: 2.680000
 ret: 0         st: 1 flags:1  ts: 1.560000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
 ret: 0         st:-1 flags:0  ts: 0.460008
-ret: 0         st: 0 flags:1 dts: 0.480000 pts: NOPTS    pos:   6144 size: 24801
+ret: 0         st: 0 flags:1 dts: 0.840000 pts: 0.960000 pos: 460800 size: 24712
 ret: 0         st:-1 flags:1  ts:-0.645825
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: NOPTS    pos:   6144 size: 24801
+ret: 0         st: 0 flags:1 dts:-0.040000 pts: 0.000000 pos:   6144 size: 24801
diff --git a/tests/ref/seek/lavf_mxf_d10 b/tests/ref/seek/lavf_mxf_d10
index c05870f..4cfe595 100644
--- a/tests/ref/seek/lavf_mxf_d10
+++ b/tests/ref/seek/lavf_mxf_d10
@@ -2,52 +2,52 @@ ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:   6144 size:150000
 ret: 0         st:-1 flags:0  ts:-1.000000
 ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:   6144 size:150000
 ret: 0         st:-1 flags:1  ts: 1.894167
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st: 0 flags:0  ts: 0.800000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.800000 pts: 0.800000 pos:4265984 size:150000
 ret: 0         st: 0 flags:1  ts:-0.320000
 ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:   6144 size:150000
 ret: 0         st: 1 flags:0  ts: 2.560000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st: 1 flags:1  ts: 1.480000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st:-1 flags:0  ts: 0.365002
-ret: 0         st: 0 flags:1 dts: 0.360000 pts: 0.360000 pos:   6144 size:150000
+ret: 0         st: 0 flags:1 dts: 0.360000 pts: 0.360000 pos:1923072 size:150000
 ret: 0         st:-1 flags:1  ts:-0.740831
 ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:   6144 size:150000
 ret: 0         st: 0 flags:0  ts: 2.160000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st: 0 flags:1  ts: 1.040000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st: 1 flags:0  ts:-0.040000
 ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:   6144 size:150000
 ret: 0         st: 1 flags:1  ts: 2.840000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st:-1 flags:0  ts: 1.730004
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st:-1 flags:1  ts: 0.624171
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.640000 pts: 0.640000 pos:3414016 size:150000
 ret: 0         st: 0 flags:0  ts:-0.480000
 ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:   6144 size:150000
 ret: 0         st: 0 flags:1  ts: 2.400000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st: 1 flags:0  ts: 1.320000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st: 1 flags:1  ts: 0.200000
-ret: 0         st: 0 flags:1 dts: 0.200000 pts: 0.200000 pos:   6144 size:150000
+ret: 0         st: 0 flags:1 dts: 0.200000 pts: 0.200000 pos:1071104 size:150000
 ret: 0         st:-1 flags:0  ts:-0.904994
 ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:   6144 size:150000
 ret: 0         st:-1 flags:1  ts: 1.989173
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st: 0 flags:0  ts: 0.880000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.880000 pts: 0.880000 pos:4691968 size:150000
 ret: 0         st: 0 flags:1  ts:-0.240000
 ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:   6144 size:150000
 ret: 0         st: 1 flags:0  ts: 2.680000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st: 1 flags:1  ts: 1.560000
-ret:-1
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
 ret: 0         st:-1 flags:0  ts: 0.460008
-ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos:   6144 size:150000
+ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos:2562048 size:150000
 ret: 0         st:-1 flags:1  ts:-0.645825
 ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:   6144 size:150000



More information about the ffmpeg-cvslog mailing list