} MXFPartitionType;
typedef enum {
- OP1a,
+ OP1a = 1,
OP1b,
OP1c,
OP2a,
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 {
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"
+ * 2011_DCPTEST_24FPS.V.mxf violates this and is frame wrapped,
+ * which is why we assume OP1a. */
+ if (nb_essence_containers != 1) {
+ /* only nag once */
+ if (!mxf->op)
+ av_log(mxf->fc, AV_LOG_WARNING,
+ "\"OPAtom\" with %u ECs - assuming OP1a\n",
+ nb_essence_containers);
+
+ mxf->op = OP1a;
+ } 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;
}
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;
}
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;
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;
+
+ if (descriptor->sample_rate.den > 0)
+ 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->bits_per_sample > 16 && descriptor->bits_per_sample <= 24)
}
}
+static int is_pcm(enum CodecID codec_id)
+{
+ /* we only care about "normal" PCM codecs until we get samples */
+ return codec_id >= CODEC_ID_PCM_S16LE && codec_id < 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");
return AVERROR_INVALIDDATA;
}
+ mxf_handle_small_eubc(s);
+
return 0;
}
return;
/* find mxf->current_edit_unit so that the next edit unit starts ahead of pkt->pos */
- for (;;) {
+ while (mxf->current_edit_unit >= 0) {
if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, 0) < 0)
break;
mxf->current_edit_unit++;
}
- if (mxf->current_edit_unit >= t->nb_ptses)
+ if (mxf->current_edit_unit < 0 || mxf->current_edit_unit >= t->nb_ptses)
return;
pkt->dts = mxf->current_edit_unit + t->first_dts;
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;
}