Skip to content

Commit

Permalink
Changed slowlink to use lost packets instead of NACKs, and made it co…
Browse files Browse the repository at this point in the history
…nfigurable (#1664)
  • Loading branch information
lminiero committed Jun 25, 2019
1 parent 1d9d4a1 commit e790e17
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 55 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ or on the command line:
-t, --no-media-timer=number Time (in s) that should pass with no media
(audio or video) being received before Janus
notifies you about this
-W, --slowlink-threshold=number
Number of lost packets (per s) that should
trigger a 'slowlink' Janus API event to users
-r, --rtp-port-range=min-max Port range to use for RTP/RTCP (only available
if the installed libnice supports it)
-B, --twcc-period=number How often (in ms) to send TWCC feedback back to
Expand Down
4 changes: 3 additions & 1 deletion conf/janus.jcfg.sample.in
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ certificates: {
# starting MTU for DTLS (1200 by default, it adapts automatically),
# how much time, in seconds, should pass with no media (audio or
# video) being received before Janus notifies you about this (default=1s,
# 0 disables these events entirely), and how often, in milliseconds,
# 0 disables these events entirely), how many lost packets should trigger
# a 'slowlink' event to users (default=4), and how often, in milliseconds,
# to send the Transport Wide Congestion Control feedback information back
# to senders, if negotiated (default=1s). Finally, if you're using BoringSSL
# you can customize the frequency of retransmissions: OpenSSL has a fixed
Expand All @@ -152,6 +153,7 @@ media: {
#rtp_port_range = "20000-40000"
#dtls_mtu = 1200
#no_media_timer = 1
#slowlink_threshold = 4
#twcc_period = 200
#dtls_timeout = 500
}
Expand Down
21 changes: 21 additions & 0 deletions html/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,22 @@ function updateSettings() {
setNoMediaTimer(result);
});
});
} else if(k === 'slowlink_threshold') {
$('#'+k).append('<button id="' + k + '_button" type="button" class="btn btn-xs btn-primary">Edit slowlink-threshold value</button>');
$('#'+k + "_button").click(function() {
bootbox.prompt("Set the new desired slowlink-threshold value (in lost packets per seconds, currently " + settings["slowlink_threshold"] + ")", function(result) {
if(isNaN(result)) {
bootbox.alert("Invalid slowlink-threshold timer (should be a positive integer)");
return;
}
result = parseInt(result);
if(result < 0) {
bootbox.alert("Invalid slowlink-threshold timer (should be a positive integer)");
return;
}
setSlowlinkThreshold(result);
});
});
} else if(k === 'locking_debug') {
$('#'+k).append('<button id="' + k + '_button" type="button" class="btn btn-xs"></button>');
$('#'+k + "_button")
Expand Down Expand Up @@ -504,6 +520,11 @@ function setNoMediaTimer(timer) {
sendSettingsRequest(request);
}

function setSlowlinkThreshold(packets) {
var request = { "janus": "set_slowlink_threshold", "slowlink_threshold": packets, "transaction": randomString(12), "admin_secret": secret };
sendSettingsRequest(request);
}

function sendSettingsRequest(request) {
console.log(request);
$.ajax({
Expand Down
90 changes: 50 additions & 40 deletions ice.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,12 +401,27 @@ void janus_set_no_media_timer(uint timer) {
if(no_media_timer == 0)
JANUS_LOG(LOG_VERB, "Disabling no-media timer\n");
else
JANUS_LOG(LOG_VERB, "Setting no-media timer to %ds\n", no_media_timer);
JANUS_LOG(LOG_VERB, "Setting no-media timer to %us\n", no_media_timer);
}
uint janus_get_no_media_timer(void) {
return no_media_timer;
}

/* Number of lost packets per seconds on a media stream (uplink or downlink,
* audio or video), that should result in a slow-link event to the iser */
#define DEFAULT_SLOWLINK_THRESHOLD 4
static uint slowlink_threshold = DEFAULT_SLOWLINK_THRESHOLD;
void janus_set_slowlink_threshold(uint packets) {
slowlink_threshold = packets;
if(slowlink_threshold == 0)
JANUS_LOG(LOG_VERB, "Disabling slow-link events\n");
else
JANUS_LOG(LOG_VERB, "Setting slowlink-threshold to %u packets\n", slowlink_threshold);
}
uint janus_get_slowlink_threshold(void) {
return slowlink_threshold;
}

/* Period, in milliseconds, to refer to for sending TWCC feedback */
#define DEFAULT_TWCC_PERIOD 1000
static uint twcc_period = DEFAULT_TWCC_PERIOD;
Expand Down Expand Up @@ -1560,32 +1575,16 @@ static void janus_ice_component_free(const janus_refcount *component_ref) {
//~ janus_mutex_unlock(&handle->mutex);
}

/* Call plugin slow_link callback if enough NACKs within a second */
#define SLOW_LINK_NACKS_PER_SEC 8
/* Call plugin slow_link callback if a minimum of lost packets are detected within a second */
static void
janus_slow_link_update(janus_ice_component *component, janus_ice_handle *handle,
guint nacks, int video, int uplink, gint64 now) {
gboolean video, gboolean uplink, guint lost) {
/* We keep the counters in different janus_ice_stats objects, depending on the direction */
gint64 sl_nack_period_ts = uplink ? component->in_stats.sl_nack_period_ts : component->out_stats.sl_nack_period_ts;
/* Is the NACK too old? */
if(now-sl_nack_period_ts > 2*G_USEC_PER_SEC) {
/* Old nacks too old, don't count them */
if(uplink) {
component->in_stats.sl_nack_period_ts = now;
component->in_stats.sl_nack_recent_cnt = 0;
} else {
component->out_stats.sl_nack_period_ts = now;
component->out_stats.sl_nack_recent_cnt = 0;
}
}
if(uplink) {
component->in_stats.sl_nack_recent_cnt += nacks;
} else {
component->out_stats.sl_nack_recent_cnt += nacks;
}
gint64 last_slowlink_time = uplink ? component->in_stats.last_slowlink_time : component->out_stats.last_slowlink_time;
guint sl_nack_recent_cnt = uplink ? component->in_stats.sl_nack_recent_cnt : component->out_stats.sl_nack_recent_cnt;
if((sl_nack_recent_cnt >= SLOW_LINK_NACKS_PER_SEC) && (now-last_slowlink_time > 1*G_USEC_PER_SEC)) {
guint sl_lost_last_count = uplink ?
(video ? component->in_stats.sl_lost_count_video : component->in_stats.sl_lost_count_audio) :
(video ? component->out_stats.sl_lost_count_video : component->out_stats.sl_lost_count_audio);
guint sl_lost_recently = (lost >= sl_lost_last_count) ? (lost - sl_lost_last_count) : 0;
if(slowlink_threshold > 0 && sl_lost_recently >= slowlink_threshold) {
/* Tell the plugin */
janus_plugin *plugin = (janus_plugin *)handle->app;
if(plugin && plugin->slow_link && janus_plugin_session_is_alive(handle->app_handle) &&
Expand All @@ -1600,8 +1599,9 @@ janus_slow_link_update(janus_ice_component *component, janus_ice_handle *handle,
json_object_set_new(event, "sender", json_integer(handle->handle_id));
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, "media", json_string(video ? "video" : "audio"));
json_object_set_new(event, "uplink", uplink ? json_true() : json_false());
json_object_set_new(event, "nacks", json_integer(sl_nack_recent_cnt));
json_object_set_new(event, "lost", json_integer(sl_lost_recently));
/* Send the event */
JANUS_LOG(LOG_VERB, "[%"SCNu64"] Sending event to transport...; %p\n", handle->handle_id, handle);
janus_session_notify_event(session, event);
Expand All @@ -1610,20 +1610,22 @@ janus_slow_link_update(janus_ice_component *component, janus_ice_handle *handle,
json_t *info = json_object();
json_object_set_new(info, "media", json_string(video ? "video" : "audio"));
json_object_set_new(info, "slow_link", json_string(uplink ? "uplink" : "downlink"));
json_object_set_new(info, "nacks_lastsec", json_integer(sl_nack_recent_cnt));
json_object_set_new(info, "lost_lastsec", json_integer(sl_lost_recently));
janus_events_notify_handlers(JANUS_EVENT_TYPE_MEDIA, session->session_id, handle->handle_id, handle->opaque_id, info);
}
}
/* Update the counters */
if(uplink) {
component->in_stats.last_slowlink_time = now;
component->in_stats.sl_nack_period_ts = now;
component->in_stats.sl_nack_recent_cnt = 0;
} else {
component->out_stats.last_slowlink_time = now;
component->out_stats.sl_nack_period_ts = now;
component->out_stats.sl_nack_recent_cnt = 0;
}
}
/* Update the counter */
if(uplink) {
if(video)
component->in_stats.sl_lost_count_video = lost;
else
component->in_stats.sl_lost_count_audio = lost;
} else {
if(video)
component->out_stats.sl_lost_count_video = lost;
else
component->out_stats.sl_lost_count_audio = lost;
}
}

Expand Down Expand Up @@ -2623,8 +2625,6 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
} else {
component->out_stats.audio.nacks += nacks_count;
}
/* Inform the plugin about the slow downlink in case it's needed */
janus_slow_link_update(component, handle, nacks_count, video, 0, now);
}
if(component->nack_sent_recent_cnt &&
(now - component->nack_sent_log_ts) > 5*G_USEC_PER_SEC) {
Expand Down Expand Up @@ -2806,8 +2806,6 @@ static void janus_ice_cb_nice_recv(NiceAgent *agent, guint stream_id, guint comp
} else {
component->in_stats.audio.nacks += nacks_count;
}
/* Inform the plugin about the slow uplink in case it's needed */
janus_slow_link_update(component, handle, retransmits_cnt, video, 1, now);
janus_mutex_unlock(&component->mutex);
g_slist_free(nacks);
nacks = NULL;
Expand Down Expand Up @@ -3578,6 +3576,9 @@ static gboolean janus_ice_outgoing_rtcp_handle(gpointer user_data) {
sdes->chunk.ssrc = htonl(stream->audio_ssrc);
/* Enqueue it, we'll send it later */
janus_ice_relay_rtcp_internal(handle, 0, rtcpbuf, srlen+sdeslen, FALSE);
/* Check if we detected too many losses, and send a slowlink event in case */
guint lost = janus_rtcp_context_get_lost_all(rtcp_ctx, TRUE);
janus_slow_link_update(stream->component, handle, FALSE, TRUE, lost);
}
if(stream && stream->audio_recv) {
/* Create a RR too */
Expand All @@ -3594,6 +3595,9 @@ static gboolean janus_ice_outgoing_rtcp_handle(gpointer user_data) {
rr->rb[0].ssrc = htonl(stream->audio_ssrc_peer);
/* Enqueue it, we'll send it later */
janus_ice_relay_rtcp_internal(handle, 0, rtcpbuf, 32, FALSE);
/* Check if we detected too many losses, and send a slowlink event in case */
guint lost = janus_rtcp_context_get_lost_all(stream->audio_rtcp_ctx, FALSE);
janus_slow_link_update(stream->component, handle, FALSE, FALSE, lost);
}
/* Now do the same for video */
if(stream && stream->component && stream->component->out_stats.video[0].packets > 0) {
Expand Down Expand Up @@ -3631,6 +3635,9 @@ static gboolean janus_ice_outgoing_rtcp_handle(gpointer user_data) {
sdes->chunk.ssrc = htonl(stream->video_ssrc);
/* Enqueue it, we'll send it later */
janus_ice_relay_rtcp_internal(handle, 1, rtcpbuf, srlen+sdeslen, FALSE);
/* Check if we detected too many losses, and send a slowlink event in case */
guint lost = janus_rtcp_context_get_lost_all(rtcp_ctx, TRUE);
janus_slow_link_update(stream->component, handle, TRUE, TRUE, lost);
}
if(stream && stream->video_recv) {
/* Create a RR too (for each SSRC, if we're simulcasting) */
Expand All @@ -3653,6 +3660,9 @@ static gboolean janus_ice_outgoing_rtcp_handle(gpointer user_data) {
janus_ice_relay_rtcp_internal(handle, 1, rtcpbuf, 32, FALSE);
}
}
/* Check if we detected too many losses, and send a slowlink event in case */
guint lost = janus_rtcp_context_get_lost_all(stream->video_rtcp_ctx[0], FALSE);
janus_slow_link_update(stream->component, handle, TRUE, FALSE, lost);
}
if(twcc_period == 1000) {
/* The Transport Wide CC feedback period is 1s as well, send it here */
Expand Down
16 changes: 10 additions & 6 deletions ice.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ void janus_set_no_media_timer(uint timer);
/*! \brief Method to get the current no-media event timer (see above)
* @returns The current no-media event timer */
uint janus_get_no_media_timer(void);
/*! \brief Method to modify the slowlink-threshold property (i.e., the number of lost packets per seconds that should trigger a slow-link event)
* @param[in] packets The new value, in lost packets per seconds */
void janus_set_slowlink_threshold(uint packets);
/*! \brief Method to get the current slowlink-threshold value (see above)
* @returns The current slowlink-threshold value */
uint janus_get_slowlink_threshold(void);
/*! \brief Method to modify the TWCC feedback period (i.e., how often TWCC feedback is sent back to media senders)
* @param[in] timer The new period value, in milliseconds */
void janus_set_twcc_period(uint period);
Expand Down Expand Up @@ -227,12 +233,10 @@ typedef struct janus_ice_stats {
janus_ice_stats_info video[3];
/*! \brief Data info */
janus_ice_stats_info data;
/*! \brief Last time the slow_link callback (of the plugin) was called */
gint64 last_slowlink_time;
/*! \brief Start time of recent NACKs (for slow_link) */
gint64 sl_nack_period_ts;
/*! \brief Count of recent NACKs (for slow_link) */
guint sl_nack_recent_cnt;
/*! \brief Last known count of lost audio packets (for slow_link) */
guint sl_lost_count_audio;
/*! \brief Last known count of lost video packets (for slow_link) */
guint sl_lost_count_video;
} janus_ice_stats;

/*! \brief Quick helper method to notify a WebRTC hangup through the Janus API
Expand Down
3 changes: 3 additions & 0 deletions janus.1
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ Maximum size of the NACK queue per user for retransmissions
.BR \-t ", " \-\-no-media-timer=\fInumber\fR
Time (in s) that should pass with no media (audio or video) being received before Janus notifies you about this
.TP
.BR \-W ", " \-\-slowlink-threshold=\fInumber\fR
Number of lost packets (per s) that should trigger a 'slowlink' Janus API event to users
.TP
.BR \-r ", " \-\-rtp-port-range=\fImin\-max\fR
Port range to use for RTP/RTCP
.TP
Expand Down
39 changes: 39 additions & 0 deletions janus.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ static struct janus_json_parameter mnq_parameters[] = {
static struct janus_json_parameter nmt_parameters[] = {
{"no_media_timer", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
};
static struct janus_json_parameter st_parameters[] = {
{"slowlink_threshold", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
};
static struct janus_json_parameter ans_parameters[] = {
{"accept", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
};
Expand Down Expand Up @@ -1775,6 +1778,7 @@ int janus_process_incoming_admin_request(janus_request *request) {
json_object_set_new(status, "libnice_debug", janus_ice_is_ice_debugging_enabled() ? json_true() : json_false());
json_object_set_new(status, "max_nack_queue", json_integer(janus_get_max_nack_queue()));
json_object_set_new(status, "no_media_timer", json_integer(janus_get_no_media_timer()));
json_object_set_new(status, "slowlink_threshold", json_integer(janus_get_slowlink_threshold()));
json_object_set_new(reply, "status", status);
/* Send the success reply */
ret = janus_process_success(request, reply);
Expand Down Expand Up @@ -1960,6 +1964,26 @@ int janus_process_incoming_admin_request(janus_request *request) {
/* Send the success reply */
ret = janus_process_success(request, reply);
goto jsondone;
} else if(!strcasecmp(message_text, "set_slowlink_threshold")) {
/* Change the current value for the slowlink-threshold value */
JANUS_VALIDATE_JSON_OBJECT(root, st_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;
}
json_t *nmt = json_object_get(root, "slowlink_threshold");
int nmt_num = json_integer_value(nmt);
janus_set_slowlink_threshold(nmt_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, "slowlink_threshold", json_integer(janus_get_slowlink_threshold()));
/* Send the success reply */
ret = janus_process_success(request, reply);
goto jsondone;
} else if(!strcasecmp(message_text, "accept_new_sessions")) {
/* Configure whether we should accept new incoming sessions or not:
* this can be particularly useful whenever, e.g., we want to stop
Expand Down Expand Up @@ -3874,6 +3898,11 @@ gint main(int argc, char *argv[])
g_snprintf(nmt, 20, "%d", args_info.no_media_timer_arg);
janus_config_add(config, config_media, janus_config_item_create("no_media_timer", nmt));
}
if(args_info.slowlink_threshold_given) {
char st[20];
g_snprintf(st, 20, "%d", args_info.slowlink_threshold_arg);
janus_config_add(config, config_media, janus_config_item_create("slowlink_threshold", st));
}
if(args_info.twcc_period_given) {
char tp[20];
g_snprintf(tp, 20, "%d", args_info.twcc_period_arg);
Expand Down Expand Up @@ -4237,6 +4266,16 @@ gint main(int argc, char *argv[])
janus_set_no_media_timer(nmt);
}
}
/* slowlink-threshold value */
item = janus_config_get(config, config_media, janus_config_type_item, "slowlink_threshold");
if(item && item->value) {
int st = atoi(item->value);
if(st < 0) {
JANUS_LOG(LOG_WARN, "Ignoring slowlink_threshold value as it's not a positive integer\n");
} else {
janus_set_slowlink_threshold(st);
}
}
/* TWCC period */
item = janus_config_get(config, config_media, janus_config_type_item, "twcc_period");
if(item && item->value) {
Expand Down
1 change: 1 addition & 0 deletions janus.ggo
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ option "ice-tcp" T "Whether to enable ICE-TCP or not (warning: only works with I
option "rfc-4588" R "Whether to enable RFC4588 retransmissions support or not" flag off
option "max-nack-queue" q "Maximum size of the NACK queue (in ms) per user for retransmissions" int typestr="number" optional
option "no-media-timer" t "Time (in s) that should pass with no media (audio or video) being received before Janus notifies you about this" int typestr="number" optional
option "slowlink-threshold" W "Number of lost packets (per s) that should trigger a 'slowlink' Janus API event to users" int typestr="number" optional
option "rtp-port-range" r "Port range to use for RTP/RTCP" string typestr="min-max" optional
option "twcc-period" B "How often (in ms) to send TWCC feedback back to senders, if negotiated (default=1s)" int typestr="number" optional
option "server-name" n "Public name of this Janus instance (default=MyJanusInstance)" string typestr="name" optional
Expand Down
3 changes: 2 additions & 1 deletion mainpage.dox
Original file line number Diff line number Diff line change
Expand Up @@ -2193,7 +2193,8 @@ const token = getJanusToken('janus', ['janus.plugin.videoroom']),
* memory leaks in the Janus structures and want to investigate them);
* - \c set_libnice_debug: selectively enable/disable libnice debugging;
* - \c set_max_nack_queue: change the value of the max NACK queue window;
* - \c set_no_media_timer: change the value of the no-media timer value.
* - \c set_no_media_timer: change the value of the no-media timer property;
* - \c set_slowlink_threshold: change the value of the slowlink-threshold property.
*
* \subsection adminreqt Token-related requests
* - \c add_token: add a valid token (only available if you enabled the \ref token);
Expand Down
7 changes: 5 additions & 2 deletions plugins/janus_echotest.c
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,11 @@ void janus_echotest_slow_link(janus_plugin_session *handle, int uplink, int vide
json_t *event = json_object();
json_object_set_new(event, "echotest", json_string("event"));
json_object_set_new(event, "event", json_string("slow_link"));
/* Also add info on what the current bitrate cap is */
json_object_set_new(event, "current-bitrate", json_integer(session->bitrate));
json_object_set_new(event, "media", json_string(video ? "video" : "audio"));
if(video) {
/* Also add info on what the current bitrate cap is */
json_object_set_new(event, "current-bitrate", json_integer(session->bitrate));
}
gateway->push_event(session->handle, &janus_echotest_plugin, NULL, event, NULL);
json_decref(event);
}
Expand Down

0 comments on commit e790e17

Please sign in to comment.