[FFmpeg-devel] [PATCH v1 2/5] avformat: add muxer support for H266/VVC

Thomas Siedel thomas.ff at spin-digital.com
Fri Jan 26 16:11:31 EET 2024


On Fri, 5 Jan 2024 at 01:31, James Almer <jamrial at gmail.com> wrote:

> On 11/3/2023 6:57 AM, Thomas Siedel wrote:
> > Add muxer for vvcc byte stream format.
> > Add AV_CODEC_ID_VVC to ff_mp4_obj_type.
> > Add AV_CODEC_ID_VVC to ISO Media codec (VvcConfigurationBox vvi1,
> > vvc1 defined in ISO/IEC 14496-15:2021).
> > Add VvcConfigurationBox vvcC which extends FullBox type in
> > ISO/IEC 14496-15:2021.
> > Add ff_vvc_muxer to RAW muxers.
> >
> > Signed-off-by: Thomas Siedel <thomas.ff at spin-digital.com>
> > ---
> >   libavformat/Makefile    |   6 +-
> >   libavformat/isom.c      |   1 +
> >   libavformat/isom_tags.c |   3 +
> >   libavformat/mov.c       |   6 +
> >   libavformat/movenc.c    |  41 +-
> >   libavformat/vvc.c       | 998 ++++++++++++++++++++++++++++++++++++++++
> >   libavformat/vvc.h       |  99 ++++
> >   7 files changed, 1150 insertions(+), 4 deletions(-)
> >   create mode 100644 libavformat/vvc.c
> >   create mode 100644 libavformat/vvc.h
> >
> > diff --git a/libavformat/Makefile b/libavformat/Makefile
> > index 329055ccfd..595f6bdf08 100644
> > --- a/libavformat/Makefile
> > +++ b/libavformat/Makefile
> > @@ -341,7 +341,7 @@ OBJS-$(CONFIG_MATROSKA_DEMUXER)          +=
> matroskadec.o matroska.o  \
> >                                               oggparsevorbis.o
> vorbiscomment.o \
> >                                               qtpalette.o replaygain.o
> dovi_isom.o
> >   OBJS-$(CONFIG_MATROSKA_MUXER)            += matroskaenc.o matroska.o \
> > -                                            av1.o avc.o hevc.o \
> > +                                            av1.o avc.o hevc.o vvc.o\
> >                                               flacenc_header.o
> avlanguage.o \
> >                                               vorbiscomment.o wv.o
> dovi_isom.o
> >   OBJS-$(CONFIG_MCA_DEMUXER)               += mca.o
> > @@ -363,7 +363,7 @@ OBJS-$(CONFIG_MODS_DEMUXER)              += mods.o
> >   OBJS-$(CONFIG_MOFLEX_DEMUXER)            += moflex.o
> >   OBJS-$(CONFIG_MOV_DEMUXER)               += mov.o mov_chan.o
> mov_esds.o \
> >                                               qtpalette.o replaygain.o
> dovi_isom.o
> > -OBJS-$(CONFIG_MOV_MUXER)                 += movenc.o av1.o avc.o hevc.o
> vpcc.o \
> > +OBJS-$(CONFIG_MOV_MUXER)                 += movenc.o av1.o avc.o hevc.o
> vvc.o vpcc.o \
> >                                               movenchint.o mov_chan.o
> rtp.o \
> >                                               movenccenc.o movenc_ttml.o
> rawutils.o \
> >                                               dovi_isom.o evc.o
> > @@ -516,7 +516,7 @@ OBJS-$(CONFIG_RTP_MUXER)                 += rtp.o
>      \
> >                                               rtpenc_vp8.o  \
> >                                               rtpenc_vp9.o
>   \
> >                                               rtpenc_xiph.o \
> > -                                            avc.o hevc.o
> > +                                            avc.o hevc.o vvc.o
> >   OBJS-$(CONFIG_RTSP_DEMUXER)              += rtsp.o rtspdec.o
> httpauth.o \
> >                                               urldecode.o
> >   OBJS-$(CONFIG_RTSP_MUXER)                += rtsp.o rtspenc.o
> httpauth.o \
> > diff --git a/libavformat/isom.c b/libavformat/isom.c
> > index 6d019881e5..9fbccd4437 100644
> > --- a/libavformat/isom.c
> > +++ b/libavformat/isom.c
> > @@ -36,6 +36,7 @@ const AVCodecTag ff_mp4_obj_type[] = {
> >       { AV_CODEC_ID_MPEG4       , 0x20 },
> >       { AV_CODEC_ID_H264        , 0x21 },
> >       { AV_CODEC_ID_HEVC        , 0x23 },
> > +    { AV_CODEC_ID_VVC         , 0x33 },
> >       { AV_CODEC_ID_AAC         , 0x40 },
> >       { AV_CODEC_ID_MP4ALS      , 0x40 }, /* 14496-3 ALS */
> >       { AV_CODEC_ID_MPEG2VIDEO  , 0x61 }, /* MPEG-2 Main */
> > diff --git a/libavformat/isom_tags.c b/libavformat/isom_tags.c
> > index a575b7c160..705811e950 100644
> > --- a/libavformat/isom_tags.c
> > +++ b/libavformat/isom_tags.c
> > @@ -123,6 +123,9 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
> >       { AV_CODEC_ID_HEVC, MKTAG('d', 'v', 'h', 'e') }, /* HEVC-based
> Dolby Vision derived from hev1 */
> >                                                        /* dvh1 is
> handled within mov.c */
> >
> > +    { AV_CODEC_ID_VVC, MKTAG('v', 'v', 'i', '1') },  /* VVC/H.266 which
> indicates parameter sets may be in ES */
> > +    { AV_CODEC_ID_VVC, MKTAG('v', 'v', 'c', '1') },  /* VVC/H.266 which
> indicates parameter shall not be in ES */
> > +
> >       { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, /* AVC-1/H.264 */
> >       { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '2') },
> >       { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') },
> > diff --git a/libavformat/mov.c b/libavformat/mov.c
> > index e8efccf6eb..11b43ac135 100644
> > --- a/libavformat/mov.c
> > +++ b/libavformat/mov.c
> > @@ -2117,6 +2117,11 @@ static int mov_read_glbl(MOVContext *c,
> AVIOContext *pb, MOVAtom atom)
> >       if ((uint64_t)atom.size > (1<<30))
> >           return AVERROR_INVALIDDATA;
> >
> > +    if (atom.type == MKTAG('v','v','c','C')) {
> > +        avio_rb32(pb);
>
> avio_skip(pb, 4);
>
> > +        atom.size -= 4;
> > +    }
> > +
> >       if (atom.size >= 10) {
> >           // Broken files created by legacy versions of libavformat will
> >           // wrap a whole fiel atom inside of a glbl atom.
> > @@ -7921,6 +7926,7 @@ static const MOVParseTableEntry
> mov_default_parse_table[] = {
> >   { MKTAG('s','g','p','d'), mov_read_sgpd },
> >   { MKTAG('s','b','g','p'), mov_read_sbgp },
> >   { MKTAG('h','v','c','C'), mov_read_glbl },
> > +{ MKTAG('v','v','c','C'), mov_read_glbl },
> >   { MKTAG('u','u','i','d'), mov_read_uuid },
> >   { MKTAG('C','i','n', 0x8e), mov_read_targa_y216 },
> >   { MKTAG('f','r','e','e'), mov_read_free },
> > diff --git a/libavformat/movenc.c b/libavformat/movenc.c
> > index e39f1ac987..093a04d0c2 100644
> > --- a/libavformat/movenc.c
> > +++ b/libavformat/movenc.c
> > @@ -68,6 +68,7 @@
> >   #include "ttmlenc.h"
> >   #include "version.h"
> >   #include "vpcc.h"
> > +#include "vvc.h"
> >
> >   static const AVOption options[] = {
> >       { "movflags", "MOV muxer flags", offsetof(MOVMuxContext, flags),
> AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX,
> AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
> > @@ -1473,6 +1474,23 @@ static int mov_write_evcc_tag(AVIOContext *pb,
> MOVTrack *track)
> >       return update_size(pb, pos);
> >   }
> >
> > +static int mov_write_vvcc_tag(AVIOContext *pb, MOVTrack *track)
> > +{
> > +    int64_t pos = avio_tell(pb);
> > +
> > +    avio_wb32(pb, 0);
> > +    ffio_wfourcc(pb, "vvcC");
> > +
> > +    avio_w8  (pb, 0); /* version */
> > +    avio_wb24(pb, 0); /* flags */
>
> Is it really a fullbox? I find it odd considering h264 and h265 don't.
>

 Yes it is. spec: ISO/IEC 14496-15:2021(E)
  Information technology — Coding of audio-visual objects — Part 15:
Carriage of network abstraction layer (NAL) unit structured video in the
ISO base media file format
  11.2.4.3.1  Definition
  This subclause specifies VvcConfigurationBox that carries a VVC decoder
configuration record.
   This box derives from FullBox and hence contains a version field. This
version of the specification defines version 0 of this box.
   Incompatible changes to the box will be indicated by a change of version
number.
   Readers shall not attempt to decode this box or the referenced CVSs if
the version number is unrecognized.


>
> > +
> > +    if (track->tag == MKTAG('v','v','c','1'))
> > +        ff_isom_write_vvcc(pb, track->vos_data, track->vos_len, 1);
> > +    else
> > +        ff_isom_write_vvcc(pb, track->vos_data, track->vos_len, 0);
> > +    return update_size(pb, pos);
> > +}
> > +
> >   /* also used by all avid codecs (dv, imx, meridien) and their variants
> */
> >   static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
> >   {
> > @@ -2382,6 +2400,8 @@ static int mov_write_video_tag(AVFormatContext *s,
> AVIOContext *pb, MOVMuxContex
> >           avid = 1;
> >       } else if (track->par->codec_id == AV_CODEC_ID_HEVC)
> >           mov_write_hvcc_tag(pb, track);
> > +    else if (track->par->codec_id == AV_CODEC_ID_VVC)
> > +        mov_write_vvcc_tag(pb, track);
> >       else if (track->par->codec_id == AV_CODEC_ID_H264 &&
> !TAG_IS_AVCI(track->tag)) {
> >           mov_write_avcc_tag(pb, track);
> >           if (track->mode == MODE_IPOD)
> > @@ -6170,6 +6190,7 @@ int ff_mov_write_packet(AVFormatContext *s,
> AVPacket *pkt)
> >       if ((par->codec_id == AV_CODEC_ID_DNXHD ||
> >            par->codec_id == AV_CODEC_ID_H264 ||
> >            par->codec_id == AV_CODEC_ID_HEVC ||
> > +         par->codec_id == AV_CODEC_ID_VVC ||
> >            par->codec_id == AV_CODEC_ID_VP9 ||
> >            par->codec_id == AV_CODEC_ID_EVC ||
> >            par->codec_id == AV_CODEC_ID_TRUEHD) && !trk->vos_len &&
> > @@ -6235,6 +6256,18 @@ int ff_mov_write_packet(AVFormatContext *s,
> AVPacket *pkt)
> >                   size = ff_hevc_annexb2mp4(pb, pkt->data, pkt->size, 0,
> NULL);
> >               }
> >           }
> > +    } else if (par->codec_id == AV_CODEC_ID_VVC && trk->vos_len > 6 &&
> > +             (AV_RB24(trk->vos_data) == 1 || AV_RB32(trk->vos_data) ==
> 1)) {
> > +      /* extradata is Annex B, assume the bitstream is too and convert
> it */
> > +      if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) {
> > +            ret = ff_h266_annexb2mp4_buf(pkt->data, &reformatted_data,
> > +                                         &size, 0, NULL);
> > +            if (ret < 0)
> > +                return ret;
> > +            avio_write(pb, reformatted_data, size);
> > +      } else {
> > +          size = ff_h266_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL);
> > +      }
> >       } else if (par->codec_id == AV_CODEC_ID_AV1) {
> >           if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams)
> {
> >               ret = ff_av1_filter_obus_buf(pkt->data, &reformatted_data,
> > @@ -6281,6 +6314,9 @@ int ff_mov_write_packet(AVFormatContext *s,
> AVPacket *pkt)
> >               } else if(par->codec_id == AV_CODEC_ID_HEVC &&
> par->extradata_size > 21) {
> >                   int nal_size_length = (par->extradata[21] & 0x3) + 1;
> >                   ret = ff_mov_cenc_avc_write_nal_units(s, &trk->cenc,
> nal_size_length, pb, pkt->data, size);
> > +            } else if(par->codec_id == AV_CODEC_ID_VVC &&
> par->extradata_size > 21) {
> > +                int nal_size_length = (par->extradata[21] & 0x3) + 1;
>
> Is this really at the exact same offset as in hevc?
>

Thank you for catching this, it should be
 int nal_size_length = ((par->extradata[4]>>1) & 0x3) + 1;


More information about the ffmpeg-devel mailing list