diff --git a/.github/workflows/janus-ci.yml b/.github/workflows/janus-ci.yml index 4806fedd79c..35b8a9561f5 100644 --- a/.github/workflows/janus-ci.yml +++ b/.github/workflows/janus-ci.yml @@ -76,11 +76,11 @@ jobs: if: ${{ matrix.deps_from_src == 'yes' }} uses: actions/setup-python@v2 with: - python-version: '3.x' + python-version: '3.8.5' architecture: 'x64' - name: install python packages if: ${{ matrix.deps_from_src == 'yes' }} - run: pip3 install wheel meson + run: pip3 install -Iv wheel meson==0.54.3 - name: setup libnice from sources if: ${{ matrix.deps_from_src == 'yes' }} run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index c1cf3461d23..d99e9456a58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,41 @@ All notable changes to this project will be documented in this file. +## [v0.11.1] - 2021-04-06 + +- Add new option to configure ICE nomination mode, if libnice is recent enough [[PR-2541](#2541)] +- Added support for per-session timeout values (thanks @alg!) [[PR-2577](#2577)] +- Added support for compilation on FreeBSD (thanks @jsm222!) [[PR-2508](#2508)] +- Fixed occasional auth errors when using both API secret and stored tokens (thanks @deep9!) [[PR-2581](#2581)] +- Added support for stdout logging to daemon-mode as well (thanks @mtorromeo!) [[PR-2591](#2591)] +- Fixed odr-violation issue between Lua and Duktape plugins [[PR-2540](#2540)] +- Fixed missing simulcast stats in Admin API and Event Handlers when using rid [[Issue-2610](#2610)] +- Fixed VideoRoom recording not stopped for participants entering after global recording was started [[PR-2550](#2550)] +- Fixed 'audiocodec'/'videocodec' being ignored when joining a VideoRoom via 'joinandconfigure' +- Added content type support to MESSAGE in SIP plugin (thanks @tijmenNL!) [[PR-2567](#2567)] +- Made RTSP timeouts configurable in Streaming plugin (thanks @pontscho!) [[PR-2598](#2598)] +- Fixed incorrect parsing of backend URL in WebSockets event handler [[Issue-2603](#2603)] +- Added support for secure connections and lws debugging to WebSockets event handler +- Fixed occasionally broken AV1 recordings post-processing +- Other smaller fixes and improvements (thanks to all who contributed pull requests and reported issues!) + +## [v0.10.10] - 2021-02-06 + +- Reduced verbosity of a few LOG_WARN messages at startup +- Close libnice agent resources asynchronously when hanging up PeerConnections (thanks @fbellet!) [[PR-2492](#2492)] +- Fixed broken parsing of SDP when trying to match specific codec profiles [[PR-2549](#2549)] +- Added muting/moderation API to the VideoRoom plugin [[PR-2513](#2513)] +- Fixed a few race conditions in VideoRoom plugin that could lead to crashes [[PR-2539](#2539)] +- Send 480 instead of BYE when hanging up calls in early dialog in the SIP plugin (thanks @zayim!) [[PR-2521](#2521)] +- Added configurable media direction when putting calls on-hold in the SIP plugin [[PR-2525](#2525)] +- Fixed rare race condition in AudioBridge when using "changeroom" (thanks @JeckLabs!) [[PR-2535](#2535)] +- Fixed broken API secret management in HTTP long polls (thanks @remvst!) [[PR-2524](#2524)] +- Report failure if binding to a socket fails in WebSockets transport plugin (thanks @Symbiatch!) [[PR-2534](#2534)] +- Updated RabbitMQ logic in both transport and event handler (thanks @chriswiggins!) [[PR-2430](#2430)] +- Fixed segfault in WebSocket event handler when backend was unreachable +- Added TLS support to MQTT event handler (thanks @RSATom!) [[PR-2517](#2517)] +- Other smaller fixes and improvements (thanks to all who contributed pull requests and reported issues!) + ## [v0.10.9] - 2020-12-23 - Replaced Travis CI with GitHub Actions [[PR-2486](#2486)] diff --git a/README.md b/README.md index eb508ffbff1..3c86cac1a6a 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,17 @@ If Doxygen and graphviz are available, the process can also build the documentat You can also selectively enable/disable other features (e.g., specific plugins you don't care about, or whether or not you want to build the recordings post-processor). Use the --help option when configuring for more info. +### Building on FreeBSD +* *Note*: rtp_forward of streams only works streaming to IPv6, +because of #2051 and thus the feature is not supported on FreeBSD at the moment. + +When building on FreeBSD you can install the depencencies from ports or packages, here only pkg method is used. You also need to use `gmake` instead of `make`, +since it is a GNU makefile. `./configure` can be run without arguments since the default prefix is `/usr/local` which is your default `LOCALBASE`. +Note that the `configure.ac` is coded to use openssl in base. If you wish to use openssl from ports or any other ssl you must change `configure.ac` accordingly. + + pkg install libsrtp2 libusrsctp jansson libnice libmicrohttpd libwebsockets curl opus sofia-sip libogg jansson libnice libconfig \ + libtool gmake autoconf autoconf-wrapper glib gengetopt + ### Building on MacOS While most of the above instructions will work when compiling Janus on MacOS as well, there are a few aspects to highlight when doing that. diff --git a/bower.json b/bower.json index 293e52abdf5..ae4a7e12957 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "janus-gateway", - "version": "0.10.10", + "version": "0.11.2", "homepage": "https://github.com/meetecho/janus-gateway", "authors": [ "Lorenzo Miniero ", diff --git a/conf/janus.eventhandler.wsevh.jcfg.sample b/conf/janus.eventhandler.wsevh.jcfg.sample index 38f22ff0681..c5cc364ca43 100644 --- a/conf/janus.eventhandler.wsevh.jcfg.sample +++ b/conf/janus.eventhandler.wsevh.jcfg.sample @@ -14,13 +14,18 @@ general: { # (one or more per HTTP POST, JSON array with objects) # The default is 'yes' to limit the number of connections. - # Address the plugin will send all events to as HTTP POST - # requests with an application/json payload. In case - # authentication is required to contact the backend, set - # the credentials as well (basic authentication only). json = "indented" # Whether the JSON messages should be indented (default), # plain (no indentation) or compact (no indentation and no spaces) + # Address the plugin will send all events to as WebSocket + # messages. In case authentication is required to contact + # the backend, set the credentials as well. backend = "ws://your.websocket.here" # subprotocol = "your-subprotocol" + + # In case you need to debug connection issues, you can configure + # the libwebsockets debugging level as a comma separated list of things + # to debug, supported values: err, warn, notice, info, debug, parser, + # header, ext, client, latency, user, count (plus 'none' and 'all') + #ws_logging = "err,warn" } diff --git a/conf/janus.jcfg.sample.in b/conf/janus.jcfg.sample.in index 7b9c3840623..83aea703dc1 100644 --- a/conf/janus.jcfg.sample.in +++ b/conf/janus.jcfg.sample.in @@ -239,7 +239,15 @@ media: { # configured to do full-trickle (Janus also trickles its candidates to # users) rather than the default half-trickle (Janus supports trickle # candidates from users, but sends its own within the SDP), and whether -# it should work in ICE-Lite mode (by default it doesn't). Finally, +# it should work in ICE-Lite mode (by default it doesn't). If libnice is +# at least 0.1.15, you can choose which ICE nomination mode to use: valid +# values are "regular" and "aggressive" (the default depends on the libnice +# version itself; if we can set it, we set aggressive nomination). You can +# also configure whether to use connectivity checks as keep-alives, which +# might help detecting when a peer is no longer available (notice that +# current libnice master is breaking connections after 50 seconds when +# keepalive-conncheck is being used, so if you want to use it, better +# sticking to 0.1.18 until the issue is addressed upstream). Finally, # you can also enable ICE-TCP support (beware that this may lead to problems # if you do not enable ICE Lite as well), choose which interfaces should # be used for gathering candidates, and enable or disable the @@ -249,6 +257,8 @@ nat: { #stun_port = 3478 nice_debug = false #full_trickle = true + #ice_nomination = "regular" + #ice_keepalive_conncheck = true #ice_lite = true #ice_tcp = true diff --git a/conf/janus.plugin.streaming.jcfg.sample.in b/conf/janus.plugin.streaming.jcfg.sample.in index 7eb058bea5d..fa5c2f43f61 100644 --- a/conf/janus.plugin.streaming.jcfg.sample.in +++ b/conf/janus.plugin.streaming.jcfg.sample.in @@ -74,12 +74,20 @@ # DO NOT SET THIS PROPERTY IF YOU DON'T KNOW WHAT YOU'RE DOING! # e2ee = true # -# The following options are only valid for the 'rstp' type: +# The following options are only valid for the 'rtsp' type: # url = RTSP stream URL (only for restreaming RTSP) # rtsp_user = RTSP authorization username (only if type=rtsp) # rtsp_pwd = RTSP authorization password (only if type=rtsp) -# rtspiface = network interface or IP address to bind to, if any (binds to all otherwise), when receiving RTSP streams # rtsp_failcheck = whether an error should be returned if connecting to the RTSP server fails (default=true) +# rtspiface = network interface or IP address to bind to, if any (binds to all otherwise), when receiving RTSP streams +# rtsp_reconnect_delay = after n seconds passed and no media assumed, the RTSP server has gone and schedule a reconnect (default=5s) +# rtsp_session_timeout = by default the streaming plugin will check the RTSP connection with an OPTIONS query, +# the value of the timeout comes from the RTSP session initializer and by default +# this session timeout is the half of this value In some cases this value can be too high (for example more than one minute) +# because of the media server. In that case this plugin will calculate the timeout with this +# formula: timeout = min(session_timeout, rtsp_session_timeout / 2). (default=0s) +# rtsp_timeout = communication timeout (CURLOPT_TIMEOUT) for cURL call gathering the RTSP information (default=10s) +# rtsp_conn_timeout = connection timeout for cURL (CURLOPT_CONNECTTIMEOUT) call gathering the RTSP information (default=5s) # # Notice that, for 'rtsp' mountpoints, normally the plugin uses the exact # SDP rtpmap and fmtp attributes the remote camera or RTSP server sent. @@ -249,4 +257,8 @@ file-ondemand-sample: { #rtsp_user = "username" #rtsp_pwd = "password" #secret = "adminpwd" + #reconnect_delay = 5 + #session_timeout = 0 + # rtsp_timeout = 10 + # rtsp_conn_timeout = 5 #} diff --git a/config.c b/config.c index 4eb553df531..f27210cd247 100644 --- a/config.c +++ b/config.c @@ -188,7 +188,7 @@ janus_config *janus_config_parse(const char *config_file) { /* Open file */ FILE *file = fopen(config_file, "rt"); if(!file) { - JANUS_LOG(LOG_ERR, " -- Error reading configuration file '%s'... error %d (%s)\n", filename, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, " -- Error reading configuration file '%s'... error %d (%s)\n", filename, errno, g_strerror(errno)); g_free(tmp_filename); return NULL; } diff --git a/configure.ac b/configure.ac index c75c6b98de2..d6f1394c194 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([Janus WebRTC Server],[0.10.10],[https://github.com/meetecho/janus-gateway],[janus-gateway],[https://janus.conf.meetecho.com]) +AC_INIT([Janus WebRTC Server],[0.11.2],[https://github.com/meetecho/janus-gateway],[janus-gateway],[https://janus.conf.meetecho.com]) AC_LANG(C) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_MACRO_DIR([m4]) @@ -61,6 +61,11 @@ clang*) -Wno-cast-align \ -Wno-initializer-overrides" ;; +cc*) + CFLAGS="$CFLAGS \ + -Wno-cast-align \ + -Wno-initializer-overrides" +;; *) # Specific gcc flags CFLAGS="$CFLAGS \ @@ -70,9 +75,9 @@ clang*) -Wunused-but-set-variable" esac -JANUS_VERSION=110 +JANUS_VERSION=112 AC_SUBST(JANUS_VERSION) -JANUS_VERSION_STRING="0.10.10" +JANUS_VERSION_STRING="0.11.2" AC_SUBST(JANUS_VERSION_STRING) case "$host_os" in @@ -83,6 +88,12 @@ darwin*) LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/local/opt/openssl/lib -L/opt/local/lib -L/usr/local/libsrtp/lib" AM_CONDITIONAL([DARWIN_OS], true) ;; +freebsd*) + CFLAGS="$CFLAGS -I/usr/include/openssl" + LDFLAGS="$LDFLAGS -Xlinker --export-dynamic" + LDFLAGS="$LDFLAGS -L/usr/lib/openssl -lcrypto -lssl -L/usr/local/lib" + AM_CONDITIONAL([DARWIN_OS], false) +;; *) LDFLAGS="$LDFLAGS -Wl,--export-dynamic" AM_CONDITIONAL([DARWIN_OS], false) @@ -337,17 +348,27 @@ AC_ARG_ENABLE([systemd-sockets], [], [enable_systemd_sockets=no]) -PKG_CHECK_MODULES([JANUS], - [ - glib-2.0 >= $glib_version +case "$host_os" in +freebsd*) + PKGCHECKMODULES="glib-2.0 >= $glib_version + gio-2.0 >= $glib_version + libconfig + nice + jansson >= $jansson_version + zlib" +;; +*) + PKGCHECKMODULES="glib-2.0 >= $glib_version gio-2.0 >= $glib_version libconfig nice jansson >= $jansson_version libssl >= $ssl_version libcrypto - zlib - ]) + zlib" +esac +PKG_CHECK_MODULES([JANUS],"$PKGCHECKMODULES") + JANUS_MANUAL_LIBS="${JANUS_MANUAL_LIBS} -lm" AC_SUBST(JANUS_MANUAL_LIBS) @@ -402,6 +423,12 @@ AC_CHECK_LIB([nice], [AC_MSG_NOTICE([libnice version does not have nice_agent_close_async])] ) +AC_CHECK_LIB([nice], + [nice_agent_new_full], + [AC_DEFINE(HAVE_ICE_NOMINATION)], + [AC_MSG_NOTICE([libnice version does not have nice_agent_new_full])] + ) + AC_CHECK_LIB([dl], [dlopen], [JANUS_MANUAL_LIBS="${JANUS_MANUAL_LIBS} -ldl"], diff --git a/docs/janus-doxygen.cfg b/docs/janus-doxygen.cfg index 55c6036de74..03c6f616f90 100644 --- a/docs/janus-doxygen.cfg +++ b/docs/janus-doxygen.cfg @@ -38,7 +38,7 @@ PROJECT_NAME = "Janus" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.10.10 +PROJECT_NUMBER = 0.11.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/events/janus_gelfevh.c b/events/janus_gelfevh.c index a3c2b8ecd38..3c243974c6a 100644 --- a/events/janus_gelfevh.c +++ b/events/janus_gelfevh.c @@ -164,14 +164,14 @@ static int janus_gelfevh_connect(void) { janus_network_address_to_string_buffer(&addr, &addr_buf) != 0) { if(res) freeaddrinfo(res); - JANUS_LOG(LOG_ERR, "Could not resolve address (%s): %d (%s)\n", backend, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "Could not resolve address (%s): %d (%s)\n", backend, errno, g_strerror(errno)); return -1; } char *host = g_strdup(janus_network_address_string_from_buffer(&addr_buf)); freeaddrinfo(res); if((sockfd = socket(AF_INET, transport, 0)) < 0 ) { - JANUS_LOG(LOG_ERR, "Socket creation failed: %d (%s)\n", errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "Socket creation failed: %d (%s)\n", errno, g_strerror(errno)); g_free(host); return -1; } @@ -206,7 +206,7 @@ static int janus_gelfevh_send(char *message) { while(length > 0) { out_bytes = send(sockfd, buffer, length + 1, 0); if(out_bytes <= 0) { - JANUS_LOG(LOG_WARN, "Sending TCP message failed, dropping event: %d (%s)\n", errno, strerror(errno)); + JANUS_LOG(LOG_WARN, "Sending TCP message failed, dropping event: %d (%s)\n", errno, g_strerror(errno)); close(sockfd); return -1; } @@ -240,7 +240,7 @@ static int janus_gelfevh_send(char *message) { if(total == 1) { int n = send(sockfd, buf, len, 0); if(n < 0) { - JANUS_LOG(LOG_WARN, "Sending UDP message failed, dropping event: %d (%s)\n", errno, strerror(errno)); + JANUS_LOG(LOG_WARN, "Sending UDP message failed, dropping event: %d (%s)\n", errno, g_strerror(errno)); return -1; } return 0; @@ -262,7 +262,7 @@ static int janus_gelfevh_send(char *message) { buf += bytesToSend; int n = send(sockfd, head, bytesToSend + 12, 0); if(n < 0) { - JANUS_LOG(LOG_WARN, "Sending UDP message failed: %d (%s)\n", errno, strerror(errno)); + JANUS_LOG(LOG_WARN, "Sending UDP message failed: %d (%s)\n", errno, g_strerror(errno)); return -1; } offset += bytesToSend; diff --git a/events/janus_mqttevh.c b/events/janus_mqttevh.c index 7be87a6eb71..14327f42f62 100644 --- a/events/janus_mqttevh.c +++ b/events/janus_mqttevh.c @@ -929,7 +929,7 @@ static int janus_mqttevh_init(const char *config_path) { item = janus_config_get(config, config_general, janus_config_type_item, "ssl_cacert"); } if(item && item->value) { - ctx->tls.cacert_file = g_strdup(item->value); + ctx->tls.cacert_file = janus_make_absolute_path(config_path, item->value); } item = janus_config_get(config, config_general, janus_config_type_item, "tls_client_cert"); @@ -937,14 +937,14 @@ static int janus_mqttevh_init(const char *config_path) { item = janus_config_get(config, config_general, janus_config_type_item, "ssl_client_cert"); } if(item && item->value) { - ctx->tls.cert_file = g_strdup(item->value); + ctx->tls.cert_file = janus_make_absolute_path(config_path, item->value); } item = janus_config_get(config, config_general, janus_config_type_item, "tls_client_key"); if(!item) { item = janus_config_get(config, config_general, janus_config_type_item, "ssl_client_key"); } if(item && item->value) { - ctx->tls.key_file = g_strdup(item->value); + ctx->tls.key_file = janus_make_absolute_path(config_path, item->value); } item = janus_config_get(config, config_general, janus_config_type_item, "tls_verify_peer"); if(!item) { diff --git a/events/janus_wsevh.c b/events/janus_wsevh.c index 6607a27185b..f0eb16f3507 100644 --- a/events/janus_wsevh.c +++ b/events/janus_wsevh.c @@ -104,10 +104,50 @@ static struct janus_json_parameter tweak_parameters[] = { #define JANUS_WSEVH_ERROR_INVALID_ELEMENT 413 #define JANUS_WSEVH_ERROR_UNKNOWN_ERROR 499 +/* Logging */ +static int wsevh_log_level = 0; +static const char *janus_wsevh_get_level_str(int level) { + switch(level) { + case LLL_ERR: + return "ERR"; + case LLL_WARN: + return "WARN"; + case LLL_NOTICE: + return "NOTICE"; + case LLL_INFO: + return "INFO"; + case LLL_DEBUG: + return "DEBUG"; + case LLL_PARSER: + return "PARSER"; + case LLL_HEADER: + return "HEADER"; + case LLL_EXT: + return "EXT"; + case LLL_CLIENT: + return "CLIENT"; + case LLL_LATENCY: + return "LATENCY"; +#if (LWS_LIBRARY_VERSION_MAJOR >= 2 && LWS_LIBRARY_VERSION_MINOR >= 2) || (LWS_LIBRARY_VERSION_MAJOR >= 3) + case LLL_USER: + return "USER"; +#endif + case LLL_COUNT: + return "COUNT"; + default: + return NULL; + } +} +static void janus_wsevh_log_emit_function(int level, const char *line) { + /* FIXME Do we want to use different Janus debug levels according to the level here? */ + JANUS_LOG(LOG_INFO, "[libwebsockets][wsevh][%s] %s", janus_wsevh_get_level_str(level), line); +} + /* WebSockets properties */ static char *backend = NULL; -static const char *protocol = NULL, *address = NULL, *path = NULL; +static const char *protocol = NULL, *address = NULL; +static char path[256]; static int port = 0; static struct lws_context *context = NULL; static gint64 disconnected = 0; @@ -243,6 +283,55 @@ int janus_wsevh_init(const char *config_path) { } } + item = janus_config_get(config, config_general, janus_config_type_item, "ws_logging"); + if(item && item->value) { + /* libwebsockets uses a mask to set log levels, as documented here: + * https://libwebsockets.org/lws-api-doc-master/html/group__log.html */ + if(strstr(item->value, "none")) { + /* Disable libwebsockets logging completely (the default) */ + } else if(strstr(item->value, "all")) { + /* Enable all libwebsockets logging */ + wsevh_log_level = LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | + LLL_DEBUG | LLL_PARSER | LLL_HEADER | LLL_EXT | +#if (LWS_LIBRARY_VERSION_MAJOR >= 2 && LWS_LIBRARY_VERSION_MINOR >= 2) || (LWS_LIBRARY_VERSION_MAJOR >= 3) + LLL_CLIENT | LLL_LATENCY | LLL_USER | LLL_COUNT; +#else + LLL_CLIENT | LLL_LATENCY | LLL_COUNT; +#endif + } else { + /* Only enable some of the properties */ + if(strstr(item->value, "err")) + wsevh_log_level |= LLL_ERR; + if(strstr(item->value, "warn")) + wsevh_log_level |= LLL_WARN; + if(strstr(item->value, "notice")) + wsevh_log_level |= LLL_NOTICE; + if(strstr(item->value, "info")) + wsevh_log_level |= LLL_INFO; + if(strstr(item->value, "debug")) + wsevh_log_level |= LLL_DEBUG; + if(strstr(item->value, "parser")) + wsevh_log_level |= LLL_PARSER; + if(strstr(item->value, "header")) + wsevh_log_level |= LLL_HEADER; + if(strstr(item->value, "ext")) + wsevh_log_level |= LLL_EXT; + if(strstr(item->value, "client")) + wsevh_log_level |= LLL_CLIENT; + if(strstr(item->value, "latency")) + wsevh_log_level |= LLL_LATENCY; +#if (LWS_LIBRARY_VERSION_MAJOR >= 2 && LWS_LIBRARY_VERSION_MINOR >= 2) || (LWS_LIBRARY_VERSION_MAJOR >= 3) + if(strstr(item->value, "user")) + wsevh_log_level |= LLL_USER; +#endif + if(strstr(item->value, "count")) + wsevh_log_level |= LLL_COUNT; + } + } + if(wsevh_log_level > 0) + JANUS_LOG(LOG_INFO, "WebSockets event handler libwebsockets logging: %d\n", wsevh_log_level); + lws_set_log_level(wsevh_log_level, janus_wsevh_log_emit_function); + /* Which events should we subscribe to? */ item = janus_config_get(config, config_general, janus_config_type_item, "events"); if(item && item->value) @@ -261,17 +350,21 @@ int janus_wsevh_init(const char *config_path) { JANUS_LOG(LOG_FATAL, "Missing WebSockets backend\n"); goto error; } - if(lws_parse_uri(backend, &protocol, &address, &port, &path)) { + const char *p = NULL; + if(lws_parse_uri(backend, &protocol, &address, &port, &p)) { JANUS_LOG(LOG_FATAL, "Error parsing address\n"); goto error; } - if(strcasecmp(protocol, "ws") || !strlen(address)) { - JANUS_LOG(LOG_FATAL, "Invalid address (only ws:// addresses are supported)\n"); + if((strcasecmp(protocol, "ws") && strcasecmp(protocol, "wss")) || !strlen(address)) { + JANUS_LOG(LOG_FATAL, "Invalid address (only ws:// and wss:// addresses are supported)\n"); JANUS_LOG(LOG_FATAL, " -- Protocol: %s\n", protocol); JANUS_LOG(LOG_FATAL, " -- Address: %s\n", address); - JANUS_LOG(LOG_FATAL, " -- Path: %s\n", path); + JANUS_LOG(LOG_FATAL, " -- Path: %s\n", p); goto error; } + path[0] = '/'; + if(strlen(p) > 1) + g_strlcpy(path + 1, p, sizeof(path)-2); /* Before connecting, let's check if the server expects a subprotocol */ item = janus_config_get(config, config_general, janus_config_type_item, "subprotocol"); if(item && item->value) @@ -279,31 +372,31 @@ int janus_wsevh_init(const char *config_path) { /* Connect */ JANUS_LOG(LOG_VERB, "WebSocketsEventHandler: Connecting to WebSockets server...\n"); - struct lws_context_creation_info info; - memset(&info, 0, sizeof(info)); + gboolean secure = !strcasecmp(protocol, "wss"); + struct lws_context_creation_info info = { 0 }; info.port = CONTEXT_PORT_NO_LISTEN; info.protocols = protocols; info.gid = -1; info.uid = -1; - info.options = 0; + if(secure) + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; context = lws_create_context(&info); if(context == NULL) { JANUS_LOG(LOG_FATAL, "Creating libwebsocket context failed\n"); goto error; } - struct lws_client_connect_info i; - memset(&i, 0, sizeof(i)); + struct lws_client_connect_info i = { 0 }; i.host = address; i.origin = address; i.address = address; i.port = port; i.path = path; i.context = context; - i.ssl_connection = 0; + if(secure) + i.ssl_connection = 1; i.ietf_version_or_minus_one = -1; i.client_exts = exts; i.protocol = protocols[0].name; - i.method = NULL; wsi = lws_client_connect_via_info(&i); if(wsi == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing WebSocket connection\n"); diff --git a/html/janus.js b/html/janus.js index a3a30f58d7f..66ca06cd5bc 100644 --- a/html/janus.js +++ b/html/janus.js @@ -2566,6 +2566,7 @@ function Janus(gatewayCallbacks) { callbacks = callbacks || {}; callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : webrtcError; + callbacks.customizeSdp = (typeof callbacks.customizeSdp == "function") ? callbacks.customizeSdp : Janus.noop; var jsep = callbacks.jsep; var pluginHandle = pluginHandles[handleId]; if(!pluginHandle || !pluginHandle.webrtcStuff) { @@ -2580,6 +2581,7 @@ function Janus(gatewayCallbacks) { callbacks.error("No PeerConnection: if this is an answer, use createAnswer and not handleRemoteJsep"); return; } + callbacks.customizeSdp(jsep); config.pc.setRemoteDescription(jsep) .then(function() { Janus.log("Remote description accepted!"); diff --git a/html/recordplaytest.js b/html/recordplaytest.js index 37b1ba826a8..9000740458f 100644 --- a/html/recordplaytest.js +++ b/html/recordplaytest.js @@ -171,19 +171,6 @@ $(document).ready(function() { Janus.log("The ID of the current recording is " + id); recordingId = id; } - } else if(event === 'slow_link') { - var uplink = result["uplink"]; - if(uplink !== 0) { - // Janus detected issues when receiving our media, let's slow down - bandwidth = parseInt(bandwidth / 1.5); - recordplay.send({ - message: { - request: 'configure', - 'video-bitrate-max': bandwidth, // Reduce the bitrate - 'video-keyframe-interval': 15000 // Keep the 15 seconds key frame interval - } - }); - } } else if(event === 'playing') { Janus.log("Playout has started!"); } else if(event === 'stopped') { @@ -290,6 +277,26 @@ $(document).ready(function() { } else { spinner.spin(); } + if($('#curres').length === 0) { + $('#videobox').append( + '' + + ''); + $("#video").bind("playing", function () { + var width = this.videoWidth; + var height = this.videoHeight; + $('#curres').text(width + 'x' + height); + }); + recordplay.bitrateTimer = setInterval(function() { + // Display updated bitrate, if supported + var bitrate = recordplay.getBitrate(); + $('#curbw').text(bitrate); + var video = $('#thevideo').get(0); + var width = video.videoWidth; + var height = video.videoHeight; + if(width > 0 && height > 0) + $('#curres').text(width + 'x' + height); + }, 1000); + } // Show the video, hide the spinner and show the resolution when we get a playing event $("#thevideo").bind("playing", function () { $('#waitingvideo').remove(); @@ -337,6 +344,9 @@ $(document).ready(function() { if(spinner) spinner.stop(); spinner = null; + if(recordplay.bitrateTimer) + clearInterval(recordplay.bitrateTimer); + delete recordplay.bitrateTimer; $('#videobox').empty(); $("#videobox").parent().unblock(); $('#video').hide(); diff --git a/html/videoroomtest.js b/html/videoroomtest.js index c8a533c5cfb..b73bd8cc57c 100644 --- a/html/videoroomtest.js +++ b/html/videoroomtest.js @@ -66,6 +66,8 @@ var bitrateTimer = []; var doSimulcast = (getQueryStringValue("simulcast") === "yes" || getQueryStringValue("simulcast") === "true"); var doSimulcast2 = (getQueryStringValue("simulcast2") === "yes" || getQueryStringValue("simulcast2") === "true"); +var acodec = (getQueryStringValue("acodec") !== "" ? getQueryStringValue("acodec") : null); +var vcodec = (getQueryStringValue("vcodec") !== "" ? getQueryStringValue("vcodec") : null); var subscriber_mode = (getQueryStringValue("subscriber-mode") === "yes" || getQueryStringValue("subscriber-mode") === "true"); $(document).ready(function() { @@ -426,7 +428,12 @@ function publishOwnFeed(useAudio) { // a codec will only work if: (1) the codec is actually in the SDP (and // so the browser supports it), and (2) the codec is in the list of // allowed codecs in a room. With respect to the point (2) above, - // refer to the text in janus.plugin.videoroom.jcfg for more details + // refer to the text in janus.plugin.videoroom.jcfg for more details. + // We allow people to specify a codec via query string, for demo purposes + if(acodec) + publish["audiocodec"] = acodec; + if(vcodec) + publish["videocodec"] = vcodec; sfutest.send({ message: publish, jsep: jsep }); }, error: function(error) { diff --git a/ice.c b/ice.c index 8ebaaeefe50..09bffdf1a03 100644 --- a/ice.c +++ b/ice.c @@ -101,6 +101,41 @@ gboolean janus_ice_is_ipv6_enabled(void) { return janus_ipv6_enabled; } +#ifdef HAVE_ICE_NOMINATION +/* Since libnice 0.1.15, we can configure the ICE nomination mode: it was + * always "aggressive" before, so we set it to "aggressive" by default as well */ +static NiceNominationMode janus_ice_nomination = NICE_NOMINATION_MODE_AGGRESSIVE; +void janus_ice_set_nomination_mode(const char *nomination) { + if(nomination == NULL) { + JANUS_LOG(LOG_WARN, "Invalid ICE nomination mode, falling back to 'aggressive'\n"); + } else if(!strcasecmp(nomination, "regular")) { + JANUS_LOG(LOG_INFO, "Configuring Janus to use ICE regular nomination\n"); + janus_ice_nomination = NICE_NOMINATION_MODE_REGULAR; + } else if(!strcasecmp(nomination, "aggressive")) { + JANUS_LOG(LOG_INFO, "Configuring Janus to use ICE aggressive nomination\n"); + janus_ice_nomination = NICE_NOMINATION_MODE_AGGRESSIVE; + } else { + JANUS_LOG(LOG_WARN, "Unsupported ICE nomination mode '%s', falling back to 'aggressive'\n", nomination); + } +} +const char *janus_ice_get_nomination_mode(void) { + return (janus_ice_nomination == NICE_NOMINATION_MODE_REGULAR ? "regular" : "aggressive"); +} +#endif + +/* Keepalive via connectivity checks */ +static gboolean janus_ice_keepalive_connchecks = FALSE; +void janus_ice_set_keepalive_conncheck_enabled(gboolean enabled) { + janus_ice_keepalive_connchecks = enabled; + if(janus_ice_keepalive_connchecks) { + JANUS_LOG(LOG_INFO, "Using connectivity checks as PeerConnection keep-alives\n"); + JANUS_LOG(LOG_WARN, "Notice that the current libnice master is breaking connections after 50s when keepalive-conncheck enabled. As such, better to stick to 0.1.18 until the issue is addressed upstream\n"); + } +} +gboolean janus_ice_is_keepalive_conncheck_enabled(void) { + return janus_ice_keepalive_connchecks; +} + /* Opaque IDs set by applications are by default only passed to event handlers * for correlation purposes, but not sent back to the user or application in * the related Janus API responses or events, unless configured otherwise */ @@ -683,7 +718,7 @@ static void janus_ice_notify_trickle(janus_ice_handle *handle, char *buffer) { janus_session_notify_event(session, event); } -static void janus_ice_notify_media(janus_ice_handle *handle, gboolean video, gboolean up) { +static void janus_ice_notify_media(janus_ice_handle *handle, gboolean video, int substream, gboolean up) { if(handle == NULL) return; /* Prepare JSON event to notify user/application */ @@ -699,6 +734,8 @@ static void janus_ice_notify_media(janus_ice_handle *handle, gboolean video, gbo if(opaqueid_in_api && handle->opaque_id != NULL) json_object_set_new(event, "opaque_id", json_string(handle->opaque_id)); json_object_set_new(event, "type", json_string(video ? "video" : "audio")); + if(video && handle->stream && handle->stream->video_rtcp_ctx[1] != NULL) + json_object_set_new(event, "substream", json_integer(substream)); json_object_set_new(event, "receiving", up ? json_true() : json_false()); if(!up && no_media_timer > 1) json_object_set_new(event, "seconds", json_integer(no_media_timer)); @@ -709,6 +746,8 @@ static void janus_ice_notify_media(janus_ice_handle *handle, gboolean video, gbo if(janus_events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "media", json_string(video ? "video" : "audio")); + if(video && handle->stream && handle->stream->video_rtcp_ctx[1] != NULL) + json_object_set_new(info, "substream", json_integer(substream)); json_object_set_new(info, "receiving", up ? json_true() : json_false()); if(!up && no_media_timer > 1) json_object_set_new(info, "seconds", json_integer(no_media_timer)); @@ -943,7 +982,7 @@ int janus_ice_test_stun_server(janus_network_address *addr, uint16_t port, addrlen = sizeof(remote6); } if(bind(fd, address, addrlen) < 0) { - JANUS_LOG(LOG_FATAL, "Bind failed for STUN BINDING test: %d (%s)\n", errno, strerror(errno)); + JANUS_LOG(LOG_FATAL, "Bind failed for STUN BINDING test: %d (%s)\n", errno, g_strerror(errno)); close(fd); return -1; } @@ -962,7 +1001,7 @@ int janus_ice_test_stun_server(janus_network_address *addr, uint16_t port, timeout.tv_usec = 0; int err = select(fd+1, &readfds, NULL, NULL, &timeout); if(err < 0) { - JANUS_LOG(LOG_FATAL, "Error waiting for a response to our STUN BINDING test: %d (%s)\n", errno, strerror(errno)); + JANUS_LOG(LOG_FATAL, "Error waiting for a response to our STUN BINDING test: %d (%s)\n", errno, g_strerror(errno)); close(fd); return -1; } @@ -2636,7 +2675,7 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp if(component->in_stats.audio.bytes == 0 || component->in_stats.audio.notified_lastsec) { /* We either received our first audio packet, or we started receiving it again after missing more than a second */ component->in_stats.audio.notified_lastsec = FALSE; - janus_ice_notify_media(handle, FALSE, TRUE); + janus_ice_notify_media(handle, FALSE, 0, TRUE); } /* Overall audio data */ component->in_stats.audio.packets++; @@ -2655,7 +2694,7 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp if(component->in_stats.video[vindex].bytes == 0 || component->in_stats.video[vindex].notified_lastsec) { /* We either received our first video packet, or we started receiving it again after missing more than a second */ component->in_stats.video[vindex].notified_lastsec = FALSE; - janus_ice_notify_media(handle, TRUE, TRUE); + janus_ice_notify_media(handle, TRUE, vindex, TRUE); } /* Overall video data for this SSRC */ component->in_stats.video[vindex].packets++; @@ -3461,6 +3500,10 @@ int janus_ice_setup_local(janus_ice_handle *handle, int offer, int audio, int vi "main-context", handle->mainctx, "reliable", FALSE, "full-mode", janus_ice_lite_enabled ? FALSE : TRUE, +#ifdef HAVE_ICE_NOMINATION + "nomination-mode", janus_ice_nomination, +#endif + "keepalive-conncheck", janus_ice_keepalive_connchecks ? TRUE : FALSE, #ifdef HAVE_LIBNICE_TCP "ice-udp", TRUE, "ice-tcp", janus_ice_tcp_enabled ? TRUE : FALSE, @@ -3537,7 +3580,7 @@ int janus_ice_setup_local(janus_ice_handle *handle, int offer, int audio, int vi char host[NI_MAXHOST]; if(getifaddrs(&ifaddr) == -1) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error getting list of interfaces... %d (%s)\n", - handle->handle_id, errno, strerror(errno)); + handle->handle_id, errno, g_strerror(errno)); } else { for(ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) { if(ifa->ifa_addr == NULL) @@ -3877,7 +3920,7 @@ static gboolean janus_ice_outgoing_rtcp_handle(gpointer user_data) { /* Create a SR/SDES compound */ int srlen = 28; int sdeslen = 16; - char rtcpbuf[srlen+sdeslen]; + char rtcpbuf[sizeof(janus_rtcp_sr)+sdeslen]; memset(rtcpbuf, 0, sizeof(rtcpbuf)); rtcp_sr *sr = (rtcp_sr *)&rtcpbuf; sr->header.version = 2; @@ -3903,7 +3946,7 @@ static gboolean janus_ice_outgoing_rtcp_handle(gpointer user_data) { } sr->si.s_packets = htonl(stream->component->out_stats.audio.packets); sr->si.s_octets = htonl(stream->component->out_stats.audio.bytes); - rtcp_sdes *sdes = (rtcp_sdes *)&rtcpbuf[28]; + rtcp_sdes *sdes = (rtcp_sdes *)&rtcpbuf[srlen]; janus_rtcp_sdes_cname((char *)sdes, sdeslen, "janus", 5); sdes->chunk.ssrc = htonl(stream->audio_ssrc); /* Enqueue it, we'll send it later */ @@ -3938,7 +3981,7 @@ static gboolean janus_ice_outgoing_rtcp_handle(gpointer user_data) { /* Create a SR/SDES compound */ int srlen = 28; int sdeslen = 16; - char rtcpbuf[srlen+sdeslen]; + char rtcpbuf[sizeof(janus_rtcp_sr)+sdeslen]; memset(rtcpbuf, 0, sizeof(rtcpbuf)); rtcp_sr *sr = (rtcp_sr *)&rtcpbuf; sr->header.version = 2; @@ -3964,7 +4007,7 @@ static gboolean janus_ice_outgoing_rtcp_handle(gpointer user_data) { } sr->si.s_packets = htonl(stream->component->out_stats.video[0].packets); sr->si.s_octets = htonl(stream->component->out_stats.video[0].bytes); - rtcp_sdes *sdes = (rtcp_sdes *)&rtcpbuf[28]; + rtcp_sdes *sdes = (rtcp_sdes *)&rtcpbuf[srlen]; janus_rtcp_sdes_cname((char *)sdes, sdeslen, "janus", 5); sdes->chunk.ssrc = htonl(stream->video_ssrc); /* Enqueue it, we'll send it later */ @@ -4052,17 +4095,20 @@ static gboolean janus_ice_outgoing_stats_handle(gpointer user_data) { /* We missed more than no_second_timer seconds of audio! */ component->in_stats.audio.notified_lastsec = TRUE; JANUS_LOG(LOG_WARN, "[%"SCNu64"] Didn't receive audio for more than %d seconds...\n", handle->handle_id, no_media_timer); - janus_ice_notify_media(handle, FALSE, FALSE); + janus_ice_notify_media(handle, FALSE, 0, FALSE); } /* Video */ - last = component->in_stats.video[0].updated; - if(!component->in_stats.video[0].notified_lastsec && last && - !component->in_stats.video[0].bytes_lastsec && !component->in_stats.video[0].bytes_lastsec_temp && - now-last >= (gint64)no_media_timer*G_USEC_PER_SEC) { - /* We missed more than no_second_timer seconds of video! */ - component->in_stats.video[0].notified_lastsec = TRUE; - JANUS_LOG(LOG_WARN, "[%"SCNu64"] Didn't receive video for more than a second...\n", handle->handle_id); - janus_ice_notify_media(handle, TRUE, FALSE); + int vindex=0; + for(vindex=0; vindex<3; vindex++) { + last = component->in_stats.video[vindex].updated; + if(!component->in_stats.video[vindex].notified_lastsec && last && + !component->in_stats.video[vindex].bytes_lastsec && !component->in_stats.video[vindex].bytes_lastsec_temp && + now-last >= (gint64)no_media_timer*G_USEC_PER_SEC) { + /* We missed more than no_second_timer seconds of this video stream! */ + component->in_stats.video[vindex].notified_lastsec = TRUE; + JANUS_LOG(LOG_WARN, "[%"SCNu64"] Didn't receive video #%d for more than a second...\n", handle->handle_id, vindex); + janus_ice_notify_media(handle, TRUE, vindex, FALSE); + } } } /* We also send live stats to event handlers every tot-seconds (configurable) */ diff --git a/ice.h b/ice.h index 8fe22c5c330..015e940306f 100644 --- a/ice.h +++ b/ice.h @@ -129,6 +129,22 @@ gboolean janus_ice_is_mdns_enabled(void); /*! \brief Method to check whether IPv6 candidates are enabled/supported or not * @returns true if IPv6 candidates are enabled/supported, false otherwise */ gboolean janus_ice_is_ipv6_enabled(void); +#ifdef HAVE_ICE_NOMINATION +/*! \brief Method to configure the ICE nomination mode (regular or aggressive) + * @param[in] nomination The ICE nomination mode (regular or aggressive) */ +void janus_ice_set_nomination_mode(const char *nomination); +/*! \brief Method to return a string description of the configured ICE nomination mode + * @returns "regular" or "aggressive" */ +const char *janus_ice_get_nomination_mode(void); +#endif +/*! \brief Method to enable/disable connectivity checks as keepalives for PeerConnections. + * \note The main rationale behind this setting is provided in the libnice documentation: + * https://libnice.freedesktop.org/libnice/NiceAgent.html#NiceAgent--keepalive-conncheck + * @param[in] enabled Whether the functionality should be enabled or disabled */ +void janus_ice_set_keepalive_conncheck_enabled(gboolean enabled); +/*! \brief Method to check whether connectivity checks will be used as keepalives + * @returns true if enabled, false (default) otherwise */ +gboolean janus_ice_is_keepalive_conncheck_enabled(void); /*! \brief Method to modify the min NACK value (i.e., the minimum time window of packets per handle to store for retransmissions) * @param[in] mnq The new min NACK value */ void janus_set_min_nack_queue(uint16_t mnq); @@ -140,7 +156,7 @@ uint16_t janus_get_min_nack_queue(void); * keyframe, as any missing packet won't be needed since the keyframe will allow the * media recipient to still restore a complete image anyway. Since this optimization * seems to cause some issues in some edge cases, it's disabled by default. - * @param[in] optimize Whether the opzimization should be enabled or disabled */ + * @param[in] optimize Whether the optimization should be enabled or disabled */ void janus_set_nack_optimizations_enabled(gboolean optimize); /*! \brief Method to check whether NACK optimizations on outgoing keyframes are enabled or not * @returns optimize if optimizations are enabled, false otherwise */ diff --git a/janus.1 b/janus.1 index 5eae19edd7f..ad3b36f652d 100644 --- a/janus.1 +++ b/janus.1 @@ -26,6 +26,9 @@ Open the specified PID file when starting Janus (default=none) .BR \-N ", " \-\-disable-stdout Disable stdout based logging (default=off) .TP +.BR \-L ", " \-\-log-stdout +Log to stdout, even when the process is daemonized (default=off) +.TP .BR \-L ", " \-\-log-file=\fIpath\fR Log to the specified file (default=stdout only) .TP diff --git a/janus.c b/janus.c index 7e103a15f36..149585fb5c1 100644 --- a/janus.c +++ b/janus.c @@ -124,6 +124,9 @@ static struct janus_json_parameter debug_parameters[] = { static struct janus_json_parameter timeout_parameters[] = { {"timeout", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE} }; +static struct janus_json_parameter session_timeout_parameters[] = { + {"timeout", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED} +}; static struct janus_json_parameter level_parameters[] = { {"level", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE} }; @@ -275,7 +278,7 @@ static json_t *janus_create_message(const char *status, uint64_t session_id, con * incurring in unexpected timeouts (when HTTP is used in janus.js, the * long poll is used as a keepalive mechanism). */ #define DEFAULT_SESSION_TIMEOUT 60 -static uint session_timeout = DEFAULT_SESSION_TIMEOUT; +static uint global_session_timeout = DEFAULT_SESSION_TIMEOUT; #define DEFAULT_RECLAIM_SESSION_TIMEOUT 0 static uint reclaim_session_timeout = DEFAULT_RECLAIM_SESSION_TIMEOUT; @@ -326,7 +329,7 @@ static json_t *janus_info(const char *transaction) { json_object_set_new(info, "data_channels", json_false()); #endif json_object_set_new(info, "accepting-new-sessions", accept_new_sessions ? json_true() : json_false()); - json_object_set_new(info, "session-timeout", json_integer(session_timeout)); + json_object_set_new(info, "session-timeout", json_integer(global_session_timeout)); json_object_set_new(info, "reclaim-session-timeout", json_integer(reclaim_session_timeout)); json_object_set_new(info, "candidates-timeout", json_integer(candidates_timeout)); json_object_set_new(info, "server-name", json_string(server_name ? server_name : JANUS_SERVER_NAME)); @@ -346,6 +349,10 @@ static json_t *janus_info(const char *transaction) { json_object_set_new(info, "ipv6", janus_ice_is_ipv6_enabled() ? json_true() : json_false()); json_object_set_new(info, "ice-lite", janus_ice_is_ice_lite_enabled() ? json_true() : json_false()); json_object_set_new(info, "ice-tcp", janus_ice_is_ice_tcp_enabled() ? json_true() : json_false()); +#ifdef HAVE_ICE_NOMINATION + json_object_set_new(info, "ice-nomination", json_string(janus_ice_get_nomination_mode())); +#endif + json_object_set_new(info, "ice-keepalive-conncheck", janus_ice_is_keepalive_conncheck_enabled() ? json_true() : json_false()); json_object_set_new(info, "full-trickle", janus_ice_is_full_trickle_enabled() ? json_true() : json_false()); json_object_set_new(info, "mdns-enabled", janus_ice_is_mdns_enabled() ? json_true() : json_false()); json_object_set_new(info, "min-nack-queue", json_integer(janus_get_min_nack_queue())); @@ -651,8 +658,6 @@ static void janus_request_unref(janus_request *request) { } static gboolean janus_check_sessions(gpointer user_data) { - if(session_timeout < 1 && reclaim_session_timeout < 1) /* Session timeouts are disabled */ - return G_SOURCE_CONTINUE; janus_mutex_lock(&sessions_mutex); if(sessions && g_hash_table_size(sessions) > 0) { GHashTableIter iter; @@ -664,10 +669,15 @@ static gboolean janus_check_sessions(gpointer user_data) { continue; } gint64 now = janus_get_monotonic_time(); - if ((session_timeout > 0 && (now - session->last_activity >= (gint64)session_timeout * G_USEC_PER_SEC) && - !g_atomic_int_compare_and_exchange(&session->timeout, 0, 1)) || + + /* Use either session-specific timeout or global. */ + gint64 timeout = (gint64)session->timeout; + if (timeout == -1) timeout = (gint64)global_session_timeout; + + if ((timeout > 0 && (now - session->last_activity >= timeout * G_USEC_PER_SEC) && + !g_atomic_int_compare_and_exchange(&session->timedout, 0, 1)) || ((g_atomic_int_get(&session->transport_gone) && now - session->last_activity >= (gint64)reclaim_session_timeout * G_USEC_PER_SEC) && - !g_atomic_int_compare_and_exchange(&session->timeout, 0, 1))) { + !g_atomic_int_compare_and_exchange(&session->timedout, 0, 1))) { JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id); /* Mark the session as over, we'll deal with it later */ janus_session_handles_clear(session); @@ -735,8 +745,9 @@ janus_session *janus_session_create(guint64 session_id) { session->session_id = session_id; janus_refcount_init(&session->ref, janus_session_free); session->source = NULL; + session->timeout = -1; /* Negative means rely on global timeout */ g_atomic_int_set(&session->destroyed, 0); - g_atomic_int_set(&session->timeout, 0); + g_atomic_int_set(&session->timedout, 0); g_atomic_int_set(&session->transport_gone, 0); session->last_activity = janus_get_monotonic_time(); session->ice_handles = NULL; @@ -915,7 +926,7 @@ static int janus_request_check_secret(janus_request *request, guint64 session_id } } /* We consider a request authorized if either the proper API secret or a valid token has been provided */ - if(!secret_authorized && !token_authorized) + if(!(api_secret != NULL && secret_authorized) && !(janus_auth_is_enabled() && token_authorized)) return JANUS_ERROR_UNAUTHORIZED; } return 0; @@ -1059,6 +1070,7 @@ int janus_process_incoming_request(janus_request *request) { goto jsondone; } } + /* Handle it */ session = janus_session_create(session_id); if(session == NULL) { @@ -1977,7 +1989,7 @@ int janus_process_incoming_admin_request(janus_request *request) { json_t *reply = janus_create_message("success", 0, transaction_text); json_t *status = json_object(); json_object_set_new(status, "token_auth", janus_auth_is_enabled() ? json_true() : json_false()); - json_object_set_new(status, "session_timeout", json_integer(session_timeout)); + json_object_set_new(status, "session_timeout", json_integer(global_session_timeout)); json_object_set_new(status, "reclaim_session_timeout", json_integer(reclaim_session_timeout)); json_object_set_new(status, "candidates_timeout", json_integer(candidates_timeout)); json_object_set_new(status, "log_level", json_integer(janus_log_level)); @@ -1995,7 +2007,6 @@ int janus_process_incoming_admin_request(janus_request *request) { ret = janus_process_success(request, reply); goto jsondone; } else if(!strcasecmp(message_text, "set_session_timeout")) { - /* Change the session timeout value */ JANUS_VALIDATE_JSON_OBJECT(root, timeout_parameters, error_code, error_cause, FALSE, JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE); @@ -2009,12 +2020,16 @@ int janus_process_incoming_admin_request(janus_request *request) { ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timeout should be a positive integer)"); goto jsondone; } - session_timeout = timeout_num; + + /* Set global session timeout */ + global_session_timeout = timeout_num; + /* Prepare JSON reply */ json_t *reply = json_object(); json_object_set_new(reply, "janus", json_string("success")); json_object_set_new(reply, "transaction", json_string(transaction_text)); - json_object_set_new(reply, "timeout", json_integer(session_timeout)); + json_object_set_new(reply, "timeout", json_integer(timeout_num)); + /* Send the success reply */ ret = janus_process_success(request, reply); goto jsondone; @@ -2664,12 +2679,45 @@ int janus_process_incoming_admin_request(janus_request *request) { janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE, session_id, "destroyed", NULL); goto jsondone; - } - /* If this is not a request to destroy a session, it must be a request to list the handles */ - if(strcasecmp(message_text, "list_handles")) { + } else if (!strcasecmp(message_text, "set_session_timeout")) { + /* Specific session timeout setting. */ + JANUS_VALIDATE_JSON_OBJECT(root, session_timeout_parameters, + error_code, error_cause, FALSE, + JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE); + if(error_code != 0) { + ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause); + goto jsondone; + } + + /* Positive timeout is seconds, 0 is unlimited, -1 is global session timeout */ + json_t *timeout = json_object_get(root, "timeout"); + int timeout_num = json_integer_value(timeout); + if(timeout_num < -1) { + ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timeout should be a non-negative integer or -1)"); + goto jsondone; + } + + /* Set specific session timeout */ + janus_mutex_lock(&session->mutex); + session->timeout = timeout_num; + janus_mutex_unlock(&session->mutex); + + /* Prepare JSON reply */ + json_t *reply = json_object(); + json_object_set_new(reply, "janus", json_string("success")); + json_object_set_new(reply, "transaction", json_string(transaction_text)); + json_object_set_new(reply, "timeout", json_integer(timeout_num)); + json_object_set_new(reply, "session_id", json_integer(session_id)); + + /* Send the success reply */ + ret = janus_process_success(request, reply); + goto jsondone; + } else if(strcasecmp(message_text, "list_handles")) { + /* If this is not a request to destroy a session, it must be a request to list the handles */ ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text); goto jsondone; } + /* List handles */ json_t *list = janus_session_handles_list_json(session); /* Prepare JSON reply */ @@ -2777,6 +2825,7 @@ int janus_process_incoming_admin_request(janus_request *request) { json_t *info = json_object(); json_object_set_new(info, "session_id", json_integer(session_id)); json_object_set_new(info, "session_last_activity", json_integer(session->last_activity)); + json_object_set_new(info, "session_timeout", json_integer(session->timeout == -1 ? global_session_timeout : (guint)session->timeout)); janus_mutex_lock(&session->mutex); if(session->source && session->source->transport) json_object_set_new(info, "session_transport", json_string(session->source->transport->get_package())); @@ -3225,7 +3274,7 @@ void janus_transport_gone(janus_transport *plugin, janus_transport_session *tran g_hash_table_iter_init(&iter, sessions); while(g_hash_table_iter_next(&iter, NULL, &value)) { janus_session *session = (janus_session *) value; - if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->timeout) || session->last_activity == 0) + if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->timedout) || session->last_activity == 0) continue; if(session->source && session->source->instance == transport) { JANUS_LOG(LOG_VERB, " -- Session %"SCNu64" will be over if not reclaimed\n", session->session_id); @@ -4070,7 +4119,7 @@ gint main(int argc, char *argv[]) if(args_info.disable_stdout_given) { use_stdout = FALSE; janus_config_add(config, config_general, janus_config_item_create("log_to_stdout", "no")); - } else { + } else if(!args_info.log_stdout_given) { /* Check if the configuration file is saying anything about this */ janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "log_to_stdout"); if(item && item->value && !janus_is_true(item->value)) @@ -4098,13 +4147,9 @@ gint main(int argc, char *argv[]) daemonize = TRUE; } /* If we're going to daemonize, make sure logging to stdout is disabled and a log file has been specified */ - if(daemonize && use_stdout) { + if(daemonize && use_stdout && !args_info.log_stdout_given) { use_stdout = FALSE; } - if(daemonize && logfile == NULL) { - g_print("Running Janus as a daemon but no log file provided, giving up...\n"); - exit(1); - } /* Daemonize now, if we need to */ if(daemonize) { g_print("Running Janus as a daemon\n"); @@ -4540,7 +4585,7 @@ gint main(int argc, char *argv[]) janus_network_address_string_buffer ibuf; if(getifaddrs(&ifas) == -1) { JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected... %d (%s)\n", - errno, strerror(errno)); + errno, g_strerror(errno)); } else { if(janus_network_lookup_interface(ifas, item->value, &iface) != 0) { JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value); @@ -4573,7 +4618,7 @@ gint main(int argc, char *argv[]) if(st == 0) { JANUS_LOG(LOG_WARN, "Session timeouts have been disabled (note, may result in orphaned sessions)\n"); } - session_timeout = st; + global_session_timeout = st; } } @@ -4787,6 +4832,17 @@ gint main(int argc, char *argv[]) JANUS_LOG(LOG_ERR, "Invalid STUN address %s:%u. STUN will be disabled\n", stun_server, stun_port); } } + item = janus_config_get(config, config_nat, janus_config_type_item, "ice_nomination"); + if(item && item->value) { +#ifndef HAVE_ICE_NOMINATION + JANUS_LOG(LOG_WARN, "This version of libnice doesn't support setting the ICE nomination mode, ignoring '%s'\n", item->value); +#else + janus_ice_set_nomination_mode(item->value); +#endif + } + item = janus_config_get(config, config_nat, janus_config_type_item, "ice_keepalive_conncheck"); + if(item && item->value) + janus_ice_set_keepalive_conncheck_enabled(janus_is_true(item->value)); if(janus_ice_set_turn_server(turn_server, turn_port, turn_type, turn_user, turn_pwd) < 0) { if(!ignore_unreachable_ice_server) { JANUS_LOG(LOG_FATAL, "Invalid TURN address %s:%u\n", turn_server, turn_port); diff --git a/janus.ggo b/janus.ggo index b668b08ef31..eff35103fa6 100644 --- a/janus.ggo +++ b/janus.ggo @@ -1,7 +1,8 @@ -#Janus 0.10.10 gengetopt file +#Janus 0.11.2 gengetopt file option "daemon" b "Launch Janus in background as a daemon" flag off option "pid-file" p "Open the specified PID file when starting Janus (default=none)" string typestr="path" optional option "disable-stdout" N "Disable stdout based logging" flag off +option "log-stdout" - "Log to stdout, even when the process is daemonized" flag off option "log-file" L "Log to the specified file (default=stdout only)" string typestr="path" optional option "cwd-path" H "Working directory for Janus daemon process (default=/)" string typestr="path" optional option "interface" i "Interface to use (will be the public IP)" string typestr="ipaddress" optional diff --git a/janus.h b/janus.h index 476acd23327..e86a72f2b32 100644 --- a/janus.h +++ b/janus.h @@ -53,7 +53,9 @@ typedef struct janus_session { /*! \brief Pointer to the request instance (and the transport that originated the session) */ janus_request *source; /*! \brief Flag to notify there's been a session timeout */ - volatile gint timeout; + volatile gint timedout; + /*! \brief Timeout value in seconds to use with this session, 0 is unlimited, -1 is global session timeout setting */ + gint timeout; /*! \brief Flag to notify that transport is gone */ volatile gint transport_gone; /*! \brief Mutex to lock/unlock this session */ diff --git a/log.c b/log.c index f6062d145bf..f9003cb90b8 100644 --- a/log.c +++ b/log.c @@ -230,7 +230,7 @@ int janus_log_init(gboolean daemon, gboolean console, const char *logfile) { /* Open a log file for writing (and append) */ janus_log_file = fopen(logfile, "awt"); if(janus_log_file == NULL) { - g_print("Error opening log file %s: %s\n", logfile, strerror(errno)); + g_print("Error opening log file %s: %s\n", logfile, g_strerror(errno)); return -1; } janus_log_filepath = g_strdup(logfile); @@ -239,7 +239,7 @@ int janus_log_init(gboolean daemon, gboolean console, const char *logfile) { g_print("WARNING: logging completely disabled!\n"); g_print(" (no stdout and no logfile, this may not be what you want...)\n"); } - if(daemon) { + if(daemon && !console) { /* Replace the standard file descriptors */ if (freopen("/dev/null", "r", stdin) == NULL) { g_print("Error replacing stdin with /dev/null\n"); diff --git a/loggers/janus_jsonlog.c b/loggers/janus_jsonlog.c index 07d2aafa963..94a756755bf 100644 --- a/loggers/janus_jsonlog.c +++ b/loggers/janus_jsonlog.c @@ -150,7 +150,7 @@ int janus_jsonlog_init(const char *server_name, const char *config_path) { logfile = fopen(logfilename, "a"); if(logfile == NULL) { JANUS_LOG(LOG_FATAL, "Error opening file '%s' (%d, %s)\n", - logfilename, errno, strerror(errno)); + logfilename, errno, g_strerror(errno)); } } diff --git a/mainpage.dox b/mainpage.dox index b28bed6493e..a67602e8e81 100644 --- a/mainpage.dox +++ b/mainpage.dox @@ -1385,6 +1385,16 @@ GET http://host:port/janus/?maxev=5 * destroy request, but a cleaner approach on the client side would help * nonetheless avoid potential issues. * + * Notice that a session may also be destroyed automatically in case of + * inactivity. If Janus doesn't receive any activity (requests, long polls) + * for a session for a time longer than the \c session_timeout value + * configured in \c janus.jcfg then the session will timeout, and a + * \c timeout event will be fired. If a \c reclaim_session_timeout value + * is configured, you can still reclaim the session from the same or + * a different transport using the \c claim request, within a limited + * amount of time. An unreclaimed session that has timed out will be + * permanently destroyed, and will destroy all its handles as well. + * * \section handles The plugin handle endpoint * Once you've created a plugin handle, a new endpoint you can use is created * in the server. Specifically, the new endpoint is constructed by @@ -1565,7 +1575,9 @@ GET http://host:port/janus/?maxev=5 * with the plugin, make sure it is torn down as part of this process. * The plugin implementation and the Janus core should do this * automatically, but implementing the right behaviour in clients would - * help avoid potential issues nonetheless. + * help avoid potential issues nonetheless. Notice that you may receive + * \c detached event after a handle has been detached, whether this was + * done in response to a request or automatically, in response to an event. * * If you're interested in keeping the handle alive but want to hang up * the associated PeerConnection, if available, just send a "hangup" \c janus @@ -1791,9 +1803,10 @@ var websocket = new WebSocket('ws://1.2.3.4:8188', 'janus-protocol'); * originated them. * * An \b important aspect to point out is related to keep-alive messages - * for WebSockets Janus channels. A Janus session is kept alive as long - * as there's no inactivity for 60 seconds: if no messages have been - * received in that time frame, the session is torn down by the server. + * for WebSockets Janus channels. As explained above, a Janus session is + * kept alive as long as there's no inactivity for \c session_timeout seconds: + * if no messages have been received in that time frame, the session is + * marked as timed-out and, unless reclaimed, will be torn down by the server. * A normal activity on a session is usually enough to prevent that; * for a more prolonged inactivity with respect to messaging, on plain * HTTP the session is usually kept alive through the regular long poll @@ -2263,7 +2276,7 @@ const token = getJanusToken('janus', ['janus.plugin.videoroom']), * \subsection adminreqc Configuration-related requests * - \c get_status: returns the current value for the settings that can be * modified at runtime via the Admin API (see below); - * - \c set_session_timeout: change the session timeout value in Janus; + * - \c set_session_timeout: change global session timeout value in Janus; * - \c set_log_level: change the log level in Janus; * - \c set_log_timestamps: selectively enable/disable adding a timestamp * to all log lines Janus writes on the console and/or to file; @@ -2293,6 +2306,7 @@ const token = getJanusToken('janus', ['janus.plugin.videoroom']), * you want to stop accepting new sessions because you're draining this instance; * - \c list_sessions: list all the sessions currently active in Janus * (returns an array of session identifiers); + * - \c set_session_timeout: change session timeout value in Janus; * - \c destroy_session: destroy a specific session; this behaves exactly * as the \c destroy request does in the Janus API. * @@ -3914,7 +3928,13 @@ ldd janus | grep asan * - Presentation on using Janus for Virtual Events at CommCon 2020; * - Workshop on Janus (lesson/tutorial) at ClueCon 2020; * - Presentation on E2EE (end-to-end encryption) and Insertable Streams at ClueCon 2020; - * - Presentation on SFUs and MCUs at IIT RTC 2020. \n\n + * - Workshop on Janus and SIP (lesson/tutorial) at OpenSIPS 2020; + * - Presentation on SFUs and MCUs at IIT RTC 2020; + * - Presentation on using Janus for IETF Virtual Participation at the MOPS WG session (IETF109); + * - Presentation on WebRTC (and Janus) as a way to help musicians at FOSDEM 2021; + * - Workshop on Janus RTP forwarders (lesson/tutorial) at ClueCon TGI2021; + * - Presentation on Janus and NDI at ClueCon TGI2021; + * - Presentation on Janus and multicast at the MBONED WG session (IETF 110). \n\n * Apart from these presentations, make sure you also check the slides * and presentations from JanusCon, * the Janus conference we hosted here in Napoli. diff --git a/npm/janus.d.ts b/npm/janus.d.ts index f81736e4ee6..bbd6cf017de 100644 --- a/npm/janus.d.ts +++ b/npm/janus.d.ts @@ -156,7 +156,7 @@ declare namespace JanusJS { isVideoMuted(): boolean; muteVideo(): void; unmuteVideo(): void; - getBitrate(): number; + getBitrate(): string; hangup(sendRequest?: boolean): void; detach(params: any): void; } diff --git a/plugins/janus_audiobridge.c b/plugins/janus_audiobridge.c index d922024266c..01e7b5a249d 100644 --- a/plugins/janus_audiobridge.c +++ b/plugins/janus_audiobridge.c @@ -857,6 +857,10 @@ room-: { */ #include "plugin.h" +#ifdef __FreeBSD__ +#include +#include +#endif #include #include @@ -6659,6 +6663,7 @@ static void *janus_audiobridge_mixer_thread(void *data) { } ps = ps->next; } + g_list_free(anncs_list); } #endif /* Are we recording the mix? (only do it if there's someone in, though...) */ @@ -6848,7 +6853,7 @@ static void *janus_audiobridge_mixer_thread(void *data) { size_t addrlen = (forwarder->serv_addr.sin_family == AF_INET ? sizeof(forwarder->serv_addr) : sizeof(forwarder->serv_addr6)); if(sendto(audiobridge->rtp_udp_sock, payload, plen, 0, address, addrlen) < 0) { JANUS_LOG(LOG_HUGE, "Error forwarding mixed RTP packet for room %s... %s (len=%d)...\n", - audiobridge->room_id_str, strerror(errno), plen); + audiobridge->room_id_str, g_strerror(errno), plen); } } } diff --git a/plugins/janus_duktape.c b/plugins/janus_duktape.c index 9a5ba7813e5..b5776fc69c9 100644 --- a/plugins/janus_duktape.c +++ b/plugins/janus_duktape.c @@ -266,7 +266,7 @@ janus_plugin *create(void) { /* Useful stuff */ volatile gint duktape_initialized = 0, duktape_stopping = 0; -janus_callbacks *janus_core = NULL; +janus_callbacks *duktape_janus_core = NULL; static char *duktape_folder = NULL; /* Duktape stuff */ @@ -401,7 +401,7 @@ static void *janus_duktape_async_event_helper(void *data) { return NULL; if(asev->type == janus_duktape_async_event_type_pushevent) { /* Send the event */ - janus_core->push_event(asev->session->handle, &janus_duktape_plugin, asev->transaction, asev->event, asev->jsep); + duktape_janus_core->push_event(asev->session->handle, &janus_duktape_plugin, asev->transaction, asev->event, asev->jsep); } json_decref(asev->event); json_decref(asev->jsep); @@ -461,7 +461,7 @@ static duk_ret_t janus_duktape_method_readfile(duk_context *ctx) { fseek(f, 0, SEEK_END); int len = (int)ftell(f); if(len < 0) { - duk_push_error_object(ctx, DUK_ERR_ERROR, "Error opening file: %s\n", strerror(errno)); + duk_push_error_object(ctx, DUK_ERR_ERROR, "Error opening file: %s\n", g_strerror(errno)); return duk_throw(ctx); } fseek(f, 0, SEEK_SET); @@ -646,7 +646,7 @@ static duk_ret_t janus_duktape_method_pushevent(duk_context *ctx) { return 1; } /* No SDP, send the event now */ - int res = janus_core->push_event(session->handle, &janus_duktape_plugin, transaction, event, NULL); + int res = duktape_janus_core->push_event(session->handle, &janus_duktape_plugin, transaction, event, NULL); janus_refcount_decrease(&session->ref); json_decref(event); duk_push_int(ctx, res); @@ -669,7 +669,7 @@ static duk_ret_t janus_duktape_method_notifyevent(duk_context *ctx) { if(event_text == NULL) return duk_throw(ctx); /* Get the arguments from the provided context */ - if(!janus_core->events_is_enabled()) { + if(!duktape_janus_core->events_is_enabled()) { /* Event handlers are disabled in the core, ignoring */ duk_push_int(ctx, 0); return 1; @@ -688,7 +688,7 @@ static duk_ret_t janus_duktape_method_notifyevent(duk_context *ctx) { janus_refcount_increase(&session->ref); janus_mutex_unlock(&duktape_sessions_mutex); /* Notify the event */ - janus_core->notify_event(&janus_duktape_plugin, session ? session->handle : NULL, event); + duktape_janus_core->notify_event(&janus_duktape_plugin, session ? session->handle : NULL, event); if(session != NULL) janus_refcount_decrease(&session->ref); duk_push_int(ctx, 0); @@ -697,7 +697,7 @@ static duk_ret_t janus_duktape_method_notifyevent(duk_context *ctx) { static duk_ret_t janus_duktape_method_eventsisenabled(duk_context *ctx) { /* Return info on whether event handlers are enabled in the core or not */ - duk_push_int(ctx, janus_core->events_is_enabled()); + duk_push_int(ctx, duktape_janus_core->events_is_enabled()); return 1; } @@ -719,7 +719,7 @@ static duk_ret_t janus_duktape_method_closepc(duk_context *ctx) { janus_refcount_increase(&session->ref); janus_mutex_unlock(&duktape_sessions_mutex); /* Close the PeerConnection */ - janus_core->close_pc(session->handle); + duktape_janus_core->close_pc(session->handle); duk_push_int(ctx, 0); return 1; } @@ -742,7 +742,7 @@ static duk_ret_t janus_duktape_method_endsession(duk_context *ctx) { janus_refcount_increase(&session->ref); janus_mutex_unlock(&duktape_sessions_mutex); /* Close the plugin handle */ - janus_core->end_session(session->handle); + duktape_janus_core->end_session(session->handle); duk_push_int(ctx, 0); return 1; } @@ -936,7 +936,7 @@ static duk_ret_t janus_duktape_method_setbitrate(duk_context *ctx) { /* Send a REMB right away too, if the PeerConnection is up */ if(g_atomic_int_get(&session->started)) { /* No limit ~= 10000000 */ - janus_core->send_remb(session->handle, session->bitrate ? session->bitrate : 10000000); + duktape_janus_core->send_remb(session->handle, session->bitrate ? session->bitrate : 10000000); } /* Done */ janus_refcount_decrease(&session->ref); @@ -1055,7 +1055,7 @@ static duk_ret_t janus_duktape_method_sendpli(duk_context *ctx) { /* Send a PLI */ session->pli_latest = janus_get_monotonic_time(); JANUS_LOG(LOG_HUGE, "Sending PLI to session %"SCNu32"\n", session->id); - janus_core->send_pli(session->handle); + duktape_janus_core->send_pli(session->handle); /* Done */ janus_refcount_decrease(&session->ref); duk_push_int(ctx, 0); @@ -1104,7 +1104,7 @@ static duk_ret_t janus_duktape_method_relayrtp(duk_context *ctx) { /* Send the RTP packet */ janus_plugin_rtp rtp = { .video = is_video, .buffer = (char *)payload, .length = len }; janus_plugin_rtp_extensions_reset(&rtp.extensions); - janus_core->relay_rtp(session->handle, &rtp); + duktape_janus_core->relay_rtp(session->handle, &rtp); duk_push_int(ctx, 0); return 1; } @@ -1150,7 +1150,7 @@ static duk_ret_t janus_duktape_method_relayrtcp(duk_context *ctx) { janus_mutex_unlock(&duktape_sessions_mutex); /* Send the RTCP packet */ janus_plugin_rtcp rtcp = { .video = is_video, .buffer = (char *)payload, .length = len }; - janus_core->relay_rtcp(session->handle, &rtcp); + duktape_janus_core->relay_rtcp(session->handle, &rtcp); duk_push_int(ctx, 0); return 1; } @@ -1203,7 +1203,7 @@ static duk_ret_t janus_duktape_method_relaytextdata(duk_context *ctx) { .buffer = (char *)payload, .length = len }; - janus_core->relay_data(session->handle, &data); + duktape_janus_core->relay_data(session->handle, &data); janus_refcount_decrease(&session->ref); duk_push_int(ctx, 0); return 1; @@ -1256,7 +1256,7 @@ static duk_ret_t janus_duktape_method_relaybinarydata(duk_context *ctx) { .buffer = (char *)payload, .length = len }; - janus_core->relay_data(session->handle, &data); + duktape_janus_core->relay_data(session->handle, &data); janus_refcount_decrease(&session->ref); duk_push_int(ctx, 0); return 1; @@ -1364,7 +1364,7 @@ static duk_ret_t janus_duktape_method_startrecording(duk_context *ctx) { /* Also send a keyframe request */ session->pli_latest = janus_get_monotonic_time(); JANUS_LOG(LOG_HUGE, "Sending PLI to session %"SCNu32"\n", session->id); - janus_core->send_pli(session->handle); + duktape_janus_core->send_pli(session->handle); } if(drc) { session->drc = drc; @@ -1699,7 +1699,7 @@ int janus_duktape_init(janus_callbacks *callback, const char *config_path) { } /* This is the callback we'll need to invoke to contact the Janus core */ - janus_core = callback; + duktape_janus_core = callback; /* Init the JS script, in case it's needed */ duk_get_global_string(duktape_ctx, "init"); @@ -2445,7 +2445,7 @@ void janus_duktape_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp * gint64 now = janus_get_monotonic_time(); if((now-session->pli_latest) >= ((gint64)session->pli_freq*G_USEC_PER_SEC)) { session->pli_latest = now; - janus_core->send_pli(handle); + duktape_janus_core->send_pli(handle); } } } @@ -2488,7 +2488,7 @@ void janus_duktape_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp uint32_t bitrate = janus_rtcp_get_remb(buf, len); if(bitrate > 0) { /* No limit ~= 10000000 */ - janus_core->send_remb(handle, session->bitrate ? session->bitrate : 10000000); + duktape_janus_core->send_remb(handle, session->bitrate ? session->bitrate : 10000000); } /* If there's an incoming PLI, instead, relay it to the source of the media if any */ if(janus_rtcp_has_pli(buf, len)) { @@ -2497,7 +2497,7 @@ void janus_duktape_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp /* Send a PLI */ session->sender->pli_latest = janus_get_monotonic_time(); JANUS_LOG(LOG_HUGE, "Sending PLI to session %"SCNu32"\n", session->sender->id); - janus_core->send_pli(session->sender->handle); + duktape_janus_core->send_pli(session->sender->handle); janus_mutex_unlock_nodebug(&session->sender->recipients_mutex); } } @@ -2733,7 +2733,7 @@ static void janus_duktape_relay_rtp_packet(gpointer data, gpointer user_data) { if(session->sim_context.need_pli && sender->handle) { /* Send a PLI */ JANUS_LOG(LOG_VERB, "We need a PLI for the simulcast context\n"); - janus_core->send_pli(sender->handle); + duktape_janus_core->send_pli(sender->handle); } /* Do we need to drop this? */ if(!relay) @@ -2787,10 +2787,10 @@ static void janus_duktape_relay_rtp_packet(gpointer data, gpointer user_data) { session->sim_context.changed_substream); } /* Send the packet */ - if(janus_core != NULL) { + if(duktape_janus_core != NULL) { janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length }; janus_plugin_rtp_extensions_reset(&rtp.extensions); - janus_core->relay_rtp(session->handle, &rtp); + duktape_janus_core->relay_rtp(session->handle, &rtp); } /* Restore the timestamp and sequence number to what the publisher set them to */ packet->data->timestamp = htonl(packet->timestamp); @@ -2803,10 +2803,10 @@ static void janus_duktape_relay_rtp_packet(gpointer data, gpointer user_data) { /* Fix sequence number and timestamp (publisher switching may be involved) */ janus_rtp_header_update(packet->data, &session->rtpctx, packet->is_video, 0); /* Send the packet */ - if(janus_core != NULL) { + if(duktape_janus_core != NULL) { janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length }; janus_plugin_rtp_extensions_reset(&rtp.extensions); - janus_core->relay_rtp(session->handle, &rtp); + duktape_janus_core->relay_rtp(session->handle, &rtp); } /* Restore the timestamp and sequence number to what the publisher set them to */ packet->data->timestamp = htonl(packet->timestamp); @@ -2827,7 +2827,7 @@ static void janus_duktape_relay_data_packet(gpointer data, gpointer user_data) { !session->accept_data || !g_atomic_int_get(&session->dataready)) { return; } - if(janus_core != NULL) { + if(duktape_janus_core != NULL) { JANUS_LOG(LOG_VERB, "Forwarding %s DataChannel message (%d bytes) to session %"SCNu32"\n", packet->textdata ? "text" : "binary", packet->length, session->id); janus_plugin_data data = { @@ -2837,7 +2837,7 @@ static void janus_duktape_relay_data_packet(gpointer data, gpointer user_data) { .buffer = (char *)packet->data, .length = packet->length }; - janus_core->relay_data(session->handle, &data); + duktape_janus_core->relay_data(session->handle, &data); } return; } diff --git a/plugins/janus_duktape_data.h b/plugins/janus_duktape_data.h index e705df33ba5..384b89ab3e2 100644 --- a/plugins/janus_duktape_data.h +++ b/plugins/janus_duktape_data.h @@ -41,7 +41,7 @@ /* Core pointer and related flags */ extern volatile gint duktape_initialized, duktape_stopping; -extern janus_callbacks *janus_core; +extern janus_callbacks *duktape_janus_core; /* Duktape context: we define context and mutex as extern */ extern duk_context *duktape_ctx; diff --git a/plugins/janus_lua.c b/plugins/janus_lua.c index 452d2716739..0366cb570e6 100644 --- a/plugins/janus_lua.c +++ b/plugins/janus_lua.c @@ -267,7 +267,7 @@ janus_plugin *create(void) { /* Useful stuff */ volatile gint lua_initialized = 0, lua_stopping = 0; -janus_callbacks *janus_core = NULL; +janus_callbacks *lua_janus_core = NULL; /* Lua stuff */ lua_State *lua_state = NULL; @@ -401,7 +401,7 @@ static void *janus_lua_async_event_helper(void *data) { return NULL; if(asev->type == janus_lua_async_event_type_pushevent) { /* Send the event */ - janus_core->push_event(asev->session->handle, &janus_lua_plugin, asev->transaction, asev->event, asev->jsep); + lua_janus_core->push_event(asev->session->handle, &janus_lua_plugin, asev->transaction, asev->event, asev->jsep); } json_decref(asev->event); json_decref(asev->jsep); @@ -571,7 +571,7 @@ static int janus_lua_method_pushevent(lua_State *s) { return 1; } /* No SDP, send the event now */ - int res = janus_core->push_event(session->handle, &janus_lua_plugin, transaction, event, NULL); + int res = lua_janus_core->push_event(session->handle, &janus_lua_plugin, transaction, event, NULL); janus_refcount_decrease(&session->ref); json_decref(event); lua_pushnumber(s, res); @@ -586,7 +586,7 @@ static int janus_lua_method_notifyevent(lua_State *s) { lua_pushnumber(s, -1); return 1; } - if(!janus_core->events_is_enabled()) { + if(!lua_janus_core->events_is_enabled()) { /* Event handlers are disabled in the core, ignoring */ lua_pushnumber(s, 0); return 1; @@ -608,7 +608,7 @@ static int janus_lua_method_notifyevent(lua_State *s) { janus_refcount_increase(&session->ref); janus_mutex_unlock(&lua_sessions_mutex); /* Notify the event */ - janus_core->notify_event(&janus_lua_plugin, session ? session->handle : NULL, event); + lua_janus_core->notify_event(&janus_lua_plugin, session ? session->handle : NULL, event); if(session != NULL) janus_refcount_decrease(&session->ref); lua_pushnumber(s, 0); @@ -624,7 +624,7 @@ static int janus_lua_method_eventsisenabled(lua_State *s) { return 1; } /* Event handlers are disabled in the core, ignoring */ - lua_pushnumber(s, janus_core->events_is_enabled()); + lua_pushnumber(s, lua_janus_core->events_is_enabled()); return 1; } @@ -648,7 +648,7 @@ static int janus_lua_method_closepc(lua_State *s) { janus_refcount_increase(&session->ref); janus_mutex_unlock(&lua_sessions_mutex); /* Close the PeerConnection */ - janus_core->close_pc(session->handle); + lua_janus_core->close_pc(session->handle); lua_pushnumber(s, 0); return 1; } @@ -673,7 +673,7 @@ static int janus_lua_method_endsession(lua_State *s) { janus_refcount_increase(&session->ref); janus_mutex_unlock(&lua_sessions_mutex); /* Close the plugin handle */ - janus_core->end_session(session->handle); + lua_janus_core->end_session(session->handle); lua_pushnumber(s, 0); return 1; } @@ -845,7 +845,7 @@ static int janus_lua_method_setbitrate(lua_State *s) { /* Send a REMB right away too, if the PeerConnection is up */ if(g_atomic_int_get(&session->started)) { /* No limit ~= 10000000 */ - janus_core->send_remb(session->handle, session->bitrate ? session->bitrate : 10000000); + lua_janus_core->send_remb(session->handle, session->bitrate ? session->bitrate : 10000000); } /* Done */ janus_refcount_decrease(&session->ref); @@ -958,7 +958,7 @@ static int janus_lua_method_sendpli(lua_State *s) { /* Send a PLI */ session->pli_latest = janus_get_monotonic_time(); JANUS_LOG(LOG_HUGE, "Sending PLI to session %"SCNu32"\n", session->id); - janus_core->send_pli(session->handle); + lua_janus_core->send_pli(session->handle); /* Done */ janus_refcount_decrease(&session->ref); lua_pushnumber(s, 0); @@ -994,7 +994,7 @@ static int janus_lua_method_relayrtp(lua_State *s) { /* Send the RTP packet */ janus_plugin_rtp rtp = { .video = is_video, .buffer = (char *)payload, .length = len }; janus_plugin_rtp_extensions_reset(&rtp.extensions); - janus_core->relay_rtp(session->handle, &rtp); + lua_janus_core->relay_rtp(session->handle, &rtp); lua_pushnumber(s, 0); return 1; } @@ -1027,7 +1027,7 @@ static int janus_lua_method_relayrtcp(lua_State *s) { janus_mutex_unlock(&lua_sessions_mutex); /* Send the RTCP packet */ janus_plugin_rtcp rtcp = { .video = is_video, .buffer = (char *)payload, .length = len }; - janus_core->relay_rtcp(session->handle, &rtcp); + lua_janus_core->relay_rtcp(session->handle, &rtcp); lua_pushnumber(s, 0); return 1; } @@ -1073,7 +1073,7 @@ static int janus_lua_method_relaytextdata(lua_State *s) { .buffer = (char *)payload, .length = len }; - janus_core->relay_data(session->handle, &data); + lua_janus_core->relay_data(session->handle, &data); janus_refcount_decrease(&session->ref); lua_pushnumber(s, 0); return 1; @@ -1120,7 +1120,7 @@ static int janus_lua_method_relaybinarydata(lua_State *s) { .buffer = (char *)payload, .length = len }; - janus_core->relay_data(session->handle, &data); + lua_janus_core->relay_data(session->handle, &data); janus_refcount_decrease(&session->ref); lua_pushnumber(s, 0); return 1; @@ -1229,7 +1229,7 @@ static int janus_lua_method_startrecording(lua_State *s) { /* Also send a keyframe request */ session->pli_latest = janus_get_monotonic_time(); JANUS_LOG(LOG_HUGE, "Sending PLI to session %"SCNu32"\n", session->id); - janus_core->send_pli(session->handle); + lua_janus_core->send_pli(session->handle); } if(drc) { session->drc = drc; @@ -1521,7 +1521,7 @@ int janus_lua_init(janus_callbacks *callback, const char *config_path) { } /* This is the callback we'll need to invoke to contact the Janus core */ - janus_core = callback; + lua_janus_core = callback; /* Init the Lua script, in case it's needed */ lua_getglobal(lua_state, "init"); @@ -2140,7 +2140,7 @@ void janus_lua_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *rtp_ if((now-session->pli_latest) >= ((gint64)session->pli_freq*G_USEC_PER_SEC)) { session->pli_latest = now; JANUS_LOG(LOG_HUGE, "Sending PLI to session %"SCNu32"\n", session->id); - janus_core->send_pli(handle); + lua_janus_core->send_pli(handle); } } } @@ -2177,7 +2177,7 @@ void janus_lua_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *pa guint32 bitrate = janus_rtcp_get_remb(buf, len); if(bitrate > 0) { /* No limit ~= 10000000 */ - janus_core->send_remb(handle, session->bitrate ? session->bitrate : 10000000); + lua_janus_core->send_remb(handle, session->bitrate ? session->bitrate : 10000000); } /* If there's an incoming PLI, instead, relay it to the source of the media if any */ if(janus_rtcp_has_pli(buf, len)) { @@ -2186,7 +2186,7 @@ void janus_lua_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *pa /* Send a PLI */ session->sender->pli_latest = janus_get_monotonic_time(); JANUS_LOG(LOG_HUGE, "Sending PLI to session %"SCNu32"\n", session->sender->id); - janus_core->send_pli(session->sender->handle); + lua_janus_core->send_pli(session->sender->handle); janus_mutex_unlock_nodebug(&session->sender->recipients_mutex); } } @@ -2398,7 +2398,7 @@ static void janus_lua_relay_rtp_packet(gpointer data, gpointer user_data) { if(session->sim_context.need_pli && sender->handle) { /* Send a PLI */ JANUS_LOG(LOG_VERB, "We need a PLI for the simulcast context\n"); - janus_core->send_pli(sender->handle); + lua_janus_core->send_pli(sender->handle); } /* Do we need to drop this? */ if(!relay) @@ -2440,10 +2440,10 @@ static void janus_lua_relay_rtp_packet(gpointer data, gpointer user_data) { session->sim_context.changed_substream); } /* Send the packet */ - if(janus_core != NULL) { + if(lua_janus_core != NULL) { janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length }; janus_plugin_rtp_extensions_reset(&rtp.extensions); - janus_core->relay_rtp(session->handle, &rtp); + lua_janus_core->relay_rtp(session->handle, &rtp); } /* Restore the timestamp and sequence number to what the publisher set them to */ packet->data->timestamp = htonl(packet->timestamp); @@ -2456,10 +2456,10 @@ static void janus_lua_relay_rtp_packet(gpointer data, gpointer user_data) { /* Fix sequence number and timestamp (publisher switching may be involved) */ janus_rtp_header_update(packet->data, &session->rtpctx, packet->is_video, 0); /* Send the packet */ - if(janus_core != NULL) { + if(lua_janus_core != NULL) { janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length }; janus_plugin_rtp_extensions_reset(&rtp.extensions); - janus_core->relay_rtp(session->handle, &rtp); + lua_janus_core->relay_rtp(session->handle, &rtp); } /* Restore the timestamp and sequence number to what the publisher set them to */ packet->data->timestamp = htonl(packet->timestamp); @@ -2480,7 +2480,7 @@ static void janus_lua_relay_data_packet(gpointer data, gpointer user_data) { !session->accept_data || !g_atomic_int_get(&session->dataready)) { return; } - if(janus_core != NULL) { + if(lua_janus_core != NULL) { JANUS_LOG(LOG_VERB, "Forwarding %s DataChannel message (%d bytes) to session %"SCNu32"\n", packet->textdata ? "text" : "binary", packet->length, session->id); janus_plugin_data data = { @@ -2490,7 +2490,7 @@ static void janus_lua_relay_data_packet(gpointer data, gpointer user_data) { .buffer = (char *)packet->data, .length = packet->length }; - janus_core->relay_data(session->handle, &data); + lua_janus_core->relay_data(session->handle, &data); } return; } diff --git a/plugins/janus_lua_data.h b/plugins/janus_lua_data.h index 2b499545464..38cb60a51f9 100644 --- a/plugins/janus_lua_data.h +++ b/plugins/janus_lua_data.h @@ -41,7 +41,7 @@ /* Core pointer and related flags */ extern volatile gint lua_initialized, lua_stopping; -extern janus_callbacks *janus_core; +extern janus_callbacks *lua_janus_core; /* Lua state: we define state and mutex as extern */ extern lua_State *lua_state; diff --git a/plugins/janus_nosip.c b/plugins/janus_nosip.c index 556904c09f9..257f5dda1e7 100644 --- a/plugins/janus_nosip.c +++ b/plugins/janus_nosip.c @@ -688,7 +688,7 @@ int janus_nosip_init(janus_callbacks *callback, const char *config_path) { janus_network_address_string_buffer ibuf; if(getifaddrs(&ifas) == -1) { JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected... %d (%s)\n", - errno, strerror(errno)); + errno, g_strerror(errno)); } else { if(janus_network_lookup_interface(ifas, item->value, &iface) != 0) { JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value); @@ -1106,7 +1106,7 @@ void janus_nosip_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *pa guint32 timestamp = ntohl(header->timestamp); guint16 seq = ntohs(header->seq_number); JANUS_LOG(LOG_HUGE, "[NoSIP-%p] Error sending %s SRTP packet... %s (len=%d, ts=%"SCNu32", seq=%"SCNu16")...\n", - session, video ? "Video" : "Audio", strerror(errno), protected, timestamp, seq); + session, video ? "Video" : "Audio", g_strerror(errno), protected, timestamp, seq); } } } else { @@ -1116,7 +1116,7 @@ void janus_nosip_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *pa guint32 timestamp = ntohl(header->timestamp); guint16 seq = ntohs(header->seq_number); JANUS_LOG(LOG_HUGE, "[NoSIP-%p] Error sending %s RTP packet... %s (len=%d, ts=%"SCNu32", seq=%"SCNu16")...\n", - session, video ? "Video" : "Audio", strerror(errno), len, timestamp, seq); + session, video ? "Video" : "Audio", g_strerror(errno), len, timestamp, seq); } } } @@ -1162,14 +1162,14 @@ void janus_nosip_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp * /* Forward the message to the peer */ if(send((video ? session->media.video_rtcp_fd : session->media.audio_rtcp_fd), sbuf, protected, 0) < 0) { JANUS_LOG(LOG_HUGE, "[NoSIP-%p] Error sending SRTCP %s packet... %s (len=%d)...\n", - session, video ? "Video" : "Audio", strerror(errno), protected); + session, video ? "Video" : "Audio", g_strerror(errno), protected); } } } else { /* Forward the message to the peer */ if(send((video ? session->media.video_rtcp_fd : session->media.audio_rtcp_fd), buf, len, 0) < 0) { JANUS_LOG(LOG_HUGE, "[NoSIP-%p] Error sending RTCP %s packet... %s (len=%d)...\n", - session, video ? "Video" : "Audio", strerror(errno), len); + session, video ? "Video" : "Audio", g_strerror(errno), len); } } } @@ -1965,14 +1965,14 @@ static int janus_nosip_allocate_port_pair(gboolean video, int fds[2], int ports[ int ret = setsockopt(rtp_fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)); if(ret < 0) { JANUS_LOG(LOG_WARN, "Error setting IP_TOS %d on audio RTP socket (error=%s)\n", - optval, strerror(errno)); + optval, g_strerror(errno)); } } else if(rtp_fd != -1 && video && dscp_video_rtp > 0) { int optval = dscp_video_rtp << 2; int ret = setsockopt(rtp_fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)); if(ret < 0) { JANUS_LOG(LOG_WARN, "Error setting IP_TOS %d on video RTP socket (error=%s)\n", - optval, strerror(errno)); + optval, g_strerror(errno)); } } } @@ -2134,28 +2134,28 @@ static void janus_nosip_connect_sockets(janus_nosip_session *session, struct soc audio_server_addr->sin_port = htons(session->media.remote_audio_rtp_port); if(connect(session->media.audio_rtp_fd, (struct sockaddr *)audio_server_addr, sizeof(struct sockaddr)) == -1) { JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't connect audio RTP? (%s:%d)\n", session, session->media.remote_audio_ip, session->media.remote_audio_rtp_port); - JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, g_strerror(errno)); } } if(session->media.remote_audio_rtcp_port && audio_server_addr && session->media.audio_rtcp_fd != -1) { audio_server_addr->sin_port = htons(session->media.remote_audio_rtcp_port); if(connect(session->media.audio_rtcp_fd, (struct sockaddr *)audio_server_addr, sizeof(struct sockaddr)) == -1) { JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't connect audio RTCP? (%s:%d)\n", session, session->media.remote_audio_ip, session->media.remote_audio_rtcp_port); - JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, g_strerror(errno)); } } if(session->media.remote_video_rtp_port && video_server_addr && session->media.video_rtp_fd != -1) { video_server_addr->sin_port = htons(session->media.remote_video_rtp_port); if(connect(session->media.video_rtp_fd, (struct sockaddr *)video_server_addr, sizeof(struct sockaddr)) == -1) { JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't connect video RTP? (%s:%d)\n", session, session->media.remote_video_ip, session->media.remote_video_rtp_port); - JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, g_strerror(errno)); } } if(session->media.remote_video_rtcp_port && video_server_addr && session->media.video_rtcp_fd != -1) { video_server_addr->sin_port = htons(session->media.remote_video_rtcp_port); if(connect(session->media.video_rtcp_fd, (struct sockaddr *)video_server_addr, sizeof(struct sockaddr)) == -1) { JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't connect video RTCP? (%s:%d)\n", session, session->media.remote_video_ip, session->media.remote_video_rtcp_port); - JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, g_strerror(errno)); } } @@ -2321,11 +2321,11 @@ static void *janus_nosip_relay_thread(void *data) { resfd = poll(fds, num, 1000); if(resfd < 0) { if(errno == EINTR) { - JANUS_LOG(LOG_HUGE, "[NoSIP-%p] Got an EINTR (%s), ignoring...\n", session, strerror(errno)); + JANUS_LOG(LOG_HUGE, "[NoSIP-%p] Got an EINTR (%s), ignoring...\n", session, g_strerror(errno)); continue; } JANUS_LOG(LOG_ERR, "[NoSIP-%p] Error polling...\n", session); - JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, g_strerror(errno)); break; } else if(resfd == 0) { /* No data, keep going */ @@ -2350,12 +2350,12 @@ static void *janus_nosip_relay_thread(void *data) { /* ICMP error? If it's related to RTCP, let's just close the RTCP socket and move on */ if(fds[i].fd == session->media.audio_rtcp_fd) { JANUS_LOG(LOG_WARN, "[NoSIP-%p] Got a '%s' on the audio RTCP socket, closing it\n", - session, strerror(error)); + session, g_strerror(error)); close(session->media.audio_rtcp_fd); session->media.audio_rtcp_fd = -1; } else if(fds[i].fd == session->media.video_rtcp_fd) { JANUS_LOG(LOG_WARN, "[NoSIP-%p] Got a '%s' on the video RTCP socket, closing it\n", - session, strerror(error)); + session, g_strerror(error)); close(session->media.video_rtcp_fd); session->media.video_rtcp_fd = -1; } @@ -2366,7 +2366,7 @@ static void *janus_nosip_relay_thread(void *data) { continue; JANUS_LOG(LOG_ERR, "[NoSIP-%p] Too many errors polling %d (socket #%d): %s...\n", session, fds[i].fd, i, fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP"); - JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, error, strerror(error)); + JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, error, g_strerror(error)); /* Can we assume it's pretty much over, after a POLLERR? */ goon = FALSE; /* FIXME Close the PeerConnection */ diff --git a/plugins/janus_recordplay.c b/plugins/janus_recordplay.c index 2427d7afe95..1433fe160af 100644 --- a/plugins/janus_recordplay.c +++ b/plugins/janus_recordplay.c @@ -580,7 +580,7 @@ static const char *janus_recordplay_parse_codec(const char *dir, const char *fil /* This is the info header */ bytes = fread(prebuffer, sizeof(char), len, file); if(bytes < 0) { - JANUS_LOG(LOG_ERR, "Error reading from file... %s\n", strerror(errno)); + JANUS_LOG(LOG_ERR, "Error reading from file... %s\n", g_strerror(errno)); fclose(file); return NULL; } @@ -814,7 +814,7 @@ int janus_recordplay_init(janus_callbacks *callback, const char *config_path) { int res = janus_mkdir(recordings_path, 0755); JANUS_LOG(LOG_VERB, "Creating folder: %d\n", res); if(res != 0) { - JANUS_LOG(LOG_ERR, "%s", strerror(errno)); + JANUS_LOG(LOG_ERR, "%s", g_strerror(errno)); return -1; /* No point going on... */ } } @@ -2317,7 +2317,7 @@ janus_recordplay_frame_packet *janus_recordplay_get_frames(const char *dir, cons JANUS_LOG(LOG_VERB, "New .mjr header format\n"); bytes = fread(prebuffer, sizeof(char), len, file); if(bytes < 0) { - JANUS_LOG(LOG_ERR, "Error reading from file... %s\n", strerror(errno)); + JANUS_LOG(LOG_ERR, "Error reading from file... %s\n", g_strerror(errno)); fclose(file); return NULL; } diff --git a/plugins/janus_sip.c b/plugins/janus_sip.c index 25fd593fd21..e4bf007ff72 100644 --- a/plugins/janus_sip.c +++ b/plugins/janus_sip.c @@ -406,12 +406,17 @@ * and will restore the media direction that was set in the SDP before * putting the call on-hold. * - * The \c message request allows you to send a SIP MESSAGE to the peer: + * The \c message request allows you to send a SIP MESSAGE to the peer. + * By default, it is sent in dialog, during active call. + * But, if the user is registered, it might be sent out of dialog also. In that case the uri parameter is required. * \verbatim { "request" : "message", - "content" : "" + "content_type" : "" + "content" : "", + "uri" : "", + "headers" : "" } \endverbatim * @@ -425,6 +430,7 @@ "event" : "message", "sender" : "", "displayname" : "", + "content_type" : "", "content" : "", "headers" : "" } @@ -798,7 +804,10 @@ static struct janus_json_parameter info_parameters[] = { {"content", JSON_STRING, JANUS_JSON_PARAM_REQUIRED} }; static struct janus_json_parameter sipmessage_parameters[] = { - {"content", JSON_STRING, JANUS_JSON_PARAM_REQUIRED} + {"content_type", JSON_STRING, 0}, + {"content", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}, + {"uri", JSON_STRING, 0}, + {"headers", JSON_OBJECT, 0} }; /* Useful stuff */ @@ -907,7 +916,7 @@ struct ssip_s { su_home_t s_home[1]; su_root_t *s_root; nua_t *s_nua; - nua_handle_t *s_nh_r, *s_nh_i; + nua_handle_t *s_nh_r, *s_nh_i, *s_nh_m; GHashTable *subscriptions; janus_mutex smutex; struct janus_sip_session *session; @@ -1763,7 +1772,7 @@ int janus_sip_init(janus_callbacks *callback, const char *config_path) { janus_network_address_string_buffer ibuf; if(getifaddrs(&ifas) == -1) { JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected... %d (%s)\n", - errno, strerror(errno)); + errno, g_strerror(errno)); } else { if(janus_network_lookup_interface(ifas, item->value, &iface) != 0) { JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value); @@ -2335,7 +2344,7 @@ void janus_sip_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *pack guint32 timestamp = ntohl(header->timestamp); guint16 seq = ntohs(header->seq_number); JANUS_LOG(LOG_HUGE, "[SIP-%s] Error sending SRTP video packet... %s (len=%d, ts=%"SCNu32", seq=%"SCNu16")...\n", - session->account.username, strerror(errno), protected, timestamp, seq); + session->account.username, g_strerror(errno), protected, timestamp, seq); } } } else { @@ -2345,7 +2354,7 @@ void janus_sip_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *pack guint32 timestamp = ntohl(header->timestamp); guint16 seq = ntohs(header->seq_number); JANUS_LOG(LOG_HUGE, "[SIP-%s] Error sending RTP video packet... %s (len=%d, ts=%"SCNu32", seq=%"SCNu16")...\n", - session->account.username, strerror(errno), len, timestamp, seq); + session->account.username, g_strerror(errno), len, timestamp, seq); } } } @@ -2381,7 +2390,7 @@ void janus_sip_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *pack guint32 timestamp = ntohl(header->timestamp); guint16 seq = ntohs(header->seq_number); JANUS_LOG(LOG_HUGE, "[SIP-%s] Error sending SRTP audio packet... %s (len=%d, ts=%"SCNu32", seq=%"SCNu16")...\n", - session->account.username, strerror(errno), protected, timestamp, seq); + session->account.username, g_strerror(errno), protected, timestamp, seq); } } } else { @@ -2391,7 +2400,7 @@ void janus_sip_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *pack guint32 timestamp = ntohl(header->timestamp); guint16 seq = ntohs(header->seq_number); JANUS_LOG(LOG_HUGE, "[SIP-%s] Error sending RTP audio packet... %s (len=%d, ts=%"SCNu32", seq=%"SCNu16")...\n", - session->account.username, strerror(errno), len, timestamp, seq); + session->account.username, g_strerror(errno), len, timestamp, seq); } } } @@ -2433,14 +2442,14 @@ void janus_sip_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *pa /* Forward the message to the peer */ if(send(session->media.video_rtcp_fd, sbuf, protected, 0) < 0) { JANUS_LOG(LOG_HUGE, "[SIP-%s] Error sending SRTCP video packet... %s (len=%d)...\n", - session->account.username, strerror(errno), protected); + session->account.username, g_strerror(errno), protected); } } } else { /* Forward the message to the peer */ if(send(session->media.video_rtcp_fd, buf, len, 0) < 0) { JANUS_LOG(LOG_HUGE, "[SIP-%s] Error sending RTCP video packet... %s (len=%d)...\n", - session->account.username, strerror(errno), len); + session->account.username, g_strerror(errno), len); } } } @@ -2463,14 +2472,14 @@ void janus_sip_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *pa /* Forward the message to the peer */ if(send(session->media.audio_rtcp_fd, sbuf, protected, 0) < 0) { JANUS_LOG(LOG_HUGE, "[SIP-%s] Error sending SRTCP audio packet... %s (len=%d)...\n", - session->account.username, strerror(errno), protected); + session->account.username, g_strerror(errno), protected); } } } else { /* Forward the message to the peer */ if(send(session->media.audio_rtcp_fd, buf, len, 0) < 0) { JANUS_LOG(LOG_HUGE, "[SIP-%s] Error sending RTCP audio packet... %s (len=%d)...\n", - session->account.username, strerror(errno), len); + session->account.username, g_strerror(errno), len); } } } @@ -2558,13 +2567,15 @@ static void janus_sip_hangup_media_internal(janus_plugin_session *handle) { session->media.autoaccept_reinvites = TRUE; session->media.ready = FALSE; session->media.on_hold = FALSE; - janus_sip_call_update_status(session, janus_sip_call_status_closing); - if(g_atomic_int_get(&session->established)) + /* Send a BYE or respond with 480 */ + if(g_atomic_int_get(&session->established) || session->status == janus_sip_call_status_inviting) nua_bye(session->stack->s_nh_i, TAG_END()); else nua_respond(session->stack->s_nh_i, 480, sip_status_phrase(480), TAG_END()); + janus_sip_call_update_status(session, janus_sip_call_status_closing); + /* Notify the operation */ json_t *event = json_object(); json_object_set_new(event, "sip", json_string("event")); @@ -3575,6 +3586,7 @@ static void *janus_sip_handler(void *data) { /* Send an ack back */ result = json_object(); json_object_set_new(result, "event", json_string("calling")); + json_object_set_new(result, "call_id", json_string(session->callid)); } else if(!strcasecmp(request_text, "accept")) { if(session->status != janus_sip_call_status_invited) { JANUS_LOG(LOG_ERR, "Wrong state (not invited? status=%s)\n", janus_sip_call_status_string(session->status)); @@ -4035,6 +4047,8 @@ static void *janus_sip_handler(void *data) { result = json_object(); json_object_set_new(result, "event", json_string("declining")); json_object_set_new(result, "code", json_integer(response_code)); + if(session->callid) + json_object_set_new(result, "call_id", json_string(session->callid)); } else if(!strcasecmp(request_text, "transfer")) { /* Transfer an existing call */ JANUS_VALIDATE_JSON_OBJECT(root, transfer_parameters, @@ -4458,34 +4472,88 @@ static void *janus_sip_handler(void *data) { result = json_object(); json_object_set_new(result, "event", json_string("infosent")); } else if(!strcasecmp(request_text, "message")) { - /* Send a SIP MESSAGE request: we'll only need the content */ - if(!(session->status == janus_sip_call_status_inviting || - janus_sip_call_is_established(session))) { - JANUS_LOG(LOG_ERR, "Wrong state (not established? status=%s)\n", janus_sip_call_status_string(session->status)); - g_snprintf(error_cause, 512, "Wrong state (not in a call?)"); - goto error; - } - janus_mutex_lock(&session->mutex); - if(session->callee == NULL) { - janus_mutex_unlock(&session->mutex); - JANUS_LOG(LOG_ERR, "Wrong state (no callee?)\n"); - error_code = JANUS_SIP_ERROR_WRONG_STATE; - g_snprintf(error_cause, 512, "Wrong state (no callee?)"); - goto error; - } - janus_mutex_unlock(&session->mutex); + /* Send a SIP MESSAGE request: we'll only need the content and optional payload type */ JANUS_VALIDATE_JSON_OBJECT(root, sipmessage_parameters, error_code, error_cause, TRUE, JANUS_SIP_ERROR_MISSING_ELEMENT, JANUS_SIP_ERROR_INVALID_ELEMENT); if(error_code != 0) { - janus_mutex_unlock(&session->mutex); goto error; } + gboolean in_dialog_message = TRUE; + json_t *uri = json_object_get(root, "uri"); + const char *uri_text = json_string_value(uri); + if(uri != NULL) + in_dialog_message = FALSE; + + if(in_dialog_message) { + if(!(session->status == janus_sip_call_status_inviting || janus_sip_call_is_established(session))) { + JANUS_LOG(LOG_ERR, "Wrong state (not established? status=%s)\n", janus_sip_call_status_string(session->status)); + g_snprintf(error_cause, 512, "Wrong state (not in a call?)"); + goto error; + } + janus_mutex_lock(&session->mutex); + if(session->callee == NULL) { + janus_mutex_unlock(&session->mutex); + JANUS_LOG(LOG_ERR, "Wrong state (no callee?)\n"); + error_code = JANUS_SIP_ERROR_WRONG_STATE; + g_snprintf(error_cause, 512, "Wrong state (no callee?)"); + goto error; + } + janus_mutex_unlock(&session->mutex); + } else { + if(session->account.registration_status != janus_sip_registration_status_registered && + session->account.registration_status != janus_sip_registration_status_disabled) { + JANUS_LOG(LOG_ERR, "Wrong state (not registered)\n"); + error_code = JANUS_SIP_ERROR_WRONG_STATE; + g_snprintf(error_cause, 512, "Wrong state (not registered)"); + goto error; + } + janus_sip_uri_t target_uri; + if(janus_sip_parse_uri(&target_uri, uri_text) < 0) { + JANUS_LOG(LOG_ERR, "Invalid user address %s\n", uri_text); + error_code = JANUS_SIP_ERROR_INVALID_ADDRESS; + g_snprintf(error_cause, 512, "Invalid user address %s\n", uri_text); + goto error; + } + } + + const char *content_type = "text/plain"; + json_t *content_type_text = json_object_get(root, "content_type"); + if(content_type_text && json_is_string(content_type_text)) + content_type = json_string_value(content_type_text); + const char *msg_content = json_string_value(json_object_get(root, "content")); - nua_message(session->stack->s_nh_i, - SIPTAG_CONTENT_TYPE_STR("text/plain"), - SIPTAG_PAYLOAD_STR(msg_content), - TAG_END()); + char custom_headers[2048]; + janus_sip_parse_custom_headers(root, (char *)&custom_headers, sizeof(custom_headers)); + + if(in_dialog_message) { + nua_message(session->stack->s_nh_i, + SIPTAG_CONTENT_TYPE_STR(content_type), + SIPTAG_PAYLOAD_STR(msg_content), + TAG_IF(strlen(custom_headers) > 0, SIPTAG_HEADER_STR(custom_headers)), + TAG_END()); + } else { + janus_mutex_lock(&session->stack->smutex); + if(session->stack->s_nh_m == NULL) { + if (session->stack->s_nua == NULL) { + janus_mutex_unlock(&session->stack->smutex); + JANUS_LOG(LOG_ERR, "NUA destroyed while sending message?\n"); + error_code = JANUS_SIP_ERROR_LIBSOFIA_ERROR; + g_snprintf(error_cause, 512, "Invalid NUA"); + goto error; + } + session->stack->s_nh_m = nua_handle(session->stack->s_nua, session, TAG_END()); + } + janus_mutex_unlock(&session->stack->smutex); + nua_message(session->stack->s_nh_m, + SIPTAG_TO_STR(uri_text), + SIPTAG_CONTENT_TYPE_STR(content_type), + SIPTAG_PAYLOAD_STR(msg_content), + NUTAG_PROXY(session->helper && session->master ? + session->master->account.outbound_proxy : session->account.outbound_proxy), + TAG_IF(strlen(custom_headers) > 0, SIPTAG_HEADER_STR(custom_headers)), + TAG_END()); + } /* Notify the operation */ result = json_object(); json_object_set_new(result, "event", json_string("messagesent")); @@ -4746,8 +4814,8 @@ void janus_sip_sofia_callback(nua_event_t event, int status, char const *phrase, nua_respond(nh, 500, sip_status_phrase(500), TAG_END()); break; } - if(sip->sip_from == NULL || sip->sip_from->a_url == NULL || - sip->sip_to == NULL || sip->sip_to->a_url == NULL) { + if(sip->sip_from == NULL || sip->sip_from->a_url->url_user == NULL || + sip->sip_to == NULL || sip->sip_to->a_url->url_user == NULL) { JANUS_LOG(LOG_ERR, "\tInvalid request (missing From or To)\n"); nua_respond(nh, 400, sip_status_phrase(400), TAG_END()); break; @@ -4922,6 +4990,8 @@ void janus_sip_sofia_callback(nua_event_t event, int status, char const *phrase, json_t *calling = json_object(); json_object_set_new(calling, "event", json_string(reinvite ? "updatingcall" : "incomingcall")); json_object_set_new(calling, "username", json_string(session->callee)); + if(session->callid) + json_object_set_new(calling, "call_id", json_string(session->callid)); if(sip->sip_from->a_display) { json_object_set_new(calling, "displayname", json_string(sip->sip_from->a_display)); } @@ -5067,8 +5137,6 @@ void janus_sip_sofia_callback(nua_event_t event, int status, char const *phrase, JANUS_LOG(LOG_VERB, "[%s][%s]: %d %s\n", session->account.username, nua_event_name(event), status, phrase ? phrase : "??"); /* We expect a payload */ if(!sip->sip_content_type || !sip->sip_content_type->c_type || !sip->sip_payload || !sip->sip_payload->pl_data) { - nua_respond(nh, 488, sip_status_phrase(488), - NUTAG_WITH_CURRENT(nua), TAG_END()); return; } const char *type = sip->sip_content_type->c_type; @@ -5090,23 +5158,21 @@ void janus_sip_sofia_callback(nua_event_t event, int status, char const *phrase, json_t *headers = janus_sip_get_incoming_headers(sip, session); json_object_set_new(result, "headers", headers); } + if(session->callid) + json_object_set_new(info, "call_id", json_string(session->callid)); json_object_set_new(info, "result", result); int ret = gateway->push_event(session->handle, &janus_sip_plugin, session->transaction, info, NULL); JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret)); json_decref(info); - /* Send a 200 back */ - nua_respond(nh, 200, sip_status_phrase(200), - NUTAG_WITH_CURRENT(nua), TAG_END()); break; } case nua_i_message: { JANUS_LOG(LOG_VERB, "[%s][%s]: %d %s\n", session->account.username, nua_event_name(event), status, phrase ? phrase : "??"); /* We expect a payload */ - if(!sip->sip_payload || !sip->sip_payload->pl_data) { - nua_respond(nh, 488, sip_status_phrase(488), - NUTAG_WITH_CURRENT(nua), TAG_END()); + if(!sip->sip_content_type || !sip->sip_content_type->c_type || !sip->sip_payload || !sip->sip_payload->pl_data) { return; } + const char *content_type = sip->sip_content_type->c_type; char *payload = sip->sip_payload->pl_data; /* Notify the application */ json_t *message = json_object(); @@ -5124,13 +5190,13 @@ void janus_sip_sofia_callback(nua_event_t event, int status, char const *phrase, json_t *headers = janus_sip_get_incoming_headers(sip, session); json_object_set_new(result, "headers", headers); } + if(session->callid) + json_object_set_new(message, "call_id", json_string(session->callid)); + json_object_set_new(result, "content_type", json_string(content_type)); json_object_set_new(message, "result", result); int ret = gateway->push_event(session->handle, &janus_sip_plugin, session->transaction, message, NULL); JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret)); json_decref(message); - /* Send a 200 back */ - nua_respond(nh, 200, sip_status_phrase(200), - NUTAG_WITH_CURRENT(nua), TAG_END()); break; } case nua_i_notify: { @@ -6071,7 +6137,7 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u int ret = setsockopt(session->media.audio_rtp_fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)); if(ret < 0) { JANUS_LOG(LOG_WARN, "Error setting IP_TOS %d on audio RTP socket (error=%s)\n", - optval, strerror(errno)); + optval, g_strerror(errno)); } } } @@ -6129,7 +6195,7 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u int ret = setsockopt(session->media.video_rtp_fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)); if(ret < 0) { JANUS_LOG(LOG_WARN, "Error setting IP_TOS %d on video RTP socket (error=%s)\n", - optval, strerror(errno)); + optval, g_strerror(errno)); } } } @@ -6188,28 +6254,28 @@ static void janus_sip_connect_sockets(janus_sip_session *session, struct sockadd audio_server_addr->sin_port = htons(session->media.remote_audio_rtp_port); if(connect(session->media.audio_rtp_fd, (struct sockaddr *)audio_server_addr, sizeof(struct sockaddr)) == -1) { JANUS_LOG(LOG_ERR, "[SIP-%s] Couldn't connect audio RTP? (%s:%d)\n", session->account.username, session->media.remote_audio_ip, session->media.remote_audio_rtp_port); - JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, g_strerror(errno)); } } if(session->media.remote_audio_rtcp_port && audio_server_addr && session->media.audio_rtcp_fd != -1) { audio_server_addr->sin_port = htons(session->media.remote_audio_rtcp_port); if(connect(session->media.audio_rtcp_fd, (struct sockaddr *)audio_server_addr, sizeof(struct sockaddr)) == -1) { JANUS_LOG(LOG_ERR, "[SIP-%s] Couldn't connect audio RTCP? (%s:%d)\n", session->account.username, session->media.remote_audio_ip, session->media.remote_audio_rtcp_port); - JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, g_strerror(errno)); } } if(session->media.remote_video_rtp_port && video_server_addr && session->media.video_rtp_fd != -1) { video_server_addr->sin_port = htons(session->media.remote_video_rtp_port); if(connect(session->media.video_rtp_fd, (struct sockaddr *)video_server_addr, sizeof(struct sockaddr)) == -1) { JANUS_LOG(LOG_ERR, "[SIP-%s] Couldn't connect video RTP? (%s:%d)\n", session->account.username, session->media.remote_video_ip, session->media.remote_video_rtp_port); - JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, g_strerror(errno)); } } if(session->media.remote_video_rtcp_port && video_server_addr && session->media.video_rtcp_fd != -1) { video_server_addr->sin_port = htons(session->media.remote_video_rtcp_port); if(connect(session->media.video_rtcp_fd, (struct sockaddr *)video_server_addr, sizeof(struct sockaddr)) == -1) { JANUS_LOG(LOG_ERR, "[SIP-%s] Couldn't connect video RTCP? (%s:%d)\n", session->account.username, session->media.remote_video_ip, session->media.remote_video_rtcp_port); - JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, g_strerror(errno)); } } } @@ -6393,11 +6459,11 @@ static void *janus_sip_relay_thread(void *data) { resfd = poll(fds, num, 1000); if(resfd < 0) { if(errno == EINTR) { - JANUS_LOG(LOG_HUGE, "[SIP-%s] Got an EINTR (%s), ignoring...\n", session->account.username, strerror(errno)); + JANUS_LOG(LOG_HUGE, "[SIP-%s] Got an EINTR (%s), ignoring...\n", session->account.username, g_strerror(errno)); continue; } JANUS_LOG(LOG_ERR, "[SIP-%s] Error polling...\n", session->account.username); - JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, errno, g_strerror(errno)); break; } else if(resfd == 0) { /* No data, keep going */ @@ -6424,13 +6490,13 @@ static void *janus_sip_relay_thread(void *data) { /* ICMP error? If it's related to RTCP, let's just close the RTCP socket and move on */ if(fds[i].fd == session->media.audio_rtcp_fd) { JANUS_LOG(LOG_WARN, "[SIP-%s] Got a '%s' on the audio RTCP socket, closing it\n", - session->account.username, strerror(error)); + session->account.username, g_strerror(error)); close(session->media.audio_rtcp_fd); session->media.audio_rtcp_fd = -1; continue; } else if(fds[i].fd == session->media.video_rtcp_fd) { JANUS_LOG(LOG_WARN, "[SIP-%s] Got a '%s' on the video RTCP socket, closing it\n", - session->account.username, strerror(error)); + session->account.username, g_strerror(error)); close(session->media.video_rtcp_fd); session->media.video_rtcp_fd = -1; continue; @@ -6442,7 +6508,7 @@ static void *janus_sip_relay_thread(void *data) { continue; JANUS_LOG(LOG_ERR, "[SIP-%s] Too many errors polling %d (socket #%d): %s...\n", session->account.username, fds[i].fd, i, fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP"); - JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, error, strerror(error)); + JANUS_LOG(LOG_ERR, "[SIP-%s] -- %d (%s)\n", session->account.username, error, g_strerror(error)); goon = FALSE; /* Can we assume it's pretty much over, after a POLLERR? */ /* FIXME Simulate a "hangup" coming from the application */ janus_sip_hangup_media(session->handle); @@ -6637,6 +6703,7 @@ gpointer janus_sip_sofia_thread(gpointer user_data) { session->stack->s_nua = NULL; session->stack->s_nh_r = NULL; session->stack->s_nh_i = NULL; + session->stack->s_nh_m = NULL; session->stack->s_root = su_root_create(session->stack); session->stack->subscriptions = NULL; janus_mutex_init(&session->stack->smutex); @@ -6686,6 +6753,10 @@ gpointer janus_sip_sofia_thread(gpointer user_data) { nua_handle_destroy(session->stack->s_nh_i); session->stack->s_nh_i = NULL; } + if(session->stack->s_nh_m != NULL) { + nua_handle_destroy(session->stack->s_nh_m); + session->stack->s_nh_m = NULL; + } nua_destroy(s_nua); su_root_destroy(session->stack->s_root); session->stack->s_root = NULL; diff --git a/plugins/janus_streaming.c b/plugins/janus_streaming.c index 9ebda945586..ee459b63dcb 100644 --- a/plugins/janus_streaming.c +++ b/plugins/janus_streaming.c @@ -134,12 +134,20 @@ so neither Janus nor the Streaming plugin have access to anything. DO NOT SET THIS PROPERTY IF YOU DON'T KNOW WHAT YOU'RE DOING! e2ee = true -The following options are only valid for the 'rstp' type: +The following options are only valid for the 'rtsp' type: url = RTSP stream URL rtsp_user = RTSP authorization username, if needed rtsp_pwd = RTSP authorization password, if needed rtsp_failcheck = whether an error should be returned if connecting to the RTSP server fails (default=true) rtspiface = network interface IP address or device name to listen on when receiving RTSP streams +rtsp_reconnect_delay = after n seconds passed and no media assumed, the RTSP server has gone and schedule a reconnect (default=5s) +rtsp_session_timeout = by default the streaming plugin will check the RTSP connection with an OPTIONS query, + the value of the timeout comes from the RTSP session initializer and by default + this session timeout is the half of this value In some cases this value can be too high (for example more than one minute) + because of the media server. In that case this plugin will calculate the timeout with this + formula: timeout = min(session_timeout, rtsp_session_timeout / 2). (default=0s) +rtsp_timeout = communication timeout (CURLOPT_TIMEOUT) for cURL call gathering the RTSP information (default=10s) +rtsp_conn_timeout = connection timeout for cURL (CURLOPT_CONNECTTIMEOUT) call gathering the RTSP information (default=5s) \endverbatim * * \section streamapi Streaming API @@ -721,6 +729,11 @@ rtspiface = network interface IP address or device name to listen on when receiv #include "../utils.h" #include "../ip-utils.h" +/* Default settings */ +#define JANUS_STREAMING_DEFAULT_SESSION_TIMEOUT 0 /* Overwrite the RTSP session timeout. If set to zero, the RTSP timeout is derived from a session. */ +#define JANUS_STREAMING_DEFAULT_RECONNECT_DELAY 5 /* Reconnecting delay in seconds. */ +#define JANUS_STREAMING_DEFAULT_CURL_TIMEOUT 10L /* Communication timeout for cURL. */ +#define JANUS_STREAMING_DEFAULT_CURL_CONNECT_TIMEOUT 5L /* Connection timeout for cURL. */ /* Plugin information */ #define JANUS_STREAMING_VERSION 8 @@ -855,6 +868,10 @@ static struct janus_json_parameter rtsp_parameters[] = { {"url", JSON_STRING, 0}, {"rtsp_user", JSON_STRING, 0}, {"rtsp_pwd", JSON_STRING, 0}, + {"rtsp_reconnect_delay", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}, + {"rtsp_session_timeout", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}, + {"rtsp_timeout", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}, + {"rtsp_conn_timeout", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}, {"audiortpmap", JSON_STRING, 0}, {"audiofmtp", JSON_STRING, 0}, {"audiopt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}, @@ -1064,10 +1081,14 @@ typedef struct janus_streaming_rtp_source { janus_streaming_buffer *curldata; char *rtsp_url; char *rtsp_username, *rtsp_password; - int ka_timeout; + gint64 ka_timeout; char *rtsp_ahost, *rtsp_vhost; gboolean reconnecting; gint64 reconnect_timer; + gint64 reconnect_delay; + gint64 session_timeout; + int rtsp_timeout; + int rtsp_conn_timeout; janus_mutex rtsp_mutex; #endif janus_streaming_rtp_keyframe keyframe; @@ -1136,7 +1157,7 @@ typedef struct janus_streaming_mountpoint { janus_refcount ref; } janus_streaming_mountpoint; GHashTable *mountpoints = NULL, *mountpoints_temp = NULL; -janus_mutex mountpoints_mutex; +janus_mutex mountpoints_mutex = JANUS_MUTEX_INITIALIZER; static char *admin_key = NULL; typedef struct janus_streaming_helper { @@ -1185,10 +1206,10 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source( char *url, char *username, char *password, gboolean doaudio, int audiopt, char *artpmap, char *afmtp, gboolean dovideo, int videopt, char *vrtpmap, char *vfmtp, gboolean bufferkf, - const janus_network_address *iface, + const janus_network_address *iface, int threads, + gint64 reconnect_delay, gint64 session_timeout, int rtsp_timeout, int rtsp_conn_timeout, gboolean error_on_failure); - typedef struct janus_streaming_message { janus_plugin_session *handle; char *transaction; @@ -1505,7 +1526,7 @@ static void janus_streaming_rtcp_pli_send(janus_streaming_rtp_source *source) { int sent = 0; if((sent = sendto(source->video_rtcp_fd, rtcp_buf, rtcp_len, 0, (struct sockaddr *)&source->video_rtcp_addr, sizeof(source->video_rtcp_addr))) < 0) { - JANUS_LOG(LOG_ERR, "Error in sendto... %d (%s)\n", errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "Error in sendto... %d (%s)\n", errno, g_strerror(errno)); } else { JANUS_LOG(LOG_HUGE, "Sent %d/%d bytes\n", sent, rtcp_len); } @@ -1530,7 +1551,7 @@ static void janus_streaming_rtcp_remb_send(janus_streaming_rtp_source *source) { int sent = 0; if((sent = sendto(source->video_rtcp_fd, rtcp_buf, rtcp_len, 0, (struct sockaddr *)&source->video_rtcp_addr, sizeof(source->video_rtcp_addr))) < 0) { - JANUS_LOG(LOG_ERR, "Error in sendto... %d (%s)\n", errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "Error in sendto... %d (%s)\n", errno, g_strerror(errno)); } else { JANUS_LOG(LOG_HUGE, "Sent %d/%d bytes\n", sent, rtcp_len); } @@ -1574,7 +1595,7 @@ int janus_streaming_init(janus_callbacks *callback, const char *config_path) { struct ifaddrs *ifas = NULL; if(getifaddrs(&ifas) == -1) { JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected... %d (%s)\n", - errno, strerror(errno)); + errno, g_strerror(errno)); } /* Read configuration */ @@ -2098,6 +2119,11 @@ int janus_streaming_init(janus_callbacks *callback, const char *config_path) { janus_config_item *vkf = janus_config_get(config, cat, janus_config_type_item, "videobufferkf"); janus_config_item *iface = janus_config_get(config, cat, janus_config_type_item, "rtspiface"); janus_config_item *failerr = janus_config_get(config, cat, janus_config_type_item, "rtsp_failcheck"); + janus_config_item *threads = janus_config_get(config, cat, janus_config_type_item, "threads"); + janus_config_item *reconnect_delay = janus_config_get(config, cat, janus_config_type_item, "rtsp_reconnect_delay"); + janus_config_item *session_timeout = janus_config_get(config, cat, janus_config_type_item, "rtsp_session_timeout"); + janus_config_item *rtsp_timeout = janus_config_get(config, cat, janus_config_type_item, "rtsp_timeout"); + janus_config_item *rtsp_conn_timeout = janus_config_get(config, cat, janus_config_type_item, "rtsp_conn_timeout"); janus_network_address iface_value; if(file == NULL || file->value == NULL) { JANUS_LOG(LOG_ERR, "Can't add 'rtsp' mountpoint '%s', missing mandatory information...\n", cat->name); @@ -2144,6 +2170,11 @@ int janus_streaming_init(janus_callbacks *callback, const char *config_path) { vfmtp ? (char *)vfmtp->value : NULL, bufferkf, iface && iface->value ? &iface_value : NULL, + (threads && threads->value) ? atoi(threads->value) : 0, + ((reconnect_delay && reconnect_delay->value) ? atoi(reconnect_delay->value) : JANUS_STREAMING_DEFAULT_RECONNECT_DELAY) * G_USEC_PER_SEC, + ((session_timeout && session_timeout->value) ? atoi(session_timeout->value) : JANUS_STREAMING_DEFAULT_SESSION_TIMEOUT) * G_USEC_PER_SEC, + ((rtsp_timeout && rtsp_timeout->value) ? atoi(rtsp_timeout->value) : JANUS_STREAMING_DEFAULT_CURL_TIMEOUT), + ((rtsp_conn_timeout && rtsp_conn_timeout->value) ? atoi(rtsp_conn_timeout->value) : JANUS_STREAMING_DEFAULT_CURL_CONNECT_TIMEOUT), error_on_failure)) == NULL) { JANUS_LOG(LOG_ERR, "Error creating 'rtsp' mountpoint '%s'...\n", cat->name); cl = cl->next; @@ -2652,7 +2683,7 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi if(getifaddrs(&ifas) == -1) { JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected... %d (%s)\n", - errno, strerror(errno)); + errno, g_strerror(errno)); } json_t *type = json_object_get(root, "type"); @@ -3152,6 +3183,10 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi json_t *password = json_object_get(root, "rtsp_pwd"); json_t *iface = json_object_get(root, "rtspiface"); json_t *failerr = json_object_get(root, "rtsp_failcheck"); + json_t *reconnect_delay = json_object_get(root, "rtsp_reconnect_delay"); + json_t *session_timeout = json_object_get(root, "rtsp_session_timeout"); + json_t *rtsp_timeout = json_object_get(root, "rtsp_timeout"); + json_t *rtsp_conn_timeout = json_object_get(root, "rtsp_conn_timeout"); if(failerr == NULL) /* For an old typo, we support the legacy syntax too */ failerr = json_object_get(root, "rtsp_check"); gboolean doaudio = audio ? json_is_true(audio) : FALSE; @@ -3194,7 +3229,11 @@ static json_t *janus_streaming_process_synchronous_request(janus_streaming_sessi dovideo, (videopt ? json_integer_value(videopt) : -1), (char *)json_string_value(videortpmap), (char *)json_string_value(videofmtp), videobufferkf ? json_is_true(videobufferkf) : FALSE, - &multicast_iface, + &multicast_iface, (threads ? json_integer_value(threads) : 0), + ((reconnect_delay ? json_integer_value(reconnect_delay) : JANUS_STREAMING_DEFAULT_RECONNECT_DELAY) * G_USEC_PER_SEC), + ((session_timeout ? json_integer_value(session_timeout) : JANUS_STREAMING_DEFAULT_SESSION_TIMEOUT) * G_USEC_PER_SEC), + (rtsp_timeout ? json_integer_value(rtsp_timeout) : JANUS_STREAMING_DEFAULT_CURL_TIMEOUT), + (rtsp_conn_timeout ? json_integer_value(rtsp_conn_timeout) : JANUS_STREAMING_DEFAULT_CURL_CONNECT_TIMEOUT), error_on_failure); janus_mutex_lock(&mountpoints_mutex); g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid); @@ -5391,14 +5430,14 @@ static int janus_streaming_create_fd(int port, in_addr_t mcast, const janus_netw fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(fd < 0) { JANUS_LOG(LOG_ERR, "[%s] Cannot create socket for %s... %d (%s)\n", - mountpointname, medianame, errno, strerror(errno)); + mountpointname, medianame, errno, g_strerror(errno)); break; } #ifdef IP_MULTICAST_ALL int mc_all = 0; if((setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all))) < 0) { JANUS_LOG(LOG_ERR, "[%s] %s listener setsockopt IP_MULTICAST_ALL failed... %d (%s)\n", - mountpointname, listenername, errno, strerror(errno)); + mountpointname, listenername, errno, g_strerror(errno)); close(fd); janus_mutex_unlock(&fd_mutex); return -1; @@ -5428,7 +5467,7 @@ static int janus_streaming_create_fd(int port, in_addr_t mcast, const janus_netw } if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { JANUS_LOG(LOG_ERR, "[%s] %s listener IP_ADD_MEMBERSHIP failed... %d (%s)\n", - mountpointname, listenername, errno, strerror(errno)); + mountpointname, listenername, errno, g_strerror(errno)); close(fd); janus_mutex_unlock(&fd_mutex); return -1; @@ -5456,7 +5495,7 @@ static int janus_streaming_create_fd(int port, in_addr_t mcast, const janus_netw int reuse = 1; if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) { JANUS_LOG(LOG_ERR, "[%s] %s listener setsockopt SO_REUSEADDR failed... %d (%s)\n", - mountpointname, listenername, errno, strerror(errno)); + mountpointname, listenername, errno, g_strerror(errno)); close(fd); janus_mutex_unlock(&fd_mutex); return -1; @@ -5493,12 +5532,12 @@ static int janus_streaming_create_fd(int port, in_addr_t mcast, const janus_netw int v6only = 0; if(fd < 0) { JANUS_LOG(LOG_ERR, "[%s] Cannot create socket for %s... %d (%s)\n", - mountpointname, medianame, errno, strerror(errno)); + mountpointname, medianame, errno, g_strerror(errno)); break; } if(family != AF_INET && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0) { JANUS_LOG(LOG_ERR, "[%s] setsockopt on socket failed for %s... %d (%s)\n", - mountpointname, medianame, errno, strerror(errno)); + mountpointname, medianame, errno, g_strerror(errno)); break; } } @@ -5508,7 +5547,7 @@ static int janus_streaming_create_fd(int port, in_addr_t mcast, const janus_netw fd = -1; if(!quiet) { JANUS_LOG(LOG_ERR, "[%s] Bind failed for %s (port %d)... %d (%s)\n", - mountpointname, medianame, port, errno, strerror(errno)); + mountpointname, medianame, port, errno, g_strerror(errno)); } if(!use_range) /* Asked for a specific port but it's not available, give up */ break; @@ -5573,7 +5612,7 @@ static int janus_streaming_allocate_port_pair(const char *name, const char *medi static int janus_streaming_get_fd_port(int fd) { struct sockaddr_in6 server = { 0 }; socklen_t len = sizeof(server); - if(getsockname(fd, &server, &len) == -1) { + if(getsockname(fd, (struct sockaddr *)&server, &len) == -1) { return -1; } @@ -6304,6 +6343,11 @@ static int janus_streaming_rtsp_parse_sdp(const char *buffer, const char *name, return 0; } +/* Helper function to calculating the minimum value if 'a' is bigger than zero */ +static inline gint64 janus_streaming_min_if(gint64 a, gint64 b) { + return a > 0 ? (a > b ? b : a) : b; +} + /* Static helper to connect to an RTSP server, considering we might do this either * when creating a new mountpoint, or when reconnecting after some failure */ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp) { @@ -6326,8 +6370,8 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt(curl, CURLOPT_URL, source->rtsp_url); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, source->rtsp_timeout); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, source->rtsp_conn_timeout); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 0L); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); #if CURL_AT_LEAST_VERSION(7, 66, 0) @@ -6379,7 +6423,6 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp } JANUS_LOG(LOG_VERB, "DESCRIBE answer:%s\n", curldata->buffer); /* Parse the SDP we just got to figure out the negotiated media */ - int ka_timeout = 0; int vpt = -1; char vrtpmap[2048]; vrtpmap[0] = '\0'; @@ -6553,8 +6596,8 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp } else if(is_session) { if(!strcasecmp(name, "timeout")) { /* Take note of the timeout, for keep-alives */ - ka_timeout = atoi(value); - JANUS_LOG(LOG_VERB, " -- RTSP session timeout (video): %d\n", ka_timeout); + source->ka_timeout = janus_streaming_min_if(source->session_timeout, atoi(value) / 2 * G_USEC_PER_SEC); + JANUS_LOG(LOG_VERB, " -- RTSP session timeout (video): %"SCNi64" ms\n", source->ka_timeout / 1000); } } } @@ -6726,8 +6769,8 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp } else if(is_session) { if(!strcasecmp(name, "timeout")) { /* Take note of the timeout, for keep-alives */ - ka_timeout = atoi(value); - JANUS_LOG(LOG_VERB, " -- RTSP session timeout (audio): %d\n", ka_timeout); + source->ka_timeout = janus_streaming_min_if(source->session_timeout, atoi(value) / 2 * G_USEC_PER_SEC); + JANUS_LOG(LOG_VERB, " -- RTSP session timeout (audio): %"SCNi64" ms\n", source->ka_timeout / 1000); } } } @@ -6829,7 +6872,6 @@ static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp source->rtsp_vhost = g_strdup(vhost); source->curl = curl; source->curldata = curldata; - source->ka_timeout = ka_timeout; return 0; } @@ -6929,7 +6971,8 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source( char *url, char *username, char *password, gboolean doaudio, int acodec, char *artpmap, char *afmtp, gboolean dovideo, int vcodec, char *vrtpmap, char *vfmtp, gboolean bufferkf, - const janus_network_address *iface, + const janus_network_address *iface, int threads, + gint64 reconnect_delay, gint64 session_timeout, int rtsp_timeout, int rtsp_conn_timeout, gboolean error_on_failure) { char id_num[30]; if(!string_ids) { @@ -6940,6 +6983,23 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source( JANUS_LOG(LOG_ERR, "Can't add 'rtsp' stream, missing url...\n"); return NULL; } + if(reconnect_delay < 0) { + JANUS_LOG(LOG_ERR, "rtsp_reconnect_delay can't be smaller than zero.\n"); + return NULL; + } + if(session_timeout < 0) { + JANUS_LOG(LOG_ERR, "rtsp_session_timeout can't be smaller than zero.\n"); + return NULL; + } + if(rtsp_timeout < 0) { + JANUS_LOG(LOG_ERR, "rtsp_timeout can't be smaller than zero.\n"); + return NULL; + } + if(rtsp_conn_timeout < 0) { + JANUS_LOG(LOG_ERR, "rtsp_conn_timeout can't be smaller than zero.\n"); + return NULL; + } + JANUS_LOG(LOG_VERB, "Audio %s, Video %s\n", doaudio ? "enabled" : "NOT enabled", dovideo ? "enabled" : "NOT enabled"); /* Create an RTP source for the media we'll get */ @@ -7004,6 +7064,11 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source( live_rtsp_source->keyframe.latest_keyframe = NULL; live_rtsp_source->keyframe.temp_keyframe = NULL; live_rtsp_source->keyframe.temp_ts = 0; + live_rtsp_source->ka_timeout = session_timeout; + live_rtsp_source->reconnect_delay = reconnect_delay; + live_rtsp_source->session_timeout = session_timeout; + live_rtsp_source->rtsp_timeout = rtsp_timeout; + live_rtsp_source->rtsp_conn_timeout = rtsp_conn_timeout; janus_mutex_init(&live_rtsp_source->keyframe.mutex); live_rtsp_source->reconnect_timer = 0; janus_mutex_init(&live_rtsp_source->rtsp_mutex); @@ -7073,7 +7138,8 @@ janus_streaming_mountpoint *janus_streaming_create_rtsp_source( char *url, char *username, char *password, gboolean doaudio, int acodec, char *audiortpmap, char *audiofmtp, gboolean dovideo, int vcodec, char *videortpmap, char *videofmtp, gboolean bufferkf, - const janus_network_address *iface, + const janus_network_address *iface, int threads, + gint64 reconnect_delay, gint64 session_timeout, int rtsp_timeout, int rtsp_conn_timeout, gboolean error_on_failure) { JANUS_LOG(LOG_ERR, "RTSP need libcurl\n"); return NULL; @@ -7441,7 +7507,7 @@ static void *janus_streaming_relay_thread(void *data) { gint64 now = janus_get_monotonic_time(), before = now, ka_timeout = 0; if(source->rtsp) { source->reconnect_timer = now; - ka_timeout = ((gint64)source->ka_timeout*G_USEC_PER_SEC)/2; + ka_timeout = source->ka_timeout; } #endif /* Loop */ @@ -7457,8 +7523,8 @@ static void *janus_streaming_relay_thread(void *data) { continue; } now = janus_get_monotonic_time(); - if(!source->reconnecting && (now - source->reconnect_timer > 5*G_USEC_PER_SEC)) { - /* 5 seconds passed and no media? Assume the RTSP server has gone and schedule a reconnect */ + if(!source->reconnecting && (now - source->reconnect_timer > source->reconnect_delay)) { + /* Assume the RTSP server has gone and schedule a reconnect */ JANUS_LOG(LOG_WARN, "[%s] %"SCNi64"s passed with no media, trying to reconnect the RTSP stream\n", name, (now - source->reconnect_timer)/G_USEC_PER_SEC); audio_fd = -1; @@ -7522,7 +7588,7 @@ static void *janus_streaming_relay_thread(void *data) { data_fd = source->data_fd; audio_rtcp_fd = source->audio_rtcp_fd; video_rtcp_fd = source->video_rtcp_fd; - ka_timeout = ((gint64)source->ka_timeout*G_USEC_PER_SEC)/2; + ka_timeout = source->ka_timeout; } } source->reconnect_timer = janus_get_monotonic_time(); @@ -7532,7 +7598,7 @@ static void *janus_streaming_relay_thread(void *data) { } if(audio_fd < 0 && video_fd[0] < 0 && video_fd[1] < 0 && video_fd[2] < 0 && data_fd < 0) { /* No socket, we may be in the process of reconnecting, or waiting to reconnect */ - g_usleep(5000000); + g_usleep(source->reconnect_delay); continue; } /* We may also need to occasionally send a OPTIONS request as a keep-alive */ @@ -7621,10 +7687,10 @@ static void *janus_streaming_relay_thread(void *data) { resfd = poll(fds, num, 1000); if(resfd < 0) { if(errno == EINTR) { - JANUS_LOG(LOG_HUGE, "[%s] Got an EINTR (%s), ignoring...\n", name, strerror(errno)); + JANUS_LOG(LOG_HUGE, "[%s] Got an EINTR (%s), ignoring...\n", name, g_strerror(errno)); continue; } - JANUS_LOG(LOG_ERR, "[%s] Error polling... %d (%s)\n", name, errno, strerror(errno)); + JANUS_LOG(LOG_ERR, "[%s] Error polling... %d (%s)\n", name, errno, g_strerror(errno)); mountpoint->enabled = FALSE; janus_mutex_lock(&source->rec_mutex); if(source->arc) { @@ -7659,7 +7725,7 @@ static void *janus_streaming_relay_thread(void *data) { if(fds[i].revents & (POLLERR | POLLHUP)) { /* Socket error? */ JANUS_LOG(LOG_ERR, "[%s] Error polling: %s... %d (%s)\n", name, - fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP", errno, strerror(errno)); + fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP", errno, g_strerror(errno)); mountpoint->enabled = FALSE; janus_mutex_lock(&source->rec_mutex); if(source->arc) { @@ -8043,6 +8109,7 @@ static void *janus_streaming_relay_thread(void *data) { JANUS_LOG(LOG_HUGE, "[%s] Got audio RTCP feedback: SSRC %"SCNu32"\n", name, janus_rtcp_get_sender_ssrc(buffer, bytes)); /* Relay on all sessions */ + packet.is_rtp = FALSE; packet.is_video = FALSE; packet.data = (janus_rtp_header *)buffer; packet.length = bytes; @@ -8069,6 +8136,7 @@ static void *janus_streaming_relay_thread(void *data) { JANUS_LOG(LOG_HUGE, "[%s] Got video RTCP feedback: SSRC %"SCNu32"\n", name, janus_rtcp_get_sender_ssrc(buffer, bytes)); /* Relay on all sessions */ + packet.is_rtp = FALSE; packet.is_video = TRUE; packet.data = (janus_rtp_header *)buffer; packet.length = bytes; @@ -8413,7 +8481,6 @@ static void janus_streaming_relay_rtp_packet(gpointer data, gpointer user_data) return; } - static void janus_streaming_relay_rtcp_packet(gpointer data, gpointer user_data) { janus_streaming_rtp_relay_packet *packet = (janus_streaming_rtp_relay_packet *)user_data; if(!packet || !packet->data || packet->length < 1) { diff --git a/plugins/janus_videoroom.c b/plugins/janus_videoroom.c index 10b499d96ca..c1f7266e715 100644 --- a/plugins/janus_videoroom.c +++ b/plugins/janus_videoroom.c @@ -104,7 +104,7 @@ room-: { * synchronous error response even for asynchronous requests. * * \c create , \c destroy , \c edit , \c exists, \c list, \c allowed, - * \c kick , \c moderate , \c enable_recording , \listparticipants + * \c kick , \c moderate , \c enable_recording , \c listparticipants * and \c listforwarders are synchronous requests, which means you'll * get a response directly within the context of the transaction. * \c create allows you to create a new video room dynamically, as an @@ -480,6 +480,9 @@ room-: { "display" : "", "audio_codec" : "