X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmxfdec.c;h=9a48e2d2d1013479089832d5409217840e503932;hb=36e156bef02566d70cea46cc5e00b3e5d5ed3286;hp=8e1089620ffba708e5d6d6f7159cf21aff900f19;hpb=3eea8edf618079e44719fe059ddd953b21ef1ba9;p=ffmpeg diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 8e1089620ff..9a48e2d2d10 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -52,6 +52,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/parseutils.h" #include "libavutil/timecode.h" +#include "libavutil/opt.h" #include "avformat.h" #include "internal.h" #include "mxf.h" @@ -265,6 +266,7 @@ typedef struct MXFIndexTable { } MXFIndexTable; typedef struct MXFContext { + const AVClass *class; /**< Class for private options. */ MXFPartition *partitions; unsigned partitions_count; MXFOP op; @@ -287,6 +289,7 @@ typedef struct MXFContext { int last_forward_partition; int nb_index_tables; MXFIndexTable *index_tables; + int eia608_extract; } MXFContext; /* NOTE: klv_offset is not set (-1) for local keys */ @@ -426,19 +429,19 @@ static int mxf_get_stream_index(AVFormatContext *s, KLVPacket *klv, int body_sid return s->nb_streams == 1 && s->streams[0]->priv_data ? 0 : -1; } -static int find_body_sid_by_offset(MXFContext *mxf, int64_t offset) +static int find_body_sid_by_absolute_offset(MXFContext *mxf, int64_t offset) { // we look for partition where the offset is placed int a, b, m; - int64_t this_partition; + int64_t pack_ofs; 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) + m = (a + b) >> 1; + pack_ofs = mxf->partitions[m].pack_ofs; + if (pack_ofs <= offset) a = m; else b = m; @@ -449,6 +452,81 @@ static int find_body_sid_by_offset(MXFContext *mxf, int64_t offset) return mxf->partitions[a].body_sid; } +static int mxf_get_eia608_packet(AVFormatContext *s, AVStream *st, AVPacket *pkt, int64_t length) +{ + int count = avio_rb16(s->pb); + int cdp_identifier, cdp_length, cdp_footer_id, ccdata_id, cc_count; + int line_num, sample_coding, sample_count; + int did, sdid, data_length; + int i, ret; + + if (count != 1) + av_log(s, AV_LOG_WARNING, "unsupported multiple ANC packets (%d) per KLV packet\n", count); + + for (i = 0; i < count; i++) { + if (length < 6) { + av_log(s, AV_LOG_ERROR, "error reading s436m packet %"PRId64"\n", length); + return AVERROR_INVALIDDATA; + } + line_num = avio_rb16(s->pb); + avio_r8(s->pb); // wrapping type + sample_coding = avio_r8(s->pb); + sample_count = avio_rb16(s->pb); + length -= 6 + 8 + sample_count; + if (line_num != 9 && line_num != 11) + continue; + if (sample_coding == 7 || sample_coding == 8 || sample_coding == 9) { + av_log(s, AV_LOG_WARNING, "unsupported s436m 10 bit sample coding\n"); + continue; + } + if (length < 0) + return AVERROR_INVALIDDATA; + + avio_rb32(s->pb); // array count + avio_rb32(s->pb); // array elem size + did = avio_r8(s->pb); + sdid = avio_r8(s->pb); + data_length = avio_r8(s->pb); + if (did != 0x61 || sdid != 1) { + av_log(s, AV_LOG_WARNING, "unsupported did or sdid: %x %x\n", did, sdid); + continue; + } + cdp_identifier = avio_rb16(s->pb); // cdp id + if (cdp_identifier != 0x9669) { + av_log(s, AV_LOG_ERROR, "wrong cdp identifier %x\n", cdp_identifier); + return AVERROR_INVALIDDATA; + } + cdp_length = avio_r8(s->pb); + avio_r8(s->pb); // cdp_frame_rate + avio_r8(s->pb); // cdp_flags + avio_rb16(s->pb); // cdp_hdr_sequence_cntr + ccdata_id = avio_r8(s->pb); // ccdata_id + if (ccdata_id != 0x72) { + av_log(s, AV_LOG_ERROR, "wrong cdp data section %x\n", ccdata_id); + return AVERROR_INVALIDDATA; + } + cc_count = avio_r8(s->pb) & 0x1f; + ret = av_get_packet(s->pb, pkt, cc_count * 3); + if (ret < 0) + return ret; + if (cdp_length - 9 - 4 < cc_count * 3) { + av_log(s, AV_LOG_ERROR, "wrong cdp size %d cc count %d\n", cdp_length, cc_count); + return AVERROR_INVALIDDATA; + } + avio_skip(s->pb, data_length - 9 - 4 - cc_count * 3); + cdp_footer_id = avio_r8(s->pb); + if (cdp_footer_id != 0x74) { + av_log(s, AV_LOG_ERROR, "wrong cdp footer section %x\n", cdp_footer_id); + return AVERROR_INVALIDDATA; + } + avio_rb16(s->pb); // cdp_ftr_sequence_cntr + avio_r8(s->pb); // packet_checksum + break; + } + + return 0; +} + /* XXX: use AVBitStreamFilter */ static int mxf_get_d10_aes3_packet(AVIOContext *pb, AVStream *st, AVPacket *pkt, int64_t length) { @@ -512,7 +590,7 @@ static int mxf_decrypt_triplet(AVFormatContext *s, AVPacket *pkt, KLVPacket *klv if (!IS_KLV_KEY(klv, mxf_essence_element_key)) return AVERROR_INVALIDDATA; - body_sid = find_body_sid_by_offset(mxf, klv->offset); + body_sid = find_body_sid_by_absolute_offset(mxf, klv->offset); index = mxf_get_stream_index(s, klv, body_sid); if (index < 0) return AVERROR_INVALIDDATA; @@ -576,6 +654,7 @@ static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, U static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) { MXFContext *mxf = arg; + AVFormatContext *s = mxf->fc; MXFPartition *partition, *tmp_part; UID op; uint64_t footer_partition; @@ -640,6 +719,12 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size } nb_essence_containers = avio_rb32(pb); + if (partition->type == Header) { + char str[36]; + snprintf(str, sizeof(str), "%08x.%08x.%08x.%08x", AV_RB32(&op[0]), AV_RB32(&op[4]), AV_RB32(&op[8]), AV_RB32(&op[12])); + av_dict_set(&s->metadata, "operational_pattern_ul", str, 0); + } + if (partition->this_partition && partition->previous_partition == partition->this_partition) { av_log(mxf->fc, AV_LOG_ERROR, @@ -1250,6 +1335,7 @@ static const MXFCodecUL mxf_picture_essence_container_uls[] = { { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x05,0x00,0x00 }, 14, AV_CODEC_ID_RAWVIDEO, NULL, 15, RawVWrap }, /* uncompressed picture */ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0a,0x0e,0x0f,0x03,0x01,0x02,0x20,0x01,0x01 }, 15, AV_CODEC_ID_HQ_HQA }, { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0a,0x0e,0x0f,0x03,0x01,0x02,0x20,0x02,0x01 }, 15, AV_CODEC_ID_HQX }, + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0a,0x0e,0x15,0x00,0x04,0x02,0x10,0x00,0x01 }, 16, AV_CODEC_ID_HEVC, NULL, 15 }, /* Canon XF-HEVC */ { { 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 }, }; @@ -1459,10 +1545,7 @@ static int mxf_absolute_bodysid_offset(MXFContext *mxf, int body_sid, int64_t of */ static int64_t mxf_essence_container_end(MXFContext *mxf, int body_sid) { - int x; - int64_t ret = 0; - - for (x = 0; x < mxf->partitions_count; x++) { + for (int x = mxf->partitions_count - 1; x >= 0; x--) { MXFPartition *p = &mxf->partitions[x]; if (p->body_sid != body_sid) @@ -1471,10 +1554,10 @@ static int64_t mxf_essence_container_end(MXFContext *mxf, int body_sid) if (!p->essence_length) return 0; - ret = p->essence_offset + p->essence_length; + return p->essence_offset + p->essence_length; } - return ret; + return 0; } /* EditUnit -> absolute offset */ @@ -1992,15 +2075,15 @@ static int mxf_parse_physical_source_package(MXFContext *mxf, MXFTrack *source_t continue; } - if (physical_track->edit_rate.num <= 0 || - physical_track->edit_rate.den <= 0) { - av_log(mxf->fc, AV_LOG_WARNING, - "Invalid edit rate (%d/%d) found on structural" - " component #%d, defaulting to 25/1\n", - physical_track->edit_rate.num, - physical_track->edit_rate.den, i); - physical_track->edit_rate = (AVRational){25, 1}; - } + if (physical_track->edit_rate.num <= 0 || + physical_track->edit_rate.den <= 0) { + av_log(mxf->fc, AV_LOG_WARNING, + "Invalid edit rate (%d/%d) found on structural" + " component #%d, defaulting to 25/1\n", + physical_track->edit_rate.num, + physical_track->edit_rate.den, i); + physical_track->edit_rate = (AVRational){25, 1}; + } 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]))) @@ -2354,6 +2437,18 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) default: av_log(mxf->fc, AV_LOG_INFO, "Unknown frame layout type: %d\n", descriptor->frame_layout); } + + if (st->codecpar->codec_id == AV_CODEC_ID_PRORES) { + switch (descriptor->essence_codec_ul[14]) { + case 1: st->codecpar->codec_tag = MKTAG('a','p','c','o'); break; + case 2: st->codecpar->codec_tag = MKTAG('a','p','c','s'); break; + case 3: st->codecpar->codec_tag = MKTAG('a','p','c','n'); break; + case 4: st->codecpar->codec_tag = MKTAG('a','p','c','h'); break; + case 5: st->codecpar->codec_tag = MKTAG('a','p','4','h'); break; + case 6: st->codecpar->codec_tag = MKTAG('a','p','4','x'); break; + } + } + if (st->codecpar->codec_id == AV_CODEC_ID_RAWVIDEO) { st->codecpar->format = descriptor->pix_fmt; if (st->codecpar->format == AV_PIX_FMT_NONE) { @@ -2390,7 +2485,6 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) if (st->codecpar->codec_id == AV_CODEC_ID_NONE || (st->codecpar->codec_id == AV_CODEC_ID_PCM_ALAW && (enum AVCodecID)container_ul->id != AV_CODEC_ID_NONE)) st->codecpar->codec_id = (enum AVCodecID)container_ul->id; st->codecpar->channels = descriptor->channels; - st->codecpar->bits_per_coded_sample = descriptor->bits_per_sample; if (descriptor->sample_rate.den > 0) { st->codecpar->sample_rate = descriptor->sample_rate.num / descriptor->sample_rate.den; @@ -2423,6 +2517,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) { st->need_parsing = AVSTREAM_PARSE_FULL; } + st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id); } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) { enum AVMediaType type; container_ul = mxf_get_codec_ul(mxf_data_essence_container_uls, essence_container_ul); @@ -2433,6 +2528,11 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->codecpar->codec_type = type; if (container_ul->desc) av_dict_set(&st->metadata, "data_type", container_ul->desc, 0); + if (mxf->eia608_extract && + !strcmp(container_ul->desc, "vbi_vanc_smpte_436M")) { + st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codecpar->codec_id = AV_CODEC_ID_EIA_608; + } } if (descriptor->extradata) { if (!ff_alloc_extradata(st->codecpar, descriptor->extradata_size)) { @@ -2453,6 +2553,24 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) } } + for (int i = 0; i < mxf->fc->nb_streams; i++) { + MXFTrack *track1 = mxf->fc->streams[i]->priv_data; + if (track1 && track1->body_sid) { + for (int j = i + 1; j < mxf->fc->nb_streams; j++) { + MXFTrack *track2 = mxf->fc->streams[j]->priv_data; + if (track2 && track1->body_sid == track2->body_sid && track1->wrapping != track2->wrapping) { + if (track1->wrapping == UnknownWrapped) + track1->wrapping = track2->wrapping; + else if (track2->wrapping == UnknownWrapped) + track2->wrapping = track1->wrapping; + else + av_log(mxf->fc, AV_LOG_ERROR, "stream %d and stream %d have the same BodySID (%d) " + "with different wrapping\n", i, j, track1->body_sid); + } + } + } + } + ret = 0; fail_and_free: return ret; @@ -2461,23 +2579,24 @@ fail_and_free: static int64_t mxf_timestamp_to_int64(uint64_t timestamp) { struct tm time = { 0 }; + int msecs; time.tm_year = (timestamp >> 48) - 1900; time.tm_mon = (timestamp >> 40 & 0xFF) - 1; time.tm_mday = (timestamp >> 32 & 0xFF); time.tm_hour = (timestamp >> 24 & 0xFF); time.tm_min = (timestamp >> 16 & 0xFF); time.tm_sec = (timestamp >> 8 & 0xFF); + msecs = (timestamp & 0xFF) * 4; - /* msvcrt versions of strftime calls the invalid parameter handler - * (aborting the process if one isn't set) if the parameters are out - * of range. */ + /* Clip values for legacy reasons. Maybe we should return error instead? */ time.tm_mon = av_clip(time.tm_mon, 0, 11); time.tm_mday = av_clip(time.tm_mday, 1, 31); time.tm_hour = av_clip(time.tm_hour, 0, 23); time.tm_min = av_clip(time.tm_min, 0, 59); time.tm_sec = av_clip(time.tm_sec, 0, 59); + msecs = av_clip(msecs, 0, 999); - return (int64_t)av_timegm(&time) * 1000000; + return (int64_t)av_timegm(&time) * 1000000 + msecs * 1000; } #define SET_STR_METADATA(pb, name, str) do { \ @@ -2495,7 +2614,7 @@ static int64_t mxf_timestamp_to_int64(uint64_t timestamp) #define SET_TS_METADATA(pb, name, var, str) do { \ var = avio_rb64(pb); \ - if ((ret = avpriv_dict_set_timestamp(&s->metadata, name, mxf_timestamp_to_int64(var)) < 0)) \ + if (var && (ret = avpriv_dict_set_timestamp(&s->metadata, name, mxf_timestamp_to_int64(var))) < 0) \ return ret; \ } while (0) @@ -3019,9 +3138,12 @@ static void mxf_read_random_index_pack(AVFormatContext *s) goto end; avio_seek(s->pb, file_size - length, SEEK_SET); if (klv_read_packet(&klv, s->pb) < 0 || - !IS_KLV_KEY(klv.key, mxf_random_index_pack_key) || - klv.length != length - 20) + !IS_KLV_KEY(klv.key, mxf_random_index_pack_key)) + goto end; + if (klv.next_klv != file_size || klv.length <= 4 || (klv.length - 4) % 12) { + av_log(s, AV_LOG_WARNING, "Invalid RIP KLV length\n"); goto end; + } avio_skip(s->pb, klv.length - 12); mxf->footer_partition = avio_rb64(s->pb); @@ -3047,6 +3169,7 @@ static int mxf_read_header(AVFormatContext *s) 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"); + //goto fail should not be needed as no metadata sets will have been parsed yet return AVERROR_INVALIDDATA; } avio_seek(s->pb, -14, SEEK_CUR); @@ -3077,7 +3200,8 @@ static int mxf_read_header(AVFormatContext *s) if (!mxf->current_partition) { av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to first PartitionPack\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } if (!mxf->current_partition->first_essence_klv.offset) @@ -3269,7 +3393,8 @@ static int64_t mxf_set_current_edit_unit(MXFContext *mxf, AVStream *st, int64_t static int mxf_set_audio_pts(MXFContext *mxf, AVCodecParameters *par, AVPacket *pkt) { - MXFTrack *track = mxf->fc->streams[pkt->stream_index]->priv_data; + AVStream *st = mxf->fc->streams[pkt->stream_index]; + MXFTrack *track = st->priv_data; int64_t bits_per_sample = par->bits_per_coded_sample; if (!bits_per_sample) @@ -3280,8 +3405,10 @@ static int mxf_set_audio_pts(MXFContext *mxf, AVCodecParameters *par, if ( par->channels <= 0 || bits_per_sample <= 0 || par->channels * (int64_t)bits_per_sample < 8) - return AVERROR(EINVAL); - track->sample_count += pkt->size / (par->channels * (int64_t)bits_per_sample / 8); + track->sample_count = mxf_compute_sample_count(mxf, st, av_rescale_q(track->sample_count, st->time_base, av_inv_q(track->edit_rate)) + 1); + else + track->sample_count += pkt->size / (par->channels * (int64_t)bits_per_sample / 8); + return 0; } @@ -3308,6 +3435,8 @@ static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt) if (ret < 0) return ret; } else if (track) { + pkt->dts = pkt->pts = track->sample_count; + pkt->duration = 1; track->sample_count++; } return 0; @@ -3347,7 +3476,7 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) 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 body_sid = find_body_sid_by_offset(mxf, klv.offset); + int body_sid = find_body_sid_by_absolute_offset(mxf, klv.offset); int index = mxf_get_stream_index(s, &klv, body_sid); int64_t next_ofs; AVStream *st; @@ -3381,8 +3510,8 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) } else { if ((size = next_ofs - pos) <= 0) { av_log(s, AV_LOG_ERROR, "bad size: %"PRId64"\n", size); - ret = AVERROR_INVALIDDATA; - goto skip; + mxf->current_klv_data = (KLVPacket){{0}}; + return AVERROR_INVALIDDATA; } // We must not overread, because the next edit unit might be in another KLV if (size > max_data_size) @@ -3404,6 +3533,13 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) mxf->current_klv_data = (KLVPacket){{0}}; return ret; } + } else if (mxf->eia608_extract && + s->streams[index]->codecpar->codec_id == AV_CODEC_ID_EIA_608) { + ret = mxf_get_eia608_packet(s, s->streams[index], pkt, klv.length); + if (ret < 0) { + mxf->current_klv_data = (KLVPacket){{0}}; + return ret; + } } else { ret = av_get_packet(s->pb, pkt, klv.length); if (ret < 0) { @@ -3447,6 +3583,7 @@ static int mxf_read_close(AVFormatContext *s) for (i = 0; i < mxf->metadata_sets_count; i++) { mxf_free_metadataset(mxf->metadata_sets + i, 1); } + mxf->metadata_sets_count = 0; av_freep(&mxf->partitions); av_freep(&mxf->metadata_sets); av_freep(&mxf->aesc); @@ -3465,7 +3602,7 @@ static int mxf_read_close(AVFormatContext *s) return 0; } -static int mxf_probe(AVProbeData *p) { +static int mxf_probe(const AVProbeData *p) { const uint8_t *bufp = p->buf; const uint8_t *end = p->buf + p->buf_size; @@ -3602,6 +3739,21 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti return 0; } +static const AVOption options[] = { + { "eia608_extract", "extract eia 608 captions from s436m track", + offsetof(MXFContext, eia608_extract), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, + AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass demuxer_class = { + .class_name = "mxf", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + AVInputFormat ff_mxf_demuxer = { .name = "mxf", .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"), @@ -3612,4 +3764,5 @@ AVInputFormat ff_mxf_demuxer = { .read_packet = mxf_read_packet, .read_close = mxf_read_close, .read_seek = mxf_read_seek, + .priv_class = &demuxer_class, };