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 |
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 |
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),
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.
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 --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),