47#include <condition_variable>
86#define MAX_STAT_EVENT_QUEUE 100
112#define CONTROL_EXIT -1
113#define CONTROL_CLOSE_HANDLE -2
115static bool parse_msg(
char *buffer,
int buffer_len,
char *
message,
int *new_buffer_len);
124#define MSG_NOSIGNAL 0
129 const char *p = (
const char *) buf;
133 while (rest > 0 && ((fd > 2 && (w = send(fd, p, rest,
MSG_NOSIGNAL)) > 0) ||
134 (w = write(fd, p, rest)) > 0)) {
156ADD_TO_PARAM(
"control-report-audio-ch-count",
"* control-report-audio-ch-count=<count>\n"
157 " The number of channels reported over control port.\n");
181 if (force_ip_version != 4) {
182 s->
socket_fd = socket(AF_INET6, SOCK_STREAM, 0);
187 if (force_ip_version == 4 || (force_ip_version == 0 && s->
socket_fd ==
INVALID_SOCKET && errno == EAFNOSUPPORT)) {
189 s->
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
197 rc = setsockopt(s->
socket_fd, SOL_SOCKET, SO_REUSEADDR,
204 if (ip_version == 6 && setsockopt(s->
socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, (
char *)&ipv6only,
205 sizeof(ipv6only)) != 0) {
212 struct sockaddr_storage ss{};
214 if (ip_version == 4) {
215 auto *s_in =
reinterpret_cast<struct sockaddr_in *
>(&ss);
216 s_in->sin_family = AF_INET;
217 s_in->sin_addr.s_addr = htonl(INADDR_ANY);
219 s_len =
sizeof *s_in;
221 auto *s_in6 =
reinterpret_cast<struct sockaddr_in6 *
>(&ss);
222 s_in6->sin6_family = AF_INET6;
223 s_in6->sin6_addr = in6addr_any;
225 s_len =
sizeof *s_in6;
228 rc = ::bind(s->
socket_fd,
reinterpret_cast<const struct sockaddr *
>(&ss), s_len);
240 if (force_ip_version == 6) {
244 s->
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
246 struct addrinfo hints, *res, *res0;
249 memset(&hints, 0,
sizeof(hints));
250 hints.ai_family = AF_INET;
251 hints.ai_socktype = SOCK_STREAM;
254 err = getaddrinfo(
"127.0.0.1", port_str, &hints, &res0);
257 fprintf(stderr,
"Unable to get address: %s\n", gai_strerror(err));
260 bool connected =
false;
262 for (res = res0; res; res = res->ai_next) {
263 if(connect(s->
socket_fd, res->ai_addr, res->ai_addrlen) == -1) {
273 fprintf(stderr,
"Unable to connect to localhost:%d\n", s->
network_port);
312#define prefix_matches(x,y) strncasecmp(x, y, strlen(y)) == 0
313#define suffix(x,y) x + strlen(y)
314#define is_internal_port(x) (x == s->internal_fd[0])
320 if (strcmp(cmd,
"mute") == 0 || strstr(cmd,
"-receiver") !=
nullptr) {
321 strncpy(path,
"audio.receiver",
sizeof path);
324 if (strcmp(cmd,
"mute") == 0) {
333 "malformed audio recv mute msg");
337 if (strstr(cmd,
"-sender") !=
nullptr) {
338 strncpy(path,
"audio.sender",
sizeof path);
348 "malformed audio send mute msg");
353 strncpy(path,
"audio.receiver",
sizeof path);
356 const char *dir =
suffix(cmd,
"volume ");
357 if (strcmp(dir,
"up") == 0) {
359 }
else if (strcmp(dir,
"down") == 0) {
378 char path[1024] =
"";
379 char path_audio[1024] =
"";
415 if(strcasecmp(
message,
"quit") == 0) {
417 }
else if (strcasecmp(
message,
"exit") == 0) {
420 }
else if (strcasecmp(
message,
"help") == 0) {
423 }
else if (strcasecmp(
message,
"noop") == 0) {
427 struct client *cur = clients;
445 if (ret != (
int) strlen(
new_msg)) {
454 if (strcasecmp(toggle,
"on") == 0) {
457 }
else if (strcasecmp(toggle,
"off") == 0) {
474 if (strchr(port_str,
':')) {
475 char *save_ptr, *
item;
476 item = strtok_r(port_str,
":", &save_ptr);
478 item = strtok_r(NULL,
":", &save_ptr);
481 item = strtok_r(NULL,
":", &save_ptr);
483 char *item2 = strtok_r(NULL,
":", &save_ptr);
486 msg_audio->
tx_port = atoi(item2);
492 msg->tx_port = atoi(port_str);
505 memcpy(path_audio, path,
sizeof(path_audio));
533 memcpy(path_audio, path,
sizeof(path_audio));
554 msg->new_rx_port = atoi(port_str);
555 if (strchr(port_str,
':')) {
556 msg_audio->
new_rx_port = atoi(strchr(port_str,
':') + 1);
576 if(strncasecmp(
fec,
"audio ", 6) == 0) {
578 strncat(
msg->text,
fec + 6,
sizeof(
msg->text) - strlen(
msg->text) - 1);
579 }
else if(strncasecmp(
fec,
"video ", 6) == 0) {
581 strncat(
msg->text,
fec + 6,
sizeof(
msg->text) - strlen(
msg->text) - 1);
607 compress =
suffix(compress,
"param ");
612 strncpy(
msg->config_string, compress,
sizeof(
msg->config_string) - 1);
628 strncpy(path,
"display",
sizeof path);
631 msg->text[
sizeof msg->text - 1] =
'\0';
633 }
else if(strcasecmp(
message,
"bye") == 0) {
636 }
else if(strcmp(
message,
"dump-tree") == 0) {
645 strncpy(
msg->text, strchr(
message,
' ') + 1,
sizeof(path) - 1);
647 strncpy(path,
message,
sizeof(path) - 1);
654 fprintf(stderr,
"No response received!\n");
655 snprintf(buf,
sizeof(buf),
"(unknown path: %s)", path);
671 strncat(buffer,
" ",
sizeof(buffer) - strlen(buffer) - 1 - 2);
672 strncat(buffer,
response_get_text(resp),
sizeof(buffer) - strlen(buffer) - 1 - 2);
674 strcat(buffer,
"\r\n");
676 ssize_t ret =
write_all(fd, buffer, strlen(buffer));
689 while (i < buffer_len) {
690 if(buffer[i] ==
'\0' || buffer[i] ==
'\n' || buffer[i] ==
'\r') {
699 for( ; i < buffer_len; ++i) {
700 if(buffer[i] ==
'\0' || buffer[i] ==
'\n' || buffer[i] ==
'\r') {
709 memmove(buffer, buffer + i, buffer_len - i);
710 *new_buffer_len = buffer_len - i;
721 malloc(
sizeof(
struct client));
723 new_client->
prev = NULL;
724 new_client->
next = clients;
725 if (new_client->
next) {
726 new_client->
next->
prev = new_client;
738 if (strcmp(
m->text,
"get_port") == 0) {
743 snprintf(port_str,
sizeof port_str,
"%hu", port);
749 }
else if (strstr(m->
text,
"execute ") == m->
text) {
763 ioctlsocket(fd, FIONBIO, &ul);
765 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
772 " Open control socket to public network.\n");
777 struct client *clients = NULL;
785 struct sockaddr_storage client_addr;
786 socklen_t
len =
sizeof client_addr;
792 bool should_exit =
false;
794 struct timeval last_report_sent;
795 const int report_interval_sec = 5;
796 gettimeofday(&last_report_sent, NULL);
798 while(!should_exit) {
809 struct client *cur = clients;
812 FD_SET(cur->
fd, &fds);
819 struct timeval timeout;
820 timeout.tv_sec = report_interval_sec;
823 struct timeval *timeout_ptr = NULL;
824 if(clients->
next != NULL) {
825 timeout_ptr = &timeout;
830 if ((rc = select(
max_fd, &fds, NULL, NULL, timeout_ptr)) >= 1) {
845 log_msg(
LOG_LEVEL_WARNING,
"[control socket] Refusing remote connection. Use \"--param control-accept-global\" to allow UG control from a remote host.\n");
846 const char *
msg =
"unauthorized\r\n";
856 struct client *cur = clients;
859 if(FD_ISSET(cur->
fd, &fds)) {
863 fprintf(stderr,
"Error reading socket, closing!!!\n");
891 char msg[
sizeof(cur->
buff) + 1];
899 shutdown(cur->
fd, SHUT_RDWR);
903 fprintf(stderr,
"Socket buffer full and no delimited message. Discarding.\n");
912 struct client *cur = clients;
916 const char *
msg =
"event exit\r\n";
935 std::unique_lock<std::mutex> lk(s->
stats_lock);
946 fprintf(stderr,
"Cannot write stat line!\n");
973 fprintf(stderr,
"Cannot exit control thread!\n");
987 std::unique_lock<std::mutex> lk(s->
stats_lock);
1022 return state ?
state->audio_channel_report_count : 0;
1027 TBOLD(
"\texit")
"\n"
1028 TBOLD(
"\tpause")
"\n"
1029 TBOLD(
"\tplay")
"\n"
1030 TBOLD(
"\treciever {pause|play}")
"\n"
1031 TBOLD(
"\treset-ssrc")
"\n"
1032 TBOLD(
"\t{receiver|sender}-port <XY>")
"\n"
1033 TBOLD(
"\tfec {audio|video} <fec-string>")
"\n"
1034 TBOLD(
"\tcompress <new-compress>")
"\n"
1035 TBOLD(
"\tcompress param <new-compress-param>")
"\n"
1036 TBOLD(
"\tvolume {up|down}")
u8"¹\n"
1037 TBOLD(
"\tav-delay <ms>")
u8"¹\n"
1038 TBOLD(
"\tmute")
" - toggles receiver mute\n"
1039 TBOLD(
"\t[un]mute-{receiver,sender}")
1040 " - (un)mutes audio sender or receiver\n"
1041 TBOLD(
"\tpostprocess <new_postprocess> | flush")
"\n"
1042 TBOLD(
"\tdump-tree")
"\n");
1043 color_printf(
"\nOther commands can be issued directly to individual "
1044 "modules (see \"" TBOLD(
"dump-tree")
"\"), eg.:\n"
1045 "\t" TBOLD(
"capture.filter mirror")
"\n"
1046 "\nSometimes those modules support help (eg. \"" TBOLD(
"capture.filter help")
"\")\n\n");
int color_printf(const char *format,...)
static struct response * process_audio_message(struct module *root_module, const char *cmd)
int control_init(int port, int connection_type, struct control_state **state, struct module *root_module, int force_ip_version)
static struct client * add_client(struct client *clients, fd_t fd)
#define is_internal_port(x)
static void new_message(struct module *m)
#define MAX_STAT_EVENT_QUEUE
static bool parse_msg(char *buffer, int buffer_len, char *message, int *new_buffer_len)
bool control_stats_enabled(struct control_state *s)
void control_done(struct control_state *s)
#define prefix_matches(x, y)
void control_start(struct control_state *s)
static void * stat_event_thread(void *args)
static void control_report_stats_event(struct control_state *s, const std::string &report_line)
static void send_response(fd_t fd, struct response *resp)
static ssize_t write_all(fd_t fd, const void *buf, size_t count)
#define CONTROL_CLOSE_HANDLE
int control_audio_ch_report_count(struct control_state *state)
void control_report_stats(struct control_state *s, const std::string &report_line)
static int process_msg(struct control_state *s, fd_t client_fd, char *message, struct client *clients)
static void process_messages(struct control_state *s)
void control_report_event(struct control_state *s, const std::string &report_line)
static void print_control_help()
static void set_socket_nonblock(fd_t fd)
static void * control_thread(void *args)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_WARNING
less severe errors
static void new_msg(struct module *mod)
void module_init_default(struct module *module_data)
void module_register(struct module *module_data, struct module *parent)
void dump_tree(struct module *root_node, int indent)
void module_done(struct module *module_data)
const char * module_class_name(enum module_class cls)
void append_message_path(char *buf, int buflen, const enum module_class *modules)
void set_audio_delay(int audio_delay)
const char * get_commandline_param(const char *key)
#define ADD_TO_PARAM(param, doc)
@ STR_LEN
"standard" string length placeholder
void free_message(struct message *msg, struct response *r)
struct response * new_response(int status, const char *text)
int response_get_status(struct response *r)
struct response * send_message(struct module *root, const char *const_path, struct message *msg)
Sends message without waiting for response.
struct message * check_message(struct module *mod)
void free_response(struct response *r)
const char * response_status_to_text(int status)
const char * response_get_text(struct response *r)
@ SENDER_MSG_CHANGE_RECEIVER
#define RESPONSE_NOT_IMPL
@ RECEIVER_MSG_MUTE_TOGGLE
@ RECEIVER_MSG_DECREASE_VOLUME
@ RECEIVER_MSG_CHANGE_RX_PORT
@ RECEIVER_MSG_INCREASE_VOLUME
#define RESPONSE_BAD_REQUEST
#define RESPONSE_INT_SERV_ERR
#define MSG_UNIVERSAL_TAG_TX
#define RESPONSE_NOT_FOUND
bool is_addr_loopback(struct sockaddr *sa)
uint16_t socket_get_recv_port(int fd)
enum connection_type connection_type
struct module * root_module
condition_variable stat_event_cv
thread stat_event_thread_id
queue< string > stat_event_queue
int audio_channel_report_count
notify_t new_message
if set, notifies module that new message is in queue, receiver lock is hold during the call
enum msg_receiver_type type
enum msg_sender_type type
void set_thread_name(const char *name)