mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-22 16:26:39 +00:00
d3d12: Add IPC elements
Adding d3d12ipcsink and d3d12ipcsrc elements, equivalent to D3D11 ones. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6428>
This commit is contained in:
parent
0006ad1f86
commit
0c148a96db
12 changed files with 3986 additions and 0 deletions
397
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipc.cpp
Normal file
397
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipc.cpp
Normal file
|
@ -0,0 +1,397 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2024 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 "gstd3d12ipc.h"
|
||||
#include <string.h>
|
||||
#include <cctype>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <algorithm>
|
||||
|
||||
#define GST_D3D12_IPC_MAGIC_NUMBER 0xD3D1210C
|
||||
|
||||
bool
|
||||
gst_d3d12_ipc_pkt_identify (std::vector < guint8 > &buf,
|
||||
GstD3D12IpcPacketHeader & header)
|
||||
{
|
||||
g_return_val_if_fail (buf.size () >= GST_D3D12_IPC_PKT_HEADER_SIZE, false);
|
||||
|
||||
memcpy (&header, &buf[0], GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
if (header.magic != GST_D3D12_IPC_MAGIC_NUMBER)
|
||||
return false;
|
||||
|
||||
buf.resize (GST_D3D12_IPC_PKT_HEADER_SIZE + header.payload_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
gst_d3d12_ipc_pkt_build_config (std::vector < guint8 > &buf,
|
||||
DWORD pid, gint64 adapter_luid, const HANDLE fence_handle, GstCaps * caps)
|
||||
{
|
||||
GstD3D12IpcPacketHeader 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 = GstD3D12IpcPktType::CONFIG;
|
||||
header.magic = GST_D3D12_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = sizeof (DWORD) + sizeof (gint64) + sizeof (HANDLE) +
|
||||
caps_size;
|
||||
|
||||
buf.resize (GST_D3D12_IPC_PKT_HEADER_SIZE + header.payload_size);
|
||||
|
||||
ptr = &buf[0];
|
||||
|
||||
memcpy (ptr, &header, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
ptr += GST_D3D12_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (ptr, &pid, sizeof (DWORD));
|
||||
ptr += sizeof (DWORD);
|
||||
|
||||
memcpy (ptr, &adapter_luid, sizeof (gint64));
|
||||
ptr += sizeof (gint64);
|
||||
|
||||
memcpy (ptr, &fence_handle, sizeof (HANDLE));
|
||||
ptr += sizeof (HANDLE);
|
||||
|
||||
strcpy ((char *) ptr, caps_str);
|
||||
g_free (caps_str);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
gst_d3d12_ipc_pkt_parse_config (std::vector < guint8 > &buf,
|
||||
DWORD & pid, gint64 & adapter_luid, HANDLE & fence_handle, GstCaps ** caps)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
const guint8 *ptr;
|
||||
std::string str;
|
||||
|
||||
g_return_val_if_fail (buf.size () >
|
||||
GST_D3D12_IPC_PKT_HEADER_SIZE + sizeof (gint64) + sizeof (HANDLE), false);
|
||||
g_return_val_if_fail (caps, false);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (&header, ptr, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
if (header.type != GstD3D12IpcPktType::CONFIG ||
|
||||
header.magic != GST_D3D12_IPC_MAGIC_NUMBER ||
|
||||
header.payload_size <= sizeof (gint64)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr += GST_D3D12_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (&pid, ptr, sizeof (DWORD));
|
||||
ptr += sizeof (DWORD);
|
||||
|
||||
memcpy (&adapter_luid, ptr, sizeof (gint64));
|
||||
ptr += sizeof (gint64);
|
||||
|
||||
memcpy (&fence_handle, ptr, sizeof (HANDLE));
|
||||
ptr += sizeof (HANDLE);
|
||||
|
||||
*caps = gst_caps_from_string ((const gchar *) ptr);
|
||||
if (*caps == nullptr)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
gst_d3d12_ipc_pkt_build_need_data (std::vector < guint8 > &buf)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
|
||||
header.type = GstD3D12IpcPktType::NEED_DATA;
|
||||
header.magic = GST_D3D12_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = 0;
|
||||
|
||||
buf.resize (GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
memcpy (&buf[0], &header, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
bool
|
||||
gst_d3d12_ipc_pkt_build_have_data (std::vector < guint8 > &buf,
|
||||
GstClockTime pts, const GstD3D12IpcMemLayout & layout,
|
||||
const HANDLE handle, guint64 fence_value, GstCaps * caps)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
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 = GstD3D12IpcPktType::HAVE_DATA;
|
||||
header.magic = GST_D3D12_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = sizeof (GstClockTime) + sizeof (GstD3D12IpcMemLayout) +
|
||||
sizeof (HANDLE) + sizeof (guint64) + caps_size;
|
||||
|
||||
buf.resize (GST_D3D12_IPC_PKT_HEADER_SIZE + header.payload_size);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (ptr, &header, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
ptr += GST_D3D12_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (ptr, &pts, sizeof (GstClockTime));
|
||||
ptr += sizeof (GstClockTime);
|
||||
|
||||
memcpy (ptr, &layout, sizeof (GstD3D12IpcMemLayout));
|
||||
ptr += sizeof (GstD3D12IpcMemLayout);
|
||||
|
||||
memcpy (ptr, &handle, sizeof (HANDLE));
|
||||
ptr += sizeof (HANDLE);
|
||||
|
||||
memcpy (ptr, &fence_value, sizeof (guint64));
|
||||
ptr += sizeof (guint64);
|
||||
|
||||
if (caps) {
|
||||
*ptr = 1;
|
||||
ptr++;
|
||||
|
||||
strcpy ((char *) ptr, caps_str);
|
||||
} else {
|
||||
*ptr = 0;
|
||||
}
|
||||
|
||||
g_free (caps_str);
|
||||
|
||||
return true;
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
bool
|
||||
gst_d3d12_ipc_pkt_parse_have_data (const std::vector < guint8 > &buf,
|
||||
GstClockTime & pts, GstD3D12IpcMemLayout & layout,
|
||||
HANDLE & handle, guint64 & fence_value, GstCaps ** caps)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
const guint8 *ptr;
|
||||
std::string str;
|
||||
|
||||
g_return_val_if_fail (buf.size () >
|
||||
GST_D3D12_IPC_PKT_HEADER_SIZE + sizeof (GstClockTime) +
|
||||
sizeof (GstD3D12IpcMemLayout) + sizeof (HANDLE) + sizeof (guint64),
|
||||
false);
|
||||
g_return_val_if_fail (caps, false);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (&header, ptr, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
if (header.type != GstD3D12IpcPktType::HAVE_DATA ||
|
||||
header.magic != GST_D3D12_IPC_MAGIC_NUMBER ||
|
||||
header.payload_size <= sizeof (GstClockTime) +
|
||||
sizeof (GstD3D12IpcMemLayout) + sizeof (HANDLE) + sizeof (guint64)) {
|
||||
return false;
|
||||
}
|
||||
ptr += GST_D3D12_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (&pts, ptr, sizeof (GstClockTime));
|
||||
ptr += sizeof (GstClockTime);
|
||||
|
||||
memcpy (&layout, ptr, sizeof (GstD3D12IpcMemLayout));
|
||||
ptr += sizeof (GstD3D12IpcMemLayout);
|
||||
|
||||
memcpy (&handle, ptr, sizeof (HANDLE));
|
||||
ptr += sizeof (HANDLE);
|
||||
|
||||
memcpy (&fence_value, ptr, sizeof (guint64));
|
||||
ptr += sizeof (guint64);
|
||||
|
||||
if (*ptr) {
|
||||
ptr++;
|
||||
|
||||
*caps = gst_caps_from_string ((const gchar *) ptr);
|
||||
if (*caps == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
gst_d3d12_ipc_pkt_build_read_done (std::vector < guint8 > &buf)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
|
||||
header.type = GstD3D12IpcPktType::READ_DONE;
|
||||
header.magic = GST_D3D12_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = 0;
|
||||
|
||||
buf.resize (GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
memcpy (&buf[0], &header, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
gst_d3d12_ipc_pkt_build_release_data (std::vector < guint8 > &buf,
|
||||
const HANDLE handle)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
guint8 *ptr;
|
||||
|
||||
header.type = GstD3D12IpcPktType::RELEASE_DATA;
|
||||
header.magic = GST_D3D12_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = sizeof (HANDLE);
|
||||
|
||||
buf.resize (GST_D3D12_IPC_PKT_HEADER_SIZE + header.payload_size);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (ptr, &header, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
ptr += GST_D3D12_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (ptr, &handle, sizeof (HANDLE));
|
||||
}
|
||||
|
||||
bool
|
||||
gst_d3d12_ipc_pkt_parse_release_data (std::vector < guint8 > &buf,
|
||||
HANDLE & handle)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
const guint8 *ptr;
|
||||
|
||||
g_return_val_if_fail (buf.size () >=
|
||||
GST_D3D12_IPC_PKT_HEADER_SIZE + sizeof (HANDLE), false);
|
||||
|
||||
ptr = &buf[0];
|
||||
memcpy (&header, ptr, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
if (header.type != GstD3D12IpcPktType::RELEASE_DATA ||
|
||||
header.magic != GST_D3D12_IPC_MAGIC_NUMBER ||
|
||||
header.payload_size != sizeof (HANDLE)) {
|
||||
return false;
|
||||
}
|
||||
ptr += GST_D3D12_IPC_PKT_HEADER_SIZE;
|
||||
|
||||
memcpy (&handle, ptr, sizeof (HANDLE));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
gst_d3d12_ipc_pkt_build_eos (std::vector < guint8 > &buf)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
|
||||
header.type = GstD3D12IpcPktType::EOS;
|
||||
header.magic = GST_D3D12_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = 0;
|
||||
|
||||
buf.resize (GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
memcpy (&buf[0], &header, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
gst_d3d12_ipc_pkt_build_fin (std::vector < guint8 > &buf)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
|
||||
header.type = GstD3D12IpcPktType::FIN;
|
||||
header.magic = GST_D3D12_IPC_MAGIC_NUMBER;
|
||||
header.payload_size = 0;
|
||||
|
||||
buf.resize (GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
|
||||
memcpy (&buf[0], &header, GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
bool
|
||||
gst_d3d12_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;
|
||||
}
|
||||
|
||||
std::string
|
||||
gst_d3d12_ipc_wstring_to_string (const std::wstring & str)
|
||||
{
|
||||
std::wstring_convert < std::codecvt_utf8 < wchar_t >>conv;
|
||||
return conv.to_bytes (str);
|
||||
}
|
||||
|
||||
std::wstring
|
||||
gst_d3d12_ipc_string_to_wstring (const std::string & str)
|
||||
{
|
||||
std::wstring_convert < std::codecvt_utf8 < wchar_t >>conv;
|
||||
return conv.from_bytes (str);
|
||||
}
|
||||
|
||||
/* *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_d3d12_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::string ret = gst_d3d12_ipc_wstring_to_string (buffer);
|
||||
rtrim (ret);
|
||||
|
||||
return ret;
|
||||
}
|
149
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipc.h
Normal file
149
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipc.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2024 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 "gstd3d12.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
* Communication Sequence
|
||||
*
|
||||
* +--------+ +--------+
|
||||
* | client | | server |
|
||||
* +--------+ +--------+
|
||||
* | |
|
||||
* | |
|
||||
* |<---------- CONFIG ------------+
|
||||
* | |
|
||||
* +--------- NEED-DATA ---------->|
|
||||
* | +-------+
|
||||
* | | Export
|
||||
* | | D3D12 memory
|
||||
* | |<------+
|
||||
* |<-------- HAVE-DATA -----------+
|
||||
* +--------+ |
|
||||
* Import | |
|
||||
* D3D12 memory | |
|
||||
* +------->+ |
|
||||
* +--------- READ-DONE ---------->|
|
||||
* +--------+ |
|
||||
* Release | |
|
||||
* D3D12 memory | |
|
||||
* +------->| |
|
||||
* +-------- RELEASE-DATA -------->|
|
||||
* | |
|
||||
* +--------- NEED-DATA ---------->|
|
||||
* | |
|
||||
* |<----------- EOS --------------+
|
||||
* +--------+ |
|
||||
* Cleanup all | |
|
||||
* shared resources | |
|
||||
* +------->| |
|
||||
* +------------ FIN ------------->|
|
||||
*/
|
||||
|
||||
enum class GstD3D12IpcPktType : guint8
|
||||
{
|
||||
UNKNOWN,
|
||||
CONFIG,
|
||||
NEED_DATA,
|
||||
HAVE_DATA,
|
||||
READ_DONE,
|
||||
RELEASE_DATA,
|
||||
EOS,
|
||||
FIN,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct GstD3D12IpcPacketHeader
|
||||
{
|
||||
GstD3D12IpcPktType type;
|
||||
guint32 payload_size;
|
||||
guint32 magic;
|
||||
};
|
||||
|
||||
struct GstD3D12IpcMemLayout
|
||||
{
|
||||
guint32 pitch;
|
||||
guint32 offset[4];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
constexpr guint GST_D3D12_IPC_PKT_HEADER_SIZE = sizeof (GstD3D12IpcPacketHeader);
|
||||
|
||||
#define GST_D3D12_IPC_FORMATS \
|
||||
"{ RGBA64_LE, RGB10A2_LE, BGRA, RGBA, BGRx, RGBx, VUYA, NV12, NV21, " \
|
||||
"P010_10LE, P012_LE, P016_LE }"
|
||||
|
||||
bool gst_d3d12_ipc_pkt_identify (std::vector<guint8> & buf,
|
||||
GstD3D12IpcPacketHeader & header);
|
||||
|
||||
bool gst_d3d12_ipc_pkt_build_config (std::vector<guint8> & buf,
|
||||
DWORD pid,
|
||||
gint64 adapter_luid,
|
||||
const HANDLE fence_handle,
|
||||
GstCaps * caps);
|
||||
|
||||
bool gst_d3d12_ipc_pkt_parse_config (std::vector<guint8> & buf,
|
||||
DWORD & pid,
|
||||
gint64 & adapter_luid,
|
||||
HANDLE & fence_handle,
|
||||
GstCaps ** caps);
|
||||
|
||||
void gst_d3d12_ipc_pkt_build_need_data (std::vector<guint8> & buf);
|
||||
|
||||
bool gst_d3d12_ipc_pkt_build_have_data (std::vector<guint8> & buf,
|
||||
GstClockTime pts,
|
||||
const GstD3D12IpcMemLayout & layout,
|
||||
const HANDLE handle,
|
||||
guint64 fence_value,
|
||||
GstCaps * caps);
|
||||
|
||||
bool gst_d3d12_ipc_pkt_parse_have_data (const std::vector<guint8> & buf,
|
||||
GstClockTime & pts,
|
||||
GstD3D12IpcMemLayout & layout,
|
||||
HANDLE & handle,
|
||||
guint64 & fence_value,
|
||||
GstCaps ** caps);
|
||||
|
||||
void gst_d3d12_ipc_pkt_build_read_done (std::vector<guint8> & buf);
|
||||
|
||||
void gst_d3d12_ipc_pkt_build_release_data (std::vector<guint8> & buf,
|
||||
const HANDLE handle);
|
||||
|
||||
bool gst_d3d12_ipc_pkt_parse_release_data (std::vector<guint8> & buf,
|
||||
HANDLE & handle);
|
||||
|
||||
void gst_d3d12_ipc_pkt_build_eos (std::vector<guint8> & buf);
|
||||
|
||||
void gst_d3d12_ipc_pkt_build_fin (std::vector<guint8> & buf);
|
||||
|
||||
bool gst_d3d12_ipc_clock_is_system (GstClock * clock);
|
||||
|
||||
std::string gst_d3d12_ipc_wstring_to_string (const std::wstring & str);
|
||||
|
||||
std::wstring gst_d3d12_ipc_string_to_wstring (const std::string & str);
|
||||
|
||||
std::string gst_d3d12_ipc_win32_error_to_string (guint err);
|
||||
|
1158
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcclient.cpp
Normal file
1158
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcclient.cpp
Normal file
File diff suppressed because it is too large
Load diff
59
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcclient.h
Normal file
59
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcclient.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2024 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 "gstd3d12ipc.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
enum GstD3D12IpcIOMode
|
||||
{
|
||||
GST_D3D12_IPC_IO_COPY,
|
||||
GST_D3D12_IPC_IO_IMPORT,
|
||||
};
|
||||
|
||||
#define GST_TYPE_D3D12_IPC_IO_MODE (gst_d3d12_ipc_io_mode_get_type ())
|
||||
GType gst_d3d12_ipc_io_mode_get_type (void);
|
||||
|
||||
#define GST_TYPE_D3D12_IPC_CLIENT (gst_d3d12_ipc_client_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstD3D12IpcClient, gst_d3d12_ipc_client,
|
||||
GST, D3D12_IPC_CLIENT, GstObject);
|
||||
|
||||
void gst_d3d12_ipc_client_deinit (void);
|
||||
|
||||
GstD3D12IpcClient * gst_d3d12_ipc_client_new (const std::string & address,
|
||||
GstD3D12Device * device,
|
||||
GstD3D12IpcIOMode io_mode,
|
||||
guint timeout);
|
||||
|
||||
GstFlowReturn gst_d3d12_ipc_client_get_sample (GstD3D12IpcClient * client,
|
||||
GstSample ** sample);
|
||||
|
||||
void gst_d3d12_ipc_client_set_flushing (GstD3D12IpcClient * client,
|
||||
bool flushing);
|
||||
|
||||
GstCaps * gst_d3d12_ipc_client_get_caps (GstD3D12IpcClient * client);
|
||||
|
||||
GstFlowReturn gst_d3d12_ipc_client_run (GstD3D12IpcClient * client);
|
||||
|
||||
void gst_d3d12_ipc_client_stop (GstD3D12IpcClient * client);
|
||||
|
||||
G_END_DECLS
|
870
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcserver.cpp
Normal file
870
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcserver.cpp
Normal file
|
@ -0,0 +1,870 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2024 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 "gstd3d12ipcserver.h"
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <wrl.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_d3d12_ipc_server_debug);
|
||||
#define GST_CAT_DEFAULT gst_d3d12_ipc_server_debug
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
struct GstD3D12IpcServerData
|
||||
{
|
||||
~GstD3D12IpcServerData ()
|
||||
{
|
||||
if (sample)
|
||||
gst_sample_unref (sample);
|
||||
}
|
||||
|
||||
GstSample *sample = nullptr;
|
||||
HANDLE handle = nullptr;
|
||||
guint64 fence_val = 0;
|
||||
GstD3D12IpcMemLayout layout;
|
||||
GstClockTime pts;
|
||||
guint64 seq_num;
|
||||
};
|
||||
|
||||
struct GstD3D12IpcServerConn : public OVERLAPPED
|
||||
{
|
||||
GstD3D12IpcServerConn (HANDLE pipe_handle) : pipe (pipe_handle)
|
||||
{
|
||||
OVERLAPPED *parent = static_cast<OVERLAPPED *> (this);
|
||||
parent->Internal = 0;
|
||||
parent->InternalHigh = 0;
|
||||
parent->Offset = 0;
|
||||
parent->OffsetHigh = 0;
|
||||
|
||||
client_msg.resize (GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
server_msg.resize (GST_D3D12_IPC_PKT_HEADER_SIZE);
|
||||
}
|
||||
|
||||
~GstD3D12IpcServerConn()
|
||||
{
|
||||
if (pipe != INVALID_HANDLE_VALUE) {
|
||||
CancelIo (pipe);
|
||||
DisconnectNamedPipe (pipe);
|
||||
CloseHandle (pipe);
|
||||
}
|
||||
|
||||
gst_clear_caps (&caps);
|
||||
}
|
||||
|
||||
GstD3D12IpcServer *server;
|
||||
|
||||
HANDLE pipe;
|
||||
|
||||
GstD3D12IpcPktType type;
|
||||
std::vector<guint8> client_msg;
|
||||
std::vector<guint8> server_msg;
|
||||
std::shared_ptr<GstD3D12IpcServerData> data;
|
||||
std::vector<std::shared_ptr<GstD3D12IpcServerData>> peer_handles;
|
||||
GstCaps *caps = nullptr;
|
||||
guint64 seq_num = 0;
|
||||
guint id;
|
||||
bool eos = false;
|
||||
bool pending_have_data = false;
|
||||
bool configured = false;
|
||||
};
|
||||
|
||||
struct GstD3D12IpcServerPrivate
|
||||
{
|
||||
GstD3D12IpcServerPrivate ()
|
||||
{
|
||||
cancellable = CreateEvent (nullptr, TRUE, FALSE, nullptr);
|
||||
wakeup_event = CreateEvent (nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
shutdown = false;
|
||||
aborted = false;
|
||||
}
|
||||
|
||||
~GstD3D12IpcServerPrivate ()
|
||||
{
|
||||
CloseHandle (cancellable);
|
||||
CloseHandle (wakeup_event);
|
||||
if (fence_handle)
|
||||
CloseHandle (fence_handle);
|
||||
gst_clear_object (&device);
|
||||
}
|
||||
|
||||
GstD3D12Device *device = nullptr;
|
||||
gint64 adapter_luid = 0;
|
||||
std::mutex lock;
|
||||
guint64 seq_num = 0;
|
||||
guint next_conn_id = 0;
|
||||
std::unordered_map<guint, std::shared_ptr<GstD3D12IpcServerConn>> conn_map;
|
||||
GThread *loop_thread = nullptr;
|
||||
std::atomic<bool>shutdown;
|
||||
std::atomic<bool>aborted;
|
||||
std::shared_ptr<GstD3D12IpcServerData> data;
|
||||
std::string address;
|
||||
HANDLE cancellable;
|
||||
HANDLE wakeup_event;
|
||||
DWORD pid;
|
||||
HANDLE fence_handle = nullptr;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
struct _GstD3D12IpcServer
|
||||
{
|
||||
GstObject parent;
|
||||
|
||||
GstD3D12IpcServerPrivate *priv;
|
||||
};
|
||||
|
||||
static void gst_d3d12_ipc_server_dispose (GObject * object);
|
||||
static void gst_d3d12_ipc_server_finalize (GObject * object);
|
||||
static void gst_d3d12_ipc_server_on_idle (GstD3D12IpcServer * self);
|
||||
static void gst_d3d12_ipc_server_send_msg (GstD3D12IpcServer * self,
|
||||
GstD3D12IpcServerConn * conn);
|
||||
static void gst_d3d12_ipc_server_wait_msg (GstD3D12IpcServer * self,
|
||||
GstD3D12IpcServerConn * conn);
|
||||
|
||||
#define gst_d3d12_ipc_server_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstD3D12IpcServer, gst_d3d12_ipc_server, GST_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_class_init (GstD3D12IpcServerClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_d3d12_ipc_server_finalize;
|
||||
object_class->dispose = gst_d3d12_ipc_server_dispose;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_d3d12_ipc_server_debug, "d3d12ipcserver",
|
||||
0, "d3d12ipcserver");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_init (GstD3D12IpcServer * self)
|
||||
{
|
||||
self->priv = new GstD3D12IpcServerPrivate ();
|
||||
self->priv->pid = GetCurrentProcessId ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_dispose (GObject * object)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SERVER (object);
|
||||
auto priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "dispose");
|
||||
|
||||
SetEvent (priv->cancellable);
|
||||
|
||||
g_clear_pointer (&priv->loop_thread, g_thread_join);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_finalize (GObject * object)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SERVER (object);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "finalize");
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static HANDLE
|
||||
gst_cuda_ipc_server_win32_create_pipe (GstD3D12IpcServer * self,
|
||||
OVERLAPPED * overlap, bool &io_pending)
|
||||
{
|
||||
auto 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_d3d12_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_d3d12_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_d3d12_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_d3d12_ipc_server_close_connection (GstD3D12IpcServer * self,
|
||||
GstD3D12IpcServerConn * conn)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
|
||||
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");
|
||||
SetEvent (priv->cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_have_data (GstD3D12IpcServer * self,
|
||||
GstD3D12IpcServerConn * conn)
|
||||
{
|
||||
GstCaps *caps;
|
||||
|
||||
if (!conn->data) {
|
||||
GST_ERROR_OBJECT (self, "Have no data to send, conn-id: %u", conn->id);
|
||||
gst_d3d12_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;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Sending HAVE-DATA with handle \"%p\", conn-id :%u",
|
||||
conn->data->handle, conn->id);
|
||||
|
||||
if (!gst_d3d12_ipc_pkt_build_have_data (conn->server_msg, conn->data->pts,
|
||||
conn->data->layout, conn->data->handle, conn->data->fence_val,
|
||||
caps)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't build HAVE-DATA pkt, conn-id: %u",
|
||||
conn->id);
|
||||
gst_d3d12_ipc_server_close_connection (self, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
conn->type = GstD3D12IpcPktType::HAVE_DATA;
|
||||
gst_d3d12_ipc_server_send_msg (self, conn);
|
||||
}
|
||||
|
||||
static bool
|
||||
gst_d3d12_ipc_server_on_release_data (GstD3D12IpcServer * self,
|
||||
GstD3D12IpcServerConn * conn)
|
||||
{
|
||||
bool found = false;
|
||||
HANDLE handle = nullptr;
|
||||
|
||||
if (!gst_d3d12_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;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "RELEASE-DATA \"%p\", conn-id: %u", handle, conn->id);
|
||||
|
||||
for (auto it = conn->peer_handles.begin (); it != conn->peer_handles.end ();
|
||||
it++) {
|
||||
auto other = (*it)->handle;
|
||||
if (handle == other) {
|
||||
found = true;
|
||||
conn->peer_handles.erase (it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
GST_WARNING_OBJECT (self,
|
||||
"Unexpected name to remove, conn-id: %u", conn->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Client is holding %" G_GSIZE_FORMAT " handles",
|
||||
conn->peer_handles.size ());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_wait_msg_finish (GstD3D12IpcServer * server,
|
||||
GstD3D12IpcServerConn * conn)
|
||||
{
|
||||
GstD3D12IpcPacketHeader header;
|
||||
|
||||
if (!gst_d3d12_ipc_pkt_identify (conn->client_msg, header)) {
|
||||
GST_ERROR_OBJECT (server, "Broken header, conn-id: %u", conn->id);
|
||||
gst_d3d12_ipc_server_close_connection (server, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (header.type) {
|
||||
case GstD3D12IpcPktType::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_d3d12_ipc_server_on_idle (server);
|
||||
return;
|
||||
}
|
||||
gst_d3d12_ipc_server_have_data (server, conn);
|
||||
break;
|
||||
case GstD3D12IpcPktType::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_d3d12_ipc_server_close_connection (server, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
conn->peer_handles.push_back (conn->data);
|
||||
conn->data = nullptr;
|
||||
gst_d3d12_ipc_server_wait_msg (server, conn);
|
||||
break;
|
||||
case GstD3D12IpcPktType::RELEASE_DATA:
|
||||
GST_LOG_OBJECT (server, "RELEASE-DATA, conn-id: %u", conn->id);
|
||||
if (!gst_d3d12_ipc_server_on_release_data (server, conn))
|
||||
gst_d3d12_ipc_server_close_connection (server, conn);
|
||||
else
|
||||
gst_d3d12_ipc_server_wait_msg (server, conn);
|
||||
break;
|
||||
case GstD3D12IpcPktType::FIN:
|
||||
GST_DEBUG_OBJECT (server, "FIN, conn-id %u", conn->id);
|
||||
gst_d3d12_ipc_server_close_connection (server, conn);
|
||||
break;
|
||||
default:
|
||||
GST_ERROR_OBJECT (server, "Unexpected packet, conn-id: %u", conn->id);
|
||||
gst_d3d12_ipc_server_close_connection (server, conn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
gst_d3d12_ipc_server_payload_finish (DWORD error_code, DWORD size,
|
||||
OVERLAPPED * overlap)
|
||||
{
|
||||
GstD3D12IpcServerConn *conn =
|
||||
static_cast < GstD3D12IpcServerConn * >(overlap);
|
||||
auto self = conn->server;
|
||||
|
||||
if (error_code != ERROR_SUCCESS) {
|
||||
auto err = gst_d3d12_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_d3d12_ipc_server_close_connection (self, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
gst_d3d12_ipc_server_wait_msg_finish (self, conn);
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
gst_d3d12_ipc_server_wait_msg_header_finish (DWORD error_code, DWORD size,
|
||||
OVERLAPPED * overlap)
|
||||
{
|
||||
GstD3D12IpcServerConn *conn =
|
||||
static_cast < GstD3D12IpcServerConn * >(overlap);
|
||||
auto self = conn->server;
|
||||
GstD3D12IpcPacketHeader header;
|
||||
|
||||
if (error_code != ERROR_SUCCESS) {
|
||||
auto err = gst_d3d12_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_d3d12_ipc_server_close_connection (self, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gst_d3d12_ipc_pkt_identify (conn->client_msg, header)) {
|
||||
GST_ERROR_OBJECT (self, "Broken header");
|
||||
gst_d3d12_ipc_server_close_connection (self, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.payload_size == 0) {
|
||||
gst_d3d12_ipc_server_wait_msg_finish (conn->server, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Reading payload");
|
||||
|
||||
if (!ReadFileEx (conn->pipe, &conn->client_msg[0] +
|
||||
GST_D3D12_IPC_PKT_HEADER_SIZE, header.payload_size, conn,
|
||||
gst_d3d12_ipc_server_payload_finish)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_d3d12_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (self, "ReadFileEx failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
gst_d3d12_ipc_server_close_connection (self, conn);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_wait_msg (GstD3D12IpcServer * self,
|
||||
GstD3D12IpcServerConn * conn)
|
||||
{
|
||||
if (!ReadFileEx (conn->pipe, &conn->client_msg[0],
|
||||
GST_D3D12_IPC_PKT_HEADER_SIZE, conn,
|
||||
gst_d3d12_ipc_server_wait_msg_header_finish)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_d3d12_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (self, "ReadFileEx failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
gst_d3d12_ipc_server_close_connection (self, conn);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_eos (GstD3D12IpcServer * self,
|
||||
GstD3D12IpcServerConn * conn)
|
||||
{
|
||||
gst_d3d12_ipc_pkt_build_eos (conn->server_msg);
|
||||
conn->eos = true;
|
||||
conn->type = GstD3D12IpcPktType::EOS;
|
||||
|
||||
gst_d3d12_ipc_server_send_msg (self, conn);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_config_data (GstD3D12IpcServer * self,
|
||||
GstD3D12IpcServerConn * conn)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
GstCaps *caps = gst_sample_get_caps (conn->data->sample);
|
||||
|
||||
gst_caps_replace (&conn->caps, caps);
|
||||
|
||||
gst_d3d12_ipc_pkt_build_config (conn->server_msg,
|
||||
priv->pid, priv->adapter_luid, priv->fence_handle, conn->caps);
|
||||
conn->type = GstD3D12IpcPktType::CONFIG;
|
||||
|
||||
GST_LOG_OBJECT (self, "Sending CONFIG, conn-id %u", conn->id);
|
||||
gst_d3d12_ipc_server_send_msg (self, conn);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_on_idle (GstD3D12IpcServer * self)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
|
||||
GST_LOG_OBJECT (self, "idle");
|
||||
|
||||
if (priv->shutdown) {
|
||||
GST_DEBUG_OBJECT (self, "We are stopping");
|
||||
|
||||
if (priv->conn_map.empty ()) {
|
||||
GST_DEBUG_OBJECT (self, "All connections were closed");
|
||||
SetEvent (priv->cancellable);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector < std::shared_ptr < GstD3D12IpcServerConn >> 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 (self, "Sending EOS to conn-id: %u", it->id);
|
||||
gst_d3d12_ipc_server_eos (self, it.get ());
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Have %" G_GSIZE_FORMAT " alive connections",
|
||||
priv->conn_map.size());
|
||||
|
||||
size_t num_closed = 0;
|
||||
for (auto it : priv->conn_map) {
|
||||
auto conn = it.second;
|
||||
GST_DEBUG_OBJECT (self, "conn-id %u"
|
||||
" peer handle size %" G_GSIZE_FORMAT, conn->id,
|
||||
conn->peer_handles.size ());
|
||||
|
||||
/* Cannot erase conn since it's still referenced.
|
||||
* Manually close connection */
|
||||
if (conn->peer_handles.empty ()) {
|
||||
if (conn->pipe != INVALID_HANDLE_VALUE) {
|
||||
CancelIo (conn->pipe);
|
||||
DisconnectNamedPipe (conn->pipe);
|
||||
CloseHandle (conn->pipe);
|
||||
conn->pipe = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
num_closed++;
|
||||
}
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
if (priv->conn_map.size () == num_closed) {
|
||||
GST_DEBUG_OBJECT (self, "All connections were closed");
|
||||
SetEvent (priv->cancellable);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->conn_map.empty ()) {
|
||||
GST_LOG_OBJECT (self, "Have no connection");
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
if (!priv->data)
|
||||
return;
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
std::vector < std::shared_ptr < GstD3D12IpcServerConn >> to_config_data;
|
||||
std::vector < std::shared_ptr < GstD3D12IpcServerConn >> 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_d3d12_ipc_server_config_data (self, it.get ());
|
||||
|
||||
for (auto it: to_send_have_data)
|
||||
gst_d3d12_ipc_server_have_data (self, it.get ());
|
||||
/* *INDENT-ON* */
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
gst_d3d12_ipc_server_send_msg_finish (DWORD error_code, DWORD size,
|
||||
OVERLAPPED * overlap)
|
||||
{
|
||||
GstD3D12IpcServerConn *conn =
|
||||
static_cast < GstD3D12IpcServerConn * >(overlap);
|
||||
auto self = conn->server;
|
||||
|
||||
if (error_code != ERROR_SUCCESS) {
|
||||
auto err = gst_d3d12_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_d3d12_ipc_server_close_connection (self, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Sent message");
|
||||
|
||||
switch (conn->type) {
|
||||
case GstD3D12IpcPktType::CONFIG:
|
||||
GST_DEBUG_OBJECT (self, "Sent CONFIG-DATA, conn-id %u", conn->id);
|
||||
gst_d3d12_ipc_server_wait_msg (self, conn);
|
||||
break;
|
||||
case GstD3D12IpcPktType::HAVE_DATA:
|
||||
GST_LOG_OBJECT (self, "Sent HAVE-DATA, conn-id %u", conn->id);
|
||||
gst_d3d12_ipc_server_wait_msg (self, conn);
|
||||
break;
|
||||
case GstD3D12IpcPktType::EOS:
|
||||
GST_DEBUG_OBJECT (self, "Sent EOS, conn-id %u", conn->id);
|
||||
gst_d3d12_ipc_server_wait_msg (self, conn);
|
||||
break;
|
||||
default:
|
||||
GST_ERROR_OBJECT (self, "Unexpected msg type");
|
||||
gst_d3d12_ipc_server_close_connection (self, conn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_send_msg (GstD3D12IpcServer * self,
|
||||
GstD3D12IpcServerConn * conn)
|
||||
{
|
||||
GST_LOG_OBJECT (self, "Sending message");
|
||||
|
||||
if (!WriteFileEx (conn->pipe, &conn->server_msg[0],
|
||||
conn->server_msg.size (), conn,
|
||||
gst_d3d12_ipc_server_send_msg_finish)) {
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_d3d12_ipc_win32_error_to_string (last_err);
|
||||
GST_WARNING_OBJECT (self, "WriteFileEx failed with 0x%x (%s)",
|
||||
last_err, err.c_str ());
|
||||
gst_d3d12_ipc_server_close_connection (self, conn);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_server_on_incoming_connection (GstD3D12IpcServer * self,
|
||||
std::shared_ptr < GstD3D12IpcServerConn > conn)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
|
||||
priv->lock.lock ();
|
||||
conn->server = self;
|
||||
conn->id = priv->next_conn_id;
|
||||
conn->data = priv->data;
|
||||
priv->next_conn_id++;
|
||||
priv->lock.unlock ();
|
||||
|
||||
GST_DEBUG_OBJECT (self, "New connection, conn-id: %u", conn->id);
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
priv->conn_map.insert ({conn->id, conn});
|
||||
/* *INDENT-ON* */
|
||||
|
||||
if (conn->data) {
|
||||
conn->configured = true;
|
||||
gst_d3d12_ipc_server_config_data (self, conn.get ());
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (self, "Have no config data yet, waiting for data");
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gst_d3d12_ipc_server_loop_thread_func (GstD3D12IpcServer * self)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
bool io_pending = false;
|
||||
guint wait_ret;
|
||||
HANDLE pipe;
|
||||
OVERLAPPED overlap;
|
||||
HANDLE waitables[3];
|
||||
|
||||
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);
|
||||
priv->aborted = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
waitables[0] = overlap.hEvent;
|
||||
waitables[1] = priv->wakeup_event;
|
||||
waitables[2] = 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_d3d12_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 < GstD3D12IpcServerConn > (pipe);
|
||||
conn->server = self;
|
||||
pipe = INVALID_HANDLE_VALUE;
|
||||
gst_d3d12_ipc_server_on_incoming_connection (self, 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_d3d12_ipc_server_on_idle (self);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
guint last_err = GetLastError ();
|
||||
auto err = gst_d3d12_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 ());
|
||||
priv->aborted = true;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
|
||||
out:
|
||||
if (pipe != INVALID_HANDLE_VALUE) {
|
||||
CancelIo (pipe);
|
||||
DisconnectNamedPipe (pipe);
|
||||
CloseHandle (pipe);
|
||||
}
|
||||
|
||||
CloseHandle (overlap.hEvent);
|
||||
|
||||
priv->conn_map.clear ();
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Exit loop thread");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
gst_d3d12_ipc_server_send_data (GstD3D12IpcServer * server, GstSample * sample,
|
||||
const GstD3D12IpcMemLayout & layout, HANDLE handle, GstClockTime pts)
|
||||
{
|
||||
GstD3D12IpcServerPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GST_IS_D3D12_IPC_SERVER (server), GST_FLOW_ERROR);
|
||||
g_return_val_if_fail (GST_IS_SAMPLE (sample), GST_FLOW_ERROR);
|
||||
|
||||
priv = server->priv;
|
||||
|
||||
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 buffer = gst_sample_get_buffer (sample);
|
||||
auto dmem = (GstD3D12Memory *) gst_buffer_peek_memory (buffer, 0);
|
||||
|
||||
auto data = std::make_shared < GstD3D12IpcServerData > ();
|
||||
data->sample = gst_sample_ref (sample);
|
||||
data->handle = handle;
|
||||
data->layout = layout;
|
||||
data->pts = pts;
|
||||
data->fence_val = dmem->fence_value;
|
||||
data->seq_num = priv->seq_num;
|
||||
|
||||
priv->seq_num++;
|
||||
priv->data = data;
|
||||
lk.unlock ();
|
||||
|
||||
SetEvent (priv->wakeup_event);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
void
|
||||
gst_d3d12_ipc_server_stop (GstD3D12IpcServer * server)
|
||||
{
|
||||
GstD3D12IpcServerPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_D3D12_IPC_SERVER (server));
|
||||
|
||||
priv = server->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (server, "Stopping");
|
||||
priv->shutdown = true;
|
||||
SetEvent (priv->wakeup_event);
|
||||
|
||||
g_clear_pointer (&priv->loop_thread, g_thread_join);
|
||||
|
||||
GST_DEBUG_OBJECT (server, "Stopped");
|
||||
}
|
||||
|
||||
GstD3D12IpcServer *
|
||||
gst_d3d12_ipc_server_new (const std::string & address, gint64 adapter_luid,
|
||||
ID3D12Fence * fence)
|
||||
{
|
||||
ComPtr < ID3D12Device > device;
|
||||
|
||||
auto hr = fence->GetDevice (IID_PPV_ARGS (&device));
|
||||
if (FAILED (hr)) {
|
||||
GST_ERROR ("Couldn't get device handle");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HANDLE fence_handle;
|
||||
hr = device->CreateSharedHandle (fence, nullptr, GENERIC_ALL, nullptr,
|
||||
&fence_handle);
|
||||
if (FAILED (hr)) {
|
||||
GST_ERROR ("Couldn't open shared handle");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto self = (GstD3D12IpcServer *)
|
||||
g_object_new (GST_TYPE_D3D12_IPC_SERVER, nullptr);
|
||||
gst_object_ref_sink (self);
|
||||
|
||||
auto priv = self->priv;
|
||||
priv->address = address;
|
||||
priv->adapter_luid = adapter_luid;
|
||||
priv->fence_handle = fence_handle;
|
||||
|
||||
priv->loop_thread = g_thread_new ("d3d12-ipc-server",
|
||||
(GThreadFunc) gst_d3d12_ipc_server_loop_thread_func, self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
gint64
|
||||
gst_d3d12_ipc_server_get_adapter_luid (GstD3D12IpcServer * server)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_D3D12_IPC_SERVER (server), 0);
|
||||
|
||||
return server->priv->adapter_luid;
|
||||
}
|
45
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcserver.h
Normal file
45
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcserver.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2024 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 "gstd3d12ipc.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_D3D12_IPC_SERVER (gst_d3d12_ipc_server_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstD3D12IpcServer, gst_d3d12_ipc_server,
|
||||
GST, D3D12_IPC_SERVER, GstObject);
|
||||
|
||||
GstD3D12IpcServer * gst_d3d12_ipc_server_new (const std::string & address,
|
||||
gint64 adapter_luid,
|
||||
ID3D12Fence * fence);
|
||||
|
||||
GstFlowReturn gst_d3d12_ipc_server_send_data (GstD3D12IpcServer * server,
|
||||
GstSample * sample,
|
||||
const GstD3D12IpcMemLayout & layout,
|
||||
HANDLE handle,
|
||||
GstClockTime pts);
|
||||
|
||||
void gst_d3d12_ipc_server_stop (GstD3D12IpcServer * server);
|
||||
|
||||
gint64 gst_d3d12_ipc_server_get_adapter_luid (GstD3D12IpcServer * server);
|
||||
|
||||
G_END_DECLS
|
706
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcsink.cpp
Normal file
706
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcsink.cpp
Normal file
|
@ -0,0 +1,706 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2024 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-d3d12ipcsink
|
||||
* @title: d3d12ipcsink
|
||||
* @short_description: Direct3D12 Inter Process Communication (IPC) sink
|
||||
*
|
||||
* d3d12ipcsink exports Direct3D12 texture for connected d3d12ipcsrc elements
|
||||
* to be able to import it
|
||||
*
|
||||
* ## Example launch line
|
||||
* ```
|
||||
* gst-launch-1.0 videotestsrc ! d3d12upload ! d3d12ipcsink
|
||||
* ```
|
||||
*
|
||||
* Since: 1.26
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstd3d12ipcsink.h"
|
||||
#include "gstd3d12ipcserver.h"
|
||||
#include "gstd3d12pluginutils.h"
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <wrl.h>
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
using namespace Microsoft::WRL;
|
||||
/* *INDENT-ON* */
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_d3d12_ipc_sink_debug);
|
||||
#define GST_CAT_DEFAULT gst_d3d12_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_D3D12_MEMORY, GST_D3D12_IPC_FORMATS) ";"
|
||||
GST_VIDEO_CAPS_MAKE (GST_D3D12_IPC_FORMATS)));
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_ADAPTER,
|
||||
PROP_PIPE_NAME,
|
||||
};
|
||||
|
||||
#define DEFAULT_ADAPTER -1
|
||||
#define DEFAULT_PIPE_NAME "\\\\.\\pipe\\gst.d3d12.ipc"
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
struct GstD3D12IpcSinkPrivate
|
||||
{
|
||||
GstD3D12Device *device = nullptr;
|
||||
|
||||
GstBufferPool *fallback_pool = nullptr;
|
||||
GstVideoInfo info;
|
||||
|
||||
GstD3D12IpcServer *server = nullptr;
|
||||
GstCaps *caps = nullptr;
|
||||
GstSample *prepared_sample = nullptr;
|
||||
HANDLE prepared_handle = nullptr;
|
||||
GstD3D12IpcMemLayout layout;
|
||||
|
||||
std::mutex lock;
|
||||
|
||||
/* properties */
|
||||
gint adapter = DEFAULT_ADAPTER;
|
||||
std::string pipe_name = DEFAULT_PIPE_NAME;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
struct _GstD3D12IpcSink
|
||||
{
|
||||
GstBaseSink parent;
|
||||
|
||||
GstD3D12IpcSinkPrivate *priv;
|
||||
};
|
||||
|
||||
static void gst_d3d12_ipc_sink_finalize (GObject * object);
|
||||
static void gst_d3d12_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_d3d12_ipc_sink_provide_clock (GstElement * elem);
|
||||
static void gst_d3d12_ipc_sink_set_context (GstElement * elem,
|
||||
GstContext * context);
|
||||
|
||||
static gboolean gst_d3d12_ipc_sink_start (GstBaseSink * sink);
|
||||
static gboolean gst_d3d12_ipc_sink_stop (GstBaseSink * sink);
|
||||
static gboolean gst_d3d12_ipc_sink_set_caps (GstBaseSink * sink,
|
||||
GstCaps * caps);
|
||||
static void gst_d3d12_ipc_sink_get_time (GstBaseSink * sink, GstBuffer * buf,
|
||||
GstClockTime * start, GstClockTime * end);
|
||||
static gboolean gst_d3d12_ipc_sink_propose_allocation (GstBaseSink * sink,
|
||||
GstQuery * query);
|
||||
static gboolean gst_d3d12_ipc_sink_query (GstBaseSink * sink, GstQuery * query);
|
||||
static GstFlowReturn gst_d3d12_ipc_sink_prepare (GstBaseSink * sink,
|
||||
GstBuffer * buf);
|
||||
static GstFlowReturn gst_d3d12_ipc_sink_render (GstBaseSink * sink,
|
||||
GstBuffer * buf);
|
||||
|
||||
#define gst_d3d12_ipc_sink_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstD3D12IpcSink, gst_d3d12_ipc_sink, GST_TYPE_BASE_SINK);
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_sink_class_init (GstD3D12IpcSinkClass * klass)
|
||||
{
|
||||
auto object_class = G_OBJECT_CLASS (klass);
|
||||
auto element_class = GST_ELEMENT_CLASS (klass);
|
||||
auto sink_class = GST_BASE_SINK_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_d3d12_ipc_sink_finalize;
|
||||
object_class->set_property = gst_d3d12_ipc_sink_set_property;
|
||||
object_class->get_property = gst_win32_video_sink_get_property;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_ADAPTER,
|
||||
g_param_spec_int ("adapter", "Adapter",
|
||||
"DXGI adapter index (-1 for default)",
|
||||
-1, G_MAXINT32, DEFAULT_ADAPTER,
|
||||
(GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_PIPE_NAME,
|
||||
g_param_spec_string ("pipe-name", "Pipe Name",
|
||||
"The name of Win32 named pipe to communicate with clients. "
|
||||
"Validation of the pipe name is caller's responsibility",
|
||||
DEFAULT_PIPE_NAME, (GParamFlags) (G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY)));
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"Direct3D12 IPC Sink", "Sink/Video",
|
||||
"Sends Direct3D12 shared handle to peer d3d12ipcsrc 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_d3d12_ipc_sink_provide_clock);
|
||||
element_class->set_context =
|
||||
GST_DEBUG_FUNCPTR (gst_d3d12_ipc_sink_set_context);
|
||||
|
||||
sink_class->start = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_sink_start);
|
||||
sink_class->stop = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_sink_stop);
|
||||
sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_sink_set_caps);
|
||||
sink_class->propose_allocation =
|
||||
GST_DEBUG_FUNCPTR (gst_d3d12_ipc_sink_propose_allocation);
|
||||
sink_class->query = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_sink_query);
|
||||
sink_class->get_times = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_sink_get_time);
|
||||
sink_class->prepare = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_sink_prepare);
|
||||
sink_class->render = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_sink_render);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_d3d12_ipc_sink_debug, "d3d12ipcsink",
|
||||
0, "d3d12ipcsink");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_sink_init (GstD3D12IpcSink * self)
|
||||
{
|
||||
self->priv = new GstD3D12IpcSinkPrivate ();
|
||||
|
||||
GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
|
||||
GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_sink_finalize (GObject * object)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (object);
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (object);
|
||||
auto priv = self->priv;
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_ADAPTER:
|
||||
priv->adapter = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_PIPE_NAME:
|
||||
{
|
||||
const gchar *pipe_name = g_value_get_string (value);
|
||||
priv->pipe_name.clear ();
|
||||
|
||||
if (pipe_name)
|
||||
priv->pipe_name = pipe_name;
|
||||
else
|
||||
priv->pipe_name = DEFAULT_PIPE_NAME;
|
||||
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)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (object);
|
||||
auto priv = self->priv;
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_ADAPTER:
|
||||
g_value_set_int (value, priv->adapter);
|
||||
break;
|
||||
case PROP_PIPE_NAME:
|
||||
g_value_set_string (value, priv->pipe_name.c_str ());
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstClock *
|
||||
gst_d3d12_ipc_sink_provide_clock (GstElement * elem)
|
||||
{
|
||||
return gst_system_clock_obtain ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_sink_set_context (GstElement * elem, GstContext * context)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (elem);
|
||||
auto priv = self->priv;
|
||||
|
||||
gst_d3d12_handle_set_context (elem, context, priv->adapter, &priv->device);
|
||||
|
||||
GST_ELEMENT_CLASS (parent_class)->set_context (elem, context);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_sink_start (GstBaseSink * sink)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (sink);
|
||||
auto priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Start");
|
||||
|
||||
if (!gst_d3d12_ensure_element_data (GST_ELEMENT_CAST (self), priv->adapter,
|
||||
&priv->device)) {
|
||||
GST_ERROR_OBJECT (sink, "Cannot create d3d12device");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_sink_stop (GstBaseSink * sink)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (sink);
|
||||
auto priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stop");
|
||||
|
||||
if (priv->server)
|
||||
gst_d3d12_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_object (&priv->device);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_sink_get_time (GstBaseSink * sink, GstBuffer * buf,
|
||||
GstClockTime * start, GstClockTime * end)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (sink);
|
||||
auto 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 GstBufferPool *
|
||||
gst_d3d12_ipc_sink_create_pool (GstD3D12IpcSink * self,
|
||||
const GstVideoInfo * info, GstCaps * caps)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
|
||||
auto pool = gst_d3d12_buffer_pool_new (priv->device);
|
||||
auto config = gst_buffer_pool_get_config (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 (info), 0, 0);
|
||||
|
||||
auto params = gst_d3d12_allocation_params_new (priv->device, &priv->info,
|
||||
GST_D3D12_ALLOCATION_FLAG_DEFAULT,
|
||||
D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS |
|
||||
D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_HEAP_FLAG_SHARED);
|
||||
|
||||
gst_buffer_pool_config_set_d3d12_allocation_params (config, params);
|
||||
gst_d3d12_allocation_params_free (params);
|
||||
|
||||
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set pool config");
|
||||
gst_object_unref (pool);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (sink);
|
||||
auto priv = self->priv;
|
||||
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;
|
||||
}
|
||||
|
||||
if (priv->fallback_pool) {
|
||||
gst_buffer_pool_set_active (priv->fallback_pool, FALSE);
|
||||
gst_clear_object (&priv->fallback_pool);
|
||||
}
|
||||
|
||||
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_D3D12_MEMORY, nullptr));
|
||||
|
||||
gst_clear_caps (&priv->caps);
|
||||
priv->caps = new_caps;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_sink_propose_allocation (GstBaseSink * sink, GstQuery * query)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (sink);
|
||||
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) {
|
||||
auto features = gst_caps_get_features (caps, 0);
|
||||
if (features
|
||||
&& gst_caps_features_contains (features,
|
||||
GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY)) {
|
||||
GST_DEBUG_OBJECT (self, "upstream support d3d12 memory");
|
||||
pool = gst_d3d12_ipc_sink_create_pool (self, &info, caps);
|
||||
if (!pool) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't create pool");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
auto config = gst_buffer_pool_get_config (pool);
|
||||
gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr,
|
||||
nullptr);
|
||||
gst_structure_free (config);
|
||||
} else {
|
||||
pool = gst_video_buffer_pool_new ();
|
||||
auto config = gst_buffer_pool_get_config (pool);
|
||||
gst_buffer_pool_config_add_option (config,
|
||||
GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||
gst_buffer_pool_config_add_option (config,
|
||||
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
|
||||
|
||||
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_d3d12_ipc_sink_query (GstBaseSink * sink, GstQuery * query)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (sink);
|
||||
auto priv = self->priv;
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_CONTEXT:
|
||||
if (gst_d3d12_handle_context_query (GST_ELEMENT (self), query,
|
||||
priv->device)) {
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GST_BASE_SINK_CLASS (parent_class)->query (sink, query);
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_d3d12_ipc_upload (GstD3D12IpcSink * self, GstBuffer * buf)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
GstBuffer *uploaded = nullptr;
|
||||
GstFlowReturn ret;
|
||||
GstMemory *mem;
|
||||
|
||||
mem = gst_buffer_peek_memory (buf, 0);
|
||||
if (gst_is_d3d12_memory (mem)) {
|
||||
auto dmem = GST_D3D12_MEMORY_CAST (mem);
|
||||
if (dmem->device == priv->device) {
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
D3D12_HEAP_FLAGS heap_flags = D3D12_HEAP_FLAG_NONE;
|
||||
|
||||
auto resource = gst_d3d12_memory_get_resource_handle (dmem);
|
||||
desc = resource->GetDesc ();
|
||||
resource->GetHeapProperties (nullptr, &heap_flags);
|
||||
if ((desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS) != 0 &&
|
||||
(heap_flags & D3D12_HEAP_FLAG_SHARED) != 0) {
|
||||
return gst_buffer_ref (buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!priv->fallback_pool) {
|
||||
priv->fallback_pool = gst_d3d12_ipc_sink_create_pool (self, &priv->info,
|
||||
priv->caps);
|
||||
if (!priv->fallback_pool) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't create fallback pool");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ret = gst_buffer_pool_acquire_buffer (priv->fallback_pool,
|
||||
&uploaded, nullptr);
|
||||
if (ret != GST_FLOW_OK) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't acquire fallback buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!gst_d3d12_buffer_copy_into (uploaded, buf, &priv->info)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't copy buffer");
|
||||
gst_buffer_unref (uploaded);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return uploaded;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_sink_ensure_server (GstD3D12IpcSink * self, GstBuffer * buffer)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
GstMemory *mem;
|
||||
gint64 adapter_luid;
|
||||
|
||||
if (priv->server)
|
||||
return TRUE;
|
||||
|
||||
g_object_get (priv->device, "adapter-luid", &adapter_luid, nullptr);
|
||||
|
||||
mem = gst_buffer_peek_memory (buffer, 0);
|
||||
if (gst_is_d3d12_memory (mem)) {
|
||||
GstD3D12Memory *dmem = GST_D3D12_MEMORY_CAST (mem);
|
||||
if (dmem->device != priv->device) {
|
||||
g_object_get (dmem->device, "adapter-luid", &adapter_luid, nullptr);
|
||||
gst_object_unref (priv->device);
|
||||
priv->device = (GstD3D12Device *) gst_object_ref (dmem->device);
|
||||
}
|
||||
}
|
||||
|
||||
auto queue = gst_d3d12_device_get_command_queue (priv->device,
|
||||
D3D12_COMMAND_LIST_TYPE_DIRECT);
|
||||
ComPtr < ID3D12Fence > fence;
|
||||
gst_d3d12_command_queue_get_fence (queue, &fence);
|
||||
|
||||
priv->server = gst_d3d12_ipc_server_new (priv->pipe_name, adapter_luid,
|
||||
fence.Get ());
|
||||
if (!priv->server) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't create server");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_d3d12_ipc_sink_prepare (GstBaseSink * sink, GstBuffer * buf)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (sink);
|
||||
auto priv = self->priv;
|
||||
GstBuffer *uploaded;
|
||||
GstD3D12Memory *dmem;
|
||||
GstVideoFrame frame;
|
||||
HANDLE nt_handle = nullptr;
|
||||
|
||||
gst_clear_sample (&priv->prepared_sample);
|
||||
|
||||
if (!gst_d3d12_ipc_sink_ensure_server (self, buf))
|
||||
return GST_FLOW_ERROR;
|
||||
|
||||
uploaded = gst_d3d12_ipc_upload (self, buf);
|
||||
if (!uploaded) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't upload buffer");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
dmem = (GstD3D12Memory *) gst_buffer_peek_memory (uploaded, 0);
|
||||
|
||||
/* Upload staging to device memory */
|
||||
if (!gst_video_frame_map (&frame, &priv->info, uploaded,
|
||||
(GstMapFlags) (GST_MAP_READ | GST_MAP_D3D12))) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't upload memory");
|
||||
gst_buffer_unref (uploaded);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
priv->layout.pitch = frame.info.stride[0];
|
||||
for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES (&frame); i++)
|
||||
priv->layout.offset[i] = frame.info.offset[i];
|
||||
|
||||
gst_video_frame_unmap (&frame);
|
||||
|
||||
if (!gst_d3d12_memory_get_nt_handle (dmem, &nt_handle)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't get NT handle");
|
||||
gst_buffer_unref (uploaded);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
priv->prepared_sample = gst_sample_new (uploaded,
|
||||
priv->caps, nullptr, nullptr);
|
||||
priv->prepared_handle = nt_handle;
|
||||
|
||||
gst_buffer_unref (uploaded);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_d3d12_ipc_sink_render (GstBaseSink * sink, GstBuffer * buf)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SINK (sink);
|
||||
auto 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_d3d12_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);
|
||||
}
|
||||
|
||||
ret = gst_d3d12_ipc_server_send_data (priv->server, priv->prepared_sample,
|
||||
priv->layout, priv->prepared_handle, pts);
|
||||
|
||||
return ret;
|
||||
}
|
33
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcsink.h
Normal file
33
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcsink.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2024 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 "gstd3d12.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_D3D12_IPC_SINK (gst_d3d12_ipc_sink_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstD3D12IpcSink, gst_d3d12_ipc_sink,
|
||||
GST, D3D12_IPC_SINK, GstBaseSink);
|
||||
|
||||
G_END_DECLS
|
523
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcsrc.cpp
Normal file
523
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcsrc.cpp
Normal file
|
@ -0,0 +1,523 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2024 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-d3d12ipcsrc
|
||||
* @title: d3d12ipcsrc
|
||||
* @short_description: Direct3D12 Inter Process Communication (IPC) src
|
||||
*
|
||||
* d3d12ipcsrc imports Direct3D12 texture exported by peer d3d12ipcsrc element
|
||||
*
|
||||
* ## Example launch line
|
||||
* ```
|
||||
* gst-launch-1.0 d3d12ipcsrc ! queue ! d3d12videosink
|
||||
* ```
|
||||
*
|
||||
* Since: 1.26
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstd3d12ipcsrc.h"
|
||||
#include "gstd3d12ipcclient.h"
|
||||
#include "gstd3d12pluginutils.h"
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_d3d12_ipc_src_debug);
|
||||
#define GST_CAT_DEFAULT gst_d3d12_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_D3D12_MEMORY, GST_D3D12_IPC_FORMATS)));
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_PIPE_NAME,
|
||||
PROP_PROCESSING_DEADLINE,
|
||||
PROP_IO_MODE,
|
||||
PROP_CONN_TIMEOUT,
|
||||
};
|
||||
|
||||
#define DEFAULT_PIPE_NAME "\\\\.\\pipe\\gst.d3d12.ipc"
|
||||
#define DEFAULT_PROCESSING_DEADLINE (20 * GST_MSECOND)
|
||||
#define DEFAULT_IO_MODE GST_D3D12_IPC_IO_COPY
|
||||
#define DEFAULT_CONN_TIMEOUT 5
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
struct GstD3D12IpcSrcPrivate
|
||||
{
|
||||
GstD3D12Device *device = nullptr;
|
||||
|
||||
GstD3D12IpcClient *client = nullptr;
|
||||
GstCaps *caps = nullptr;
|
||||
|
||||
GstVideoInfo info;
|
||||
std::mutex lock;
|
||||
bool flushing = false;
|
||||
|
||||
/* properties */
|
||||
std::string pipe_name = DEFAULT_PIPE_NAME;
|
||||
GstClockTime processing_deadline = DEFAULT_PROCESSING_DEADLINE;
|
||||
GstD3D12IpcIOMode io_mode = DEFAULT_IO_MODE;
|
||||
guint conn_timeout = DEFAULT_CONN_TIMEOUT;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
struct _GstD3D12IpcSrc
|
||||
{
|
||||
GstBaseSrc parent;
|
||||
|
||||
GstD3D12IpcSrcPrivate *priv;
|
||||
};
|
||||
|
||||
static void gst_d3d12_ipc_src_finalize (GObject * object);
|
||||
static void gst_d3d12_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_d3d12_ipc_src_provide_clock (GstElement * elem);
|
||||
static void gst_d3d12_ipc_src_set_context (GstElement * elem,
|
||||
GstContext * context);
|
||||
|
||||
static gboolean gst_d3d12_ipc_src_start (GstBaseSrc * src);
|
||||
static gboolean gst_d3d12_ipc_src_stop (GstBaseSrc * src);
|
||||
static gboolean gst_d3d12_ipc_src_unlock (GstBaseSrc * src);
|
||||
static gboolean gst_d3d12_ipc_src_unlock_stop (GstBaseSrc * src);
|
||||
static gboolean gst_d3d12_ipc_src_query (GstBaseSrc * src, GstQuery * query);
|
||||
static GstCaps *gst_d3d12_ipc_src_get_caps (GstBaseSrc * src, GstCaps * filter);
|
||||
static GstCaps *gst_d3d12_ipc_src_fixate (GstBaseSrc * src, GstCaps * caps);
|
||||
static GstFlowReturn gst_d3d12_ipc_src_create (GstBaseSrc * src, guint64 offset,
|
||||
guint size, GstBuffer ** buf);
|
||||
|
||||
#define gst_d3d12_ipc_src_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstD3D12IpcSrc, gst_d3d12_ipc_src, GST_TYPE_BASE_SRC);
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_src_class_init (GstD3D12IpcSrcClass * klass)
|
||||
{
|
||||
auto object_class = G_OBJECT_CLASS (klass);
|
||||
auto element_class = GST_ELEMENT_CLASS (klass);
|
||||
auto src_class = GST_BASE_SRC_CLASS (klass);
|
||||
|
||||
object_class->finalize = gst_d3d12_ipc_src_finalize;
|
||||
object_class->set_property = gst_d3d12_ipc_src_set_property;
|
||||
object_class->get_property = gst_win32_video_src_get_property;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_PIPE_NAME,
|
||||
g_param_spec_string ("pipe-name", "Pipe Name",
|
||||
"The name of Win32 named pipe to communicate with clients. "
|
||||
"Validation of the pipe name is caller's responsibility",
|
||||
DEFAULT_PIPE_NAME, (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",
|
||||
GST_TYPE_D3D12_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)));
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"Direct3D12 IPC Source", "Source/Video",
|
||||
"Receives Direct3D12 shared handle from the d3d12ipcsink 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_d3d12_ipc_src_provide_clock);
|
||||
element_class->set_context =
|
||||
GST_DEBUG_FUNCPTR (gst_d3d12_ipc_src_set_context);
|
||||
|
||||
src_class->start = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_src_start);
|
||||
src_class->stop = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_src_stop);
|
||||
src_class->unlock = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_src_unlock);
|
||||
src_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_src_unlock_stop);
|
||||
src_class->query = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_src_query);
|
||||
src_class->get_caps = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_src_get_caps);
|
||||
src_class->fixate = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_src_fixate);
|
||||
src_class->create = GST_DEBUG_FUNCPTR (gst_d3d12_ipc_src_create);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_d3d12_ipc_src_debug, "d3d12ipcsrc",
|
||||
0, "d3d12ipcsrc");
|
||||
|
||||
gst_type_mark_as_plugin_api (GST_TYPE_D3D12_IPC_IO_MODE,
|
||||
(GstPluginAPIFlags) 0);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_src_init (GstD3D12IpcSrc * 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 GstD3D12IpcSrcPrivate ();
|
||||
|
||||
GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
|
||||
GST_OBJECT_FLAG_SET (self, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_src_finalize (GObject * object)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (object);
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_src_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (object);
|
||||
auto priv = self->priv;
|
||||
std::unique_lock < std::mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PIPE_NAME:
|
||||
{
|
||||
const gchar *pipe_name = g_value_get_string (value);
|
||||
priv->pipe_name.clear ();
|
||||
|
||||
if (pipe_name)
|
||||
priv->pipe_name = pipe_name;
|
||||
else
|
||||
priv->pipe_name = DEFAULT_PIPE_NAME;
|
||||
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 = (GstD3D12IpcIOMode) g_value_get_enum (value);
|
||||
break;
|
||||
case PROP_CONN_TIMEOUT:
|
||||
priv->conn_timeout = 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)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (object);
|
||||
auto priv = self->priv;
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PIPE_NAME:
|
||||
g_value_set_string (value, priv->pipe_name.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;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstClock *
|
||||
gst_d3d12_ipc_src_provide_clock (GstElement * elem)
|
||||
{
|
||||
return gst_system_clock_obtain ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_ipc_src_set_context (GstElement * elem, GstContext * context)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (elem);
|
||||
auto priv = self->priv;
|
||||
|
||||
gst_d3d12_handle_set_context (elem, context, -1, &priv->device);
|
||||
|
||||
GST_ELEMENT_CLASS (parent_class)->set_context (elem, context);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_src_start (GstBaseSrc * src)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (src);
|
||||
auto priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Start");
|
||||
|
||||
if (!gst_d3d12_ensure_element_data (GST_ELEMENT_CAST (self),
|
||||
-1, &priv->device)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't get D3D12 context");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
priv->client = gst_d3d12_ipc_client_new (priv->pipe_name, priv->device,
|
||||
priv->io_mode, priv->conn_timeout);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_src_stop (GstBaseSrc * src)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (src);
|
||||
auto priv = self->priv;
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stop");
|
||||
|
||||
if (priv->client)
|
||||
gst_d3d12_ipc_client_stop (priv->client);
|
||||
|
||||
gst_clear_object (&priv->client);
|
||||
gst_clear_object (&priv->device);
|
||||
gst_clear_caps (&priv->caps);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_src_unlock (GstBaseSrc * src)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (src);
|
||||
auto priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Unlock");
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
priv->flushing = true;
|
||||
if (priv->client)
|
||||
gst_d3d12_ipc_client_set_flushing (priv->client, true);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_src_unlock_stop (GstBaseSrc * src)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (src);
|
||||
auto priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Unlock stop");
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->lock);
|
||||
priv->flushing = false;
|
||||
if (priv->client)
|
||||
gst_d3d12_ipc_client_set_flushing (priv->client, false);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_d3d12_ipc_src_query (GstBaseSrc * src, GstQuery * query)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (src);
|
||||
auto 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_d3d12_handle_context_query (GST_ELEMENT (self), query,
|
||||
priv->device)) {
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GST_BASE_SRC_CLASS (parent_class)->query (src, query);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_d3d12_ipc_src_get_caps (GstBaseSrc * src, GstCaps * filter)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (src);
|
||||
auto priv = self->priv;
|
||||
GstD3D12IpcClient *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 = (GstD3D12IpcClient *) gst_object_ref (priv->client);
|
||||
priv->lock.unlock ();
|
||||
|
||||
if (!caps && client)
|
||||
caps = gst_d3d12_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_d3d12_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_d3d12_ipc_src_create (GstBaseSrc * src, guint64 offset, guint size,
|
||||
GstBuffer ** buf)
|
||||
{
|
||||
auto self = GST_D3D12_IPC_SRC (src);
|
||||
auto 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_d3d12_ipc_client_run (priv->client);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
|
||||
ret = gst_d3d12_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_d3d12_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/d3d12/gstd3d12ipcsrc.h
Normal file
33
subprojects/gst-plugins-bad/sys/d3d12/gstd3d12ipcsrc.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 "gstd3d12.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_D3D12_IPC_SRC (gst_d3d12_ipc_src_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstD3D12IpcSrc, gst_d3d12_ipc_src,
|
||||
GST, D3D12_IPC_SRC, GstBaseSrc);
|
||||
|
||||
G_END_DECLS
|
|
@ -24,6 +24,11 @@ d3d12_sources = [
|
|||
'gstd3d12h264dec.cpp',
|
||||
'gstd3d12h264enc.cpp',
|
||||
'gstd3d12h265dec.cpp',
|
||||
'gstd3d12ipc.cpp',
|
||||
'gstd3d12ipcclient.cpp',
|
||||
'gstd3d12ipcserver.cpp',
|
||||
'gstd3d12ipcsink.cpp',
|
||||
'gstd3d12ipcsrc.cpp',
|
||||
'gstd3d12memory.cpp',
|
||||
'gstd3d12mpeg2dec.cpp',
|
||||
'gstd3d12overlaycompositor.cpp',
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
#include "gstd3d12vp8dec.h"
|
||||
#include "gstd3d12vp9dec.h"
|
||||
#include "gstd3d12av1dec.h"
|
||||
#include "gstd3d12ipcclient.h"
|
||||
#include "gstd3d12ipcsrc.h"
|
||||
#include "gstd3d12ipcsink.h"
|
||||
#include <windows.h>
|
||||
#include <versionhelpers.h>
|
||||
#include <wrl.h>
|
||||
|
@ -64,6 +67,7 @@ GST_DEBUG_CATEGORY (gst_d3d12_utils_debug);
|
|||
static void
|
||||
plugin_deinit (gpointer data)
|
||||
{
|
||||
gst_d3d12_ipc_client_deinit ();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -142,6 +146,10 @@ plugin_init (GstPlugin * plugin)
|
|||
gst_device_provider_register (plugin,
|
||||
"d3d12screencapturedeviceprovider", GST_RANK_PRIMARY,
|
||||
GST_TYPE_D3D12_SCREEN_CAPTURE_DEVICE_PROVIDER);
|
||||
gst_element_register (plugin,
|
||||
"d3d12ipcsrc", GST_RANK_NONE, GST_TYPE_D3D12_IPC_SRC);
|
||||
gst_element_register (plugin,
|
||||
"d3d12ipcsink", GST_RANK_NONE, GST_TYPE_D3D12_IPC_SINK);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (plugin),
|
||||
"plugin-d3d12-shutdown", (gpointer) "shutdown-data",
|
||||
|
|
Loading…
Reference in a new issue