Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial support for DTX (EchoTest, VideoRoom) #2789

Merged
merged 2 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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