#include "config.h"
#include "nvenc.h"
+#include "hevc_sei.h"
#include "libavutil/hwcontext_cuda.h"
#include "libavutil/hwcontext.h"
+#include "libavutil/cuda_check.h"
#include "libavutil/imgutils.h"
#include "libavutil/avassert.h"
#include "libavutil/mem.h"
#include "libavutil/pixdesc.h"
+#include "encode.h"
#include "internal.h"
+#include "packet_internal.h"
+
+#define CHECK_CU(x) FF_CUDA_CHECK_DL(avctx, dl_fn->cuda_dl, x)
#define NVENC_CAP 0x30
#define IS_CBR(rc) (rc == NV_ENC_PARAMS_RC_CBR || \
rc == NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ || \
rc == NV_ENC_PARAMS_RC_CBR_HQ)
+#ifdef NVENC_HAVE_NEW_PRESETS
+#define IS_SDK10_PRESET(p) ((p) >= PRESET_P1 && (p) <= PRESET_P7)
+#else
+#define IS_SDK10_PRESET(p) 0
+#endif
+
const enum AVPixelFormat ff_nvenc_pix_fmts[] = {
AV_PIX_FMT_YUV420P,
AV_PIX_FMT_NV12,
AV_PIX_FMT_NONE
};
+const AVCodecHWConfigInternal *ff_nvenc_hw_configs[] = {
+ HW_CONFIG_ENCODER_FRAMES(CUDA, CUDA),
+ HW_CONFIG_ENCODER_DEVICE(NONE, CUDA),
+#if CONFIG_D3D11VA
+ HW_CONFIG_ENCODER_FRAMES(D3D11, D3D11VA),
+ HW_CONFIG_ENCODER_DEVICE(NONE, D3D11VA),
+#endif
+ NULL,
+};
+
#define IS_10BIT(pix_fmt) (pix_fmt == AV_PIX_FMT_P010 || \
pix_fmt == AV_PIX_FMT_P016 || \
pix_fmt == AV_PIX_FMT_YUV444P16)
return AVERROR_UNKNOWN;
}
-static int nvenc_print_error(void *log_ctx, NVENCSTATUS err,
+static int nvenc_print_error(AVCodecContext *avctx, NVENCSTATUS err,
const char *error_string)
{
const char *desc;
- int ret;
- ret = nvenc_map_error(err, &desc);
- av_log(log_ctx, AV_LOG_ERROR, "%s: %s (%d)\n", error_string, desc, err);
+ const char *details = "(no details)";
+ int ret = nvenc_map_error(err, &desc);
+
+#ifdef NVENC_HAVE_GETLASTERRORSTRING
+ NvencContext *ctx = avctx->priv_data;
+ NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &ctx->nvenc_dload_funcs.nvenc_funcs;
+
+ if (p_nvenc && ctx->nvencoder)
+ details = p_nvenc->nvEncGetLastErrorString(ctx->nvencoder);
+#endif
+
+ av_log(avctx, AV_LOG_ERROR, "%s: %s (%d): %s\n", error_string, desc, err, details);
+
return ret;
}
static void nvenc_print_driver_requirement(AVCodecContext *avctx, int level)
{
-#if NVENCAPI_VERSION >= 0x01000008
+#if NVENCAPI_CHECK_VERSION(10, 1)
+ const char *minver = "(unknown)";
+#elif NVENCAPI_CHECK_VERSION(10, 0)
+# if defined(_WIN32) || defined(__CYGWIN__)
+ const char *minver = "450.51";
+# else
+ const char *minver = "445.87";
+# endif
+#elif NVENCAPI_CHECK_VERSION(9, 1)
+# if defined(_WIN32) || defined(__CYGWIN__)
+ const char *minver = "436.15";
+# else
+ const char *minver = "435.21";
+# endif
+#elif NVENCAPI_CHECK_VERSION(9, 0)
+# if defined(_WIN32) || defined(__CYGWIN__)
+ const char *minver = "418.81";
+# else
+ const char *minver = "418.30";
+# endif
+#elif NVENCAPI_CHECK_VERSION(8, 2)
+# if defined(_WIN32) || defined(__CYGWIN__)
+ const char *minver = "397.93";
+# else
+ const char *minver = "396.24";
+#endif
+#elif NVENCAPI_CHECK_VERSION(8, 1)
# if defined(_WIN32) || defined(__CYGWIN__)
const char *minver = "390.77";
# else
{
NvencContext *ctx = avctx->priv_data;
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
- CUresult cu_res;
if (ctx->d3d11_device)
return 0;
- cu_res = dl_fn->cuda_dl->cuCtxPushCurrent(ctx->cu_context);
- if (cu_res != CUDA_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "cuCtxPushCurrent failed\n");
- return AVERROR_EXTERNAL;
- }
-
- return 0;
+ return CHECK_CU(dl_fn->cuda_dl->cuCtxPushCurrent(ctx->cu_context));
}
static int nvenc_pop_context(AVCodecContext *avctx)
{
NvencContext *ctx = avctx->priv_data;
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
- CUresult cu_res;
CUcontext dummy;
if (ctx->d3d11_device)
return 0;
- cu_res = dl_fn->cuda_dl->cuCtxPopCurrent(&dummy);
- if (cu_res != CUDA_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "cuCtxPopCurrent failed\n");
- return AVERROR_EXTERNAL;
- }
-
- return 0;
+ return CHECK_CU(dl_fn->cuda_dl->cuCtxPopCurrent(&dummy));
}
static av_cold int nvenc_open_session(AVCodecContext *avctx)
ret = nvenc_check_codec_support(avctx);
if (ret < 0) {
- av_log(avctx, AV_LOG_VERBOSE, "Codec not supported\n");
+ av_log(avctx, AV_LOG_WARNING, "Codec not supported\n");
return ret;
}
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_YUV444_ENCODE);
if (IS_YUV444(ctx->data_pix_fmt) && ret <= 0) {
- av_log(avctx, AV_LOG_VERBOSE, "YUV444P not supported\n");
+ av_log(avctx, AV_LOG_WARNING, "YUV444P not supported\n");
return AVERROR(ENOSYS);
}
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE);
if (ctx->preset >= PRESET_LOSSLESS_DEFAULT && ret <= 0) {
- av_log(avctx, AV_LOG_VERBOSE, "Lossless encoding not supported\n");
+ av_log(avctx, AV_LOG_WARNING, "Lossless encoding not supported\n");
return AVERROR(ENOSYS);
}
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_WIDTH_MAX);
if (ret < avctx->width) {
- av_log(avctx, AV_LOG_VERBOSE, "Width %d exceeds %d\n",
+ av_log(avctx, AV_LOG_WARNING, "Width %d exceeds %d\n",
avctx->width, ret);
return AVERROR(ENOSYS);
}
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_HEIGHT_MAX);
if (ret < avctx->height) {
- av_log(avctx, AV_LOG_VERBOSE, "Height %d exceeds %d\n",
+ av_log(avctx, AV_LOG_WARNING, "Height %d exceeds %d\n",
avctx->height, ret);
return AVERROR(ENOSYS);
}
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_NUM_MAX_BFRAMES);
if (ret < avctx->max_b_frames) {
- av_log(avctx, AV_LOG_VERBOSE, "Max B-frames %d exceed %d\n",
+ av_log(avctx, AV_LOG_WARNING, "Max B-frames %d exceed %d\n",
avctx->max_b_frames, ret);
return AVERROR(ENOSYS);
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_FIELD_ENCODING);
if (ret < 1 && avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) {
- av_log(avctx, AV_LOG_VERBOSE,
+ av_log(avctx, AV_LOG_WARNING,
"Interlaced encoding is not supported. Supported level: %d\n",
ret);
return AVERROR(ENOSYS);
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_10BIT_ENCODE);
if (IS_10BIT(ctx->data_pix_fmt) && ret <= 0) {
- av_log(avctx, AV_LOG_VERBOSE, "10 bit encode not supported\n");
+ av_log(avctx, AV_LOG_WARNING, "10 bit encode not supported\n");
return AVERROR(ENOSYS);
}
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_LOOKAHEAD);
if (ctx->rc_lookahead > 0 && ret <= 0) {
- av_log(avctx, AV_LOG_VERBOSE, "RC lookahead not supported\n");
+ av_log(avctx, AV_LOG_WARNING, "RC lookahead not supported\n");
return AVERROR(ENOSYS);
}
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ);
if (ctx->temporal_aq > 0 && ret <= 0) {
- av_log(avctx, AV_LOG_VERBOSE, "Temporal AQ not supported\n");
+ av_log(avctx, AV_LOG_WARNING, "Temporal AQ not supported\n");
return AVERROR(ENOSYS);
}
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION);
if (ctx->weighted_pred > 0 && ret <= 0) {
- av_log (avctx, AV_LOG_VERBOSE, "Weighted Prediction not supported\n");
+ av_log (avctx, AV_LOG_WARNING, "Weighted Prediction not supported\n");
return AVERROR(ENOSYS);
}
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_CABAC);
if (ctx->coder == NV_ENC_H264_ENTROPY_CODING_MODE_CABAC && ret <= 0) {
- av_log(avctx, AV_LOG_VERBOSE, "CABAC entropy coding not supported\n");
+ av_log(avctx, AV_LOG_WARNING, "CABAC entropy coding not supported\n");
return AVERROR(ENOSYS);
}
#ifdef NVENC_HAVE_BFRAME_REF_MODE
ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE);
if (ctx->b_ref_mode == NV_ENC_BFRAME_REF_MODE_EACH && ret != 1) {
- av_log(avctx, AV_LOG_VERBOSE, "Each B frame as reference is not supported\n");
+ av_log(avctx, AV_LOG_WARNING, "Each B frame as reference is not supported\n");
return AVERROR(ENOSYS);
} else if (ctx->b_ref_mode != NV_ENC_BFRAME_REF_MODE_DISABLED && ret == 0) {
- av_log(avctx, AV_LOG_VERBOSE, "B frames as references are not supported\n");
+ av_log(avctx, AV_LOG_WARNING, "B frames as references are not supported\n");
return AVERROR(ENOSYS);
}
#else
if (ctx->b_ref_mode != 0) {
- av_log(avctx, AV_LOG_VERBOSE, "B frames as references need SDK 8.1 at build time\n");
+ av_log(avctx, AV_LOG_WARNING, "B frames as references need SDK 8.1 at build time\n");
return AVERROR(ENOSYS);
}
#endif
+#ifdef NVENC_HAVE_MULTIPLE_REF_FRAMES
+ ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES);
+ if(avctx->refs != NV_ENC_NUM_REF_FRAMES_AUTOSELECT && ret <= 0) {
+ av_log(avctx, AV_LOG_WARNING, "Multiple reference frames are not supported by the device\n");
+ return AVERROR(ENOSYS);
+ }
+#else
+ if(avctx->refs != 0) {
+ av_log(avctx, AV_LOG_WARNING, "Multiple reference frames need SDK 9.1 at build time\n");
+ return AVERROR(ENOSYS);
+ }
+#endif
+
+ ctx->support_dyn_bitrate = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE);
+
return 0;
}
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
char name[128] = { 0};
int major, minor, ret;
- CUresult cu_res;
CUdevice cu_device;
int loglevel = AV_LOG_VERBOSE;
if (ctx->device == LIST_DEVICES)
loglevel = AV_LOG_INFO;
- cu_res = dl_fn->cuda_dl->cuDeviceGet(&cu_device, idx);
- if (cu_res != CUDA_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR,
- "Cannot access the CUDA device %d\n",
- idx);
- return -1;
- }
+ ret = CHECK_CU(dl_fn->cuda_dl->cuDeviceGet(&cu_device, idx));
+ if (ret < 0)
+ return ret;
- cu_res = dl_fn->cuda_dl->cuDeviceGetName(name, sizeof(name), cu_device);
- if (cu_res != CUDA_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "cuDeviceGetName failed on device %d\n", idx);
- return -1;
- }
+ ret = CHECK_CU(dl_fn->cuda_dl->cuDeviceGetName(name, sizeof(name), cu_device));
+ if (ret < 0)
+ return ret;
- cu_res = dl_fn->cuda_dl->cuDeviceComputeCapability(&major, &minor, cu_device);
- if (cu_res != CUDA_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "cuDeviceComputeCapability failed on device %d\n", idx);
- return -1;
- }
+ ret = CHECK_CU(dl_fn->cuda_dl->cuDeviceComputeCapability(&major, &minor, cu_device));
+ if (ret < 0)
+ return ret;
av_log(avctx, loglevel, "[ GPU #%d - < %s > has Compute SM %d.%d ]\n", idx, name, major, minor);
if (((major << 4) | minor) < NVENC_CAP) {
if (ctx->device != idx && ctx->device != ANY_DEVICE)
return -1;
- cu_res = dl_fn->cuda_dl->cuCtxCreate(&ctx->cu_context_internal, 0, cu_device);
- if (cu_res != CUDA_SUCCESS) {
- av_log(avctx, AV_LOG_FATAL, "Failed creating CUDA context for NVENC: 0x%x\n", (int)cu_res);
+ ret = CHECK_CU(dl_fn->cuda_dl->cuCtxCreate(&ctx->cu_context_internal, 0, cu_device));
+ if (ret < 0)
goto fail;
- }
ctx->cu_context = ctx->cu_context_internal;
+ ctx->cu_stream = NULL;
if ((ret = nvenc_pop_context(avctx)) < 0)
goto fail2;
return ret;
fail2:
- dl_fn->cuda_dl->cuCtxDestroy(ctx->cu_context_internal);
+ CHECK_CU(dl_fn->cuda_dl->cuCtxDestroy(ctx->cu_context_internal));
ctx->cu_context_internal = NULL;
fail:
if (cuda_device_hwctx) {
ctx->cu_context = cuda_device_hwctx->cuda_ctx;
+ ctx->cu_stream = cuda_device_hwctx->stream;
}
#if CONFIG_D3D11VA
else if (d3d11_device_hwctx) {
} else {
int i, nb_devices = 0;
- if ((dl_fn->cuda_dl->cuInit(0)) != CUDA_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR,
- "Cannot init CUDA\n");
+ if (CHECK_CU(dl_fn->cuda_dl->cuInit(0)) < 0)
return AVERROR_UNKNOWN;
- }
- if ((dl_fn->cuda_dl->cuDeviceGetCount(&nb_devices)) != CUDA_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR,
- "Cannot enumerate the CUDA devices\n");
+ if (CHECK_CU(dl_fn->cuda_dl->cuDeviceGetCount(&nb_devices)) < 0)
return AVERROR_UNKNOWN;
- }
if (!nb_devices) {
av_log(avctx, AV_LOG_FATAL, "No CUDA capable devices found\n");
return AVERROR_EXIT;
if (!dl_fn->nvenc_device_count) {
- av_log(avctx, AV_LOG_FATAL, "No NVENC capable devices found\n");
+ av_log(avctx, AV_LOG_FATAL, "No capable devices found\n");
return AVERROR_EXTERNAL;
}
PRESET(LOW_LATENCY_HQ, NVENC_LOWLATENCY),
PRESET(LOSSLESS_DEFAULT, NVENC_LOSSLESS),
PRESET(LOSSLESS_HP, NVENC_LOSSLESS),
+#ifdef NVENC_HAVE_NEW_PRESETS
+ PRESET(P1),
+ PRESET(P2),
+ PRESET(P3),
+ PRESET(P4),
+ PRESET(P5),
+ PRESET(P6),
+ PRESET(P7),
+#endif
};
GUIDTuple *t = &presets[ctx->preset];
if (avctx->rc_max_rate > 0)
ctx->encode_config.rcParams.maxBitRate = avctx->rc_max_rate;
+#ifdef NVENC_HAVE_MULTIPASS
+ ctx->encode_config.rcParams.multiPass = ctx->multipass;
+ if (ctx->encode_config.rcParams.multiPass != NV_ENC_MULTI_PASS_DISABLED)
+ ctx->flags |= NVENC_TWO_PASSES;
+#endif
+
if (ctx->rc < 0) {
if (ctx->flags & NVENC_ONE_PASS)
ctx->twopass = 0;
ctx->rc &= ~RC_MODE_DEPRECATED;
}
+#ifdef NVENC_HAVE_LDKFS
+ if (ctx->ldkfs)
+ ctx->encode_config.rcParams.lowDelayKeyFrameScale = ctx->ldkfs;
+#endif
+
if (ctx->flags & NVENC_LOSSLESS) {
set_lossless(avctx);
} else if (ctx->rc >= 0) {
if (avctx->rc_buffer_size > 0) {
ctx->encode_config.rcParams.vbvBufferSize = avctx->rc_buffer_size;
} else if (ctx->encode_config.rcParams.averageBitRate > 0) {
- ctx->encode_config.rcParams.vbvBufferSize = 2 * ctx->encode_config.rcParams.averageBitRate;
+ avctx->rc_buffer_size = ctx->encode_config.rcParams.vbvBufferSize = 2 * ctx->encode_config.rcParams.averageBitRate;
}
if (ctx->aq) {
if (ctx->zerolatency)
ctx->encode_config.rcParams.zeroReorderDelay = 1;
- if (ctx->quality)
- {
+ if (ctx->quality) {
//convert from float to fixed point 8.8
int tmp_quality = (int)(ctx->quality * 256.0f);
ctx->encode_config.rcParams.targetQuality = (uint8_t)(tmp_quality >> 8);
ctx->encode_config.rcParams.targetQualityLSB = (uint8_t)(tmp_quality & 0xff);
+
+ av_log(avctx, AV_LOG_VERBOSE, "CQ(%d) mode enabled.\n", tmp_quality);
+
+ //CQ mode shall discard avg bitrate & honor max bitrate;
+ ctx->encode_config.rcParams.averageBitRate = avctx->bit_rate = 0;
+ ctx->encode_config.rcParams.maxBitRate = avctx->rc_max_rate;
}
}
h264->repeatSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 0 : 1;
h264->outputAUD = ctx->aud;
- if (avctx->refs >= 0) {
+ if (ctx->dpb_size >= 0) {
/* 0 means "let the hardware decide" */
- h264->maxNumRefFrames = avctx->refs;
+ h264->maxNumRefFrames = ctx->dpb_size;
}
if (avctx->gop_size >= 0) {
h264->idrPeriod = cc->gopLength;
h264->useBFramesAsRef = ctx->b_ref_mode;
#endif
+#ifdef NVENC_HAVE_MULTIPLE_REF_FRAMES
+ h264->numRefL0 = avctx->refs;
+ h264->numRefL1 = avctx->refs;
+#endif
+
return 0;
}
hevc->repeatSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 0 : 1;
hevc->outputAUD = ctx->aud;
- if (avctx->refs >= 0) {
+ if (ctx->dpb_size >= 0) {
/* 0 means "let the hardware decide" */
- hevc->maxNumRefFramesInDPB = avctx->refs;
+ hevc->maxNumRefFramesInDPB = ctx->dpb_size;
}
if (avctx->gop_size >= 0) {
hevc->idrPeriod = cc->gopLength;
hevc->tier = ctx->tier;
+#ifdef NVENC_HAVE_HEVC_BFRAME_REF_MODE
+ hevc->useBFramesAsRef = ctx->b_ref_mode;
+#endif
+
+#ifdef NVENC_HAVE_MULTIPLE_REF_FRAMES
+ hevc->numRefL0 = avctx->refs;
+ hevc->numRefL1 = avctx->refs;
+#endif
+
return 0;
}
return 0;
}
+static void compute_dar(AVCodecContext *avctx, int *dw, int *dh) {
+ int sw, sh;
+
+ sw = avctx->width;
+ sh = avctx->height;
+
+ if (avctx->sample_aspect_ratio.num > 0 && avctx->sample_aspect_ratio.den > 0) {
+ sw *= avctx->sample_aspect_ratio.num;
+ sh *= avctx->sample_aspect_ratio.den;
+ }
+
+ av_reduce(dw, dh, sw, sh, 1024 * 1024);
+}
+
static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
{
NvencContext *ctx = avctx->priv_data;
preset_config.version = NV_ENC_PRESET_CONFIG_VER;
preset_config.presetCfg.version = NV_ENC_CONFIG_VER;
- nv_status = p_nvenc->nvEncGetEncodePresetConfig(ctx->nvencoder,
- ctx->init_encode_params.encodeGUID,
- ctx->init_encode_params.presetGUID,
- &preset_config);
+ if (IS_SDK10_PRESET(ctx->preset)) {
+#ifdef NVENC_HAVE_NEW_PRESETS
+ ctx->init_encode_params.tuningInfo = ctx->tuning_info;
+
+ nv_status = p_nvenc->nvEncGetEncodePresetConfigEx(ctx->nvencoder,
+ ctx->init_encode_params.encodeGUID,
+ ctx->init_encode_params.presetGUID,
+ ctx->init_encode_params.tuningInfo,
+ &preset_config);
+#endif
+ } else {
+#ifdef NVENC_HAVE_NEW_PRESETS
+ // Turn off tuning info parameter if older presets are on
+ ctx->init_encode_params.tuningInfo = 0;
+#endif
+
+ nv_status = p_nvenc->nvEncGetEncodePresetConfig(ctx->nvencoder,
+ ctx->init_encode_params.encodeGUID,
+ ctx->init_encode_params.presetGUID,
+ &preset_config);
+ }
if (nv_status != NV_ENC_SUCCESS)
return nvenc_print_error(avctx, nv_status, "Cannot get the preset configuration");
ctx->encode_config.version = NV_ENC_CONFIG_VER;
- dw = avctx->width;
- dh = avctx->height;
- if (avctx->sample_aspect_ratio.num > 0 && avctx->sample_aspect_ratio.den > 0) {
- dw*= avctx->sample_aspect_ratio.num;
- dh*= avctx->sample_aspect_ratio.den;
- }
- av_reduce(&dw, &dh, dw, dh, 1024 * 1024);
+ compute_dar(avctx, &dw, &dh);
ctx->init_encode_params.darHeight = dh;
ctx->init_encode_params.darWidth = dw;
- ctx->init_encode_params.frameRateNum = avctx->time_base.den;
- ctx->init_encode_params.frameRateDen = avctx->time_base.num * avctx->ticks_per_frame;
+ if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
+ ctx->init_encode_params.frameRateNum = avctx->framerate.num;
+ ctx->init_encode_params.frameRateDen = avctx->framerate.den;
+ } else {
+ ctx->init_encode_params.frameRateNum = avctx->time_base.den;
+ ctx->init_encode_params.frameRateDen = avctx->time_base.num * avctx->ticks_per_frame;
+ }
ctx->init_encode_params.enableEncodeAsync = 0;
ctx->init_encode_params.enablePTD = 1;
+#ifdef NVENC_HAVE_NEW_PRESETS
+ /* If lookahead isn't set from CLI, use value from preset.
+ * P6 & P7 presets may enable lookahead for better quality.
+ * */
+ if (ctx->rc_lookahead == 0 && ctx->encode_config.rcParams.enableLookahead)
+ ctx->rc_lookahead = ctx->encode_config.rcParams.lookaheadDepth;
+
+ if (ctx->init_encode_params.tuningInfo == NV_ENC_TUNING_INFO_LOSSLESS)
+ ctx->flags |= NVENC_LOSSLESS;
+#endif
+
if (ctx->weighted_pred == 1)
ctx->init_encode_params.enableWeightedPrediction = 1;
if (ctx->bluray_compat) {
ctx->aud = 1;
- avctx->refs = FFMIN(FFMAX(avctx->refs, 0), 6);
+ ctx->dpb_size = FFMIN(FFMAX(avctx->refs, 0), 6);
avctx->max_b_frames = FFMIN(avctx->max_b_frames, 3);
switch (avctx->codec->id) {
case AV_CODEC_ID_H264:
ctx->encode_config.gopLength = 1;
}
- ctx->initial_pts[0] = AV_NOPTS_VALUE;
- ctx->initial_pts[1] = AV_NOPTS_VALUE;
-
nvenc_recalc_surfaces(avctx);
nvenc_setup_rate_control(avctx);
return res;
nv_status = p_nvenc->nvEncInitializeEncoder(ctx->nvencoder, &ctx->init_encode_params);
+ if (nv_status != NV_ENC_SUCCESS) {
+ nvenc_pop_context(avctx);
+ return nvenc_print_error(avctx, nv_status, "InitializeEncoder failed");
+ }
+
+#ifdef NVENC_HAVE_CUSTREAM_PTR
+ if (ctx->cu_context) {
+ nv_status = p_nvenc->nvEncSetIOCudaStreams(ctx->nvencoder, &ctx->cu_stream, &ctx->cu_stream);
+ if (nv_status != NV_ENC_SUCCESS) {
+ nvenc_pop_context(avctx);
+ return nvenc_print_error(avctx, nv_status, "SetIOCudaStreams failed");
+ }
+ }
+#endif
res = nvenc_pop_context(avctx);
if (res < 0)
return res;
- if (nv_status != NV_ENC_SUCCESS) {
- return nvenc_print_error(avctx, nv_status, "InitializeEncoder failed");
- }
-
if (ctx->encode_config.frameIntervalP > 1)
avctx->has_b_frames = 2;
av_freep(&ctx->surfaces);
ctx->nb_surfaces = 0;
+ av_frame_free(&ctx->frame);
+
if (ctx->nvencoder) {
p_nvenc->nvEncDestroyEncoder(ctx->nvencoder);
ctx->nvencoder = NULL;
if (ctx->cu_context_internal)
- dl_fn->cuda_dl->cuCtxDestroy(ctx->cu_context_internal);
+ CHECK_CU(dl_fn->cuda_dl->cuCtxDestroy(ctx->cu_context_internal));
ctx->cu_context = ctx->cu_context_internal = NULL;
#if CONFIG_D3D11VA
ctx->data_pix_fmt = avctx->pix_fmt;
}
+ ctx->frame = av_frame_alloc();
+ if (!ctx->frame)
+ return AVERROR(ENOMEM);
+
if ((ret = nvenc_load_libraries(avctx)) < 0)
return ret;
NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
NVENCSTATUS nv_status;
- int i;
+ int i, first_round;
if (ctx->nb_registered_frames == FF_ARRAY_ELEMS(ctx->registered_frames)) {
- for (i = 0; i < ctx->nb_registered_frames; i++) {
- if (!ctx->registered_frames[i].mapped) {
- if (ctx->registered_frames[i].regptr) {
- nv_status = p_nvenc->nvEncUnregisterResource(ctx->nvencoder, ctx->registered_frames[i].regptr);
- if (nv_status != NV_ENC_SUCCESS)
- return nvenc_print_error(avctx, nv_status, "Failed unregistering unused input resource");
- ctx->registered_frames[i].ptr = NULL;
- ctx->registered_frames[i].regptr = NULL;
+ for (first_round = 1; first_round >= 0; first_round--) {
+ for (i = 0; i < ctx->nb_registered_frames; i++) {
+ if (!ctx->registered_frames[i].mapped) {
+ if (ctx->registered_frames[i].regptr) {
+ if (first_round)
+ continue;
+ nv_status = p_nvenc->nvEncUnregisterResource(ctx->nvencoder, ctx->registered_frames[i].regptr);
+ if (nv_status != NV_ENC_SUCCESS)
+ return nvenc_print_error(avctx, nv_status, "Failed unregistering unused input resource");
+ ctx->registered_frames[i].ptr = NULL;
+ ctx->registered_frames[i].regptr = NULL;
+ }
+ return i;
}
- return i;
}
}
} else {
}
static void nvenc_codec_specific_pic_params(AVCodecContext *avctx,
- NV_ENC_PIC_PARAMS *params)
+ NV_ENC_PIC_PARAMS *params,
+ NV_ENC_SEI_PAYLOAD *sei_data,
+ int sei_count)
{
NvencContext *ctx = avctx->priv_data;
ctx->encode_config.encodeCodecConfig.h264Config.sliceMode;
params->codecPicParams.h264PicParams.sliceModeData =
ctx->encode_config.encodeCodecConfig.h264Config.sliceModeData;
+ if (sei_count > 0) {
+ params->codecPicParams.h264PicParams.seiPayloadArray = sei_data;
+ params->codecPicParams.h264PicParams.seiPayloadArrayCnt = sei_count;
+ }
+
break;
case AV_CODEC_ID_HEVC:
params->codecPicParams.hevcPicParams.sliceMode =
ctx->encode_config.encodeCodecConfig.hevcConfig.sliceMode;
params->codecPicParams.hevcPicParams.sliceModeData =
ctx->encode_config.encodeCodecConfig.hevcConfig.sliceModeData;
+ if (sei_count > 0) {
+ params->codecPicParams.hevcPicParams.seiPayloadArray = sei_data;
+ params->codecPicParams.hevcPicParams.seiPayloadArrayCnt = sei_count;
+ }
+
break;
}
}
NvencContext *ctx = avctx->priv_data;
pkt->pts = params->outputTimeStamp;
-
- /* generate the first dts by linearly extrapolating the
- * first two pts values to the past */
- if (avctx->max_b_frames > 0 && !ctx->first_packet_output &&
- ctx->initial_pts[1] != AV_NOPTS_VALUE) {
- int64_t ts0 = ctx->initial_pts[0], ts1 = ctx->initial_pts[1];
- int64_t delta;
-
- if ((ts0 < 0 && ts1 > INT64_MAX + ts0) ||
- (ts0 > 0 && ts1 < INT64_MIN + ts0))
- return AVERROR(ERANGE);
- delta = ts1 - ts0;
-
- if ((delta < 0 && ts0 > INT64_MAX + delta) ||
- (delta > 0 && ts0 < INT64_MIN + delta))
- return AVERROR(ERANGE);
- pkt->dts = ts0 - delta;
-
- ctx->first_packet_output = 1;
- return 0;
- }
-
pkt->dts = timestamp_queue_dequeue(ctx->timestamp_list);
+ pkt->dts -= FFMAX(avctx->max_b_frames, 0) * FFMIN(avctx->ticks_per_frame, 1);
+
return 0;
}
goto error;
}
- if (res = ff_alloc_packet2(avctx, pkt, lock_params.bitstreamSizeInBytes,0)) {
+ res = av_new_packet(pkt, lock_params.bitstreamSizeInBytes);
+
+ if (res < 0) {
p_nvenc->nvEncUnlockBitstream(ctx->nvencoder, tmpoutsurf->output_surface);
goto error;
}
res = nvenc_print_error(avctx, nv_status, "Failed unmapping input resource");
goto error;
}
- nv_status = p_nvenc->nvEncUnregisterResource(ctx->nvencoder, ctx->registered_frames[tmpoutsurf->reg_idx].regptr);
- if (nv_status != NV_ENC_SUCCESS) {
- res = nvenc_print_error(avctx, nv_status, "Failed unregistering input resource");
- goto error;
- }
- ctx->registered_frames[tmpoutsurf->reg_idx].ptr = NULL;
- ctx->registered_frames[tmpoutsurf->reg_idx].regptr = NULL;
} else if (ctx->registered_frames[tmpoutsurf->reg_idx].mapped < 0) {
res = AVERROR_BUG;
goto error;
NvencContext *ctx = avctx->priv_data;
int nb_ready, nb_pending;
- /* when B-frames are enabled, we wait for two initial timestamps to
- * calculate the first dts */
- if (!flush && avctx->max_b_frames > 0 &&
- (ctx->initial_pts[0] == AV_NOPTS_VALUE || ctx->initial_pts[1] == AV_NOPTS_VALUE))
- return 0;
-
nb_ready = av_fifo_size(ctx->output_surface_ready_queue) / sizeof(NvencSurface*);
nb_pending = av_fifo_size(ctx->output_surface_queue) / sizeof(NvencSurface*);
if (flush)
return (nb_ready > 0) && (nb_ready + nb_pending >= ctx->async_depth);
}
-int ff_nvenc_send_frame(AVCodecContext *avctx, const AVFrame *frame)
+static void reconfig_encoder(AVCodecContext *avctx, const AVFrame *frame)
+{
+ NvencContext *ctx = avctx->priv_data;
+ NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &ctx->nvenc_dload_funcs.nvenc_funcs;
+ NVENCSTATUS ret;
+
+ NV_ENC_RECONFIGURE_PARAMS params = { 0 };
+ int needs_reconfig = 0;
+ int needs_encode_config = 0;
+ int reconfig_bitrate = 0, reconfig_dar = 0;
+ int dw, dh;
+
+ params.version = NV_ENC_RECONFIGURE_PARAMS_VER;
+ params.reInitEncodeParams = ctx->init_encode_params;
+
+ compute_dar(avctx, &dw, &dh);
+ if (dw != ctx->init_encode_params.darWidth || dh != ctx->init_encode_params.darHeight) {
+ av_log(avctx, AV_LOG_VERBOSE,
+ "aspect ratio change (DAR): %d:%d -> %d:%d\n",
+ ctx->init_encode_params.darWidth,
+ ctx->init_encode_params.darHeight, dw, dh);
+
+ params.reInitEncodeParams.darHeight = dh;
+ params.reInitEncodeParams.darWidth = dw;
+
+ needs_reconfig = 1;
+ reconfig_dar = 1;
+ }
+
+ if (ctx->rc != NV_ENC_PARAMS_RC_CONSTQP && ctx->support_dyn_bitrate) {
+ if (avctx->bit_rate > 0 && params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate != avctx->bit_rate) {
+ av_log(avctx, AV_LOG_VERBOSE,
+ "avg bitrate change: %d -> %d\n",
+ params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate,
+ (uint32_t)avctx->bit_rate);
+
+ params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate = avctx->bit_rate;
+ reconfig_bitrate = 1;
+ }
+
+ if (avctx->rc_max_rate > 0 && ctx->encode_config.rcParams.maxBitRate != avctx->rc_max_rate) {
+ av_log(avctx, AV_LOG_VERBOSE,
+ "max bitrate change: %d -> %d\n",
+ params.reInitEncodeParams.encodeConfig->rcParams.maxBitRate,
+ (uint32_t)avctx->rc_max_rate);
+
+ params.reInitEncodeParams.encodeConfig->rcParams.maxBitRate = avctx->rc_max_rate;
+ reconfig_bitrate = 1;
+ }
+
+ if (avctx->rc_buffer_size > 0 && ctx->encode_config.rcParams.vbvBufferSize != avctx->rc_buffer_size) {
+ av_log(avctx, AV_LOG_VERBOSE,
+ "vbv buffer size change: %d -> %d\n",
+ params.reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize,
+ avctx->rc_buffer_size);
+
+ params.reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize = avctx->rc_buffer_size;
+ reconfig_bitrate = 1;
+ }
+
+ if (reconfig_bitrate) {
+ params.resetEncoder = 1;
+ params.forceIDR = 1;
+
+ needs_encode_config = 1;
+ needs_reconfig = 1;
+ }
+ }
+
+ if (!needs_encode_config)
+ params.reInitEncodeParams.encodeConfig = NULL;
+
+ if (needs_reconfig) {
+ ret = p_nvenc->nvEncReconfigureEncoder(ctx->nvencoder, ¶ms);
+ if (ret != NV_ENC_SUCCESS) {
+ nvenc_print_error(avctx, ret, "failed to reconfigure nvenc");
+ } else {
+ if (reconfig_dar) {
+ ctx->init_encode_params.darHeight = dh;
+ ctx->init_encode_params.darWidth = dw;
+ }
+
+ if (reconfig_bitrate) {
+ ctx->encode_config.rcParams.averageBitRate = params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate;
+ ctx->encode_config.rcParams.maxBitRate = params.reInitEncodeParams.encodeConfig->rcParams.maxBitRate;
+ ctx->encode_config.rcParams.vbvBufferSize = params.reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize;
+ }
+
+ }
+ }
+}
+
+static int nvenc_send_frame(AVCodecContext *avctx, const AVFrame *frame)
{
NVENCSTATUS nv_status;
NvencSurface *tmp_out_surf, *in_surf;
int res, res2;
+ NV_ENC_SEI_PAYLOAD sei_data[8];
+ int sei_count = 0;
+ int i;
NvencContext *ctx = avctx->priv_data;
NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
if ((!ctx->cu_context && !ctx->d3d11_device) || !ctx->nvencoder)
return AVERROR(EINVAL);
- if (ctx->encoder_flushing)
- return AVERROR_EOF;
-
- if (frame) {
+ if (frame && frame->buf[0]) {
in_surf = get_free_frame(ctx);
if (!in_surf)
return AVERROR(EAGAIN);
if (res < 0)
return res;
+ reconfig_encoder(avctx, frame);
+
res = nvenc_upload_frame(avctx, frame, in_surf);
res2 = nvenc_pop_context(avctx);
pic_params.inputTimeStamp = frame->pts;
- nvenc_codec_specific_pic_params(avctx, &pic_params);
+ if (ctx->a53_cc && av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC)) {
+ void *a53_data = NULL;
+ size_t a53_size = 0;
+
+ if (ff_alloc_a53_sei(frame, 0, (void**)&a53_data, &a53_size) < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Not enough memory for closed captions, skipping\n");
+ }
+
+ if (a53_data) {
+ sei_data[sei_count].payloadSize = (uint32_t)a53_size;
+ sei_data[sei_count].payloadType = 4;
+ sei_data[sei_count].payload = (uint8_t*)a53_data;
+ sei_count ++;
+ }
+ }
+
+ if (ctx->s12m_tc && av_frame_get_side_data(frame, AV_FRAME_DATA_S12M_TIMECODE)) {
+ void *tc_data = NULL;
+ size_t tc_size = 0;
+
+ if (ff_alloc_timecode_sei(frame, 0, (void**)&tc_data, &tc_size) < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Not enough memory for timecode sei, skipping\n");
+ }
+
+ if (tc_data) {
+ sei_data[sei_count].payloadSize = (uint32_t)tc_size;
+ sei_data[sei_count].payloadType = HEVC_SEI_TYPE_TIME_CODE;
+ sei_data[sei_count].payload = (uint8_t*)tc_data;
+ sei_count ++;
+ }
+ }
+
+ nvenc_codec_specific_pic_params(avctx, &pic_params, sei_data, sei_count);
} else {
pic_params.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
- ctx->encoder_flushing = 1;
}
res = nvenc_push_context(avctx);
nv_status = p_nvenc->nvEncEncodePicture(ctx->nvencoder, &pic_params);
+ for ( i = 0; i < sei_count; i++)
+ av_freep(&sei_data[i].payload);
+
res = nvenc_pop_context(avctx);
if (res < 0)
return res;
nv_status != NV_ENC_ERR_NEED_MORE_INPUT)
return nvenc_print_error(avctx, nv_status, "EncodePicture failed!");
- if (frame) {
+ if (frame && frame->buf[0]) {
av_fifo_generic_write(ctx->output_surface_queue, &in_surf, sizeof(in_surf), NULL);
timestamp_queue_enqueue(ctx->timestamp_list, frame->pts);
-
- if (ctx->initial_pts[0] == AV_NOPTS_VALUE)
- ctx->initial_pts[0] = frame->pts;
- else if (ctx->initial_pts[1] == AV_NOPTS_VALUE)
- ctx->initial_pts[1] = frame->pts;
}
/* all the pending buffers are now ready for output */
NvencContext *ctx = avctx->priv_data;
+ AVFrame *frame = ctx->frame;
+
if ((!ctx->cu_context && !ctx->d3d11_device) || !ctx->nvencoder)
return AVERROR(EINVAL);
- if (output_ready(avctx, ctx->encoder_flushing)) {
+ if (!frame->buf[0]) {
+ res = ff_encode_get_frame(avctx, frame);
+ if (res < 0 && res != AVERROR_EOF)
+ return res;
+ }
+
+ res = nvenc_send_frame(avctx, frame);
+ if (res < 0) {
+ if (res != AVERROR(EAGAIN))
+ return res;
+ } else
+ av_frame_unref(frame);
+
+ if (output_ready(avctx, avctx->internal->draining)) {
av_fifo_generic_read(ctx->output_surface_ready_queue, &tmp_out_surf, sizeof(tmp_out_surf), NULL);
res = nvenc_push_context(avctx);
return res;
av_fifo_generic_write(ctx->unused_surface_queue, &tmp_out_surf, sizeof(tmp_out_surf), NULL);
- } else if (ctx->encoder_flushing) {
+ } else if (avctx->internal->draining) {
return AVERROR_EOF;
} else {
return AVERROR(EAGAIN);
return 0;
}
-int ff_nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
- const AVFrame *frame, int *got_packet)
+av_cold void ff_nvenc_encode_flush(AVCodecContext *avctx)
{
NvencContext *ctx = avctx->priv_data;
- int res;
-
- if (!ctx->encoder_flushing) {
- res = ff_nvenc_send_frame(avctx, frame);
- if (res < 0)
- return res;
- }
-
- res = ff_nvenc_receive_packet(avctx, pkt);
- if (res == AVERROR(EAGAIN) || res == AVERROR_EOF) {
- *got_packet = 0;
- } else if (res < 0) {
- return res;
- } else {
- *got_packet = 1;
- }
- return 0;
+ nvenc_send_frame(avctx, NULL);
+ av_fifo_reset(ctx->timestamp_list);
}