RTPENGINE(1)
๐ธ RTPENGINE ๐ธ
okay bestie, sit down, hydrate, because this is the manual page for rtpengine โ the NGCP proxy for RTP and other UDP based media traffic. it's giving telecom infrastructure realness and honestly? it ate. ๐
โจ SYNOPSIS โจ
rtpengine --interface=addr...
--listen-tcp|--listen-udp|--listen-ng|--listen-tcp-ng|--listen-http|--listen-https=addr...
[option...]
๐ฆ DESCRIPTION ๐ฆ
so the Sipwise NGCP rtpengine is a proxy for RTP traffic and other UDP based media traffic. it's meant to be used alongside the Kamailio SIP proxy, and it's a drop-in replacement for any of the other RTP and media proxies out there. like, any of them. no cap. ๐ซถ
๐ OPTIONS ๐
most of these options are, in fact, genuinely optional (iconic behaviour honestly). but there are two big exceptions and you can NOT skip them:
- you must specify at least one local IP address with
--interface - you must give at least one of the
--listen-...options
also, hot tip: all of these options can โ and like, should โ be put in a config file instead of dumping them all on the command line. check --config-file below for the tea. ๐
--helpโ prints the usage information. that's it, that's the whole thing. ๐-v,--versionโ if you call thertpenginedaemon with this, it just prints its version number and exits. no drama, no notes.--codecsโ prints a list of supported codecs and exits. codec reveal!! โจ--config-file=FILEโ tells you where the config file lives. this config file is an.inistyle config file, and every command-line option listed on this page is also a valid option in the config file. a few rules though, bestie, listen up:- for all command-line options, you gotta use the long name version, not the single-character version (so like
table, not justt). - for boolean options that are either present or not (like
no-fallback), you have to use a boolean value in the config file โ eithertrueorfalse. - if an option shows up in both the config file and the command line, the command-line value wins. command line is the main character here. ๐
- options that can be specified multiple times on the command line must only be given once in the config file, with the multiple values separated by semicolons (there's an example in the INTERFACES section below).
- for all command-line options, you gotta use the long name version, not the single-character version (so like
and as a special value, you can pass none here to suppress loading of the default config file. slay.
--config-section=STRINGโ picks which.inistyle section gets used in the config file. you can have multiple sections chillin' in there, but only one can be used at a time. default value isrtpengine. a section gets started in the config file with square brackets, like[rtpengine]. ๐ง-t,--table=INTโ takes an integer argument and says which kernel table to use for in-kernel packet forwarding. more detail lives in the section on in-kernel operation in theREADME.md. optional, defaults to zero. and if in-kernel operation is simply not the vibe, you can pass a negative number. ๐-F,--no-fallbackโ stops it from falling back to userspace-only operation when the kernel module isn't available. heads up: if you give this option and the kernel module isn't there, startup of the daemon fails with an error. periodt.-S,--save-interface-portsโ binds ports only on the first available local interface, of the desired family, of the logical interface. if there are no ports available on any local interface of the desired family, you get an error message.
โ ๏ธ and in this case, ICE will be broken. we said what we said. ๐ญ
-i,--interface=[NAME/]IP[!IP]โ specifies a local network interface for RTP. you need at least one, but you can absolutely specify multiple. full details are in the INTERFACES section right below. ๐-l,--listen-tcp=[IP:]PORT-u,--listen-udp=[IP46:]PORT-n,--listen-ng=[IP46:]PORT-n,--listen-tcp-ng=[IP46:]PORT
each of these enables one of the 4 available control protocols if given, and each takes either just a port number as its argument, or an address:port pair separated by a colon. at least one of these 3 options must be given.
- the
tcpprotocol is obsolete. it was used by old versions of OpenSER and its mediaproxy module. it's provided for backwards compatibility only โ vintage, not current. ๐ - the
udpprotocol is what Kamailio'srtpproxymodule uses. in this mode,rtpenginecan be used as a drop-in replacement for any other compatible RTP proxy. - the
ngprotocol is an advanced control protocol and works with Kamailio'srtpenginemodule. with this one, the complete SDP body gets passed tortpengine, rewritten, and passed right back to Kamailio. you also unlock several additional features here โ ICE handling, SRTP bridging, etc. ๐ฅ - the
tcp-ngprotocol is literally thengprotocol but transported over TCP.
๐ก recommendation from us to you: specify not only a local port number, but also 127.0.0.1 as the interface to bind to. trust.
-c,--listen-cli=[IP46:]PORTโ the TCP ip and port to listen on for the CLI (command line interface). ๐ฅ๏ธ-g,--graphite=IP46:PORTโ the address of the graphite statistics server.-w,--graphite-interval=INTโ how often information gets sent to the graphite server. โฐ--graphite-prefix=STRINGโ adds a prefix to every graphite line.--graphite-timeout=INTโ sets how much time (in seconds) before forcing a failed graphite socket connection, for when the graphite server is filtered out. if set to0, there are no changes. ๐-t,--tos=INTโ takes an integer and, if given, sets the TOS value that should go into outgoing packets. default is to leave the TOS field untouched. a typical value is184(Expedited Forwarding). โจ--control-tos=INTโ takes an integer and, if given, sets the TOS value that should be set in thecontrol-nginterface packets. default is to leave the TOS field untouched. this parameter can also be set or listed viartpengine-ctl. ๐๏ธ-o,--timeout=SECSโ takes a number of seconds; after that many seconds with no media traffic received, a media stream is considered dead. and if all the media streams belonging to a particular call go dead, the whole call gets removed fromrtpengine's internal state table. defaults to60seconds. ๐-s,--silent-timeout=SECSโ ditto as--timeout, but it applies to muted or inactive media streams instead. defaults to3600(one hour).-a,--final-timeout=SECSโ the number of seconds since call creation, after which the call is deleted. super useful for limiting the lifetime of a call. you can disable this feature entirely by setting the parameter to0. by default this timeout is disabled. ๐ฐ๏ธ--offer-timeout=SECSโ this timeout (in seconds) is applied to calls which only had an offer but no answer. defaults to3600(one hour). the situationship calls, if you will. ๐ญ-p,--pidfile=FILEโ specifies a path and file name to write the daemon's PID number to. ๐-f,--foregroundโ if given, stops the daemon from daemonizing, meaning it stays in the foreground. extremely useful for debugging. ๐-m,--port-min=INT-M,--port-max=INT
both take an integer as an argument and together they define the local port range from which rtpengine will allocate UDP ports for media traffic relay. defaults are 30000 and 40000 respectively. ๐
-L,--log-level=INTโ takes an integer and controls the highest log level that gets sent to syslog. this is merely the default log level used for logging subsystems (see below) that don't explicitly have a separate log level configured.
the log levels match the ones in the syslog(3) man page. default value is 6, equivalent to LOG_INFO. the highest possible value is 7 (LOG_DEBUG), which logs everything. ๐
during runtime, you can decrease the log level by sending the signal SIGURS1 to the daemon, and increase it with the signal SIGUSR2. โจ
--log-level-subsystem=INTโ configures a log level for one of the logging subsystems. a logging subsystem that doesn't have a log level configured explicitly takes its default value from thelog-levelsetting described above โ with the exception of theinternalssubsystem, which by default has all logging disabled. ๐คซ
the full list of logging subsystems can be viewed by pulling up the --help online help. some (if not all) subsystems are:
corespandsp(messages generated by SpanDSP itself)ffmpeg(messages generated by ffmpeg libraries themselves)transcoding(messages related to RTP/media transcoding)codec(messages related to codec negotiation)rtcpicecrypto(messages related to crypto/SRTP/SDES/DTLS negotiation)srtp(messages related to RTP/SRTP en/decryption)internals(disabled by default)http(includes WebSocket)control(messages related to control protocols, including SDP exchanges)
--log-facilty=daemon|local0|...|local7|...โ the syslog facility to use when sending log messages to the syslog daemon. defaults todaemon. ๐ง--log-facilty-cdr=daemon|local0|...|local7|...โ same as--log-facility, except the difference is that only CDRs are written to this log facility. ๐--log-facilty-rtcp=daemon|local0|...|local7|...โ same as--log-facility, except the difference is that only RTCP data is written to this log facility.
โ ๏ธ be careful with this parameter, because there may be a LOT of information written to it. like, a lot a lot. ๐
--log-facilty-dtmf=daemon|local0|...|local7|...โ same as--log-facility, except the difference is that only DTMF events are written to this log facility. DTMF events are extracted from RTP packets conforming toRFC 4733, are encoded in JSON format, and get written as soon as the end of an event is detected. ๐ฅ--log-format=default|parsableโ picks between multiple log output styles. the default is to prefix log lines with a description of the relevant entity, such as[CALLID]or[CALLID port 12345]. theparsableoutput style is similar, but makes the ID easier to parse by enclosing it in quotes, like[ID="CALLID"]or[ID="CALLID" port="12345"]. both iconic, honestly. โจ--dtmf-log-dest=IP46:PORTโ configures a target address for logging detected DTMF events. similar to the feature enabled by--log-facilty-dtmf, but instead of writing detected DTMF events to syslog, this sends the JSON payload to the given address as UDP packets. ๐ก--dtmf-log-ng-tcpโ if--listen-tcp-ngis enabled, this will send DTMF events to all connected clients encoded inbencodeformat. ๐--dtmf-no-suppressโ some RTP clients keep sending audio RTP packets during a DTMF event, which means you get both audio packets and DTMF packets showing up at the same time. by default, when transcoding,rtpenginesuppresses audio packets during a DTMF event and will only send DTMF packets until the DTMF event is over. setting this option disables that feature. ๐
(this is only part 1, bestie โ there's more coming, don't go anywhere ๐)
--log-srtp-keysโ bestie this one writes your SRTP keys to the error log instead of the debug log. yes, for real. ๐-E,--log-stderrโ log to stderr instead of syslog. only useful when you're also using--foreground, so don't come crying to me when it does nothing on its own. ๐ญ--split-logsโ splits multi-line log messages into separate individual log messages, so every single line gets its own little log line prefix. it's giving organized. โจ--max-log-line-length=INTโ when a log line goes over the character count you put here, it gets chopped into multiple lines. you can set it to a negative value to allow unlimited length log lines (go off), and setting it to zero means "use the default", which is unlimited if you're logging to stderr, or 500 if you're logging to syslog. ๐--no-log-timestampsโ no timestamps on log lines written to stderr. only useful together with--log-stderr, iykyk. ๐--log-mark-prefix=STRINGโ a prefix that gets added to particular data fields in log files that are deemed sensitive and/or private info. defaults to an empty string. ๐ซถ--log-mark-suffix=STRINGโ same vibe, but a suffix instead. also defaults to an empty string. ๐--num-threads=INTโ how many worker threads to create, and it has to be at least one, periodt. default is to create as many threads as there are CPU cores available. but if the number of CPU cores can't be determined, or if it's less than four, then the default is four. โญ--media-num-threads=INTโ number of threads to launch for media playback. defaults to the same number as num-threads. you can set it to zero if you don't want any media playback functionality at all.
okay so media playback is actually handled by two threads: one for reading and decoding the media file, and another to schedule and send out RTP packets. so like, if you set this option to 4, you get 8 threads launched in total. math is math. ๐ฅ
--thread-stack=INTโ sets the stack size of each thread to the value given in kB. defaults to 2048 kB. you can set it to -1 to leave the default provided by the OS unchanged. ๐ง--sip-sourceโ storytime: the original rtpproxy, and older versions of rtpengine, by default did not honour IP addresses given in the SDP body, and instead used the source address of the received SIP message as the default endpoint address. newer versions of rtpengine reverse this behaviour and honour the addresses given in the SDP body by default. this option restores the old behaviour. so it's lowkey a time machine. โณ--dtls-passiveโ enables theDTLS=passiveflag for all calls, unconditionally. no cap. ๐ธ-d,--delete-delay=INTโ deletes the call from memory after the specified delay. you can set it to zero for immediate call deletion. poof, gone. ๐-r,--redis=[PW@]IP:PORT/INTโ connects to the specified Redis database (with the given database number) and uses it for persistence storage. the format isADDRESS:PORT/DBNUM, for example127.0.0.1:6379/12to connect to the Redis DB number 12 running on localhost on the default Redis port.
if your Redis database is protected with an authentication password, you can supply it by prefixing the argument value with the password, separated by an @ symbol, like foobar@127.0.0.1:6379/12.
โ ๏ธ heads up bestie โ that leaves the password visible in the process list, which poses a security risk if untrusted users access the same system. ๐ฌ
as an alternative, the password can also be supplied in the shell environment through the environment variable RTPENGINE_REDIS_AUTH_PW.
on startup, rtpengine will read the contents of this database and restore all calls stored therein. during runtime operation, rtpengine will continually update the database's contents to keep it current, so that in case of a service disruption, the last state can be restored upon a restart.
when this option is given, rtpengine will delay startup until the Redis database adopts the master role (but see below ๐).
-w,--redis-write=[PW@]IP:PORT/INTโ configures a second Redis database for write operations. if you give this option in addition to the first one, then the first database gets used for read operations (i.e. to restore calls from), while the second one gets used for write operations (to update states in the database). read and write, living their separate lives. ๐
for password protected Redis servers, the environment variable for the password is RTPENGINE_REDIS_WRITE_AUTH_PW.
when both options are given, rtpengine will start and use the Redis database regardless of the database's role (master or slave).
-k,--subscribe-keyspace=INTโ list of redis keyspaces to subscribe. if this isn't present, no keyspaces are subscribed (that's the default behaviour). further subscriptions could be added/removed viartpengine-ctl ksadd/ksrm. this may lead to enabling/disabling of the redis keyspace notification feature. ๐ฆ--redis-num-threads=INTโ how many redis restore threads to create. default is 4. ๐--redis-expires=INTโ expire time in seconds for redis keys. default is 86400. โฐ--active-switchoverโ with this enabled, any activity (like signalling or media) on a call that was created through a Redis keyspace notification will make rtpengine take control of that call. without this option, an explicit command is required for rtpengine to take (or relinquish) control of a call. main character energy tbh. ๐-q,--no-redis-requiredโ when this parameter is present, orNO_REDIS_REQUIRED='yes'or'1'in the config file, rtpengine starts even if there's no initial connection to redis databases (either to-ror to-wor to both redis).
๐ซ be aware that if the -r redis cannot be initially connected, sessions are not reloaded upon rtpengine startup, even though rtpengine still starts.
--redis-allowed-errorsโ if this parameter is present and has a value>= 0, it configures how many consecutive errors are allowed when communicating with a redis server before the redis communication gets temporarily disabled for that server. while communication is disabled there will be no attempts to reconnect to redis or send commands to that server. default value is -1, meaning this feature is disabled. this parameter can also be set or listed viartpengine-ctl. ๐--redis-disable-timeโ configures the number of seconds redis communication is disabled because of errors. works together with the redis-allowed-errors parameter. the default value is 10. this parameter can also be set or listed viartpengine-ctl. โฒ๏ธ--redis-cmd-timeout=INTโ if set to a non-zero value it sets the timeout, in milliseconds, for each command to the redis server. if redis doesn't reply within the specified timeout, the command fails. the default value is 0, meaning the commands will be blocking without timeout. this parameter can also be set or listed viartpengine-ctl; note that setting the parameter to 0 will require a reconnect on all configured redis servers. ๐--redis-connect-timeout=INTโ sets the timeout value, in milliseconds, when connecting to a redis server. if the connection can't be made within the specified timeout, the connection fails. note that in case of failure, when reconnecting to redis, aPINGcommand is issued before attempting to connect, so the--redis-cmd-timeoutvalue will also be added to the total waiting time. this is useful if you're using--redis-allowed-errors, when attempting to estimate the total lost time in case of redis failures. the default value for the connection timeout is 1000ms. this parameter can also be set or listed viartpengine-ctl. ๐-b,--b2b-url=STRINGโ enables and sets the URI for an XMLRPC callback to be made when a call is torn down due to packet timeout. the special code%%can be used in place of an IP address, in which case the source address of the originating request (or alternatively the address specified using thexmlrpc-callbackng protocol option) will be used. ๐ฅ-x,--xmlrpc-format=INTโ selects the internal format of the XMLRPC callback message for B2BUA call teardown.0is for SEMS,1is for a generic format containing the call-ID only,2is for Kamailio. pick your fighter. โ๏ธ--max-sessions=INTโ limits the number of maximum concurrent sessions. set at startup viaMAX_SESSIONSin the config file. set at runtime via thertpengine-ctl util. settingrtpengine-ctl set maxsessions 0can be used in draining rtpengine sessions.- enable feature:
MAX_SESSIONS=1000 - enable feature:
rtpengine-ctl set maxsessions >= 0 - disable feature:
rtpengine-ctl set maxsessions -1
- enable feature:
by default, the feature is disabled (i.e. maxsessions == -1). ๐ฆ
--max-load=FLOATโ if the current 1-minute load average exceeds the value given here, reject new sessions until the load average drops below the threshold. ๐--max-cpu=FLOATโ if the current CPU usage (in percent) exceeds the value given here, reject new sessions until the CPU usage drops below the threshold. CPU usage is sampled in 0.5-second intervals. only supported on systems providing a Linux-style/proc/stat. ๐ฅ๏ธ--max-bandwidth=INTโ if the current bandwidth usage (in bytes per second) exceeds the value given here, reject new sessions until the bandwidth usage drops below the threshold. bandwidth usage is sampled in 1-second intervals and is based on received packets, not sent packets. ๐ถ--homer=IP46:PORTโ enables sending the decoded contents of RTCP packets to a Homer SIP capture server. the transport is HEP version 3 and the payload format is JSON. this argument takes an IP address and a port number as its value. ๐--homer-protocol=udp|tcpโ can be eitherudportcp, withudpbeing the default. ๐ฐ๏ธ--homer-id=INTโ the HEP protocol used by Homer contains a "capture ID" used to distinguish different sources of capture data. this ID can be specified using this argument. ๐--recording-dir=FILEโ an optional argument to specify a path to a directory where PCAP recording files and recording metadata files should be stored. if not specified, support for call recording will be disabled. ๐ฅ
rtpengine supports multiple mechanisms for recording calls. see recording-method below for a list. the default recording method pcap is described in this section.
PCAP files will be stored within a pcap subdirectory and metadata within a metadata subdirectory. ๐
the format for a metadata file is (with a trailing newline):
/path/to/recording-pcap.pcap
SDP mode: offer
SDP before RTP packet: 1
first SDP
SDP mode: answer
SDP before RTP packet: 1
second SDP
...
SDP mode: answer
SDP before RTP packet: 100
n-th and final SDP
start timestamp (YYYY-MM-DDThh:mm:ss)
end timestamp (YYYY-MM-DDThh:mm:ss)
generic metadata
So bestie, notice how there's two whole empty lines between every single logic block of metadata? Yeah, that's the vibe, don't skip them ๐ We write out all the answer SDP, each one separated from the next by exactly one empty line. And that generic metadata at the end? It can be literally any length, any number of lines, go off. โจ
The metadata files show up in the subdirectory when the call completes, but the PCAP files get written into that subdirectory while the call is being recorded โ different timing, same folder, it's giving organized queen ๐
๐จ Okay important one: since call recording this way happens entirely in userspace, in-kernel packet forwarding cannot be used for calls that are currently being recorded. Packet forwarding will therefore be done in userspace only. No cap.
--recording-method=pcap|procโ pick which flavor of call recording you want, because there's more than one ๐
The options on the menu rn are pcap and proc. The default is pcap, and that's the one described above (the subdirectory + metadata + pcap file situation).
So proc works differently: it writes metadata files directly into the recording-dir (no subdirectory, periodt), and instead of dumping RTP packet data into pcap files, the packet data gets exposed through a special interface in the /proc filesystem. Some dedicated userspace component has to go grab the packets from that interface โ usually a daemon, like recording-daemon, which is included in this repository ๐ซถ
Packet data hangs out in kernel memory until the userspace component retrieves it, but only a limited number of packets per media stream (default 10). If nobody retrieves them in time, they just get discarded ๐ Which honestly is the point โ it means you can flag every single call</em> to be recorded and then let the userspace component decide whether the packet data is useful for anything or not.
And here's the slay: with this recording method, in-kernel packet forwarding is fully supported even for calls that are being recorded. ๐
--recording-format=raw|ethโ choose the shape of your pcap recording ๐
In raw format (which is the default), there's no ethernet header. Set it to eth and a fake ethernet header gets added, which makes every packet 14 bytes larger. That's it, that's the whole thing.
--iptables-chain=STRINGโ turn on explicit management of an iptables chain, if that's your thing ๐ฅ
When enabled, rtpengine takes control of the iptables chain you name, and that chain must already exist before you start the daemon โ it will not be created for you, bestie. On startup, rtpengine flushes the chain, then adds one ACCEPT rule for each media port (RTP/RTCP) opened. Every rule matches the individual port and destination IP address exactly, and gets created with the call ID as the iptables comment. When the port closes, the rule gets deleted. Cleanup queen โจ
The whole point of this option is that it lets you build a firewall with a default DROP policy across the entire port range rtpengine uses, and then reference that given iptables chain to only selectively allow the ports that are <em>actually</em> in use. Selective, iconic, we love to see it ๐
Heads up: this applies only to media ports. It does not apply to any other ports (like the control ports) that rtpengine uses. ๐
Also heads up, and this one's real: the iptables API is not the most efficient one around and does not lend itself to fast dynamic creation and deletion of rules. If you have a high call volume โ especially many call attempts per second โ you might experience significant performance impact. This is not a shortcoming of rtpengine, it's a shortcoming of iptables and how its API is implemented in the Linux kernel, fr fr. In that case the recommendation is to add a static iptables rule for the entire media port range instead, and not use this option. ๐
--scheduling=default|...--priority=INT--idle-scheduling=default|...--idle-priority=INT
These four control the various thread scheduling parameters ๐ง The scheduling and priority settings apply to the main worker threads, while the idle- versions of those settings apply to various lower priority threads, like timer runs.
The scheduling settings take the name of one of the supported scheduler policies. Setting it to default or none is equivalent to not setting the option at all and just leaves the system default in place. The strings fifo and rr refer to realtime scheduling policies. other is the Linux default scheduling policy. batch is similar to other except for a small wake-up scheduling penalty. idle is an extremely low priority scheduling policy. The Linux-specific deadline policy is not supported by rtpengine. And ngl, not all systems necessarily support all scheduling policies โ go check your system's sched(7) man page for the details ๐
The priority settings correspond to the scheduling priority for realtime (fifo or rr) scheduling policies and must be in the range of 1 (low) through 99 (high). For all other scheduling policies (including when no policy is specified at all), the priority settings correspond to the nice value and should be in the range of -20 (high) through 19 (low). Now, not all systems support thread-specific nice values โ on a system that doesn't, using these settings might have unexpected results ๐ญ (Linux <em>does</em> support thread-specific nice values, so you're fine there.) Again: refer to your system's sched(7) man page. โจ
--mysql-host=HOST|IP--mysql-port=INT--mysql-user=USERNAME--mysql-pass=PASSWORD
Config for playing back media files stored in a MySQL (or MariaDB) database ๐ At minimum, mysql-host has to be configured for this to work at all. The others are optional and just default to their respective values from the MySQL/MariaDB client library. Easy.
--mysql-query=STRINGโ the query used to pull media files out of the database ๐
There's no default for this one, which means it's a mandatory configuration if you want media playback from a database. The query string you provide must contain the single format placeholder %llu and must not contain any other format placeholders. The ID value that rtpengine gets in the db-id key of the play media message is what gets dropped in place of that placeholder when querying the database. Cute, right?
An example configuration might look like this:
mysql-query = select data from voip.files where id = %llu
--endpoint-learning=delayed|immediate|off|heuristicโ pick your algorithm for learning RTP endpoint addresses ๐ง โจ
The legacy setting is delayed, which waits 3 seconds before committing to an endpoint address โ and that address is then learned from the first incoming RTP packet seen after the delay. The immediate setting learns the endpoint address from the first incoming packet seen, no 3-second delay. Using off disables endpoint learning altogether, which is likely breaking clients behind NAT ๐ so like, be careful. And heuristic includes the 3-second delay too, but ranks the source addresses seen from incoming RTP packets by preference:
- If a packet is seen with a source address <em>and</em> port matching the SDP address, that address is used.
- Otherwise, if a packet with a matching source address (but a different port) is seen, that address is used.
- Otherwise, if a packet with a matching source port (but a different address) is seen, that address is used.
- Otherwise, the source address of <em>any</em> incoming packet seen is used.
--jitter-buffer=INTโ size of the (incoming) jitter buffer, in packets ๐
A value of zero (which is the default) disables the jitter buffer. And rn, the jitter buffer is only implemented for userspace operation, so keep that in mind.
--jb-clock-driftโ turns on clock drift compensation for the jitter buffer โฐ slay.--debug-srtpโ extra log messages to help you debug SRTP issues ๐๐ญ
Per-packet details get recorded to the log โ sequence numbers, ROC, payloads (both plain text <em>and</em> encrypted), authentication tags, etc. Every RTCP packet is logged this way, while every 512th RTP packet is logged. Only applies to packets forwarded/processed in userspace.
--reject-invalid-sdpโ with this set, rtpengine refuses to process SDP bodies that couldn't be cleanly parsed, instead of skipping over the parsing error and processing the SDP anyway ๐ โโ๏ธ
Currently this only affects the processing of SDP bodies that end in a blank line. That's the whole scope, periodt.
--listen-http=[IP|HOSTNAME:]PORT--listen-https=[IP|HOSTNAME:]PORT
Turn on listening for HTTP or WebSocket connections, or their TLS-secured counterparts HTTPS and WSS ๐ฆ If you don't specify an interface, the listening socket gets bound to all interfaces.
The HTTP listener supports both HTTP and WS, while the HTTPS listener supports both HTTPS and WSS. Two-for-one, we love efficiency ๐
If HTTPS/WSS is enabled, you must also provide a certificate using the options below. ๐
--https-cert=FILE--https-key=FILEโ provide a server certificate and its corresponding private key for the HTTPS/WSS listener, in PEM format ๐--http-threads=INTโ number of worker threads for HTTP/HTTPS/WS/WSS ๐
If you don't specify it, then the same number as given under num-threads gets used. And if <em>no</em> HTTP listeners are enabled, then no threads are created at all. Efficient.
--software-id=STRINGโ sets a free-form string that identifies this software towards external systems, for example in outgoing ICE/STUN requests โจ
Defaults to rtpengine-VERSION. And the string gets sanitised โ every non-alphanumeric character gets replaced with a dash โ so it's universally usable no matter what you throw at it. ๐
--dtx-delay=INTโ processing delay in milliseconds to handle discontinuous transmission (DTX) or other transmission gaps ๐
Defaults to zero (disabled) and only applies to transcoded audio streams. When you enable it, it delays processing of received packets for the specified amount of time (much like a jitter buffer) so that DTX handling gets triggered when a transmission gap happens. The decoder is then instructed to fill in the missing time during a transmission gap โ for example by generating comfort noise. Pro tip from the manpage: the delay should be configured to be higher than the expected incoming jitter ๐ซถ
--max-dtx=INTโ maximum duration for DTX handling, in seconds โณ
If no further RTP media is received within that time frame, DTX processing stops. You can set it to zero or negative to disable that limit and keep DTX processing on indefinitely. Defaults to 30 seconds.
--dtx-buffer=INT,--dtx-lag=INTโ bestie these two are a duo, you set them together and they cap how much stuff is allowed to pile up in the DTX buffer ๐.--dtx-bufferlimits the number of packets chillin' in the DTX buffer, and--dtx-laglimits the amount of audio (in milliseconds, duh) held in there. A DTX buffer overflow gets declared when both limits are blown past, and when that happens DTX processing gets sped up bydtx-shiftmilliseconds ๐โโ๏ธ๐จ. The defaults are 10 packets and 100 milliseconds.--dtx-shift=INTโ how many milliseconds DTX processing gets shifted forward (sped up โฉ) or backwards (delayed โช) when there's a DTX buffer overflow or underflow. An underflow = RTP packets arriving slower than expected ๐ข, and an overflow = packets arriving faster than expected ๐. If you set this value to zero then no adjustments of the DTX timer happen at all โ instead, to keep up with the flow of received RTP packets, packets get dropped or extra DTX audio gets generated as needed ๐ค.--dtx-cn-params=INTโ specify one comfort noise parameter โจ. This option follows the exact same format ascn-payloaddescribed below, okurrr. It applies to audio generated to fill in transmission gaps during a DTX event. Default setting is no value, which means silence gets generated to fill in those DTX gaps ๐คซ. If any CN parameters are configured, they get passed to an RFC 3389 CN decoder, and the generated comfort noise is used to fill in the DTX gaps ๐.--amr-dtx=native|CNโ pick the DTX behaviour for AMR codecs ๐ง. The default is to use the codec's internal processing: during a DTX event, a"no data"frame gets passed to the decoder and the output is used as audio data. If you pickCNhere, AMR uses the same DTX mechanism as every other codec โ filling DTX gaps with either silence or RFC 3389 comfort noise (peepdtx-cn-params๐). This also changes how received SID frames are processed: SID frames would not be passed to the codec, and instead get replaced by generated silence or comfort noise ๐ .--silence-detect=FLOATโ turn on silence detection and set the threshold as a percent ๐. Only applies to transcoded streams, and defaults to zero (aka disabled, she's off duty). When enabled, silence detection runs on all transcoded audio streams. The threshold is basically the sensitivity dial: higher thresholds = more audio flagged as silence, lower thresholds = less audio flagged as silence. It's a percent between zero and 100. Set it to 100 and literally all audio gets detected as silence; set it to 50 and any audio quieter than 50% of the maximum volume gets flagged as silence; and so on, you get the vibe ๐ธ. Setting it to zero disables silence detection. If you only wanna catch silence that's very near or exactly absolute silence, set this to a low number like0.01. (For certain codecs such as PCMA, a higher minimum threshold is needed to detect complete silence, because their compressed payloads don't decode to actual silence โ they decode to a residual DC offset instead ๐ญ. For PCMA the minimum value is0.013.) Audio detected as silence gets replaced by comfort noise as specified by thecn-payloadoption (see below โฌ๏ธ). Currently this only works for RTP peers that have advertised support for the CN RTP payload type, in which case those silence audio frames get swapped out for CN RTP frames ๐ฆ.--cn-payload=INTโ specify one comfort noise parameter ๐. This one can be given multiple times and the format follows RFC 3389. On the command line you list the--cn-payload=option multiple times, each one specifying a single CN parameter. In the config file though, you list the option only a single time and separate multiple CN parameters with semicolons (e.g.cn-payload = 20;40;60) ๐. The first CN payload value is the noise level, specified as-dBovper RFC 3389 โ so a noise level of zero means maximum volume ๐, and higher numbers mean lower volumes ๐ค. The highest allowable number is 127, corresponding to-127 dBov, which is near silence ๐ค. Subsequent CN payload values carry spectral information (reflection coefficients) per RFC 3389. Allowable values for each coefficient are between 0 and 254. Specifying spectral info is optional and the number of coefficients listed (the model order) is variable ๐๏ธ. This option only applies to CN packets generated from the silence detection mechanism described above. The configured CN parameters get used directly as the payload of CN packets sent byrtpengineโ๏ธ. Defaults are 32 (-32 dBov) for the noise level and no spectral information.--poller-per-threadโ turns on the 'poller per thread' functionality โจ: for every worker thread (check the--num-threadsoption) a poller gets created. With this on, it's guaranteed that only a single thread will ever read from a particular socket, which keeps the order of the packets intact ๐. Might help you out when you're having issues with DTMF packets (RFC 2833) ๐.--dtls-cert-cipher=prime256v1|RSAโ pick the type of key used for the signature on the self-signed certificate used for DTLS ๐. The previous default wasRSA. The current default โ and the only other option โ isprime256v1, which is a 256-bit elliptic-curve key ๐.--dtls-signature=SHA-256|SHA-1โ pick the hash algorithm used for the signature on the self-signed certificate used for DTLS ๐. Default isSHA-256. Do not confuse this with the hash algorithm used for the certificate fingerprint that gets inserted into the SDP (a=fingerprint:), because that one is independent of the certificate's signature and can be selected during runtime โ ๏ธ.--dtls-rsa-key-size=INTโ size in bits of the RSA key used by the DTLS certificate, if RSA is in use ๐๏ธ. Default is 2048 bits.--dtls-ciphers=STRINGโ the ciphers allowed during the DTLS key exchange (and no, not to be confused with the cipher used by the DTLS certificate, bestie ๐). The format of this string is an OpenSSL cipher list. The default isDEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK๐ฅ.--dtls-mtu=INTโ set the DTLS MTU to enable fragmenting of large DTLS packets ๐ฆ. Defaults to 1200. Minimum value is 576, because the internet protocol requires that hosts must be able to process IP datagrams of at least 576 bytes (for IPv4) or 1280 bytes (for IPv6) ๐ง. This does not preclude link layers with an MTU smaller than this minimum MTU from conveying IP data. Internet IPv4 path MTU is 68 bytes.--mqtt-host=HOST|IPโ host or IP address of the Mosquitto broker to connect to ๐ก. Must be set to enable exporting stats to Mosquitto.--mqtt-port=INTโ port of the Mosquitto broker. Defaults to 1883.--mqtt-id=STRINGโ client ID to use for Mosquitto ๐ท๏ธ. Default is a generated random string ๐ฒ.--mqtt-keepalive=INTโ keepalive interval in seconds โฑ๏ธ. Defaults to 30.--mqtt-user=USERNAME,--mqtt-pass=PASSWORDโ credentials for connecting to the Mosquitto broker ๐. At least a username must be given to enable authentication.--mqtt-cafile=FILE,--mqtt-capath=PATH,--mqtt-certfile=FILE,--mqtt-keyfile=FILEโ enable TLS to connect to the Mosquitto broker, optionally with client certificate authentication ๐โจ. At leastcafileorcapathmust be given to enable TLS. To enable client certificate authentication, bothcertfileandkeyfilemust be set. All files must be in PEM format. Password-protected files are not supported ๐ซ (keep those secrets unlocked, bestie).--mqtt-publish-qos=0|1|2โ QoS value to use for publishing to Mosquitto ๐ฎ. See the Mosquitto docs for the details, iykyk.--mqtt-publish-topic=STRINGโ topic string to use for publishing to Mosquitto ๐ข. Must be set to a non-empty string, no blank slates allowed ๐ .--mqtt-publish-interval=MILLISECONDSโ interval in milliseconds to publish to Mosquitto โณ. Defaults to 5000 (that's 5 seconds, cutie).--mqtt-publish-scope=global|call|mediaโ set toglobaland one message gets published to Mosquitto everyintervalmilliseconds containing global stats plus a list of all running calls with stats for each call ๐. Set tocalland one message per call gets published with stats for that call everyintervalmilliseconds, plus one message everyintervalmilliseconds with global stats ๐. Set tomediaand one message per call media (usually one media per call participant, so usually 2 media per call) gets published with stats for that call media everyintervalmilliseconds, plus one message everyintervalmilliseconds with global stats ๐ฅ.--mos=CQ|LQโ the MOS calculation formula ๐. Defaults toCQ(conversational quality) which takes RTT into account and therefore requires peers to correctly send RTCP. If set toLQ(listening quality) then RTT is ignored, which lets a MOS be calculated even when there's no RTCP ๐ฆ.--socket-cpu-affinity=INTโ enables setting the socket CPU affinity via theSO_INCOMING_CPUsocket option, if it's available ๐ง โ๏ธ. The default value is zero, which disables this feature. If set to a positive number then the CPU affinity for all sockets belonging to the same call gets set to the same value. The number specifies the upper limit of the affinity to be set, and the values get used in a round-robin fashion (e.g. if set to 8 then the values 0 through 7 get used to set the affinity ๐). If this option is set to a negative number, then the number of available CPU cores gets used ๐.
๐ธ INTERFACES ๐ธ
ok so bestie, the -i / --interface command-line option (or, same energy, the interface config file option) is how you tell RTP where your local network interfaces live. You need at least one โ that's not optional, babes โ but you can totally stack multiples. The value format is:
[NAME/]IP[!IP]
and IP can be any of these divas:
- an IPv4 address
- an IPv6 address
- the name of a system network interface (like
eth0) - a DNS host name (like
test.example.com) - or the literal word
any
Now don't get it twisted ๐
โ configuring a network interface by name is a whole different thing from the logical interface name that rtpengine uses internally (more on that below). In the [NAME/]IP[!IP] syntax, NAME is the internal logical interface name. The system network interface name goes where the first IP token goes. So for example, if you want a logical interface called int using all the addresses off your existing eth0, you write:
int/eth0
(and unless you leave it out, that second IP token โ the advertised address โ has to be a real network address, it cannot be an interface name, no exceptions bestie.)
If you use DNS host names instead of addresses or interface names, the lookup happens only once during daemon start-up. One and done. โจ
The special keyword any means "listen on literally every available local interface address except loopback devices." Lowkey iconic. But you should only give any once, in place of a more explicit interface configuration. Don't be greedy.
To configure multiple interfaces from the command line, just drop multiple -i or --interface options. But when you're in the config file? Only use a single interface line, and separate the values with semicolons, like:
interface = internal/12.23.34.45;external/23.34.45.54
Here's where it gets spicy ๐ถ๏ธ โ if you give an interface option using a system interface name instead of a network address, and that system interface has multiple network addresses on it, rtpengine acts like you handed it multiple --interface options. Example time: say eth0 exists with both 192.168.1.120 and 2001:db8:85a3::7334 configured on it, and you run --interface=ext/eth0. Then rtpengine behaves exactly as if you'd given both --interface=ext/192.168.1.120 and --interface=ext/2001:db8:85a3::7334. It's giving "two for one." ๐ซถ
That second IP address, the one after the exclamation point, is optional โ and it's for when the address you want to advertise in outgoing SDP bodies should be different from your actual local address. Super useful in certain situations, like when your SIP proxy is chilling behind NAT. For example:
--interface=10.65.76.2!192.0.2.4
means 10.65.76.2 is the real local address on the server, but outgoing SDP bodies should advertise 192.0.2.4 as the address endpoints should talk to. โ ๏ธ Heads up though: you may have to escape that exclamation point from your shell when using command-line options, like \!.
Naming an interface (slash-separated from the address) is optional. If you skip it, the name default is used. Names are the move when you want to create logical interfaces made of one or more local addresses. That way you can tell rtpengine to use particular interfaces while processing an SDP message, i.e. use different local addresses when talking to different endpoints. The most common use case? Bridging between one or more private IP networks and the public internet. Classic.
Say, for example, clients from a private IP network need to communicate their RTP with the local address 10.35.2.75, while clients from the public internet need to hit your other local address 192.0.2.67. You'd make one logical interface pub and a second one priv:
--interface=pub/192.0.2.67 --interface=priv/10.35.2.75
Then you use the direction option to tell rtpengine which local address to use for which endpoints (either pub or priv). Chef's kiss ๐
If multiple logical interfaces are configured but the direction option isn't given in a particular call, then the first interface given on the command line gets used. First come, first served, periodt.
You can also specify multiple addresses for the same logical interface (same name). Most commonly that's one IPv4 address and one IPv6 address, like:
--interface=192.168.63.1 --interface=fe80::800:27ff:fe00:0
In that example no interface name is given, so both addresses get added to a logical interface named default. You'd then use the address family option to tell rtpengine which address to use in a particular case. ๐
And yes, you can have multiple addresses of the same family on one logical network interface. In that case, the first address (of a particular family) given for an interface becomes the primary address rtpengine uses for most purposes. Any extra addresses get advertised as additional ICE candidates with increasingly lower priority. This is great on multi-homed systems and lets endpoints pick the best possible path to reach the RTP proxy. If ICE isn't being used, those extra addresses just go unused โ even though ports would still get allocated on those interfaces. ๐ (wasteful but valid)
Another option: give interface names in the format BASE:SUFFIX. This lets interfaces be used in a round-robin fashion, which is useful for load-balancing the port ranges of multiple interfaces. Consider this config:
--interface=pub:1/192.0.2.67 --interface=pub:2/10.35.2.75
These two interfaces can still be referenced directly by name (e.g. direction=pub:1), but now you can also reference just the base name (direction=pub). If you use the base name, one of the two interfaces is selected round-robin style โ and only if that interface actually has enough open ports available. This effectively increases the number of available media ports across multiple IP addresses. There's no limit on how many interfaces can share the same base name. Go off. ๐ฅ
You can combine BASE:SUFFIX notation with multiple addresses for the same interface name too. Here's an advanced example (config file notation, actual network addresses omitted):
interface = pub:1/IPv4 pub:1/IPv4 pub:1/IPv6 pub:2/IPv4 pub:2/IPv6 pub:3/IPv6 pub:4/IPv4
In this example, when direction=pub and IPv4 is needed as a primary address, either pub:1, pub:2, or pub:4 might be selected. When pub:1 is selected, one IPv4 and one IPv6 address will be used as additional ICE alternatives. For pub:2, only one IPv6 is used as an ICE alternative, and for pub:4 no alternatives would be used. When IPv6 is needed as a primary address, either pub:1, pub:2, or pub:3 might be selected. And if at any given time not enough ports are available on any interface, it won't be selected by the round-robin algorithm. No ports, no party. ๐
You can use the round-robin algorithm even when direction isn't given. If the first given interface has the BASE:SUFFIX format, then the round-robin algorithm kicks in and selects interfaces with the same BASE name. โจ
One last thing, and this one's important: if you're not using the NG protocol but rather the legacy UDP protocol used by the rtpproxy module, then the interfaces must be named internal and external, corresponding to the i and e flags, if you want network bridging in this mode. Old school rules, bestie.
๐ EXIT STATUS ๐
0โ Successful termination. We love a happy ending. ๐1โ An error occurred. ๐ญ
๐ง ENVIRONMENT ๐ง
RTPENGINE_REDIS_AUTH_PWโ Redis server password for persistent state storage.RTPENGINE_REDIS_WRITE_AUTH_PWโ Redis server password for write operations, if--redishas been specified, in which case the one specified in--rediswill be used for read operations only.
๐ฆ FILES ๐ฆ
/etc/rtpengine/rtpengine.confโ Configuration file.
๐ EXAMPLES ๐
A typical command line (enabling both UDP and NG protocols) may look like:
rtpengine --table=0 --interface=10.64.73.31 --interface=2001:db8::4f3:3d \
--listen-udp=127.0.0.1:22222 --listen-ng=127.0.0.1:2223 --tos=184 \
--pidfile=/run/rtpengine.pid
ok that's the whole vibe. copy it, run it, slay. ๐
โญ SEE ALSO โญ
and that's a wrap on the whole page, bestie!! ๐ซถ this absolute icon of a manual page comes to you as version 10.5.3.5-1+deb12u1, dated 2026-07-20, and it's the RTPENGINE(1) page โ go forth and proxy some RTP, you've got this, mwah ๐๐
๐ฆ where this page comes from
- debian bookworm: rtpengine-daemon 10.5.3.5-1+deb12u1 ยท GPL-3.0-or-later WITH OpenSSL exception
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms