From 126a69f29c1b71d4352aa6f6d4bd056491e73a0f Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 9 Feb 2018 10:13:00 -0500 Subject: [PATCH] TT#28163 logging and documentation improvements Change-Id: Ib82b80c9ddcf557cd5ab6f99e693a5234471b1ac --- README.md | 58 +++++++++++++++++++++++++++++++++++++++- daemon/call_interfaces.c | 2 ++ daemon/codec.c | 12 +++++++-- daemon/main.c | 9 ++++++- lib/codeclib.c | 36 ++++++++++++++----------- lib/codeclib.h | 8 +++--- recording-daemon/main.c | 2 +- 7 files changed, 104 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 467bf5c36..1db249ec6 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ the following additional features are available: - RTP/RTCP multiplexing (RFC 5761) and demultiplexing - Breaking of BUNDLE'd media streams (draft-ietf-mmusic-sdp-bundle-negotiation) - Recording of media streams, decrypted if possible +- Transcoding and repacketization *Rtpengine* does not (yet) support: @@ -58,7 +59,7 @@ On a Debian System On a Debian system, everything can be built and packaged into Debian packages by executing `dpkg-buildpackage` (which can be found in the `dpkg-dev` package) in the main directory. This script will issue an error and stop if any of the dependency packages are -not installed. +not installed. The script `dpkg-checkbuilddeps` can be used to check missing dependencies. Before that, run `./debian/flavors/no_ngcp` in order to remove any NGCP dependencies. @@ -96,6 +97,10 @@ The generated files are (with version 2.3.0 being built on an amd64 system): module for manual compilation. Required for in-kernel operation, but only if the DKMS package can't be used. +For transcoding purposes, Debian provides an additional package `libavcodec-extra` to replace +the regular `libavcodec` package. It is recommended to install this extra package to offer support +for additional codecs. + Manual Compilation ------------------ @@ -115,6 +120,7 @@ There's 3 parts to *rtpengine*, which can be found in the respective subdirector - *XMLRPC-C* version 1.16.08 or higher - *hiredis* library - *libiptc* library for iptables management (optional) + - *ffmpeg* codec libraries for transcoding (options) such as *libavcodec*, *libavfilter*, *libavresample* The `Makefile` contains a few Debian-specific flags, which may have to removed for compilation to be successful. This will not affect operation in any way. @@ -123,6 +129,9 @@ There's 3 parts to *rtpengine*, which can be found in the respective subdirector `Makefile` also contains a switch to disable it. See the `--iptables-chain` option for a description. + Similarly, the transcoding feature can be excluded via a switch in the `Makefile`, making it + unnecessary to have the *ffmpeg* libraries installed. + * `iptables-extension` Required for in-kernel packet forwarding. @@ -207,6 +216,7 @@ option and which are reproduced below: --recording-method=pcap|proc Strategy for call recording --recording-format=raw|eth PCAP file format for recorded calls. --iptables-chain=STRING Add explicit firewall rules to this iptables chain + --codecs Print a list of supported codecs and exit Most of these options are indeed optional, with two exceptions. It's mandatory to specify at least one local IP address through `--interface`, and at least one of the `--listen-...` options must be given. @@ -855,6 +865,52 @@ then the start-up sequence might look like this: With this setup, the SIP proxy can choose which instance of *rtpengine* to talk to and thus which local interface to use by sending its control messages to either port 2223 or port 2224. +Transcoding +=========== + +Currently transcoding is supported for audio streams. The feature can be disabled on a compile-time +basis, and is enabled by default. + +Even though the transcoding feature is available by default, it is not automatically engaged for +normal calls. Normally *rtpengine* leaves codec negotation up to the clients involved in the call +and does not interfere. In this case, if the clients fail to agree on a codec, the call will fail. + +The transcoding feature can be engaged for a call by instructing *rtpengine* to do so by using +one of the transcoding options in the *ng* control protocol, such as `transcode` or `ptime` (see below). +If a codec is requested via the `transcode` option that was not originally offered, transcoding will +be engaged for that call. + +With transcoding active for a call, all unsupported codecs will be removed from the SDP. Transcoding +happens in userspace only, so in-kernel packet forwarding will not be available for transcoded codecs. + +The following codecs are supported by *rtpengine*: + +* G.711 (a-Law and ยต-Law) +* G.722 +* G.723.1 +* Speex +* GSM +* iLBC +* Opus +* AMR (narrowband and wideband) + +Codec support is dependent on support provided by the `ffmpeg` codec libraries, which may vary from +version to version. Use the `--codecs` command line option to have *rtpengine* print a list of codecs +and their supported status. The list includes some codecs that are not listed above. Some of these +are not actual VoIP codecs (such as MP3), while others lack support for encoding by *ffmpeg* at the +time of writing (such as G.729, QCELP, or ATRAC). If encoding support for these codecs becomes available +in *ffmpeg*, *rtpengine* will be able to support them. + +Audio format conversion including resampling and mono/stereo up/down-mixing happens automatically +as required by the codecs involved. For example, one side could be using stereo Opus at 48 kHz +sampling rate, and the other side could be using mono G.711 at 8 kHz, and *rtpengine* will perform +the necessary conversions. + +If repacketization (using the `ptime` option) is requested, the transcoding feature will also be +engaged for the call, even if no additional codecs were requested. + +Non-audio pseudo-codecs (such as T.38 or RFC 4733 `telephone-event`) are not currently supported. + The *ng* Control Protocol ========================= diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 2b04b0792..cb78ee6ac 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1042,9 +1042,11 @@ static void ng_stats_media(bencode_item_t *list, const struct call_media *m, BF_M("passthrough", PASSTHRU); BF_M("ICE", ICE); BF_M("trickle ICE", TRICKLE_ICE); + BF_M("ICE controlling", ICE_CONTROLLING); BF_M("ICE-lite", ICE_LITE); BF_M("unidirectional", UNIDIRECTIONAL); BF_M("loop check", LOOP_CHECK); + BF_M("transcoding", TRANSCODE); stats: for (l = m->streams.head; l; l = l->next) { diff --git a/daemon/codec.c b/daemon/codec.c index 4de4d6d61..cc5ed6a80 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -135,7 +135,7 @@ static void __ensure_codec_def(struct rtp_payload_type *pt, struct call_media *m pt->codec_def = codec_find(&pt->encoding, media->type_id); if (!pt->codec_def) return; - if (!pt->codec_def->encoder || !pt->codec_def->decoder) + if (pt->codec_def->avcodec_id != -1 && (!pt->codec_def->encoder || !pt->codec_def->decoder)) pt->codec_def = NULL; } static GList *__delete_receiver_codec(struct call_media *receiver, GList *link) { @@ -345,6 +345,8 @@ next: // the list, as we must expect to potentially receive media in that codec, which we // then could not transcode. if (MEDIA_ISSET(receiver, TRANSCODE)) { + ilog(LOG_INFO, "Enabling transcoding engine"); + for (GList *l = receiver->codecs_prefs_recv.head; l; ) { struct rtp_payload_type *pt = l->data; @@ -720,6 +722,7 @@ static struct rtp_payload_type *codec_make_dynamic_payload_type(const codec_def_ } +// special return value `(void *) 0x1` to signal type mismatch static struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct call_media *media) { str codec_fmt = *codec_str; str codec, parms, chans, opts; @@ -736,9 +739,11 @@ static struct rtp_payload_type *codec_make_payload_type(const str *codec_str, st if (clockrate && !channels) channels = 1; - const codec_def_t *dec = codec_find(&codec, media->type_id); + const codec_def_t *dec = codec_find(&codec, 0); if (!dec) return NULL; + if (media->type_id && dec->type != media->type_id) + return (void *) 0x1; // we must support both encoding and decoding if (!dec->encoder) return NULL; @@ -769,6 +774,9 @@ static struct rtp_payload_type *codec_add_payload_type(const str *codec, struct STR_FMT(codec)); return NULL; } + if (pt == (void *) 0x1) + return NULL; + // find an unused payload type number if (pt->payload_type < 0) pt->payload_type = 96; // default first dynamic payload type number diff --git a/daemon/main.c b/daemon/main.c index 06e6088b5..94ba33b41 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -244,6 +244,7 @@ static void options(int *argc, char ***argv) { char *homerp = NULL; char *homerproto = NULL; char *endptr; + int codecs = 0; GOptionEntry e[] = { { "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" }, @@ -292,12 +293,18 @@ static void options(int *argc, char ***argv) { #ifdef WITH_IPTABLES_OPTION { "iptables-chain",0,0, G_OPTION_ARG_STRING, &rtpe_config.iptables_chain,"Add explicit firewall rules to this iptables chain","STRING" }, #endif + { "codecs", 0, 0, G_OPTION_ARG_NONE, &codecs, "Print a list of supported codecs and exit", NULL }, { NULL, } }; config_load(argc, argv, e, " - next-generation media proxy", "/etc/rtpengine/rtpengine.conf", "rtpengine", &rtpe_config.common); + if (codecs) { + codeclib_init(1); + exit(0); + } + if (!if_a) die("Missing option --interface"); if (!listenps && !listenudps && !listenngs) @@ -556,7 +563,7 @@ static void init_everything() { if (call_interfaces_init()) abort(); statistics_init(); - codeclib_init(); + codeclib_init(0); } diff --git a/lib/codeclib.c b/lib/codeclib.c index 165fd1383..023a5db66 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -86,7 +86,6 @@ static codec_def_t __codec_defs[] = { .clockrate_mult = 1, .default_ptime = 20, .packetizer = packetizer_passthrough, - .decode_only_ok = 1, .type = MT_AUDIO, }, { @@ -169,7 +168,6 @@ static codec_def_t __codec_defs[] = { .avcodec_id = AV_CODEC_ID_ATRAC3, .default_ptime = 20, .packetizer = packetizer_passthrough, - .decode_only_ok = 1, .type = MT_AUDIO, }, { @@ -177,7 +175,6 @@ static codec_def_t __codec_defs[] = { .avcodec_id = AV_CODEC_ID_ATRAC3P, .default_ptime = 20, .packetizer = packetizer_passthrough, - .decode_only_ok = 1, .type = MT_AUDIO, }, #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) @@ -187,7 +184,6 @@ static codec_def_t __codec_defs[] = { .avcodec_name = NULL, .default_ptime = 20, .packetizer = packetizer_passthrough, - .decode_only_ok = 1, .type = MT_AUDIO, }, { @@ -197,7 +193,6 @@ static codec_def_t __codec_defs[] = { .default_clockrate = 8000, .default_ptime = 20, .packetizer = packetizer_passthrough, - .decode_only_ok = 1, .type = MT_AUDIO, }, { @@ -207,7 +202,6 @@ static codec_def_t __codec_defs[] = { .default_clockrate = 8000, .default_ptime = 20, .packetizer = packetizer_passthrough, - .decode_only_ok = 1, .type = MT_AUDIO, }, #endif @@ -531,7 +525,7 @@ static void avlog_ilog(void *ptr, int loglevel, const char *fmt, va_list ap) { free(msg); } } -void codeclib_init() { +void codeclib_init(int print) { av_register_all(); avcodec_register_all(); avfilter_register_all(); @@ -577,16 +571,28 @@ void codeclib_init() { } // check if we have support if we are supposed to if (def->avcodec_name || def->avcodec_id >= 0) { - if (!def->encoder && !def->decoder) - ilog(LOG_INFO, "Codec %s is not supported by codec library", def->rtpname); - else if (!def->encoder) { - if (!def->decode_only_ok) - ilog(LOG_INFO, "Codec %s is only supported for decoding by codec library", + if (print) { + if (def->encoder && def->decoder) + printf("%20s: fully supported\n", def->rtpname); + else if (def->decoder) + printf("%20s: supported for decoding only\n", def->rtpname); + else if (def->encoder) + printf("%20s: supported for encoding only\n", def->rtpname); + else + printf("%20s: not supported\n", def->rtpname); + } + else { + if (!def->encoder && !def->decoder) + ilog(LOG_DEBUG, "Codec %s is not supported by codec library", def->rtpname); + else if (!def->encoder) { + ilog(LOG_DEBUG, "Codec %s is only supported for decoding " + "by codec library", def->rtpname); + } + else if (!def->decoder) + ilog(LOG_DEBUG, "Codec %s is only supported for encoding " + "by codec library", def->rtpname); } - else if (!def->decoder) - ilog(LOG_INFO, "Codec %s is only supported for encoding by codec library", - def->rtpname); } } } diff --git a/lib/codeclib.h b/lib/codeclib.h index 6bea648be..766572610 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -57,7 +57,6 @@ struct codec_def_s { int default_ptime; packetizer_f * const packetizer; const int bits_per_sample; - const int decode_only_ok; const enum media_type type; // codec-specific callbacks @@ -125,7 +124,7 @@ struct packet_sequencer_s { -void codeclib_init(void); +void codeclib_init(int); const codec_def_t *codec_find(const str *name, enum media_type); @@ -181,7 +180,10 @@ enum media_type { struct codec_def_s { }; -INLINE void codeclib_init(void) { } +INLINE void codeclib_init(int print) { + if (print) + printf("No codecs supported.\n"); +} INLINE enum media_type codec_get_type(const str *type) { return -1; diff --git a/recording-daemon/main.c b/recording-daemon/main.c index 75437a9ce..ef5b2ff5e 100644 --- a/recording-daemon/main.c +++ b/recording-daemon/main.c @@ -65,7 +65,7 @@ static void signals(void) { static void setup(void) { log_init("rtpengine-recording"); if (output_enabled) { - codeclib_init(); + codeclib_init(0); output_init(output_format); if (!g_file_test(output_dir, G_FILE_TEST_IS_DIR)) { ilog(LOG_INFO, "Creating output dir '%s'", output_dir);