diff mbox series

[FFmpeg-devel] lavc/libx264: support AV_CODEC_CAP_ENCODER_RECON_FRAME

Message ID 20220718071225.4446-1-anton@khirnov.net
State New
Headers show
Series [FFmpeg-devel] lavc/libx264: support AV_CODEC_CAP_ENCODER_RECON_FRAME | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Anton Khirnov July 18, 2022, 7:12 a.m. UTC
---
 libavcodec/libx264.c | 52 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 51 insertions(+), 1 deletion(-)

Comments

James Almer July 18, 2022, 12:23 p.m. UTC | #1
On 7/18/2022 4:12 AM, Anton Khirnov wrote:
> ---
>   libavcodec/libx264.c | 52 +++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 51 insertions(+), 1 deletion(-)
> 
> diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
> index 98ec030865..5e360682e6 100644
> --- a/libavcodec/libx264.c
> +++ b/libavcodec/libx264.c
> @@ -311,6 +311,25 @@ static void free_picture(AVCodecContext *ctx)
>       pic->extra_sei.num_payloads = 0;
>   }
>   
> +static enum AVPixelFormat csp_to_pixfmt(int csp)
> +{
> +    switch (csp) {
> +#ifdef X264_CSP_I400
> +    case X264_CSP_I400:                         return AV_PIX_FMT_GRAY8;
> +    case X264_CSP_I400 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_GRAY10;
> +#endif
> +    case X264_CSP_I420:                         return AV_PIX_FMT_YUV420P;
> +    case X264_CSP_I420 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV420P10;
> +    case X264_CSP_I422:                         return AV_PIX_FMT_YUV422P;
> +    case X264_CSP_I422 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV422P10;
> +    case X264_CSP_I444:                         return AV_PIX_FMT_YUV444P;
> +    case X264_CSP_I444 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV444P10;

We're still supporting old x264 versions, so you should add some 
considerations for 9 bit pixfmts. On X264_BUILD < 153, just check the 
value of x264_bit_depth.

> +    case X264_CSP_NV12:                         return AV_PIX_FMT_NV12;
> +    case X264_CSP_NV16:                         return AV_PIX_FMT_NV16;

Missing X264_CSP_NV21 (wrapped like X264_CSP_I400).

That aside, LGTM.

> +    };
> +    return AV_PIX_FMT_NONE;
> +}
> +
>   static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame,
>                         int *got_packet)
>   {
> @@ -496,6 +515,33 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame,
>           if (x264_encoder_encode(x4->enc, &nal, &nnal, frame? &x4->pic: NULL, &pic_out) < 0)
>               return AVERROR_EXTERNAL;
>   
> +        if (nnal && (ctx->flags & AV_CODEC_FLAG_RECON_FRAME)) {
> +            AVCodecInternal *avci = ctx->internal;
> +
> +            av_frame_unref(avci->recon_frame);
> +
> +            avci->recon_frame->format = csp_to_pixfmt(pic_out.img.i_csp);
> +            if (avci->recon_frame->format == AV_PIX_FMT_NONE) {
> +                av_log(ctx, AV_LOG_ERROR,
> +                       "Unhandled reconstructed frame colorspace: %d\n",
> +                       pic_out.img.i_csp);
> +                return AVERROR(ENOSYS);
> +            }
> +
> +            avci->recon_frame->width  = ctx->width;
> +            avci->recon_frame->height = ctx->height;
> +            for (int i = 0; i < pic_out.img.i_plane; i++) {
> +                avci->recon_frame->data[i]     = pic_out.img.plane[i];
> +                avci->recon_frame->linesize[i] = pic_out.img.i_stride[i];
> +            }
> +
> +            ret = av_frame_make_writable(avci->recon_frame);
> +            if (ret < 0) {
> +                av_frame_unref(avci->recon_frame);
> +                return ret;
> +            }
> +        }
> +
>           ret = encode_nals(ctx, pkt, nal, nnal);
>           if (ret < 0)
>               return ret;
> @@ -928,6 +974,9 @@ static av_cold int X264_init(AVCodecContext *avctx)
>       if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
>           x4->params.b_repeat_headers = 0;
>   
> +    if (avctx->flags & AV_CODEC_FLAG_RECON_FRAME)
> +        x4->params.b_full_recon = 1;
> +
>       if(x4->x264opts){
>           const char *p= x4->x264opts;
>           while(p){
> @@ -1223,7 +1272,8 @@ FFCodec ff_libx264_encoder = {
>       .p.id             = AV_CODEC_ID_H264,
>       .p.capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
>                           AV_CODEC_CAP_OTHER_THREADS |
> -                        AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
> +                        AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE |
> +                        AV_CODEC_CAP_ENCODER_RECON_FRAME,
>       .p.priv_class     = &x264_class,
>       .p.wrapper_name   = "libx264",
>       .priv_data_size   = sizeof(X264Context),
James Almer July 18, 2022, 5:41 p.m. UTC | #2
On 7/18/2022 4:12 AM, Anton Khirnov wrote:
> ---
>   libavcodec/libx264.c | 52 +++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 51 insertions(+), 1 deletion(-)
> 
> diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
> index 98ec030865..5e360682e6 100644
> --- a/libavcodec/libx264.c
> +++ b/libavcodec/libx264.c
> @@ -311,6 +311,25 @@ static void free_picture(AVCodecContext *ctx)
>       pic->extra_sei.num_payloads = 0;
>   }
>   
> +static enum AVPixelFormat csp_to_pixfmt(int csp)
> +{
> +    switch (csp) {
> +#ifdef X264_CSP_I400
> +    case X264_CSP_I400:                         return AV_PIX_FMT_GRAY8;
> +    case X264_CSP_I400 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_GRAY10;
> +#endif
> +    case X264_CSP_I420:                         return AV_PIX_FMT_YUV420P;
> +    case X264_CSP_I420 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV420P10;
> +    case X264_CSP_I422:                         return AV_PIX_FMT_YUV422P;
> +    case X264_CSP_I422 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV422P10;
> +    case X264_CSP_I444:                         return AV_PIX_FMT_YUV444P;
> +    case X264_CSP_I444 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV444P10;
> +    case X264_CSP_NV12:                         return AV_PIX_FMT_NV12;
> +    case X264_CSP_NV16:                         return AV_PIX_FMT_NV16;
> +    };
> +    return AV_PIX_FMT_NONE;
> +}
> +
>   static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame,
>                         int *got_packet)
>   {
> @@ -496,6 +515,33 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame,
>           if (x264_encoder_encode(x4->enc, &nal, &nnal, frame? &x4->pic: NULL, &pic_out) < 0)
>               return AVERROR_EXTERNAL;
>   
> +        if (nnal && (ctx->flags & AV_CODEC_FLAG_RECON_FRAME)) {
> +            AVCodecInternal *avci = ctx->internal;
> +
> +            av_frame_unref(avci->recon_frame);
> +
> +            avci->recon_frame->format = csp_to_pixfmt(pic_out.img.i_csp);
> +            if (avci->recon_frame->format == AV_PIX_FMT_NONE) {
> +                av_log(ctx, AV_LOG_ERROR,
> +                       "Unhandled reconstructed frame colorspace: %d\n",
> +                       pic_out.img.i_csp);
> +                return AVERROR(ENOSYS);
> +            }
> +
> +            avci->recon_frame->width  = ctx->width;
> +            avci->recon_frame->height = ctx->height;
> +            for (int i = 0; i < pic_out.img.i_plane; i++) {
> +                avci->recon_frame->data[i]     = pic_out.img.plane[i];
> +                avci->recon_frame->linesize[i] = pic_out.img.i_stride[i];
> +            }
> +
> +            ret = av_frame_make_writable(avci->recon_frame);

Why not ff_encode_alloc_frame() for this? Can 
csp_to_pixfmt(pic_out.img.i_csp) be different than ctx->pix_fmt?


> +            if (ret < 0) {
> +                av_frame_unref(avci->recon_frame);
> +                return ret;
> +            }
> +        }
> +
>           ret = encode_nals(ctx, pkt, nal, nnal);
>           if (ret < 0)
>               return ret;
> @@ -928,6 +974,9 @@ static av_cold int X264_init(AVCodecContext *avctx)
>       if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
>           x4->params.b_repeat_headers = 0;
>   
> +    if (avctx->flags & AV_CODEC_FLAG_RECON_FRAME)
> +        x4->params.b_full_recon = 1;
> +
>       if(x4->x264opts){
>           const char *p= x4->x264opts;
>           while(p){
> @@ -1223,7 +1272,8 @@ FFCodec ff_libx264_encoder = {
>       .p.id             = AV_CODEC_ID_H264,
>       .p.capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
>                           AV_CODEC_CAP_OTHER_THREADS |
> -                        AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
> +                        AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE |
> +                        AV_CODEC_CAP_ENCODER_RECON_FRAME,
>       .p.priv_class     = &x264_class,
>       .p.wrapper_name   = "libx264",
>       .priv_data_size   = sizeof(X264Context),
Anton Khirnov July 18, 2022, 6:07 p.m. UTC | #3
Quoting James Almer (2022-07-18 19:41:27)
> Why not ff_encode_alloc_frame() for this? Can 
> csp_to_pixfmt(pic_out.img.i_csp) be different than ctx->pix_fmt?

Yes, the reconstructed frame is in x264's internal format, which will
typically be some flavor of nv12, even if the input format is fully
planar.
Anton Khirnov July 18, 2022, 6:15 p.m. UTC | #4
Quoting James Almer (2022-07-18 14:23:58)
> On 7/18/2022 4:12 AM, Anton Khirnov wrote:
> > ---
> >   libavcodec/libx264.c | 52 +++++++++++++++++++++++++++++++++++++++++++-
> >   1 file changed, 51 insertions(+), 1 deletion(-)
> > 
> > diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
> > index 98ec030865..5e360682e6 100644
> > --- a/libavcodec/libx264.c
> > +++ b/libavcodec/libx264.c
> > @@ -311,6 +311,25 @@ static void free_picture(AVCodecContext *ctx)
> >       pic->extra_sei.num_payloads = 0;
> >   }
> >   
> > +static enum AVPixelFormat csp_to_pixfmt(int csp)
> > +{
> > +    switch (csp) {
> > +#ifdef X264_CSP_I400
> > +    case X264_CSP_I400:                         return AV_PIX_FMT_GRAY8;
> > +    case X264_CSP_I400 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_GRAY10;
> > +#endif
> > +    case X264_CSP_I420:                         return AV_PIX_FMT_YUV420P;
> > +    case X264_CSP_I420 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV420P10;
> > +    case X264_CSP_I422:                         return AV_PIX_FMT_YUV422P;
> > +    case X264_CSP_I422 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV422P10;
> > +    case X264_CSP_I444:                         return AV_PIX_FMT_YUV444P;
> > +    case X264_CSP_I444 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV444P10;
> 
> We're still supporting old x264 versions, so you should add some 
> considerations for 9 bit pixfmts. On X264_BUILD < 153, just check the 
> value of x264_bit_depth.

Is there much point in continuing to support 5.5 year old x264? We could
drop a lot of ugliness, including the abominable X264_init_static().
James Almer July 18, 2022, 6:18 p.m. UTC | #5
On 7/18/2022 3:15 PM, Anton Khirnov wrote:
> Quoting James Almer (2022-07-18 14:23:58)
>> On 7/18/2022 4:12 AM, Anton Khirnov wrote:
>>> ---
>>>    libavcodec/libx264.c | 52 +++++++++++++++++++++++++++++++++++++++++++-
>>>    1 file changed, 51 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
>>> index 98ec030865..5e360682e6 100644
>>> --- a/libavcodec/libx264.c
>>> +++ b/libavcodec/libx264.c
>>> @@ -311,6 +311,25 @@ static void free_picture(AVCodecContext *ctx)
>>>        pic->extra_sei.num_payloads = 0;
>>>    }
>>>    
>>> +static enum AVPixelFormat csp_to_pixfmt(int csp)
>>> +{
>>> +    switch (csp) {
>>> +#ifdef X264_CSP_I400
>>> +    case X264_CSP_I400:                         return AV_PIX_FMT_GRAY8;
>>> +    case X264_CSP_I400 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_GRAY10;
>>> +#endif
>>> +    case X264_CSP_I420:                         return AV_PIX_FMT_YUV420P;
>>> +    case X264_CSP_I420 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV420P10;
>>> +    case X264_CSP_I422:                         return AV_PIX_FMT_YUV422P;
>>> +    case X264_CSP_I422 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV422P10;
>>> +    case X264_CSP_I444:                         return AV_PIX_FMT_YUV444P;
>>> +    case X264_CSP_I444 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV444P10;
>>
>> We're still supporting old x264 versions, so you should add some
>> considerations for 9 bit pixfmts. On X264_BUILD < 153, just check the
>> value of x264_bit_depth.
> 
> Is there much point in continuing to support 5.5 year old x264? We could
> drop a lot of ugliness, including the abominable X264_init_static().

Afaik we tend to support what's shipped with not yet EOL'd big distros 
like Debian and Ubuntu. I assume Michael's Ubuntu setup is one of those, 
and it has a libx264 without X264_CSP_I400, for example.
Anton Khirnov July 18, 2022, 6:29 p.m. UTC | #6
Quoting James Almer (2022-07-18 20:18:16)
> 
> 
> On 7/18/2022 3:15 PM, Anton Khirnov wrote:
> > Quoting James Almer (2022-07-18 14:23:58)
> >> On 7/18/2022 4:12 AM, Anton Khirnov wrote:
> >>> ---
> >>>    libavcodec/libx264.c | 52 +++++++++++++++++++++++++++++++++++++++++++-
> >>>    1 file changed, 51 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
> >>> index 98ec030865..5e360682e6 100644
> >>> --- a/libavcodec/libx264.c
> >>> +++ b/libavcodec/libx264.c
> >>> @@ -311,6 +311,25 @@ static void free_picture(AVCodecContext *ctx)
> >>>        pic->extra_sei.num_payloads = 0;
> >>>    }
> >>>    
> >>> +static enum AVPixelFormat csp_to_pixfmt(int csp)
> >>> +{
> >>> +    switch (csp) {
> >>> +#ifdef X264_CSP_I400
> >>> +    case X264_CSP_I400:                         return AV_PIX_FMT_GRAY8;
> >>> +    case X264_CSP_I400 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_GRAY10;
> >>> +#endif
> >>> +    case X264_CSP_I420:                         return AV_PIX_FMT_YUV420P;
> >>> +    case X264_CSP_I420 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV420P10;
> >>> +    case X264_CSP_I422:                         return AV_PIX_FMT_YUV422P;
> >>> +    case X264_CSP_I422 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV422P10;
> >>> +    case X264_CSP_I444:                         return AV_PIX_FMT_YUV444P;
> >>> +    case X264_CSP_I444 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV444P10;
> >>
> >> We're still supporting old x264 versions, so you should add some
> >> considerations for 9 bit pixfmts. On X264_BUILD < 153, just check the
> >> value of x264_bit_depth.
> > 
> > Is there much point in continuing to support 5.5 year old x264? We could
> > drop a lot of ugliness, including the abominable X264_init_static().
> 
> Afaik we tend to support what's shipped with not yet EOL'd big distros 
> like Debian and Ubuntu. I assume Michael's Ubuntu setup is one of those, 
> and it has a libx264 without X264_CSP_I400, for example.

Debian oldstable has libx264 155 at least.
Michael Niedermayer July 19, 2022, 11:44 a.m. UTC | #7
On Mon, Jul 18, 2022 at 08:29:56PM +0200, Anton Khirnov wrote:
> Quoting James Almer (2022-07-18 20:18:16)
> > 
> > 
> > On 7/18/2022 3:15 PM, Anton Khirnov wrote:
> > > Quoting James Almer (2022-07-18 14:23:58)
> > >> On 7/18/2022 4:12 AM, Anton Khirnov wrote:
> > >>> ---
> > >>>    libavcodec/libx264.c | 52 +++++++++++++++++++++++++++++++++++++++++++-
> > >>>    1 file changed, 51 insertions(+), 1 deletion(-)
> > >>>
> > >>> diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
> > >>> index 98ec030865..5e360682e6 100644
> > >>> --- a/libavcodec/libx264.c
> > >>> +++ b/libavcodec/libx264.c
> > >>> @@ -311,6 +311,25 @@ static void free_picture(AVCodecContext *ctx)
> > >>>        pic->extra_sei.num_payloads = 0;
> > >>>    }
> > >>>    
> > >>> +static enum AVPixelFormat csp_to_pixfmt(int csp)
> > >>> +{
> > >>> +    switch (csp) {
> > >>> +#ifdef X264_CSP_I400
> > >>> +    case X264_CSP_I400:                         return AV_PIX_FMT_GRAY8;
> > >>> +    case X264_CSP_I400 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_GRAY10;
> > >>> +#endif
> > >>> +    case X264_CSP_I420:                         return AV_PIX_FMT_YUV420P;
> > >>> +    case X264_CSP_I420 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV420P10;
> > >>> +    case X264_CSP_I422:                         return AV_PIX_FMT_YUV422P;
> > >>> +    case X264_CSP_I422 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV422P10;
> > >>> +    case X264_CSP_I444:                         return AV_PIX_FMT_YUV444P;
> > >>> +    case X264_CSP_I444 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV444P10;
> > >>
> > >> We're still supporting old x264 versions, so you should add some
> > >> considerations for 9 bit pixfmts. On X264_BUILD < 153, just check the
> > >> value of x264_bit_depth.
> > > 
> > > Is there much point in continuing to support 5.5 year old x264? We could
> > > drop a lot of ugliness, including the abominable X264_init_static().
> > 
> > Afaik we tend to support what's shipped with not yet EOL'd big distros 
> > like Debian and Ubuntu. I assume Michael's Ubuntu setup is one of those, 
> > and it has a libx264 without X264_CSP_I400, for example.
> 
> Debian oldstable has libx264 155 at least.

Ubuntu 18.04LTS has 152
https://packages.ubuntu.com/search?keywords=x264&searchon=names&suite=bionic&section=all

[...]
Anton Khirnov July 19, 2022, 12:43 p.m. UTC | #8
Quoting James Almer (2022-07-18 14:23:58)
> On 7/18/2022 4:12 AM, Anton Khirnov wrote:
> > ---
> >   libavcodec/libx264.c | 52 +++++++++++++++++++++++++++++++++++++++++++-
> >   1 file changed, 51 insertions(+), 1 deletion(-)
> > 
> > diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
> > index 98ec030865..5e360682e6 100644
> > --- a/libavcodec/libx264.c
> > +++ b/libavcodec/libx264.c
> > @@ -311,6 +311,25 @@ static void free_picture(AVCodecContext *ctx)
> >       pic->extra_sei.num_payloads = 0;
> >   }
> >   
> > +static enum AVPixelFormat csp_to_pixfmt(int csp)
> > +{
> > +    switch (csp) {
> > +#ifdef X264_CSP_I400
> > +    case X264_CSP_I400:                         return AV_PIX_FMT_GRAY8;
> > +    case X264_CSP_I400 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_GRAY10;
> > +#endif
> > +    case X264_CSP_I420:                         return AV_PIX_FMT_YUV420P;
> > +    case X264_CSP_I420 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV420P10;
> > +    case X264_CSP_I422:                         return AV_PIX_FMT_YUV422P;
> > +    case X264_CSP_I422 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV422P10;
> > +    case X264_CSP_I444:                         return AV_PIX_FMT_YUV444P;
> > +    case X264_CSP_I444 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV444P10;
> 
> We're still supporting old x264 versions, so you should add some 
> considerations for 9 bit pixfmts. On X264_BUILD < 153, just check the 
> value of x264_bit_depth.

This will actually build even with pre-153 versions, except subsampled
(420/422) 10-bit reconstructed frames won't actually work with any
version because we lack a pixel format for them (higher-bit nv12
analogue).
diff mbox series

Patch

diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
index 98ec030865..5e360682e6 100644
--- a/libavcodec/libx264.c
+++ b/libavcodec/libx264.c
@@ -311,6 +311,25 @@  static void free_picture(AVCodecContext *ctx)
     pic->extra_sei.num_payloads = 0;
 }
 
+static enum AVPixelFormat csp_to_pixfmt(int csp)
+{
+    switch (csp) {
+#ifdef X264_CSP_I400
+    case X264_CSP_I400:                         return AV_PIX_FMT_GRAY8;
+    case X264_CSP_I400 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_GRAY10;
+#endif
+    case X264_CSP_I420:                         return AV_PIX_FMT_YUV420P;
+    case X264_CSP_I420 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV420P10;
+    case X264_CSP_I422:                         return AV_PIX_FMT_YUV422P;
+    case X264_CSP_I422 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV422P10;
+    case X264_CSP_I444:                         return AV_PIX_FMT_YUV444P;
+    case X264_CSP_I444 | X264_CSP_HIGH_DEPTH:   return AV_PIX_FMT_YUV444P10;
+    case X264_CSP_NV12:                         return AV_PIX_FMT_NV12;
+    case X264_CSP_NV16:                         return AV_PIX_FMT_NV16;
+    };
+    return AV_PIX_FMT_NONE;
+}
+
 static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame,
                       int *got_packet)
 {
@@ -496,6 +515,33 @@  static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame,
         if (x264_encoder_encode(x4->enc, &nal, &nnal, frame? &x4->pic: NULL, &pic_out) < 0)
             return AVERROR_EXTERNAL;
 
+        if (nnal && (ctx->flags & AV_CODEC_FLAG_RECON_FRAME)) {
+            AVCodecInternal *avci = ctx->internal;
+
+            av_frame_unref(avci->recon_frame);
+
+            avci->recon_frame->format = csp_to_pixfmt(pic_out.img.i_csp);
+            if (avci->recon_frame->format == AV_PIX_FMT_NONE) {
+                av_log(ctx, AV_LOG_ERROR,
+                       "Unhandled reconstructed frame colorspace: %d\n",
+                       pic_out.img.i_csp);
+                return AVERROR(ENOSYS);
+            }
+
+            avci->recon_frame->width  = ctx->width;
+            avci->recon_frame->height = ctx->height;
+            for (int i = 0; i < pic_out.img.i_plane; i++) {
+                avci->recon_frame->data[i]     = pic_out.img.plane[i];
+                avci->recon_frame->linesize[i] = pic_out.img.i_stride[i];
+            }
+
+            ret = av_frame_make_writable(avci->recon_frame);
+            if (ret < 0) {
+                av_frame_unref(avci->recon_frame);
+                return ret;
+            }
+        }
+
         ret = encode_nals(ctx, pkt, nal, nnal);
         if (ret < 0)
             return ret;
@@ -928,6 +974,9 @@  static av_cold int X264_init(AVCodecContext *avctx)
     if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
         x4->params.b_repeat_headers = 0;
 
+    if (avctx->flags & AV_CODEC_FLAG_RECON_FRAME)
+        x4->params.b_full_recon = 1;
+
     if(x4->x264opts){
         const char *p= x4->x264opts;
         while(p){
@@ -1223,7 +1272,8 @@  FFCodec ff_libx264_encoder = {
     .p.id             = AV_CODEC_ID_H264,
     .p.capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
                         AV_CODEC_CAP_OTHER_THREADS |
-                        AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
+                        AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE |
+                        AV_CODEC_CAP_ENCODER_RECON_FRAME,
     .p.priv_class     = &x264_class,
     .p.wrapper_name   = "libx264",
     .priv_data_size   = sizeof(X264Context),