Add Nvidia based hardware encoder element

Currently only h264 is supported

https://bugzilla.gnome.org/show_bug.cgi?id=753405
This commit is contained in:
Matthew Waters 2015-07-30 16:42:38 +10:00
parent da5c0bddb2
commit b1d13e10af
11 changed files with 2904 additions and 2 deletions

View file

@ -1726,6 +1726,108 @@ AC_SUBST(LIBUDEV_LIBS)
AC_SUBST(LIBUSB_CFLAGS)
AC_SUBST(LIBUSB_LIBS)
dnl *** NVENC ***
translit(dnm, m, l) AM_CONDITIONAL(USE_NVENC, true)
AG_GST_CHECK_FEATURE(NVENC, [NVIDIA Encode API], nvenc, [
AG_GST_PKG_CHECK_MODULES(GST_VIDEO, gstreamer-video-1.0)
AG_GST_PKG_CHECK_MODULES(GST_PBUTILS, gstreamer-pbutils-1.0)
AC_ARG_WITH([cuda-prefix],
AS_HELP_STRING([--with-cuda-prefix],
[Use the provided prefix for detecting the cuda installation]),
[AS_IF([test "x$with_cuda_prefix" != "x"],
[CUDA_PREFIX="$with_cuda_prefix"])],
[CUDA_PREFIX=""])
HAVE_CUDA="yes"
if test "x$CUDA_PREFIX" != "x"; then
dnl only override if not already set
if test "x$CUDA_CFLAGS" = "x" -a "x$CUDA_LIBS" = "x"; then
dnl this is an educated guess, user can always override these
CUDA_CFLAGS="-I$CUDA_PREFIX/include"
CUDA_LIBS="-L$CUDA_PREFIX/lib -L$CUDA_PREFIX/lib64 -L$CUDA_PREFIX/lib/stubs -L$CUDA_PREFIX/lib64/stubs -lcuda -lcudart"
fi
else
PKG_CHECK_MODULES([CUDA], [cuda-7.5 cudart-7.5],, [
PKG_CHECK_MODULES([CUDA], [cuda-7.0 cudart-7.0],, [
PKG_CHECK_MODULES([CUDA], [cuda-6.5 cudart-6.5],, [
AC_MSG_WARN([Could not find cuda headers/libraries])])])])
fi
HAVE_CUDA_H=no
HAVE_CUDART_H=no
save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CUDA_CFLAGS $save_CPPFLAGS "
AC_CHECK_HEADER([cuda.h], [HAVE_CUDA_H=yes],
AC_MSG_WARN([Could not find cuda.h]))
AC_CHECK_HEADER([cuda_runtime_api.h], [HAVE_CUDART_H=yes],
AC_MSG_WARN([Could not find cuda_runtime_api.h]))
CPPFLAGS=$save_CPPFLAGS
dnl libcuda and libcudart libraries
save_LIBS="$LIBS"
LIBS="$CUDA_LIBS $save_LIBS"
HAVE_CUDART_LIB="no"
AC_CHECK_LIB(cudart,cudaGetErrorString,[HAVE_CUDART_LIB="yes"], [
AC_MSG_WARN([Could not find cudart library])])
HAVE_CUDA_LIB="no"
AC_CHECK_LIB(cuda,cuInit,[HAVE_CUDA_LIB="yes"], [
AC_MSG_WARN([Could not find cuda library])])
LIBS="$save_LIBS"
dnl nvEncodeAPI.h header
HAVE_NVENCODEAPI_H=no
AC_ARG_VAR(NVENCODE_CFLAGS, [C compiler flags for NvEncodeAPI.h])
save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$NVENCODE_CFLAGS $save_CPPFLAGS"
AC_CHECK_HEADER([nvEncodeAPI.h], [
AC_PREPROC_IFELSE([AC_LANG_SOURCE([[#include <nvEncodeAPI.h>
#if NVENCAPI_MAJOR_VERSION < 5
#error "Need nvEncodeAPI.h >= 5.0"
#endif
]])], [
HAVE_NVENCODEAPI_H=yes
], [
AC_MSG_WARN([nvEncodeAPI.h must be >= 5.0])
])
],
AC_MSG_WARN([Could not find nvEncodeAPI.h]))
AC_SUBST(NVENCODE_CFLAGS)
CPPFLAGS="$save_CPPFLAGS"
dnl libnvnidia-encode library
HAVE_NVENCODE_LIB=no
AC_ARG_VAR(NVENCODE_LIBS, [linker flags for nvidia-encode])
saved_LIBS="$LIBS"
LIBS="$NVENCODE_LIBS $saved_LIBS"
AC_CHECK_LIB(nvidia-encode, NvEncodeAPICreateInstance, [HAVE_NVENCODE_LIB="yes"],
AC_MSG_WARN([Could not find library nvidia-encode]))
NVENCODE_LIBS="$NVENCODE_LIBS -lnvidia-encode"
AC_SUBST(NVENCODE_LIBS)
LIBS="$saved_LIBS"
USE_NVENC_GST_GL=no
if test "x$HAVE_CUDA_H" = "xyes" \
-a "x$HAVE_CUDART_H" = "xyes" \
-a "x$HAVE_CUDA_LIB" = "xyes" \
-a "x$HAVE_CUDART_LIB" = "xyes" \
-a "x$HAVE_NVENCODEAPI_H" = "xyes" \
-a "x$HAVE_NVENCODE_LIB" = "xyes"; then
HAVE_NVENC="yes"
if test x"$USE_OPENGL" = x"yes"; then
dnl cuda-gl interop header
save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$save_CPPFLAGS $CUDA_CFLAGS"
AC_CHECK_HEADER([cuda_gl_interop.h], [
USE_NVENC_GST_GL="yes"
AC_DEFINE(HAVE_NVENC_GST_GL, [1] , [NVENC GStreamer OpenGL support available])
])
CPPFLAGS="$save_CPPFLAGS"
fi
else
HAVE_NVENC="no"
fi
])
AM_CONDITIONAL(USE_NVENC_GST_GL, test "x$USE_NVENC_GST_GL" = "xyes")
dnl *** ext plug-ins ***
dnl keep this list sorted alphabetically !
@ -3363,6 +3465,7 @@ sys/dshowvideosink/Makefile
sys/dvb/Makefile
sys/fbdev/Makefile
sys/linsys/Makefile
sys/nvenc/Makefile
sys/opensles/Makefile
sys/shm/Makefile
sys/uvch264/Makefile

View file

@ -142,9 +142,16 @@ else
UVCH264_DIR=
endif
SUBDIRS = $(ACM_DIR) $(ANDROID_MEDIA_DIR) $(APPLE_MEDIA_DIR) $(AVC_DIR) $(BLUEZ_DIR) $(D3DVIDEOSINK_DIR) $(DECKLINK_DIR) $(DIRECTSOUND_DIR) $(WINKS_DIR) $(DVB_DIR) $(FBDEV_DIR) $(LINSYS_DIR) $(OPENSLES_DIR) $(PVR_DIR) $(SHM_DIR) $(UVCH264_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) $(WINSCREENCAP_DIR) $(WASAPI_DIR)
if USE_NVENC
NVENC_DIR=nvenc
else
NVENC_DIR=
endif
SUBDIRS = $(ACM_DIR) $(ANDROID_MEDIA_DIR) $(APPLE_MEDIA_DIR) $(AVC_DIR) $(BLUEZ_DIR) $(D3DVIDEOSINK_DIR) $(DECKLINK_DIR) $(DIRECTSOUND_DIR) $(WINKS_DIR) $(DVB_DIR) $(FBDEV_DIR) $(LINSYS_DIR) $(OPENSLES_DIR) $(PVR_DIR) $(SHM_DIR) $(UVCH264_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) $(WINSCREENCAP_DIR) $(WASAPI_DIR) $(NVENC_DIR)
DIST_SUBDIRS = acmenc acmmp3dec androidmedia applemedia applemedia-nonpublic avc bluez d3dvideosink decklink directsound dvb linsys fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \
opensles pvr2d shm uvch264 vcd vdpau wasapi wininet winks winscreencap
opensles pvr2d shm uvch264 vcd vdpau wasapi wininet winks winscreencap \
nvenc
include $(top_srcdir)/common/parallel-subdirs.mak

33
sys/nvenc/Makefile.am Normal file
View file

@ -0,0 +1,33 @@
plugin_LTLIBRARIES = libgstnvenc.la
libgstnvenc_la_SOURCES = \
gstnvenc.c \
gstnvbaseenc.c \
gstnvh264enc.c
noinst_HEADERS = \
gstnvenc.h \
gstnvbaseenc.h \
gstnvh264enc.h
libgstnvenc_la_CFLAGS = \
-I$(top_srcdir)/gst-libs \
$(GST_CFLAGS) \
$(GST_PBUTILS_CFLAGS) \
$(GST_VIDEO_CFLAGS) \
$(CUDA_CFLAGS) \
$(NVENCODE_CFLAGS)
libgstnvenc_la_LIBADD = \
$(GST_LIBS) \
$(GST_PBUTILS_LIBS) \
$(GST_VIDEO_LIBS) \
$(CUDA_LIBS) \
$(NVENCODE_LIBS)
if USE_NVENC_GST_GL
libgstnvenc_la_LIBADD += \
$(top_builddir)/gst-libs/gst/gl/libgstgl-$(GST_API_VERSION).la
endif
libgstnvenc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstnvenc_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)

30
sys/nvenc/README Normal file
View file

@ -0,0 +1,30 @@
This plugin is intended for use with NVIDIA hardware. Specifically, the NVENC
block available in recent NVIDIA GPU hardware. This is provided by a
libnvidia-encode library provided by NVIDIA graphic drivers.
Requirements
------------
Cuda > 6.5
NVENC 5.0
See https://developer.nvidia.com/nvidia-video-codec-sdk for a list of
supported GPU's.
Building
--------
1. Retrieve the NVENC SDK
from https://developer.nvidia.com/nvidia-video-codec-sdk
- http://developer.download.nvidia.com/compute/nvenc/v5.0/nvenc_5.0.1_sdk.zip
2. unzip this somewhere and retreive or note the location of the
nvEncodeAPI.h under nvenc_api-5.0.1/Samples/common/inc/
3. Retreive a version of cuda from
https://developer.nvidia.com/cuda-downloads and install somewhere noting
the installation prefix (typically /opt/cuda or /usr/local/cuda)
4. Now that the dependencies are sorted, there are a couple of
environment variables and/or or configure arguments that are needed to
detect the necessary libraries/headers.
More information is available from the following locations
[1] - https://developer.nvidia.com/cuda-downloads
[2] - https://developer.nvidia.com/nvidia-video-codec-sdk

11
sys/nvenc/TODO Normal file
View file

@ -0,0 +1,11 @@
- check supported encoding formats (H.264 etc.), don't assume H.264
- check performance (time taken) of first cuInit()
- provide buffer pool
- more formats
- renegotiation
- support outputting of AVC as well as byte-stream, negotiate automatically

1567
sys/nvenc/gstnvbaseenc.c Normal file

File diff suppressed because it is too large Load diff

114
sys/nvenc/gstnvbaseenc.h Normal file
View file

@ -0,0 +1,114 @@
/* GStreamer NVENC plugin
* Copyright (C) 2015 Centricular Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_NV_BASE_ENC_H_INCLUDED__
#define __GST_NV_BASE_ENC_H_INCLUDED__
#include "gstnvenc.h"
#include <gst/video/gstvideoencoder.h>
#define GST_TYPE_NV_BASE_ENC \
(gst_nv_base_enc_get_type())
#define GST_NV_BASE_ENC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NV_BASE_ENC,GstNvBaseEnc))
#define GST_NV_BASE_ENC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NV_BASE_ENC,GstNvBaseEncClass))
#define GST_NV_BASE_ENC_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_NV_BASE_ENC,GstNvBaseEncClass))
#define GST_IS_NV_BASE_ENC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NV_BASE_ENC))
#define GST_IS_NV_BASE_ENC_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NV_BASE_ENC))
typedef struct {
GstVideoEncoder video_encoder;
/* properties */
guint cuda_device_id;
CUcontext cuda_ctx;
void * encoder;
/* the supported input formats */
GValue * input_formats; /* OBJECT LOCK */
GstVideoCodecState *input_state;
gboolean gl_input;
/* allocated buffers */
gpointer *input_bufs; /* array of n_allocs input buffers */
NV_ENC_OUTPUT_PTR *output_bufs; /* array of n_allocs output buffers */
guint n_bufs;
/* input and output buffers currently available */
GAsyncQueue *in_bufs_pool;
GAsyncQueue *bitstream_pool;
/* output bufs in use (input bufs in use are tracked via the codec frames) */
GAsyncQueue *bitstream_queue;
/* we spawn a thread that does the (blocking) waits for output buffers
* to become available, so we can continue to feed data to the encoder
* while we wait */
GThread *bitstream_thread;
/* supported interlacing input modes.
* 0 = none, 1 = fields, 2 = interleaved */
gint interlace_modes;
void *display; /* GstGLDisplay */
void *other_context; /* GstGLContext */
/* the maximum buffer size the encoder is configured for */
guint max_encode_width;
guint max_encode_height;
GstVideoInfo input_info; /* buffer configuration for buffers sent to NVENC */
GstFlowReturn last_flow; /* ATOMIC */
} GstNvBaseEnc;
typedef struct {
GstVideoEncoderClass video_encoder_class;
GUID codec_id;
gboolean (*initialize_encoder) (GstNvBaseEnc * nvenc,
GstVideoCodecState * old_state,
GstVideoCodecState * state);
gboolean (*set_src_caps) (GstNvBaseEnc * nvenc,
GstVideoCodecState * state);
gboolean (*set_pic_params) (GstNvBaseEnc * nvenc,
GstVideoCodecFrame * frame,
NV_ENC_PIC_PARAMS * pic_params);
} GstNvBaseEncClass;
G_GNUC_INTERNAL
GType gst_nv_base_enc_get_type (void);
void gst_nv_base_enc_get_max_encode_size (GstNvBaseEnc * nvenc,
guint * max_width,
guint * max_height);
void gst_nv_base_enc_set_max_encode_size (GstNvBaseEnc * nvenc,
guint max_width,
guint max_height);
#endif /* __GST_NV_BASE_ENC_H_INCLUDED__ */

328
sys/nvenc/gstnvenc.c Normal file
View file

@ -0,0 +1,328 @@
/* GStreamer NVENC plugin
* Copyright (C) 2015 Centricular Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstnvenc.h"
#include "gstnvh264enc.h"
GST_DEBUG_CATEGORY (gst_nvenc_debug);
static NV_ENCODE_API_FUNCTION_LIST nvenc_api;
NVENCSTATUS
NvEncOpenEncodeSessionEx (NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS * params,
void **encoder)
{
g_assert (nvenc_api.nvEncOpenEncodeSessionEx != NULL);
return nvenc_api.nvEncOpenEncodeSessionEx (params, encoder);
}
NVENCSTATUS
NvEncDestroyEncoder (void *encoder)
{
g_assert (nvenc_api.nvEncDestroyEncoder != NULL);
return nvenc_api.nvEncDestroyEncoder (encoder);
}
NVENCSTATUS
NvEncGetEncodeGUIDs (void *encoder, GUID * array, uint32_t array_size,
uint32_t * count)
{
g_assert (nvenc_api.nvEncGetEncodeGUIDs != NULL);
return nvenc_api.nvEncGetEncodeGUIDs (encoder, array, array_size, count);
}
NVENCSTATUS
NvEncGetEncodeProfileGUIDCount (void *encoder, GUID encodeGUID,
uint32_t * encodeProfileGUIDCount)
{
g_assert (nvenc_api.nvEncGetEncodeProfileGUIDCount != NULL);
return nvenc_api.nvEncGetEncodeProfileGUIDCount (encoder, encodeGUID,
encodeProfileGUIDCount);
}
NVENCSTATUS
NvEncGetEncodeProfileGUIDs (void *encoder, GUID encodeGUID,
GUID * profileGUIDs, uint32_t guidArraySize, uint32_t * GUIDCount)
{
g_assert (nvenc_api.nvEncGetEncodeProfileGUIDs != NULL);
return nvenc_api.nvEncGetEncodeProfileGUIDs (encoder, encodeGUID,
profileGUIDs, guidArraySize, GUIDCount);
}
NVENCSTATUS
NvEncGetInputFormats (void *encoder, GUID enc_guid,
NV_ENC_BUFFER_FORMAT * array, uint32_t size, uint32_t * num)
{
g_assert (nvenc_api.nvEncGetInputFormats != NULL);
return nvenc_api.nvEncGetInputFormats (encoder, enc_guid, array, size, num);
}
NVENCSTATUS
NvEncGetEncodePresetConfig (void *encoder, GUID encodeGUID,
GUID presetGUID, NV_ENC_PRESET_CONFIG * presetConfig)
{
g_assert (nvenc_api.nvEncGetEncodePresetConfig != NULL);
return nvenc_api.nvEncGetEncodePresetConfig (encoder, encodeGUID, presetGUID,
presetConfig);
}
NVENCSTATUS
NvEncGetEncodeCaps (void *encoder, GUID encodeGUID,
NV_ENC_CAPS_PARAM * capsParam, int *capsVal)
{
g_assert (nvenc_api.nvEncGetEncodeCaps != NULL);
return nvenc_api.nvEncGetEncodeCaps (encoder, encodeGUID, capsParam, capsVal);
}
NVENCSTATUS
NvEncGetSequenceParams (void *encoder,
NV_ENC_SEQUENCE_PARAM_PAYLOAD * sequenceParamPayload)
{
g_assert (nvenc_api.nvEncGetSequenceParams != NULL);
return nvenc_api.nvEncGetSequenceParams (encoder, sequenceParamPayload);
}
NVENCSTATUS
NvEncInitializeEncoder (void *encoder, NV_ENC_INITIALIZE_PARAMS * params)
{
g_assert (nvenc_api.nvEncInitializeEncoder != NULL);
return nvenc_api.nvEncInitializeEncoder (encoder, params);
}
NVENCSTATUS
NvEncReconfigureEncoder (void *encoder, NV_ENC_RECONFIGURE_PARAMS * params)
{
g_assert (nvenc_api.nvEncReconfigureEncoder != NULL);
return nvenc_api.nvEncReconfigureEncoder (encoder, params);
}
NVENCSTATUS
NvEncRegisterResource (void *encoder, NV_ENC_REGISTER_RESOURCE * params)
{
g_assert (nvenc_api.nvEncRegisterResource != NULL);
return nvenc_api.nvEncRegisterResource (encoder, params);
}
NVENCSTATUS
NvEncUnregisterResource (void *encoder, NV_ENC_REGISTERED_PTR resource)
{
g_assert (nvenc_api.nvEncUnregisterResource != NULL);
return nvenc_api.nvEncUnregisterResource (encoder, resource);
}
NVENCSTATUS
NvEncMapInputResource (void *encoder, NV_ENC_MAP_INPUT_RESOURCE * params)
{
g_assert (nvenc_api.nvEncMapInputResource != NULL);
return nvenc_api.nvEncMapInputResource (encoder, params);
}
NVENCSTATUS
NvEncUnmapInputResource (void *encoder, NV_ENC_INPUT_PTR input_buffer)
{
g_assert (nvenc_api.nvEncUnmapInputResource != NULL);
return nvenc_api.nvEncUnmapInputResource (encoder, input_buffer);
}
NVENCSTATUS
NvEncCreateInputBuffer (void *encoder, NV_ENC_CREATE_INPUT_BUFFER * input_buf)
{
g_assert (nvenc_api.nvEncCreateInputBuffer != NULL);
return nvenc_api.nvEncCreateInputBuffer (encoder, input_buf);
}
NVENCSTATUS
NvEncLockInputBuffer (void *encoder, NV_ENC_LOCK_INPUT_BUFFER * input_buf)
{
g_assert (nvenc_api.nvEncLockInputBuffer != NULL);
return nvenc_api.nvEncLockInputBuffer (encoder, input_buf);
}
NVENCSTATUS
NvEncUnlockInputBuffer (void *encoder, NV_ENC_INPUT_PTR input_buf)
{
g_assert (nvenc_api.nvEncUnlockInputBuffer != NULL);
return nvenc_api.nvEncUnlockInputBuffer (encoder, input_buf);
}
NVENCSTATUS
NvEncDestroyInputBuffer (void *encoder, NV_ENC_INPUT_PTR input_buf)
{
g_assert (nvenc_api.nvEncDestroyInputBuffer != NULL);
return nvenc_api.nvEncDestroyInputBuffer (encoder, input_buf);
}
NVENCSTATUS
NvEncCreateBitstreamBuffer (void *encoder, NV_ENC_CREATE_BITSTREAM_BUFFER * bb)
{
g_assert (nvenc_api.nvEncCreateBitstreamBuffer != NULL);
return nvenc_api.nvEncCreateBitstreamBuffer (encoder, bb);
}
NVENCSTATUS
NvEncLockBitstream (void *encoder, NV_ENC_LOCK_BITSTREAM * lock_bs)
{
g_assert (nvenc_api.nvEncLockBitstream != NULL);
return nvenc_api.nvEncLockBitstream (encoder, lock_bs);
}
NVENCSTATUS
NvEncUnlockBitstream (void *encoder, NV_ENC_OUTPUT_PTR bb)
{
g_assert (nvenc_api.nvEncUnlockBitstream != NULL);
return nvenc_api.nvEncUnlockBitstream (encoder, bb);
}
NVENCSTATUS
NvEncDestroyBitstreamBuffer (void *encoder, NV_ENC_OUTPUT_PTR bit_buf)
{
g_assert (nvenc_api.nvEncDestroyBitstreamBuffer != NULL);
return nvenc_api.nvEncDestroyBitstreamBuffer (encoder, bit_buf);
}
NVENCSTATUS
NvEncEncodePicture (void *encoder, NV_ENC_PIC_PARAMS * pic_params)
{
g_assert (nvenc_api.nvEncEncodePicture != NULL);
return nvenc_api.nvEncEncodePicture (encoder, pic_params);
}
gboolean
gst_nvenc_cmp_guid (GUID g1, GUID g2)
{
return (g1.Data1 == g2.Data1 && g1.Data2 == g2.Data2 && g1.Data3 == g2.Data3
&& g1.Data4[0] == g2.Data4[0] && g1.Data4[1] == g2.Data4[1]
&& g1.Data4[2] == g2.Data4[2] && g1.Data4[3] == g2.Data4[3]
&& g1.Data4[4] == g2.Data4[4] && g1.Data4[5] == g2.Data4[5]
&& g1.Data4[6] == g2.Data4[6] && g1.Data4[7] == g2.Data4[7]);
}
NV_ENC_BUFFER_FORMAT
gst_nvenc_get_nv_buffer_format (GstVideoFormat fmt)
{
switch (fmt) {
case GST_VIDEO_FORMAT_NV12:
return NV_ENC_BUFFER_FORMAT_NV12_PL;
case GST_VIDEO_FORMAT_YV12:
return NV_ENC_BUFFER_FORMAT_YV12_PL;
case GST_VIDEO_FORMAT_I420:
return NV_ENC_BUFFER_FORMAT_IYUV_PL;
case GST_VIDEO_FORMAT_Y444:
return NV_ENC_BUFFER_FORMAT_YUV444_PL;
default:
break;
}
return NV_ENC_BUFFER_FORMAT_UNDEFINED;
}
CUcontext
gst_nvenc_create_cuda_context (guint device_id)
{
CUcontext cuda_ctx, old_ctx;
CUresult cres = CUDA_SUCCESS;
CUdevice cdev = 0, cuda_dev = -1;
int dev_count = 0;
char name[256];
int min = 0, maj = 0;
int i;
GST_INFO ("Initialising CUDA..");
cres = cuInit (0);
if (cres != CUDA_SUCCESS) {
GST_WARNING ("Failed to initialise CUDA, error code: 0x%08x", cres);
return NULL;
}
GST_INFO ("Initialised CUDA");
cres = cuDeviceGetCount (&dev_count);
if (cres != CUDA_SUCCESS || dev_count == 0) {
GST_WARNING ("No CUDA devices detected");
return NULL;
}
GST_INFO ("%d CUDA device(s) detected", dev_count);
for (i = 0; i < dev_count; ++i) {
if (cuDeviceGet (&cdev, i) == CUDA_SUCCESS
&& cuDeviceGetName (name, sizeof (name), cdev) == CUDA_SUCCESS
&& cuDeviceComputeCapability (&maj, &min, cdev) == CUDA_SUCCESS) {
GST_INFO ("GPU #%d supports NVENC: %s (%s) (Compute SM %d.%d)",
i, (((maj << 4) + min) >= 0x30) ? "yes" : "no", name, maj, min);
if (i == device_id) {
cuda_dev = cdev;
}
}
}
if (cuda_dev == -1) {
GST_WARNING ("Device with id %d does not exist or does not support NVENC",
device_id);
return NULL;
}
if (cuCtxCreate (&cuda_ctx, 0, cuda_dev) != CUDA_SUCCESS) {
GST_WARNING ("Failed to create CUDA context for cuda device %d", cuda_dev);
return NULL;
}
if (cuCtxPopCurrent (&old_ctx) != CUDA_SUCCESS) {
return NULL;
}
GST_INFO ("Created CUDA context %p", cuda_ctx);
return cuda_ctx;
}
gboolean
gst_nvenc_destroy_cuda_context (CUcontext ctx)
{
GST_INFO ("Destroying CUDA context %p", ctx);
return (cuCtxDestroy (ctx) == CUDA_SUCCESS);
}
static gboolean
plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_nvenc_debug, "nvenc", 0, "Nvidia NVENC encoder");
nvenc_api.version = NV_ENCODE_API_FUNCTION_LIST_VER;
if (NvEncodeAPICreateInstance (&nvenc_api) != NV_ENC_SUCCESS) {
GST_ERROR ("Failed to get NVEncodeAPI function table!");
} else {
GST_INFO ("Created NVEncodeAPI instance, got function table");
gst_element_register (plugin, "nvh264enc", GST_RANK_PRIMARY * 2,
gst_nv_h264_enc_get_type ());
}
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
nvenc,
"GStreamer NVENC plugin",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

40
sys/nvenc/gstnvenc.h Normal file
View file

@ -0,0 +1,40 @@
/* GStreamer NVENC plugin
* Copyright (C) 2015 Centricular Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_NVENC_H_INCLUDED__
#define __GST_NVENC_H_INCLUDED__
#include <gst/gst.h>
#include <gst/video/video.h>
#include <nvEncodeAPI.h>
#include <cuda.h>
GST_DEBUG_CATEGORY_EXTERN (gst_nvenc_debug);
#define GST_CAT_DEFAULT gst_nvenc_debug
CUcontext gst_nvenc_create_cuda_context (guint device_id);
gboolean gst_nvenc_destroy_cuda_context (CUcontext ctx);
gboolean gst_nvenc_cmp_guid (GUID g1, GUID g2);
NV_ENC_BUFFER_FORMAT gst_nvenc_get_nv_buffer_format (GstVideoFormat fmt);
#endif /* __GST_NVENC_H_INCLUDED__ */

610
sys/nvenc/gstnvh264enc.c Normal file
View file

@ -0,0 +1,610 @@
/* GStreamer NVENC plugin
* Copyright (C) 2015 Centricular Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstnvh264enc.h"
#include <gst/pbutils/codec-utils.h>
#include <string.h>
#if HAVE_GST_GL
#include <cuda.h>
#include <cuda_runtime_api.h>
#include <cuda_gl_interop.h>
#define GST_USE_UNSTABLE_API
#include <gst/gl/gl.h>
#endif
#define parent_class gst_nv_h264_enc_parent_class
G_DEFINE_TYPE (GstNvH264Enc, gst_nv_h264_enc, GST_TYPE_NV_BASE_ENC);
/* *INDENT-OFF* */
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-h264, "
"width = (int) [ 1, 4096 ], height = (int) [ 1, 2160 ], "
"framerate = (fraction) [0/1, MAX], "
"stream-format = (string) byte-stream, " // TODO: avc support
"alignment = (string) au, "
"profile = (string) { high, main, baseline }") // TODO: a couple of others
);
/* *INDENT-ON* */
static gboolean gst_nv_h264_enc_open (GstVideoEncoder * enc);
static gboolean gst_nv_h264_enc_close (GstVideoEncoder * enc);
static GstCaps *gst_nv_h264_enc_getcaps (GstVideoEncoder * enc,
GstCaps * filter);
static gboolean gst_nv_h264_enc_set_src_caps (GstNvBaseEnc * nvenc,
GstVideoCodecState * state);
static gboolean gst_nv_h264_enc_initialize_encoder (GstNvBaseEnc * nvenc,
GstVideoCodecState * old_state, GstVideoCodecState * state);
static gboolean gst_nv_h264_enc_set_pic_params (GstNvBaseEnc * nvenc,
GstVideoCodecFrame * frame, NV_ENC_PIC_PARAMS * pic_params);
static void gst_nv_h264_enc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_nv_h264_enc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_nv_h264_enc_finalize (GObject * obj);
static void
gst_nv_h264_enc_class_init (GstNvH264EncClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_CLASS (klass);
gobject_class->set_property = gst_nv_h264_enc_set_property;
gobject_class->get_property = gst_nv_h264_enc_get_property;
gobject_class->finalize = gst_nv_h264_enc_finalize;
videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_h264_enc_open);
videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_h264_enc_close);
videoenc_class->getcaps = GST_DEBUG_FUNCPTR (gst_nv_h264_enc_getcaps);
nvenc_class->codec_id = NV_ENC_CODEC_H264_GUID;
nvenc_class->initialize_encoder = gst_nv_h264_enc_initialize_encoder;
nvenc_class->set_src_caps = gst_nv_h264_enc_set_src_caps;
nvenc_class->set_pic_params = gst_nv_h264_enc_set_pic_params;
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
gst_element_class_set_static_metadata (element_class,
"NVENC H.264 Video Encoder",
"Codec/Encoder/Video",
"Encode H.264 video streams using NVIDIA's hardware-accelerated NVENC encoder API",
"Tim-Philipp Müller <tim@centricular.com>\n"
"Matthew Waters <matthew@centricular.com>");
}
static void
gst_nv_h264_enc_init (GstNvH264Enc * nvenc)
{
}
static void
gst_nv_h264_enc_finalize (GObject * obj)
{
G_OBJECT_CLASS (gst_nv_h264_enc_parent_class)->finalize (obj);
}
static gboolean
_get_supported_profiles (GstNvH264Enc * nvenc)
{
NVENCSTATUS nv_ret;
GUID profile_guids[64];
GValue list = G_VALUE_INIT;
GValue val = G_VALUE_INIT;
guint i, n, n_profiles;
nv_ret =
NvEncGetEncodeProfileGUIDCount (GST_NV_BASE_ENC (nvenc)->encoder,
NV_ENC_CODEC_H264_GUID, &n);
if (nv_ret != NV_ENC_SUCCESS)
return FALSE;
nv_ret =
NvEncGetEncodeProfileGUIDs (GST_NV_BASE_ENC (nvenc)->encoder,
NV_ENC_CODEC_H264_GUID, profile_guids, G_N_ELEMENTS (profile_guids), &n);
if (nv_ret != NV_ENC_SUCCESS)
return FALSE;
n_profiles = 0;
g_value_init (&list, GST_TYPE_LIST);
for (i = 0; i < n; i++) {
g_value_init (&val, G_TYPE_STRING);
if (gst_nvenc_cmp_guid (profile_guids[i],
NV_ENC_H264_PROFILE_BASELINE_GUID)) {
g_value_set_static_string (&val, "baseline");
gst_value_list_append_value (&list, &val);
n_profiles++;
} else if (gst_nvenc_cmp_guid (profile_guids[i],
NV_ENC_H264_PROFILE_MAIN_GUID)) {
g_value_set_static_string (&val, "main");
gst_value_list_append_value (&list, &val);
n_profiles++;
} else if (gst_nvenc_cmp_guid (profile_guids[i],
NV_ENC_H264_PROFILE_HIGH_GUID)) {
g_value_set_static_string (&val, "high");
gst_value_list_append_value (&list, &val);
n_profiles++;
}
/* TODO: map HIGH_444, STEREO, CONSTRAINED_HIGH, SVC_TEMPORAL_SCALABILITY */
g_value_unset (&val);
}
if (n_profiles == 0)
return FALSE;
GST_OBJECT_LOCK (nvenc);
g_free (nvenc->supported_profiles);
nvenc->supported_profiles = g_memdup (&list, sizeof (GValue));
GST_OBJECT_UNLOCK (nvenc);
return TRUE;
}
static gboolean
gst_nv_h264_enc_open (GstVideoEncoder * enc)
{
GstNvH264Enc *nvenc = GST_NV_H264_ENC (enc);
if (!GST_VIDEO_ENCODER_CLASS (gst_nv_h264_enc_parent_class)->open (enc))
return FALSE;
/* Check if H.264 is supported */
{
uint32_t i, num = 0;
GUID guids[16];
NvEncGetEncodeGUIDs (GST_NV_BASE_ENC (nvenc)->encoder, guids,
G_N_ELEMENTS (guids), &num);
for (i = 0; i < num; ++i) {
if (gst_nvenc_cmp_guid (guids[i], NV_ENC_CODEC_H264_GUID))
break;
}
GST_INFO_OBJECT (enc, "H.264 encoding %ssupported", (i == num) ? "un" : "");
if (i == num) {
gst_nv_h264_enc_close (enc);
return FALSE;
}
}
/* query supported input formats */
if (!_get_supported_profiles (nvenc)) {
GST_WARNING_OBJECT (nvenc, "No supported encoding profiles");
gst_nv_h264_enc_close (enc);
return FALSE;
}
return TRUE;
}
static gboolean
gst_nv_h264_enc_close (GstVideoEncoder * enc)
{
GstNvH264Enc *nvenc = GST_NV_H264_ENC (enc);
GST_OBJECT_LOCK (nvenc);
g_free (nvenc->supported_profiles);
nvenc->supported_profiles = NULL;
GST_OBJECT_UNLOCK (nvenc);
return GST_VIDEO_ENCODER_CLASS (gst_nv_h264_enc_parent_class)->close (enc);
}
static GValue *
_get_interlace_modes (GstNvH264Enc * nvenc)
{
NV_ENC_CAPS_PARAM caps_param = { 0, };
GValue *list = g_new0 (GValue, 1);
GValue val = G_VALUE_INIT;
g_value_init (list, GST_TYPE_LIST);
g_value_init (&val, G_TYPE_STRING);
g_value_set_static_string (&val, "progressive");
gst_value_list_append_value (list, &val);
caps_param.version = NV_ENC_CAPS_PARAM_VER;
caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_FIELD_ENCODING;
if (NvEncGetEncodeCaps (GST_NV_BASE_ENC (nvenc)->encoder,
NV_ENC_CODEC_H264_GUID, &caps_param,
&nvenc->interlace_modes) != NV_ENC_SUCCESS)
nvenc->interlace_modes = 0;
if (nvenc->interlace_modes >= 1) {
g_value_set_static_string (&val, "interleaved");
gst_value_list_append_value (list, &val);
g_value_set_static_string (&val, "mixed");
gst_value_list_append_value (list, &val);
}
/* TODO: figure out what nvenc frame based interlacing means in gst terms */
return list;
}
static GstCaps *
gst_nv_h264_enc_getcaps (GstVideoEncoder * enc, GstCaps * filter)
{
GstNvH264Enc *nvenc = GST_NV_H264_ENC (enc);
GstCaps *supported_incaps = NULL;
GstCaps *template_caps, *caps;
GValue *input_formats = GST_NV_BASE_ENC (enc)->input_formats;
GST_OBJECT_LOCK (nvenc);
if (input_formats != NULL) {
GValue *val;
template_caps = gst_pad_get_pad_template_caps (enc->sinkpad);
supported_incaps = gst_caps_copy (template_caps);
gst_caps_set_value (supported_incaps, "format", input_formats);
val = _get_interlace_modes (nvenc);
gst_caps_set_value (supported_incaps, "interlace-mode", val);
g_free (val);
GST_LOG_OBJECT (enc, "codec input caps %" GST_PTR_FORMAT, supported_incaps);
GST_LOG_OBJECT (enc, " template caps %" GST_PTR_FORMAT, template_caps);
caps = gst_caps_intersect (template_caps, supported_incaps);
gst_caps_unref (template_caps);
gst_caps_unref (supported_incaps);
supported_incaps = caps;
GST_LOG_OBJECT (enc, " supported caps %" GST_PTR_FORMAT, supported_incaps);
}
GST_OBJECT_UNLOCK (nvenc);
caps = gst_video_encoder_proxy_getcaps (enc, supported_incaps, filter);
if (supported_incaps)
gst_caps_unref (supported_incaps);
GST_DEBUG_OBJECT (nvenc, " returning caps %" GST_PTR_FORMAT, caps);
return caps;
}
static gboolean
gst_nv_h264_enc_set_profile_and_level (GstNvH264Enc * nvenc, GstCaps * caps)
{
#define N_BYTES_SPS 128
guint8 sps[N_BYTES_SPS];
NV_ENC_SEQUENCE_PARAM_PAYLOAD spp = { 0, };
GstStructure *s;
const gchar *profile;
GstCaps *allowed_caps;
GstStructure *s2;
const gchar *allowed_profile;
NVENCSTATUS nv_ret;
guint32 seq_size;
spp.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER;
spp.inBufferSize = N_BYTES_SPS;
spp.spsId = 0;
spp.ppsId = 0;
spp.spsppsBuffer = &sps;
spp.outSPSPPSPayloadSize = &seq_size;
nv_ret = NvEncGetSequenceParams (GST_NV_BASE_ENC (nvenc)->encoder, &spp);
if (nv_ret != NV_ENC_SUCCESS) {
GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, ("Encode header failed."),
("NvEncGetSequenceParams return code=%d", nv_ret));
return FALSE;
}
if (seq_size < 8) {
GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, ("Encode header failed."),
("NvEncGetSequenceParams returned incomplete data"));
return FALSE;
}
/* skip nal header and identifier */
gst_codec_utils_h264_caps_set_level_and_profile (caps, &sps[5], 3);
/* Constrained baseline is a strict subset of baseline. If downstream
* wanted baseline and we produced constrained baseline, we can just
* set the profile to baseline in the caps to make negotiation happy.
* Same goes for baseline as subset of main profile and main as a subset
* of high profile.
*/
s = gst_caps_get_structure (caps, 0);
profile = gst_structure_get_string (s, "profile");
allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (nvenc));
if (allowed_caps == NULL)
goto no_peer;
if (!gst_caps_can_intersect (allowed_caps, caps)) {
allowed_caps = gst_caps_make_writable (allowed_caps);
allowed_caps = gst_caps_truncate (allowed_caps);
s2 = gst_caps_get_structure (allowed_caps, 0);
gst_structure_fixate_field_string (s2, "profile", profile);
allowed_profile = gst_structure_get_string (s2, "profile");
if (!strcmp (allowed_profile, "high")) {
if (!strcmp (profile, "constrained-baseline")
|| !strcmp (profile, "baseline") || !strcmp (profile, "main")) {
gst_structure_set (s, "profile", G_TYPE_STRING, "high", NULL);
GST_INFO_OBJECT (nvenc, "downstream requested high profile, but "
"encoder will now output %s profile (which is a subset), due "
"to how it's been configured", profile);
}
} else if (!strcmp (allowed_profile, "main")) {
if (!strcmp (profile, "constrained-baseline")
|| !strcmp (profile, "baseline")) {
gst_structure_set (s, "profile", G_TYPE_STRING, "main", NULL);
GST_INFO_OBJECT (nvenc, "downstream requested main profile, but "
"encoder will now output %s profile (which is a subset), due "
"to how it's been configured", profile);
}
} else if (!strcmp (allowed_profile, "baseline")) {
if (!strcmp (profile, "constrained-baseline"))
gst_structure_set (s, "profile", G_TYPE_STRING, "baseline", NULL);
}
}
gst_caps_unref (allowed_caps);
no_peer:
return TRUE;
#undef N_BYTES_SPS
}
static gboolean
gst_nv_h264_enc_set_src_caps (GstNvBaseEnc * nvenc, GstVideoCodecState * state)
{
GstNvH264Enc *h264enc = GST_NV_H264_ENC (nvenc);
GstVideoCodecState *out_state;
GstStructure *s;
GstCaps *out_caps;
out_caps = gst_caps_new_empty_simple ("video/x-h264");
s = gst_caps_get_structure (out_caps, 0);
/* TODO: add support for avc format as well */
gst_structure_set (s, "stream-format", G_TYPE_STRING, "byte-stream",
"alignment", G_TYPE_STRING, "au", NULL);
if (!gst_nv_h264_enc_set_profile_and_level (h264enc, out_caps)) {
gst_caps_unref (out_caps);
return FALSE;
}
out_state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (nvenc),
out_caps, state);
GST_INFO_OBJECT (nvenc, "output caps: %" GST_PTR_FORMAT, out_state->caps);
/* encoder will keep it around for us */
gst_video_codec_state_unref (out_state);
/* TODO: would be nice to also send some tags with the codec name */
return TRUE;
}
static gboolean
gst_nv_h264_enc_initialize_encoder (GstNvBaseEnc * nvenc,
GstVideoCodecState * old_state, GstVideoCodecState * state)
{
GstNvH264Enc *h264enc = GST_NV_H264_ENC (nvenc);
NV_ENC_RECONFIGURE_PARAMS reconfigure_params = { 0, };
NV_ENC_INITIALIZE_PARAMS init_params = { 0, };
NV_ENC_INITIALIZE_PARAMS *params;
NV_ENC_PRESET_CONFIG preset_config = { 0, };
NVENCSTATUS nv_ret;
GstVideoInfo *info = &state->info;
GstCaps *allowed_caps, *template_caps;
GUID selected_profile = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID;
int level_idc = NV_ENC_LEVEL_AUTOSELECT;
/* TODO: support reconfiguration */
if (old_state) {
reconfigure_params.version = NV_ENC_RECONFIGURE_PARAMS_VER;
params = &reconfigure_params.reInitEncodeParams;
} else {
params = &init_params;
}
template_caps = gst_static_pad_template_get_caps (&src_factory);
allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (h264enc));
if (template_caps == allowed_caps) {
GST_INFO_OBJECT (h264enc, "downstream has ANY caps");
} else if (allowed_caps) {
GstStructure *s;
const gchar *profile;
const gchar *level;
if (gst_caps_is_empty (allowed_caps)) {
gst_caps_unref (allowed_caps);
gst_caps_unref (template_caps);
return FALSE;
}
allowed_caps = gst_caps_make_writable (allowed_caps);
allowed_caps = gst_caps_fixate (allowed_caps);
s = gst_caps_get_structure (allowed_caps, 0);
profile = gst_structure_get_string (s, "profile");
if (profile) {
if (!strcmp (profile, "baseline")) {
selected_profile = NV_ENC_H264_PROFILE_BASELINE_GUID;
} else if (g_str_has_prefix (profile, "high-4:4:4")) {
selected_profile = NV_ENC_H264_PROFILE_HIGH_444_GUID;
} else if (g_str_has_prefix (profile, "high-10")) {
g_assert_not_reached ();
} else if (g_str_has_prefix (profile, "high-4:2:2")) {
g_assert_not_reached ();
} else if (g_str_has_prefix (profile, "high")) {
selected_profile = NV_ENC_H264_PROFILE_HIGH_GUID;
} else if (g_str_has_prefix (profile, "main")) {
selected_profile = NV_ENC_H264_PROFILE_MAIN_GUID;
} else {
g_assert_not_reached ();
}
}
level = gst_structure_get_string (s, "level");
if (level)
/* matches values stored in NV_ENC_LEVEL */
level_idc = gst_codec_utils_h264_get_level_idc (level);
gst_caps_unref (allowed_caps);
}
gst_caps_unref (template_caps);
params->version = NV_ENC_INITIALIZE_PARAMS_VER;
params->encodeGUID = NV_ENC_CODEC_H264_GUID;
params->encodeWidth = GST_VIDEO_INFO_WIDTH (info);
params->encodeHeight = GST_VIDEO_INFO_HEIGHT (info);
/* FIXME: make this a property */
params->presetGUID = NV_ENC_PRESET_HP_GUID; // _DEFAULT
params->enablePTD = 1;
if (!old_state) {
/* this sets the required buffer size and the maximum allowed size on
* subsequent reconfigures */
/* FIXME: propertise this */
params->maxEncodeWidth = GST_VIDEO_INFO_WIDTH (info);
params->maxEncodeHeight = GST_VIDEO_INFO_HEIGHT (info);
gst_nv_base_enc_set_max_encode_size (GST_NV_BASE_ENC (h264enc),
params->maxEncodeWidth, params->maxEncodeHeight);
} else {
guint max_width, max_height;
gst_nv_base_enc_get_max_encode_size (GST_NV_BASE_ENC (h264enc),
&max_width, &max_height);
if (GST_VIDEO_INFO_WIDTH (info) > max_width
|| GST_VIDEO_INFO_HEIGHT (info) > max_height) {
GST_ELEMENT_ERROR (h264enc, STREAM, FORMAT, ("%s", "Requested stream "
"size is larger than the maximum configured size"), (NULL));
return FALSE;
}
}
preset_config.version = NV_ENC_PRESET_CONFIG_VER;
preset_config.presetCfg.version = NV_ENC_CONFIG_VER;
nv_ret =
NvEncGetEncodePresetConfig (GST_NV_BASE_ENC (h264enc)->encoder,
params->encodeGUID, params->presetGUID, &preset_config);
if (nv_ret != NV_ENC_SUCCESS) {
GST_ELEMENT_ERROR (h264enc, LIBRARY, SETTINGS, (NULL),
("Failed to get encode preset configuration: %d", nv_ret));
return FALSE;
}
params->encodeConfig = &preset_config.presetCfg;
/* override some defaults */
GST_LOG_OBJECT (h264enc, "setting parameters");
preset_config.presetCfg.version = NV_ENC_CONFIG_VER;
preset_config.presetCfg.profileGUID = selected_profile;
preset_config.presetCfg.encodeCodecConfig.h264Config.level = level_idc;
preset_config.presetCfg.encodeCodecConfig.h264Config.chromaFormatIDC = 1;
if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_Y444) {
GST_DEBUG_OBJECT (h264enc, "have Y444 input, setting config accordingly");
preset_config.presetCfg.encodeCodecConfig.
h264Config.separateColourPlaneFlag = 1;
preset_config.presetCfg.encodeCodecConfig.h264Config.chromaFormatIDC = 3;
}
/* FIXME: make property */
preset_config.presetCfg.encodeCodecConfig.h264Config.outputAUD = 1;
if (GST_VIDEO_INFO_IS_INTERLACED (info)) {
if (GST_VIDEO_INFO_INTERLACE_MODE (info) ==
GST_VIDEO_INTERLACE_MODE_INTERLEAVED
|| GST_VIDEO_INFO_INTERLACE_MODE (info) ==
GST_VIDEO_INTERLACE_MODE_MIXED) {
preset_config.presetCfg.frameFieldMode =
NV_ENC_PARAMS_FRAME_FIELD_MODE_FIELD;
}
}
if (info->fps_d > 0 && info->fps_n > 0) {
params->frameRateNum = info->fps_n;
params->frameRateDen = info->fps_d;
} else {
GST_FIXME_OBJECT (h264enc, "variable framerate");
}
if (old_state) {
nv_ret =
NvEncReconfigureEncoder (GST_NV_BASE_ENC (h264enc)->encoder,
&reconfigure_params);
} else {
nv_ret =
NvEncInitializeEncoder (GST_NV_BASE_ENC (h264enc)->encoder, params);
}
if (nv_ret != NV_ENC_SUCCESS) {
GST_ELEMENT_ERROR (h264enc, LIBRARY, SETTINGS, (NULL),
("Failed to %sinit encoder: %d", old_state ? "re" : "", nv_ret));
return FALSE;
}
GST_INFO_OBJECT (h264enc, "configured encoder");
return TRUE;
}
static gboolean
gst_nv_h264_enc_set_pic_params (GstNvBaseEnc * enc, GstVideoCodecFrame * frame,
NV_ENC_PIC_PARAMS * pic_params)
{
/* encode whole picture in one single slice */
pic_params->codecPicParams.h264PicParams.sliceMode = 0;
pic_params->codecPicParams.h264PicParams.sliceModeData = 0;
return TRUE;
}
static void
gst_nv_h264_enc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_nv_h264_enc_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}

59
sys/nvenc/gstnvh264enc.h Normal file
View file

@ -0,0 +1,59 @@
/* GStreamer NVENC plugin
* Copyright (C) 2015 Centricular Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_NV_H264_ENC_H_INCLUDED__
#define __GST_NV_H264_ENC_H_INCLUDED__
#include "gstnvbaseenc.h"
#define GST_TYPE_NV_H264_ENC \
(gst_nv_h264_enc_get_type())
#define GST_NV_H264_ENC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NV_H264_ENC,GstNvH264Enc))
#define GST_NV_H264_ENC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NV_H264_ENC,GstNvH264EncClass))
#define GST_NV_H264_ENC_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_NV_H264_ENC,GstNvH264EncClass))
#define GST_IS_NV_H264_ENC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NV_H264_ENC))
#define GST_IS_NV_H264_ENC_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NV_H264_ENC))
typedef struct {
GstNvBaseEnc base_nvenc;
/* the supported input formats */
GValue * supported_profiles; /* OBJECT LOCK */
GstVideoCodecState *input_state;
gboolean gl_input;
/* supported interlacing input modes.
* 0 = none, 1 = fields, 2 = interleaved */
gint interlace_modes;
} GstNvH264Enc;
typedef struct {
GstNvBaseEncClass video_encoder_class;
} GstNvH264EncClass;
G_GNUC_INTERNAL
GType gst_nv_h264_enc_get_type (void);
#endif /* __GST_NV_H264_ENC_H_INCLUDED__ */