nvencoder: Reuse input resource

Call input resource map functions (i.e., nvEncRegisterResource,
nvEncUnregisterResource, nvEncMapInputResource, and
nvEncUnmapInputResource) only once and reuse the mapped resources,
instead of per input frame map/unmap

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3884>
This commit is contained in:
Seungha Yang 2023-02-05 23:12:47 +09:00 committed by GStreamer Marge Bot
parent ff3120a38c
commit eb0fca4180
7 changed files with 1700 additions and 791 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,247 @@
/* GStreamer
* Copyright (C) 2023 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/cuda/gstcuda.h>
#ifdef G_OS_WIN32
#include <gst/d3d11/gstd3d11.h>
#endif
#include "nvEncodeAPI.h"
#include "gstnvenc.h"
#include <memory>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <set>
#include <string>
#include <atomic>
G_BEGIN_DECLS
#define GST_TYPE_NV_ENC_BUFFER (gst_nv_enc_buffer_get_type ())
struct GstNvEncBuffer;
GType gst_nv_enc_buffer_get_type (void);
NVENCSTATUS gst_nv_enc_buffer_lock (GstNvEncBuffer * buffer,
gpointer * data,
guint32 * pitch);
void gst_nv_enc_buffer_unlock (GstNvEncBuffer * buffer);
static inline GstNvEncBuffer *
gst_nv_enc_buffer_ref (GstNvEncBuffer * buffer)
{
return (GstNvEncBuffer *)
gst_mini_object_ref (GST_MINI_OBJECT_CAST (buffer));
}
static inline void
gst_nv_enc_buffer_unref (GstNvEncBuffer * buffer)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (buffer));
}
static inline void
gst_clear_nv_encoder_buffer (GstNvEncBuffer ** buffer)
{
if (buffer && *buffer) {
gst_nv_enc_buffer_unref (*buffer);
*buffer = NULL;
}
}
#define GST_TYPE_NV_ENC_RESOURCE (gst_nv_enc_resource_get_type ())
struct GstNvEncResource;
GType gst_nv_enc_resource_get_type (void);
static inline GstNvEncResource *
gst_nv_enc_resource_ref (GstNvEncResource * resource)
{
return (GstNvEncResource *)
gst_mini_object_ref (GST_MINI_OBJECT_CAST (resource));
}
static inline void
gst_nv_enc_resource_unref (GstNvEncResource * resource)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (resource));
}
static inline void
gst_clear_nv_encoder_resource (GstNvEncResource ** resource)
{
if (resource && *resource) {
gst_nv_enc_resource_unref (*resource);
*resource = NULL;
}
}
#define GST_TYPE_NV_ENC_TASK (gst_nv_enc_task_get_type ())
struct GstNvEncTask;
GType gst_nv_enc_task_get_type (void);
gboolean gst_nv_enc_task_set_buffer (GstNvEncTask * task,
GstNvEncBuffer * buffer);
gboolean gst_nv_enc_task_set_resource (GstNvEncTask * task,
GstBuffer * buffer,
GstNvEncResource * resource);
NVENCSTATUS gst_nv_enc_task_lock_bitstream (GstNvEncTask * task,
NV_ENC_LOCK_BITSTREAM * bitstream);
void gst_nv_enc_task_unlock_bitstream (GstNvEncTask * task);
static inline GstNvEncTask *
gst_nv_enc_task_ref (GstNvEncTask * task)
{
return (GstNvEncTask *)
gst_mini_object_ref (GST_MINI_OBJECT_CAST (task));
}
static inline void
gst_nv_enc_task_unref (GstNvEncTask * task)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (task));
}
const gchar * nvenc_status_to_string (NVENCSTATUS status);
G_END_DECLS
class GstNvEncObject : public std::enable_shared_from_this <GstNvEncObject>
{
public:
static bool IsSuccess (NVENCSTATUS status,
GstNvEncObject * self,
const gchar * file,
const gchar * function,
gint line);
static std::shared_ptr<GstNvEncObject>
CreateInstance (GstElement * client,
GstObject * device,
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS * params);
~GstNvEncObject ();
gpointer GetHandle ();
guint GetTaskSize ();
NVENCSTATUS InitSession (NV_ENC_INITIALIZE_PARAMS * params,
GstCudaStream * stream,
const GstVideoInfo * info,
guint pool_size);
NVENCSTATUS Reconfigure (NV_ENC_RECONFIGURE_PARAMS * params);
void SetFlushing (bool flushing);
NVENCSTATUS Encode (GstVideoCodecFrame * codec_frame,
NV_ENC_PIC_STRUCT pic_struct,
GstNvEncTask * task);
NVENCSTATUS Drain (GstNvEncTask * task);
GstFlowReturn GetOutput (GstNvEncTask ** task);
NVENCSTATUS LockBitstream (NV_ENC_LOCK_BITSTREAM * bitstream);
NVENCSTATUS UnlockBitstream (NV_ENC_OUTPUT_PTR output_ptr);
NVENCSTATUS AcquireBuffer (GstNvEncBuffer ** buffer);
NVENCSTATUS AcquireResource (GstMemory * mem,
GstNvEncResource ** resource);
GstFlowReturn AcquireTask (GstNvEncTask ** task,
bool force);
void PushEmptyTask (GstNvEncTask * task);
void PushEmptyBuffer (GstNvEncBuffer * buffer);
void ReleaseResource (GstNvEncResource * resource);
void DeactivateResource (GstNvEncResource * resource);
bool DeviceLock ();
bool DeviceUnlock ();
private:
void releaseResourceUnlocked (GstNvEncResource * resource);
void releaseTaskUnlocked (GstNvEncTask * task);
NVENCSTATUS acquireResourceCuda (GstMemory * mem,
GstNvEncResource ** resource);
#ifdef G_OS_WIN32
NVENCSTATUS acquireResourceD3D11 (GstMemory * mem,
GstNvEncResource ** resource);
#endif
void runResourceGC ();
private:
std::string id_;
std::mutex lock_;
std::recursive_mutex resource_lock_;
std::condition_variable cond_;
/* holding unused GstNvEncBuffer object, holding ownership */
std::queue <GstNvEncBuffer *> buffer_queue_;
/* GstNvEncResource resource is always owned by GstMemory.
* below two data struct will track the resource's life cycle */
/* list of all registered GstNvEncResource, without ownership */
std::set <GstNvEncResource *> resource_queue_;
/* list of GstNvEncResource in task_queue */
std::set <GstNvEncResource *> active_resource_queue_;
std::queue <GstNvEncTask *> task_queue_;
std::queue <GstNvEncTask *> empty_task_queue_;
gint64 user_token_;
GstCudaContext *context_ = nullptr;
GstCudaStream *stream_ = nullptr;
#ifdef G_OS_WIN32
GstD3D11Device *device_ = nullptr;
#endif
GstVideoInfo info_;
gpointer session_ = nullptr;
bool initialized_ = false;
bool flushing_ = false;
guint task_size_ = 0;
NV_ENC_DEVICE_TYPE device_type_ = NV_ENC_DEVICE_TYPE_CUDA;
NV_ENC_BUFFER_FORMAT buffer_format_ = NV_ENC_BUFFER_FORMAT_UNDEFINED;
std::atomic<guint> buffer_seq_;
std::atomic<guint> resource_seq_;
std::atomic<guint> task_seq_;
};

File diff suppressed because it is too large Load diff

View file

@ -162,27 +162,6 @@ typedef struct
gint ref_count; gint ref_count;
} GstNvEncoderClassData; } GstNvEncoderClassData;
typedef struct
{
/* without ref */
GstNvEncoder *encoder;
/* Holds ownership */
GstBuffer *buffer;
GstMapInfo map_info;
NV_ENC_REGISTER_RESOURCE register_resource;
NV_ENC_MAP_INPUT_RESOURCE mapped_resource;
/* Used when input resource cannot be registered */
NV_ENC_CREATE_INPUT_BUFFER input_buffer;
NV_ENC_LOCK_INPUT_BUFFER lk_input_buffer;
NV_ENC_OUTPUT_PTR output_ptr;
gpointer event_handle;
gboolean is_eos;
} GstNvEncoderTask;
typedef struct typedef struct
{ {
GstNvEncoderDeviceMode device_mode; GstNvEncoderDeviceMode device_mode;
@ -228,12 +207,6 @@ struct _GstNvEncoderClass
GType gst_nv_encoder_get_type (void); GType gst_nv_encoder_get_type (void);
guint gst_nv_encoder_get_task_size (GstNvEncoder * encoder);
const gchar * gst_nv_encoder_status_to_string (NVENCSTATUS status);
#define GST_NVENC_STATUS_FORMAT "s (%d)"
#define GST_NVENC_STATUS_ARGS(s) gst_nv_encoder_status_to_string (s), s
void gst_nv_encoder_preset_to_guid (GstNvEncoderPreset preset, void gst_nv_encoder_preset_to_guid (GstNvEncoderPreset preset,
GUID * guid); GUID * guid);
@ -258,6 +231,14 @@ void gst_nv_encoder_merge_device_caps (const GstNvEncoderDeviceCaps * a,
const GstNvEncoderDeviceCaps * b, const GstNvEncoderDeviceCaps * b,
GstNvEncoderDeviceCaps * merged); GstNvEncoderDeviceCaps * merged);
gboolean _gst_nv_enc_result (NVENCSTATUS status,
GObject * self,
const gchar * file,
const gchar * function,
gint line);
#define gst_nv_enc_result(status,self) \
_gst_nv_enc_result (status, (GObject *) self, __FILE__, GST_FUNCTION, __LINE__)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstNvEncoder, gst_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstNvEncoder, gst_object_unref)

View file

@ -1201,9 +1201,8 @@ gst_nv_h264_encoder_set_format (GstNvEncoder * encoder,
status = NvEncGetEncodePresetConfig (session, NV_ENC_CODEC_H264_GUID, status = NvEncGetEncodePresetConfig (session, NV_ENC_CODEC_H264_GUID,
init_params->presetGUID, &preset_config); init_params->presetGUID, &preset_config);
if (status != NV_ENC_SUCCESS) { if (!gst_nv_enc_result (status, self)) {
GST_ERROR_OBJECT (self, "Failed to get preset config %" GST_ERROR_OBJECT (self, "Failed to get preset config");
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
g_mutex_unlock (&self->prop_lock); g_mutex_unlock (&self->prop_lock);
return FALSE; return FALSE;
} }
@ -1431,9 +1430,8 @@ gst_nv_h264_encoder_set_output_state (GstNvEncoder * encoder,
seq_params.spsppsBuffer = &spspps; seq_params.spsppsBuffer = &spspps;
seq_params.outSPSPPSPayloadSize = &seq_size; seq_params.outSPSPPSPayloadSize = &seq_size;
status = NvEncGetSequenceParams (session, &seq_params); status = NvEncGetSequenceParams (session, &seq_params);
if (status != NV_ENC_SUCCESS) { if (!gst_nv_enc_result (status, self)) {
GST_ERROR_OBJECT (self, "Failed to get sequence header, status %" GST_ERROR_OBJECT (self, "Failed to get sequence header");
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
return FALSE; return FALSE;
} }

View file

@ -1184,9 +1184,8 @@ gst_nv_h265_encoder_set_format (GstNvEncoder * encoder,
status = NvEncGetEncodePresetConfig (session, NV_ENC_CODEC_HEVC_GUID, status = NvEncGetEncodePresetConfig (session, NV_ENC_CODEC_HEVC_GUID,
init_params->presetGUID, &preset_config); init_params->presetGUID, &preset_config);
if (status != NV_ENC_SUCCESS) { if (!gst_nv_enc_result (status, self)) {
GST_ERROR_OBJECT (self, "Failed to get preset config %" GST_ERROR_OBJECT (self, "Failed to get preset config");
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
g_mutex_unlock (&self->prop_lock); g_mutex_unlock (&self->prop_lock);
return FALSE; return FALSE;
} }
@ -1378,9 +1377,8 @@ gst_nv_h265_encoder_set_output_state (GstNvEncoder * encoder,
seq_params.spsppsBuffer = &vpsspspps; seq_params.spsppsBuffer = &vpsspspps;
seq_params.outSPSPPSPayloadSize = &seq_size; seq_params.outSPSPPSPayloadSize = &seq_size;
status = NvEncGetSequenceParams (session, &seq_params); status = NvEncGetSequenceParams (session, &seq_params);
if (status != NV_ENC_SUCCESS) { if (!gst_nv_enc_result (status, self)) {
GST_ERROR_OBJECT (self, "Failed to get sequence header, status %" GST_ERROR_OBJECT (self, "Failed to get sequence header");
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
return FALSE; return FALSE;
} }

View file

@ -11,6 +11,7 @@ nvcodec_sources = [
'gstnvdecobject.cpp', 'gstnvdecobject.cpp',
'gstnvdecoder.cpp', 'gstnvdecoder.cpp',
'gstnvenc.c', 'gstnvenc.c',
'gstnvencobject.cpp',
'gstnvencoder.cpp', 'gstnvencoder.cpp',
'gstnvh264dec.cpp', 'gstnvh264dec.cpp',
'gstnvh264enc.c', 'gstnvh264enc.c',
@ -82,6 +83,7 @@ gstnvcodec = library('gstnvcodec',
cpp_args : gst_plugins_bad_args + extra_args, cpp_args : gst_plugins_bad_args + extra_args,
include_directories : plugin_incdirs, include_directories : plugin_incdirs,
dependencies : [gstbase_dep, gstvideo_dep, gstpbutils_dep, gstgl_dep, gstglproto_dep, gmodule_dep, gstcodecs_dep, gstd3d11_dep, gstcuda_dep], dependencies : [gstbase_dep, gstvideo_dep, gstpbutils_dep, gstgl_dep, gstglproto_dep, gmodule_dep, gstcodecs_dep, gstd3d11_dep, gstcuda_dep],
override_options : ['cpp_std=c++14'],
install : true, install : true,
install_dir : plugins_install_dir, install_dir : plugins_install_dir,
) )