From: Vignesh Venkatasubramanian <vigneshv-at-google.com@ffmpeg.org> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Subject: Re: [FFmpeg-devel] [PATCH 3/3] avformat/movenc: Add support for AVIF muxing Date: Fri, 29 Apr 2022 09:03:53 -0700 Message-ID: <CAOJaEP+WYLLZD8aVhgW-XXX1VgUM9AZsJvy+0ygwt=kdz_s+AQ@mail.gmail.com> (raw) In-Reply-To: <CAOJaEPLez_TaHgr_vsB6ug_nR0FxjROcMe9yv=cqTCmXi0oYMQ@mail.gmail.com> On Thu, Apr 21, 2022 at 9:38 AM Vignesh Venkatasubramanian <vigneshv@google.com> wrote: > > On Wed, Apr 13, 2022 at 2:35 PM Vignesh Venkatasubramanian > <vigneshv@google.com> wrote: > > > > On Wed, Apr 13, 2022 at 2:04 PM Andreas Rheinhardt > > <andreas.rheinhardt@outlook.com> wrote: > > > > > > Vignesh Venkatasubramanian: > > > > On Mon, Mar 21, 2022 at 1:46 PM Andreas Rheinhardt > > > > <andreas.rheinhardt@outlook.com> 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 <vigneshv@google.com> > > > >>> --- > > > >>> 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? > > > > > > > The following options will make sense for AVIF (and may just work > > as-is, i am not sure of this): write_colr, prefer_icc, > > video_track_timescale and a few other generic MOV options like > > use_stream_ids_as_track_ids, empty_hdlr_name, and brand. > > > > Any thoughts on this? > Another ping on this. :) > > > > 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". > > > > > > > > -- > > Vignesh > > > > -- > Vignesh -- Vignesh _______________________________________________ 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".
next prev parent reply other threads:[~2022-04-29 16:04 UTC|newest] Thread overview: 71+ messages / expand[flat|nested] mbox.gz Atom feed top 2022-02-17 5:51 [FFmpeg-devel] [PATCH 1/3] avcodec/libaomenc: Add parameter for avif single image encoding Vignesh Venkatasubramanian 2022-02-17 5:51 ` [FFmpeg-devel] [PATCH 2/3] avformat/av1: Add a parameter to av1c to omit seq header Vignesh Venkatasubramanian 2022-03-02 22:57 ` James Almer 2022-03-02 23:22 ` Vignesh Venkatasubramanian 2022-03-02 23:23 ` Vignesh Venkatasubramanian 2022-03-02 23:27 ` James Almer 2022-03-28 20:48 ` Vignesh Venkatasubramanian 2022-04-13 20:40 ` Vignesh Venkatasubramanian 2022-05-02 21:36 ` Vignesh Venkatasubramanian 2022-02-17 5:51 ` [FFmpeg-devel] [PATCH 3/3] avformat/movenc: Add support for AVIF muxing Vignesh Venkatasubramanian 2022-02-22 18:40 ` Vignesh Venkatasubramanian 2022-02-22 20:03 ` James Almer 2022-02-22 21:37 ` Vignesh Venkatasubramanian 2022-02-22 21:38 ` Vignesh Venkatasubramanian 2022-02-22 21:43 ` Vignesh Venkatasubramanian 2022-02-24 17:34 ` Vignesh Venkatasubramanian 2022-03-01 16:49 ` Vignesh Venkatasubramanian 2022-03-03 15:36 ` James Almer 2022-03-03 19:16 ` Vignesh Venkatasubramanian 2022-03-04 11:24 ` James Almer 2022-03-04 17:52 ` Vignesh Venkatasubramanian 2022-03-04 17:54 ` Vignesh Venkatasubramanian 2022-03-09 19:34 ` Vignesh Venkatasubramanian 2022-03-10 16:01 ` Andreas Rheinhardt 2022-03-10 18:12 ` Vignesh Venkatasubramanian 2022-03-21 20:46 ` Andreas Rheinhardt 2022-03-22 16:45 ` Vignesh Venkatasubramanian 2022-03-22 16:46 ` Vignesh Venkatasubramanian 2022-03-28 17:06 ` Vignesh Venkatasubramanian 2022-03-28 20:49 ` Vignesh Venkatasubramanian 2022-04-07 18:25 ` Vignesh Venkatasubramanian 2022-04-13 17:21 ` James Zern 2022-04-13 20:40 ` Vignesh Venkatasubramanian 2022-04-13 21:01 ` Andreas Rheinhardt 2022-04-13 21:33 ` Vignesh Venkatasubramanian 2022-05-02 17:28 ` James Zern 2022-05-02 21:34 ` Vignesh Venkatasubramanian 2022-05-02 21:35 ` Vignesh Venkatasubramanian 2022-05-03 23:39 ` James Zern 2022-05-04 2:46 ` "zhilizhao(赵志立)" 2022-05-04 16:45 ` Vignesh Venkatasubramanian 2022-05-04 16:48 ` Vignesh Venkatasubramanian 2022-05-04 17:10 ` "zhilizhao(赵志立)" 2022-05-04 17:14 ` Vignesh Venkatasubramanian 2022-05-04 17:15 ` Vignesh Venkatasubramanian 2022-05-11 16:54 ` Vignesh Venkatasubramanian 2022-05-11 17:25 ` Gyan Doshi 2022-05-12 10:26 ` Gyan Doshi 2022-05-12 16:23 ` Vignesh Venkatasubramanian 2022-05-12 16:23 ` Vignesh Venkatasubramanian 2022-05-13 7:22 ` Gyan Doshi 2022-04-13 20:41 ` Vignesh Venkatasubramanian 2022-04-13 21:04 ` Andreas Rheinhardt 2022-04-13 21:35 ` Vignesh Venkatasubramanian 2022-04-21 16:38 ` Vignesh Venkatasubramanian 2022-04-29 16:03 ` Vignesh Venkatasubramanian [this message] 2022-03-10 18:14 ` Vignesh Venkatasubramanian 2022-03-15 15:59 ` Vignesh Venkatasubramanian 2022-03-21 17:07 ` Vignesh Venkatasubramanian 2022-03-03 19:20 ` Vignesh Venkatasubramanian 2022-03-03 19:46 ` James Almer 2022-03-03 19:57 ` Vignesh Venkatasubramanian 2022-02-17 18:09 ` [FFmpeg-devel] [PATCH 1/3] avcodec/libaomenc: Add parameter for avif single image encoding James Zern 2022-02-17 19:33 ` Vignesh Venkatasubramanian 2022-02-17 20:59 ` James Almer 2022-02-17 21:18 ` [FFmpeg-devel] [PATCH] " Vignesh Venkatasubramanian 2022-02-17 21:20 ` [FFmpeg-devel] [PATCH 1/3] " Vignesh Venkatasubramanian 2022-02-22 21:36 ` Vignesh Venkatasubramanian 2022-03-28 20:47 ` Vignesh Venkatasubramanian 2022-04-13 20:39 ` Vignesh Venkatasubramanian 2022-05-02 21:37 ` Vignesh Venkatasubramanian
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to='CAOJaEP+WYLLZD8aVhgW-XXX1VgUM9AZsJvy+0ygwt=kdz_s+AQ@mail.gmail.com' \ --to=vigneshv-at-google.com@ffmpeg.org \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git