X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmxfdec.c;h=8595d72083bad05dcdf90b678eaab7f0c4ea4061;hb=5595368bcc6df7d307876bc051f41a8c72f38ce9;hp=25d8c2b4c6c0954f32a73a8dce432a6097f4b763;hpb=d00257e96a777587085edc411a45fd3e6ca27939;p=ffmpeg diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 25d8c2b4c6c..8595d72083b 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -59,7 +59,7 @@ typedef enum { } MXFPartitionType; typedef enum { - OP1a, + OP1a = 1, OP1b, OP1c, OP2a, @@ -121,6 +121,7 @@ typedef struct { int track_id; uint8_t track_number[4]; AVRational edit_rate; + int intra_only; } MXFTrack; typedef struct { @@ -131,15 +132,19 @@ typedef struct { AVRational sample_rate; AVRational aspect_ratio; int width; - int height; + int height; /* Field height, not frame height */ + int frame_layout; /* See MXFFrameLayout enum */ int channels; int bits_per_sample; + unsigned int component_depth; + unsigned int horiz_subsampling; + unsigned int vert_subsampling; UID *sub_descriptors_refs; int sub_descriptors_count; int linked_track_id; uint8_t *extradata; int extradata_size; - enum PixelFormat pix_fmt; + enum AVPixelFormat pix_fmt; } MXFDescriptor; typedef struct { @@ -207,6 +212,7 @@ typedef struct { int current_edit_unit; int nb_index_tables; MXFIndexTable *index_tables; + int edit_units_per_packet; ///< how many edit units to read at a time (PCM, OPAtom) } MXFContext; enum MXFWrappingScheme { @@ -334,7 +340,7 @@ static int mxf_decrypt_triplet(AVFormatContext *s, AVPacket *pkt, KLVPacket *klv int index; if (!mxf->aesc && s->key && s->keylen == 16) { - mxf->aesc = av_malloc(av_aes_size); + mxf->aesc = av_aes_alloc(); if (!mxf->aesc) return AVERROR(ENOMEM); av_aes_init(mxf->aesc, s->key, 128, 1); @@ -410,6 +416,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size MXFPartition *partition, *tmp_part; UID op; uint64_t footer_partition; + uint32_t nb_essence_containers; if (mxf->partitions_count+1 >= UINT_MAX / sizeof(*mxf->partitions)) return AVERROR(ENOMEM); @@ -464,6 +471,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size avio_skip(pb, 8); partition->body_sid = avio_rb32(pb); avio_read(pb, op, sizeof(UID)); + nb_essence_containers = avio_rb32(pb); /* some files don'thave FooterPartition set in every partition */ if (footer_partition) { @@ -501,9 +509,26 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size 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] == 0x10) mxf->op = OPAtom; else if (op[12] == 64&& op[13] == 1) mxf->op = OPSonyOpt; - else { + 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 %u 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; } @@ -615,8 +640,8 @@ static int mxf_read_track(void *arg, AVIOContext *pb, int tag, int size, UID uid avio_read(pb, track->track_number, 4); break; case 0x4B01: - track->edit_rate.den = avio_rb32(pb); track->edit_rate.num = avio_rb32(pb); + track->edit_rate.den = avio_rb32(pb); break; case 0x4803: avio_read(pb, track->sequence_ref, 16); @@ -684,7 +709,7 @@ static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *seg return 0; else if (segment->nb_index_entries < 0 || segment->nb_index_entries > - (INT_MAX >> av_log2(sizeof(*segment->stream_offset_entries)))) + (INT_MAX / sizeof(*segment->stream_offset_entries))) return AVERROR(ENOMEM); length = avio_rb32(pb); @@ -773,6 +798,7 @@ static void mxf_read_pixel_layout(AVIOContext *pb, MXFDescriptor *descriptor) static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFDescriptor *descriptor = arg; + descriptor->pix_fmt = AV_PIX_FMT_NONE; switch(tag) { case 0x3F01: descriptor->sub_descriptors_count = avio_rb32(pb); @@ -799,10 +825,22 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int case 0x3202: descriptor->height = avio_rb32(pb); break; + case 0x320C: + descriptor->frame_layout = avio_r8(pb); + break; case 0x320E: descriptor->aspect_ratio.num = avio_rb32(pb); descriptor->aspect_ratio.den = avio_rb32(pb); break; + case 0x3301: + descriptor->component_depth = avio_rb32(pb); + break; + case 0x3302: + descriptor->horiz_subsampling = avio_rb32(pb); + break; + case 0x3308: + descriptor->vert_subsampling = avio_rb32(pb); + break; case 0x3D03: descriptor->sample_rate.num = avio_rb32(pb); descriptor->sample_rate.den = avio_rb32(pb); @@ -874,17 +912,31 @@ static void *mxf_resolve_strong_ref(MXFContext *mxf, UID *strong_ref, enum MXFMe static const MXFCodecUL mxf_picture_essence_container_uls[] = { // video essence container uls - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, 14, CODEC_ID_MPEG2VIDEO }, /* MPEG-ES Frame wrapped */ - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, 14, CODEC_ID_DVVIDEO }, /* DV 625 25mbps */ - { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, CODEC_ID_NONE }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* MPEG-ES Frame wrapped */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, 14, AV_CODEC_ID_DVVIDEO }, /* DV 625 25mbps */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x05,0x00,0x00 }, 14, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed Picture */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, }; + +/* EC ULs for intra-only formats */ +static const MXFCodecUL mxf_intra_only_essence_container_uls[] = { + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x00,0x00 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* MXF-GC SMPTE D-10 Mappings */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, +}; + +/* intra-only PictureEssenceCoding ULs, where no corresponding EC UL exists */ +static const MXFCodecUL mxf_intra_only_picture_essence_coding_uls[] = { + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x00,0x00 }, 14, AV_CODEC_ID_H264 }, /* H.264/MPEG-4 AVC Intra Profiles */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, +}; + static const MXFCodecUL mxf_sound_essence_container_uls[] = { // sound essence container uls - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x01,0x00 }, 14, CODEC_ID_PCM_S16LE }, /* BWF Frame wrapped */ - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x40,0x01 }, 14, CODEC_ID_MP2 }, /* MPEG-ES Frame wrapped, 0x40 ??? stream id */ - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, 14, CODEC_ID_PCM_S16LE }, /* D-10 Mapping 50Mbps PAL Extended Template */ - { { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0xFF,0x4B,0x46,0x41,0x41,0x00,0x0D,0x4D,0x4F }, 14, CODEC_ID_PCM_S16LE }, /* 0001GL00.MXF.A1.mxf_opatom.mxf */ - { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, CODEC_ID_NONE }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x01,0x00 }, 14, AV_CODEC_ID_PCM_S16LE }, /* BWF Frame wrapped */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x40,0x01 }, 14, AV_CODEC_ID_MP2 }, /* MPEG-ES Frame wrapped, 0x40 ??? stream id */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, 14, AV_CODEC_ID_PCM_S16LE }, /* D-10 Mapping 50Mbps PAL Extended Template */ + { { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0xFF,0x4B,0x46,0x41,0x41,0x00,0x0D,0x4D,0x4F }, 14, AV_CODEC_ID_PCM_S16LE }, /* 0001GL00.MXF.A1.mxf_opatom.mxf */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, }; static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segments, MXFIndexTableSegment ***sorted_segments) @@ -898,6 +950,9 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment if (mxf->metadata_sets[i]->type == IndexTableSegment) nb_segments++; + if (!nb_segments) + return AVERROR_INVALIDDATA; + *sorted_segments = av_mallocz(nb_segments * sizeof(**sorted_segments)); unsorted_segments = av_mallocz(nb_segments * sizeof(*unsorted_segments)); if (!sorted_segments || !unsorted_segments) { @@ -1018,7 +1073,7 @@ static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_t if (s->nb_index_entries == 2 * s->index_duration + 1) index *= 2; /* Avid index */ - if (index < 0 || index > s->nb_index_entries) { + 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; @@ -1068,7 +1123,7 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta if (index_table->nb_ptses <= 0) return 0; - if (index_table->nb_ptses > INT_MAX >> av_log2(sizeof(AVIndexEntry)) + 1) + if (index_table->nb_ptses > INT_MAX / sizeof(AVIndexEntry)) return AVERROR(ENOMEM); index_table->ptses = av_mallocz(index_table->nb_ptses * @@ -1115,11 +1170,15 @@ static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_ta 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) + 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 < s->nb_index_entries; j += index_delta, x++) { + for (j = 0; j < n; j += index_delta, x++) { int offset = s->temporal_offset_entries[j] / index_delta; int index = x + offset; @@ -1176,7 +1235,7 @@ static int mxf_compute_index_tables(MXFContext *mxf) } } - if (mxf->nb_index_tables > INT_MAX >> av_log2(sizeof(MXFIndexTable)) + 1 || + if (mxf->nb_index_tables > INT_MAX / sizeof(MXFIndexTable) || !(mxf->index_tables = av_mallocz(mxf->nb_index_tables * sizeof(MXFIndexTable)))) { av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate index tables\n"); @@ -1198,7 +1257,7 @@ static int mxf_compute_index_tables(MXFContext *mxf) MXFIndexTable *t = &mxf->index_tables[j]; if (t->nb_segments > - (INT_MAX >> av_log2(sizeof(MXFIndexTableSegment *))) || + (INT_MAX / sizeof(MXFIndexTableSegment *)) || !(t->segments = av_mallocz(t->nb_segments * sizeof(MXFIndexTableSegment*)))) { av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate IndexTableSegment" @@ -1246,6 +1305,14 @@ finish_decoding_index: return ret; } +static int mxf_is_intra_only(MXFDescriptor *d) +{ + return mxf_get_codec_ul(mxf_intra_only_essence_container_uls, + &d->essence_container_ul)->id != AV_CODEC_ID_NONE || + mxf_get_codec_ul(mxf_intra_only_picture_essence_coding_uls, + &d->essence_codec_ul)->id != AV_CODEC_ID_NONE; +} + static int mxf_parse_structural_metadata(MXFContext *mxf) { MXFPackage *material_package = NULL; @@ -1273,6 +1340,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) UID *essence_container_ul = NULL; const MXFCodecUL *codec_ul = NULL; const MXFCodecUL *container_ul = NULL; + const MXFCodecUL *pix_fmt_ul = NULL; AVStream *st; if (!(material_track = mxf_resolve_strong_ref(mxf, &material_package->tracks_refs[i], Track))) { @@ -1349,7 +1417,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) if (st->duration == -1) st->duration = AV_NOPTS_VALUE; st->start_time = component->start_position; - avpriv_set_pts_info(st, 64, material_track->edit_rate.num, material_track->edit_rate.den); + avpriv_set_pts_info(st, 64, material_track->edit_rate.den, material_track->edit_rate.num); PRINT_KEY(mxf->fc, "data definition ul", source_track->sequence->data_definition_ul); codec_ul = mxf_get_codec_ul(ff_mxf_data_definition_uls, &source_track->sequence->data_definition_ul); @@ -1401,33 +1469,79 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->codec->extradata_size = descriptor->extradata_size; } if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + source_track->intra_only = mxf_is_intra_only(descriptor); container_ul = mxf_get_codec_ul(mxf_picture_essence_container_uls, essence_container_ul); - if (st->codec->codec_id == CODEC_ID_NONE) + if (st->codec->codec_id == AV_CODEC_ID_NONE) st->codec->codec_id = container_ul->id; st->codec->width = descriptor->width; + /* Field height, not frame height */ st->codec->height = descriptor->height; - if (st->codec->codec_id == CODEC_ID_RAWVIDEO) + switch (descriptor->frame_layout) { + case SegmentedFrame: + /* This one is a weird layout I don't fully understand. */ + av_log(mxf->fc, AV_LOG_INFO, + "SegmentedFrame layout isn't currently supported\n"); + break; + case FullFrame: + break; + case OneField: + /* Every other line is stored and needs to be duplicated. */ + av_log(mxf->fc, AV_LOG_INFO, + "OneField frame layout isn't currently supported\n"); + break; + /* The correct thing to do here is fall through, but by + * breaking we might be able to decode some streams at half + * the vertical resolution, rather than not al all. + * It's also for compatibility with the old behavior. */ + case SeparateFields: + case MixedFields: + /* Turn field height into frame height. */ + st->codec->height *= 2; + default: + av_log(mxf->fc, AV_LOG_INFO, + "Unknown frame layout type: %d\n", + descriptor->frame_layout); + } + if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO) { st->codec->pix_fmt = descriptor->pix_fmt; + if (st->codec->pix_fmt == AV_PIX_FMT_NONE) { + pix_fmt_ul = mxf_get_codec_ul(ff_mxf_pixel_format_uls, + &descriptor->essence_codec_ul); + st->codec->pix_fmt = pix_fmt_ul->id; + if (st->codec->pix_fmt == AV_PIX_FMT_NONE) { + /* support files created before RP224v10 by defaulting to UYVY422 + if subsampling is 4:2:2 and component depth is 8-bit */ + if (descriptor->horiz_subsampling == 2 && + descriptor->vert_subsampling == 1 && + descriptor->component_depth == 8) { + st->codec->pix_fmt = AV_PIX_FMT_UYVY422; + } + } + } + } st->need_parsing = AVSTREAM_PARSE_HEADERS; } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul); - if (st->codec->codec_id == CODEC_ID_NONE) + if (st->codec->codec_id == AV_CODEC_ID_NONE) st->codec->codec_id = container_ul->id; st->codec->channels = descriptor->channels; st->codec->bits_per_coded_sample = descriptor->bits_per_sample; - st->codec->sample_rate = descriptor->sample_rate.num / descriptor->sample_rate.den; - /* TODO: implement CODEC_ID_RAWAUDIO */ - if (st->codec->codec_id == CODEC_ID_PCM_S16LE) { + + if (descriptor->sample_rate.den > 0) + st->codec->sample_rate = descriptor->sample_rate.num / descriptor->sample_rate.den; + + /* TODO: implement AV_CODEC_ID_RAWAUDIO */ + if (st->codec->codec_id == AV_CODEC_ID_PCM_S16LE) { if (descriptor->bits_per_sample > 16 && descriptor->bits_per_sample <= 24) - st->codec->codec_id = CODEC_ID_PCM_S24LE; + st->codec->codec_id = AV_CODEC_ID_PCM_S24LE; else if (descriptor->bits_per_sample == 32) - st->codec->codec_id = CODEC_ID_PCM_S32LE; - } else if (st->codec->codec_id == CODEC_ID_PCM_S16BE) { + st->codec->codec_id = AV_CODEC_ID_PCM_S32LE; + } else if (st->codec->codec_id == AV_CODEC_ID_PCM_S16BE) { if (descriptor->bits_per_sample > 16 && descriptor->bits_per_sample <= 24) - st->codec->codec_id = CODEC_ID_PCM_S24BE; + st->codec->codec_id = AV_CODEC_ID_PCM_S24BE; else if (descriptor->bits_per_sample == 32) - st->codec->codec_id = CODEC_ID_PCM_S32BE; - } else if (st->codec->codec_id == CODEC_ID_MP2) { + st->codec->codec_id = AV_CODEC_ID_PCM_S32BE; + } else if (st->codec->codec_id == AV_CODEC_ID_MP2) { st->need_parsing = AVSTREAM_PARSE_FULL; } } @@ -1668,6 +1782,42 @@ static inline void compute_partition_essence_offset(AVFormatContext *s, } } +static int is_pcm(enum AVCodecID codec_id) +{ + /* we only care about "normal" PCM codecs until we get samples */ + return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD; +} + +/** + * Deal with the case where for some audio atoms EditUnitByteCount is + * very small (2, 4..). In those cases we should read more than one + * sample per call to mxf_read_packet(). + */ +static void mxf_handle_small_eubc(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + + /* assuming non-OPAtom == frame wrapped + * no sane writer would wrap 2 byte PCM packets with 20 byte headers.. */ + if (mxf->op != OPAtom) + return; + + /* expect PCM with exactly one index table segment and a small (< 32) EUBC */ + if (s->nb_streams != 1 || + s->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO || + !is_pcm(s->streams[0]->codec->codec_id) || + mxf->nb_index_tables != 1 || + mxf->index_tables[0].nb_segments != 1 || + mxf->index_tables[0].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. */ + mxf->edit_units_per_packet = 1920; +} + static int mxf_read_header(AVFormatContext *s) { MXFContext *mxf = s->priv_data; @@ -1676,6 +1826,7 @@ static int mxf_read_header(AVFormatContext *s) int ret; mxf->last_forward_tell = INT64_MAX; + mxf->edit_units_per_packet = 1; 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"); @@ -1725,6 +1876,9 @@ static int mxf_read_header(AVFormatContext *s) /* 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++) { @@ -1781,52 +1935,59 @@ static int mxf_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } + mxf_handle_small_eubc(s); + return 0; } /** - * 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]; /* this is called from the OP1a demuxing logic, which means there * may be no index tables */ 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 */ - for (;;) { - if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, 0) < 0) - break; + /* 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, 0) < 0) + return -1; 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; + return -1; } - if (next_ofs > pkt->pos) + if (next_ofs > current_offset) break; last_ofs = next_ofs; mxf->current_edit_unit++; } - if (mxf->current_edit_unit >= t->nb_ptses) - return; + /* not checking mxf->current_edit_unit >= t->nb_ptses here since CBR files + * may lack IndexEntryArrays */ + if (mxf->current_edit_unit < 0) + return -1; - pkt->dts = mxf->current_edit_unit + t->first_dts; - pkt->pts = t->ptses[mxf->current_edit_unit]; + return next_ofs; } static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) { KLVPacket klv; + MXFContext *mxf = s->priv_data; while (!s->pb->eof_reached) { if (klv_read_packet(&klv, s->pb) < 0) @@ -1844,12 +2005,36 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) 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); + int64_t next_ofs, next_klv; + AVStream *st; + MXFTrack *track; + if (index < 0) { av_log(s, AV_LOG_ERROR, "error getting stream index %d\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_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 */ 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) { @@ -1864,8 +2049,25 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) 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 */ + if (s->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO && next_ofs >= 0) { + /* 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]; + } 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; + } + } + + /* seek for truncated packets */ + avio_seek(s->pb, next_klv, SEEK_SET); return 0; } else @@ -1882,6 +2084,7 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) int64_t ret64, pos, next_pos; AVStream *st; MXFIndexTable *t; + int edit_units; if (mxf->op != OPAtom) return mxf_read_packet_old(s, pkt); @@ -1894,12 +2097,14 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) if (mxf->current_edit_unit >= st->duration) return AVERROR_EOF; + edit_units = FFMIN(mxf->edit_units_per_packet, st->duration - mxf->current_edit_unit); + 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 && + if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + edit_units, 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; @@ -1923,7 +2128,7 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) } pkt->stream_index = 0; - mxf->current_edit_unit++; + mxf->current_edit_unit += edit_units; return 0; } @@ -2044,7 +2249,7 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti AVInputFormat ff_mxf_demuxer = { .name = "mxf", - .long_name = NULL_IF_CONFIG_SMALL("Material eXchange Format"), + .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"), .priv_data_size = sizeof(MXFContext), .read_probe = mxf_probe, .read_header = mxf_read_header,