UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
hd-rum-translator.cpp
Go to the documentation of this file.
1
15/*
16 * Copyright (c) 2013-2024 CESNET
17 * All rights reserved.
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, is permitted provided that the following conditions
21 * are met:
22 *
23 * 1. Redistributions of source code must retain the above copyright
24 * notice, this list of conditions and the following disclaimer.
25 *
26 * 2. Redistributions in binary form must reproduce the above copyright
27 * notice, this list of conditions and the following disclaimer in the
28 * documentation and/or other materials provided with the distribution.
29 *
30 * 3. Neither the name of CESNET nor the names of its contributors may be
31 * used to endorse or promote products derived from this software without
32 * specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
35 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
36 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
37 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
38 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
39 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
44 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
45 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 */
47
48#include <atomic> // for atomic, memory_order
49#include <cassert> // for assert
50#include <cctype> // for isdigit
51#include <cinttypes>
52#include <climits>
53#include <csignal> // for sigaction, signal
54#include <cstdio>
55#include <cstdlib>
56#include <cstring>
57#include <ctime> // for localtime, strftime
58#include <getopt.h>
59#include <memory> // for shared_ptr, operator!=
60#include <pthread.h>
61#include <stdexcept> // for invalid_argument
62#include <string>
63#include <thread>
64#include <unistd.h> // for optarg, optind, ssi...
65#include <utility> // for swap, move
66#include <vector>
67
68#include "control_socket.h"
69#include "debug.h"
72#include "host.h"
73#include "lib_common.h"
74#include "messaging.h"
75#include "module.h"
76#include "rtp/net_udp.h"
77#include "tv.h"
78#include "ug_runtime_error.hpp"
79#include "utils/color_out.h"
80#include "utils/misc.h" // format_in_si_units, unit_evaluate
81#include "utils/net.h"
82
83using std::invalid_argument;
84using std::stoi;
85using std::string;
86using std::to_string;
87using std::vector;
88
89#define MOD_NAME "[hd-rum-trans] "
90
91#define REPLICA_MAGIC 0xd2ff3323
92
93static char *get_replica_mod_name(const char *addr, uint16_t tx_port){
94 const size_t len = strlen(addr) + 2 /* [ ] for IPv6 addr */ + 6 /* port (including ':') */ + 1 /* '\0' */;
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);
99
100 return name;
101}
102
103struct replica {
104 replica(const char *addr, uint16_t rx_port, uint16_t tx_port, int bufsize, struct module *parent, int force_ip_version) {
106 host = addr;
107 m_tx_port = tx_port;
108 sock = std::shared_ptr<socket_udp>(udp_init(addr, rx_port, tx_port, 255, force_ip_version, false), udp_exit);
109 int mode = 0;
110 int res = resolve_addrinfo(addr, tx_port, &sockaddr, &sockaddr_len, &mode);
111 if (!sock || res != 0) {
112 throw string("Cannot initialize output port!\n");
113 }
114 if (!udp_set_send_buf(sock.get(), bufsize)) {
115 fprintf(stderr, "Cannot set send buffer to %sB!\n",
116 format_in_si_units(bufsize));
117 }
120 mod.name = get_replica_mod_name(addr, tx_port);
121 mod.priv_data = this;
123 type = replica::type_t::NONE;
124 }
125
127 assert(magic == REPLICA_MAGIC);
129 }
130
131 struct module mod;
132 uint32_t magic;
133 string host;
135
142 std::shared_ptr<socket_udp> sock;
143 sockaddr_storage sockaddr;
144 socklen_t sockaddr_len;
145};
146
150 pthread_mutex_init(&qempty_mtx, NULL);
151 pthread_mutex_init(&qfull_mtx, NULL);
152 pthread_cond_init(&qempty_cond, NULL);
153 pthread_cond_init(&qfull_cond, NULL);
154 }
156 pthread_mutex_destroy(&qempty_mtx);
157 pthread_mutex_destroy(&qfull_mtx);
158 pthread_cond_destroy(&qempty_cond);
159 pthread_cond_destroy(&qfull_cond);
161 }
162 struct module mod;
163 int bufsize = 0;
164 struct control_state *control_state = nullptr;
165 struct item *queue = nullptr;
166 struct item *qhead = nullptr;
167 struct item *qtail = nullptr;
168 int qempty = 1;
169 int qfull = 0;
170 pthread_mutex_t qempty_mtx;
171 pthread_mutex_t qfull_mtx;
172 pthread_cond_t qempty_cond;
173 pthread_cond_t qfull_cond;
174
175 vector<replica *> replicas;
176 std::shared_ptr<socket_udp> server_socket;
177 void *decompress = nullptr;
178 struct state_recompress *recompress = nullptr;
179};
180
181/*
182 * Prototypes
183 */
184static struct item *qinit(int qsize);
185static void qdestroy(struct item *queue);
186static void *writer(void *arg);
187
188static void signal_handler(int signum)
189{
190#ifdef SIGPIPE
191 if (signum == SIGPIPE) {
192 signal(SIGPIPE, SIG_IGN);
193 }
194#endif // defined SIGPIPE
195 if (log_level >= LOG_LEVEL_DEBUG) {
196 char msg[] = "Caught signal ";
197 char buf[128];
198 char *ptr = buf;
199 for (size_t i = 0; i < sizeof msg - 1; ++i) {
200 *ptr++ = msg[i];
201 }
202 if (signum / 10) {
203 *ptr++ = '0' + signum / 10;
204 }
205 *ptr++ = '0' + signum % 10;
206 *ptr++ = '\n';
207 size_t bytes = ptr - buf;
208 ptr = buf;
209 do {
210 ssize_t written = write(STDERR_FILENO, ptr, bytes);
211 if (written < 0) {
212 break;
213 }
214 bytes -= written;
215 ptr += written;
216 } while (bytes > 0);
217 }
218 exit_uv(0);
219}
220
221#define MAX_PKT_SIZE 10000
222
223#ifdef _WIN32
224struct wsa_aux_storage {
225 WSAOVERLAPPED *overlapped;
226 int ref;
227};
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))
231#else
232#define SIZE MAX_PKT_SIZE
233#endif
234
235struct item {
236 struct item *next;
237 long size;
238 char *buf;
239};
240
241static struct item *qinit(int qsize)
242{
243 struct item *queue;
244 int i;
245
246 if (qsize <= 0) {
247 fprintf(stderr, "wrong packet queue size %d items\n", qsize);
248 return nullptr;
249 }
250
251 printf("initializing packet queue for %d items\n", qsize);
252
253 queue = (struct item *) calloc(qsize, sizeof(struct item));
254 if (queue == NULL) {
255 fprintf(stderr, "not enough memory\n");
256 exit(2);
257 }
258
259 for (i = 0; i < qsize; i++) {
260 queue[i].buf = (char *) malloc(SIZE);
261 queue[i].next = queue + i + 1;
262 }
263 queue[qsize - 1].next = queue;
264
265 return queue;
266}
267
268static void qdestroy(struct item *queue)
269{
270 if (queue == nullptr) {
271 return;
272 }
273
274 struct item *q = queue;
275 do {
276 free(q->buf);
277 q = q->next;
278 } while (q != queue);
279 free(queue);
280}
281
282#define prefix_matches(x,y) strncasecmp(x, y, strlen(y)) == 0
284 struct module *mod, struct message *msg, int index)
285{
286 struct replica *r = (struct replica *) mod->priv_data;
287
288 struct msg_universal *data = (struct msg_universal *) msg;
289
290 if (strcasecmp(data->text, "sock") == 0) {
291 r->type = replica::type_t::USE_SOCK;
292 log_msg(LOG_LEVEL_NOTICE, "Output port %d is now forwarding.\n", index);
293 } else if (strcasecmp(data->text, "recompress") == 0) {
294 r->type = replica::type_t::RECOMPRESS;
295 log_msg(LOG_LEVEL_NOTICE, "Output port %d is now transcoding.\n", index);
296 } else if (prefix_matches(data->text, "compress ")) {
297 if(recompress_port_change_compress(s->recompress, index, data->text + strlen("compress "))){
298 log_msg(LOG_LEVEL_NOTICE, "Output port %d compression changed.\n", index);
299 } else {
300 log_msg(LOG_LEVEL_ERROR, "Failed to change port %d compression. Port removed.\n", index);
301 }
302 } else {
303 fprintf(stderr, "Unknown replica type \"%s\"\n", data->text);
305 }
306
308 r->type == replica::type_t::RECOMPRESS);
309
310 return new_response(RESPONSE_OK, NULL);
311}
312
313#ifdef _WIN32
314static VOID CALLBACK wsa_deleter(DWORD /* dwErrorCode */,
315 DWORD /* dwNumberOfBytesTransfered */,
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);
321 }
322}
323#endif
324
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)
329{
330 struct replica *rep;
331 try {
332 rep = new replica(addr, rx_port, tx_port, bufsize, &s->mod,
333 common->force_ip_version);
334 if(use_server_sock){
335 rep->sock = s->server_socket;
336 }
337 } catch (string const & s) {
338 fputs(s.c_str(), stderr);
339 const char *err_msg = "cannot create output port (wrong address?)";
340 log_msg(LOG_LEVEL_ERROR, "%s\n", err_msg);
341 return -1;
342 }
343 s->replicas.push_back(rep);
344
345 struct common_opts com_opts = *common;
346 com_opts.parent = &rep->mod;
347 rep->type = compression ? replica::type_t::RECOMPRESS : replica::type_t::USE_SOCK;
348 int idx = recompress_add_port(s->recompress,
349 addr, compression ? compression : "none",
350 0, tx_port, &com_opts, fec, bitrate);
351 if (idx < 0) {
352 fprintf(stderr, "Initializing output port '%s' compression failed!\n", addr);
353
354 delete s->replicas.back();
355 s->replicas.pop_back();
356
357 return -1;
358 }
359
360 assert((unsigned) idx == s->replicas.size() - 1);
361 recompress_port_set_active(s->recompress, idx, compression != nullptr);
362
363 return idx;
364}
365
366static void *writer(void *arg)
367{
368 struct hd_rum_translator_state *s =
369 (struct hd_rum_translator_state *) arg;
370
371 while (1) {
372 // first check messages
373 for (unsigned int i = 0; i < s->replicas.size(); i++) {
374 struct message *msg;
375 while ((msg = check_message(&s->replicas[i]->mod))) {
376 struct response *r = change_replica_type(s, &s->replicas[i]->mod, msg, i);
377 free_message(msg, r);
378 }
379 }
380
381 struct msg_universal *msg;
382 while ((msg = (struct msg_universal *) check_message(&s->mod))) {
383 struct response *r = NULL;
384 if (strncasecmp(msg->text, "delete-port ", strlen("delete-port ")) == 0) {
385 char *port_spec = msg->text + strlen("delete-port ");
386 int index = -1;
387 if (isdigit(port_spec[0])) {
388 int i = stoi(port_spec);
389 if (i >= 0 && i < (int) s->replicas.size()) {
390 index = i;
391 } else {
392 log_msg(LOG_LEVEL_WARNING, "Invalid port index: %d. Not removing.\n", i);
393 }
394 } else {
395 int i = 0;
396 for (auto r : s->replicas) {
397 if (strcmp(r->mod.name, port_spec) == 0) {
398 index = i;
399 break;
400 }
401 i++;
402 }
403 if (index == -1) {
404 log_msg(LOG_LEVEL_WARNING, "Unknown port name: %s. Not removing.\n", port_spec);
405 }
406 }
407 if (index >= 0) {
409 delete s->replicas[index];
410 s->replicas.erase(s->replicas.begin() + index);
411 log_msg(LOG_LEVEL_NOTICE, "Deleted output port %d.\n", index);
412 }
413 } else if (strncasecmp(msg->text, "create-port", strlen("create-port")) == 0) {
414 // format of parameters is either:
415 // <host>:<port> [<compression>]
416 // or (for compat with older CoUniverse version)
417 // <host> <port> [<compression>]
418 char *host_port, *port_str = NULL, *save_ptr;
419 char *host;
420 int tx_port;
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)) {
424 if (port_str) {
425 host = host_port;
426 tx_port = stoi(port_str);
427 } else {
428 tx_port = stoi(strrchr(host_port, ':') + 1);
429 host = host_port;
430 *strrchr(host_port, ':') = '\0';
431 }
432 // handle square brackets around an IPv6 address
433 if (host[0] == '[' && host[strlen(host) - 1] == ']') {
434 host += 1;
435 host[strlen(host) - 1] = '\0';
436 }
437 } else {
438 const char *err_msg = "wrong format";
439 log_msg(LOG_LEVEL_ERROR, "%s\n", err_msg);
441 continue;
442 }
443 char *compress = strtok_r(NULL, " ", &save_ptr);
444
445 struct common_opts opts = { COMMON_OPTS_INIT };
446 int idx = create_output_port(s,
447 host, 0, tx_port, s->bufsize, &opts,
448 compress, nullptr, RATE_UNLIMITED, s->server_socket != nullptr);
449
450 if(idx < 0) {
451 free_message((struct message *) msg, new_response(RESPONSE_INT_SERV_ERR, "Cannot create output port."));
452 continue;
453 }
454
455 if(compress)
456 log_msg(LOG_LEVEL_NOTICE, "Created new transcoding output port %s:%d:0x%08" PRIx32 ".\n", host, tx_port, recompress_get_port_ssrc(s->recompress, idx));
457 else
458 log_msg(LOG_LEVEL_NOTICE, "Created new forwarding output port %s:%d.\n", host, tx_port);
459
460 } else {
462 }
463
464 free_message((struct message *) msg, r ? r : new_response(RESPONSE_OK, NULL));
465 }
466
467 // then process incoming packets
468 while (s->qhead != s->qtail) {
469 if(s->qhead->size == 0) { // poisoned pill
470 return NULL;
471 }
472
473 // pass it for transcoding if needed
475 ssize_t ret = hd_rum_decompress_write(s->decompress, s->qhead->buf, s->qhead->size);
476 if (ret < 0) {
477 perror("hd_rum_decompress_write");
478 }
479 }
480
481 // distribute it to output ports that don't need transcoding
482#ifdef _WIN32
483 // send it asynchronously in MSW (performance optimalization)
484 SleepEx(0, TRUE); // allow system to call our completion routines in APC
485 int ref = 0;
486 for (unsigned int i = 0; i < s->replicas.size(); i++) {
487 if(s->replicas[i]->type == replica::type_t::USE_SOCK) {
488 ref++;
489 }
490 }
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));
494 aux->ref = ref;
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;
499 ssize_t ret = udp_sendto_wsa_async(s->replicas[i]->sock.get(), s->qhead->buf, s->qhead->size,
500 wsa_deleter, &aux->overlapped[overlapped_idx], (sockaddr *) &s->replicas[i]->sockaddr, s->replicas[i]->sockaddr_len);
501 if (ret < 0) {
502 perror("Hd-rum-translator send");
503 }
504 overlapped_idx += 1;
505 }
506 }
507 // reallocate the buffer since the last one will be freeed automaticaly
508 s->qhead->buf = (char *) malloc(SIZE);
509#else
510 for (unsigned int i = 0; i < s->replicas.size(); i++) {
511 if(s->replicas[i]->type == replica::type_t::USE_SOCK) {
512 ssize_t ret = udp_sendto(s->replicas[i]->sock.get(), s->qhead->buf, s->qhead->size, (sockaddr *) &s->replicas[i]->sockaddr, s->replicas[i]->sockaddr_len);
513 if (ret < 0) {
514 perror("Hd-rum-translator send");
515 }
516 }
517 }
518#endif
519 s->qhead = s->qhead->next;
520
521 pthread_mutex_lock(&s->qfull_mtx);
522 s->qfull = 0;
523 pthread_cond_signal(&s->qfull_cond);
524 pthread_mutex_unlock(&s->qfull_mtx);
525 }
526
527 pthread_mutex_lock(&s->qempty_mtx);
528 if (s->qempty)
529 pthread_cond_wait(&s->qempty_cond, &s->qempty_mtx);
530 s->qempty = 1;
531 pthread_mutex_unlock(&s->qempty_mtx);
532 }
533
534 return NULL;
535}
536
537static void usage(const char *progname) {
538 col() << "Usage:\n\t"
539 << SBOLD(SRED(progname)
540 << " [global_opts] buffer_size port \\\n\t\t[host1_options] "
541 "host1 [[host2_options] host2] ...")
542 << "\n";
543
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";
549 col() << SUNDERLINE("global_opts") << " may be:\n"
550 << SBOLD("\t--control-port|-n <port_number>[:0|:1]")
551 << " - control port to connect to, optionally client/server "
552 "(default)\n"
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 "
558 "port\n"
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 "
584 "also 'none'\n"
585 "for uncompressed transmission (see 'uv -c help' for list).\n");
586}
587
588struct host_opts {
589 char *addr;
592 const char *compression;
593 char *fec;
594 long long int bitrate;
596};
597
600 int port;
601 vector<struct host_opts> hosts;
603 int control_port = -1;
605 int server_port = -1;
607 const char *capture_filter = NULL;
608 int log_level = -1;
609 const char *conference_compression = nullptr;
610};
611
613static int
614parse_size(const char *sz_str) noexcept(false)
615{
616 int ret = 0;
617 size_t end = 0;
618
619 try {
620 ret = stoi(sz_str, &end);
621 } catch (invalid_argument &e) {
622 throw ug_runtime_error(string("invalid buffer size: ") + sz_str);
623 }
624 if (ret <= 0) {
625 throw ug_runtime_error(string("size must be positive: ") + sz_str);
626 }
627
628 switch (sz_str[end]) {
629 case 'K':
630 case 'k':
631 ret *= 1024;
632 break;
633
634 case 'M':
635 case 'm':
636 ret *= 1024 * 1024;
637 break;
638 }
639
640 return ret;
641}
642
643static int parse_global_opts(int argc, char **argv,
644 struct cmdline_parameters *parsed) noexcept(false)
645{
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 }
660 };
661 const char *const optstring = "+BF:LO:R:S:Vbhn:r:v";
662
663 int ch = 0;
664 while ((ch = getopt_long(argc, argv, optstring, getopt_options,
665 nullptr)) != -1) {
666 switch (ch) {
667 case 'c':
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);
673 }
674 break;
675 case 'b':
677 return 1;
678 case 'B':
679 parsed->out_conf.mode = BLEND;
680 break;
681 case 'r':
682 parsed->out_conf.mode = CONFERENCE;
683 parsed->out_conf.arg = optarg;
684 break;
685 case 'R':
686 parsed->conference_compression = optarg;
687 break;
688 case 'S':
689 parsed->server_port = stoi(optarg);
690 break;
691 case 'F':
692 parsed->capture_filter = optarg;
693 break;
694 case 'h':
695 usage(argv[0]);
696 return 1;
697 case 'v':
698 // nothing needed, version is printed everytime
699 return 1;
700 case 'V':
701 break; // already handled in common_preinit()
702 case 'O':
703 if (!parse_params(optarg, false)) {
704 return 1;
705 }
706 break;
707 case 'L':
709 return 1;
710 case '?':
711 default:
712 MSG(FATAL, "Unknown global parameter\n\n");
713 usage(argv[0]);
714 return -1;
715 }
716 }
717 return 0;
718}
719
727static int
728parse_fmt(int argc, char **argv,
729 struct cmdline_parameters *parsed) noexcept(false)
730{
731 const int rc = parse_global_opts(argc, argv, parsed);
732 if (rc != 0) {
733 return rc;
734 }
735
736 if (optind + 2 > argc) {
737 MSG(FATAL, "Missing parameter%s port!\n\n",
738 argc < optind + 1 ? " buffer_size and" : "");
739 usage(argv[0]);
740 return -1;
741 }
742
743 parsed->bufsize = parse_size(argv[optind++]);
744 try {
745 parsed->port = stoi(argv[optind]);
746 } catch (invalid_argument &) {
747 throw ug_runtime_error(string("invalid port number: ") +
748 argv[optind]);
749 }
750 if (parsed->port < 0 || parsed->port >= USHRT_MAX) {
751 throw ug_runtime_error(string("port must be in range [0..") +
752 to_string(USHRT_MAX) +
753 "], given: " + argv[optind]);
754 }
755 optind++;
756 if (parsed->port == 0) {
757 MSG(WARNING,
758 "Given RX port 0, reflector will listen on a random port.\n");
759 }
760
761 parsed->host_count = 0;
762 while (optind < argc) {
763 parsed->hosts.resize(parsed->host_count + 1);
764 parsed->hosts[parsed->host_count].bitrate = RATE_UNLIMITED;
765
766 const char *const optstring = "+46P:c:f:l:m:";
767 int ch = 0;
768 while ((ch = getopt(argc, argv, optstring)) != -1) {
769 switch (ch) {
770 case 'P':
771 if (strchr(optarg, ':') != nullptr) {
772 parsed->hosts[parsed->host_count].rx_port =
773 stoi(optarg);
774 parsed->hosts[parsed->host_count].tx_port =
775 stoi(strchr(optarg, ':') + 1);
776 } else {
777 parsed->hosts[parsed->host_count].tx_port =
778 stoi(optarg);
779 }
780 break;
781 case 'm':
782 parsed->hosts[parsed->host_count].common_opts.mtu =
783 stoi(optarg);
784 break;
785 case 'c':
786 parsed->hosts[parsed->host_count].compression = optarg;
787 break;
788 case 'f':
789 parsed->hosts[parsed->host_count].fec = optarg;
790 break;
791 case 'l':
792 if (!parse_bitrate(
793 optarg, &parsed->hosts[parsed->host_count]
794 .bitrate)) {
795 return -1;
796 }
797 break;
798 case '4':
799 parsed->hosts[parsed->host_count]
800 .common_opts.force_ip_version = 4;
801 break;
802 case '6':
803 parsed->hosts[parsed->host_count]
804 .common_opts.force_ip_version = 6;
805 break;
806 default:
807 MSG(FATAL, "Error: invalid host option.\nn");
808 return -1;
809 }
810 }
811 if (optind == argc) {
812 MSG(ERROR,
813 "Error: host option following last host address!\n");
814 return -1;
815 }
816 parsed->hosts[parsed->host_count++].addr = argv[optind++];
817 }
818
819 return 0;
820}
821
823 if(s->decompress) {
825 }
826
827 if(s->recompress)
829
830 for (unsigned int i = 0; i < s->replicas.size(); i++) {
831 delete s->replicas[i];
832 }
833
835
836 qdestroy(s->queue);
837}
838
839static bool sockaddr_equal(struct sockaddr_storage *a, struct sockaddr_storage *b){
840 if(a->ss_family != b->ss_family)
841 return false;
842
843 switch(a->ss_family){
844 case AF_INET:{
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;
849 }
850 case AF_INET6:{
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;
855 }
856 default:
857 assert(false && "Comparison not implemented for address type");
858 abort();
859 }
860}
861
863 struct sockaddr_storage addr;
864 socklen_t addrlen;
865 struct timeval last_recv;
866};
867
869public:
870 Participant_manager(module& mod, const char *compress) : mod(mod), compression(compress ? compress : "") { }
872 should_stop.store(true, std::memory_order_relaxed);
873 if(worker_thread.joinable())
874 worker_thread.join();
875 }
876
877 void tick(sockaddr_storage& sin, socklen_t addrlen){
878 struct timeval t;
879 gettimeofday(&t, NULL);
880 bool seen = false;
881 for(auto it = participants.begin(); it != participants.end();){
882 if(addrlen > 0 && sockaddr_equal(&it->addr, &sin)){
883 it->last_recv = t;
884 seen = true;
885 }
886 const double participant_timeout = 10.0;
887 if(tv_diff(t, it->last_recv) > participant_timeout){
888 log_msg(LOG_LEVEL_NOTICE, "Removing participant\n");
889 std::string msg = "delete-port ";
890 auto addr = reinterpret_cast<struct sockaddr *>(&it->addr);
891 char addr_str[128];
892 get_sockaddr_addr_str(addr, addr_str, sizeof(addr_str));
893 char *replica_name = get_replica_mod_name(addr_str, get_sockaddr_addr_port(addr));
894 msg += replica_name;
895 free(replica_name);
896
897 std::swap(*it, participants.back());
898 participants.pop_back();
899
900 struct msg_universal *m = (struct msg_universal *) new_message(sizeof(struct msg_universal));
901 strncpy(m->text, msg.c_str(), sizeof(m->text) - 1);
902 log_msg(LOG_LEVEL_NOTICE, "Msg: %s\n", m->text);
903 struct response *r = send_message_to_receiver(&mod, (struct message *) m);
905 log_msg(LOG_LEVEL_ERROR, "Couldn't remove participant (error %d)!\n", response_get_status(r));
906 }
907 free_response(r);
908 } else {
909 it++;
910 }
911 }
912
913 if(!seen && addrlen > 0){
915 p.addr = sin;
916 p.addrlen = addrlen;
917 p.last_recv = t;
918
919 log_msg(LOG_LEVEL_NOTICE, "New participant\n");
920 std::string msg = "create-port ";
921 msg += get_sockaddr_str(reinterpret_cast<sockaddr *>(&sin));
922 if(!compression.empty()){
923 msg += " ";
924 msg += compression;
925 }
926
927 struct msg_universal *m = (struct msg_universal *) new_message(sizeof(struct msg_universal));
928 strncpy(m->text, msg.c_str(), sizeof(m->text) - 1);
929 log_msg(LOG_LEVEL_NOTICE, "Msg: %s\n", m->text);
930 struct response *r = send_message_to_receiver(&mod, (struct message *) m);
932 log_msg(LOG_LEVEL_ERROR, "Cannot add new participant (error %d)!\n", response_get_status(r));
933 } else{
934 participants.push_back(p);
935 }
936 free_response(r);
937 }
938 }
939
940 void run_async(std::shared_ptr<socket_udp> server_sock){
941 sock = std::move(server_sock);
942 worker_thread = std::thread(&Participant_manager::worker, this);
943 }
944
945private:
946 std::vector<Conf_participant> participants;
947 struct module& mod;
948 std::string compression;
949 std::shared_ptr<socket_udp> sock;
950 std::thread worker_thread;
951 std::atomic<bool> should_stop = false;
952
953 void worker(){
954 struct timeval timeout = { 1, 0 };
955 char dummy_buf[256];
956
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);
961
962 if(size > 0)
963 tick(sin, addrlen);
964 }
965 }
966};
967
969 volatile auto *should_exit = (volatile bool *) arg;
970 *should_exit = true;
971}
972
973#define EXIT(retval) \
974 { \
975 exit_uv(0); \
976 hd_rum_translator_deinit(&state); \
977 if (sock_in != nullptr) \
978 udp_exit(sock_in); \
979 common_cleanup(init); \
980 return retval; \
981 }
982
983int main(int argc, char **argv)
984{
985 struct init_data *init;
987
988 int qsize;
989 socket_udp *sock_in = nullptr;
990 pthread_t thread;
991 int err = 0;
992 int i;
993 struct cmdline_parameters params = {};
994
995 if ((init = common_preinit(argc, argv)) == nullptr) {
996 EXIT(EXIT_FAILURE);
997 }
998
1000 printf("\n");
1001
1002#ifndef _WIN32
1003 struct sigaction sa;
1004 memset(&sa, 0, sizeof(sa));
1005 sa.sa_handler = signal_handler;
1006 sigemptyset(&sa.sa_mask);
1007
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);
1013 signal(SIGQUIT, crash_signal_handler);
1014 signal(SIGALRM, hang_signal_handler);
1015#else
1016 signal(SIGINT, signal_handler);
1017 signal(SIGTERM, signal_handler);
1018 signal(SIGABRT, signal_handler);
1019#endif
1020 signal(SIGABRT, crash_signal_handler);
1021 signal(SIGFPE, crash_signal_handler);
1022 signal(SIGILL, crash_signal_handler);
1023 signal(SIGSEGV, crash_signal_handler);
1024
1025 int ret = -1;
1026 try {
1027 ret = parse_fmt(argc, argv, &params);
1028 } catch (invalid_argument &e) {
1029 if (!invalid_arg_is_numeric(e.what())) {
1030 throw;
1031 }
1032 MSG(ERROR, "Non-numeric value passed to option "
1033 "expecting a number!\n");
1034 } catch (ug_runtime_error &e) {
1035 MSG(FATAL, "%s\n", e.what());
1036 }
1037 if (ret != 0) {
1038 EXIT(ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
1039 }
1040
1041 if (params.log_level != -1) {
1042 log_level = params.log_level;
1043 }
1044
1045 state.bufsize = params.bufsize;
1046
1047 qsize = state.bufsize / 8000;
1048
1049 printf("using UDP send and receive buffer size of %d bytes\n", state.bufsize);
1050
1051 state.qhead = state.qtail = state.queue = qinit(qsize);
1052 if (!state.qhead) {
1053 EXIT(EXIT_FAILURE);
1054 }
1055
1056 /* input socket */
1057 if ((sock_in = udp_init_if("localhost", NULL, params.port, 0, 255, false, false)) == NULL) {
1058 perror("input socket");
1059 EXIT(EXIT_FAILURE);
1060 }
1061
1062 if (!udp_set_recv_buf(sock_in, state.bufsize)) {
1063 fprintf(stderr, "Cannot set recv buffer to %sB!\n",
1064 format_in_si_units(state.bufsize));
1065 }
1066
1067 printf("listening on *:%d\n", udp_get_udp_rx_port(sock_in));
1068
1069 if (params.control_port != -1) {
1070 if (control_init(params.control_port, params.control_connection_type, &state.control_state, &state.mod, 0) != 0) {
1071 fprintf(stderr, "Warning: Unable to create remote control.\n");
1073 }
1074 control_start(state.control_state);
1075 }
1076
1077 //one shared recompressor, which manages compressions and sends to all hosts
1078 state.recompress = recompress_init(&state.mod);
1079 if(!state.recompress) {
1081 }
1082
1083 // we need only one shared receiver decompressor for all recompressing streams
1084 state.decompress = hd_rum_decompress_init(&state.mod, params.out_conf,
1085 params.capture_filter, state.recompress);
1086 if(!state.decompress) {
1088 }
1089
1090 if(params.server_port > 0){
1091 state.server_socket = std::shared_ptr<socket_udp>(udp_init("localhost", params.server_port, 0, 255, 0, false), udp_exit);
1092 if (!state.server_socket) {
1093 EXIT(EXIT_FAILURE);
1094 }
1095 }
1096
1097 if(params.out_conf.mode == CONFERENCE && !params.conference_compression){
1098 params.conference_compression = "libavcodec";
1099 }
1100
1101 for (i = 0; i < params.host_count; i++) {
1102 int tx_port = params.port; // use default rx port by default
1103 int rx_port = params.hosts[i].rx_port;
1104 if (params.hosts[i].tx_port) {
1105 tx_port = params.hosts[i].tx_port;
1106 }
1107
1108 auto& h = params.hosts[i];
1109
1110 if(!h.compression && params.out_conf.mode == CONFERENCE){
1111 h.compression = params.conference_compression;
1112 }
1113
1114 int idx = create_output_port(&state,
1115 h.addr, rx_port, tx_port, state.bufsize, &h.common_opts,
1116 h.compression, h.compression ? h.fec : nullptr, h.bitrate);
1117 if(idx < 0) {
1118 EXIT(EXIT_FAILURE);
1119 }
1120 }
1121
1122 if (pthread_create(&thread, NULL, writer, (void *) &state)) {
1123 fprintf(stderr, "cannot create writer thread\n");
1124 EXIT(2);
1125 }
1126
1127 uint64_t received_data = 0;
1128 struct timeval t0;
1129 gettimeofday(&t0, NULL);
1130
1131 unsigned long long int last_data = 0ull;
1132
1133 Participant_manager participant_mgr(state.mod, params.conference_compression);
1134
1135 if(state.server_socket){
1136 participant_mgr.run_async(state.server_socket);
1137 }
1138
1139 volatile bool should_exit = false;
1140 register_should_exit_callback(&state.mod, hd_rum_translator_should_exit_callback, const_cast<bool *>(&should_exit));
1141 /* main loop */
1142 while (!should_exit) {
1143 while (state.qtail->next != state.qhead && !should_exit) {
1144 struct timeval timeout = { 1, 0 };
1145
1146 struct sockaddr_storage sin = {};
1147 socklen_t addrlen = sizeof(sin);
1148 state.qtail->size = udp_recvfrom_timeout(sock_in, state.qtail->buf, SIZE, &timeout, (sockaddr *) &sin, &addrlen);
1149 if(state.qtail->size <= 0)
1150 break;
1151
1152 struct timeval t;
1153 gettimeofday(&t, NULL);
1154
1155 if(params.out_conf.mode == CONFERENCE){
1156 participant_mgr.tick(sin, addrlen);
1157 }
1158
1159 received_data += state.qtail->size;
1160
1161 state.qtail = state.qtail->next;
1162
1163 pthread_mutex_lock(&state.qempty_mtx);
1164 state.qempty = 0;
1165 pthread_cond_signal(&state.qempty_cond);
1166 pthread_mutex_unlock(&state.qempty_mtx);
1167
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;
1172 char tim_str[20];
1173 time_t tim = time(NULL);
1174 struct tm *tmp = localtime(&tim);
1175 if (tmp) {
1176 strftime(tim_str, sizeof(tim_str), "%F %T", tmp);
1177 }
1178 log_msg(LOG_LEVEL_INFO, "[%s] Received %llu bytes in %g seconds = %sbps\n", tim_str, cur_data, seconds, format_in_si_units(bps * 8));
1179 t0 = t;
1180 last_data = received_data;
1181 }
1182 }
1183
1184 if (state.qtail->size <= 0)
1185 continue;
1186
1187 pthread_mutex_lock(&state.qfull_mtx);
1188 if (state.qfull)
1189 pthread_cond_wait(&state.qfull_cond, &state.qfull_mtx);
1190 state.qfull = 1;
1191 pthread_mutex_unlock(&state.qfull_mtx);
1192 }
1193
1194 if (state.qtail->size < 0 && !should_exit) {
1195 printf("read: %s\n", strerror(err));
1196 EXIT(2);
1197 }
1198
1199 // pass poisoned pill to the worker
1200 state.qtail->size = 0;
1201 state.qtail = state.qtail->next;
1202
1203 pthread_mutex_lock(&state.qempty_mtx);
1204 state.qempty = 0;
1205 pthread_cond_signal(&state.qempty_cond);
1206 pthread_mutex_unlock(&state.qempty_mtx);
1207
1208 alarm(5);
1209 pthread_join(thread, NULL);
1210
1212 udp_exit(sock_in);
1214 unregister_should_exit_callback(&state.mod, hd_rum_translator_should_exit_callback, const_cast<bool *>(&should_exit));
1215
1216 printf("Exit\n");
1217
1218 return 0;
1219}
1220
1221/* vim: set sw=4 expandtab : */
void ** state
int void size_t * len
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::shared_ptr< socket_udp > sock
void run_async(std::shared_ptr< socket_udp > server_sock)
void tick(sockaddr_storage &sin, socklen_t addrlen)
#define SRED(x)
Definition color_out.h:85
#define SBOLD(x)
Definition color_out.h:80
#define SUNDERLINE(x)
Definition color_out.h:81
#define TRUE
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,...)
Definition debug.cpp:130
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define MSG(l, fmt,...)
Definition debug.h:96
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
void module_init_default(struct module *module_data)
Definition module.c:48
void module_register(struct module *module_data, struct module *parent)
Definition module.c:82
void module_done(struct module *module_data)
Definition module.c:93
@ MODULE_CLASS_PORT
Definition module.h:84
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)
@ NORMAL
@ BLEND
@ CONFERENCE
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)
#define EXIT(retval)
static void * writer(void *arg)
static void qdestroy(struct item *queue)
#define SIZE
#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)
#define REPLICA_MAGIC
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
Definition hd-rum.c:33
void crash_signal_handler(int sig)
Definition host.cpp:1253
void init_root_module(struct module *root_mod)
Definition host.cpp:610
void exit_uv(int status)
Definition host.cpp:625
void print_capabilities(const char *cfg)
Definition host.cpp:801
void register_should_exit_callback(struct module *mod, void(*callback)(void *), void *udata)
Definition host.cpp:1144
void print_version()
Definition host.cpp:868
void unregister_should_exit_callback(struct module *mod, void(*callback)(void *), void *udata)
Definition host.cpp:1158
bool parse_params(const char *optarg, bool preinit)
Definition host.cpp:976
void hang_signal_handler(int sig)
Definition host.cpp:1276
bool parse_bitrate(char *optarg, long long int *bitrate)
Definition host.cpp:1012
struct init_data * common_preinit(int argc, char *argv[])
Definition host.cpp:435
void common_cleanup(struct init_data *init)
Definition host.cpp:203
#define EXIT_FAIL_CONTROL_SOCK
Definition host.h:68
#define EXIT_FAIL_COMPRESS
Definition host.h:67
#define RATE_UNLIMITED
Definition host.h:122
#define EXIT_FAIL_DECODER
Definition host.h:65
#define COMMON_OPTS_INIT
Definition host.h:179
#define LOG_LEVEL_DEBUG
Definition kernels.cu:48
volatile int log_level
Definition debug.cpp:65
void usage()
Definition main.cpp:28
bool list_all_modules()
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_OK
Definition messaging.h:57
#define RESPONSE_BAD_REQUEST
Definition messaging.h:60
#define RESPONSE_INT_SERV_ERR
Definition messaging.h:63
#define RESPONSE_ACCEPTED
Definition messaging.h:58
bool invalid_arg_is_numeric(const char *what)
Definition misc.cpp:340
const char * format_in_si_units(unsigned long long int val)
Definition misc.cpp:154
unsigned get_sockaddr_addr_port(struct sockaddr *sa)
Definition net.c:414
void get_sockaddr_addr_str(struct sockaddr *sa, char *buf, size_t n)
Definition net.c:427
const char * get_sockaddr_str(struct sockaddr *sa)
Definition net.c:450
void udp_exit(socket_udp *s)
Definition net_udp.c:974
int udp_get_udp_rx_port(socket_udp *s)
Definition net_udp.c:1756
int udp_recvfrom_timeout(socket_udp *s, char *buffer, int buflen, struct timeval *timeout, struct sockaddr *src_addr, socklen_t *addrlen)
Definition net_udp.c:1715
socket_udp * udp_init(const char *addr, uint16_t rx_port, uint16_t tx_port, int ttl, int force_ip_version, bool multithreaded)
Definition net_udp.c:681
int udp_sendto(socket_udp *s, char *buffer, int buflen, struct sockaddr *dst_addr, socklen_t addrlen)
Definition net_udp.c:1038
bool udp_set_recv_buf(socket_udp *s, int size)
Definition net_udp.c:1486
bool udp_set_send_buf(socket_udp *s, int size)
Definition net_udp.c:1492
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)
Definition net_udp.c:789
int resolve_addrinfo(const char *addr, uint16_t tx_port, struct sockaddr_storage *dst, socklen_t *len, int *mode)
Definition net_udp.c:1397
enum openssl_mode mode
struct msghdr msg
const char * parent
Definition settings.cpp:198
const char * name
Definition settings.cpp:193
struct timeval last_recv
struct sockaddr_storage addr
vector< struct host_opts > hosts
const char * conference_compression
struct hd_rum_output_conf out_conf
struct module * parent
Definition host.h:171
int force_ip_version
Definition host.h:176
Definition fec.h:51
struct state_recompress * recompress
std::shared_ptr< socket_udp > server_socket
struct control_state * control_state
vector< replica * > replicas
const char * compression
long long int bitrate
Definition hd-rum.c:27
long size
Definition hd-rum.c:29
struct item * next
Definition hd-rum.c:28
char buf[SIZE]
Definition hd-rum.c:30
char * name
Definition module.h:129
enum module_class cls
Definition module.h:115
void * priv_data
Definition module.h:126
char text[8192]
Definition messaging.h:157
enum type_t type
int sock
Definition hd-rum.c:23
std::shared_ptr< socket_udp > sock
sockaddr_storage sockaddr
socklen_t sockaddr_len
struct module mod
replica(const char *addr, uint16_t rx_port, uint16_t tx_port, int bufsize, struct module *parent, int force_ip_version)
const char * host
Definition hd-rum.c:21
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97