[FFmpeg-devel] [PATCH v3 05/10] avformat: add demuxer and probe support for H266/VVC

Thomas Siedel thomas.ff at spin-digital.com
Tue Nov 15 12:43:41 EET 2022


On Mon, 7 Nov 2022 at 03:28, mypopy at gmail.com <mypopy at gmail.com> wrote:

> On Thu, Nov 3, 2022 at 8:23 PM Thomas Siedel <thomas.ff at spin-digital.com>
> wrote:
> >
> > Add demuxer to probe raw vvc and parse vvcc byte stream format.
> >
> > Signed-off-by: Thomas Siedel <thomas.ff at spin-digital.com>
> > ---
> >  libavformat/Makefile     |   1 +
> >  libavformat/allformats.c |   1 +
> >  libavformat/demux.c      |   7 +-
> >  libavformat/vvc.c        | 919 +++++++++++++++++++++++++++++++++++++++
> >  libavformat/vvc.h        |  99 +++++
> >  libavformat/vvcdec.c     |  61 +++
> >  6 files changed, 1086 insertions(+), 2 deletions(-)
> >  create mode 100644 libavformat/vvc.c
> >  create mode 100644 libavformat/vvc.h
> >  create mode 100644 libavformat/vvcdec.c
> >
> > diff --git a/libavformat/Makefile b/libavformat/Makefile
> > index d7f198bf39..00ab4ded89 100644
> > --- a/libavformat/Makefile
> > +++ b/libavformat/Makefile
> > @@ -595,6 +595,7 @@ OBJS-$(CONFIG_VOC_MUXER)                 += vocenc.o
> voc.o
> >  OBJS-$(CONFIG_VPK_DEMUXER)               += vpk.o
> >  OBJS-$(CONFIG_VPLAYER_DEMUXER)           += vplayerdec.o subtitles.o
> >  OBJS-$(CONFIG_VQF_DEMUXER)               += vqf.o
> > +OBJS-$(CONFIG_VVC_DEMUXER)               += vvcdec.o rawdec.o
> >  OBJS-$(CONFIG_W64_DEMUXER)               += wavdec.o w64.o pcm.o
> >  OBJS-$(CONFIG_W64_MUXER)                 += wavenc.o w64.o
> >  OBJS-$(CONFIG_WAV_DEMUXER)               += wavdec.o pcm.o
> > diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> > index 47c419a009..a4e3822681 100644
> > --- a/libavformat/allformats.c
> > +++ b/libavformat/allformats.c
> > @@ -474,6 +474,7 @@ extern const AVOutputFormat ff_voc_muxer;
> >  extern const AVInputFormat  ff_vpk_demuxer;
> >  extern const AVInputFormat  ff_vplayer_demuxer;
> >  extern const AVInputFormat  ff_vqf_demuxer;
> > +extern const AVInputFormat  ff_vvc_demuxer;
> >  extern const AVInputFormat  ff_w64_demuxer;
> >  extern const AVOutputFormat ff_w64_muxer;
> >  extern const AVInputFormat  ff_wav_demuxer;
> > diff --git a/libavformat/demux.c b/libavformat/demux.c
> > index 2dfd82a63c..8dbde23fcd 100644
> > --- a/libavformat/demux.c
> > +++ b/libavformat/demux.c
> > @@ -120,6 +120,7 @@ static int set_codec_from_probe_data(AVFormatContext
> *s, AVStream *st,
> >          { "mp3",        AV_CODEC_ID_MP3,          AVMEDIA_TYPE_AUDIO
> },
> >          { "mpegvideo",  AV_CODEC_ID_MPEG2VIDEO,   AVMEDIA_TYPE_VIDEO
> },
> >          { "truehd",     AV_CODEC_ID_TRUEHD,       AVMEDIA_TYPE_AUDIO
> },
> > +        { "vvc",        AV_CODEC_ID_VVC,          AVMEDIA_TYPE_VIDEO
> },
> >          { 0 }
> >      };
> >      int score;
> > @@ -743,7 +744,8 @@ static int64_t select_from_pts_buffer(AVStream *st,
> int64_t *pts_buffer, int64_t
> >  {
> >      FFStream *const sti = ffstream(st);
> >      int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 &&
> > -                       st->codecpar->codec_id != AV_CODEC_ID_HEVC;
> > +                       st->codecpar->codec_id != AV_CODEC_ID_HEVC &&
> > +                       st->codecpar->codec_id != AV_CODEC_ID_VVC;
> >
> >      if (!onein_oneout) {
> >          int delay = sti->avctx->has_b_frames;
> > @@ -933,7 +935,8 @@ static void compute_pkt_fields(AVFormatContext *s,
> AVStream *st,
> >      int64_t offset;
> >      AVRational duration;
> >      int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 &&
> > -                       st->codecpar->codec_id != AV_CODEC_ID_HEVC;
> > +                       st->codecpar->codec_id != AV_CODEC_ID_HEVC &&
> > +                       st->codecpar->codec_id != AV_CODEC_ID_VVC;
> >
> >      if (s->flags & AVFMT_FLAG_NOFILLIN)
> >          return;
> > diff --git a/libavformat/vvc.c b/libavformat/vvc.c
> > new file mode 100644
> > index 0000000000..b27a522009
> > --- /dev/null
> > +++ b/libavformat/vvc.c
> > @@ -0,0 +1,919 @@
> > +/*
> > + * VVC helper functions for muxers
> > + *
> > + * Copyright (C) 2022, Thomas Siedel
> > + *
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> > + */
> > +
> > +#include "libavcodec/get_bits.h"
> > +#include "libavcodec/golomb.h"
> > +#include "libavcodec/vvc.h"
> > +#include "libavutil/intreadwrite.h"
> > +#include "avc.h"
> > +#include "avio.h"
> > +#include "avio_internal.h"
> > +#include "vvc.h"
> > +
> > +typedef struct VVCCNALUnitArray {
> > +    uint8_t  array_completeness;
> > +    uint8_t  NAL_unit_type;
> > +    uint16_t num_nalus;
> > +    uint16_t *nal_unit_length;
> > +    uint8_t  **nal_unit;
> > +} VVCCNALUnitArray;
> > +
> > +typedef struct VVCPTLRecord {
> > +    uint8_t  num_bytes_constraint_info;
> > +    uint8_t  general_profile_idc;
> > +    uint8_t  general_tier_flag;
> > +    uint8_t  general_level_idc;
> > +    uint8_t  ptl_frame_only_constraint_flag;
> > +    uint8_t  ptl_multilayer_enabled_flag;
> > +    uint8_t  general_constraint_info[9];
> > +    uint8_t  *ptl_sublayer_level_present_flag;
> > +    uint8_t  *sublayer_level_idc;
> > +    uint8_t  ptl_num_sub_profiles;
> > +    uint32_t *general_sub_profile_idc;
> > +} VVCPTLRecord;
> > +
> > +typedef struct VVCDecoderConfigurationRecord {
> > +    uint8_t  lengthSizeMinusOne;
> > +    uint8_t  ptl_present_flag;
> > +    uint16_t ols_idx;
> > +    uint8_t  num_sublayers;
> > +    uint8_t  constant_frame_rate;
> > +    uint8_t  chroma_format_idc;
> > +    uint8_t  bit_depth_minus8;
> > +    VVCPTLRecord ptl;
> > +    uint16_t max_picture_width;
> > +    uint16_t max_picture_height;
> > +    uint16_t avg_frame_rate;
> > +    uint8_t  num_of_arrays;
> > +    VVCCNALUnitArray *array;
> > +} VVCDecoderConfigurationRecord;
> > +
> > +typedef struct VVCCProfileTierLevel {
> > +    uint8_t profile_idc;
> > +    uint8_t tier_flag;
> > +    uint8_t general_level_idc;
> > +    uint8_t ptl_frame_only_constraint_flag;
> > +    uint8_t ptl_multilayer_enabled_flag;
> > +// general_constraint_info
> > +    uint8_t  gci_present_flag;
> > +    uint8_t  gci_general_constraints[9];
> > +    uint8_t  gci_num_reserved_bits;
> > +// end general_constraint_info
> > +    uint8_t  *ptl_sublayer_level_present_flag;
> > +    uint8_t  *sublayer_level_idc;
> > +    uint8_t  ptl_num_sub_profiles;
> > +    uint32_t *general_sub_profile_idc;
> > +} VVCCProfileTierLevel;
> > +
> > +
> > +static void vvcc_update_ptl(VVCDecoderConfigurationRecord *vvcc,
> > +                            VVCCProfileTierLevel *ptl)
> > +{
> > +    /*
> > +     * The level indication general_level_idc must indicate a level of
> > +     * capability equal to or greater than the highest level indicated
> for the
> > +     * highest tier in all the parameter sets.
> > +     */
> > +    if (vvcc->ptl.general_tier_flag < ptl->tier_flag)
> > +        vvcc->ptl.general_level_idc = ptl->general_level_idc;
> > +    else
> > +        vvcc->ptl.general_level_idc =
> FFMAX(vvcc->ptl.general_level_idc, ptl->general_level_idc);
> > +
> > +    /*
> > +     * The tier indication general_tier_flag must indicate a tier equal
> to or
> > +     * greater than the highest tier indicated in all the parameter
> sets.
> > +     */
> > +    vvcc->ptl.general_tier_flag = FFMAX(vvcc->ptl.general_tier_flag,
> ptl->tier_flag);
> > +
> > +    /*
> > +     * The profile indication general_profile_idc must indicate a
> profile to
> > +     * which the stream associated with this configuration record
> conforms.
> > +     *
> > +     * If the sequence parameter sets are marked with different
> profiles, then
> > +     * the stream may need examination to determine which profile, if
> any, the
> > +     * entire stream conforms to. If the entire stream is not examined,
> or the
> > +     * examination reveals that there is no profile to which the entire
> stream
> > +     * conforms, then the entire stream must be split into two or more
> > +     * sub-streams with separate configuration records in which these
> rules can
> > +     * be met.
> > +     *
> > +     * Note: set the profile to the highest value for the sake of
> simplicity.
> > +     */
> > +    vvcc->ptl.general_profile_idc =
> FFMAX(vvcc->ptl.general_profile_idc, ptl->profile_idc);
> > +
> > +    /*
> > +     * Each bit in flags may only be set if all
> > +     * the parameter sets set that bit.
> > +     */
> > +    vvcc->ptl.ptl_frame_only_constraint_flag &=
> ptl->ptl_frame_only_constraint_flag;
> > +    vvcc->ptl.ptl_multilayer_enabled_flag &=
> ptl->ptl_multilayer_enabled_flag;
> > +
> > +    /*
> > +     * Constraints Info
> > +     */
> > +    if(ptl->gci_present_flag) {
> > +        vvcc->ptl.num_bytes_constraint_info = 9;
> > +        memcpy(&vvcc->ptl.general_constraint_info[0],
> &ptl->gci_general_constraints[0], sizeof(uint8_t)*9);
> > +
> > +    } else {
> > +        vvcc->ptl.num_bytes_constraint_info = 1;
> > +        memset(&vvcc->ptl.general_constraint_info[0], 0,
> sizeof(uint8_t)*9);
> > +    }
> > +
> > +    /*
> > +     * Each bit in flags may only be set if one of
> > +     * the parameter sets set that bit.
> > +     */
> > +    vvcc->ptl.ptl_sublayer_level_present_flag = (uint8_t *)
> malloc(sizeof(uint8_t)*vvcc->num_sublayers-1);
> > +    vvcc->ptl.sublayer_level_idc              = (uint8_t *)
> malloc(sizeof(uint8_t)*vvcc->num_sublayers-1);
> > +
> > +    memset(vvcc->ptl.ptl_sublayer_level_present_flag, 0,
> sizeof(uint8_t)*vvcc->num_sublayers-1);
> > +    memset(vvcc->ptl.sublayer_level_idc, 0,
> sizeof(uint8_t)*vvcc->num_sublayers-1);
> > +
> > +    for(int i = vvcc->num_sublayers - 2; i >= 0; i--)
> > +    {
> > +        vvcc->ptl.ptl_sublayer_level_present_flag[i] |=
> ptl->ptl_sublayer_level_present_flag[i];
> > +        if(vvcc->ptl.ptl_sublayer_level_present_flag[i]){
> > +            vvcc->ptl.sublayer_level_idc[i] =
> FFMAX(vvcc->ptl.sublayer_level_idc[i], ptl->sublayer_level_idc[i]);
> > +        }
> > +        else{
> > +          if(i == vvcc->num_sublayers - 1){
> > +              vvcc->ptl.sublayer_level_idc[i] =
> vvcc->ptl.general_level_idc;
> > +          }else{
> > +              vvcc->ptl.sublayer_level_idc[i] =
> vvcc->ptl.sublayer_level_idc[i+1];
> > +          }
> > +        }
> > +    }
> > +
> > +    vvcc->ptl.ptl_num_sub_profiles =
> FFMAX(vvcc->ptl.ptl_num_sub_profiles, ptl->ptl_num_sub_profiles);
> > +    if( vvcc->ptl.ptl_num_sub_profiles ){
> > +        vvcc->ptl.general_sub_profile_idc = (uint32_t *)
> malloc(sizeof(uint32_t)*vvcc->ptl.ptl_num_sub_profiles);
> > +        for(int i = 0; i < vvcc->ptl.ptl_num_sub_profiles; i++) {
> > +            vvcc->ptl.general_sub_profile_idc[i] =
> ptl->general_sub_profile_idc[i];
> > +        }
> > +    }
> > +    else
> > +    {
> > +      vvcc->ptl.general_sub_profile_idc = (uint32_t *)
> malloc(sizeof(uint32_t));
> > +    }
> > +}
> > +
> > +static void vvcc_parse_ptl(GetBitContext *gb,
> > +                           VVCDecoderConfigurationRecord *vvcc,
> > +                           unsigned int profileTierPresentFlag,
> > +                           unsigned int max_sub_layers_minus1)
> > +{
> > +    VVCCProfileTierLevel general_ptl;
> > +    int j;
> > +
> > +    if(profileTierPresentFlag) {
> > +        general_ptl.profile_idc                 = get_bits(gb, 7);
> > +        general_ptl.tier_flag                   = get_bits1(gb);
> > +    }
> > +    general_ptl.general_level_idc                   = get_bits(gb, 8);
> > +
> > +    general_ptl.ptl_frame_only_constraint_flag = get_bits1(gb);
> > +    general_ptl.ptl_multilayer_enabled_flag = get_bits1(gb);
> > +    if(profileTierPresentFlag) {  // parse constraint info
> > +        general_ptl.gci_present_flag = get_bits1(gb);
> > +        if(general_ptl.gci_present_flag) {
> > +            for (j = 0; j < 8; j++)
> > +                general_ptl.gci_general_constraints[j] = get_bits(gb,
> 8);
> > +            general_ptl.gci_general_constraints[8] = 0;
> > +            general_ptl.gci_general_constraints[8] = get_bits(gb, 7);
> > +
> > +            general_ptl.gci_num_reserved_bits = get_bits(gb, 8);
> > +            skip_bits(gb, general_ptl.gci_num_reserved_bits);
> > +        }
> > +        while(gb->index % 8 != 0)
> > +            skip_bits1(gb);
> > +    }
> > +
> > +    general_ptl.ptl_sublayer_level_present_flag = (uint8_t *)
> malloc(sizeof(uint8_t)*max_sub_layers_minus1);
> > +    for(int i = max_sub_layers_minus1-1; i >= 0; i--) {
> > +        general_ptl.ptl_sublayer_level_present_flag[i] = get_bits1(gb);
> > +    }
> > +    while(gb->index%8 != 0)
> > +        skip_bits1(gb);
> > +
> > +    general_ptl.sublayer_level_idc = (uint8_t *)
> malloc(sizeof(uint8_t)*max_sub_layers_minus1);
> > +    for(int i = max_sub_layers_minus1-1; i >= 0; i--) {
> > +        if( general_ptl.ptl_sublayer_level_present_flag[i] )
> > +            general_ptl.sublayer_level_idc[i] = get_bits(gb, 8);
> > +    }
> > +
> > +    if(profileTierPresentFlag) {
> > +        general_ptl.ptl_num_sub_profiles = get_bits(gb, 8);
> > +        if( general_ptl.ptl_num_sub_profiles) {
> > +            general_ptl.general_sub_profile_idc = (uint32_t *)
> malloc(sizeof(uint32_t)*general_ptl.ptl_num_sub_profiles);
> > +            for(int i = 0; i < general_ptl.ptl_num_sub_profiles; i++) {
> > +                general_ptl.general_sub_profile_idc[i] =
> get_bits_long(gb, 32);
> > +            }
> > +        }
> > +        else
> > +        {
> > +            general_ptl.general_sub_profile_idc = (uint32_t *)
> malloc(sizeof(uint32_t));
> > +        }
> > +    }
> > +
> > +    vvcc_update_ptl(vvcc, &general_ptl);
> > +
> > +    free(general_ptl.ptl_sublayer_level_present_flag);
> > +    free(general_ptl.sublayer_level_idc);
> > +    free(general_ptl.general_sub_profile_idc);
> > +}
> > +
> > +static int vvcc_parse_vps(GetBitContext *gb,
> > +                          VVCDecoderConfigurationRecord *vvcc)
> > +{
> > +  unsigned int vps_max_layers_minus1;
> > +  unsigned int vps_max_sub_layers_minus1;
> > +  unsigned int vps_default_ptl_dpb_hrd_max_tid_flag;
> > +  unsigned int vps_all_independant_layer_flag;
> > +  unsigned int vps_each_layer_is_an_ols_flag;
> > +  unsigned int vps_ols_mode_idc;
> > +
>
> in FFmpeg, the indent size is 4
>
>
Thank you for pointing this out.
I now updated the code formatting and submitted a new version of the patch
series, so now it should be correct everywhere.


More information about the ffmpeg-devel mailing list