Commit message: avformat/mxfdec: Fix av_log context

Fixes: out of array access
Fixes: mxf-crash-1c2e59bf07a34675bfb3ada5e1ec22fa9f38f923

Found-by: Paul Ch <paulcher@icloud.com>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
CVE: CVE-2018-1999014
CWE: CWE-125
Vulnerability description: FFmpeg before commit bab0716c7f4793ec42e05a5aa7e80d82a0dd4e75 contains an out of array access vulnerability in MXF format demuxer that can result in DoS. This attack appear to be exploitable via specially crafted MXF file which has to be provided as input. This vulnerability appears to have been fixed in bab0716c7f4793ec42e05a5aa7e80d82a0dd4e75 and later.

Project: FFmpeg

static int find_body_sid_by_offset(MXFContext *mxf, int64_t offset)
{
    int a, b, m;
    int64_t this_partition;

    a = -1;
    b = mxf->partitions_count;

    while (b - a > 1) {
        m         = (a + b) >> 1;
        this_partition = mxf->partitions[m].this_partition;
        if (this_partition <= offset)
            a = m;
        else
            b = m;
    }

    if (a == -1)
        return 0;
    return mxf->partitions[a].body_sid;
}

Project: FFmpeg

static int64_t klv_decode_ber_length(AVIOContext *pb)
{
    uint64_t size = avio_r8(pb);
    if (size & 0x80) { /* long form */
        int bytes_num = size & 0x7f;
        /* SMPTE 379M 5.3.4 guarantee that bytes_num must not exceed 8 bytes */
        if (bytes_num > 8)
            return AVERROR_INVALIDDATA;
        size = 0;
        while (bytes_num--)
            size = size << 8 | avio_r8(pb);
    }
    if (size > INT64_MAX)
        return AVERROR_INVALIDDATA;
    return size;
}

Project: FFmpeg

static int klv_read_packet(KLVPacket *klv, AVIOContext *pb)
{
    int64_t length, pos;
    if (!mxf_read_sync(pb, mxf_klv_key, 4))
        return AVERROR_INVALIDDATA;
    klv->offset = avio_tell(pb) - 4;
    memcpy(klv->key, mxf_klv_key, 4);
    avio_read(pb, klv->key + 4, 12);
    length = klv_decode_ber_length(pb);
    if (length < 0)
        return length;
    klv->length = length;
    pos = avio_tell(pb);
    if (pos > INT64_MAX - length)
        return AVERROR_INVALIDDATA;
    klv->next_klv = pos + length;
    return 0;
}

Project: FFmpeg

static void mxf_compute_edit_units_per_packet(MXFContext *mxf, AVStream *st)
{
    MXFTrack *track = st->priv_data;
    MXFIndexTable *t;

    if (!track)
        return;
    track->edit_units_per_packet = 1;
    if (track->wrapping != ClipWrapped)
        return;

    t = mxf_find_index_table(mxf, track->index_sid);

    /* expect PCM with exactly one index table segment and a small (< 32) EUBC */
    if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO         ||
        !is_pcm(st->codecpar->codec_id)                        ||
        !t                                                     ||
        t->nb_segments != 1                                    ||
        t->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. */
    track->edit_units_per_packet = FFMAX(1, track->edit_rate.num / track->edit_rate.den / 25);
}

Project: FFmpeg

static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_table)
{
    int i, j, x;
    int8_t max_temporal_offset = -128;
    uint8_t *flags;

    /* 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) {
            index_table->nb_ptses = 0;
            return 0;                               /* no TemporalOffsets */
        }

        if (s->index_duration > INT_MAX - index_table->nb_ptses) {
            index_table->nb_ptses = 0;
            av_log(mxf->fc, AV_LOG_ERROR, "ignoring IndexSID %d, duration is too large\n", s->index_sid);
            return 0;
        }

        index_table->nb_ptses += s->index_duration;
    }

    /* paranoid check */
    if (index_table->nb_ptses <= 0)
        return 0;

    if (!(index_table->ptses      = av_calloc(index_table->nb_ptses, sizeof(int64_t))) ||
        !(index_table->fake_index = av_calloc(index_table->nb_ptses, sizeof(AVIndexEntry))) ||
        !(index_table->offsets    = av_calloc(index_table->nb_ptses, sizeof(int8_t))) ||
        !(flags                   = av_calloc(index_table->nb_ptses, sizeof(uint8_t)))) {
        av_freep(&index_table->ptses);
        av_freep(&index_table->fake_index);
        av_freep(&index_table->offsets);
        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;
        int n = s->nb_index_entries;

        if (s->nb_index_entries == 2 * s->index_duration + 1) {
            index_delta = 2;    /* Avid index */
            /* ignore the last entry - it's the size of the essence container */
            n--;
        }

        for (j = 0; j < n; j += index_delta, x++) {
            int offset = s->temporal_offset_entries[j] / index_delta;
            int index  = x + offset;

            if (x >= index_table->nb_ptses) {
                av_log(mxf->fc, AV_LOG_ERROR,
                       "x >= nb_ptses - IndexEntryCount %i < IndexDuration %"PRId64"?\n",
                       s->nb_index_entries, s->index_duration);
                break;
            }

            flags[x] = !(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->offsets[x] = offset;
            index_table->ptses[index] = x;
            max_temporal_offset = FFMAX(max_temporal_offset, offset);
        }
    }

    /* calculate the fake index table in display order */
    for (x = 0; x < index_table->nb_ptses; x++) {
        index_table->fake_index[x].timestamp = x;
        if (index_table->ptses[x] != AV_NOPTS_VALUE)
            index_table->fake_index[index_table->ptses[x]].flags = flags[x];
    }
    av_freep(&flags);

    index_table->first_dts = -max_temporal_offset;

    return 0;
}

Project: FFmpeg

static int64_t mxf_compute_sample_count(MXFContext *mxf, AVStream *st,
                                        int64_t edit_unit)
{
    int i, total = 0, size = 0;
    MXFTrack *track = st->priv_data;
    AVRational time_base = av_inv_q(track->edit_rate);
    AVRational sample_rate = av_inv_q(st->time_base);
    const MXFSamplesPerFrame *spf = NULL;
    int64_t sample_count;

    if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
        return edit_unit;

    if ((sample_rate.num / sample_rate.den) == 48000)
        spf = ff_mxf_get_samples_per_frame(mxf->fc, time_base);
    if (!spf) {
        int remainder = (sample_rate.num * time_base.num) %
                        (time_base.den * sample_rate.den);
        if (remainder)
            av_log(mxf->fc, AV_LOG_WARNING,
                   "seeking detected on stream #%d with time base (%d/%d) and "
                   "sample rate (%d/%d), audio pts won't be accurate.\n",
                   st->index, time_base.num, time_base.den,
                   sample_rate.num, sample_rate.den);
        return av_rescale_q(edit_unit, sample_rate, track->edit_rate);
    }

    while (spf->samples_per_frame[size]) {
        total += spf->samples_per_frame[size];
        size++;
    }

    av_assert2(size);

    sample_count = (edit_unit / size) * (uint64_t)total;
    for (i = 0; i < edit_unit % size; i++) {
        sample_count += spf->samples_per_frame[i];
    }

    return sample_count;
}

Project: FFmpeg

static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_table, int64_t edit_unit, AVRational edit_rate, int64_t *edit_unit_out, int64_t *offset_out, MXFPartition **partition_out, int nag)
{
    int i;
    int64_t offset_temp = 0;

    edit_unit = av_rescale_q(edit_unit, index_table->segments[0]->index_edit_rate, edit_rate);

    for (i = 0; i < index_table->nb_segments; i++) {
        MXFIndexTableSegment *s = index_table->segments[i];

        edit_unit = FFMAX(edit_unit, s->index_start_position);  /* clamp if trying to seek before start */

        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;
                }

                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;
            }

            if (edit_unit_out)
                *edit_unit_out = av_rescale_q(edit_unit, edit_rate, s->index_edit_rate);

            return mxf_absolute_bodysid_offset(mxf, index_table->body_sid, offset_temp, offset_out, partition_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;
        }
    }

    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;
}

Project: FFmpeg

static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int index_sid)
{
    int i;
    for (i = 0; i < mxf->nb_index_tables; i++)
        if (mxf->index_tables[i].index_sid == index_sid)
            return &mxf->index_tables[i];
    return NULL;
}

Project: FFmpeg

static int mxf_get_next_track_edit_unit(MXFContext *mxf, MXFTrack *track, int64_t current_offset, int64_t *edit_unit_out)
{
    int64_t a, b, m, offset;
    MXFIndexTable *t = mxf_find_index_table(mxf, track->index_sid);

    if (!t || track->original_duration <= 0)
        return -1;

    a = -1;
    b = track->original_duration;

    while (b - a > 1) {
        m = (a + b) >> 1;
        if (mxf_edit_unit_absolute_offset(mxf, t, m, track->edit_rate, NULL, &offset, NULL, 0) < 0)
            return -1;
        if (offset < current_offset)
            a = m;
        else
            b = m;
    }

    *edit_unit_out = b;

    return 0;
}

Project: FFmpeg

static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segments, MXFIndexTableSegment ***sorted_segments)
{
    int i, j, nb_segments = 0;
    MXFIndexTableSegment **unsorted_segments;
    int last_body_sid = -1, last_index_sid = -1, last_index_start = -1;

    /* count number of segments, allocate arrays and copy unsorted segments */
    for (i = 0; i < mxf->metadata_sets_count; i++)
        if (mxf->metadata_sets[i]->type == IndexTableSegment)
            nb_segments++;

    if (!nb_segments)
        return AVERROR_INVALIDDATA;

    if (!(unsorted_segments = av_calloc(nb_segments, sizeof(*unsorted_segments))) ||
        !(*sorted_segments  = av_calloc(nb_segments, sizeof(**sorted_segments)))) {
        av_freep(sorted_segments);
        av_free(unsorted_segments);
        return AVERROR(ENOMEM);
    }

    for (i = j = 0; i < mxf->metadata_sets_count; i++)
        if (mxf->metadata_sets[i]->type == IndexTableSegment)
            unsorted_segments[j++] = (MXFIndexTableSegment*)mxf->metadata_sets[i];

    *nb_sorted_segments = 0;

    /* sort segments by {BodySID, IndexSID, IndexStartPosition}, remove duplicates while we're at it */
    for (i = 0; i < nb_segments; i++) {
        int best = -1, best_body_sid = -1, best_index_sid = -1, best_index_start = -1;
        uint64_t best_index_duration = 0;

        for (j = 0; j < nb_segments; j++) {
            MXFIndexTableSegment *s = unsorted_segments[j];

            /* Require larger BosySID, IndexSID or IndexStartPosition then the previous entry. This removes duplicates.
             * We want the smallest values for the keys than what we currently have, unless this is the first such entry this time around.
             * If we come across an entry with the same IndexStartPosition but larger IndexDuration, then we'll prefer it over the one we currently have.
             */
            if ((i == 0 ||
                 s->body_sid >  last_body_sid ||
                 s->body_sid == last_body_sid && s->index_sid >  last_index_sid ||
                 s->body_sid == last_body_sid && s->index_sid == last_index_sid && s->index_start_position > last_index_start) &&
                (best == -1 ||
                 s->body_sid <  best_body_sid ||
                 s->body_sid == best_body_sid && s->index_sid <  best_index_sid ||
                 s->body_sid == best_body_sid && s->index_sid == best_index_sid && s->index_start_position <  best_index_start ||
                 s->body_sid == best_body_sid && s->index_sid == best_index_sid && s->index_start_position == best_index_start && s->index_duration > best_index_duration)) {
                best             = j;
                best_body_sid    = s->body_sid;
                best_index_sid   = s->index_sid;
                best_index_start = s->index_start_position;
                best_index_duration = s->index_duration;
            }
        }

        /* no suitable entry found -> we're done */
        if (best == -1)
            break;

        (*sorted_segments)[(*nb_sorted_segments)++] = unsorted_segments[best];
        last_body_sid    = best_body_sid;
        last_index_sid   = best_index_sid;
        last_index_start = best_index_start;
    }

    av_free(unsorted_segments);

    return 0;
}

Project: FFmpeg

static int mxf_get_stream_index(AVFormatContext *s, KLVPacket *klv, int body_sid)
{
    int i;

    for (i = 0; i < s->nb_streams; i++) {
        MXFTrack *track = s->streams[i]->priv_data;
        /* SMPTE 379M 7.3 */
        if (track && (!body_sid || !track->body_sid || track->body_sid == body_sid) && !memcmp(klv->key + sizeof(mxf_essence_element_key), track->track_number, sizeof(track->track_number)))
            return i;
    }
    /* return 0 if only one stream, for OP Atom files with 0 as track number */
    return s->nb_streams == 1 ? 0 : -1;
}

Project: FFmpeg

static MXFWrappingScheme mxf_get_wrapping_by_body_sid(AVFormatContext *s, int body_sid)
{
    for (int i = 0; i < s->nb_streams; i++) {
        MXFTrack *track = s->streams[i]->priv_data;
        if (track && track->body_sid == body_sid && track->wrapping != UnknownWrapped)
            return track->wrapping;
    }
    return UnknownWrapped;
}

Project: FFmpeg

static MXFWrappingScheme mxf_get_wrapping_kind(UID *essence_container_ul)
{
    int val;
    const MXFCodecUL *codec_ul;

    codec_ul = mxf_get_codec_ul(mxf_picture_essence_container_uls, essence_container_ul);
    if (!codec_ul->uid[0])
        codec_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul);
    if (!codec_ul->uid[0])
        codec_ul = mxf_get_codec_ul(mxf_data_essence_container_uls, essence_container_ul);
    if (!codec_ul->uid[0] || !codec_ul->wrapping_indicator_pos)
        return UnknownWrapped;

    val = (*essence_container_ul)[codec_ul->wrapping_indicator_pos];
    switch (codec_ul->wrapping_indicator_type) {
        case RawVWrap:
            val = val % 4;
            break;
        case RawAWrap:
            if (val == 0x03 || val == 0x04)
                val -= 0x02;
            break;
        case D10D11Wrap:
            if (val == 0x02)
                val = 0x01;
            break;
    }
    if (val == 0x01)
        return FrameWrapped;
    if (val == 0x02)
        return ClipWrapped;
    return UnknownWrapped;
}

Project: FFmpeg

static int mxf_handle_missing_index_segment(MXFContext *mxf, AVStream *st)
{
    MXFTrack *track = st->priv_data;
    MXFIndexTableSegment *segment = NULL;
    MXFPartition *p = NULL;
    int essence_partition_count = 0;
    int edit_unit_byte_count = 0;
    int i, ret;

    if (!track || track->wrapping != ClipWrapped)
        return 0;

    /* check if track already has an IndexTableSegment */
    for (i = 0; i < mxf->metadata_sets_count; i++) {
        if (mxf->metadata_sets[i]->type == IndexTableSegment) {
            MXFIndexTableSegment *s = (MXFIndexTableSegment*)mxf->metadata_sets[i];
            if (s->body_sid == track->body_sid)
                return 0;
        }
    }

    /* find the essence partition */
    for (i = 0; i < mxf->partitions_count; i++) {
        /* BodySID == 0 -> no essence */
        if (mxf->partitions[i].body_sid != track->body_sid)
            continue;

        p = &mxf->partitions[i];
        essence_partition_count++;
    }

    /* only handle files with a single essence partition */
    if (essence_partition_count != 1)
        return 0;

    if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && is_pcm(st->codecpar->codec_id)) {
        edit_unit_byte_count = (av_get_bits_per_sample(st->codecpar->codec_id) * st->codecpar->channels) >> 3;
    } else if (st->duration > 0 && p->first_essence_klv.length > 0 && p->first_essence_klv.length % st->duration == 0) {
        edit_unit_byte_count = p->first_essence_klv.length / st->duration;
    }

    if (edit_unit_byte_count <= 0)
        return 0;

    av_log(mxf->fc, AV_LOG_WARNING, "guessing index for stream %d using edit unit byte count %d\n", st->index, edit_unit_byte_count);

    if (!(segment = av_mallocz(sizeof(*segment))))
        return AVERROR(ENOMEM);

    if ((ret = mxf_add_metadata_set(mxf, segment))) {
        mxf_free_metadataset((MXFMetadataSet**)&segment, 1);
        return ret;
    }

    /* Make sure we have nonzero unique index_sid, body_sid will be ok, because
     * using the same SID for index is forbidden in MXF. */
    if (!track->index_sid)
        track->index_sid = track->body_sid;

    segment->type = IndexTableSegment;
    /* stream will be treated as small EditUnitByteCount */
    segment->edit_unit_byte_count = edit_unit_byte_count;
    segment->index_start_position = 0;
    segment->index_duration = st->duration;
    segment->index_edit_rate = av_inv_q(st->time_base);
    segment->index_sid = track->index_sid;
    segment->body_sid = p->body_sid;
    return 0;
}

Project: FFmpeg

static int mxf_parse_physical_source_package(MXFContext *mxf, MXFTrack *source_track, AVStream *st)
{
    MXFPackage *physical_package = NULL;
    MXFTrack *physical_track = NULL;
    MXFStructuralComponent *sourceclip = NULL;
    MXFTimecodeComponent *mxf_tc = NULL;
    int i, j, k;
    AVTimecode tc;
    int flags;
    int64_t start_position;

    for (i = 0; i < source_track->sequence->structural_components_count; i++) {
        sourceclip = mxf_resolve_strong_ref(mxf, &source_track->sequence->structural_components_refs[i], SourceClip);
        if (!sourceclip)
            continue;

        if (!(physical_package = mxf_resolve_source_package(mxf, sourceclip->source_package_ul, sourceclip->source_package_uid)))
            break;

        mxf_add_umid_metadata(&st->metadata, "reel_umid", physical_package);

        /* the name of physical source package is name of the reel or tape */
        if (physical_package->name && physical_package->name[0])
            av_dict_set(&st->metadata, "reel_name", physical_package->name, 0);

        /* the source timecode is calculated by adding the start_position of the sourceclip from the file source package track
         * to the start_frame of the timecode component located on one of the tracks of the physical source package.
         */
        for (j = 0; j < physical_package->tracks_count; j++) {
            if (!(physical_track = mxf_resolve_strong_ref(mxf, &physical_package->tracks_refs[j], Track))) {
                av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track strong ref\n");
                continue;
            }

            if (!(physical_track->sequence = mxf_resolve_strong_ref(mxf, &physical_track->sequence_ref, Sequence))) {
                av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n");
                continue;
            }

        if (physical_track->edit_rate.num <= 0 ||
            physical_track->edit_rate.den <= 0) {
            av_log(mxf->fc, AV_LOG_WARNING,
                   "Invalid edit rate (%d/%d) found on structural"
                   " component #%d, defaulting to 25/1\n",
                   physical_track->edit_rate.num,
                   physical_track->edit_rate.den, i);
            physical_track->edit_rate = (AVRational){25, 1};
        }

            for (k = 0; k < physical_track->sequence->structural_components_count; k++) {
                if (!(mxf_tc = mxf_resolve_timecode_component(mxf, &physical_track->sequence->structural_components_refs[k])))
                    continue;

                flags = mxf_tc->drop_frame == 1 ? AV_TIMECODE_FLAG_DROPFRAME : 0;
                /* scale sourceclip start_position to match physical track edit rate */
                start_position = av_rescale_q(sourceclip->start_position,
                                              physical_track->edit_rate,
                                              source_track->edit_rate);

                if (av_timecode_init(&tc, mxf_tc->rate, flags, start_position + mxf_tc->start_frame, mxf->fc) == 0) {
                    mxf_add_timecode_metadata(&st->metadata, "timecode", &tc);
                    return 0;
                }
            }
        }
    }

    return 0;
}

Project: FFmpeg

static int mxf_read_close(AVFormatContext *s)
{
    MXFContext *mxf = s->priv_data;
    int i;

    av_freep(&mxf->packages_refs);
    av_freep(&mxf->essence_container_data_refs);

    for (i = 0; i < s->nb_streams; i++)
        s->streams[i]->priv_data = NULL;

    for (i = 0; i < mxf->metadata_sets_count; i++) {
        mxf_free_metadataset(mxf->metadata_sets + i, 1);
    }
    av_freep(&mxf->partitions);
    av_freep(&mxf->metadata_sets);
    av_freep(&mxf->aesc);
    av_freep(&mxf->local_tags);

    if (mxf->index_tables) {
        for (i = 0; i < mxf->nb_index_tables; i++) {
            av_freep(&mxf->index_tables[i].segments);
            av_freep(&mxf->index_tables[i].ptses);
            av_freep(&mxf->index_tables[i].fake_index);
            av_freep(&mxf->index_tables[i].offsets);
        }
    }
    av_freep(&mxf->index_tables);

    return 0;
}

Project: FFmpeg

static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
    MXFContext *mxf = arg;
    switch (tag) {
    case 0x1901:
        if (mxf->packages_refs)
            av_log(mxf->fc, AV_LOG_VERBOSE, "Multiple packages_refs\n");
        av_free(mxf->packages_refs);
        return mxf_read_strong_ref_array(pb, &mxf->packages_refs, &mxf->packages_count);
    case 0x1902:
        av_free(mxf->essence_container_data_refs);
        return mxf_read_strong_ref_array(pb, &mxf->essence_container_data_refs, &mxf->essence_container_data_count);
    }
    return 0;
}

Project: FFmpeg

static int mxf_read_header(AVFormatContext *s)
{
    MXFContext *mxf = s->priv_data;
    KLVPacket klv;
    int64_t essence_offset = 0;
    int ret;

    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);

    mxf_read_random_index_pack(s);

    while (!avio_feof(s->pb)) {
        const MXFMetadataReadTableEntry *metadata;

        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_log(s, AV_LOG_TRACE, "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) ||
            IS_KLV_KEY(klv.key, mxf_canopus_essence_element_key) ||
            IS_KLV_KEY(klv.key, mxf_avid_essence_element_key) ||
            IS_KLV_KEY(klv.key, mxf_system_item_key_cp) ||
            IS_KLV_KEY(klv.key, mxf_system_item_key_gc)) {

            if (!mxf->current_partition) {
                av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to first PartitionPack\n");
                return AVERROR_INVALIDDATA;
            }

            if (!mxf->current_partition->first_essence_klv.offset)
                mxf->current_partition->first_essence_klv = klv;

            if (!essence_offset)
                essence_offset = klv.offset;

            /* seek to footer, previous partition or stop */
            if (mxf_parse_handle_essence(mxf) <= 0)
                break;
            continue;
        } else if (mxf_is_partition_pack_key(klv.key) && mxf->current_partition) {
            /* next partition pack - keep going, seek to previous partition or stop */
            if(mxf_parse_handle_partition_or_eof(mxf) <= 0)
                break;
            else if (mxf->parsing_backward)
                continue;
            /* we're still parsing forward. proceed to parsing this partition pack */
        }

        for (metadata = mxf_metadata_read_table; metadata->read; metadata++) {
            if (IS_KLV_KEY(klv.key, metadata->key)) {
                if ((ret = mxf_parse_klv(mxf, klv, metadata->read, metadata->ctx_size, metadata->type)) < 0)
                    goto fail;
                break;
            }
        }
        if (!metadata->read) {
            av_log(s, AV_LOG_VERBOSE, "Dark key " PRIxUID "\n",
                            UID_ARG(klv.key));
            avio_skip(s->pb, klv.length);
        }
    }
    /* FIXME avoid seek */
    if (!essence_offset)  {
        av_log(s, AV_LOG_ERROR, "no essence\n");
        ret = AVERROR_INVALIDDATA;
        goto fail;
    }
    avio_seek(s->pb, essence_offset, SEEK_SET);

    /* 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)
        goto fail;

    for (int i = 0; i < s->nb_streams; i++)
        mxf_handle_missing_index_segment(mxf, s->streams[i]);

    if ((ret = mxf_compute_index_tables(mxf)) < 0)
        goto fail;

    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 && (s->error_recognition & AV_EF_EXPLODE)) {
        av_log(mxf->fc, AV_LOG_ERROR, "cannot demux OPAtom without an index\n");
        ret = AVERROR_INVALIDDATA;
        goto fail;
    }

    mxf_compute_essence_containers(s);

    for (int i = 0; i < s->nb_streams; i++)
        mxf_compute_edit_units_per_packet(mxf, s->streams[i]);

    return 0;
fail:
    mxf_read_close(s);

    return ret;
}

Project: FFmpeg

static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    KLVPacket klv;
    MXFContext *mxf = s->priv_data;
    int ret;

    while (1) {
        int64_t max_data_size;
        int64_t pos = avio_tell(s->pb);

        if (pos < mxf->current_klv_data.next_klv - mxf->current_klv_data.length || pos >= mxf->current_klv_data.next_klv) {
            mxf->current_klv_data = (KLVPacket){{0}};
            ret = klv_read_packet(&klv, s->pb);
            if (ret < 0)
                break;
            max_data_size = klv.length;
            pos = klv.next_klv - klv.length;
            PRINT_KEY(s, "read packet", klv.key);
            av_log(s, AV_LOG_TRACE, "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 ret;
                }
                return 0;
            }
        } else {
            klv = mxf->current_klv_data;
            max_data_size = klv.next_klv - pos;
        }
        if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
            IS_KLV_KEY(klv.key, mxf_canopus_essence_element_key) ||
            IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) {
            int body_sid = find_body_sid_by_offset(mxf, klv.offset);
            int index = mxf_get_stream_index(s, &klv, body_sid);
            int64_t next_ofs;
            AVStream *st;
            MXFTrack *track;

            if (index < 0) {
                av_log(s, AV_LOG_ERROR,
                       "error getting stream index %"PRIu32"\n",
                       AV_RB32(klv.key + 12));
                goto skip;
            }

            st = s->streams[index];
            track = st->priv_data;

            if (s->streams[index]->discard == AVDISCARD_ALL)
                goto skip;

            next_ofs = mxf_set_current_edit_unit(mxf, st, pos, 1);

            if (track->wrapping != FrameWrapped) {
                int64_t size;

                if (next_ofs <= 0) {
                    if (klv.next_klv - klv.length == pos && max_data_size > MXF_MAX_CHUNK_SIZE) {
                        st->need_parsing = AVSTREAM_PARSE_FULL;
                        avpriv_request_sample(s, "Huge KLV without proper index in non-frame wrapped essence");
                    }
                    size = FFMIN(max_data_size, MXF_MAX_CHUNK_SIZE);
                } else {
                    if ((size = next_ofs - pos) <= 0) {
                        av_log(s, AV_LOG_ERROR, "bad size: %"PRId64"\n", size);
                        ret = AVERROR_INVALIDDATA;
                        goto skip;
                    }
                    if (size > max_data_size)
                        size = max_data_size;
                }

                mxf->current_klv_data = klv;
                klv.offset = pos;
                klv.length = size;
                klv.next_klv = klv.offset + klv.length;
            }

            /* check for 8 channels AES3 element */
            if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) {
                ret = mxf_get_d10_aes3_packet(s->pb, s->streams[index],
                                              pkt, klv.length);
                if (ret < 0) {
                    av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n");
                    mxf->current_klv_data = (KLVPacket){{0}};
                    return ret;
                }
            } else {
                ret = av_get_packet(s->pb, pkt, klv.length);
                if (ret < 0) {
                    mxf->current_klv_data = (KLVPacket){{0}};
                    return ret;
                }
            }
            pkt->stream_index = index;
            pkt->pos = klv.offset;

            ret = mxf_set_pts(mxf, st, pkt);
            if (ret < 0) {
                mxf->current_klv_data = (KLVPacket){{0}};
                return ret;
            }

            /* seek for truncated packets */
            avio_seek(s->pb, klv.next_klv, SEEK_SET);

            return 0;
        } else {
        skip:
            avio_skip(s->pb, max_data_size);
            mxf->current_klv_data = (KLVPacket){{0}};
        }
    }
    return avio_feof(s->pb) ? AVERROR_EOF : ret;
}

Project: FFmpeg

static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
    MXFContext *mxf = arg;
    MXFPartition *partition, *tmp_part;
    UID op;
    uint64_t footer_partition;
    uint32_t nb_essence_containers;

    if (mxf->partitions_count >= INT_MAX / 2)
        return AVERROR_INVALIDDATA;

    tmp_part = av_realloc_array(mxf->partitions, mxf->partitions_count + 1, sizeof(*mxf->partitions));
    if (!tmp_part)
        return AVERROR(ENOMEM);
    mxf->partitions = tmp_part;

    if (mxf->parsing_backward) {
        /* insert the new partition pack in the middle
         * this makes the entries in mxf->partitions sorted by offset */
        memmove(&mxf->partitions[mxf->last_forward_partition+1],
                &mxf->partitions[mxf->last_forward_partition],
                (mxf->partitions_count - mxf->last_forward_partition)*sizeof(*mxf->partitions));
        partition = mxf->current_partition = &mxf->partitions[mxf->last_forward_partition];
    } else {
        mxf->last_forward_partition++;
        partition = mxf->current_partition = &mxf->partitions[mxf->partitions_count];
    }

    memset(partition, 0, sizeof(*partition));
    mxf->partitions_count++;
    partition->pack_length = avio_tell(pb) - klv_offset + size;
    partition->pack_ofs    = klv_offset;

    switch(uid[13]) {
    case 2:
        partition->type = Header;
        break;
    case 3:
        partition->type = BodyPartition;
        break;
    case 4:
        partition->type = Footer;
        break;
    default:
        av_log(mxf->fc, AV_LOG_ERROR, "unknown partition type %i\n", uid[13]);
        return AVERROR_INVALIDDATA;
    }

    /* consider both footers to be closed (there is only Footer and CompleteFooter) */
    partition->closed = partition->type == Footer || !(uid[14] & 1);
    partition->complete = uid[14] > 2;
    avio_skip(pb, 4);
    partition->kag_size = avio_rb32(pb);
    partition->this_partition = avio_rb64(pb);
    partition->previous_partition = avio_rb64(pb);
    footer_partition = avio_rb64(pb);
    partition->header_byte_count = avio_rb64(pb);
    partition->index_byte_count = avio_rb64(pb);
    partition->index_sid = avio_rb32(pb);
    partition->body_offset = avio_rb64(pb);
    partition->body_sid = avio_rb32(pb);
    if (avio_read(pb, op, sizeof(UID)) != sizeof(UID)) {
        av_log(mxf->fc, AV_LOG_ERROR, "Failed reading UID\n");
        return AVERROR_INVALIDDATA;
    }
    nb_essence_containers = avio_rb32(pb);

    if (partition->this_partition &&
        partition->previous_partition == partition->this_partition) {
        av_log(mxf->fc, AV_LOG_ERROR,
               "PreviousPartition equal to ThisPartition %"PRIx64"\n",
               partition->previous_partition);
        /* override with the actual previous partition offset */
        if (!mxf->parsing_backward && mxf->last_forward_partition > 1) {
            MXFPartition *prev =
                mxf->partitions + mxf->last_forward_partition - 2;
            partition->previous_partition = prev->this_partition;
        }
        /* if no previous body partition are found point to the header
         * partition */
        if (partition->previous_partition == partition->this_partition)
            partition->previous_partition = 0;
        av_log(mxf->fc, AV_LOG_ERROR,
               "Overriding PreviousPartition with %"PRIx64"\n",
               partition->previous_partition);
    }

    /* some files don't have FooterPartition set in every partition */
    if (footer_partition) {
        if (mxf->footer_partition && mxf->footer_partition != footer_partition) {
            av_log(mxf->fc, AV_LOG_ERROR,
                   "inconsistent FooterPartition value: %"PRIu64" != %"PRIu64"\n",
                   mxf->footer_partition, footer_partition);
        } else {
            mxf->footer_partition = footer_partition;
        }
    }

    av_log(mxf->fc, AV_LOG_TRACE,
            "PartitionPack: ThisPartition = 0x%"PRIX64
            ", PreviousPartition = 0x%"PRIX64", "
            "FooterPartition = 0x%"PRIX64", IndexSID = %i, BodySID = %i\n",
            partition->this_partition,
            partition->previous_partition, footer_partition,
            partition->index_sid, partition->body_sid);

    /* sanity check PreviousPartition if set */
    if (partition->previous_partition &&
        mxf->run_in + partition->previous_partition >= klv_offset) {
        av_log(mxf->fc, AV_LOG_ERROR,
               "PreviousPartition points to this partition or forward\n");
        return AVERROR_INVALIDDATA;
    }

    if      (op[12] == 1 && op[13] == 1) mxf->op = OP1a;
    else if (op[12] == 1 && op[13] == 2) mxf->op = OP1b;
    else if (op[12] == 1 && op[13] == 3) mxf->op = OP1c;
    else if (op[12] == 2 && op[13] == 1) mxf->op = OP2a;
    else if (op[12] == 2 && op[13] == 2) mxf->op = OP2b;
    else if (op[12] == 2 && op[13] == 3) mxf->op = OP2c;
    else if (op[12] == 3 && op[13] == 1) mxf->op = OP3a;
    else if (op[12] == 3 && op[13] == 2) mxf->op = OP3b;
    else if (op[12] == 3 && op[13] == 3) mxf->op = OP3c;
    else if (op[12] == 64&& op[13] == 1) mxf->op = OPSONYOpt;
    else if (op[12] == 0x10) {
        /* SMPTE 390m: "There shall be exactly one essence container"
         * The following block deals with files that violate this, namely:
         * 2011_DCPTEST_24FPS.V.mxf - two ECs, OP1a
         * abcdefghiv016f56415e.mxf - zero ECs, OPAtom, output by Avid AirSpeed */
        if (nb_essence_containers != 1) {
            MXFOP op = nb_essence_containers ? OP1a : OPAtom;

            /* only nag once */
            if (!mxf->op)
                av_log(mxf->fc, AV_LOG_WARNING,
                       "\"OPAtom\" with %"PRIu32" ECs - assuming %s\n",
                       nb_essence_containers,
                       op == OP1a ? "OP1a" : "OPAtom");

            mxf->op = op;
        } else
            mxf->op = OPAtom;
    } else {
        av_log(mxf->fc, AV_LOG_ERROR, "unknown operational pattern: %02xh %02xh - guessing OP1a\n", op[12], op[13]);
        mxf->op = OP1a;
    }

    if (partition->kag_size <= 0 || partition->kag_size > (1 << 20)) {
        av_log(mxf->fc, AV_LOG_WARNING, "invalid KAGSize %"PRId32" - guessing ",
               partition->kag_size);

        if (mxf->op == OPSONYOpt)
            partition->kag_size = 512;
        else
            partition->kag_size = 1;

        av_log(mxf->fc, AV_LOG_WARNING, "%"PRId32"\n", partition->kag_size);
    }

    return 0;
}

Project: FFmpeg
File: mxfdec.c

static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
    MXFContext *mxf = arg;
    int item_num = avio_rb32(pb);
    int item_len = avio_rb32(pb);

    if (item_len != 18) {
        avpriv_request_sample(pb, "Primer pack item length %d", item_len);
        return AVERROR_PATCHWELCOME;
    }
    if (item_num > 65536 || item_num < 0) {
        av_log(mxf->fc, AV_LOG_ERROR, "item_num %d is too large\n", item_num);
        return AVERROR_INVALIDDATA;
    }
    if (mxf->local_tags)
        av_log(mxf->fc, AV_LOG_VERBOSE, "Multiple primer packs\n");
    av_free(mxf->local_tags);
    mxf->local_tags_count = 0;
    mxf->local_tags = av_calloc(item_num, item_len);
    if (!mxf->local_tags)
        return AVERROR(ENOMEM);
    mxf->local_tags_count = item_num;
    avio_read(pb, mxf->local_tags, item_num*item_len);
    return 0;
}

Project: FFmpeg

static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags)
{
    AVStream *st = s->streams[stream_index];
    int64_t seconds;
    MXFContext* mxf = s->priv_data;
    int64_t seekpos;
    int i, ret;
    MXFIndexTable *t;
    MXFTrack *source_track = st->priv_data;

    if (!source_track)
        return 0;

    /* if audio then truncate sample_time to EditRate */
    if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        sample_time = av_rescale_q(sample_time, st->time_base,
                                   av_inv_q(source_track->edit_rate));

    if (mxf->nb_index_tables <= 0) {
        if (!s->bit_rate)
            return AVERROR_INVALIDDATA;
        if (sample_time < 0)
            sample_time = 0;
        seconds = av_rescale(sample_time, st->time_base.num, st->time_base.den);

        seekpos = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET);
        if (seekpos < 0)
            return seekpos;

        ff_update_cur_dts(s, st, sample_time);
        mxf->current_klv_data = (KLVPacket){{0}};
    } else {
        MXFPartition *partition;

        t = &mxf->index_tables[0];
        if (t->index_sid != source_track->index_sid) {
            /* If the first index table does not belong to the stream, then find a stream which does belong to the index table */
            for (i = 0; i < s->nb_streams; i++) {
                MXFTrack *new_source_track = s->streams[i]->priv_data;
                if (new_source_track && new_source_track->index_sid == t->index_sid) {
                    sample_time = av_rescale_q(sample_time, new_source_track->edit_rate, source_track->edit_rate);
                    source_track = new_source_track;
                    st = s->streams[i];
                    break;
                }
            }
            if (i == s->nb_streams)
                return AVERROR_INVALIDDATA;
        }

        /* 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) {
            /* The first frames may not be keyframes in presentation order, so
             * we have to advance the target to be able to find the first
             * keyframe backwards... */
            if (!(flags & AVSEEK_FLAG_ANY) &&
                (flags & AVSEEK_FLAG_BACKWARD) &&
                t->ptses[0] != AV_NOPTS_VALUE &&
                sample_time < t->ptses[0] &&
                (t->fake_index[t->ptses[0]].flags & AVINDEX_KEYFRAME))
                sample_time = t->ptses[0];

            /* 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;
            /* get the stored order index from the display order index */
            sample_time += t->offsets[sample_time];
        } else {
            /* no IndexEntryArray (one or more CBR segments)
             * make sure we don't seek past the end */
            sample_time = FFMIN(sample_time, source_track->original_duration - 1);
        }

        if (source_track->wrapping == UnknownWrapped)
            av_log(mxf->fc, AV_LOG_WARNING, "attempted seek in an UnknownWrapped essence\n");

        if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, source_track->edit_rate, &sample_time, &seekpos, &partition, 1)) < 0)
            return ret;

        ff_update_cur_dts(s, st, sample_time);
        if (source_track->wrapping == ClipWrapped) {
            KLVPacket klv = partition->first_essence_klv;
            if (seekpos < klv.next_klv - klv.length || seekpos >= klv.next_klv) {
                av_log(mxf->fc, AV_LOG_ERROR, "attempted seek out of clip wrapped KLV\n");
                return AVERROR_INVALIDDATA;
            }
            mxf->current_klv_data = klv;
        } else {
            mxf->current_klv_data = (KLVPacket){{0}};
        }
        avio_seek(s->pb, seekpos, SEEK_SET);
    }

    for (i = 0; i < s->nb_streams; i++) {
        AVStream *cur_st = s->streams[i];
        MXFTrack *cur_track = cur_st->priv_data;
        if (cur_track) {
            int64_t track_edit_unit = sample_time;
            if (st != cur_st)
                mxf_get_next_track_edit_unit(mxf, cur_track, seekpos, &track_edit_unit);
            cur_track->sample_count = mxf_compute_sample_count(mxf, cur_st, track_edit_unit);
        }
    }
    return 0;
}

Project: FFmpeg

static MXFStructuralComponent* mxf_resolve_essence_group_choice(MXFContext *mxf, MXFEssenceGroup *essence_group)
{
    MXFStructuralComponent *component = NULL;
    MXFPackage *package = NULL;
    MXFDescriptor *descriptor = NULL;
    int i;

    if (!essence_group || !essence_group->structural_components_count)
        return NULL;

    /* essence groups contains multiple representations of the same media,
       this return the first components with a valid Descriptor typically index 0 */
    for (i =0; i < essence_group->structural_components_count; i++){
        component = mxf_resolve_strong_ref(mxf, &essence_group->structural_components_refs[i], SourceClip);
        if (!component)
            continue;

        if (!(package = mxf_resolve_source_package(mxf, component->source_package_ul, component->source_package_uid)))
            continue;

        descriptor = mxf_resolve_strong_ref(mxf, &package->descriptor_ref, Descriptor);
        if (descriptor)
            return component;
    }
    return NULL;
}

Project: FFmpeg

static MXFPackage* mxf_resolve_source_package(MXFContext *mxf, UID package_ul, UID package_uid)
{
    MXFPackage *package = NULL;
    int i;

    for (i = 0; i < mxf->packages_count; i++) {
        package = mxf_resolve_strong_ref(mxf, &mxf->packages_refs[i], SourcePackage);
        if (!package)
            continue;

        if (!memcmp(package->package_ul, package_ul, 16) && !memcmp(package->package_uid, package_uid, 16))
            return package;
    }
    return NULL;
}

Project: FFmpeg

static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt)
{
    AVCodecParameters *par = st->codecpar;
    MXFTrack *track = st->priv_data;

    if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
        /* see if we have an index table to derive timestamps from */
        MXFIndexTable *t = mxf_find_index_table(mxf, track->index_sid);

        if (t && track->sample_count < t->nb_ptses) {
            pkt->dts = track->sample_count + t->first_dts;
            pkt->pts = t->ptses[track->sample_count];
        } else if (track->intra_only) {
            /* intra-only -> PTS = EditUnit.
             * let utils.c figure out DTS since it can be < PTS if low_delay = 0 (Sony IMX30) */
            pkt->pts = track->sample_count;
        }
        track->sample_count++;
    } else if (par->codec_type == AVMEDIA_TYPE_AUDIO) {
        int ret = mxf_set_audio_pts(mxf, par, pkt);
        if (ret < 0)
            return ret;
    } else if (track) {
        track->sample_count++;
    }
    return 0;
}