obs-studio-plugin-unix-sock.../src/socket.c
Stephen Seo 4c708a2b9a Version 1.3.0
Allow toggling streaming and replay-buffer.
2024-01-26 17:37:03 +09:00

269 lines
9.1 KiB
C

#include "socket.h"
#include "common_constants.h"
// standard library includes
#include <stdlib.h>
#include <stdio.h>
// unix includes
#include <string.h>
#include <sys/stat.h>
#include <threads.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
// obs-studio includes
#include <obs-frontend-api.h>
int unix_socket_handler_thread_function(void *ud) {
UnixSocketHandler *handler = (UnixSocketHandler*)ud;
struct timespec duration;
duration.tv_sec = 0;
duration.tv_nsec = 10000000;
int ret;
int data_socket;
char buffer[8];
char ret_buffer[8];
unsigned int ticks;
while(1) {
mtx_lock(handler->mutex);
if ((handler->ccflags & 1) != 0) {
mtx_unlock(handler->mutex);
return 0;
}
mtx_unlock(handler->mutex);
data_socket = accept(handler->socket_descriptor, 0, 0);
if (data_socket == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
thrd_sleep(&duration, 0);
continue;
}
// Error. TODO handle this.
break;
}
ticks = 0;
memset(ret_buffer, 0, sizeof(ret_buffer));
while (1) {
mtx_lock(handler->mutex);
if ((handler->ccflags & 1) != 0) {
mtx_unlock(handler->mutex);
close(data_socket);
return 0;
}
mtx_unlock(handler->mutex);
ret = read(data_socket, buffer, sizeof(buffer));
if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
if (++ticks > 200) {
// Timed out.
ret = -1;
break;
}
thrd_sleep(&duration, 0);
continue;
}
// 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_TOGGLE_RECORDING) {
ret_buffer[0] = UNIX_SOCKET_EVENT_TOGGLE_RECORDING;
if (obs_frontend_recording_active()) {
obs_frontend_recording_stop();
ret_buffer[1] = UNIX_SOCKET_EVENT_STOP_RECORDING;
} else {
obs_frontend_recording_start();
ret_buffer[1] = UNIX_SOCKET_EVENT_START_RECORDING;
}
} 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_TOGGLE_STREAMING) {
ret_buffer[0] = UNIX_SOCKET_EVENT_TOGGLE_STREAMING;
if (obs_frontend_streaming_active()) {
obs_frontend_streaming_stop();
ret_buffer[1] = UNIX_SOCKET_EVENT_STOP_STREAMING;
} else {
obs_frontend_streaming_start();
ret_buffer[1] = UNIX_SOCKET_EVENT_START_STREAMING;
}
} 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;
}
if (obs_frontend_replay_buffer_active()) {
ret_buffer[1] |= 4;
}
} else if (buffer[0] == UNIX_SOCKET_EVENT_START_REPLAY_BUFFER) {
obs_frontend_replay_buffer_start();
ret_buffer[0] = UNIX_SOCKET_EVENT_NOP;
} else if (buffer[0] == UNIX_SOCKET_EVENT_STOP_REPLAY_BUFFER) {
obs_frontend_replay_buffer_stop();
ret_buffer[0] = UNIX_SOCKET_EVENT_NOP;
} else if (buffer[0] == UNIX_SOCKET_EVENT_TOGGLE_REPLAY_BUFFER) {
ret_buffer[0] = UNIX_SOCKET_EVENT_TOGGLE_REPLAY_BUFFER;
if (obs_frontend_replay_buffer_active()) {
obs_frontend_replay_buffer_stop();
ret_buffer[1] = UNIX_SOCKET_EVENT_STOP_REPLAY_BUFFER;
} else {
obs_frontend_replay_buffer_start();
ret_buffer[1] = UNIX_SOCKET_EVENT_START_REPLAY_BUFFER;
}
} else if (buffer[0] == UNIX_SOCKET_EVENT_SAVE_REPLAY_BUFFER) {
if (obs_frontend_replay_buffer_active()) {
obs_frontend_replay_buffer_save();
ret_buffer[0] = UNIX_SOCKET_EVENT_NOP;
} else {
ret = -1;
break;
}
}
ret = 0;
break;
}
if (ret == 0) {
ret = write(data_socket, ret_buffer, sizeof(ret_buffer));
if (ret == -1) {
// Error. TODO handle this.
close(data_socket);
continue;
}
}
close(data_socket);
}
return 0;
}
void init_unix_socket_handler(UnixSocketHandler *handler) {
memset(handler, 0, sizeof(UnixSocketHandler));
snprintf(handler->socket_filename, sizeof(handler->socket_filename),
UNIX_SOCKET_HANDLER_SOCKET_FMT_STRING, getenv("USER"));
umask(S_IRWXO);
// Set up unix socket.
handler->socket_descriptor = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (handler->socket_descriptor == -1) {
handler->flags = 0xFFFFFFFFFFFFFFFF;
fprintf(stderr, "ERROR: unix-socket-control: Failed to create socket!\n");
return;
}
int ret = fcntl(handler->socket_descriptor, F_SETFL, O_NONBLOCK, 1);
if (ret == -1) {
close(handler->socket_descriptor);
handler->socket_descriptor = -1;
handler->flags = 0xFFFFFFFFFFFFFFFF;
fprintf(stderr, "ERROR: unix-socket-control: Failed to set socket non-blocking!\n");
return;
}
handler->name.sun_family = AF_UNIX;
strncpy(handler->name.sun_path,
handler->socket_filename,
sizeof(handler->name.sun_path) - 1);
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;
fprintf(stderr, "ERROR: unix-socket-control: Failed to bind socket to filename!\n");
return;
}
ret = listen(handler->socket_descriptor, 20);
if (ret == -1) {
close(handler->socket_descriptor);
handler->socket_descriptor = -1;
unlink(handler->socket_filename);
handler->flags = 0xFFFFFFFFFFFFFFFF;
fprintf(stderr, "ERROR: unix-socket-control: Failed to set socket listen!\n");
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(handler->socket_filename);
handler->flags = 0xFFFFFFFFFFFFFFFF;
fprintf(stderr, "ERROR: unix-socket-control: Failed to init mutex!\n");
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(handler->socket_filename);
mtx_destroy(handler->mutex);
handler->flags = 0xFFFFFFFFFFFFFFFF;
fprintf(stderr, "ERROR: unix-socket-control: Failed to init thread!\n");
return;
}
return;
}
void cleanup_unix_socket_handler(UnixSocketHandler *handler) {
if (handler->flags == 0xFFFFFFFFFFFFFFFF) {
return;
}
if (handler->socket_descriptor >= 0) {
close(handler->socket_descriptor);
handler->socket_descriptor = -1;
unlink(handler->socket_filename);
}
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;
}
int is_unix_socket_handler_valid(const UnixSocketHandler *handler) {
return handler->flags == 0xFFFFFFFFFFFFFFFF ? 0 : 1;
}