mxfdec: truncate packets that extend past the next edit unit

This fixes rare cases where OPAtom may be treated
as OP1a, causing all essence to be read into RAM.

Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Signed-off-by: Luca Barbato <lu_zero@gentoo.org>
This commit is contained in:
Tomas Härdin 2012-03-02 11:16:10 +01:00 committed by Luca Barbato
parent fda891e108
commit 1fadf53e00

View File

@ -1877,48 +1877,53 @@ static int mxf_read_header(AVFormatContext *s)
} }
/** /**
* Computes DTS and PTS for the given video packet based on its offset. * Sets mxf->current_edit_unit based on what offset we're currently at.
* @return next_ofs if OK, <0 on error
*/ */
static void mxf_packet_timestamps(MXFContext *mxf, AVPacket *pkt) static int64_t mxf_set_current_edit_unit(MXFContext *mxf, int64_t current_offset)
{ {
int64_t last_ofs = -1, next_ofs; int64_t last_ofs = -1, next_ofs = -1;
MXFIndexTable *t = &mxf->index_tables[0]; MXFIndexTable *t = &mxf->index_tables[0];
/* this is called from the OP1a demuxing logic, which means there /* this is called from the OP1a demuxing logic, which means there
* may be no index tables */ * may be no index tables */
if (mxf->nb_index_tables <= 0) if (mxf->nb_index_tables <= 0)
return; return -1;
/* find mxf->current_edit_unit so that the next edit unit starts ahead of pkt->pos */ /* find mxf->current_edit_unit so that the next edit unit starts ahead
* of current_offset */
while (mxf->current_edit_unit >= 0) { while (mxf->current_edit_unit >= 0) {
if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, 0) < 0) if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1,
break; NULL, &next_ofs, 0) < 0)
return -1;
if (next_ofs <= last_ofs) { if (next_ofs <= last_ofs) {
/* large next_ofs didn't change or current_edit_unit wrapped /* large next_ofs didn't change or current_edit_unit wrapped
* around this fixes the infinite loop on zzuf3.mxf */ * around this fixes the infinite loop on zzuf3.mxf */
av_log(mxf->fc, AV_LOG_ERROR, av_log(mxf->fc, AV_LOG_ERROR,
"next_ofs didn't change. not deriving packet timestamps\n"); "next_ofs didn't change. not deriving packet timestamps\n");
return; return -1;
} }
if (next_ofs > pkt->pos) if (next_ofs > current_offset)
break; break;
last_ofs = next_ofs; last_ofs = next_ofs;
mxf->current_edit_unit++; mxf->current_edit_unit++;
} }
if (mxf->current_edit_unit < 0 || mxf->current_edit_unit >= t->nb_ptses) /* not checking mxf->current_edit_unit >= t->nb_ptses here since CBR files
return; * may lack IndexEntryArrays */
if (mxf->current_edit_unit < 0)
return -1;
pkt->dts = mxf->current_edit_unit + t->first_dts; return next_ofs;
pkt->pts = t->ptses[mxf->current_edit_unit];
} }
static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
{ {
KLVPacket klv; KLVPacket klv;
MXFContext *mxf = s->priv_data;
while (!s->pb->eof_reached) { while (!s->pb->eof_reached) {
if (klv_read_packet(&klv, s->pb) < 0) if (klv_read_packet(&klv, s->pb) < 0)
@ -1936,12 +1941,30 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
if (IS_KLV_KEY(klv.key, mxf_essence_element_key) || if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) { IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) {
int index = mxf_get_stream_index(s, &klv); int index = mxf_get_stream_index(s, &klv);
int64_t next_ofs, next_klv;
if (index < 0) { if (index < 0) {
av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12)); av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12));
goto skip; goto skip;
} }
if (s->streams[index]->discard == AVDISCARD_ALL) if (s->streams[index]->discard == AVDISCARD_ALL)
goto skip; goto skip;
next_klv = avio_tell(s->pb) + klv.length;
next_ofs = mxf_set_current_edit_unit(mxf, klv.offset);
if (next_ofs >= 0 && 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) */
av_log_ask_for_sample(s,
"KLV for edit unit %i extends into next "
"edit unit - OPAtom misinterpreted as "
"OP1a?\n",
mxf->current_edit_unit);
klv.length = next_ofs - avio_tell(s->pb);
}
/* check for 8 channels AES3 element */ /* check for 8 channels AES3 element */
if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) { 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) { if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) {
@ -1956,8 +1979,20 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
pkt->stream_index = index; pkt->stream_index = index;
pkt->pos = klv.offset; pkt->pos = klv.offset;
if (s->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO) if (s->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO && next_ofs >= 0) {
mxf_packet_timestamps(s->priv_data, pkt); /* offset -> EditUnit -> DTS/PTS */ /* mxf->current_edit_unit good - see if we have an
* index table to derive timestamps from */
MXFIndexTable *t = &mxf->index_tables[0];
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];
}
}
/* seek for truncated packets */
avio_seek(s->pb, next_klv, SEEK_SET);
return 0; return 0;
} else } else