diff options
author | Jonas Smedegaard <dr@jones.dk> | 2019-07-09 00:06:45 -0300 |
---|---|---|
committer | Jonas Smedegaard <dr@jones.dk> | 2019-07-09 00:06:45 -0300 |
commit | 560b5aa2fe651fc4cc649f74efc5e62cf773e250 (patch) | |
tree | f3d8ba9d6d1fc9cb0abc6f2f9de5864d785839a1 /website/mic.js | |
parent | 58f371bbc0091dd5f642c34dff9ebc1ad2018aee (diff) |
Add website.
Diffstat (limited to 'website/mic.js')
-rw-r--r-- | website/mic.js | 819 |
1 files changed, 819 insertions, 0 deletions
diff --git a/website/mic.js b/website/mic.js new file mode 100644 index 0000000..ecbd682 --- /dev/null +++ b/website/mic.js @@ -0,0 +1,819 @@ +var websocket_server = null; +if(window.location.protocol === 'http:') + websocket_server = "ws://" + window.location.hostname + "/janus-ws/janus"; +else + websocket_server = "wss://" + window.location.hostname + "/janus-ws/janus"; + +var janus = null; +var streaming = null; +var mixertest = null; +var opaqueId = "streamingwithfeedback-"+Janus.randomString(12); + +var bitrateTimer = null; +var spinner = null; + +var simulcastStarted = false, svcStarted = false; + +var selectedStream = null; + +var myroom = 1234; // Demo room +var myusername = null; +var myid = null; +var webrtcUp = false; +var audioenabled = 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; + } + // Create session + janus = new Janus( + { + server: [websocket_server, "/janus"], + iceServers: [{url: "turn:morla.jones.dk", username: "myturn", credential: "notsecure"}, + {url: "turn:jawa.homebase.dk", username: "myturn", credential: "notsecure"}], + success: function() { + // Attach to streaming plugin + janus.attach( + { + plugin: "janus.plugin.streaming", + opaqueId: opaqueId, + success: function(pluginHandle) { + $('#details').remove(); + streaming = pluginHandle; + Janus.log("Plugin attached! (" + streaming.getPlugin() + ", id=" + streaming.getId() + ")"); + // Setup streaming session + $('#update-streams').click(updateStreamsList); + updateStreamsList(); + $('#start').removeAttr('disabled').html("Stop") + .click(function() { + $(this).attr('disabled', true); + clearInterval(bitrateTimer); + janus.destroy(); + $('#streamslist').attr('disabled', true); + $('#watch').attr('disabled', true).unbind('click'); + $('#start').attr('disabled', true).html("Bye").unbind('click'); + }); + }, + error: function(error) { + Janus.error(" -- Error attaching plugin... ", error); + bootbox.alert("Error attaching plugin... " + error); + }, + onmessage: function(msg, jsep) { + Janus.debug(" ::: Got a message :::"); + Janus.debug(msg); + var result = msg["result"]; + if(result !== null && result !== undefined) { + if(result["status"] !== undefined && result["status"] !== null) { + var status = result["status"]; + if(status === 'starting') + $('#status').removeClass('hide').text("Starting, please wait...").show(); + else if(status === 'started') + $('#status').removeClass('hide').text("Started").show(); + else if(status === 'stopped') + stopStream(); + } else if(msg["streaming"] === "event") { + // Is simulcast in place? + var substream = result["substream"]; + var temporal = result["temporal"]; + if((substream !== null && substream !== undefined) || (temporal !== null && temporal !== undefined)) { + if(!simulcastStarted) { + simulcastStarted = true; + addSimulcastButtons(temporal !== null && temporal !== undefined); + } + // We just received notice that there's been a switch, update the buttons + updateSimulcastButtons(substream, temporal); + } + // Is VP9/SVC in place? + var spatial = result["spatial_layer"]; + temporal = result["temporal_layer"]; + if((spatial !== null && spatial !== undefined) || (temporal !== null && temporal !== undefined)) { + if(!svcStarted) { + svcStarted = true; + addSvcButtons(); + } + // We just received notice that there's been a switch, update the buttons + updateSvcButtons(spatial, temporal); + } + } + } else if(msg["error"] !== undefined && msg["error"] !== null) { + bootbox.alert(msg["error"]); + stopStream(); + return; + } + if(jsep !== undefined && jsep !== null) { + Janus.debug("Handling SDP as well..."); + Janus.debug(jsep); + // Offer from the plugin, let's answer + streaming.createAnswer( + { + jsep: jsep, + // We want recvonly audio/video and, if negotiated, datachannels + media: { audioSend: false, videoSend: false, data: true }, + success: function(jsep) { + Janus.debug("Got SDP!"); + Janus.debug(jsep); + var body = { "request": "start" }; + streaming.send({"message": body, "jsep": jsep}); + $('#watch').html("Stop").removeAttr('disabled').click(stopStream); + }, + error: function(error) { + Janus.error("WebRTC error:", error); + bootbox.alert("WebRTC error... " + JSON.stringify(error)); + } + }); + } + }, + onremotestream: function(stream) { + Janus.debug(" ::: Got a remote stream :::"); + Janus.debug(stream); + var addButtons = false; + if($('#remotevideo').length === 0) { + addButtons = true; + $('#stream').append('<video class="rounded centered hide" id="remotevideo" width=320 height=240 autoplay playsinline/>'); + // Show the stream and hide the spinner when we get a playing event + $("#remotevideo").bind("playing", function () { + $('#waitingvideo').remove(); + if(this.videoWidth) + $('#remotevideo').removeClass('hide').show(); + if(spinner !== null && spinner !== undefined) + spinner.stop(); + spinner = null; + var videoTracks = stream.getVideoTracks(); + if(videoTracks === null || videoTracks === undefined || videoTracks.length === 0) + return; + var width = this.videoWidth; + var height = this.videoHeight; + $('#curres').removeClass('hide').text(width+'x'+height).show(); + if(Janus.webRTCAdapter.browserDetails.browser === "firefox") { + // Firefox Stable has a bug: width and height are not immediately available after a playing + setTimeout(function() { + var width = $("#remotevideo").get(0).videoWidth; + var height = $("#remotevideo").get(0).videoHeight; + $('#curres').removeClass('hide').text(width+'x'+height).show(); + }, 2000); + } + }); + } + Janus.attachMediaStream($('#remotevideo').get(0), stream); + var videoTracks = stream.getVideoTracks(); + if(videoTracks === null || videoTracks === undefined || videoTracks.length === 0) { + // No remote video + $('#remotevideo').hide(); + if($('#stream .no-video-container').length === 0) { + $('#stream').append( + '<div class="no-video-container">' + + '<i class="fa fa-video-camera fa-5 no-video-icon"></i>' + + '<span class="no-video-text">No remote video available</span>' + + '</div>'); + } + } else { + $('#stream .no-video-container').remove(); + $('#remotevideo').removeClass('hide').show(); + } + if(!addButtons) + return; + if(videoTracks && videoTracks.length && + (Janus.webRTCAdapter.browserDetails.browser === "chrome" || + Janus.webRTCAdapter.browserDetails.browser === "firefox" || + Janus.webRTCAdapter.browserDetails.browser === "safari")) { + $('#curbitrate').removeClass('hide').show(); + bitrateTimer = setInterval(function() { + // Display updated bitrate, if supported + var bitrate = streaming.getBitrate(); + //~ Janus.debug("Current bitrate is " + streaming.getBitrate()); + $('#curbitrate').text(bitrate); + // Check if the resolution changed too + var width = $("#remotevideo").get(0).videoWidth; + var height = $("#remotevideo").get(0).videoHeight; + if(width > 0 && height > 0) + $('#curres').removeClass('hide').text(width+'x'+height).show(); + }, 1000); + } + }, + ondataopen: function(data) { + Janus.log("The DataChannel is available!"); + $('#waitingvideo').remove(); + $('#stream').append( + '<input class="form-control" type="text" id="datarecv" disabled></input>' + ); + if(spinner !== null && spinner !== undefined) + spinner.stop(); + spinner = null; + }, + ondata: function(data) { + Janus.debug("We got data from the DataChannel! " + data); + $('#datarecv').val(data); + }, + oncleanup: function() { + Janus.log(" ::: Got a cleanup notification :::"); + $('#waitingvideo').remove(); + $('#remotevideo').remove(); + $('#datarecv').remove(); + $('.no-video-container').remove(); + $('#bitrate').attr('disabled', true); + $('#bitrateset').html('Bandwidth<span class="caret"></span>'); + $('#curbitrate').hide(); + if(bitrateTimer !== null && bitrateTimer !== undefined) + clearInterval(bitrateTimer); + bitrateTimer = null; + $('#curres').hide(); + $('#simulcast').remove(); + simulcastStarted = false; + } + }); + // Attach to Audio Bridge test plugin + janus.attach( + { + plugin: "janus.plugin.audiobridge", + opaqueId: opaqueId, + success: function(pluginHandle) { + $('#details').remove(); + mixertest = pluginHandle; + Janus.log("Plugin attached! (" + mixertest.getPlugin() + ", id=" + mixertest.getId() + ")"); + // Prepare the username registration + $('#audiojoin').removeClass('hide').show(); + $('#registernow').removeClass('hide').show(); + $('#register').click(registerUsername); + $('#username').focus(); + $('#start').removeAttr('disabled').html("Stop") + .click(function() { + $(this).attr('disabled', true); + janus.destroy(); + }); + }, + error: function(error) { + Janus.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: '<div><img src="img/up_arrow.png"/></div>', + css: { + border: 'none', + padding: '15px', + backgroundColor: 'transparent', + color: '#aaa', + top: '10px', + left: (navigator.mozGetUserMedia ? '-100px' : '300px') + } }); + } else { + // Restore screen + $.unblockUI(); + } + }, + onmessage: function(msg, jsep) { + Janus.debug(" ::: Got a message :::"); + Janus.debug(msg); + var event = msg["audiobridge"]; + Janus.debug("Event: " + event); + if(event != undefined && event != null) { + if(event === "joined") { + // Successfully joined, negotiate WebRTC now + myid = msg["id"]; + Janus.log("Successfully joined room " + msg["room"] + " with ID " + myid); + if(!webrtcUp) { + webrtcUp = true; + // Publish our stream + mixertest.createOffer( + { + media: { video: false}, // This is an audio only room + success: function(jsep) { + Janus.debug("Got SDP!"); + Janus.debug(jsep); + var publish = { "request": "configure", "muted": false }; + mixertest.send({"message": publish, "jsep": jsep}); + }, + error: function(error) { + Janus.error("WebRTC error:", error); + bootbox.alert("WebRTC error... " + JSON.stringify(error)); + } + }); + } + // Any room participant? + if(msg["participants"] !== undefined && msg["participants"] !== null) { + var list = msg["participants"]; + Janus.debug("Got a list of participants:"); + Janus.debug(list); + for(var f in list) { + var id = list[f]["id"]; + var display = list[f]["display"]; + var setup = list[f]["setup"]; + var muted = list[f]["muted"]; + Janus.debug(" >> [" + id + "] " + display + " (setup=" + setup + ", muted=" + muted + ")"); + if($('#rp'+id).length === 0) { + // Add to the participants list + $('#list').append('<li id="rp'+id+'" class="list-group-item">'+display+ + ' <i class="absetup fa fa-chain-broken"></i>' + + ' <i class="abmuted fa fa-microphone-slash"></i></li>'); + $('#rp'+id + ' > i').hide(); + } + if(muted === true || muted === "true") + $('#rp'+id + ' > i.abmuted').removeClass('hide').show(); + else + $('#rp'+id + ' > i.abmuted').hide(); + if(setup === true || setup === "true") + $('#rp'+id + ' > i.absetup').hide(); + else + $('#rp'+id + ' > i.absetup').removeClass('hide').show(); + } + } + } else if(event === "roomchanged") { + // The user switched to a different room + myid = msg["id"]; + Janus.log("Moved to room " + msg["room"] + ", new ID: " + myid); + // Any room participant? + $('#list').empty(); + if(msg["participants"] !== undefined && msg["participants"] !== null) { + var list = msg["participants"]; + Janus.debug("Got a list of participants:"); + Janus.debug(list); + for(var f in list) { + var id = list[f]["id"]; + var display = list[f]["display"]; + var setup = list[f]["setup"]; + var muted = list[f]["muted"]; + Janus.debug(" >> [" + id + "] " + display + " (setup=" + setup + ", muted=" + muted + ")"); + if($('#rp'+id).length === 0) { + // Add to the participants list + $('#list').append('<li id="rp'+id+'" class="list-group-item">'+display+ + ' <i class="absetup fa fa-chain-broken"></i>' + + ' <i class="abmuted fa fa-microphone-slash"></i></li>'); + $('#rp'+id + ' > i').hide(); + } + if(muted === true || muted === "true") + $('#rp'+id + ' > i.abmuted').removeClass('hide').show(); + else + $('#rp'+id + ' > i.abmuted').hide(); + if(setup === true || setup === "true") + $('#rp'+id + ' > i.absetup').hide(); + else + $('#rp'+id + ' > i.absetup').removeClass('hide').show(); + } + } + } else if(event === "destroyed") { + // The room has been destroyed + Janus.warn("The room has been destroyed!"); + bootbox.alert("The room has been destroyed", function() { + window.location.reload(); + }); + } else if(event === "event") { + if(msg["participants"] !== undefined && msg["participants"] !== null) { + var list = msg["participants"]; + Janus.debug("Got a list of participants:"); + Janus.debug(list); + for(var f in list) { + var id = list[f]["id"]; + var display = list[f]["display"]; + var setup = list[f]["setup"]; + var muted = list[f]["muted"]; + Janus.debug(" >> [" + id + "] " + display + " (setup=" + setup + ", muted=" + muted + ")"); + if($('#rp'+id).length === 0) { + // Add to the participants list + $('#list').append('<li id="rp'+id+'" class="list-group-item">'+display+ + ' <i class="absetup fa fa-chain-broken"></i>' + + ' <i class="abmuted fa fa-microphone-slash"></i></li>'); + $('#rp'+id + ' > i').hide(); + } + if(muted === true || muted === "true") + $('#rp'+id + ' > i.abmuted').removeClass('hide').show(); + else + $('#rp'+id + ' > i.abmuted').hide(); + if(setup === true || setup === "true") + $('#rp'+id + ' > i.absetup').hide(); + else + $('#rp'+id + ' > i.absetup').removeClass('hide').show(); + } + } else if(msg["error"] !== undefined && msg["error"] !== null) { + if(msg["error_code"] === 485) { + // This is a "no such room" error: give a more meaningful description + bootbox.alert( + "<p>Apparently room <code>" + myroom + "</code> (the one this demo uses as a test room) " + + "does not exist...</p><p>Do you have an updated <code>janus.plugin.audiobridge.cfg</code> " + + "configuration file? If not, make sure you copy the details of room <code>" + myroom + "</code> " + + "from that sample in your current configuration file, then restart Janus and try again." + ); + } else { + bootbox.alert(msg["error"]); + } + return; + } + // Any new feed to attach to? + if(msg["leaving"] !== undefined && msg["leaving"] !== null) { + // One of the participants has gone away? + var leaving = msg["leaving"]; + Janus.log("Participant left: " + leaving + " (we have " + $('#rp'+leaving).length + " elements with ID #rp" +leaving + ")"); + $('#rp'+leaving).remove(); + } + } + } + if(jsep !== undefined && jsep !== null) { + Janus.debug("Handling SDP as well..."); + Janus.debug(jsep); + mixertest.handleRemoteJsep({jsep: jsep}); + } + }, + onlocalstream: function(stream) { + Janus.debug(" ::: Got a local stream :::"); + Janus.debug(stream); + // We're not going to attach the local audio stream + $('#audiojoin').hide(); + $('#room').removeClass('hide').show(); + $('#participant').removeClass('hide').html(myusername).show(); + }, + onremotestream: function(stream) { + $('#room').removeClass('hide').show(); + var addButtons = false; + if($('#roomaudio').length === 0) { + addButtons = true; + $('#mixedaudio').append('<audio class="rounded centered" id="roomaudio" width="100%" height="100%" autoplay/>'); + } + Janus.attachMediaStream($('#roomaudio').get(0), stream); + if(!addButtons) + return; + // Mute button + audioenabled = true; + $('#toggleaudio').click( + function() { + audioenabled = !audioenabled; + if(audioenabled) + $('#toggleaudio').html("Mute").removeClass("btn-success").addClass("btn-danger"); + else + $('#toggleaudio').html("Unmute").removeClass("btn-danger").addClass("btn-success"); + mixertest.send({message: { "request": "configure", "muted": !audioenabled }}); + }).removeClass('hide').show(); + + }, + oncleanup: function() { + webrtcUp = false; + Janus.log(" ::: Got a cleanup notification :::"); + $('#participant').empty().hide(); + $('#list').empty(); + $('#mixedaudio').empty(); + $('#room').hide(); + } + }); + }, + error: function(error) { + Janus.error(error); + bootbox.alert(error, function() { + window.location.reload(); + }); + }, + destroyed: function() { + window.location.reload(); + } + }); + }); + }}); +}); + +function updateStreamsList() { + $('#update-streams').unbind('click').addClass('fa-spin'); + var body = { "request": "list" }; + Janus.debug("Sending message (" + JSON.stringify(body) + ")"); + streaming.send({"message": body, success: function(result) { + setTimeout(function() { + $('#update-streams').removeClass('fa-spin').click(updateStreamsList); + }, 500); + if(result === null || result === undefined) { + bootbox.alert("Got no response to our query for available streams"); + return; + } + if(result["list"] !== undefined && result["list"] !== null) { + $('#streams').removeClass('hide').show(); + $('#streamslist').empty(); + $('#watch').attr('disabled', true).unbind('click'); + var list = result["list"]; + Janus.log("Got a list of available streams"); + Janus.debug(list); + for(var mp in list) { + Janus.debug(" >> [" + list[mp]["id"] + "] " + list[mp]["description"] + " (" + list[mp]["type"] + ")"); + $('#streamslist').append("<li><a href='#' id='" + list[mp]["id"] + "'>" + list[mp]["description"] + " (" + list[mp]["type"] + ")" + "</a></li>"); + } + $('#streamslist a').unbind('click').click(function() { + selectedStream = $(this).attr("id"); + $('#streamset').html($(this).html()).parent().removeClass('open'); + return false; + + }); + $('#watch').removeAttr('disabled').unbind('click').click(startStream); + } + }}); +} + +function startStream() { + Janus.log("Selected video id #" + selectedStream); + if(selectedStream === undefined || selectedStream === null) { + bootbox.alert("Select a stream from the list"); + return; + } + $('#streamset').attr('disabled', true); + $('#streamslist').attr('disabled', true); + $('#watch').attr('disabled', true).unbind('click'); + var body = { "request": "watch", id: parseInt(selectedStream) }; + streaming.send({"message": body}); + // No remote video yet + $('#stream').append('<video class="rounded centered" id="waitingvideo" width=320 height=240 />'); + if(spinner == null) { + var target = document.getElementById('stream'); + spinner = new Spinner({top:100}).spin(target); + } else { + spinner.spin(); + } +} + +function stopStream() { + $('#watch').attr('disabled', true).unbind('click'); + var body = { "request": "stop" }; + streaming.send({"message": body}); + streaming.hangup(); + $('#streamset').removeAttr('disabled'); + $('#streamslist').removeAttr('disabled'); + $('#watch').html("Watch or Listen").removeAttr('disabled').unbind('click').click(startStream); + $('#status').empty().hide(); + $('#bitrate').attr('disabled', true); + $('#bitrateset').html('Bandwidth<span class="caret"></span>'); + $('#curbitrate').hide(); + if(bitrateTimer !== null && bitrateTimer !== undefined) + clearInterval(bitrateTimer); + bitrateTimer = null; + $('#curres').empty().hide(); + $('#simulcast').remove(); + simulcastStarted = false; +} + +// Helpers to create Simulcast-related UI, if enabled +function addSimulcastButtons(temporal) { + $('#curres').parent().append( + '<div id="simulcast" class="btn-group-vertical btn-group-vertical-xs pull-right">' + + ' <div class"row">' + + ' <div class="btn-group btn-group-xs" style="width: 100%">' + + ' <button id="sl-2" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to higher quality" style="width: 33%">SL 2</button>' + + ' <button id="sl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to normal quality" style="width: 33%">SL 1</button>' + + ' <button id="sl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to lower quality" style="width: 34%">SL 0</button>' + + ' </div>' + + ' </div>' + + ' <div class"row">' + + ' <div class="btn-group btn-group-xs hide" style="width: 100%">' + + ' <button id="tl-2" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 2" style="width: 34%">TL 2</button>' + + ' <button id="tl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 1" style="width: 33%">TL 1</button>' + + ' <button id="tl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 0" style="width: 33%">TL 0</button>' + + ' </div>' + + ' </div>' + + '</div>'); + // Enable the simulcast selection buttons + $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Switching simulcast substream, wait for it... (lower quality)", null, {timeOut: 2000}); + if(!$('#sl-2').hasClass('btn-success')) + $('#sl-2').removeClass('btn-primary btn-info').addClass('btn-primary'); + if(!$('#sl-1').hasClass('btn-success')) + $('#sl-1').removeClass('btn-primary btn-info').addClass('btn-primary'); + $('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info'); + streaming.send({message: { request: "configure", substream: 0 }}); + }); + $('#sl-1').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Switching simulcast substream, wait for it... (normal quality)", null, {timeOut: 2000}); + if(!$('#sl-2').hasClass('btn-success')) + $('#sl-2').removeClass('btn-primary btn-info').addClass('btn-primary'); + $('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info'); + if(!$('#sl-0').hasClass('btn-success')) + $('#sl-0').removeClass('btn-primary btn-info').addClass('btn-primary'); + streaming.send({message: { request: "configure", substream: 1 }}); + }); + $('#sl-2').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Switching simulcast substream, wait for it... (higher quality)", null, {timeOut: 2000}); + $('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info'); + if(!$('#sl-1').hasClass('btn-success')) + $('#sl-1').removeClass('btn-primary btn-info').addClass('btn-primary'); + if(!$('#sl-0').hasClass('btn-success')) + $('#sl-0').removeClass('btn-primary btn-info').addClass('btn-primary'); + streaming.send({message: { request: "configure", substream: 2 }}); + }); + if(!temporal) // No temporal layer support + return; + $('#tl-0').parent().removeClass('hide'); + $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Capping simulcast temporal layer, wait for it... (lowest FPS)", null, {timeOut: 2000}); + if(!$('#tl-2').hasClass('btn-success')) + $('#tl-2').removeClass('btn-primary btn-info').addClass('btn-primary'); + if(!$('#tl-1').hasClass('btn-success')) + $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-primary'); + $('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info'); + streaming.send({message: { request: "configure", temporal: 0 }}); + }); + $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Capping simulcast temporal layer, wait for it... (medium FPS)", null, {timeOut: 2000}); + if(!$('#tl-2').hasClass('btn-success')) + $('#tl-2').removeClass('btn-primary btn-info').addClass('btn-primary'); + $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-info'); + if(!$('#tl-0').hasClass('btn-success')) + $('#tl-0').removeClass('btn-primary btn-info').addClass('btn-primary'); + streaming.send({message: { request: "configure", temporal: 1 }}); + }); + $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Capping simulcast temporal layer, wait for it... (highest FPS)", null, {timeOut: 2000}); + $('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info'); + if(!$('#tl-1').hasClass('btn-success')) + $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-primary'); + if(!$('#tl-0').hasClass('btn-success')) + $('#tl-0').removeClass('btn-primary btn-info').addClass('btn-primary'); + streaming.send({message: { request: "configure", temporal: 2 }}); + }); +} + +function updateSimulcastButtons(substream, temporal) { + // Check the substream + if(substream === 0) { + toastr.success("Switched simulcast substream! (lower quality)", null, {timeOut: 2000}); + $('#sl-2').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#sl-1').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + } else if(substream === 1) { + toastr.success("Switched simulcast substream! (normal quality)", null, {timeOut: 2000}); + $('#sl-2').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary'); + } else if(substream === 2) { + toastr.success("Switched simulcast substream! (higher quality)", null, {timeOut: 2000}); + $('#sl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + $('#sl-1').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary'); + } + // Check the temporal layer + if(temporal === 0) { + toastr.success("Capped simulcast temporal layer! (lowest FPS)", null, {timeOut: 2000}); + $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + } else if(temporal === 1) { + toastr.success("Capped simulcast temporal layer! (medium FPS)", null, {timeOut: 2000}); + $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary'); + } else if(temporal === 2) { + toastr.success("Capped simulcast temporal layer! (highest FPS)", null, {timeOut: 2000}); + $('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary'); + } +} + +// Helpers to create SVC-related UI for a new viewer +function addSvcButtons() { + if($('#svc').length > 0) + return; + $('#curres').parent().append( + '<div id="svc" class="btn-group-vertical btn-group-vertical-xs pull-right">' + + ' <div class"row">' + + ' <div class="btn-group btn-group-xs" style="width: 100%">' + + ' <button id="sl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to normal resolution" style="width: 50%">SL 1</button>' + + ' <button id="sl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Switch to low resolution" style="width: 50%">SL 0</button>' + + ' </div>' + + ' </div>' + + ' <div class"row">' + + ' <div class="btn-group btn-group-xs" style="width: 100%">' + + ' <button id="tl-2" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 2 (high FPS)" style="width: 34%">TL 2</button>' + + ' <button id="tl-1" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 1 (medium FPS)" style="width: 33%">TL 1</button>' + + ' <button id="tl-0" type="button" class="btn btn-primary" data-toggle="tooltip" title="Cap to temporal layer 0 (low FPS)" style="width: 33%">TL 0</button>' + + ' </div>' + + ' </div>' + + '</div>' + ); + // Enable the VP8 simulcast selection buttons + $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Switching SVC spatial layer, wait for it... (low resolution)", null, {timeOut: 2000}); + if(!$('#sl-1').hasClass('btn-success')) + $('#sl-1').removeClass('btn-primary btn-info').addClass('btn-primary'); + $('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info'); + streaming.send({message: { request: "configure", spatial_layer: 0 }}); + }); + $('#sl-1').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Switching SVC spatial layer, wait for it... (normal resolution)", null, {timeOut: 2000}); + $('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-info'); + if(!$('#sl-0').hasClass('btn-success')) + $('#sl-0').removeClass('btn-primary btn-info').addClass('btn-primary'); + streaming.send({message: { request: "configure", spatial_layer: 1 }}); + }); + $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Capping SVC temporal layer, wait for it... (lowest FPS)", null, {timeOut: 2000}); + if(!$('#tl-2').hasClass('btn-success')) + $('#tl-2').removeClass('btn-primary btn-info').addClass('btn-primary'); + if(!$('#tl-1').hasClass('btn-success')) + $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-primary'); + $('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-info'); + streaming.send({message: { request: "configure", temporal_layer: 0 }}); + }); + $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Capping SVC temporal layer, wait for it... (medium FPS)", null, {timeOut: 2000}); + if(!$('#tl-2').hasClass('btn-success')) + $('#tl-2').removeClass('btn-primary btn-info').addClass('btn-primary'); + $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-info'); + if(!$('#tl-0').hasClass('btn-success')) + $('#tl-0').removeClass('btn-primary btn-info').addClass('btn-primary'); + streaming.send({message: { request: "configure", temporal_layer: 1 }}); + }); + $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary') + .unbind('click').click(function() { + toastr.info("Capping SVC temporal layer, wait for it... (highest FPS)", null, {timeOut: 2000}); + $('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-info'); + if(!$('#tl-1').hasClass('btn-success')) + $('#tl-1').removeClass('btn-primary btn-info').addClass('btn-primary'); + if(!$('#tl-0').hasClass('btn-success')) + $('#tl-0').removeClass('btn-primary btn-info').addClass('btn-primary'); + streaming.send({message: { request: "configure", temporal_layer: 2 }}); + }); +} + +function updateSvcButtons(spatial, temporal) { + // Check the spatial layer + if(spatial === 0) { + toastr.success("Switched SVC spatial layer! (lower resolution)", null, {timeOut: 2000}); + $('#sl-1').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#sl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + } else if(spatial === 1) { + toastr.success("Switched SVC spatial layer! (normal resolution)", null, {timeOut: 2000}); + $('#sl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + $('#sl-0').removeClass('btn-primary btn-success').addClass('btn-primary'); + } + // Check the temporal layer + if(temporal === 0) { + toastr.success("Capped SVC temporal layer! (lowest FPS)", null, {timeOut: 2000}); + $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#tl-0').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + } else if(temporal === 1) { + toastr.success("Capped SVC temporal layer! (medium FPS)", null, {timeOut: 2000}); + $('#tl-2').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#tl-1').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary'); + } else if(temporal === 2) { + toastr.success("Capped SVC temporal layer! (highest FPS)", null, {timeOut: 2000}); + $('#tl-2').removeClass('btn-primary btn-info btn-success').addClass('btn-success'); + $('#tl-1').removeClass('btn-primary btn-success').addClass('btn-primary'); + $('#tl-0').removeClass('btn-primary btn-success').addClass('btn-primary'); + } +} + +function checkEnter(field, event) { + var theCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode; + if(theCode == 13) { + registerUsername(); + return false; + } else { + return true; + } +} + +function registerUsername() { + if($('#username').length === 0) { + // Create fields to register + $('#register').click(registerUsername); + $('#username').focus(); + } else { + // Try a registration + $('#username').attr('disabled', true); + $('#register').attr('disabled', true).unbind('click'); + var username = $('#username').val(); + if(username === "") { + $('#you') + .removeClass().addClass('label label-warning') + .html("Insert your display name (e.g., pippo)"); + $('#username').removeAttr('disabled'); + $('#register').removeAttr('disabled').click(registerUsername); + return; + } + if(/[^a-zA-Z0-9]/.test(username)) { + $('#you') + .removeClass().addClass('label label-warning') + .html('Input is not alphanumeric'); + $('#username').removeAttr('disabled').val(""); + $('#register').removeAttr('disabled').click(registerUsername); + return; + } + var register = { "request": "join", "room": myroom, "display": username }; + myusername = username; + mixertest.send({"message": register}); + } +} |