avcodec/nvenc: handle frame durations and AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE

This commit is contained in:
Timo Rothenpieler 2023-06-16 21:35:45 +02:00
parent 6c418ae25e
commit 16fdb48e0d
5 changed files with 104 additions and 3 deletions

View File

@ -971,6 +971,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;
}
@ -1767,6 +1771,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);
@ -1857,6 +1865,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)
@ -2233,6 +2247,65 @@ FF_ENABLE_DEPRECATION_WARNINGS
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;
@ -2319,6 +2392,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:
@ -2614,6 +2691,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;

View File

@ -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"
@ -95,6 +96,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;
@ -173,6 +186,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;

View File

@ -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",

View File

@ -244,7 +244,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,

View File

@ -226,7 +226,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",