diff --git a/conf/janus.plugin.streaming.jcfg.sample.in b/conf/janus.plugin.streaming.jcfg.sample.in index 1fb4751774..7eb058bea5 100644 --- a/conf/janus.plugin.streaming.jcfg.sample.in +++ b/conf/janus.plugin.streaming.jcfg.sample.in @@ -65,6 +65,15 @@ # srtpsuite = 32 # srtpcrypto = WbTBosdVUZqEb6Htqhn+m3z7wUh4RJVR8nE15GbN # +# The Streaming plugin can also be used to (re)stream media that has been +# encrypted using something that can be consumed via Insertable Streams. +# In that case, we only need to be aware of it, so that we can send the +# info along with the SDP. How to decrypt the media is out of scope, and +# up to the application since, again, this is end-to-end encryption and +# 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: # url = RTSP stream URL (only for restreaming RTSP) # rtsp_user = RTSP authorization username (only if type=rtsp) diff --git a/conf/janus.plugin.videoroom.jcfg.sample b/conf/janus.plugin.videoroom.jcfg.sample index c0e6ee764f..e91c3ce867 100644 --- a/conf/janus.plugin.videoroom.jcfg.sample +++ b/conf/janus.plugin.videoroom.jcfg.sample @@ -39,6 +39,8 @@ # new feeds (publishers), and enabling this may result extra notification # traffic. This flag is particularly useful when enabled with require_pvtid # for admin to manage listening only participants. default=false) +# require_e2ee = true|false (whether all participants are required to publish and subscribe +# using end-to-end media encryption, e.g., via Insertable Streams; default=false) #} general: { diff --git a/html/demos.html b/html/demos.html index 40a5d5f679..63763a3e07 100644 --- a/html/demos.html +++ b/html/demos.html @@ -94,6 +94,10 @@

Janus WebRTC Server: Demo Tests

Device Selection A variant of the Echo Test demo, that allows you to choose a specific capture device. + + End-to-end Encryption + A variant of the Echo Test demo, that allows you to encrypt the video in a way that Janus can't access it, but can still route it. + Multichannel Opus (surround) A variant of the Echo Test demo, that shows multichannel/surround Opus support. diff --git a/html/devicetest.html b/html/devicetest.html index 9fae73a81d..945a88074d 100644 --- a/html/devicetest.html +++ b/html/devicetest.html @@ -4,7 +4,7 @@ -Janus WebRTC Server: Echo Test +Janus WebRTC Server: Device Selection Test diff --git a/html/e2etest.html b/html/e2etest.html new file mode 100644 index 0000000000..47dbb2b20b --- /dev/null +++ b/html/e2etest.html @@ -0,0 +1,128 @@ + + + + + + +Janus WebRTC Server: End-to-end Encryption Test + + + + + + + + + + + + + + + + + +Fork me on GitHub + + + +
+
+
+ +
+
+
+

Demo details

+

This is a variant of the Echo Test demo: everything is exactly + the same in term of available controls, features, and the like, with + the substantial difference that it shows how you can configure an + end-to-end encryption context using the recently introduced + Insertable Streams.

+

Specifically, this demo will prompt for a secret, and then use the same transform functions as + this official demo + to encrypt the media: this means Janus will NOT have access to the media, but + will still be able to pass the packets to the EchoTest plugin and send them + back, thus ensuring an end-to-end encryption of the content. In this context, + the same page that encrypted the media will also decrypt it, which means + that if you see both local and remote video, then it's working as expected. + You'll probably want to then experiment with this feature by playing with + other plugins as well, e.g., the VideoRoom, for E2E-encrypted conferences.

+

Notice that at the time of writing, only video can be end-to-end encrypted, + and not audio. More importantly, at the moment this will only work if you're + using a recent of Chrome version that has been started either with the following flag:

+

--enable-experimental-web-platform-features --force-fieldtrials=WebRTC-GenericDescriptorAdvertised/Enabled/

+

Press the Start button above to launch the demo.

+
+
+
+
+
+
+
+
+

Local Stream +
+ + + +
+

+
+
+
+
+ + +
+
+
+
+
+

Remote Stream

+
+
+
+
+ + +
+
+
+
+
+
+ +
+ +
+ + + diff --git a/html/e2etest.js b/html/e2etest.js new file mode 100644 index 0000000000..41ebb489c6 --- /dev/null +++ b/html/e2etest.js @@ -0,0 +1,652 @@ +// We make use of this 'server' variable to provide the address of the +// REST Janus API. By default, in this example we assume that Janus is +// co-located with the web server hosting the HTML pages but listening +// on a different port (8088, the default for HTTP in Janus), which is +// why we make use of the 'window.location.hostname' base address. Since +// Janus can also do HTTPS, and considering we don't really want to make +// use of HTTP for Janus if your demos are served on HTTPS, we also rely +// on the 'window.location.protocol' prefix to build the variable, in +// particular to also change the port used to contact Janus (8088 for +// HTTP and 8089 for HTTPS, if enabled). +// In case you place Janus behind an Apache frontend (as we did on the +// online demos at http://janus.conf.meetecho.com) you can just use a +// relative path for the variable, e.g.: +// +// var server = "/janus"; +// +// which will take care of this on its own. +// +// +// If you want to use the WebSockets frontend to Janus, instead, you'll +// have to pass a different kind of address, e.g.: +// +// var server = "ws://" + window.location.hostname + ":8188"; +// +// Of course this assumes that support for WebSockets has been built in +// when compiling the server. WebSockets support has not been tested +// as much as the REST API, so handle with care! +// +// +// If you have multiple options available, and want to let the library +// autodetect the best way to contact your server (or pool of servers), +// you can also pass an array of servers, e.g., to provide alternative +// means of access (e.g., try WebSockets first and, if that fails, fall +// back to plain HTTP) or just have failover servers: +// +// var server = [ +// "ws://" + window.location.hostname + ":8188", +// "/janus" +// ]; +// +// This will tell the library to try connecting to each of the servers +// in the presented order. The first working server will be used for +// the whole session. +// +var server = null; +if(window.location.protocol === 'http:') + server = "http://" + window.location.hostname + ":8088/janus"; +else + server = "https://" + window.location.hostname + ":8089/janus"; + +var janus = null; +var echotest = null; +var opaqueId = "echotest-"+Janus.randomString(12); + +var bitrateTimer = null; +var spinner = null; + +var audioenabled = false; +var videoenabled = false; + +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 simulcastStarted = false; + +$(document).ready(function() { + // Initialize the library (all console debuggers enabled) + Janus.init({debug: "all", callback: function() { + // Use a button to start the demo + $('#start').one('click', function() { + $(this).attr('disabled', true).unbind('click'); + // Make sure the browser supports WebRTC + if(!Janus.isWebrtcSupported()) { + bootbox.alert("No WebRTC support... "); + return; + } + // Make sure Insertable Streams are supported too + if(!RTCRtpSender.prototype.createEncodedAudioStreams || + !RTCRtpSender.prototype.createEncodedVideoStreams) { + bootbox.alert("Insertable Streams not supported by this browser... "); + return; + } + // Create session + janus = new Janus( + { + server: server, + // No "iceServers" is provided, meaning janus.js will use a default STUN server + // Here are some examples of how an iceServers field may look like to support TURN + // iceServers: [{urls: "turn:yourturnserver.com:3478", username: "janususer", credential: "januspwd"}], + // iceServers: [{urls: "turn:yourturnserver.com:443?transport=tcp", username: "janususer", credential: "januspwd"}], + // iceServers: [{urls: "turns:yourturnserver.com:443?transport=tcp", username: "janususer", credential: "januspwd"}], + // Should the Janus API require authentication, you can specify either the API secret or user token here too + // token: "mytoken", + // or + // apisecret: "serversecret", + success: function() { + // Attach to EchoTest plugin + janus.attach( + { + plugin: "janus.plugin.echotest", + opaqueId: opaqueId, + success: function(pluginHandle) { + $('#details').remove(); + echotest = pluginHandle; + Janus.log("Plugin attached! (" + echotest.getPlugin() + ", id=" + echotest.getId() + ")"); + $('#start').removeAttr('disabled').html("Stop") + .click(function() { + $(this).attr('disabled', true); + if(bitrateTimer) + clearInterval(bitrateTimer); + bitrateTimer = null; + janus.destroy(); + }); + // Before starting, we prompt for a secret + promptCryptoKey(); + }, + error: function(error) { + console.error(" -- Error attaching plugin...", error); + bootbox.alert("Error attaching plugin... " + error); + }, + consentDialog: function(on) { + Janus.debug("Consent dialog should be " + (on ? "on" : "off") + " now"); + if(on) { + // Darken screen and show hint + $.blockUI({ + message: '
', + css: { + border: 'none', + padding: '15px', + backgroundColor: 'transparent', + color: '#aaa', + top: '10px', + left: (navigator.mozGetUserMedia ? '-100px' : '300px') + } }); + } else { + // Restore screen + $.unblockUI(); + } + }, + iceState: function(state) { + Janus.log("ICE state changed to " + state); + }, + mediaState: function(medium, on) { + Janus.log("Janus " + (on ? "started" : "stopped") + " receiving our " + medium); + }, + webrtcState: function(on) { + Janus.log("Janus says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now"); + $("#videoleft").parent().unblock(); + }, + slowLink: function(uplink, lost) { + Janus.warn("Janus reports problems " + (uplink ? "sending" : "receiving") + + " packets on this PeerConnection (" + lost + " lost packets)"); + }, + onmessage: function(msg, jsep) { + Janus.debug(" ::: Got a message :::", msg); + if(jsep !== undefined && jsep !== null) { + Janus.debug("Handling SDP as well...", jsep); + echotest.handleRemoteJsep({ jsep: jsep }); + } + var result = msg["result"]; + if(result) { + if(result === "done") { + // The plugin closed the echo test + bootbox.alert("The Echo Test is over"); + if(spinner) + spinner.stop(); + spinner = null; + $('#myvideo').remove(); + $('#waitingvideo').remove(); + $('#peervideo').remove(); + $('#toggleaudio').attr('disabled', true); + $('#togglevideo').attr('disabled', true); + $('#bitrate').attr('disabled', true); + $('#curbitrate').hide(); + $('#curres').hide(); + return; + } + // Any loss? + var status = result["status"]; + if(status === "slow_link") { + //~ var bitrate = result["bitrate"]; + //~ toastr.warning("The bitrate has been cut to " + (bitrate/1000) + "kbps", "Packet loss?", {timeOut: 2000}); + toastr.warning("Janus apparently missed many packets we sent, maybe we should reduce the bitrate", "Packet loss?", {timeOut: 2000}); + } + } + // Is simulcast in place? + var substream = msg["substream"]; + var temporal = msg["temporal"]; + if((substream !== null && substream !== undefined) || (temporal !== null && temporal !== undefined)) { + if(!simulcastStarted) { + simulcastStarted = true; + addSimulcastButtons(msg["videocodec"] === "vp8" || msg["videocodec"] === "h264"); + } + // We just received notice that there's been a switch, update the buttons + updateSimulcastButtons(substream, temporal); + } + }, + onlocalstream: function(stream) { + Janus.debug(" ::: Got a local stream :::", stream); + if($('#myvideo').length === 0) { + $('#videos').removeClass('hide').show(); + $('#videoleft').append('