X-Git-Url: https://git.piment-noir.org/?p=deb_ffmpeg.git;a=blobdiff_plain;f=ffmpeg%2Flibavformat%2Fmxfdec.c;h=0c88a8ae9cbda0a7db8ccef79ce5a8e1625c4b72;hp=1dcdae0a08429297aa950564224274c713af9a2e;hb=f6fa7814ccfe3e76514b36cf04f5cd3cb657c8cf;hpb=2ba45a602cbfa7b771effba9b11bb4245c21bc00 diff --git a/ffmpeg/libavformat/mxfdec.c b/ffmpeg/libavformat/mxfdec.c index 1dcdae0..0c88a8a 100644 --- a/ffmpeg/libavformat/mxfdec.c +++ b/ffmpeg/libavformat/mxfdec.c @@ -89,6 +89,7 @@ typedef struct { int64_t header_byte_count; int64_t index_byte_count; int pack_length; + int64_t pack_ofs; ///< absolute offset of pack in file, including run-in } MXFPartition; typedef struct { @@ -126,6 +127,20 @@ typedef struct { AVTimecode tc; } MXFTimecodeComponent; +typedef struct { + UID uid; + enum MXFMetadataSetType type; + UID input_segment_ref; +} MXFPulldownComponent; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; + UID *structural_components_refs; + int structural_components_count; + int64_t duration; +} MXFEssenceGroup; + typedef struct { UID uid; enum MXFMetadataSetType type; @@ -154,6 +169,7 @@ typedef struct { int field_dominance; int channels; int bits_per_sample; + int64_t duration; /* ContainerDuration optional property */ unsigned int component_depth; unsigned int horiz_subsampling; unsigned int vert_subsampling; @@ -188,6 +204,7 @@ typedef struct { int tracks_count; MXFDescriptor *descriptor; /* only one */ UID descriptor_ref; + char *name; } MXFPackage; typedef struct { @@ -219,7 +236,6 @@ typedef struct { struct AVAES *aesc; uint8_t *local_tags; int local_tags_count; - uint64_t last_partition; uint64_t footer_partition; KLVPacket current_klv_data; int current_klv_index; @@ -425,6 +441,10 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U av_log(mxf->fc, AV_LOG_ERROR, "item_num %d is too large\n", item_num); return AVERROR_INVALIDDATA; } + if (mxf->local_tags) + av_log(mxf->fc, AV_LOG_VERBOSE, "Multiple primer packs\n"); + av_free(mxf->local_tags); + mxf->local_tags_count = 0; mxf->local_tags = av_calloc(item_num, item_len); if (!mxf->local_tags) return AVERROR(ENOMEM); @@ -461,6 +481,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size memset(partition, 0, sizeof(*partition)); mxf->partitions_count++; partition->pack_length = avio_tell(pb) - klv_offset + size; + partition->pack_ofs = klv_offset; switch(uid[13]) { case 2: @@ -536,6 +557,7 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size partition->index_sid, partition->body_sid); /* sanity check PreviousPartition if set */ + //NOTE: this isn't actually enough, see mxf_seek_to_previous_partition() if (partition->previous_partition && mxf->run_in + partition->previous_partition >= klv_offset) { av_log(mxf->fc, AV_LOG_ERROR, @@ -619,6 +641,9 @@ static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int siz MXFContext *mxf = arg; switch (tag) { case 0x1901: + if (mxf->packages_refs) + av_log(mxf->fc, AV_LOG_VERBOSE, "Multiple packages_refs\n"); + av_free(mxf->packages_refs); mxf->packages_count = avio_rb32(pb); mxf->packages_refs = av_calloc(mxf->packages_count, sizeof(UID)); if (!mxf->packages_refs) @@ -652,22 +677,6 @@ static int mxf_read_source_clip(void *arg, AVIOContext *pb, int tag, int size, U return 0; } -static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) -{ - MXFPackage *package = arg; - switch(tag) { - case 0x4403: - package->tracks_count = avio_rb32(pb); - package->tracks_refs = av_calloc(package->tracks_count, sizeof(UID)); - if (!package->tracks_refs) - 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_timecode_component(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFTimecodeComponent *mxf_timecode = arg; @@ -685,6 +694,17 @@ static int mxf_read_timecode_component(void *arg, AVIOContext *pb, int tag, int return 0; } +static int mxf_read_pulldown_component(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFPulldownComponent *mxf_pulldown = arg; + switch(tag) { + case 0x0d01: + avio_read(pb, mxf_pulldown->input_segment_ref, 16); + break; + } + return 0; +} + static int mxf_read_track(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFTrack *track = arg; @@ -722,8 +742,10 @@ static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID case 0x1001: sequence->structural_components_count = avio_rb32(pb); sequence->structural_components_refs = av_calloc(sequence->structural_components_count, sizeof(UID)); - if (!sequence->structural_components_refs) + if (!sequence->structural_components_refs) { + sequence->structural_components_count = 0; 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; @@ -731,7 +753,49 @@ static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID return 0; } -static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +static int mxf_read_essence_group(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFEssenceGroup *essence_group = arg; + switch (tag) { + case 0x0202: + essence_group->duration = avio_rb64(pb); + break; + case 0x0501: + essence_group->structural_components_count = avio_rb32(pb); + essence_group->structural_components_refs = av_calloc(essence_group->structural_components_count, sizeof(UID)); + if (!essence_group->structural_components_refs) { + essence_group->structural_components_count = 0; + return AVERROR(ENOMEM); + } + avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ + avio_read(pb, (uint8_t *)essence_group->structural_components_refs, essence_group->structural_components_count * sizeof(UID)); + break; + } + return 0; +} + +static int mxf_read_utf16_string(AVIOContext *pb, int size, char** str) +{ + int ret; + size_t buf_size; + + if (size < 0) + return AVERROR(EINVAL); + + buf_size = size + size / 2 + 1; + *str = av_malloc(buf_size); + if (!*str) + return AVERROR(ENOMEM); + + if ((ret = avio_get_str16be(pb, size, *str, buf_size)) < 0) { + av_freep(str); + return ret; + } + + return ret; +} + +static int mxf_read_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFPackage *package = arg; switch(tag) { @@ -751,6 +815,8 @@ static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size case 0x4701: avio_read(pb, package->descriptor_ref, 16); break; + case 0x4402: + return mxf_read_utf16_string(pb, size, &package->name); } return 0; } @@ -838,7 +904,6 @@ 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); @@ -848,6 +913,9 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int 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; + case 0x3002: /* ContainerDuration */ + descriptor->duration = avio_rb64(pb); + break; case 0x3004: avio_read(pb, descriptor->essence_container_ul, 16); break; @@ -960,6 +1028,7 @@ static const MXFCodecUL mxf_picture_essence_container_uls[] = { { { 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 */ + { { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0xff,0x4b,0x46,0x41,0x41,0x00,0x0d,0x4d,0x4f }, 14, AV_CODEC_ID_RAWVIDEO }, /* Legacy ?? Uncompressed Picture */ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, }; @@ -1366,6 +1435,34 @@ static int mxf_is_intra_only(MXFDescriptor *descriptor) &descriptor->essence_codec_ul)->id != AV_CODEC_ID_NONE; } +static int mxf_uid_to_str(UID uid, char **str) +{ + int i; + char *p; + p = *str = av_mallocz(sizeof(UID) * 2 + 4 + 1); + if (!p) + return AVERROR(ENOMEM); + for (i = 0; i < sizeof(UID); i++) { + snprintf(p, 2 + 1, "%.2x", uid[i]); + p += 2; + if (i == 3 || i == 5 || i == 7 || i == 9) { + snprintf(p, 1 + 1, "-"); + p++; + } + } + return 0; +} + +static int mxf_add_uid_metadata(AVDictionary **pm, const char *key, UID uid) +{ + char *str; + int ret; + if ((ret = mxf_uid_to_str(uid, &str)) < 0) + return ret; + av_dict_set(pm, key, str, AV_DICT_DONT_STRDUP_VAL); + return 0; +} + static int mxf_add_timecode_metadata(AVDictionary **pm, const char *key, AVTimecode *tc) { char buf[AV_TIMECODE_STR_SIZE]; @@ -1374,10 +1471,155 @@ static int mxf_add_timecode_metadata(AVDictionary **pm, const char *key, AVTimec return 0; } +static MXFTimecodeComponent* mxf_resolve_timecode_component(MXFContext *mxf, UID *strong_ref) +{ + MXFStructuralComponent *component = NULL; + MXFPulldownComponent *pulldown = NULL; + + component = mxf_resolve_strong_ref(mxf, strong_ref, AnyType); + if (!component) + return NULL; + + switch (component->type) { + case TimecodeComponent: + return (MXFTimecodeComponent*)component; + case PulldownComponent: /* timcode component may be located on a pulldown component */ + pulldown = (MXFPulldownComponent*)component; + return mxf_resolve_strong_ref(mxf, &pulldown->input_segment_ref, TimecodeComponent); + default: + break; + } + return NULL; +} + +static MXFPackage* mxf_resolve_source_package(MXFContext *mxf, UID package_uid) +{ + MXFPackage *package = NULL; + int i; + + for (i = 0; i < mxf->packages_count; i++) { + package = mxf_resolve_strong_ref(mxf, &mxf->packages_refs[i], SourcePackage); + if (!package) + continue; + + if (!memcmp(package->package_uid, package_uid, 16)) + return package; + } + return NULL; +} + +static MXFStructuralComponent* mxf_resolve_essence_group_choice(MXFContext *mxf, MXFEssenceGroup *essence_group) +{ + MXFStructuralComponent *component = NULL; + MXFPackage *package = NULL; + MXFDescriptor *descriptor = NULL; + int i; + + if (!essence_group || !essence_group->structural_components_count) + return NULL; + + /* essence groups contains multiple representations of the same media, + this return the first components with a valid Descriptor typically index 0 */ + for (i =0; i < essence_group->structural_components_count; i++){ + component = mxf_resolve_strong_ref(mxf, &essence_group->structural_components_refs[i], SourceClip); + if (!component) + continue; + + if (!(package = mxf_resolve_source_package(mxf, component->source_package_uid))) + continue; + + descriptor = mxf_resolve_strong_ref(mxf, &package->descriptor_ref, Descriptor); + if (descriptor){ + /* HACK: force the duration of the component to match the duration of the descriptor */ + if (descriptor->duration != AV_NOPTS_VALUE) + component->duration = descriptor->duration; + return component; + } + } + return NULL; +} + +static MXFStructuralComponent* mxf_resolve_sourceclip(MXFContext *mxf, UID *strong_ref) +{ + MXFStructuralComponent *component = NULL; + + component = mxf_resolve_strong_ref(mxf, strong_ref, AnyType); + if (!component) + return NULL; + switch (component->type) { + case SourceClip: + return component; + case EssenceGroup: + return mxf_resolve_essence_group_choice(mxf, (MXFEssenceGroup*) component); + default: + break; + } + return NULL; +} + +static int mxf_parse_physical_source_package(MXFContext *mxf, MXFTrack *source_track, AVStream *st) +{ + MXFPackage *physical_package = NULL; + MXFTrack *physical_track = NULL; + MXFStructuralComponent *sourceclip = NULL; + MXFTimecodeComponent *mxf_tc = NULL; + int i, j, k; + AVTimecode tc; + int flags; + int64_t start_position; + + for (i = 0; i < source_track->sequence->structural_components_count; i++) { + sourceclip = mxf_resolve_strong_ref(mxf, &source_track->sequence->structural_components_refs[i], SourceClip); + if (!sourceclip) + continue; + + if (!(physical_package = mxf_resolve_source_package(mxf, sourceclip->source_package_uid))) + break; + + mxf_add_uid_metadata(&st->metadata, "reel_uid", physical_package->package_uid); + + /* the name of physical source package is name of the reel or tape */ + if (physical_package->name && physical_package->name[0]) + av_dict_set(&st->metadata, "reel_name", physical_package->name, 0); + + /* the source timecode is calculated by adding the start_position of the sourceclip from the file source package track + * to the start_frame of the timecode component located on one of the tracks of the physical source package. + */ + for (j = 0; j < physical_package->tracks_count; j++) { + if (!(physical_track = mxf_resolve_strong_ref(mxf, &physical_package->tracks_refs[j], Track))) { + av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track strong ref\n"); + continue; + } + + if (!(physical_track->sequence = mxf_resolve_strong_ref(mxf, &physical_track->sequence_ref, Sequence))) { + av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n"); + continue; + } + + for (k = 0; k < physical_track->sequence->structural_components_count; k++) { + if (!(mxf_tc = mxf_resolve_timecode_component(mxf, &physical_track->sequence->structural_components_refs[k]))) + continue; + + flags = mxf_tc->drop_frame == 1 ? AV_TIMECODE_FLAG_DROPFRAME : 0; + /* scale sourceclip start_position to match physical track edit rate */ + start_position = av_rescale_q(sourceclip->start_position, + physical_track->edit_rate, + source_track->edit_rate); + + if (av_timecode_init(&tc, mxf_tc->rate, flags, start_position + mxf_tc->start_frame, mxf->fc) == 0) { + mxf_add_timecode_metadata(&st->metadata, "timecode", &tc); + return 0; + } + } + } + } + + return 0; +} + static int mxf_parse_structural_metadata(MXFContext *mxf) { MXFPackage *material_package = NULL; - MXFPackage *temp_package = NULL; int i, j, k, ret; av_dlog(mxf->fc, "metadata sets count %d\n", mxf->metadata_sets_count); @@ -1391,6 +1633,10 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) return AVERROR_INVALIDDATA; } + mxf_add_uid_metadata(&mxf->fc->metadata, "material_package_uid", material_package->package_uid); + if (material_package->name && material_package->name[0]) + av_dict_set(&mxf->fc->metadata, "material_package_name", material_package->name, 0); + for (i = 0; i < material_package->tracks_count; i++) { MXFPackage *source_package = NULL; MXFTrack *material_track = NULL; @@ -1440,19 +1686,11 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) /* TODO: handle multiple source clips */ for (j = 0; j < material_track->sequence->structural_components_count; j++) { - component = mxf_resolve_strong_ref(mxf, &material_track->sequence->structural_components_refs[j], SourceClip); + component = mxf_resolve_sourceclip(mxf, &material_track->sequence->structural_components_refs[j]); if (!component) continue; - for (k = 0; k < mxf->packages_count; k++) { - temp_package = mxf_resolve_strong_ref(mxf, &mxf->packages_refs[k], SourcePackage); - if (!temp_package) - continue; - if (!memcmp(temp_package->package_uid, component->source_package_uid, 16)) { - source_package = temp_package; - break; - } - } + source_package = mxf_resolve_source_package(mxf, component->source_package_uid); if (!source_package) { av_dlog(mxf->fc, "material track %d: no corresponding source package found\n", material_track->track_id); break; @@ -1571,6 +1809,12 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } av_log(mxf->fc, AV_LOG_VERBOSE, "\n"); + mxf_add_uid_metadata(&st->metadata, "file_package_uid", source_package->package_uid); + if (source_package->name && source_package->name[0]) + av_dict_set(&st->metadata, "file_package_name", source_package->name, 0); + + mxf_parse_physical_source_package(mxf, source_track, st); + 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); @@ -1606,6 +1850,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) avpriv_request_sample(mxf->fc, "Field dominance %d support", descriptor->field_dominance); + case 0: // we already have many samples with field_dominance == unknown break; } /* Turn field height into frame height. */ @@ -1638,6 +1883,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) if (source_track->sequence->origin) { av_dict_set_int(&st->metadata, "source_track_origin", source_track->sequence->origin, 0); } + if (descriptor->aspect_ratio.num && descriptor->aspect_ratio.den) + st->display_aspect_ratio = descriptor->aspect_ratio; } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul); /* Only overwrite existing codec ID if it is unset or A-law, which is the default according to SMPTE RP 224. */ @@ -1706,45 +1953,6 @@ fail_and_free: return ret; } -static int mxf_read_utf16_string(AVIOContext *pb, int size, char** str) -{ - int ret; - size_t buf_size; - - if (size < 0) - return AVERROR(EINVAL); - - buf_size = size + size / 2 + 1; - *str = av_malloc(buf_size); - if (!*str) - return AVERROR(ENOMEM); - - if ((ret = avio_get_str16be(pb, size, *str, buf_size)) < 0) { - av_freep(str); - return ret; - } - - return ret; -} - -static int mxf_uid_to_str(UID uid, char **str) -{ - int i; - char *p; - p = *str = av_mallocz(sizeof(UID) * 2 + 4 + 1); - if (!p) - return AVERROR(ENOMEM); - for (i = 0; i < sizeof(UID); i++) { - snprintf(p, 2 + 1, "%.2x", uid[i]); - p += 2; - if (i == 3 || i == 5 || i == 7 || i == 9) { - snprintf(p, 1 + 1, "-"); - p++; - } - } - return 0; -} - static int mxf_timestamp_to_str(uint64_t timestamp, char **str) { struct tm time = { 0 }; @@ -1767,7 +1975,8 @@ static int mxf_timestamp_to_str(uint64_t timestamp, char **str) *str = av_mallocz(32); if (!*str) return AVERROR(ENOMEM); - strftime(*str, 32, "%Y-%m-%d %H:%M:%S", &time); + if (!strftime(*str, 32, "%Y-%m-%d %H:%M:%S", &time)) + str[0] = '\0'; return 0; } @@ -1843,9 +2052,10 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }, mxf_read_partition_pack }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x30,0x00 }, mxf_read_identification_metadata }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x18,0x00 }, mxf_read_content_storage, 0, AnyType }, - { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x37,0x00 }, mxf_read_source_package, sizeof(MXFPackage), SourcePackage }, - { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x36,0x00 }, mxf_read_material_package, sizeof(MXFPackage), MaterialPackage }, + { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x37,0x00 }, mxf_read_package, sizeof(MXFPackage), SourcePackage }, + { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x36,0x00 }, mxf_read_package, sizeof(MXFPackage), MaterialPackage }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x0f,0x00 }, mxf_read_sequence, sizeof(MXFSequence), Sequence }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x05,0x00 }, mxf_read_essence_group, sizeof(MXFEssenceGroup), EssenceGroup}, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x11,0x00 }, mxf_read_source_clip, sizeof(MXFStructuralComponent), SourceClip }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x44,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), MultipleDescriptor }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x42,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* Generic Sound */ @@ -1860,11 +2070,26 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent }, + { { 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 }, { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, NULL, 0, AnyType }, }; +static int mxf_metadataset_init(MXFMetadataSet *ctx, enum MXFMetadataSetType type) +{ + switch (type){ + case MultipleDescriptor: + case Descriptor: + ((MXFDescriptor*)ctx)->pix_fmt = AV_PIX_FMT_NONE; + ((MXFDescriptor*)ctx)->duration = AV_NOPTS_VALUE; + break; + default: + break; + } + return 0; +} + static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadFunc *read_child, int ctx_size, enum MXFMetadataSetType type) { AVIOContext *pb = mxf->fc->pb; @@ -1873,6 +2098,7 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF if (!ctx) return AVERROR(ENOMEM); + mxf_metadataset_init(ctx, type); while (avio_tell(pb) + 4 < klv_end && !avio_feof(pb)) { int ret; int tag = avio_rb16(pb); @@ -1919,23 +2145,98 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF } /** - * Seeks to the previous partition, if possible + * Matches any partition pack key, in other words: + * - HeaderPartition + * - BodyPartition + * - FooterPartition + * @return non-zero if the key is a partition pack key, zero otherwise + */ +static int mxf_is_partition_pack_key(UID key) +{ + //NOTE: this is a little lax since it doesn't constraint key[14] + return !memcmp(key, mxf_header_partition_pack_key, 13) && + key[13] >= 2 && key[13] <= 4; +} + +/** + * Parses a metadata KLV + * @return <0 on error, 0 otherwise + */ +static int mxf_parse_klv(MXFContext *mxf, KLVPacket klv, MXFMetadataReadFunc *read, + int ctx_size, enum MXFMetadataSetType type) +{ + AVFormatContext *s = mxf->fc; + int res; + if (klv.key[5] == 0x53) { + res = mxf_read_local_tags(mxf, &klv, read, ctx_size, type); + } else { + uint64_t next = avio_tell(s->pb) + klv.length; + res = 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 res; + } + return 0; +} + +/** + * Seeks to the previous partition and parses it, if possible * @return <= 0 if we should stop parsing, > 0 if we should keep going */ static int mxf_seek_to_previous_partition(MXFContext *mxf) { AVIOContext *pb = mxf->fc->pb; + KLVPacket klv; + int64_t current_partition_ofs; + int ret; if (!mxf->current_partition || mxf->run_in + mxf->current_partition->previous_partition <= mxf->last_forward_tell) return 0; /* we've parsed all partitions */ /* seek to previous partition */ + current_partition_ofs = mxf->current_partition->pack_ofs; //includes run-in avio_seek(pb, mxf->run_in + mxf->current_partition->previous_partition, SEEK_SET); mxf->current_partition = NULL; av_dlog(mxf->fc, "seeking to previous partition\n"); + /* Make sure this is actually a PartitionPack, and if so parse it. + * See deadlock2.mxf + */ + if ((ret = klv_read_packet(&klv, pb)) < 0) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to read PartitionPack KLV\n"); + return ret; + } + + if (!mxf_is_partition_pack_key(klv.key)) { + av_log(mxf->fc, AV_LOG_ERROR, "PreviousPartition @ %" PRIx64 " isn't a PartitionPack\n", klv.offset); + return AVERROR_INVALIDDATA; + } + + /* We can't just check ofs >= current_partition_ofs because PreviousPartition + * can point to just before the current partition, causing klv_read_packet() + * to sync back up to it. See deadlock3.mxf + */ + if (klv.offset >= current_partition_ofs) { + av_log(mxf->fc, AV_LOG_ERROR, "PreviousPartition for PartitionPack @ %" + PRIx64 " indirectly points to itself\n", current_partition_ofs); + return AVERROR_INVALIDDATA; + } + + if ((ret = mxf_parse_klv(mxf, klv, mxf_read_partition_pack, 0, 0)) < 0) + return ret; + return 1; } @@ -1951,30 +2252,27 @@ static int mxf_parse_handle_essence(MXFContext *mxf) if (mxf->parsing_backward) { return mxf_seek_to_previous_partition(mxf); } else { - uint64_t offset = mxf->footer_partition ? mxf->footer_partition - : mxf->last_partition; - - if (!offset) { - av_dlog(mxf->fc, "no last partition\n"); + if (!mxf->footer_partition) { + av_dlog(mxf->fc, "no FooterPartition\n"); return 0; } - av_dlog(mxf->fc, "seeking to last partition\n"); + av_dlog(mxf->fc, "seeking to FooterPartition\n"); /* remember where we were so we don't end up seeking further back than this */ mxf->last_forward_tell = avio_tell(pb); if (!pb->seekable) { - av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing last partition\n"); + av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing FooterPartition\n"); return -1; } - /* seek to last partition and parse backward */ - if ((ret = avio_seek(pb, mxf->run_in + offset, SEEK_SET)) < 0) { + /* seek to FooterPartition and parse backward */ + if ((ret = avio_seek(pb, mxf->run_in + mxf->footer_partition, SEEK_SET)) < 0) { av_log(mxf->fc, AV_LOG_ERROR, - "failed to seek to last partition @ 0x%" PRIx64 + "failed to seek to FooterPartition @ 0x%" PRIx64 " (%"PRId64") - partial file?\n", - mxf->run_in + offset, ret); + mxf->run_in + mxf->footer_partition, ret); return ret; } @@ -2012,7 +2310,7 @@ static void mxf_compute_essence_containers(MXFContext *mxf) continue; /* BodySID == 0 -> no essence */ if (x >= mxf->partitions_count - 1) - break; /* last partition - can't compute length (and we don't need to) */ + break; /* FooterPartition - can't compute length (and we don't need to) */ /* essence container spans to the next partition */ p->essence_length = mxf->partitions[x+1].this_partition - p->essence_offset; @@ -2075,16 +2373,33 @@ static void mxf_read_random_index_pack(AVFormatContext *s) { MXFContext *mxf = s->priv_data; uint32_t length; - int64_t file_size; + int64_t file_size, max_rip_length, min_rip_length; KLVPacket klv; if (!s->pb->seekable) return; file_size = avio_size(s->pb); + + /* S377m says to check the RIP length for "silly" values, without defining "silly". + * The limit below assumes a file with nothing but partition packs and a RIP. + * Before changing this, consider that a muxer may place each sample in its own partition. + * + * 105 is the size of the smallest possible PartitionPack + * 12 is the size of each RIP entry + * 28 is the size of the RIP header and footer, assuming an 8-byte BER + */ + max_rip_length = ((file_size - mxf->run_in) / 105) * 12 + 28; + max_rip_length = FFMIN(max_rip_length, INT_MAX); //2 GiB and up is also silly + + /* We're only interested in RIPs with at least two entries.. */ + min_rip_length = 16+1+24+4; + + /* See S377m section 11 */ avio_seek(s->pb, file_size - 4, SEEK_SET); length = avio_rb32(s->pb); - if (length <= 32 || length >= FFMIN(file_size, INT_MAX)) + + if (length < min_rip_length || length > max_rip_length) goto end; avio_seek(s->pb, file_size - length, SEEK_SET); if (klv_read_packet(&klv, s->pb) < 0 || @@ -2093,7 +2408,13 @@ static void mxf_read_random_index_pack(AVFormatContext *s) goto end; avio_skip(s->pb, klv.length - 12); - mxf->last_partition = avio_rb64(s->pb); + mxf->footer_partition = avio_rb64(s->pb); + + /* sanity check */ + if (mxf->run_in + mxf->footer_partition >= file_size) { + av_log(s, AV_LOG_WARNING, "bad FooterPartition in RIP - ignoring\n"); + mxf->footer_partition = 0; + } end: avio_seek(s->pb, mxf->run_in, SEEK_SET); @@ -2104,8 +2425,6 @@ static int mxf_read_header(AVFormatContext *s) MXFContext *mxf = s->priv_data; KLVPacket klv; int64_t essence_offset = 0; - int64_t last_pos = -1; - uint64_t last_pos_index = 1; int ret; mxf->last_forward_tell = INT64_MAX; @@ -2123,12 +2442,7 @@ static int mxf_read_header(AVFormatContext *s) while (!avio_feof(s->pb)) { const MXFMetadataReadTableEntry *metadata; - if (avio_tell(s->pb) == last_pos) { - av_log(mxf->fc, AV_LOG_ERROR, "MXF structure loop detected\n"); - return AVERROR_INVALIDDATA; - } - if ((1ULL<<61) % last_pos_index++ == 0) - last_pos = avio_tell(s->pb); + if (klv_read_packet(&klv, s->pb) < 0) { /* EOF - seek to previous partition or stop */ if(mxf_parse_handle_partition_or_eof(mxf) <= 0) @@ -2180,8 +2494,7 @@ static int mxf_read_header(AVFormatContext *s) if (mxf_parse_handle_essence(mxf) <= 0) break; continue; - } else if (!memcmp(klv.key, mxf_header_partition_pack_key, 13) && - klv.key[13] >= 2 && klv.key[13] <= 4 && mxf->current_partition) { + } else if (mxf_is_partition_pack_key(klv.key) && mxf->current_partition) { /* next partition pack - keep going, seek to previous partition or stop */ if(mxf_parse_handle_partition_or_eof(mxf) <= 0) break; @@ -2192,26 +2505,8 @@ static int mxf_read_header(AVFormatContext *s) for (metadata = mxf_metadata_read_table; metadata->read; metadata++) { if (IS_KLV_KEY(klv.key, metadata->key)) { - int res; - if (klv.key[5] == 0x53) { - res = mxf_read_local_tags(mxf, &klv, metadata->read, metadata->ctx_size, metadata->type); - } 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 res; - } + if ((ret = mxf_parse_klv(mxf, klv, metadata->read, metadata->ctx_size, metadata->type)) < 0) + goto fail; break; } else { av_log(s, AV_LOG_VERBOSE, "Dark key " PRIxUID "\n", @@ -2426,7 +2721,7 @@ static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) pkt->stream_index = index; pkt->pos = klv.offset; - codec = s->streams[index]->codec; + codec = st->codec; if (codec->codec_type == AVMEDIA_TYPE_VIDEO && next_ofs >= 0) { /* mxf->current_edit_unit good - see if we have an @@ -2541,9 +2836,13 @@ static int mxf_read_close(AVFormatContext *s) case Sequence: av_freep(&((MXFSequence *)mxf->metadata_sets[i])->structural_components_refs); break; + case EssenceGroup: + av_freep(&((MXFEssenceGroup *)mxf->metadata_sets[i])->structural_components_refs); + break; case SourcePackage: case MaterialPackage: av_freep(&((MXFPackage *)mxf->metadata_sets[i])->tracks_refs); + av_freep(&((MXFPackage *)mxf->metadata_sets[i])->name); break; case IndexTableSegment: seg = (MXFIndexTableSegment *)mxf->metadata_sets[i];