diff options
author | Timo Rothenpieler <timo@rothenpieler.org> | 2023-06-16 21:35:45 +0200 |
---|---|---|
committer | Timo Rothenpieler <timo@rothenpieler.org> | 2023-06-16 22:45:24 +0200 |
commit | 868af7d7a406b13648c7dff9fc8c4a7221e86849 (patch) | |
tree | 19addd547c0425119fc44374c40e632791fc16b4 | |
parent | d341895a087b32efd4472fbbf6eb342eaf220bd2 (diff) | |
download | ffmpeg-868af7d7a406b13648c7dff9fc8c4a7221e86849.tar.gz |
avcodec/nvenc: handle frame durations and AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE
-rw-r--r-- | libavcodec/nvenc.c | 81 | ||||
-rw-r--r-- | libavcodec/nvenc.h | 17 | ||||
-rw-r--r-- | libavcodec/nvenc_av1.c | 3 | ||||
-rw-r--r-- | libavcodec/nvenc_h264.c | 3 | ||||
-rw-r--r-- | libavcodec/nvenc_hevc.c | 3 |
5 files changed, 104 insertions, 3 deletions
diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c index aa9cd81485..15dd819a58 100644 --- a/libavcodec/nvenc.c +++ b/libavcodec/nvenc.c @@ -962,6 +962,10 @@ static av_cold int nvenc_recalc_surfaces(AVCodecContext *avctx) ctx->nb_surfaces = FFMAX(1, FFMIN(MAX_REGISTERED_FRAMES, ctx->nb_surfaces)); ctx->async_depth = FFMIN(ctx->async_depth, ctx->nb_surfaces - 1); + // Output in the worst case will only start when the surface buffer is completely full. + // Hence we need to keep at least the max amount of surfaces plus the max reorder delay around. + ctx->frame_data_array_nb = ctx->nb_surfaces + ctx->encode_config.frameIntervalP - 1; + return 0; } @@ -1750,6 +1754,10 @@ static av_cold int nvenc_setup_surfaces(AVCodecContext *avctx) if (!ctx->surfaces) return AVERROR(ENOMEM); + ctx->frame_data_array = av_calloc(ctx->frame_data_array_nb, sizeof(*ctx->frame_data_array)); + if (!ctx->frame_data_array) + return AVERROR(ENOMEM); + ctx->timestamp_list = av_fifo_alloc2(ctx->nb_surfaces, sizeof(int64_t), 0); if (!ctx->timestamp_list) return AVERROR(ENOMEM); @@ -1840,6 +1848,12 @@ av_cold int ff_nvenc_encode_close(AVCodecContext *avctx) av_fifo_freep2(&ctx->output_surface_queue); av_fifo_freep2(&ctx->unused_surface_queue); + if (ctx->frame_data_array) { + for (i = 0; i < ctx->nb_surfaces; i++) + av_buffer_unref(&ctx->frame_data_array[i].frame_opaque_ref); + av_freep(&ctx->frame_data_array); + } + if (ctx->surfaces && (avctx->pix_fmt == AV_PIX_FMT_CUDA || avctx->pix_fmt == AV_PIX_FMT_D3D11)) { for (i = 0; i < ctx->nb_registered_frames; i++) { if (ctx->registered_frames[i].mapped) @@ -2211,6 +2225,65 @@ static int nvenc_set_timestamp(AVCodecContext *avctx, return 0; } +static int nvenc_store_frame_data(AVCodecContext *avctx, NV_ENC_PIC_PARAMS *pic_params, const AVFrame *frame) +{ + NvencContext *ctx = avctx->priv_data; + int res = 0; + + int idx = ctx->frame_data_array_pos; + NvencFrameData *frame_data = &ctx->frame_data_array[idx]; + + // in case the encoder got reconfigured, there might be leftovers + av_buffer_unref(&frame_data->frame_opaque_ref); + + if (frame && frame->opaque_ref && avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + frame_data->frame_opaque_ref = av_buffer_ref(frame->opaque_ref); + if (!frame_data->frame_opaque_ref) + return AVERROR(ENOMEM); + } + + frame_data->duration = frame->duration; + frame_data->frame_opaque = frame->opaque; + +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + frame_data->reordered_opaque = frame->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + ctx->frame_data_array_pos = (ctx->frame_data_array_pos + 1) % ctx->frame_data_array_nb; + pic_params->inputDuration = idx; + + return res; +} + +static int nvenc_retrieve_frame_data(AVCodecContext *avctx, NV_ENC_LOCK_BITSTREAM *lock_params, AVPacket *pkt) +{ + NvencContext *ctx = avctx->priv_data; + int res = 0; + + int idx = lock_params->outputDuration; + NvencFrameData *frame_data = &ctx->frame_data_array[idx]; + + pkt->duration = frame_data->duration; + +#if FF_API_REORDERED_OPAQUE +FF_DISABLE_DEPRECATION_WARNINGS + avctx->reordered_opaque = frame_data->reordered_opaque; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = frame_data->frame_opaque; + pkt->opaque_ref = frame_data->frame_opaque_ref; + frame_data->frame_opaque_ref = NULL; + } + + av_buffer_unref(&frame_data->frame_opaque_ref); + + return res; +} + static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSurface *tmpoutsurf) { NvencContext *ctx = avctx->priv_data; @@ -2297,6 +2370,10 @@ static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSur if (res < 0) goto error2; + res = nvenc_retrieve_frame_data(avctx, &lock_params, pkt); + if (res < 0) + goto error2; + return 0; error: @@ -2592,6 +2669,10 @@ static int nvenc_send_frame(AVCodecContext *avctx, const AVFrame *frame) sei_count = res; } + res = nvenc_store_frame_data(avctx, &pic_params, frame); + if (res < 0) + return res; + nvenc_codec_specific_pic_params(avctx, &pic_params, ctx->sei_data, sei_count); } else { pic_params.encodePicFlags = NV_ENC_PIC_FLAG_EOS; diff --git a/libavcodec/nvenc.h b/libavcodec/nvenc.h index 05a7ac48b1..6cedd5bc27 100644 --- a/libavcodec/nvenc.h +++ b/libavcodec/nvenc.h @@ -31,6 +31,7 @@ typedef void ID3D11Device; #include <ffnvcodec/nvEncodeAPI.h> #include "compat/cuda/dynlink_loader.h" +#include "libavutil/buffer.h" #include "libavutil/fifo.h" #include "libavutil/opt.h" #include "hwconfig.h" @@ -90,6 +91,18 @@ typedef struct NvencSurface NV_ENC_BUFFER_FORMAT format; } NvencSurface; +typedef struct NvencFrameData +{ + int64_t duration; + +#if FF_API_REORDERED_OPAQUE + int64_t reordered_opaque; +#endif + + void *frame_opaque; + AVBufferRef *frame_opaque_ref; +} NvencFrameData; + typedef struct NvencDynLoadFunctions { CudaFunctions *cuda_dl; @@ -168,6 +181,10 @@ typedef struct NvencContext int nb_surfaces; NvencSurface *surfaces; + NvencFrameData *frame_data_array; + int frame_data_array_nb; + int frame_data_array_pos; + AVFifo *unused_surface_queue; AVFifo *output_surface_queue; AVFifo *output_surface_ready_queue; diff --git a/libavcodec/nvenc_av1.c b/libavcodec/nvenc_av1.c index 2ed99d948b..2b349c7b61 100644 --- a/libavcodec/nvenc_av1.c +++ b/libavcodec/nvenc_av1.c @@ -181,7 +181,8 @@ const FFCodec ff_av1_nvenc_encoder = { .defaults = defaults, .p.pix_fmts = ff_nvenc_pix_fmts, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1, + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .p.wrapper_name = "nvenc", diff --git a/libavcodec/nvenc_h264.c b/libavcodec/nvenc_h264.c index a69358b03b..5dc2961c3b 100644 --- a/libavcodec/nvenc_h264.c +++ b/libavcodec/nvenc_h264.c @@ -232,7 +232,8 @@ const FFCodec ff_h264_nvenc_encoder = { .p.priv_class = &h264_nvenc_class, .defaults = defaults, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1, + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .p.pix_fmts = ff_nvenc_pix_fmts, diff --git a/libavcodec/nvenc_hevc.c b/libavcodec/nvenc_hevc.c index 5ad423444a..1362a927c8 100644 --- a/libavcodec/nvenc_hevc.c +++ b/libavcodec/nvenc_hevc.c @@ -214,7 +214,8 @@ const FFCodec ff_hevc_nvenc_encoder = { .defaults = defaults, .p.pix_fmts = ff_nvenc_pix_fmts, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1, + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .p.wrapper_name = "nvenc", |