WIP: Impl. "server" plugin and "client" executable

Untested.
This commit is contained in:
Stephen Seo 2023-08-12 21:59:52 +09:00
parent c850536731
commit 99fab95766
6 changed files with 278 additions and 45 deletions

View file

@ -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)

119
src/client.c Normal file
View file

@ -0,0 +1,119 @@
// standard library includes
#include <stdio.h>
#include <string.h>
// unix includes
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
// 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;
}

15
src/common_constants.h Normal file
View file

@ -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

View file

@ -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;
}

View file

@ -6,47 +6,137 @@
// unix includes
#include <string.h>
#include <sys/stat.h>
#include <threads.h>
#include <unistd.h>
#define UNIX_SOCKET_HANDLER_SOCKET_NAME "/tmp/obs-studio-plugin-unix-socket-handler-socket"
// obs-studio includes
#include <obs-frontend-api.h>
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;
}

View file

@ -3,39 +3,32 @@
// standard library includes
#include <stdint.h>
#include <threads.h>
// unix includes
#include <sys/socket.h>
#include <sys/un.h>
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);