/* GStreamer * Copyright (C) 2023 Seungha Yang * * 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 "gstd3d11ipcserver.h" #include #include #include #include #include GST_DEBUG_CATEGORY_STATIC (gst_d3d11_ipc_server_debug); #define GST_CAT_DEFAULT gst_d3d11_ipc_server_debug /* *INDENT-OFF* */ struct GstD3D11IpcServerData { ~GstD3D11IpcServerData () { if (sample) gst_sample_unref (sample); } GstSample *sample = nullptr; HANDLE handle = nullptr; GstD3D11IpcMemLayout layout; GstClockTime pts; guint64 seq_num; }; struct GstD3D11IpcServerConn : public OVERLAPPED { GstD3D11IpcServerConn (HANDLE pipe_handle) : pipe (pipe_handle) { OVERLAPPED *parent = static_cast (this); parent->Internal = 0; parent->InternalHigh = 0; parent->Offset = 0; parent->OffsetHigh = 0; client_msg.resize (GST_D3D11_IPC_PKT_HEADER_SIZE); server_msg.resize (GST_D3D11_IPC_PKT_HEADER_SIZE); } ~GstD3D11IpcServerConn() { if (pipe != INVALID_HANDLE_VALUE) { CancelIo (pipe); DisconnectNamedPipe (pipe); CloseHandle (pipe); } gst_clear_caps (&caps); } GstD3D11IpcServer *server; HANDLE pipe; GstD3D11IpcPktType type; std::vector client_msg; std::vector server_msg; std::shared_ptr data; std::vector> peer_handles; GstCaps *caps = nullptr; guint64 seq_num = 0; guint id; bool eos = false; bool pending_have_data = false; bool configured = false; }; struct GstD3D11IpcServerPrivate { GstD3D11IpcServerPrivate () { cancellable = CreateEvent (nullptr, TRUE, FALSE, nullptr); wakeup_event = CreateEvent (nullptr, FALSE, FALSE, nullptr); shutdown = false; aborted = false; } ~GstD3D11IpcServerPrivate () { CloseHandle (cancellable); CloseHandle (wakeup_event); gst_clear_object (&device); } GstD3D11Device *device = nullptr; gint64 adapter_luid = 0; std::mutex lock; guint64 seq_num = 0; guint next_conn_id = 0; std::unordered_map> conn_map; GThread *loop_thread = nullptr; std::atomicshutdown; std::atomicaborted; std::shared_ptr data; std::string address; HANDLE cancellable; HANDLE wakeup_event; DWORD pid; }; /* *INDENT-ON* */ struct _GstD3D11IpcServer { GstObject parent; GstD3D11IpcServerPrivate *priv; }; static void gst_d3d11_ipc_server_dispose (GObject * object); static void gst_d3d11_ipc_server_finalize (GObject * object); static void gst_d3d11_ipc_server_on_idle (GstD3D11IpcServer * self); static void gst_d3d11_ipc_server_send_msg (GstD3D11IpcServer * self, GstD3D11IpcServerConn * conn); static void gst_d3d11_ipc_server_wait_msg (GstD3D11IpcServer * self, GstD3D11IpcServerConn * conn); #define gst_d3d11_ipc_server_parent_class parent_class G_DEFINE_TYPE (GstD3D11IpcServer, gst_d3d11_ipc_server, GST_TYPE_OBJECT); static void gst_d3d11_ipc_server_class_init (GstD3D11IpcServerClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gst_d3d11_ipc_server_finalize; object_class->dispose = gst_d3d11_ipc_server_dispose; GST_DEBUG_CATEGORY_INIT (gst_d3d11_ipc_server_debug, "d3d11ipcserver", 0, "d3d11ipcserver"); } static void gst_d3d11_ipc_server_init (GstD3D11IpcServer * self) { self->priv = new GstD3D11IpcServerPrivate (); self->priv->pid = GetCurrentProcessId (); } static void gst_d3d11_ipc_server_dispose (GObject * object) { GstD3D11IpcServer *self = GST_D3D11_IPC_SERVER (object); GstD3D11IpcServerPrivate *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_d3d11_ipc_server_finalize (GObject * object) { GstD3D11IpcServer *self = GST_D3D11_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 (GstD3D11IpcServer * self, OVERLAPPED * overlap, bool &io_pending) { GstD3D11IpcServerPrivate *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_d3d11_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_d3d11_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_d3d11_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_d3d11_ipc_server_close_connection (GstD3D11IpcServer * self, GstD3D11IpcServerConn * conn) { GstD3D11IpcServerPrivate *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_d3d11_ipc_server_have_data (GstD3D11IpcServer * self, GstD3D11IpcServerConn * conn) { GstCaps *caps; if (!conn->data) { GST_ERROR_OBJECT (self, "Have no data to send, conn-id: %u", conn->id); gst_d3d11_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_d3d11_ipc_pkt_build_have_data (conn->server_msg, conn->data->pts, conn->data->layout, conn->data->handle, caps)) { GST_ERROR_OBJECT (self, "Couldn't build HAVE-DATA pkt, conn-id: %u", conn->id); gst_d3d11_ipc_server_close_connection (self, conn); return; } conn->type = GstD3D11IpcPktType::HAVE_DATA; gst_d3d11_ipc_server_send_msg (self, conn); } static bool gst_d3d11_ipc_server_on_release_data (GstD3D11IpcServer * self, GstD3D11IpcServerConn * conn) { bool found = false; HANDLE handle = nullptr; if (!gst_d3d11_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_d3d11_ipc_server_wait_msg_finish (GstD3D11IpcServer * server, GstD3D11IpcServerConn * conn) { GstD3D11IpcPacketHeader header; if (!gst_d3d11_ipc_pkt_identify (conn->client_msg, header)) { GST_ERROR_OBJECT (server, "Broken header, conn-id: %u", conn->id); gst_d3d11_ipc_server_close_connection (server, conn); return; } switch (header.type) { case GstD3D11IpcPktType::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_d3d11_ipc_server_on_idle (server); return; } gst_d3d11_ipc_server_have_data (server, conn); break; case GstD3D11IpcPktType::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_d3d11_ipc_server_close_connection (server, conn); return; } conn->peer_handles.push_back (conn->data); conn->data = nullptr; gst_d3d11_ipc_server_wait_msg (server, conn); break; case GstD3D11IpcPktType::RELEASE_DATA: GST_LOG_OBJECT (server, "RELEASE-DATA, conn-id: %u", conn->id); if (!gst_d3d11_ipc_server_on_release_data (server, conn)) gst_d3d11_ipc_server_close_connection (server, conn); else gst_d3d11_ipc_server_wait_msg (server, conn); break; case GstD3D11IpcPktType::FIN: GST_DEBUG_OBJECT (server, "FIN, conn-id %u", conn->id); gst_d3d11_ipc_server_close_connection (server, conn); break; default: GST_ERROR_OBJECT (server, "Unexpected packet, conn-id: %u", conn->id); gst_d3d11_ipc_server_close_connection (server, conn); break; } } static void WINAPI gst_d3d11_ipc_server_payload_finish (DWORD error_code, DWORD size, OVERLAPPED * overlap) { GstD3D11IpcServerConn *conn = static_cast < GstD3D11IpcServerConn * >(overlap); GstD3D11IpcServer *self = conn->server; if (error_code != ERROR_SUCCESS) { auto err = gst_d3d11_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_d3d11_ipc_server_close_connection (self, conn); return; } gst_d3d11_ipc_server_wait_msg_finish (self, conn); } static void WINAPI gst_d3d11_ipc_server_wait_msg_header_finish (DWORD error_code, DWORD size, OVERLAPPED * overlap) { GstD3D11IpcServerConn *conn = static_cast < GstD3D11IpcServerConn * >(overlap); GstD3D11IpcServer *self = conn->server; GstD3D11IpcPacketHeader header; if (error_code != ERROR_SUCCESS) { auto err = gst_d3d11_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_d3d11_ipc_server_close_connection (self, conn); return; } if (!gst_d3d11_ipc_pkt_identify (conn->client_msg, header)) { GST_ERROR_OBJECT (self, "Broken header"); gst_d3d11_ipc_server_close_connection (self, conn); return; } if (header.payload_size == 0) { gst_d3d11_ipc_server_wait_msg_finish (conn->server, conn); return; } GST_LOG_OBJECT (self, "Reading payload"); if (!ReadFileEx (conn->pipe, &conn->client_msg[0] + GST_D3D11_IPC_PKT_HEADER_SIZE, header.payload_size, conn, gst_d3d11_ipc_server_payload_finish)) { guint last_err = GetLastError (); auto err = gst_d3d11_ipc_win32_error_to_string (last_err); GST_WARNING_OBJECT (self, "ReadFileEx failed with 0x%x (%s)", last_err, err.c_str ()); gst_d3d11_ipc_server_close_connection (self, conn); } } static void gst_d3d11_ipc_server_wait_msg (GstD3D11IpcServer * self, GstD3D11IpcServerConn * conn) { if (!ReadFileEx (conn->pipe, &conn->client_msg[0], GST_D3D11_IPC_PKT_HEADER_SIZE, conn, gst_d3d11_ipc_server_wait_msg_header_finish)) { guint last_err = GetLastError (); auto err = gst_d3d11_ipc_win32_error_to_string (last_err); GST_WARNING_OBJECT (self, "ReadFileEx failed with 0x%x (%s)", last_err, err.c_str ()); gst_d3d11_ipc_server_close_connection (self, conn); } } static void gst_d3d11_ipc_server_eos (GstD3D11IpcServer * self, GstD3D11IpcServerConn * conn) { gst_d3d11_ipc_pkt_build_eos (conn->server_msg); conn->eos = true; conn->type = GstD3D11IpcPktType::EOS; gst_d3d11_ipc_server_send_msg (self, conn); } static void gst_d3d11_ipc_server_config_data (GstD3D11IpcServer * self, GstD3D11IpcServerConn * conn) { GstD3D11IpcServerPrivate *priv = self->priv; GstCaps *caps = gst_sample_get_caps (conn->data->sample); gst_caps_replace (&conn->caps, caps); gst_d3d11_ipc_pkt_build_config (conn->server_msg, priv->pid, priv->adapter_luid, conn->caps); conn->type = GstD3D11IpcPktType::CONFIG; GST_LOG_OBJECT (self, "Sending CONFIG, conn-id %u", conn->id); gst_d3d11_ipc_server_send_msg (self, conn); } static void gst_d3d11_ipc_server_on_idle (GstD3D11IpcServer * self) { GstD3D11IpcServerPrivate *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 < GstD3D11IpcServerConn >> 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_d3d11_ipc_server_eos (self, it.get ()); } GST_DEBUG_OBJECT (self, "Have %" G_GSIZE_FORMAT " alive connections", priv->conn_map.size()); 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 ()); } /* *INDENT-ON* */ 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 < GstD3D11IpcServerConn >> to_config_data; std::vector < std::shared_ptr < GstD3D11IpcServerConn >> 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_d3d11_ipc_server_config_data (self, it.get ()); for (auto it: to_send_have_data) gst_d3d11_ipc_server_have_data (self, it.get ()); /* *INDENT-ON* */ } static void WINAPI gst_d3d11_ipc_server_send_msg_finish (DWORD error_code, DWORD size, OVERLAPPED * overlap) { GstD3D11IpcServerConn *conn = static_cast < GstD3D11IpcServerConn * >(overlap); GstD3D11IpcServer *self = conn->server; if (error_code != ERROR_SUCCESS) { auto err = gst_d3d11_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_d3d11_ipc_server_close_connection (self, conn); return; } GST_LOG_OBJECT (self, "Sent message"); switch (conn->type) { case GstD3D11IpcPktType::CONFIG: GST_DEBUG_OBJECT (self, "Sent CONFIG-DATA, conn-id %u", conn->id); gst_d3d11_ipc_server_wait_msg (self, conn); break; case GstD3D11IpcPktType::HAVE_DATA: GST_LOG_OBJECT (self, "Sent HAVE-DATA, conn-id %u", conn->id); gst_d3d11_ipc_server_wait_msg (self, conn); break; case GstD3D11IpcPktType::EOS: GST_DEBUG_OBJECT (self, "Sent EOS, conn-id %u", conn->id); gst_d3d11_ipc_server_wait_msg (self, conn); break; default: GST_ERROR_OBJECT (self, "Unexpected msg type"); gst_d3d11_ipc_server_close_connection (self, conn); break; } } static void gst_d3d11_ipc_server_send_msg (GstD3D11IpcServer * self, GstD3D11IpcServerConn * conn) { GST_LOG_OBJECT (self, "Sending message"); if (!WriteFileEx (conn->pipe, &conn->server_msg[0], conn->server_msg.size (), conn, gst_d3d11_ipc_server_send_msg_finish)) { guint last_err = GetLastError (); auto err = gst_d3d11_ipc_win32_error_to_string (last_err); GST_WARNING_OBJECT (self, "WriteFileEx failed with 0x%x (%s)", last_err, err.c_str ()); gst_d3d11_ipc_server_close_connection (self, conn); } } static void gst_d3d11_ipc_server_on_incoming_connection (GstD3D11IpcServer * self, std::shared_ptr < GstD3D11IpcServerConn > conn) { GstD3D11IpcServerPrivate *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_d3d11_ipc_server_config_data (self, conn.get ()); } else { GST_DEBUG_OBJECT (self, "Have no config data yet, waiting for data"); } } static gpointer gst_d3d11_ipc_server_loop_thread_func (GstD3D11IpcServer * self) { GstD3D11IpcServerPrivate *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_d3d11_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 < GstD3D11IpcServerConn > (pipe); conn->server = self; pipe = INVALID_HANDLE_VALUE; gst_d3d11_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_d3d11_ipc_server_on_idle (self); break; default: { guint last_err = GetLastError (); auto err = gst_d3d11_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_d3d11_ipc_server_send_data (GstD3D11IpcServer * server, GstSample * sample, const GstD3D11IpcMemLayout & layout, HANDLE handle, GstClockTime pts) { GstD3D11IpcServerPrivate *priv; g_return_val_if_fail (GST_IS_D3D11_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 data = std::make_shared < GstD3D11IpcServerData > (); data->sample = gst_sample_ref (sample); data->handle = handle; data->layout = layout; data->pts = pts; data->seq_num = priv->seq_num; priv->seq_num++; priv->data = data; lk.unlock (); SetEvent (priv->wakeup_event); return GST_FLOW_OK; } void gst_d3d11_ipc_server_stop (GstD3D11IpcServer * server) { GstD3D11IpcServerPrivate *priv; g_return_if_fail (GST_IS_D3D11_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"); } GstD3D11IpcServer * gst_d3d11_ipc_server_new (const std::string & address, gint64 adapter_luid) { GstD3D11IpcServer *self; GstD3D11IpcServerPrivate *priv; self = (GstD3D11IpcServer *) g_object_new (GST_TYPE_D3D11_IPC_SERVER, nullptr); gst_object_ref_sink (self); priv = self->priv; priv->address = address; priv->adapter_luid = adapter_luid; priv->loop_thread = g_thread_new ("d3d11-ipc-server", (GThreadFunc) gst_d3d11_ipc_server_loop_thread_func, self); return self; } gint64 gst_d3d11_ipc_server_get_adapter_luid (GstD3D11IpcServer * server) { g_return_val_if_fail (GST_IS_D3D11_IPC_SERVER (server), 0); return server->priv->adapter_luid; }