From 99fab95766898d99e3eb545ec7e0cf9b990d2585 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Sat, 12 Aug 2023 21:59:52 +0900 Subject: [PATCH] WIP: Impl. "server" plugin and "client" executable Untested. --- CMakeLists.txt | 2 + src/client.c | 119 +++++++++++++++++++++++++++++++ src/common_constants.h | 15 ++++ src/plugin.c | 2 +- src/socket.c | 156 ++++++++++++++++++++++++++++++++++------- src/socket.h | 29 +++----- 6 files changed, 278 insertions(+), 45 deletions(-) create mode 100644 src/client.c create mode 100644 src/common_constants.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f84fa4c..2a312a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,3 +21,5 @@ add_library(unix-socket-control MODULE find_package(libobs REQUIRED) target_link_libraries(unix-socket-control PRIVATE OBS::libobs) + +add_executable(unix-socket-control-client src/client.c) diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..246cb5c --- /dev/null +++ b/src/client.c @@ -0,0 +1,119 @@ +// standard library includes +#include +#include + +// unix includes +#include +#include +#include + +// local includes +#include "common_constants.h" + +void print_usage(char *name) { + printf("Usage:\n"); + printf(" %s\n", name); + printf(" [--start-recording \n"); + printf(" | --stop-recording\n"); + printf(" | --start-streaming\n"); + printf(" | --stop-streaming\n"); + printf(" | --get-status]\n"); +} + +void cleanup_data_socket(int *data_socket) { + if (*data_socket >= 0) { + close(*data_socket); + } +} + +int main(int argc, char **argv) { + UnixSocketEventType type = UNIX_SOCKET_EVENT_NOP; + + if (argc == 2) { + if (strncmp(argv[1], "--start-recording", 17) == 0) { + type = UNIX_SOCKET_EVENT_START_RECORDING; + } else if (strncmp(argv[1], "--stop-recording", 16) == 0) { + type = UNIX_SOCKET_EVENT_STOP_RECORDING; + } else if (strncmp(argv[1], "--start-streaming", 17) == 0) { + type = UNIX_SOCKET_EVENT_START_STREAMING; + } else if (strncmp(argv[1], "--stop-streaming", 16) == 0) { + type = UNIX_SOCKET_EVENT_STOP_STREAMING; + } else if (strncmp(argv[1], "--get-status", 12) == 0) { + type = UNIX_SOCKET_EVENT_GET_STATUS; + } else { + puts("ERROR: Invalid arg!"); + print_usage(argv[0]); + return 2; + } + } else { + print_usage(argv[0]); + return 1; + } + + struct sockaddr_un addr; + int ret; + __attribute__((cleanup(cleanup_data_socket))) int data_socket = -1; + char send_buf = (char)type; + char buffer[8]; + + memset(buffer, 0, sizeof(buffer)); + + data_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (data_socket == -1) { + // Error. TODO handle this. + return 3; + } + + memset(&addr, 0, sizeof(addr)); + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, + UNIX_SOCKET_HANDLER_SOCKET_NAME, + sizeof(addr.sun_path) - 1); + + ret = connect(data_socket, (const struct sockaddr*) &addr, sizeof(addr)); + if (ret == -1) { + // Error. TODO handle this. + return 4; + } + + ret = write(data_socket, &send_buf, 1); + if (ret == -1) { + // Error. TODO handle this. + return 5; + } + + ret = read(data_socket, buffer, sizeof(buffer)); + if (ret == -1 || ret < 8) { + return 6; + } + + if (type == UNIX_SOCKET_EVENT_GET_STATUS && buffer[0] == UNIX_SOCKET_EVENT_GET_STATUS) { + printf("Is recording: %s\nIs streaming: %s\n", + (buffer[1] & 1) != 0 ? "true" : "false", + (buffer[1] & 2) != 0 ? "true" : "false"); + } else if (buffer[0] != UNIX_SOCKET_EVENT_NOP) { + // Error. TODO handle this. + return 7; + } else { + switch(type) { + case UNIX_SOCKET_EVENT_START_RECORDING: + puts("Sent event \"start recording\"!"); + break; + case UNIX_SOCKET_EVENT_STOP_RECORDING: + puts("Sent event \"stop recording\"!"); + break; + case UNIX_SOCKET_EVENT_START_STREAMING: + puts("Sent event \"start streaming\"!"); + break; + case UNIX_SOCKET_EVENT_STOP_STREAMING: + puts("Sent event \"stop streaming\"!"); + break; + default: + // Error. TODO handle this + return 8; + } + } + + return 0; +} diff --git a/src/common_constants.h b/src/common_constants.h new file mode 100644 index 0000000..b21f2c6 --- /dev/null +++ b/src/common_constants.h @@ -0,0 +1,15 @@ +#ifndef OBS_STUDIO_PLUGIN_UNIX_SOCKET_CONTROL_COMMON_CONSTANTS_H_ +#define OBS_STUDIO_PLUGIN_UNIX_SOCKET_CONTROL_COMMON_CONSTANTS_H_ + +#define UNIX_SOCKET_HANDLER_SOCKET_NAME "/tmp/obs-studio-plugin-unix-socket-handler-socket" + +typedef enum UnixSocketEventType { + UNIX_SOCKET_EVENT_NOP = 0, + UNIX_SOCKET_EVENT_START_RECORDING, + UNIX_SOCKET_EVENT_STOP_RECORDING, + UNIX_SOCKET_EVENT_START_STREAMING, + UNIX_SOCKET_EVENT_STOP_STREAMING, + UNIX_SOCKET_EVENT_GET_STATUS +} UnixSocketEventType; + +#endif diff --git a/src/plugin.c b/src/plugin.c index 68adc95..8af859f 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -14,7 +14,7 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("unix-socket-control", "en-US") bool obs_module_load(void) { - global_unix_socket_handler = init_unix_socket_handler(); + init_unix_socket_handler(&global_unix_socket_handler); return is_unix_socket_handler_valid(global_unix_socket_handler) ? true : false; } diff --git a/src/socket.c b/src/socket.c index 7bfc3cf..3c65be5 100644 --- a/src/socket.c +++ b/src/socket.c @@ -6,47 +6,137 @@ // unix includes #include #include +#include #include -#define UNIX_SOCKET_HANDLER_SOCKET_NAME "/tmp/obs-studio-plugin-unix-socket-handler-socket" +// obs-studio includes +#include -UnixSocketHandler init_unix_socket_handler(void) { - UnixSocketHandler handler; +int unix_socket_handler_thread_function(void *ud) { + UnixSocketHandler *handler = (UnixSocketHandler*)ud; - memset(&handler, 0, sizeof(UnixSocketHandler)); + int ret; + int data_socket; + char buffer[8]; + char ret_buffer[8]; + + while(1) { + mtx_lock(handler->mutex); + if ((handler->ccflags & 1) != 0) { + mtx_unlock(handler->mutex); + break; + } + mtx_unlock(handler->mutex); + + data_socket = accept(handler->socket_descriptor, 0, 0); + if (data_socket == -1) { + // Error. TODO handle this. + break; + } + + memset(ret_buffer, 0, sizeof(ret_buffer)); + + ret = read(data_socket, buffer, sizeof(buffer)); + if (ret == -1) { + // Error. TODO handle this. + break; + } + + if (buffer[0] == UNIX_SOCKET_EVENT_START_RECORDING) { + obs_frontend_recording_start(); + ret_buffer[0] = UNIX_SOCKET_EVENT_NOP; + } else if (buffer[0] == UNIX_SOCKET_EVENT_STOP_RECORDING) { + obs_frontend_recording_stop(); + ret_buffer[0] = UNIX_SOCKET_EVENT_NOP; + } else if (buffer[0] == UNIX_SOCKET_EVENT_START_STREAMING) { + obs_frontend_streaming_start(); + ret_buffer[0] = UNIX_SOCKET_EVENT_NOP; + } else if (buffer[0] == UNIX_SOCKET_EVENT_STOP_STREAMING) { + obs_frontend_streaming_stop(); + ret_buffer[0] = UNIX_SOCKET_EVENT_NOP; + } else if (buffer[0] == UNIX_SOCKET_EVENT_GET_STATUS) { + ret_buffer[0] = UNIX_SOCKET_EVENT_GET_STATUS; + if (obs_frontend_recording_active()) { + ret_buffer[1] |= 1; + } + if (obs_frontend_streaming_active()) { + ret_buffer[1] |= 2; + } + } + + ret = write(data_socket, ret_buffer, sizeof(ret_buffer)); + if (ret == -1) { + // Error. TODO handle this. + break; + } + + close(data_socket); + } + + return 0; +} + +void init_unix_socket_handler(UnixSocketHandler *handler) { + memset(handler, 0, sizeof(UnixSocketHandler)); umask(S_IRWXO); - // Set up linked list of events. - handler.events_head.next = &handler.events_tail; - handler.events_tail.prev = &handler.events_head; - - handler.events_head.type = UNIX_SOCKET_EVENT_HEAD; - handler.events_tail.type = UNIX_SOCKET_EVENT_TAIL; - // Set up unix socket. - handler.socket_descriptor = socket(AF_UNIX, SOCK_SEQPACKET, 0); - if (handler.socket_descriptor == -1) { - handler.flags = 0xFFFFFFFFFFFFFFFF; - return handler; + handler->socket_descriptor = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (handler->socket_descriptor == -1) { + handler->flags = 0xFFFFFFFFFFFFFFFF; + return; } - handler.name.sun_family = AF_UNIX; - strncpy(handler.name.sun_path, + handler->name.sun_family = AF_UNIX; + strncpy(handler->name.sun_path, UNIX_SOCKET_HANDLER_SOCKET_NAME, - sizeof(handler.name.sun_path) - 1); + sizeof(handler->name.sun_path) - 1); - int ret = bind(handler.socket_descriptor, - (const struct sockaddr*) &handler.name, - sizeof(handler.name)); + int ret = bind(handler->socket_descriptor, + (const struct sockaddr*) &handler->name, + sizeof(handler->name)); if (ret == -1) { - close(handler.socket_descriptor); - handler.socket_descriptor = -1; - handler.flags = 0xFFFFFFFFFFFFFFFF; - return handler; + close(handler->socket_descriptor); + handler->socket_descriptor = -1; + handler->flags = 0xFFFFFFFFFFFFFFFF; + return; } - return handler; + ret = listen(handler->socket_descriptor, 20); + if (ret == -1) { + close(handler->socket_descriptor); + handler->socket_descriptor = -1; + unlink(UNIX_SOCKET_HANDLER_SOCKET_NAME); + handler->flags = 0xFFFFFFFFFFFFFFFF; + return; + } + + // Set up concurrency. + handler->mutex = malloc(sizeof(mtx_t)); + ret = mtx_init(handler->mutex, mtx_plain); + if (ret != thrd_success) { + close(handler->socket_descriptor); + handler->socket_descriptor = -1; + unlink(UNIX_SOCKET_HANDLER_SOCKET_NAME); + handler->flags = 0xFFFFFFFFFFFFFFFF; + return; + } + + handler->thread = malloc(sizeof(thrd_t)); + ret = thrd_create(handler->thread, + unix_socket_handler_thread_function, + handler); + if (ret != thrd_success) { + close(handler->socket_descriptor); + handler->socket_descriptor = -1; + unlink(UNIX_SOCKET_HANDLER_SOCKET_NAME); + mtx_destroy(handler->mutex); + handler->flags = 0xFFFFFFFFFFFFFFFF; + return; + } + + return; } void cleanup_unix_socket_handler(UnixSocketHandler *handler) { @@ -59,6 +149,20 @@ void cleanup_unix_socket_handler(UnixSocketHandler *handler) { handler->socket_descriptor = -1; unlink(UNIX_SOCKET_HANDLER_SOCKET_NAME); } + if (handler->mutex) { + mtx_lock(handler->mutex); + handler->ccflags |= 1; + mtx_unlock(handler->mutex); + thrd_join(*handler->thread, 0); + + free(handler->thread); + handler->thread = 0; + + mtx_destroy(handler->mutex); + free(handler->mutex); + handler->mutex = 0; + } + handler->flags = 0xFFFFFFFFFFFFFFFF; } diff --git a/src/socket.h b/src/socket.h index 78d1560..e00e278 100644 --- a/src/socket.h +++ b/src/socket.h @@ -3,39 +3,32 @@ // standard library includes #include +#include // unix includes #include #include -typedef enum UnixSocketEventType { - UNIX_SOCKET_EVENT_NOP, - UNIX_SOCKET_EVENT_HEAD, - UNIX_SOCKET_EVENT_TAIL, - UNIX_SOCKET_EVENT_START_RECORDING, - UNIX_SOCKET_EVENT_STOP_RECORDING, - UNIX_SOCKET_EVENT_START_STREAMING, - UNIX_SOCKET_EVENT_STOP_STREAMING -} UnixSocketEventType; - -typedef struct UnixSocketEvent { - struct UnixSocketEvent *next; - struct UnixSocketEvent *prev; - UnixSocketEventType type; -} UnixSocketEvent; +// local includes +#include "common_constants.h" typedef struct UnixSocketHandler { /* * All ones - invalid/cleaned-up instance */ uint64_t flags; - UnixSocketEvent events_head; - UnixSocketEvent events_tail; struct sockaddr_un name; + thrd_t *thread; + mtx_t *mutex; + /* + * ???? 0001 - thread should stop + */ + volatile uint64_t ccflags; + int socket_descriptor; } UnixSocketHandler; -UnixSocketHandler init_unix_socket_handler(void); +void init_unix_socket_handler(UnixSocketHandler *handler); void cleanup_unix_socket_handler(UnixSocketHandler *handler); int is_unix_socket_handler_valid(UnixSocketHandler handler);