[FFmpeg-cvslog] avformat/mxfdec: add support for clip wrapped essences
Marton Balint
git at videolan.org
Wed Jul 4 23:28:30 EEST 2018
ffmpeg | branch: master | Marton Balint <cus at passwd.hu> | Thu May 10 20:30:19 2018 +0200| [00a2652df3bf25a27d174cc67ed508b5317cb115] | committer: Marton Balint
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 <cus at passwd.hu>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=00a2652df3bf25a27d174cc67ed508b5317cb115
---
libavformat/mxfdec.c | 282 ++++++++++++++++++++++++---------------------------
1 file changed, 131 insertions(+), 151 deletions(-)
diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index 8c417aea26..c25595a91f 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,
};
More information about the ffmpeg-cvslog
mailing list