/* 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 "gstcudaipcserver_unix.h" #include #include #include #include #include GST_DEBUG_CATEGORY_EXTERN (cuda_ipc_server_debug); #define GST_CAT_DEFAULT cuda_ipc_server_debug /* *INDENT-OFF* */ struct GstCudaIpcServerConnUnix : public GstCudaIpcServerConn { GstCudaIpcServerConnUnix (GSocketConnection * conn) { socket_conn = (GSocketConnection *) g_object_ref (conn); istream = g_io_stream_get_input_stream (G_IO_STREAM (socket_conn)); ostream = g_io_stream_get_output_stream (G_IO_STREAM (socket_conn)); } ~GstCudaIpcServerConnUnix () { g_clear_object (&socket_conn); } void CloseConn () { if (socket_conn) g_io_stream_close (G_IO_STREAM (socket_conn), nullptr, nullptr); } /* Holds ref */ GSocketConnection *socket_conn = nullptr; /* Owned by socket_conn */ GInputStream *istream; GOutputStream *ostream; }; struct GstCudaIpcServerUnixPrivate { GstCudaIpcServerUnixPrivate () { main_context = g_main_context_new (); main_loop = g_main_loop_new (main_context, FALSE); cancellable = g_cancellable_new (); } ~GstCudaIpcServerUnixPrivate () { g_main_loop_unref (main_loop); g_main_context_unref (main_context); g_object_unref (cancellable); } std::string address; GMainLoop *main_loop; GMainContext *main_context; GCancellable *cancellable; }; /* *INDENT-ON* */ struct _GstCudaIpcServerUnix { GstCudaIpcServer parent; GstCudaIpcServerUnixPrivate *priv; }; static void gst_cuda_ipc_server_unix_finalize (GObject * object); static void gst_cuda_ipc_server_unix_loop (GstCudaIpcServer * server); static void gst_cuda_ipc_server_unix_terminate (GstCudaIpcServer * server); static void gst_cuda_ipc_server_unix_invoke (GstCudaIpcServer * server); static bool gst_cuda_ipc_server_unix_wait_msg (GstCudaIpcServer * server, GstCudaIpcServerConn * conn); static bool gst_cuda_ipc_server_unix_send_msg (GstCudaIpcServer * server, GstCudaIpcServerConn * conn); static bool gst_cuda_ipc_server_unix_send_mmap_msg (GstCudaIpcServer * server, GstCudaIpcServerConn * conn, GstCudaSharableHandle handle); #define gst_cuda_ipc_server_unix_parent_class parent_class G_DEFINE_TYPE (GstCudaIpcServerUnix, gst_cuda_ipc_server_unix, GST_TYPE_CUDA_IPC_SERVER); static void gst_cuda_ipc_server_unix_class_init (GstCudaIpcServerUnixClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GstCudaIpcServerClass *server_class = GST_CUDA_IPC_SERVER_CLASS (klass); object_class->finalize = gst_cuda_ipc_server_unix_finalize; server_class->loop = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_loop); server_class->terminate = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_terminate); server_class->invoke = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_invoke); server_class->wait_msg = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_wait_msg); server_class->send_msg = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_send_msg); server_class->send_mmap_msg = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_unix_send_mmap_msg); } static void gst_cuda_ipc_server_unix_init (GstCudaIpcServerUnix * self) { self->priv = new GstCudaIpcServerUnixPrivate (); } static void gst_cuda_ipc_server_unix_finalize (GObject * object) { GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (object); GST_DEBUG_OBJECT (self, "finalize"); delete self->priv; G_OBJECT_CLASS (parent_class)->finalize (object); } static void gst_cuda_ipc_server_unix_terminate (GstCudaIpcServer * server) { GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (server); GstCudaIpcServerUnixPrivate *priv = self->priv; GST_DEBUG_OBJECT (self, "terminate"); g_main_loop_quit (priv->main_loop); } static gboolean gst_cuda_ipc_server_invoke_func (GstCudaIpcServer * server) { gst_cuda_ipc_server_on_idle (server); return G_SOURCE_REMOVE; } static void gst_cuda_ipc_server_unix_invoke (GstCudaIpcServer * server) { GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (server); GstCudaIpcServerUnixPrivate *priv = self->priv; g_main_context_invoke (priv->main_context, (GSourceFunc) gst_cuda_ipc_server_invoke_func, server); } static void gst_cuda_ipc_server_unix_payload_finish (GObject * source, GAsyncResult * result, GstCudaIpcServerConnUnix * conn) { GstCudaIpcServer *server = conn->server; gsize size; GError *err = nullptr; bool ret = true; if (!g_input_stream_read_all_finish (conn->istream, result, &size, &err)) { GST_WARNING_OBJECT (server, "Read failed with %s, conn-id: %u", err->message, conn->id); g_clear_error (&err); ret = false; } gst_cuda_ipc_server_wait_msg_finish (server, conn, ret); } static void gst_cuda_ipc_server_unix_wait_msg_finish (GObject * source, GAsyncResult * result, GstCudaIpcServerConnUnix * conn) { GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server); GstCudaIpcServerUnixPrivate *priv = self->priv; gsize size; GError *err = nullptr; GstCudaIpcPacketHeader header; if (!g_input_stream_read_all_finish (conn->istream, result, &size, &err)) { GST_WARNING_OBJECT (self, "Read failed with %s, conn-id: %u", err->message, conn->id); g_clear_error (&err); gst_cuda_ipc_server_wait_msg_finish (conn->server, conn, false); return; } if (!gst_cuda_ipc_pkt_identify (conn->client_msg, header)) { GST_ERROR_OBJECT (self, "Broken header, conn-id: %u", conn->id); gst_cuda_ipc_server_wait_msg_finish (conn->server, conn, false); return; } if (header.payload_size == 0) { gst_cuda_ipc_server_wait_msg_finish (conn->server, conn, true); return; } GST_LOG_OBJECT (self, "Reading payload"); g_input_stream_read_all_async (conn->istream, &conn->client_msg[0] + GST_CUDA_IPC_PKT_HEADER_SIZE, header.payload_size, G_PRIORITY_DEFAULT, priv->cancellable, (GAsyncReadyCallback) gst_cuda_ipc_server_unix_payload_finish, conn); } static bool gst_cuda_ipc_server_unix_wait_msg (GstCudaIpcServer * server, GstCudaIpcServerConn * conn) { GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server); GstCudaIpcServerUnixPrivate *priv = self->priv; GstCudaIpcServerConnUnix *unix_conn = static_cast < GstCudaIpcServerConnUnix * >(conn); GST_LOG_OBJECT (self, "Waiting for client message"); g_input_stream_read_all_async (unix_conn->istream, &conn->client_msg[0], GST_CUDA_IPC_PKT_HEADER_SIZE, G_PRIORITY_DEFAULT, priv->cancellable, (GAsyncReadyCallback) gst_cuda_ipc_server_unix_wait_msg_finish, unix_conn); return true; } static void gst_cuda_ipc_server_unix_send_msg_finish (GObject * source, GAsyncResult * result, GstCudaIpcServerConnUnix * conn) { GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server); gsize size; GError *err = nullptr; if (!g_output_stream_write_all_finish (conn->ostream, result, &size, &err)) { GST_WARNING_OBJECT (self, "Write failed with %s, conn-id: %u", err->message, conn->id); g_clear_error (&err); gst_cuda_ipc_server_send_msg_finish (conn->server, conn, false); return; } GST_LOG_OBJECT (self, "Sent message"); gst_cuda_ipc_server_send_msg_finish (conn->server, conn, true); } static bool gst_cuda_ipc_server_unix_send_msg (GstCudaIpcServer * server, GstCudaIpcServerConn * conn) { GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server); GstCudaIpcServerUnixPrivate *priv = self->priv; GstCudaIpcServerConnUnix *unix_conn = static_cast < GstCudaIpcServerConnUnix * >(conn); GST_LOG_OBJECT (self, "Sending message"); g_output_stream_write_all_async (unix_conn->ostream, &conn->server_msg[0], conn->server_msg.size (), G_PRIORITY_DEFAULT, priv->cancellable, (GAsyncReadyCallback) gst_cuda_ipc_server_unix_send_msg_finish, unix_conn); return true; } static bool gst_cuda_ipc_server_unix_send_mmap_msg (GstCudaIpcServer * server, GstCudaIpcServerConn * conn, GstCudaSharableHandle handle) { GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (conn->server); GstCudaIpcServerUnixPrivate *priv = self->priv; GstCudaIpcServerConnUnix *unix_conn = static_cast < GstCudaIpcServerConnUnix * >(conn); GError *err = nullptr; GST_LOG_OBJECT (self, "Sending mmap message"); if (!g_output_stream_write_all (unix_conn->ostream, &conn->server_msg[0], conn->server_msg.size (), nullptr, priv->cancellable, &err)) { GST_WARNING_OBJECT (self, "Couldn't write mmap data, %s", err->message); g_clear_error (&err); return false; } if (!g_unix_connection_send_fd (G_UNIX_CONNECTION (unix_conn->socket_conn), handle, priv->cancellable, &err)) { GST_WARNING ("Couldn't send fd, %s", err->message); g_clear_error (&err); return false; } gst_cuda_ipc_server_send_msg_finish (server, conn, true); return true; } static gboolean gst_cuda_ipc_server_unix_on_incoming (GSocketService * service, GSocketConnection * socket_conn, GObject * source_obj, GstCudaIpcServer * self) { GST_DEBUG_OBJECT (self, "Got new connection"); auto conn = std::make_shared < GstCudaIpcServerConnUnix > (socket_conn); gst_cuda_ipc_server_on_incoming_connection (self, conn); return TRUE; } static void gst_cuda_ipc_server_unix_loop (GstCudaIpcServer * server) { GstCudaIpcServerUnix *self = GST_CUDA_IPC_SERVER_UNIX (server); GstCudaIpcServerUnixPrivate *priv = self->priv; GError *err = nullptr; GSocketAddress *addr; GSocketService *service; gboolean ret; g_main_context_push_thread_default (priv->main_context); service = g_socket_service_new (); addr = g_unix_socket_address_new (priv->address.c_str ()); GST_DEBUG_OBJECT (self, "Creating service with address \"%s\"", priv->address.c_str ()); ret = g_socket_listener_add_address (G_SOCKET_LISTENER (service), addr, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, nullptr, nullptr, &err); g_object_unref (addr); if (!ret) { GST_ERROR_OBJECT (self, "Setup failed, error: %s", err->message); g_clear_error (&err); g_clear_object (&service); gst_cuda_ipc_server_abort (server); } else { g_signal_connect (service, "incoming", G_CALLBACK (gst_cuda_ipc_server_unix_on_incoming), self); g_socket_service_start (service); } GST_DEBUG_OBJECT (self, "Starting loop"); g_main_loop_run (priv->main_loop); GST_DEBUG_OBJECT (self, "Loop stopped"); if (service) { g_cancellable_cancel (priv->cancellable); g_unlink (priv->address.c_str ()); g_clear_object (&service); } g_main_context_pop_thread_default (priv->main_context); } GstCudaIpcServer * gst_cuda_ipc_server_new (const gchar * address, GstCudaContext * context, GstCudaIpcMode ipc_mode) { GstCudaIpcServerUnix *self; GstCudaIpcServer *server; g_return_val_if_fail (address, nullptr); g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), nullptr); self = (GstCudaIpcServerUnix *) g_object_new (GST_TYPE_CUDA_IPC_SERVER_UNIX, nullptr); gst_object_ref_sink (self); self->priv->address = address; server = GST_CUDA_IPC_SERVER (self); server->context = (GstCudaContext *) gst_object_ref (context); server->ipc_mode = ipc_mode; server->pid = getpid (); gst_cuda_ipc_server_run (server); return server; }