mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-27 08:26:39 +00:00
Add new property to support application allocated GstCudaMemory. CUDA memory alloc/free is a global device synchronization point as if launching CUDA kernel on default CUDA stream. To avoid the global synchronization, we added stream-ordered allocation support which allocates CUDA memory asynchronously. However, NVENC does not allow registering the stream-ordered allocated memory. Thus encoder was allocating normal CUDA memory in case that input CUDA memory is stream-ordered type. In this commit, newly introduced property will allow application to provide encoder with GstCudaBufferPool. Application can preallocate sufficient amount of CUDA memory in advance to avoid global device synchronization while pipeline is running. For now, this pool is used only if input CUDA memory is allocated via stream-ordered-allocation Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8516>
3331 lines
99 KiB
C++
3331 lines
99 KiB
C++
/* GStreamer
|
|
* Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
|
|
*
|
|
* 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 "gstnvencoder.h"
|
|
|
|
#include <gst/cuda/gstcudautils.h>
|
|
#include <gst/cuda/gstcudamemory.h>
|
|
#include <gst/cuda/gstcudabufferpool.h>
|
|
#include <gst/cuda/gstcudastream.h>
|
|
#include <gst/cuda/gstcuda-private.h>
|
|
#include <gst/base/gstbytewriter.h>
|
|
#include <string.h>
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
#include <thread>
|
|
#include <memory>
|
|
#include <atomic>
|
|
#include "gstnvencobject.h"
|
|
|
|
#ifdef HAVE_GST_D3D12
|
|
#include "gstcudainterop_d3d12.h"
|
|
#endif
|
|
|
|
#ifdef G_OS_WIN32
|
|
#include <wrl.h>
|
|
|
|
/* *INDENT-OFF* */
|
|
using namespace Microsoft::WRL;
|
|
/* *INDENT-ON* */
|
|
#endif
|
|
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
#define SUPPORTED_GL_APIS GST_GL_API_OPENGL3
|
|
#endif
|
|
|
|
GST_DEBUG_CATEGORY (gst_nv_encoder_debug);
|
|
#define GST_CAT_DEFAULT gst_nv_encoder_debug
|
|
|
|
#define GST_NVENC_STATUS_FORMAT "s (%d)"
|
|
#define GST_NVENC_STATUS_ARGS(s) nvenc_status_to_string (s), s
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_CC_INSERT,
|
|
PROP_EXTERN_POOL,
|
|
};
|
|
|
|
#define DEFAULT_CC_INSERT GST_NV_ENCODER_SEI_INSERT
|
|
|
|
struct _GstNvEncoderPrivate
|
|
{
|
|
_GstNvEncoderPrivate ()
|
|
{
|
|
memset (&init_params, 0, sizeof (NV_ENC_INITIALIZE_PARAMS));
|
|
memset (&config, 0, sizeof (NV_ENC_CONFIG));
|
|
}
|
|
|
|
~_GstNvEncoderPrivate ()
|
|
{
|
|
gst_clear_object (&extern_pool);
|
|
}
|
|
|
|
GstCudaContext *context = nullptr;
|
|
GstCudaStream *stream = nullptr;
|
|
|
|
#ifdef G_OS_WIN32
|
|
GstD3D11Device *device = nullptr;
|
|
GstD3D11Fence *fence = nullptr;
|
|
#endif
|
|
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
GstGLDisplay *gl_display = nullptr;
|
|
GstGLContext *gl_context = nullptr;
|
|
GstGLContext *other_gl_context = nullptr;
|
|
|
|
gboolean gl_interop = FALSE;
|
|
#endif
|
|
|
|
#ifdef HAVE_GST_D3D12
|
|
GstD3D12Device *device_12 = nullptr;
|
|
GstCudaD3D12Interop *interop_12 = nullptr;
|
|
#endif
|
|
|
|
std::shared_ptr < GstNvEncObject > object;
|
|
|
|
GstNvEncoderDeviceMode subclass_device_mode;
|
|
GstNvEncoderDeviceMode selected_device_mode;
|
|
gint64 dxgi_adapter_luid = 0;
|
|
guint cuda_device_id = 0;
|
|
|
|
NV_ENC_INITIALIZE_PARAMS init_params;
|
|
NV_ENC_CONFIG config;
|
|
|
|
GstVideoCodecState *input_state = nullptr;
|
|
|
|
GstBufferPool *internal_pool = nullptr;
|
|
|
|
GstClockTime dts_offset = 0;
|
|
|
|
std::mutex lock;
|
|
std::condition_variable cond;
|
|
|
|
std::recursive_mutex context_lock;
|
|
|
|
std::unique_ptr < std::thread > encoding_thread;
|
|
|
|
std::atomic < GstFlowReturn > last_flow;
|
|
|
|
GstVideoInfo extern_pool_info;
|
|
|
|
/* properties */
|
|
GstNvEncoderSeiInsertMode cc_insert = DEFAULT_CC_INSERT;
|
|
GstBufferPool *extern_pool = nullptr;
|
|
};
|
|
|
|
/**
|
|
* GstNvEncoder:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
#define gst_nv_encoder_parent_class parent_class
|
|
G_DEFINE_ABSTRACT_TYPE (GstNvEncoder, gst_nv_encoder, GST_TYPE_VIDEO_ENCODER);
|
|
|
|
static void gst_nv_encoder_finalize (GObject * object);
|
|
static void gst_nv_encoder_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_nv_encoder_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
static void gst_nv_encoder_set_context (GstElement * element,
|
|
GstContext * context);
|
|
static gboolean gst_nv_encoder_open (GstVideoEncoder * encoder);
|
|
static gboolean gst_nv_encoder_close (GstVideoEncoder * encoder);
|
|
static gboolean gst_nv_encoder_stop (GstVideoEncoder * encoder);
|
|
static gboolean gst_nv_encoder_sink_event (GstVideoEncoder * encoder,
|
|
GstEvent * event);
|
|
static gboolean gst_nv_encoder_sink_query (GstVideoEncoder * encoder,
|
|
GstQuery * query);
|
|
static gboolean gst_nv_encoder_src_query (GstVideoEncoder * encoder,
|
|
GstQuery * query);
|
|
static gboolean gst_nv_encoder_propose_allocation (GstVideoEncoder *
|
|
encoder, GstQuery * query);
|
|
static gboolean gst_nv_encoder_set_format (GstVideoEncoder * encoder,
|
|
GstVideoCodecState * state);
|
|
static GstFlowReturn gst_nv_encoder_handle_frame (GstVideoEncoder *
|
|
encoder, GstVideoCodecFrame * frame);
|
|
static GstFlowReturn gst_nv_encoder_finish (GstVideoEncoder * encoder);
|
|
static gboolean gst_nv_encoder_flush (GstVideoEncoder * encoder);
|
|
static gboolean gst_nv_encoder_transform_meta (GstVideoEncoder * encoder,
|
|
GstVideoCodecFrame * frame, GstMeta * meta);
|
|
|
|
static void
|
|
gst_nv_encoder_class_init (GstNvEncoderClass * klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
|
GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
|
|
|
|
object_class->finalize = gst_nv_encoder_finalize;
|
|
object_class->set_property = gst_nv_encoder_set_property;
|
|
object_class->get_property = gst_nv_encoder_get_property;
|
|
|
|
/**
|
|
* GstNvEncoder:cc-insert:
|
|
*
|
|
* Closed Caption insert mode
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_CC_INSERT,
|
|
g_param_spec_enum ("cc-insert", "Closed Caption Insert",
|
|
"Closed Caption Insert mode",
|
|
GST_TYPE_NV_ENCODER_SEI_INSERT_MODE, DEFAULT_CC_INSERT,
|
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
|
|
|
/**
|
|
* GstNvEncoder:extern-cuda-bufferpool:
|
|
*
|
|
* GstCudaBufferPool prepared by application. Application can pass
|
|
* a buffer pool instance prepared in advance, to avoid
|
|
* global device synchronization caused by CUDA memory allocation.
|
|
*
|
|
* The buffer pool should be configured with stream-ordered-allocation disabled
|
|
*
|
|
* Since: 1.26
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_EXTERN_POOL,
|
|
g_param_spec_object ("extern-cuda-bufferpool", "Extern CUDA Buffer Pool",
|
|
"GstCudaBufferPool prepared by application",
|
|
GST_TYPE_OBJECT,
|
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
|
GST_PARAM_MUTABLE_READY)));
|
|
|
|
|
|
element_class->set_context = GST_DEBUG_FUNCPTR (gst_nv_encoder_set_context);
|
|
|
|
videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_encoder_open);
|
|
videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_encoder_close);
|
|
videoenc_class->stop = GST_DEBUG_FUNCPTR (gst_nv_encoder_stop);
|
|
videoenc_class->sink_event = GST_DEBUG_FUNCPTR (gst_nv_encoder_sink_event);
|
|
videoenc_class->sink_query = GST_DEBUG_FUNCPTR (gst_nv_encoder_sink_query);
|
|
videoenc_class->src_query = GST_DEBUG_FUNCPTR (gst_nv_encoder_src_query);
|
|
videoenc_class->propose_allocation =
|
|
GST_DEBUG_FUNCPTR (gst_nv_encoder_propose_allocation);
|
|
videoenc_class->set_format = GST_DEBUG_FUNCPTR (gst_nv_encoder_set_format);
|
|
videoenc_class->handle_frame =
|
|
GST_DEBUG_FUNCPTR (gst_nv_encoder_handle_frame);
|
|
videoenc_class->finish = GST_DEBUG_FUNCPTR (gst_nv_encoder_finish);
|
|
videoenc_class->flush = GST_DEBUG_FUNCPTR (gst_nv_encoder_flush);
|
|
videoenc_class->transform_meta =
|
|
GST_DEBUG_FUNCPTR (gst_nv_encoder_transform_meta);
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_nv_encoder_debug, "nvencoder", 0, "nvencoder");
|
|
|
|
gst_type_mark_as_plugin_api (GST_TYPE_NV_ENCODER, (GstPluginAPIFlags) 0);
|
|
gst_type_mark_as_plugin_api (GST_TYPE_NV_ENCODER_PRESET,
|
|
(GstPluginAPIFlags) 0);
|
|
gst_type_mark_as_plugin_api (GST_TYPE_NV_ENCODER_RC_MODE,
|
|
(GstPluginAPIFlags) 0);
|
|
gst_type_mark_as_plugin_api (GST_TYPE_NV_ENCODER_SEI_INSERT_MODE,
|
|
(GstPluginAPIFlags) 0);
|
|
gst_type_mark_as_plugin_api (GST_TYPE_NV_ENCODER_MULTI_PASS,
|
|
(GstPluginAPIFlags) 0);
|
|
gst_type_mark_as_plugin_api (GST_TYPE_NV_ENCODER_TUNE, (GstPluginAPIFlags) 0);
|
|
}
|
|
|
|
static void
|
|
gst_nv_encoder_init (GstNvEncoder * self)
|
|
{
|
|
self->priv = new GstNvEncoderPrivate ();
|
|
|
|
gst_video_encoder_set_min_pts (GST_VIDEO_ENCODER (self),
|
|
GST_SECOND * 60 * 60 * 1000);
|
|
GST_PAD_SET_ACCEPT_INTERSECT (GST_VIDEO_ENCODER_SINK_PAD (self));
|
|
}
|
|
|
|
static void
|
|
gst_nv_encoder_finalize (GObject * object)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (object);
|
|
|
|
delete self->priv;
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gst_nv_encoder_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (object);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
switch (prop_id) {
|
|
case PROP_CC_INSERT:
|
|
priv->cc_insert = (GstNvEncoderSeiInsertMode) g_value_get_enum (value);
|
|
break;
|
|
case PROP_EXTERN_POOL:
|
|
gst_clear_object (&priv->extern_pool);
|
|
priv->extern_pool = (GstBufferPool *) g_value_dup_object (value);
|
|
if (priv->extern_pool) {
|
|
if (!GST_IS_CUDA_BUFFER_POOL (priv->extern_pool)) {
|
|
GST_ERROR_OBJECT (self, "Not a CUDA buffer pool");
|
|
gst_clear_object (&priv->extern_pool);
|
|
} else if (!gst_buffer_pool_set_active (priv->extern_pool, TRUE)) {
|
|
GST_ERROR_OBJECT (self, "Set active failed");
|
|
gst_clear_object (&priv->extern_pool);
|
|
} else {
|
|
auto config = gst_buffer_pool_get_config (priv->extern_pool);
|
|
GstCaps *caps;
|
|
gst_buffer_pool_config_get_params (config,
|
|
&caps, nullptr, nullptr, nullptr);
|
|
auto is_valid = gst_video_info_from_caps (&priv->extern_pool_info,
|
|
caps);
|
|
gst_structure_free (config);
|
|
if (!is_valid) {
|
|
GST_ERROR_OBJECT (self, "Invalid buffer pool");
|
|
gst_clear_object (&priv->extern_pool);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_nv_encoder_get_property (GObject * object, guint prop_id, GValue * value,
|
|
GParamSpec * pspec)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (object);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
switch (prop_id) {
|
|
case PROP_CC_INSERT:
|
|
g_value_set_enum (value, priv->cc_insert);
|
|
break;
|
|
case PROP_EXTERN_POOL:
|
|
g_value_set_object (value, priv->extern_pool);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_nv_encoder_set_context (GstElement * element, GstContext * context)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (element);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
std::unique_lock < std::recursive_mutex > lk (priv->context_lock);
|
|
|
|
switch (priv->selected_device_mode) {
|
|
#ifdef G_OS_WIN32
|
|
case GST_NV_ENCODER_DEVICE_D3D11:
|
|
gst_d3d11_handle_set_context_for_adapter_luid (element,
|
|
context, priv->dxgi_adapter_luid, &priv->device);
|
|
break;
|
|
#endif
|
|
case GST_NV_ENCODER_DEVICE_CUDA:
|
|
gst_cuda_handle_set_context (element, context, priv->cuda_device_id,
|
|
&priv->context);
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
if (gst_gl_handle_set_context (element, context, &priv->gl_display,
|
|
&priv->other_gl_context)) {
|
|
if (priv->gl_display)
|
|
gst_gl_display_filter_gl_api (priv->gl_display, SUPPORTED_GL_APIS);
|
|
}
|
|
#endif
|
|
#ifdef HAVE_GST_D3D12
|
|
gst_d3d12_handle_set_context_for_adapter_luid (element,
|
|
context, priv->dxgi_adapter_luid, &priv->device_12);
|
|
#endif
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
lk.unlock ();
|
|
|
|
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_reset (GstNvEncoder * self)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
GST_LOG_OBJECT (self, "Reset");
|
|
|
|
if (priv->internal_pool) {
|
|
gst_buffer_pool_set_active (priv->internal_pool, FALSE);
|
|
gst_clear_object (&priv->internal_pool);
|
|
}
|
|
|
|
if (priv->encoding_thread) {
|
|
priv->encoding_thread->join ();
|
|
priv->encoding_thread = nullptr;
|
|
}
|
|
|
|
priv->object = nullptr;
|
|
priv->last_flow = GST_FLOW_OK;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_device_lock (GstNvEncoder * self)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
gboolean ret = TRUE;
|
|
|
|
switch (priv->selected_device_mode) {
|
|
#ifdef G_OS_WIN32
|
|
case GST_NV_ENCODER_DEVICE_D3D11:
|
|
gst_d3d11_device_lock (priv->device);
|
|
break;
|
|
#endif
|
|
case GST_NV_ENCODER_DEVICE_CUDA:
|
|
ret = gst_cuda_context_push (priv->context);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_device_unlock (GstNvEncoder * self)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
gboolean ret = TRUE;
|
|
|
|
switch (priv->selected_device_mode) {
|
|
#ifdef G_OS_WIN32
|
|
case GST_NV_ENCODER_DEVICE_D3D11:
|
|
gst_d3d11_device_unlock (priv->device);
|
|
break;
|
|
#endif
|
|
case GST_NV_ENCODER_DEVICE_CUDA:
|
|
ret = gst_cuda_context_pop (nullptr);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_drain (GstNvEncoder * self, gboolean locked)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
NVENCSTATUS status;
|
|
GstNvEncTask *task = nullptr;
|
|
|
|
if (!priv->object || !priv->encoding_thread)
|
|
return TRUE;
|
|
|
|
GST_DEBUG_OBJECT (self, "Drain");
|
|
|
|
if (locked)
|
|
GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
|
|
|
|
priv->object->AcquireTask (&task, true);
|
|
|
|
status = priv->object->Drain (task);
|
|
gst_nv_enc_result (status, self);
|
|
|
|
priv->encoding_thread->join ();
|
|
priv->encoding_thread = nullptr;
|
|
|
|
gst_nv_encoder_reset (self);
|
|
|
|
if (locked)
|
|
GST_VIDEO_ENCODER_STREAM_LOCK (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef G_OS_WIN32
|
|
static gboolean
|
|
gst_nv_encoder_open_d3d11_device (GstNvEncoder * self)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
ComPtr < ID3D10Multithread > multi_thread;
|
|
ID3D11Device *device_handle;
|
|
HRESULT hr;
|
|
|
|
if (!gst_d3d11_ensure_element_data_for_adapter_luid (GST_ELEMENT (self),
|
|
priv->dxgi_adapter_luid, &priv->device)) {
|
|
GST_ERROR_OBJECT (self, "Cannot create d3d11device");
|
|
return FALSE;
|
|
}
|
|
|
|
device_handle = gst_d3d11_device_get_device_handle (priv->device);
|
|
hr = device_handle->QueryInterface (IID_PPV_ARGS (&multi_thread));
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_ERROR_OBJECT (self, "ID3D10Multithread interface is unavailable");
|
|
gst_clear_object (&priv->device);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
multi_thread->SetMultithreadProtected (TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
static gboolean
|
|
gst_nv_encoder_open (GstVideoEncoder * encoder)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
switch (priv->selected_device_mode) {
|
|
case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
|
|
/* Will open GPU later */
|
|
return TRUE;
|
|
#ifdef G_OS_WIN32
|
|
case GST_NV_ENCODER_DEVICE_D3D11:
|
|
return gst_nv_encoder_open_d3d11_device (self);
|
|
#endif
|
|
case GST_NV_ENCODER_DEVICE_CUDA:
|
|
if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (encoder),
|
|
priv->cuda_device_id, &priv->context)) {
|
|
GST_ERROR_OBJECT (self, "failed to create CUDA context");
|
|
return FALSE;
|
|
}
|
|
if (!priv->stream && gst_nvenc_have_set_io_cuda_streams ())
|
|
priv->stream = gst_cuda_stream_new (priv->context);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_close (GstVideoEncoder * encoder)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
gst_clear_cuda_stream (&priv->stream);
|
|
gst_clear_object (&priv->context);
|
|
#ifdef G_OS_WIN32
|
|
gst_clear_d3d11_fence (&priv->fence);
|
|
gst_clear_object (&priv->device);
|
|
#endif
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
gst_clear_object (&priv->gl_display);
|
|
gst_clear_object (&priv->gl_context);
|
|
gst_clear_object (&priv->other_gl_context);
|
|
#endif
|
|
#ifdef HAVE_GST_D3D12
|
|
gst_clear_object (&priv->interop_12);
|
|
gst_clear_object (&priv->device_12);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_stop (GstVideoEncoder * encoder)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
GST_DEBUG_OBJECT (self, "Stop");
|
|
|
|
gst_nv_encoder_drain (self, FALSE);
|
|
|
|
if (priv->subclass_device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) {
|
|
gst_clear_cuda_stream (&priv->stream);
|
|
gst_clear_object (&priv->context);
|
|
#ifdef G_OS_WIN32
|
|
gst_clear_object (&priv->device);
|
|
#endif
|
|
priv->selected_device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT;
|
|
}
|
|
|
|
g_clear_pointer (&priv->input_state, gst_video_codec_state_unref);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_sink_event (GstVideoEncoder * encoder, GstEvent * event)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
switch (GST_EVENT_TYPE (event)) {
|
|
case GST_EVENT_FLUSH_START:
|
|
if (priv->object)
|
|
priv->object->SetFlushing (TRUE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_event (encoder, event);
|
|
}
|
|
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
static void
|
|
gst_nv_encoder_check_cuda_device_from_gl_context (GstGLContext * context,
|
|
gboolean * ret)
|
|
{
|
|
guint device_count = 0;
|
|
CUdevice device_list[1] = { 0, };
|
|
CUresult cuda_ret;
|
|
|
|
*ret = FALSE;
|
|
|
|
cuda_ret = CuGLGetDevices (&device_count,
|
|
device_list, 1, CU_GL_DEVICE_LIST_ALL);
|
|
|
|
if (!gst_cuda_result (cuda_ret) || device_count == 0)
|
|
return;
|
|
|
|
*ret = TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_ensure_gl_context (GstNvEncoder * self)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
gboolean ret = FALSE;
|
|
|
|
std::unique_lock < std::recursive_mutex > lk (priv->context_lock);
|
|
|
|
if (!gst_gl_ensure_element_data (GST_ELEMENT (self), &priv->gl_display,
|
|
&priv->other_gl_context)) {
|
|
GST_DEBUG_OBJECT (self, "Couldn't get GL display");
|
|
return FALSE;
|
|
}
|
|
|
|
gst_gl_display_filter_gl_api (priv->gl_display, SUPPORTED_GL_APIS);
|
|
|
|
if (!gst_gl_display_ensure_context (priv->gl_display, priv->other_gl_context,
|
|
&priv->gl_context, nullptr)) {
|
|
GST_DEBUG_OBJECT (self, "Couldn't get GL context");
|
|
return FALSE;
|
|
}
|
|
|
|
gst_gl_context_thread_add (priv->gl_context,
|
|
(GstGLContextThreadFunc) gst_nv_encoder_check_cuda_device_from_gl_context,
|
|
&ret);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static gboolean
|
|
gst_nv_encoder_handle_context_query (GstNvEncoder * self, GstQuery * query)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
gboolean ret = FALSE;
|
|
|
|
std::unique_lock < std::recursive_mutex > lk (priv->context_lock);
|
|
|
|
switch (priv->selected_device_mode) {
|
|
#ifdef G_OS_WIN32
|
|
case GST_NV_ENCODER_DEVICE_D3D11:
|
|
ret = gst_d3d11_handle_context_query (GST_ELEMENT (self),
|
|
query, priv->device);
|
|
break;
|
|
#endif
|
|
case GST_NV_ENCODER_DEVICE_CUDA:
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
{
|
|
GstGLDisplay *display = nullptr;
|
|
GstGLContext *other = nullptr;
|
|
GstGLContext *local = nullptr;
|
|
|
|
if (priv->gl_display)
|
|
display = (GstGLDisplay *) gst_object_ref (priv->gl_display);
|
|
if (priv->gl_context)
|
|
local = (GstGLContext *) gst_object_ref (priv->gl_context);
|
|
if (priv->other_gl_context)
|
|
other = (GstGLContext *) gst_object_ref (priv->other_gl_context);
|
|
|
|
lk.unlock ();
|
|
ret = gst_gl_handle_context_query (GST_ELEMENT (self), query,
|
|
display, local, other);
|
|
lk.lock ();
|
|
|
|
gst_clear_object (&display);
|
|
gst_clear_object (&other);
|
|
gst_clear_object (&local);
|
|
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_GST_D3D12
|
|
if (gst_d3d12_handle_context_query (GST_ELEMENT (self), query,
|
|
priv->device_12)) {
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
ret = gst_cuda_handle_context_query (GST_ELEMENT (self),
|
|
query, priv->context);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_sink_query (GstVideoEncoder * encoder, GstQuery * query)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
|
|
switch (GST_QUERY_TYPE (query)) {
|
|
case GST_QUERY_CONTEXT:
|
|
if (gst_nv_encoder_handle_context_query (self, query))
|
|
return TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (encoder, query);
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_src_query (GstVideoEncoder * encoder, GstQuery * query)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
|
|
switch (GST_QUERY_TYPE (query)) {
|
|
case GST_QUERY_CONTEXT:
|
|
if (gst_nv_encoder_handle_context_query (self, query))
|
|
return TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GST_VIDEO_ENCODER_CLASS (parent_class)->src_query (encoder, query);
|
|
}
|
|
|
|
static guint
|
|
gst_nv_encoder_get_task_size (GstNvEncoder * self)
|
|
{
|
|
std::shared_ptr < GstNvEncObject > object = self->priv->object;
|
|
|
|
if (!object)
|
|
return 0;
|
|
|
|
return object->GetTaskSize ();
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
GstVideoInfo info;
|
|
GstBufferPool *pool = NULL;
|
|
GstCaps *caps;
|
|
guint size;
|
|
GstStructure *config;
|
|
GstCapsFeatures *features;
|
|
guint min_buffers;
|
|
gboolean use_cuda_pool = FALSE;
|
|
|
|
gst_query_parse_allocation (query, &caps, NULL);
|
|
if (!caps) {
|
|
GST_WARNING_OBJECT (self, "null caps in query");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_video_info_from_caps (&info, caps)) {
|
|
GST_WARNING_OBJECT (self, "Failed to convert caps into info");
|
|
return FALSE;
|
|
}
|
|
|
|
features = gst_caps_get_features (caps, 0);
|
|
min_buffers = gst_nv_encoder_get_task_size (self);
|
|
if (min_buffers == 0) {
|
|
GstNvEncoderClass *klass = GST_NV_ENCODER_GET_CLASS (self);
|
|
|
|
min_buffers = klass->calculate_min_buffers (self);
|
|
}
|
|
|
|
switch (priv->subclass_device_mode) {
|
|
case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
|
|
/* Use upstream pool in case of auto select mode. We don't know which
|
|
* GPU to use at this moment */
|
|
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
|
|
gst_query_add_allocation_pool (query, nullptr, info.size, min_buffers, 0);
|
|
return TRUE;
|
|
#ifdef G_OS_WIN32
|
|
case GST_NV_ENCODER_DEVICE_D3D11:
|
|
if (features && gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
|
|
GST_DEBUG_OBJECT (self, "upstream support d3d11 memory");
|
|
pool = gst_d3d11_buffer_pool_new (priv->device);
|
|
}
|
|
break;
|
|
#endif
|
|
case GST_NV_ENCODER_DEVICE_CUDA:
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
if (features && gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
|
|
GST_DEBUG_OBJECT (self, "upstream support GL memory");
|
|
if (!gst_nv_encoder_ensure_gl_context (self)) {
|
|
GST_WARNING_OBJECT (self, "Couldn't get GL context");
|
|
priv->gl_interop = FALSE;
|
|
gst_query_add_allocation_meta (query,
|
|
GST_VIDEO_META_API_TYPE, nullptr);
|
|
gst_query_add_allocation_pool (query,
|
|
nullptr, info.size, min_buffers, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
pool = gst_gl_buffer_pool_new (priv->gl_context);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (features && gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY)) {
|
|
GST_DEBUG_OBJECT (self, "upstream support CUDA memory");
|
|
pool = gst_cuda_buffer_pool_new (priv->context);
|
|
use_cuda_pool = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
|
|
if (!pool)
|
|
pool = gst_video_buffer_pool_new ();
|
|
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
|
|
|
size = GST_VIDEO_INFO_SIZE (&info);
|
|
gst_buffer_pool_config_set_params (config, caps, size, min_buffers, 0);
|
|
if (use_cuda_pool && priv->stream) {
|
|
/* Set our stream on buffer pool config so that CUstream can be shared */
|
|
gst_buffer_pool_config_set_cuda_stream (config, priv->stream);
|
|
|
|
/* Encoder does not seem to support stream ordered allocation */
|
|
if (!priv->extern_pool)
|
|
gst_buffer_pool_config_set_cuda_stream_ordered_alloc (config, FALSE);
|
|
}
|
|
|
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
|
GST_WARNING_OBJECT (self, "Failed to set pool config");
|
|
gst_object_unref (pool);
|
|
return FALSE;
|
|
}
|
|
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_get_params (config, NULL, &size, NULL, NULL);
|
|
gst_structure_free (config);
|
|
|
|
gst_query_add_allocation_pool (query, pool, size, min_buffers, 0);
|
|
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
|
|
gst_object_unref (pool);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static NV_ENC_PIC_STRUCT
|
|
gst_nv_encoder_get_pic_struct (GstNvEncoder * self, GstBuffer * buffer)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
GstVideoInfo *info = &priv->input_state->info;
|
|
|
|
if (!GST_VIDEO_INFO_IS_INTERLACED (info))
|
|
return NV_ENC_PIC_STRUCT_FRAME;
|
|
|
|
if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED) {
|
|
if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
|
|
return NV_ENC_PIC_STRUCT_FRAME;
|
|
}
|
|
|
|
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF))
|
|
return NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM;
|
|
|
|
return NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP;
|
|
}
|
|
|
|
switch (GST_VIDEO_INFO_FIELD_ORDER (info)) {
|
|
case GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST:
|
|
return NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM;
|
|
break;
|
|
case GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST:
|
|
return NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF))
|
|
return NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM;
|
|
|
|
return NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP;
|
|
}
|
|
|
|
static GstVideoCodecFrame *
|
|
gst_nv_encoder_find_output_frame (GstVideoEncoder * self, GstNvEncTask * task)
|
|
{
|
|
GList *frames, *iter;
|
|
GstVideoCodecFrame *ret = NULL;
|
|
|
|
frames = gst_video_encoder_get_frames (self);
|
|
|
|
for (iter = frames; iter; iter = g_list_next (iter)) {
|
|
GstVideoCodecFrame *frame = (GstVideoCodecFrame *) iter->data;
|
|
GstNvEncTask *other = (GstNvEncTask *)
|
|
gst_video_codec_frame_get_user_data (frame);
|
|
|
|
if (!other)
|
|
continue;
|
|
|
|
if (other == task) {
|
|
ret = frame;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret)
|
|
gst_video_codec_frame_ref (ret);
|
|
|
|
if (frames)
|
|
g_list_free_full (frames, (GDestroyNotify) gst_video_codec_frame_unref);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_nv_encoder_thread_func (GstNvEncoder * self)
|
|
{
|
|
GstVideoEncoder *encoder = GST_VIDEO_ENCODER (self);
|
|
GstNvEncoderClass *klass = GST_NV_ENCODER_GET_CLASS (self);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
std::shared_ptr < GstNvEncObject > object = priv->object;
|
|
|
|
GST_INFO_OBJECT (self, "Entering encoding loop");
|
|
|
|
do {
|
|
GstFlowReturn ret;
|
|
GstNvEncTask *task = nullptr;
|
|
GstVideoCodecFrame *frame;
|
|
NV_ENC_LOCK_BITSTREAM bitstream;
|
|
NVENCSTATUS status;
|
|
|
|
ret = object->GetOutput (&task);
|
|
if (ret == GST_FLOW_EOS) {
|
|
g_assert (!task);
|
|
GST_INFO_OBJECT (self, "Got EOS task");
|
|
break;
|
|
}
|
|
|
|
frame = gst_nv_encoder_find_output_frame (encoder, task);
|
|
if (!frame) {
|
|
gst_nv_enc_task_unref (task);
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
|
|
("Failed to find associated codec frame"));
|
|
priv->last_flow = GST_FLOW_ERROR;
|
|
continue;
|
|
}
|
|
|
|
status = gst_nv_enc_task_lock_bitstream (task, &bitstream);
|
|
if (status != NV_ENC_SUCCESS) {
|
|
gst_nv_enc_task_unref (task);
|
|
gst_video_encoder_finish_frame (encoder, frame);
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
|
|
("Failed to lock bitstream, status: %" GST_NVENC_STATUS_FORMAT,
|
|
GST_NVENC_STATUS_ARGS (status)));
|
|
priv->last_flow = GST_FLOW_ERROR;
|
|
continue;
|
|
}
|
|
|
|
if (priv->last_flow != GST_FLOW_OK) {
|
|
gst_nv_enc_task_unlock_bitstream (task);
|
|
gst_nv_enc_task_unref (task);
|
|
continue;
|
|
}
|
|
|
|
if (klass->create_output_buffer) {
|
|
frame->output_buffer = klass->create_output_buffer (self, &bitstream);
|
|
} else {
|
|
frame->output_buffer =
|
|
gst_buffer_new_memdup (bitstream.bitstreamBufferPtr,
|
|
bitstream.bitstreamSizeInBytes);
|
|
}
|
|
|
|
GST_BUFFER_FLAG_SET (frame->output_buffer, GST_BUFFER_FLAG_MARKER);
|
|
|
|
if (bitstream.pictureType == NV_ENC_PIC_TYPE_IDR)
|
|
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
|
|
|
|
frame->dts = frame->pts - priv->dts_offset;
|
|
frame->pts = bitstream.outputTimeStamp;
|
|
frame->duration = bitstream.outputDuration;
|
|
|
|
gst_nv_enc_task_unlock_bitstream (task);
|
|
gst_nv_enc_task_unref (task);
|
|
|
|
priv->last_flow = gst_video_encoder_finish_frame (encoder, frame);
|
|
if (priv->last_flow != GST_FLOW_OK) {
|
|
GST_INFO_OBJECT (self,
|
|
"Finish frame returned %s", gst_flow_get_name (priv->last_flow));
|
|
}
|
|
} while (true);
|
|
|
|
GST_INFO_OBJECT (self, "Exiting thread");
|
|
}
|
|
|
|
static guint
|
|
gst_nv_encoder_calculate_task_pool_size (GstNvEncoder * self,
|
|
NV_ENC_CONFIG * config)
|
|
{
|
|
guint num_tasks;
|
|
|
|
/* At least 4 surfaces are required as documented by Nvidia Encoder guide */
|
|
num_tasks = 4;
|
|
|
|
/* lookahead depth */
|
|
num_tasks += config->rcParams.lookaheadDepth;
|
|
|
|
/* B frames + 1 */
|
|
num_tasks += MAX (0, config->frameIntervalP - 1) + 1;
|
|
|
|
GST_DEBUG_OBJECT (self, "Calculated task pool size: %d "
|
|
"(lookahead %d, frameIntervalP %d)",
|
|
num_tasks, config->rcParams.lookaheadDepth, config->frameIntervalP);
|
|
|
|
return num_tasks;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_open_encode_session (GstNvEncoder * self)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, };
|
|
session_params.version =
|
|
gst_nvenc_get_open_encode_session_ex_params_version ();
|
|
session_params.apiVersion = gst_nvenc_get_api_version ();
|
|
GstObject *device = (GstObject *) priv->context;
|
|
|
|
switch (priv->selected_device_mode) {
|
|
#ifdef G_OS_WIN32
|
|
case GST_NV_ENCODER_DEVICE_D3D11:
|
|
session_params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX;
|
|
session_params.device = gst_d3d11_device_get_device_handle (priv->device);
|
|
device = (GstObject *) priv->device;
|
|
break;
|
|
#endif
|
|
case GST_NV_ENCODER_DEVICE_CUDA:
|
|
session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
|
|
session_params.device = gst_cuda_context_get_handle (priv->context);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
|
|
priv->object = GstNvEncObject::CreateInstance (GST_ELEMENT_CAST (self),
|
|
device, &session_params);
|
|
|
|
if (!priv->object) {
|
|
GST_ERROR_OBJECT (self, "Couldn't create encoder session");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef G_OS_WIN32
|
|
static GstBufferPool *
|
|
gst_nv_encoder_create_d3d11_pool (GstNvEncoder * self,
|
|
GstVideoCodecState * state)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
GstStructure *config;
|
|
GstBufferPool *pool = NULL;
|
|
GstD3D11AllocationParams *params;
|
|
|
|
params = gst_d3d11_allocation_params_new (priv->device, &state->info,
|
|
GST_D3D11_ALLOCATION_FLAG_DEFAULT, 0, D3D11_RESOURCE_MISC_SHARED);
|
|
|
|
pool = gst_d3d11_buffer_pool_new (priv->device);
|
|
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_set_d3d11_allocation_params (config, params);
|
|
gst_d3d11_allocation_params_free (params);
|
|
|
|
gst_buffer_pool_config_set_params (config, state->caps,
|
|
GST_VIDEO_INFO_SIZE (&state->info), 0, 0);
|
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
|
GST_ERROR_OBJECT (self, "Failed to set pool config");
|
|
gst_object_unref (pool);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (!gst_buffer_pool_set_active (pool, TRUE)) {
|
|
GST_ERROR_OBJECT (self, "Failed to set active");
|
|
gst_object_unref (pool);
|
|
return NULL;
|
|
}
|
|
|
|
return pool;
|
|
}
|
|
#endif
|
|
|
|
static GstBufferPool *
|
|
gst_nv_encoder_create_pool (GstNvEncoder * self, GstVideoCodecState * state)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
GstStructure *config;
|
|
GstBufferPool *pool = NULL;
|
|
|
|
/* At this moment device type must be selected already */
|
|
switch (priv->selected_device_mode) {
|
|
#ifdef G_OS_WIN32
|
|
case GST_NV_ENCODER_DEVICE_D3D11:
|
|
return gst_nv_encoder_create_d3d11_pool (self, state);
|
|
#endif
|
|
case GST_NV_ENCODER_DEVICE_CUDA:
|
|
pool = gst_cuda_buffer_pool_new (priv->context);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_set_params (config, state->caps,
|
|
GST_VIDEO_INFO_SIZE (&state->info), 0, 0);
|
|
if (priv->selected_device_mode == GST_NV_ENCODER_DEVICE_CUDA && priv->stream)
|
|
gst_buffer_pool_config_set_cuda_stream (config, priv->stream);
|
|
|
|
/* Encoder does not seem to support stream ordered allocation */
|
|
gst_buffer_pool_config_set_cuda_stream_ordered_alloc (config, FALSE);
|
|
|
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
|
GST_ERROR_OBJECT (self, "Failed to set pool config");
|
|
gst_object_unref (pool);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (!gst_buffer_pool_set_active (pool, TRUE)) {
|
|
GST_ERROR_OBJECT (self, "Failed to set active");
|
|
gst_object_unref (pool);
|
|
return NULL;
|
|
}
|
|
|
|
return pool;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_init_session (GstNvEncoder * self, GstBuffer * in_buf)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
GstNvEncoderClass *klass = GST_NV_ENCODER_GET_CLASS (self);
|
|
GstVideoCodecState *state = priv->input_state;
|
|
GstVideoInfo *info = &state->info;
|
|
NVENCSTATUS status;
|
|
guint task_pool_size;
|
|
gint fps_n, fps_d;
|
|
GstClockTime frame_duration, min_latency, max_latency;
|
|
|
|
gst_nv_encoder_reset (self);
|
|
|
|
memset (&priv->init_params, 0, sizeof (NV_ENC_INITIALIZE_PARAMS));
|
|
memset (&priv->config, 0, sizeof (NV_ENC_CONFIG));
|
|
|
|
if (priv->selected_device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) {
|
|
GstNvEncoderDeviceData data;
|
|
gboolean ret;
|
|
|
|
if (!in_buf) {
|
|
GST_DEBUG_OBJECT (self, "Unknown device mode, open session later");
|
|
return TRUE;
|
|
}
|
|
|
|
if (!klass->select_device (self, info, in_buf, &data)) {
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
|
|
("Failed to select device mode"));
|
|
return FALSE;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self,
|
|
"Selected device mode: %d, cuda-device-id: %d, adapter-luid %"
|
|
G_GINT64_FORMAT, data.device_mode, data.cuda_device_id,
|
|
data.adapter_luid);
|
|
|
|
g_assert (data.device_mode == GST_NV_ENCODER_DEVICE_CUDA ||
|
|
data.device_mode == GST_NV_ENCODER_DEVICE_D3D11);
|
|
|
|
std::lock_guard < std::recursive_mutex > clk (priv->context_lock);
|
|
priv->selected_device_mode = data.device_mode;
|
|
priv->cuda_device_id = data.cuda_device_id;
|
|
priv->dxgi_adapter_luid = data.adapter_luid;
|
|
gst_clear_object (&priv->context);
|
|
if (data.device_mode == GST_NV_ENCODER_DEVICE_CUDA) {
|
|
GstMemory *mem = gst_buffer_peek_memory (in_buf, 0);
|
|
|
|
priv->context = (GstCudaContext *) data.device;
|
|
gst_clear_cuda_stream (&priv->stream);
|
|
|
|
if (gst_nvenc_have_set_io_cuda_streams ()) {
|
|
if (gst_is_cuda_memory (mem)) {
|
|
/* Use upstream CUDA stream */
|
|
priv->stream =
|
|
gst_cuda_memory_get_stream (GST_CUDA_MEMORY_CAST (mem));
|
|
if (priv->stream)
|
|
gst_cuda_stream_ref (priv->stream);
|
|
}
|
|
}
|
|
}
|
|
#ifdef G_OS_WIN32
|
|
gst_clear_object (&priv->device);
|
|
if (data.device_mode == GST_NV_ENCODER_DEVICE_D3D11)
|
|
priv->device = (GstD3D11Device *) data.device;
|
|
#endif
|
|
|
|
ret = gst_nv_encoder_open (GST_VIDEO_ENCODER (self));
|
|
|
|
if (!ret) {
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
|
|
("Failed to open device"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
priv->internal_pool = gst_nv_encoder_create_pool (self, state);
|
|
if (!priv->internal_pool) {
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
|
|
("Failed to create internal pool"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_nv_encoder_device_lock (self)) {
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL), ("Failed to lock device"));
|
|
gst_nv_encoder_reset (self);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_nv_encoder_open_encode_session (self)) {
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
|
|
("Failed to open session"));
|
|
goto error;
|
|
}
|
|
|
|
if (!klass->set_format (self, state, priv->object->GetHandle (),
|
|
&priv->init_params, &priv->config)) {
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL), ("Failed to set format"));
|
|
goto error;
|
|
}
|
|
|
|
task_pool_size = gst_nv_encoder_calculate_task_pool_size (self,
|
|
&priv->config);
|
|
|
|
priv->init_params.encodeConfig = &priv->config;
|
|
status = priv->object->InitSession (&priv->init_params,
|
|
priv->stream, &priv->input_state->info, task_pool_size);
|
|
if (!gst_nv_enc_result (status, self)) {
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
|
|
("Failed to init encoder, status: %"
|
|
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status)));
|
|
goto error;
|
|
}
|
|
|
|
gst_nv_encoder_device_unlock (self);
|
|
|
|
if (!klass->set_output_state (self, priv->input_state,
|
|
priv->object->GetHandle ())) {
|
|
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
|
|
("Failed to set output state"));
|
|
gst_nv_encoder_reset (self);
|
|
return FALSE;
|
|
}
|
|
|
|
priv->encoding_thread = std::make_unique < std::thread >
|
|
(gst_nv_encoder_thread_func, self);
|
|
|
|
if (info->fps_n > 0 && info->fps_d > 0) {
|
|
fps_n = info->fps_n;
|
|
fps_d = info->fps_d;
|
|
} else {
|
|
fps_n = 25;
|
|
fps_d = 1;
|
|
}
|
|
|
|
frame_duration = gst_util_uint64_scale (GST_SECOND, fps_d, fps_n);
|
|
|
|
priv->dts_offset = 0;
|
|
/* Calculate DTS offset for B frame. NVENC does not provide DTS */
|
|
if (priv->config.frameIntervalP > 1)
|
|
priv->dts_offset = frame_duration * (priv->config.frameIntervalP - 1);
|
|
|
|
min_latency = priv->dts_offset +
|
|
priv->config.rcParams.lookaheadDepth * frame_duration;
|
|
max_latency = frame_duration * task_pool_size;
|
|
gst_video_encoder_set_latency (GST_VIDEO_ENCODER (self),
|
|
min_latency, max_latency);
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
gst_nv_encoder_device_unlock (self);
|
|
|
|
gst_nv_encoder_reset (self);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_reconfigure_session (GstNvEncoder * self)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
NV_ENC_RECONFIGURE_PARAMS params = { 0, };
|
|
NVENCSTATUS status;
|
|
|
|
if (!priv->object) {
|
|
GST_WARNING_OBJECT (self,
|
|
"Encoding session was not configured, open session");
|
|
gst_nv_encoder_drain (self, TRUE);
|
|
|
|
return gst_nv_encoder_init_session (self, nullptr);
|
|
}
|
|
|
|
params.version = gst_nvenc_get_reconfigure_params_version ();
|
|
params.reInitEncodeParams = priv->init_params;
|
|
params.reInitEncodeParams.encodeConfig = &priv->config;
|
|
|
|
status = priv->object->Reconfigure (¶ms);
|
|
if (!gst_nv_enc_result (status, self)) {
|
|
gst_nv_encoder_drain (self, TRUE);
|
|
|
|
return gst_nv_encoder_init_session (self, nullptr);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_set_format (GstVideoEncoder * encoder,
|
|
GstVideoCodecState * state)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
gst_nv_encoder_drain (self, TRUE);
|
|
|
|
g_clear_pointer (&priv->input_state, gst_video_codec_state_unref);
|
|
priv->input_state = gst_video_codec_state_ref (state);
|
|
priv->last_flow = GST_FLOW_OK;
|
|
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
{
|
|
GstCapsFeatures *features = gst_caps_get_features (state->caps, 0);
|
|
if (gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
|
|
priv->gl_interop = TRUE;
|
|
} else {
|
|
priv->gl_interop = FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_GST_D3D12
|
|
{
|
|
auto features = gst_caps_get_features (state->caps, 0);
|
|
gst_clear_object (&priv->interop_12);
|
|
if (gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY) &&
|
|
gst_d3d12_ensure_element_data_for_adapter_luid (GST_ELEMENT (self),
|
|
priv->dxgi_adapter_luid, &priv->device_12)) {
|
|
priv->interop_12 = gst_cuda_d3d12_interop_new (priv->context,
|
|
priv->device_12, &state->info, TRUE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* select device again on next buffer */
|
|
if (priv->subclass_device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT)
|
|
priv->selected_device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT;
|
|
|
|
return gst_nv_encoder_init_session (self, nullptr);
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_nv_encoder_copy_system (GstNvEncoder * self, const GstVideoInfo * info,
|
|
GstBuffer * buffer, GstNvEncTask * task)
|
|
{
|
|
std::shared_ptr < GstNvEncObject > object = self->priv->object;
|
|
NVENCSTATUS status;
|
|
GstVideoFrame frame;
|
|
guint8 *dst_data;
|
|
guint32 pitch;
|
|
GstNvEncBuffer *inbuf = nullptr;
|
|
|
|
if (!gst_video_frame_map (&frame, info, buffer, GST_MAP_READ)) {
|
|
GST_ERROR_OBJECT (self, "Failed to map buffer");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
status = object->AcquireBuffer (&inbuf);
|
|
if (!gst_nv_enc_result (status, self)) {
|
|
gst_video_frame_unmap (&frame);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
status = gst_nv_enc_buffer_lock (inbuf, (gpointer *) & dst_data, &pitch);
|
|
if (!gst_nv_enc_result (status, self)) {
|
|
gst_video_frame_unmap (&frame);
|
|
gst_nv_enc_buffer_unref (inbuf);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES (&frame); i++) {
|
|
guint8 *src_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame, i);
|
|
guint width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&frame, i) *
|
|
GST_VIDEO_FRAME_COMP_PSTRIDE (&frame, i);
|
|
guint stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, i);
|
|
guint height = GST_VIDEO_FRAME_COMP_HEIGHT (&frame, i);
|
|
|
|
for (guint j = 0; j < height; j++) {
|
|
memcpy (dst_data, src_data, width_in_bytes);
|
|
dst_data += pitch;
|
|
src_data += stride;
|
|
}
|
|
}
|
|
|
|
gst_nv_enc_buffer_unlock (inbuf);
|
|
gst_video_frame_unmap (&frame);
|
|
|
|
gst_nv_enc_task_set_buffer (task, inbuf);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
static GstCudaGraphicsResource *
|
|
gst_nv_encoder_ensure_gl_cuda_resource (GstNvEncoder * self, GstMemory * mem)
|
|
{
|
|
GQuark quark;
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
GstCudaGraphicsResource *resource;
|
|
|
|
if (!gst_is_gl_memory_pbo (mem)) {
|
|
GST_WARNING_OBJECT (self, "memory is not GL PBO memory, %s",
|
|
mem->allocator->mem_type);
|
|
return nullptr;
|
|
}
|
|
|
|
quark = gst_cuda_quark_from_id (GST_CUDA_QUARK_GRAPHICS_RESOURCE);
|
|
resource = (GstCudaGraphicsResource *)
|
|
gst_mini_object_get_qdata (GST_MINI_OBJECT (mem), quark);
|
|
|
|
if (!resource) {
|
|
GstMapInfo map_info;
|
|
GstGLMemoryPBO *pbo = (GstGLMemoryPBO *) mem;
|
|
GstGLBuffer *gl_buf = pbo->pbo;
|
|
gboolean ret;
|
|
|
|
if (!gst_memory_map (mem, &map_info,
|
|
(GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) {
|
|
GST_ERROR_OBJECT (self, "Couldn't map gl memory");
|
|
return nullptr;
|
|
}
|
|
|
|
resource = gst_cuda_graphics_resource_new (priv->context,
|
|
GST_OBJECT (GST_GL_BASE_MEMORY_CAST (mem)->context),
|
|
GST_CUDA_GRAPHICS_RESOURCE_GL_BUFFER);
|
|
|
|
GST_LOG_OBJECT (self, "registering gl buffer %d to CUDA", gl_buf->id);
|
|
ret = gst_cuda_graphics_resource_register_gl_buffer (resource, gl_buf->id,
|
|
CU_GRAPHICS_REGISTER_FLAGS_NONE);
|
|
gst_memory_unmap (mem, &map_info);
|
|
|
|
if (!ret) {
|
|
GST_ERROR_OBJECT (self, "Couldn't register gl buffer %d", gl_buf->id);
|
|
gst_cuda_graphics_resource_free (resource);
|
|
return nullptr;
|
|
}
|
|
|
|
gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), quark, resource,
|
|
(GDestroyNotify) gst_cuda_graphics_resource_free);
|
|
}
|
|
|
|
return resource;
|
|
}
|
|
|
|
struct GstNvEncGLInteropData
|
|
{
|
|
GstNvEncoder *self = nullptr;
|
|
GstBuffer *in_buf = nullptr;
|
|
GstBuffer *out_buf = nullptr;
|
|
};
|
|
|
|
static void
|
|
gst_nv_encoder_upload_gl (GstGLContext * context, GstNvEncGLInteropData * data)
|
|
{
|
|
GstNvEncoder *self = data->self;
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
CUDA_MEMCPY2D copy_param;
|
|
GstCudaGraphicsResource *gst_res[GST_VIDEO_MAX_PLANES] = { nullptr, };
|
|
CUgraphicsResource cuda_res[GST_VIDEO_MAX_PLANES] = { nullptr, };
|
|
CUdeviceptr src_devptr[GST_VIDEO_MAX_PLANES] = { 0, };
|
|
const GstVideoInfo *info = &priv->input_state->info;
|
|
CUstream stream = gst_cuda_stream_get_handle (priv->stream);
|
|
GstCudaMemory *cmem;
|
|
GstMapInfo map_info;
|
|
CUresult cuda_ret;
|
|
gboolean ret = FALSE;
|
|
|
|
gst_cuda_context_push (priv->context);
|
|
|
|
for (guint i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
|
|
GstMemory *mem = gst_buffer_peek_memory (data->in_buf, i);
|
|
GstGLMemoryPBO *pbo = (GstGLMemoryPBO *) mem;
|
|
gsize src_size;
|
|
|
|
if (!gst_is_gl_memory_pbo (mem)) {
|
|
GST_ERROR_OBJECT (self, "Not a GL PBO memory");
|
|
goto out;
|
|
}
|
|
|
|
gst_res[i] = gst_nv_encoder_ensure_gl_cuda_resource (self, mem);
|
|
if (!gst_res[i]) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get resource %d", i);
|
|
goto out;
|
|
}
|
|
|
|
gst_gl_memory_pbo_upload_transfer (pbo);
|
|
gst_gl_memory_pbo_download_transfer (pbo);
|
|
|
|
cuda_res[i] = gst_cuda_graphics_resource_map (gst_res[i], stream,
|
|
CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
|
|
if (!cuda_res[i]) {
|
|
GST_ERROR_OBJECT (self, "Couldn't map resource");
|
|
goto out;
|
|
}
|
|
|
|
cuda_ret = CuGraphicsResourceGetMappedPointer (&src_devptr[i],
|
|
&src_size, cuda_res[i]);
|
|
if (!gst_cuda_result (cuda_ret)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get mapped device pointer");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (gst_buffer_pool_acquire_buffer (priv->internal_pool,
|
|
&data->out_buf, nullptr) != GST_FLOW_OK) {
|
|
GST_ERROR_OBJECT (self, "Couldn't acquire fallback buffer");
|
|
goto out;
|
|
}
|
|
|
|
cmem = (GstCudaMemory *) gst_buffer_peek_memory (data->out_buf, 0);
|
|
if (!gst_memory_map (GST_MEMORY_CAST (cmem), &map_info,
|
|
(GstMapFlags) (GST_MAP_WRITE | GST_MAP_CUDA))) {
|
|
GST_ERROR_OBJECT (self, "Couldn't map fallback memory");
|
|
goto out;
|
|
}
|
|
|
|
memset (©_param, 0, sizeof (CUDA_MEMCPY2D));
|
|
|
|
for (guint i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
|
|
copy_param.srcMemoryType = CU_MEMORYTYPE_DEVICE;
|
|
copy_param.srcDevice = src_devptr[i];
|
|
copy_param.srcPitch = GST_VIDEO_INFO_PLANE_STRIDE (info, i);
|
|
|
|
copy_param.dstMemoryType = CU_MEMORYTYPE_DEVICE;
|
|
copy_param.dstDevice = ((CUdeviceptr) map_info.data) + cmem->info.offset[i];
|
|
copy_param.dstPitch = cmem->info.stride[0];
|
|
|
|
copy_param.WidthInBytes = GST_VIDEO_INFO_COMP_WIDTH (info, i) *
|
|
GST_VIDEO_INFO_COMP_PSTRIDE (info, i);
|
|
copy_param.Height = GST_VIDEO_INFO_COMP_HEIGHT (info, i);
|
|
|
|
if (!gst_cuda_result (CuMemcpy2DAsync (©_param, stream))) {
|
|
gst_memory_unmap (GST_MEMORY_CAST (cmem), &map_info);
|
|
GST_ERROR_OBJECT (self, "Couldn't copy plane %d", i);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
gst_memory_unmap (GST_MEMORY_CAST (cmem), &map_info);
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
for (guint i = 0; i < gst_buffer_n_memory (data->in_buf); i++) {
|
|
if (!gst_res[i])
|
|
break;
|
|
|
|
gst_cuda_graphics_resource_unmap (gst_res[i], stream);
|
|
}
|
|
|
|
CuStreamSynchronize (stream);
|
|
gst_cuda_context_pop (nullptr);
|
|
if (!ret)
|
|
gst_clear_buffer (&data->out_buf);
|
|
}
|
|
#endif /* HAVE_CUDA_GST_GL */
|
|
|
|
static GstFlowReturn
|
|
gst_nv_encoder_prepare_task_input_cuda (GstNvEncoder * self,
|
|
GstBuffer * buffer, GstNvEncTask * task)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
std::shared_ptr < GstNvEncObject > object = priv->object;
|
|
GstMemory *mem;
|
|
GstCudaMemory *cmem;
|
|
NVENCSTATUS status;
|
|
GstCudaStream *stream;
|
|
GstNvEncResource *resource = nullptr;
|
|
const GstVideoInfo *info = &priv->input_state->info;
|
|
gboolean sync_done = FALSE;
|
|
guint out_stride = 0;
|
|
gboolean is_extern_mem = FALSE;
|
|
|
|
mem = gst_buffer_peek_memory (buffer, 0);
|
|
|
|
#ifdef HAVE_CUDA_GST_GL
|
|
if (priv->gl_interop) {
|
|
if (gst_is_gl_memory (mem) && gst_buffer_n_memory (buffer) ==
|
|
GST_VIDEO_INFO_N_PLANES (info)) {
|
|
GstNvEncGLInteropData gl_data;
|
|
GstGLMemory *gl_mem = (GstGLMemory *) mem;
|
|
gl_data.self = self;
|
|
gl_data.in_buf = buffer;
|
|
gl_data.out_buf = nullptr;
|
|
gst_gl_context_thread_add (gl_mem->mem.context,
|
|
(GstGLContextThreadFunc) gst_nv_encoder_upload_gl, &gl_data);
|
|
if (gl_data.out_buf) {
|
|
mem = gst_buffer_peek_memory (gl_data.out_buf, 0);
|
|
status = object->AcquireResource (mem, &resource);
|
|
|
|
if (status != NV_ENC_SUCCESS) {
|
|
gst_buffer_unref (gl_data.out_buf);
|
|
GST_ERROR_OBJECT (self, "Failed to get resource, status %"
|
|
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
gst_nv_enc_task_set_resource (task, gl_data.out_buf, resource);
|
|
|
|
return GST_FLOW_OK;
|
|
} else {
|
|
GST_WARNING_OBJECT (self, "GL interop failed");
|
|
priv->gl_interop = FALSE;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_GST_D3D12
|
|
if (priv->interop_12) {
|
|
if (gst_is_d3d12_memory (mem)) {
|
|
auto dmem = GST_D3D12_MEMORY_CAST (mem);
|
|
if (!gst_d3d12_device_is_equal (dmem->device, priv->device_12)) {
|
|
GST_DEBUG_OBJECT (self, "Different d3d12 device");
|
|
gst_clear_object (&priv->interop_12);
|
|
}
|
|
} else {
|
|
GST_WARNING_OBJECT (self, "Not a d3d12 buffer");
|
|
gst_clear_object (&priv->interop_12);
|
|
}
|
|
}
|
|
|
|
if (priv->interop_12) {
|
|
GstBuffer *copy = nullptr;
|
|
gst_buffer_pool_acquire_buffer (priv->internal_pool, ©, nullptr);
|
|
if (!copy) {
|
|
GST_ERROR_OBJECT (self, "Couldn't acquire buffer");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
if (!gst_cuda_d3d12_interop_upload_async (priv->interop_12,
|
|
copy, buffer, priv->stream)) {
|
|
GST_WARNING_OBJECT (self, "Couldn't upload d3d12 to cuda");
|
|
gst_buffer_unref (copy);
|
|
gst_clear_object (&priv->interop_12);
|
|
} else {
|
|
mem = gst_buffer_peek_memory (copy, 0);
|
|
|
|
status = object->AcquireResource (mem, &resource);
|
|
if (status != NV_ENC_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "Failed to get resource, status %"
|
|
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
|
|
gst_buffer_unref (copy);
|
|
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
gst_nv_enc_task_set_resource (task, copy, resource);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!gst_is_cuda_memory (mem)) {
|
|
GST_LOG_OBJECT (self, "Not a CUDA buffer, system copy");
|
|
return gst_nv_encoder_copy_system (self, info, buffer, task);
|
|
}
|
|
|
|
cmem = GST_CUDA_MEMORY_CAST (mem);
|
|
if (cmem->context != priv->context) {
|
|
GST_LOG_OBJECT (self, "Different context, system copy");
|
|
return gst_nv_encoder_copy_system (self, info, buffer, task);
|
|
}
|
|
|
|
out_stride = cmem->info.stride[0];
|
|
|
|
if (gst_cuda_memory_is_stream_ordered (mem)) {
|
|
GstBuffer *copy = nullptr;
|
|
GstVideoFrame in_frame;
|
|
CUDA_MEMCPY2D copy_params = { };
|
|
GstMemory *out_mem;
|
|
GstMapInfo out_map;
|
|
guint8 *out_data;
|
|
|
|
stream = gst_cuda_memory_get_stream (cmem);
|
|
|
|
GST_LOG_OBJECT (self, "Stream ordered allocation needs memory copy");
|
|
|
|
if (!gst_video_frame_map (&in_frame, info, buffer,
|
|
(GstMapFlags) (GST_MAP_READ | GST_MAP_CUDA))) {
|
|
GST_ERROR_OBJECT (self, "Couldn't map input buffer");
|
|
gst_buffer_unref (copy);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
if (priv->extern_pool) {
|
|
auto cuda_pool = GST_CUDA_BUFFER_POOL (priv->extern_pool);
|
|
if (cuda_pool->context == priv->context) {
|
|
gst_buffer_pool_acquire_buffer (priv->extern_pool, ©, nullptr);
|
|
if (copy) {
|
|
auto copy_mem = gst_buffer_peek_memory (copy, 0);
|
|
if (gst_cuda_memory_is_stream_ordered (copy_mem)) {
|
|
GST_LOG_OBJECT (self, "External pool uses stream ordered alloc");
|
|
gst_clear_buffer (©);
|
|
} else if (gst_memory_get_sizes (mem, nullptr, nullptr) >
|
|
gst_memory_get_sizes (copy_mem, nullptr, nullptr)) {
|
|
GST_LOG_OBJECT (self, "Too small extern pool buffer");
|
|
gst_clear_buffer (©);
|
|
} else {
|
|
is_extern_mem = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!copy)
|
|
gst_buffer_pool_acquire_buffer (priv->internal_pool, ©, nullptr);
|
|
|
|
if (!copy) {
|
|
GST_ERROR_OBJECT (self, "Couldn't allocate internal buffer");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
out_mem = gst_buffer_peek_memory (copy, 0);
|
|
|
|
if (!gst_memory_map (out_mem,
|
|
&out_map, (GstMapFlags) (GST_MAP_WRITE | GST_MAP_CUDA))) {
|
|
GST_ERROR_OBJECT (self, "Couldn't map output buffer");
|
|
gst_video_frame_unmap (&in_frame);
|
|
gst_buffer_unref (copy);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
out_data = (guint8 *) out_map.data;
|
|
if (is_extern_mem)
|
|
out_stride = in_frame.info.stride[0];
|
|
else
|
|
out_stride = GST_CUDA_MEMORY_CAST (out_mem)->info.stride[0];
|
|
|
|
for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES (&in_frame); i++) {
|
|
copy_params.srcMemoryType = CU_MEMORYTYPE_DEVICE;
|
|
copy_params.srcDevice = (CUdeviceptr)
|
|
GST_VIDEO_FRAME_PLANE_DATA (&in_frame, i);
|
|
copy_params.srcPitch = GST_VIDEO_FRAME_PLANE_STRIDE (&in_frame, i);
|
|
|
|
copy_params.dstMemoryType = CU_MEMORYTYPE_DEVICE;
|
|
copy_params.dstDevice = (CUdeviceptr) out_data;
|
|
copy_params.dstPitch = out_stride;
|
|
|
|
copy_params.WidthInBytes = GST_VIDEO_INFO_COMP_WIDTH (info, i) *
|
|
GST_VIDEO_INFO_COMP_PSTRIDE (info, i);
|
|
copy_params.Height = GST_VIDEO_INFO_COMP_HEIGHT (info, i);
|
|
|
|
auto cuda_ret = CuMemcpy2DAsync (©_params,
|
|
gst_cuda_stream_get_handle (stream));
|
|
if (!gst_cuda_result (cuda_ret)) {
|
|
GST_ERROR_OBJECT (self, "Copy failed");
|
|
gst_video_frame_unmap (&in_frame);
|
|
gst_memory_unmap (out_mem, &out_map);
|
|
|
|
gst_buffer_unref (copy);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
out_data += GST_VIDEO_INFO_COMP_HEIGHT (info, i) * out_stride;
|
|
}
|
|
|
|
gst_video_frame_unmap (&in_frame);
|
|
gst_memory_unmap (out_mem, &out_map);
|
|
|
|
if (stream && stream != priv->stream) {
|
|
CuStreamSynchronize (gst_cuda_stream_get_handle (stream));
|
|
sync_done = TRUE;
|
|
}
|
|
|
|
buffer = copy;
|
|
mem = gst_buffer_peek_memory (copy, 0);
|
|
cmem = GST_CUDA_MEMORY_CAST (mem);
|
|
} else {
|
|
buffer = gst_buffer_ref (buffer);
|
|
}
|
|
|
|
if (is_extern_mem) {
|
|
status = object->AcquireResourceWithSize (mem, info->width, info->height,
|
|
out_stride, &resource);
|
|
} else {
|
|
status = object->AcquireResource (mem, &resource);
|
|
}
|
|
|
|
if (status != NV_ENC_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "Failed to get resource, status %"
|
|
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
|
|
gst_buffer_unref (buffer);
|
|
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
stream = gst_cuda_memory_get_stream (cmem);
|
|
if (stream != priv->stream && !sync_done) {
|
|
/* different stream, needs sync */
|
|
gst_cuda_memory_sync (cmem);
|
|
}
|
|
|
|
gst_nv_enc_task_set_resource (task, buffer, resource);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
#ifdef G_OS_WIN32
|
|
static GstBuffer *
|
|
gst_nv_encoder_copy_d3d11 (GstNvEncoder * self,
|
|
GstBuffer * src_buffer, GstBufferPool * pool, gboolean shared)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
D3D11_TEXTURE2D_DESC src_desc, dst_desc;
|
|
D3D11_BOX src_box;
|
|
guint subresource_idx;
|
|
GstMemory *src_mem, *dst_mem;
|
|
GstMapInfo src_info, dst_info;
|
|
ID3D11Texture2D *src_tex, *dst_tex;
|
|
ID3D11Device *device_handle;
|
|
ID3D11DeviceContext *device_context;
|
|
GstBuffer *dst_buffer;
|
|
GstFlowReturn ret;
|
|
ComPtr < IDXGIResource > dxgi_resource;
|
|
ComPtr < ID3D11Texture2D > shared_texture;
|
|
HANDLE shared_handle;
|
|
GstD3D11Device *device;
|
|
HRESULT hr;
|
|
|
|
ret = gst_buffer_pool_acquire_buffer (pool, &dst_buffer, NULL);
|
|
if (ret != GST_FLOW_OK) {
|
|
GST_ERROR_OBJECT (self, "Failed to acquire buffer");
|
|
return NULL;
|
|
}
|
|
|
|
src_mem = gst_buffer_peek_memory (src_buffer, 0);
|
|
dst_mem = gst_buffer_peek_memory (dst_buffer, 0);
|
|
|
|
device = GST_D3D11_MEMORY_CAST (src_mem)->device;
|
|
|
|
device_handle = gst_d3d11_device_get_device_handle (device);
|
|
device_context = gst_d3d11_device_get_device_context_handle (device);
|
|
|
|
if (!gst_memory_map (src_mem, &src_info,
|
|
(GstMapFlags) (GST_MAP_READ | GST_MAP_D3D11))) {
|
|
GST_WARNING ("Failed to map src memory");
|
|
gst_buffer_unref (dst_buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (!gst_memory_map (dst_mem, &dst_info,
|
|
(GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11))) {
|
|
GST_WARNING ("Failed to map dst memory");
|
|
gst_memory_unmap (src_mem, &src_info);
|
|
gst_buffer_unref (dst_buffer);
|
|
return NULL;
|
|
}
|
|
|
|
src_tex = (ID3D11Texture2D *) src_info.data;
|
|
dst_tex = (ID3D11Texture2D *) dst_info.data;
|
|
|
|
gst_d3d11_memory_get_texture_desc (GST_D3D11_MEMORY_CAST (src_mem),
|
|
&src_desc);
|
|
gst_d3d11_memory_get_texture_desc (GST_D3D11_MEMORY_CAST (dst_mem),
|
|
&dst_desc);
|
|
subresource_idx =
|
|
gst_d3d11_memory_get_subresource_index (GST_D3D11_MEMORY_CAST (src_mem));
|
|
|
|
if (shared) {
|
|
hr = dst_tex->QueryInterface (IID_PPV_ARGS (&dxgi_resource));
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_ERROR_OBJECT (self,
|
|
"IDXGIResource interface is not available, hr: 0x%x", (guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
hr = dxgi_resource->GetSharedHandle (&shared_handle);
|
|
if (!gst_d3d11_result (hr, priv->device)) {
|
|
GST_ERROR_OBJECT (self, "Failed to get shared handle, hr: 0x%x",
|
|
(guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
hr = device_handle->OpenSharedResource (shared_handle,
|
|
IID_PPV_ARGS (&shared_texture));
|
|
|
|
if (!gst_d3d11_result (hr, device)) {
|
|
GST_ERROR_OBJECT (self, "Failed to get shared texture, hr: 0x%x",
|
|
(guint) hr);
|
|
goto error;
|
|
}
|
|
|
|
dst_tex = shared_texture.Get ();
|
|
}
|
|
|
|
src_box.left = 0;
|
|
src_box.top = 0;
|
|
src_box.front = 0;
|
|
src_box.back = 1;
|
|
src_box.right = MIN (src_desc.Width, dst_desc.Width);
|
|
src_box.bottom = MIN (src_desc.Height, dst_desc.Height);
|
|
|
|
if (shared) {
|
|
if (priv->fence && priv->fence->device != device)
|
|
gst_clear_d3d11_fence (&priv->fence);
|
|
|
|
if (!priv->fence)
|
|
priv->fence = gst_d3d11_device_create_fence (device);
|
|
|
|
if (!priv->fence) {
|
|
GST_ERROR_OBJECT (self, "Couldn't crete fence");
|
|
goto error;
|
|
}
|
|
|
|
gst_d3d11_device_lock (device);
|
|
}
|
|
|
|
device_context->CopySubresourceRegion (dst_tex, 0,
|
|
0, 0, 0, src_tex, subresource_idx, &src_box);
|
|
|
|
if (shared) {
|
|
if (!gst_d3d11_fence_signal (priv->fence) ||
|
|
!gst_d3d11_fence_wait (priv->fence)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't sync GPU operation");
|
|
gst_d3d11_device_unlock (device);
|
|
gst_clear_d3d11_fence (&priv->fence);
|
|
goto error;
|
|
}
|
|
|
|
gst_d3d11_device_unlock (device);
|
|
}
|
|
|
|
gst_memory_unmap (dst_mem, &dst_info);
|
|
gst_memory_unmap (src_mem, &src_info);
|
|
|
|
return dst_buffer;
|
|
|
|
error:
|
|
gst_memory_unmap (dst_mem, &dst_info);
|
|
gst_memory_unmap (src_mem, &src_info);
|
|
gst_buffer_unref (dst_buffer);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GstBuffer *
|
|
gst_nv_encoder_upload_d3d11_frame (GstNvEncoder * self,
|
|
const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool)
|
|
{
|
|
GstD3D11Memory *dmem;
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
|
|
dmem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, 0);
|
|
|
|
gst_d3d11_memory_get_texture_desc (dmem, &desc);
|
|
if (desc.Usage != D3D11_USAGE_DEFAULT) {
|
|
GST_TRACE_OBJECT (self, "Not a default usage texture, d3d11 copy");
|
|
return gst_nv_encoder_copy_d3d11 (self, buffer, pool, FALSE);
|
|
}
|
|
|
|
GST_TRACE_OBJECT (self, "Use input buffer without copy");
|
|
|
|
return gst_buffer_ref (buffer);
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_nv_encoder_prepare_task_input_d3d11 (GstNvEncoder * self,
|
|
GstBuffer * buffer, GstNvEncTask * task)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
std::shared_ptr < GstNvEncObject > object = priv->object;
|
|
GstMemory *mem;
|
|
GstD3D11Memory *dmem;
|
|
NVENCSTATUS status;
|
|
GstBuffer *upload_buffer = nullptr;
|
|
GstNvEncResource *resource = nullptr;
|
|
const GstVideoInfo *info = &priv->input_state->info;
|
|
GstBufferPool *pool = priv->internal_pool;
|
|
|
|
if (gst_buffer_n_memory (buffer) > 1) {
|
|
GST_LOG_OBJECT (self, "Not a native DXGI format, system copy");
|
|
return gst_nv_encoder_copy_system (self, info, buffer, task);
|
|
}
|
|
|
|
mem = gst_buffer_peek_memory (buffer, 0);
|
|
if (!gst_is_d3d11_memory (mem)) {
|
|
GST_LOG_OBJECT (self, "Not a D3D11 buffer, system copy");
|
|
return gst_nv_encoder_copy_system (self, info, buffer, task);
|
|
}
|
|
|
|
dmem = GST_D3D11_MEMORY_CAST (mem);
|
|
if (dmem->device != priv->device) {
|
|
gint64 adapter_luid;
|
|
|
|
g_object_get (dmem->device, "adapter-luid", &adapter_luid, NULL);
|
|
if (adapter_luid == priv->dxgi_adapter_luid) {
|
|
GST_LOG_OBJECT (self, "Different device but same GPU, copy d3d11");
|
|
upload_buffer = gst_nv_encoder_copy_d3d11 (self, buffer, pool, TRUE);
|
|
} else {
|
|
GST_LOG_OBJECT (self, "Different device, system copy");
|
|
return gst_nv_encoder_copy_system (self, info, buffer, task);
|
|
}
|
|
}
|
|
|
|
if (!upload_buffer)
|
|
upload_buffer =
|
|
gst_nv_encoder_upload_d3d11_frame (self, info, buffer, pool);
|
|
|
|
if (!upload_buffer) {
|
|
GST_ERROR_OBJECT (self, "Failed to upload buffer");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
status = object->AcquireResource (mem, &resource);
|
|
|
|
if (status != NV_ENC_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "Failed to get resource, status %"
|
|
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
|
|
gst_buffer_unref (upload_buffer);
|
|
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
gst_nv_enc_task_set_resource (task, upload_buffer, resource);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
#endif
|
|
|
|
static GstFlowReturn
|
|
gst_nv_encoder_prepare_task_input (GstNvEncoder * self,
|
|
GstBuffer * buffer, GstNvEncTask * task)
|
|
{
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
GstFlowReturn ret = GST_FLOW_ERROR;
|
|
|
|
switch (priv->selected_device_mode) {
|
|
#ifdef G_OS_WIN32
|
|
case GST_NV_ENCODER_DEVICE_D3D11:
|
|
ret = gst_nv_encoder_prepare_task_input_d3d11 (self, buffer, task);
|
|
break;
|
|
#endif
|
|
case GST_NV_ENCODER_DEVICE_CUDA:
|
|
ret = gst_nv_encoder_prepare_task_input_cuda (self, buffer, task);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_foreach_caption_meta (GstBuffer * buffer, GstMeta ** meta,
|
|
GArray * payload)
|
|
{
|
|
GstVideoCaptionMeta *cc_meta;
|
|
GstByteWriter br;
|
|
guint payload_size;
|
|
NV_ENC_SEI_PAYLOAD sei_payload;
|
|
|
|
if ((*meta)->info->api != GST_VIDEO_CAPTION_META_API_TYPE)
|
|
return TRUE;
|
|
|
|
cc_meta = (GstVideoCaptionMeta *) (*meta);
|
|
|
|
if (cc_meta->caption_type != GST_VIDEO_CAPTION_TYPE_CEA708_RAW)
|
|
return TRUE;
|
|
|
|
/* 1 byte contry_code + 10 bytes CEA-708 specific data + caption data */
|
|
payload_size = 11 + cc_meta->size;
|
|
|
|
gst_byte_writer_init_with_size (&br, payload_size, FALSE);
|
|
|
|
/* 8-bits itu_t_t35_country_code */
|
|
gst_byte_writer_put_uint8 (&br, 181);
|
|
|
|
/* 16-bits itu_t_t35_provider_code */
|
|
gst_byte_writer_put_uint8 (&br, 0);
|
|
gst_byte_writer_put_uint8 (&br, 49);
|
|
|
|
/* 32-bits ATSC_user_identifier */
|
|
gst_byte_writer_put_uint8 (&br, 'G');
|
|
gst_byte_writer_put_uint8 (&br, 'A');
|
|
gst_byte_writer_put_uint8 (&br, '9');
|
|
gst_byte_writer_put_uint8 (&br, '4');
|
|
|
|
/* 8-bits ATSC1_data_user_data_type_code */
|
|
gst_byte_writer_put_uint8 (&br, 3);
|
|
|
|
/* 8-bits:
|
|
* 1 bit process_em_data_flag (0)
|
|
* 1 bit process_cc_data_flag (1)
|
|
* 1 bit additional_data_flag (0)
|
|
* 5-bits cc_count
|
|
*/
|
|
gst_byte_writer_put_uint8 (&br, ((cc_meta->size / 3) & 0x1f) | 0x40);
|
|
|
|
/* 8 bits em_data, unused */
|
|
gst_byte_writer_put_uint8 (&br, 255);
|
|
|
|
gst_byte_writer_put_data (&br, cc_meta->data, cc_meta->size);
|
|
|
|
/* 8 marker bits */
|
|
gst_byte_writer_put_uint8 (&br, 255);
|
|
|
|
sei_payload.payloadSize = gst_byte_writer_get_pos (&br);
|
|
sei_payload.payloadType = 4;
|
|
sei_payload.payload = gst_byte_writer_reset_and_get_data (&br);
|
|
|
|
g_array_append_val (payload, sei_payload);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_nv_encoder_handle_frame (GstVideoEncoder * encoder,
|
|
GstVideoCodecFrame * frame)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
GstNvEncoderClass *klass = GST_NV_ENCODER_GET_CLASS (self);
|
|
GstFlowReturn ret = GST_FLOW_ERROR;
|
|
GstNvEncTask *task = nullptr;
|
|
GstNvEncoderReconfigure reconfig;
|
|
GstBuffer *in_buf = frame->input_buffer;
|
|
NVENCSTATUS status;
|
|
|
|
if (priv->last_flow != GST_FLOW_OK) {
|
|
GST_INFO_OBJECT (self, "Last flow was %s",
|
|
gst_flow_get_name (priv->last_flow));
|
|
gst_video_encoder_finish_frame (encoder, frame);
|
|
|
|
return priv->last_flow;
|
|
}
|
|
|
|
if (!priv->object && !gst_nv_encoder_init_session (self, in_buf)) {
|
|
GST_ERROR_OBJECT (self, "Encoder object was not configured");
|
|
gst_video_encoder_finish_frame (encoder, frame);
|
|
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
|
|
reconfig = klass->check_reconfigure (self, &priv->config);
|
|
switch (reconfig) {
|
|
case GST_NV_ENCODER_RECONFIGURE_BITRATE:
|
|
if (!gst_nv_encoder_reconfigure_session (self)) {
|
|
gst_video_encoder_finish_frame (encoder, frame);
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_RECONFIGURE_FULL:
|
|
{
|
|
gst_nv_encoder_drain (self, TRUE);
|
|
if (!gst_nv_encoder_init_session (self, nullptr)) {
|
|
gst_video_encoder_finish_frame (encoder, frame);
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Release stream lock temporarily for encoding thread to be able to
|
|
* push encoded data */
|
|
GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
|
|
GST_TRACE_OBJECT (self, "Waiting for new task");
|
|
ret = priv->object->AcquireTask (&task, false);
|
|
GST_VIDEO_ENCODER_STREAM_LOCK (self);
|
|
|
|
if (priv->last_flow != GST_FLOW_OK) {
|
|
GST_INFO_OBJECT (self, "Last flow was %s",
|
|
gst_flow_get_name (priv->last_flow));
|
|
gst_video_encoder_finish_frame (encoder, frame);
|
|
|
|
return priv->last_flow;
|
|
}
|
|
|
|
if (ret != GST_FLOW_OK) {
|
|
GST_DEBUG_OBJECT (self, "AcquireTask returned %s", gst_flow_get_name (ret));
|
|
gst_video_encoder_finish_frame (encoder, frame);
|
|
return ret;
|
|
}
|
|
|
|
gst_nv_encoder_device_lock (self);
|
|
ret = gst_nv_encoder_prepare_task_input (self, in_buf, task);
|
|
gst_nv_encoder_device_unlock (self);
|
|
|
|
if (ret != GST_FLOW_OK) {
|
|
GST_ERROR_OBJECT (self, "Failed to upload frame");
|
|
gst_nv_enc_task_unref (task);
|
|
gst_video_encoder_finish_frame (encoder, frame);
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (priv->cc_insert != GST_NV_ENCODER_SEI_DISABLED) {
|
|
gst_buffer_foreach_meta (in_buf,
|
|
(GstBufferForeachMetaFunc) gst_nv_encoder_foreach_caption_meta,
|
|
gst_nv_enc_task_get_sei_payload (task));
|
|
}
|
|
|
|
status = priv->object->Encode (frame,
|
|
gst_nv_encoder_get_pic_struct (self, in_buf), task);
|
|
if (status != NV_ENC_SUCCESS) {
|
|
GST_ERROR_OBJECT (self, "Failed to encode frame");
|
|
gst_video_encoder_finish_frame (encoder, frame);
|
|
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
gst_video_codec_frame_unref (frame);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_nv_encoder_finish (GstVideoEncoder * encoder)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
|
|
GST_DEBUG_OBJECT (self, "Finish");
|
|
|
|
gst_nv_encoder_drain (self, TRUE);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_flush (GstVideoEncoder * encoder)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
|
|
GST_DEBUG_OBJECT (self, "Flush");
|
|
|
|
gst_nv_encoder_drain (self, TRUE);
|
|
|
|
priv->last_flow = GST_FLOW_OK;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_encoder_transform_meta (GstVideoEncoder * encoder,
|
|
GstVideoCodecFrame * frame, GstMeta * meta)
|
|
{
|
|
GstNvEncoder *self = GST_NV_ENCODER (encoder);
|
|
GstNvEncoderPrivate *priv = self->priv;
|
|
GstVideoCaptionMeta *cc_meta;
|
|
|
|
/* We need to handle only case CC meta should be dropped */
|
|
if (priv->cc_insert != GST_NV_ENCODER_SEI_INSERT_AND_DROP)
|
|
goto out;
|
|
|
|
if (meta->info->api != GST_VIDEO_CAPTION_META_API_TYPE)
|
|
goto out;
|
|
|
|
cc_meta = (GstVideoCaptionMeta *) meta;
|
|
if (cc_meta->caption_type != GST_VIDEO_CAPTION_TYPE_CEA708_RAW)
|
|
goto out;
|
|
|
|
/* Don't copy this meta into output buffer */
|
|
return FALSE;
|
|
|
|
out:
|
|
return GST_VIDEO_ENCODER_CLASS (parent_class)->transform_meta (encoder,
|
|
frame, meta);
|
|
}
|
|
|
|
void
|
|
gst_nv_encoder_set_device_mode (GstNvEncoder * encoder,
|
|
GstNvEncoderDeviceMode mode, guint cuda_device_id, gint64 adapter_luid)
|
|
{
|
|
GstNvEncoderPrivate *priv = encoder->priv;
|
|
|
|
priv->subclass_device_mode = mode;
|
|
priv->selected_device_mode = mode;
|
|
priv->cuda_device_id = cuda_device_id;
|
|
priv->dxgi_adapter_luid = adapter_luid;
|
|
}
|
|
|
|
/**
|
|
* GstNvEncoderPreset:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
GType
|
|
gst_nv_encoder_preset_get_type (void)
|
|
{
|
|
static GType preset_type = 0;
|
|
static const GEnumValue presets[] = {
|
|
/**
|
|
* GstNvEncoderPreset::default:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_DEFAULT, "Default (deprecated, use p1~7 with tune)",
|
|
"default"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::hp:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_HP,
|
|
"High Performance (deprecated, use p1~7 with tune)", "hp"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::hq:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_HQ, "High Quality (deprecated, use p1~7 with tune)",
|
|
"hq"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::low-latency:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_LOW_LATENCY_DEFAULT,
|
|
"Low Latency (deprecated, use p1~7 with tune)", "low-latency"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::low-latency-hq:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_LOW_LATENCY_HQ,
|
|
"Low Latency (deprecated, use p1~7 with tune), High Quality",
|
|
"low-latency-hq"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::low-latency-hq:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_LOW_LATENCY_HP,
|
|
"Low Latency (deprecated, use p1~7 with tune), High Performance",
|
|
"low-latency-hp"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::lossless:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_LOSSLESS_DEFAULT,
|
|
"Lossless (deprecated, use p1~7 with tune)", "lossless"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::lossless-hp:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_LOSSLESS_HP,
|
|
"Lossless (deprecated, use p1~7 with tune), High Performance",
|
|
"lossless-hp"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::p1:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_P1, "P1, fastest", "p1"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::p2:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_P2, "P2, faster", "p2"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::p3:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_P3, "P3, fast", "p3"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::p4:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_P4, "P4, medium", "p4"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::p5:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_P5, "P5, slow", "p5"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::p6:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_P6, "P6, slower", "p6"},
|
|
|
|
/**
|
|
* GstNvEncoderPreset::p7:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_PRESET_P7, "P7, slowest", "p7"},
|
|
{0, NULL, NULL},
|
|
};
|
|
|
|
if (g_once_init_enter (&preset_type)) {
|
|
GType type = g_enum_register_static ("GstNvEncoderPreset", presets);
|
|
|
|
g_once_init_leave (&preset_type, type);
|
|
}
|
|
|
|
return preset_type;
|
|
}
|
|
|
|
/**
|
|
* GstNvEncoderRCMode:
|
|
*
|
|
* Since: 1.22
|
|
*/
|
|
GType
|
|
gst_nv_encoder_rc_mode_get_type (void)
|
|
{
|
|
static GType rc_mode_type = 0;
|
|
static const GEnumValue rc_modes[] = {
|
|
{GST_NV_ENCODER_RC_MODE_DEFAULT, "Default", "default"},
|
|
{GST_NV_ENCODER_RC_MODE_CONSTQP, "Constant Quantization", "constqp"},
|
|
{GST_NV_ENCODER_RC_MODE_CBR, "Constant Bit Rate", "cbr"},
|
|
{GST_NV_ENCODER_RC_MODE_VBR, "Variable Bit Rate", "vbr"},
|
|
{GST_NV_ENCODER_RC_MODE_VBR_MINQP,
|
|
"Variable Bit Rate "
|
|
"(deprecated, use vbr and qp options)", "vbr-minqp"},
|
|
{GST_NV_ENCODER_RC_MODE_CBR_LOWDELAY_HQ,
|
|
"Low-Delay CBR, High Quality "
|
|
"(deprecated, use cbr with tune and multipass)", "cbr-ld-hq"},
|
|
{GST_NV_ENCODER_RC_MODE_CBR_HQ, "CBR, High Quality "
|
|
"(deprecated, use cbr with tune and multipass)", "cbr-hq"},
|
|
{GST_NV_ENCODER_RC_MODE_VBR_HQ, "VBR, High Quality "
|
|
"(deprecated, use vbr with tune and multipass)", "vbr-hq"},
|
|
{0, NULL, NULL},
|
|
};
|
|
|
|
if (g_once_init_enter (&rc_mode_type)) {
|
|
GType type = g_enum_register_static ("GstNvEncoderRCMode", rc_modes);
|
|
|
|
g_once_init_leave (&rc_mode_type, type);
|
|
}
|
|
|
|
return rc_mode_type;
|
|
}
|
|
|
|
static void
|
|
gst_nv_encoder_update_preset_to_native (const GstNvEncoderPresetOptions * input,
|
|
GstNvEncoderPresetOptionsNative * output)
|
|
{
|
|
GstNvEncoderPresetOptions updated = *input;
|
|
switch (updated.preset) {
|
|
case GST_NV_ENCODER_PRESET_P1:
|
|
output->preset = NV_ENC_PRESET_P1_GUID;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_P2:
|
|
output->preset = NV_ENC_PRESET_P2_GUID;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_P3:
|
|
output->preset = NV_ENC_PRESET_P3_GUID;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_P4:
|
|
output->preset = NV_ENC_PRESET_P4_GUID;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_P5:
|
|
output->preset = NV_ENC_PRESET_P5_GUID;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_P6:
|
|
output->preset = NV_ENC_PRESET_P6_GUID;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_P7:
|
|
output->preset = NV_ENC_PRESET_P7_GUID;
|
|
break;
|
|
default:
|
|
GST_WARNING ("Unexpected preset %d", input->preset);
|
|
output->preset = NV_ENC_PRESET_P4_GUID;
|
|
break;
|
|
}
|
|
|
|
switch (updated.rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_VBR:
|
|
case GST_NV_ENCODER_RC_MODE_VBR_MINQP:
|
|
case GST_NV_ENCODER_RC_MODE_VBR_HQ:
|
|
output->rc_mode = NV_ENC_PARAMS_RC_VBR;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR:
|
|
case GST_NV_ENCODER_RC_MODE_CBR_HQ:
|
|
output->rc_mode = NV_ENC_PARAMS_RC_CBR;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_LOWDELAY_HQ:
|
|
output->rc_mode = NV_ENC_PARAMS_RC_CBR;
|
|
if (updated.tune == GST_NV_ENCODER_TUNE_DEFAULT)
|
|
updated.tune = GST_NV_ENCODER_TUNE_LOW_LATENCY;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CONSTQP:
|
|
output->rc_mode = NV_ENC_PARAMS_RC_CONSTQP;
|
|
break;
|
|
default:
|
|
output->rc_mode = NV_ENC_PARAMS_RC_VBR;
|
|
break;
|
|
}
|
|
|
|
output->tune = NV_ENC_TUNING_INFO_UNDEFINED;
|
|
switch (updated.tune) {
|
|
case GST_NV_ENCODER_TUNE_DEFAULT:
|
|
case GST_NV_ENCODER_TUNE_HIGH_QUALITY:
|
|
output->tune = NV_ENC_TUNING_INFO_HIGH_QUALITY;
|
|
break;
|
|
case GST_NV_ENCODER_TUNE_LOW_LATENCY:
|
|
output->tune = NV_ENC_TUNING_INFO_LOW_LATENCY;
|
|
break;
|
|
case GST_NV_ENCODER_TUNE_ULTRA_LOW_LATENCY:
|
|
output->tune = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY;
|
|
break;
|
|
case GST_NV_ENCODER_TUNE_LOSSLESS:
|
|
output->tune = NV_ENC_TUNING_INFO_LOSSLESS;
|
|
break;
|
|
}
|
|
|
|
if (output->tune == NV_ENC_TUNING_INFO_UNDEFINED) {
|
|
GST_WARNING ("Unexpected input tune %d", updated.tune);
|
|
output->tune = NV_ENC_TUNING_INFO_HIGH_QUALITY;
|
|
}
|
|
|
|
switch (updated.multi_pass) {
|
|
case GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION:
|
|
output->multi_pass = NV_ENC_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION:
|
|
output->multi_pass = NV_ENC_TWO_PASS_FULL_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_MULTI_PASS_DEFAULT:
|
|
case GST_NV_ENCODER_MULTI_PASS_DISABLED:
|
|
default:
|
|
output->multi_pass = NV_ENC_MULTI_PASS_DISABLED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_nv_encoder_preset_to_native_h264 (GstNvEncoderPresetResolution resolution,
|
|
const GstNvEncoderPresetOptions * input,
|
|
GstNvEncoderPresetOptionsNative * output)
|
|
{
|
|
GstNvEncoderPresetOptions result = *input;
|
|
|
|
/* Converts legacy preset to new preset */
|
|
switch (input->preset) {
|
|
case GST_NV_ENCODER_PRESET_HP:
|
|
result.preset = GST_NV_ENCODER_PRESET_P2;
|
|
result.tune = GST_NV_ENCODER_TUNE_HIGH_QUALITY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_VBR_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_VBR:
|
|
case GST_NV_ENCODER_RC_MODE_VBR_MINQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_DEFAULT:
|
|
result.preset = GST_NV_ENCODER_PRESET_P3;
|
|
result.tune = GST_NV_ENCODER_TUNE_HIGH_QUALITY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_VBR_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_2160)
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
else
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_VBR:
|
|
case GST_NV_ENCODER_RC_MODE_VBR_MINQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_HQ:
|
|
result.preset = GST_NV_ENCODER_PRESET_P4;
|
|
result.tune = GST_NV_ENCODER_TUNE_HIGH_QUALITY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_VBR_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
if (resolution == GST_NV_ENCODER_PRESET_720) {
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
} else if (resolution == GST_NV_ENCODER_PRESET_1080) {
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
} else {
|
|
result.preset = GST_NV_ENCODER_PRESET_P5;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_VBR:
|
|
case GST_NV_ENCODER_RC_MODE_VBR_MINQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOW_LATENCY_HP:
|
|
result.preset = GST_NV_ENCODER_PRESET_P2;
|
|
result.tune = GST_NV_ENCODER_TUNE_LOW_LATENCY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_CBR:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_HQ:
|
|
result.tune = GST_NV_ENCODER_TUNE_ULTRA_LOW_LATENCY;
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_LOWDELAY_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOW_LATENCY_DEFAULT:
|
|
switch (resolution) {
|
|
case GST_NV_ENCODER_PRESET_720:
|
|
result.preset = GST_NV_ENCODER_PRESET_P4;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_1080:
|
|
result.preset = GST_NV_ENCODER_PRESET_P3;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_2160:
|
|
default:
|
|
result.preset = GST_NV_ENCODER_PRESET_P2;
|
|
break;
|
|
}
|
|
result.tune = GST_NV_ENCODER_TUNE_LOW_LATENCY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_CBR:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_HQ:
|
|
result.tune = GST_NV_ENCODER_TUNE_ULTRA_LOW_LATENCY;
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_2160)
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_LOWDELAY_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_2160)
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOW_LATENCY_HQ:
|
|
result.preset = GST_NV_ENCODER_PRESET_P4;
|
|
result.tune = GST_NV_ENCODER_TUNE_LOW_LATENCY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_CBR:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_HQ:
|
|
result.tune = GST_NV_ENCODER_TUNE_ULTRA_LOW_LATENCY;
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_2160)
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_LOWDELAY_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_2160)
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOSSLESS_HP:
|
|
result.preset = GST_NV_ENCODER_PRESET_P2;
|
|
result.tune = GST_NV_ENCODER_TUNE_LOSSLESS;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_CONSTQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CONSTQP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOSSLESS_DEFAULT:
|
|
result.preset = GST_NV_ENCODER_PRESET_P3;
|
|
result.tune = GST_NV_ENCODER_TUNE_LOSSLESS;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_CONSTQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CONSTQP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
gst_nv_encoder_update_preset_to_native (&result, output);
|
|
}
|
|
|
|
void
|
|
gst_nv_encoder_preset_to_native (GstNvEncoderPresetResolution resolution,
|
|
const GstNvEncoderPresetOptions * input,
|
|
GstNvEncoderPresetOptionsNative * output)
|
|
{
|
|
GstNvEncoderPresetOptions result = *input;
|
|
|
|
/* Converts legacy preset to new preset */
|
|
switch (input->preset) {
|
|
case GST_NV_ENCODER_PRESET_HP:
|
|
result.preset = GST_NV_ENCODER_PRESET_P1;
|
|
result.tune = GST_NV_ENCODER_TUNE_HIGH_QUALITY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_VBR_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_VBR:
|
|
case GST_NV_ENCODER_RC_MODE_VBR_MINQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_DEFAULT:
|
|
result.preset = GST_NV_ENCODER_PRESET_P5;
|
|
result.tune = GST_NV_ENCODER_TUNE_HIGH_QUALITY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_VBR_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_VBR:
|
|
case GST_NV_ENCODER_RC_MODE_VBR_MINQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_HQ:
|
|
result.preset = GST_NV_ENCODER_PRESET_P6;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_2160)
|
|
result.preset = GST_NV_ENCODER_PRESET_P5;
|
|
result.tune = GST_NV_ENCODER_TUNE_HIGH_QUALITY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_VBR_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_VBR:
|
|
case GST_NV_ENCODER_RC_MODE_VBR_MINQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_VBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOW_LATENCY_HP:
|
|
result.preset = GST_NV_ENCODER_PRESET_P2;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_2160)
|
|
result.preset = GST_NV_ENCODER_PRESET_P1;
|
|
result.tune = GST_NV_ENCODER_TUNE_LOW_LATENCY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_CBR:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_HQ:
|
|
result.tune = GST_NV_ENCODER_TUNE_ULTRA_LOW_LATENCY;
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_LOWDELAY_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOW_LATENCY_DEFAULT:
|
|
switch (resolution) {
|
|
case GST_NV_ENCODER_PRESET_720:
|
|
result.preset = GST_NV_ENCODER_PRESET_P4;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_1080:
|
|
result.preset = GST_NV_ENCODER_PRESET_P3;
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_2160:
|
|
default:
|
|
result.preset = GST_NV_ENCODER_PRESET_P2;
|
|
break;
|
|
}
|
|
result.tune = GST_NV_ENCODER_TUNE_LOW_LATENCY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_CBR:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_HQ:
|
|
result.tune = GST_NV_ENCODER_TUNE_ULTRA_LOW_LATENCY;
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_1080)
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_LOWDELAY_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_1080)
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOW_LATENCY_HQ:
|
|
result.preset = GST_NV_ENCODER_PRESET_P5;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_1080)
|
|
result.preset = GST_NV_ENCODER_PRESET_P4;
|
|
result.tune = GST_NV_ENCODER_TUNE_LOW_LATENCY;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_CBR:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_HQ:
|
|
result.tune = GST_NV_ENCODER_TUNE_ULTRA_LOW_LATENCY;
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_2160)
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
case GST_NV_ENCODER_RC_MODE_CBR_LOWDELAY_HQ:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CBR;
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION;
|
|
if (resolution >= GST_NV_ENCODER_PRESET_2160)
|
|
result.multi_pass = GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOSSLESS_HP:
|
|
result.preset = GST_NV_ENCODER_PRESET_P3;
|
|
result.tune = GST_NV_ENCODER_TUNE_LOSSLESS;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_CONSTQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CONSTQP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_NV_ENCODER_PRESET_LOSSLESS_DEFAULT:
|
|
result.preset = GST_NV_ENCODER_PRESET_P5;
|
|
result.tune = GST_NV_ENCODER_TUNE_LOSSLESS;
|
|
result.multi_pass = GST_NV_ENCODER_MULTI_PASS_DISABLED;
|
|
switch (input->rc_mode) {
|
|
case GST_NV_ENCODER_RC_MODE_DEFAULT:
|
|
case GST_NV_ENCODER_RC_MODE_CONSTQP:
|
|
result.rc_mode = GST_NV_ENCODER_RC_MODE_CONSTQP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
gst_nv_encoder_update_preset_to_native (&result, output);
|
|
}
|
|
|
|
/**
|
|
* GstNvEncoderSeiInsertMode:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
GType
|
|
gst_nv_encoder_sei_insert_mode_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
static const GEnumValue insert_modes[] = {
|
|
/**
|
|
* GstNvEncoderSeiInsertMode::insert:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_SEI_INSERT, "Insert SEI", "insert"},
|
|
|
|
/**
|
|
* GstNvEncoderSeiInsertMode::insert-and-drop:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_SEI_INSERT_AND_DROP,
|
|
"Insert SEI and remove corresponding meta from output buffer",
|
|
"insert-and-drop"},
|
|
|
|
/**
|
|
* GstNvEncoderSeiInsertMode::disabled:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_SEI_DISABLED, "Disable SEI insertion", "disabled"},
|
|
{0, nullptr, nullptr}
|
|
};
|
|
|
|
GST_CUDA_CALL_ONCE_BEGIN {
|
|
type = g_enum_register_static ("GstNvEncoderSeiInsertMode", insert_modes);
|
|
} GST_CUDA_CALL_ONCE_END;
|
|
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* GstNvEncoderMultiPass:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
GType
|
|
gst_nv_encoder_multi_pass_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
static const GEnumValue modes[] = {
|
|
/**
|
|
* GstNvEncoderMultiPass::disabled:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_MULTI_PASS_DEFAULT,
|
|
"Disable multi-pass when cqp, vbr or cbr is used. "
|
|
"Otherwise encoder will select it based on rc-mode", "default"},
|
|
|
|
/**
|
|
* GstNvEncoderMultiPass::disabled:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_MULTI_PASS_DISABLED, "Disabled", "disabled"},
|
|
|
|
/**
|
|
* GstNvEncoderMultiPass::two-pass-quarter:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_TWO_PASS_QUARTER_RESOLUTION,
|
|
"Two pass with quarter resolution encoding in first pass",
|
|
"two-pass-quarter"},
|
|
|
|
/**
|
|
* GstNvEncoderMultiPass::two-pass:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_TWO_PASS_FULL_RESOLUTION, "Two pass", "two-pass"},
|
|
{0, nullptr, nullptr}
|
|
};
|
|
|
|
GST_CUDA_CALL_ONCE_BEGIN {
|
|
type = g_enum_register_static ("GstNvEncoderMultiPass", modes);
|
|
} GST_CUDA_CALL_ONCE_END;
|
|
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* GstNvEncoderTune:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
GType
|
|
gst_nv_encoder_tune_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
static const GEnumValue modes[] = {
|
|
/**
|
|
* GstNvEncoderTune::default:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_TUNE_DEFAULT, "High quality when p1~7 preset is used. "
|
|
"Otherwise encoder will select it based on preset", "default"},
|
|
|
|
/**
|
|
* GstNvEncoderTune::high-quality:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_TUNE_HIGH_QUALITY, "High quality", "high-quality"},
|
|
|
|
/**
|
|
* GstNvEncoderMultiPass::low-latency:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_TUNE_LOW_LATENCY, "Low latency", "low-latency"},
|
|
|
|
/**
|
|
* GstNvEncoderMultiPass::ultra-low-latency:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_TUNE_ULTRA_LOW_LATENCY, "Ultra low latency",
|
|
"ultra-low-latency"},
|
|
|
|
/**
|
|
* GstNvEncoderMultiPass::lossless:
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
{GST_NV_ENCODER_TUNE_LOSSLESS, "Lossless", "lossless"},
|
|
{0, nullptr, nullptr}
|
|
};
|
|
|
|
GST_CUDA_CALL_ONCE_BEGIN {
|
|
type = g_enum_register_static ("GstNvEncoderTune", modes);
|
|
} GST_CUDA_CALL_ONCE_END;
|
|
|
|
return type;
|
|
}
|
|
|
|
GstNvEncoderClassData *
|
|
gst_nv_encoder_class_data_new (void)
|
|
{
|
|
GstNvEncoderClassData *data = g_new0 (GstNvEncoderClassData, 1);
|
|
data->ref_count = 1;
|
|
|
|
return data;
|
|
}
|
|
|
|
GstNvEncoderClassData *
|
|
gst_nv_encoder_class_data_ref (GstNvEncoderClassData * cdata)
|
|
{
|
|
g_atomic_int_add (&cdata->ref_count, 1);
|
|
|
|
return cdata;
|
|
}
|
|
|
|
void
|
|
gst_nv_encoder_class_data_unref (GstNvEncoderClassData * cdata)
|
|
{
|
|
if (g_atomic_int_dec_and_test (&cdata->ref_count)) {
|
|
gst_clear_caps (&cdata->sink_caps);
|
|
gst_clear_caps (&cdata->src_caps);
|
|
if (cdata->formats)
|
|
g_list_free_full (cdata->formats, (GDestroyNotify) g_free);
|
|
if (cdata->profiles)
|
|
g_list_free_full (cdata->profiles, (GDestroyNotify) g_free);
|
|
g_free (cdata);
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_nv_encoder_get_encoder_caps (gpointer session, const GUID * encode_guid,
|
|
GstNvEncoderDeviceCaps * device_caps)
|
|
{
|
|
GstNvEncoderDeviceCaps dev_caps = { 0, };
|
|
NV_ENC_CAPS_PARAM caps_param = { 0, };
|
|
NVENCSTATUS status;
|
|
GUID guid = *encode_guid;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_nv_encoder_debug, "nvencoder", 0, "nvencoder");
|
|
|
|
caps_param.version = gst_nvenc_get_caps_param_version ();
|
|
|
|
#define CHECK_CAPS(to_query,val,default_val) G_STMT_START { \
|
|
gint _val; \
|
|
caps_param.capsToQuery = to_query; \
|
|
status = NvEncGetEncodeCaps (session, guid, &caps_param, \
|
|
&_val); \
|
|
if (status != NV_ENC_SUCCESS) { \
|
|
GST_WARNING ("Unable to query %s, status: %" \
|
|
GST_NVENC_STATUS_FORMAT, G_STRINGIFY (to_query), \
|
|
GST_NVENC_STATUS_ARGS (status)); \
|
|
val = default_val; \
|
|
} else { \
|
|
GST_DEBUG ("%s: %d", G_STRINGIFY (to_query), _val); \
|
|
val = _val; \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_BFRAMES, dev_caps.max_bframes, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES,
|
|
dev_caps.ratecontrol_modes, NV_ENC_PARAMS_RC_VBR);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FIELD_ENCODING, dev_caps.field_encoding, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MONOCHROME, dev_caps.monochrome, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FMO, dev_caps.fmo, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_QPELMV, dev_caps.qpelmv, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BDIRECT_MODE, dev_caps.bdirect_mode, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CABAC, dev_caps.cabac, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM,
|
|
dev_caps.adaptive_transform, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_STEREO_MVC, dev_caps.stereo_mvc, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS, dev_caps.temoral_layers, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_PFRAMES,
|
|
dev_caps.hierarchical_pframes, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_BFRAMES,
|
|
dev_caps.hierarchical_bframes, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_LEVEL_MAX, dev_caps.level_max, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_LEVEL_MIN, dev_caps.level_min, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SEPARATE_COLOUR_PLANE,
|
|
dev_caps.separate_colour_plane, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_WIDTH_MAX, dev_caps.width_max, 4096);
|
|
CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MAX, dev_caps.height_max, 4096);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC, dev_caps.temporal_svc, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE, dev_caps.dyn_res_change, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE,
|
|
dev_caps.dyn_bitrate_change, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_FORCE_CONSTQP,
|
|
dev_caps.dyn_force_constqp, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RCMODE_CHANGE,
|
|
dev_caps.dyn_rcmode_change, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SUBFRAME_READBACK,
|
|
dev_caps.subframe_readback, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING,
|
|
dev_caps.constrained_encoding, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_INTRA_REFRESH, dev_caps.intra_refresh, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE,
|
|
dev_caps.custom_vbv_buf_size, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYNAMIC_SLICE_MODE,
|
|
dev_caps.dynamic_slice_mode, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION,
|
|
dev_caps.ref_pic_invalidation, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_PREPROC_SUPPORT, dev_caps.preproc_support, 0);
|
|
/* NOTE: Async is Windows only */
|
|
#ifdef G_OS_WIN32
|
|
CHECK_CAPS (NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT,
|
|
dev_caps.async_encoding_support, 0);
|
|
#endif
|
|
CHECK_CAPS (NV_ENC_CAPS_MB_NUM_MAX, dev_caps.mb_num_max, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_MB_PER_SEC_MAX, dev_caps.mb_per_sec_max, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, dev_caps.yuv444_encode, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, dev_caps.lossless_encode, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SAO, dev_caps.sao, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MEONLY_MODE, dev_caps.meonly_mode, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOOKAHEAD, dev_caps.lookahead, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, dev_caps.temporal_aq, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_10BIT_ENCODE,
|
|
dev_caps.supports_10bit_encode, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_LTR_FRAMES, dev_caps.num_max_ltr_frames, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION,
|
|
dev_caps.weighted_prediction, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, dev_caps.bframe_ref_mode, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP,
|
|
dev_caps.emphasis_level_map, 0);
|
|
CHECK_CAPS (NV_ENC_CAPS_WIDTH_MIN, dev_caps.width_min, 16);
|
|
CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MIN, dev_caps.height_min, 16);
|
|
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES,
|
|
dev_caps.multiple_ref_frames, 0);
|
|
#undef CHECK_CAPS
|
|
|
|
*device_caps = dev_caps;
|
|
}
|
|
|
|
void
|
|
gst_nv_encoder_merge_device_caps (const GstNvEncoderDeviceCaps * a,
|
|
const GstNvEncoderDeviceCaps * b, GstNvEncoderDeviceCaps * merged)
|
|
{
|
|
GstNvEncoderDeviceCaps caps;
|
|
|
|
#define SELECT_MAX(value) G_STMT_START { \
|
|
caps.value = MAX (a->value, b->value); \
|
|
} G_STMT_END
|
|
|
|
#define SELECT_MIN(value) G_STMT_START { \
|
|
caps.value = MAX (MIN (a->value, b->value), 1); \
|
|
} G_STMT_END
|
|
|
|
SELECT_MAX (max_bframes);
|
|
SELECT_MAX (ratecontrol_modes);
|
|
SELECT_MAX (field_encoding);
|
|
SELECT_MAX (monochrome);
|
|
SELECT_MAX (fmo);
|
|
SELECT_MAX (qpelmv);
|
|
SELECT_MAX (bdirect_mode);
|
|
SELECT_MAX (cabac);
|
|
SELECT_MAX (adaptive_transform);
|
|
SELECT_MAX (stereo_mvc);
|
|
SELECT_MAX (temoral_layers);
|
|
SELECT_MAX (hierarchical_pframes);
|
|
SELECT_MAX (hierarchical_bframes);
|
|
SELECT_MAX (level_max);
|
|
SELECT_MAX (level_min);
|
|
SELECT_MAX (separate_colour_plane);
|
|
SELECT_MAX (width_max);
|
|
SELECT_MAX (height_max);
|
|
SELECT_MAX (temporal_svc);
|
|
SELECT_MAX (dyn_res_change);
|
|
SELECT_MAX (dyn_bitrate_change);
|
|
SELECT_MAX (dyn_force_constqp);
|
|
SELECT_MAX (dyn_rcmode_change);
|
|
SELECT_MAX (subframe_readback);
|
|
SELECT_MAX (constrained_encoding);
|
|
SELECT_MAX (intra_refresh);
|
|
SELECT_MAX (custom_vbv_buf_size);
|
|
SELECT_MAX (dynamic_slice_mode);
|
|
SELECT_MAX (ref_pic_invalidation);
|
|
SELECT_MAX (preproc_support);
|
|
SELECT_MAX (async_encoding_support);
|
|
SELECT_MAX (mb_num_max);
|
|
SELECT_MAX (mb_per_sec_max);
|
|
SELECT_MAX (yuv444_encode);
|
|
SELECT_MAX (lossless_encode);
|
|
SELECT_MAX (sao);
|
|
SELECT_MAX (meonly_mode);
|
|
SELECT_MAX (lookahead);
|
|
SELECT_MAX (temporal_aq);
|
|
SELECT_MAX (supports_10bit_encode);
|
|
SELECT_MAX (num_max_ltr_frames);
|
|
SELECT_MAX (weighted_prediction);
|
|
SELECT_MAX (bframe_ref_mode);
|
|
SELECT_MAX (emphasis_level_map);
|
|
SELECT_MIN (width_min);
|
|
SELECT_MIN (height_min);
|
|
SELECT_MAX (multiple_ref_frames);
|
|
|
|
#undef SELECT_MAX
|
|
#undef SELECT_MIN
|
|
|
|
*merged = caps;
|
|
}
|
|
|
|
gboolean
|
|
_gst_nv_enc_result (NVENCSTATUS status, GObject * self, const gchar * file,
|
|
const gchar * function, gint line)
|
|
{
|
|
if (status == NV_ENC_SUCCESS)
|
|
return TRUE;
|
|
|
|
#ifndef GST_DISABLE_GST_DEBUG
|
|
const gchar *status_str = nvenc_status_to_string (status);
|
|
|
|
gst_debug_log (GST_CAT_DEFAULT, GST_LEVEL_ERROR, file, function,
|
|
line, self, "NvEnc API call failed: 0x%x, %s",
|
|
(guint) status, status_str);
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|