diff --git a/html/nosiptest.html b/html/nosiptest.html index cd7f5f52ca..96dae0405a 100644 --- a/html/nosiptest.html +++ b/html/nosiptest.html @@ -73,7 +73,11 @@

Demo details

-

Caller

+

Caller +
+ +
+

diff --git a/html/nosiptest.js b/html/nosiptest.js index 2f1598b5cd..098e00ff14 100644 --- a/html/nosiptest.js +++ b/html/nosiptest.js @@ -56,6 +56,9 @@ var opaqueId = Janus.randomString(12); var spinner = null; +var videoenabled = true; +var srtp = undefined ; // use "sdes_mandatory" to test SRTP-SDES + $(document).ready(function() { // Initialize the library (all console debuggers enabled) Janus.init({debug: "all", callback: function() { @@ -91,7 +94,7 @@ $(document).ready(function() { Janus.debug("[caller] Trying a createOffer too (audio/video sendrecv)"); caller.createOffer( { - // No media provided: by default, it's sendrecv for audio and video + media: {audio: true, video: videoenabled}, success: function(jsep) { Janus.debug("[caller] Got SDP!", jsep); // We now have a WebRTC SDP: to get a barebone SDP legacy @@ -101,7 +104,8 @@ $(document).ready(function() { // the SIP plugin uses (mandatory vs. optional). We'll // get the result in an event called "generated" here. var body = { - request: "generate" + request: "generate", + srtp: srtp }; caller.send({ message: body, jsep: jsep }); }, @@ -172,7 +176,8 @@ $(document).ready(function() { request: "process", type: result["type"], sdp: result["sdp"], - update: result["update"] + update: result["update"], + srtp: srtp } callee.send({ message: processOffer }); } else if(event === "processed") { @@ -273,6 +278,43 @@ $(document).ready(function() { $('#videoright .no-video-container').remove(); $('#peervideo').removeClass('hide').show(); } + + if(videoenabled) { + $('#togglevideo').html("Disable video").removeClass("btn-success").addClass("btn-danger"); + } else { + $('#togglevideo').html("Enable video").removeClass("btn-danger").addClass("btn-success"); + } + + $('#togglevideo').unbind('click').removeAttr('disabled').click( + function() { + videoenabled = !videoenabled; + var media; + if(videoenabled) { + $('#togglevideo').html("Disable video").removeClass("btn-success").addClass("btn-danger"); + media = {addVideo: true}; + } else { + $('#togglevideo').html("Enable video").removeClass("btn-danger").addClass("btn-success"); + media = {removeVideo: true}; + } + caller.createOffer( + { + media: media, + success: function(jsep) { + Janus.debug("[caller] Got UPDATE SDP!"); + Janus.debug(jsep); + var body = { + request: "generate", + update: true, + srtp: srtp + }; + caller.send({message: body, jsep: jsep}); + }, + error: function(error) { + Janus.error("WebRTC error:", error); + bootbox.alert("WebRTC error... " + JSON.stringify(error)); + } + }); + }); }, oncleanup: function() { Janus.log("[caller] ::: Got a cleanup notification :::"); @@ -363,7 +405,8 @@ $(document).ready(function() { // We'll get the result in an event called "generated" here. var body = { request: "generate", - update: update + update: update, + srtp: srtp }; callee.send({ message: body, jsep: jsep }); }, @@ -385,7 +428,8 @@ $(document).ready(function() { request: "process", type: result["type"], sdp: result["sdp"], - update: result["update"] + update: result["update"], + srtp: srtp } caller.send({ message: processAnswer }); } diff --git a/plugins/janus_nosip.c b/plugins/janus_nosip.c index 87d8ab524b..df8b3dd540 100644 --- a/plugins/janus_nosip.c +++ b/plugins/janus_nosip.c @@ -300,6 +300,7 @@ typedef struct janus_nosip_media { const char *audio_pt_name; srtp_t audio_srtp_in, audio_srtp_out; srtp_policy_t audio_remote_policy, audio_local_policy; + char *audio_srtp_local_profile, *audio_srtp_local_crypto; gboolean audio_send; int has_video:1; int video_rtp_fd, video_rtcp_fd; @@ -311,6 +312,7 @@ typedef struct janus_nosip_media { const char *video_pt_name; srtp_t video_srtp_in, video_srtp_out; srtp_policy_t video_remote_policy, video_local_policy; + char *video_srtp_local_profile, *video_srtp_local_crypto; gboolean video_send; janus_rtp_switching_context context; int pipefd[2]; @@ -575,6 +577,14 @@ static void janus_nosip_srtp_cleanup(janus_nosip_session *session) { session->media.audio_srtp_in = NULL; g_free(session->media.audio_remote_policy.key); session->media.audio_remote_policy.key = NULL; + if(session->media.audio_srtp_local_profile) { + g_free(session->media.audio_srtp_local_profile); + session->media.audio_srtp_local_profile = NULL; + } + if(session->media.audio_srtp_local_crypto) { + g_free(session->media.audio_srtp_local_crypto); + session->media.audio_srtp_local_crypto = NULL; + } /* Video */ if(session->media.video_srtp_out) srtp_dealloc(session->media.video_srtp_out); @@ -586,6 +596,14 @@ static void janus_nosip_srtp_cleanup(janus_nosip_session *session) { session->media.video_srtp_in = NULL; g_free(session->media.video_remote_policy.key); session->media.video_remote_policy.key = NULL; + if(session->media.video_srtp_local_profile) { + g_free(session->media.video_srtp_local_profile); + session->media.video_srtp_local_profile = NULL; + } + if(session->media.video_srtp_local_crypto) { + g_free(session->media.video_srtp_local_crypto); + session->media.video_srtp_local_crypto = NULL; + } } void janus_nosip_media_reset(janus_nosip_session *session) { @@ -868,6 +886,10 @@ void janus_nosip_create_session(janus_plugin_session *handle, int *error) { session->media.has_srtp_local = FALSE; session->media.has_srtp_remote = FALSE; session->media.srtp_profile = 0; + session->media.audio_srtp_local_profile = NULL; + session->media.audio_srtp_local_crypto = NULL; + session->media.video_srtp_local_profile = NULL; + session->media.video_srtp_local_crypto = NULL; session->media.has_audio = 0; session->media.audio_rtp_fd = -1; session->media.audio_rtcp_fd = -1; @@ -1331,68 +1353,66 @@ static void *janus_nosip_handler(void *data) { const char *info = json_string_value(json_object_get(root, "info")); /* SDES-SRTP is disabled by default, let's see if we need to enable it */ gboolean do_srtp = FALSE, require_srtp = FALSE; - janus_srtp_profile srtp_profile = JANUS_SRTP_AES128_CM_SHA1_80; + json_t *srtp = json_object_get(root, "srtp"); + if(srtp) { + const char *srtp_text = json_string_value(srtp); + if(!strcasecmp(srtp_text, "sdes_optional")) { + /* Negotiate SDES, but make it optional */ + do_srtp = TRUE; + } else if(!strcasecmp(srtp_text, "sdes_mandatory")) { + /* Negotiate SDES, and require it */ + do_srtp = TRUE; + require_srtp = TRUE; + } else { + JANUS_LOG(LOG_ERR, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)\n"); + error_code = JANUS_NOSIP_ERROR_INVALID_ELEMENT; + g_snprintf(error_cause, 512, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)"); + goto error; + } + } + if(offer && !sdp_update) { + /* Clean up SRTP stuff from before first, in case it's still needed */ + janus_nosip_srtp_cleanup(session); + if(do_srtp) { + JANUS_LOG(LOG_VERB, "Going to negotiate SDES-SRTP (%s)...\n", require_srtp ? "mandatory" : "optional"); + } + } + session->media.require_srtp = require_srtp; if(generate) { - json_t *srtp = json_object_get(root, "srtp"); - if(srtp) { - const char *srtp_text = json_string_value(srtp); - if(!strcasecmp(srtp_text, "sdes_optional")) { - /* Negotiate SDES, but make it optional */ - do_srtp = TRUE; - } else if(!strcasecmp(srtp_text, "sdes_mandatory")) { - /* Negotiate SDES, and require it */ - do_srtp = TRUE; - require_srtp = TRUE; - } else { - JANUS_LOG(LOG_ERR, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)\n"); - error_code = JANUS_NOSIP_ERROR_INVALID_ELEMENT; - g_snprintf(error_cause, 512, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)"); + if(!offer) { + do_srtp = do_srtp || session->media.has_srtp_remote; + /* Make sure the request is consistent with the state (original offer) */ + if(session->media.require_srtp && !session->media.has_srtp_remote) { + JANUS_LOG(LOG_ERR, "Can't generate answer: SDES-SRTP required, but caller didn't offer it\n"); + error_code = JANUS_NOSIP_ERROR_TOO_STRICT; + g_snprintf(error_cause, 512, "Can't generate answer: SDES-SRTP required, but caller didn't offer it"); goto error; } - if(do_srtp) { - /* Any SRTP profile different from the default? */ - srtp_profile = JANUS_SRTP_AES128_CM_SHA1_80; - const char *profile = json_string_value(json_object_get(root, "srtp_profile")); - if(profile) { - if(!strcmp(profile, "AES_CM_128_HMAC_SHA1_32")) { - srtp_profile = JANUS_SRTP_AES128_CM_SHA1_32; - } else if(!strcmp(profile, "AES_CM_128_HMAC_SHA1_80")) { - srtp_profile = JANUS_SRTP_AES128_CM_SHA1_80; + } + session->media.has_srtp_local = do_srtp; + if(do_srtp) { + /* Any SRTP profile different from the default? */ + janus_srtp_profile srtp_profile = JANUS_SRTP_AES128_CM_SHA1_80; + const char *profile = json_string_value(json_object_get(root, "srtp_profile")); + if(profile) { + if(!strcmp(profile, "AES_CM_128_HMAC_SHA1_32")) { + srtp_profile = JANUS_SRTP_AES128_CM_SHA1_32; + } else if(!strcmp(profile, "AES_CM_128_HMAC_SHA1_80")) { + srtp_profile = JANUS_SRTP_AES128_CM_SHA1_80; #ifdef HAVE_SRTP_AESGCM } else if(!strcmp(profile, "AEAD_AES_128_GCM")) { srtp_profile = JANUS_SRTP_AEAD_AES_128_GCM; } else if(!strcmp(profile, "AEAD_AES_256_GCM")) { srtp_profile = JANUS_SRTP_AEAD_AES_256_GCM; #endif - } else { - JANUS_LOG(LOG_ERR, "Invalid element (unsupported SRTP profile)\n"); - error_code = JANUS_NOSIP_ERROR_INVALID_ELEMENT; - g_snprintf(error_cause, 512, "Invalid element (unsupported SRTP profile)"); - goto error; - } - } - } - } - if(!sdp_update) { - if(offer) { - /* Clean up SRTP stuff from before first, in case it's still needed */ - janus_nosip_srtp_cleanup(session); - session->media.require_srtp = require_srtp; - session->media.has_srtp_local = do_srtp; - session->media.srtp_profile = srtp_profile; - if(do_srtp) { - JANUS_LOG(LOG_VERB, "Going to negotiate SDES-SRTP (%s)...\n", require_srtp ? "mandatory" : "optional"); - } - } else { - /* Make sure the request is consistent with the state (original offer) */ - if(session->media.require_srtp && !session->media.has_srtp_remote) { - JANUS_LOG(LOG_ERR, "Can't generate answer: SDES-SRTP required, but caller didn't offer it\n"); - error_code = JANUS_NOSIP_ERROR_TOO_STRICT; - g_snprintf(error_cause, 512, "Can't generate answer: SDES-SRTP required, but caller didn't offer it"); + } else { + JANUS_LOG(LOG_ERR, "Invalid element (unsupported SRTP profile)\n"); + error_code = JANUS_NOSIP_ERROR_INVALID_ELEMENT; + g_snprintf(error_cause, 512, "Invalid element (unsupported SRTP profile)"); goto error; } - do_srtp = do_srtp || session->media.has_srtp_remote; } + session->media.srtp_profile = srtp_profile; } } /* Get video-orientation extension id from SDP we got */ @@ -1466,10 +1486,6 @@ static void *janus_nosip_handler(void *data) { } else { /* We got a barebone offer or answer from our peer: process it accordingly */ gboolean changed = FALSE; - if(!sdp_update && offer) { - /* Clean up SRTP stuff from before first, in case it's still needed */ - janus_nosip_srtp_cleanup(session); - } janus_nosip_sdp_process(session, parsed_sdp, !offer, sdp_update, &changed); /* Check if offer has neither audio nor video, fail */ if(!session->media.has_audio && !session->media.has_video) { @@ -1487,6 +1503,12 @@ static void *janus_nosip_handler(void *data) { g_snprintf(error_cause, 512, "No remote IP addresses"); goto error; } + if(session->media.require_srtp && !session->media.has_srtp_remote) { + JANUS_LOG(LOG_ERR, "Can't process request: SDES-SRTP required, but caller didn't offer it\n"); + error_code = JANUS_NOSIP_ERROR_TOO_STRICT; + g_snprintf(error_cause, 512, "Can't process request: SDES-SRTP required, but caller didn't offer it"); + goto error; + } /* Take note of the SDP (may be useful for UPDATEs or re-INVITEs) */ janus_sdp_destroy(session->sdp); session->sdp = parsed_sdp; @@ -1800,17 +1822,17 @@ void janus_nosip_sdp_process(janus_nosip_session *session, janus_sdp *sdp, gbool g_free(session->media.remote_video_ip); session->media.remote_video_ip = g_strdup(m->c_addr); } - if(update) { - /* FIXME This is a session update, we only accept changes in IP/ports */ - temp = temp->next; - continue; - } GList *tempA = m->attributes; while(tempA) { janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data; if(a->name) { if(!strcasecmp(a->name, "crypto")) { if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) { + if((m->type == JANUS_SDP_AUDIO && session->media.audio_srtp_in != NULL) || (m->type == JANUS_SDP_VIDEO && session->media.video_srtp_in != NULL)) { + /* Remote SRTP is already set */ + tempA = tempA->next; + continue; + } gint32 tag = 0; char profile[101], crypto[101]; /* FIXME inline can be more complex than that, and we're currently only offering SHA1_80 */ @@ -1873,23 +1895,19 @@ char *janus_nosip_sdp_manipulate(janus_nosip_session *session, janus_sdp *sdp, g if(m->type == JANUS_SDP_AUDIO) { m->port = session->media.local_audio_rtp_port; if(session->media.has_srtp_local) { - char *profile = NULL; - char *crypto = NULL; - janus_nosip_srtp_set_local(session, FALSE, &profile, &crypto); - janus_sdp_attribute *a = janus_sdp_attribute_create("crypto", "1 %s inline:%s", profile, crypto); - g_free(profile); - g_free(crypto); + if(!session->media.audio_srtp_local_profile || !session->media.audio_srtp_local_crypto) { + janus_nosip_srtp_set_local(session, FALSE, &session->media.audio_srtp_local_profile, &session->media.audio_srtp_local_crypto); + } + janus_sdp_attribute *a = janus_sdp_attribute_create("crypto", "1 %s inline:%s", session->media.audio_srtp_local_profile, session->media.audio_srtp_local_crypto); m->attributes = g_list_append(m->attributes, a); } } else if(m->type == JANUS_SDP_VIDEO) { m->port = session->media.local_video_rtp_port; if(session->media.has_srtp_local) { - char *profile = NULL; - char *crypto = NULL; - janus_nosip_srtp_set_local(session, TRUE, &profile, &crypto); - janus_sdp_attribute *a = janus_sdp_attribute_create("crypto", "1 %s inline:%s", profile, crypto); - g_free(profile); - g_free(crypto); + if(!session->media.video_srtp_local_profile || !session->media.video_srtp_local_crypto) { + janus_nosip_srtp_set_local(session, TRUE, &session->media.video_srtp_local_profile, &session->media.video_srtp_local_crypto); + } + janus_sdp_attribute *a = janus_sdp_attribute_create("crypto", "1 %s inline:%s", session->media.video_srtp_local_profile, session->media.video_srtp_local_crypto); m->attributes = g_list_append(m->attributes, a); } } @@ -2208,8 +2226,6 @@ static void *janus_nosip_relay_thread(void *data) { /* Loop */ int num = 0; gboolean goon = TRUE; - int astep = 0, vstep = 0; - guint32 ats = 0, vts = 0; session->media.updated = TRUE; /* Connect UDP sockets upon loop entry */ gboolean have_audio_server_ip = TRUE; @@ -2409,25 +2425,6 @@ static void *janus_nosip_relay_thread(void *data) { /* Check if the SSRC changed (e.g., after a re-INVITE or UPDATE) */ guint32 timestamp = ntohl(header->timestamp); janus_rtp_header_update(header, &session->media.context, video, 0); - if(video) { - if(vts == 0) { - vts = timestamp; - } else if(vstep == 0) { - vstep = timestamp-vts; - if(vstep < 0) { - vstep = 0; - } - } - } else { - if(ats == 0) { - ats = timestamp; - } else if(astep == 0) { - astep = timestamp-ats; - if(astep < 0) { - astep = 0; - } - } - } /* Save the frame if we're recording */ janus_recorder_save_frame(video ? session->vrc_peer : session->arc_peer, buffer, bytes); /* Relay to browser */