} MXFPartitionType;
typedef enum {
- OP1a,
+ OP1a = 1,
OP1b,
OP1c,
OP2a,
int index_sid;
int body_sid;
int64_t this_partition;
- int64_t essence_offset; /* absolute offset of essence */
+ int64_t essence_offset; ///< absolute offset of essence
int64_t essence_length;
int32_t kag_size;
int64_t header_byte_count;
int edit_unit_byte_count;
int index_sid;
int body_sid;
- int slice_count;
AVRational index_edit_rate;
uint64_t index_start_position;
uint64_t index_duration;
- int *slice;
- int *element_delta;
- int nb_delta_entries;
int8_t *temporal_offset_entries;
int *flag_entries;
uint64_t *stream_offset_entries;
- uint32_t **slice_offset_entries;
int nb_index_entries;
} MXFIndexTableSegment;
int64_t last_forward_tell;
int last_forward_partition;
int current_edit_unit;
- int current_stream;
- int d10;
- int broken_index;
- int64_t first_dts; /* DTS = EditUnit + first_dts */
- int64_t *ptses; /* maps EditUnit -> PTS */
- int nb_ptses;
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 bytes_num = size & 0x7f;
/* SMPTE 379M 5.3.4 guarantee that bytes_num must not exceed 8 bytes */
if (bytes_num > 8)
- return -1;
+ return AVERROR_INVALIDDATA;
size = 0;
while (bytes_num--)
size = size << 8 | avio_r8(pb);
static int klv_read_packet(KLVPacket *klv, AVIOContext *pb)
{
if (!mxf_read_sync(pb, mxf_klv_key, 4))
- return -1;
+ return AVERROR_INVALIDDATA;
klv->offset = avio_tell(pb) - 4;
memcpy(klv->key, mxf_klv_key, 4);
avio_read(pb, klv->key + 4, 12);
int i;
if (length > 61444) /* worst case PAL 1920 samples 8 channels */
- return -1;
+ return AVERROR_INVALIDDATA;
length = av_get_packet(pb, pkt, length);
if (length < 0)
return length;
if (!mxf->aesc && s->key && s->keylen == 16) {
mxf->aesc = av_malloc(av_aes_size);
if (!mxf->aesc)
- return -1;
+ return AVERROR(ENOMEM);
av_aes_init(mxf->aesc, s->key, 128, 1);
}
// crypto context
klv_decode_ber_length(pb);
avio_read(pb, klv->key, 16);
if (!IS_KLV_KEY(klv, mxf_essence_element_key))
- return -1;
+ return AVERROR_INVALIDDATA;
index = mxf_get_stream_index(s, klv);
if (index < 0)
- return -1;
+ return AVERROR_INVALIDDATA;
// source size
klv_decode_ber_length(pb);
orig_size = avio_rb64(pb);
if (orig_size < plaintext_size)
- return -1;
+ return AVERROR_INVALIDDATA;
// enc. code
size = klv_decode_ber_length(pb);
if (size < 32 || size - 32 < orig_size)
- return -1;
+ return AVERROR_INVALIDDATA;
avio_read(pb, ivec, 16);
avio_read(pb, tmpbuf, 16);
if (mxf->aesc)
int item_len = avio_rb32(pb);
if (item_len != 18) {
- av_log(mxf->fc, AV_LOG_ERROR, "unsupported primer pack item length\n");
- return -1;
+ av_log_ask_for_sample(pb, "unsupported primer pack item length %d\n",
+ item_len);
+ return AVERROR_PATCHWELCOME;
}
if (item_num > UINT_MAX / item_len)
- return -1;
+ return AVERROR_INVALIDDATA;
mxf->local_tags_count = item_num;
mxf->local_tags = av_malloc(item_num*item_len);
if (!mxf->local_tags)
- return -1;
+ return AVERROR(ENOMEM);
avio_read(pb, mxf->local_tags, item_num*item_len);
return 0;
}
static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
MXFContext *mxf = arg;
- MXFPartition *partition;
+ 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);
- mxf->partitions = av_realloc(mxf->partitions, (mxf->partitions_count + 1) * sizeof(*mxf->partitions));
- if (!mxf->partitions)
+ tmp_part = av_realloc(mxf->partitions, (mxf->partitions_count + 1) * sizeof(*mxf->partitions));
+ if (!tmp_part)
return AVERROR(ENOMEM);
+ mxf->partitions = tmp_part;
if (mxf->parsing_backward) {
/* insert the new partition pack in the middle
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) {
if (mxf->footer_partition && mxf->footer_partition != footer_partition) {
- av_log(mxf->fc, AV_LOG_ERROR, "inconsistent FooterPartition value: %li != %li\n",
+ av_log(mxf->fc, AV_LOG_ERROR,
+ "inconsistent FooterPartition value: %"PRIu64" != %"PRIu64"\n",
mxf->footer_partition, footer_partition);
} else {
mxf->footer_partition = footer_partition;
}
}
- av_dlog(mxf->fc, "PartitionPack: ThisPartition = 0x%lx, PreviousPartition = 0x%lx, "
- "FooterPartition = 0x%lx, IndexSID = %i, BodySID = %i\n",
+ av_dlog(mxf->fc,
+ "PartitionPack: ThisPartition = 0x%"PRIX64
+ ", PreviousPartition = 0x%"PRIX64", "
+ "FooterPartition = 0x%"PRIX64", IndexSID = %i, BodySID = %i\n",
partition->this_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, hence 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;
}
static int mxf_add_metadata_set(MXFContext *mxf, void *metadata_set)
{
+ MXFMetadataSet **tmp;
if (mxf->metadata_sets_count+1 >= UINT_MAX / sizeof(*mxf->metadata_sets))
return AVERROR(ENOMEM);
- mxf->metadata_sets = av_realloc(mxf->metadata_sets, (mxf->metadata_sets_count + 1) * sizeof(*mxf->metadata_sets));
- if (!mxf->metadata_sets)
- return -1;
+ tmp = av_realloc(mxf->metadata_sets, (mxf->metadata_sets_count + 1) * sizeof(*mxf->metadata_sets));
+ if (!tmp)
+ return AVERROR(ENOMEM);
+ mxf->metadata_sets = tmp;
mxf->metadata_sets[mxf->metadata_sets_count] = metadata_set;
mxf->metadata_sets_count++;
return 0;
{
MXFCryptoContext *cryptocontext = arg;
if (size != 16)
- return -1;
+ return AVERROR_INVALIDDATA;
if (IS_KLV_KEY(uid, mxf_crypto_source_container_ul))
avio_read(pb, cryptocontext->source_container_ul, 16);
return 0;
case 0x1901:
mxf->packages_count = avio_rb32(pb);
if (mxf->packages_count >= UINT_MAX / sizeof(UID))
- return -1;
+ return AVERROR_INVALIDDATA;
mxf->packages_refs = av_malloc(mxf->packages_count * sizeof(UID));
if (!mxf->packages_refs)
- return -1;
+ return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
avio_read(pb, (uint8_t *)mxf->packages_refs, mxf->packages_count * sizeof(UID));
break;
case 0x4403:
package->tracks_count = avio_rb32(pb);
if (package->tracks_count >= UINT_MAX / sizeof(UID))
- return -1;
+ return AVERROR_INVALIDDATA;
package->tracks_refs = av_malloc(package->tracks_count * sizeof(UID));
if (!package->tracks_refs)
- return -1;
+ return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
avio_read(pb, (uint8_t *)package->tracks_refs, package->tracks_count * sizeof(UID));
break;
case 0x1001:
sequence->structural_components_count = avio_rb32(pb);
if (sequence->structural_components_count >= UINT_MAX / sizeof(UID))
- return -1;
+ return AVERROR_INVALIDDATA;
sequence->structural_components_refs = av_malloc(sequence->structural_components_count * sizeof(UID));
if (!sequence->structural_components_refs)
- return -1;
+ return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
avio_read(pb, (uint8_t *)sequence->structural_components_refs, sequence->structural_components_count * sizeof(UID));
break;
case 0x4403:
package->tracks_count = avio_rb32(pb);
if (package->tracks_count >= UINT_MAX / sizeof(UID))
- return -1;
+ return AVERROR_INVALIDDATA;
package->tracks_refs = av_malloc(package->tracks_count * sizeof(UID));
if (!package->tracks_refs)
- return -1;
+ return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
avio_read(pb, (uint8_t *)package->tracks_refs, package->tracks_count * sizeof(UID));
break;
return 0;
}
-static int mxf_read_delta_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment)
-{
- int i, length;
-
- segment->nb_delta_entries = avio_rb32(pb);
- length = avio_rb32(pb);
-
- if (!(segment->slice = av_calloc(segment->nb_delta_entries, sizeof(*segment->slice))) ||
- !(segment->element_delta = av_calloc(segment->nb_delta_entries, sizeof(*segment->element_delta))))
- return AVERROR(ENOMEM);
-
- for (i = 0; i < segment->nb_delta_entries; i++) {
- avio_r8(pb); /* PosTableIndex */
- segment->slice[i] = avio_r8(pb);
- segment->element_delta[i] = avio_rb32(pb);
- }
- return 0;
-}
-
static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment)
{
- int i, j, length;
+ int i, length;
segment->nb_index_entries = avio_rb32(pb);
length = avio_rb32(pb);
!(segment->stream_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->stream_offset_entries))))
return AVERROR(ENOMEM);
- if (segment->slice_count &&
- !(segment->slice_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->slice_offset_entries))))
- return AVERROR(ENOMEM);
-
for (i = 0; i < segment->nb_index_entries; i++) {
segment->temporal_offset_entries[i] = avio_r8(pb);
avio_r8(pb); /* KeyFrameOffset */
segment->flag_entries[i] = avio_r8(pb);
segment->stream_offset_entries[i] = avio_rb64(pb);
- if (segment->slice_count) {
- if (!(segment->slice_offset_entries[i] = av_calloc(segment->slice_count, sizeof(**segment->slice_offset_entries))))
- return AVERROR(ENOMEM);
-
- for (j = 0; j < segment->slice_count; j++)
- segment->slice_offset_entries[i][j] = avio_rb32(pb);
- }
-
- avio_skip(pb, length - 11 - 4 * segment->slice_count);
+ avio_skip(pb, length - 11);
}
return 0;
}
segment->body_sid = avio_rb32(pb);
av_dlog(NULL, "BodySID %d\n", segment->body_sid);
break;
- case 0x3F08:
- segment->slice_count = avio_r8(pb);
- av_dlog(NULL, "SliceCount %d\n", segment->slice_count);
- break;
- case 0x3F09:
- av_dlog(NULL, "DeltaEntryArray found\n");
- return mxf_read_delta_entry_array(pb, segment);
case 0x3F0A:
av_dlog(NULL, "IndexEntryArray found\n");
return mxf_read_index_entry_array(pb, segment);
case 0x3F01:
descriptor->sub_descriptors_count = avio_rb32(pb);
if (descriptor->sub_descriptors_count >= UINT_MAX / sizeof(UID))
- return -1;
+ return AVERROR_INVALIDDATA;
descriptor->sub_descriptors_refs = av_malloc(descriptor->sub_descriptors_count * sizeof(UID));
if (!descriptor->sub_descriptors_refs)
- return -1;
+ return AVERROR(ENOMEM);
avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */
avio_read(pb, (uint8_t *)descriptor->sub_descriptors_refs, descriptor->sub_descriptors_count * sizeof(UID));
break;
if (IS_KLV_KEY(uid, mxf_sony_mpeg4_extradata)) {
descriptor->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
if (!descriptor->extradata)
- return -1;
+ return AVERROR(ENOMEM);
descriptor->extradata_size = size;
avio_read(pb, descriptor->extradata, size);
}
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, CODEC_ID_NONE },
};
-static UID mxf_d10_ul = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 };
-
static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segments, MXFIndexTableSegment ***sorted_segments)
{
int i, j, nb_segments = 0;
if (!(unsorted_segments = av_calloc(nb_segments, sizeof(*unsorted_segments))) ||
!(*sorted_segments = av_calloc(nb_segments, sizeof(**sorted_segments)))) {
+ av_freep(sorted_segments);
av_free(unsorted_segments);
return AVERROR(ENOMEM);
}
offset -= p->essence_length;
}
- av_log(mxf->fc, AV_LOG_ERROR, "failed to find absolute offset of %lx in BodySID %i - partial file?\n",
+ av_log(mxf->fc, AV_LOG_ERROR,
+ "failed to find absolute offset of %"PRIX64" in BodySID %i - partial file?\n",
offset_in, body_sid);
return AVERROR_INVALIDDATA;
}
-/**
- * Returns the length of the essence container with given BodySID, or zero if unknown
- */
-static int64_t mxf_essence_container_length(MXFContext *mxf, int body_sid)
-{
- int x;
- int64_t ret = 0;
-
- for (x = 0; x < mxf->partitions_count; x++) {
- MXFPartition *p = &mxf->partitions[x];
-
- if (p->body_sid != body_sid)
- continue;
-
- if (!p->essence_length)
- return 0;
-
- ret += p->essence_length;
- }
-
- return ret;
-}
-
/**
* Returns the end position of the essence container with given BodySID, or zero if unknown
*/
static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_table, int64_t edit_unit, int64_t *edit_unit_out, int64_t *offset_out, int nag)
{
int i;
- int offset_temp = 0;
+ int64_t offset_temp = 0;
for (i = 0; i < index_table->nb_segments; i++) {
MXFIndexTableSegment *s = index_table->segments[i];
return AVERROR_INVALIDDATA;
}
-static int mxf_parse_index(MXFContext *mxf, int track_id, AVStream *st, MXFIndexTableSegment **sorted_segments, int nb_sorted_segments)
-{
- int64_t accumulated_offset = 0;
- int j, k, l, ret;
- int n_delta = track_id - 1; /* TrackID = 1-based stream index */
-
- if (track_id < 1) {
- av_log(mxf->fc, AV_LOG_ERROR, "TrackID not positive: %i\n", track_id);
- return AVERROR_INVALIDDATA;
- }
-
- for (j = 0; j < nb_sorted_segments; j++) {
- int duration, sample_duration = 1, last_sample_size = 0;
- int64_t segment_size;
- MXFIndexTableSegment *tableseg = sorted_segments[j];
- int index_delta = 1, last_size_unknown = 0;
- int64_t last_pos = 0;
-
- /* reset accumulated_offset on BodySID change */
- if (j > 0 && tableseg->body_sid != sorted_segments[j-1]->body_sid)
- accumulated_offset = 0;
-
- if (tableseg->nb_index_entries == 2 * tableseg->index_duration + 1) {
- /* Avid index - duplicate entries and total size as last entry */
- index_delta = 2;
- }
-
- if (n_delta >= tableseg->nb_delta_entries && st->index != 0)
- continue;
- duration = tableseg->index_duration > 0 ? tableseg->index_duration :
- st->duration - st->nb_index_entries;
- segment_size = tableseg->edit_unit_byte_count * duration;
- /* check small EditUnitByteCount for audio */
- if (tableseg->edit_unit_byte_count && tableseg->edit_unit_byte_count < 32
- && !tableseg->index_duration) {
- /* duration might be prime relative to the new sample_duration,
- * which means we need to handle the last frame differently */
- sample_duration = 8192;
- last_sample_size = (duration % sample_duration) * tableseg->edit_unit_byte_count;
- tableseg->edit_unit_byte_count *= sample_duration;
- duration /= sample_duration;
- if (last_sample_size) duration++;
- }
-
- if (duration <= 0) {
- av_log(mxf->fc, AV_LOG_WARNING, "0 duration in index for stream %i\n", st->index);
- mxf->broken_index = 1;
- return 0;
- }
-
- for (k = l = 0; k < duration; k++, l += index_delta) {
- int64_t pos;
- int size, flags = 0;
-
- if (l < tableseg->nb_index_entries) {
- pos = tableseg->stream_offset_entries[l];
- if (n_delta < tableseg->nb_delta_entries) {
- if (n_delta < tableseg->nb_delta_entries - 1) {
- size =
- tableseg->slice_offset_entries[l][tableseg->slice[n_delta+1]-1] +
- tableseg->element_delta[n_delta+1] -
- tableseg->element_delta[n_delta];
- if (tableseg->slice[n_delta] > 0)
- size -= tableseg->slice_offset_entries[l][tableseg->slice[n_delta]-1];
- } else if (l < tableseg->nb_index_entries - 1) {
- size = tableseg->stream_offset_entries[l+1] -
- tableseg->stream_offset_entries[l] -
- tableseg->slice_offset_entries[l][tableseg->slice[tableseg->nb_delta_entries-1]-1] -
- tableseg->element_delta[tableseg->nb_delta_entries-1];
- } else
- size = 0;
- if (tableseg->slice[n_delta] > 0)
- pos += tableseg->slice_offset_entries[l][tableseg->slice[n_delta]-1];
- pos += tableseg->element_delta[n_delta];
- } else
- size = 0;
- flags = !(tableseg->flag_entries[l] & 0x30) ? AVINDEX_KEYFRAME : 0;
- } else {
- pos = (int64_t)k * tableseg->edit_unit_byte_count + accumulated_offset;
- if (n_delta < tableseg->nb_delta_entries - 1)
- size = tableseg->element_delta[n_delta+1] - tableseg->element_delta[n_delta];
- else {
- /* use smaller size for last sample if we should */
- if (last_sample_size && k == duration - 1)
- size = last_sample_size;
- else
- size = tableseg->edit_unit_byte_count;
- if (tableseg->nb_delta_entries)
- size -= tableseg->element_delta[tableseg->nb_delta_entries-1];
- }
- if (n_delta < tableseg->nb_delta_entries)
- pos += tableseg->element_delta[n_delta];
- flags = AVINDEX_KEYFRAME;
- }
-
- if (last_size_unknown)
- st->index_entries[st->nb_index_entries-1].size = pos - last_pos;
-
- last_size_unknown = size == 0;
- last_pos = pos;
-
- if (mxf_absolute_bodysid_offset(mxf, tableseg->body_sid, pos, &pos) < 0) {
- /* probably partial file - no point going further for this stream */
- break;
- }
-
- av_dlog(mxf->fc, "Stream %d IndexEntry %d TrackID %d Offset %"PRIx64" Timestamp %"PRId64"\n",
- st->index, st->nb_index_entries, track_id, pos, sample_duration * st->nb_index_entries);
-
- if ((ret = av_add_index_entry(st, pos, sample_duration * st->nb_index_entries, size, 0, flags)) < 0)
- return ret;
- }
-
- if (last_size_unknown) {
- int64_t ecl = mxf_essence_container_length(mxf, tableseg->body_sid);
-
- if (ecl > 0)
- st->index_entries[st->nb_index_entries-1].size = ecl - last_pos;
- }
-
- accumulated_offset += segment_size;
- }
-
- return 0;
-}
-
static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_table)
{
int i, j, x;
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 */
- for (j = 0; j < s->nb_index_entries; j += index_delta, x++) {
+ /* ignore the last entry - it's the size of the essence container */
+ n--;
+ }
+
+ for (j = 0; j < n; j += index_delta, x++) {
int offset = s->temporal_offset_entries[j] / index_delta;
int index = x + offset;
+ if (x >= index_table->nb_ptses) {
+ av_log(mxf->fc, AV_LOG_ERROR, "x >= nb_ptses - IndexEntryCount %i < IndexDuration %"PRId64"?\n",
+ s->nb_index_entries, s->index_duration);
+ break;
+ }
+
index_table->fake_index[x].timestamp = x;
index_table->fake_index[x].flags = !(s->flag_entries[j] & 0x30) ? AVINDEX_KEYFRAME : 0;
{
MXFPackage *material_package = NULL;
MXFPackage *temp_package = NULL;
- int i, j, k, ret, nb_sorted_segments;
- MXFIndexTableSegment **sorted_segments = NULL;
+ int i, j, k, ret;
av_dlog(mxf->fc, "metadata sets count %d\n", mxf->metadata_sets_count);
/* TODO: handle multiple material packages (OP3x) */
}
if (!material_package) {
av_log(mxf->fc, AV_LOG_ERROR, "no material package found\n");
- return -1;
- }
-
- if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments)) ||
- nb_sorted_segments <= 0) {
- av_log(mxf->fc, AV_LOG_WARNING, "broken or empty index\n");
- mxf->broken_index = 1;
+ return AVERROR_INVALIDDATA;
}
for (i = 0; i < material_package->tracks_count; i++) {
for (k = 0; k < source_package->tracks_count; k++) {
if (!(temp_track = mxf_resolve_strong_ref(mxf, &source_package->tracks_refs[k], Track))) {
av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track strong ref\n");
- ret = -1;
+ ret = AVERROR_INVALIDDATA;
goto fail_and_free;
}
if (temp_track->track_id == component->source_track_id) {
break;
}
}
- if (!source_track)
+ if (!source_track || !component)
continue;
if (!(source_track->sequence = mxf_resolve_strong_ref(mxf, &source_track->sequence_ref, Sequence))) {
av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n");
- ret = -1;
+ ret = AVERROR_INVALIDDATA;
goto fail_and_free;
}
}
}
- /* HACK: revert to the old demuxing/seeking scode for D-10 for now */
- if (mxf_match_uid(essence_container_ul, mxf_d10_ul, 14))
- mxf->d10 = 1;
-
/* TODO: drop PictureEssenceCoding and SoundEssenceCompression, only check EssenceContainer */
codec_ul = mxf_get_codec_ul(ff_mxf_codec_uls, &descriptor->essence_codec_ul);
st->codec->codec_id = codec_ul->id;
st->codec->codec_id = container_ul->id;
st->codec->channels = descriptor->channels;
st->codec->bits_per_coded_sample = descriptor->bits_per_sample;
+ 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) {
/* TODO: decode timestamps */
st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS;
}
-
- if (!mxf->broken_index &&
- (ret = mxf_parse_index(mxf, material_track->track_id, st, sorted_segments, nb_sorted_segments)))
- goto fail_and_free;
}
ret = 0;
fail_and_free:
- av_free(sorted_segments);
return ret;
}
uint64_t klv_end = avio_tell(pb) + klv->length;
if (!ctx)
- return -1;
- while (avio_tell(pb) + 4 < klv_end) {
+ return AVERROR(ENOMEM);
+ while (avio_tell(pb) + 4 < klv_end && !url_feof(pb)) {
+ int ret;
int tag = avio_rb16(pb);
int size = avio_rb16(pb); /* KLV specified by 0x53 */
uint64_t next = avio_tell(pb) + size;
}
if (ctx_size && tag == 0x3C0A)
avio_read(pb, ctx->uid, 16);
- else if (read_child(ctx, pb, tag, size, uid, -1) < 0)
- return -1;
+ else if ((ret = read_child(ctx, pb, tag, size, uid, -1)) < 0)
+ return ret;
+ /* accept the 64k local set limit being exceeded (Avid)
+ * don't accept it extending past the end of the KLV though (zzuf5.mxf) */
+ if (avio_tell(pb) > klv_end) {
+ av_log(mxf->fc, AV_LOG_ERROR, "local tag %#04x extends past end of local set @ %#"PRIx64"\n",
+ tag, klv->offset);
+ return AVERROR_INVALIDDATA;
+ } else if (avio_tell(pb) <= next) /* only seek forward, else this can loop for a long time */
avio_seek(pb, next, SEEK_SET);
}
if (ctx_size) ctx->type = type;
AVIOContext *pb = mxf->fc->pb;
int64_t ret;
- if (!mxf->current_partition) {
- av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to PartitionPack\n");
- return AVERROR_INVALIDDATA;
- }
-
if (mxf->parsing_backward) {
return mxf_seek_to_previous_partition(mxf);
} else {
if (p->essence_length < 0) {
/* next ThisPartition < essence_offset */
p->essence_length = 0;
- av_log(mxf->fc, AV_LOG_ERROR, "partition %i: bad ThisPartition = %lx\n",
+ av_log(mxf->fc, AV_LOG_ERROR,
+ "partition %i: bad ThisPartition = %"PRIX64"\n",
x+1, mxf->partitions[x+1].this_partition);
}
}
return ret == position ? ret : ret + kag_size;
}
-static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap)
+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;
+}
+
+/**
+ * Deals 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;
KLVPacket klv;
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 -1;
+ return AVERROR_INVALIDDATA;
}
avio_seek(s->pb, -14, SEEK_CUR);
mxf->fc = s;
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_system_item_key)) {
+
+ if (!mxf->current_partition) {
+ av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to first PartitionPack\n");
+ return AVERROR_INVALIDDATA;
+ }
+
if (!mxf->current_partition->essence_offset) {
/* for OP1a we compute essence_offset
* for OPAtom we point essence_offset after the KL (usually op1a_essence_offset + 20 or 25)
} else {
uint64_t next = avio_tell(s->pb) + klv.length;
res = metadata->read(mxf, s->pb, 0, klv.length, klv.key, klv.offset);
+
+ /* only seek forward, else this can loop for a long time */
+ if (avio_tell(s->pb) > next) {
+ av_log(s, AV_LOG_ERROR, "read past end of KLV @ %#"PRIx64"\n",
+ klv.offset);
+ return AVERROR_INVALIDDATA;
+ }
+
avio_seek(s->pb, next, SEEK_SET);
}
if (res < 0) {
av_log(s, AV_LOG_ERROR, "error reading header metadata\n");
- return -1;
+ return res;
}
break;
}
return AVERROR_INVALIDDATA;
}
+ mxf_handle_small_eubc(s);
+
return 0;
}
*/
static void mxf_packet_timestamps(MXFContext *mxf, AVPacket *pkt)
{
- int64_t next_ofs;
+ int64_t last_ofs = -1, next_ofs;
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;
+
/* 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;
+ 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;
+ }
+
if (next_ofs > pkt->pos)
break;
+ last_ofs = next_ofs;
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;
KLVPacket klv;
while (!url_feof(s->pb)) {
+ int ret;
if (klv_read_packet(&klv, s->pb) < 0)
return -1;
PRINT_KEY(s, "read packet", klv.key);
av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset);
if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
- int res = mxf_decrypt_triplet(s, pkt, &klv);
- if (res < 0) {
+ ret = mxf_decrypt_triplet(s, pkt, &klv);
+ if (ret < 0) {
av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
return 0;
}
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) {
av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n");
- return -1;
+ return AVERROR_INVALIDDATA;
}
} else {
- int ret = av_get_packet(s->pb, pkt, klv.length);
+ ret = av_get_packet(s->pb, pkt, klv.length);
if (ret < 0)
return ret;
}
int64_t ret64, pos, next_pos;
AVStream *st;
MXFIndexTable *t;
+ int edit_units;
if (mxf->op != OPAtom)
return mxf_read_packet_old(s, pkt);
/* OPAtom - clip wrapped demuxing */
+ /* NOTE: mxf_read_header() makes sure nb_index_tables > 0 for OPAtom */
st = s->streams[0];
t = &mxf->index_tables[0];
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;
}
{
MXFContext *mxf = s->priv_data;
MXFIndexTableSegment *seg;
- int i, j;
+ int i;
av_freep(&mxf->packages_refs);
break;
case IndexTableSegment:
seg = (MXFIndexTableSegment *)mxf->metadata_sets[i];
- if (seg->slice_count)
- for (j = 0; j < seg->nb_index_entries; j++)
- av_freep(&seg->slice_offset_entries[j]);
- av_freep(&seg->slice);
- av_freep(&seg->element_delta);
av_freep(&seg->temporal_offset_entries);
av_freep(&seg->flag_entries);
av_freep(&seg->stream_offset_entries);
- av_freep(&seg->slice_offset_entries);
break;
default:
break;
av_freep(&mxf->metadata_sets);
av_freep(&mxf->aesc);
av_freep(&mxf->local_tags);
- av_freep(&mxf->ptses);
for (i = 0; i < mxf->nb_index_tables; i++) {
av_freep(&mxf->index_tables[i].segments);
+ av_freep(&mxf->index_tables[i].ptses);
av_freep(&mxf->index_tables[i].fake_index);
}
av_freep(&mxf->index_tables);
if (mxf->index_tables <= 0) {
if (!s->bit_rate)
- return -1;
+ return AVERROR_INVALIDDATA;
if (sample_time < 0)
sample_time = 0;
seconds = av_rescale(sample_time, st->time_base.num, st->time_base.den);
- if (avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET) < 0)
- return -1;
+
+ if ((ret = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET)) < 0)
+ return ret;
ff_update_cur_dts(s, st, sample_time);
} else {
t = &mxf->index_tables[0];
if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) << 0)
return ret;
- av_update_cur_dts(s, st, sample_time);
+ ff_update_cur_dts(s, st, sample_time);
mxf->current_edit_unit = sample_time;
avio_seek(s->pb, seekpos, SEEK_SET);
}