diff --git a/daemon/codec.c b/daemon/codec.c index 2d86ad4a3..834f7e6d8 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -6022,6 +6022,29 @@ static void codec_worker(void *d) { mutex_unlock(&transcode_lock); thread_waker_del(&waker); } + +static unsigned int __codec_pipeline_hash(const struct codec_pipeline_index *i) { + return str_case_hash(&i->src.encoding) + ^ str_case_hash(&i->dst.encoding) + ^ i->src.clock_rate + ^ i->dst.clock_rate + ^ i->src.channels + ^ i->dst.channels; +} + +static gboolean __codec_pipeline_eq(const struct codec_pipeline_index *a, const struct codec_pipeline_index *b) { + return str_case_equal(&a->src.encoding, &b->src.encoding) + && str_case_equal(&a->dst.encoding, &b->dst.encoding) + && a->src.clock_rate == b->src.clock_rate + && a->dst.clock_rate == b->dst.clock_rate + && a->src.channels == b->src.channels + && a->dst.channels == b->dst.channels; +} + +TYPED_GHASHTABLE_IMPL(transcode_config_ht, __codec_pipeline_hash, __codec_pipeline_eq, NULL, NULL) + +transcode_config_ht rtpe_transcode_config; + #endif void codecs_init(void) { @@ -6036,6 +6059,19 @@ void codecs_init(void) { } else __rtp_decode = __rtp_decode_direct; + + rtpe_transcode_config = transcode_config_ht_new(); + + for (__auto_type l = rtpe_config.transcode_config.head; l; l = l->next) { + __auto_type tcc = l->data; + + codec_init_payload_type(&tcc->i.src, MT_UNKNOWN); + codec_init_payload_type(&tcc->i.dst, MT_UNKNOWN); + + if (t_hash_table_lookup(rtpe_transcode_config, &tcc->i)) + die("Duplicate entry in transcode config '%s'", tcc->name); + t_hash_table_insert(rtpe_transcode_config, &tcc->i, tcc); + } #endif } void codecs_cleanup(void) { diff --git a/daemon/main.c b/daemon/main.c index 8261c93c5..dbb33d2ad 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -414,6 +414,33 @@ static void add_if_from_config(const char *name, charp_ht ht, struct interface_c die("Failed to parse interface information '%s' from config file", orig_name); } +struct transcode_config_callback_arg { + transcode_config_q q; +}; + +static void do_transcode_config(const char *name, charp_ht ht, struct transcode_config_callback_arg *a) { + char *src = t_hash_table_lookup(ht, "source"); + if (!src) + die("Transcode config '%s' has no 'source' set", name); + char *dst = t_hash_table_lookup(ht, "destination"); + if (!dst) + die("Transcode config '%s' has no 'destination' set", name); + + __auto_type tc = g_new0(struct transcode_config, 1); + tc->name = g_strdup(name); + t_queue_push_tail(&a->q, tc); + + tc->src = g_strdup(src); + tc->dst = g_strdup(dst); + + if (!codec_parse_payload_type(&tc->i.src, STR_PTR(tc->src))) + die("Failed to parse source codec '%s' in transcode config '%s'", src, name); + if (!codec_parse_payload_type(&tc->i.dst, STR_PTR(tc->dst))) + die("Failed to parse source codec '%s' in transcode config '%s'", src, name); + + die("Transcode config '%s' has no verdict", name); +} + static bool if_addr_parse(intf_config_q *q, char *s, struct ifaddrs *ifas) { str name; char *c; @@ -622,6 +649,7 @@ static void options(int *argc, char ***argv, charp_ht templates) { g_autoptr(char) redis_format = NULL; g_autoptr(char) templates_section = NULL; g_autoptr(char) interfaces_config = NULL; + g_autoptr(char) transcode_config = NULL; GOptionEntry e[] = { { "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" }, @@ -772,6 +800,7 @@ static void options(int *argc, char ***argv, charp_ht templates) { { "audio-buffer-length",0,0, G_OPTION_ARG_INT,&rtpe_config.audio_buffer_length,"Length in milliseconds of audio buffer","INT"}, { "audio-buffer-delay",0,0, G_OPTION_ARG_INT,&rtpe_config.audio_buffer_delay,"Initial delay in milliseconds for buffered audio","INT"}, { "audio-player",0,0, G_OPTION_ARG_STRING, &use_audio_player, "When to enable the internal audio player","on-demand|play-media|transcoding|always"}, + { "transcode-config",0,0,G_OPTION_ARG_STRING, &transcode_config, "Config section to use for transcoding rules","STR"}, #endif #ifdef HAVE_MQTT { "mqtt-host",0,0, G_OPTION_ARG_STRING, &rtpe_config.mqtt_host, "Mosquitto broker host or address", "HOST|IP"}, @@ -824,6 +853,8 @@ static void options(int *argc, char ***argv, charp_ht templates) { // global port-min/max may not be set yet. intf_config_q icq = TYPED_GQUEUE_INIT; + struct transcode_config_callback_arg tcca = { .q = TYPED_GQUEUE_INIT }; + config_load_ext(argc, argv, e, " - next-generation media proxy", "/etc/rtpengine/rtpengine.conf", "rtpengine", &rtpe_config.common, (struct rtpenging_config_callback []) { @@ -846,6 +877,14 @@ static void options(int *argc, char ***argv, charp_ht templates) { .callback = add_if_from_config, }, }, + { + .type = RCC_FILE_GROUPS, + .arg.tcca = &tcca, + .file_groups = { + .prefix = &transcode_config, + .callback = do_transcode_config, + }, + }, { 0 }, }); @@ -1297,8 +1336,9 @@ static void options(int *argc, char ***argv, charp_ht templates) { if (rtpe_config.ng_client_retries <= 0) die("Invalid value for 'ng-client-retries'"); - // everything OK, do post-processing + rtpe_config.transcode_config = tcca.q; + // everything OK, do post-processing } static void fill_initial_rtpe_cfg(struct rtpengine_config* ini_rtpe_cfg) { diff --git a/docs/rtpengine.md b/docs/rtpengine.md index be72cee9c..a533fc06f 100644 --- a/docs/rtpengine.md +++ b/docs/rtpengine.md @@ -1504,6 +1504,11 @@ call to inject-DTMF won't be sent to __\-\-dtmf-log-dest=__ or __\-\-listen-tcp- defaults to 50. The number of retries must be larger than zero and defaults to 5. +- __\-\-transcode-config=__*STR* + + Sets the prefix for sections in the config file to use for transcode + configurations. See section *TRANSCODE CONFIG* below. + ## INTERFACES This section describes the legacy syntax for configuring interfaces, which can @@ -1825,6 +1830,26 @@ template that is applied to all signalling messages. These templates are automatically applied without needing to refer to them by name using __template=...__ from within the signalling message. +## TRANSCODE CONFIG + +If the setting __transcode-config__ is set to a non-empty string, then +*rtpengine* will look for sections in the configuration file starting with the +given prefix strings (plus a dash) to use as transcode configurations. For +example, if __transcode-config = tx__ is set, then sections in the config file +such as __tx-1__ or __tx-foobar__ will be considered as transcode +configurations. The names of all sections in the config file must be unique. + +Each transcoding section must list a source codec as __source=...__ and a +destination codec as __destination=...__. Codecs can be named simply by their +basic name (such as __PCMA__) or with a complete SDP-like definition (such as +__opus/48000/2__). Each section must also list an action or a verdict. + +For example, if __source=PCMU__ and __destination=PCMA__ are set, then this +particular transcoding config section would be considered if the received codec +is PCMU and the requested output codec is PCMA. + +No verdicts are supported at this time. + ## EXIT STATUS - __0__ diff --git a/include/codec.h b/include/codec.h index c33b7a18c..3ce297631 100644 --- a/include/codec.h +++ b/include/codec.h @@ -104,6 +104,15 @@ struct codec_scheduler { long output_skew; }; +struct transcode_config { + char *src; // unparsed + char *dst; // unparsed + + char *name; // of the config section + + struct codec_pipeline_index i; // parsed +}; + typedef union { struct call_monologue *ml; } codec_timer_callback_arg_t __attribute__ ((__transparent_union__)); @@ -251,6 +260,9 @@ INLINE struct codec_handler *codec_handler_lookup(codec_handlers_ht ht, int pt, } +extern transcode_config_ht rtpe_transcode_config; + + #else diff --git a/include/main.h b/include/main.h index 67f60075d..41ace5e6c 100644 --- a/include/main.h +++ b/include/main.h @@ -258,6 +258,7 @@ RTPE_CONFIG_CHARPP_PARAMS enum xmlrpc_format fmt; enum log_format log_format; intf_config_q interfaces; + transcode_config_q transcode_config; enum { REDIS_FORMAT_BENCODE = 0, REDIS_FORMAT_JSON, diff --git a/include/types.h b/include/types.h index c4a014f19..1b79264b6 100644 --- a/include/types.h +++ b/include/types.h @@ -74,6 +74,11 @@ struct codec_handler; TYPED_GHASHTABLE_PROTO(codec_handlers_ht, struct codec_handler_index, struct codec_handler) TYPED_GQUEUE(codec_handlers, struct codec_handler) +struct transcode_config; +struct codec_pipeline_index; +TYPED_GHASHTABLE_PROTO(transcode_config_ht, struct codec_pipeline_index, struct transcode_config) +TYPED_GQUEUE(transcode_config, struct transcode_config) + struct codec_packet; TYPED_GQUEUE(codec_packet, struct codec_packet) diff --git a/lib/auxlib.h b/lib/auxlib.h index 8d7f35d4d..f48395fbc 100644 --- a/lib/auxlib.h +++ b/lib/auxlib.h @@ -57,6 +57,7 @@ TYPED_GHASHTABLE(charp_ht, char, char, c_str_hash, c_str_equal, g_free, g_free) union rtpenging_config_callback_arg { charp_ht ht; struct interface_config_callback_arg *icca; + struct transcode_config_callback_arg *tcca; } __attribute__((__transparent_union__)); struct rtpenging_config_callback {