mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 01:00:37 +00:00
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:
parent
da5c0bddb2
commit
b1d13e10af
11 changed files with 2904 additions and 2 deletions
103
configure.ac
103
configure.ac
|
@ -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
|
||||
|
|
|
@ -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
33
sys/nvenc/Makefile.am
Normal 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
30
sys/nvenc/README
Normal 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
11
sys/nvenc/TODO
Normal 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
1567
sys/nvenc/gstnvbaseenc.c
Normal file
File diff suppressed because it is too large
Load diff
114
sys/nvenc/gstnvbaseenc.h
Normal file
114
sys/nvenc/gstnvbaseenc.h
Normal 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
328
sys/nvenc/gstnvenc.c
Normal 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
40
sys/nvenc/gstnvenc.h
Normal 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
610
sys/nvenc/gstnvh264enc.c
Normal 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
59
sys/nvenc/gstnvh264enc.h
Normal 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__ */
|
Loading…
Reference in a new issue