mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-25 19:21:06 +00:00
nvcodec: Add support for CUDA IPC
Adding cudaipc{src,sink} element for CUDA IPC support. Implementation note: * For the communication between end points, Win32 named-pipe and unix domain socket will be used on Windows and Linux respectively. * cudaipcsink behaves as a server, and all GPU resources will be owned by the server process and exported for other processes, then cudaipcsrc (client) will import each exported handle. * User can select IPC mode via "ipc-mode" property of cudaipcsink. There are two IPC mode, one is "legacy" which uses legacy CUDA IPC method and the other is "mmap" which uses CUDA virtual memory API with OS's resource handle sharing method such as DuplicateHandle() on Windows. The "mmap" mode might be better than "legacy" in terms of stability since it relies on OS's resource management but it would consume more GPU memory than "legacy" mode. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4510>
This commit is contained in:
parent
7b1e4d6051
commit
7b6023d9cf
21 changed files with 6221 additions and 2 deletions
554
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipc.cpp
Normal file
554
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipc.cpp
Normal file
|
@ -0,0 +1,554 @@
|
|||
/* 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 "gstcudaipc.h"
|
||||
#include <gst/cuda/gstcuda-private.h>
|
||||
#include <string.h>
|
||||
#include <cctype>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#define GST_CUDA_IPC_MAGIC_NUMBER 0xC0DA10C0
|
||||
|
||||
constexpr guint GST_CUDA_IPC_PKT_HAVE_DATA_PAYLOAD_MIN_SIZE =
|
||||
sizeof (GstClockTime) + sizeof (CUipcMemHandle) +
|
||||
sizeof (GstCudaIpcMemLayout) + sizeof (guint8);
|
||||
constexpr guint GST_CUDA_IPC_PKT_RELEASE_DATA_PAYLOAD_SIZE =
|
||||
sizeof (CUipcMemHandle);
|
||||
|
||||
bool
|
||||
gst_cuda_ipc_pkt_identify (std::vector < guint8 > &buf,
|
||||
GstCudaIpcPacketHeader & header)
|
||||
{
|
||||
g_return_val_if_fail (buf.size () >= GST_CUDA_IPC_PKT_HEADER_SIZE, false);
|
||||
|
||||
memcpy (&header, &buf[0], GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
if (header.magic != GST_CUDA_IPC_MAGIC_NUMBER)
|
||||
return false;
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE + header.payload_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
gst_cuda_ipc_pkt_build_config (std::vector < guint8 > &buf,
|
||||
GstCudaPid pid, gboolean use_mmap, GstCaps * caps)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
guint8 *ptr;
|
||||
gchar *caps_str = nullptr;
|
||||
guint caps_size = 0;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CAPS (caps), false);
|
||||
|
||||
caps_str = gst_caps_serialize (caps, GST_SERIALIZE_FLAG_NONE);
|
||||
if (!caps_str)
|
||||
return false;
|
||||
|
||||
caps_size = strlen (caps_str) + 1;
|
||||
|
||||
header.type = GstCudaIpcPktType::CONFIG;
|
||||
header.magic = GST_CUDA_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = sizeof (GstCudaPid) + sizeof (gboolean) + caps_size;
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE + header.payload_size);
|
||||
|
||||
ptr = &buf[0];
|
||||
|
||||
memcpy (ptr, &header, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
ptr += GST_CUDA_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
*((GstCudaPid *) ptr) = pid;
|
||||
ptr += sizeof (GstCudaPid);
|
||||
|
||||
*((gboolean *) ptr) = use_mmap;
|
||||
ptr += sizeof (gboolean);
|
||||
|
||||
strcpy ((char *) ptr, caps_str);
|
||||
g_free (caps_str);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
gst_cuda_ipc_pkt_parse_config (std::vector < guint8 > &buf, GstCudaPid * pid,
|
||||
gboolean * use_mmap, GstCaps ** caps)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
const guint8 *ptr;
|
||||
std::string str;
|
||||
|
||||
g_return_val_if_fail (buf.size () > GST_CUDA_IPC_PKT_HEADER_SIZE, false);
|
||||
g_return_val_if_fail (caps, false);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (&header, ptr, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
if (header.type != GstCudaIpcPktType::CONFIG ||
|
||||
header.magic != GST_CUDA_IPC_MAGIC_NUMBER ||
|
||||
header.payload_size <= sizeof (GstCudaPid) + sizeof (gboolean)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr += GST_CUDA_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (pid, ptr, sizeof (GstCudaPid));
|
||||
ptr += sizeof (GstCudaPid);
|
||||
|
||||
memcpy (use_mmap, ptr, sizeof (gboolean));
|
||||
ptr += sizeof (gboolean);
|
||||
|
||||
*caps = gst_caps_from_string ((const gchar *) ptr);
|
||||
if (*caps == nullptr)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_pkt_build_need_data (std::vector < guint8 > &buf)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
header.type = GstCudaIpcPktType::NEED_DATA;
|
||||
header.magic = GST_CUDA_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = 0;
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
memcpy (&buf[0], &header, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
bool
|
||||
gst_cuda_ipc_pkt_build_have_data (std::vector < guint8 > &buf, GstClockTime pts,
|
||||
const GstVideoInfo & info, const CUipcMemHandle & handle, GstCaps * caps)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
GstCudaIpcMemLayout layout;
|
||||
guint8 *ptr;
|
||||
gchar *caps_str = nullptr;
|
||||
guint caps_size = 1;
|
||||
|
||||
if (caps) {
|
||||
caps_str = gst_caps_serialize (caps, GST_SERIALIZE_FLAG_NONE);
|
||||
if (!caps_str)
|
||||
return false;
|
||||
|
||||
caps_size += strlen (caps_str) + 1;
|
||||
}
|
||||
|
||||
header.type = GstCudaIpcPktType::HAVE_DATA;
|
||||
header.magic = GST_CUDA_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = sizeof (GstClockTime) + sizeof (CUipcMemHandle) +
|
||||
sizeof (GstCudaIpcMemLayout) + caps_size;
|
||||
|
||||
layout.size = layout.max_size = info.size;
|
||||
layout.pitch = info.stride[0];
|
||||
for (guint i = 0; i < 4; i++)
|
||||
layout.offset[i] = info.offset[i];
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE + header.payload_size);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (ptr, &header, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
ptr += GST_CUDA_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (ptr, &pts, sizeof (GstClockTime));
|
||||
ptr += sizeof (GstClockTime);
|
||||
|
||||
memcpy (ptr, &layout, sizeof (GstCudaIpcMemLayout));
|
||||
ptr += sizeof (GstCudaIpcMemLayout);
|
||||
|
||||
memcpy (ptr, &handle, sizeof (CUipcMemHandle));
|
||||
ptr += sizeof (CUipcMemHandle);
|
||||
|
||||
if (caps) {
|
||||
*ptr = 1;
|
||||
ptr++;
|
||||
|
||||
strcpy ((char *) ptr, caps_str);
|
||||
} else {
|
||||
*ptr = 0;
|
||||
}
|
||||
|
||||
g_free (caps_str);
|
||||
|
||||
return true;
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
bool
|
||||
gst_cuda_ipc_pkt_parse_have_data (const std::vector < guint8 > &buf,
|
||||
GstClockTime & pts, GstCudaIpcMemLayout & layout, CUipcMemHandle & handle,
|
||||
GstCaps ** caps)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
const guint8 *ptr;
|
||||
std::string str;
|
||||
|
||||
g_return_val_if_fail (buf.size () >=
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE +
|
||||
GST_CUDA_IPC_PKT_HAVE_DATA_PAYLOAD_MIN_SIZE, false);
|
||||
g_return_val_if_fail (caps, false);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (&header, ptr, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
if (header.type != GstCudaIpcPktType::HAVE_DATA ||
|
||||
header.magic != GST_CUDA_IPC_MAGIC_NUMBER ||
|
||||
header.payload_size < GST_CUDA_IPC_PKT_HAVE_DATA_PAYLOAD_MIN_SIZE) {
|
||||
return false;
|
||||
}
|
||||
ptr += GST_CUDA_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (&pts, ptr, sizeof (GstClockTime));
|
||||
ptr += sizeof (GstClockTime);
|
||||
|
||||
memcpy (&layout, ptr, sizeof (GstCudaIpcMemLayout));
|
||||
ptr += sizeof (GstCudaIpcMemLayout);
|
||||
|
||||
memcpy (&handle, ptr, sizeof (CUipcMemHandle));
|
||||
ptr += sizeof (CUipcMemHandle);
|
||||
|
||||
if (*ptr) {
|
||||
ptr++;
|
||||
|
||||
*caps = gst_caps_from_string ((const gchar *) ptr);
|
||||
if (*caps == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
bool
|
||||
gst_cuda_ipc_pkt_build_have_mmap_data (std::vector<guint8> & buf,
|
||||
GstClockTime pts, const GstVideoInfo & info, guint32 max_size,
|
||||
GstCudaSharableHandle handle, GstCaps * caps)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
GstCudaIpcMemLayout layout;
|
||||
guint8 *ptr;
|
||||
gchar *caps_str = nullptr;
|
||||
guint caps_size = 1;
|
||||
|
||||
if (caps) {
|
||||
caps_str = gst_caps_serialize (caps, GST_SERIALIZE_FLAG_NONE);
|
||||
if (!caps_str)
|
||||
return false;
|
||||
|
||||
caps_size = strlen (caps_str) + 1;
|
||||
}
|
||||
|
||||
header.type = GstCudaIpcPktType::HAVE_MMAP_DATA;
|
||||
header.magic = GST_CUDA_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = sizeof (GstClockTime) + sizeof (GstCudaIpcMemLayout) +
|
||||
sizeof (GstCudaSharableHandle) + caps_size;
|
||||
|
||||
layout.size = info.size;
|
||||
layout.max_size = max_size;
|
||||
layout.pitch = info.stride[0];
|
||||
for (guint i = 0; i < 4; i++)
|
||||
layout.offset[i] = info.offset[i];
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE + header.payload_size);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (ptr, &header, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
ptr += GST_CUDA_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (ptr, &pts, sizeof (GstClockTime));
|
||||
ptr += sizeof (GstClockTime);
|
||||
|
||||
memcpy (ptr, &layout, sizeof (GstCudaIpcMemLayout));
|
||||
ptr += sizeof (GstCudaIpcMemLayout);
|
||||
|
||||
*((GstCudaSharableHandle *) ptr) = handle;
|
||||
ptr += sizeof (GstCudaSharableHandle);
|
||||
|
||||
if (caps) {
|
||||
*ptr = 1;
|
||||
ptr++;
|
||||
|
||||
strcpy ((char *) ptr, caps_str);
|
||||
} else {
|
||||
*ptr = 0;
|
||||
}
|
||||
|
||||
g_free (caps_str);
|
||||
|
||||
return true;
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
bool
|
||||
gst_cuda_ipc_pkt_parse_have_mmap_data (const std::vector < guint8 > &buf,
|
||||
GstClockTime & pts, GstCudaIpcMemLayout & layout,
|
||||
GstCudaSharableHandle * handle, GstCaps ** caps)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
const guint8 *ptr;
|
||||
std::string str;
|
||||
|
||||
g_return_val_if_fail (buf.size () >
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE +
|
||||
sizeof (GstClockTime) + sizeof (GstCudaIpcMemLayout) +
|
||||
sizeof (GstCudaSharableHandle), false);
|
||||
g_return_val_if_fail (caps, false);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (&header, ptr, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
if (header.type != GstCudaIpcPktType::HAVE_MMAP_DATA ||
|
||||
header.magic != GST_CUDA_IPC_MAGIC_NUMBER ||
|
||||
header.payload_size <=
|
||||
sizeof (GstClockTime) + sizeof (GstCudaIpcMemLayout) +
|
||||
sizeof (GstCudaSharableHandle)) {
|
||||
return false;
|
||||
}
|
||||
ptr += GST_CUDA_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (&pts, ptr, sizeof (GstClockTime));
|
||||
ptr += sizeof (GstClockTime);
|
||||
|
||||
memcpy (&layout, ptr, sizeof (GstCudaIpcMemLayout));
|
||||
ptr += sizeof (GstCudaIpcMemLayout);
|
||||
|
||||
*handle = *((GstCudaSharableHandle *) ptr);
|
||||
ptr += sizeof (GstCudaSharableHandle);
|
||||
|
||||
if (*ptr) {
|
||||
ptr++;
|
||||
|
||||
*caps = gst_caps_from_string ((const gchar *) ptr);
|
||||
if (*caps == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_pkt_build_read_done (std::vector < guint8 > &buf)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
header.type = GstCudaIpcPktType::READ_DONE;
|
||||
header.magic = GST_CUDA_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = 0;
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
memcpy (&buf[0], &header, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_pkt_build_release_data (std::vector < guint8 > &buf,
|
||||
const CUipcMemHandle & handle)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
guint8 *ptr;
|
||||
|
||||
header.type = GstCudaIpcPktType::RELEASE_DATA;
|
||||
header.magic = GST_CUDA_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = sizeof (CUipcMemHandle);
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE +
|
||||
GST_CUDA_IPC_PKT_RELEASE_DATA_PAYLOAD_SIZE);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (ptr, &header, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
ptr += GST_CUDA_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (ptr, &handle, GST_CUDA_IPC_PKT_RELEASE_DATA_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
bool
|
||||
gst_cuda_ipc_pkt_parse_release_data (std::vector < guint8 > &buf,
|
||||
CUipcMemHandle & handle)
|
||||
{
|
||||
g_return_val_if_fail (buf.size () >=
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE + GST_CUDA_IPC_PKT_RELEASE_DATA_PAYLOAD_SIZE,
|
||||
false);
|
||||
|
||||
memcpy (&handle, &buf[0] + GST_CUDA_IPC_PKT_HEADER_SIZE,
|
||||
GST_CUDA_IPC_PKT_RELEASE_DATA_PAYLOAD_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_pkt_build_release_mmap_data (std::vector < guint8 > &buf,
|
||||
GstCudaSharableHandle handle)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
guint8 *ptr;
|
||||
|
||||
header.type = GstCudaIpcPktType::RELEASE_MMAP_DATA;
|
||||
header.magic = GST_CUDA_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = sizeof (GstCudaSharableHandle);
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE + header.payload_size);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (ptr, &header, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
ptr += GST_CUDA_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
*((GstCudaSharableHandle *) ptr) = handle;
|
||||
}
|
||||
|
||||
bool
|
||||
gst_cuda_ipc_pkt_parse_release_mmap_data (std::vector < guint8 > &buf,
|
||||
GstCudaSharableHandle * handle)
|
||||
{
|
||||
guint8 *ptr;
|
||||
|
||||
g_return_val_if_fail (buf.size () >=
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE + sizeof (GstCudaSharableHandle), false);
|
||||
|
||||
ptr = &buf[0];
|
||||
ptr += GST_CUDA_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
*handle = *((GstCudaSharableHandle *) ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_pkt_build_eos (std::vector < guint8 > &buf)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
header.type = GstCudaIpcPktType::EOS;
|
||||
header.magic = GST_CUDA_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = 0;
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
memcpy (&buf[0], &header, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_pkt_build_fin (std::vector < guint8 > &buf)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
header.type = GstCudaIpcPktType::FIN;
|
||||
header.magic = GST_CUDA_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = 0;
|
||||
|
||||
buf.resize (GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
memcpy (&buf[0], &header, GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
std::string
|
||||
gst_cuda_ipc_mem_handle_to_string (const CUipcMemHandle & handle)
|
||||
{
|
||||
std::string dump (68, '\0');
|
||||
guint val[16];
|
||||
for (guint i = 0; i < 16; i++) {
|
||||
val[i] = *((guint *) (handle.reserved + i * 4));
|
||||
}
|
||||
|
||||
sprintf (&dump[0], "%x%x%x%x-%x%x%x%x-%x%x%x%x-%x%x%x%x", val[0],
|
||||
val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8],
|
||||
val[9], val[10], val[11], val[12], val[13], val[14], val[15]);
|
||||
|
||||
return dump;
|
||||
}
|
||||
|
||||
bool
|
||||
gst_cuda_ipc_clock_is_system (GstClock * clock)
|
||||
{
|
||||
GstClockType clock_type = GST_CLOCK_TYPE_MONOTONIC;
|
||||
GstClock *mclock;
|
||||
|
||||
if (G_OBJECT_TYPE (clock) != GST_TYPE_SYSTEM_CLOCK)
|
||||
return false;
|
||||
|
||||
g_object_get (clock, "clock-type", &clock_type, nullptr);
|
||||
if (clock_type != GST_CLOCK_TYPE_MONOTONIC)
|
||||
return false;
|
||||
|
||||
mclock = gst_clock_get_master (clock);
|
||||
if (!mclock)
|
||||
return true;
|
||||
|
||||
gst_object_unref (mclock);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
/* *INDENT-OFF* */
|
||||
static inline void rtrim(std::string &s) {
|
||||
s.erase (std::find_if (s.rbegin(), s.rend(),
|
||||
[](unsigned char ch) {
|
||||
return !std::isspace (ch);
|
||||
}).base (), s.end ());
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
std::string
|
||||
gst_cuda_ipc_win32_error_to_string (guint err)
|
||||
{
|
||||
wchar_t buffer[1024];
|
||||
|
||||
if (!FormatMessageW (FORMAT_MESSAGE_IGNORE_INSERTS |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, 0, buffer, 1024, nullptr)) {
|
||||
return std::string ("");
|
||||
}
|
||||
|
||||
std::wstring_convert < std::codecvt_utf8 < wchar_t >, wchar_t >converter;
|
||||
std::string ret = converter.to_bytes (buffer);
|
||||
rtrim (ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
std::string
|
||||
gst_cuda_ipc_win32_error_to_string (guint err)
|
||||
{
|
||||
return std::string ("");
|
||||
}
|
||||
#endif /* G_OS_WIN32 */
|
||||
|
||||
bool
|
||||
gst_cuda_ipc_handle_is_equal (const CUipcMemHandle & handle,
|
||||
const CUipcMemHandle & other)
|
||||
{
|
||||
if (memcmp (&handle, &other, sizeof (CUipcMemHandle)) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
184
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipc.h
Normal file
184
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipc.h
Normal file
|
@ -0,0 +1,184 @@
|
|||
/* 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>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#ifdef G_OS_WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Communication Sequence
|
||||
*
|
||||
* +--------+ +--------+
|
||||
* | client | | server |
|
||||
* +--------+ +--------+
|
||||
* | |
|
||||
* | |
|
||||
* +<---------- CONFIG ------------|
|
||||
* | |
|
||||
* +--------- NEED-DATA ---------->|
|
||||
* | +-------+
|
||||
* | | Export
|
||||
* | | CUDA memory
|
||||
* | +<------+
|
||||
* +<-------- HAVE-DATA -----------|
|
||||
* +--------+ |
|
||||
* Import | |
|
||||
* CUDA memory | |
|
||||
* +------->+ |
|
||||
* |--------- READ-DONE ---------->|
|
||||
* +--------+ |
|
||||
* Release | |
|
||||
* CUDA memory | |
|
||||
* +--------| |
|
||||
* |-------- RELEASE-DATA -------->|
|
||||
* | |
|
||||
* +--------- NEED-DATA ---------->|
|
||||
* | |
|
||||
* |<----------- EOS --------------|
|
||||
* +--------+ |
|
||||
* Cleanup all | |
|
||||
* shared resources | |
|
||||
* +--------| |
|
||||
* |------------ FIN ------------->|
|
||||
*/
|
||||
|
||||
enum class GstCudaIpcPktType : guint8
|
||||
{
|
||||
UNKNOWN,
|
||||
CONFIG,
|
||||
NEED_DATA,
|
||||
HAVE_DATA,
|
||||
READ_DONE,
|
||||
RELEASE_DATA,
|
||||
HAVE_MMAP_DATA,
|
||||
RELEASE_MMAP_DATA,
|
||||
EOS,
|
||||
FIN,
|
||||
};
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
typedef HANDLE GstCudaSharableHandle;
|
||||
typedef DWORD GstCudaPid;
|
||||
#define GST_CUDA_OS_HANDLE_FORMAT "p"
|
||||
#else
|
||||
typedef int GstCudaSharableHandle;
|
||||
typedef pid_t GstCudaPid;
|
||||
#define GST_CUDA_OS_HANDLE_FORMAT "d"
|
||||
struct OVERLAPPED
|
||||
{
|
||||
gpointer dummy;
|
||||
};
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct GstCudaIpcPacketHeader
|
||||
{
|
||||
GstCudaIpcPktType type;
|
||||
guint32 payload_size;
|
||||
guint32 magic;
|
||||
};
|
||||
|
||||
struct GstCudaIpcMemLayout
|
||||
{
|
||||
guint32 size;
|
||||
guint32 max_size;
|
||||
guint32 pitch;
|
||||
guint32 offset[4];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
constexpr guint GST_CUDA_IPC_PKT_HEADER_SIZE = sizeof (GstCudaIpcPacketHeader);
|
||||
|
||||
bool gst_cuda_ipc_pkt_identify (std::vector<guint8> & buf,
|
||||
GstCudaIpcPacketHeader & header);
|
||||
|
||||
bool gst_cuda_ipc_pkt_build_config (std::vector<guint8> & buf,
|
||||
GstCudaPid pid,
|
||||
gboolean use_mmap,
|
||||
GstCaps * caps);
|
||||
|
||||
bool gst_cuda_ipc_pkt_parse_config (std::vector<guint8> & buf,
|
||||
GstCudaPid * pid,
|
||||
gboolean * use_mmap,
|
||||
GstCaps ** caps);
|
||||
|
||||
void gst_cuda_ipc_pkt_build_need_data (std::vector<guint8> & buf);
|
||||
|
||||
bool gst_cuda_ipc_pkt_build_have_data (std::vector<guint8> & buf,
|
||||
GstClockTime pts,
|
||||
const GstVideoInfo & info,
|
||||
const CUipcMemHandle & handle,
|
||||
GstCaps * caps);
|
||||
|
||||
bool gst_cuda_ipc_pkt_parse_have_data (const std::vector<guint8> & buf,
|
||||
GstClockTime & pts,
|
||||
GstCudaIpcMemLayout & layout,
|
||||
CUipcMemHandle & handle,
|
||||
GstCaps ** caps);
|
||||
|
||||
bool gst_cuda_ipc_pkt_build_have_mmap_data (std::vector<guint8> & buf,
|
||||
GstClockTime pts,
|
||||
const GstVideoInfo & info,
|
||||
guint32 max_size,
|
||||
GstCudaSharableHandle handle,
|
||||
GstCaps * caps);
|
||||
|
||||
bool gst_cuda_ipc_pkt_parse_have_mmap_data (const std::vector<guint8> & buf,
|
||||
GstClockTime & pts,
|
||||
GstCudaIpcMemLayout & layout,
|
||||
GstCudaSharableHandle * handle,
|
||||
GstCaps ** caps);
|
||||
|
||||
void gst_cuda_ipc_pkt_build_read_done (std::vector<guint8> & buf);
|
||||
|
||||
void gst_cuda_ipc_pkt_build_release_data (std::vector<guint8> & buf,
|
||||
const CUipcMemHandle & handle);
|
||||
|
||||
bool gst_cuda_ipc_pkt_parse_release_data (std::vector<guint8> & buf,
|
||||
CUipcMemHandle & handle);
|
||||
|
||||
void gst_cuda_ipc_pkt_build_release_mmap_data (std::vector<guint8> & buf,
|
||||
GstCudaSharableHandle handle);
|
||||
|
||||
bool gst_cuda_ipc_pkt_parse_release_mmap_data (std::vector<guint8> & buf,
|
||||
GstCudaSharableHandle * handle);
|
||||
|
||||
|
||||
void gst_cuda_ipc_pkt_build_eos (std::vector<guint8> & buf);
|
||||
|
||||
void gst_cuda_ipc_pkt_build_fin (std::vector<guint8> & buf);
|
||||
|
||||
std::string gst_cuda_ipc_mem_handle_to_string (const CUipcMemHandle & handle);
|
||||
|
||||
bool gst_cuda_ipc_clock_is_system (GstClock * clock);
|
||||
|
||||
std::string gst_cuda_ipc_win32_error_to_string (guint err);
|
||||
|
||||
bool gst_cuda_ipc_handle_is_equal (const CUipcMemHandle & handle,
|
||||
const CUipcMemHandle & other);
|
1173
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcclient.cpp
Normal file
1173
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcclient.cpp
Normal file
File diff suppressed because it is too large
Load diff
144
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcclient.h
Normal file
144
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcclient.h
Normal file
|
@ -0,0 +1,144 @@
|
|||
/* 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/cuda/gstcuda.h>
|
||||
#include "gstcudaipc.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_CLIENT (gst_cuda_ipc_client_get_type())
|
||||
#define GST_CUDA_IPC_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CUDA_IPC_CLIENT,GstCudaIpcClient))
|
||||
#define GST_CUDA_IPC_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CUDA_IPC_CLIENT,GstCudaIpcClientClass))
|
||||
#define GST_CUDA_IPC_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_CUDA_IPC_CLIENT,GstCudaIpcClientClass))
|
||||
#define GST_IS_CUDA_IPC_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CUDA_IPC_CLIENT))
|
||||
#define GST_IS_CUDA_IPC_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CUDA_IPC_CLIENT))
|
||||
|
||||
struct GstCudaIpcClientConn;
|
||||
struct GstCudaIpcClientPrivate;
|
||||
|
||||
enum GstCudaIpcIOMode
|
||||
{
|
||||
GST_CUDA_IPC_IO_COPY,
|
||||
GST_CUDA_IPC_IO_IMPORT,
|
||||
};
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_IO_MODE (gst_cuda_ipc_io_mode_get_type ())
|
||||
GType gst_cuda_ipc_io_mode_get_type (void);
|
||||
|
||||
struct GstCudaIpcClient
|
||||
{
|
||||
GstObject parent;
|
||||
|
||||
GstCudaContext *context;
|
||||
GstCudaStream *stream;
|
||||
GstCudaIpcIOMode io_mode;
|
||||
guint buffer_size;
|
||||
|
||||
GstCudaIpcClientPrivate *priv;
|
||||
};
|
||||
|
||||
struct GstCudaIpcClientClass
|
||||
{
|
||||
GstObjectClass parent_class;
|
||||
|
||||
bool (*send_msg) (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn);
|
||||
|
||||
bool (*wait_msg) (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn);
|
||||
|
||||
void (*terminate) (GstCudaIpcClient * client);
|
||||
|
||||
void (*invoke) (GstCudaIpcClient * client);
|
||||
|
||||
void (*set_flushing) (GstCudaIpcClient * client,
|
||||
bool flushing);
|
||||
|
||||
void (*loop) (GstCudaIpcClient * client);
|
||||
|
||||
bool (*config) (GstCudaIpcClient * client,
|
||||
GstCudaPid pid,
|
||||
gboolean use_mmap);
|
||||
};
|
||||
|
||||
GType gst_cuda_ipc_client_get_type (void);
|
||||
|
||||
GstFlowReturn gst_cuda_ipc_client_get_sample (GstCudaIpcClient * client,
|
||||
GstSample ** sample);
|
||||
|
||||
void gst_cuda_ipc_client_set_flushing (GstCudaIpcClient * client,
|
||||
bool flushing);
|
||||
|
||||
GstCaps * gst_cuda_ipc_client_get_caps (GstCudaIpcClient * client);
|
||||
|
||||
GstFlowReturn gst_cuda_ipc_client_run (GstCudaIpcClient * client);
|
||||
|
||||
void gst_cuda_ipc_client_stop (GstCudaIpcClient * client);
|
||||
|
||||
/* subclass methods */
|
||||
void gst_cuda_ipc_client_new_connection (GstCudaIpcClient * client,
|
||||
std::shared_ptr<GstCudaIpcClientConn> conn);
|
||||
|
||||
void gst_cuda_ipc_client_send_msg_finish (GstCudaIpcClient * client,
|
||||
bool result);
|
||||
|
||||
void gst_cuda_ipc_client_wait_msg_finish (GstCudaIpcClient * client,
|
||||
bool result);
|
||||
|
||||
void gst_cuda_ipc_client_have_mmap_data (GstCudaIpcClient * client,
|
||||
GstClockTime pts,
|
||||
const GstCudaIpcMemLayout & layout,
|
||||
GstCaps * caps,
|
||||
GstCudaSharableHandle server_handle,
|
||||
GstCudaSharableHandle client_handle);
|
||||
|
||||
void gst_cuda_ipc_client_on_idle (GstCudaIpcClient * client);
|
||||
|
||||
void gst_cuda_ipc_client_abort (GstCudaIpcClient * client);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstCudaIpcClient, gst_object_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
struct GstCudaIpcClientConn : public OVERLAPPED
|
||||
{
|
||||
GstCudaIpcClientConn ()
|
||||
{
|
||||
client_msg.resize (GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
server_msg.resize (GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
virtual ~GstCudaIpcClientConn()
|
||||
{
|
||||
gst_clear_object (&context);
|
||||
}
|
||||
|
||||
GstCudaIpcClient *client;
|
||||
|
||||
GstCudaContext *context = nullptr;
|
||||
|
||||
GstCudaIpcPktType type;
|
||||
std::vector<guint8> client_msg;
|
||||
std::vector<guint8> server_msg;
|
||||
};
|
|
@ -0,0 +1,432 @@
|
|||
/* 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 "gstcudaipcclient_unix.h"
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
|
||||
#include <gio/gunixsocketaddress.h>
|
||||
#include <gio/gunixconnection.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (cuda_ipc_client_debug);
|
||||
#define GST_CAT_DEFAULT cuda_ipc_client_debug
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
struct GstCudaIpcClientConnUnix : public GstCudaIpcClientConn
|
||||
{
|
||||
GstCudaIpcClientConnUnix (GSocketConnection * socket_conn,
|
||||
GCancellable * cancel)
|
||||
{
|
||||
conn = socket_conn;
|
||||
cancellable = (GCancellable *) g_object_ref (cancel);
|
||||
istream = g_io_stream_get_input_stream (G_IO_STREAM (conn));
|
||||
ostream = g_io_stream_get_output_stream (G_IO_STREAM (conn));
|
||||
}
|
||||
|
||||
~GstCudaIpcClientConnUnix ()
|
||||
{
|
||||
g_cancellable_cancel (cancellable);
|
||||
g_object_unref (conn);
|
||||
g_object_unref (cancellable);
|
||||
}
|
||||
|
||||
GSocketConnection *conn;
|
||||
GInputStream *istream;
|
||||
GOutputStream *ostream;
|
||||
GCancellable *cancellable;
|
||||
};
|
||||
|
||||
struct GstCudaIpcClientUnixPrivate
|
||||
{
|
||||
GstCudaIpcClientUnixPrivate ()
|
||||
{
|
||||
main_context = g_main_context_new ();
|
||||
main_loop = g_main_loop_new (main_context, FALSE);
|
||||
cancellable = g_cancellable_new ();
|
||||
}
|
||||
|
||||
~GstCudaIpcClientUnixPrivate ()
|
||||
{
|
||||
g_main_loop_unref (main_loop);
|
||||
g_main_context_unref (main_context);
|
||||
}
|
||||
|
||||
std::string address;
|
||||
GstClockTime timeout;
|
||||
std::mutex lock;
|
||||
std::condition_variable cond;
|
||||
|
||||
GMainLoop *main_loop;
|
||||
GMainContext *main_context;
|
||||
GCancellable *cancellable;
|
||||
bool flushing = false;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
struct _GstCudaIpcClientUnix
|
||||
{
|
||||
GstCudaIpcClient parent;
|
||||
|
||||
GstCudaIpcClientUnixPrivate *priv;
|
||||
};
|
||||
|
||||
static void gst_cuda_ipc_client_unix_finalize (GObject * object);
|
||||
static bool gst_cuda_ipc_client_unix_send_msg (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn);
|
||||
static bool gst_cuda_ipc_client_unix_wait_msg (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn);
|
||||
static void gst_cuda_ipc_client_unix_terminate (GstCudaIpcClient * client);
|
||||
static void gst_cuda_ipc_client_unix_invoke (GstCudaIpcClient * client);
|
||||
static void gst_cuda_ipc_client_unix_set_flushing (GstCudaIpcClient * client,
|
||||
bool flushing);
|
||||
static void gst_cuda_ipc_client_unix_loop (GstCudaIpcClient * client);
|
||||
|
||||
#define gst_cuda_ipc_client_unix_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstCudaIpcClientUnix,
|
||||
gst_cuda_ipc_client_unix, GST_TYPE_CUDA_IPC_CLIENT);
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_class_init (GstCudaIpcClientUnixClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GstCudaIpcClientClass *client_class = GST_CUDA_IPC_CLIENT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_cuda_ipc_client_unix_finalize;
|
||||
|
||||
client_class->send_msg =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_unix_send_msg);
|
||||
client_class->wait_msg =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_unix_wait_msg);
|
||||
client_class->terminate =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_unix_terminate);
|
||||
client_class->invoke = GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_unix_invoke);
|
||||
client_class->set_flushing =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_unix_set_flushing);
|
||||
client_class->loop = GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_unix_loop);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_init (GstCudaIpcClientUnix * self)
|
||||
{
|
||||
self->priv = new GstCudaIpcClientUnixPrivate ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_finalize (GObject * object)
|
||||
{
|
||||
GstCudaIpcClientUnix *self = GST_CUDA_IPC_CLIENT_UNIX (object);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "finalize");
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_send_msg_finish (GObject * source,
|
||||
GAsyncResult * result, GstCudaIpcClientConnUnix * conn)
|
||||
{
|
||||
GstCudaIpcClient *client = conn->client;
|
||||
gsize size;
|
||||
GError *err = nullptr;
|
||||
bool ret = true;
|
||||
|
||||
if (!g_output_stream_write_all_finish (conn->ostream, result, &size, &err)) {
|
||||
GST_WARNING_OBJECT (client, "Write failed with %s", err->message);
|
||||
g_clear_error (&err);
|
||||
ret = false;
|
||||
}
|
||||
|
||||
gst_cuda_ipc_client_send_msg_finish (client, ret);
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_client_unix_send_msg (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn)
|
||||
{
|
||||
GstCudaIpcClientConnUnix *unix_conn =
|
||||
static_cast < GstCudaIpcClientConnUnix * >(conn);
|
||||
|
||||
g_output_stream_write_all_async (unix_conn->ostream, &conn->client_msg[0],
|
||||
conn->client_msg.size (), G_PRIORITY_DEFAULT, unix_conn->cancellable,
|
||||
(GAsyncReadyCallback) gst_cuda_ipc_client_unix_send_msg_finish,
|
||||
unix_conn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_finish_have_mmap_data (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConnUnix * conn)
|
||||
{
|
||||
GstClockTime pts;
|
||||
GstCudaIpcMemLayout layout;
|
||||
GstCudaSharableHandle server_handle = 0;
|
||||
GstCudaSharableHandle client_handle = 0;
|
||||
GstCaps *caps = nullptr;
|
||||
GError *err = nullptr;
|
||||
|
||||
if (!gst_cuda_ipc_pkt_parse_have_mmap_data (conn->server_msg, pts,
|
||||
layout, &server_handle, &caps)) {
|
||||
GST_ERROR_OBJECT (client, "Couldn't parse MMAP-DATA");
|
||||
goto error;
|
||||
}
|
||||
|
||||
client_handle =
|
||||
g_unix_connection_receive_fd (G_UNIX_CONNECTION (conn->conn),
|
||||
conn->cancellable, &err);
|
||||
if (err) {
|
||||
GST_ERROR_OBJECT (client, "Couldn't get fd, %s", err->message);
|
||||
goto error;
|
||||
}
|
||||
|
||||
gst_cuda_ipc_client_have_mmap_data (client, pts, layout, caps,
|
||||
server_handle, client_handle);
|
||||
return;
|
||||
|
||||
error:
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, false);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_payload_finish (GObject * source,
|
||||
GAsyncResult * result, GstCudaIpcClientConnUnix * conn)
|
||||
{
|
||||
GstCudaIpcClient *client = conn->client;
|
||||
gsize size;
|
||||
GError *err = nullptr;
|
||||
bool ret = true;
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
if (!g_input_stream_read_all_finish (conn->istream, result, &size, &err)) {
|
||||
GST_WARNING_OBJECT (client, "Read failed with %s", err->message);
|
||||
g_clear_error (&err);
|
||||
ret = false;
|
||||
} else if (!gst_cuda_ipc_pkt_identify (conn->server_msg, header)) {
|
||||
GST_ERROR_OBJECT (client, "Broken header");
|
||||
ret = false;
|
||||
} else if (header.type == GstCudaIpcPktType::HAVE_MMAP_DATA) {
|
||||
gst_cuda_ipc_client_unix_finish_have_mmap_data (client, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, ret);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_wait_finish (GObject * source,
|
||||
GAsyncResult * result, GstCudaIpcClientConnUnix * conn)
|
||||
{
|
||||
GstCudaIpcClient *client = conn->client;
|
||||
gsize size;
|
||||
GError *err = nullptr;
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
if (!g_input_stream_read_all_finish (conn->istream, result, &size, &err)) {
|
||||
GST_WARNING_OBJECT (client, "Read failed with %s", err->message);
|
||||
g_clear_error (&err);
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gst_cuda_ipc_pkt_identify (conn->server_msg, header)) {
|
||||
GST_ERROR_OBJECT (client, "Broken header");
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.payload_size == 0) {
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, true);
|
||||
return;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (client, "Reading payload");
|
||||
|
||||
g_input_stream_read_all_async (conn->istream,
|
||||
&conn->server_msg[0] + GST_CUDA_IPC_PKT_HEADER_SIZE, header.payload_size,
|
||||
G_PRIORITY_DEFAULT, conn->cancellable,
|
||||
(GAsyncReadyCallback) gst_cuda_ipc_client_unix_payload_finish, conn);
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_client_unix_wait_msg (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn)
|
||||
{
|
||||
GstCudaIpcClientConnUnix *unix_conn =
|
||||
static_cast < GstCudaIpcClientConnUnix * >(conn);
|
||||
|
||||
g_input_stream_read_all_async (unix_conn->istream,
|
||||
&conn->server_msg[0], GST_CUDA_IPC_PKT_HEADER_SIZE,
|
||||
G_PRIORITY_DEFAULT, unix_conn->cancellable,
|
||||
(GAsyncReadyCallback) gst_cuda_ipc_client_unix_wait_finish, unix_conn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_terminate (GstCudaIpcClient * client)
|
||||
{
|
||||
GstCudaIpcClientUnix *self = GST_CUDA_IPC_CLIENT_UNIX (client);
|
||||
GstCudaIpcClientUnixPrivate *priv = self->priv;
|
||||
|
||||
g_main_loop_quit (priv->main_loop);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_client_unix_invoke_func (GstCudaIpcClient * client)
|
||||
{
|
||||
gst_cuda_ipc_client_on_idle (client);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_invoke (GstCudaIpcClient * client)
|
||||
{
|
||||
GstCudaIpcClientUnix *self = GST_CUDA_IPC_CLIENT_UNIX (client);
|
||||
GstCudaIpcClientUnixPrivate *priv = self->priv;
|
||||
|
||||
g_main_context_invoke (priv->main_context,
|
||||
(GSourceFunc) gst_cuda_ipc_client_unix_invoke_func, client);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_set_flushing (GstCudaIpcClient * client, bool flushing)
|
||||
{
|
||||
GstCudaIpcClientUnix *self = GST_CUDA_IPC_CLIENT_UNIX (client);
|
||||
GstCudaIpcClientUnixPrivate *priv = self->priv;
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
priv->flushing = flushing;
|
||||
priv->cond.notify_all ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_unix_loop (GstCudaIpcClient * client)
|
||||
{
|
||||
GstCudaIpcClientUnix *self = GST_CUDA_IPC_CLIENT_UNIX (client);
|
||||
GstCudaIpcClientUnixPrivate *priv = self->priv;
|
||||
GSocketConnection *socket_conn;
|
||||
GSocketClient *socket_client;
|
||||
GSocketAddress *addr;
|
||||
GError *err = nullptr;
|
||||
GstClockTime start_time = gst_util_get_timestamp ();
|
||||
|
||||
g_main_context_push_thread_default (priv->main_context);
|
||||
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
|
||||
socket_client = g_socket_client_new ();
|
||||
addr = g_unix_socket_address_new (priv->address.c_str ());
|
||||
|
||||
do {
|
||||
GstClockTime diff;
|
||||
|
||||
if (priv->flushing) {
|
||||
GST_DEBUG_OBJECT (self, "We are flushing");
|
||||
gst_cuda_ipc_client_abort (client);
|
||||
return;
|
||||
}
|
||||
|
||||
socket_conn = g_socket_client_connect (socket_client,
|
||||
G_SOCKET_CONNECTABLE (addr), priv->cancellable, &err);
|
||||
if (socket_conn)
|
||||
break;
|
||||
|
||||
if (err->code == G_IO_ERROR_CANCELLED) {
|
||||
GST_DEBUG_OBJECT (self, "Operation cancelled");
|
||||
g_clear_error (&err);
|
||||
break;
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (self, "Connection failed with error %s", err->message);
|
||||
g_clear_error (&err);
|
||||
}
|
||||
|
||||
if (priv->timeout > 0) {
|
||||
diff = gst_util_get_timestamp () - start_time;
|
||||
if (diff > priv->timeout) {
|
||||
GST_WARNING_OBJECT (self, "Timeout");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Sleep for next retry");
|
||||
priv->cond.wait_for (lk, std::chrono::milliseconds (100));
|
||||
} while (true);
|
||||
lk.unlock ();
|
||||
|
||||
g_object_unref (socket_client);
|
||||
g_object_unref (addr);
|
||||
|
||||
if (socket_conn) {
|
||||
GST_DEBUG_OBJECT (self, "Connection established");
|
||||
|
||||
auto conn = std::make_shared < GstCudaIpcClientConnUnix > (socket_conn,
|
||||
priv->cancellable);
|
||||
gst_cuda_ipc_client_new_connection (client, conn);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (self, "Connection failed");
|
||||
gst_cuda_ipc_client_abort (client);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Starting loop");
|
||||
|
||||
g_main_loop_run (priv->main_loop);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Exit loop");
|
||||
|
||||
g_cancellable_cancel (priv->cancellable);
|
||||
|
||||
g_main_context_pop_thread_default (priv->main_context);
|
||||
}
|
||||
|
||||
GstCudaIpcClient *
|
||||
gst_cuda_ipc_client_new (const gchar * address, GstCudaContext * context,
|
||||
GstCudaStream * stream, GstCudaIpcIOMode io_mode, guint timeout,
|
||||
guint buffer_size)
|
||||
{
|
||||
GstCudaIpcClient *client;
|
||||
GstCudaIpcClientUnix *self;
|
||||
|
||||
g_return_val_if_fail (address, nullptr);
|
||||
g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), nullptr);
|
||||
|
||||
self = (GstCudaIpcClientUnix *)
|
||||
g_object_new (GST_TYPE_CUDA_IPC_CLIENT_UNIX, nullptr);
|
||||
gst_object_ref_sink (self);
|
||||
|
||||
self->priv->address = address;
|
||||
self->priv->timeout = timeout * GST_SECOND;
|
||||
|
||||
client = GST_CUDA_IPC_CLIENT (self);
|
||||
client->context = (GstCudaContext *) gst_object_ref (context);
|
||||
if (stream)
|
||||
client->stream = gst_cuda_stream_ref (stream);
|
||||
client->io_mode = io_mode;
|
||||
client->buffer_size = buffer_size;
|
||||
|
||||
return client;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* 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 "gstcudaipcclient.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_CLIENT_UNIX (gst_cuda_ipc_client_unix_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstCudaIpcClientUnix, gst_cuda_ipc_client_unix,
|
||||
GST, CUDA_IPC_CLIENT_UNIX, GstCudaIpcClient);
|
||||
|
||||
GstCudaIpcClient * gst_cuda_ipc_client_new (const gchar * address,
|
||||
GstCudaContext * context,
|
||||
GstCudaStream * stream,
|
||||
GstCudaIpcIOMode io_mode,
|
||||
guint timeout,
|
||||
guint buffer_size);
|
||||
|
||||
G_END_DECLS
|
|
@ -0,0 +1,481 @@
|
|||
/* 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 "gstcudaipcclient_win32.h"
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (cuda_ipc_client_debug);
|
||||
#define GST_CAT_DEFAULT cuda_ipc_client_debug
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
struct GstCudaIpcClientConnWin32 : public GstCudaIpcClientConn
|
||||
{
|
||||
GstCudaIpcClientConnWin32 (HANDLE pipe) : pipe (pipe)
|
||||
{
|
||||
OVERLAPPED *parent = static_cast<OVERLAPPED *> (this);
|
||||
parent->Internal = 0;
|
||||
parent->InternalHigh = 0;
|
||||
parent->Offset = 0;
|
||||
parent->OffsetHigh = 0;
|
||||
}
|
||||
|
||||
~GstCudaIpcClientConnWin32 ()
|
||||
{
|
||||
if (pipe != INVALID_HANDLE_VALUE) {
|
||||
CancelIo (pipe);
|
||||
CloseHandle (pipe);
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE pipe;
|
||||
};
|
||||
|
||||
struct GstCudaIpcClientWin32Private
|
||||
{
|
||||
GstCudaIpcClientWin32Private ()
|
||||
{
|
||||
wakeup_event = CreateEvent (nullptr, FALSE, FALSE, nullptr);
|
||||
cancellable = CreateEvent (nullptr, TRUE, FALSE, nullptr);
|
||||
}
|
||||
|
||||
~GstCudaIpcClientWin32Private ()
|
||||
{
|
||||
CloseHandle (wakeup_event);
|
||||
CloseHandle (cancellable);
|
||||
if (server_process)
|
||||
CloseHandle (server_process);
|
||||
}
|
||||
|
||||
std::string address;
|
||||
GstClockTime timeout;
|
||||
std::mutex lock;
|
||||
std::condition_variable cond;
|
||||
|
||||
HANDLE wakeup_event;
|
||||
HANDLE cancellable;
|
||||
HANDLE server_process = nullptr;
|
||||
guint last_err = ERROR_SUCCESS;
|
||||
bool flushing = false;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
struct _GstCudaIpcClientWin32
|
||||
{
|
||||
GstCudaIpcClient parent;
|
||||
|
||||
GstCudaIpcClientWin32Private *priv;
|
||||
};
|
||||
|
||||
static void gst_cuda_ipc_client_win32_finalize (GObject * object);
|
||||
static bool gst_cuda_ipc_client_win32_send_msg (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn);
|
||||
static bool gst_cuda_ipc_client_win32_wait_msg (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn);
|
||||
static void gst_cuda_ipc_client_win32_terminate (GstCudaIpcClient * client);
|
||||
static void gst_cuda_ipc_client_win32_invoke (GstCudaIpcClient * client);
|
||||
static void gst_cuda_ipc_client_win32_set_flushing (GstCudaIpcClient * client,
|
||||
bool flushing);
|
||||
static void gst_cuda_ipc_client_win32_loop (GstCudaIpcClient * client);
|
||||
static bool gst_cuda_ipc_client_win32_config (GstCudaIpcClient * client,
|
||||
GstCudaPid pid, gboolean use_mmap);
|
||||
|
||||
#define gst_cuda_ipc_client_win32_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstCudaIpcClientWin32,
|
||||
gst_cuda_ipc_client_win32, GST_TYPE_CUDA_IPC_CLIENT);
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_win32_class_init (GstCudaIpcClientWin32Class * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GstCudaIpcClientClass *client_class = GST_CUDA_IPC_CLIENT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_cuda_ipc_client_win32_finalize;
|
||||
|
||||
client_class->send_msg =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_win32_send_msg);
|
||||
client_class->wait_msg =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_win32_wait_msg);
|
||||
client_class->terminate =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_win32_terminate);
|
||||
client_class->invoke = GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_win32_invoke);
|
||||
client_class->set_flushing =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_win32_set_flushing);
|
||||
client_class->loop = GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_win32_loop);
|
||||
client_class->config = GST_DEBUG_FUNCPTR (gst_cuda_ipc_client_win32_config);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_win32_init (GstCudaIpcClientWin32 * self)
|
||||
{
|
||||
self->priv = new GstCudaIpcClientWin32Private ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_win32_finalize (GObject * object)
|
||||
{
|
||||
GstCudaIpcClientWin32 *self = GST_CUDA_IPC_CLIENT_WIN32 (object);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "finalize");
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
gst_cuda_ipc_client_win32_send_msg_finish (DWORD error_code, DWORD size,
|
||||
OVERLAPPED * overlap)
|
||||
{
|
||||
GstCudaIpcClientConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcClientConnWin32 * >(overlap);
|
||||
GstCudaIpcClient *client = win32_conn->client;
|
||||
bool ret = true;
|
||||
|
||||
if (error_code != ERROR_SUCCESS) {
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (error_code);
|
||||
GST_WARNING_OBJECT (client, "WriteFileEx callback failed with 0x%x (%s)",
|
||||
(guint) error_code, err.c_str ());
|
||||
ret = false;
|
||||
}
|
||||
|
||||
gst_cuda_ipc_client_send_msg_finish (client, ret);
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_client_win32_send_msg (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn)
|
||||
{
|
||||
GstCudaIpcClientConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcClientConnWin32 * >(conn);
|
||||
|
||||
if (!WriteFileEx (win32_conn->pipe, &conn->client_msg[0],
|
||||
conn->client_msg.size (), win32_conn,
|
||||
gst_cuda_ipc_client_win32_send_msg_finish)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (client, "WriteFileEx failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_win32_finish_have_mmap_data (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConnWin32 * win32_conn)
|
||||
{
|
||||
GstCudaIpcClientWin32 *self = GST_CUDA_IPC_CLIENT_WIN32 (client);
|
||||
GstCudaIpcClientWin32Private *priv = self->priv;
|
||||
GstClockTime pts;
|
||||
GstCudaIpcMemLayout layout;
|
||||
GstCudaSharableHandle server_handle = nullptr;
|
||||
GstCudaSharableHandle client_handle = nullptr;
|
||||
GstCaps *caps = nullptr;
|
||||
|
||||
if (!priv->server_process) {
|
||||
GST_ERROR_OBJECT (self, "Server process handle is not available");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!gst_cuda_ipc_pkt_parse_have_mmap_data (win32_conn->server_msg, pts,
|
||||
layout, &server_handle, &caps)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't parse MMAP-DATA");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!DuplicateHandle (priv->server_process, server_handle,
|
||||
GetCurrentProcess (), &client_handle, 0, FALSE,
|
||||
DUPLICATE_SAME_ACCESS)) {
|
||||
DWORD error_code = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (error_code);
|
||||
GST_ERROR_OBJECT (self, "Couldn't duplicate handle, 0x%x (%s)",
|
||||
(guint) error_code, err.c_str ());
|
||||
goto error;
|
||||
}
|
||||
|
||||
gst_cuda_ipc_client_have_mmap_data (client, pts, layout, caps,
|
||||
server_handle, client_handle);
|
||||
return;
|
||||
|
||||
error:
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, false);
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
gst_cuda_ipc_client_win32_payload_finish (DWORD error_code, DWORD size,
|
||||
OVERLAPPED * overlap)
|
||||
{
|
||||
GstCudaIpcClientConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcClientConnWin32 * >(overlap);
|
||||
GstCudaIpcClient *client = win32_conn->client;
|
||||
GstCudaIpcPacketHeader header;
|
||||
bool ret = true;
|
||||
|
||||
if (error_code != ERROR_SUCCESS) {
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (error_code);
|
||||
GST_WARNING_OBJECT (client, "ReadFileEx callback failed with 0x%x (%s)",
|
||||
(guint) error_code, err.c_str ());
|
||||
ret = false;
|
||||
} else if (!gst_cuda_ipc_pkt_identify (win32_conn->server_msg, header)) {
|
||||
GST_ERROR_OBJECT (client, "Broken header");
|
||||
ret = false;
|
||||
} else if (header.type == GstCudaIpcPktType::HAVE_MMAP_DATA) {
|
||||
gst_cuda_ipc_client_win32_finish_have_mmap_data (client, win32_conn);
|
||||
return;
|
||||
}
|
||||
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, ret);
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
gst_cuda_ipc_client_win32_wait_msg_finish (DWORD error_code, DWORD size,
|
||||
OVERLAPPED * overlap)
|
||||
{
|
||||
GstCudaIpcClientConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcClientConnWin32 * >(overlap);
|
||||
GstCudaIpcClient *client = win32_conn->client;
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
if (error_code != ERROR_SUCCESS) {
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (error_code);
|
||||
GST_WARNING_OBJECT (client, "ReadFileEx callback failed with 0x%x (%s)",
|
||||
(guint) error_code, err.c_str ());
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gst_cuda_ipc_pkt_identify (win32_conn->server_msg, header)) {
|
||||
GST_ERROR_OBJECT (client, "Broken header");
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.payload_size == 0) {
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, true);
|
||||
return;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (client, "Reading payload");
|
||||
|
||||
if (!ReadFileEx (win32_conn->pipe, &win32_conn->server_msg[0] +
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE, header.payload_size, win32_conn,
|
||||
gst_cuda_ipc_client_win32_payload_finish)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (client, "ReadFileEx failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
gst_cuda_ipc_client_wait_msg_finish (client, false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_client_win32_wait_msg (GstCudaIpcClient * client,
|
||||
GstCudaIpcClientConn * conn)
|
||||
{
|
||||
GstCudaIpcClientConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcClientConnWin32 * >(conn);
|
||||
|
||||
if (!ReadFileEx (win32_conn->pipe, &conn->server_msg[0],
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE, win32_conn,
|
||||
gst_cuda_ipc_client_win32_wait_msg_finish)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (client, "ReadFileEx failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_win32_terminate (GstCudaIpcClient * client)
|
||||
{
|
||||
GstCudaIpcClientWin32 *self = GST_CUDA_IPC_CLIENT_WIN32 (client);
|
||||
GstCudaIpcClientWin32Private *priv = self->priv;
|
||||
|
||||
SetEvent (priv->cancellable);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_win32_invoke (GstCudaIpcClient * client)
|
||||
{
|
||||
GstCudaIpcClientWin32 *self = GST_CUDA_IPC_CLIENT_WIN32 (client);
|
||||
GstCudaIpcClientWin32Private *priv = self->priv;
|
||||
|
||||
SetEvent (priv->wakeup_event);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_win32_set_flushing (GstCudaIpcClient * client,
|
||||
bool flushing)
|
||||
{
|
||||
GstCudaIpcClientWin32 *self = GST_CUDA_IPC_CLIENT_WIN32 (client);
|
||||
GstCudaIpcClientWin32Private *priv = self->priv;
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
priv->flushing = flushing;
|
||||
priv->cond.notify_all ();
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_client_win32_config (GstCudaIpcClient * client, GstCudaPid pid,
|
||||
gboolean use_mmap)
|
||||
{
|
||||
GstCudaIpcClientWin32 *self = GST_CUDA_IPC_CLIENT_WIN32 (client);
|
||||
GstCudaIpcClientWin32Private *priv = self->priv;
|
||||
|
||||
if (!use_mmap)
|
||||
return TRUE;
|
||||
|
||||
if (priv->server_process) {
|
||||
GST_WARNING_OBJECT (self, "Have server process already");
|
||||
CloseHandle (priv->server_process);
|
||||
}
|
||||
|
||||
priv->server_process = OpenProcess (PROCESS_DUP_HANDLE, FALSE, pid);
|
||||
|
||||
if (priv->server_process)
|
||||
return true;
|
||||
|
||||
DWORD error_code = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (error_code);
|
||||
GST_ERROR_OBJECT (self, "Couldn't open server process, 0x%x (%s)",
|
||||
(guint) error_code, err.c_str ());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_client_win32_loop (GstCudaIpcClient * client)
|
||||
{
|
||||
GstCudaIpcClientWin32 *self = GST_CUDA_IPC_CLIENT_WIN32 (client);
|
||||
GstCudaIpcClientWin32Private *priv = self->priv;
|
||||
DWORD mode = PIPE_READMODE_MESSAGE;
|
||||
guint wait_ret;
|
||||
HANDLE pipe = INVALID_HANDLE_VALUE;
|
||||
GstClockTime start_time = gst_util_get_timestamp ();
|
||||
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
do {
|
||||
GstClockTime diff;
|
||||
|
||||
if (priv->flushing) {
|
||||
GST_DEBUG_OBJECT (self, "We are flushing");
|
||||
gst_cuda_ipc_client_abort (client);
|
||||
return;
|
||||
}
|
||||
|
||||
pipe = CreateFileA (priv->address.c_str (),
|
||||
GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED, nullptr);
|
||||
if (pipe != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
|
||||
if (priv->timeout > 0) {
|
||||
diff = gst_util_get_timestamp () - start_time;
|
||||
if (diff > priv->timeout) {
|
||||
GST_WARNING_OBJECT (self, "Timeout");
|
||||
gst_cuda_ipc_client_abort (client);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Retry per 100ms */
|
||||
GST_DEBUG_OBJECT (self, "Sleep for next retry");
|
||||
priv->cond.wait_for (lk, std::chrono::milliseconds (100));
|
||||
} while (true);
|
||||
lk.unlock ();
|
||||
|
||||
g_assert (pipe != INVALID_HANDLE_VALUE);
|
||||
|
||||
if (!SetNamedPipeHandleState (pipe, &mode, nullptr, nullptr)) {
|
||||
priv->last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (priv->last_err);
|
||||
GST_WARNING_OBJECT (self, "SetNamedPipeHandleState failed with 0x%x (%s)",
|
||||
priv->last_err, err.c_str ());
|
||||
|
||||
CloseHandle (pipe);
|
||||
gst_cuda_ipc_client_abort (client);
|
||||
return;
|
||||
}
|
||||
|
||||
auto conn = std::make_shared < GstCudaIpcClientConnWin32 > (pipe);
|
||||
gst_cuda_ipc_client_new_connection (client, conn);
|
||||
|
||||
HANDLE waitables[] = { priv->cancellable, priv->wakeup_event };
|
||||
do {
|
||||
/* Enters alertable thread state and wait for I/O completion event
|
||||
* or cancellable event */
|
||||
wait_ret = WaitForMultipleObjectsEx (G_N_ELEMENTS (waitables), waitables,
|
||||
FALSE, INFINITE, TRUE);
|
||||
if (wait_ret == WAIT_OBJECT_0) {
|
||||
GST_DEBUG ("Operation cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (wait_ret) {
|
||||
case WAIT_IO_COMPLETION:
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
gst_cuda_ipc_client_on_idle (client);
|
||||
break;
|
||||
default:
|
||||
GST_WARNING ("Unexpected wait return 0x%x", wait_ret);
|
||||
gst_cuda_ipc_client_abort (client);
|
||||
return;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
GstCudaIpcClient *
|
||||
gst_cuda_ipc_client_new (const gchar * address, GstCudaContext * context,
|
||||
GstCudaStream * stream, GstCudaIpcIOMode io_mode, guint timeout,
|
||||
guint buffer_size)
|
||||
{
|
||||
GstCudaIpcClient *client;
|
||||
GstCudaIpcClientWin32 *self;
|
||||
|
||||
g_return_val_if_fail (address, nullptr);
|
||||
g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), nullptr);
|
||||
|
||||
self = (GstCudaIpcClientWin32 *)
|
||||
g_object_new (GST_TYPE_CUDA_IPC_CLIENT_WIN32, nullptr);
|
||||
gst_object_ref_sink (self);
|
||||
|
||||
self->priv->address = address;
|
||||
self->priv->timeout = timeout * GST_SECOND;
|
||||
|
||||
client = GST_CUDA_IPC_CLIENT (self);
|
||||
client->context = (GstCudaContext *) gst_object_ref (context);
|
||||
if (stream)
|
||||
client->stream = gst_cuda_stream_ref (stream);
|
||||
client->io_mode = io_mode;
|
||||
client->buffer_size = buffer_size;
|
||||
|
||||
return client;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* 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 "gstcudaipcclient.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_CLIENT_WIN32 (gst_cuda_ipc_client_win32_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstCudaIpcClientWin32, gst_cuda_ipc_client_win32,
|
||||
GST, CUDA_IPC_CLIENT_WIN32, GstCudaIpcClient);
|
||||
|
||||
GstCudaIpcClient * gst_cuda_ipc_client_new (const gchar * address,
|
||||
GstCudaContext * context,
|
||||
GstCudaStream * stream,
|
||||
GstCudaIpcIOMode io_mode,
|
||||
guint timeout,
|
||||
guint buffer_size);
|
||||
|
||||
G_END_DECLS
|
683
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcserver.cpp
Normal file
683
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcserver.cpp
Normal file
|
@ -0,0 +1,683 @@
|
|||
/* 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 "gstcudaipcserver.h"
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <gst/cuda/gstcuda-private.h>
|
||||
|
||||
GST_DEBUG_CATEGORY (cuda_ipc_server_debug);
|
||||
#define GST_CAT_DEFAULT cuda_ipc_server_debug
|
||||
|
||||
GType
|
||||
gst_cuda_ipc_mode_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
static const GEnumValue ipc_modes[] = {
|
||||
{GST_CUDA_IPC_LEGACY, "Legacy IPC mode", "legacy"},
|
||||
{GST_CUDA_IPC_MMAP, "Memory Map", "mmap"},
|
||||
{0, nullptr, nullptr}
|
||||
};
|
||||
|
||||
GST_CUDA_CALL_ONCE_BEGIN {
|
||||
type = g_enum_register_static ("GstCudaIpcMode", ipc_modes);
|
||||
} GST_CUDA_CALL_ONCE_END;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
struct GstCudaIpcServerPrivate
|
||||
{
|
||||
GstCudaIpcServerPrivate ()
|
||||
{
|
||||
shutdown = false;
|
||||
aborted = false;
|
||||
}
|
||||
|
||||
std::mutex lock;
|
||||
guint64 seq_num = 0;
|
||||
guint next_conn_id = 0;
|
||||
std::unordered_map < guint,
|
||||
std::shared_ptr < GstCudaIpcServerConn >> conn_map;
|
||||
GThread *loop_thread = nullptr;
|
||||
std::atomic < bool >shutdown;
|
||||
std::atomic < bool >aborted;
|
||||
std::shared_ptr < GstCudaIpcServerData > data;
|
||||
};
|
||||
|
||||
static void gst_cuda_ipc_server_dispose (GObject * object);
|
||||
static void gst_cuda_ipc_server_finalize (GObject * object);
|
||||
|
||||
#define gst_cuda_ipc_server_parent_class parent_class
|
||||
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstCudaIpcServer,
|
||||
gst_cuda_ipc_server, GST_TYPE_OBJECT,
|
||||
GST_DEBUG_CATEGORY_INIT (cuda_ipc_server_debug, "cudaipcserver",
|
||||
0, "cudaipcserver"));
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_class_init (GstCudaIpcServerClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_cuda_ipc_server_finalize;
|
||||
object_class->dispose = gst_cuda_ipc_server_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_init (GstCudaIpcServer * self)
|
||||
{
|
||||
self->priv = new GstCudaIpcServerPrivate ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_dispose (GObject * object)
|
||||
{
|
||||
GstCudaIpcServer *self = GST_CUDA_IPC_SERVER (object);
|
||||
GstCudaIpcServerPrivate *priv = self->priv;
|
||||
GstCudaIpcServerClass *klass = GST_CUDA_IPC_SERVER_GET_CLASS (self);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "dispose");
|
||||
|
||||
g_assert (klass->terminate);
|
||||
klass->terminate (self);
|
||||
|
||||
g_clear_pointer (&priv->loop_thread, g_thread_join);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_finalize (GObject * object)
|
||||
{
|
||||
GstCudaIpcServer *self = GST_CUDA_IPC_SERVER (object);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "finalize");
|
||||
|
||||
gst_clear_object (&self->context);
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
gst_cuda_ipc_server_send_data (GstCudaIpcServer * server, GstSample * sample,
|
||||
const GstVideoInfo & info, const CUipcMemHandle & handle, GstClockTime pts)
|
||||
{
|
||||
GstCudaIpcServerPrivate *priv;
|
||||
GstCudaIpcServerClass *klass;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CUDA_IPC_SERVER (server), GST_FLOW_ERROR);
|
||||
g_return_val_if_fail (GST_IS_SAMPLE (sample), GST_FLOW_ERROR);
|
||||
|
||||
if (server->ipc_mode != GST_CUDA_IPC_LEGACY) {
|
||||
GST_ERROR_OBJECT (server, "Invalid call");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
priv = server->priv;
|
||||
klass = GST_CUDA_IPC_SERVER_GET_CLASS (server);
|
||||
|
||||
GST_LOG_OBJECT (server, "Sending data");
|
||||
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
if (priv->aborted) {
|
||||
GST_DEBUG_OBJECT (server, "Was aborted");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
auto data = std::make_shared < GstCudaIpcServerData > ();
|
||||
data->sample = gst_sample_ref (sample);
|
||||
data->info = info;
|
||||
data->handle = handle;
|
||||
data->pts = pts;
|
||||
data->seq_num = priv->seq_num;
|
||||
|
||||
priv->seq_num++;
|
||||
priv->data = data;
|
||||
lk.unlock ();
|
||||
|
||||
klass->invoke (server);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
gst_cuda_ipc_server_send_mmap_data (GstCudaIpcServer * server,
|
||||
GstSample * sample, const GstVideoInfo & info, GstCudaSharableHandle handle,
|
||||
GstClockTime pts)
|
||||
{
|
||||
GstCudaIpcServerPrivate *priv;
|
||||
GstCudaIpcServerClass *klass;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CUDA_IPC_SERVER (server), GST_FLOW_ERROR);
|
||||
g_return_val_if_fail (GST_IS_SAMPLE (sample), GST_FLOW_ERROR);
|
||||
|
||||
if (server->ipc_mode != GST_CUDA_IPC_MMAP) {
|
||||
GST_ERROR_OBJECT (server, "Invalid call");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
priv = server->priv;
|
||||
klass = GST_CUDA_IPC_SERVER_GET_CLASS (server);
|
||||
|
||||
GST_LOG_OBJECT (server, "Sending data");
|
||||
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
if (priv->aborted) {
|
||||
GST_DEBUG_OBJECT (server, "Was aborted");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
auto data = std::make_shared < GstCudaIpcServerData > ();
|
||||
data->sample = gst_sample_ref (sample);
|
||||
data->info = info;
|
||||
data->os_handle = handle;
|
||||
data->pts = pts;
|
||||
data->seq_num = priv->seq_num;
|
||||
|
||||
priv->seq_num++;
|
||||
priv->data = data;
|
||||
lk.unlock ();
|
||||
|
||||
klass->invoke (server);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gst_cuda_ipc_server_loop_thread_func (GstCudaIpcServer * self)
|
||||
{
|
||||
GstCudaIpcServerPrivate *priv = self->priv;
|
||||
GstCudaIpcServerClass *klass = GST_CUDA_IPC_SERVER_GET_CLASS (self);
|
||||
|
||||
g_assert (klass->loop);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Start loop thread");
|
||||
|
||||
klass->loop (self);
|
||||
priv->conn_map.clear ();
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Exit loop thread");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_server_run (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_CUDA_IPC_SERVER (server));
|
||||
|
||||
priv = server->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (server, "Running");
|
||||
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
if (priv->loop_thread) {
|
||||
GST_DEBUG_OBJECT (server, "Already running");
|
||||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (server, "Spawning thread");
|
||||
priv->loop_thread = g_thread_new ("cuda-ipc-server",
|
||||
(GThreadFunc) gst_cuda_ipc_server_loop_thread_func, server);
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_server_stop (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerPrivate *priv;
|
||||
GstCudaIpcServerClass *klass;
|
||||
|
||||
g_return_if_fail (GST_IS_CUDA_IPC_SERVER (server));
|
||||
|
||||
priv = server->priv;
|
||||
klass = GST_CUDA_IPC_SERVER_GET_CLASS (server);
|
||||
|
||||
GST_DEBUG_OBJECT (server, "Stopping");
|
||||
priv->shutdown = true;
|
||||
klass->invoke (server);
|
||||
|
||||
g_clear_pointer (&priv->loop_thread, g_thread_join);
|
||||
|
||||
GST_DEBUG_OBJECT (server, "Stopped");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_close_connection (GstCudaIpcServer * self,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
GstCudaIpcServerPrivate *priv = self->priv;
|
||||
GstCudaIpcServerClass *klass = GST_CUDA_IPC_SERVER_GET_CLASS (self);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Closing conn-id %u", conn->id);
|
||||
|
||||
priv->conn_map.erase (conn->id);
|
||||
|
||||
if (priv->shutdown && priv->conn_map.empty ()) {
|
||||
GST_DEBUG_OBJECT (self, "All connection were closed");
|
||||
klass->terminate (self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_send_msg (GstCudaIpcServer * self,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
GstCudaIpcServerClass *klass = GST_CUDA_IPC_SERVER_GET_CLASS (self);
|
||||
|
||||
if (!klass->send_msg (self, conn)) {
|
||||
GST_WARNING_OBJECT (self, "Send msg failed");
|
||||
gst_cuda_ipc_server_close_connection (self, conn);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_config_data (GstCudaIpcServer * self,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
GstCaps *caps = gst_sample_get_caps (conn->data->sample);
|
||||
|
||||
gst_caps_replace (&conn->caps, caps);
|
||||
|
||||
gst_cuda_ipc_pkt_build_config (conn->server_msg, self->pid,
|
||||
self->ipc_mode == GST_CUDA_IPC_MMAP, conn->caps);
|
||||
conn->type = GstCudaIpcPktType::CONFIG;
|
||||
|
||||
GST_LOG_OBJECT (self, "Sending CONFIG, conn-id %u", conn->id);
|
||||
gst_cuda_ipc_server_send_msg (self, conn);
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_server_on_incoming_connection (GstCudaIpcServer * server,
|
||||
std::shared_ptr < GstCudaIpcServerConn > conn)
|
||||
{
|
||||
GstCudaIpcServerPrivate *priv = server->priv;
|
||||
|
||||
priv->lock.lock ();
|
||||
conn->server = server;
|
||||
conn->id = priv->next_conn_id;
|
||||
conn->context = (GstCudaContext *) gst_object_ref (server->context);
|
||||
conn->data = priv->data;
|
||||
priv->next_conn_id++;
|
||||
priv->lock.unlock ();
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
priv->conn_map.insert ({conn->id, conn});
|
||||
/* *INDENT-ON* */
|
||||
|
||||
if (conn->data) {
|
||||
conn->configured = true;
|
||||
gst_cuda_ipc_server_config_data (server, conn.get ());
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (server, "Have no config data yet, waiting for data");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_have_data (GstCudaIpcServer * self,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
GstCudaIpcServerClass *klass = GST_CUDA_IPC_SERVER_GET_CLASS (self);
|
||||
GstCaps *caps;
|
||||
GstBuffer *buffer;
|
||||
GstCudaMemory *cmem;
|
||||
|
||||
if (!conn->data) {
|
||||
GST_ERROR_OBJECT (self, "Have no data to send, conn-id: %u", conn->id);
|
||||
gst_cuda_ipc_server_close_connection (self, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
conn->pending_have_data = false;
|
||||
conn->seq_num = conn->data->seq_num + 1;
|
||||
|
||||
caps = gst_sample_get_caps (conn->data->sample);
|
||||
if (!conn->caps || !gst_caps_is_equal (conn->caps, caps)) {
|
||||
GST_DEBUG_OBJECT (self, "Sending caps %" GST_PTR_FORMAT " to conn-id %u",
|
||||
caps, conn->id);
|
||||
gst_caps_replace (&conn->caps, caps);
|
||||
} else {
|
||||
caps = nullptr;
|
||||
}
|
||||
|
||||
buffer = gst_sample_get_buffer (conn->data->sample);
|
||||
cmem = (GstCudaMemory *) gst_buffer_peek_memory (buffer, 0);
|
||||
|
||||
if (self->ipc_mode == GST_CUDA_IPC_LEGACY) {
|
||||
auto handle_dump = gst_cuda_ipc_mem_handle_to_string (conn->data->handle);
|
||||
GST_LOG_OBJECT (self, "Sending HAVE-DATA with handle %s, conn-id: %u",
|
||||
handle_dump.c_str (), conn->id);
|
||||
|
||||
if (!gst_cuda_ipc_pkt_build_have_data (conn->server_msg, conn->data->pts,
|
||||
conn->data->info, conn->data->handle, caps)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't build HAVE-DATA pkt, conn-id: %u",
|
||||
conn->id);
|
||||
gst_cuda_ipc_server_close_connection (self, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
conn->type = GstCudaIpcPktType::HAVE_DATA;
|
||||
} else {
|
||||
guint max_size = cmem->mem.maxsize;
|
||||
GST_LOG_OBJECT (self, "Sending HAVE-MMAP-DATA with handle %"
|
||||
GST_CUDA_OS_HANDLE_FORMAT ", conn-id: %u", conn->data->os_handle,
|
||||
conn->id);
|
||||
if (!gst_cuda_ipc_pkt_build_have_mmap_data (conn->server_msg,
|
||||
conn->data->pts, conn->data->info, max_size, conn->data->os_handle,
|
||||
caps)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't build HAVE-MMAP-DATA pkt, conn-id: %u",
|
||||
conn->id);
|
||||
gst_cuda_ipc_server_close_connection (self, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
conn->type = GstCudaIpcPktType::HAVE_MMAP_DATA;
|
||||
if (klass->send_mmap_msg) {
|
||||
if (!klass->send_mmap_msg (self, conn, conn->data->os_handle)) {
|
||||
GST_WARNING_OBJECT (self, "Send msg failed");
|
||||
gst_cuda_ipc_server_close_connection (self, conn);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gst_cuda_ipc_server_send_msg (self, conn);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_wait_msg (GstCudaIpcServer * self,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
GstCudaIpcServerClass *klass = GST_CUDA_IPC_SERVER_GET_CLASS (self);
|
||||
|
||||
if (!klass->wait_msg (self, conn)) {
|
||||
GST_WARNING_OBJECT (self, "Wait msg failed, conn-id: %u", conn->id);
|
||||
gst_cuda_ipc_server_close_connection (self, conn);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_server_on_release_data (GstCudaIpcServer * self,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
if (self->ipc_mode == GST_CUDA_IPC_LEGACY) {
|
||||
CUipcMemHandle handle;
|
||||
if (!gst_cuda_ipc_pkt_parse_release_data (conn->client_msg, handle)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't parse RELEASE-DATA, conn-id: %u",
|
||||
conn->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto handle_dump = gst_cuda_ipc_mem_handle_to_string (handle);
|
||||
GST_LOG_OBJECT (self, "RELEASE-DATA %s, conn-id: %u",
|
||||
handle_dump.c_str (), conn->id);
|
||||
|
||||
for (auto it = conn->peer_handles.begin (); it != conn->peer_handles.end ();
|
||||
it++) {
|
||||
CUipcMemHandle & tmp = (*it)->handle;
|
||||
if (gst_cuda_ipc_handle_is_equal (tmp, handle)) {
|
||||
found = true;
|
||||
conn->peer_handles.erase (it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
GST_WARNING_OBJECT (self,
|
||||
"Unexpected memory handle to remove %s, conn-id: %u",
|
||||
handle_dump.c_str (), conn->id);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
GstCudaSharableHandle handle;
|
||||
if (!gst_cuda_ipc_pkt_parse_release_mmap_data (conn->client_msg, &handle)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't parse RELEASE-MMAP-DATA, conn-id: %u",
|
||||
conn->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "RELEASE-MMAP-DATA %" GST_CUDA_OS_HANDLE_FORMAT
|
||||
", conn-id %u", handle, conn->id);
|
||||
|
||||
for (auto it = conn->peer_handles.begin (); it != conn->peer_handles.end ();
|
||||
it++) {
|
||||
if ((*it)->os_handle == handle) {
|
||||
found = true;
|
||||
conn->peer_handles.erase (it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
GST_WARNING_OBJECT (self,
|
||||
"Unexpected memory handle to remove %" GST_CUDA_OS_HANDLE_FORMAT
|
||||
", conn-id: %u", handle, conn->id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Client is holding %" G_GSIZE_FORMAT " handles",
|
||||
conn->peer_handles.size ());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_server_wait_msg_finish (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn, bool result)
|
||||
{
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
if (!result) {
|
||||
GST_WARNING_OBJECT (server, "Wait msg failed, conn->id: %u", conn->id);
|
||||
gst_cuda_ipc_server_close_connection (server, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gst_cuda_ipc_pkt_identify (conn->client_msg, header)) {
|
||||
GST_ERROR_OBJECT (server, "Broken header, conn-id: %u", conn->id);
|
||||
gst_cuda_ipc_server_close_connection (server, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (header.type) {
|
||||
case GstCudaIpcPktType::NEED_DATA:
|
||||
GST_LOG_OBJECT (server, "NEED-DATA, conn-id: %u", conn->id);
|
||||
if (!conn->data) {
|
||||
GST_LOG_OBJECT (server, "Wait for available data, conn-id: %u",
|
||||
conn->id);
|
||||
conn->pending_have_data = true;
|
||||
gst_cuda_ipc_server_on_idle (server);
|
||||
return;
|
||||
}
|
||||
gst_cuda_ipc_server_have_data (server, conn);
|
||||
break;
|
||||
case GstCudaIpcPktType::READ_DONE:
|
||||
GST_LOG_OBJECT (server, "READ-DONE, conn-id: %u", conn->id);
|
||||
|
||||
if (!conn->data) {
|
||||
GST_ERROR_OBJECT (server, "Unexpected READ-DATA, conn-id: %u",
|
||||
conn->id);
|
||||
gst_cuda_ipc_server_close_connection (server, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
conn->peer_handles.push_back (conn->data);
|
||||
conn->data = nullptr;
|
||||
gst_cuda_ipc_server_wait_msg (server, conn);
|
||||
break;
|
||||
case GstCudaIpcPktType::RELEASE_DATA:
|
||||
case GstCudaIpcPktType::RELEASE_MMAP_DATA:
|
||||
GST_LOG_OBJECT (server, "RELEASE-DATA, conn-id: %u", conn->id);
|
||||
if (!gst_cuda_ipc_server_on_release_data (server, conn))
|
||||
gst_cuda_ipc_server_close_connection (server, conn);
|
||||
else
|
||||
gst_cuda_ipc_server_wait_msg (server, conn);
|
||||
break;
|
||||
case GstCudaIpcPktType::FIN:
|
||||
GST_DEBUG_OBJECT (server, "FIN, conn-id %u", conn->id);
|
||||
gst_cuda_ipc_server_close_connection (server, conn);
|
||||
break;
|
||||
default:
|
||||
GST_ERROR_OBJECT (server, "Unexpected packet, conn-id: %u", conn->id);
|
||||
gst_cuda_ipc_server_close_connection (server, conn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_server_send_msg_finish (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn, bool result)
|
||||
{
|
||||
if (!result) {
|
||||
GST_WARNING_OBJECT (server, "Send msg failed, conn-id %u", conn->id);
|
||||
gst_cuda_ipc_server_close_connection (server, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (conn->type) {
|
||||
case GstCudaIpcPktType::CONFIG:
|
||||
GST_DEBUG_OBJECT (server, "Sent CONFIG-DATA, conn-id %u", conn->id);
|
||||
gst_cuda_ipc_server_wait_msg (server, conn);
|
||||
break;
|
||||
case GstCudaIpcPktType::HAVE_DATA:
|
||||
GST_LOG_OBJECT (server, "Sent HAVE-DATA, conn-id %u", conn->id);
|
||||
gst_cuda_ipc_server_wait_msg (server, conn);
|
||||
break;
|
||||
case GstCudaIpcPktType::HAVE_MMAP_DATA:
|
||||
GST_LOG_OBJECT (server, "Sent HAVE-MMAP-DATA, conn-id %u", conn->id);
|
||||
gst_cuda_ipc_server_wait_msg (server, conn);
|
||||
break;
|
||||
case GstCudaIpcPktType::EOS:
|
||||
GST_DEBUG_OBJECT (server, "Sent EOS, conn-id %u", conn->id);
|
||||
gst_cuda_ipc_server_wait_msg (server, conn);
|
||||
break;
|
||||
default:
|
||||
GST_ERROR_OBJECT (server, "Unexpected msg type %d", (gint) conn->type);
|
||||
gst_cuda_ipc_server_close_connection (server, conn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_eos (GstCudaIpcServer * self, GstCudaIpcServerConn * conn)
|
||||
{
|
||||
gst_cuda_ipc_pkt_build_eos (conn->server_msg);
|
||||
conn->eos = true;
|
||||
conn->type = GstCudaIpcPktType::EOS;
|
||||
|
||||
gst_cuda_ipc_server_send_msg (self, conn);
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_server_on_idle (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerClass *klass = GST_CUDA_IPC_SERVER_GET_CLASS (server);
|
||||
GstCudaIpcServerPrivate *priv = server->priv;
|
||||
|
||||
GST_LOG_OBJECT (server, "idle");
|
||||
|
||||
if (priv->shutdown) {
|
||||
GST_DEBUG_OBJECT (server, "We are stopping");
|
||||
|
||||
if (priv->conn_map.empty ()) {
|
||||
GST_DEBUG_OBJECT (server, "All connections were closed");
|
||||
klass->terminate (server);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector < std::shared_ptr < GstCudaIpcServerConn >> to_send_eos;
|
||||
/* *INDENT-OFF* */
|
||||
for (auto it : priv->conn_map) {
|
||||
auto conn = it.second;
|
||||
if (conn->eos || !conn->pending_have_data)
|
||||
continue;
|
||||
|
||||
to_send_eos.push_back (conn);
|
||||
}
|
||||
|
||||
for (auto it : to_send_eos) {
|
||||
GST_DEBUG_OBJECT (server, "Sending EOS to conn-id: %u", it->id);
|
||||
gst_cuda_ipc_server_eos (server, it.get ());
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (server, "Have %" G_GSIZE_FORMAT " alive connections",
|
||||
priv->conn_map.size());
|
||||
for (auto it : priv->conn_map) {
|
||||
auto conn = it.second;
|
||||
GST_DEBUG_OBJECT (server, "conn-id %u"
|
||||
" peer handle size %" G_GSIZE_FORMAT, conn->id,
|
||||
conn->peer_handles.size ());
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->conn_map.empty ()) {
|
||||
GST_LOG_OBJECT (server, "Have no connection");
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
if (!priv->data)
|
||||
return;
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
std::vector < std::shared_ptr < GstCudaIpcServerConn >> to_config_data;
|
||||
std::vector < std::shared_ptr < GstCudaIpcServerConn >> to_send_have_data;
|
||||
for (auto it : priv->conn_map) {
|
||||
auto conn = it.second;
|
||||
if (!conn->configured) {
|
||||
conn->configured = true;
|
||||
conn->data = priv->data;
|
||||
to_config_data.push_back (conn);
|
||||
} else if (conn->pending_have_data && conn->seq_num <= priv->data->seq_num) {
|
||||
conn->data = priv->data;
|
||||
to_send_have_data.push_back (conn);
|
||||
}
|
||||
}
|
||||
lk.unlock ();
|
||||
|
||||
for (auto it: to_config_data)
|
||||
gst_cuda_ipc_server_config_data (server, it.get ());
|
||||
|
||||
for (auto it: to_send_have_data)
|
||||
gst_cuda_ipc_server_have_data (server, it.get ());
|
||||
/* *INDENT-ON* */
|
||||
}
|
||||
|
||||
void
|
||||
gst_cuda_ipc_server_abort (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerPrivate *priv = server->priv;
|
||||
|
||||
priv->aborted = true;
|
||||
}
|
168
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcserver.h
Normal file
168
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcserver.h
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* 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/cuda/gstcuda.h>
|
||||
#include "gstcudaipc.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_SERVER (gst_cuda_ipc_server_get_type())
|
||||
#define GST_CUDA_IPC_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CUDA_IPC_SERVER,GstCudaIpcServer))
|
||||
#define GST_CUDA_IPC_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CUDA_IPC_SERVER,GstCudaIpcServerClass))
|
||||
#define GST_CUDA_IPC_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_CUDA_IPC_SERVER,GstCudaIpcServerClass))
|
||||
#define GST_IS_CUDA_IPC_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CUDA_IPC_SERVER))
|
||||
#define GST_IS_CUDA_IPC_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CUDA_IPC_SERVER))
|
||||
|
||||
struct GstCudaIpcServerData;
|
||||
struct GstCudaIpcServerConn;
|
||||
struct GstCudaIpcServerPrivate;
|
||||
|
||||
enum GstCudaIpcMode
|
||||
{
|
||||
GST_CUDA_IPC_LEGACY,
|
||||
GST_CUDA_IPC_MMAP,
|
||||
};
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_MODE (gst_cuda_ipc_mode_get_type())
|
||||
GType gst_cuda_ipc_mode_get_type (void);
|
||||
|
||||
struct GstCudaIpcServer
|
||||
{
|
||||
GstObject parent;
|
||||
|
||||
GstCudaContext *context;
|
||||
GstCudaIpcMode ipc_mode;
|
||||
GstCudaPid pid;
|
||||
|
||||
GstCudaIpcServerPrivate *priv;
|
||||
};
|
||||
|
||||
struct GstCudaIpcServerClass
|
||||
{
|
||||
GstObjectClass parent_class;
|
||||
|
||||
void (*loop) (GstCudaIpcServer * server);
|
||||
|
||||
void (*terminate) (GstCudaIpcServer * server);
|
||||
|
||||
void (*invoke) (GstCudaIpcServer * server);
|
||||
|
||||
bool (*wait_msg) (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn);
|
||||
|
||||
bool (*send_msg) (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn);
|
||||
|
||||
bool (*send_mmap_msg) (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn,
|
||||
GstCudaSharableHandle handle);
|
||||
};
|
||||
|
||||
GType gst_cuda_ipc_server_get_type (void);
|
||||
|
||||
GstFlowReturn gst_cuda_ipc_server_send_data (GstCudaIpcServer * server,
|
||||
GstSample * sample,
|
||||
const GstVideoInfo & info,
|
||||
const CUipcMemHandle & handle,
|
||||
GstClockTime pts);
|
||||
|
||||
GstFlowReturn gst_cuda_ipc_server_send_mmap_data (GstCudaIpcServer * server,
|
||||
GstSample * sample,
|
||||
const GstVideoInfo & info,
|
||||
GstCudaSharableHandle handle,
|
||||
GstClockTime pts);
|
||||
|
||||
|
||||
void gst_cuda_ipc_server_stop (GstCudaIpcServer * server);
|
||||
|
||||
|
||||
/* subclass methods */
|
||||
void gst_cuda_ipc_server_run (GstCudaIpcServer * server);
|
||||
|
||||
void gst_cuda_ipc_server_wait_msg_finish (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn,
|
||||
bool result);
|
||||
|
||||
void gst_cuda_ipc_server_send_msg_finish (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn,
|
||||
bool result);
|
||||
|
||||
void gst_cuda_ipc_server_on_incoming_connection (GstCudaIpcServer * server,
|
||||
std::shared_ptr<GstCudaIpcServerConn> conn);
|
||||
|
||||
void gst_cuda_ipc_server_on_idle (GstCudaIpcServer * server);
|
||||
|
||||
void gst_cuda_ipc_server_abort (GstCudaIpcServer * server);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstCudaIpcServer, gst_object_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
struct GstCudaIpcServerData
|
||||
{
|
||||
~GstCudaIpcServerData ()
|
||||
{
|
||||
if (sample)
|
||||
gst_sample_unref (sample);
|
||||
}
|
||||
|
||||
GstSample *sample;
|
||||
GstVideoInfo info;
|
||||
CUipcMemHandle handle;
|
||||
GstCudaSharableHandle os_handle;
|
||||
GstClockTime pts;
|
||||
guint64 seq_num;
|
||||
};
|
||||
|
||||
struct GstCudaIpcServerConn : public OVERLAPPED
|
||||
{
|
||||
GstCudaIpcServerConn ()
|
||||
{
|
||||
client_msg.resize (GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
server_msg.resize (GST_CUDA_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
virtual ~GstCudaIpcServerConn()
|
||||
{
|
||||
gst_clear_object (&context);
|
||||
gst_clear_caps (&caps);
|
||||
}
|
||||
|
||||
GstCudaIpcServer *server;
|
||||
|
||||
GstCudaContext *context = nullptr;
|
||||
|
||||
GstCudaIpcPktType type;
|
||||
std::vector<guint8> client_msg;
|
||||
std::vector<guint8> server_msg;
|
||||
std::shared_ptr<GstCudaIpcServerData> data;
|
||||
std::vector<std::shared_ptr<GstCudaIpcServerData>> peer_handles;
|
||||
GstCaps *caps = nullptr;
|
||||
guint64 seq_num = 0;
|
||||
guint id;
|
||||
bool eos = false;
|
||||
bool pending_have_data = false;
|
||||
bool configured = false;
|
||||
};
|
|
@ -0,0 +1,400 @@
|
|||
/* 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 "gstcudaipcserver_unix.h"
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include <gio/gunixsocketaddress.h>
|
||||
#include <gio/gunixconnection.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (cuda_ipc_server_debug);
|
||||
#define GST_CAT_DEFAULT cuda_ipc_server_debug
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
struct GstCudaIpcServerConnUnix : public GstCudaIpcServerConn
|
||||
{
|
||||
GstCudaIpcServerConnUnix (GSocketConnection * conn)
|
||||
{
|
||||
socket_conn = (GSocketConnection *) g_object_ref (conn);
|
||||
istream = g_io_stream_get_input_stream (G_IO_STREAM (socket_conn));
|
||||
ostream = g_io_stream_get_output_stream (G_IO_STREAM (socket_conn));
|
||||
}
|
||||
|
||||
~GstCudaIpcServerConnUnix ()
|
||||
{
|
||||
g_clear_object (&socket_conn);
|
||||
}
|
||||
|
||||
/* Holds ref */
|
||||
GSocketConnection *socket_conn;
|
||||
|
||||
/* Owned by socket_conn */
|
||||
GInputStream *istream;
|
||||
GOutputStream *ostream;
|
||||
};
|
||||
struct GstCudaIpcServerUnixPrivate
|
||||
{
|
||||
GstCudaIpcServerUnixPrivate ()
|
||||
{
|
||||
main_context = g_main_context_new ();
|
||||
main_loop = g_main_loop_new (main_context, FALSE);
|
||||
cancellable = g_cancellable_new ();
|
||||
}
|
||||
|
||||
~GstCudaIpcServerUnixPrivate ()
|
||||
{
|
||||
g_main_loop_unref (main_loop);
|
||||
g_main_context_unref (main_context);
|
||||
g_object_unref (cancellable);
|
||||
}
|
||||
|
||||
std::string address;
|
||||
GMainLoop *main_loop;
|
||||
GMainContext *main_context;
|
||||
GCancellable *cancellable;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
struct _GstCudaIpcServerUnix
|
||||
{
|
||||
GstCudaIpcServer parent;
|
||||
|
||||
GstCudaIpcServerUnixPrivate *priv;
|
||||
};
|
||||
|
||||
static void gst_cuda_ipc_server_unix_finalize (GObject * object);
|
||||
static void gst_cuda_ipc_server_unix_loop (GstCudaIpcServer * server);
|
||||
static void gst_cuda_ipc_server_unix_terminate (GstCudaIpcServer * server);
|
||||
static void gst_cuda_ipc_server_unix_invoke (GstCudaIpcServer * server);
|
||||
static bool gst_cuda_ipc_server_unix_wait_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn);
|
||||
static bool gst_cuda_ipc_server_unix_send_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn);
|
||||
static bool gst_cuda_ipc_server_unix_send_mmap_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn, GstCudaSharableHandle handle);
|
||||
|
||||
#define gst_cuda_ipc_server_unix_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstCudaIpcServerUnix,
|
||||
gst_cuda_ipc_server_unix, GST_TYPE_CUDA_IPC_SERVER);
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_unix_class_init (GstCudaIpcServerUnixClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GstCudaIpcServerClass *server_class = GST_CUDA_IPC_SERVER_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_cuda_ipc_server_unix_finalize;
|
||||
|
||||
server_class->loop = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_loop);
|
||||
server_class->terminate =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_terminate);
|
||||
server_class->invoke = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_invoke);
|
||||
server_class->wait_msg =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_wait_msg);
|
||||
server_class->send_msg =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_send_msg);
|
||||
server_class->send_mmap_msg =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_send_mmap_msg);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_unix_init (GstCudaIpcServerUnix * self)
|
||||
{
|
||||
self->priv = new GstCudaIpcServerUnixPrivate ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_unix_finalize (GObject * object)
|
||||
{
|
||||
GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (object);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "finalize");
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_unix_terminate (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (server);
|
||||
GstCudaIpcServerUnixPrivate *priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "terminate");
|
||||
|
||||
g_main_loop_quit (priv->main_loop);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_server_invoke_func (GstCudaIpcServer * server)
|
||||
{
|
||||
gst_cuda_ipc_server_on_idle (server);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_unix_invoke (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (server);
|
||||
GstCudaIpcServerUnixPrivate *priv = self->priv;
|
||||
|
||||
g_main_context_invoke (priv->main_context,
|
||||
(GSourceFunc) gst_cuda_ipc_server_invoke_func, server);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_unix_payload_finish (GObject * source,
|
||||
GAsyncResult * result, GstCudaIpcServerConnUnix * conn)
|
||||
{
|
||||
GstCudaIpcServer *server = conn->server;
|
||||
gsize size;
|
||||
GError *err = nullptr;
|
||||
bool ret = true;
|
||||
|
||||
if (!g_input_stream_read_all_finish (conn->istream, result, &size, &err)) {
|
||||
GST_WARNING_OBJECT (server, "Read failed with %s, conn-id: %u",
|
||||
err->message, conn->id);
|
||||
g_clear_error (&err);
|
||||
ret = false;
|
||||
}
|
||||
|
||||
gst_cuda_ipc_server_wait_msg_finish (server, conn, ret);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_unix_wait_msg_finish (GObject * source,
|
||||
GAsyncResult * result, GstCudaIpcServerConnUnix * conn)
|
||||
{
|
||||
GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server);
|
||||
GstCudaIpcServerUnixPrivate *priv = self->priv;
|
||||
gsize size;
|
||||
GError *err = nullptr;
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
if (!g_input_stream_read_all_finish (conn->istream, result, &size, &err)) {
|
||||
GST_WARNING_OBJECT (self, "Read failed with %s, conn-id: %u",
|
||||
err->message, conn->id);
|
||||
g_clear_error (&err);
|
||||
gst_cuda_ipc_server_wait_msg_finish (conn->server, conn, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gst_cuda_ipc_pkt_identify (conn->client_msg, header)) {
|
||||
GST_ERROR_OBJECT (self, "Broken header, conn-id: %u", conn->id);
|
||||
gst_cuda_ipc_server_wait_msg_finish (conn->server, conn, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.payload_size == 0) {
|
||||
gst_cuda_ipc_server_wait_msg_finish (conn->server, conn, true);
|
||||
return;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Reading payload");
|
||||
|
||||
g_input_stream_read_all_async (conn->istream, &conn->client_msg[0] +
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE, header.payload_size, G_PRIORITY_DEFAULT,
|
||||
priv->cancellable,
|
||||
(GAsyncReadyCallback) gst_cuda_ipc_server_unix_payload_finish, conn);
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_server_unix_wait_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server);
|
||||
GstCudaIpcServerUnixPrivate *priv = self->priv;
|
||||
GstCudaIpcServerConnUnix *unix_conn =
|
||||
static_cast < GstCudaIpcServerConnUnix * >(conn);
|
||||
|
||||
GST_LOG_OBJECT (self, "Waiting for client message");
|
||||
|
||||
g_input_stream_read_all_async (unix_conn->istream, &conn->client_msg[0],
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE, G_PRIORITY_DEFAULT, priv->cancellable,
|
||||
(GAsyncReadyCallback) gst_cuda_ipc_server_unix_wait_msg_finish,
|
||||
unix_conn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_unix_send_msg_finish (GObject * source,
|
||||
GAsyncResult * result, GstCudaIpcServerConnUnix * conn)
|
||||
{
|
||||
GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server);
|
||||
gsize size;
|
||||
GError *err = nullptr;
|
||||
|
||||
if (!g_output_stream_write_all_finish (conn->ostream, result, &size, &err)) {
|
||||
GST_WARNING_OBJECT (self, "Write failed with %s, conn-id: %u",
|
||||
err->message, conn->id);
|
||||
g_clear_error (&err);
|
||||
gst_cuda_ipc_server_send_msg_finish (conn->server, conn, false);
|
||||
return;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Sent message");
|
||||
|
||||
gst_cuda_ipc_server_send_msg_finish (conn->server, conn, true);
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_server_unix_send_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server);
|
||||
GstCudaIpcServerUnixPrivate *priv = self->priv;
|
||||
GstCudaIpcServerConnUnix *unix_conn =
|
||||
static_cast < GstCudaIpcServerConnUnix * >(conn);
|
||||
|
||||
GST_LOG_OBJECT (self, "Sending message");
|
||||
|
||||
g_output_stream_write_all_async (unix_conn->ostream, &conn->server_msg[0],
|
||||
conn->server_msg.size (), G_PRIORITY_DEFAULT, priv->cancellable,
|
||||
(GAsyncReadyCallback) gst_cuda_ipc_server_unix_send_msg_finish,
|
||||
unix_conn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_server_unix_send_mmap_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn, GstCudaSharableHandle handle)
|
||||
{
|
||||
GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server);
|
||||
GstCudaIpcServerUnixPrivate *priv = self->priv;
|
||||
GstCudaIpcServerConnUnix *unix_conn =
|
||||
static_cast < GstCudaIpcServerConnUnix * >(conn);
|
||||
GError *err = nullptr;
|
||||
|
||||
GST_LOG_OBJECT (self, "Sending mmap message");
|
||||
|
||||
if (!g_output_stream_write_all (unix_conn->ostream, &conn->server_msg[0],
|
||||
conn->server_msg.size (), nullptr, priv->cancellable, &err)) {
|
||||
GST_WARNING_OBJECT (self, "Couldn't write mmap data, %s", err->message);
|
||||
g_clear_error (&err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_unix_connection_send_fd (G_UNIX_CONNECTION (unix_conn->socket_conn),
|
||||
handle, priv->cancellable, &err)) {
|
||||
GST_WARNING ("Couldn't send fd, %s", err->message);
|
||||
g_clear_error (&err);
|
||||
return false;
|
||||
}
|
||||
|
||||
gst_cuda_ipc_server_send_msg_finish (server, conn, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_server_unix_on_incoming (GSocketService * service,
|
||||
GSocketConnection * socket_conn, GObject * source_obj,
|
||||
GstCudaIpcServer * self)
|
||||
{
|
||||
GST_DEBUG_OBJECT (self, "Got new connection");
|
||||
|
||||
auto conn = std::make_shared < GstCudaIpcServerConnUnix > (socket_conn);
|
||||
gst_cuda_ipc_server_on_incoming_connection (self, conn);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_unix_loop (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (server);
|
||||
GstCudaIpcServerUnixPrivate *priv = self->priv;
|
||||
GError *err = nullptr;
|
||||
GSocketAddress *addr;
|
||||
GSocketService *service;
|
||||
gboolean ret;
|
||||
|
||||
g_main_context_push_thread_default (priv->main_context);
|
||||
|
||||
service = g_socket_service_new ();
|
||||
addr = g_unix_socket_address_new (priv->address.c_str ());
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Creating service with address \"%s\"",
|
||||
priv->address.c_str ());
|
||||
|
||||
ret = g_socket_listener_add_address (G_SOCKET_LISTENER (service),
|
||||
addr, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, nullptr,
|
||||
nullptr, &err);
|
||||
g_object_unref (addr);
|
||||
|
||||
if (!ret) {
|
||||
GST_ERROR_OBJECT (self, "Setup failed, error: %s", err->message);
|
||||
g_clear_error (&err);
|
||||
g_clear_object (&service);
|
||||
gst_cuda_ipc_server_abort (server);
|
||||
} else {
|
||||
g_signal_connect (service,
|
||||
"incoming", G_CALLBACK (gst_cuda_ipc_server_unix_on_incoming), self);
|
||||
g_socket_service_start (service);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Starting loop");
|
||||
|
||||
g_main_loop_run (priv->main_loop);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Loop stopped");
|
||||
|
||||
if (service) {
|
||||
g_cancellable_cancel (priv->cancellable);
|
||||
g_unlink (priv->address.c_str ());
|
||||
g_clear_object (&service);
|
||||
}
|
||||
|
||||
g_main_context_pop_thread_default (priv->main_context);
|
||||
}
|
||||
|
||||
GstCudaIpcServer *
|
||||
gst_cuda_ipc_server_new (const gchar * address, GstCudaContext * context,
|
||||
GstCudaIpcMode ipc_mode)
|
||||
{
|
||||
GstCudaIpcServerUnix *self;
|
||||
GstCudaIpcServer *server;
|
||||
|
||||
g_return_val_if_fail (address, nullptr);
|
||||
g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), nullptr);
|
||||
|
||||
self = (GstCudaIpcServerUnix *)
|
||||
g_object_new (GST_TYPE_CUDA_IPC_SERVER_UNIX, nullptr);
|
||||
gst_object_ref_sink (self);
|
||||
|
||||
self->priv->address = address;
|
||||
server = GST_CUDA_IPC_SERVER (self);
|
||||
server->context = (GstCudaContext *) gst_object_ref (context);
|
||||
server->ipc_mode = ipc_mode;
|
||||
server->pid = getpid ();
|
||||
|
||||
gst_cuda_ipc_server_run (server);
|
||||
|
||||
return server;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* 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 "gstcudaipcserver.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_SERVER_UNIX (gst_cuda_ipc_server_unix_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstCudaIpcServerUnix, gst_cuda_ipc_server_unix,
|
||||
GST, CUDA_IPC_SERVER_UNIX, GstCudaIpcServer);
|
||||
|
||||
GstCudaIpcServer * gst_cuda_ipc_server_new (const gchar * address,
|
||||
GstCudaContext * context,
|
||||
GstCudaIpcMode ipc_mode);
|
||||
|
||||
G_END_DECLS
|
|
@ -0,0 +1,440 @@
|
|||
/* 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 "gstcudaipcserver_win32.h"
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (cuda_ipc_server_debug);
|
||||
#define GST_CAT_DEFAULT cuda_ipc_server_debug
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
struct GstCudaIpcServerConnWin32 : public GstCudaIpcServerConn
|
||||
{
|
||||
GstCudaIpcServerConnWin32 (HANDLE pipe_handle) : pipe (pipe_handle)
|
||||
{
|
||||
OVERLAPPED *parent = static_cast<OVERLAPPED *> (this);
|
||||
parent->Internal = 0;
|
||||
parent->InternalHigh = 0;
|
||||
parent->Offset = 0;
|
||||
parent->OffsetHigh = 0;
|
||||
}
|
||||
|
||||
virtual ~GstCudaIpcServerConnWin32 ()
|
||||
{
|
||||
if (pipe != INVALID_HANDLE_VALUE) {
|
||||
CancelIo (pipe);
|
||||
DisconnectNamedPipe (pipe);
|
||||
CloseHandle (pipe);
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE pipe;
|
||||
};
|
||||
|
||||
struct GstCudaIpcServerWin32Private
|
||||
{
|
||||
GstCudaIpcServerWin32Private ()
|
||||
{
|
||||
cancellable = CreateEvent (nullptr, TRUE, FALSE, nullptr);
|
||||
wakeup_event = CreateEvent (nullptr, FALSE, FALSE, nullptr);
|
||||
}
|
||||
|
||||
~GstCudaIpcServerWin32Private ()
|
||||
{
|
||||
CloseHandle (cancellable);
|
||||
CloseHandle (wakeup_event);
|
||||
}
|
||||
|
||||
std::string address;
|
||||
HANDLE cancellable;
|
||||
HANDLE wakeup_event;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
struct _GstCudaIpcServerWin32
|
||||
{
|
||||
GstCudaIpcServer parent;
|
||||
|
||||
GstCudaIpcServerWin32Private *priv;
|
||||
};
|
||||
|
||||
static void gst_cuda_ipc_server_win32_finalize (GObject * object);
|
||||
static void gst_cuda_ipc_server_win32_loop (GstCudaIpcServer * server);
|
||||
static void gst_cuda_ipc_server_win32_terminate (GstCudaIpcServer * server);
|
||||
static void gst_cuda_ipc_server_win32_invoke (GstCudaIpcServer * server);
|
||||
static bool gst_cuda_ipc_server_win32_wait_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn);
|
||||
static bool gst_cuda_ipc_server_win32_send_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn);
|
||||
|
||||
#define gst_cuda_ipc_server_win32_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstCudaIpcServerWin32,
|
||||
gst_cuda_ipc_server_win32, GST_TYPE_CUDA_IPC_SERVER);
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_win32_class_init (GstCudaIpcServerWin32Class * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GstCudaIpcServerClass *server_class = GST_CUDA_IPC_SERVER_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_cuda_ipc_server_win32_finalize;
|
||||
|
||||
server_class->loop = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_loop);
|
||||
server_class->terminate =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_terminate);
|
||||
server_class->invoke = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_invoke);
|
||||
server_class->wait_msg =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_wait_msg);
|
||||
server_class->send_msg =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_send_msg);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_win32_init (GstCudaIpcServerWin32 * self)
|
||||
{
|
||||
self->priv = new GstCudaIpcServerWin32Private ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_win32_finalize (GObject * object)
|
||||
{
|
||||
GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (object);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "finalize");
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_win32_terminate (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server);
|
||||
GstCudaIpcServerWin32Private *priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "terminate");
|
||||
|
||||
SetEvent (priv->cancellable);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_win32_invoke (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server);
|
||||
GstCudaIpcServerWin32Private *priv = self->priv;
|
||||
|
||||
SetEvent (priv->wakeup_event);
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
gst_cuda_ipc_server_win32_payload_finish (DWORD error_code, DWORD size,
|
||||
OVERLAPPED * overlap)
|
||||
{
|
||||
GstCudaIpcServerConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcServerConnWin32 * >(overlap);
|
||||
GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (win32_conn->server);
|
||||
bool ret = true;
|
||||
|
||||
if (error_code != ERROR_SUCCESS) {
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (error_code);
|
||||
GST_WARNING_OBJECT (self, "ReadFileEx callback failed with 0x%x (%s)",
|
||||
(guint) error_code, err.c_str ());
|
||||
ret = false;
|
||||
}
|
||||
|
||||
gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, ret);
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
gst_cuda_ipc_server_win32_wait_msg_finish (DWORD error_code, DWORD size,
|
||||
OVERLAPPED * overlap)
|
||||
{
|
||||
GstCudaIpcServerConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcServerConnWin32 * >(overlap);
|
||||
GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (win32_conn->server);
|
||||
GstCudaIpcPacketHeader header;
|
||||
|
||||
if (error_code != ERROR_SUCCESS) {
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (error_code);
|
||||
GST_WARNING_OBJECT (self, "ReadFileEx callback failed with 0x%x (%s)",
|
||||
(guint) error_code, err.c_str ());
|
||||
gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gst_cuda_ipc_pkt_identify (win32_conn->client_msg, header)) {
|
||||
GST_ERROR_OBJECT (self, "Broken header");
|
||||
gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.payload_size == 0) {
|
||||
gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, true);
|
||||
return;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Reading payload");
|
||||
|
||||
if (!ReadFileEx (win32_conn->pipe, &win32_conn->client_msg[0] +
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE, header.payload_size, win32_conn,
|
||||
gst_cuda_ipc_server_win32_payload_finish)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (self, "ReadFileEx failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_server_win32_wait_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server);
|
||||
GstCudaIpcServerConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcServerConnWin32 * >(conn);
|
||||
|
||||
GST_LOG_OBJECT (self, "Waiting for client message");
|
||||
|
||||
if (!ReadFileEx (win32_conn->pipe, &conn->client_msg[0],
|
||||
GST_CUDA_IPC_PKT_HEADER_SIZE, win32_conn,
|
||||
gst_cuda_ipc_server_win32_wait_msg_finish)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (self, "ReadFileEx failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
gst_cuda_ipc_server_win32_send_msg_finish (DWORD error_code, DWORD size,
|
||||
OVERLAPPED * overlap)
|
||||
{
|
||||
GstCudaIpcServerConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcServerConnWin32 * >(overlap);
|
||||
GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (win32_conn->server);
|
||||
bool ret = true;
|
||||
|
||||
if (error_code != ERROR_SUCCESS) {
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (error_code);
|
||||
GST_WARNING_OBJECT (self, "ReadFileEx callback failed with 0x%x (%s)",
|
||||
(guint) error_code, err.c_str ());
|
||||
ret = false;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Sent message");
|
||||
|
||||
gst_cuda_ipc_server_send_msg_finish (win32_conn->server, win32_conn, ret);
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_cuda_ipc_server_win32_send_msg (GstCudaIpcServer * server,
|
||||
GstCudaIpcServerConn * conn)
|
||||
{
|
||||
GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server);
|
||||
GstCudaIpcServerConnWin32 *win32_conn =
|
||||
static_cast < GstCudaIpcServerConnWin32 * >(conn);
|
||||
|
||||
GST_LOG_OBJECT (self, "Sending message");
|
||||
|
||||
if (!WriteFileEx (win32_conn->pipe, &conn->server_msg[0],
|
||||
conn->server_msg.size (), win32_conn,
|
||||
gst_cuda_ipc_server_win32_send_msg_finish)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (self, "WriteFileEx failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static HANDLE
|
||||
gst_cuda_ipc_server_win32_create_pipe (GstCudaIpcServerWin32 * self,
|
||||
OVERLAPPED * overlap, bool &io_pending)
|
||||
{
|
||||
GstCudaIpcServerWin32Private *priv = self->priv;
|
||||
HANDLE pipe = CreateNamedPipeA (priv->address.c_str (),
|
||||
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES, 1024, 1024, 5000, nullptr);
|
||||
|
||||
if (pipe == INVALID_HANDLE_VALUE) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_ERROR_OBJECT (self, "CreateNamedPipeA failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (ConnectNamedPipe (pipe, overlap)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_ERROR_OBJECT (self, "ConnectNamedPipe failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
io_pending = false;
|
||||
guint last_err = GetLastError ();
|
||||
|
||||
switch (last_err) {
|
||||
case ERROR_IO_PENDING:
|
||||
io_pending = true;
|
||||
break;
|
||||
case ERROR_PIPE_CONNECTED:
|
||||
SetEvent (overlap->hEvent);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_ERROR_OBJECT (self, "ConnectNamedPipe failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
CloseHandle (pipe);
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
return pipe;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_server_win32_loop (GstCudaIpcServer * server)
|
||||
{
|
||||
GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server);
|
||||
GstCudaIpcServerWin32Private *priv = self->priv;
|
||||
bool io_pending = false;
|
||||
guint wait_ret;
|
||||
HANDLE pipe;
|
||||
OVERLAPPED overlap;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Entering loop");
|
||||
|
||||
memset (&overlap, 0, sizeof (OVERLAPPED));
|
||||
|
||||
overlap.hEvent = CreateEvent (nullptr, TRUE, TRUE, nullptr);
|
||||
|
||||
pipe = gst_cuda_ipc_server_win32_create_pipe (self, &overlap, io_pending);
|
||||
if (pipe == INVALID_HANDLE_VALUE) {
|
||||
CloseHandle (overlap.hEvent);
|
||||
gst_cuda_ipc_server_abort (server);
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE waitables[] =
|
||||
{ overlap.hEvent, priv->wakeup_event, priv->cancellable };
|
||||
|
||||
do {
|
||||
wait_ret = WaitForMultipleObjectsEx (G_N_ELEMENTS (waitables), waitables,
|
||||
FALSE, INFINITE, TRUE);
|
||||
|
||||
if (wait_ret == WAIT_OBJECT_0 + 2) {
|
||||
GST_DEBUG_OBJECT (self, "Operation cancelled");
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (wait_ret) {
|
||||
case WAIT_OBJECT_0:
|
||||
{
|
||||
DWORD n_bytes;
|
||||
|
||||
if (io_pending
|
||||
&& !GetOverlappedResult (pipe, &overlap, &n_bytes, FALSE)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (self, "GetOverlappedResult failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
CloseHandle (pipe);
|
||||
pipe = INVALID_HANDLE_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
auto conn = std::make_shared < GstCudaIpcServerConnWin32 > (pipe);
|
||||
pipe = INVALID_HANDLE_VALUE;
|
||||
gst_cuda_ipc_server_on_incoming_connection (server, conn);
|
||||
|
||||
pipe = gst_cuda_ipc_server_win32_create_pipe (self,
|
||||
&overlap, io_pending);
|
||||
break;
|
||||
}
|
||||
case WAIT_IO_COMPLETION:
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
gst_cuda_ipc_server_on_idle (server);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_cuda_ipc_win32_error_to_string (last_err);
|
||||
GST_ERROR_OBJECT (self,
|
||||
"WaitForMultipleObjectsEx return 0x%x, last error 0x%x (%s)",
|
||||
wait_ret, last_err, err.c_str ());
|
||||
gst_cuda_ipc_server_abort (server);
|
||||
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
|
||||
out:
|
||||
if (pipe != INVALID_HANDLE_VALUE) {
|
||||
CancelIo (pipe);
|
||||
DisconnectNamedPipe (pipe);
|
||||
CloseHandle (pipe);
|
||||
}
|
||||
|
||||
CloseHandle (overlap.hEvent);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Exit loop");
|
||||
}
|
||||
|
||||
GstCudaIpcServer *
|
||||
gst_cuda_ipc_server_new (const gchar * address, GstCudaContext * context,
|
||||
GstCudaIpcMode ipc_mode)
|
||||
{
|
||||
GstCudaIpcServerWin32 *self;
|
||||
GstCudaIpcServer *server;
|
||||
|
||||
g_return_val_if_fail (address, nullptr);
|
||||
g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), nullptr);
|
||||
|
||||
self = (GstCudaIpcServerWin32 *)
|
||||
g_object_new (GST_TYPE_CUDA_IPC_SERVER_WIN32, nullptr);
|
||||
gst_object_ref_sink (self);
|
||||
|
||||
self->priv->address = address;
|
||||
server = GST_CUDA_IPC_SERVER (self);
|
||||
server->context = (GstCudaContext *) gst_object_ref (context);
|
||||
server->ipc_mode = ipc_mode;
|
||||
server->pid = GetCurrentProcessId ();
|
||||
|
||||
gst_cuda_ipc_server_run (server);
|
||||
|
||||
return server;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* 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 "gstcudaipcserver.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_SERVER_WIN32 (gst_cuda_ipc_server_win32_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstCudaIpcServerWin32, gst_cuda_ipc_server_win32,
|
||||
GST, CUDA_IPC_SERVER_WIN32, GstCudaIpcServer);
|
||||
|
||||
GstCudaIpcServer * gst_cuda_ipc_server_new (const gchar * address,
|
||||
GstCudaContext * context,
|
||||
GstCudaIpcMode ipc_mode);
|
||||
|
||||
G_END_DECLS
|
716
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcsink.cpp
Normal file
716
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcsink.cpp
Normal file
|
@ -0,0 +1,716 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-cudaipcsink
|
||||
* @title: cudaipcsink
|
||||
* @short_description: CUDA Inter Process Communication (IPC) sink
|
||||
*
|
||||
* cudaipcsink exports CUDA memory for connected cudaipcsrc elements to be able
|
||||
* to import it
|
||||
*
|
||||
* ## Example launch line
|
||||
* ```
|
||||
* gst-launch-1.0 videotestsrc ! cudaupload ! cudaipcsink
|
||||
* ```
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstcudaipcsink.h"
|
||||
#include "gstcudaformat.h"
|
||||
#include <gst/cuda/gstcuda-private.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include "gstcudaipcserver_win32.h"
|
||||
#else
|
||||
#include "gstcudaipcserver_unix.h"
|
||||
#endif
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (cuda_ipc_sink_debug);
|
||||
#define GST_CAT_DEFAULT cuda_ipc_sink_debug
|
||||
|
||||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
||||
(GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, GST_CUDA_FORMATS) "; "
|
||||
GST_VIDEO_CAPS_MAKE (GST_CUDA_FORMATS)));
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DEVICE_ID,
|
||||
PROP_ADDRESS,
|
||||
PROP_IPC_MODE,
|
||||
};
|
||||
|
||||
#define DEFAULT_DEVICE_ID -1
|
||||
#ifdef G_OS_WIN32
|
||||
#define DEFAULT_ADDRESS "\\\\.\\pipe\\gst.cuda.ipc"
|
||||
#else
|
||||
#define DEFAULT_ADDRESS "/tmp/gst.cuda.ipc"
|
||||
#endif
|
||||
#define DEFAULT_IPC_MODE GST_CUDA_IPC_LEGACY
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
struct GstCudaIpcSinkPrivate
|
||||
{
|
||||
GstCudaContext *context = nullptr;
|
||||
GstCudaStream *stream = nullptr;
|
||||
|
||||
GstBufferPool *fallback_pool = nullptr;
|
||||
GstVideoInfo info;
|
||||
|
||||
GstCudaIpcServer *server = nullptr;
|
||||
GstCaps *caps = nullptr;
|
||||
GstSample *prepared_sample = nullptr;
|
||||
GstVideoInfo mem_info;
|
||||
CUipcMemHandle prepared_handle;
|
||||
GstCudaSharableHandle prepared_os_handle;
|
||||
|
||||
std::mutex lock;
|
||||
|
||||
/* properties */
|
||||
gint device_id = DEFAULT_DEVICE_ID;
|
||||
std::string address = DEFAULT_ADDRESS;
|
||||
GstCudaIpcMode ipc_mode = DEFAULT_IPC_MODE;
|
||||
GstCudaIpcMode configured_ipc_mode = DEFAULT_IPC_MODE;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
struct _GstCudaIpcSink
|
||||
{
|
||||
GstBaseSink parent;
|
||||
|
||||
GstCudaIpcSinkPrivate *priv;
|
||||
};
|
||||
|
||||
static void gst_cuda_ipc_sink_finalize (GObject * object);
|
||||
static void gst_cuda_ipc_sink_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_win32_video_sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstClock *gst_cuda_ipc_sink_provide_clock (GstElement * elem);
|
||||
static void gst_cuda_ipc_sink_set_context (GstElement * elem,
|
||||
GstContext * context);
|
||||
|
||||
static gboolean gst_cuda_ipc_sink_start (GstBaseSink * sink);
|
||||
static gboolean gst_cuda_ipc_sink_stop (GstBaseSink * sink);
|
||||
static gboolean gst_cuda_ipc_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
|
||||
static void gst_cuda_ipc_sink_get_time (GstBaseSink * sink,
|
||||
GstBuffer * buf, GstClockTime * start, GstClockTime * end);
|
||||
static gboolean gst_cuda_ipc_sink_propose_allocation (GstBaseSink * sink,
|
||||
GstQuery * query);
|
||||
static gboolean gst_cuda_ipc_sink_query (GstBaseSink * sink, GstQuery * query);
|
||||
static GstFlowReturn gst_cuda_ipc_sink_prepare (GstBaseSink * sink,
|
||||
GstBuffer * buf);
|
||||
static GstFlowReturn gst_cuda_ipc_sink_render (GstBaseSink * sink,
|
||||
GstBuffer * buf);
|
||||
|
||||
#define gst_cuda_ipc_sink_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstCudaIpcSink, gst_cuda_ipc_sink, GST_TYPE_BASE_SINK);
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_sink_class_init (GstCudaIpcSinkClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
GstBaseSinkClass *sink_class = GST_BASE_SINK_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_cuda_ipc_sink_finalize;
|
||||
object_class->set_property = gst_cuda_ipc_sink_set_property;
|
||||
object_class->get_property = gst_win32_video_sink_get_property;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_DEVICE_ID,
|
||||
g_param_spec_int ("cuda-device-id", "CUDA Device ID",
|
||||
"CUDA device id to use (-1 = auto)", -1, G_MAXINT, DEFAULT_DEVICE_ID,
|
||||
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_ADDRESS,
|
||||
g_param_spec_string ("address", "Address",
|
||||
"Server address. Specifies name of WIN32 named pipe "
|
||||
"or unix domain socket path on Linux",
|
||||
DEFAULT_ADDRESS, (GParamFlags) (G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_IPC_MODE,
|
||||
g_param_spec_enum ("ipc-mode", "IPC Mode",
|
||||
"IPC mode to use", GST_TYPE_CUDA_IPC_MODE, DEFAULT_IPC_MODE,
|
||||
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"CUDA IPC Sink", "Sink/Video",
|
||||
"Send CUDA memory to peer cudaipcsrc elements",
|
||||
"Seungha Yang <seungha@centricular.com>");
|
||||
gst_element_class_add_static_pad_template (element_class, &sink_template);
|
||||
|
||||
element_class->provide_clock =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_provide_clock);
|
||||
element_class->set_context =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_set_context);
|
||||
|
||||
sink_class->start = GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_start);
|
||||
sink_class->stop = GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_stop);
|
||||
sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_set_caps);
|
||||
sink_class->propose_allocation =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_propose_allocation);
|
||||
sink_class->query = GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_query);
|
||||
sink_class->get_times = GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_get_time);
|
||||
sink_class->prepare = GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_prepare);
|
||||
sink_class->render = GST_DEBUG_FUNCPTR (gst_cuda_ipc_sink_render);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (cuda_ipc_sink_debug, "cudaipcsink",
|
||||
0, "cudaipcsink");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_sink_init (GstCudaIpcSink * self)
|
||||
{
|
||||
self->priv = new GstCudaIpcSinkPrivate ();
|
||||
|
||||
GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
|
||||
GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_sink_finalize (GObject * object)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (object);
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (object);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_ID:
|
||||
priv->device_id = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_ADDRESS:
|
||||
{
|
||||
const gchar *address = g_value_get_string (value);
|
||||
priv->address.clear ();
|
||||
|
||||
if (address)
|
||||
priv->address = address;
|
||||
break;
|
||||
}
|
||||
case PROP_IPC_MODE:
|
||||
priv->ipc_mode = (GstCudaIpcMode) g_value_get_enum (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_win32_video_sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (object);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_ID:
|
||||
g_value_set_int (value, priv->device_id);
|
||||
break;
|
||||
case PROP_ADDRESS:
|
||||
g_value_set_string (value, priv->address.c_str ());
|
||||
break;
|
||||
case PROP_IPC_MODE:
|
||||
g_value_set_enum (value, priv->ipc_mode);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstClock *
|
||||
gst_cuda_ipc_sink_provide_clock (GstElement * elem)
|
||||
{
|
||||
return gst_system_clock_obtain ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_sink_set_context (GstElement * elem, GstContext * context)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (elem);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
|
||||
gst_cuda_handle_set_context (elem, context, priv->device_id, &priv->context);
|
||||
|
||||
GST_ELEMENT_CLASS (parent_class)->set_context (elem, context);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_sink_start (GstBaseSink * sink)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (sink);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
gboolean virtual_memory = FALSE;
|
||||
gboolean os_handle = FALSE;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Start");
|
||||
|
||||
if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (self),
|
||||
priv->device_id, &priv->context)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't get CUDA context");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_object_get (priv->context, "virtual-memory", &virtual_memory,
|
||||
"os-handle", &os_handle, nullptr);
|
||||
|
||||
GST_DEBUG_OBJECT (self,
|
||||
"virtual-memory: %d, OS-handle: %d, requested IPC mode: %d",
|
||||
virtual_memory, os_handle, priv->ipc_mode);
|
||||
|
||||
priv->configured_ipc_mode = priv->ipc_mode;
|
||||
if (priv->configured_ipc_mode == GST_CUDA_IPC_MMAP &&
|
||||
(!virtual_memory || !os_handle)) {
|
||||
GST_ELEMENT_WARNING (self, RESOURCE, SETTINGS, ("Not supported IPC mode"),
|
||||
("MMAP mode IPC is not supported by device"));
|
||||
priv->configured_ipc_mode = GST_CUDA_IPC_LEGACY;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Selected IPC mode: %d", priv->configured_ipc_mode);
|
||||
|
||||
priv->server = gst_cuda_ipc_server_new (priv->address.c_str (),
|
||||
priv->context, priv->configured_ipc_mode);
|
||||
if (!priv->server) {
|
||||
gst_clear_object (&priv->context);
|
||||
GST_ERROR_OBJECT (self, "Couldn't create server object");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
priv->stream = gst_cuda_stream_new (priv->context);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_sink_stop (GstBaseSink * sink)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (sink);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stop");
|
||||
|
||||
if (priv->server)
|
||||
gst_cuda_ipc_server_stop (priv->server);
|
||||
gst_clear_object (&priv->server);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Server cleared");
|
||||
|
||||
if (priv->fallback_pool) {
|
||||
gst_buffer_pool_set_active (priv->fallback_pool, FALSE);
|
||||
gst_clear_object (&priv->fallback_pool);
|
||||
}
|
||||
|
||||
gst_clear_sample (&priv->prepared_sample);
|
||||
gst_clear_cuda_stream (&priv->stream);
|
||||
gst_clear_object (&priv->context);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_sink_get_time (GstBaseSink * sink, GstBuffer * buf,
|
||||
GstClockTime * start, GstClockTime * end)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (sink);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
GstClockTime timestamp;
|
||||
|
||||
timestamp = GST_BUFFER_PTS (buf);
|
||||
if (!GST_CLOCK_TIME_IS_VALID (timestamp))
|
||||
timestamp = GST_BUFFER_DTS (buf);
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
|
||||
*start = timestamp;
|
||||
if (GST_BUFFER_DURATION_IS_VALID (buf)) {
|
||||
*end = timestamp + GST_BUFFER_DURATION (buf);
|
||||
} else if (priv->info.fps_n > 0) {
|
||||
*end = timestamp +
|
||||
gst_util_uint64_scale_int (GST_SECOND, priv->info.fps_d,
|
||||
priv->info.fps_n);
|
||||
} else if (sink->segment.rate < 0) {
|
||||
*end = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (sink);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
GstStructure *config;
|
||||
GstCaps *new_caps;
|
||||
GstStructure *s;
|
||||
const gchar *str;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "New caps %" GST_PTR_FORMAT, caps);
|
||||
|
||||
if (!gst_video_info_from_caps (&priv->info, caps)) {
|
||||
GST_ERROR_OBJECT (self, "Invalid caps %" GST_PTR_FORMAT, caps);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
|
||||
/* Takes values we know it's always deserializable */
|
||||
new_caps = gst_caps_new_empty_simple ("video/x-raw");
|
||||
gst_caps_set_simple (new_caps, "format", G_TYPE_STRING,
|
||||
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&priv->info)),
|
||||
"width", G_TYPE_INT, priv->info.width,
|
||||
"height", G_TYPE_INT, priv->info.height,
|
||||
"framerate", GST_TYPE_FRACTION, priv->info.fps_n, priv->info.fps_d,
|
||||
"pixel-aspect-ratio", GST_TYPE_FRACTION, priv->info.par_n,
|
||||
priv->info.par_d, nullptr);
|
||||
|
||||
str = gst_structure_get_string (s, "colorimetry");
|
||||
if (str)
|
||||
gst_caps_set_simple (new_caps, "colorimetry", G_TYPE_STRING, str, nullptr);
|
||||
|
||||
str = gst_structure_get_string (s, "mastering-display-info");
|
||||
if (str) {
|
||||
gst_caps_set_simple (new_caps, "mastering-display-info", G_TYPE_STRING,
|
||||
str, nullptr);
|
||||
}
|
||||
|
||||
str = gst_structure_get_string (s, "content-light-level");
|
||||
if (str) {
|
||||
gst_caps_set_simple (new_caps, "content-light-level", G_TYPE_STRING,
|
||||
str, nullptr);
|
||||
}
|
||||
|
||||
gst_caps_set_features_simple (new_caps,
|
||||
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, nullptr));
|
||||
|
||||
gst_clear_caps (&priv->caps);
|
||||
priv->caps = new_caps;
|
||||
|
||||
if (priv->fallback_pool) {
|
||||
gst_buffer_pool_set_active (priv->fallback_pool, FALSE);
|
||||
gst_object_unref (priv->fallback_pool);
|
||||
}
|
||||
|
||||
priv->fallback_pool = gst_cuda_buffer_pool_new (priv->context);
|
||||
config = gst_buffer_pool_get_config (priv->fallback_pool);
|
||||
|
||||
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||
gst_buffer_pool_config_set_params (config, priv->caps,
|
||||
GST_VIDEO_INFO_SIZE (&priv->info), 0, 0);
|
||||
if (priv->stream)
|
||||
gst_buffer_pool_config_set_cuda_stream (config, priv->stream);
|
||||
|
||||
if (priv->configured_ipc_mode == GST_CUDA_IPC_MMAP) {
|
||||
gst_buffer_pool_config_set_cuda_alloc_method (config,
|
||||
GST_CUDA_MEMORY_ALLOC_MMAP);
|
||||
}
|
||||
|
||||
if (!gst_buffer_pool_set_config (priv->fallback_pool, config)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set pool config");
|
||||
gst_clear_object (&priv->fallback_pool);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_buffer_pool_set_active (priv->fallback_pool, TRUE)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't active pool");
|
||||
gst_clear_object (&priv->fallback_pool);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_sink_propose_allocation (GstBaseSink * sink, GstQuery * query)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (sink);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
GstCaps *caps;
|
||||
GstBufferPool *pool = nullptr;
|
||||
GstVideoInfo info;
|
||||
guint size;
|
||||
gboolean need_pool;
|
||||
|
||||
gst_query_parse_allocation (query, &caps, &need_pool);
|
||||
if (!caps) {
|
||||
GST_WARNING_OBJECT (sink, "No caps specified");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_video_info_from_caps (&info, caps)) {
|
||||
GST_WARNING_OBJECT (sink, "Invalid caps %" GST_PTR_FORMAT, caps);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* the normal size of a frame */
|
||||
size = info.size;
|
||||
if (need_pool) {
|
||||
GstStructure *config;
|
||||
|
||||
pool = gst_cuda_buffer_pool_new (priv->context);
|
||||
|
||||
config = gst_buffer_pool_get_config (pool);
|
||||
gst_buffer_pool_config_add_option (config,
|
||||
GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||
if (priv->stream)
|
||||
gst_buffer_pool_config_set_cuda_stream (config, priv->stream);
|
||||
|
||||
if (priv->configured_ipc_mode == GST_CUDA_IPC_MMAP) {
|
||||
gst_buffer_pool_config_set_cuda_alloc_method (config,
|
||||
GST_CUDA_MEMORY_ALLOC_MMAP);
|
||||
}
|
||||
|
||||
size = GST_VIDEO_INFO_SIZE (&info);
|
||||
gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
|
||||
|
||||
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||
GST_ERROR_OBJECT (pool, "Couldn't set config");
|
||||
gst_object_unref (pool);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gst_query_add_allocation_pool (query, pool, size, 0, 0);
|
||||
gst_clear_object (&pool);
|
||||
|
||||
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_sink_query (GstBaseSink * sink, GstQuery * query)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (sink);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_CONTEXT:
|
||||
if (gst_cuda_handle_context_query (GST_ELEMENT (self), query,
|
||||
priv->context)) {
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GST_BASE_SINK_CLASS (parent_class)->query (sink, query);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_cuda_ipc_sink_prepare (GstBaseSink * sink, GstBuffer * buf)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (sink);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
GstBuffer *cuda_buf;
|
||||
GstMemory *mem;
|
||||
GstCudaMemory *cmem;
|
||||
GstMapInfo info;
|
||||
CUresult ret;
|
||||
CUdeviceptr ptr;
|
||||
std::string handle_dump;
|
||||
|
||||
gst_clear_sample (&priv->prepared_sample);
|
||||
|
||||
cuda_buf = buf;
|
||||
mem = gst_buffer_peek_memory (cuda_buf, 0);
|
||||
if (!gst_is_cuda_memory (mem) ||
|
||||
GST_CUDA_MEMORY_CAST (mem)->context != priv->context) {
|
||||
if (gst_buffer_pool_acquire_buffer (priv->fallback_pool, &cuda_buf,
|
||||
nullptr) != GST_FLOW_OK) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't acquire fallback buffer");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
if (!gst_cuda_buffer_copy (cuda_buf, GST_CUDA_BUFFER_COPY_CUDA,
|
||||
&priv->info, buf, GST_CUDA_BUFFER_COPY_SYSTEM, &priv->info,
|
||||
priv->context, priv->stream)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't copy memory");
|
||||
goto error;
|
||||
}
|
||||
|
||||
mem = gst_buffer_peek_memory (cuda_buf, 0);
|
||||
} else {
|
||||
GstCudaMemory *cmem = GST_CUDA_MEMORY_CAST (mem);
|
||||
GstCudaMemoryAllocMethod alloc_method =
|
||||
gst_cuda_memory_get_alloc_method (cmem);
|
||||
|
||||
/* Copy into fallback pool if the memory belongs to fixed size buffer pool
|
||||
* (e.g., decoder output) or mem allocation type is different */
|
||||
if (gst_cuda_memory_is_from_fixed_pool (mem) ||
|
||||
(priv->configured_ipc_mode == GST_CUDA_IPC_MMAP &&
|
||||
alloc_method != GST_CUDA_MEMORY_ALLOC_MMAP) ||
|
||||
(priv->configured_ipc_mode == GST_CUDA_IPC_LEGACY &&
|
||||
alloc_method != GST_CUDA_MEMORY_ALLOC_MALLOC)) {
|
||||
if (gst_buffer_pool_acquire_buffer (priv->fallback_pool, &cuda_buf,
|
||||
nullptr) != GST_FLOW_OK) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't acquire fallback buffer");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
if (!gst_cuda_buffer_copy (cuda_buf, GST_CUDA_BUFFER_COPY_CUDA,
|
||||
&priv->info, buf, GST_CUDA_BUFFER_COPY_CUDA, &priv->info,
|
||||
priv->context, priv->stream)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't copy memory");
|
||||
goto error;
|
||||
}
|
||||
|
||||
mem = gst_buffer_peek_memory (cuda_buf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
cmem = GST_CUDA_MEMORY_CAST (mem);
|
||||
priv->mem_info = cmem->info;
|
||||
if (!gst_memory_map (mem, &info, (GstMapFlags) (GST_MAP_READ | GST_MAP_CUDA))) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't map memory");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ptr = (CUdeviceptr) info.data;
|
||||
gst_memory_unmap (mem, &info);
|
||||
gst_cuda_memory_sync (cmem);
|
||||
|
||||
if (priv->configured_ipc_mode == GST_CUDA_IPC_MMAP) {
|
||||
if (!gst_cuda_memory_export (cmem, (void *) &priv->prepared_os_handle)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't export memory");
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
if (!gst_cuda_context_push (cmem->context)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't push context");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = CuIpcGetMemHandle (&priv->prepared_handle, ptr);
|
||||
gst_cuda_context_pop (nullptr);
|
||||
|
||||
if (!gst_cuda_result (ret)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't get IPC handle");
|
||||
goto error;
|
||||
}
|
||||
|
||||
handle_dump = gst_cuda_ipc_mem_handle_to_string (priv->prepared_handle);
|
||||
GST_TRACE_OBJECT (self, "Exported handle value for %" G_GUINTPTR_FORMAT
|
||||
" %s", ptr, handle_dump.c_str ());
|
||||
}
|
||||
|
||||
priv->prepared_sample = gst_sample_new (cuda_buf,
|
||||
priv->caps, nullptr, nullptr);
|
||||
|
||||
if (cuda_buf != buf)
|
||||
gst_buffer_unref (cuda_buf);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
error:
|
||||
if (cuda_buf != buf)
|
||||
gst_buffer_unref (cuda_buf);
|
||||
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_cuda_ipc_sink_render (GstBaseSink * sink, GstBuffer * buf)
|
||||
{
|
||||
GstCudaIpcSink *self = GST_CUDA_IPC_SINK (sink);
|
||||
GstCudaIpcSinkPrivate *priv = self->priv;
|
||||
GstClockTime pts;
|
||||
GstClockTime now_system;
|
||||
GstClockTime buf_pts;
|
||||
GstClockTime buffer_clock = GST_CLOCK_TIME_NONE;
|
||||
GstFlowReturn ret;
|
||||
|
||||
if (!priv->prepared_sample) {
|
||||
GST_ERROR_OBJECT (self, "Have no prepared sample");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
pts = now_system = gst_util_get_timestamp ();
|
||||
buf_pts = GST_BUFFER_PTS (buf);
|
||||
if (!GST_CLOCK_TIME_IS_VALID (buf_pts))
|
||||
buf_pts = GST_BUFFER_DTS (buf);
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (buf_pts)) {
|
||||
buffer_clock = gst_segment_to_running_time (&sink->segment,
|
||||
GST_FORMAT_TIME, buf_pts) +
|
||||
GST_ELEMENT_CAST (sink)->base_time + gst_base_sink_get_latency (sink);
|
||||
}
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (buffer_clock)) {
|
||||
GstClock *clock = gst_element_get_clock (GST_ELEMENT_CAST (sink));
|
||||
if (!gst_cuda_ipc_clock_is_system (clock)) {
|
||||
GstClockTime now_gst = gst_clock_get_time (clock);
|
||||
GstClockTimeDiff converted = buffer_clock;
|
||||
|
||||
converted -= now_gst;
|
||||
converted += now_system;
|
||||
|
||||
if (converted < 0) {
|
||||
/* Shouldn't happen */
|
||||
GST_WARNING_OBJECT (self, "Negative buffer clock");
|
||||
pts = 0;
|
||||
} else {
|
||||
pts = converted;
|
||||
}
|
||||
} else {
|
||||
/* buffer clock is already system time */
|
||||
pts = buffer_clock;
|
||||
}
|
||||
gst_object_unref (clock);
|
||||
}
|
||||
|
||||
if (priv->ipc_mode == GST_CUDA_IPC_LEGACY) {
|
||||
ret = gst_cuda_ipc_server_send_data (priv->server, priv->prepared_sample,
|
||||
priv->mem_info, priv->prepared_handle, pts);
|
||||
} else {
|
||||
ret = gst_cuda_ipc_server_send_mmap_data (priv->server,
|
||||
priv->prepared_sample, priv->mem_info, priv->prepared_os_handle, pts);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
33
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcsink.h
Normal file
33
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcsink.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* 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/base/gstbasesink.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/cuda/gstcuda.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_SINK (gst_cuda_ipc_sink_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstCudaIpcSink, gst_cuda_ipc_sink,
|
||||
GST, CUDA_IPC_SINK, GstBaseSink);
|
||||
|
||||
G_END_DECLS
|
566
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcsrc.cpp
Normal file
566
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcsrc.cpp
Normal file
|
@ -0,0 +1,566 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-cudaipcsrc
|
||||
* @title: cudaipcsrc
|
||||
* @short_description: CUDA Inter Process Communication (IPC) src
|
||||
*
|
||||
* cudaipcsrc imports CUDA memory exported by peer cudaipcsrc element
|
||||
*
|
||||
* ## Example launch line
|
||||
* ```
|
||||
* gst-launch-1.0 cudaipcsrc ! queue ! cudadownload ! videoconvert ! queue ! autovideosink
|
||||
* ```
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstcudaipcsrc.h"
|
||||
#include "gstcudaformat.h"
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include "gstcudaipcclient_win32.h"
|
||||
#else
|
||||
#include "gstcudaipcclient_unix.h"
|
||||
#endif
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_cuda_ipc_src_debug);
|
||||
#define GST_CAT_DEFAULT gst_cuda_ipc_src_debug
|
||||
|
||||
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
||||
(GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY,
|
||||
GST_CUDA_FORMATS)));
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DEVICE_ID,
|
||||
PROP_ADDRESS,
|
||||
PROP_PROCESSING_DEADLINE,
|
||||
PROP_IO_MODE,
|
||||
PROP_CONN_TIMEOUT,
|
||||
PROP_BUFFER_SIZE,
|
||||
};
|
||||
|
||||
#define DEFAULT_DEVICE_ID -1
|
||||
#ifdef G_OS_WIN32
|
||||
#define DEFAULT_ADDRESS "\\\\.\\pipe\\gst.cuda.ipc"
|
||||
#else
|
||||
#define DEFAULT_ADDRESS "/tmp/gst.cuda.ipc"
|
||||
#endif
|
||||
#define DEFAULT_PROCESSING_DEADLINE (20 * GST_MSECOND)
|
||||
#define DEFAULT_IO_MODE GST_CUDA_IPC_IO_COPY
|
||||
#define DEFAULT_CONN_TIMEOUT 5
|
||||
#define DEFAULT_BUFFER_SIZE 3
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
struct GstCudaIpcSrcPrivate
|
||||
{
|
||||
GstCudaContext *context = nullptr;
|
||||
GstCudaStream *stream = nullptr;
|
||||
|
||||
GstCudaIpcClient *client = nullptr;
|
||||
GstCaps *caps = nullptr;
|
||||
|
||||
GstVideoInfo info;
|
||||
std::mutex lock;
|
||||
bool flushing = false;
|
||||
|
||||
/* properties */
|
||||
gint device_id = DEFAULT_DEVICE_ID;
|
||||
std::string address = DEFAULT_ADDRESS;
|
||||
GstClockTime processing_deadline = DEFAULT_PROCESSING_DEADLINE;
|
||||
GstCudaIpcIOMode io_mode = DEFAULT_IO_MODE;
|
||||
guint conn_timeout = DEFAULT_CONN_TIMEOUT;
|
||||
guint buffer_size = DEFAULT_BUFFER_SIZE;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
struct _GstCudaIpcSrc
|
||||
{
|
||||
GstBaseSrc parent;
|
||||
|
||||
GstCudaIpcSrcPrivate *priv;
|
||||
};
|
||||
|
||||
static void gst_cuda_ipc_src_finalize (GObject * object);
|
||||
static void gst_cuda_ipc_src_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_win32_video_src_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstClock *gst_cuda_ipc_src_provide_clock (GstElement * elem);
|
||||
static void gst_cuda_ipc_src_set_context (GstElement * elem,
|
||||
GstContext * context);
|
||||
|
||||
static gboolean gst_cuda_ipc_src_start (GstBaseSrc * src);
|
||||
static gboolean gst_cuda_ipc_src_stop (GstBaseSrc * src);
|
||||
static gboolean gst_cuda_ipc_src_unlock (GstBaseSrc * src);
|
||||
static gboolean gst_cuda_ipc_src_unlock_stop (GstBaseSrc * src);
|
||||
static gboolean gst_cuda_ipc_src_query (GstBaseSrc * src, GstQuery * query);
|
||||
static GstCaps *gst_cuda_ipc_src_get_caps (GstBaseSrc * src, GstCaps * filter);
|
||||
static GstCaps *gst_cuda_ipc_src_fixate (GstBaseSrc * src, GstCaps * caps);
|
||||
static GstFlowReturn gst_cuda_ipc_src_create (GstBaseSrc * src, guint64 offset,
|
||||
guint size, GstBuffer ** buf);
|
||||
|
||||
#define gst_cuda_ipc_src_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstCudaIpcSrc, gst_cuda_ipc_src, GST_TYPE_BASE_SRC);
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_src_class_init (GstCudaIpcSrcClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
GstBaseSrcClass *src_class = GST_BASE_SRC_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_cuda_ipc_src_finalize;
|
||||
object_class->set_property = gst_cuda_ipc_src_set_property;
|
||||
object_class->get_property = gst_win32_video_src_get_property;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_DEVICE_ID,
|
||||
g_param_spec_int ("cuda-device-id", "CUDA Device ID",
|
||||
"CUDA device id to use (-1 = auto)", -1, G_MAXINT, DEFAULT_DEVICE_ID,
|
||||
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_ADDRESS,
|
||||
g_param_spec_string ("address", "Address",
|
||||
"Server address. Specifies name of WIN32 named pipe "
|
||||
"or unix domain socket path on Linux",
|
||||
DEFAULT_ADDRESS, (GParamFlags) (G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_PROCESSING_DEADLINE,
|
||||
g_param_spec_uint64 ("processing-deadline", "Processing deadline",
|
||||
"Maximum processing time for a buffer in nanoseconds", 0, G_MAXUINT64,
|
||||
DEFAULT_PROCESSING_DEADLINE, (GParamFlags) (G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_IO_MODE,
|
||||
g_param_spec_enum ("io-mode", "IO Mode",
|
||||
"Memory I/O mode to use. This option will be ignored if the selected "
|
||||
"IPC mode is mmap",
|
||||
GST_TYPE_CUDA_IPC_IO_MODE, DEFAULT_IO_MODE,
|
||||
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_CONN_TIMEOUT,
|
||||
g_param_spec_uint ("connection-timeout", "Connection Timeout",
|
||||
"Connection timeout in seconds (0 = never timeout)", 0, G_MAXINT,
|
||||
DEFAULT_CONN_TIMEOUT,
|
||||
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_BUFFER_SIZE,
|
||||
g_param_spec_uint ("buffer-size", "Buffer Size",
|
||||
"Size of internal buffer", 1, G_MAXINT,
|
||||
DEFAULT_BUFFER_SIZE,
|
||||
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"CUDA IPC Src", "Source/Video",
|
||||
"Receive CUDA memory from the cudaipcsrc element",
|
||||
"Seungha Yang <seungha@centricular.com>");
|
||||
gst_element_class_add_static_pad_template (element_class, &src_template);
|
||||
|
||||
element_class->provide_clock =
|
||||
GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_provide_clock);
|
||||
element_class->set_context = GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_set_context);
|
||||
|
||||
src_class->start = GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_start);
|
||||
src_class->stop = GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_stop);
|
||||
src_class->unlock = GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_unlock);
|
||||
src_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_unlock_stop);
|
||||
src_class->query = GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_query);
|
||||
src_class->get_caps = GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_get_caps);
|
||||
src_class->fixate = GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_fixate);
|
||||
src_class->create = GST_DEBUG_FUNCPTR (gst_cuda_ipc_src_create);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_cuda_ipc_src_debug, "cudaipcsrc",
|
||||
0, "cudaipcsrc");
|
||||
|
||||
gst_type_mark_as_plugin_api (GST_TYPE_CUDA_IPC_IO_MODE,
|
||||
(GstPluginAPIFlags) 0);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_src_init (GstCudaIpcSrc * self)
|
||||
{
|
||||
gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
|
||||
gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
|
||||
|
||||
self->priv = new GstCudaIpcSrcPrivate ();
|
||||
|
||||
GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
|
||||
GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_src_finalize (GObject * object)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (object);
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_src_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (object);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_ID:
|
||||
priv->device_id = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_ADDRESS:
|
||||
{
|
||||
const gchar *address = g_value_get_string (value);
|
||||
priv->address.clear ();
|
||||
|
||||
if (address)
|
||||
priv->address = address;
|
||||
break;
|
||||
}
|
||||
case PROP_PROCESSING_DEADLINE:
|
||||
{
|
||||
GstClockTime prev_val, new_val;
|
||||
prev_val = priv->processing_deadline;
|
||||
new_val = g_value_get_uint64 (value);
|
||||
priv->processing_deadline = new_val;
|
||||
|
||||
if (prev_val != new_val) {
|
||||
lk.unlock ();
|
||||
GST_DEBUG_OBJECT (self, "Posting latency message");
|
||||
gst_element_post_message (GST_ELEMENT_CAST (self),
|
||||
gst_message_new_latency (GST_OBJECT_CAST (self)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROP_IO_MODE:
|
||||
priv->io_mode = (GstCudaIpcIOMode) g_value_get_enum (value);
|
||||
break;
|
||||
case PROP_CONN_TIMEOUT:
|
||||
priv->conn_timeout = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_BUFFER_SIZE:
|
||||
priv->buffer_size = g_value_get_uint (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_win32_video_src_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (object);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_ID:
|
||||
g_value_set_int (value, priv->device_id);
|
||||
break;
|
||||
case PROP_ADDRESS:
|
||||
g_value_set_string (value, priv->address.c_str ());
|
||||
break;
|
||||
case PROP_PROCESSING_DEADLINE:
|
||||
g_value_set_uint64 (value, priv->processing_deadline);
|
||||
break;
|
||||
case PROP_IO_MODE:
|
||||
g_value_set_enum (value, priv->io_mode);
|
||||
break;
|
||||
case PROP_CONN_TIMEOUT:
|
||||
g_value_set_uint (value, priv->conn_timeout);
|
||||
break;
|
||||
case PROP_BUFFER_SIZE:
|
||||
g_value_set_uint (value, priv->buffer_size);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstClock *
|
||||
gst_cuda_ipc_src_provide_clock (GstElement * elem)
|
||||
{
|
||||
return gst_system_clock_obtain ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cuda_ipc_src_set_context (GstElement * elem, GstContext * context)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (elem);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
|
||||
gst_cuda_handle_set_context (elem, context, priv->device_id, &priv->context);
|
||||
|
||||
GST_ELEMENT_CLASS (parent_class)->set_context (elem, context);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_src_start (GstBaseSrc * src)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (src);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Start");
|
||||
|
||||
if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (self),
|
||||
priv->device_id, &priv->context)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't get CUDA context");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
priv->stream = gst_cuda_stream_new (priv->context);
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
priv->client = gst_cuda_ipc_client_new (priv->address.c_str (), priv->context,
|
||||
priv->stream, priv->io_mode, priv->conn_timeout, priv->buffer_size - 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_src_stop (GstBaseSrc * src)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (src);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stop");
|
||||
|
||||
if (priv->client)
|
||||
gst_cuda_ipc_client_stop (priv->client);
|
||||
|
||||
gst_clear_object (&priv->client);
|
||||
gst_clear_cuda_stream (&priv->stream);
|
||||
gst_clear_object (&priv->context);
|
||||
gst_clear_caps (&priv->caps);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_src_unlock (GstBaseSrc * src)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (src);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Unlock");
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
priv->flushing = true;
|
||||
if (priv->client)
|
||||
gst_cuda_ipc_client_set_flushing (priv->client, true);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_src_unlock_stop (GstBaseSrc * src)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (src);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Unlock stop");
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
priv->flushing = false;
|
||||
if (priv->client)
|
||||
gst_cuda_ipc_client_set_flushing (priv->client, false);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_cuda_ipc_src_query (GstBaseSrc * src, GstQuery * query)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (src);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_LATENCY:
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
if (GST_CLOCK_TIME_IS_VALID (priv->processing_deadline)) {
|
||||
gst_query_set_latency (query, TRUE, priv->processing_deadline,
|
||||
GST_CLOCK_TIME_NONE);
|
||||
} else {
|
||||
gst_query_set_latency (query, TRUE, 0, 0);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
case GST_QUERY_CONTEXT:
|
||||
if (gst_cuda_handle_context_query (GST_ELEMENT (self), query,
|
||||
priv->context)) {
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GST_BASE_SRC_CLASS (parent_class)->query (src, query);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_cuda_ipc_src_get_caps (GstBaseSrc * src, GstCaps * filter)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (src);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
GstCudaIpcClient *client = nullptr;
|
||||
GstCaps *caps = nullptr;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Get caps");
|
||||
|
||||
priv->lock.lock ();
|
||||
if (priv->caps)
|
||||
caps = gst_caps_ref (priv->caps);
|
||||
else if (priv->client)
|
||||
client = (GstCudaIpcClient *) gst_object_ref (priv->client);
|
||||
priv->lock.unlock ();
|
||||
|
||||
if (!caps && client)
|
||||
caps = gst_cuda_ipc_client_get_caps (priv->client);
|
||||
|
||||
if (!caps)
|
||||
caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (src));
|
||||
|
||||
if (filter) {
|
||||
GstCaps *tmp = gst_caps_intersect_full (filter,
|
||||
caps, GST_CAPS_INTERSECT_FIRST);
|
||||
gst_caps_unref (caps);
|
||||
caps = tmp;
|
||||
}
|
||||
|
||||
gst_clear_object (&client);
|
||||
GST_DEBUG_OBJECT (self, "Returning caps %" GST_PTR_FORMAT, caps);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_cuda_ipc_src_fixate (GstBaseSrc * src, GstCaps * caps)
|
||||
{
|
||||
/* We don't negotiate with server. In here, we do fixate resolution to
|
||||
* 320 x 240 (same as default of videotestsrc) which makes a little more
|
||||
* sense than 1x1 */
|
||||
caps = gst_caps_make_writable (caps);
|
||||
|
||||
for (guint i = 0; i < gst_caps_get_size (caps); i++) {
|
||||
GstStructure *s = gst_caps_get_structure (caps, i);
|
||||
|
||||
gst_structure_fixate_field_nearest_int (s, "width", 320);
|
||||
gst_structure_fixate_field_nearest_int (s, "height", 240);
|
||||
}
|
||||
|
||||
return gst_caps_fixate (caps);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_cuda_ipc_src_create (GstBaseSrc * src, guint64 offset, guint size,
|
||||
GstBuffer ** buf)
|
||||
{
|
||||
GstCudaIpcSrc *self = GST_CUDA_IPC_SRC (src);
|
||||
GstCudaIpcSrcPrivate *priv = self->priv;
|
||||
GstFlowReturn ret;
|
||||
GstSample *sample = nullptr;
|
||||
GstCaps *caps;
|
||||
GstClock *clock;
|
||||
bool is_system_clock = true;
|
||||
GstClockTime pts;
|
||||
GstClockTime base_time;
|
||||
GstClockTime now_system;
|
||||
GstClockTime now_gst;
|
||||
GstClockTime remote_pts;
|
||||
GstBuffer *buffer;
|
||||
|
||||
ret = gst_cuda_ipc_client_run (priv->client);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
|
||||
ret = gst_cuda_ipc_client_get_sample (priv->client, &sample);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
|
||||
now_system = gst_util_get_timestamp ();
|
||||
clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
|
||||
now_gst = gst_clock_get_time (clock);
|
||||
base_time = GST_ELEMENT_CAST (self)->base_time;
|
||||
is_system_clock = gst_cuda_ipc_clock_is_system (clock);
|
||||
gst_object_unref (clock);
|
||||
|
||||
buffer = gst_sample_get_buffer (sample);
|
||||
remote_pts = GST_BUFFER_PTS (buffer);
|
||||
|
||||
if (!is_system_clock) {
|
||||
GstClockTimeDiff now_pts = now_gst - base_time + remote_pts - now_system;
|
||||
|
||||
if (now_pts >= 0)
|
||||
pts = now_pts;
|
||||
else
|
||||
pts = 0;
|
||||
} else {
|
||||
if (remote_pts >= base_time) {
|
||||
pts = remote_pts - base_time;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (self,
|
||||
"Remote clock is smaller than our base time, remote %"
|
||||
GST_TIME_FORMAT ", base_time %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (remote_pts), GST_TIME_ARGS (base_time));
|
||||
pts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GST_BUFFER_PTS (buffer) = pts;
|
||||
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
caps = gst_sample_get_caps (sample);
|
||||
if (!priv->caps || !gst_caps_is_equal (priv->caps, caps)) {
|
||||
gst_caps_replace (&priv->caps, caps);
|
||||
lk.unlock ();
|
||||
gst_base_src_set_caps (src, priv->caps);
|
||||
}
|
||||
|
||||
*buf = gst_buffer_ref (buffer);
|
||||
gst_sample_unref (sample);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
33
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcsrc.h
Normal file
33
subprojects/gst-plugins-bad/sys/nvcodec/gstcudaipcsrc.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* 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/base/gstbasesrc.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/cuda/gstcuda.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CUDA_IPC_SRC (gst_cuda_ipc_src_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstCudaIpcSrc, gst_cuda_ipc_src,
|
||||
GST, CUDA_IPC_SRC, GstBaseSrc);
|
||||
|
||||
G_END_DECLS
|
28
subprojects/gst-plugins-bad/sys/nvcodec/gstnvcodecutils.h
Normal file
28
subprojects/gst-plugins-bad/sys/nvcodec/gstnvcodecutils.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* 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>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gst_cuda_ipc_client_deinit (void);
|
||||
|
||||
G_END_DECLS
|
|
@ -3,6 +3,11 @@ nvcodec_sources = [
|
|||
'gstcudaconverter.c',
|
||||
'gstcudaconvertscale.c',
|
||||
'gstcudafilter.c',
|
||||
'gstcudaipc.cpp',
|
||||
'gstcudaipcclient.cpp',
|
||||
'gstcudaipcserver.cpp',
|
||||
'gstcudaipcsink.cpp',
|
||||
'gstcudaipcsrc.cpp',
|
||||
'gstcudamemorycopy.c',
|
||||
'gstcuvidloader.c',
|
||||
'gstnvav1dec.cpp',
|
||||
|
@ -28,6 +33,16 @@ nvmm_sources = [
|
|||
'gstcudanvmm.c',
|
||||
]
|
||||
|
||||
nvcodec_win32_sources = [
|
||||
'gstcudaipcclient_win32.cpp',
|
||||
'gstcudaipcserver_win32.cpp',
|
||||
]
|
||||
|
||||
nvcodec_unix_sources = [
|
||||
'gstcudaipcclient_unix.cpp',
|
||||
'gstcudaipcserver_unix.cpp',
|
||||
]
|
||||
|
||||
if get_option('nvcodec').disabled()
|
||||
subdir_done()
|
||||
endif
|
||||
|
@ -41,6 +56,7 @@ endif
|
|||
|
||||
plugin_incdirs = [configinc, cuda_stubinc]
|
||||
extra_args = ['-DGST_USE_UNSTABLE_API']
|
||||
extra_deps = []
|
||||
|
||||
if gstgl_dep.found()
|
||||
extra_args += ['-DHAVE_CUDA_GST_GL']
|
||||
|
@ -60,6 +76,13 @@ if host_system == 'linux'
|
|||
extra_args += ['-DHAVE_NVCODEC_NVMM']
|
||||
nvcodec_sources += nvmm_sources
|
||||
endif
|
||||
|
||||
gio_unix_dep = dependency('gio-unix-2.0', required : get_option('nvcodec'))
|
||||
extra_deps += [gio_dep, gio_unix_dep]
|
||||
|
||||
nvcodec_sources += nvcodec_unix_sources
|
||||
else
|
||||
nvcodec_sources += nvcodec_win32_sources
|
||||
endif
|
||||
|
||||
if cc.get_id() != 'msvc'
|
||||
|
@ -82,7 +105,9 @@ gstnvcodec = library('gstnvcodec',
|
|||
c_args : gst_plugins_bad_args + extra_args,
|
||||
cpp_args : gst_plugins_bad_args + extra_args,
|
||||
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] + extra_deps,
|
||||
override_options : ['cpp_std=c++14'],
|
||||
install : true,
|
||||
install_dir : plugins_install_dir,
|
||||
|
|
|
@ -49,6 +49,9 @@
|
|||
#endif
|
||||
#include "gstnvh264encoder.h"
|
||||
#include "gstnvh265encoder.h"
|
||||
#include "gstcudaipcsink.h"
|
||||
#include "gstcudaipcsrc.h"
|
||||
#include "gstnvcodecutils.h"
|
||||
|
||||
GST_DEBUG_CATEGORY (gst_nvcodec_debug);
|
||||
GST_DEBUG_CATEGORY (gst_nvdec_debug);
|
||||
|
@ -61,6 +64,12 @@ GST_DEBUG_CATEGORY (gst_cuda_nvmm_debug);
|
|||
|
||||
#define GST_CAT_DEFAULT gst_nvcodec_debug
|
||||
|
||||
static void
|
||||
plugin_deinit (gpointer data)
|
||||
{
|
||||
gst_cuda_ipc_client_deinit ();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
|
@ -245,8 +254,12 @@ plugin_init (GstPlugin * plugin)
|
|||
}
|
||||
|
||||
gst_cuda_memory_copy_register (plugin, GST_RANK_NONE);
|
||||
|
||||
gst_cuda_filter_plugin_init (plugin);
|
||||
gst_element_register (plugin,
|
||||
"cudaipcsink", GST_RANK_NONE, GST_TYPE_CUDA_IPC_SINK);
|
||||
gst_element_register (plugin,
|
||||
"cudaipcsrc", GST_RANK_NONE, GST_TYPE_CUDA_IPC_SRC);
|
||||
|
||||
gst_cuda_memory_init_once ();
|
||||
|
||||
#ifdef HAVE_NVCODEC_NVMM
|
||||
|
@ -255,6 +268,10 @@ plugin_init (GstPlugin * plugin)
|
|||
}
|
||||
#endif
|
||||
|
||||
g_object_set_data_full (G_OBJECT (plugin),
|
||||
"plugin-nvcodec-shutdown", (gpointer) "shutdown-data",
|
||||
(GDestroyNotify) plugin_deinit);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue