[vlc-devel] [PATCH 1/2] NVENC: Nvidia H264/HEVC GPU encoder

Felix Paul Kühne fkuehne at videolan.org
Thu Apr 7 16:43:32 CEST 2016


From: Jean-Paul Saman <jpsaman at videolan.org>

Nvidia H264 and HEVC GPU encoder module for VLC.
It requires the Nvidia SDK and a modern graphics card.

The implementation is based on FFMPEG 3.0 nvenc.c.

Signed-off-by: Felix Paul Kühne <fkuehne at videolan.org>
---
 NEWS                         |    1 +
 configure.ac                 |   30 +
 modules/MODULES_LIST         |    1 +
 modules/Makefile.am          |    1 +
 modules/hw/nvenc/Makefile.am |   10 +
 modules/hw/nvenc/encoder.c   |  458 ++++++++++++++
 modules/hw/nvenc/nvenc.c     | 1383 ++++++++++++++++++++++++++++++++++++++++++
 modules/hw/nvenc/nvenc.h     |  261 ++++++++
 po/POTFILES.in               |    2 +
 9 files changed, 2147 insertions(+)
 create mode 100644 modules/hw/nvenc/Makefile.am
 create mode 100644 modules/hw/nvenc/encoder.c
 create mode 100644 modules/hw/nvenc/nvenc.c
 create mode 100644 modules/hw/nvenc/nvenc.h

diff --git a/NEWS b/NEWS
index 1c3e2f0..ff64b5f 100644
--- a/NEWS
+++ b/NEWS
@@ -134,6 +134,7 @@ Stream Output:
 Encoder:
  * Support for Daala video in 4:2:0 and 4:4:4
  * VP8 and VP9 encoder using libvpx
+ * Added NVENC for Nvidia CUDA HW encoder graphics cards
 
 Muxers:
  * Added fragmented/streamable MP4 muxer
diff --git a/configure.ac b/configure.ac
index 1842021..d84edac 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3119,6 +3119,36 @@ AS_IF([test "${have_vdpau}" = "yes" -a "${have_avcodec}" = "yes"], [
 ])
 AM_CONDITIONAL([HAVE_AVCODEC_VDPAU], [test "${have_avcodec_vdpau}" = "yes"])
 
+dnl
+dnl NVidia Encoder needs X11
+dnl
+AC_ARG_ENABLE(nvenc,
+  [AS_HELP_STRING([--enable-nvenc], [NVidia Encoder hardware support (default auto)])])
+AC_ARG_WITH([nvenc-sdk], [
+    AS_HELP_STRING([--with-nvenc-sdk=PATH],
+        [Path to NVidia Encoder SDK])])
+
+have_nvenc="no"
+AS_IF([test "${enable_nvenc}" != "no" -a "${enable_sout}" != "no"], [
+  AS_IF([test "${no_x}" = "yes"], [
+    AC_MSG_ERROR([NVenc requires Xlib (X11).])
+  ])
+  VLC_ADD_CFLAGS([nvenc], [-I${with_nvenc_sdk}/inc])
+  VLC_SAVE_FLAGS
+  CFLAGS="${CFLAGS} ${CFLAGS_nvenc}"
+  AC_CHECK_HEADERS(nvEncodeAPI.h, [
+      have_nvenc="yes"
+    ], [
+      AC_MSG_WARN([NVenc SDK not found.])
+    ])
+  VLC_RESTORE_FLAGS
+  AS_IF([test "${have_nvenc}" = "yes"], [
+    VLC_ADD_PLUGIN([nvenc])
+    VLC_ADD_CFLAGS([nvenc], [-I${with_nvenc_sdk}/inc])
+    VLC_ADD_LDFLAGS([nvenc],[-L${with_nvenc_sdk}/lib/linux/x86_64])
+  ])
+])
+AM_CONDITIONAL([HAVE_NVENC], [test "${have_nvenc}" = "yes"])
 
 dnl
 dnl  Wayland
diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 610b75c..4f60286 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -267,6 +267,7 @@ $Id$
  * nsv: NullSoft Video demuxer
  * ntservice: run VLC as a NT service
  * nuv: NUV demuxer
+ * nvenc: NVidia NVENC h264 hardware encoder
  * ogg: input module for OGG decapsulation
  * oggspots: an OggSpots video decoder/packetizer/encoder
  * oldmovie: oldmovie style video filter
diff --git a/modules/Makefile.am b/modules/Makefile.am
index a4a9977..76ff380 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -51,6 +51,7 @@ if ENABLE_SOUT
 include access_output/Makefile.am
 include mux/Makefile.am
 include stream_out/Makefile.am
+include hw/nvenc/Makefile.am
 endif
 
 BUILT_SOURCES += dummy.cpp
diff --git a/modules/hw/nvenc/Makefile.am b/modules/hw/nvenc/Makefile.am
new file mode 100644
index 0000000..6eae991
--- /dev/null
+++ b/modules/hw/nvenc/Makefile.am
@@ -0,0 +1,10 @@
+nvencdir = $(pluginsdir)/nvenc
+
+libnvenc_codec_plugin_la_SOURCES = hw/nvenc/encoder.c hw/nvenc/nvenc.c hw/nvenc/nvenc.h
+libnvenc_codec_plugin_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS_nvenc) $(X_CFLAGS)
+libnvenc_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAGS_nvenc) $(X_LIBS) $(X_PRE_LIBS) -lX11 \
+	$(LIBDL) $(LIBPTHREAD)
+libnvenc_codec_plugin_la_LIBADD = $(LIBS_nvenc)
+if HAVE_NVENC
+nvenc_LTLIBRARIES = libnvenc_codec_plugin.la
+endif
diff --git a/modules/hw/nvenc/encoder.c b/modules/hw/nvenc/encoder.c
new file mode 100644
index 0000000..d69482f
--- /dev/null
+++ b/modules/hw/nvenc/encoder.c
@@ -0,0 +1,458 @@
+/*****************************************************************************
+ * encoder.c: NVEnc-based hardware encoder plugin for NVidia CUDA graphics cards
+ *****************************************************************************
+ * Copyright © 2016 VideoLAN
+ * $Id$
+ *
+ * Authors: Jean-Paul Saman <jpsaman at videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <limits.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_sout.h>
+#include <vlc_codec.h>
+#include <vlc_charset.h>
+#include <vlc_cpu.h>
+
+#include <nvEncodeAPI.h>
+
+#include "nvenc.h"
+
+#include <assert.h>
+
+/* GPU */
+#define GPU_TEXT N_("GPU (default 0)")
+#define GPU_LONGTEXT N_( \
+    "Selects which NVENC capable GPU to use. First GPU is 0, second is 1, and so on.")
+
+/* Preset */
+#define PRESET_TEXT N_("Default preset setting used" )
+
+#define HRD_TEXT N_("HRD-timing information")
+
+/* Frame type options */
+#define KEYINT_TEXT N_("Maximum GOP size")
+#define KEYINT_LONGTEXT N_( "Sets maximum interval between IDR-frames." \
+    "Larger values save bits, thus improving quality for a given bitrate at " \
+    "the cost of seeking precision. Use -1 for infinite." )
+
+#define MIN_KEYINT_TEXT N_("Minimum GOP size")
+#define MIN_KEYINT_LONGTEXT N_( "Sets minimum interval between IDR-frames. " \
+    "In H.264, I-frames do not necessarily bound a closed GOP because it is " \
+    "allowable for a P-frame to be predicted from more frames than just the " \
+    "one frame before it (also see reference frame option). Therefore, " \
+    "I-frames are not necessarily seekable. IDR-frames restrict subsequent " \
+    "P-frames from referring to any frame prior to the IDR-frame. \n" \
+    "If scenecuts appear within this interval, they are still encoded as " \
+    "I-frames, but do not start a new GOP." )
+
+#define REF_TEXT N_("Number of reference frames")
+#define REF_LONGTEXT N_( "Number of previous frames used as predictors. " \
+    "This is effective in Anime, but seems to make little difference in " \
+    "live-action source material. Some decoders are unable to deal with " \
+    "large frameref values. Range 1 to 16." )
+
+#define FULLRANGE_TEXT N_("Use fullrange instead of TV colorrange")
+#define FULLRANGE_LONGTEXT N_("TV-range is usually used colorrange, defining this to true " \
+                              "will enable H.264 to use full colorrange on encoding")
+
+#define BFRAMES_TEXT N_("B-frames between I and P")
+#define BFRAMES_LONGTEXT N_( "Number of consecutive B-frames between I and " \
+    "P-frames. Range 1 to 16." )
+
+/* In order to play an interlaced output stream encoded by H.264, a decoder needs
+   mbaff support. r570 is using the 'mb' part and not 'aff' yet; so it's really
+   'pure-interlaced' mode */
+#define INTERLACED_TEXT N_("Interlaced mode")
+#define INTERLACED_LONGTEXT N_( "Pure-interlaced mode.")
+
+#define LEVEL_TEXT N_("H.264/HEVC level")
+#define LEVEL_LONGTEXT N_( "Specify H.264/HEVC level (as defined by Annex A " \
+    "of the standard). Levels are not enforced; it's up to the user to select " \
+    "a level compatible with the rest of the encoding options. Range 1 to 5.1 " \
+    "(10 to 51 is also allowed). Set to 0 for auto selection of level.")
+
+#define PROFILE_TEXT N_("H.264 profile")
+#define PROFILE_LONGTEXT N_("Specify H.264 profile which limits are enforced over " \
+        "other settings" )
+
+/* Ratecontrol */
+#define QP_TEXT N_("Set QP")
+#define QP_LONGTEXT N_( "This selects the quantizer to use. " \
+    "Lower values result in better fidelity, but higher bitrates. 26 is a " \
+    "good default value. Range 0 (lossless) to 51." )
+
+#define QPMIN_TEXT N_("Min QP")
+#define QPMIN_LONGTEXT N_( "Minimum quantizer parameter. 15 to 35 seems to " \
+    "be a useful range." )
+
+#define QPMAX_TEXT N_("Max QP")
+#define QPMAX_LONGTEXT N_( "Maximum quantizer parameter." )
+
+#define VBV_MAXRATE_TEXT N_("Max local bitrate")
+#define VBV_MAXRATE_LONGTEXT N_( "Sets a maximum local bitrate (in kbits/s).")
+
+#define VBV_BUFSIZE_TEXT N_("VBV buffer")
+#define VBV_BUFSIZE_LONGTEXT N_( "Averaging period for the maximum " \
+    "local bitrate (in kbits).")
+
+#define VBV_INIT_TEXT N_("Initial VBV buffer occupancy")
+#define VBV_INIT_LONGTEXT N_( "Sets the initial buffer occupancy as a " \
+    "fraction of the buffer size. Range 0.0 to 1.0.")
+
+/* IP Ratio < 1 is technically valid but should never improve quality */
+#define IPRATIO_TEXT N_("QP factor between I and P")
+#define IPRATIO_LONGTEXT N_( "QP factor between I and P. Range 1.0 to 2.0.")
+
+/* PB ratio < 1 is not valid and breaks ratecontrol */
+#define PBRATIO_TEXT N_("QP factor between P and B")
+#define PBRATIO_LONGTEXT N_( "QP factor between P and B. Range 1.0 to 2.0.")
+
+#define PASS_TEXT N_("Multipass ratecontrol")
+#define PASS_LONGTEXT N_( "Multipass ratecontrol:\n" \
+    " - 0: Disabled\n" \
+    " - 1: First pass, creates stats file\n" \
+    " - 2: Last pass, does not overwrite stats file\n" \
+    " - 3: Nth pass, overwrites stats file\n" )
+
+/* HEVC */
+#define TIER_TEXT N_("HEVC Tier (default main)")
+#define TIER_LONGTEXT N_("HEVC Tier list: main (default), high")
+
+/* local prototypes */
+static int Open(vlc_object_t *obj);
+static void Close(vlc_object_t *obj);
+static block_t *Encode(encoder_t *enc, picture_t *pic);
+
+#define SOUT_CFG_PREFIX "sout-nvenc-"
+
+static const char *const level_list[] = /* h264 and hevc */
+  { "auto", "0",
+    "1",  "1b", "1.1", "1.2", "1.3",
+    "2", "2.1", "2.2",
+    "3", "3.1", "3.2",
+    "4", "4.1", "4.2",
+    "5", "5.1", "5.2",
+    "6", "6.1", "6.2" };
+
+static const char *const profile_list[] = /* h264 */
+  { "baseline", "main", "high", "high444p" };
+
+static const char *const tier_list[] =
+  { "main", "high" };
+
+static const char *const preset_list[] =
+  { "slow", "medium", "fast", "hq", "hp", "bd", "ll", "llhq", "llhp",
+    "lossless", "losslesshp", "default" };
+
+static const char *const hrd_list[] =
+  { "cbr", "vbr" };
+
+vlc_module_begin()
+    set_shortname(N_("NVidia NVENC H264/HEVC encoder"))
+    set_description(N_("NVidia NVENC H264/HEVC hardware encoder for CUDA based graphics cards"))
+    set_capability("encoder", 0)
+    add_shortcut("nvenc")
+    set_callbacks(Open, Close)
+
+    add_integer( SOUT_CFG_PREFIX "gpu", 0, GPU_TEXT, GPU_LONGTEXT, true )
+        change_integer_range( 0, INT_MAX )
+
+    // h264 options (see x264.c)
+
+    add_string( SOUT_CFG_PREFIX "preset", "hq" , PRESET_TEXT , PRESET_TEXT, false )
+        change_string_list(preset_list, &preset_list)
+
+    add_string( SOUT_CFG_PREFIX "hrd", "none", HRD_TEXT, HRD_TEXT, true )
+        change_string_list(hrd_list, &hrd_list);
+
+    /* Frame type options */
+    add_integer( SOUT_CFG_PREFIX "keyint", 250, KEYINT_TEXT,
+                 KEYINT_LONGTEXT, false )
+
+    add_integer( SOUT_CFG_PREFIX "min-keyint", 25, MIN_KEYINT_TEXT,
+                 MIN_KEYINT_LONGTEXT, true )
+
+    add_bool( SOUT_CFG_PREFIX "fullrange", false, FULLRANGE_TEXT, FULLRANGE_LONGTEXT,
+              true )
+
+    add_integer( SOUT_CFG_PREFIX "ref", 3, REF_TEXT,
+                 REF_LONGTEXT, true )
+        change_integer_range( 1, 16 )
+
+    add_integer( SOUT_CFG_PREFIX "bframes", 3, BFRAMES_TEXT,
+                 BFRAMES_LONGTEXT, true )
+        change_integer_range( 0, 16 )
+
+    add_string( SOUT_CFG_PREFIX "level", "0", LEVEL_TEXT,
+               LEVEL_LONGTEXT, false )
+        change_string_list(level_list, &level_list)
+
+    add_string( SOUT_CFG_PREFIX "profile", "high", PROFILE_TEXT,
+               PROFILE_LONGTEXT, false )
+        change_string_list(profile_list, &profile_list)
+
+    add_bool( SOUT_CFG_PREFIX "interlaced", false, INTERLACED_TEXT, INTERLACED_LONGTEXT,
+              true )
+
+    /* Rate control */
+    add_integer( SOUT_CFG_PREFIX "qp", -1, QP_TEXT, QP_LONGTEXT,
+                 true )
+        change_integer_range( -1, 51 ) /* QP 0 -> lossless encoding */
+
+    add_integer( SOUT_CFG_PREFIX "qpmin", 10, QPMIN_TEXT,
+                 QPMIN_LONGTEXT, true )
+        change_integer_range( 0, 51 )
+
+    add_integer( SOUT_CFG_PREFIX "qpmax", 51, QPMAX_TEXT,
+                 QPMAX_LONGTEXT, true )
+        change_integer_range( 0, 51 )
+
+    add_integer( SOUT_CFG_PREFIX "vbv-maxrate", 0, VBV_MAXRATE_TEXT,
+                VBV_MAXRATE_LONGTEXT, true )
+
+    add_integer( SOUT_CFG_PREFIX "vbv-bufsize", 0, VBV_BUFSIZE_TEXT,
+                 VBV_BUFSIZE_LONGTEXT, true )
+
+    add_float( SOUT_CFG_PREFIX "vbv-init", 0.9, VBV_INIT_TEXT,
+               VBV_INIT_LONGTEXT, true )
+        change_float_range( 0, 1 )
+
+    add_float( SOUT_CFG_PREFIX "ipratio", 1.40, IPRATIO_TEXT,
+               IPRATIO_LONGTEXT, true )
+        change_float_range( 1, 2 )
+
+    add_float( SOUT_CFG_PREFIX "pbratio", 1.30, PBRATIO_TEXT,
+               PBRATIO_LONGTEXT, true )
+        change_float_range( 1, 2 )
+
+    add_integer( SOUT_CFG_PREFIX "pass", 0, PASS_TEXT,
+                 PASS_LONGTEXT, false )
+        change_integer_range( 0, 3 )
+
+    // hevc options (see x265.c)
+    add_string( SOUT_CFG_PREFIX "tier", "main", TIER_TEXT,
+                TIER_LONGTEXT, true )
+        change_string_list(tier_list, &tier_list)
+
+vlc_module_end()
+
+static const char *const ppsz_sout_options[] = {
+    /* GPU */
+    "gpu",
+
+    /* other */
+    "preset", "hrd",
+
+    /* Frame type options */
+    "bframes", "fullrange", "interlaced", "keyint", "min-keyint",
+    "level", "profile", "ref",
+
+
+    /* Rate control */
+    "ipratio", "pbratio",
+    "pass", "qp", "qpmin", "qpmax",
+    "vbv-maxrate", "vbv-bufsize", "vbv-init",
+
+    /* HEVC */
+    "tier",
+
+    NULL
+};
+
+static int Open(vlc_object_t *obj)
+{
+    encoder_t *enc = (encoder_t*)obj;
+    encoder_sys_t *sys;
+
+    if (enc->fmt_in.i_cat != VIDEO_ES)
+        return VLC_EGENERIC;
+
+    if ((enc->fmt_out.i_codec != VLC_CODEC_H264) &&
+        (enc->fmt_out.i_codec != VLC_CODEC_HEVC) &&
+        !enc->b_force)
+        return VLC_EGENERIC;
+
+    config_ChainParse( enc, SOUT_CFG_PREFIX, ppsz_sout_options, enc->p_cfg );
+
+    sys = calloc(1, sizeof(encoder_sys_t));
+    if (!sys)
+        return VLC_ENOMEM;
+    enc->p_sys = sys;
+
+    /* user specified settings */
+    NvencContext *ctx = &sys->ctx;
+    ctx->gpu = var_InheritInteger(enc, SOUT_CFG_PREFIX "gpu");
+    ctx->preset = var_InheritString(enc, SOUT_CFG_PREFIX "preset");
+    /* NOTE: hw seems to only support 2pass */
+    ctx->twopass = var_InheritInteger(enc, SOUT_CFG_PREFIX "pass");
+
+    char *hrd = var_InheritString(enc, SOUT_CFG_PREFIX "hrd");
+    if (strncmp(hrd, "cbr", 3) == 0)
+        ctx->cbr = true;
+    else
+        ctx->cbr = false;
+    free(hrd);
+
+    ctx->bit_rate = enc->fmt_out.i_bitrate;
+    ctx->keyint = var_InheritInteger(enc, SOUT_CFG_PREFIX "keyint");
+    ctx->min_keyint = var_InheritInteger(enc, SOUT_CFG_PREFIX "min-keyint");
+    ctx->gop_size = ctx->keyint;
+    ctx->refs = var_InheritInteger(enc, SOUT_CFG_PREFIX "ref");
+    ctx->max_b_frames = var_InheritInteger(enc, SOUT_CFG_PREFIX "bframes");
+    ctx->interlaced = var_InheritBool(enc, SOUT_CFG_PREFIX "interlaced");
+
+    /* We will repeat headers ourselfs if necessary */
+    ctx->repeat_headers = true;
+
+    /*  */
+    ctx->fullrange = var_InheritBool(enc, SOUT_CFG_PREFIX "fullrange");
+
+    char *profile = var_InheritString(enc, SOUT_CFG_PREFIX "profile");
+    if (profile) {
+        if (strcmp(profile, "baseline") == 0) {
+            ctx->profile = VLC_NVENC_H264_PROFILE_BASELINE;
+            enc->fmt_in.i_codec = VLC_CODEC_I420;
+        } else if (strcmp(profile, "main") == 0) {
+            ctx->profile = VLC_NVENC_H264_PROFILE_MAIN;
+            enc->fmt_in.i_codec = ctx->fullrange ? VLC_CODEC_NV12 : VLC_CODEC_I420;
+        } else if (strcmp(profile, "high") == 0) {
+            ctx->profile = VLC_NVENC_H264_PROFILE_HIGH;
+            enc->fmt_in.i_codec = ctx->fullrange ? VLC_CODEC_NV12 : VLC_CODEC_I420;
+        } else if (strcmp(profile, "high444") == 0) {
+            ctx->profile = VLC_NVENC_H264_PROFILE_HIGH_444_PREDICTIVE;
+            enc->fmt_in.i_codec = VLC_CODEC_I444;
+        } else {
+            ctx->profile = VLC_NVENC_H264_PROFILE_HIGH;
+            enc->fmt_in.i_codec = ctx->fullrange ? VLC_CODEC_NV12 : VLC_CODEC_I420;
+            msg_Err(enc, "Falling back to 'high' profile");
+        }
+        free(profile);
+    }
+
+    switch (enc->fmt_in.i_codec) {
+    case VLC_CODEC_I444:
+        ctx->color_trc = ctx->fullrange ? VLC_NVENC_TRC_BT2020_10 : VLC_NVENC_TRC_BT709;
+        ctx->colorspace = ctx->fullrange ? VLC_NVENC_SPC_BT2020_CL : VLC_NVENC_SPC_BT709;
+        ctx->color_primaries = ctx->fullrange ? VLC_NVENC_PRI_BT2020 : VLC_NVENC_PRI_BT709;
+        break;
+    case VLC_CODEC_I420:
+    case VLC_CODEC_YV12:
+    case VLC_FOURCC('I','Y','U','V'):
+        enc->fmt_in.i_codec = ctx->fullrange ? VLC_CODEC_J420 : VLC_CODEC_I420;
+    case VLC_CODEC_NV12:
+        ctx->color_trc = VLC_NVENC_TRC_BT709;
+        ctx->colorspace = VLC_NVENC_SPC_BT709;
+        ctx->color_primaries = VLC_NVENC_PRI_BT709;
+        break;
+    default:
+        enc->fmt_in.i_codec = ctx->fullrange ? VLC_CODEC_J420 : VLC_CODEC_I420;
+        ctx->color_trc = VLC_NVENC_TRC_BT709;
+        ctx->colorspace = VLC_NVENC_SPC_BT709;
+        ctx->color_primaries = VLC_NVENC_PRI_BT709;
+        break;
+    }
+
+    char *level = var_InheritString(enc, SOUT_CFG_PREFIX "level");
+    if (level) {
+        if (strcmp(level, "0") == 0)
+            ctx->level = strdup("auto");
+        else
+            ctx->level = strdup(level);
+        free(level);
+    }
+
+    /* rate control */
+    ctx->global_quality = var_InheritInteger(enc, SOUT_CFG_PREFIX "qp");
+    ctx->qmin = var_InheritInteger(enc, SOUT_CFG_PREFIX "qpmin");
+    ctx->qmax = var_InheritInteger(enc, SOUT_CFG_PREFIX "qpmax");
+    ctx->rc_buffer_size = var_InheritInteger(enc, SOUT_CFG_PREFIX "vbv-bufsize");
+    ctx->rc_max_rate = var_InheritInteger(enc, SOUT_CFG_PREFIX "vbv-maxrate");
+    ctx->rc_buffer_initial_delay = var_InheritFloat(enc,  SOUT_CFG_PREFIX "vbv-init");
+    ctx->b_quant_factor = var_InheritFloat(enc, SOUT_CFG_PREFIX "pbratio");
+    ctx->b_quant_offset = 1.25; /* FFMPEG default */
+    ctx->i_quant_factor = var_InheritFloat(enc, SOUT_CFG_PREFIX "ipratio");
+    ctx->i_quant_offset = 0.0; /* FFMPEG default */
+
+    /* Delay initializing the GPU till we have more information about
+     * the exact picture dimensions and format */
+    sys->b_nvenc_initialized = false;
+
+    /* Fill in callbacks */
+    enc->pf_encode_video = Encode;
+    enc->pf_encode_audio = NULL;
+    enc->pf_encode_sub = NULL;
+
+    return VLC_SUCCESS;
+}
+
+static void Close(vlc_object_t *obj)
+{
+    encoder_t *enc = (encoder_t *)obj;
+    encoder_sys_t *sys = enc->p_sys;
+
+    if (!sys) return;
+
+    int result = nvenc_encode_close(enc);
+    if (result != VLC_SUCCESS)
+        msg_Err(enc, "NVENC: failed to shutdown.");
+
+    NvencContext *ctx = &sys->ctx;
+    free(ctx->preset);
+    free(ctx->level);
+    free(sys);
+}
+
+static block_t *Encode(encoder_t *enc, picture_t *pic)
+{
+    encoder_sys_t *sys = enc->p_sys;
+
+    if (!sys->b_nvenc_initialized) {
+        /* setup hardware */
+        int result = nvenc_encode_init(enc);
+        if (result != VLC_SUCCESS) {
+            msg_Err(enc, "Failed to initialize the GPU");
+            return NULL;
+        }
+        sys->b_nvenc_initialized = true;
+    }
+
+    if (likely(pic)) {
+        switch (pic->format.i_chroma) {
+        case VLC_CODEC_VDPAU_VIDEO_420:
+        case VLC_CODEC_VDPAU_VIDEO_422:
+        case VLC_CODEC_VDPAU_VIDEO_444:
+            msg_Err(enc, "NVidia hardware surfaces are not supported as input format. Disable hardware decoding.");
+            return NULL;
+        default:
+            /* nothing todo */
+            break;
+        }
+    }
+
+    /* pic == NULL indicates EOS */
+    return nvenc_encode_frame(enc, pic);
+}
diff --git a/modules/hw/nvenc/nvenc.c b/modules/hw/nvenc/nvenc.c
new file mode 100644
index 0000000..50aa5bf
--- /dev/null
+++ b/modules/hw/nvenc/nvenc.c
@@ -0,0 +1,1383 @@
+/*****************************************************************************
+ * nvec.c: NVEnc-based hardware encoder plugin for NVidia CUDA graphics cards
+ *****************************************************************************
+ * Copyright © 2016 VideoLAN
+ * $Id$
+ *
+ * Authors: Jean-Paul Saman <jpsaman at videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*
+ * This file is in part based on FFmpeg v3.0 nvenc.c.
+ *
+ * H.264 hardware encoding using nvidia nvenc
+ * Copyright (c) 2014 Timo Rothenpieler <timo at rothenpieler.org>
+ *
+ */
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_sout.h>
+#include <vlc_codec.h>
+#include <vlc_charset.h>
+#include <vlc_cpu.h>
+
+#include <math.h>
+
+#include <nvEncodeAPI.h>
+#include "nvenc.h"
+
+#include <assert.h>
+
+#if defined(_WIN32)
+#define LOAD_FUNC(l, s) GetProcAddress(l, s)
+#define DL_OPEN_FUNC(l) LoadLibrary(l)
+#define DL_CLOSE_FUNC(l) FreeLibrary(l)
+#else
+#define LOAD_FUNC(l, s) dlsym(l, s)
+#define DL_CLOSE_FUNC(l) dlclose(l)
+#endif
+
+typedef NVENCSTATUS (NVENCAPI* PNVENCODEAPICREATEINSTANCE)(NV_ENCODE_API_FUNCTION_LIST *functionList);
+
+typedef struct NvencValuePair
+{
+    const char *str;
+    uint32_t num;
+} NvencValuePair;
+
+static const NvencValuePair nvenc_h264_level_pairs[] = {
+    { "auto", NV_ENC_LEVEL_AUTOSELECT },
+    { "1"   , NV_ENC_LEVEL_H264_1     },
+    { "1.0" , NV_ENC_LEVEL_H264_1     },
+    { "1b"  , NV_ENC_LEVEL_H264_1b    },
+    { "1.0b", NV_ENC_LEVEL_H264_1b    },
+    { "1.1" , NV_ENC_LEVEL_H264_11    },
+    { "1.2" , NV_ENC_LEVEL_H264_12    },
+    { "1.3" , NV_ENC_LEVEL_H264_13    },
+    { "2"   , NV_ENC_LEVEL_H264_2     },
+    { "2.0" , NV_ENC_LEVEL_H264_2     },
+    { "2.1" , NV_ENC_LEVEL_H264_21    },
+    { "2.2" , NV_ENC_LEVEL_H264_22    },
+    { "3"   , NV_ENC_LEVEL_H264_3     },
+    { "3.0" , NV_ENC_LEVEL_H264_3     },
+    { "3.1" , NV_ENC_LEVEL_H264_31    },
+    { "3.2" , NV_ENC_LEVEL_H264_32    },
+    { "4"   , NV_ENC_LEVEL_H264_4     },
+    { "4.0" , NV_ENC_LEVEL_H264_4     },
+    { "4.1" , NV_ENC_LEVEL_H264_41    },
+    { "4.2" , NV_ENC_LEVEL_H264_42    },
+    { "5"   , NV_ENC_LEVEL_H264_5     },
+    { "5.0" , NV_ENC_LEVEL_H264_5     },
+    { "5.1" , NV_ENC_LEVEL_H264_51    },
+    { NULL }
+};
+
+static const NvencValuePair nvenc_hevc_level_pairs[] = {
+    { "auto", NV_ENC_LEVEL_AUTOSELECT },
+    { "1"   , NV_ENC_LEVEL_HEVC_1     },
+    { "1.0" , NV_ENC_LEVEL_HEVC_1     },
+    { "2"   , NV_ENC_LEVEL_HEVC_2     },
+    { "2.0" , NV_ENC_LEVEL_HEVC_2     },
+    { "2.1" , NV_ENC_LEVEL_HEVC_21    },
+    { "3"   , NV_ENC_LEVEL_HEVC_3     },
+    { "3.0" , NV_ENC_LEVEL_HEVC_3     },
+    { "3.1" , NV_ENC_LEVEL_HEVC_31    },
+    { "4"   , NV_ENC_LEVEL_HEVC_4     },
+    { "4.0" , NV_ENC_LEVEL_HEVC_4     },
+    { "4.1" , NV_ENC_LEVEL_HEVC_41    },
+    { "5"   , NV_ENC_LEVEL_HEVC_5     },
+    { "5.0" , NV_ENC_LEVEL_HEVC_5     },
+    { "5.1" , NV_ENC_LEVEL_HEVC_51    },
+    { "5.2" , NV_ENC_LEVEL_HEVC_52    },
+    { "6"   , NV_ENC_LEVEL_HEVC_6     },
+    { "6.0" , NV_ENC_LEVEL_HEVC_6     },
+    { "6.1" , NV_ENC_LEVEL_HEVC_61    },
+    { "6.2" , NV_ENC_LEVEL_HEVC_62    },
+    { NULL }
+};
+
+static int input_string_to_uint32(const NvencValuePair *pair, const char *input, uint32_t *output)
+{
+    for (; pair->str; ++pair) {
+        if (!strcmp(input, pair->str)) {
+            *output = pair->num;
+            return 0;
+        }
+    }
+
+    return VLC_EBADVAR;
+}
+
+static NvencData* data_queue_dequeue(NvencDataList* queue)
+{
+    uint32_t mask;
+    uint32_t read_pos;
+
+    assert(queue);
+    assert(queue->size);
+    assert(queue->data);
+
+    if (!queue->count)
+        return NULL;
+
+    /* Size always is a multiple of two */
+    mask = queue->size - 1;
+    read_pos = (queue->pos - queue->count) & mask;
+    queue->count--;
+
+    return &queue->data[read_pos];
+}
+
+static int data_queue_enqueue(NvencDataList* queue, NvencData *data)
+{
+    NvencDataList new_queue;
+    NvencData* tmp_data;
+    uint32_t mask;
+
+    if (!queue->size) {
+        /* size always has to be a multiple of two */
+        queue->size = 4;
+        queue->pos = 0;
+        queue->count = 0;
+
+        queue->data = malloc(queue->size * sizeof(*(queue->data)));
+
+        if (!queue->data) {
+            queue->size = 0;
+            return VLC_ENOMEM;
+        }
+    }
+
+    if (queue->count == queue->size) {
+        new_queue.size = queue->size << 1;
+        new_queue.pos = 0;
+        new_queue.count = 0;
+        new_queue.data = malloc(new_queue.size * sizeof(*(queue->data)));
+
+        if (!new_queue.data)
+            return VLC_ENOMEM;
+
+        while((tmp_data = data_queue_dequeue(queue)))
+            data_queue_enqueue(&new_queue, tmp_data);
+
+        free(queue->data);
+        *queue = new_queue;
+    }
+
+    mask = queue->size - 1;
+
+    queue->data[queue->pos] = *data;
+    queue->pos = (queue->pos + 1) & mask;
+    queue->count++;
+
+    return 0;
+}
+
+static int out_surf_queue_enqueue(NvencDataList* queue, NvencOutputSurface* surface)
+{
+    NvencData data;
+    data.u.surface = surface;
+
+    return data_queue_enqueue(queue, &data);
+}
+
+static NvencOutputSurface* out_surf_queue_dequeue(NvencDataList* queue)
+{
+    NvencData* res = data_queue_dequeue(queue);
+
+    if (!res)
+        return NULL;
+
+    return res->u.surface;
+}
+
+static int timestamp_queue_enqueue(NvencDataList* queue, int64_t timestamp)
+{
+    NvencData data;
+    data.u.timestamp = timestamp;
+
+    return data_queue_enqueue(queue, &data);
+}
+
+static int64_t timestamp_queue_dequeue(NvencDataList* queue)
+{
+    NvencData* res = data_queue_dequeue(queue);
+    if (!res)
+        return VLC_TS_INVALID;
+
+    return res->u.timestamp;
+}
+
+#define CHECK_LOAD_FUNC(t, f, s) \
+do { \
+    (f) = (t)LOAD_FUNC(dl_fn->cuda_lib, s); \
+    if (!(f)) { \
+        msg_Err(enc, "Failed loading %s from CUDA library", s); \
+        goto error; \
+    } \
+} while (0)
+
+static bool nvenc_dyload_cuda(encoder_t *enc)
+{
+    encoder_sys_t *sys = enc->p_sys;
+    NvencContext *ctx = &sys->ctx;
+    NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
+
+    if (dl_fn->cuda_lib)
+        return true;
+
+#if defined(_WIN32)
+    dl_fn->cuda_lib = DL_OPEN_FUNC(TEXT("nvcuda.dll"));
+#else
+    dl_fn->cuda_lib = dlopen("libcuda.so", RTLD_LAZY);
+#endif
+
+    if (!dl_fn->cuda_lib) {
+        msg_Err(enc, "Failed loading CUDA library");
+        goto error;
+    }
+
+    CHECK_LOAD_FUNC(PCUINIT, dl_fn->cu_init, "cuInit");
+    CHECK_LOAD_FUNC(PCUDEVICEGETCOUNT, dl_fn->cu_device_get_count, "cuDeviceGetCount");
+    CHECK_LOAD_FUNC(PCUDEVICEGET, dl_fn->cu_device_get, "cuDeviceGet");
+    CHECK_LOAD_FUNC(PCUDEVICEGETNAME, dl_fn->cu_device_get_name, "cuDeviceGetName");
+    CHECK_LOAD_FUNC(PCUDEVICECOMPUTECAPABILITY, dl_fn->cu_device_compute_capability, "cuDeviceComputeCapability");
+    CHECK_LOAD_FUNC(PCUCTXCREATE, dl_fn->cu_ctx_create, "cuCtxCreate_v2");
+    CHECK_LOAD_FUNC(PCUCTXPOPCURRENT, dl_fn->cu_ctx_pop_current, "cuCtxPopCurrent_v2");
+    CHECK_LOAD_FUNC(PCUCTXDESTROY, dl_fn->cu_ctx_destroy, "cuCtxDestroy_v2");
+
+    return true;
+
+error:
+
+    if (dl_fn->cuda_lib)
+        DL_CLOSE_FUNC(dl_fn->cuda_lib);
+
+    dl_fn->cuda_lib = NULL;
+
+    return false;
+}
+
+static int check_cuda_errors(encoder_t *enc, CUresult err, const char *func)
+{
+    if (err != CU_SUCCESS) {
+        msg_Err(enc, ">> %s - failed with error code 0x%x", func, err);
+        return 0;
+    }
+    return 1;
+}
+#define check_cuda_errors(f) if (!check_cuda_errors(enc, f, #f)) goto error
+
+static bool nvenc_check_cuda(encoder_t *enc)
+{
+    encoder_sys_t *sys = enc->p_sys;
+
+    int device_count = 0;
+    CUdevice cu_device = 0;
+    char gpu_name[128];
+    int smminor = 0, smmajor = 0;
+    int i, smver, target_smver;
+
+    NvencContext *ctx = &sys->ctx;
+    NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
+
+    switch (enc->fmt_out.i_codec) {
+    case VLC_CODEC_H264:
+        target_smver = (enc->fmt_out.video.i_chroma == VLC_CODEC_I444) ? 0x52 : 0x30;
+        break;
+    case VLC_CODEC_HEVC:
+        target_smver = 0x52;
+        break;
+    default:
+        msg_Err(enc, "Unknown codec name");
+        goto error;
+    }
+
+    if (!nvenc_dyload_cuda(enc))
+        return false;
+
+    if (dl_fn->nvenc_device_count > 0)
+        return true;
+
+    check_cuda_errors(dl_fn->cu_init(0));
+
+    check_cuda_errors(dl_fn->cu_device_get_count(&device_count));
+    if (!device_count) {
+        msg_Err(enc, "No CUDA capable devices found");
+        goto error;
+    }
+
+    msg_Info(enc, "%d CUDA capable devices found", device_count);
+
+    dl_fn->nvenc_device_count = 0;
+
+    for (i = 0; i < device_count; ++i) {
+        check_cuda_errors(dl_fn->cu_device_get(&cu_device, i));
+        check_cuda_errors(dl_fn->cu_device_get_name(gpu_name, sizeof(gpu_name), cu_device));
+        check_cuda_errors(dl_fn->cu_device_compute_capability(&smmajor, &smminor, cu_device));
+
+        smver = (smmajor << 4) | smminor;
+
+        msg_Info(enc, "[ GPU #%d - < %s > has Compute SM %d.%d, NVENC %s ]",
+                 i, gpu_name, smmajor, smminor,
+                 (smver >= target_smver) ? "Available" : "Not Available");
+
+        if (smver >= target_smver)
+            dl_fn->nvenc_devices[dl_fn->nvenc_device_count++] = cu_device;
+    }
+
+    if (!dl_fn->nvenc_device_count) {
+        msg_Err(enc, "No NVENC capable devices found");
+        goto error;
+    }
+
+    return true;
+
+error:
+    dl_fn->nvenc_device_count = 0;
+    return false;
+}
+
+static bool nvenc_dyload_nvenc(encoder_t *enc)
+{
+    encoder_sys_t *sys = enc->p_sys;
+
+    PNVENCODEAPICREATEINSTANCE nvEncodeAPICreateInstance = 0;
+    NVENCSTATUS nvstatus;
+
+    NvencContext *ctx = &sys->ctx;
+    NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
+
+    if (!nvenc_check_cuda(enc))
+        return false;
+
+    if (dl_fn->nvenc_lib)
+        return true;
+
+#if defined(_WIN32)
+    if (sizeof(void*) == 8) {
+        dl_fn->nvenc_lib = DL_OPEN_FUNC(TEXT("nvEncodeAPI64.dll"));
+    } else {
+        dl_fn->nvenc_lib = DL_OPEN_FUNC(TEXT("nvEncodeAPI.dll"));
+    }
+#else
+    dl_fn->nvenc_lib = dlopen("libnvidia-encode.so.1", RTLD_LAZY);
+#endif
+
+    if (!dl_fn->nvenc_lib) {
+        msg_Err(enc, "Failed loading the nvenc library");
+        goto error;
+    }
+
+    nvEncodeAPICreateInstance = (PNVENCODEAPICREATEINSTANCE)LOAD_FUNC(dl_fn->nvenc_lib, "NvEncodeAPICreateInstance");
+    if (!nvEncodeAPICreateInstance) {
+        msg_Err(enc, "Failed to load nvenc entrypoint");
+        goto error;
+    }
+
+    dl_fn->nvenc_funcs.version = NV_ENCODE_API_FUNCTION_LIST_VER;
+
+    nvstatus = nvEncodeAPICreateInstance(&dl_fn->nvenc_funcs);
+
+    if (nvstatus != NV_ENC_SUCCESS) {
+        msg_Err(enc, "Failed to create nvenc instance");
+        goto error;
+    }
+
+    msg_Dbg(enc, "Nvenc instance created successfully");
+    return true;
+
+error:
+    if (dl_fn->nvenc_lib)
+        DL_CLOSE_FUNC(dl_fn->nvenc_lib);
+
+    dl_fn->nvenc_lib = NULL;
+    return false;
+}
+
+static void nvenc_unload_nvenc(encoder_t *enc)
+{
+    encoder_sys_t *sys = enc->p_sys;
+    NvencContext *ctx = &sys->ctx;
+    NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
+
+    DL_CLOSE_FUNC(dl_fn->nvenc_lib);
+    dl_fn->nvenc_lib = NULL;
+
+    dl_fn->nvenc_device_count = 0;
+
+    DL_CLOSE_FUNC(dl_fn->cuda_lib);
+    dl_fn->cuda_lib = NULL;
+
+    dl_fn->cu_init = NULL;
+    dl_fn->cu_device_get_count = NULL;
+    dl_fn->cu_device_get = NULL;
+    dl_fn->cu_device_get_name = NULL;
+    dl_fn->cu_device_compute_capability = NULL;
+    dl_fn->cu_ctx_create = NULL;
+    dl_fn->cu_ctx_pop_current = NULL;
+    dl_fn->cu_ctx_destroy = NULL;
+
+    msg_Dbg(enc, "Nvenc instance destroyed");
+}
+
+int nvenc_encode_init(encoder_t *enc)
+{
+    encoder_sys_t *sys = enc->p_sys;
+
+    NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encode_session_params = { 0 };
+    NV_ENC_PRESET_CONFIG preset_config = { 0 };
+    CUcontext cu_context_curr;
+    CUresult cu_res;
+    GUID encoder_preset = NV_ENC_PRESET_HQ_GUID;
+    GUID codec;
+    NVENCSTATUS nv_status = NV_ENC_SUCCESS;
+    // AVCPBProperties *cpb_props;/* VLC does not have a sidedata concept */
+    uint32_t surfaceCount = 0;
+    int num_mbs;
+    int isLL = 0;
+    int lossless = 0;
+    int res = VLC_SUCCESS;
+    unsigned int dw, dh;
+    int qp_inter_p;
+
+    NvencContext *ctx = &sys->ctx;
+    NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
+    NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
+
+    if (!nvenc_dyload_nvenc(enc))
+        return VLC_EGENERIC;
+
+    ctx->last_dts = VLC_TS_INVALID;
+
+    ctx->encode_config.version = NV_ENC_CONFIG_VER;
+    ctx->init_encode_params.version = NV_ENC_INITIALIZE_PARAMS_VER;
+    preset_config.version = NV_ENC_PRESET_CONFIG_VER;
+    preset_config.presetCfg.version = NV_ENC_CONFIG_VER;
+    encode_session_params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
+    encode_session_params.apiVersion = NVENCAPI_VERSION;
+
+    if (ctx->gpu >= dl_fn->nvenc_device_count) {
+        msg_Err(enc, "Requested GPU %d, but only %d GPUs are available!",
+                ctx->gpu, dl_fn->nvenc_device_count);
+        res = VLC_EBADVAR;
+        goto error;
+    }
+
+    ctx->cu_context = NULL;
+    cu_res = dl_fn->cu_ctx_create(&ctx->cu_context, 4, dl_fn->nvenc_devices[ctx->gpu]); // CU_CTX_SCHED_BLOCKING_SYNC=4, avoid CPU spins
+    if (cu_res != CU_SUCCESS) {
+        msg_Err(enc, "Failed creating CUDA context for NVENC: 0x%x", (int)cu_res);
+        res = VLC_EGENERIC;
+        goto error;
+    }
+
+    cu_res = dl_fn->cu_ctx_pop_current(&cu_context_curr);
+    if (cu_res != CU_SUCCESS) {
+        msg_Err(enc, "Failed popping CUDA context: 0x%x", (int)cu_res);
+        res = VLC_EGENERIC;
+        goto error;
+    }
+
+    encode_session_params.device = ctx->cu_context;
+    encode_session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
+
+    nv_status = p_nvenc->nvEncOpenEncodeSessionEx(&encode_session_params, &ctx->nvencoder);
+    if (nv_status != NV_ENC_SUCCESS) {
+        ctx->nvencoder = NULL;
+        msg_Err(enc, "OpenEncodeSessionEx failed: 0x%x", (int)nv_status);
+        res = VLC_EGENERIC;
+        goto error;
+    }
+
+    if (ctx->preset) {
+        if (!strcmp(ctx->preset, "slow")) {
+            encoder_preset = NV_ENC_PRESET_HQ_GUID;
+            ctx->twopass = 1;
+        } else if (!strcmp(ctx->preset, "medium")) {
+            encoder_preset = NV_ENC_PRESET_HQ_GUID;
+            ctx->twopass = 0;
+        } else if (!strcmp(ctx->preset, "fast")) {
+            encoder_preset = NV_ENC_PRESET_HP_GUID;
+            ctx->twopass = 0;
+        } else if (!strcmp(ctx->preset, "hq")) {
+            encoder_preset = NV_ENC_PRESET_HQ_GUID;
+        } else if (!strcmp(ctx->preset, "hp")) {
+            encoder_preset = NV_ENC_PRESET_HP_GUID;
+        } else if (!strcmp(ctx->preset, "bd")) {
+            encoder_preset = NV_ENC_PRESET_BD_GUID;
+        } else if (!strcmp(ctx->preset, "ll")) {
+            encoder_preset = NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID;
+            isLL = 1;
+        } else if (!strcmp(ctx->preset, "llhp")) {
+            encoder_preset = NV_ENC_PRESET_LOW_LATENCY_HP_GUID;
+            isLL = 1;
+        } else if (!strcmp(ctx->preset, "llhq")) {
+            encoder_preset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID;
+            isLL = 1;
+        } else if (!strcmp(ctx->preset, "lossless")) {
+            encoder_preset = NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID;
+            lossless = 1;
+        } else if (!strcmp(ctx->preset, "losslesshp")) {
+            encoder_preset = NV_ENC_PRESET_LOSSLESS_HP_GUID;
+            lossless = 1;
+        } else if (!strcmp(ctx->preset, "default")) {
+            encoder_preset = NV_ENC_PRESET_DEFAULT_GUID;
+        } else {
+            msg_Err(enc, "Preset \"%s\" is unknown! Supported presets: slow, medium, high, hp, hq, bd, ll, llhp, llhq, lossless, losslesshp, default", ctx->preset);
+            res = VLC_EBADVAR;
+            goto error;
+        }
+    }
+
+    if (ctx->twopass < 0) {
+        ctx->twopass = isLL;
+    }
+
+    switch (enc->fmt_out.i_codec) {
+    case VLC_CODEC_H264:
+        codec = NV_ENC_CODEC_H264_GUID;
+        break;
+    case VLC_CODEC_HEVC:
+        codec = NV_ENC_CODEC_HEVC_GUID;
+        break;
+    default:
+        msg_Err(enc, "Unknown codec name");
+        res = VLC_EBADVAR;
+        goto error;
+    }
+
+    nv_status = p_nvenc->nvEncGetEncodePresetConfig(ctx->nvencoder, codec, encoder_preset, &preset_config);
+    if (nv_status != NV_ENC_SUCCESS) {
+        msg_Err(enc, "GetEncodePresetConfig failed: 0x%x", (int)nv_status);
+        res = VLC_EGENERIC;
+        goto error;
+    }
+
+    ctx->init_encode_params.encodeGUID = codec;
+    ctx->init_encode_params.encodeHeight = enc->fmt_out.video.i_height;
+    ctx->init_encode_params.encodeWidth = enc->fmt_out.video.i_width;
+    ctx->init_encode_params.maxEncodeWidth  = 0; /* no dynamic size change */
+    ctx->init_encode_params.maxEncodeHeight = 0; /* no dynamic size change */
+
+    if (enc->fmt_out.video.i_sar_num && enc->fmt_out.video.i_sar_den &&
+        (enc->fmt_out.video.i_sar_num != 1 || enc->fmt_out.video.i_sar_den != 1)) {
+        vlc_ureduce(&dw, &dh,
+                    enc->fmt_out.video.i_height * enc->fmt_out.video.i_sar_num,
+                    enc->fmt_out.video.i_width * enc->fmt_out.video.i_sar_den,
+                    1024 * 1024);
+        ctx->init_encode_params.darHeight = dh;
+        ctx->init_encode_params.darWidth = dw;
+    } else {
+        ctx->init_encode_params.darHeight = enc->fmt_out.video.i_height;
+        ctx->init_encode_params.darWidth = enc->fmt_out.video.i_width;
+    }
+
+    // De-compensate for hardware, dubiously, trying to compensate for
+    // playback at 704 pixel width.
+    if (enc->fmt_out.video.i_width == 720 &&
+        (enc->fmt_out.video.i_height == 480 || enc->fmt_out.video.i_height == 576)) {
+        vlc_ureduce(&dw, &dh,
+                    ctx->init_encode_params.darWidth * 44,
+                    ctx->init_encode_params.darHeight * 45,
+                    1024 * 1024);
+        ctx->init_encode_params.darHeight = dh;
+        ctx->init_encode_params.darWidth = dw;
+    }
+
+    ctx->init_encode_params.frameRateNum = enc->fmt_out.video.i_frame_rate;
+    ctx->init_encode_params.frameRateDen = enc->fmt_out.video.i_frame_rate_base;
+
+    num_mbs = ((enc->fmt_out.video.i_width + 15) >> 4) * ((enc->fmt_out.video.i_height + 15) >> 4);
+    ctx->max_surface_count = (num_mbs >= 8160) ? 32 : 48;
+
+    if (ctx->buffer_delay >= ctx->max_surface_count)
+        ctx->buffer_delay = ctx->max_surface_count - 1;
+
+    ctx->init_encode_params.enableEncodeAsync = 0;
+    ctx->init_encode_params.enablePTD = 1;
+
+    ctx->init_encode_params.presetGUID = encoder_preset;
+
+    ctx->init_encode_params.encodeConfig = &ctx->encode_config;
+    memcpy(&ctx->encode_config, &preset_config.presetCfg, sizeof(ctx->encode_config));
+    ctx->encode_config.version = NV_ENC_CONFIG_VER;
+
+    if (ctx->refs >= 0) {
+        /* 0 means "let the hardware decide" */
+        switch (enc->fmt_out.i_codec) {
+        case VLC_CODEC_H264:
+            ctx->encode_config.encodeCodecConfig.h264Config.maxNumRefFrames = ctx->refs;
+            break;
+        case VLC_CODEC_HEVC:
+            ctx->encode_config.encodeCodecConfig.hevcConfig.maxNumRefFramesInDPB = ctx->refs;
+            break;
+        /* Earlier switch/case will return if unknown codec is passed. */
+        }
+    }
+
+    if (ctx->gop_size > 0) {
+        if (ctx->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. */
+            ctx->encode_config.frameIntervalP = ctx->max_b_frames + 1;
+        }
+
+        ctx->encode_config.gopLength = ctx->gop_size;
+        switch (enc->fmt_out.i_codec) {
+        case VLC_CODEC_H264:
+            ctx->encode_config.encodeCodecConfig.h264Config.idrPeriod = ctx->gop_size;
+            break;
+        case VLC_CODEC_HEVC:
+            ctx->encode_config.encodeCodecConfig.hevcConfig.idrPeriod = ctx->gop_size;
+            break;
+        /* Earlier switch/case will return if unknown codec is passed. */
+        }
+    } else if (ctx->gop_size == 0) {
+        ctx->encode_config.frameIntervalP = 0;
+        ctx->encode_config.gopLength = 1;
+        switch (enc->fmt_out.i_codec) {
+        case VLC_CODEC_H264:
+            ctx->encode_config.encodeCodecConfig.h264Config.idrPeriod = 1;
+            break;
+        case VLC_CODEC_HEVC:
+            ctx->encode_config.encodeCodecConfig.hevcConfig.idrPeriod = 1;
+            break;
+        /* Earlier switch/case will return if unknown codec is passed. */
+        }
+    }
+
+    /* when there're b frames, set dts offset */
+    if (ctx->encode_config.frameIntervalP >= 2)
+        ctx->last_dts = -2;
+
+    if (ctx->bit_rate > 0) {
+        ctx->encode_config.rcParams.averageBitRate = ctx->bit_rate;
+    } else if (ctx->encode_config.rcParams.averageBitRate > 0) {
+        ctx->encode_config.rcParams.maxBitRate = ctx->encode_config.rcParams.averageBitRate;
+    }
+
+    if (ctx->rc_max_rate > 0)
+        ctx->encode_config.rcParams.maxBitRate = ctx->rc_max_rate;
+
+    if (ctx->rc_buffer_initial_delay > 0)
+        ctx->encode_config.rcParams.vbvInitialDelay = ctx->rc_buffer_initial_delay;
+
+    if (lossless) {
+        if (enc->fmt_out.i_codec == VLC_CODEC_H264)
+            ctx->encode_config.encodeCodecConfig.h264Config.qpPrimeYZeroTransformBypassFlag = 1;
+
+        ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
+        ctx->encode_config.rcParams.constQP.qpInterB = 0;
+        ctx->encode_config.rcParams.constQP.qpInterP = 0;
+        ctx->encode_config.rcParams.constQP.qpIntra = 0;
+
+        ctx->qmin = -1;
+        ctx->qmax = -1;
+    } else if (ctx->cbr) {
+        if (!ctx->twopass) {
+            ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR;
+        } else {
+            ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_2_PASS_QUALITY;
+
+            if (enc->fmt_out.i_codec == VLC_CODEC_H264) {
+                ctx->encode_config.encodeCodecConfig.h264Config.adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE;
+                ctx->encode_config.encodeCodecConfig.h264Config.fmoMode = NV_ENC_H264_FMO_DISABLE;
+            }
+        }
+    } else if (ctx->global_quality > 0) {
+        ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
+        ctx->encode_config.rcParams.constQP.qpInterB = ctx->global_quality;
+        ctx->encode_config.rcParams.constQP.qpInterP = ctx->global_quality;
+        ctx->encode_config.rcParams.constQP.qpIntra = ctx->global_quality;
+
+        ctx->qmin = -1;
+        ctx->qmax = -1;
+    } else {
+        if (ctx->qmin >= 0 && ctx->qmax >= 0) {
+            ctx->encode_config.rcParams.enableMinQP = 1;
+            ctx->encode_config.rcParams.enableMaxQP = 1;
+
+            ctx->encode_config.rcParams.minQP.qpInterB = ctx->qmin;
+            ctx->encode_config.rcParams.minQP.qpInterP = ctx->qmin;
+            ctx->encode_config.rcParams.minQP.qpIntra = ctx->qmin;
+
+            ctx->encode_config.rcParams.maxQP.qpInterB = ctx->qmax;
+            ctx->encode_config.rcParams.maxQP.qpInterP = ctx->qmax;
+            ctx->encode_config.rcParams.maxQP.qpIntra = ctx->qmax;
+
+            qp_inter_p = (ctx->qmax + 3 * ctx->qmin) / 4; // biased towards Qmin
+
+            if (ctx->twopass) {
+                ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_2_PASS_VBR;
+                if (enc->fmt_out.i_codec == VLC_CODEC_H264) {
+                    ctx->encode_config.encodeCodecConfig.h264Config.adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE;
+                    ctx->encode_config.encodeCodecConfig.h264Config.fmoMode = NV_ENC_H264_FMO_DISABLE;
+                }
+            } else {
+                ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_VBR_MINQP;
+            }
+        } else {
+            qp_inter_p = 26; // default to 26
+
+            if (ctx->twopass) {
+                ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_2_PASS_VBR;
+            } else {
+                ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_VBR;
+            }
+        }
+
+        ctx->encode_config.rcParams.enableInitialRCQP = 1;
+        ctx->encode_config.rcParams.initialRCQP.qpInterP  = qp_inter_p;
+
+        if(ctx->i_quant_factor != 0.0 && ctx->b_quant_factor != 0.0) {
+            ctx->encode_config.rcParams.initialRCQP.qpIntra = VLC_CLIP(
+                qp_inter_p * fabs(ctx->i_quant_factor) + ctx->i_quant_offset, 0, 51);
+            ctx->encode_config.rcParams.initialRCQP.qpInterB = VLC_CLIP(
+                qp_inter_p * fabs(ctx->b_quant_factor) + ctx->b_quant_offset, 0, 51);
+        } else {
+            ctx->encode_config.rcParams.initialRCQP.qpIntra = qp_inter_p;
+            ctx->encode_config.rcParams.initialRCQP.qpInterB = qp_inter_p;
+        }
+    }
+
+    if (ctx->rc_buffer_size > 0) {
+        ctx->encode_config.rcParams.vbvBufferSize = ctx->rc_buffer_size;
+    } else if (ctx->encode_config.rcParams.averageBitRate > 0) {
+        ctx->encode_config.rcParams.vbvBufferSize = 2 * ctx->encode_config.rcParams.averageBitRate;
+    }
+
+    ctx->encode_config.frameFieldMode = ctx->interlaced ?
+                NV_ENC_PARAMS_FRAME_FIELD_MODE_FIELD :
+                NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME;
+
+    switch (enc->fmt_out.i_codec) {
+    case VLC_CODEC_H264:
+        ctx->encode_config.encodeCodecConfig.h264Config.h264VUIParameters.colourDescriptionPresentFlag = 1;
+        ctx->encode_config.encodeCodecConfig.h264Config.h264VUIParameters.videoSignalTypePresentFlag = 1;
+
+        ctx->encode_config.encodeCodecConfig.h264Config.h264VUIParameters.colourMatrix = ctx->colorspace;
+        ctx->encode_config.encodeCodecConfig.h264Config.h264VUIParameters.colourPrimaries = ctx->color_primaries;
+        ctx->encode_config.encodeCodecConfig.h264Config.h264VUIParameters.transferCharacteristics = ctx->color_trc;
+
+        ctx->encode_config.encodeCodecConfig.h264Config.h264VUIParameters.videoFullRangeFlag = ctx->fullrange;
+
+        ctx->encode_config.encodeCodecConfig.h264Config.sliceMode = 3;
+        ctx->encode_config.encodeCodecConfig.h264Config.sliceModeData = 1;
+
+        ctx->encode_config.encodeCodecConfig.h264Config.disableSPSPPS = ctx->repeat_headers ? 0 : 1;
+        ctx->encode_config.encodeCodecConfig.h264Config.repeatSPSPPS  = ctx->repeat_headers ? 1 : 0;
+
+        switch (ctx->profile) {
+        case VLC_NVENC_H264_PROFILE_BASELINE:
+            ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID;
+            break;
+        case VLC_NVENC_H264_PROFILE_MAIN:
+            ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
+            break;
+        case VLC_NVENC_H264_PROFILE_HIGH:
+            ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID;
+            break;
+        case VLC_NVENC_H264_PROFILE_HIGH_444_PREDICTIVE:
+            ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
+            break;
+        default:
+            msg_Warn(enc, "Unsupported profile requested, falling back to high");
+            ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID;
+            break;
+        }
+
+        // force setting profile as high444p if input is VLC_CODEC_I444
+        if (enc->fmt_in.i_codec == VLC_CODEC_I444) {
+            ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
+            ctx->profile = VLC_NVENC_H264_PROFILE_HIGH_444_PREDICTIVE;
+        }
+
+        ctx->encode_config.encodeCodecConfig.h264Config.chromaFormatIDC =
+                ctx->profile == VLC_NVENC_H264_PROFILE_HIGH_444_PREDICTIVE ? 3 : 1;
+
+        if (ctx->level) {
+            res = input_string_to_uint32(nvenc_h264_level_pairs, ctx->level,
+                                         &ctx->encode_config.encodeCodecConfig.h264Config.level);
+            if (res) {
+                msg_Err(enc, "Level \"%s\" is unknown! Supported levels: auto, 1, 1b, 1.1, 1.2, 1.3, 2, 2.1, 2.2, 3, 3.1, 3.2, 4, 4.1, 4.2, 5, 5.1", ctx->level);
+                goto error;
+            }
+        } else {
+            ctx->encode_config.encodeCodecConfig.h264Config.level = NV_ENC_LEVEL_AUTOSELECT;
+        }
+
+        break;
+    case VLC_CODEC_HEVC:
+        ctx->encode_config.encodeCodecConfig.hevcConfig.sliceMode = 3;
+        ctx->encode_config.encodeCodecConfig.hevcConfig.sliceModeData = 1;
+
+        ctx->encode_config.encodeCodecConfig.hevcConfig.disableSPSPPS = ctx->repeat_headers ? 0 : 1;
+        ctx->encode_config.encodeCodecConfig.hevcConfig.repeatSPSPPS  = ctx->repeat_headers ? 1 : 0;
+
+        /* No other profile is supported in the current SDK version 5 */
+        ctx->encode_config.profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID;
+        ctx->profile = VLC_NVENC_HEVC_PROFILE_MAIN;
+
+        if (ctx->level) {
+            res = input_string_to_uint32(nvenc_hevc_level_pairs, ctx->level,
+                                         &ctx->encode_config.encodeCodecConfig.hevcConfig.level);
+
+            if (res) {
+                msg_Err(enc, "Level \"%s\" is unknown! Supported levels: auto, 1, 2, 2.1, 3, 3.1, 4, 4.1, 5, 5.1, 5.2, 6, 6.1, 6.2", ctx->level);
+                goto error;
+            }
+        } else {
+            ctx->encode_config.encodeCodecConfig.hevcConfig.level = NV_ENC_LEVEL_AUTOSELECT;
+        }
+
+        if (ctx->tier) {
+            if (!strcmp(ctx->tier, "main")) {
+                ctx->encode_config.encodeCodecConfig.hevcConfig.tier = NV_ENC_TIER_HEVC_MAIN;
+            } else if (!strcmp(ctx->tier, "high")) {
+                ctx->encode_config.encodeCodecConfig.hevcConfig.tier = NV_ENC_TIER_HEVC_HIGH;
+            } else {
+                msg_Err(enc, "Tier \"%s\" is unknown! Supported tiers: main, high", ctx->tier);
+                res = VLC_EBADVAR;
+                goto error;
+            }
+        }
+
+        break;
+    /* Earlier switch/case will return if unknown codec is passed. */
+    }
+
+    nv_status = p_nvenc->nvEncInitializeEncoder(ctx->nvencoder, &ctx->init_encode_params);
+    if (nv_status != NV_ENC_SUCCESS) {
+        msg_Err(enc, "InitializeEncoder failed: 0x%x", (int)nv_status);
+        res = VLC_EGENERIC;
+        goto error;
+    }
+
+    ctx->input_surfaces = calloc(ctx->max_surface_count, sizeof(*ctx->input_surfaces));
+    if (!ctx->input_surfaces) {
+        res = VLC_ENOMEM;
+        goto error;
+    }
+
+    ctx->output_surfaces = calloc(ctx->max_surface_count, sizeof(*ctx->output_surfaces));
+    if (!ctx->output_surfaces) {
+        res = VLC_ENOMEM;
+        goto error;
+    }
+
+    for (surfaceCount = 0; surfaceCount < ctx->max_surface_count; surfaceCount++) {
+        NV_ENC_CREATE_INPUT_BUFFER allocSurf = { 0 };
+        NV_ENC_CREATE_BITSTREAM_BUFFER allocOut = { 0 };
+        allocSurf.version = NV_ENC_CREATE_INPUT_BUFFER_VER;
+        allocOut.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
+
+        allocSurf.width = (enc->fmt_in.video.i_width + 31) & ~31;
+        allocSurf.height = (enc->fmt_in.video.i_height + 31) & ~31;
+
+        allocSurf.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
+
+        switch (enc->fmt_in.i_codec) {
+        case VLC_CODEC_I420:
+        case VLC_CODEC_YV12:
+        case VLC_FOURCC('I','Y','U','V'):
+            allocSurf.bufferFmt = NV_ENC_BUFFER_FORMAT_YV12_PL;
+            break;
+
+        case VLC_CODEC_NV12:
+            allocSurf.bufferFmt = NV_ENC_BUFFER_FORMAT_NV12_PL;
+            break;
+
+        case VLC_CODEC_I444:
+            allocSurf.bufferFmt = NV_ENC_BUFFER_FORMAT_YUV444_PL;
+            break;
+
+        default:
+            msg_Err(enc, "Invalid input pixel format");
+            res = VLC_EBADVAR;
+            goto error;
+        }
+
+        nv_status = p_nvenc->nvEncCreateInputBuffer(ctx->nvencoder, &allocSurf);
+        if (nv_status != NV_ENC_SUCCESS) {
+            msg_Err(enc, "CreateInputBuffer failed");
+            res = VLC_EGENERIC;
+            goto error;
+        }
+
+        ctx->input_surfaces[surfaceCount].lockCount = 0;
+        ctx->input_surfaces[surfaceCount].input_surface = allocSurf.inputBuffer;
+        ctx->input_surfaces[surfaceCount].format = allocSurf.bufferFmt;
+        ctx->input_surfaces[surfaceCount].width = allocSurf.width;
+        ctx->input_surfaces[surfaceCount].height = allocSurf.height;
+
+        /* 1MB is large enough to hold most output frames.
+         * NVENC increases this automatically if it's not enough. */
+        allocOut.size = 1024 * 1024;
+
+        allocOut.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
+
+        nv_status = p_nvenc->nvEncCreateBitstreamBuffer(ctx->nvencoder, &allocOut);
+        if (nv_status != NV_ENC_SUCCESS) {
+            msg_Err(enc, "CreateOutputBuffer failed");
+            ctx->output_surfaces[surfaceCount++].output_surface = NULL;
+            res = VLC_EGENERIC;
+            goto error;
+        }
+
+        ctx->output_surfaces[surfaceCount].output_surface = allocOut.bitstreamBuffer;
+        ctx->output_surfaces[surfaceCount].size = allocOut.size;
+        ctx->output_surfaces[surfaceCount].busy = 0;
+    }
+
+    if (!ctx->repeat_headers) {
+        uint32_t outSize = 0;
+        char tmpHeader[256];
+        NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = { 0 };
+        payload.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER;
+
+        payload.spsppsBuffer = tmpHeader;
+        payload.inBufferSize = sizeof(tmpHeader);
+        payload.outSPSPPSPayloadSize = &outSize;
+
+        nv_status = p_nvenc->nvEncGetSequenceParams(ctx->nvencoder, &payload);
+        if (nv_status != NV_ENC_SUCCESS) {
+            msg_Err(enc, "GetSequenceParams failed");
+            goto error;
+        }
+
+        ctx->extradata_size = outSize;
+        ctx->extradata = calloc(1, outSize + 32);
+        if (!ctx->extradata) {
+            res = VLC_ENOMEM;
+            goto error;
+        }
+
+        memcpy(ctx->extradata, tmpHeader, outSize);
+    }
+
+    if (ctx->encode_config.frameIntervalP > 1)
+        ctx->has_b_frames = 2;
+
+    if (ctx->encode_config.rcParams.averageBitRate > 0)
+        ctx->bit_rate = ctx->encode_config.rcParams.averageBitRate;
+#if 0
+    cpb_props = ff_add_cpb_side_data(avctx);
+    if (!cpb_props) {
+        res = VLC_ENOMEM;
+        goto error;
+    }
+    cpb_props->max_bitrate = ctx->encode_config.rcParams.maxBitRate;
+    cpb_props->avg_bitrate = ctx->bit_rate;
+    cpb_props->buffer_size = ctx->encode_config.rcParams.vbvBufferSize;
+#endif
+    return VLC_SUCCESS;
+
+error:
+    for (uint32_t i = 0; i < surfaceCount; i++) {
+        if (ctx->input_surfaces[i].input_surface)
+            p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder,
+                                             ctx->input_surfaces[i].input_surface);
+        if (ctx->output_surfaces[i].output_surface)
+            p_nvenc->nvEncDestroyBitstreamBuffer(ctx->nvencoder,
+                                                 ctx->output_surfaces[i].output_surface);
+    }
+    ctx->max_surface_count = 0;
+    nvenc_encode_close(enc);
+    return res;
+}
+
+int nvenc_encode_close(encoder_t *enc)
+{
+    encoder_sys_t *sys = enc->p_sys;
+    NvencContext *ctx = &sys->ctx;
+    NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
+    NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
+
+    free(ctx->timestamp_list.data);
+    free(ctx->output_surface_ready_queue.data);
+    free(ctx->output_surface_queue.data);
+
+    for (uint32_t i = 0; i < ctx->max_surface_count; i++) {
+        if (ctx->input_surfaces[i].input_surface)
+            p_nvenc->nvEncDestroyInputBuffer(ctx->nvencoder,
+                                             ctx->input_surfaces[i].input_surface);
+        if (ctx->output_surfaces[i].output_surface)
+            p_nvenc->nvEncDestroyBitstreamBuffer(ctx->nvencoder,
+                                                 ctx->output_surfaces[i].output_surface);
+    }
+
+    ctx->max_surface_count = 0;
+
+    free(ctx->input_surfaces);
+    free(ctx->output_surfaces);
+    free(ctx->extradata);
+
+    if (ctx->nvencoder)
+        p_nvenc->nvEncDestroyEncoder(ctx->nvencoder);
+    if (ctx->cu_context)
+        dl_fn->cu_ctx_destroy(ctx->cu_context);
+    if (dl_fn->nvenc_lib)
+        nvenc_unload_nvenc(enc);
+    return VLC_SUCCESS;
+}
+
+static block_t *process_output_surface(encoder_t *enc, NvencOutputSurface *tmpoutsurf)
+{
+    encoder_sys_t *sys = enc->p_sys;
+    NvencContext *ctx = &sys->ctx;
+    NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
+    NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
+
+    uint32_t slice_mode_data;
+    uint32_t *slice_offsets = NULL;
+    NV_ENC_LOCK_BITSTREAM lock_params = { 0 };
+    NVENCSTATUS nv_status;
+
+    block_t *block = NULL;
+
+    switch (enc->fmt_out.i_codec) {
+    case VLC_CODEC_H264:
+      slice_mode_data = ctx->encode_config.encodeCodecConfig.h264Config.sliceModeData;
+      break;
+    case VLC_CODEC_HEVC:
+      slice_mode_data = ctx->encode_config.encodeCodecConfig.hevcConfig.sliceModeData;
+      break;
+    default:
+      msg_Err(enc, "Unknown codec name");
+      goto error;
+    }
+
+    slice_offsets = calloc(slice_mode_data, sizeof(*slice_offsets));
+    if (!slice_offsets)
+        return NULL;
+
+    lock_params.version = NV_ENC_LOCK_BITSTREAM_VER;
+
+    lock_params.doNotWait = 0;
+    lock_params.outputBitstream = tmpoutsurf->output_surface;
+    lock_params.sliceOffsets = slice_offsets;
+
+    nv_status = p_nvenc->nvEncLockBitstream(ctx->nvencoder, &lock_params);
+    if (nv_status != NV_ENC_SUCCESS) {
+        msg_Err(enc, "Failed locking bitstream buffer");
+        goto error;
+    }
+
+    block = block_Alloc(lock_params.bitstreamSizeInBytes);
+    if (!block) {
+        p_nvenc->nvEncUnlockBitstream(ctx->nvencoder, tmpoutsurf->output_surface);
+        goto error;
+    }
+
+    memcpy(block->p_buffer, lock_params.bitstreamBufferPtr, lock_params.bitstreamSizeInBytes);
+    block->i_buffer = lock_params.bitstreamSizeInBytes;
+
+    nv_status = p_nvenc->nvEncUnlockBitstream(ctx->nvencoder, tmpoutsurf->output_surface);
+    if (nv_status != NV_ENC_SUCCESS)
+        msg_Err(enc, "Failed unlocking bitstream buffer, expect the gates of mordor to open");
+
+    switch (lock_params.pictureType) {
+    case NV_ENC_PIC_TYPE_IDR:
+        block->i_flags |= BLOCK_FLAG_TYPE_I;
+    case NV_ENC_PIC_TYPE_I:
+        block->i_flags |= BLOCK_FLAG_TYPE_I;
+        break;
+    case NV_ENC_PIC_TYPE_P:
+        block->i_flags |= BLOCK_FLAG_TYPE_P;
+        break;
+    case NV_ENC_PIC_TYPE_B:
+        block->i_flags |= BLOCK_FLAG_TYPE_B;
+        break;
+    case NV_ENC_PIC_TYPE_BI:
+        block->i_flags |= BLOCK_FLAG_TYPE_PB;
+        break;
+    default:
+        block_Release(block);
+        msg_Err(enc, "Unknown picture type encountered, expect the output to be broken.");
+        msg_Err(enc, "Please report this error and include as much information on how to reproduce it as possible.");
+        goto error;
+    }
+
+    block->i_pts = lock_params.outputTimeStamp;
+    block->i_dts = timestamp_queue_dequeue(&ctx->timestamp_list);
+
+    /* when there're b frame(s), set dts offset */
+    if (ctx->encode_config.frameIntervalP >= 2)
+        block->i_dts -= 1;
+
+    if (block->i_dts > block->i_pts)
+        block->i_dts = block->i_pts;
+
+    if (ctx->last_dts != VLC_TS_INVALID && block->i_dts <= ctx->last_dts)
+        block->i_dts = ctx->last_dts + 1;
+
+    ctx->last_dts = block->i_dts;
+
+    free(slice_offsets);
+    return block;
+
+error:
+    free(slice_offsets);
+    timestamp_queue_dequeue(&ctx->timestamp_list);
+    return NULL;
+}
+
+static void image_copy_plane(uint8_t *restrict dst, int dst_pitch,
+                             const uint8_t *src, const int src_pitch,
+                             int width, int height)
+{
+    if (!dst || !src)
+        return;
+
+    assert(src_pitch >= width);
+    assert(dst_pitch >= width);
+    for (int i = height; i > 0; i--) {
+        memcpy(dst, src, width);
+        dst += dst_pitch;
+        src += src_pitch;
+    }
+}
+
+block_t *nvenc_encode_frame(encoder_t *enc, const picture_t *pic)
+{
+    encoder_sys_t *sys = enc->p_sys;
+
+    NVENCSTATUS nv_status;
+    NvencOutputSurface *tmpoutsurf;
+    uint32_t i = 0;
+    int res;
+
+    NvencContext *ctx = &sys->ctx;
+    NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
+    NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
+
+    NV_ENC_PIC_PARAMS pic_params = { 0 };
+    pic_params.version = NV_ENC_PIC_PARAMS_VER;
+
+    if (pic) {
+        NV_ENC_LOCK_INPUT_BUFFER lockBufferParams = { 0 };
+        NvencInputSurface *inSurf = NULL;
+
+        for (i = 0; i < ctx->max_surface_count; ++i) {
+            if (!ctx->input_surfaces[i].lockCount) {
+                inSurf = &ctx->input_surfaces[i];
+                break;
+            }
+        }
+
+        if (i == ctx->max_surface_count) {
+            msg_Err(enc, "Failed finding an empty nvenc input buffer");
+            return NULL;
+        }
+        assert(inSurf);
+
+        inSurf->lockCount = 1;
+
+        lockBufferParams.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
+        lockBufferParams.inputBuffer = inSurf->input_surface;
+
+        nv_status = p_nvenc->nvEncLockInputBuffer(ctx->nvencoder, &lockBufferParams);
+        if (nv_status != NV_ENC_SUCCESS) {
+            msg_Err(enc, "Failed locking nvenc input buffer");
+            return NULL;
+        }
+
+        if (enc->fmt_in.i_codec == VLC_CODEC_I420 ||
+            enc->fmt_in.i_codec == VLC_CODEC_YV12 ||
+            enc->fmt_in.i_codec == VLC_FOURCC('I','Y','U','V')) {
+
+            assert(pic->i_planes == 3);
+
+            uint8_t *buf = lockBufferParams.bufferDataPtr;
+
+            image_copy_plane(buf, lockBufferParams.pitch,
+                pic->Y_PIXELS, pic->p[Y_PLANE].i_pitch,
+                enc->fmt_in.video.i_width,
+                enc->fmt_in.video.i_height);
+
+            buf += inSurf->height * lockBufferParams.pitch;
+
+            image_copy_plane(buf, lockBufferParams.pitch >> 1,
+                pic->V_PIXELS, pic->p[V_PLANE].i_pitch,
+                enc->fmt_in.video.i_width >> 1,
+                enc->fmt_in.video.i_height >> 1);
+
+            buf += (inSurf->height * lockBufferParams.pitch) >> 2;
+
+            image_copy_plane(buf, lockBufferParams.pitch >> 1,
+                pic->U_PIXELS, pic->p[U_PLANE].i_pitch,
+                enc->fmt_in.video.i_width >> 1,
+                enc->fmt_in.video.i_height >> 1);
+
+        } else if (enc->fmt_in.i_codec == VLC_CODEC_NV12) {
+
+            assert(pic->i_planes == 2);
+
+            uint8_t *buf = lockBufferParams.bufferDataPtr;
+
+            image_copy_plane(buf, lockBufferParams.pitch,
+                pic->Y_PIXELS, pic->p[Y_PLANE].i_pitch,
+                enc->fmt_in.video.i_width,
+                enc->fmt_in.video.i_height);
+
+            buf += inSurf->height * lockBufferParams.pitch;
+
+            image_copy_plane(buf, lockBufferParams.pitch,
+                pic->U_PIXELS, pic->p[U_PLANE].i_pitch,
+                enc->fmt_in.video.i_width,
+                enc->fmt_in.video.i_height >> 1);
+
+        } else if (enc->fmt_in.i_codec == VLC_CODEC_I444) {
+
+            assert(pic->i_planes == 3);
+
+            uint8_t *buf = lockBufferParams.bufferDataPtr;
+
+            image_copy_plane(buf, lockBufferParams.pitch,
+                   pic->Y_PIXELS, pic->p[Y_PLANE].i_pitch,
+                   enc->fmt_in.video.i_width,
+                   enc->fmt_in.video.i_height);
+
+            buf += inSurf->height * lockBufferParams.pitch;
+
+            image_copy_plane(buf, lockBufferParams.pitch,
+                   pic->U_PIXELS, pic->p[U_PLANE].i_pitch,
+                   enc->fmt_in.video.i_width,
+                   enc->fmt_in.video.i_height);
+
+            buf += inSurf->height * lockBufferParams.pitch;
+
+            image_copy_plane(buf, lockBufferParams.pitch,
+                pic->V_PIXELS, pic->p[V_PLANE].i_pitch,
+                enc->fmt_in.video.i_width,
+                enc->fmt_in.video.i_height);
+        } else {
+            msg_Err(enc, "Invalid pixel format!");
+            return NULL;
+        }
+
+        nv_status = p_nvenc->nvEncUnlockInputBuffer(ctx->nvencoder, inSurf->input_surface);
+        if (nv_status != NV_ENC_SUCCESS) {
+            msg_Err(enc, "Failed unlocking input buffer!");
+            return NULL;
+        }
+
+        for (i = 0; i < ctx->max_surface_count; ++i)
+            if (!ctx->output_surfaces[i].busy)
+                break;
+
+        if (i == ctx->max_surface_count) {
+            inSurf->lockCount = 0;
+            msg_Err(enc, "No free output surface found!");
+            return NULL;
+        }
+
+        ctx->output_surfaces[i].input_surface = inSurf;
+
+        pic_params.inputBuffer = inSurf->input_surface;
+        pic_params.bufferFmt = inSurf->format;
+        pic_params.inputWidth = enc->fmt_in.video.i_width;
+        pic_params.inputHeight = enc->fmt_in.video.i_height;
+        pic_params.outputBitstream = ctx->output_surfaces[i].output_surface;
+        pic_params.completionEvent = 0;
+
+        if (ctx->interlaced) {
+            pic_params.pictureStruct = (pic->b_top_field_first) ?
+                        NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM :
+                        NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP;
+        } else {
+            pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
+        }
+
+        pic_params.encodePicFlags = 0;
+        pic_params.inputTimeStamp = pic->date;
+        pic_params.inputDuration = 0;
+        switch (enc->fmt_out.i_codec) {
+        case VLC_CODEC_H264:
+          pic_params.codecPicParams.h264PicParams.sliceMode = ctx->encode_config.encodeCodecConfig.h264Config.sliceMode;
+          pic_params.codecPicParams.h264PicParams.sliceModeData = ctx->encode_config.encodeCodecConfig.h264Config.sliceModeData;
+          break;
+        case VLC_CODEC_HEVC:
+          pic_params.codecPicParams.hevcPicParams.sliceMode = ctx->encode_config.encodeCodecConfig.hevcConfig.sliceMode;
+          pic_params.codecPicParams.hevcPicParams.sliceModeData = ctx->encode_config.encodeCodecConfig.hevcConfig.sliceModeData;
+          break;
+        default:
+          msg_Err(enc, "Unknown codec name");
+          return NULL;
+        }
+
+        res = timestamp_queue_enqueue(&ctx->timestamp_list, pic->date);
+        if (res)
+            return NULL;
+    } else {
+        pic_params.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
+    }
+
+    nv_status = p_nvenc->nvEncEncodePicture(ctx->nvencoder, &pic_params);
+    if (pic && nv_status == NV_ENC_ERR_NEED_MORE_INPUT) {
+        res = out_surf_queue_enqueue(&ctx->output_surface_queue, &ctx->output_surfaces[i]);
+        if (res)
+            return NULL;
+
+        ctx->output_surfaces[i].busy = 1;
+    }
+
+    if (nv_status != NV_ENC_SUCCESS && nv_status != NV_ENC_ERR_NEED_MORE_INPUT) {
+        msg_Err(enc, "EncodePicture failed!");
+        return NULL;
+    }
+
+    if (nv_status != NV_ENC_ERR_NEED_MORE_INPUT) {
+        while (ctx->output_surface_queue.count) {
+            tmpoutsurf = out_surf_queue_dequeue(&ctx->output_surface_queue);
+            res = out_surf_queue_enqueue(&ctx->output_surface_ready_queue, tmpoutsurf);
+            if (res)
+                return NULL;
+        }
+
+        if (pic) {
+            res = out_surf_queue_enqueue(&ctx->output_surface_ready_queue, &ctx->output_surfaces[i]);
+            if (res)
+                return NULL;
+
+            ctx->output_surfaces[i].busy = 1;
+        }
+    }
+
+    if (ctx->output_surface_ready_queue.count &&
+       (!pic || ctx->output_surface_ready_queue.count + ctx->output_surface_queue.count >= ctx->buffer_delay)) {
+        tmpoutsurf = out_surf_queue_dequeue(&ctx->output_surface_ready_queue);
+
+        block_t *block = process_output_surface(enc, tmpoutsurf);
+        if (block) {
+            tmpoutsurf->busy = 0;
+            assert(tmpoutsurf->input_surface->lockCount);
+            tmpoutsurf->input_surface->lockCount--;
+            return block;
+        }
+    }
+
+    return NULL;
+}
diff --git a/modules/hw/nvenc/nvenc.h b/modules/hw/nvenc/nvenc.h
new file mode 100644
index 0000000..7ae1784
--- /dev/null
+++ b/modules/hw/nvenc/nvenc.h
@@ -0,0 +1,261 @@
+/*****************************************************************************
+ * nvec.c: NVEnc-based hardware encoder plugin for NVidia CUDA graphics cards
+ *****************************************************************************
+ * Copyright © 2016 VideoLAN
+ * $Id$
+ *
+ * Authors: Jean-Paul Saman <jpsaman at videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*
+ * This file is in part based on FFmpeg v3.0 nvenc.c.
+ *
+ * H.264 hardware encoding using nvidia nvenc
+ * Copyright (c) 2014 Timo Rothenpieler <timo at rothenpieler.org>
+ *
+ */
+
+#ifndef VLC_HW_NVENC_H_
+#define VLC_HW_NVENC_H_ 1
+
+#if defined(_WIN32)
+#include <windows.h>
+#else
+#include <dlfcn.h>
+#endif
+
+/* H264/H265 profiles */
+typedef enum {
+    /* H264 profiles */
+    VLC_NVENC_H264_PROFILE_BASELINE = 0,
+    VLC_NVENC_H264_PROFILE_MAIN = 1,
+    VLC_NVENC_H264_PROFILE_HIGH = 2,
+    VLC_NVENC_H264_PROFILE_HIGH_444_PREDICTIVE = 3,
+
+    /* HEVC profiles */
+    VLC_NVENC_HEVC_PROFILE_MAIN,
+} vlc_nvenc_profile_t;
+
+/* Color Primaries */
+typedef enum {
+    VLC_NVENC_PRI_RESERVED0    = 0,
+    VLC_NVENC_PRI_BT709        = 1, /* also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B */
+    VLC_NVENC_PRI_UNSPECIFIED  = 2,
+    VLC_NVENC_PRI_RESERVED     = 3,
+    VLC_NVENC_PRI_BT470M       = 4, /* also FCC Title 47 Code of Federal Regulations 73.682 (a)(20) */
+    VLC_NVENC_PRI_BT470BG      = 5, /* also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM */
+    VLC_NVENC_PRI_SMPTE170M    = 6, /* also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC */
+    VLC_NVENC_PRI_SMPTE240M    = 7, /* functionally identical to above */
+    VLC_NVENC_PRI_FILM         = 8, /* colour filters using Illuminant C */
+    VLC_NVENC_PRI_BT2020       = 9, /* ITU-R BT2020 */
+    VLC_NVENC_PRI_SMPTEST428_1 = 10,/* SMPTE ST 428-1 (CIE 1931 XYZ) */
+} vlc_nvenc_color_primaries_t;
+
+/* YUV Color space */
+typedef enum {
+    VLC_NVENC_SPC_RGB         = 0,  /* order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB) */
+    VLC_NVENC_SPC_BT709       = 1,  /* also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B */
+    VLC_NVENC_SPC_UNSPECIFIED = 2,
+    VLC_NVENC_SPC_RESERVED    = 3,
+    VLC_NVENC_SPC_FCC         = 4,  /* FCC Title 47 Code of Federal Regulations 73.682 (a)(20) */
+    VLC_NVENC_SPC_BT470BG     = 5,  /* also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601 */
+    VLC_NVENC_SPC_SMPTE170M   = 6,  /* also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC / functionally identical to above */
+    VLC_NVENC_SPC_SMPTE240M   = 7,
+    VLC_NVENC_SPC_YCOCG       = 8,  /* Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16 */
+    VLC_NVENC_SPC_BT2020_NCL  = 9,  /* ITU-R BT2020 non-constant luminance system */
+    VLC_NVENC_SPC_BT2020_CL   = 10, /* ITU-R BT2020 constant luminance system */
+} vlc_nvenc_color_space_t;
+
+/* Color Transfer Characteristics */
+typedef enum {
+    VLC_NVENC_TRC_RESERVED0    = 0,
+    VLC_NVENC_TRC_BT709        = 1,  /* also ITU-R BT1361 */
+    VLC_NVENC_TRC_UNSPECIFIED  = 2,
+    VLC_NVENC_TRC_RESERVED     = 3,
+    VLC_NVENC_TRC_GAMMA22      = 4,  /* also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM */
+    VLC_NVENC_TRC_GAMMA28      = 5,  /* also ITU-R BT470BG */
+    VLC_NVENC_TRC_SMPTE170M    = 6,  /* also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC */
+    VLC_NVENC_TRC_SMPTE240M    = 7,
+    VLC_NVENC_TRC_LINEAR       = 8,  /* "Linear transfer characteristics" */
+    VLC_NVENC_TRC_LOG          = 9,  /* "Logarithmic transfer characteristic (100:1 range)" */
+    VLC_NVENC_TRC_LOG_SQRT     = 10, /* "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)" */
+    VLC_NVENC_TRC_IEC61966_2_4 = 11, /* IEC 61966-2-4 */
+    VLC_NVENC_TRC_BT1361_ECG   = 12, /* ITU-R BT1361 Extended Colour Gamut */
+    VLC_NVENC_TRC_IEC61966_2_1 = 13, /* IEC 61966-2-1 (sRGB or sYCC) */
+    VLC_NVENC_TRC_BT2020_10    = 14, /* ITU-R BT2020 for 10 bit system */
+    VLC_NVENC_TRC_BT2020_12    = 15, /* ITU-R BT2020 for 12 bit system */
+    VLC_NVENC_TRC_SMPTEST2084  = 16, /* SMPTE ST 2084 for 10, 12, 14 and 16 bit systems */
+    VLC_NVENC_TRC_SMPTEST428_1 = 17, /* SMPTE ST 428-1 */
+} vlc_nvenc_color_trc_t;
+
+/* NVENC */
+#if defined(_WIN32)
+#define CUDAAPI __stdcall
+#else
+#define CUDAAPI
+#endif
+
+typedef enum cuError_enum {
+    CU_SUCCESS = 0
+} CUresult;
+typedef int CUdevice;
+typedef void* CUcontext;
+
+typedef CUresult(CUDAAPI *PCUINIT)(unsigned int Flags);
+typedef CUresult(CUDAAPI *PCUDEVICEGETCOUNT)(int *count);
+typedef CUresult(CUDAAPI *PCUDEVICEGET)(CUdevice *device, int ordinal);
+typedef CUresult(CUDAAPI *PCUDEVICEGETNAME)(char *name, int len, CUdevice dev);
+typedef CUresult(CUDAAPI *PCUDEVICECOMPUTECAPABILITY)(int *major, int *minor, CUdevice dev);
+typedef CUresult(CUDAAPI *PCUCTXCREATE)(CUcontext *pctx, unsigned int flags, CUdevice dev);
+typedef CUresult(CUDAAPI *PCUCTXPOPCURRENT)(CUcontext *pctx);
+typedef CUresult(CUDAAPI *PCUCTXDESTROY)(CUcontext ctx);
+
+typedef struct NvencInputSurface
+{
+    NV_ENC_INPUT_PTR input_surface;
+    int width;
+    int height;
+
+    int lockCount;
+
+    NV_ENC_BUFFER_FORMAT format;
+} NvencInputSurface;
+
+typedef struct NvencOutputSurface
+{
+    NV_ENC_OUTPUT_PTR output_surface;
+    int size;
+
+    NvencInputSurface* input_surface;
+
+    int busy;
+} NvencOutputSurface;
+
+typedef struct NvencData
+{
+    union {
+        int64_t timestamp;
+        NvencOutputSurface *surface;
+    } u;
+} NvencData;
+
+typedef struct NvencDataList
+{
+    NvencData* data;
+
+    uint32_t pos;
+    uint32_t count;
+    uint32_t size;
+} NvencDataList;
+
+typedef struct NvencDynLoadFunctions
+{
+    PCUINIT cu_init;
+    PCUDEVICEGETCOUNT cu_device_get_count;
+    PCUDEVICEGET cu_device_get;
+    PCUDEVICEGETNAME cu_device_get_name;
+    PCUDEVICECOMPUTECAPABILITY cu_device_compute_capability;
+    PCUCTXCREATE cu_ctx_create;
+    PCUCTXPOPCURRENT cu_ctx_pop_current;
+    PCUCTXDESTROY cu_ctx_destroy;
+
+    NV_ENCODE_API_FUNCTION_LIST nvenc_funcs;
+    int nvenc_device_count;
+    CUdevice nvenc_devices[16];
+
+#if defined(_WIN32)
+    HMODULE cuda_lib;
+    HMODULE nvenc_lib;
+#else
+    void* cuda_lib;
+    void* nvenc_lib;
+#endif
+} NvencDynLoadFunctions;
+
+typedef struct NvencContext
+{
+    NvencDynLoadFunctions nvenc_dload_funcs;
+
+    NV_ENC_INITIALIZE_PARAMS init_encode_params;
+    NV_ENC_CONFIG encode_config;
+    CUcontext cu_context;
+
+    uint32_t max_surface_count;
+    NvencInputSurface *input_surfaces;
+    NvencOutputSurface *output_surfaces;
+
+    NvencDataList output_surface_queue;
+    NvencDataList output_surface_ready_queue;
+    NvencDataList timestamp_list;
+    int64_t last_dts;
+
+    void *nvencoder;
+
+    char *preset;
+    char *level;
+    char *tier;
+
+    int cbr;
+    int twopass;
+    int gpu;
+    uint32_t buffer_delay;
+
+    int64_t bit_rate;
+
+    int refs;
+    int keyint;     /* # key frames */
+    int min_keyint; /* min GOP length */
+    int gop_size;   /* max GOP length */
+    int max_b_frames;
+    int has_b_frames;
+    int qmin;
+    int qmax;
+    int global_quality;
+
+    int rc_max_rate;    /* vbv_maxrate */
+    int rc_buffer_size; /* vbv_bufsize */
+    float rc_buffer_initial_delay; /* vbv_init */
+
+    float b_quant_factor; /* qscale factor between B and P frames */
+    float b_quant_offset;
+    float i_quant_factor; /* qscale factor between I and P frames */
+    float i_quant_offset;
+
+    vlc_nvenc_profile_t profile;  /* H264/HEVC profile */
+    vlc_nvenc_color_space_t colorspace;
+    vlc_nvenc_color_primaries_t color_primaries;
+    vlc_nvenc_color_trc_t color_trc;
+
+    size_t extradata_size;
+    uint8_t *extradata;
+
+    bool interlaced; /* Interlaced encoding */
+    bool fullrange;  /* Fullrange colorspace */
+    bool repeat_headers; /* repeat h264 headers */
+
+} NvencContext;
+
+struct encoder_sys_t {
+    NvencContext ctx;
+    bool b_nvenc_initialized;
+};
+
+int nvenc_encode_init(encoder_t *enc);
+int nvenc_encode_close(encoder_t *enc);
+block_t *nvenc_encode_frame(encoder_t *enc, const picture_t *pic);
+
+#endif /* VLC_HW_NVENC_H_ */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3828449..502760d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -937,6 +937,8 @@ modules/gui/skins2/x11/x11_window.cpp
 modules/gui/skins2/x11/x11_window.hpp
 modules/hw/mmal/codec.c
 modules/hw/mmal/vout.c
+modules/hw/nvenc/encoder.c
+modules/hw/nvenc/nvenc.c
 modules/hw/vdpau/adjust.c
 modules/hw/vdpau/avcodec.c
 modules/hw/vdpau/chroma.c
-- 
2.8.1



More information about the vlc-devel mailing list