diff --git a/configure b/configure
index 77272948e3..1e9a44db31 100755
--- a/configure
+++ b/configure
@@ -285,6 +285,7 @@ External library support:
   --enable-libvorbis       enable Vorbis en/decoding via libvorbis,
                            native implementation exists [no]
   --enable-libvpx          enable VP8 and VP9 de/encoding via libvpx [no]
+  --enable-libvvdec        enable VVC video encoding via libvvdec [no]
   --enable-libwebp         enable WebP encoding via libwebp [no]
   --enable-libx264         enable H.264 encoding via x264 [no]
   --enable-libx265         enable HEVC encoding via x265 [no]
@@ -1816,6 +1817,7 @@ EXTERNAL_LIBRARY_LIST="
     libvmaf
     libvorbis
     libvpx
+    libvvdec
     libwebp
     libxml2
     libzimg
@@ -3281,6 +3283,7 @@ libvpx_vp8_decoder_deps="libvpx"
 libvpx_vp8_encoder_deps="libvpx"
 libvpx_vp9_decoder_deps="libvpx"
 libvpx_vp9_encoder_deps="libvpx"
+libvvdec_decoder_deps="libvvdec"
 libwebp_encoder_deps="libwebp"
 libwebp_anim_encoder_deps="libwebp"
 libx262_encoder_deps="libx262"
@@ -6455,7 +6458,7 @@ enabled libvpx            && {
         die "libvpx enabled but no supported decoders found"
     fi
 }
-
+enabled libvvdec          && require_pkg_config libvvdec "libvvdec >= 0.1.2" vvdec/version.h VVDEC_VERSION_MAJOR
 enabled libwebp           && {
     enabled libwebp_encoder      && require_pkg_config libwebp "libwebp >= 0.2.0" webp/encode.h WebPGetEncoderVersion
     enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit; }
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 82cc9b8b93..ebd3781982 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1059,6 +1059,7 @@ OBJS-$(CONFIG_LIBVPX_VP8_DECODER)         += libvpxdec.o
 OBJS-$(CONFIG_LIBVPX_VP8_ENCODER)         += libvpxenc.o
 OBJS-$(CONFIG_LIBVPX_VP9_DECODER)         += libvpxdec.o libvpx.o
 OBJS-$(CONFIG_LIBVPX_VP9_ENCODER)         += libvpxenc.o libvpx.o
+OBJS-$(CONFIG_LIBVVDEC_DECODER)           += libvvdec.o
 OBJS-$(CONFIG_LIBWEBP_ENCODER)            += libwebpenc_common.o libwebpenc.o
 OBJS-$(CONFIG_LIBWEBP_ANIM_ENCODER)       += libwebpenc_common.o libwebpenc_animencoder.o
 OBJS-$(CONFIG_LIBX262_ENCODER)            += libx264.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index f00d524747..545c38f541 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -752,6 +752,7 @@ extern AVCodec ff_libvpx_vp8_encoder;
 extern AVCodec ff_libvpx_vp8_decoder;
 extern AVCodec ff_libvpx_vp9_encoder;
 extern AVCodec ff_libvpx_vp9_decoder;
+extern AVCodec ff_libvvdec_decoder;
 /* preferred over libwebp */
 extern AVCodec ff_libwebp_anim_encoder;
 extern AVCodec ff_libwebp_encoder;
diff --git a/libavcodec/libvvdec.cpp b/libavcodec/libvvdec.cpp
new file mode 100644
index 0000000000..73fe13f61e
--- /dev/null
+++ b/libavcodec/libvvdec.cpp
@@ -0,0 +1,244 @@
+/*
+ * vvdec H.266/VVC decoder
+ * Copyright (c) 2020 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
+ */
+
+/**
+ * @file
+ * AV1 decoder support via libaom
+ */
+
+#include "vvdec/vvdec.h"
+
+extern "C" {
+
+#include "libavutil/common.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/internal.h"
+
+#include "avcodec.h"
+#include "h2645_parse.h"
+#include "internal.h"
+#include "profiles.h"
+
+typedef struct VVdecContext {
+    AVCodecContext *avctx;
+    vvdec::VVDec* vvdec;
+
+    H2645Packet pkt;
+    int is_nalff;           ///< this flag is != 0 if bitstream is encapsulated
+                            ///< as a format defined in 14496-15
+    int nal_length_size;    ///< Number of bytes used for nal length (1, 2 or 4)
+} VVdecContext;
+
+}
+
+static int map_error(int vret)
+{
+    switch(vret)
+    {
+        case vvdec::VVDEC_OK :                  return 0;
+        case vvdec::VVDEC_ERR_UNSPECIFIED:      return AVERROR_UNKNOWN;
+        case vvdec::VVDEC_ERR_INITIALIZE:       return AVERROR_BUG;
+        case vvdec::VVDEC_ERR_ALLOCATE:         return AVERROR(ENOMEM);
+        case vvdec::VVDEC_NOT_ENOUGH_MEM:       return AVERROR(ENOMEM);
+        case vvdec::VVDEC_ERR_PARAMETER:        return AVERROR(EINVAL);
+        case vvdec::VVDEC_ERR_NOT_SUPPORTED:    return AVERROR(ENOSYS);
+        case vvdec::VVDEC_ERR_RESTART_REQUIRED: return AVERROR_BUG;
+        case vvdec::VVDEC_ERR_CPU:              return AVERROR(ENOSYS);
+        case vvdec::VVDEC_TRY_AGAIN:            return AVERROR(EAGAIN);
+        case vvdec::VVDEC_EOF:                  return AVERROR(EOF);
+        default:                                return AVERROR_UNKNOWN;
+    }
+}
+
+static int check_vret(VVdecContext* s, int vret) {
+    vvdec::VVDec* vvdec = s->vvdec;
+    if (vret && vret != vvdec::VVDEC_EOF) {
+        av_log(s->avctx, AV_LOG_ERROR, "vvdec returns error: %s\n", vvdec->getErrorMsg(vret));
+    }
+    return map_error(vret);
+}
+
+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 AVPixelFormat get_format(const vvdec::Frame* frame)
+{
+    if (frame->m_eColorFormat != vvdec::VVC_CF_INVALID) {
+        switch (frame->m_uiBitDepth) {
+            case 8:
+                return pix_fmts_8bit[frame->m_eColorFormat];
+            case 10:
+                return pix_fmts_10bit[frame->m_eColorFormat];
+        }
+    }
+    return AV_PIX_FMT_NONE;
+}
+
+static int set_pix_fmt(VVdecContext* s, const vvdec::Frame* frame)
+{
+    AVPixelFormat format = get_format(frame);
+    if (format == AV_PIX_FMT_NONE) {
+        av_log(s->avctx, AV_LOG_ERROR,
+               "unsupported, depth = %d, color format = %d.\n", frame->m_uiBitDepth, frame->m_eColorFormat);
+        return AVERROR_INVALIDDATA;
+    }
+    s->avctx->pix_fmt = format;
+    return 0;
+}
+
+static int copy_to_avframe(VVdecContext* s, const vvdec::Frame* src, AVFrame *dest)
+{
+    AVCodecContext* avctx = s->avctx;
+    int i, ret, width, height;
+    uint8_t *data[4];
+    int linesize[4];
+
+    ret = set_pix_fmt(s, src);
+    if (ret < 0)
+        return ret;
+    width = (int)src->m_uiWidth;
+    height = (int)src->m_uiHeight;
+    if (width != avctx->width || height != avctx->height) {
+        av_log(avctx, AV_LOG_INFO, "dimension change! %dx%d -> %dx%d\n",
+                avctx->width, avctx->height, width, height);
+        if ((ret = ff_set_dimensions(avctx, width, height)) < 0)
+            return ret;
+    }
+    if ((ret = ff_get_buffer(avctx, dest, 0)) < 0)
+        return ret;
+
+    for (i = 0; i < 3; i++) {
+        const vvdec::Component& plane = src->m_cComponent[i];
+        data[i] = plane.m_pucBuffer;
+        linesize[i] = plane.m_iStride;
+    }
+    data[3] = 0; linesize[3] = 0;
+
+    //TODO: conformance window?
+    av_image_copy(dest->data, dest->linesize, (const uint8_t **)data, linesize,
+                avctx->pix_fmt, width, height);
+    return 0;
+}
+
+static int vvdec_decode(AVCodecContext *avctx, void *data, int *got_frame,
+                      AVPacket *avpkt)
+{
+    VVdecContext *s = (VVdecContext *)avctx->priv_data;
+    vvdec::VVDec* vvdec = s->vvdec;
+    AVFrame *picture      = (AVFrame*)data;
+    int vret, ret = 0;
+    vvdec::Frame* frame = NULL;
+
+    if (!avpkt->size) {
+        vret = vvdec->flush(&frame);
+        if ((ret = check_vret(s, vret)) < 0)  {
+            goto error_out;
+        }
+    } else {
+        uint8_t *new_extradata;
+        int new_extradata_size;
+        new_extradata = av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA,
+                                            &new_extradata_size);
+        if (new_extradata && new_extradata_size > 0) {
+            return AVERROR_PATCHWELCOME;
+        }
+
+        vvdec::AccessUnit au;
+        au.m_pucBuffer = (unsigned char*)avpkt->data;
+        au.m_iUsedSize  = avpkt->size;
+        au.m_uiCts = avpkt->pts;
+        au.m_bCtsValid = true;
+        vret = vvdec->decode(au, &frame);
+        if (vret && vret != vvdec::VVDEC_TRY_AGAIN) {
+            if ((ret = check_vret(s, vret)) < 0)  {
+                goto error_out;
+            }
+        }
+
+    }
+    if (frame) {
+        ret = copy_to_avframe(s, frame, picture);
+        if (ret < 0)
+            goto error_out;
+        *got_frame = 1;
+    }
+    return 0;
+error_out:
+    if (frame) {
+        vvdec->objectUnref(frame);
+    }
+    return ret;
+}
+
+static av_cold int vvdec_free(AVCodecContext *avctx)
+{
+    VVdecContext *ctx           = (VVdecContext *)avctx->priv_data;
+    vvdec::VVDec* vvdec = ctx->vvdec;
+    if (vvdec) {
+        vvdec->uninit();
+        delete vvdec;
+    }
+    return 0;
+}
+
+static av_cold int vvdec_init(AVCodecContext *avctx)
+{
+    VVdecContext *s = (VVdecContext *)avctx->priv_data;
+    vvdec::VVDecParameter param;
+    vvdec::VVDec* vvdec = new vvdec::VVDec;
+    int vret, ret = 0;
+
+    av_log(avctx, AV_LOG_INFO, "VVDec version: %s\n", vvdec->getVersionNumber());
+
+    s->avctx = avctx;
+    s->vvdec = vvdec;
+    vret = vvdec->init(param);
+    if ((ret = check_vret(s, vret)) < 0) {
+        delete vvdec;
+        s->vvdec = NULL;
+    }
+    return ret;
+}
+
+extern "C" {
+
+AVCodec ff_libvvdec_decoder = {
+    .name           = "vvdec",
+    .long_name      = NULL_IF_CONFIG_SMALL("vvdec vvc decoder"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_H266,
+    .capabilities   = AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY,
+    .profiles       = NULL_IF_CONFIG_SMALL(ff_h266_profiles),
+    .wrapper_name   = "libvvdec",
+    .priv_data_size = sizeof(VVdecContext),
+    .init           = vvdec_init,
+    .decode         = vvdec_decode,
+    .close          = vvdec_free,
+};
+
+}
