int64_t index_byte_count;
int pack_length;
int64_t pack_ofs; ///< absolute offset of pack in file, including run-in
+ int64_t body_offset;
} MXFPartition;
typedef struct MXFCryptoContext {
int intra_only;
uint64_t sample_count;
int64_t original_duration; /* st->duration in SampleRate/EditRate units */
+ int index_sid;
+ int body_sid;
} MXFTrack;
typedef struct MXFDescriptor {
int comment_count;
} MXFPackage;
+typedef struct MXFEssenceContainerData {
+ UID uid;
+ enum MXFMetadataSetType type;
+ UID package_uid;
+ UID package_ul;
+ int index_sid;
+ int body_sid;
+} MXFEssenceContainerData;
+
typedef struct MXFMetadataSet {
UID uid;
enum MXFMetadataSetType type;
MXFOP op;
UID *packages_refs;
int packages_count;
+ UID *essence_container_data_refs;
+ int essence_container_data_count;
MXFMetadataSet **metadata_sets;
int metadata_sets_count;
AVFormatContext *fc;
static const uint8_t mxf_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 };
static const uint8_t mxf_avid_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0e,0x04,0x03,0x01 };
static const uint8_t mxf_canopus_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x0a,0x0e,0x0f,0x03,0x01 };
-static const uint8_t mxf_system_item_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x03,0x01,0x04 };
+static const uint8_t mxf_system_item_key_cp[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x03,0x01,0x04 };
+static const uint8_t mxf_system_item_key_gc[] = { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x03,0x01,0x14 };
static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 };
/* complete keys to match */
static const uint8_t mxf_crypto_source_container_ul[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 };
return klv->length == -1 ? -1 : 0;
}
-static int mxf_get_stream_index(AVFormatContext *s, KLVPacket *klv)
+static int mxf_get_stream_index(AVFormatContext *s, KLVPacket *klv, int body_sid)
{
int i;
for (i = 0; i < s->nb_streams; i++) {
MXFTrack *track = s->streams[i]->priv_data;
/* SMPTE 379M 7.3 */
- if (track && !memcmp(klv->key + sizeof(mxf_essence_element_key), track->track_number, sizeof(track->track_number)))
+ if (track && (!body_sid || !track->body_sid || track->body_sid == body_sid) && !memcmp(klv->key + sizeof(mxf_essence_element_key), track->track_number, sizeof(track->track_number)))
return i;
}
/* return 0 if only one stream, for OP Atom files with 0 as track number */
return s->nb_streams == 1 ? 0 : -1;
}
+static int find_body_sid_by_offset(MXFContext *mxf, int64_t offset)
+{
+ // we look for partition where the offset is placed
+ int a, b, m;
+ int64_t this_partition;
+
+ a = -1;
+ b = mxf->partitions_count;
+
+ while (b - a > 1) {
+ m = (a + b) >> 1;
+ this_partition = mxf->partitions[m].this_partition;
+ if (this_partition <= offset)
+ a = m;
+ else
+ b = m;
+ }
+
+ if (a == -1)
+ return 0;
+ return mxf->partitions[a].body_sid;
+}
+
/* XXX: use AVBitStreamFilter */
static int mxf_get_d10_aes3_packet(AVIOContext *pb, AVStream *st, AVPacket *pkt, int64_t length)
{
uint8_t ivec[16];
uint8_t tmpbuf[16];
int index;
+ int body_sid;
if (!mxf->aesc && s->key && s->keylen == 16) {
mxf->aesc = av_aes_alloc();
avio_read(pb, klv->key, 16);
if (!IS_KLV_KEY(klv, mxf_essence_element_key))
return AVERROR_INVALIDDATA;
- index = mxf_get_stream_index(s, klv);
+
+ body_sid = find_body_sid_by_offset(mxf, klv->offset);
+ index = mxf_get_stream_index(s, klv, body_sid);
if (index < 0)
return AVERROR_INVALIDDATA;
// source size
partition->header_byte_count = avio_rb64(pb);
partition->index_byte_count = avio_rb64(pb);
partition->index_sid = avio_rb32(pb);
- avio_skip(pb, 8);
+ partition->body_offset = avio_rb64(pb);
partition->body_sid = avio_rb32(pb);
if (avio_read(pb, op, sizeof(UID)) != sizeof(UID)) {
av_log(mxf->fc, AV_LOG_ERROR, "Failed reading UID\n");
av_log(mxf->fc, AV_LOG_VERBOSE, "Multiple packages_refs\n");
av_free(mxf->packages_refs);
return mxf_read_strong_ref_array(pb, &mxf->packages_refs, &mxf->packages_count);
+ case 0x1902:
+ av_free(mxf->essence_container_data_refs);
+ return mxf_read_strong_ref_array(pb, &mxf->essence_container_data_refs, &mxf->essence_container_data_count);
}
return 0;
}
return 0;
}
+static int mxf_read_essence_container_data(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
+{
+ MXFEssenceContainerData *essence_data = arg;
+ switch(tag) {
+ case 0x2701:
+ /* linked package umid UMID */
+ avio_read(pb, essence_data->package_ul, 16);
+ avio_read(pb, essence_data->package_uid, 16);
+ break;
+ case 0x3f06:
+ essence_data->index_sid = avio_rb32(pb);
+ break;
+ case 0x3f07:
+ essence_data->body_sid = avio_rb32(pb);
+ break;
+ }
+ return 0;
+}
+
static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment)
{
int i, length;
* We want the smallest values for the keys than what we currently have, unless this is the first such entry this time around.
* If we come across an entry with the same IndexStartPosition but larger IndexDuration, then we'll prefer it over the one we currently have.
*/
- if ((i == 0 || s->body_sid > last_body_sid || s->index_sid > last_index_sid || s->index_start_position > last_index_start) &&
- (best == -1 || s->body_sid < best_body_sid || s->index_sid < best_index_sid || s->index_start_position < best_index_start ||
- (s->index_start_position == best_index_start && s->index_duration > best_index_duration))) {
+ if ((i == 0 ||
+ s->body_sid > last_body_sid ||
+ s->body_sid == last_body_sid && s->index_sid > last_index_sid ||
+ s->body_sid == last_body_sid && s->index_sid == last_index_sid && s->index_start_position > last_index_start) &&
+ (best == -1 ||
+ s->body_sid < best_body_sid ||
+ s->body_sid == best_body_sid && s->index_sid < best_index_sid ||
+ s->body_sid == best_body_sid && s->index_sid == best_index_sid && s->index_start_position < best_index_start ||
+ s->body_sid == best_body_sid && s->index_sid == best_index_sid && s->index_start_position == best_index_start && s->index_duration > best_index_duration)) {
best = j;
best_body_sid = s->body_sid;
best_index_sid = s->index_sid;
static int mxf_absolute_bodysid_offset(MXFContext *mxf, int body_sid, int64_t offset, int64_t *offset_out)
{
int x;
- int64_t offset_in = offset; /* for logging */
+ MXFPartition *last_p = NULL;
+
+ if (offset < 0)
+ return AVERROR(EINVAL);
for (x = 0; x < mxf->partitions_count; x++) {
MXFPartition *p = &mxf->partitions[x];
if (p->body_sid != body_sid)
continue;
- if (offset < p->essence_length || !p->essence_length) {
- *offset_out = p->essence_offset + offset;
- return 0;
- }
+ if (p->body_offset > offset)
+ break;
- offset -= p->essence_length;
+ last_p = p;
+ }
+
+ if (last_p && (!last_p->essence_length || last_p->essence_length > (offset - last_p->body_offset))) {
+ *offset_out = last_p->essence_offset + (offset - last_p->body_offset);
+ return 0;
}
av_log(mxf->fc, AV_LOG_ERROR,
"failed to find absolute offset of %"PRIX64" in BodySID %i - partial file?\n",
- offset_in, body_sid);
+ offset, body_sid);
return AVERROR_INVALIDDATA;
}
{
int i, j, k, ret, nb_sorted_segments;
MXFIndexTableSegment **sorted_segments = NULL;
- AVStream *st = NULL;
-
- for (i = 0; i < mxf->fc->nb_streams; i++) {
- if (mxf->fc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_DATA)
- continue;
- st = mxf->fc->streams[i];
- break;
- }
if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments)) ||
nb_sorted_segments <= 0) {
for (i = j = 0; j < mxf->nb_index_tables; i += mxf->index_tables[j++].nb_segments) {
MXFIndexTable *t = &mxf->index_tables[j];
+ MXFTrack *mxf_track = NULL;
t->segments = av_mallocz_array(t->nb_segments,
sizeof(*t->segments));
if ((ret = mxf_compute_ptses_fake_index(mxf, t)) < 0)
goto finish_decoding_index;
+ for (k = 0; k < mxf->fc->nb_streams; k++) {
+ MXFTrack *track = mxf->fc->streams[k]->priv_data;
+ if (track && track->index_sid == t->index_sid) {
+ mxf_track = track;
+ break;
+ }
+ }
+
/* fix zero IndexDurations */
for (k = 0; k < t->nb_segments; k++) {
if (t->segments[k]->index_duration)
av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment %i has zero IndexDuration and there's more than one segment\n",
t->index_sid, k);
- if (!st) {
+ if (!mxf_track) {
av_log(mxf->fc, AV_LOG_WARNING, "no streams?\n");
break;
}
/* assume the first stream's duration is reasonable
* leave index_duration = 0 on further segments in case we have any (unlikely)
*/
- t->segments[k]->index_duration = st->duration;
+ t->segments[k]->index_duration = mxf_track->original_duration;
break;
}
}
return NULL;
}
-static MXFPackage* mxf_resolve_source_package(MXFContext *mxf, UID package_uid)
+static MXFPackage* mxf_resolve_source_package(MXFContext *mxf, UID package_ul, UID package_uid)
{
MXFPackage *package = NULL;
int i;
if (!package)
continue;
- if (!memcmp(package->package_uid, package_uid, 16))
+ if (!memcmp(package->package_ul, package_ul, 16) && !memcmp(package->package_uid, package_uid, 16))
return package;
}
return NULL;
if (!component)
continue;
- if (!(package = mxf_resolve_source_package(mxf, component->source_package_uid)))
+ if (!(package = mxf_resolve_source_package(mxf, component->source_package_ul, component->source_package_uid)))
continue;
descriptor = mxf_resolve_strong_ref(mxf, &package->descriptor_ref, Descriptor);
if (!sourceclip)
continue;
- if (!(physical_package = mxf_resolve_source_package(mxf, sourceclip->source_package_uid)))
+ if (!(physical_package = mxf_resolve_source_package(mxf, sourceclip->source_package_ul, sourceclip->source_package_uid)))
break;
mxf_add_umid_metadata(&st->metadata, "reel_umid", physical_package);
if (!component)
continue;
- source_package = mxf_resolve_source_package(mxf, component->source_package_uid);
+ source_package = mxf_resolve_source_package(mxf, component->source_package_ul, component->source_package_uid);
if (!source_package) {
av_log(mxf->fc, AV_LOG_TRACE, "material track %d: no corresponding source package found\n", material_track->track_id);
continue;
av_log(mxf->fc, AV_LOG_ERROR, "material track %d: no corresponding source track found\n", material_track->track_id);
break;
}
+
+ for (k = 0; k < mxf->essence_container_data_count; k++) {
+ MXFEssenceContainerData *essence_data;
+
+ if (!(essence_data = mxf_resolve_strong_ref(mxf, &mxf->essence_container_data_refs[k], EssenceContainerData))) {
+ av_log(mxf, AV_LOG_TRACE, "could not resolve essence container data strong ref\n");
+ continue;
+ }
+ if (!memcmp(component->source_package_ul, essence_data->package_ul, sizeof(UID)) && !memcmp(component->source_package_uid, essence_data->package_uid, sizeof(UID))) {
+ source_track->body_sid = essence_data->body_sid;
+ source_track->index_sid = essence_data->index_sid;
+ break;
+ }
+ }
+
if(source_track && component)
break;
}
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x0c,0x00 }, mxf_read_pulldown_component, sizeof(MXFPulldownComponent), PulldownComponent },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x04,0x01,0x02,0x02,0x00,0x00 }, mxf_read_cryptographic_context, sizeof(MXFCryptoContext), CryptoContext },
{ { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x10,0x01,0x00 }, mxf_read_index_table_segment, sizeof(MXFIndexTableSegment), IndexTableSegment },
+ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x23,0x00 }, mxf_read_essence_container_data, sizeof(MXFEssenceContainerData), EssenceContainerData },
{ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, NULL, 0, AnyType },
};
if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key) ||
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)) {
+ IS_KLV_KEY(klv.key, mxf_system_item_key_cp) ||
+ IS_KLV_KEY(klv.key, mxf_system_item_key_gc)) {
if (!mxf->current_partition) {
av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to first PartitionPack\n");
* for OPAtom we still need the actual essence_offset though (the KL's length can vary)
*/
int64_t op1a_essence_offset =
- round_to_kag(mxf->current_partition->this_partition +
- mxf->current_partition->pack_length, mxf->current_partition->kag_size) +
+ mxf->current_partition->this_partition +
+ round_to_kag(mxf->current_partition->pack_length, mxf->current_partition->kag_size) +
round_to_kag(mxf->current_partition->header_byte_count, mxf->current_partition->kag_size) +
round_to_kag(mxf->current_partition->index_byte_count, mxf->current_partition->kag_size);
mxf->current_partition->essence_length = klv.length;
} else {
/* NOTE: op1a_essence_offset may be less than to klv.offset (C0023S01.mxf) */
- mxf->current_partition->essence_offset = op1a_essence_offset;
+ if (IS_KLV_KEY(klv.key, mxf_system_item_key_cp) || IS_KLV_KEY(klv.key, mxf_system_item_key_gc))
+ mxf->current_partition->essence_offset = klv.offset;
+ else
+ mxf->current_partition->essence_offset = op1a_essence_offset;
}
}
return ret;
}
+static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int index_sid)
+{
+ int i;
+ for (i = 0; i < mxf->nb_index_tables; i++)
+ if (mxf->index_tables[i].index_sid == index_sid)
+ return &mxf->index_tables[i];
+ return NULL;
+}
+
+/* Get the edit unit of the next packet from current_offset in a track. The returned edit unit can be original_duration as well! */
+static int mxf_get_next_track_edit_unit(MXFContext *mxf, MXFTrack *track, int64_t current_offset, int64_t *edit_unit_out)
+{
+ int64_t a, b, m, offset;
+ MXFIndexTable *t = mxf_find_index_table(mxf, track->index_sid);
+
+ if (!t || track->original_duration <= 0)
+ return -1;
+
+ a = -1;
+ b = track->original_duration;
+
+ while (b - a > 1) {
+ m = (a + b) >> 1;
+ if (mxf_edit_unit_absolute_offset(mxf, t, m, NULL, &offset, 0) < 0)
+ return -1;
+ if (offset < current_offset)
+ a = m;
+ else
+ b = m;
+ }
+
+ *edit_unit_out = b;
+
+ return 0;
+}
+
/**
* Sets mxf->current_edit_unit based on what offset we're currently at.
* @return next_ofs if OK, <0 on error
if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
IS_KLV_KEY(klv.key, mxf_canopus_essence_element_key) ||
IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) {
- int index = mxf_get_stream_index(s, &klv);
+ int body_sid = find_body_sid_by_offset(mxf, klv.offset);
+ int index = mxf_get_stream_index(s, &klv, body_sid);
int64_t next_ofs, next_klv;
AVStream *st;
int i;
av_freep(&mxf->packages_refs);
+ av_freep(&mxf->essence_container_data_refs);
for (i = 0; i < s->nb_streams; i++)
s->streams[i]->priv_data = NULL;
av_inv_q(source_track->edit_rate));
if (mxf->nb_index_tables <= 0) {
- if (!s->bit_rate)
- return AVERROR_INVALIDDATA;
- if (sample_time < 0)
- sample_time = 0;
- seconds = av_rescale(sample_time, st->time_base.num, st->time_base.den);
+ if (!s->bit_rate)
+ return AVERROR_INVALIDDATA;
+ if (sample_time < 0)
+ sample_time = 0;
+ seconds = av_rescale(sample_time, st->time_base.num, st->time_base.den);
- seekpos = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET);
- if (seekpos < 0)
- return seekpos;
+ seekpos = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET);
+ if (seekpos < 0)
+ return seekpos;
- ff_update_cur_dts(s, st, sample_time);
- mxf->current_edit_unit = sample_time;
+ ff_update_cur_dts(s, st, sample_time);
+ mxf->current_edit_unit = sample_time;
} else {
t = &mxf->index_tables[0];
+ if (t->index_sid != source_track->index_sid) {
+ /* If the first index table does not belong to the stream, then find a stream which does belong to the index table */
+ for (i = 0; i < s->nb_streams; i++) {
+ MXFTrack *new_source_track = s->streams[i]->priv_data;
+ if (new_source_track && new_source_track->index_sid == t->index_sid) {
+ sample_time = av_rescale_q(sample_time, new_source_track->edit_rate, source_track->edit_rate);
+ source_track = new_source_track;
+ st = s->streams[i];
+ break;
+ }
+ }
+ if (i == s->nb_streams)
+ return AVERROR_INVALIDDATA;
+ }
/* clamp above zero, else ff_index_search_timestamp() returns negative
* this also means we allow seeking before the start */
for (i = 0; i < s->nb_streams; i++) {
AVStream *cur_st = s->streams[i];
MXFTrack *cur_track = cur_st->priv_data;
- uint64_t current_sample_count = 0;
if (cur_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- ret = mxf_compute_sample_count(mxf, i, ¤t_sample_count);
- if (ret < 0)
- return ret;
-
- cur_track->sample_count = current_sample_count;
+ int64_t track_edit_unit;
+ if (st != cur_st && mxf_get_next_track_edit_unit(mxf, cur_track, seekpos, &track_edit_unit) >= 0) {
+ cur_track->sample_count = av_rescale_q(track_edit_unit,
+ av_inv_q(cur_track->edit_rate),
+ cur_st->time_base);
+ } else {
+ uint64_t current_sample_count = 0;
+ ret = mxf_compute_sample_count(mxf, i, ¤t_sample_count);
+ if (ret < 0)
+ return ret;
+ cur_track->sample_count = current_sample_count;
+ }
}
}
return 0;