diff mbox series

[FFmpeg-devel,v6,6/9] avcodec: add vvc parser

Message ID 20210217015146.19724-7-nuomi2021@gmail.com
State Superseded
Headers show
Series add vvc raw demuxer, muxer, parser, metadata bsf
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Nuo Mi Feb. 17, 2021, 1:51 a.m. UTC
---
 configure               |   1 +
 libavcodec/Makefile     |   1 +
 libavcodec/parsers.c    |   1 +
 libavcodec/vvc_parser.c | 310 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 313 insertions(+)
 create mode 100644 libavcodec/vvc_parser.c

Comments

Mark Thompson Feb. 18, 2021, 12:10 a.m. UTC | #1
On 17/02/2021 01:51, Nuo Mi wrote:
> ---
>   configure               |   1 +
>   libavcodec/Makefile     |   1 +
>   libavcodec/parsers.c    |   1 +
>   libavcodec/vvc_parser.c | 310 ++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 313 insertions(+)
>   create mode 100644 libavcodec/vvc_parser.c
> 
> diff --git a/configure b/configure
> index 11df59a229..24463595d2 100755
> --- a/configure
> +++ b/configure
> @@ -3167,6 +3167,7 @@ mpegaudio_parser_select="mpegaudioheader"
>   mpegvideo_parser_select="mpegvideo"
>   mpeg4video_parser_select="h263dsp mpegvideo qpeldsp"
>   vc1_parser_select="vc1dsp"
> +vcc_parser_select="cbs_h266"

Typo - "vcc".  (There is no checking on names here, because they are just shell variables.  The only way to verify if particular dependencies are correct is to build that thing with everything else disabled.)

>   
>   # bitstream_filters
>   aac_adtstoasc_bsf_select="adts_header"
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 23553e68e9..5adb96c8df 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -1128,6 +1128,7 @@ OBJS-$(CONFIG_VC1_PARSER)              += vc1_parser.o vc1.o vc1data.o  \
>   OBJS-$(CONFIG_VP3_PARSER)              += vp3_parser.o
>   OBJS-$(CONFIG_VP8_PARSER)              += vp8_parser.o
>   OBJS-$(CONFIG_VP9_PARSER)              += vp9_parser.o
> +OBJS-$(CONFIG_VVC_PARSER)              += vvc_parser.o
>   OBJS-$(CONFIG_WEBP_PARSER)             += webp_parser.o
>   OBJS-$(CONFIG_XBM_PARSER)              += xbm_parser.o
>   OBJS-$(CONFIG_XMA_PARSER)              += xma_parser.o
> diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c
> index f8cfa1cde9..61d753d680 100644
> --- a/libavcodec/parsers.c
> +++ b/libavcodec/parsers.c
> @@ -72,6 +72,7 @@ extern AVCodecParser ff_vorbis_parser;
>   extern AVCodecParser ff_vp3_parser;
>   extern AVCodecParser ff_vp8_parser;
>   extern AVCodecParser ff_vp9_parser;
> +extern AVCodecParser ff_vvc_parser;
>   extern AVCodecParser ff_webp_parser;
>   extern AVCodecParser ff_xbm_parser;
>   extern AVCodecParser ff_xma_parser;
> diff --git a/libavcodec/vvc_parser.c b/libavcodec/vvc_parser.c
> new file mode 100644
> index 0000000000..4b4aa4b625
> --- /dev/null
> +++ b/libavcodec/vvc_parser.c
> @@ -0,0 +1,310 @@
> +/*
> + * VVC parser
> + *
> + * Copyright (C) 2029 Nuo Mi <nuomi2021@gmail.com>

Code from the future!

> + *
> ...

- Mark
Nuo Mi Feb. 18, 2021, 6:44 a.m. UTC | #2
On Thu, Feb 18, 2021 at 8:10 AM Mark Thompson <sw@jkqxz.net> wrote:

> On 17/02/2021 01:51, Nuo Mi wrote:
> > ---
> >   configure               |   1 +
> >   libavcodec/Makefile     |   1 +
> >   libavcodec/parsers.c    |   1 +
> >   libavcodec/vvc_parser.c | 310 ++++++++++++++++++++++++++++++++++++++++
> >   4 files changed, 313 insertions(+)
> >   create mode 100644 libavcodec/vvc_parser.c
> >
> > diff --git a/configure b/configure
> > index 11df59a229..24463595d2 100755
> > --- a/configure
> > +++ b/configure
> > @@ -3167,6 +3167,7 @@ mpegaudio_parser_select="mpegaudioheader"
> >   mpegvideo_parser_select="mpegvideo"
> >   mpeg4video_parser_select="h263dsp mpegvideo qpeldsp"
> >   vc1_parser_select="vc1dsp"
> > +vcc_parser_select="cbs_h266"
>
> Typo - "vcc".  (There is no checking on names here, because they are just
> shell variables.  The only way to verify if particular dependencies are
> correct is to build that thing with everything else disabled.)
>
Fixed.

>
> >
> >   # bitstream_filters
> >   aac_adtstoasc_bsf_select="adts_header"
> > diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> > index 23553e68e9..5adb96c8df 100644
> > --- a/libavcodec/Makefile
> > +++ b/libavcodec/Makefile
> > @@ -1128,6 +1128,7 @@ OBJS-$(CONFIG_VC1_PARSER)              +=
> vc1_parser.o vc1.o vc1data.o  \
> >   OBJS-$(CONFIG_VP3_PARSER)              += vp3_parser.o
> >   OBJS-$(CONFIG_VP8_PARSER)              += vp8_parser.o
> >   OBJS-$(CONFIG_VP9_PARSER)              += vp9_parser.o
> > +OBJS-$(CONFIG_VVC_PARSER)              += vvc_parser.o
> >   OBJS-$(CONFIG_WEBP_PARSER)             += webp_parser.o
> >   OBJS-$(CONFIG_XBM_PARSER)              += xbm_parser.o
> >   OBJS-$(CONFIG_XMA_PARSER)              += xma_parser.o
> > diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c
> > index f8cfa1cde9..61d753d680 100644
> > --- a/libavcodec/parsers.c
> > +++ b/libavcodec/parsers.c
> > @@ -72,6 +72,7 @@ extern AVCodecParser ff_vorbis_parser;
> >   extern AVCodecParser ff_vp3_parser;
> >   extern AVCodecParser ff_vp8_parser;
> >   extern AVCodecParser ff_vp9_parser;
> > +extern AVCodecParser ff_vvc_parser;
> >   extern AVCodecParser ff_webp_parser;
> >   extern AVCodecParser ff_xbm_parser;
> >   extern AVCodecParser ff_xma_parser;
> > diff --git a/libavcodec/vvc_parser.c b/libavcodec/vvc_parser.c
> > new file mode 100644
> > index 0000000000..4b4aa4b625
> > --- /dev/null
> > +++ b/libavcodec/vvc_parser.c
> > @@ -0,0 +1,310 @@
> > +/*
> > + * VVC parser
> > + *
> > + * Copyright (C) 2029 Nuo Mi <nuomi2021@gmail.com>
>
> Code from the future!
>
fixed :)

>
> > + *
> > ...
>
> - Mark
> _______________________________________________
> 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".
diff mbox series

Patch

diff --git a/configure b/configure
index 11df59a229..24463595d2 100755
--- a/configure
+++ b/configure
@@ -3167,6 +3167,7 @@  mpegaudio_parser_select="mpegaudioheader"
 mpegvideo_parser_select="mpegvideo"
 mpeg4video_parser_select="h263dsp mpegvideo qpeldsp"
 vc1_parser_select="vc1dsp"
+vcc_parser_select="cbs_h266"
 
 # bitstream_filters
 aac_adtstoasc_bsf_select="adts_header"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 23553e68e9..5adb96c8df 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1128,6 +1128,7 @@  OBJS-$(CONFIG_VC1_PARSER)              += vc1_parser.o vc1.o vc1data.o  \
 OBJS-$(CONFIG_VP3_PARSER)              += vp3_parser.o
 OBJS-$(CONFIG_VP8_PARSER)              += vp8_parser.o
 OBJS-$(CONFIG_VP9_PARSER)              += vp9_parser.o
+OBJS-$(CONFIG_VVC_PARSER)              += vvc_parser.o
 OBJS-$(CONFIG_WEBP_PARSER)             += webp_parser.o
 OBJS-$(CONFIG_XBM_PARSER)              += xbm_parser.o
 OBJS-$(CONFIG_XMA_PARSER)              += xma_parser.o
diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c
index f8cfa1cde9..61d753d680 100644
--- a/libavcodec/parsers.c
+++ b/libavcodec/parsers.c
@@ -72,6 +72,7 @@  extern AVCodecParser ff_vorbis_parser;
 extern AVCodecParser ff_vp3_parser;
 extern AVCodecParser ff_vp8_parser;
 extern AVCodecParser ff_vp9_parser;
+extern AVCodecParser ff_vvc_parser;
 extern AVCodecParser ff_webp_parser;
 extern AVCodecParser ff_xbm_parser;
 extern AVCodecParser ff_xma_parser;
diff --git a/libavcodec/vvc_parser.c b/libavcodec/vvc_parser.c
new file mode 100644
index 0000000000..4b4aa4b625
--- /dev/null
+++ b/libavcodec/vvc_parser.c
@@ -0,0 +1,310 @@ 
+/*
+ * VVC parser
+ *
+ * Copyright (C) 2029 Nuo Mi <nuomi2021@gmail.com>
+ *
+ * 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 "cbs.h"
+#include "cbs_h266.h"
+#include "internal.h"
+#include "parser.h"
+
+#define START_CODE 0x000001 ///< start_code_prefix_one_3bytes
+
+#define IS_SLICE(nut) (nut <= VVC_RASL_NUT || (nut >= VVC_IDR_W_RADL && nut <= VVC_GDR_NUT))
+
+typedef struct VVCParserContext {
+    ParseContext pc;
+    CodedBitstreamContext *cbc;
+    CodedBitstreamFragment picture_unit;
+    int parsed_extradata;
+} VVCParserContext;
+
+static const enum AVPixelFormat pix_fmts_8bit[] = {
+    AV_PIX_FMT_GRAY8, AV_PIX_FMT_YUV420P,
+    AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P
+};
+
+static const enum AVPixelFormat pix_fmts_10bit[] = {
+    AV_PIX_FMT_GRAY10, AV_PIX_FMT_YUV420P10,
+    AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10
+};
+
+static int get_format(const H266RawSPS* sps)
+{
+    switch (sps->sps_bitdepth_minus8) {
+        case 0:
+            return pix_fmts_8bit[sps->sps_chroma_format_idc];
+        case 2:
+            return pix_fmts_10bit[sps->sps_chroma_format_idc];
+    }
+    return AV_PIX_FMT_NONE;
+}
+
+/**
+ * Find the end of the current frame in the bitstream.
+ * @return the position of the first byte of the next frame, or END_NOT_FOUND
+ */
+static int find_frame_end(AVCodecParserContext *s, const uint8_t *buf,
+                               int buf_size)
+{
+    VVCParserContext *ctx = s->priv_data;
+    ParseContext       *pc = &ctx->pc;
+    int i;
+
+    for (i = 0; i < buf_size; i++) {
+        int nut;
+
+        pc->state64 = (pc->state64 << 8) | buf[i];
+
+        if (((pc->state64 >> 3 * 8) & 0xFFFFFF) != START_CODE)
+            continue;
+
+        nut = (pc->state64 >> (8 + 3)) & 0x1F;
+        // 7.4.2.4.3 and 7.4.2.4.4
+        if ((nut >= VVC_OPI_NUT && nut <= VVC_PREFIX_APS_NUT && nut != VVC_PH_NUT) ||
+            nut == VVC_AUD_NUT || nut == VVC_PREFIX_SEI_NUT || nut == VVC_RSV_NVCL_26 ||
+            nut == VVC_UNSPEC_28 || nut == VVC_UNSPEC_29) {
+            if (pc->frame_start_found) {
+                pc->frame_start_found = 0;
+                return i - 5;
+            }
+        } else if (nut == VVC_PH_NUT  || IS_SLICE(nut)) {
+            int sh_picture_header_in_slice_header_flag = buf[i] >> 7;
+
+            if (nut == VVC_PH_NUT || sh_picture_header_in_slice_header_flag) {
+                if (!pc->frame_start_found) {
+                    pc->frame_start_found = 1;
+                } else { // First slice of next frame found
+                    pc->frame_start_found = 0;
+                    return i - 5;
+                }
+            }
+        }
+    }
+    return END_NOT_FOUND;
+}
+
+static int get_pict_type(const CodedBitstreamFragment *pu)
+{
+    int has_p = 0;
+    for (int i = 0; i < pu->nb_units; i++) {
+        CodedBitstreamUnit *unit = &pu->units[i];
+        if (IS_SLICE(unit->type)) {
+            const H266RawSlice *slice = unit->content;
+            uint8_t type = slice->header.sh_slice_type;
+            if (type == VVC_SLICE_TYPE_B) {
+                return AV_PICTURE_TYPE_B;
+            }
+            if (type == VVC_SLICE_TYPE_P) {
+                has_p = 1;
+            }
+        }
+    }
+    return has_p ? AV_PICTURE_TYPE_P : AV_PICTURE_TYPE_I;
+}
+
+/**
+ * Parse NAL units of found picture and decode some basic information.
+ *
+ * @param s parser context.
+ * @param avctx codec context.
+ * @param buf buffer with field/frame data.
+ * @param buf_size size of the buffer.
+ */
+static int parse_nal_units(AVCodecParserContext *ctx, const uint8_t *buf,
+                           int buf_size, AVCodecContext *avctx)
+{
+    VVCParserContext *s = ctx->priv_data;
+    CodedBitstreamFragment *pu = &s->picture_unit;
+    CodedBitstreamH266Context *h266 = s->cbc->priv_data;
+    const H266RawSPS *sps;
+    const H266RawPPS *pps;
+    const H266RawPH *ph;
+    int ret, num = 0, den = 0;
+    static const uint8_t h266_sub_width_c[] = {
+        1, 2, 2, 1
+    };
+    static const uint8_t h266_sub_height_c[] = {
+        1, 2, 1, 1
+    };
+
+    /* set some sane default values */
+    ctx->pict_type         = AV_PICTURE_TYPE_I;
+    ctx->key_frame         = 0;
+    ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
+
+    s->cbc->log_ctx = avctx;
+
+    if (avctx->extradata_size && !s->parsed_extradata) {
+        s->parsed_extradata = 1;
+
+        if ((ret = ff_cbs_read(s->cbc, pu, avctx->extradata, avctx->extradata_size)) < 0)
+            av_log(avctx, AV_LOG_WARNING, "Failed to parse extradata.\n");
+
+        ff_cbs_fragment_reset(pu);
+    }
+
+    if ((ret = ff_cbs_read(s->cbc, pu, buf, buf_size))< 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to parse picture unit.\n");
+        goto end;
+    }
+
+    ph = h266->ph;
+    if (!ph) {
+        av_log(avctx, AV_LOG_ERROR, "No picture header.\n");
+        ret = AVERROR_INVALIDDATA;
+        goto end;
+    }
+    pps = h266->pps[ph->ph_pic_parameter_set_id];
+    if (!pps) {
+        av_log(avctx, AV_LOG_ERROR, "PPS id %d not available.\n",
+               ph->ph_pic_parameter_set_id);
+        ret = AVERROR_INVALIDDATA;
+        goto end;
+    }
+    sps = h266->sps[pps->pps_seq_parameter_set_id];
+    if (!sps) {
+        av_log(avctx, AV_LOG_ERROR, "PPS id %d not available.\n",
+               pps->pps_seq_parameter_set_id);
+        ret = AVERROR_INVALIDDATA;
+        goto end;
+    }
+
+    ctx->key_frame    = ph->ph_gdr_or_irap_pic_flag;
+    ctx->coded_width  = pps->pps_pic_width_in_luma_samples;
+    ctx->coded_height = pps->pps_pic_height_in_luma_samples;
+    ctx->width        = pps->pps_pic_width_in_luma_samples  -
+        (pps->pps_conf_win_left_offset + pps->pps_conf_win_right_offset) *
+        h266_sub_width_c[sps->sps_chroma_format_idc];
+    ctx->height       = pps->pps_pic_height_in_luma_samples -
+        (pps->pps_conf_win_top_offset + pps->pps_conf_win_bottom_offset) *
+        h266_sub_height_c[sps->sps_chroma_format_idc];;
+    ctx->pict_type    = get_pict_type(pu);
+    ctx->format       = get_format(sps);
+
+    avctx->profile  = sps->profile_tier_level.general_profile_idc;
+    avctx->level    = sps->profile_tier_level.general_level_idc;
+
+    if (ctx->width != avctx->width || ctx->height != avctx->height) {
+        ret = ff_set_dimensions(avctx, ctx->width, ctx->height);
+        if (ret < 0)
+            goto end;
+    }
+    avctx->pix_fmt = ctx->format;
+
+    if(sps->sps_ptl_dpb_hrd_params_present_flag && sps->sps_timing_hrd_params_present_flag) {
+        num = sps->sps_general_timing_hrd_parameters.num_units_in_tick;
+        den = sps->sps_general_timing_hrd_parameters.time_scale;
+    } else {
+        goto end;
+    }
+    if (num != 0 && den != 0)
+        av_reduce(&avctx->framerate.den, &avctx->framerate.num,
+                  num, den, 1 << 30);
+end:
+    ff_cbs_fragment_reset(pu);
+    s->cbc->log_ctx = NULL;
+    return ret;
+}
+
+static int vvc_parser_parse(AVCodecParserContext *s, AVCodecContext *avctx,
+                      const uint8_t **poutbuf, int *poutbuf_size,
+                      const uint8_t *buf, int buf_size)
+{
+    int next;
+    VVCParserContext *ctx = s->priv_data;
+    ParseContext *pc = &ctx->pc;
+    int is_dummy_buf = !buf_size;
+    const uint8_t *dummy_buf = buf;
+
+    if (avctx->extradata && !ctx->parsed_extradata) {
+        av_log(avctx, AV_LOG_INFO, "extra data is not supported yet.\n");
+        return AVERROR_PATCHWELCOME;
+    }
+
+    if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) {
+        next = buf_size;
+    } else {
+        next = find_frame_end(s, buf, buf_size);
+        if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) {
+            *poutbuf      = NULL;
+            *poutbuf_size = 0;
+            return buf_size;
+        }
+    }
+
+    is_dummy_buf &= (dummy_buf == buf);
+
+    if (!is_dummy_buf)
+        parse_nal_units(s, buf, buf_size, avctx);
+
+    *poutbuf      = buf;
+    *poutbuf_size = buf_size;
+    return next;
+
+}
+
+static const CodedBitstreamUnitType decompose_unit_types[] = {
+    VVC_TRAIL_NUT,
+    VVC_STSA_NUT,
+    VVC_RADL_NUT,
+    VVC_RASL_NUT,
+    VVC_IDR_W_RADL,
+    VVC_IDR_N_LP,
+    VVC_CRA_NUT,
+    VVC_GDR_NUT,
+    VVC_VPS_NUT,
+    VVC_SPS_NUT,
+    VVC_PPS_NUT,
+    VVC_PH_NUT,
+    VVC_AUD_NUT,
+};
+
+static av_cold int vvc_parser_init(AVCodecParserContext *ctx)
+{
+    VVCParserContext *s = ctx->priv_data;
+    int ret;
+
+    ret = ff_cbs_init(&s->cbc, AV_CODEC_ID_VVC, NULL);
+    if (ret < 0)
+        return ret;
+
+    s->cbc->decompose_unit_types    = decompose_unit_types;
+    s->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types);
+
+    return 0;
+}
+
+static void vvc_parser_close(AVCodecParserContext *ctx)
+{
+    VVCParserContext *s = ctx->priv_data;
+
+    ff_cbs_fragment_free(&s->picture_unit);
+    ff_cbs_close(&s->cbc);
+    av_freep(&s->pc.buffer);
+}
+
+AVCodecParser ff_vvc_parser = {
+    .codec_ids      = { AV_CODEC_ID_VVC },
+    .priv_data_size = sizeof(VVCParserContext),
+    .parser_init    = vvc_parser_init,
+    .parser_close   = vvc_parser_close,
+    .parser_parse   = vvc_parser_parse,
+};