} MXFPartitionType;
typedef enum {
- OP1a,
+ OP1a = 1,
OP1b,
OP1c,
OP2a,
int track_id;
uint8_t track_number[4];
AVRational edit_rate;
+ int intra_only;
} MXFTrack;
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 {
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 {
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);
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);
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) {
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] == 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;
}
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);
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);
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);
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);
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)
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) {
+ if (!*sorted_segments || !unsorted_segments) {
av_freep(sorted_segments);
av_free(unsorted_segments);
return AVERROR(ENOMEM);
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;
for (i = 0; i < index_table->nb_segments; i++) {
MXFIndexTableSegment *s = index_table->segments[i];
- if (!s->nb_index_entries)
+ if (!s->nb_index_entries) {
+ index_table->nb_ptses = 0;
return 0; /* no TemporalOffsets */
+ }
index_table->nb_ptses += s->index_duration;
}
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 *
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;
}
}
- 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");
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"
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;
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))) {
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);
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;
}
}
}
}
+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;
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");
/* 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++) {
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)
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) {
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
int64_t ret64, pos, next_pos;
AVStream *st;
MXFIndexTable *t;
+ int edit_units;
if (mxf->op != OPAtom)
return mxf_read_packet_old(s, 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;
}
pkt->stream_index = 0;
- mxf->current_edit_unit++;
+ mxf->current_edit_unit += edit_units;
return 0;
}
int ret;
MXFIndexTable *t;
- if (mxf->index_tables <= 0) {
+ if (mxf->nb_index_tables <= 0) {
if (!s->bit_rate)
return AVERROR_INVALIDDATA;
if (sample_time < 0)
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,