diff mbox series

[FFmpeg-devel,v3,2/2] avcodec: add external dec libvvdec for H266/VVC

Message ID 20240514150936.39657-3-chris10317h5@gmail.com
State New
Headers show
Series Add support for H266/VVC encoding | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Christian Bartnik May 14, 2024, 3:09 p.m. UTC
From: Thomas Siedel <thomas.ff@spin-digital.com>

Add external decoder VVdeC for H266/VVC decoding.
Register new decoder libvvdec.
Add libvvdec to wrap the vvdec interface.
Enable decoder by adding --enable-libvvdec in configure step.

Co-authored-by: Christian Bartnik chris10317h5@gmail.com
Signed-off-by: Christian Bartnik <chris10317h5@gmail.com>
---
 configure              |   5 +
 libavcodec/Makefile    |   1 +
 libavcodec/allcodecs.c |   1 +
 libavcodec/libvvdec.c  | 617 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 624 insertions(+)
 create mode 100644 libavcodec/libvvdec.c

Comments

Lynne May 14, 2024, 4:28 p.m. UTC | #1
On 14/05/2024 17:09, Christian Bartnik wrote:
> From: Thomas Siedel <thomas.ff@spin-digital.com>
> 
> Add external decoder VVdeC for H266/VVC decoding.
> Register new decoder libvvdec.
> Add libvvdec to wrap the vvdec interface.
> Enable decoder by adding --enable-libvvdec in configure step.
> 
> Co-authored-by: Christian Bartnik chris10317h5@gmail.com
> Signed-off-by: Christian Bartnik <chris10317h5@gmail.com>
> ---
>   configure              |   5 +
>   libavcodec/Makefile    |   1 +
>   libavcodec/allcodecs.c |   1 +
>   libavcodec/libvvdec.c  | 617 +++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 624 insertions(+)
>   create mode 100644 libavcodec/libvvdec.c

I would prefer to have this one skipped, as initially suggested.
Christian Bartnik May 17, 2024, 10:15 a.m. UTC | #2
> On 14. May 2024, at 18:28, Lynne via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> wrote:
> 
> On 14/05/2024 17:09, Christian Bartnik wrote:
>> From: Thomas Siedel <thomas.ff@spin-digital.com>
>> Add external decoder VVdeC for H266/VVC decoding.
>> Register new decoder libvvdec.
>> Add libvvdec to wrap the vvdec interface.
>> Enable decoder by adding --enable-libvvdec in configure step.
>> Co-authored-by: Christian Bartnik chris10317h5@gmail.com
>> Signed-off-by: Christian Bartnik <chris10317h5@gmail.com>
>> ---
>>  configure              |   5 +
>>  libavcodec/Makefile    |   1 +
>>  libavcodec/allcodecs.c |   1 +
>>  libavcodec/libvvdec.c  | 617 +++++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 624 insertions(+)
>>  create mode 100644 libavcodec/libvvdec.c
> 
> I would prefer to have this one skipped, as initially suggested.

Ok, the next patch version will only contain libvvenc


> <OpenPGP_0xA2FEA5F03F034464.asc>_______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org <mailto:ffmpeg-devel@ffmpeg.org>
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org <mailto:ffmpeg-devel-request@ffmpeg.org> with subject "unsubscribe".
Cosmin Stejerean May 17, 2024, 5:20 p.m. UTC | #3
> On May 14, 2024, at 9:28 AM, Lynne via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> wrote:
> 
> On 14/05/2024 17:09, Christian Bartnik wrote:
>> From: Thomas Siedel <thomas.ff@spin-digital.com>
>> Add external decoder VVdeC for H266/VVC decoding.
>> Register new decoder libvvdec.
>> Add libvvdec to wrap the vvdec interface.
>> Enable decoder by adding --enable-libvvdec in configure step.
>> Co-authored-by: Christian Bartnik chris10317h5@gmail.com
>> Signed-off-by: Christian Bartnik <chris10317h5@gmail.com>
>> ---
>>  configure              |   5 +
>>  libavcodec/Makefile    |   1 +
>>  libavcodec/allcodecs.c |   1 +
>>  libavcodec/libvvdec.c  | 617 +++++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 624 insertions(+)
>>  create mode 100644 libavcodec/libvvdec.c
> 
> I would prefer to have this one skipped, as initially suggested.

Why? I tried to look back through the list but didn't see anything.

- Cosmin
Kieran Kunhya May 17, 2024, 7:14 p.m. UTC | #4
In this project we prefer internal decoders to external libs.

On Fri, 17 May 2024, 18:20 Cosmin Stejerean via ffmpeg-devel, <
ffmpeg-devel@ffmpeg.org> wrote:

>
>
> > On May 14, 2024, at 9:28 AM, Lynne via ffmpeg-devel <
> ffmpeg-devel@ffmpeg.org> wrote:
> >
> > On 14/05/2024 17:09, Christian Bartnik wrote:
> >> From: Thomas Siedel <thomas.ff@spin-digital.com>
> >> Add external decoder VVdeC for H266/VVC decoding.
> >> Register new decoder libvvdec.
> >> Add libvvdec to wrap the vvdec interface.
> >> Enable decoder by adding --enable-libvvdec in configure step.
> >> Co-authored-by: Christian Bartnik chris10317h5@gmail.com
> >> Signed-off-by: Christian Bartnik <chris10317h5@gmail.com>
> >> ---
> >>  configure              |   5 +
> >>  libavcodec/Makefile    |   1 +
> >>  libavcodec/allcodecs.c |   1 +
> >>  libavcodec/libvvdec.c  | 617 +++++++++++++++++++++++++++++++++++++++++
> >>  4 files changed, 624 insertions(+)
> >>  create mode 100644 libavcodec/libvvdec.c
> >
> > I would prefer to have this one skipped, as initially suggested.
>
> Why? I tried to look back through the list but didn't see anything.
>
> - Cosmin
>
Nuo Mi May 18, 2024, 2:04 p.m. UTC | #5
On Sat, May 18, 2024 at 1:20 AM Cosmin Stejerean via ffmpeg-devel <
ffmpeg-devel@ffmpeg.org> wrote:

>
>
> > On May 14, 2024, at 9:28 AM, Lynne via ffmpeg-devel <
> ffmpeg-devel@ffmpeg.org> wrote:
> >
> > On 14/05/2024 17:09, Christian Bartnik wrote:
> >> From: Thomas Siedel <thomas.ff@spin-digital.com>
> >> Add external decoder VVdeC for H266/VVC decoding.
> >> Register new decoder libvvdec.
> >> Add libvvdec to wrap the vvdec interface.
> >> Enable decoder by adding --enable-libvvdec in configure step.
> >> Co-authored-by: Christian Bartnik chris10317h5@gmail.com
> >> Signed-off-by: Christian Bartnik <chris10317h5@gmail.com>
> >> ---
> >>  configure              |   5 +
> >>  libavcodec/Makefile    |   1 +
> >>  libavcodec/allcodecs.c |   1 +
> >>  libavcodec/libvvdec.c  | 617 +++++++++++++++++++++++++++++++++++++++++
> >>  4 files changed, 624 insertions(+)
> >>  create mode 100644 libavcodec/libvvdec.c
> >
> > I would prefer to have this one skipped, as initially suggested.
>
> Why? I tried to look back through the list but didn't see anything.
>
Hi Cosmin,
This happened many years ago. See the discussion here:
https://patchwork.ffmpeg.org/project/ffmpeg/patch/20201221060710.12230-6-nuomi2021@gmail.com/#60589
Now that we have an internal vvc decoder, we can focus on improving it.
As for the encoder, it is far more complex than the decoder. Reasonable to
wrapper other libraries just like libx264 and libx265...

>
> - Cosmin
> _______________________________________________
> 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".
>
Cosmin Stejerean May 18, 2024, 6:55 p.m. UTC | #6
> On May 18, 2024, at 7:04 AM, Nuo Mi <nuomi2021@gmail.com> wrote:
> 
> This happened many years ago. See the discussion here:
> https://patchwork.ffmpeg.org/project/ffmpeg/patch/20201221060710.12230-6-nuomi2021@gmail.com/#60589
> Now that we have an internal vvc decoder, we can focus on improving it.
> As for the encoder, it is far more complex than the decoder. Reasonable to
> wrapper other libraries just like libx264 and libx265...

I'm all for improving the internal decoder, and I agree that the internal decoder should be preferred and be used by default, but it's not clear why that should preclude adding an external decoder as an option.

Sometimes bugs are found in the internal decoders or some functionality may be missing, and having the option to fallback to an external decoder as a workaround is very useful in practice.

For example there's a native AAC decoder in ffmpeg but it's still useful to have FDK-AAC available for decoding when running into edge cases on the native AAC decoder, or to decode USAC while the new decoder is under development, etc.

- Cosmin
Rémi Denis-Courmont May 19, 2024, 5:48 a.m. UTC | #7
Hi,

Le 18 mai 2024 21:55:04 GMT+03:00, Cosmin Stejerean via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> a écrit :
>
>
>> On May 18, 2024, at 7:04 AM, Nuo Mi <nuomi2021@gmail.com> wrote:
>> 
>> This happened many years ago. See the discussion here:
>> https://patchwork.ffmpeg.org/project/ffmpeg/patch/20201221060710.12230-6-nuomi2021@gmail.com/#60589
>> Now that we have an internal vvc decoder, we can focus on improving it.
>> As for the encoder, it is far more complex than the decoder. Reasonable to
>> wrapper other libraries just like libx264 and libx265...
>
>I'm all for improving the internal decoder, and I agree that the internal decoder should be preferred and be used by default, but it's not clear why that should preclude adding an external decoder as an option.

Adding a disabled-by-default decoder is adding bloat, if there is not a specific known reason why that is needed.

This is especially true for video decoders, where the external decoders typically will have no or worse optimisations for DSP functions, no thread support (or not accessible via libavcodec), and no integration with hwaccel.

The later point has become a problem even with dav1d...

>Sometimes bugs are found in the internal decoders

Providing a non-default choice is a poor excuse for a bug. Most users won't know how to do that, and many FFmpeg reverse dependencies don't even allow it.

> or some functionality may be missing, and having the option to fallback to an external decoder as a workaround is very useful in practice.

That I can agree, but what do you find missing in the VVC decoder compared to libvvcdec?
Cosmin Stejerean May 20, 2024, 4:24 p.m. UTC | #8
On May 18, 2024, at 10:48 PM, Rémi Denis-Courmont <remi@remlab.net> wrote:

Le 18 mai 2024 21:55:04 GMT+03:00, Cosmin Stejerean via ffmpeg-devel <ffmpeg-devel@ffmpeg.org <mailto:ffmpeg-devel@ffmpeg.org> > a écrit :


On May 18, 2024, at 7:04 AM, Nuo Mi <nuomi2021@gmail.com <mailto:nuomi2021@gmail.com> > wrote:

This happened many years ago. See the discussion here:
https://patchwork.ffmpeg.org/project/ffmpeg/patch/20201221060710.12230-6-nuomi2021@gmail.com/#60589 <https://patchwork.ffmpeg.org/project/ffmpeg/patch/20201221060710.12230-6-nuomi2021@gmail.com/#60589> 
Now that we have an internal vvc decoder, we can focus on improving it.
As for the encoder, it is far more complex than the decoder. Reasonable to
wrapper other libraries just like libx264 and libx265...

I'm all for improving the internal decoder, and I agree that the internal decoder should be preferred and be used by default, but it's not clear why that should preclude adding an external decoder as an option.

Adding a disabled-by-default decoder is adding bloat, if there is not a specific known reason why that is needed.

This is especially true for video decoders, where the external decoders typically will have no or worse optimisations for DSP functions, no thread support (or not accessible via libavcodec), 

Is this an actual problem with vvdec? To me it seems like a reasonably optimized decoder with support for threading, etc.

and no integration with hwaccel.

The later point has become a problem even with dav1d...

hwaccel decoding seems somewhat orthogonal


Sometimes bugs are found in the internal decoders

Providing a non-default choice is a poor excuse for a bug. Most users won't know how to do that, 

need better documentation?

and many FFmpeg reverse dependencies don't even allow it.

not sure that designing for the lowest common denominator of reverse dependencies is the best way to do things, but at a minimum one can rebuild ffmpeg with only one decoder enabled to get around this


or some functionality may be missing, and having the option to fallback to an external decoder as a workaround is very useful in practice.

That I can agree, but what do you find missing in the VVC decoder compared to libvvcdec?
Cosmin Stejerean May 20, 2024, 4:33 p.m. UTC | #9
Trying again with better formatting, hopefully

> On May 18, 2024, at 10:48 PM, Rémi Denis-Courmont <remi@remlab.net> wrote:
> 
> Hi,
> 
> Le 18 mai 2024 21:55:04 GMT+03:00, Cosmin Stejerean via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> a écrit :
>> 
>> 
>>> On May 18, 2024, at 7:04 AM, Nuo Mi <nuomi2021@gmail.com> wrote:
>>> 
>>> This happened many years ago. See the discussion here:
>>> https://patchwork.ffmpeg.org/project/ffmpeg/patch/20201221060710.12230-6-nuomi2021@gmail.com/#60589
>>> Now that we have an internal vvc decoder, we can focus on improving it.
>>> As for the encoder, it is far more complex than the decoder. Reasonable to
>>> wrapper other libraries just like libx264 and libx265...
>> 
>> I'm all for improving the internal decoder, and I agree that the internal decoder should be preferred and be used by default, but it's not clear why that should preclude adding an external decoder as an option.
> 
> Adding a disabled-by-default decoder is adding bloat, if there is not a specific known reason why that is needed.
> 
> This is especially true for video decoders, where the external decoders typically will have no or worse optimisations for DSP functions, no thread support (or not accessible via libavcodec), and no integration with hwaccel.

Is this an actual problem with vvdec? To me it seems like a reasonably optimized decoder with support for threading, etc. hwaccel decoding seems somewhat orthogonal

> 
> The later point has become a problem even with dav1d...
> 
>> Sometimes bugs are found in the internal decoders
> 
> Providing a non-default choice is a poor excuse for a bug. Most users won't know how to do that, and many FFmpeg reverse dependencies don't even allow it.

Need better documentation then? Not sure that designing for the lowest common denominator of reverse dependencies is the best way to do things, but at a minimum one can rebuild ffmpeg with only one decoder enabled to get around this.

> 
>> or some functionality may be missing, and having the option to fallback to an external decoder as a workaround is very useful in practice.
> 
> That I can agree, but what do you find missing in the VVC decoder compared to libvvcdec?

This I'm not sure, perhaps there is no feature gap? Does the internal VVC decoder supports all features of the Main10 profile?

- Cosmin
Rémi Denis-Courmont May 20, 2024, 6:03 p.m. UTC | #10
Le maanantaina 20. toukokuuta 2024, 19.33.43 EEST Cosmin Stejerean via ffmpeg-
devel a écrit :
> > Adding a disabled-by-default decoder is adding bloat, if there is not a
> > specific known reason why that is needed.
 
> > This is especially true for video decoders, where the external decoders
> > typically will have no or worse optimisations for DSP functions, no
> > thread support (or not accessible via libavcodec), and no integration
> > with hwaccel.
> 
> Is this an actual problem with vvdec?

Yes?

> To me it seems like a reasonably optimized decoder with support for
> threading, etc.

It seems to be vendoring SIMDe, which is 1) a bad thing according to most 
distro policies, 2) not as good as FFmpeg optimisations generally speaking and 
3) lacking several ISAs featured by FFmpeg.

> hwaccel decoding seems somewhat orthogonal

How exactly will that work then? Either vvdec is the default, and hwaccel 
won't work, or vvdec is not the default and it's essentially dead code.

> > Providing a non-default choice is a poor excuse for a bug. Most users
> > won't know how to do that, and many FFmpeg reverse dependencies don't
> > even allow it.
> 
> Need better documentation then?

Well, good luck with that. Documentation for working around as yet unknown 
bugs sounds a bit difficult to write, and even then, requiring manual decoder 
selection seems very user-hostile to me.

> Not sure that designing for the lowest common denominator of reverse
> dependencies is the best way to do things, but at a minimum one can rebuild
> ffmpeg with only one decoder enabled to get around this.

To the contrary, gstreamer, mpv, VLC et al. are better equipped to handle 
this, starting with a proper plugin architecture.
Cosmin Stejerean May 20, 2024, 6:39 p.m. UTC | #11
> On May 20, 2024, at 11:03 AM, Rémi Denis-Courmont <remi@remlab.net> wrote:
> 
> Le maanantaina 20. toukokuuta 2024, 19.33.43 EEST Cosmin Stejerean via ffmpeg-
> devel a écrit :
> 
>> hwaccel decoding seems somewhat orthogonal
> 
> How exactly will that work then? Either vvdec is the default, and hwaccel 
> won't work, or vvdec is not the default and it's essentially dead code.

The same way using FDK-AAC as a decoder works, if you want to use it as the *AAC decoder you have to specify the decoder with -c:a libfdk_aac before the input.-

> 
>>> Providing a non-default choice is a poor excuse for a bug. Most users
>>> won't know how to do that, and many FFmpeg reverse dependencies don't
>>> even allow it.
>> 
>> Need better documentation then?
> 
> Well, good luck with that. Documentation for working around as yet unknown 
> bugs sounds a bit difficult to write, and even then, requiring manual decoder 
> selection seems very user-hostile to me.

Where is the "requiring" part coming in? I'm saying that manual decoder selection is an option in the ffmpeg CLI, and can be used to select an alternate decoder if the default one is not sufficient for some reason. 

This is a thing I've had to do repeatedly over the past few years for FDK-AAC.

> 
>> Not sure that designing for the lowest common denominator of reverse
>> dependencies is the best way to do things, but at a minimum one can rebuild
>> ffmpeg with only one decoder enabled to get around this.
> 
> To the contrary, gstreamer, mpv, VLC et al. are better equipped to handle 
> this, starting with a proper plugin architecture.

Better equipped to handle what? For the usecase of transcoding it doesn't matter whether or not downstream player dependencies do or don't expose alternate decoders in libavcodec, to the extent they even use libavcodec for decoding.

So it seems weird to reject a patch to add a decoder option because it may or may not be useful to the downstream players.
 
- Cosmin
Rémi Denis-Courmont May 20, 2024, 7:01 p.m. UTC | #12
Le maanantaina 20. toukokuuta 2024, 21.39.18 EEST Cosmin Stejerean via ffmpeg-
devel a écrit :
> The same way using FDK-AAC as a decoder works, if you want to use it as the
> *AAC decoder you have to specify the decoder with -c:a libfdk_aac before
> the input.-

AFAIK, we don't have hwaccel for audio codecs. That sentence makes zero sense.

And again, you can't expect users to select decoders manually. If vvdec is the 
default, hwaccel won't work. If vvdec is not the default, then it's dead code.

> Where is the "requiring" part coming in? I'm saying that manual decoder
> selection is an option in the ffmpeg CLI, and can be used to select an
> alternate decoder if the default one is not sufficient for some reason. 

So most people use libavcodec through higher-level frameworks or applications, 
not the CLI tool, and that is especially true for playback. If that's the 
super-niche use-case for vvdec, then I agree with Kieran that it's just bloat.

> > To the contrary, gstreamer, mpv, VLC et al. are better equipped to handle
> > this, starting with a proper plugin architecture.
> 
> 
> Better equipped to handle what?

To handle multiple implementations of decoding for a given codec, and not 
force them as installation and load-time dependencies.
Cosmin Stejerean May 20, 2024, 7:33 p.m. UTC | #13
> On May 20, 2024, at 12:01 PM, Rémi Denis-Courmont <remi@remlab.net> wrote:
> 
> Le maanantaina 20. toukokuuta 2024, 21.39.18 EEST Cosmin Stejerean via ffmpeg-
> devel a écrit :
>> The same way using FDK-AAC as a decoder works, if you want to use it as the
>> *AAC decoder you have to specify the decoder with -c:a libfdk_aac before
>> the input.-
> 
> AFAIK, we don't have hwaccel for audio codecs. That sentence makes zero sense.

This is unrelated to hwaccel, I'm illustrating how a non-default decoder is selected in practice. It just so happens I always need to do this for audio currently.

> 
> And again, you can't expect users to select decoders manually. If vvdec is the 
> default, hwaccel won't work. If vvdec is not the default, then it's dead code.

Not sure why you keep returning to this false dichotomy. 

You absolutely can have a decoder that's not the default, there is a facility for selecting it, and I use this to select the non-default decoder despite you claiming that non-default decoders are dead code, or that manually selecting them isn't a thing that users do.

>> Where is the "requiring" part coming in? I'm saying that manual decoder
>> selection is an option in the ffmpeg CLI, and can be used to select an
>> alternate decoder if the default one is not sufficient for some reason. 
> 
> So most people use libavcodec through higher-level frameworks or applications, 
> not the CLI tool, and that is especially true for playback. If that's the 
> super-niche use-case for vvdec, then I agree with Kieran that it's just bloat.

Yes, the use case would be to be an alternate decoder that's available to users that want to use it. If that's not your use case that's ok, don't build with --enable-libvvdec.



- Cosmin
Rémi Denis-Courmont May 20, 2024, 8:05 p.m. UTC | #14
Le maanantaina 20. toukokuuta 2024, 22.33.45 EEST Cosmin Stejerean via ffmpeg-
devel a écrit :
> > And again, you can't expect users to select decoders manually. If vvdec is
> > the 
> > default, hwaccel won't work. If vvdec is not the default, then it's
> > dead code.
> 
> Not sure why you keep returning to this false dichotomy. 

It is the default or it is not the default. This is a true dichotomy and it is 
very disingenous to claim otherwise, so I will leave it at that.
Cosmin Stejerean May 20, 2024, 8:38 p.m. UTC | #15
> On May 20, 2024, at 1:05 PM, Rémi Denis-Courmont <remi@remlab.net> wrote:
> 
> Le maanantaina 20. toukokuuta 2024, 22.33.45 EEST Cosmin Stejerean via ffmpeg-
> devel a écrit :
>>> And again, you can't expect users to select decoders manually. If vvdec is
>>> the 
>>> default, hwaccel won't work. If vvdec is not the default, then it's
>>> dead code.
>> 
>> Not sure why you keep returning to this false dichotomy. 
> 
> It is the default or it is not the default. This is a true dichotomy and it is 
> very disingenous to claim otherwise, so I will leave it at that.

You stated it's either the default or dead code. This is the false dichotomy I was referring to. Seems disingenuous to claim otherwise.

- Cosmin
diff mbox series

Patch

diff --git a/configure b/configure
index 5d9a14821b..a5df482215 100755
--- a/configure
+++ b/configure
@@ -294,6 +294,7 @@  External library support:
                            native implementation exists [no]
   --enable-libvpx          enable VP8 and VP9 de/encoding via libvpx [no]
   --enable-libvvenc        enable H.266/VVC encoding via vvenc [no]
+  --enable-libvvdec        enable H.266/VVC decoding via vvdec [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]
@@ -1968,6 +1969,7 @@  EXTERNAL_LIBRARY_LIST="
     libvorbis
     libvpx
     libvvenc
+    libvvdec
     libwebp
     libxevd
     libxeve
@@ -3561,6 +3563,8 @@  libvpx_vp8_encoder_deps="libvpx"
 libvpx_vp9_decoder_deps="libvpx"
 libvpx_vp9_encoder_deps="libvpx"
 libvvenc_encoder_deps="libvvenc"
+libvvdec_decoder_deps="libvvdec"
+libvvdec_decoder_select="vvc_mp4toannexb_bsf"
 libwebp_encoder_deps="libwebp"
 libwebp_anim_encoder_deps="libwebp"
 libx262_encoder_deps="libx262"
@@ -7029,6 +7033,7 @@  enabled libvpx            && {
     fi
 }
 enabled libvvenc          && require_pkg_config libvvenc "libvvenc >= 1.6.1" "vvenc/vvenc.h" vvenc_get_version
+enabled libvvdec          && require_pkg_config libvvdec "libvvdec >= 1.6.0" "vvdec/vvdec.h" vvdec_get_version
 
 enabled libwebp           && {
     enabled libwebp_encoder      && require_pkg_config libwebp "libwebp >= 0.2.0" webp/encode.h WebPGetEncoderVersion
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 5d7349090e..318b22a1fa 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1154,6 +1154,7 @@  OBJS-$(CONFIG_LIBVPX_VP8_ENCODER)         += libvpxenc.o
 OBJS-$(CONFIG_LIBVPX_VP9_DECODER)         += libvpxdec.o
 OBJS-$(CONFIG_LIBVPX_VP9_ENCODER)         += libvpxenc.o
 OBJS-$(CONFIG_LIBVVENC_ENCODER)           += libvvenc.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 59d36dbd56..4120681d17 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -801,6 +801,7 @@  extern const FFCodec ff_libvpx_vp8_decoder;
 extern FFCodec ff_libvpx_vp9_encoder;
 extern const FFCodec ff_libvpx_vp9_decoder;
 extern const FFCodec ff_libvvenc_encoder;
+extern const FFCodec ff_libvvdec_decoder;
 /* preferred over libwebp */
 extern const FFCodec ff_libwebp_anim_encoder;
 extern const FFCodec ff_libwebp_encoder;
diff --git a/libavcodec/libvvdec.c b/libavcodec/libvvdec.c
new file mode 100644
index 0000000000..7f94a81b37
--- /dev/null
+++ b/libavcodec/libvvdec.c
@@ -0,0 +1,617 @@ 
+/*
+ * H.266 decoding using the VVdeC library
+ *
+ * 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 "config_components.h"
+
+#include <vvdec/vvdec.h>
+
+#include "libavutil/common.h"
+#include "libavutil/avutil.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/opt.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/frame.h"
+#include "libavutil/mastering_display_metadata.h"
+#include "libavutil/log.h"
+
+#include "avcodec.h"
+#include "codec_internal.h"
+#include "decode.h"
+#include "internal.h"
+#include "profiles.h"
+
+#include "cbs_h266.h"
+
+typedef struct VVdeCContext {
+    AVClass      *av_class;
+    vvdecDecoder *vvdecDec;
+    vvdecParams  vvdecParams;
+    bool         bFlush;
+    AVBufferPool *pools[3];     /** Pools for each data plane. */
+    int          pool_size[3];
+    CodedBitstreamContext *cbc;
+    CodedBitstreamFragment current_frame;
+} VVdeCContext;
+
+
+static void ff_vvdec_log_callback(void *avctx, int level, const char *fmt,
+                                  va_list args)
+{
+    vfprintf(level == 1 ? stderr : stdout, fmt, args);
+}
+
+static void *ff_vvdec_buffer_allocator(void *ctx, vvdecComponentType comp,
+                                       uint32_t size, uint32_t alignment,
+                                       void **allocator)
+{
+    AVBufferRef *buf;
+    VVdeCContext *s;
+    int plane;
+
+    uint32_t alignedsize = FFALIGN(size, alignment);
+    s = (VVdeCContext *) ctx;
+    plane = (int) comp;
+
+    if (plane < 0 || plane > 3)
+        return NULL;
+
+    if (alignedsize != s->pool_size[plane]) {
+        av_buffer_pool_uninit(&s->pools[plane]);
+        s->pools[plane] = av_buffer_pool_init(alignedsize, NULL);
+        if (!s->pools[plane]) {
+            s->pool_size[plane] = 0;
+            return NULL;
+        }
+        s->pool_size[plane] = alignedsize;
+    }
+
+    buf = av_buffer_pool_get(s->pools[plane]);
+    if (!buf)
+        return NULL;
+
+    *allocator = (void *) buf;
+    return buf->data;
+}
+
+static void ff_vvdec_buffer_unref(void *ctx, void *allocator)
+{
+    AVBufferRef *buf = (AVBufferRef *) allocator;
+    av_buffer_unref(&buf);
+}
+
+static void ff_vvdec_printParameterInfo(AVCodecContext *avctx,
+                                        vvdecParams *params)
+{
+    av_log(avctx, AV_LOG_DEBUG, "Version info: vvdec %s ( threads %d)\n",
+           vvdec_get_version(), params->threads);
+}
+
+static int ff_vvdec_set_pix_fmt(AVCodecContext *avctx, vvdecFrame *frame)
+{
+    if (NULL != frame->picAttributes && NULL != frame->picAttributes->vui &&
+        frame->picAttributes->vui->colourDescriptionPresentFlag) {
+        avctx->color_trc       = frame->picAttributes->vui->transferCharacteristics;
+        avctx->color_primaries = frame->picAttributes->vui->colourPrimaries;
+        avctx->colorspace      = frame->picAttributes->vui->matrixCoefficients;
+    } else {
+        avctx->color_primaries = AVCOL_PRI_UNSPECIFIED;
+        avctx->color_trc       = AVCOL_TRC_UNSPECIFIED;
+        avctx->colorspace      = AVCOL_SPC_UNSPECIFIED;
+    }
+
+    if (NULL != frame->picAttributes && NULL != frame->picAttributes->vui &&
+        frame->picAttributes->vui->videoSignalTypePresentFlag) {
+        avctx->color_range = frame->picAttributes->vui->videoFullRangeFlag ?
+                             AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
+    } else {
+        avctx->color_range = AVCOL_RANGE_MPEG;
+    }
+
+    switch (frame->colorFormat) {
+    case VVDEC_CF_YUV420_PLANAR:
+    case VVDEC_CF_YUV400_PLANAR:
+        if (frame->bitDepth == 8) {
+            avctx->pix_fmt = frame->numPlanes == 1 ?
+                             AV_PIX_FMT_GRAY8 : AV_PIX_FMT_YUV420P;
+            avctx->profile = FF_PROFILE_VVC_MAIN_10;
+            return 0;
+        } else if (frame->bitDepth == 10) {
+            avctx->pix_fmt = frame->numPlanes == 1 ?
+                AV_PIX_FMT_GRAY10 : AV_PIX_FMT_YUV420P10;
+            avctx->profile = FF_PROFILE_VVC_MAIN_10;
+            return 0;
+        } else {
+            return AVERROR_INVALIDDATA;
+        }
+    case VVDEC_CF_YUV422_PLANAR:
+    case VVDEC_CF_YUV444_PLANAR:
+        if (frame->bitDepth == 8) {
+            avctx->pix_fmt = frame->colorFormat == VVDEC_CF_YUV444_PLANAR ?
+                             AV_PIX_FMT_YUV444P : AV_PIX_FMT_YUV422P;
+            if (avctx->profile != FF_PROFILE_VVC_MAIN_10_444)
+                avctx->profile = FF_PROFILE_VVC_MAIN_10_444;
+            return 0;
+        } else if (frame->bitDepth == 10) {
+            avctx->pix_fmt = frame->colorFormat == VVDEC_CF_YUV444_PLANAR ?
+                             AV_PIX_FMT_YUV444P10 : AV_PIX_FMT_YUV422P10;
+            if (avctx->profile != FF_PROFILE_VVC_MAIN_10_444)
+                avctx->profile = FF_PROFILE_VVC_MAIN_10_444;
+            return 0;
+        } else {
+            return AVERROR_INVALIDDATA;
+        }
+    default:
+        return AVERROR_INVALIDDATA;
+    }
+}
+
+static int set_side_data(AVCodecContext *avctx, AVFrame *avframe,
+                         vvdecFrame *frame)
+{
+    vvdecSEI *sei;
+    VVdeCContext *s = (VVdeCContext *) avctx->priv_data;
+
+    sei = vvdec_find_frame_sei(s->vvdecDec,
+                               VVDEC_MASTERING_DISPLAY_COLOUR_VOLUME, frame);
+    if (sei) {
+        // VVC uses a g,b,r ordering, which we convert to a more natural r,g,b
+        const int mapping[3] = { 2, 0, 1 };
+        const int chroma_den = 50000;
+        const int luma_den = 10000;
+        int i;
+        vvdecSEIMasteringDisplayColourVolume *p;
+        AVMasteringDisplayMetadata *metadata =
+            av_mastering_display_metadata_create_side_data(avframe);
+        p = (vvdecSEIMasteringDisplayColourVolume *) (sei->payload);
+        if (p && metadata) {
+            for (i = 0; i < 3; i++) {
+                const int j = mapping[i];
+                metadata->display_primaries[i][0].num = p->primaries[j][0];
+                metadata->display_primaries[i][0].den = chroma_den;
+                metadata->display_primaries[i][1].num = p->primaries[j][1];
+                metadata->display_primaries[i][1].den = chroma_den;
+            }
+            metadata->white_point[0].num = p->whitePoint[0];
+            metadata->white_point[0].den = chroma_den;
+            metadata->white_point[1].num = p->whitePoint[1];
+            metadata->white_point[1].den = chroma_den;
+
+            metadata->max_luminance.num = p->maxLuminance;
+            metadata->max_luminance.den = luma_den;
+            metadata->min_luminance.num = p->minLuminance;
+            metadata->min_luminance.den = luma_den;
+            metadata->has_luminance = 1;
+            metadata->has_primaries = 1;
+
+            av_log(avctx, AV_LOG_DEBUG, "Mastering Display Metadata:\n");
+            av_log(avctx, AV_LOG_DEBUG,
+                   "r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f)\n",
+                   av_q2d(metadata->display_primaries[0][0]),
+                   av_q2d(metadata->display_primaries[0][1]),
+                   av_q2d(metadata->display_primaries[1][0]),
+                   av_q2d(metadata->display_primaries[1][1]),
+                   av_q2d(metadata->display_primaries[2][0]),
+                   av_q2d(metadata->display_primaries[2][1]),
+                   av_q2d(metadata->white_point[0]),
+                   av_q2d(metadata->white_point[1]));
+            av_log(avctx, AV_LOG_DEBUG, "min_luminance=%f, max_luminance=%f\n",
+                   av_q2d(metadata->min_luminance),
+                   av_q2d(metadata->max_luminance));
+        }
+        return 0;
+    }
+
+    sei = vvdec_find_frame_sei(s->vvdecDec, VVDEC_CONTENT_LIGHT_LEVEL_INFO,
+                               frame);
+    if (sei) {
+        vvdecSEIContentLightLevelInfo *p = NULL;
+        AVContentLightMetadata *light =
+            av_content_light_metadata_create_side_data(avframe);
+        p = (vvdecSEIContentLightLevelInfo *) (sei->payload);
+        if (p && light) {
+            light->MaxCLL  = p->maxContentLightLevel;
+            light->MaxFALL = p->maxPicAverageLightLevel;
+        }
+
+        av_log(avctx, AV_LOG_DEBUG, "Content Light Level Metadata:\n");
+        av_log(avctx, AV_LOG_DEBUG, "MaxCLL=%d, MaxFALL=%d\n",
+               light->MaxCLL, light->MaxFALL);
+    }
+
+    return 0;
+}
+
+static int set_pixel_format(AVCodecContext *avctx, const H266RawSPS *sps)
+{
+    enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;
+    const AVPixFmtDescriptor *desc;
+    switch (sps->sps_bitdepth_minus8+8) {
+    case 8:
+        if (sps->sps_chroma_format_idc == 0)
+            pix_fmt = AV_PIX_FMT_GRAY8;
+        if (sps->sps_chroma_format_idc == 1)
+            pix_fmt = AV_PIX_FMT_YUV420P;
+        if (sps->sps_chroma_format_idc == 2)
+            pix_fmt = AV_PIX_FMT_YUV422P;
+        if (sps->sps_chroma_format_idc == 3)
+            pix_fmt = AV_PIX_FMT_YUV444P;
+        break;
+    case 9:
+        if (sps->sps_chroma_format_idc == 0)
+            pix_fmt = AV_PIX_FMT_GRAY9;
+        if (sps->sps_chroma_format_idc == 1)
+            pix_fmt = AV_PIX_FMT_YUV420P9;
+        if (sps->sps_chroma_format_idc == 2)
+            pix_fmt = AV_PIX_FMT_YUV422P9;
+        if (sps->sps_chroma_format_idc == 3)
+            pix_fmt = AV_PIX_FMT_YUV444P9;
+        break;
+    case 10:
+        if (sps->sps_chroma_format_idc == 0)
+            pix_fmt = AV_PIX_FMT_GRAY10;
+        if (sps->sps_chroma_format_idc == 1)
+            pix_fmt = AV_PIX_FMT_YUV420P10;
+        if (sps->sps_chroma_format_idc == 2)
+            pix_fmt = AV_PIX_FMT_YUV422P10;
+        if (sps->sps_chroma_format_idc == 3)
+            pix_fmt = AV_PIX_FMT_YUV444P10;
+        break;
+    case 12:
+        if (sps->sps_chroma_format_idc == 0)
+            pix_fmt = AV_PIX_FMT_GRAY12;
+        if (sps->sps_chroma_format_idc == 1)
+            pix_fmt = AV_PIX_FMT_YUV420P12;
+        if (sps->sps_chroma_format_idc == 2)
+            pix_fmt = AV_PIX_FMT_YUV422P12;
+        if (sps->sps_chroma_format_idc == 3)
+            pix_fmt = AV_PIX_FMT_YUV444P12;
+        break;
+    default:
+        av_log(avctx, AV_LOG_ERROR,
+               "The following bit-depths are currently specified: 8, 9, 10 and 12 bits, "
+               "sps_chroma_format_idc is %d, depth is %d\n",
+               sps->sps_chroma_format_idc, sps->sps_bitdepth_minus8+8);
+        return AVERROR_INVALIDDATA;
+    }
+
+    desc = av_pix_fmt_desc_get(pix_fmt);
+    if (!desc)
+        return AVERROR(EINVAL);
+
+    avctx->pix_fmt = pix_fmt;
+
+    return 0;
+}
+
+static void export_stream_params(AVCodecContext *avctx, const H266RawSPS *sps)
+{
+    avctx->coded_width  = sps->sps_pic_width_max_in_luma_samples;
+    avctx->coded_height = sps->sps_pic_height_max_in_luma_samples;
+    avctx->width        = sps->sps_pic_width_max_in_luma_samples -
+                          sps->sps_conf_win_left_offset -
+                          sps->sps_conf_win_right_offset;
+    avctx->height       = sps->sps_pic_height_max_in_luma_samples -
+                          sps->sps_conf_win_top_offset -
+                          sps->sps_conf_win_bottom_offset;
+    avctx->has_b_frames = sps->sps_max_sublayers_minus1+1;
+    avctx->profile      = sps->profile_tier_level.general_profile_idc;
+    avctx->level        = sps->profile_tier_level.general_level_idc;
+
+    set_pixel_format( avctx, sps);
+
+    avctx->color_range = sps->vui.vui_full_range_flag ? AVCOL_RANGE_JPEG :
+                                                        AVCOL_RANGE_MPEG;
+
+    if (sps->vui.vui_colour_description_present_flag) {
+        avctx->color_primaries = sps->vui.vui_colour_primaries;
+        avctx->color_trc       = sps->vui.vui_transfer_characteristics;
+        avctx->colorspace      = sps->vui.vui_matrix_coeffs;
+    } else {
+        avctx->color_primaries = AVCOL_PRI_UNSPECIFIED;
+        avctx->color_trc       = AVCOL_TRC_UNSPECIFIED;
+        avctx->colorspace      = AVCOL_SPC_UNSPECIFIED;
+    }
+
+    avctx->chroma_sample_location = AVCHROMA_LOC_UNSPECIFIED;
+    if (sps->sps_chroma_format_idc == 1) {
+        if (sps->vui.vui_chroma_loc_info_present_flag) {
+            if (sps->vui.vui_chroma_sample_loc_type_top_field <= 5)
+                avctx->chroma_sample_location =
+                    sps->vui.vui_chroma_sample_loc_type_top_field + 1;
+        } else
+            avctx->chroma_sample_location = AVCHROMA_LOC_LEFT;
+    }
+
+    if (sps->sps_timing_hrd_params_present_flag &&
+        sps->sps_general_timing_hrd_parameters.num_units_in_tick &&
+        sps->sps_general_timing_hrd_parameters.time_scale) {
+        av_reduce(&avctx->framerate.den, &avctx->framerate.num,
+                  sps->sps_general_timing_hrd_parameters.num_units_in_tick,
+                  sps->sps_general_timing_hrd_parameters.time_scale, INT_MAX);
+    }
+}
+
+static av_cold int ff_vvdec_decode_init(AVCodecContext *avctx)
+{
+    int i, ret;
+    VVdeCContext *s = (VVdeCContext *) avctx->priv_data;
+
+    vvdec_params_default(&s->vvdecParams);
+    s->vvdecParams.logLevel = VVDEC_DETAILS;
+
+    if (av_log_get_level() >= AV_LOG_DEBUG)
+        s->vvdecParams.logLevel = VVDEC_DETAILS;
+    else if (av_log_get_level() >= AV_LOG_VERBOSE)
+        s->vvdecParams.logLevel = VVDEC_INFO;     // VVDEC_INFO will output per picture info
+    else if (av_log_get_level() >= AV_LOG_INFO)
+        s->vvdecParams.logLevel = VVDEC_WARNING;  // AV_LOG_INFO is ffmpeg default
+    else
+        s->vvdecParams.logLevel = VVDEC_SILENT;
+
+    if (avctx->thread_count > 0)
+        s->vvdecParams.threads = avctx->thread_count;   // number of worker threads (should not exceed the number of physical cpu's)
+    else
+        s->vvdecParams.threads = -1;    // get max cpus
+
+    ff_vvdec_printParameterInfo(avctx, &s->vvdecParams);
+
+    // using buffer allocation by using AVBufferPool
+    s->vvdecParams.opaque = avctx->priv_data;
+    s->vvdecDec = vvdec_decoder_open_with_allocator(&s->vvdecParams,
+                                                    ff_vvdec_buffer_allocator,
+                                                    ff_vvdec_buffer_unref);
+
+
+    if (!s->vvdecDec) {
+        av_log(avctx, AV_LOG_ERROR, "cannot init vvdec decoder\n");
+        return -1;
+    }
+
+    vvdec_set_logging_callback(s->vvdecDec, ff_vvdec_log_callback);
+
+    s->bFlush = false;
+
+    for (i = 0; i < FF_ARRAY_ELEMS(s->pools); i++) {
+        s->pools[i] = NULL;
+        s->pool_size[i] = 0;
+    }
+
+    ret = ff_cbs_init(&s->cbc, AV_CODEC_ID_VVC, avctx);
+    if (ret)
+        return ret;
+
+    if (!avctx->internal->is_copy) {
+        if (avctx->extradata_size > 0 && avctx->extradata) {
+            const CodedBitstreamH266Context *h266 = s->cbc->priv_data;
+            ff_cbs_fragment_reset(&s->current_frame);
+            ret = ff_cbs_read_extradata_from_codec(s->cbc, &s->current_frame, avctx);
+            if (ret < 0)
+                return ret;
+
+            if ( h266->sps[0] != NULL)
+              export_stream_params(avctx, h266->sps[0]);
+        }
+    }
+
+    return 0;
+}
+
+static av_cold int ff_vvdec_decode_close(AVCodecContext *avctx)
+{
+    VVdeCContext *s = (VVdeCContext *) avctx->priv_data;
+
+    for (int i = 0; i < FF_ARRAY_ELEMS(s->pools); i++) {
+        av_buffer_pool_uninit(&s->pools[i]);
+        s->pool_size[i] = 0;
+    }
+
+    if (0 != vvdec_decoder_close(s->vvdecDec)) {
+        av_log(avctx, AV_LOG_ERROR, "cannot close vvdec\n");
+        return -1;
+    }
+
+    ff_cbs_fragment_free(&s->current_frame);
+    ff_cbs_close(&s->cbc);
+
+    s->bFlush = false;
+    return 0;
+}
+
+static av_cold int ff_vvdec_decode_frame(AVCodecContext *avctx, AVFrame *data,
+                                         int *got_frame, AVPacket *avpkt)
+{
+    VVdeCContext *s = avctx->priv_data;
+    AVFrame *avframe = data;
+
+    int ret = 0;
+    vvdecFrame *frame = NULL;
+
+    if (avframe) {
+        if (!avpkt->size && !s->bFlush)
+            s->bFlush = true;
+
+        if (s->bFlush)
+            ret = vvdec_flush(s->vvdecDec, &frame);
+        else {
+            vvdecAccessUnit accessUnit;
+            vvdec_accessUnit_default(&accessUnit);
+            accessUnit.payload = avpkt->data;
+            accessUnit.payloadSize = avpkt->size;
+            accessUnit.payloadUsedSize = avpkt->size;
+
+            accessUnit.cts = avpkt->pts;
+            accessUnit.ctsValid = true;
+            accessUnit.dts = avpkt->dts;
+            accessUnit.dtsValid = true;
+
+            ret = vvdec_decode(s->vvdecDec, &accessUnit, &frame);
+        }
+
+        if (ret < 0) {
+            if (ret == VVDEC_EOF)
+                s->bFlush = true;
+            else if (ret != VVDEC_TRY_AGAIN) {
+                av_log(avctx, AV_LOG_ERROR,
+                       "error in vvdec::decode - ret:%d - %s %s\n", ret,
+                       vvdec_get_last_error(s->vvdecDec), vvdec_get_last_additional_error( s->vvdecDec));
+                ret=AVERROR_EXTERNAL;
+                goto fail;
+            }
+        } else if (NULL != frame) {
+            const uint8_t *src_data[4] = { frame->planes[0].ptr,
+                                           frame->planes[1].ptr,
+                                           frame->planes[2].ptr, NULL };
+            const int src_linesizes[4] = { (int) frame->planes[0].stride,
+                                           (int) frame->planes[1].stride,
+                                           (int) frame->planes[2].stride, 0 };
+
+            if ((ret = ff_vvdec_set_pix_fmt(avctx, frame)) < 0) {
+                av_log(avctx, AV_LOG_ERROR,
+                       "Unsupported output colorspace (%d) / bit_depth (%d)\n",
+                       frame->colorFormat, frame->bitDepth);
+                goto fail;
+            }
+
+            if ((int) frame->width != avctx->width ||
+                (int) frame->height != avctx->height) {
+                av_log(avctx, AV_LOG_INFO, "dimension change! %dx%d -> %dx%d\n",
+                       avctx->width, avctx->height, frame->width, frame->height);
+
+                ret = ff_set_dimensions(avctx, frame->width, frame->height);
+                if (ret < 0)
+                    goto fail;
+            }
+
+            if (frame->planes[0].allocator)
+                avframe->buf[0] =
+                    av_buffer_ref((AVBufferRef *) frame->planes[0].allocator);
+            if (frame->planes[1].allocator)
+                avframe->buf[1] =
+                    av_buffer_ref((AVBufferRef *) frame->planes[1].allocator);
+            if (frame->planes[2].allocator)
+                avframe->buf[2] =
+                    av_buffer_ref((AVBufferRef *) frame->planes[2].allocator);
+
+            for (int i = 0; i < 4; i++) {
+                avframe->data[i] = (uint8_t *) src_data[i];
+                avframe->linesize[i] = src_linesizes[i];
+            }
+
+            ret = ff_decode_frame_props(avctx, avframe);
+            if (ret < 0)
+                goto fail;
+
+            if (frame->picAttributes) {
+                if (frame->picAttributes->isRefPic)
+                    avframe->flags |= AV_FRAME_FLAG_KEY;
+                else
+                    avframe->flags &= ~AV_FRAME_FLAG_KEY;
+
+                avframe->pict_type = (frame->picAttributes->sliceType !=
+                    VVDEC_SLICETYPE_UNKNOWN) ?
+                    frame->picAttributes->sliceType + 1 : AV_PICTURE_TYPE_NONE;
+            }
+
+            if (frame->ctsValid)
+                avframe->pts = frame->cts;
+
+            ret = set_side_data(avctx, avframe, frame);
+            if (ret < 0)
+                goto fail;
+
+            if (0 != vvdec_frame_unref(s->vvdecDec, frame))
+                av_log(avctx, AV_LOG_ERROR, "cannot free picture memory\n");
+
+            *got_frame = 1;
+        }
+    }
+
+    return avpkt->size;
+
+  fail:
+    if (frame) {
+        if (frame->planes[0].allocator)
+            av_buffer_unref((AVBufferRef **) &frame->planes[0].allocator);
+        if (frame->planes[1].allocator)
+            av_buffer_unref((AVBufferRef **) &frame->planes[1].allocator);
+        if (frame->planes[2].allocator)
+            av_buffer_unref((AVBufferRef **) &frame->planes[2].allocator);
+
+        vvdec_frame_unref(s->vvdecDec, frame);
+    }
+    return ret;
+}
+
+static av_cold void ff_vvdec_decode_flush(AVCodecContext *avctx)
+{
+    VVdeCContext *s = (VVdeCContext *) avctx->priv_data;
+
+    if (0 != vvdec_decoder_close(s->vvdecDec))
+        av_log(avctx, AV_LOG_ERROR, "cannot close vvdec during flush\n");
+
+    s->vvdecDec = vvdec_decoder_open_with_allocator(&s->vvdecParams,
+                                                    ff_vvdec_buffer_allocator,
+                                                    ff_vvdec_buffer_unref);
+    if (!s->vvdecDec)
+        av_log(avctx, AV_LOG_ERROR, "cannot reinit vvdec during flush\n");
+
+    vvdec_set_logging_callback(s->vvdecDec, ff_vvdec_log_callback);
+
+    s->bFlush = false;
+}
+
+static const enum AVPixelFormat pix_fmts_vvdec[] = {
+    AV_PIX_FMT_GRAY8,
+    AV_PIX_FMT_GRAY10,
+    AV_PIX_FMT_YUV420P,
+    AV_PIX_FMT_YUV422P,
+    AV_PIX_FMT_YUV444P,
+    AV_PIX_FMT_YUV420P10LE,
+    AV_PIX_FMT_YUV422P10LE,
+    AV_PIX_FMT_YUV444P10LE,
+    AV_PIX_FMT_NONE
+};
+
+static const AVClass class_libvvdec = {
+    .class_name = "libvvdec-vvc decoder",
+    .item_name  = av_default_item_name,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+FFCodec ff_libvvdec_decoder = {
+    .p.name         = "libvvdec",
+    CODEC_LONG_NAME("H.266 / VVC Decoder VVdeC"),
+    .p.type         = AVMEDIA_TYPE_VIDEO,
+    .p.id           = AV_CODEC_ID_VVC,
+    .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS,
+    .p.profiles     = NULL_IF_CONFIG_SMALL(ff_vvc_profiles),
+    .p.priv_class   = &class_libvvdec,
+    .p.wrapper_name = "libvvdec",
+    .priv_data_size = sizeof(VVdeCContext),
+    .p.pix_fmts     = pix_fmts_vvdec,
+    .init           = ff_vvdec_decode_init,
+    FF_CODEC_DECODE_CB(ff_vvdec_decode_frame),
+    .close          = ff_vvdec_decode_close,
+    .flush          = ff_vvdec_decode_flush,
+    .bsfs           = "vvc_mp4toannexb",
+    .caps_internal  = FF_CODEC_CAP_AUTO_THREADS,
+};