diff --git a/configure b/configure
index 46d7a5cf0e..770cbb2f8e 100755
--- a/configure
+++ b/configure
@@ -3013,6 +3013,7 @@ vp6f_decoder_select="vp6_decoder"
 vp7_decoder_select="h264pred videodsp vp8dsp"
 vp8_decoder_select="h264pred videodsp vp8dsp"
 vp9_decoder_select="videodsp vp9_parser vp9_superframe_split_bsf"
+vvc_decoder_select="cabac golomb videodsp"
 wcmv_decoder_select="inflate_wrapper"
 webp_decoder_select="vp8_decoder exif"
 wmalossless_decoder_select="llauddsp"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 57d57f3ab5..aba849f408 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -64,6 +64,7 @@ OBJS = ac3_parser.o                                                     \
        xiph.o                                                           \
 
 # subsystems
+include $(SRC_PATH)/libavcodec/vvc/Makefile
 OBJS-$(CONFIG_AANDCTTABLES)            += aandcttab.o
 OBJS-$(CONFIG_AC3DSP)                  += ac3dsp.o ac3.o ac3tab.o
 OBJS-$(CONFIG_ADTS_HEADER)             += adts_header.o mpeg4audio_sample_rates.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 2662adb754..6c5aa6b44b 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -389,6 +389,7 @@ extern const FFCodec ff_vp9_rkmpp_decoder;
 extern const FFCodec ff_vp9_v4l2m2m_decoder;
 extern const FFCodec ff_vqa_decoder;
 extern const FFCodec ff_vqc_decoder;
+extern const FFCodec ff_vvc_decoder;
 extern const FFCodec ff_wbmp_decoder;
 extern const FFCodec ff_wbmp_encoder;
 extern const FFCodec ff_webp_decoder;
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
new file mode 100644
index 0000000000..bd14dbc1df
--- /dev/null
+++ b/libavcodec/vvc/Makefile
@@ -0,0 +1,4 @@
+clean::
+	$(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%)
+
+OBJS-$(CONFIG_VVC_DECODER)          +=  vvc/vvcdec.o            \
diff --git a/libavcodec/vvc/vvcdec.c b/libavcodec/vvc/vvcdec.c
new file mode 100644
index 0000000000..3c591ce875
--- /dev/null
+++ b/libavcodec/vvc/vvcdec.c
@@ -0,0 +1,62 @@
+/*
+ * VVC video decoder
+ *
+ * Copyright (C) 2021 Nuo Mi
+ * Copyright (C) 2022 Xu Mu
+ *
+ * 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/codec_internal.h"
+#include "libavcodec/profiles.h"
+
+#include "vvcdec.h"
+
+static int vvc_decode_frame(AVCodecContext *avctx, AVFrame *output,
+    int *got_output, AVPacket *avpkt)
+{
+    return avpkt->size;
+}
+
+static void vvc_decode_flush(AVCodecContext *avctx)
+{
+}
+
+static av_cold int vvc_decode_free(AVCodecContext *avctx)
+{
+    return 0;
+}
+
+static av_cold int vvc_decode_init(AVCodecContext *avctx)
+{
+    return 0;
+}
+
+const FFCodec ff_vvc_decoder = {
+    .p.name                  = "vvc",
+    .p.long_name             = NULL_IF_CONFIG_SMALL("VVC (Versatile Video Coding)"),
+    .p.type                  = AVMEDIA_TYPE_VIDEO,
+    .p.id                    = AV_CODEC_ID_VVC,
+    .priv_data_size          = sizeof(VVCContext),
+    .init                    = vvc_decode_init,
+    .close                   = vvc_decode_free,
+    FF_CODEC_DECODE_CB(vvc_decode_frame),
+    .flush                   = vvc_decode_flush,
+    .p.capabilities          = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS,
+    .caps_internal           = FF_CODEC_CAP_EXPORTS_CROPPING | FF_CODEC_CAP_INIT_CLEANUP |
+                               FF_CODEC_CAP_AUTO_THREADS,
+    .p.profiles              = NULL_IF_CONFIG_SMALL(ff_vvc_profiles),
+};
diff --git a/libavcodec/vvc/vvcdec.h b/libavcodec/vvc/vvcdec.h
new file mode 100644
index 0000000000..2823faee75
--- /dev/null
+++ b/libavcodec/vvc/vvcdec.h
@@ -0,0 +1,204 @@
+/*
+ * VVC video decoder
+ *
+ * Copyright (C) 2021 Nuo Mi
+ * Copyright (C) 2022 Xu Mu
+ *
+ * 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
+ */
+
+#ifndef AVCODEC_VVCDEC_H
+#define AVCODEC_VVCDEC_H
+
+#include "libavcodec/vvc.h"
+
+#define LUMA                    0
+#define CHROMA                  1
+#define CB                      1
+#define CR                      2
+#define JCBCR                   3
+
+#define MIN_TU_LOG2             2                       ///< MinTbLog2SizeY
+#define MIN_PU_LOG2             2
+
+#define L0                      0
+#define L1                      1
+
+typedef struct RefPicList {
+    struct VVCFrame *ref[VVC_MAX_REF_ENTRIES];
+    int list[VVC_MAX_REF_ENTRIES];
+    int isLongTerm[VVC_MAX_REF_ENTRIES];
+    int nb_refs;
+} RefPicList;
+
+typedef struct RefPicListTab {
+    RefPicList refPicList[2];
+} RefPicListTab;
+
+typedef struct VVCFrame {
+    struct AVFrame *frame;
+
+    struct MvField *tab_dmvr_mvf;               ///< RefStruct reference
+    RefPicListTab **rpl_tab;                    ///< RefStruct reference
+    RefPicListTab  *rpl;                        ///< RefStruct reference
+    int nb_rpl_elems;
+
+    int ctb_count;
+
+    int poc;
+
+    struct VVCFrame *collocated_ref;
+
+    struct FrameProgress *progress;             ///< RefStruct reference
+
+    /**
+     * A sequence counter, so that old frames are output first
+     * after a POC reset
+     */
+    uint16_t sequence;
+    /**
+     * A combination of VVC_FRAME_FLAG_*
+     */
+    uint8_t flags;
+} VVCFrame;
+
+typedef struct SliceContext {
+    int slice_idx;
+    struct EntryPoint *eps;
+    int nb_eps;
+    RefPicList *rpl;
+} SliceContext;
+
+typedef struct VVCFrameContext {
+    struct AVCodecContext *avctx;
+
+    // +1 for the current frame
+    VVCFrame DPB[VVC_MAX_DPB_SIZE + 1];
+
+    struct AVFrame *frame;
+    struct AVFrame *output_frame;
+
+    SliceContext  **slices;
+    int nb_slices;
+    int nb_slices_allocated;
+
+    VVCFrame *ref;
+
+    struct VVCFrameThread *ft;
+
+    uint64_t decode_order;
+
+    struct FFRefStructPool *tab_dmvr_mvf_pool;
+    struct FFRefStructPool *rpl_tab_pool;
+
+    struct FFRefStructPool *cu_pool;
+    struct FFRefStructPool *tu_pool;
+
+    struct {
+        int16_t *slice_idx;
+
+        int     *cb_pos_x[2];                           ///< CbPosX[][][]
+        int     *cb_pos_y[2];                           ///< CbPosY[][][]
+        uint8_t *cb_width[2];                           ///< CbWidth[][][]
+        uint8_t *cb_height[2];                          ///< CbHeight[][][]
+        uint8_t *cqt_depth[2];                          ///< CqtDepth[][][]
+        int8_t  *qp[VVC_MAX_SAMPLE_ARRAYS];
+
+        uint8_t *skip;                                  ///< CuSkipFlag[][]
+        uint8_t *ispmf;                                 ///< intra_sub_partitions_mode_flag
+        uint8_t *msm[2];                                ///< MttSplitMode[][][] in 32 pixels
+        uint8_t *imf;                                   ///< IntraMipFlag[][]
+        uint8_t *imtf;                                  ///< intra_mip_transposed_flag[][]
+        uint8_t *imm;                                   ///< intra_mip_mode[][]
+        uint8_t *ipm;                                   ///< IntraPredModeY[][]
+        uint8_t *cpm[2];                                ///< CuPredMode[][][]
+        uint8_t *msf;                                   ///< MergeSubblockFlag[][]
+        uint8_t *iaf;                                   ///< InterAffineFlag[][]
+        uint8_t *mmi;                                   ///< MotionModelIdc[][]
+        struct Mv      *cp_mv[2];                       ///< CpMvLX[][][][MAX_CONTROL_POINTS];
+        struct MvField *mvf;                            ///< MvDmvrL0, MvDmvrL1
+
+        uint8_t *tu_coded_flag[VVC_MAX_SAMPLE_ARRAYS];  ///< tu_y_coded_flag[][],  tu_cb_coded_flag[][],  tu_cr_coded_flag[][]
+        uint8_t *tu_joint_cbcr_residual_flag;           ///< tu_joint_cbcr_residual_flag[][]
+        int     *tb_pos_x0[2];
+        int     *tb_pos_y0[2];
+        uint8_t *tb_width[2];
+        uint8_t *tb_height[2];
+        uint8_t *pcmf[2];
+
+        uint8_t *horizontal_bs[VVC_MAX_SAMPLE_ARRAYS];
+        uint8_t *vertical_bs[VVC_MAX_SAMPLE_ARRAYS];
+        uint8_t *horizontal_p;                          ///< horizontal maxFilterLengthPs for luma
+        uint8_t *horizontal_q;                          ///< horizontal maxFilterLengthQs for luma
+        uint8_t *vertical_p;                            ///< vertical   maxFilterLengthPs for luma
+        uint8_t *vertical_q;                            ///< vertical   maxFilterLengthQs for luma
+
+        uint8_t *sao_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS];
+        uint8_t *sao_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS];
+        uint8_t *alf_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS][2];
+        uint8_t *alf_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS][2];
+
+        int         *coeffs;
+        struct CTU  *ctus;
+
+        //used in arrays_init only
+        int ctu_count;
+        int ctu_size;
+        int pic_size_in_min_cb;
+        int pic_size_in_min_pu;
+        int pic_size_in_min_tu;
+        int ctu_width;
+        int ctu_height;
+        int width;
+        int height;
+        int chroma_format_idc;
+        int pixel_shift;
+        int bs_width;
+        int bs_height;
+    } tab;
+} VVCFrameContext;
+
+typedef struct VVCContext {
+    struct AVCodecContext *avctx;
+
+    int temporal_id;        ///< temporal_id_plus1 - 1
+    int poc_tid0;
+
+    int eos;                ///< current packet contains an EOS/EOB NAL
+    int last_eos;           ///< last packet contains an EOS/EOB NAL
+
+    enum VVCNALUnitType vcl_unit_type;
+    int no_output_before_recovery_flag; ///< NoOutputBeforeRecoveryFlag
+    int gdr_recovery_point_poc;         ///< recoveryPointPocVal
+
+    /**
+     * Sequence counters for decoded and output frames, so that old
+     * frames are output first after a POC reset
+     */
+    uint16_t seq_decode;
+    uint16_t seq_output;
+
+    struct AVExecutor *executor;
+
+    VVCFrameContext *fcs;
+    int nb_fcs;
+
+    uint64_t nb_frames;     ///< processed frames
+    int nb_delayed;         ///< delayed frames
+}  VVCContext ;
+
+#endif /* AVCODEC_VVCDEC_H */
