UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
hd-rum-translator.cpp File Reference
#include <atomic>
#include <cassert>
#include <cctype>
#include <cinttypes>
#include <climits>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <getopt.h>
#include <memory>
#include <pthread.h>
#include <stdexcept>
#include <string>
#include <thread>
#include <unistd.h>
#include <utility>
#include <vector>
#include "control_socket.h"
#include "debug.h"
#include "hd-rum-translator/hd-rum-decompress.h"
#include "hd-rum-translator/hd-rum-recompress.h"
#include "host.h"
#include "lib_common.h"
#include "messaging.h"
#include "module.h"
#include "rtp/net_udp.h"
#include "tv.h"
#include "ug_runtime_error.hpp"
#include "utils/color_out.h"
#include "utils/misc.h"
#include "utils/net.h"
Include dependency graph for hd-rum-translator.cpp:

Go to the source code of this file.

Classes

struct  cmdline_parameters
 
struct  Conf_participant
 
struct  hd_rum_translator_state
 
struct  host_opts
 
struct  item
 
class  Participant_manager
 
struct  replica
 

Macros

#define EXIT(retval)
 
#define MAX_PKT_SIZE   10000
 
#define MOD_NAME   "[hd-rum-trans] "
 
#define prefix_matches(x, y)
 
#define REPLICA_MAGIC   0xd2ff3323
 
#define SIZE   MAX_PKT_SIZE
 

Functions

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 char * get_replica_mod_name (const char *addr, uint16_t tx_port)
 
static void hd_rum_translator_deinit (struct hd_rum_translator_state *s)
 
static void hd_rum_translator_should_exit_callback (void *arg)
 
int main (int argc, char **argv)
 
static int parse_fmt (int argc, char **argv, struct cmdline_parameters *parsed) noexcept(false)
 
static int parse_global_opts (int argc, char **argv, struct cmdline_parameters *parsed) noexcept(false)
 
static int parse_size (const char *sz_str) noexcept(false)
 unit_evaluate() is similar but uses SI prefixes
 
static void qdestroy (struct item *queue)
 
static struct item * qinit (int qsize)
 
static void signal_handler (int signum)
 
static bool sockaddr_equal (struct sockaddr_storage *a, struct sockaddr_storage *b)
 
static void usage (const char *progname)
 
static void * writer (void *arg)
 

Detailed Description

Author
Martin Pulec pulec.nosp@m.@ces.nosp@m.net.c.nosp@m.z
Martin Piatka piatk.nosp@m.a@ce.nosp@m.snet..nosp@m.cz

Main part of transcoding reflector. This component provides a runtime for the reflector. Componets are following:

  • legacy packet reflector (defined in this file) for backward compatiblity. It handles those recipient that doesn't need transcoding.
  • decompressor - decompresses the stream if there are some host that need transcoding
  • recompressor - for every transcoded host, there is a recompressor that compresses and sends frame to receiver

Definition in file hd-rum-translator.cpp.

Macro Definition Documentation

◆ EXIT

#define EXIT ( retval)
Value:
{ \
exit_uv(0); \
hd_rum_translator_deinit(&state); \
if (sock_in != nullptr) \
udp_exit(sock_in); \
common_cleanup(init); \
return retval; \
}
void ** state
static af_result_code init(struct module *parent, const char *cfg, void **state)

Definition at line 973 of file hd-rum-translator.cpp.

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 }

Referenced by main().

◆ MAX_PKT_SIZE

#define MAX_PKT_SIZE   10000

Definition at line 221 of file hd-rum-translator.cpp.

◆ MOD_NAME

#define MOD_NAME   "[hd-rum-trans] "

Definition at line 89 of file hd-rum-translator.cpp.

◆ prefix_matches

#define prefix_matches ( x,
y )
Value:
strncasecmp(x, y, strlen(y)) == 0

Definition at line 282 of file hd-rum-translator.cpp.

Referenced by change_replica_type().

◆ REPLICA_MAGIC

#define REPLICA_MAGIC   0xd2ff3323

Definition at line 91 of file hd-rum-translator.cpp.

Referenced by replica::replica(), and replica::~replica().

◆ SIZE

#define SIZE   MAX_PKT_SIZE

Definition at line 232 of file hd-rum-translator.cpp.

Referenced by main(), qinit(), and writer().

Function Documentation

◆ change_replica_type()

static struct response * change_replica_type ( struct hd_rum_translator_state * s,
struct module * mod,
struct message * msg,
int index )
static

Definition at line 283 of file hd-rum-translator.cpp.

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}
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_ERROR
general errors
Definition debug.h:59
void recompress_port_set_active(struct state_recompress *s, int index, bool active)
bool recompress_port_change_compress(struct state_recompress *s, int index, const char *new_compress)
#define prefix_matches(x, y)
struct response * new_response(int status, const char *text)
#define RESPONSE_OK
Definition messaging.h:57
#define RESPONSE_BAD_REQUEST
Definition messaging.h:60
struct state_recompress * recompress
void * priv_data
Definition module.h:126
char text[8192]
Definition messaging.h:157
struct module mod

References LOG_LEVEL_ERROR, LOG_LEVEL_NOTICE, log_msg(), replica::mod, new_response(), prefix_matches, module::priv_data, hd_rum_translator_state::recompress, recompress_port_change_compress(), recompress_port_set_active(), RESPONSE_BAD_REQUEST, RESPONSE_OK, msg_universal::text, and replica::type.

Referenced by writer().

Here is the call graph for this function:

◆ create_output_port()

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

Definition at line 325 of file hd-rum-translator.cpp.

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}
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 module * parent
Definition host.h:171
int force_ip_version
Definition host.h:176
Definition fec.h:51
std::shared_ptr< socket_udp > server_socket
enum type_t type
int sock
Definition hd-rum.c:23
replica(const char *addr, uint16_t rx_port, uint16_t tx_port, int bufsize, struct module *parent, int force_ip_version)

References replica::replica(), common_opts::force_ip_version, LOG_LEVEL_ERROR, log_msg(), hd_rum_translator_state::mod, replica::mod, common_opts::parent, hd_rum_translator_state::recompress, recompress_add_port(), recompress_port_set_active(), hd_rum_translator_state::replicas, hd_rum_translator_state::server_socket, replica::sock, and replica::type.

Referenced by main(), and writer().

Here is the call graph for this function:

◆ get_replica_mod_name()

static char * get_replica_mod_name ( const char * addr,
uint16_t tx_port )
static

Definition at line 93 of file hd-rum-translator.cpp.

93 {
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}
int void size_t * len
const char * name
Definition settings.cpp:193

References len, and name.

Referenced by replica::replica(), and Participant_manager::tick().

◆ hd_rum_translator_deinit()

static void hd_rum_translator_deinit ( struct hd_rum_translator_state * s)
static

Definition at line 822 of file hd-rum-translator.cpp.

822 {
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}
void control_done(struct control_state *s)
void hd_rum_decompress_done(void *state)
void recompress_done(struct state_recompress *s)
static void qdestroy(struct item *queue)
struct control_state * control_state
vector< replica * > replicas

References control_done(), hd_rum_translator_state::control_state, hd_rum_translator_state::decompress, hd_rum_decompress_done(), qdestroy(), hd_rum_translator_state::queue, hd_rum_translator_state::recompress, recompress_done(), and hd_rum_translator_state::replicas.

Referenced by main().

Here is the call graph for this function:

◆ hd_rum_translator_should_exit_callback()

static void hd_rum_translator_should_exit_callback ( void * arg)
static

Definition at line 968 of file hd-rum-translator.cpp.

968 {
969 volatile auto *should_exit = (volatile bool *) arg;
970 *should_exit = true;
971}

Referenced by main().

◆ main()

int main ( int argc,
char ** argv )

Definition at line 983 of file hd-rum-translator.cpp.

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}
int control_init(int port, int connection_type, struct control_state **state, struct module *root_module, int force_ip_version)
void control_start(struct control_state *s)
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define MSG(l, fmt,...)
Definition debug.h:96
void * hd_rum_decompress_init(struct module *parent, struct hd_rum_output_conf conf, const char *capture_filter, struct state_recompress *recompress)
@ CONFERENCE
struct state_recompress * recompress_init(struct module *parent)
static struct item * qinit(int qsize)
#define EXIT(retval)
static void * writer(void *arg)
#define SIZE
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 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 void signal_handler(int signum)
void crash_signal_handler(int sig)
Definition host.cpp:1253
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
void hang_signal_handler(int sig)
Definition host.cpp:1276
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 EXIT_FAIL_DECODER
Definition host.h:65
volatile int log_level
Definition debug.cpp:65
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
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
bool udp_set_recv_buf(socket_udp *s, int size)
Definition net_udp.c:1486
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
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97

References common_cleanup(), common_preinit(), CONFERENCE, control_init(), control_start(), crash_signal_handler(), create_output_port(), EXIT, EXIT_FAIL_COMPRESS, EXIT_FAIL_CONTROL_SOCK, EXIT_FAIL_DECODER, format_in_si_units(), hang_signal_handler(), hd_rum_decompress_init(), hd_rum_translator_deinit(), hd_rum_translator_should_exit_callback(), init(), invalid_arg_is_numeric(), log_level, LOG_LEVEL_INFO, log_msg(), MSG, parse_fmt(), params::port, print_version(), qinit(), recompress_init(), register_should_exit_callback(), Participant_manager::run_async(), signal_handler(), SIZE, state, Participant_manager::tick(), tv_diff(), udp_exit(), udp_get_udp_rx_port(), udp_init(), udp_init_if(), udp_recvfrom_timeout(), udp_set_recv_buf(), unregister_should_exit_callback(), and writer().

Here is the call graph for this function:

◆ parse_fmt()

static int parse_fmt ( int argc,
char ** argv,
struct cmdline_parameters * parsed )
static
Todo
Use rather getopt() than manual parsing.
Return values
-1failure
0success
1help shown

Definition at line 728 of file hd-rum-translator.cpp.

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}
static int parse_size(const char *sz_str) noexcept(false)
unit_evaluate() is similar but uses SI prefixes
static int parse_global_opts(int argc, char **argv, struct cmdline_parameters *parsed) noexcept(false)
bool parse_bitrate(char *optarg, long long int *bitrate)
Definition host.cpp:1012
#define RATE_UNLIMITED
Definition host.h:122
void usage()
Definition main.cpp:28
vector< struct host_opts > hosts

References MSG, parse_bitrate(), parse_global_opts(), parse_size(), RATE_UNLIMITED, and usage().

Referenced by main().

Here is the call graph for this function:

◆ parse_global_opts()

static int parse_global_opts ( int argc,
char ** argv,
struct cmdline_parameters * parsed )
static

Definition at line 643 of file hd-rum-translator.cpp.

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) {
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}
@ BLEND
void print_capabilities(const char *cfg)
Definition host.cpp:801
bool parse_params(const char *optarg, bool preinit)
Definition host.cpp:976
bool list_all_modules()
const char * conference_compression
struct hd_rum_output_conf out_conf
enum hd_rum_mode_t mode

References BLEND, CONFERENCE, list_all_modules(), MSG, parse_params(), print_capabilities(), and usage().

Referenced by parse_fmt().

Here is the call graph for this function:

◆ parse_size()

static int parse_size ( const char * sz_str)
static

unit_evaluate() is similar but uses SI prefixes

Definition at line 614 of file hd-rum-translator.cpp.

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}

Referenced by parse_fmt().

◆ qdestroy()

static void qdestroy ( struct item * queue)
static

Definition at line 268 of file hd-rum-translator.cpp.

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}
static struct item * queue
Definition hd-rum.c:33
Definition hd-rum.c:27
struct item * next
Definition hd-rum.c:28
char buf[SIZE]
Definition hd-rum.c:30

References item::buf, item::next, and queue.

Referenced by hd_rum_translator_deinit().

◆ qinit()

static struct item * qinit ( int qsize)
static

Definition at line 241 of file hd-rum-translator.cpp.

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}

References queue, and SIZE.

Referenced by main().

◆ signal_handler()

static void signal_handler ( int signum)
static

Definition at line 188 of file hd-rum-translator.cpp.

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}
void exit_uv(int status)
Definition host.cpp:625
#define LOG_LEVEL_DEBUG
Definition kernels.cu:48

References item::buf, exit_uv(), log_level, and LOG_LEVEL_DEBUG.

Referenced by main().

Here is the call graph for this function:

◆ sockaddr_equal()

static bool sockaddr_equal ( struct sockaddr_storage * a,
struct sockaddr_storage * b )
static

Definition at line 839 of file hd-rum-translator.cpp.

839 {
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}

Referenced by Participant_manager::tick().

◆ usage()

static void usage ( const char * progname)
static

Definition at line 537 of file hd-rum-translator.cpp.

537 {
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}
#define SRED(x)
Definition color_out.h:85
#define SBOLD(x)
Definition color_out.h:80
#define SUNDERLINE(x)
Definition color_out.h:81

References SBOLD, SRED, and SUNDERLINE.

◆ writer()

static void * writer ( void * arg)
static

Definition at line 366 of file hd-rum-translator.cpp.

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}
#define TRUE
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
ssize_t hd_rum_decompress_write(void *state, void *buf, size_t count)
int recompress_get_num_active_ports(struct state_recompress *s)
uint32_t recompress_get_port_ssrc(struct state_recompress *s, int idx)
void recompress_remove_port(struct state_recompress *s, int index)
static struct response * change_replica_type(struct hd_rum_translator_state *s, struct module *mod, struct message *msg, int index)
#define COMMON_OPTS_INIT
Definition host.h:179
void free_message(struct message *msg, struct response *r)
struct message * check_message(struct module *mod)
#define RESPONSE_INT_SERV_ERR
Definition messaging.h:63
int udp_sendto(socket_udp *s, char *buffer, int buflen, struct sockaddr *dst_addr, socklen_t addrlen)
Definition net_udp.c:1038
struct msghdr msg
long size
Definition hd-rum.c:29

References item::buf, hd_rum_translator_state::bufsize, change_replica_type(), check_message(), COMMON_OPTS_INIT, create_output_port(), hd_rum_translator_state::decompress, free_message(), hd_rum_decompress_write(), LOG_LEVEL_ERROR, LOG_LEVEL_NOTICE, LOG_LEVEL_WARNING, log_msg(), hd_rum_translator_state::mod, msg, new_response(), item::next, hd_rum_translator_state::qempty, hd_rum_translator_state::qempty_cond, hd_rum_translator_state::qempty_mtx, hd_rum_translator_state::qfull, hd_rum_translator_state::qfull_cond, hd_rum_translator_state::qfull_mtx, hd_rum_translator_state::qhead, hd_rum_translator_state::qtail, RATE_UNLIMITED, hd_rum_translator_state::recompress, recompress_get_num_active_ports(), recompress_get_port_ssrc(), recompress_remove_port(), hd_rum_translator_state::replicas, RESPONSE_BAD_REQUEST, RESPONSE_INT_SERV_ERR, RESPONSE_OK, hd_rum_translator_state::server_socket, SIZE, item::size, TRUE, and udp_sendto().

Referenced by main().

Here is the call graph for this function: