/*
* NVIDIA NVENC Support
* Copyright (C) 2015 Luca Barbato
+ * Copyright (C) 2015 Philip Langdale <philipl@overt.org>
+ * Copyright (C) 2014 Timo Rothenpieler <timo@rothenpieler.org>
*
* This file is part of Libav.
*
#include "config.h"
-#include <cuda.h>
#include <nvEncodeAPI.h>
#include <string.h>
#include "libavutil/common.h"
#include "libavutil/hwcontext.h"
-#include "libavutil/hwcontext_cuda.h"
#include "libavutil/imgutils.h"
#include "libavutil/mem.h"
#include "avcodec.h"
#include "internal.h"
#include "nvenc.h"
+#if CONFIG_CUDA
+#include "libavutil/hwcontext_cuda.h"
+#endif
+
#define NVENC_CAP 0x30
#define BITSTREAM_BUFFER_SIZE 1024 * 1024
+#define IS_CBR(rc) (rc == NV_ENC_PARAMS_RC_CBR || \
+ rc == NV_ENC_PARAMS_RC_2_PASS_QUALITY || \
+ rc == NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP)
#define LOAD_LIBRARY(l, path) \
do { \
AV_PIX_FMT_NV12,
AV_PIX_FMT_YUV420P,
AV_PIX_FMT_YUV444P,
+#if NVENCAPI_MAJOR_VERSION >= 7
+ AV_PIX_FMT_P010,
+ AV_PIX_FMT_YUV444P16,
+#endif
+#if CONFIG_CUDA
AV_PIX_FMT_CUDA,
+#endif
AV_PIX_FMT_NONE
};
+#define IS_10BIT(pix_fmt) (pix_fmt == AV_PIX_FMT_P010 || \
+ pix_fmt == AV_PIX_FMT_YUV444P16)
+
+#define IS_YUV444(pix_fmt) (pix_fmt == AV_PIX_FMT_YUV444P || \
+ pix_fmt == AV_PIX_FMT_YUV444P16)
+
static const struct {
NVENCSTATUS nverr;
int averr;
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_VERBOSE, "Max B-frames %d exceed %d\n",
avctx->max_b_frames, ret);
return AVERROR(ENOSYS);
}
if (avctx->pix_fmt == AV_PIX_FMT_CUDA) {
+#if CONFIG_CUDA
AVHWFramesContext *frames_ctx;
AVCUDADeviceContext *device_hwctx;
int ret;
ret = nvenc_check_capabilities(avctx);
if (ret < 0)
return ret;
+#else
+ return AVERROR_BUG;
+#endif
} else {
int i, nb_devices = 0;
int flags;
} GUIDTuple;
+#define PRESET_ALIAS(alias, name, ...) \
+ [PRESET_ ## alias] = { NV_ENC_PRESET_ ## name ## _GUID, __VA_ARGS__ }
+
+#define PRESET(name, ...) PRESET_ALIAS(name, name, __VA_ARGS__)
+
static int nvec_map_preset(NVENCContext *ctx)
{
GUIDTuple presets[] = {
- { NV_ENC_PRESET_DEFAULT_GUID },
- { NV_ENC_PRESET_HP_GUID },
- { NV_ENC_PRESET_HQ_GUID },
- { NV_ENC_PRESET_BD_GUID },
- { NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID, NVENC_LOWLATENCY },
- { NV_ENC_PRESET_LOW_LATENCY_HP_GUID, NVENC_LOWLATENCY },
- { NV_ENC_PRESET_LOW_LATENCY_HQ_GUID, NVENC_LOWLATENCY },
- { NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID, NVENC_LOSSLESS },
- { NV_ENC_PRESET_LOSSLESS_HP_GUID, NVENC_LOSSLESS },
+ PRESET(DEFAULT),
+ PRESET(HP),
+ PRESET(HQ),
+ PRESET(BD),
+ PRESET(LOW_LATENCY_DEFAULT, NVENC_LOWLATENCY),
+ PRESET(LOW_LATENCY_HP, NVENC_LOWLATENCY),
+ PRESET(LOW_LATENCY_HQ, NVENC_LOWLATENCY),
+ PRESET(LOSSLESS_DEFAULT, NVENC_LOSSLESS),
+ PRESET(LOSSLESS_HP, NVENC_LOSSLESS),
+ PRESET_ALIAS(SLOW, HQ, NVENC_TWO_PASSES),
+ PRESET_ALIAS(MEDIUM, HQ, NVENC_ONE_PASS),
+ PRESET_ALIAS(FAST, HP, NVENC_ONE_PASS),
{ { 0 } }
};
return AVERROR(EINVAL);
}
+#undef PRESET
+#undef PRESET_ALIAS
+
static void set_constqp(AVCodecContext *avctx, NV_ENC_RC_PARAMS *rc)
{
rc->rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
}
}
+static void set_lossless(AVCodecContext *avctx, NV_ENC_RC_PARAMS *rc)
+{
+ rc->rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
+ rc->constQP.qpInterB = 0;
+ rc->constQP.qpInterP = 0;
+ rc->constQP.qpIntra = 0;
+}
+
static void nvenc_override_rate_control(AVCodecContext *avctx,
NV_ENC_RC_PARAMS *rc)
{
if (ctx->rc > 0) {
nvenc_override_rate_control(avctx, rc);
+ } else if (ctx->flags & NVENC_LOSSLESS) {
+ set_lossless(avctx, rc);
} else if (avctx->global_quality > 0) {
set_constqp(avctx, rc);
} else if (avctx->qmin >= 0 && avctx->qmax >= 0) {
if (rc->averageBitRate > 0)
avctx->bit_rate = rc->averageBitRate;
+
+#if NVENCAPI_MAJOR_VERSION >= 7
+ if (ctx->aq) {
+ ctx->config.rcParams.enableAQ = 1;
+ ctx->config.rcParams.aqStrength = ctx->aq_strength;
+ av_log(avctx, AV_LOG_VERBOSE, "AQ enabled.\n");
+ }
+
+ if (ctx->temporal_aq) {
+ ctx->config.rcParams.enableTemporalAQ = 1;
+ av_log(avctx, AV_LOG_VERBOSE, "Temporal AQ enabled.\n");
+ }
+
+ if (ctx->rc_lookahead) {
+ int lkd_bound = FFMIN(ctx->nb_surfaces, ctx->async_depth) -
+ ctx->config.frameIntervalP - 4;
+
+ if (lkd_bound < 0) {
+ av_log(avctx, AV_LOG_WARNING,
+ "Lookahead not enabled. Increase buffer delay (-delay).\n");
+ } else {
+ ctx->config.rcParams.enableLookahead = 1;
+ ctx->config.rcParams.lookaheadDepth = av_clip(ctx->rc_lookahead, 0, lkd_bound);
+ ctx->config.rcParams.disableIadapt = ctx->no_scenecut;
+ ctx->config.rcParams.disableBadapt = !ctx->b_adapt;
+ av_log(avctx, AV_LOG_VERBOSE,
+ "Lookahead enabled: depth %d, scenecut %s, B-adapt %s.\n",
+ ctx->config.rcParams.lookaheadDepth,
+ ctx->config.rcParams.disableIadapt ? "disabled" : "enabled",
+ ctx->config.rcParams.disableBadapt ? "disabled" : "enabled");
+ }
+ }
+
+ if (ctx->strict_gop) {
+ ctx->config.rcParams.strictGOPTarget = 1;
+ av_log(avctx, AV_LOG_VERBOSE, "Strict GOP target enabled.\n");
+ }
+
+ if (ctx->nonref_p)
+ ctx->config.rcParams.enableNonRefP = 1;
+
+ if (ctx->zerolatency)
+ ctx->config.rcParams.zeroReorderDelay = 1;
+
+ if (ctx->quality)
+ ctx->config.rcParams.targetQuality = ctx->quality;
+#endif /* NVENCAPI_MAJOR_VERSION >= 7 */
}
static int nvenc_setup_h264_config(AVCodecContext *avctx)
NV_ENC_CONFIG_H264 *h264 = &cc->encodeCodecConfig.h264Config;
NV_ENC_CONFIG_H264_VUI_PARAMETERS *vui = &h264->h264VUIParameters;
- vui->colourDescriptionPresentFlag = 1;
- vui->videoSignalTypePresentFlag = 1;
+ vui->colourDescriptionPresentFlag = avctx->colorspace != AVCOL_SPC_UNSPECIFIED ||
+ avctx->color_primaries != AVCOL_PRI_UNSPECIFIED ||
+ avctx->color_trc != AVCOL_TRC_UNSPECIFIED;
vui->colourMatrix = avctx->colorspace;
vui->colourPrimaries = avctx->color_primaries;
vui->videoFullRangeFlag = avctx->color_range == AVCOL_RANGE_JPEG;
+ vui->videoSignalTypePresentFlag = vui->colourDescriptionPresentFlag ||
+ vui->videoFullRangeFlag;
+
h264->disableSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 1 : 0;
h264->repeatSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 0 : 1;
+ h264->outputAUD = 1;
h264->maxNumRefFrames = avctx->refs;
h264->idrPeriod = cc->gopLength;
+ h264->sliceMode = 3;
+ h264->sliceModeData = FFMAX(avctx->slices, 1);
+
+ if (ctx->flags & NVENC_LOSSLESS)
+ h264->qpPrimeYZeroTransformBypassFlag = 1;
+
+ if (IS_CBR(cc->rcParams.rateControlMode)) {
+ h264->outputBufferingPeriodSEI = 1;
+ h264->outputPictureTimingSEI = 1;
+ }
+
if (ctx->profile)
avctx->profile = ctx->profile;
break;
}
+ if (ctx->data_pix_fmt == AV_PIX_FMT_YUV444P) {
+ cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
+ avctx->profile = FF_PROFILE_H264_HIGH_444_PREDICTIVE;
+ }
+
h264->level = ctx->level;
return 0;
NVENCContext *ctx = avctx->priv_data;
NV_ENC_CONFIG *cc = &ctx->config;
NV_ENC_CONFIG_HEVC *hevc = &cc->encodeCodecConfig.hevcConfig;
+ NV_ENC_CONFIG_HEVC_VUI_PARAMETERS *vui = &hevc->hevcVUIParameters;
+
+ vui->colourDescriptionPresentFlag = avctx->colorspace != AVCOL_SPC_UNSPECIFIED ||
+ avctx->color_primaries != AVCOL_PRI_UNSPECIFIED ||
+ avctx->color_trc != AVCOL_TRC_UNSPECIFIED;
+
+ vui->colourMatrix = avctx->colorspace;
+ vui->colourPrimaries = avctx->color_primaries;
+ vui->transferCharacteristics = avctx->color_trc;
+
+ vui->videoFullRangeFlag = avctx->color_range == AVCOL_RANGE_JPEG;
+
+ vui->videoSignalTypePresentFlag = vui->colourDescriptionPresentFlag ||
+ vui->videoFullRangeFlag;
hevc->disableSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 1 : 0;
hevc->repeatSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 0 : 1;
+ hevc->outputAUD = 1;
hevc->maxNumRefFramesInDPB = avctx->refs;
hevc->idrPeriod = cc->gopLength;
- /* No other profile is supported in the current SDK version 5 */
- cc->profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID;
- avctx->profile = FF_PROFILE_HEVC_MAIN;
+ if (IS_CBR(cc->rcParams.rateControlMode)) {
+ hevc->outputBufferingPeriodSEI = 1;
+ hevc->outputPictureTimingSEI = 1;
+ }
+
+ switch (ctx->profile) {
+ case NV_ENC_HEVC_PROFILE_MAIN:
+ cc->profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID;
+ avctx->profile = FF_PROFILE_HEVC_MAIN;
+ break;
+#if NVENCAPI_MAJOR_VERSION >= 7
+ case NV_ENC_HEVC_PROFILE_MAIN_10:
+ cc->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID;
+ avctx->profile = FF_PROFILE_HEVC_MAIN_10;
+ break;
+ case NV_ENC_HEVC_PROFILE_REXT:
+ cc->profileGUID = NV_ENC_HEVC_PROFILE_FREXT_GUID;
+ avctx->profile = FF_PROFILE_HEVC_REXT;
+ break;
+#endif /* NVENCAPI_MAJOR_VERSION >= 7 */
+ }
+
+ // force setting profile for various input formats
+ switch (ctx->data_pix_fmt) {
+ case AV_PIX_FMT_YUV420P:
+ case AV_PIX_FMT_NV12:
+ cc->profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID;
+ avctx->profile = FF_PROFILE_HEVC_MAIN;
+ break;
+#if NVENCAPI_MAJOR_VERSION >= 7
+ case AV_PIX_FMT_P010:
+ cc->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID;
+ avctx->profile = FF_PROFILE_HEVC_MAIN_10;
+ break;
+ case AV_PIX_FMT_YUV444P:
+ case AV_PIX_FMT_YUV444P16:
+ cc->profileGUID = NV_ENC_HEVC_PROFILE_FREXT_GUID;
+ avctx->profile = FF_PROFILE_HEVC_REXT;
+ break;
+#endif /* NVENCAPI_MAJOR_VERSION >= 7 */
+ }
+
+#if NVENCAPI_MAJOR_VERSION >= 7
+ hevc->chromaFormatIDC = IS_YUV444(ctx->data_pix_fmt) ? 3 : 1;
+ hevc->pixelBitDepthMinus8 = IS_10BIT(ctx->data_pix_fmt) ? 2 : 0;
+#endif /* NVENCAPI_MAJOR_VERSION >= 7 */
+
+ hevc->sliceMode = 3;
+ hevc->sliceModeData = FFMAX(avctx->slices, 1);
if (ctx->level) {
hevc->level = ctx->level;
ctx->params.darWidth = avctx->width;
}
+ // De-compensate for hardware, dubiously, trying to compensate for
+ // playback at 704 pixel width.
+ if (avctx->width == 720 && (avctx->height == 480 || avctx->height == 576)) {
+ av_reduce(&ctx->params.darWidth, &ctx->params.darHeight,
+ ctx->params.darWidth * 44,
+ ctx->params.darHeight * 45,
+ 1024 * 1024);
+ }
+
ctx->params.frameRateNum = avctx->time_base.den;
ctx->params.frameRateDen = avctx->time_base.num * avctx->ticks_per_frame;
if (avctx->max_b_frames > 0) {
/* 0 is intra-only,
* 1 is I/P only,
- * 2 is one B Frame,
- * 3 two B frames, and so on. */
+ * 2 is one B-Frame,
+ * 3 two B-frames, and so on. */
ctx->config.frameIntervalP = avctx->max_b_frames + 1;
} else if (avctx->max_b_frames == 0) {
ctx->config.frameIntervalP = 1;
ret = nv->nvEncInitializeEncoder(ctx->nvenc_ctx, &ctx->params);
if (ret != NV_ENC_SUCCESS)
- return nvenc_print_error(avctx, ret, "Cannot initialize the decoder");
+ return nvenc_print_error(avctx, ret, "InitializeEncoder failed");
cpb_props = ff_add_cpb_side_data(avctx);
if (!cpb_props)
case AV_PIX_FMT_YUV444P:
ctx->frames[idx].format = NV_ENC_BUFFER_FORMAT_YUV444_PL;
break;
+#if NVENCAPI_MAJOR_VERSION >= 7
+ case AV_PIX_FMT_P010:
+ ctx->frames[idx].format = NV_ENC_BUFFER_FORMAT_YUV420_10BIT;
+ break;
+ case AV_PIX_FMT_YUV444P16:
+ ctx->frames[idx].format = NV_ENC_BUFFER_FORMAT_YUV444_10BIT;
+ break;
+#endif /* NVENCAPI_MAJOR_VERSION >= 7 */
default:
return AVERROR_BUG;
}
out_buffer.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
/* 1MB is large enough to hold most output frames.
- * NVENC increases this automaticaly if it's not enough. */
+ * NVENC increases this automatically if it is not enough. */
out_buffer.size = BITSTREAM_BUFFER_SIZE;
out_buffer.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_UNCACHED;
ctx->nb_surfaces = FFMAX(4 + avctx->max_b_frames,
ctx->nb_surfaces);
+ ctx->async_depth = FFMIN(ctx->async_depth, ctx->nb_surfaces - 1);
+
ctx->frames = av_mallocz_array(ctx->nb_surfaces, sizeof(*ctx->frames));
if (!ctx->frames)
frame->data[1], frame->linesize[1],
frame->width, frame->height >> 1);
break;
+ case AV_PIX_FMT_P010:
+ av_image_copy_plane(buf, in->pitch,
+ frame->data[0], frame->linesize[0],
+ frame->width << 1, frame->height);
+ buf += off;
+
+ av_image_copy_plane(buf, in->pitch,
+ frame->data[1], frame->linesize[1],
+ frame->width << 1, frame->height >> 1);
+ break;
case AV_PIX_FMT_YUV444P:
av_image_copy_plane(buf, in->pitch,
frame->data[0], frame->linesize[0],
frame->data[2], frame->linesize[2],
frame->width, frame->height);
break;
+ case AV_PIX_FMT_YUV444P16:
+ av_image_copy_plane(buf, in->pitch,
+ frame->data[0], frame->linesize[0],
+ frame->width << 1, frame->height);
+ buf += off;
+
+ av_image_copy_plane(buf, in->pitch,
+ frame->data[1], frame->linesize[1],
+ frame->width << 1, frame->height);
+ buf += off;
+
+ av_image_copy_plane(buf, in->pitch,
+ frame->data[2], frame->linesize[2],
+ frame->width << 1, frame->height);
+ break;
default:
return AVERROR_BUG;
}
static int output_ready(AVCodecContext *avctx, int flush)
{
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;
- return av_fifo_size(ctx->ready) > 0;
+
+ nb_ready = av_fifo_size(ctx->ready) / sizeof(NVENCFrame*);
+ nb_pending = av_fifo_size(ctx->pending) / sizeof(NVENCFrame*);
+ if (flush)
+ return nb_ready > 0;
+ return (nb_ready > 0) && (nb_ready + nb_pending >= ctx->async_depth);
}
int ff_nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,