diff --git a/libavformat/isom.h b/libavformat/isom.h index 3d375d7a46..b30b9da65e 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -327,6 +327,8 @@ typedef struct MOVContext { int64_t extent_offset; } *avif_info; int avif_info_size; + int64_t hvcC_offset; + int hvcC_size; int interleaved_read; } MOVContext; diff --git a/libavformat/mov.c b/libavformat/mov.c index 72050a96a7..c6398d6d81 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -1233,7 +1233,8 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) c->isom = 1; av_log(c->fc, AV_LOG_DEBUG, "ISO: File Type Major Brand: %.4s\n",(char *)&type); av_dict_set(&c->fc->metadata, "major_brand", type, 0); - c->is_still_picture_avif = !strncmp(type, "avif", 4); + c->is_still_picture_avif = !strncmp(type, "avif", 4) || + !strncmp(type, "mif1", 4); minor_ver = avio_rb32(pb); /* minor version */ av_dict_set_int(&c->fc->metadata, "minor_version", minor_ver, 0); @@ -4940,6 +4941,19 @@ static int avif_add_stream(MOVContext *c, int item_id) st->priv_data = sc; st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; st->codecpar->codec_id = AV_CODEC_ID_AV1; + if (c->hvcC_offset >= 0) { + int ret; + int64_t pos = avio_tell(c->fc->pb); + st->codecpar->codec_id = AV_CODEC_ID_HEVC; + if (avio_seek(c->fc->pb, c->hvcC_offset, SEEK_SET) != c->hvcC_offset) { + av_log(c->fc, AV_LOG_ERROR, "Failed to seek to hvcC data.\n"); + return AVERROR_UNKNOWN; + } + ret = ff_get_extradata(c->fc, st->codecpar, c->fc->pb, c->hvcC_size); + if (ret < 0) + return ret; + avio_seek(c->fc->pb, pos, SEEK_SET); + } sc->ffindex = st->index; c->trak_index = st->index; st->avg_frame_rate.num = st->avg_frame_rate.den = 1; @@ -4982,6 +4996,8 @@ static int avif_add_stream(MOVContext *c, int item_id) static int mov_read_meta(MOVContext *c, AVIOContext *pb, MOVAtom atom) { + c->hvcC_offset = -1; + c->hvcC_size = 0; while (atom.size > 8) { uint32_t tag; if (avio_feof(pb)) @@ -7859,6 +7875,28 @@ static int mov_read_iloc(MOVContext *c, AVIOContext *pb, MOVAtom atom) return atom.size; } +static int mov_read_iprp(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int size = avio_rb32(pb); + if (avio_rl32(pb) != MKTAG('i','p','c','o')) + return AVERROR_INVALIDDATA; + size -= 8; + while (size > 0) { + int sub_size, sub_type; + sub_size = avio_rb32(pb); + sub_type = avio_rl32(pb); + sub_size -= 8; + size -= sub_size + 8; + if (sub_type == MKTAG('h','v','c','C')) { + c->hvcC_offset = avio_tell(pb); + c->hvcC_size = sub_size; + break; + } + avio_skip(pb, sub_size); + } + return atom.size; +} + static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('A','C','L','R'), mov_read_aclr }, { MKTAG('A','P','R','G'), mov_read_avid }, @@ -7966,6 +8004,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('p','c','m','C'), mov_read_pcmc }, /* PCM configuration box */ { MKTAG('p','i','t','m'), mov_read_pitm }, { MKTAG('e','v','c','C'), mov_read_glbl }, +{ MKTAG('i','p','r','p'), mov_read_iprp }, { 0, NULL } }; @@ -9335,7 +9374,7 @@ const AVInputFormat ff_mov_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), .priv_class = &mov_class, .priv_data_size = sizeof(MOVContext), - .extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v,avif", + .extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v,avif,heic,heif", .flags_internal = FF_FMT_INIT_CLEANUP, .read_probe = mov_probe, .read_header = mov_read_header, diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak index 8541b05b29..97f46f99ad 100644 --- a/tests/fate/mov.mak +++ b/tests/fate/mov.mak @@ -20,6 +20,8 @@ FATE_MOV = fate-mov-3elist \ fate-mov-pcm-remux \ fate-mov-avif-demux-still-image-1-item \ fate-mov-avif-demux-still-image-multiple-items \ + fate-mov-heic-demux-still-image-1-item \ + fate-mov-heic-demux-still-image-multiple-items \ FATE_MOV_FFPROBE = fate-mov-neg-firstpts-discard \ fate-mov-neg-firstpts-discard-vorbis \ @@ -148,6 +150,10 @@ fate-mov-avif-demux-still-image-1-item: CMD = framemd5 -i $(TARGET_SAMPLES)/avif # parsed. fate-mov-avif-demux-still-image-multiple-items: CMD = framemd5 -i $(TARGET_SAMPLES)/avif/still_image_exif.avif -c:v copy +fate-mov-heic-demux-still-image-1-item: CMD = framemd5 -i $(TARGET_SAMPLES)/heif-conformance/C002.heic -c:v copy + +fate-mov-heic-demux-still-image-multiple-items: CMD = framemd5 -i $(TARGET_SAMPLES)/heif-conformance/C003.heic -c:v copy + # Resulting remux should have: # 1. first audio stream with AV_DISPOSITION_HEARING_IMPAIRED # 2. second audio stream with AV_DISPOSITION_VISUAL_IMPAIRED | DESCRIPTIONS diff --git a/tests/ref/fate/mov-heic-demux-still-image-1-item b/tests/ref/fate/mov-heic-demux-still-image-1-item new file mode 100644 index 0000000000..c850c1ff9c --- /dev/null +++ b/tests/ref/fate/mov-heic-demux-still-image-1-item @@ -0,0 +1,11 @@ +#format: frame checksums +#version: 2 +#hash: MD5 +#extradata 0, 100, 5444bf01e03182c73ae957179d560f4d +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: hevc +#dimensions 0: 1280x720 +#sar 0: 0/1 +#stream#, dts, pts, duration, size, hash +0, 0, 0, 1, 111554, 03ceabfab39afd2e2e796b9362111f32 diff --git a/tests/ref/fate/mov-heic-demux-still-image-multiple-items b/tests/ref/fate/mov-heic-demux-still-image-multiple-items new file mode 100644 index 0000000000..c850c1ff9c --- /dev/null +++ b/tests/ref/fate/mov-heic-demux-still-image-multiple-items @@ -0,0 +1,11 @@ +#format: frame checksums +#version: 2 +#hash: MD5 +#extradata 0, 100, 5444bf01e03182c73ae957179d560f4d +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: hevc +#dimensions 0: 1280x720 +#sar 0: 0/1 +#stream#, dts, pts, duration, size, hash +0, 0, 0, 1, 111554, 03ceabfab39afd2e2e796b9362111f32