83using std::invalid_argument;
89#define MOD_NAME "[hd-rum-trans] "
91#define REPLICA_MAGIC 0xd2ff3323
94 const size_t len = strlen(addr) + 2 + 6 + 1 ;
95 char *
name = (
char *) malloc(
len);
96 bool is_ipv6 = strchr(addr,
':') != NULL;
97 bool add_bracket = is_ipv6 && addr[0] !=
'[';
98 snprintf(
name,
len,
"%s%s%s:%" PRIu16, add_bracket ?
"[" :
"", addr, add_bracket ?
"]" :
"", tx_port);
104 replica(
const char *addr, uint16_t rx_port, uint16_t tx_port,
int bufsize,
struct module *
parent,
int force_ip_version) {
108 sock = std::shared_ptr<socket_udp>(
udp_init(addr, rx_port, tx_port, 255, force_ip_version,
false),
udp_exit);
111 if (!
sock || res != 0) {
112 throw string(
"Cannot initialize output port!\n");
115 fprintf(stderr,
"Cannot set send buffer to %sB!\n",
123 type = replica::type_t::NONE;
142 std::shared_ptr<socket_udp>
sock;
186static void *
writer(
void *arg);
191 if (signum == SIGPIPE) {
192 signal(SIGPIPE, SIG_IGN);
196 char msg[] =
"Caught signal ";
199 for (
size_t i = 0; i <
sizeof msg - 1; ++i) {
203 *ptr++ =
'0' + signum / 10;
205 *ptr++ =
'0' + signum % 10;
207 size_t bytes = ptr -
buf;
210 ssize_t written = write(STDERR_FILENO, ptr, bytes);
221#define MAX_PKT_SIZE 10000
224struct wsa_aux_storage {
225 WSAOVERLAPPED *overlapped;
228#define ALIGNMENT std::alignment_of<wsa_aux_storage>::value
229#define OFFSET ((MAX_PKT_SIZE + ALIGNMENT - 1) / ALIGNMENT * ALIGNMENT)
230#define SIZE (OFFSET + sizeof(wsa_aux_storage))
232#define SIZE MAX_PKT_SIZE
247 fprintf(stderr,
"wrong packet queue size %d items\n", qsize);
251 printf(
"initializing packet queue for %d items\n", qsize);
253 queue = (
struct item *) calloc(qsize,
sizeof(
struct item));
255 fprintf(stderr,
"not enough memory\n");
259 for (i = 0; i < qsize; i++) {
260 queue[i].buf = (
char *) malloc(
SIZE);
261 queue[i].next = queue + i + 1;
263 queue[qsize - 1].next =
queue;
270 if (queue ==
nullptr) {
278 }
while (q != queue);
282#define prefix_matches(x,y) strncasecmp(x, y, strlen(y)) == 0
290 if (strcasecmp(data->
text,
"sock") == 0) {
291 r->
type = replica::type_t::USE_SOCK;
293 }
else if (strcasecmp(data->
text,
"recompress") == 0) {
294 r->
type = replica::type_t::RECOMPRESS;
303 fprintf(stderr,
"Unknown replica type \"%s\"\n", data->
text);
308 r->
type == replica::type_t::RECOMPRESS);
314static VOID CALLBACK wsa_deleter(DWORD ,
316 LPOVERLAPPED lpOverlapped,
long unsigned int) {
317 struct wsa_aux_storage *aux = (
struct wsa_aux_storage *)(
void *) ((
char *) lpOverlapped->hEvent + OFFSET);
318 if (--aux->ref == 0) {
319 free(aux->overlapped);
320 free(lpOverlapped->hEvent);
326 const char *addr,
int rx_port,
int tx_port,
int bufsize,
327 struct common_opts *common,
const char *compression,
const char *
fec,
328 int bitrate,
bool use_server_sock =
false)
332 rep =
new replica(addr, rx_port, tx_port, bufsize, &s->
mod,
337 }
catch (
string const & s) {
338 fputs(s.c_str(), stderr);
339 const char *err_msg =
"cannot create output port (wrong address?)";
347 rep->
type = compression ? replica::type_t::RECOMPRESS : replica::type_t::USE_SOCK;
349 addr, compression ? compression :
"none",
350 0, tx_port, &com_opts,
fec, bitrate);
352 fprintf(stderr,
"Initializing output port '%s' compression failed!\n", addr);
360 assert((
unsigned) idx == s->
replicas.size() - 1);
373 for (
unsigned int i = 0; i < s->
replicas.size(); i++) {
384 if (strncasecmp(
msg->text,
"delete-port ", strlen(
"delete-port ")) == 0) {
385 char *port_spec =
msg->text + strlen(
"delete-port ");
387 if (isdigit(port_spec[0])) {
388 int i = stoi(port_spec);
389 if (i >= 0 && i < (
int) s->
replicas.size()) {
397 if (strcmp(r->mod.name, port_spec) == 0) {
413 }
else if (strncasecmp(
msg->text,
"create-port", strlen(
"create-port")) == 0) {
418 char *host_port, *port_str = NULL, *save_ptr;
421 strtok_r(
msg->text,
" ", &save_ptr);
422 host_port = strtok_r(NULL,
" ", &save_ptr);
423 if (host_port && (strchr(host_port,
':') != NULL || (port_str = strtok_r(NULL,
" ", &save_ptr)) != NULL)) {
426 tx_port = stoi(port_str);
428 tx_port = stoi(strrchr(host_port,
':') + 1);
430 *strrchr(host_port,
':') =
'\0';
433 if (host[0] ==
'[' && host[strlen(host) - 1] ==
']') {
435 host[strlen(host) - 1] =
'\0';
438 const char *err_msg =
"wrong format";
443 char *compress = strtok_r(NULL,
" ", &save_ptr);
447 host, 0, tx_port, s->
bufsize, &opts,
477 perror(
"hd_rum_decompress_write");
486 for (
unsigned int i = 0; i < s->
replicas.size(); i++) {
487 if(s->
replicas[i]->type == replica::type_t::USE_SOCK) {
491 struct wsa_aux_storage *aux = (
struct wsa_aux_storage *)(
void *) ((
char *) s->
qhead->
buf + OFFSET);
492 memset(aux, 0,
sizeof *aux);
493 aux->overlapped = (WSAOVERLAPPED *) calloc(ref,
sizeof(WSAOVERLAPPED));
495 int overlapped_idx = 0;
496 for (
unsigned int i = 0; i < s->
replicas.size(); i++) {
497 if(s->
replicas[i]->type == replica::type_t::USE_SOCK) {
498 aux->overlapped[overlapped_idx].hEvent = s->
qhead->
buf;
500 wsa_deleter, &aux->overlapped[overlapped_idx], (sockaddr *) &s->
replicas[i]->sockaddr, s->
replicas[i]->sockaddr_len);
502 perror(
"Hd-rum-translator send");
510 for (
unsigned int i = 0; i < s->
replicas.size(); i++) {
511 if(s->
replicas[i]->type == replica::type_t::USE_SOCK) {
514 perror(
"Hd-rum-translator send");
537static void usage(
const char *progname) {
538 col() <<
"Usage:\n\t"
540 <<
" [global_opts] buffer_size port \\\n\t\t[host1_options] "
541 "host1 [[host2_options] host2] ...")
544 col() <<
"\nwhere:\n"
545 <<
SBOLD(
"\tbuffer_size")
546 <<
" - network buffer size, eg. 200k for compressed\n\t\t or "
547 "8M for uncompressed video\n"
548 <<
SBOLD(
"\tport") <<
" - UDP port number\n";
550 <<
SBOLD(
"\t--control-port|-n <port_number>[:0|:1]")
551 <<
" - control port to connect to, optionally client/server "
553 <<
SBOLD(
"\t--blend|-B")
554 <<
" - enable blending from original to newly received stream, "
555 "increases latency\n"
556 <<
SBOLD(
"\t--server|-S <port>")
557 <<
" - enable server mode for clients to connect on specified "
559 <<
SBOLD(
"\t--conference|-r <width>:<height>[:fps]")
560 <<
" - enable combining of multiple inputs, increases latency\n"
561 <<
SBOLD(
"\t--conference-compression|-R <compression>")
562 <<
" - compression for conference participants\n"
563 <<
SBOLD(
"\t--capture-filter|-F <cfg_string>")
564 <<
" - apply video capture filter to incoming video\n"
565 <<
SBOLD(
"\t--param|-O") <<
" - additional parameters\n"
566 <<
SBOLD(
"\t--help|-h\n") <<
SBOLD(
"\t--verbose|-V\n") <<
SBOLD(
"\t-v")
567 <<
" - print version\n";
568 col() <<
"and " <<
SUNDERLINE(
"hostX_options") <<
" may be:\n"
569 <<
SBOLD(
"\t-P [<rx_port>:]<tx_port>")
570 <<
" - TX port to be used (optionally also RX)\n"
571 <<
SBOLD(
"\t-c <compression>") <<
" - compression\n"
572 <<
"\tFollowing options will be used only if " <<
SUNDERLINE(
"'-c'")
573 <<
" parameter is set:\n"
574 <<
SBOLD(
"\t-m <mtu>") <<
" - MTU size\n"
575 <<
SBOLD(
"\t-l <limiting_bitrate>") <<
" - bitrate to be shaped to\n"
576 <<
SBOLD(
"\t-f <fec>")
577 <<
" - FEC that will be used for transmission.\n"
578 <<
SBOLD(
"\t-4/-6") <<
" - force IPv4/IPv6\n";
579 printf(
"\nPlease note that blending and capture filter is used only "
580 "for host for which\n"
581 "compression is specified (transcoding is active). If "
582 "compression is not\n"
583 "set, simple packet retransmission is used. Compression can be "
585 "for uncompressed transmission (see 'uv -c help' for list).\n");
620 ret = stoi(sz_str, &end);
621 }
catch (invalid_argument &e) {
628 switch (sz_str[end]) {
646 const struct option getopt_options[] = {
647 {
"blend", no_argument,
nullptr,
'B'},
648 {
"capture-filter", required_argument,
nullptr,
'F'},
649 {
"list-modules", required_argument,
nullptr,
'L'},
650 {
"param", required_argument,
nullptr,
'O'},
651 {
"conference-compression", required_argument,
nullptr,
'R'},
652 {
"server", required_argument,
nullptr,
'S'},
653 {
"verbose", optional_argument,
nullptr,
'V'},
654 {
"capabilities", no_argument,
nullptr,
'b'},
655 {
"help", no_argument,
nullptr,
'h'},
656 {
"contol-port", required_argument,
nullptr,
'n'},
657 {
"conference", required_argument,
nullptr,
'r'},
658 {
"version", no_argument,
nullptr,
'v'},
659 {
nullptr, 0,
nullptr, 0 }
661 const char *
const optstring =
"+BF:LO:R:S:Vbhn:r:v";
664 while ((ch = getopt_long(argc, argv, optstring, getopt_options,
668 parsed->control_port = stoi(optarg);
669 parsed->control_connection_type = 0;
670 if (strchr(optarg,
':') !=
nullptr) {
671 parsed->control_connection_type =
672 stoi(strchr(optarg,
':') + 1);
679 parsed->out_conf.mode =
BLEND;
683 parsed->out_conf.arg = optarg;
686 parsed->conference_compression = optarg;
689 parsed->server_port = stoi(optarg);
692 parsed->capture_filter = optarg;
712 MSG(FATAL,
"Unknown global parameter\n\n");
736 if (optind + 2 > argc) {
737 MSG(FATAL,
"Missing parameter%s port!\n\n",
738 argc < optind + 1 ?
" buffer_size and" :
"");
745 parsed->port = stoi(argv[optind]);
746 }
catch (invalid_argument &) {
750 if (parsed->port < 0 || parsed->port >= USHRT_MAX) {
752 to_string(USHRT_MAX) +
753 "], given: " + argv[optind]);
756 if (parsed->port == 0) {
758 "Given RX port 0, reflector will listen on a random port.\n");
761 parsed->host_count = 0;
762 while (optind < argc) {
763 parsed->hosts.resize(parsed->host_count + 1);
766 const char *
const optstring =
"+46P:c:f:l:m:";
768 while ((ch = getopt(argc, argv, optstring)) != -1) {
771 if (strchr(optarg,
':') !=
nullptr) {
772 parsed->hosts[parsed->host_count].rx_port =
774 parsed->hosts[parsed->host_count].tx_port =
775 stoi(strchr(optarg,
':') + 1);
777 parsed->hosts[parsed->host_count].tx_port =
782 parsed->hosts[parsed->host_count].common_opts.mtu =
786 parsed->hosts[parsed->host_count].compression = optarg;
789 parsed->hosts[parsed->host_count].fec = optarg;
793 optarg, &parsed->hosts[parsed->host_count]
799 parsed->hosts[parsed->host_count]
800 .common_opts.force_ip_version = 4;
803 parsed->hosts[parsed->host_count]
804 .common_opts.force_ip_version = 6;
807 MSG(FATAL,
"Error: invalid host option.\nn");
811 if (optind == argc) {
813 "Error: host option following last host address!\n");
816 parsed->hosts[parsed->host_count++].addr = argv[optind++];
830 for (
unsigned int i = 0; i < s->
replicas.size(); i++) {
839static bool sockaddr_equal(
struct sockaddr_storage *a,
struct sockaddr_storage *b){
840 if(a->ss_family != b->ss_family)
843 switch(a->ss_family){
845 auto a_in =
reinterpret_cast<struct sockaddr_in *
>(a);
846 auto b_in =
reinterpret_cast<struct sockaddr_in *
>(b);
847 return a_in->sin_port == b_in->sin_port
848 && a_in->sin_addr.s_addr == b_in->sin_addr.s_addr;
851 auto a_in =
reinterpret_cast<struct sockaddr_in6 *
>(a);
852 auto b_in =
reinterpret_cast<struct sockaddr_in6 *
>(b);
853 return a_in->sin6_port == b_in->sin6_port
854 && memcmp(a_in->sin6_addr.s6_addr, b_in->sin6_addr.s6_addr,
sizeof(
struct in6_addr)) == 0;
857 assert(
false &&
"Comparison not implemented for address type");
872 should_stop.store(
true, std::memory_order_relaxed);
877 void tick(sockaddr_storage& sin, socklen_t addrlen){
879 gettimeofday(&t, NULL);
886 const double participant_timeout = 10.0;
887 if(
tv_diff(t, it->last_recv) > participant_timeout){
889 std::string
msg =
"delete-port ";
890 auto addr =
reinterpret_cast<struct sockaddr *
>(&it->addr);
901 strncpy(m->
text,
msg.c_str(),
sizeof(m->
text) - 1);
913 if(!seen && addrlen > 0){
920 std::string
msg =
"create-port ";
928 strncpy(m->
text,
msg.c_str(),
sizeof(m->
text) - 1);
940 void run_async(std::shared_ptr<socket_udp> server_sock){
941 sock = std::move(server_sock);
949 std::shared_ptr<socket_udp>
sock;
954 struct timeval timeout = { 1, 0 };
957 while(!
should_stop.load(std::memory_order_relaxed)){
958 struct sockaddr_storage sin = {};
959 socklen_t addrlen =
sizeof(sin);
960 int size =
udp_recvfrom_timeout(
sock.get(), dummy_buf, std::size(dummy_buf), &timeout, (sockaddr *) &sin, &addrlen);
969 volatile auto *should_exit = (
volatile bool *) arg;
973#define EXIT(retval) \
976 hd_rum_translator_deinit(&state); \
977 if (sock_in != nullptr) \
979 common_cleanup(init); \
1003 struct sigaction sa;
1004 memset(&sa, 0,
sizeof(sa));
1006 sigemptyset(&sa.sa_mask);
1008 sigaction(SIGINT, &sa, NULL);
1009 sigaction(SIGTERM, &sa, NULL);
1010 sigaction(SIGHUP, &sa, NULL);
1011 sigaction(SIGABRT, &sa, NULL);
1012 sigaction(SIGPIPE, &sa, NULL);
1028 }
catch (invalid_argument &e) {
1032 MSG(ERROR,
"Non-numeric value passed to option "
1033 "expecting a number!\n");
1035 MSG(FATAL,
"%s\n", e.what());
1038 EXIT(ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
1041 if (
params.log_level != -1) {
1047 qsize =
state.bufsize / 8000;
1049 printf(
"using UDP send and receive buffer size of %d bytes\n",
state.bufsize);
1058 perror(
"input socket");
1063 fprintf(stderr,
"Cannot set recv buffer to %sB!\n",
1069 if (
params.control_port != -1) {
1071 fprintf(stderr,
"Warning: Unable to create remote control.\n");
1079 if(!
state.recompress) {
1086 if(!
state.decompress) {
1090 if(
params.server_port > 0){
1092 if (!
state.server_socket) {
1098 params.conference_compression =
"libavcodec";
1101 for (i = 0; i <
params.host_count; i++) {
1103 int rx_port =
params.hosts[i].rx_port;
1104 if (
params.hosts[i].tx_port) {
1105 tx_port =
params.hosts[i].tx_port;
1108 auto& h =
params.hosts[i];
1111 h.compression =
params.conference_compression;
1115 h.addr, rx_port, tx_port,
state.bufsize, &h.common_opts,
1116 h.compression, h.compression ? h.fec :
nullptr, h.bitrate);
1122 if (pthread_create(&thread, NULL,
writer, (
void *) &
state)) {
1123 fprintf(stderr,
"cannot create writer thread\n");
1127 uint64_t received_data = 0;
1129 gettimeofday(&t0, NULL);
1131 unsigned long long int last_data = 0ull;
1135 if(
state.server_socket){
1139 volatile bool should_exit =
false;
1142 while (!should_exit) {
1143 while (
state.qtail->next !=
state.qhead && !should_exit) {
1144 struct timeval timeout = { 1, 0 };
1146 struct sockaddr_storage sin = {};
1147 socklen_t addrlen =
sizeof(sin);
1149 if(
state.qtail->size <= 0)
1153 gettimeofday(&t, NULL);
1156 participant_mgr.
tick(sin, addrlen);
1159 received_data +=
state.qtail->size;
1163 pthread_mutex_lock(&
state.qempty_mtx);
1165 pthread_cond_signal(&
state.qempty_cond);
1166 pthread_mutex_unlock(&
state.qempty_mtx);
1168 double seconds =
tv_diff(t, t0);
1169 if (seconds > 5.0) {
1170 unsigned long long int cur_data = (received_data - last_data);
1171 unsigned long long int bps = cur_data / seconds;
1173 time_t tim = time(NULL);
1174 struct tm *tmp = localtime(&tim);
1176 strftime(tim_str,
sizeof(tim_str),
"%F %T", tmp);
1180 last_data = received_data;
1184 if (
state.qtail->size <= 0)
1187 pthread_mutex_lock(&
state.qfull_mtx);
1189 pthread_cond_wait(&
state.qfull_cond, &
state.qfull_mtx);
1191 pthread_mutex_unlock(&
state.qfull_mtx);
1194 if (
state.qtail->size < 0 && !should_exit) {
1195 printf(
"read: %s\n", strerror(err));
1200 state.qtail->size = 0;
1203 pthread_mutex_lock(&
state.qempty_mtx);
1205 pthread_cond_signal(&
state.qempty_cond);
1206 pthread_mutex_unlock(&
state.qempty_mtx);
1209 pthread_join(thread, NULL);
static af_result_code init(struct module *parent, const char *cfg, void **state)
std::atomic< bool > should_stop
std::vector< Conf_participant > participants
Participant_manager(module &mod, const char *compress)
std::thread worker_thread
std::shared_ptr< socket_udp > sock
void run_async(std::shared_ptr< socket_udp > server_sock)
void tick(sockaddr_storage &sin, socklen_t addrlen)
int control_init(int port, int connection_type, struct control_state **state, struct module *root_module, int force_ip_version)
static void new_message(struct module *m)
void control_done(struct control_state *s)
void control_start(struct control_state *s)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_WARNING
less severe errors
void module_init_default(struct module *module_data)
void module_register(struct module *module_data, struct module *parent)
void module_done(struct module *module_data)
void * hd_rum_decompress_init(struct module *parent, struct hd_rum_output_conf conf, const char *capture_filter, struct state_recompress *recompress)
ssize_t hd_rum_decompress_write(void *state, void *buf, size_t count)
void hd_rum_decompress_done(void *state)
int recompress_get_num_active_ports(struct state_recompress *s)
void recompress_port_set_active(struct state_recompress *s, int index, bool active)
uint32_t recompress_get_port_ssrc(struct state_recompress *s, int idx)
void recompress_remove_port(struct state_recompress *s, int index)
int recompress_add_port(struct state_recompress *s, const char *host, const char *compress, unsigned short rx_port, unsigned short tx_port, const struct common_opts *common, const char *fec, long long bitrate)
struct state_recompress * recompress_init(struct module *parent)
void recompress_done(struct state_recompress *s)
bool recompress_port_change_compress(struct state_recompress *s, int index, const char *new_compress)
static char * get_replica_mod_name(const char *addr, uint16_t tx_port)
static struct item * qinit(int qsize)
static void * writer(void *arg)
static void qdestroy(struct item *queue)
#define prefix_matches(x, y)
static int parse_size(const char *sz_str) noexcept(false)
unit_evaluate() is similar but uses SI prefixes
static void hd_rum_translator_should_exit_callback(void *arg)
static int parse_fmt(int argc, char **argv, struct cmdline_parameters *parsed) noexcept(false)
static void hd_rum_translator_deinit(struct hd_rum_translator_state *s)
static int parse_global_opts(int argc, char **argv, struct cmdline_parameters *parsed) noexcept(false)
static struct response * change_replica_type(struct hd_rum_translator_state *s, struct module *mod, struct message *msg, int index)
static int create_output_port(struct hd_rum_translator_state *s, const char *addr, int rx_port, int tx_port, int bufsize, struct common_opts *common, const char *compression, const char *fec, int bitrate, bool use_server_sock=false)
static bool sockaddr_equal(struct sockaddr_storage *a, struct sockaddr_storage *b)
static void signal_handler(int signum)
static struct item * queue
void crash_signal_handler(int sig)
void init_root_module(struct module *root_mod)
void print_capabilities(const char *cfg)
void register_should_exit_callback(struct module *mod, void(*callback)(void *), void *udata)
void unregister_should_exit_callback(struct module *mod, void(*callback)(void *), void *udata)
bool parse_params(const char *optarg, bool preinit)
void hang_signal_handler(int sig)
bool parse_bitrate(char *optarg, long long int *bitrate)
struct init_data * common_preinit(int argc, char *argv[])
void common_cleanup(struct init_data *init)
#define EXIT_FAIL_CONTROL_SOCK
#define EXIT_FAIL_COMPRESS
#define EXIT_FAIL_DECODER
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_to_receiver(struct module *receiver, struct message *msg)
struct message * check_message(struct module *mod)
void free_response(struct response *r)
#define RESPONSE_BAD_REQUEST
#define RESPONSE_INT_SERV_ERR
#define RESPONSE_ACCEPTED
bool invalid_arg_is_numeric(const char *what)
const char * format_in_si_units(unsigned long long int val)
unsigned get_sockaddr_addr_port(struct sockaddr *sa)
void get_sockaddr_addr_str(struct sockaddr *sa, char *buf, size_t n)
const char * get_sockaddr_str(struct sockaddr *sa)
void udp_exit(socket_udp *s)
int udp_get_udp_rx_port(socket_udp *s)
int udp_recvfrom_timeout(socket_udp *s, char *buffer, int buflen, struct timeval *timeout, struct sockaddr *src_addr, socklen_t *addrlen)
socket_udp * udp_init(const char *addr, uint16_t rx_port, uint16_t tx_port, int ttl, int force_ip_version, bool multithreaded)
int udp_sendto(socket_udp *s, char *buffer, int buflen, struct sockaddr *dst_addr, socklen_t addrlen)
bool udp_set_recv_buf(socket_udp *s, int size)
bool udp_set_send_buf(socket_udp *s, int size)
socket_udp * udp_init_if(const char *addr, const char *iface, uint16_t rx_port, uint16_t tx_port, int ttl, int force_ip_version, bool multithreaded)
int resolve_addrinfo(const char *addr, uint16_t tx_port, struct sockaddr_storage *dst, socklen_t *len, int *mode)
struct sockaddr_storage addr
vector< struct host_opts > hosts
int control_connection_type
const char * conference_compression
struct hd_rum_output_conf out_conf
pthread_mutex_t qfull_mtx
pthread_cond_t qempty_cond
struct state_recompress * recompress
std::shared_ptr< socket_udp > server_socket
struct control_state * control_state
pthread_cond_t qfull_cond
~hd_rum_translator_state()
pthread_mutex_t qempty_mtx
hd_rum_translator_state()
vector< replica * > replicas
std::shared_ptr< socket_udp > sock
sockaddr_storage sockaddr
replica(const char *addr, uint16_t rx_port, uint16_t tx_port, int bufsize, struct module *parent, int force_ip_version)
double tv_diff(struct timeval curr_time, struct timeval prev_time)