mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
106187cc59
Adding RGBA, RGBx, BGRA, BGRx, VUYA and RGB10A2_LE format support for performance. However, these formats are not still recommended if upstream can support native YUV formats (e.g., NV12, P010) since NVENC does not expose conversion related optiones. Note that VUYA format is 4:4:4 YUV format already but NVENC runtime will convert it to 4:2:0 format internally Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6417>
1296 lines
34 KiB
C++
1296 lines
34 KiB
C++
/* 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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstnvencobject.h"
|
|
#include <algorithm>
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_nv_encoder_debug);
|
|
#define GST_CAT_DEFAULT gst_nv_encoder_debug
|
|
|
|
/* Both CUDA and D3D11 use the same value */
|
|
#define GST_MAP_NVENC (GST_MAP_FLAG_LAST << 1)
|
|
#define GST_MAP_READ_NVENC (GstMapFlags)(GST_MAP_READ | GST_MAP_NVENC)
|
|
|
|
/* *INDENT-OFF* */
|
|
static GstNvEncBuffer * gst_nv_enc_buffer_new (const std::string & id,
|
|
guint seq_num);
|
|
static GstNvEncResource * gst_nv_enc_resource_new (const std::string & id,
|
|
guint seq_num);
|
|
static GstNvEncTask * gst_nv_enc_task_new (const std::string & id,
|
|
guint seq_num);
|
|
|
|
struct GstNvEncBuffer : public GstMiniObject
|
|
{
|
|
GstNvEncBuffer (const std::string parent_id, guint seq)
|
|
: id (parent_id), seq_num (seq)
|
|
{
|
|
memset (&buffer, 0, sizeof (NV_ENC_CREATE_INPUT_BUFFER));
|
|
memset (&buffer_lock, 0, sizeof (NV_ENC_LOCK_INPUT_BUFFER));
|
|
|
|
buffer.version = gst_nvenc_get_create_input_buffer_version ();
|
|
buffer_lock.version = gst_nvenc_get_lock_input_buffer_version ();
|
|
}
|
|
|
|
std::shared_ptr<GstNvEncObject> object;
|
|
|
|
NV_ENC_CREATE_INPUT_BUFFER buffer;
|
|
NV_ENC_LOCK_INPUT_BUFFER buffer_lock;
|
|
|
|
bool locked = false;
|
|
std::string id;
|
|
guint seq_num;
|
|
};
|
|
|
|
GST_DEFINE_MINI_OBJECT_TYPE (GstNvEncBuffer, gst_nv_enc_buffer);
|
|
|
|
struct GstNvEncResource : public GstMiniObject
|
|
{
|
|
GstNvEncResource (const std::string & parent_id, guint seq)
|
|
: id (parent_id), seq_num (seq)
|
|
{
|
|
memset (&resource, 0, sizeof (NV_ENC_REGISTER_RESOURCE));
|
|
memset (&mapped_resource, 0, sizeof (NV_ENC_MAP_INPUT_RESOURCE));
|
|
|
|
resource.version = gst_nvenc_get_register_resource_version ();
|
|
mapped_resource.version = gst_nvenc_get_map_input_resource_version ();
|
|
}
|
|
|
|
std::weak_ptr<GstNvEncObject> object;
|
|
|
|
NV_ENC_REGISTER_RESOURCE resource;
|
|
NV_ENC_MAP_INPUT_RESOURCE mapped_resource;
|
|
|
|
std::string id;
|
|
guint seq_num;
|
|
};
|
|
|
|
GST_DEFINE_MINI_OBJECT_TYPE (GstNvEncResource, gst_nv_enc_resource);
|
|
|
|
static void
|
|
gst_nv_enc_task_clear_sei (NV_ENC_SEI_PAYLOAD * payload)
|
|
{
|
|
g_clear_pointer (&payload->payload, g_free);
|
|
}
|
|
|
|
struct GstNvEncTask : public GstMiniObject
|
|
{
|
|
GstNvEncTask (const std::string & parent_id, guint seq)
|
|
: id (parent_id), seq_num (seq)
|
|
{
|
|
memset (&event_params, 0, sizeof (NV_ENC_EVENT_PARAMS));
|
|
memset (&bitstream, 0, sizeof (NV_ENC_LOCK_BITSTREAM));
|
|
|
|
event_params.version = gst_nvenc_get_event_params_version ();
|
|
bitstream.version = gst_nvenc_get_lock_bitstream_version ();
|
|
|
|
sei_payload = g_array_new (FALSE, FALSE, sizeof (NV_ENC_SEI_PAYLOAD));
|
|
g_array_set_clear_func (sei_payload,
|
|
(GDestroyNotify) gst_nv_enc_task_clear_sei);
|
|
}
|
|
|
|
~GstNvEncTask ()
|
|
{
|
|
if (sei_payload)
|
|
g_array_unref (sei_payload);
|
|
}
|
|
|
|
std::shared_ptr<GstNvEncObject> object;
|
|
|
|
GstNvEncBuffer *buffer = nullptr;
|
|
GstNvEncResource *resource = nullptr;
|
|
|
|
GstBuffer *gst_buffer = nullptr;;
|
|
GstMapInfo info;
|
|
NV_ENC_DEVICE_TYPE device_type = NV_ENC_DEVICE_TYPE_CUDA;
|
|
|
|
NV_ENC_EVENT_PARAMS event_params;
|
|
NV_ENC_OUTPUT_PTR output_ptr = nullptr;
|
|
|
|
NV_ENC_LOCK_BITSTREAM bitstream;
|
|
bool locked = false;
|
|
std::string id;
|
|
guint seq_num;
|
|
|
|
GArray *sei_payload;
|
|
};
|
|
|
|
GST_DEFINE_MINI_OBJECT_TYPE (GstNvEncTask, gst_nv_enc_task);
|
|
|
|
bool
|
|
GstNvEncObject::IsSuccess (NVENCSTATUS status, GstNvEncObject * 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);
|
|
|
|
if (self) {
|
|
gst_debug_log_id (GST_CAT_DEFAULT, GST_LEVEL_ERROR, file, function,
|
|
line, self->id_.c_str (), "NvEnc API call failed: 0x%x, %s",
|
|
(guint) status, status_str);
|
|
} else {
|
|
gst_debug_log (GST_CAT_DEFAULT, GST_LEVEL_ERROR, file, function,
|
|
line, nullptr, "NvEnc API call failed: 0x%x, %s",
|
|
(guint) status, status_str);
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
#define NVENC_IS_SUCCESS(status,obj) \
|
|
GstNvEncObject::IsSuccess (status, obj, __FILE__, GST_FUNCTION, __LINE__)
|
|
|
|
std::shared_ptr<GstNvEncObject>
|
|
GstNvEncObject::CreateInstance (GstElement * client, GstObject * device,
|
|
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS * params)
|
|
{
|
|
NVENCSTATUS status;
|
|
gpointer session;
|
|
|
|
status = NvEncOpenEncodeSessionEx (params, &session);
|
|
if (!NVENC_IS_SUCCESS (status, nullptr)) {
|
|
GST_ERROR_OBJECT (device, "NvEncOpenEncodeSessionEx failed");
|
|
/* Report error to abort if GST_CUDA_CRITICAL_ERRORS is configured */
|
|
gst_cuda_result (CUDA_ERROR_NO_DEVICE);
|
|
return nullptr;
|
|
}
|
|
|
|
std::shared_ptr<GstNvEncObject> self =
|
|
std::make_shared <GstNvEncObject> ();
|
|
self->id_ = GST_ELEMENT_NAME (client);
|
|
self->session_ = session;
|
|
|
|
#ifdef G_OS_WIN32
|
|
if (params->deviceType == NV_ENC_DEVICE_TYPE_DIRECTX) {
|
|
self->device_ = (GstD3D11Device *) gst_object_ref (device);
|
|
self->user_token_ = gst_d3d11_create_user_token ();
|
|
} else
|
|
#endif
|
|
{
|
|
self->context_ = (GstCudaContext *) gst_object_ref (device);
|
|
self->user_token_ = gst_cuda_create_user_token ();
|
|
}
|
|
|
|
self->device_type_ = params->deviceType;
|
|
self->buffer_seq_ = 0;
|
|
self->resource_seq_ = 0;
|
|
self->task_seq_ = 0;
|
|
|
|
GST_INFO_ID (self->id_.c_str (), "New encoder object for type %d is created",
|
|
self->device_type_);
|
|
|
|
return self;
|
|
}
|
|
|
|
GstNvEncObject::~GstNvEncObject ()
|
|
{
|
|
GST_INFO_ID (id_.c_str (), "Destroying instance");
|
|
|
|
DeviceLock ();
|
|
while (!buffer_queue_.empty ()) {
|
|
GstNvEncBuffer *buf = buffer_queue_.front ();
|
|
|
|
NvEncDestroyInputBuffer (session_, buf->buffer.inputBuffer);
|
|
gst_nv_enc_buffer_unref (buf);
|
|
buffer_queue_.pop ();
|
|
}
|
|
|
|
if (!resource_queue_.empty ()) {
|
|
GST_INFO_ID (id_.c_str (), "Have %u outstanding input resource(s)",
|
|
(guint) resource_queue_.size ());
|
|
for (auto it : resource_queue_)
|
|
releaseResourceUnlocked (it);
|
|
}
|
|
|
|
while (!empty_task_queue_.empty ()) {
|
|
GstNvEncTask *task = empty_task_queue_.front ();
|
|
|
|
releaseTaskUnlocked (task);
|
|
empty_task_queue_.pop ();
|
|
}
|
|
|
|
NvEncDestroyEncoder (session_);
|
|
DeviceUnlock ();
|
|
|
|
gst_clear_object (&context_);
|
|
gst_clear_cuda_stream (&stream_);
|
|
#ifdef G_OS_WIN32
|
|
gst_clear_object (&device_);
|
|
#endif
|
|
|
|
GST_INFO_ID (id_.c_str (), "Cleared all resources");
|
|
}
|
|
|
|
gpointer
|
|
GstNvEncObject::GetHandle ()
|
|
{
|
|
return session_;
|
|
}
|
|
|
|
guint
|
|
GstNvEncObject::GetTaskSize ()
|
|
{
|
|
return task_size_;
|
|
}
|
|
|
|
void
|
|
GstNvEncObject::releaseTaskUnlocked (GstNvEncTask * task)
|
|
{
|
|
if (!task)
|
|
return;
|
|
|
|
if (task->output_ptr) {
|
|
NvEncDestroyBitstreamBuffer (session_, task->output_ptr);
|
|
task->output_ptr = nullptr;
|
|
}
|
|
|
|
#ifdef G_OS_WIN32
|
|
if (task->event_params.completionEvent) {
|
|
gpointer handle = task->event_params.completionEvent;
|
|
NvEncUnregisterAsyncEvent (session_, &task->event_params);
|
|
CloseHandle (handle);
|
|
|
|
memset (&task->event_params, 0, sizeof (NV_ENC_EVENT_PARAMS));
|
|
}
|
|
#endif
|
|
|
|
gst_nv_enc_task_unref (task);
|
|
}
|
|
|
|
NVENCSTATUS
|
|
GstNvEncObject::InitSession (NV_ENC_INITIALIZE_PARAMS * params,
|
|
GstCudaStream * stream, const GstVideoInfo * info, guint task_size)
|
|
{
|
|
NVENCSTATUS status = NV_ENC_SUCCESS;
|
|
|
|
if (initialized_) {
|
|
GST_ERROR_ID (id_.c_str(), "Was initialized");
|
|
return NV_ENC_ERR_INVALID_CALL;
|
|
}
|
|
|
|
if (memcmp (¶ms->encodeGUID, &NV_ENC_CODEC_H264_GUID, sizeof (GUID)) == 0) {
|
|
codec_ = GST_NV_ENC_CODEC_H264;
|
|
} else {
|
|
codec_ = GST_NV_ENC_CODEC_H265;
|
|
}
|
|
|
|
info_ = *info;
|
|
switch (GST_VIDEO_INFO_FORMAT (info)) {
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
buffer_format_ = NV_ENC_BUFFER_FORMAT_NV12;
|
|
break;
|
|
case GST_VIDEO_FORMAT_Y444:
|
|
case GST_VIDEO_FORMAT_GBR:
|
|
buffer_format_ = NV_ENC_BUFFER_FORMAT_YUV444;
|
|
break;
|
|
case GST_VIDEO_FORMAT_P010_10LE:
|
|
buffer_format_ = NV_ENC_BUFFER_FORMAT_YUV420_10BIT;
|
|
break;
|
|
case GST_VIDEO_FORMAT_Y444_16LE:
|
|
case GST_VIDEO_FORMAT_GBR_16LE:
|
|
buffer_format_ = NV_ENC_BUFFER_FORMAT_YUV444_10BIT;
|
|
break;
|
|
case GST_VIDEO_FORMAT_VUYA:
|
|
buffer_format_ = NV_ENC_BUFFER_FORMAT_AYUV;
|
|
break;
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
buffer_format_ = NV_ENC_BUFFER_FORMAT_ABGR;
|
|
break;
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
buffer_format_ = NV_ENC_BUFFER_FORMAT_ARGB;
|
|
break;
|
|
case GST_VIDEO_FORMAT_RGB10A2_LE:
|
|
buffer_format_ = NV_ENC_BUFFER_FORMAT_ABGR10;
|
|
break;
|
|
default:
|
|
GST_ERROR_ID (id_.c_str (), "Unexpected format %s",
|
|
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)));
|
|
return NV_ENC_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
GST_DEBUG_ID (id_.c_str (), "Initializing encoder, buffer type %d",
|
|
buffer_format_);
|
|
|
|
status = NvEncInitializeEncoder (session_, params);
|
|
if (!NVENC_IS_SUCCESS (status, this))
|
|
return status;
|
|
|
|
if (device_type_ == NV_ENC_DEVICE_TYPE_CUDA && stream) {
|
|
CUstream stream_handle;
|
|
|
|
stream_ = gst_cuda_stream_ref (stream);
|
|
stream_handle = gst_cuda_stream_get_handle (stream);
|
|
status = NvEncSetIOCudaStreams (session_,
|
|
(NV_ENC_CUSTREAM_PTR) & stream_handle,
|
|
(NV_ENC_CUSTREAM_PTR) & stream_handle);
|
|
if (!NVENC_IS_SUCCESS (status, this))
|
|
return status;
|
|
}
|
|
|
|
for (guint i = 0; i < task_size; i++) {
|
|
GstNvEncTask *task = gst_nv_enc_task_new (id_, task_seq_.fetch_add (1));
|
|
NV_ENC_CREATE_BITSTREAM_BUFFER buffer_params = { 0, };
|
|
|
|
task->device_type = device_type_;
|
|
|
|
buffer_params.version = gst_nvenc_get_create_bitstream_buffer_version ();
|
|
status = NvEncCreateBitstreamBuffer (session_, &buffer_params);
|
|
if (!NVENC_IS_SUCCESS (status, this)) {
|
|
gst_nv_enc_task_unref (task);
|
|
goto out;
|
|
}
|
|
|
|
task->output_ptr = buffer_params.bitstreamBuffer;
|
|
|
|
#ifdef G_OS_WIN32
|
|
if (params->enableEncodeAsync) {
|
|
task->event_params.version = gst_nvenc_get_event_params_version ();
|
|
task->event_params.completionEvent = CreateEvent (nullptr,
|
|
FALSE, FALSE, nullptr);
|
|
status = NvEncRegisterAsyncEvent (session_, &task->event_params);
|
|
if (!NVENC_IS_SUCCESS (status, this)) {
|
|
CloseHandle (task->event_params.completionEvent);
|
|
releaseTaskUnlocked (task);
|
|
goto out;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
empty_task_queue_.push (task);
|
|
}
|
|
|
|
task_size_ = task_size;
|
|
lookahead_ = params->encodeConfig->rcParams.lookaheadDepth;
|
|
initialized_ = true;
|
|
|
|
out:
|
|
if (status != NV_ENC_SUCCESS) {
|
|
while (!empty_task_queue_.empty ()) {
|
|
GstNvEncTask *task = empty_task_queue_.front ();
|
|
|
|
releaseTaskUnlocked (task);
|
|
empty_task_queue_.pop ();
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NVENCSTATUS
|
|
GstNvEncObject::Reconfigure (NV_ENC_RECONFIGURE_PARAMS * params)
|
|
{
|
|
return NvEncReconfigureEncoder (session_, params);
|
|
}
|
|
|
|
void
|
|
GstNvEncObject::SetFlushing (bool flushing)
|
|
{
|
|
std::lock_guard <std::mutex> lk (lock_);
|
|
flushing_ = flushing;
|
|
cond_.notify_all ();
|
|
}
|
|
|
|
NVENCSTATUS
|
|
GstNvEncObject::Encode (GstVideoCodecFrame * codec_frame,
|
|
NV_ENC_PIC_STRUCT pic_struct, GstNvEncTask * task)
|
|
{
|
|
NVENCSTATUS status;
|
|
guint retry_count = 0;
|
|
const guint retry_threshold = 100;
|
|
NV_ENC_PIC_PARAMS params = { 0, };
|
|
|
|
std::unique_lock <std::mutex> lk (lock_);
|
|
|
|
params.version = gst_nvenc_get_pic_params_version ();
|
|
params.completionEvent = task->event_params.completionEvent;
|
|
|
|
g_assert (task->buffer || task->resource);
|
|
|
|
GST_LOG_ID (id_.c_str (), "Encoding frame %u",
|
|
codec_frame->system_frame_number);
|
|
|
|
if (task->buffer) {
|
|
params.inputWidth = task->buffer->buffer.width;
|
|
params.inputHeight = task->buffer->buffer.height;
|
|
params.inputPitch = task->buffer->buffer_lock.pitch;
|
|
params.inputBuffer = task->buffer->buffer.inputBuffer;
|
|
params.bufferFmt = task->buffer->buffer.bufferFmt;
|
|
} else {
|
|
params.inputWidth = task->resource->resource.width;
|
|
params.inputHeight = task->resource->resource.height;
|
|
params.inputPitch = task->resource->resource.pitch;
|
|
params.inputBuffer = task->resource->mapped_resource.mappedResource;
|
|
params.bufferFmt = task->resource->mapped_resource.mappedBufferFmt;
|
|
}
|
|
|
|
params.frameIdx = codec_frame->system_frame_number;
|
|
params.inputTimeStamp = codec_frame->pts;
|
|
params.inputDuration = codec_frame->duration;
|
|
params.outputBitstream = task->output_ptr;
|
|
params.pictureStruct = pic_struct;
|
|
if (task->sei_payload->len > 0) {
|
|
if (codec_ == GST_NV_ENC_CODEC_H264) {
|
|
params.codecPicParams.h264PicParams.seiPayloadArray =
|
|
&g_array_index (task->sei_payload, NV_ENC_SEI_PAYLOAD, 0);
|
|
params.codecPicParams.h264PicParams.seiPayloadArrayCnt =
|
|
task->sei_payload->len;
|
|
} else {
|
|
params.codecPicParams.hevcPicParams.seiPayloadArray =
|
|
&g_array_index (task->sei_payload, NV_ENC_SEI_PAYLOAD, 0);
|
|
params.codecPicParams.hevcPicParams.seiPayloadArrayCnt =
|
|
task->sei_payload->len;
|
|
}
|
|
}
|
|
|
|
if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (codec_frame))
|
|
params.encodePicFlags = NV_ENC_PIC_FLAG_FORCEIDR;
|
|
|
|
do {
|
|
DeviceLock ();
|
|
status = NvEncEncodePicture (session_, ¶ms);
|
|
DeviceUnlock ();
|
|
|
|
if (status == NV_ENC_ERR_ENCODER_BUSY) {
|
|
if (retry_count < 100) {
|
|
GST_DEBUG_ID (id_.c_str (), "GPU is busy, retry count (%d/%d)",
|
|
retry_count, retry_threshold);
|
|
retry_count++;
|
|
|
|
/* Magic number 1ms */
|
|
g_usleep (1000);
|
|
continue;
|
|
} else {
|
|
GST_ERROR_ID (id_.c_str (), "GPU is keep busy, give up");
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
} while (true);
|
|
|
|
if (status != NV_ENC_SUCCESS && status != NV_ENC_ERR_NEED_MORE_INPUT) {
|
|
NVENC_IS_SUCCESS (status, this);
|
|
lk.unlock ();
|
|
gst_nv_enc_task_unref (task);
|
|
return status;
|
|
}
|
|
|
|
gst_video_codec_frame_set_user_data (codec_frame, task, nullptr);
|
|
|
|
{
|
|
std::lock_guard <std::recursive_mutex> rlk (resource_lock_);
|
|
|
|
if (task->resource)
|
|
active_resource_queue_.insert (task->resource);
|
|
}
|
|
|
|
/* On Windows and if async encoding is enabled, output thread will wait
|
|
* for completion event. But on Linux, async encoding is not supported.
|
|
* So, we should wait for NV_ENC_SUCCESS in case of sync mode
|
|
* (it would introduce latency though).
|
|
* Otherwise nvEncLockBitstream() will return error */
|
|
if (params.completionEvent) {
|
|
/* Windows only path */
|
|
task_queue_.push (task);
|
|
cond_.notify_all ();
|
|
} else {
|
|
pending_task_queue_.push (task);
|
|
if (status == NV_ENC_SUCCESS) {
|
|
bool notify = false;
|
|
|
|
/* XXX: nvEncLockBitstream() will return NV_ENC_ERR_INVALID_PARAM
|
|
* if lookahead is enabled. See also
|
|
* https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/494
|
|
*/
|
|
while (pending_task_queue_.size() > lookahead_) {
|
|
notify = true;
|
|
task_queue_.push (pending_task_queue_.front ());
|
|
pending_task_queue_.pop ();
|
|
}
|
|
|
|
if (notify)
|
|
cond_.notify_all ();
|
|
}
|
|
}
|
|
|
|
return NV_ENC_SUCCESS;
|
|
}
|
|
|
|
NVENCSTATUS
|
|
GstNvEncObject::Drain (GstNvEncTask * task)
|
|
{
|
|
NVENCSTATUS status;
|
|
guint retry_count = 0;
|
|
const guint retry_threshold = 100;
|
|
NV_ENC_PIC_PARAMS params = { 0, };
|
|
|
|
std::unique_lock <std::mutex> lk (lock_);
|
|
|
|
params.version = gst_nvenc_get_pic_params_version ();
|
|
params.completionEvent = task->event_params.completionEvent;
|
|
params.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
|
|
|
|
do {
|
|
status = NvEncEncodePicture (session_, ¶ms);
|
|
|
|
if (status == NV_ENC_ERR_ENCODER_BUSY) {
|
|
if (retry_count < 100) {
|
|
GST_DEBUG_ID (id_.c_str (), "GPU is busy, retry count (%d/%d)",
|
|
retry_count, retry_threshold);
|
|
retry_count++;
|
|
|
|
/* Magic number 1ms */
|
|
g_usleep (1000);
|
|
continue;
|
|
} else {
|
|
GST_ERROR_ID (id_.c_str (), "GPU is keep busy, give up");
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
} while (true);
|
|
|
|
while (!pending_task_queue_.empty ()) {
|
|
task_queue_.push (pending_task_queue_.front ());
|
|
pending_task_queue_.pop ();
|
|
}
|
|
|
|
task_queue_.push (task);
|
|
cond_.notify_all ();
|
|
|
|
return status;
|
|
}
|
|
|
|
GstFlowReturn
|
|
GstNvEncObject::GetOutput (GstNvEncTask ** task)
|
|
{
|
|
GstNvEncTask *ret = nullptr;
|
|
std::unique_lock <std::mutex> lk (lock_);
|
|
|
|
while (task_queue_.empty ())
|
|
cond_.wait (lk);
|
|
|
|
ret = task_queue_.front ();
|
|
task_queue_.pop ();
|
|
lk.unlock ();
|
|
|
|
if (!ret->buffer && !ret->resource) {
|
|
gst_nv_enc_task_unref (ret);
|
|
return GST_FLOW_EOS;
|
|
}
|
|
|
|
#ifdef G_OS_WIN32
|
|
if (ret->event_params.completionEvent &&
|
|
WaitForSingleObject (ret->event_params.completionEvent, 20000) ==
|
|
WAIT_FAILED) {
|
|
GST_ERROR_ID (id_.c_str (), "Failed to wait for completion event");
|
|
gst_nv_enc_task_unref (ret);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
#endif
|
|
|
|
*task = ret;
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
NVENCSTATUS
|
|
GstNvEncObject::LockBitstream (NV_ENC_LOCK_BITSTREAM * bitstream)
|
|
{
|
|
return NvEncLockBitstream (session_, bitstream);
|
|
}
|
|
|
|
NVENCSTATUS
|
|
GstNvEncObject::UnlockBitstream (NV_ENC_OUTPUT_PTR output_ptr)
|
|
{
|
|
return NvEncUnlockBitstream (session_, output_ptr);
|
|
}
|
|
|
|
NVENCSTATUS
|
|
GstNvEncObject::AcquireBuffer (GstNvEncBuffer ** buffer)
|
|
{
|
|
GstNvEncBuffer *new_buf = nullptr;
|
|
std::unique_lock <std::mutex> lk (lock_);
|
|
|
|
if (buffer_queue_.empty ()) {
|
|
NVENCSTATUS status;
|
|
NV_ENC_CREATE_INPUT_BUFFER in_buf = { 0, };
|
|
|
|
GST_LOG_ID (id_.c_str (), "No available input buffer, creating new one");
|
|
|
|
in_buf.version = gst_nvenc_get_create_input_buffer_version ();
|
|
in_buf.width = info_.width;
|
|
in_buf.height = info_.height;
|
|
in_buf.bufferFmt = buffer_format_;
|
|
|
|
status = NvEncCreateInputBuffer (session_, &in_buf);
|
|
if (!NVENC_IS_SUCCESS (status, this))
|
|
return status;
|
|
|
|
new_buf = gst_nv_enc_buffer_new (id_, buffer_seq_.fetch_add (1));
|
|
new_buf->buffer = in_buf;
|
|
new_buf->buffer_lock.inputBuffer = in_buf.inputBuffer;
|
|
} else {
|
|
new_buf = buffer_queue_.front ();
|
|
buffer_queue_.pop ();
|
|
}
|
|
|
|
g_assert (!new_buf->object);
|
|
|
|
new_buf->object = shared_from_this ();
|
|
|
|
*buffer = new_buf;
|
|
|
|
GST_TRACE_ID (id_.c_str (), "Acquired buffer %u", new_buf->seq_num);
|
|
|
|
return NV_ENC_SUCCESS;
|
|
}
|
|
|
|
void
|
|
GstNvEncObject::runResourceGC ()
|
|
{
|
|
std::lock_guard <std::recursive_mutex> lk (resource_lock_);
|
|
|
|
/* hard coded max size 64 */
|
|
if (resource_queue_.size () < 64)
|
|
return;
|
|
|
|
GST_LOG_ID (id_.c_str (), "Running resource GC");
|
|
|
|
DeviceLock ();
|
|
for (auto it : resource_queue_) {
|
|
if (active_resource_queue_.find (it) == active_resource_queue_.end ()) {
|
|
releaseResourceUnlocked (it);
|
|
resource_queue_.erase (it);
|
|
}
|
|
}
|
|
DeviceUnlock ();
|
|
|
|
GST_LOG_ID (id_.c_str (), "resource queue size after GC %u",
|
|
(guint) resource_queue_.size ());
|
|
}
|
|
|
|
bool
|
|
GstNvEncObject::DeviceLock ()
|
|
{
|
|
if (context_)
|
|
return gst_cuda_context_push (context_);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
GstNvEncObject::DeviceUnlock ()
|
|
{
|
|
if (context_)
|
|
return gst_cuda_context_pop (nullptr);
|
|
|
|
return true;
|
|
}
|
|
|
|
NVENCSTATUS
|
|
GstNvEncObject::acquireResourceCuda (GstMemory * mem,
|
|
GstNvEncResource ** resource)
|
|
{
|
|
GstNvEncResource *res;
|
|
GstCudaMemory *cmem;
|
|
NV_ENC_REGISTER_RESOURCE new_resource;
|
|
NV_ENC_MAP_INPUT_RESOURCE mapped_resource;
|
|
NVENCSTATUS status;
|
|
GstMapInfo info;
|
|
|
|
if (!gst_is_cuda_memory (mem)) {
|
|
GST_ERROR_ID (id_.c_str (), "Not a CUDA memory");
|
|
return NV_ENC_ERR_INVALID_CALL;
|
|
}
|
|
|
|
cmem = GST_CUDA_MEMORY_CAST (mem);
|
|
|
|
res = (GstNvEncResource *) gst_cuda_memory_get_token_data (cmem,
|
|
user_token_);
|
|
if (res) {
|
|
auto iter = resource_queue_.find (res);
|
|
/* This resource can be released already */
|
|
if (iter != resource_queue_.end ()) {
|
|
GST_LOG_ID (id_.c_str (), "Memory is holding registered resource");
|
|
*resource = gst_nv_enc_resource_ref (res);
|
|
return NV_ENC_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (!gst_memory_map (mem, &info, GST_MAP_READ_NVENC)) {
|
|
GST_ERROR_ID (id_.c_str (), "Couldn't map CUDA memory");
|
|
return NV_ENC_ERR_MAP_FAILED;
|
|
}
|
|
|
|
memset (&new_resource, 0, sizeof (NV_ENC_REGISTER_RESOURCE));
|
|
memset (&mapped_resource, 0, sizeof (NV_ENC_MAP_INPUT_RESOURCE));
|
|
|
|
new_resource.version = gst_nvenc_get_register_resource_version ();
|
|
new_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR;
|
|
new_resource.width = cmem->info.width;
|
|
new_resource.height = cmem->info.height;
|
|
new_resource.pitch = cmem->info.stride[0];
|
|
new_resource.resourceToRegister = info.data;
|
|
new_resource.bufferFormat = buffer_format_;
|
|
|
|
GST_LOG_ID (id_.c_str (), "Registering CUDA resource %p, %dx%d, pitch %u",
|
|
info.data, new_resource.width, new_resource.height, new_resource.pitch);
|
|
|
|
status = NvEncRegisterResource (session_, &new_resource);
|
|
gst_memory_unmap (mem, &info);
|
|
|
|
if (!NVENC_IS_SUCCESS (status, this))
|
|
return status;
|
|
|
|
mapped_resource.version = gst_nvenc_get_map_input_resource_version ();
|
|
mapped_resource.registeredResource = new_resource.registeredResource;
|
|
status = NvEncMapInputResource (session_, &mapped_resource);
|
|
if (!NVENC_IS_SUCCESS (status, this)) {
|
|
NvEncUnregisterResource (session_, new_resource.registeredResource);
|
|
return status;
|
|
}
|
|
|
|
res = gst_nv_enc_resource_new (id_, resource_seq_.fetch_add (1));
|
|
/* weak ref */
|
|
res->object = shared_from_this ();
|
|
|
|
res->resource = new_resource;
|
|
res->mapped_resource = mapped_resource;
|
|
|
|
gst_cuda_memory_set_token_data (cmem, user_token_,
|
|
gst_nv_enc_resource_ref (res),
|
|
(GDestroyNotify) gst_nv_enc_resource_unref);
|
|
resource_queue_.insert (res);
|
|
|
|
*resource = res;
|
|
|
|
return NV_ENC_SUCCESS;
|
|
}
|
|
|
|
#ifdef G_OS_WIN32
|
|
NVENCSTATUS
|
|
GstNvEncObject::acquireResourceD3D11 (GstMemory * mem,
|
|
GstNvEncResource ** resource)
|
|
{
|
|
GstNvEncResource *res;
|
|
GstD3D11Memory *dmem;
|
|
NV_ENC_REGISTER_RESOURCE new_resource;
|
|
NV_ENC_MAP_INPUT_RESOURCE mapped_resource;
|
|
NVENCSTATUS status;
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
GstMapInfo info;
|
|
|
|
if (!gst_is_d3d11_memory (mem)) {
|
|
GST_ERROR_ID (id_.c_str (), "Not a D3D11 memory");
|
|
return NV_ENC_ERR_INVALID_CALL;
|
|
}
|
|
|
|
dmem = GST_D3D11_MEMORY_CAST (mem);
|
|
|
|
res = (GstNvEncResource *) gst_d3d11_memory_get_token_data (dmem,
|
|
user_token_);
|
|
if (res) {
|
|
auto iter = resource_queue_.find (res);
|
|
/* This resource can be released already */
|
|
if (iter != resource_queue_.end ()) {
|
|
GST_LOG_ID (id_.c_str (), "Memory is holding registered resource");
|
|
*resource = gst_nv_enc_resource_ref (res);
|
|
return NV_ENC_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (!gst_memory_map (mem, &info, GST_MAP_READ_NVENC)) {
|
|
GST_ERROR_ID (id_.c_str (), "Couldn't map D3D11 memory");
|
|
return NV_ENC_ERR_MAP_FAILED;
|
|
}
|
|
|
|
gst_d3d11_memory_get_texture_desc (dmem, &desc);
|
|
|
|
memset (&new_resource, 0, sizeof (NV_ENC_REGISTER_RESOURCE));
|
|
memset (&mapped_resource, 0, sizeof (NV_ENC_MAP_INPUT_RESOURCE));
|
|
|
|
new_resource.version = gst_nvenc_get_register_resource_version ();
|
|
new_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX;
|
|
new_resource.width = desc.Width;
|
|
new_resource.height = desc.Height;
|
|
new_resource.pitch = 0;
|
|
new_resource.resourceToRegister = info.data;
|
|
new_resource.subResourceIndex = gst_d3d11_memory_get_subresource_index (dmem);
|
|
new_resource.bufferFormat = buffer_format_;
|
|
|
|
status = NvEncRegisterResource (session_, &new_resource);
|
|
gst_memory_unmap (mem, &info);
|
|
if (!NVENC_IS_SUCCESS (status, this))
|
|
return status;
|
|
|
|
mapped_resource.version = gst_nvenc_get_map_input_resource_version ();
|
|
mapped_resource.registeredResource = new_resource.registeredResource;
|
|
status = NvEncMapInputResource (session_, &mapped_resource);
|
|
if (!NVENC_IS_SUCCESS (status, this)) {
|
|
NvEncUnregisterResource (session_, new_resource.registeredResource);
|
|
return status;
|
|
}
|
|
|
|
res = gst_nv_enc_resource_new (id_, resource_seq_.fetch_add (1));
|
|
/* weak ref */
|
|
res->object = shared_from_this ();
|
|
|
|
res->resource = new_resource;
|
|
res->mapped_resource = mapped_resource;
|
|
|
|
gst_d3d11_memory_set_token_data (dmem, user_token_,
|
|
gst_nv_enc_resource_ref (res),
|
|
(GDestroyNotify) gst_nv_enc_resource_unref);
|
|
resource_queue_.insert (res);
|
|
|
|
*resource = res;
|
|
|
|
return NV_ENC_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
NVENCSTATUS
|
|
GstNvEncObject::AcquireResource (GstMemory * mem, GstNvEncResource ** resource)
|
|
{
|
|
NVENCSTATUS status;
|
|
std::lock_guard <std::recursive_mutex> lk (resource_lock_);
|
|
|
|
#ifdef G_OS_WIN32
|
|
if (device_type_ == NV_ENC_DEVICE_TYPE_DIRECTX) {
|
|
status = acquireResourceD3D11 (mem, resource);
|
|
} else
|
|
#endif
|
|
{
|
|
status = acquireResourceCuda (mem, resource);
|
|
}
|
|
|
|
if (status == NV_ENC_SUCCESS) {
|
|
GST_TRACE_ID (id_.c_str (), "Returning resource %u, "
|
|
"resource queue size %u (active %u)",
|
|
(*resource)->seq_num, (guint) resource_queue_.size (),
|
|
(guint) active_resource_queue_.size ());
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
GstFlowReturn
|
|
GstNvEncObject::AcquireTask (GstNvEncTask ** task, bool force)
|
|
{
|
|
GstNvEncTask *new_task = nullptr;
|
|
|
|
std::unique_lock <std::mutex> lk (lock_);
|
|
|
|
do {
|
|
if (!force && flushing_) {
|
|
GST_DEBUG_ID (id_.c_str (), "We are flushing");
|
|
return GST_FLOW_FLUSHING;
|
|
}
|
|
|
|
if (!empty_task_queue_.empty ()) {
|
|
new_task = empty_task_queue_.front ();
|
|
empty_task_queue_.pop ();
|
|
break;
|
|
}
|
|
|
|
GST_LOG_ID (id_.c_str (), "No available task, waiting for release");
|
|
cond_.wait (lk);
|
|
} while (true);
|
|
|
|
g_assert (!new_task->object);
|
|
|
|
new_task->object = shared_from_this ();
|
|
g_array_set_size (new_task->sei_payload, 0);
|
|
|
|
*task = new_task;
|
|
|
|
GST_TRACE_ID (id_.c_str (), "Acquired task %u", new_task->seq_num);
|
|
|
|
runResourceGC ();
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
void
|
|
GstNvEncObject::PushEmptyTask (GstNvEncTask * task)
|
|
{
|
|
std::lock_guard <std::mutex> lk (lock_);
|
|
|
|
empty_task_queue_.push (task);
|
|
cond_.notify_all ();
|
|
}
|
|
|
|
void
|
|
GstNvEncObject::PushEmptyBuffer (GstNvEncBuffer * buffer)
|
|
{
|
|
std::lock_guard <std::mutex> lk (lock_);
|
|
|
|
buffer_queue_.push (buffer);
|
|
cond_.notify_all ();
|
|
}
|
|
|
|
void
|
|
GstNvEncObject::releaseResourceUnlocked (GstNvEncResource * resource)
|
|
{
|
|
NvEncUnmapInputResource (session_, resource->mapped_resource.mappedResource);
|
|
NvEncUnregisterResource (session_, resource->resource.registeredResource);
|
|
|
|
resource->mapped_resource.mappedResource = nullptr;
|
|
resource->resource.registeredResource = nullptr;
|
|
}
|
|
|
|
void
|
|
GstNvEncObject::ReleaseResource (GstNvEncResource * resource)
|
|
{
|
|
std::lock_guard <std::recursive_mutex> lk (resource_lock_);
|
|
|
|
active_resource_queue_.erase (resource);
|
|
|
|
auto it = resource_queue_.find (resource);
|
|
if (it != resource_queue_.end ()) {
|
|
DeviceLock ();
|
|
releaseResourceUnlocked (resource);
|
|
DeviceUnlock ();
|
|
resource_queue_.erase (it);
|
|
}
|
|
}
|
|
|
|
void
|
|
GstNvEncObject::DeactivateResource (GstNvEncResource * resource)
|
|
{
|
|
std::lock_guard <std::recursive_mutex> lk (resource_lock_);
|
|
|
|
GST_TRACE_ID (resource->id.c_str (), "Deactivating resource %u",
|
|
resource->seq_num);
|
|
|
|
active_resource_queue_.erase (resource);
|
|
}
|
|
|
|
/* *INDENT-ON* */
|
|
|
|
NVENCSTATUS
|
|
gst_nv_enc_buffer_lock (GstNvEncBuffer * buffer,
|
|
gpointer * data, guint32 * pitch)
|
|
{
|
|
std::shared_ptr < GstNvEncObject > object = buffer->object;
|
|
NVENCSTATUS status;
|
|
|
|
g_assert (object);
|
|
|
|
GST_TRACE_ID (buffer->id.c_str (), "Locking buffer %u", buffer->seq_num);
|
|
|
|
if (!buffer->locked) {
|
|
buffer->buffer_lock.inputBuffer = buffer->buffer.inputBuffer;
|
|
status = NvEncLockInputBuffer (object->GetHandle (), &buffer->buffer_lock);
|
|
if (!NVENC_IS_SUCCESS (status, object.get ()))
|
|
return status;
|
|
|
|
buffer->locked = true;
|
|
}
|
|
|
|
*data = buffer->buffer_lock.bufferDataPtr;
|
|
*pitch = buffer->buffer_lock.pitch;
|
|
|
|
return NV_ENC_SUCCESS;
|
|
}
|
|
|
|
void
|
|
gst_nv_enc_buffer_unlock (GstNvEncBuffer * buffer)
|
|
{
|
|
std::shared_ptr < GstNvEncObject > object = buffer->object;
|
|
|
|
if (!buffer->locked) {
|
|
GST_DEBUG_ID (buffer->id.c_str (),
|
|
"Buffer %u was not locked", buffer->seq_num);
|
|
return;
|
|
}
|
|
|
|
g_assert (object);
|
|
|
|
NvEncUnlockInputBuffer (object->GetHandle (), buffer->buffer.inputBuffer);
|
|
buffer->locked = false;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_enc_buffer_dispose (GstNvEncBuffer * buffer)
|
|
{
|
|
std::shared_ptr < GstNvEncObject > object = buffer->object;
|
|
|
|
GST_TRACE_ID (buffer->id.c_str (), "Disposing buffer %u", buffer->seq_num);
|
|
|
|
if (!object)
|
|
return TRUE;
|
|
|
|
gst_nv_enc_buffer_unlock (buffer);
|
|
buffer->object = nullptr;
|
|
|
|
GST_TRACE_ID (buffer->id.c_str (),
|
|
"Back to buffer queue %u", buffer->seq_num);
|
|
|
|
/* Back to task queue */
|
|
gst_nv_enc_buffer_ref (buffer);
|
|
object->PushEmptyBuffer (buffer);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gst_nv_enc_buffer_free (GstNvEncBuffer * buffer)
|
|
{
|
|
GST_TRACE_ID (buffer->id.c_str (), "Freeing buffer %u", buffer->seq_num);
|
|
|
|
delete buffer;
|
|
}
|
|
|
|
static GstNvEncBuffer *
|
|
gst_nv_enc_buffer_new (const std::string & id, guint seq_num)
|
|
{
|
|
GstNvEncBuffer *buffer = new GstNvEncBuffer (id, seq_num);
|
|
|
|
gst_mini_object_init (buffer, 0, GST_TYPE_NV_ENC_BUFFER, nullptr,
|
|
(GstMiniObjectDisposeFunction) gst_nv_enc_buffer_dispose,
|
|
(GstMiniObjectFreeFunction) gst_nv_enc_buffer_free);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_enc_resource_dispose (GstNvEncResource * resource)
|
|
{
|
|
std::shared_ptr < GstNvEncObject > object;
|
|
|
|
GST_TRACE_ID (resource->id.c_str (),
|
|
"Disposing resource %u", resource->seq_num);
|
|
|
|
object = resource->object.lock ();
|
|
|
|
if (!object)
|
|
return TRUE;
|
|
|
|
object->ReleaseResource (resource);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_nv_enc_resource_free (GstNvEncResource * resource)
|
|
{
|
|
GST_TRACE_ID (resource->id.c_str (),
|
|
"Freeing resource %u", resource->seq_num);
|
|
|
|
delete resource;
|
|
}
|
|
|
|
static GstNvEncResource *
|
|
gst_nv_enc_resource_new (const std::string & id, guint seq_num)
|
|
{
|
|
GstNvEncResource *resource = new GstNvEncResource (id, seq_num);
|
|
|
|
gst_mini_object_init (resource, 0, GST_TYPE_NV_ENC_RESOURCE, nullptr,
|
|
(GstMiniObjectDisposeFunction) gst_nv_enc_resource_dispose,
|
|
(GstMiniObjectFreeFunction) gst_nv_enc_resource_free);
|
|
|
|
return resource;
|
|
}
|
|
|
|
gboolean
|
|
gst_nv_enc_task_set_buffer (GstNvEncTask * task, GstNvEncBuffer * buffer)
|
|
{
|
|
g_assert (!task->buffer);
|
|
g_assert (!task->resource);
|
|
|
|
task->buffer = buffer;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_nv_enc_task_set_resource (GstNvEncTask * task,
|
|
GstBuffer * buffer, GstNvEncResource * resource)
|
|
{
|
|
if (!gst_buffer_map (buffer, &task->info, GST_MAP_READ_NVENC)) {
|
|
GST_ERROR_ID (task->id.c_str (), "Couldn't map resource buffer");
|
|
gst_buffer_unref (buffer);
|
|
gst_nv_enc_resource_unref (resource);
|
|
return FALSE;
|
|
}
|
|
|
|
task->gst_buffer = buffer;
|
|
task->resource = resource;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GArray *
|
|
gst_nv_enc_task_get_sei_payload (GstNvEncTask * task)
|
|
{
|
|
return task->sei_payload;
|
|
}
|
|
|
|
NVENCSTATUS
|
|
gst_nv_enc_task_lock_bitstream (GstNvEncTask * task,
|
|
NV_ENC_LOCK_BITSTREAM * bitstream)
|
|
{
|
|
NVENCSTATUS status;
|
|
|
|
if (task->locked) {
|
|
GST_ERROR_ID (task->id.c_str (), "Bitstream was locked already");
|
|
return NV_ENC_ERR_INVALID_CALL;
|
|
}
|
|
|
|
task->bitstream.outputBitstream = task->output_ptr;
|
|
status = task->object->LockBitstream (&task->bitstream);
|
|
|
|
if (!NVENC_IS_SUCCESS (status, task->object.get ()))
|
|
return status;
|
|
|
|
task->locked = true;
|
|
*bitstream = task->bitstream;
|
|
|
|
return NV_ENC_SUCCESS;
|
|
}
|
|
|
|
void
|
|
gst_nv_enc_task_unlock_bitstream (GstNvEncTask * task)
|
|
{
|
|
NVENCSTATUS status;
|
|
|
|
if (!task->locked)
|
|
return;
|
|
|
|
status = task->object->UnlockBitstream (task->output_ptr);
|
|
|
|
NVENC_IS_SUCCESS (status, task->object.get ());
|
|
task->locked = false;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nv_enc_task_dispose (GstNvEncTask * task)
|
|
{
|
|
std::shared_ptr < GstNvEncObject > object;
|
|
|
|
GST_TRACE_ID (task->id.c_str (), "Disposing task %u", task->seq_num);
|
|
|
|
object = task->object;
|
|
|
|
g_array_set_size (task->sei_payload, 0);
|
|
|
|
if (task->resource) {
|
|
object->DeactivateResource (task->resource);
|
|
gst_clear_nv_encoder_resource (&task->resource);
|
|
}
|
|
|
|
gst_clear_nv_encoder_buffer (&task->buffer);
|
|
|
|
if (task->gst_buffer) {
|
|
if (task->device_type == NV_ENC_DEVICE_TYPE_CUDA) {
|
|
GstMemory *mem = gst_buffer_peek_memory (task->gst_buffer, 0);
|
|
if (gst_is_cuda_memory (mem))
|
|
GST_MEMORY_FLAG_UNSET (mem, GST_CUDA_MEMORY_TRANSFER_NEED_SYNC);
|
|
}
|
|
gst_buffer_unmap (task->gst_buffer, &task->info);
|
|
gst_clear_buffer (&task->gst_buffer);
|
|
}
|
|
|
|
if (!object)
|
|
return TRUE;
|
|
|
|
task->object = nullptr;
|
|
|
|
GST_TRACE_ID (task->id.c_str (), "Back to task queue %u", task->seq_num);
|
|
|
|
/* Back to task queue */
|
|
gst_nv_enc_task_ref (task);
|
|
object->PushEmptyTask (task);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gst_nv_enc_task_free (GstNvEncTask * task)
|
|
{
|
|
GST_TRACE_ID (task->id.c_str (), "Freeing task %u", task->seq_num);
|
|
|
|
delete task;
|
|
}
|
|
|
|
static GstNvEncTask *
|
|
gst_nv_enc_task_new (const std::string & id, guint seq_num)
|
|
{
|
|
GstNvEncTask *task = new GstNvEncTask (id, seq_num);
|
|
|
|
gst_mini_object_init (task, 0, GST_TYPE_NV_ENC_TASK, nullptr,
|
|
(GstMiniObjectDisposeFunction) gst_nv_enc_task_dispose,
|
|
(GstMiniObjectFreeFunction) gst_nv_enc_task_free);
|
|
|
|
return task;
|
|
}
|
|
|
|
const gchar *
|
|
nvenc_status_to_string (NVENCSTATUS status)
|
|
{
|
|
#define CASE(err) \
|
|
case err: \
|
|
return G_STRINGIFY (err);
|
|
|
|
switch (status) {
|
|
CASE (NV_ENC_SUCCESS);
|
|
CASE (NV_ENC_ERR_NO_ENCODE_DEVICE);
|
|
CASE (NV_ENC_ERR_UNSUPPORTED_DEVICE);
|
|
CASE (NV_ENC_ERR_INVALID_ENCODERDEVICE);
|
|
CASE (NV_ENC_ERR_INVALID_DEVICE);
|
|
CASE (NV_ENC_ERR_DEVICE_NOT_EXIST);
|
|
CASE (NV_ENC_ERR_INVALID_PTR);
|
|
CASE (NV_ENC_ERR_INVALID_EVENT);
|
|
CASE (NV_ENC_ERR_INVALID_PARAM);
|
|
CASE (NV_ENC_ERR_INVALID_CALL);
|
|
CASE (NV_ENC_ERR_OUT_OF_MEMORY);
|
|
CASE (NV_ENC_ERR_ENCODER_NOT_INITIALIZED);
|
|
CASE (NV_ENC_ERR_UNSUPPORTED_PARAM);
|
|
CASE (NV_ENC_ERR_LOCK_BUSY);
|
|
CASE (NV_ENC_ERR_NOT_ENOUGH_BUFFER);
|
|
CASE (NV_ENC_ERR_INVALID_VERSION);
|
|
CASE (NV_ENC_ERR_MAP_FAILED);
|
|
CASE (NV_ENC_ERR_NEED_MORE_INPUT);
|
|
CASE (NV_ENC_ERR_ENCODER_BUSY);
|
|
CASE (NV_ENC_ERR_EVENT_NOT_REGISTERD);
|
|
CASE (NV_ENC_ERR_GENERIC);
|
|
CASE (NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY);
|
|
CASE (NV_ENC_ERR_UNIMPLEMENTED);
|
|
CASE (NV_ENC_ERR_RESOURCE_REGISTER_FAILED);
|
|
CASE (NV_ENC_ERR_RESOURCE_NOT_REGISTERED);
|
|
CASE (NV_ENC_ERR_RESOURCE_NOT_MAPPED);
|
|
default:
|
|
break;
|
|
}
|
|
#undef CASE
|
|
|
|
return "Unknown";
|
|
}
|