From 00a2652df3bf25a27d174cc67ed508b5317cb115 Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Thu, 10 May 2018 20:30:19 +0200 Subject: avformat/mxfdec: add support for clip wrapped essences Also use common code with opAtom. Fixes ticket #2776. Partially fixes ticket #5671. Fixes ticket #5866. Signed-off-by: Marton Balint --- libavformat/mxfdec.c | 282 ++++++++++++++++++++++++--------------------------- 1 file changed, 131 insertions(+), 151 deletions(-) (limited to 'libavformat/mxfdec.c') diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 8c417ae..c25595a 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -56,6 +56,8 @@ #include "internal.h" #include "mxf.h" +#define MXF_MAX_CHUNK_SIZE (32 << 20) + typedef enum { Header, BodyPartition, @@ -278,13 +280,11 @@ typedef struct MXFContext { int local_tags_count; uint64_t footer_partition; KLVPacket current_klv_data; - int current_klv_index; int run_in; MXFPartition *current_partition; int parsing_backward; int64_t last_forward_tell; int last_forward_partition; - int64_t current_edit_unit; int nb_index_tables; MXFIndexTable *index_tables; } MXFContext; @@ -2429,7 +2429,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) if (ret < 0) return ret; } - if (st->codecpar->codec_type != AVMEDIA_TYPE_DATA && (*essence_container_ul)[15] > 0x01) { + if (st->codecpar->codec_type != AVMEDIA_TYPE_DATA && source_track->wrapping != FrameWrapped) { /* TODO: decode timestamps */ st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS; } @@ -2851,21 +2851,6 @@ static int is_pcm(enum AVCodecID codec_id) return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD; } -static AVStream* mxf_get_opatom_stream(MXFContext *mxf) -{ - int i; - - if (mxf->op != OPAtom) - return NULL; - - for (i = 0; i < mxf->fc->nb_streams; i++) { - if (mxf->fc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_DATA) - continue; - return mxf->fc->streams[i]; - } - return NULL; -} - static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int index_sid) { int i; @@ -3166,47 +3151,6 @@ static int mxf_get_next_track_edit_unit(MXFContext *mxf, MXFTrack *track, int64_ return 0; } -/** - * Sets mxf->current_edit_unit based on what offset we're currently at. - * @return next_ofs if OK, <0 on error - */ -static int64_t mxf_set_current_edit_unit(MXFContext *mxf, int64_t current_offset) -{ - int64_t last_ofs = -1, next_ofs = -1; - MXFIndexTable *t = &mxf->index_tables[0]; - - /* this is called from the OP1a demuxing logic, which means there - * may be no index tables */ - if (mxf->nb_index_tables <= 0) - return -1; - - /* find mxf->current_edit_unit so that the next edit unit starts ahead of current_offset */ - while (mxf->current_edit_unit >= 0) { - if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, NULL, 0) < 0) - return -2; - - if (next_ofs <= last_ofs) { - /* large next_ofs didn't change or current_edit_unit wrapped - * around this fixes the infinite loop on zzuf3.mxf */ - av_log(mxf->fc, AV_LOG_ERROR, - "next_ofs didn't change. not deriving packet timestamps\n"); - return -1; - } - - if (next_ofs > current_offset) - break; - - last_ofs = next_ofs; - mxf->current_edit_unit++; - } - - /* not checking mxf->current_edit_unit >= t->nb_ptses here since CBR files may lack IndexEntryArrays */ - if (mxf->current_edit_unit < 0) - return -1; - - return next_ofs; -} - static int64_t mxf_compute_sample_count(MXFContext *mxf, AVStream *st, int64_t edit_unit) { @@ -3250,6 +3194,49 @@ static int64_t mxf_compute_sample_count(MXFContext *mxf, AVStream *st, return sample_count; } +/** + * Make sure track->sample_count is correct based on what offset we're currently at. + * Also determine the next edit unit (or packet) offset. + * @return next_ofs if OK, <0 on error + */ +static int64_t mxf_set_current_edit_unit(MXFContext *mxf, AVStream *st, int64_t current_offset, int resync) +{ + int64_t next_ofs = -1; + MXFTrack *track = st->priv_data; + int64_t edit_unit = av_rescale_q(track->sample_count, st->time_base, av_inv_q(track->edit_rate)); + int64_t new_edit_unit; + MXFIndexTable *t = mxf_find_index_table(mxf, track->index_sid); + + if (!t || track->wrapping == UnknownWrapped) + return -1; + + if (mxf_edit_unit_absolute_offset(mxf, t, edit_unit + track->edit_units_per_packet, NULL, &next_ofs, NULL, 0) < 0 && + (next_ofs = mxf_essence_container_end(mxf, t->body_sid)) <= 0) { + av_log(mxf->fc, AV_LOG_ERROR, "unable to compute the size of the last packet\n"); + return -1; + } + + /* check if the next edit unit offset (next_ofs) starts ahead of current_offset */ + if (next_ofs > current_offset) + return next_ofs; + + if (!resync) { + av_log(mxf->fc, AV_LOG_ERROR, "cannot find current edit unit for stream %d, invalid index?\n", st->index); + return -1; + } + + if (mxf_get_next_track_edit_unit(mxf, track, current_offset + 1, &new_edit_unit) < 0 || new_edit_unit <= 0) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to find next track edit unit in stream %d\n", st->index); + return -1; + } + + new_edit_unit--; + track->sample_count = mxf_compute_sample_count(mxf, st, new_edit_unit); + av_log(mxf->fc, AV_LOG_WARNING, "edit unit sync lost on stream %d, jumping from %"PRId64" to %"PRId64"\n", st->index, edit_unit, new_edit_unit); + + return mxf_set_current_edit_unit(mxf, st, current_offset, 0); +} + static int mxf_set_audio_pts(MXFContext *mxf, AVCodecParameters *par, AVPacket *pkt) { @@ -3269,28 +3256,30 @@ static int mxf_set_audio_pts(MXFContext *mxf, AVCodecParameters *par, return 0; } -static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt, int64_t next_ofs) +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 && (next_ofs >= 0 || next_ofs == -2 && st->duration == mxf->current_edit_unit + 1)) { - /* mxf->current_edit_unit good - see if we have an - * index table to derive timestamps from */ - MXFIndexTable *t = &mxf->index_tables[0]; + 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 (mxf->nb_index_tables >= 1 && mxf->current_edit_unit < t->nb_ptses) { - pkt->dts = mxf->current_edit_unit + t->first_dts; - pkt->pts = t->ptses[mxf->current_edit_unit]; + 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 = mxf->current_edit_unit; + 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; } @@ -3301,7 +3290,17 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) MXFContext *mxf = s->priv_data; int ret; - while ((ret = klv_read_packet(&klv, s->pb)) == 0) { + 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)) { @@ -3312,6 +3311,10 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) } 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)) { @@ -3319,6 +3322,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) 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, @@ -3328,21 +3332,38 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) } 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, klv.offset); - - if (next_ofs >= 0 && klv.next_klv > next_ofs) { - /* if this check is hit then it's possible OPAtom was treated as OP1a - * truncate the packet since it's probably very large (>2 GiB is common) */ - avpriv_request_sample(s, - "OPAtom misinterpreted as OP1a? " - "KLV for edit unit %"PRId64" extending into " - "next edit unit", - mxf->current_edit_unit); - klv.length = next_ofs - avio_tell(s->pb); + next_ofs = mxf_set_current_edit_unit(mxf, st, pos, 1); + + if (track->wrapping != FrameWrapped) { + int64_t size; + + if (next_ofs <= 0) { + // If we have no way to packetize the data, then return it in chunks... + 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; + } + // We must not overread, because the next edit unit might be in another KLV + 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 */ @@ -3351,93 +3372,38 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) 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) + 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, next_ofs); - if (ret < 0) + 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 + } else { skip: - avio_skip(s->pb, klv.length); + avio_skip(s->pb, max_data_size); + mxf->current_klv_data = (KLVPacket){{0}}; + } } return avio_feof(s->pb) ? AVERROR_EOF : ret; } -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; - MXFTrack *track; - int edit_units; - - if (mxf->op != OPAtom) - return mxf_read_packet_old(s, pkt); - - // If we have no streams then we basically are at EOF - st = mxf_get_opatom_stream(mxf); - if (!st) - return AVERROR_EOF; - - track = st->priv_data; - - /* OPAtom - clip wrapped demuxing */ - /* NOTE: mxf_read_header() makes sure nb_index_tables > 0 for OPAtom */ - t = &mxf->index_tables[0]; - - if (mxf->current_edit_unit >= track->original_duration) - return AVERROR_EOF; - - edit_units = FFMIN(track->edit_units_per_packet, track->original_duration - mxf->current_edit_unit); - - if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit, NULL, &pos, NULL, 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 + edit_units, NULL, &next_pos, NULL, 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 ((size = av_get_packet(s->pb, pkt, size)) < 0) - return size; - - pkt->stream_index = st->index; - - ret = mxf_set_pts(mxf, st, pkt, next_pos); - if (ret < 0) - return ret; - - mxf->current_edit_unit += edit_units; - - return 0; -} - static int mxf_read_close(AVFormatContext *s) { MXFContext *mxf = s->priv_data; @@ -3527,8 +3493,10 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti return seekpos; ff_update_cur_dts(s, st, sample_time); - mxf->current_edit_unit = 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 */ @@ -3571,11 +3539,23 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti sample_time = FFMIN(sample_time, source_track->original_duration - 1); } - if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, NULL, 1)) < 0) + 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, &sample_time, &seekpos, &partition, 1)) < 0) return ret; ff_update_cur_dts(s, st, sample_time); - mxf->current_edit_unit = 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); } @@ -3600,7 +3580,7 @@ AVInputFormat ff_mxf_demuxer = { .priv_data_size = sizeof(MXFContext), .read_probe = mxf_probe, .read_header = mxf_read_header, - .read_packet = mxf_read_packet, + .read_packet = mxf_read_packet_old, .read_close = mxf_read_close, .read_seek = mxf_read_seek, }; -- cgit v1.1