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 638CE41042 for ; Wed, 13 Apr 2022 21:04:46 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 85E5068B459; Thu, 14 Apr 2022 00:04:43 +0300 (EEST) Received: from EUR01-HE1-obe.outbound.protection.outlook.com (mail-oln040092065093.outbound.protection.outlook.com [40.92.65.93]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 8364968ADCC for ; Thu, 14 Apr 2022 00:04:36 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ZUvG40bKxXmX6m13nU24hchlyHAPWZ/25PYyehKi2DmaAxy48hWsWFNNb8nCNoUCKWy8sIS/uxnYYziQiGtMdyHoLna9DS0UP6oEsm6rJBOJ4Dl+Y8rG4DMqWEGkOe8WMyGmnI/ELUOYiQGZpbpd3hHu47l9I3+DyHzNsZvRwWfCW3Ml4J4UkLFFwI0ZUVFTh5jqyB7pnBobmBc5P7jJQpvYcG3ZjRmK0c19cYlhTfQbVOaT2ZkfKRCf6mW2oqMgYhvL1fdYKob6EuBaJlaDcrmpa62szS6h67QEapzP8gpfV5qnjXpRg/ZRpbNCeAmVNEFJoJlhssuSkjtlZ7aOfg== 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=lmoq4rYzEkfwC4WO4WCmIWgRWiLYOEd10Kf8g5kxB8M=; b=bDk92KIvXtK33YJIGfIVIphfNzaA2RZoH8Hn90kyMvzW4hHqy1DWr+/jI8yYbOnXBLV7gcXCDLtzJ8YEsWzFJeR6xX+O2mY89TRDj4glZ2UNvGZLeqIdk377byqAleu2+AWGmhN+sXTy+nIfVYJzSxTtaie7cIJx6O/bDOiDjUseMVcaghbSIGqXG0Om7GR23+ugh9uOl4rpq4+WRW78WUwOatMmch3JLIUspfQK2oj2cc4VE4LQx0DHJYVauuXqa8YpR/HbWFiFMKT/9gvMBCgzNyHZZrgPRpMBmRc8xKbHc395UuZ3F/CgrKvfwVGGPyI40uP7QR2CQ9cIvIPiWg== 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=lmoq4rYzEkfwC4WO4WCmIWgRWiLYOEd10Kf8g5kxB8M=; b=NLJUMUDHmWxkrc4jMMDHnoUNlJxV3h/8NlFlFij7kyoT8VZMtHsYzOHTtv8qX+lWnNz0gnV8HqPIVGaUkoWd8INowVV7BNJfcahkTSmHzzUZXtQYNos0knI3jYoPkTvVFlvftvMkoHxY8GXYfF2puTWWhZZfLzrxOvm7VBuQk2HnVnuhGywh4XnysjWOOG+9fg02vbNxJQfMtVUiRVnYDCgW1Gng9TBamT22T0HEEy6b5tNcNbcpk3W/0evD1vBNs6xKqtRp5mlJxuE/nyhhXzwvKczWp8yRkBDPeBXtimDZv5GY+T1g5EnJHh6VkCjNAdam04yK1Q7zr+Q0x6Ex2w== Received: from AS8PR01MB7944.eurprd01.prod.exchangelabs.com (2603:10a6:20b:373::5) by HE1PR01MB3739.eurprd01.prod.exchangelabs.com (2603:10a6:7:9f::26) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5144.29; Wed, 13 Apr 2022 21:04:34 +0000 Received: from AS8PR01MB7944.eurprd01.prod.exchangelabs.com ([fe80::fcc3:1285:374c:9b2]) by AS8PR01MB7944.eurprd01.prod.exchangelabs.com ([fe80::fcc3:1285:374c:9b2%5]) with mapi id 15.20.5164.020; Wed, 13 Apr 2022 21:04:34 +0000 Message-ID: Date: Wed, 13 Apr 2022 23:04:32 +0200 Content-Language: en-US To: ffmpeg-devel@ffmpeg.org References: <20220310181232.1924687-1-vigneshv@google.com> From: Andreas Rheinhardt In-Reply-To: X-TMN: [rYNbwSz/DVF5KlmrSKVqWvd46kmvhenQ] X-ClientProxiedBy: ZR0P278CA0116.CHEP278.PROD.OUTLOOK.COM (2603:10a6:910:20::13) To AS8PR01MB7944.eurprd01.prod.exchangelabs.com (2603:10a6:20b:373::5) X-Microsoft-Original-Message-ID: MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: a14d4179-2c55-4ed2-9571-08da1d913603 X-MS-TrafficTypeDiagnostic: HE1PR01MB3739:EE_ X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: mgKvGbjZXf4ar1I7lhw26dj1GSm86L9qVl/Ac1JvHiM8W3Bk+ECeQfqIiAIaE3iBnfasEuk7URDSeEDyXZ7Es/8TIOnyPRkXjcZ04Z9Z9zF1N3ufIqN/jY4Up5LGdAeNOidDurnzFpofY7AgxldQO4j61WK+CadHcuiNudnOItstihNSQm1eZQykMp8lfDQ0z94Ji1l4cDFI8ae4nPuuCILVyiXtmJ7N4kNmR+6FZRD+zXfqrqiK2XWWnzq2RD+9nyOT9nvBx3NdpZVo8ywuRrEgQp0ancEJqn0EtizY3CDKkgukE2ykUKfZ41M182njqIwRsVmfh8gf1p5elkfBJQvHFpTC988rm+nqv3gKbXLv9v69I5sA3FI1SqX37okSSGq07kolS3NHNeaXKJTS3x43oGUZxSpOWfCurZChJWdXyehxvUJ3kX5V16nu2UKI3wp06Ti1dhlJISRyoflX0S2M0mqU/YyKi5XiLXbLkC+gld0aKOObVpJRyWAYlg3U4ymf7c1a6l3p+XHKigAT0PhB+enUEFxVz6Eh9yWJFpva1EY3CVvvpQk4CosefGZIyu3X7N/TKqaKVcCjC8ria7kSZB1naWpFNSrFtCJI2h0l0JSeW0h0Th30hGRAA0ANJFJ3cPeHaQnqKLiCvMOdT0Z1814M6EytQb0YS0s6Lio= X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?OHVJUTZkOW5IQTB0ZElDUEgvQWhKQ21QTW5KcDZEdVV0aEw0WWovL3J2VEV1?= =?utf-8?B?OUhDTVFpTWhFWnorTW5rTWtjMWZEN2ZIQy9YNThtK3lxYXI3RXRDUFN0eFNN?= =?utf-8?B?bUZ4NGlmWUlLTk90cjArQWpQbG0zcHhWVk5CcHVEVWlZRVhFTDFJYjl3b21Z?= =?utf-8?B?c1lRTlpzdDh4QUhvMk02eGtkVVVKZmZIMTE4V1oyN2FSQXJKaDRPak1Ndk56?= =?utf-8?B?Q0xLTWx1L01oN1VGcEdMRlpKbXBBaDBrd05GRDN5NmhNRmZ6dkordnlldGR4?= =?utf-8?B?WE5xZXpnRU96eElXdmk1VTVKVkh2TFBtVGcvbHRIODVYM2c0cTc1ckZsRXo0?= =?utf-8?B?RzJ1WjdOVVM4OXAvaStDQUlWMVZ0SXJRSUR3UHVJeVNGMG1YMGNDQVZueTZH?= =?utf-8?B?R0RmdTJ4UFBzdW5nWndhb28wNzRyK3pQS0tZelE3dW1seUFLOWFUWGJ3Y1VS?= =?utf-8?B?MGJIc1lwakZJSmdlZk55TkExOGxveFUxb3BhdkZHWHpaQnZYeno5TVYxYmE2?= =?utf-8?B?R2VYb0xwVkRDYWtPK0s0K3VaaXIwMGZEN09iR21yUCtUN29Mc3IyOTJqajZu?= =?utf-8?B?QzdwdG1JWXcvQ3F2L3VHTklTUThvSUJLYWJicHdHc3lQYWc4eEZOUk96N2xo?= =?utf-8?B?REt4b1M1NVh6UXNNdEx3SjhuT3BMYlREODNCaElDSkxhMTR0TWUyZzNyMDJO?= =?utf-8?B?K0ExZTh4elE2SGtpcVVnWFQ2MnJ1QU1rcGFqTmtpNWZEK1dGaVB4SFRIWVBM?= =?utf-8?B?N1ZTQmU3NHErK1NmZmhvNFE0U2xseUNaeGQyNWQ4MFp1WVhnbkhzaGhkYU5Y?= =?utf-8?B?TUxjSVJFTlNwTW1CZGJ4SEVCTWUvaWdnVTB3WmJwRFNuMElpMzI3amd2Q2pq?= =?utf-8?B?dkNkLzQ5aTZjenc5V2xQcFBGaHJHUkRCVmFaQnc2UGlRUHlLV2IwMkZuZk5W?= =?utf-8?B?Mjl5NnpqTkJ2Uk1zWk1uTW5WT04rWSsyNHZ2bUphakphZmpxK1lTMVlZdlF5?= =?utf-8?B?Ync1d2FIZ1VEN3RtWGdKalU4Y3VyUnRPcXh0MXJoc2MwaStiNTUvTFZJZGNO?= =?utf-8?B?dmN0ZGdFSnRvMGFyL3hYK2t5bVcvSUs0QlZURUZOeWZ4ZVFyK0toVVZ5YVc3?= =?utf-8?B?ajEyWG5kclFvRGNNUmdCTHM5ZnBpVWo3MnRPN1lGbm14aVFNQU80c2tMaHo1?= =?utf-8?B?dFZhbksxRUNJQWV3V0xoZU0vc1A2cFIxekxHeldGOFlMZk9CY2FlSnFmUkts?= =?utf-8?B?em80c05hMURJdFQ2NHh5V1FGaE9IOHFHc2djaTM5ZHllRzhPMmE1YVF3L1ZZ?= =?utf-8?B?amwzcHpmSVppRDdYQVM2eEpPa3djSHVMZ2tIWFpJL0hzY1RZbm1KblRqcUxh?= =?utf-8?B?Ym5Da0JUZjRkVld3NVdqVHE1eTlBMzdISEVjR2ZheXVYT3I1TmdmdnJIYlFY?= =?utf-8?B?NGRKdlFNMU56Z3RMVkc1bWMvanNuYkZnZkRGTlFnUmVTUGpQRXBUYmJsOWFJ?= =?utf-8?B?WThKVFdEa1FMeHA3elkycGJzM3d1Sk5iMHVBOVVUSThSbUdJY3doT3J4WTBj?= =?utf-8?B?OW5uZnhKRFF3RForNnAyYXRoK3VBTklRNDBwc1ArZGc1VUROY3NaMDRhSlJ3?= =?utf-8?B?L3QzOXFhR2tRRjN0TFplZlNrOGF4YzZSOE1MNXcwRVJJT0JUdGtJMFpjL1g4?= =?utf-8?B?MkhhZDhuT1BMZzJrODZGZXJWUEp3L1VoeU9Yem1EYmorSFJ0S0dCNHh1MTlP?= =?utf-8?B?UlBDZksxUlpmNjdGWXRESlV1VjJBdnhMR1JPdEQzSjBJNDI5MHN6UlJWaHdT?= =?utf-8?B?L3lJZ2V0OU8rRERDdzh0OGdnMlBLTTM1TlZieWl3RXBVTVhXcUNFZjdlcGtp?= =?utf-8?B?OGNiVGMzM3ZUS2RqbEs5Q0xGZ2NmZzhQNG1jbHRiUkc2NWNITXYxWjdwYUZT?= =?utf-8?Q?UoUnZi/ylI1N3mBWgS95xhxsC6ZLu3nl?= X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: a14d4179-2c55-4ed2-9571-08da1d913603 X-MS-Exchange-CrossTenant-AuthSource: AS8PR01MB7944.eurprd01.prod.exchangelabs.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 13 Apr 2022 21:04:34.1901 (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: HE1PR01MB3739 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: > On Mon, Mar 21, 2022 at 1:46 PM Andreas Rheinhardt > wrote: >> >> 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 | 341 ++++++++++++++++++++++++++++++++++++--- >>> libavformat/movenc.h | 5 + >>> 4 files changed, 322 insertions(+), 26 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..ff41579300 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); >> >> Is the number of planes really the correct number here? After all, for a >> muxer (instead of a decoder) it does not matter whether the chroma >> planes are interleaved like in AV_PIX_FMT_NV12 or not like in >> AV_PIX_FMT_YUV420P. You should better use pixdesc->nb_components. >> > > Done. > >>> + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(s->streams[0]->codecpar->format); >>> + int i; >> >> We allow and use "for (int i = 0;" from C99 to save lines like these. >> > > Done. > >>> + 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. >>> + 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) { >> >> Why this? AVIF requires that AVI-in-ISOBMFF sample format is honoured >> and this contains e.g. "OBUs of type OBU_TEMPORAL_DELIMITER, >> OBU_PADDING, or OBU_REDUNDANT_FRAME_HEADER SHOULD NOT be used". >> ff_av1_filter_obus(_buf)? merely ensures that these OBUs are stripped away. >> >> If the aim of this check is to disallow hint tracks or so, then it fails >> (all it does is ensuring that both the ordinary track as well as the >> hint track get data that might contain OBUs that should not be there). >> > > Done. I had to update the extent offset code here as well. > >>> + 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 || >>> @@ -6654,11 +6854,25 @@ static int mov_init(AVFormatContext *s) >>> /* Non-seekable output is ok if using fragmentation. If ism_lookahead >>> * is enabled, we don't support non-seekable output at all. */ >>> if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL) && >>> - (!(mov->flags & FF_MOV_FLAG_FRAGMENT) || mov->ism_lookahead)) { >>> + (!(mov->flags & FF_MOV_FLAG_FRAGMENT) || mov->ism_lookahead || >>> + mov->mode == MODE_AVIF)) { >>> av_log(s, AV_LOG_ERROR, "muxer does not support non seekable output\n"); >>> return AVERROR(EINVAL); >>> } >>> >>> + /* AVIF output must have exactly one video stream */ >>> + if (mov->mode == MODE_AVIF) { >>> + if (s->nb_streams > 1) { >>> + av_log(s, AV_LOG_ERROR, "AVIF output requires exactly one stream\n"); >>> + return AVERROR(EINVAL); >>> + } >>> + if (s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) { >>> + av_log(s, AV_LOG_ERROR, "AVIF output requires one video stream\n"); >>> + return AVERROR(EINVAL); >>> + } >>> + } >>> + >>> + >>> mov->nb_streams = s->nb_streams; >>> if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters) >>> mov->chapter_track = mov->nb_streams++; >>> @@ -6797,12 +7011,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 +7218,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 +7506,48 @@ 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; >> >> Don't call avio_tell() in a loop (and I wonder whether this loop is even >> necessary given that s->nb_streams is checked to be one). >> > > Removed the loop. I was thinking about potentially supporting multiple > output tracks in the future (for alpha channel for example), but this > code would not work in that case any way. > >>> + >>> + 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); >> >> Unnecessary: This will be freed in mov_free(). >> > > Removed. > >>> + >>> + // write extent offset. >>> + pos_backup = avio_tell(pb); >>> + avio_seek(pb, mov->avif_extent_pos, SEEK_SET); >>> + avio_wb32(pb, mdat_pos); /* rewrite offset */ >> >> What guarantees that this fits into 32bits? >> > > Added a check to fail if it does not fit in 32-bits. > >>> + avio_seek(pb, pos_backup, SEEK_SET); >>> + >>> + 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 +7630,20 @@ static const AVCodecTag codec_f4v_tags[] = { >>> { AV_CODEC_ID_NONE, 0 }, >>> }; >>> >>> +#if CONFIG_AVIF_MUXER >>> +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 }; >>> + >>> +static const AVClass mov_avif_muxer_class = { >>> + .class_name = "avif muxer", >>> + .item_name = av_default_item_name, >>> + .version = LIBAVUTIL_VERSION_INT, >>> +}; >> >> It's not mandatory for a muxer to have a private class; it is only >> necessary for options. If you do not have options (like here), then the >> AVClass is useless. >> I actually wanted that you support the options that make sense for AVIF >> and I dislike that the avif muxer uses a different write_trailer than >> every other muxer here (which is the reason why e.g. the faststart >> option is not supported by it). Is it really so different? >> Furthermore, given that you don't use the same AVClass as everyone else, >> the values for fields that are AVOpt-enabled are zero; and this does not >> always coincide with the default value of the relevant option. See e.g. >> skip_iods, iods_audio_profile, iods_video_profile etc. movie_timescale >> will even be set to a value that is outside of its legal range. I don't >> know whether this can lead to any divide-by-zero crashes or asserts, but >> it is certainly very fragile. >> > > Hmm, i see a couple of solutions here: > 1) Use the same private class as the MOV muxer and document that not > all options are supported when the format is AVIF. I think there are > already cases within this file where not all muxers may support all > the options. > 2) Leave it as-is since the code always checks for AVIF mode first and > then does the rest, so the defaults of the non-relevant options should > not matter. But i agree that this is very fragile. One way to ensure > future changes don't break this structure is to add a fate test. > Can you tell which options (if enabled) would work and which would not work or would create spec-incompliant output? > Please let me know what you think. > > About using a separate write_trailer function, i can re-use the > existing mov_write_trailer but it will mostly be a special case for > AVIF mode with the code in avif_write_trailer, so i thought it was > cleaner to have a separate function anyway. I am not sure if there are > any other implications because of this. If you prefer that i move it > into mov_write_trailer, please let me know and i can do that as well. > _______________________________________________ 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".