From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTP id 54CA340D74 for ; Thu, 10 Mar 2022 16:01:18 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 40A5068AB18; Thu, 10 Mar 2022 18:01:15 +0200 (EET) Received: from EUR04-DB3-obe.outbound.protection.outlook.com (mail-oln040092074066.outbound.protection.outlook.com [40.92.74.66]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3844168AF1E for ; Thu, 10 Mar 2022 18:01:07 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=CFTNydibfHBCmuSh7DgqHAgt16W1+g+PXgs+auKBYE7EiBF/YQRh95B9tAMwuZ3ExGNSJSW1YqM6KMkbnGzRAvwMFvPNNVxUcFw9zlj6YAXRVzGnBr5ZV2GfyliEsXzialgOKHlMIisu/JYqll/TTLCDJoF+E7ZwhDTQBNvkcz0Fq7ChCFD+FURuXTs2Dso6X1BJHOZuasuE+tae+5kwW0kzTxZju3i1Fk5QDaWSNgf0c4udqgzONJmawByl1A+mMTbNhIp4nPXjEJR6qOEqYz9F1Ayj1eWvxkMvAhNpqVNRGzDozvqfbdmn+P+crjcrbbE02H/z5llmLFPNEd3yZw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=wmEjC39zFF1yp+vvyzscVrifPO+gI1aGKba4EQaywYc=; b=mj5qrUnfLi1mdzVNujanYdwSpoKZLSuWNVTk3eGWcvwnc4w6SLM3h8DCpyfAX3cD5jaLPQ+fAbm0VEhBwtsk41rf9nfN56U4pt7W1aGOtH8zk3XXvV6L5kMpYup5vtV/pWpYqifV7xNNeyVHiCOKMUHq4EhFqTdT68NoWkWhdjbGnqS4JQe8vLCAuSYz8bOAaRoosvf5DTA1m1K/Ikr1Q8rcGzXKzZ1J0OJIPwX5lfjHD0YXqwQ/KwDOrwQfJLsajnp+yDDxzUzJQsvWoX9a8aCTFG6JGfc6BthGYt7QulW+NPEvMrNch0Vozy4e18Paq+5eKONugMkbk3aK7HzDEA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=wmEjC39zFF1yp+vvyzscVrifPO+gI1aGKba4EQaywYc=; b=DoFIYWjBh60UnWT6T/LBj4LQXItCjMHMkVmY2XPPbH+qxRg3CNTGmWUavfAHHPgCQPGuoyUoWbrVI4dGXkquXmg4c1oqDdMBfFLSN38rigDs1HrLHYfs3K2hHBTWlT1n+P5ibAosuyOxYMm3Fx9wKkJg3+DQOK1kgSLj/uHoESLf6TVTx9KyFbjASewvM5JXHmOqT9YtYEniJCJpqScKVezAR9W+UfJHVstZk/1JFVKJQSbnsdpMygTOe9aOv6JQJdrcGuKJwL1P/UtI3YEQQKgzpjF1/VASGc/LC9jY18wFPeQw/PCm2f2BoPMfeapARp+NhFe6bcSGdON8/ult9A== Received: from PR3PR03MB6665.eurprd03.prod.outlook.com (2603:10a6:102:7d::6) by DU2PR03MB7941.eurprd03.prod.outlook.com (2603:10a6:10:2d8::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5061.22; Thu, 10 Mar 2022 16:01:05 +0000 Received: from PR3PR03MB6665.eurprd03.prod.outlook.com ([fe80::5dfc:6a00:3e35:c1d5]) by PR3PR03MB6665.eurprd03.prod.outlook.com ([fe80::5dfc:6a00:3e35:c1d5%3]) with mapi id 15.20.5038.027; Thu, 10 Mar 2022 16:01:05 +0000 Message-ID: Date: Thu, 10 Mar 2022 17:01:02 +0100 Content-Language: en-US To: ffmpeg-devel@ffmpeg.org References: <53482b5b-9293-65c5-a9e7-88f28d30353d@gmail.com> <20220303191615.2784209-1-vigneshv@google.com> From: Andreas Rheinhardt In-Reply-To: <20220303191615.2784209-1-vigneshv@google.com> X-TMN: [dRVgnE2nWWirnmBNuIysfrnTpE565qZ2] X-ClientProxiedBy: AM5PR04CA0014.eurprd04.prod.outlook.com (2603:10a6:206:1::27) To PR3PR03MB6665.eurprd03.prod.outlook.com (2603:10a6:102:7d::6) X-Microsoft-Original-Message-ID: MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 3234a061-7810-4026-ca98-08da02af2e3d X-MS-TrafficTypeDiagnostic: DU2PR03MB7941:EE_ X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 1tKfsYXW4z4LSqiNnBcrepv09ACxAub0mFVRTso4RM5Ho9xg1k8SDlCzXH0G7fkVc/Lvxr+31k5wPlhJ4soIe6FTvamZW13CpIr9zar84hzLCMEDLbB8D6BKbSkiWI2a2lGdV0xc4mfttHH6gDSI+dAfcxe4FDkU+pmXeKN67Q+qEITJH5OyGYQnsRXKch4rFbFSWp4IHJ7lB5wbOwocSKRk5h6Muaz90f16iQbIyshKfPn1OcXx7THKpbAZ5BXLLhuKVXarbWBYP5DFvMJK485x+aD4TXOCxOs6D2rjb4s3R5hwkBZdc7WaDO2e2ljuDQFu25FaoQ172Vfdv4e9Ir6FlALSwrnqIZ6XcoLyC9fpEZmtvGfe3M4+IzljTzus1CwspGaV8xRzHuwezqZmypstI+3W9Hj8MXdaX7zyDUY/vJt4LZUTooHvR4Oy8egPBO0Wnd9SDKpVcncVb2xpD6Pj6ZvjxCeF9EwurY2PblUBWnSlxTce1zPNcZyCCmAloIeHXSk/g9JEl1yovj2oWcKBxd/k5CBLLKl6vTh9L3n38v9qu9mUC8HUMpAEeNcTZfyKJ7yz4bRTOSH5ql26rWzkbjignfbZhe+1OhXAzpTs3PVv7L3uq2eA0ACXN/Fg X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?T09FTFhIUGo1N2UzZzBYdDNlb2Z0NHFUdEdRcWFlamx6M09qNDFUY3lUUG5P?= =?utf-8?B?MnIwMVREejZyKzBVVndyNW53Q1RVaElXZEVHVlFYQ2pDaTdnQUhGVGVpaXJU?= =?utf-8?B?b1RyL0pxdGMxUnkyMmFRV1R5eFRLUk9uY2kyU2MybXFSaklRcURwMDZFcGJ1?= =?utf-8?B?Z1d5bWFoQisyeXVLdDhNVldVanNKR0JWNnNPOGw1eEVJZXVPOHJFSkNsaEZM?= =?utf-8?B?VE01MkxheUw4UnZaTGJIdTArWGpnNlZyeitJam5JQlNKUXRwMVlkbjBtNnVJ?= =?utf-8?B?bnpBL1JGZHFwTFJ6cEdUY0xicU5WWHVlZjNvdEgrL3dHa3BGZFo3Um04Z05R?= =?utf-8?B?T3g0Mjd0TzVuK0JuTmVyeHNTNWhqeGRWWFFxTFJibGZicXJaTU1Xei9FaFdD?= =?utf-8?B?V0NITkpYYU9zNThpaWhjODVrVWVxK0FXZGNvaEI1Y1ZVNVNFS0k1N1hkU1JU?= =?utf-8?B?QmJvdXlRNCttS1FQN0ZISkNJUWNubXAreGZwaDFrQUhuM1VkUFhSVGk5U1NW?= =?utf-8?B?RVJ4YUNzYkVQUmxUVUhnRklZYVR6ck1KNXBOQ1lXREk0dlV5cE41S2VySlIy?= =?utf-8?B?bzJvSS9IblFvS2dtRjlkY3Q5VUhsVmRpdWtkOVdOK3RBcnVzR29zcDBjSWY5?= =?utf-8?B?NFY4Wmd3aW1CNitwKzUyY0JhRUo2RDBRTVh3YXpYejVRSnZJMkZNc2NOeXlZ?= =?utf-8?B?K3VaVm5BclJydGQ5VUdWZWFsTWk5dkV1bDF6MVZYMVRsV2MwaytPanUxQ3E5?= =?utf-8?B?cmtHa1pCN3AyYnpmMTU0dXA5UWs5L21VRitPazZFaGhtaWVrK1A5cFNmTmt3?= =?utf-8?B?Q0IvQTBTRk9GbTZibHpWOEk3MVVJUFVuZE5TZzczODdFcW9LNjNadmN3aXlh?= =?utf-8?B?WjVvTlQ2Y3JCRUM4K2NmdjJjMDU3NzlSZVpTWDJzS1ZZT2YxUzdhdENrcW1G?= =?utf-8?B?RjJteEZDSTZaWFJodXptdWtlbEhPaHlqT3B4eWFHbTJqREZ1NlZWckFPMk9Z?= =?utf-8?B?UWNZaGNHMUxhL1o4bDNXdzNmWTZjblY4eExxUWo3Z25oMmZEcUxDWDFNTmNZ?= =?utf-8?B?Uk5pa3I5RnZNUzNYekZFTVkvK3o0NDQ2UTNVSi92bStIeDgyaWZ2UFFISkkz?= =?utf-8?B?UkpJUW1OOHVzQW82TWhBZy9ING9BSFRsUExhQUlzS3c5YXcyT29qL2MwVnVl?= =?utf-8?B?eDVDais1QUplbmYwSDNSbnNvd0p6RjhTY2RxNUxOK04zMU9XVWlFRUVXbStt?= =?utf-8?B?Uk9PRkRxd3MwRlFMMlVNRmNZZmZKaGQxdEszdmovKzluOURIM3JsclhhbFJy?= =?utf-8?B?UHdRQ3E4NW1lcUkzMitUSjRJaFB3MENaUms2THFNSHVheXQ5YUxkczdyWlNx?= =?utf-8?B?WkkvUi9lUVhOMGNSSU5ZMXAwRmdpZkdMYmZaWU56L3BPZDY0Y1pmWmcvQjFu?= =?utf-8?B?NHBQSkQrWFFtNW91WGdEWVFiYUx4WlEyNFVTM3FTZVdEZzFwZFFvNXJWVG1Y?= =?utf-8?B?L3Q0UGVlTjlBcnJsUVlQb2JEdW9PL1VTbkhlZTNrVW95ZXJmOXd4ajFYellq?= =?utf-8?Q?TZKzLnftPdm8ml+M2rImEZ1MDvs1bnlFgIg73rygRu/3LL?= X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 3234a061-7810-4026-ca98-08da02af2e3d X-MS-Exchange-CrossTenant-AuthSource: PR3PR03MB6665.eurprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 10 Mar 2022 16:01:04.9835 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU2PR03MB7941 Subject: Re: [FFmpeg-devel] [PATCH 3/3] avformat/movenc: Add support for AVIF muxing X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: Vignesh Venkatasubramanian: > Add an AVIF muxer by re-using the existing the mov/mp4 muxer. > > AVIF Specifiation: https://aomediacodec.github.io/av1-avif > > Sample usage for still image: > ffmpeg -i image.png -c:v libaom-av1 -avif-image 1 image.avif > > Sample usage for animated AVIF image: > ffmpeg -i video.mp4 animated.avif > > We can re-use any of the AV1 encoding options that will make > sense for image encoding (like bitrate, tiles, encoding speed, > etc). > > The files generated by this muxer has been verified to be valid > AVIF files by the following: > 1) Displays on Chrome (both still and animated images). > 2) Displays on Firefox (only still images, firefox does not support > animated AVIF yet). > 3) Verfied to be valid by Compliance Warden: > https://github.com/gpac/ComplianceWarden > > Fixes the encoder/muxer part of Trac Ticket #7621 > > Signed-off-by: Vignesh Venkatasubramanian > --- > configure | 1 + > libavformat/allformats.c | 1 + > libavformat/movenc.c | 323 ++++++++++++++++++++++++++++++++++++--- > libavformat/movenc.h | 5 + > 4 files changed, 305 insertions(+), 25 deletions(-) > > diff --git a/configure b/configure > index 8c69ab0c86..6d7020e96b 100755 > --- a/configure > +++ b/configure > @@ -3390,6 +3390,7 @@ asf_stream_muxer_select="asf_muxer" > av1_demuxer_select="av1_frame_merge_bsf av1_parser" > avi_demuxer_select="riffdec exif" > avi_muxer_select="riffenc" > +avif_muxer_select="mov_muxer" > caf_demuxer_select="iso_media" > caf_muxer_select="iso_media" > dash_muxer_select="mp4_muxer" > diff --git a/libavformat/allformats.c b/libavformat/allformats.c > index d066a7745b..400c17afbd 100644 > --- a/libavformat/allformats.c > +++ b/libavformat/allformats.c > @@ -81,6 +81,7 @@ extern const AVOutputFormat ff_au_muxer; > extern const AVInputFormat ff_av1_demuxer; > extern const AVInputFormat ff_avi_demuxer; > extern const AVOutputFormat ff_avi_muxer; > +extern const AVOutputFormat ff_avif_muxer; > extern const AVInputFormat ff_avisynth_demuxer; > extern const AVOutputFormat ff_avm2_muxer; > extern const AVInputFormat ff_avr_demuxer; > diff --git a/libavformat/movenc.c b/libavformat/movenc.c > index 1a746a67fd..504403ab0b 100644 > --- a/libavformat/movenc.c > +++ b/libavformat/movenc.c > @@ -1303,7 +1303,7 @@ static int mov_write_av1c_tag(AVIOContext *pb, MOVTrack *track) > > avio_wb32(pb, 0); > ffio_wfourcc(pb, "av1C"); > - ff_isom_write_av1c(pb, track->vos_data, track->vos_len, 1); > + ff_isom_write_av1c(pb, track->vos_data, track->vos_len, track->mode != MODE_AVIF); > return update_size(pb, pos); > } > > @@ -2004,12 +2004,13 @@ static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track, int prefer_icc) > } > } > > - /* We should only ever be called by MOV or MP4. */ > - av_assert0(track->mode == MODE_MOV || track->mode == MODE_MP4); > + /* We should only ever be called for MOV, MP4 and AVIF. */ > + av_assert0(track->mode == MODE_MOV || track->mode == MODE_MP4 || > + track->mode == MODE_AVIF); > > avio_wb32(pb, 0); /* size */ > ffio_wfourcc(pb, "colr"); > - if (track->mode == MODE_MP4) > + if (track->mode == MODE_MP4 || track->mode == MODE_AVIF) > ffio_wfourcc(pb, "nclx"); > else > ffio_wfourcc(pb, "nclc"); > @@ -2019,7 +2020,7 @@ static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track, int prefer_icc) > avio_wb16(pb, track->par->color_primaries); > avio_wb16(pb, track->par->color_trc); > avio_wb16(pb, track->par->color_space); > - if (track->mode == MODE_MP4) { > + if (track->mode == MODE_MP4 || track->mode == MODE_AVIF) { > int full_range = track->par->color_range == AVCOL_RANGE_JPEG; > avio_w8(pb, full_range << 7); > } > @@ -2085,7 +2086,7 @@ static void find_compressor(char * compressor_name, int len, MOVTrack *track) > || (track->par->width == 1440 && track->par->height == 1080) > || (track->par->width == 1920 && track->par->height == 1080); > > - if (track->mode == MODE_MOV && > + if ((track->mode == MODE_AVIF || track->mode == MODE_MOV) && > (encoder = av_dict_get(track->st->metadata, "encoder", NULL, 0))) { > av_strlcpy(compressor_name, encoder->value, 32); > } else if (track->par->codec_id == AV_CODEC_ID_MPEG2VIDEO && xdcam_res) { > @@ -2106,6 +2107,25 @@ static void find_compressor(char * compressor_name, int len, MOVTrack *track) > } > } > > +static int mov_write_ccst_tag(AVIOContext *pb) > +{ > + int64_t pos = avio_tell(pb); > + // Write sane defaults: > + // all_ref_pics_intra = 0 : all samples can use any type of reference. > + // intra_pred_used = 1 : intra prediction may or may not be used. > + // max_ref_per_pic = 15 : reserved value to indicate that any number of > + // reference images can be used. > + uint8_t ccstValue = (0 << 7) | /* all_ref_pics_intra */ > + (1 << 6) | /* intra_pred_used */ > + (15 << 2); /* max_ref_per_pic */ > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "ccst"); > + avio_wb32(pb, 0); /* Version & flags */ > + avio_w8(pb, ccstValue); > + avio_wb24(pb, 0); /* reserved */ > + return update_size(pb, pos); > +} > + > static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) > { > int ret = AVERROR_BUG; > @@ -2123,6 +2143,8 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex > avio_wb32(pb, 0); /* size */ > if (mov->encryption_scheme != MOV_ENC_NONE) { > ffio_wfourcc(pb, "encv"); > + } else if (track->mode == MODE_AVIF) { > + ffio_wfourcc(pb, "av01"); > } else { > avio_wl32(pb, track->tag); // store it byteswapped > } > @@ -2239,7 +2261,7 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex > else > av_log(mov->fc, AV_LOG_WARNING, "Not writing 'gama' atom. Format is not MOV.\n"); > } > - if (track->mode == MODE_MOV || track->mode == MODE_MP4) { > + if (track->mode == MODE_MOV || track->mode == MODE_MP4 || track->mode == MODE_AVIF) { > int has_color_info = track->par->color_primaries != AVCOL_PRI_UNSPECIFIED && > track->par->color_trc != AVCOL_TRC_UNSPECIFIED && > track->par->color_space != AVCOL_SPC_UNSPECIFIED; > @@ -2291,6 +2313,9 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex > if (avid) > avio_wb32(pb, 0); > > + if (track->mode == MODE_AVIF) > + mov_write_ccst_tag(pb); > + > return update_size(pb, pos); > } > > @@ -2792,7 +2817,10 @@ static int mov_write_hdlr_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra > > if (track) { > hdlr = (track->mode == MODE_MOV) ? "mhlr" : "\0\0\0\0"; > - if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) { > + if (track->mode == MODE_AVIF) { > + hdlr_type = "pict"; > + descr = "ffmpeg"; > + } else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) { > hdlr_type = "vide"; > descr = "VideoHandler"; > } else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO) { > @@ -2859,6 +2887,131 @@ static int mov_write_hdlr_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra > return update_size(pb, pos); > } > > +static int mov_write_pitm_tag(AVIOContext *pb, int item_id) > +{ > + int64_t pos = avio_tell(pb); > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "pitm"); > + avio_wb32(pb, 0); /* Version & flags */ > + avio_wb16(pb, item_id); /* item_id */ > + return update_size(pb, pos); > +} > + > +static int mov_write_iloc_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) > +{ > + int64_t pos = avio_tell(pb); > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "iloc"); > + avio_wb32(pb, 0); /* Version & flags */ > + avio_w8(pb, (4 << 4) + 4); /* offset_size(4) and length_size(4) */ > + avio_w8(pb, 0); /* base_offset_size(4) and reserved(4) */ > + avio_wb16(pb, 1); /* item_count */ > + > + avio_wb16(pb, 1); /* item_id */ > + avio_wb16(pb, 0); /* data_reference_index */ > + avio_wb16(pb, 1); /* extent_count */ > + mov->avif_extent_pos = avio_tell(pb); > + avio_wb32(pb, 0); /* extent_offset (written later) */ > + // For animated AVIF, we simply write the first packet's size. > + avio_wb32(pb, mov->avif_extent_length); /* extent_length */ > + > + return update_size(pb, pos); > +} > + > +static int mov_write_iinf_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) > +{ > + int64_t infe_pos; > + int64_t iinf_pos = avio_tell(pb); > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "iinf"); > + avio_wb32(pb, 0); /* Version & flags */ > + avio_wb16(pb, 1); /* entry_count */ > + > + infe_pos = avio_tell(pb); > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "infe"); > + avio_w8(pb, 0x2); /* Version */ > + avio_wb24(pb, 0); /* flags */ > + avio_wb16(pb, 1); /* item_id */ > + avio_wb16(pb, 0); /* item_protection_index */ > + avio_write(pb, "av01", 4); /* item_type */ > + avio_write(pb, "Color\0", 6); /* item_name */ > + update_size(pb, infe_pos); > + > + return update_size(pb, iinf_pos); > +} > + > +static int mov_write_ispe_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) > +{ > + int64_t pos = avio_tell(pb); > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "ispe"); > + avio_wb32(pb, 0); /* Version & flags */ > + avio_wb32(pb, s->streams[0]->codecpar->width); /* image_width */ > + avio_wb32(pb, s->streams[0]->codecpar->height); /* image_height */ > + return update_size(pb, pos); > +} > + > + > +static int mov_write_pixi_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) > +{ > + int64_t pos = avio_tell(pb); > + int num_channels = av_pix_fmt_count_planes(s->streams[0]->codecpar->format); > + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(s->streams[0]->codecpar->format); > + int i; > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "pixi"); > + avio_wb32(pb, 0); /* Version & flags */ > + avio_w8(pb, num_channels); /* num_channels */ > + for (i = 0; i < num_channels; ++i) { > + avio_w8(pb, pixdesc->comp[i].depth); /* bits_per_channel */ > + } > + return update_size(pb, pos); > +} > + > +static int mov_write_ipco_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) > +{ > + int64_t pos = avio_tell(pb); > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "ipco"); > + mov_write_ispe_tag(pb, mov, s); > + mov_write_pixi_tag(pb, mov, s); > + mov_write_av1c_tag(pb, &mov->tracks[0]); > + mov_write_colr_tag(pb, &mov->tracks[0], 0); > + return update_size(pb, pos); > +} > + > +static int mov_write_ipma_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) > +{ > + int64_t pos = avio_tell(pb); > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "ipma"); > + avio_wb32(pb, 0); /* Version & flags */ > + avio_wb32(pb, 1); /* entry_count */ > + avio_wb16(pb, 1); /* item_ID */ > + avio_w8(pb, 4); /* association_count */ > + > + // ispe association. > + avio_w8(pb, 1); /* essential and property_index */ > + // pixi association. > + avio_w8(pb, 2); /* essential and property_index */ > + // av1C association. > + avio_w8(pb, 0x80 | 3); /* essential and property_index */ > + // colr association. > + avio_w8(pb, 4); /* essential and property_index */ > + return update_size(pb, pos); > +} > + > +static int mov_write_iprp_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) > +{ > + int64_t pos = avio_tell(pb); > + avio_wb32(pb, 0); /* size */ > + ffio_wfourcc(pb, "iprp"); > + mov_write_ipco_tag(pb, mov, s); > + mov_write_ipma_tag(pb, mov, s); > + return update_size(pb, pos); > +} > + > static int mov_write_hmhd_tag(AVIOContext *pb) > { > /* This atom must be present, but leaving the values at zero > @@ -3056,7 +3209,7 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, > display_matrix = NULL; > } > > - if (track->flags & MOV_TRACK_ENABLED) > + if (track->flags & MOV_TRACK_ENABLED || track->mode == MODE_AVIF) > flags |= MOV_TKHD_FLAG_ENABLED; > > if (track->mode == MODE_ISM) > @@ -3104,7 +3257,7 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, > if (st && (track->par->codec_type == AVMEDIA_TYPE_VIDEO || > track->par->codec_type == AVMEDIA_TYPE_SUBTITLE)) { > int64_t track_width_1616; > - if (track->mode == MODE_MOV) { > + if (track->mode == MODE_MOV || track->mode == MODE_AVIF) { > track_width_1616 = track->par->width * 0x10000ULL; > } else { > track_width_1616 = av_rescale(st->sample_aspect_ratio.num, > @@ -3439,7 +3592,8 @@ static int mov_write_trak_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext > mov_write_tapt_tag(pb, track); > } > } > - mov_write_track_udta_tag(pb, mov, st); > + if (track->mode != MODE_AVIF) > + mov_write_track_udta_tag(pb, mov, st); > track->entry = entry_backup; > track->chunkCount = chunk_backup; > return update_size(pb, pos); > @@ -3914,8 +4068,15 @@ static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov, > mov_write_mdta_hdlr_tag(pb, mov, s); > mov_write_mdta_keys_tag(pb, mov, s); > mov_write_mdta_ilst_tag(pb, mov, s); > - } > - else { > + } else if (mov->mode == MODE_AVIF) { > + mov_write_hdlr_tag(s, pb, &mov->tracks[0]); > + // We always write the primary item id as 1 since only one track is > + // supported for AVIF. Various parts of this patch seem to presume this (they always use the first stream), yet I fail to see what ensures this. > + mov_write_pitm_tag(pb, 1); > + mov_write_iloc_tag(pb, mov, s); > + mov_write_iinf_tag(pb, mov, s); > + mov_write_iprp_tag(pb, mov, s); > + } else { > /* iTunes metadata tag */ > mov_write_itunes_hdlr_tag(pb, mov, s); > mov_write_ilst_tag(pb, mov, s); > @@ -4245,10 +4406,11 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, > } > > mov_write_mvhd_tag(pb, mov); > - if (mov->mode != MODE_MOV && !mov->iods_skip) > + if (mov->mode != MODE_MOV && mov->mode != MODE_AVIF && !mov->iods_skip) > mov_write_iods_tag(pb, mov); > for (i = 0; i < mov->nb_streams; i++) { > - if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_FRAGMENT) { > + if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_FRAGMENT || > + mov->mode == MODE_AVIF) { > int ret = mov_write_trak_tag(s, pb, mov, &(mov->tracks[i]), i < s->nb_streams ? s->streams[i] : NULL); > if (ret < 0) > return ret; > @@ -4259,7 +4421,7 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, > > if (mov->mode == MODE_PSP) > mov_write_uuidusmt_tag(pb, s); > - else > + else if (mov->mode != MODE_AVIF) > mov_write_udta_tag(pb, mov, s); > > return update_size(pb, pos); > @@ -5002,6 +5164,9 @@ static void mov_write_ftyp_tag_internal(AVIOContext *pb, AVFormatContext *s, > else if (mov->mode == MODE_3GP) { > ffio_wfourcc(pb, has_h264 ? "3gp6" : "3gp4"); > minor = has_h264 ? 0x100 : 0x200; > + } else if (mov->mode == MODE_AVIF) { > + ffio_wfourcc(pb, mov->is_animated_avif ? "avis" : "avif"); > + minor = 0; > } else if (mov->mode & MODE_3G2) { > ffio_wfourcc(pb, has_h264 ? "3g2b" : "3g2a"); > minor = has_h264 ? 0x20000 : 0x10000; > @@ -5065,6 +5230,31 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) > // compatible brand a second time. > if (mov->mode == MODE_ISM) { > ffio_wfourcc(pb, "piff"); > + } else if (mov->mode == MODE_AVIF) { > + const AVPixFmtDescriptor *pix_fmt_desc = > + av_pix_fmt_desc_get(s->streams[0]->codecpar->format); > + const int depth = pix_fmt_desc->comp[0].depth; > + if (mov->is_animated_avif) { > + // For animated AVIF, major brand is "avis". Add "avif" as a > + // compatible brand. > + ffio_wfourcc(pb, "avif"); > + ffio_wfourcc(pb, "msf1"); > + ffio_wfourcc(pb, "iso8"); > + } > + ffio_wfourcc(pb, "mif1"); > + ffio_wfourcc(pb, "miaf"); > + if (depth == 8 || depth == 10) { > + // MA1B and MA1A brands are based on AV1 profile. Short hand for > + // computing that is based on chroma subsampling type. 420 chroma > + // subsampling is MA1B. 444 chroma subsampling is MA1A. > + if (pix_fmt_desc->log2_chroma_w == 0 && pix_fmt_desc->log2_chroma_h == 0) { > + // 444 chroma subsampling. > + ffio_wfourcc(pb, "MA1A"); > + } else { > + // 420 chroma subsampling. > + ffio_wfourcc(pb, "MA1B"); > + } > + } > } else if (mov->mode != MODE_MOV) { > // We add tfdt atoms when fragmenting, signal this with the iso6 compatible > // brand, if not already the major brand. This is compatible with users that > @@ -5669,7 +5859,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) > if (ret < 0) > return ret; > > - if (mov->flags & FF_MOV_FLAG_FRAGMENT) { > + if (mov->flags & FF_MOV_FLAG_FRAGMENT || mov->mode == MODE_AVIF) { > int ret; > if (mov->moov_written || mov->flags & FF_MOV_FLAG_EMPTY_MOOV) { > if (mov->frag_interleave && mov->fragments > 0) { > @@ -5802,7 +5992,9 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) > } > } > } else if (par->codec_id == AV_CODEC_ID_AV1) { > - if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { > + if (trk->mode == MODE_AVIF) { > + avio_write(pb, pkt->data, pkt->size); > + } else if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { > ret = ff_av1_filter_obus_buf(pkt->data, &reformatted_data, > &size, &offset); > if (ret < 0) > @@ -6230,6 +6422,10 @@ fail: > } > } > > + if (trk->mode == MODE_AVIF && !mov->avif_extent_length) { > + mov->avif_extent_length = pkt->size; > + } > + > return mov_write_single_packet(s, pkt); > } > } > @@ -6569,11 +6765,15 @@ static int mov_init(AVFormatContext *s) > else if (IS_MODE(ipod, IPOD)) mov->mode = MODE_IPOD; > else if (IS_MODE(ismv, ISMV)) mov->mode = MODE_ISM; > else if (IS_MODE(f4v, F4V)) mov->mode = MODE_F4V; > + else if (IS_MODE(avif, AVIF)) mov->mode = MODE_AVIF; > #undef IS_MODE > > if (mov->flags & FF_MOV_FLAG_DELAY_MOOV) > mov->flags |= FF_MOV_FLAG_EMPTY_MOOV; > > + if (mov->mode == MODE_AVIF) > + mov->flags |= FF_MOV_FLAG_DELAY_MOOV; > + > /* Set the FRAGMENT flag if any of the fragmentation methods are > * enabled. */ > if (mov->max_fragment_duration || mov->max_fragment_size || > @@ -6797,12 +6997,13 @@ static int mov_init(AVFormatContext *s) > pix_fmt == AV_PIX_FMT_MONOWHITE || > pix_fmt == AV_PIX_FMT_MONOBLACK; > } > - if (track->par->codec_id == AV_CODEC_ID_VP9 || > - track->par->codec_id == AV_CODEC_ID_AV1) { > - if (track->mode != MODE_MP4) { > - av_log(s, AV_LOG_ERROR, "%s only supported in MP4.\n", avcodec_get_name(track->par->codec_id)); > - return AVERROR(EINVAL); > - } > + if (track->par->codec_id == AV_CODEC_ID_VP9 && track->mode != MODE_MP4) { > + av_log(s, AV_LOG_ERROR, "%s only supported in MP4.\n", avcodec_get_name(track->par->codec_id)); > + return AVERROR(EINVAL); > + } else if (track->par->codec_id == AV_CODEC_ID_AV1 && > + track->mode != MODE_MP4 && track->mode != MODE_AVIF) { > + av_log(s, AV_LOG_ERROR, "%s only supported in MP4 and AVIF.\n", avcodec_get_name(track->par->codec_id)); > + return AVERROR(EINVAL); > } else if (track->par->codec_id == AV_CODEC_ID_VP8) { > /* altref frames handling is not defined in the spec as of version v1.0, > * so just forbid muxing VP8 streams altogether until a new version does */ > @@ -7003,7 +7204,7 @@ static int mov_write_header(AVFormatContext *s) > FF_MOV_FLAG_FRAG_EVERY_FRAME)) && > !mov->max_fragment_duration && !mov->max_fragment_size) > mov->flags |= FF_MOV_FLAG_FRAG_KEYFRAME; > - } else { > + } else if (mov->mode != MODE_AVIF) { > if (mov->flags & FF_MOV_FLAG_FASTSTART) > mov->reserved_header_pos = avio_tell(pb); > mov_write_mdat_tag(pb, mov); > @@ -7291,6 +7492,54 @@ static int mov_check_bitstream(AVFormatContext *s, AVStream *st, > return ret; > } > > +static int avif_write_trailer(AVFormatContext *s) > +{ > + AVIOContext *pb = s->pb; > + MOVMuxContext *mov = s->priv_data; > + int64_t pos_backup, mdat_pos; > + uint8_t *buf; > + int buf_size, moov_size; > + int i; > + > + if (mov->moov_written) return 0; > + > + mov->is_animated_avif = s->streams[0]->nb_frames > 1; > + mov_write_identification(pb, s); > + mov_write_meta_tag(pb, mov, s); > + > + moov_size = get_moov_size(s); > + for (i = 0; i < mov->nb_streams; i++) > + mov->tracks[i].data_offset = avio_tell(pb) + moov_size + 8; > + > + if (mov->is_animated_avif) { > + int ret; > + if ((ret = mov_write_moov_tag(pb, mov, s)) < 0) > + return ret; > + } > + > + buf_size = avio_get_dyn_buf(mov->mdat_buf, &buf); > + avio_wb32(pb, buf_size + 8); > + ffio_wfourcc(pb, "mdat"); > + mdat_pos = avio_tell(pb); > + > + avio_write(pb, buf, buf_size); > + ffio_free_dyn_buf(&mov->mdat_buf); > + > + // write extent offset. > + pos_backup = avio_tell(pb); > + avio_seek(pb, mov->avif_extent_pos, SEEK_SET); > + avio_wb32(pb, mdat_pos); /* rewrite offset */ > + avio_seek(pb, pos_backup, SEEK_SET); > + > + mov->moov_written = 1; > + mov->mdat_size = 0; > + for (i = 0; i < mov->nb_streams; i++) { > + mov->tracks[i].entry = 0; > + mov->tracks[i].end_reliable = 0; > + } Why this? write_trailer is only called once. > + return 0; > +}> + > #if CONFIG_TGP_MUXER || CONFIG_TG2_MUXER > static const AVCodecTag codec_3gp_tags[] = { > { AV_CODEC_ID_H263, MKTAG('s','2','6','3') }, > @@ -7373,6 +7622,12 @@ static const AVCodecTag codec_f4v_tags[] = { > { AV_CODEC_ID_NONE, 0 }, > }; > > +static const AVCodecTag codec_avif_tags[] = { > + { AV_CODEC_ID_AV1, MKTAG('a','v','0','1') }, > + { AV_CODEC_ID_NONE, 0 }, > +}; > +static const AVCodecTag *const codec_avif_tags_list[] = { codec_avif_tags, NULL }; Why is this not under #if CONFIG_AVIF_MUXER (or part of the other #if CONFIG_AVIF_MUXER below)? > + > #if CONFIG_MOV_MUXER > const AVOutputFormat ff_mov_muxer = { > .name = "mov", > @@ -7535,3 +7790,21 @@ const AVOutputFormat ff_f4v_muxer = { > .priv_class = &mov_isobmff_muxer_class, > }; > #endif > +#if CONFIG_AVIF_MUXER > +const AVOutputFormat ff_avif_muxer = { > + .name = "avif", > + .long_name = NULL_IF_CONFIG_SMALL("AVIF"), > + .mime_type = "image/avif", > + .extensions = "avif", > + .priv_data_size = sizeof(MOVMuxContext), > + .video_codec = AV_CODEC_ID_AV1, > + .init = mov_init, > + .write_header = mov_write_header, > + .write_packet = mov_write_packet, > + .write_trailer = avif_write_trailer, > + .deinit = mov_free, > + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, > + .codec_tag = codec_avif_tags_list, > + .priv_class = &mov_isobmff_muxer_class, This gives this muxer all the options of the other muxers; yet which one of these are actually supported? E.g. is faststart supported? The code for it is in mov_write_trailer() and that is not called. > +}; > +#endif > diff --git a/libavformat/movenc.h b/libavformat/movenc.h > index 2ac84ed070..55b8469f68 100644 > --- a/libavformat/movenc.h > +++ b/libavformat/movenc.h > @@ -43,6 +43,7 @@ > #define MODE_IPOD 0x20 > #define MODE_ISM 0x40 > #define MODE_F4V 0x80 > +#define MODE_AVIF 0x100 > > typedef struct MOVIentry { > uint64_t pos; > @@ -242,6 +243,10 @@ typedef struct MOVMuxContext { > MOVPrftBox write_prft; > int empty_hdlr_name; > int movie_timescale; > + > + int64_t avif_extent_pos; > + int avif_extent_length; > + int is_animated_avif; > } MOVMuxContext; > > #define FF_MOV_FLAG_RTP_HINT (1 << 0) _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".