]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/nvenc.c
dxva2: fix surface selection when compiled with both d3d11va and dxva2
[ffmpeg] / libavcodec / nvenc.c
index 4d17f989e67cfd9f1cca7227d79a24392db7f877..d5bfd74e30dd6738dc09408733518b673dcb3fcc 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * 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.
  *
@@ -21,7 +23,6 @@
 
 #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 {                                        \
@@ -82,10 +89,22 @@ const enum AVPixelFormat ff_nvenc_pix_fmts[] = {
     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;
@@ -295,7 +314,7 @@ static int nvenc_check_capabilities(AVCodecContext *avctx)
 
     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);
@@ -392,6 +411,7 @@ static int nvenc_setup_device(AVCodecContext *avctx)
     }
 
     if (avctx->pix_fmt == AV_PIX_FMT_CUDA) {
+#if CONFIG_CUDA
         AVHWFramesContext   *frames_ctx;
         AVCUDADeviceContext *device_hwctx;
         int ret;
@@ -411,6 +431,9 @@ static int nvenc_setup_device(AVCodecContext *avctx)
         ret = nvenc_check_capabilities(avctx);
         if (ret < 0)
             return ret;
+#else
+        return AVERROR_BUG;
+#endif
     } else {
         int i, nb_devices = 0;
 
@@ -446,18 +469,26 @@ typedef struct GUIDTuple {
     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 } }
     };
 
@@ -469,6 +500,9 @@ static int nvec_map_preset(NVENCContext *ctx)
     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;
@@ -494,6 +528,14 @@ static void set_vbr(AVCodecContext *avctx, NV_ENC_RC_PARAMS *rc)
     }
 }
 
+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)
 {
@@ -554,6 +596,8 @@ static void nvenc_setup_rate_control(AVCodecContext *avctx)
 
     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) {
@@ -566,6 +610,53 @@ static void nvenc_setup_rate_control(AVCodecContext *avctx)
 
     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)
@@ -575,8 +666,9 @@ 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;
@@ -584,12 +676,27 @@ static int nvenc_setup_h264_config(AVCodecContext *avctx)
 
     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;
 
@@ -616,6 +723,11 @@ static int nvenc_setup_h264_config(AVCodecContext *avctx)
         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;
@@ -626,16 +738,77 @@ static int nvenc_setup_hevc_config(AVCodecContext *avctx)
     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;
@@ -687,6 +860,15 @@ static int nvenc_setup_encoder(AVCodecContext *avctx)
         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;
 
@@ -715,8 +897,8 @@ static int nvenc_setup_encoder(AVCodecContext *avctx)
         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;
@@ -746,7 +928,7 @@ static int nvenc_setup_encoder(AVCodecContext *avctx)
 
     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)
@@ -776,6 +958,14 @@ static int nvenc_alloc_surface(AVCodecContext *avctx, int idx)
     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;
     }
@@ -804,7 +994,7 @@ static int nvenc_alloc_surface(AVCodecContext *avctx, int idx)
 
     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;
@@ -825,6 +1015,8 @@ static int nvenc_setup_surfaces(AVCodecContext *avctx)
 
     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)
@@ -1012,6 +1204,16 @@ static int nvenc_copy_frame(NV_ENC_LOCK_INPUT_BUFFER *in, const AVFrame *frame)
                             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],
@@ -1027,6 +1229,21 @@ static int nvenc_copy_frame(NV_ENC_LOCK_INPUT_BUFFER *in, const AVFrame *frame)
                             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;
     }
@@ -1286,13 +1503,19 @@ FF_ENABLE_DEPRECATION_WARNINGS
 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,