Skip to content

Commit

Permalink
Initial support for DTX (EchoTest, VideoRoom) (#2789)
Browse files Browse the repository at this point in the history
  • Loading branch information
lminiero committed Nov 3, 2021
1 parent 86e349e commit b94a89d
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 20 deletions.
5 changes: 3 additions & 2 deletions conf/janus.plugin.videoroom.jcfg.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# is_private = true|false (whether this room should be in the public list, default=true)
# secret = <optional password needed for manipulating (e.g. destroying) the room>
# pin = <optional password needed for joining the room>
# require_pvtid = true|false (whether subscriptions are required to provide a valid private_id
# require_pvtid = true|false (whether subscriptions are required to provide a valid private_id
# to associate with a publisher, default=false)
# publishers = <max number of concurrent senders> (e.g., 6 for a video
# conference or 1 for a webinar)
Expand All @@ -17,7 +17,8 @@
# can be a comma separated list in order of preference, e.g., vp9,vp8,h264)
# vp9_profile = VP9-specific profile to prefer (e.g., "2" for "profile-id=2")
# h264_profile = H.264-specific profile to prefer (e.g., "42e01f" for "profile-level-id=42e01f")
# opus_fec = true|false (whether inband FEC must be negotiated; only works for Opus, default=false)
# opus_fec = true|false (whether inband FEC must be negotiated; only works for Opus, default=true)
# opus_dtx = true|false (whether DTX must be negotiated; only works for Opus, default=false)
# video_svc = true|false (whether SVC support must be enabled; only works for VP9, default=false)
# audiolevel_ext = true|false (whether the ssrc-audio-level RTP extension must
# be negotiated/used or not for new publishers, default=true)
Expand Down
8 changes: 8 additions & 0 deletions html/echotest.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ var doSimulcast2 = (getQueryStringValue("simulcast2") === "yes" || getQueryStrin
var acodec = (getQueryStringValue("acodec") !== "" ? getQueryStringValue("acodec") : null);
var vcodec = (getQueryStringValue("vcodec") !== "" ? getQueryStringValue("vcodec") : null);
var vprofile = (getQueryStringValue("vprofile") !== "" ? getQueryStringValue("vprofile") : null);
var doDtx = (getQueryStringValue("dtx") === "yes" || getQueryStringValue("dtx") === "true");
var simulcastStarted = false;

$(document).ready(function() {
Expand Down Expand Up @@ -123,6 +124,13 @@ $(document).ready(function() {
// the following 'simulcast' property to pass to janus.js to true
simulcast: doSimulcast,
simulcast2: doSimulcast2,
customizeSdp: function(jsep) {
// If DTX is enabled, munge the SDP
if(doDtx) {
jsep.sdp = jsep.sdp
.replace("useinbandfec=1", "useinbandfec=1;usedtx=1")
}
},
success: function(jsep) {
Janus.debug("Got SDP!", jsep);
echotest.send({ message: body, jsep: jsep });
Expand Down
15 changes: 15 additions & 0 deletions html/videoroomtest.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var doSimulcast = (getQueryStringValue("simulcast") === "yes" || getQueryStringV
var doSimulcast2 = (getQueryStringValue("simulcast2") === "yes" || getQueryStringValue("simulcast2") === "true");
var acodec = (getQueryStringValue("acodec") !== "" ? getQueryStringValue("acodec") : null);
var vcodec = (getQueryStringValue("vcodec") !== "" ? getQueryStringValue("vcodec") : null);
var doDtx = (getQueryStringValue("dtx") === "yes" || getQueryStringValue("dtx") === "true");
var subscriber_mode = (getQueryStringValue("subscriber-mode") === "yes" || getQueryStringValue("subscriber-mode") === "true");

$(document).ready(function() {
Expand Down Expand Up @@ -416,6 +417,13 @@ function publishOwnFeed(useAudio) {
// the following 'simulcast' property to pass to janus.js to true
simulcast: doSimulcast,
simulcast2: doSimulcast2,
customizeSdp: function(jsep) {
// If DTX is enabled, munge the SDP
if(doDtx) {
jsep.sdp = jsep.sdp
.replace("useinbandfec=1", "useinbandfec=1;usedtx=1")
}
},
success: function(jsep) {
Janus.debug("Got publisher SDP!", jsep);
var publish = { request: "configure", audio: useAudio, video: true };
Expand Down Expand Up @@ -550,13 +558,20 @@ function newRemoteFeed(id, display, audio, video) {
}
if(jsep) {
Janus.debug("Handling SDP as well...", jsep);
var stereo = (jsep.sdp.indexOf("stereo=1") !== -1);
// Answer and attach
remoteFeed.createAnswer(
{
jsep: jsep,
// Add data:true here if you want to subscribe to datachannels as well
// (obviously only works if the publisher offered them in the first place)
media: { audioSend: false, videoSend: false }, // We want recvonly audio/video
customizeSdp: function(jsep) {
if(stereo && jsep.sdp.indexOf("stereo=1") == -1) {
// Make sure that our offer contains stereo too
jsep.sdp = jsep.sdp.replace("useinbandfec=1", "useinbandfec=1;stereo=1");
}
},
success: function(jsep) {
Janus.debug("Got SDP!", jsep);
var body = { request: "start", room: myroom };
Expand Down
36 changes: 30 additions & 6 deletions plugins/janus_echotest.c
Original file line number Diff line number Diff line change
Expand Up @@ -1065,8 +1065,10 @@ static void *janus_echotest_handler(void *data) {
g_snprintf(error_cause, 512, "Error parsing offer: %s", error_str);
goto error;
}
/* Check if we need to negotiate Opus FEC */
gboolean opus_fec = FALSE;
/* Check if we need to negotiate Opus FEC and/or DTX */
gboolean opus_fec = FALSE, opus_dtx = FALSE;
char custom_fmtp[256];
custom_fmtp[0] = '\0';
GList *temp = offer->m_lines;
while(temp) {
/* Which media are available? */
Expand All @@ -1077,9 +1079,31 @@ static void *janus_echotest_handler(void *data) {
while(ma) {
janus_sdp_attribute *a = (janus_sdp_attribute *)ma->data;
if(a->value) {
if(m->type == JANUS_SDP_AUDIO && !strcasecmp(a->name, "fmtp") &&
strstr(a->value, "useinbandfec=1")) {
opus_fec = TRUE;
if(m->type == JANUS_SDP_AUDIO && !strcasecmp(a->name, "fmtp")) {
if(strstr(a->value, "useinbandfec=1")) {
opus_fec = TRUE;
if(strlen(custom_fmtp) == 0) {
g_snprintf(custom_fmtp, sizeof(custom_fmtp), "useinbandfec=1");
} else {
g_strlcat(custom_fmtp, ";useinbandfec=1", sizeof(custom_fmtp));
}
}
if(strstr(a->value, "usedtx=1")) {
opus_dtx = TRUE;
if(strlen(custom_fmtp) == 0) {
g_snprintf(custom_fmtp, sizeof(custom_fmtp), "usedtx=1");
} else {
g_strlcat(custom_fmtp, ";usedtx=1", sizeof(custom_fmtp));
}
}
if(strstr(a->value, "stereo=1")) {
opus_dtx = TRUE;
if(strlen(custom_fmtp) == 0) {
g_snprintf(custom_fmtp, sizeof(custom_fmtp), "usedtx=1");
} else {
g_strlcat(custom_fmtp, ";usedtx=1", sizeof(custom_fmtp));
}
}
}
}
ma = ma->next;
Expand All @@ -1089,7 +1113,7 @@ static void *janus_echotest_handler(void *data) {
}
janus_sdp *answer = janus_sdp_generate_answer(offer,
JANUS_SDP_OA_AUDIO_CODEC, json_string_value(audiocodec),
JANUS_SDP_OA_AUDIO_FMTP, opus_fec ? "useinbandfec=1" : NULL,
JANUS_SDP_OA_AUDIO_FMTP, (opus_fec || opus_dtx ? custom_fmtp : NULL),
JANUS_SDP_OA_VIDEO_CODEC, json_string_value(videocodec),
JANUS_SDP_OA_VP9_PROFILE, json_string_value(videoprofile),
JANUS_SDP_OA_H264_PROFILE, json_string_value(videoprofile),
Expand Down
70 changes: 65 additions & 5 deletions plugins/janus_videoroom.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ room-<unique room ID>: {
can be a comma separated list in order of preference, e.g., vp9,vp8,h264)
vp9_profile = VP9-specific profile to prefer (e.g., "2" for "profile-id=2")
h264_profile = H.264-specific profile to prefer (e.g., "42e01f" for "profile-level-id=42e01f")
opus_fec = true|false (whether inband FEC must be negotiated; only works for Opus, default=false)
opus_fec = true|false (whether inband FEC must be negotiated; only works for Opus, default=true)
opus_dtx = true|false (whether DTX must be negotiated; only works for Opus, default=false)
video_svc = true|false (whether SVC support must be enabled; only works for VP9, default=false)
audiolevel_ext = true|false (whether the ssrc-audio-level RTP extension must be
negotiated/used or not for new publishers, default=true)
Expand Down Expand Up @@ -387,6 +388,7 @@ room-<unique room ID>: {
"audiocodec" : "<comma separated list of allowed audio codecs>",
"videocodec" : "<comma separated list of allowed video codecs>",
"opus_fec": <true|false, whether inband FEC must be negotiated (note: only available for Opus) (optional)>,
"opus_dtx": <true|false, whether DTX must be negotiated (note: only available for Opus) (optional)>,
"video_svc": <true|false, whether SVC must be done for video (note: only available for VP9 right now) (optional)>,
"record" : <true|false, whether the room is being recorded>,
"rec_dir" : "<if recording, the path where the .mjr files are being saved>",
Expand Down Expand Up @@ -1267,6 +1269,7 @@ static struct janus_json_parameter create_parameters[] = {
{"vp9_profile", JSON_STRING, 0},
{"h264_profile", JSON_STRING, 0},
{"opus_fec", JANUS_JSON_BOOL, 0},
{"opus_dtx", JANUS_JSON_BOOL, 0},
{"video_svc", JANUS_JSON_BOOL, 0},
{"audiolevel_ext", JANUS_JSON_BOOL, 0},
{"audiolevel_event", JANUS_JSON_BOOL, 0},
Expand Down Expand Up @@ -1491,6 +1494,7 @@ typedef struct janus_videoroom {
char *vp9_profile; /* VP9 codec profile to prefer, if more are negotiated */
char *h264_profile; /* H.264 codec profile to prefer, if more are negotiated */
gboolean do_opusfec; /* Whether inband FEC must be negotiated (note: only available for Opus) */
gboolean do_opusdtx; /* Whether DTX must be negotiated (note: only available for Opus) */
gboolean do_svc; /* Whether SVC must be done for video (note: only available for VP9 right now) */
gboolean audiolevel_ext; /* Whether the ssrc-audio-level extension must be negotiated or not for new publishers */
gboolean audiolevel_event; /* Whether to emit event to other users about audiolevel */
Expand Down Expand Up @@ -1633,6 +1637,7 @@ typedef struct janus_videoroom_publisher {
guint32 audio_ssrc; /* Audio SSRC of this publisher */
guint32 video_ssrc; /* Video SSRC of this publisher */
gboolean do_opusfec; /* Whether this publisher is sending inband Opus FEC */
gboolean do_opusdtx; /* Whether this publisher is using Opus DTX (Discontinuous Transmission) */
uint32_t ssrc[3]; /* Only needed in case VP8 (or H.264) simulcasting is involved */
char *rid[3]; /* Only needed if simulcasting is rid-based */
int rid_extmap_id; /* rid extmap ID */
Expand Down Expand Up @@ -2254,6 +2259,7 @@ int janus_videoroom_init(janus_callbacks *callback, const char *config_path) {
janus_config_item *vp9profile = janus_config_get(config, cat, janus_config_type_item, "vp9_profile");
janus_config_item *h264profile = janus_config_get(config, cat, janus_config_type_item, "h264_profile");
janus_config_item *fec = janus_config_get(config, cat, janus_config_type_item, "opus_fec");
janus_config_item *dtx = janus_config_get(config, cat, janus_config_type_item, "opus_dtx");
janus_config_item *svc = janus_config_get(config, cat, janus_config_type_item, "video_svc");
janus_config_item *audiolevel_ext = janus_config_get(config, cat, janus_config_type_item, "audiolevel_ext");
janus_config_item *audiolevel_event = janus_config_get(config, cat, janus_config_type_item, "audiolevel_event");
Expand Down Expand Up @@ -2395,6 +2401,7 @@ int janus_videoroom_init(janus_callbacks *callback, const char *config_path) {
videoroom->vcodec[4] == JANUS_VIDEOCODEC_H264)) {
videoroom->h264_profile = g_strdup(h264profile->value);
}
videoroom->do_opusfec = TRUE;
if(fec && fec->value) {
videoroom->do_opusfec = janus_is_true(fec->value);
if(videoroom->acodec[0] != JANUS_AUDIOCODEC_OPUS &&
Expand All @@ -2406,6 +2413,17 @@ int janus_videoroom_init(janus_callbacks *callback, const char *config_path) {
JANUS_LOG(LOG_WARN, "Inband FEC is only supported for rooms that allow Opus: disabling it...\n");
}
}
if(dtx && dtx->value) {
videoroom->do_opusdtx = janus_is_true(dtx->value);
if(videoroom->acodec[0] != JANUS_AUDIOCODEC_OPUS &&
videoroom->acodec[1] != JANUS_AUDIOCODEC_OPUS &&
videoroom->acodec[2] != JANUS_AUDIOCODEC_OPUS &&
videoroom->acodec[3] != JANUS_AUDIOCODEC_OPUS &&
videoroom->acodec[4] != JANUS_AUDIOCODEC_OPUS) {
videoroom->do_opusdtx = FALSE;
JANUS_LOG(LOG_WARN, "DTX is only supported for rooms that allow Opus: disabling it...\n");
}
}
if(svc && svc->value && janus_is_true(svc->value)) {
if(videoroom->vcodec[0] == JANUS_VIDEOCODEC_VP9 &&
videoroom->vcodec[1] == JANUS_VIDEOCODEC_NONE &&
Expand Down Expand Up @@ -3096,6 +3114,7 @@ static json_t *janus_videoroom_process_synchronous_request(janus_videoroom_sessi
json_t *vp9profile = json_object_get(root, "vp9_profile");
json_t *h264profile = json_object_get(root, "h264_profile");
json_t *fec = json_object_get(root, "opus_fec");
json_t *dtx = json_object_get(root, "opus_dtx");
json_t *svc = json_object_get(root, "video_svc");
json_t *audiolevel_ext = json_object_get(root, "audiolevel_ext");
json_t *audiolevel_event = json_object_get(root, "audiolevel_event");
Expand Down Expand Up @@ -3287,6 +3306,7 @@ static json_t *janus_videoroom_process_synchronous_request(janus_videoroom_sessi
videoroom->vcodec[4] == JANUS_VIDEOCODEC_H264)) {
videoroom->h264_profile = g_strdup(h264_profile);
}
videoroom->do_opusfec = TRUE;
if(fec) {
videoroom->do_opusfec = json_is_true(fec);
if(videoroom->acodec[0] != JANUS_AUDIOCODEC_OPUS &&
Expand All @@ -3298,6 +3318,17 @@ static json_t *janus_videoroom_process_synchronous_request(janus_videoroom_sessi
JANUS_LOG(LOG_WARN, "Inband FEC is only supported for rooms that allow Opus: disabling it...\n");
}
}
if(dtx) {
videoroom->do_opusdtx = json_is_true(dtx);
if(videoroom->acodec[0] != JANUS_AUDIOCODEC_OPUS &&
videoroom->acodec[1] != JANUS_AUDIOCODEC_OPUS &&
videoroom->acodec[2] != JANUS_AUDIOCODEC_OPUS &&
videoroom->acodec[3] != JANUS_AUDIOCODEC_OPUS &&
videoroom->acodec[4] != JANUS_AUDIOCODEC_OPUS) {
videoroom->do_opusdtx = FALSE;
JANUS_LOG(LOG_WARN, "DTX is only supported for rooms that allow Opus: disabling it...\n");
}
}
if(svc && json_is_true(svc)) {
if(videoroom->vcodec[0] == JANUS_VIDEOCODEC_VP9 &&
videoroom->vcodec[1] == JANUS_VIDEOCODEC_NONE &&
Expand Down Expand Up @@ -3413,6 +3444,8 @@ static json_t *janus_videoroom_process_synchronous_request(janus_videoroom_sessi
janus_config_add(config, c, janus_config_item_create("h264_profile", videoroom->h264_profile));
if(videoroom->do_opusfec)
janus_config_add(config, c, janus_config_item_create("opus_fec", "yes"));
if(videoroom->do_opusdtx)
janus_config_add(config, c, janus_config_item_create("opus_dtx", "yes"));
if(videoroom->do_svc)
janus_config_add(config, c, janus_config_item_create("video_svc", "yes"));
if(videoroom->room_secret)
Expand Down Expand Up @@ -3595,6 +3628,8 @@ static json_t *janus_videoroom_process_synchronous_request(janus_videoroom_sessi
janus_config_add(config, c, janus_config_item_create("h264_profile", videoroom->h264_profile));
if(videoroom->do_opusfec)
janus_config_add(config, c, janus_config_item_create("opus_fec", "yes"));
if(videoroom->do_opusdtx)
janus_config_add(config, c, janus_config_item_create("opus_dtx", "yes"));
if(videoroom->do_svc)
janus_config_add(config, c, janus_config_item_create("video_svc", "yes"));
if(videoroom->room_secret)
Expand Down Expand Up @@ -3797,6 +3832,8 @@ static json_t *janus_videoroom_process_synchronous_request(janus_videoroom_sessi
json_object_set_new(rl, "videocodec", json_string(video_codecs));
if(room->do_opusfec)
json_object_set_new(rl, "opus_fec", json_true());
if(room->do_opusdtx)
json_object_set_new(rl, "opus_dtx", json_true());
if(room->do_svc)
json_object_set_new(rl, "video_svc", json_true());
json_object_set_new(rl, "record", room->record ? json_true() : json_false());
Expand Down Expand Up @@ -7540,6 +7577,8 @@ static void *janus_videoroom_handler(void *data) {
goto error;
}
char *audio_fmtp = NULL;
char custom_fmtp[256];
custom_fmtp[0] = '\0';
GList *temp = offer->m_lines;
while(temp) {
/* Which media are available? */
Expand Down Expand Up @@ -7569,8 +7608,29 @@ static void *janus_videoroom_handler(void *data) {
if(janus_string_to_uint8(a->value, &participant->playout_delay_extmap_id) < 0)
JANUS_LOG(LOG_WARN, "Invalid playout-delay extension ID: %s\n", a->value);
} else if(m->type == JANUS_SDP_AUDIO && !strcasecmp(a->name, "fmtp")) {
if(strstr(a->value, "useinbandfec=1"))
participant->do_opusfec = videoroom->do_opusfec;
if(strstr(a->value, "useinbandfec=1") && videoroom->do_opusfec) {
participant->do_opusfec = TRUE;
if(strlen(custom_fmtp) == 0) {
g_snprintf(custom_fmtp, sizeof(custom_fmtp), "useinbandfec=1");
} else {
g_strlcat(custom_fmtp, ";useinbandfec=1", sizeof(custom_fmtp));
}
}
if(strstr(a->value, "usedtx=1") && videoroom->do_opusdtx) {
participant->do_opusdtx = TRUE;
if(strlen(custom_fmtp) == 0) {
g_snprintf(custom_fmtp, sizeof(custom_fmtp), "usedtx=1");
} else {
g_strlcat(custom_fmtp, ";usedtx=1", sizeof(custom_fmtp));
}
}
if(strstr(a->value, "stereo=1")) {
if(strlen(custom_fmtp) == 0) {
g_snprintf(custom_fmtp, sizeof(custom_fmtp), "stereo=1");
} else {
g_strlcat(custom_fmtp, ";stereo=1", sizeof(custom_fmtp));
}
}
char *tmp = strchr(a->value, ' ');
if(tmp && strlen(tmp) > 1) {
tmp++;
Expand Down Expand Up @@ -7646,7 +7706,7 @@ static void *janus_videoroom_handler(void *data) {
janus_sdp *answer = janus_sdp_generate_answer(offer,
JANUS_SDP_OA_AUDIO_CODEC, janus_audiocodec_name(participant->acodec),
JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_RECVONLY,
JANUS_SDP_OA_AUDIO_FMTP, audio_fmtp ? audio_fmtp : (participant->do_opusfec ? "useinbandfec=1" : NULL),
JANUS_SDP_OA_AUDIO_FMTP, audio_fmtp ? audio_fmtp : (strlen(custom_fmtp) > 0 ? custom_fmtp : NULL),
JANUS_SDP_OA_VIDEO_CODEC, janus_videocodec_name(participant->vcodec),
JANUS_SDP_OA_VP9_PROFILE, vp9_profile,
JANUS_SDP_OA_H264_PROFILE, h264_profile,
Expand Down Expand Up @@ -7744,7 +7804,7 @@ static void *janus_videoroom_handler(void *data) {
JANUS_SDP_OA_AUDIO_CODEC, janus_audiocodec_name(participant->acodec),
JANUS_SDP_OA_AUDIO_PT, janus_audiocodec_pt(participant->acodec),
JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_SENDONLY,
JANUS_SDP_OA_AUDIO_FMTP, audio_fmtp ? audio_fmtp : (participant->do_opusfec ? "useinbandfec=1" : NULL),
JANUS_SDP_OA_AUDIO_FMTP, audio_fmtp ? audio_fmtp : (strlen(custom_fmtp) > 0 ? custom_fmtp : NULL),
JANUS_SDP_OA_AUDIO_EXTENSION, JANUS_RTP_EXTMAP_AUDIO_LEVEL,
participant->audio_level_extmap_id > 0 ? participant->audio_level_extmap_id : 0,
JANUS_SDP_OA_AUDIO_EXTENSION, JANUS_RTP_EXTMAP_MID, mid_ext_id,
Expand Down
3 changes: 3 additions & 0 deletions postprocessing/janus-pp-rec.1
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ Time threshold to trigger an audio skew compensation, disabled if 0 (default=0)
.BR \-C ", " \-\-silence-distance=count
RTP packets distance used to detect RTP silence suppression, disabled if 0 (default=100)
.TP
.BR \-X ", " \-\-dtx
Enable DTX mode (disables code to handle silence suppression) (default=off)
.TP
.BR \-r ", " \-\-restamp=count
If the latency of a packet is bigger than the `moving_average_latency * (<restamp>/1000)` the timestamps will be corrected, disabled if 0 (default=0)
.TP
Expand Down
Loading

0 comments on commit b94a89d

Please sign in to comment.