diff mbox series

[FFmpeg-devel,v2] lavc/libx264: support AV_CODEC_CAP_ENCODER_RECON_FRAME

Message ID 20220719124711.25500-1-anton@khirnov.net
State New
Headers show
Series [FFmpeg-devel,v2] 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 19, 2022, 12:47 p.m. UTC
---
Now tested with x264 118, the oldest supported.
---
 libavcodec/libx264.c | 57 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)

Comments

James Almer July 19, 2022, 12:51 p.m. UTC | #1
On 7/19/2022 9:47 AM, Anton Khirnov wrote:
> ---
> Now tested with x264 118, the oldest supported.
> ---
>   libavcodec/libx264.c | 57 +++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 56 insertions(+), 1 deletion(-)
> 
> diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
> index e8c1fb2106..04e8147276 100644
> --- a/libavcodec/libx264.c
> +++ b/libavcodec/libx264.c
> @@ -311,6 +311,28 @@ 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;
> +#ifdef X264_CSP_NV21
> +    case X264_CSP_NV21:                         return AV_PIX_FMT_NV21;
> +#endif
> +    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 +518,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 +977,11 @@ static av_cold int X264_init(AVCodecContext *avctx)
>       if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
>           x4->params.b_repeat_headers = 0;
>   
> +#if X264_BUILD >= 122
> +    if (avctx->flags & AV_CODEC_FLAG_RECON_FRAME)
> +        x4->params.b_full_recon = 1;

If this is only >= 122, what will pic_out above contain in older versions?

> +#endif
> +
>       if(x4->x264opts){
>           const char *p= x4->x264opts;
>           while(p){
> @@ -1223,7 +1277,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 19, 2022, 12:57 p.m. UTC | #2
Quoting James Almer (2022-07-19 14:51:13)
> If this is only >= 122, what will pic_out above contain in older versions?

IIUC it won't do deblocking, so the reconstructed frame won't be exactly
as decoded.
James Almer July 19, 2022, 6:13 p.m. UTC | #3
On 7/19/2022 9:57 AM, Anton Khirnov wrote:
> Quoting James Almer (2022-07-19 14:51:13)
>> If this is only >= 122, what will pic_out above contain in older versions?
> 
> IIUC it won't do deblocking, so the reconstructed frame won't be exactly
> as decoded.

That kinda defeats the purpose, and is also inconsistent. Maybe we can 
signal somehow at the API level that the reconstructed frame is not 
fully done? Or making the kind of reconstruction the user requests 
explicit, like having the recon_frame AVOption be an enum in 
AVCodecContext with defined values like full_recon (hard requirement), 
partial_recon (soft requirement, may still return full recon if that's 
all the encoder can generate), etc, instead of simply a flag for 
avctx->flags?

Or just disabling it for libx264 this old so output/behavior is 
consistent across all encoders regardless of version and the API remains 
as simple as possible.
diff mbox series

Patch

diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
index e8c1fb2106..04e8147276 100644
--- a/libavcodec/libx264.c
+++ b/libavcodec/libx264.c
@@ -311,6 +311,28 @@  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;
+#ifdef X264_CSP_NV21
+    case X264_CSP_NV21:                         return AV_PIX_FMT_NV21;
+#endif
+    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 +518,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 +977,11 @@  static av_cold int X264_init(AVCodecContext *avctx)
     if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
         x4->params.b_repeat_headers = 0;
 
+#if X264_BUILD >= 122
+    if (avctx->flags & AV_CODEC_FLAG_RECON_FRAME)
+        x4->params.b_full_recon = 1;
+#endif
+
     if(x4->x264opts){
         const char *p= x4->x264opts;
         while(p){
@@ -1223,7 +1277,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),