summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js/lib/janus.min.js2
-rw-r--r--js/lib/janus.nojquery.min.js2
2 files changed, 2 insertions, 2 deletions
diff --git a/js/lib/janus.min.js b/js/lib/janus.min.js
index 6403526..63bdc4b 100644
--- a/js/lib/janus.min.js
+++ b/js/lib/janus.min.js
@@ -1,2 +1,2 @@
Janus.sessions={};Janus.extensionId="hapfgfdkleiggjjpfpenajgdnfckjpaj";Janus.isExtensionEnabled=function(){if(window.navigator.userAgent.match("Chrome")){var chromever=parseInt(window.navigator.userAgent.match(/Chrome\/(.*) /)[1],10);var maxver=33;if(window.navigator.userAgent.match("Linux"))maxver=35;if(chromever>=26&&chromever<=maxver){return true}return $("#janus-extension-installed").length>0}else{return true}};Janus.noop=function(){};Janus.init=function(options){options=options||{};options.callback=typeof options.callback=="function"?options.callback:Janus.noop;if(Janus.initDone===true){options.callback()}else{if(typeof console=="undefined"||typeof console.log=="undefined")console={log:function(){}};Janus.trace=Janus.noop;Janus.debug=Janus.noop;Janus.vdebug=Janus.noop;Janus.log=Janus.noop;Janus.warn=Janus.noop;Janus.error=Janus.noop;if(options.debug===true||options.debug==="all"){Janus.trace=console.trace.bind(console);Janus.debug=console.debug.bind(console);Janus.vdebug=console.debug.bind(console);Janus.log=console.log.bind(console);Janus.warn=console.warn.bind(console);Janus.error=console.error.bind(console)}else if(Array.isArray(options.debug)){for(var i in options.debug){var d=options.debug[i];switch(d){case"trace":Janus.trace=console.trace.bind(console);break;case"debug":Janus.debug=console.debug.bind(console);break;case"vdebug":Janus.vdebug=console.debug.bind(console);break;case"log":Janus.log=console.log.bind(console);break;case"warn":Janus.warn=console.warn.bind(console);break;case"error":Janus.error=console.error.bind(console);break;default:console.error("Unknown debugging option '"+d+"' (supported: 'trace', 'debug', 'vdebug', 'log', warn', 'error')");break}}}Janus.log("Initializing library");Janus.listDevices=function(callback){callback=typeof callback=="function"?callback:Janus.noop;if(navigator.mediaDevices){navigator.mediaDevices.getUserMedia({audio:true,video:true}).then(function(stream){navigator.mediaDevices.enumerateDevices().then(function(devices){Janus.debug(devices);callback(devices);try{stream.stop()}catch(e){}try{var tracks=stream.getTracks();for(var i in tracks){var mst=tracks[i];if(mst!==null&&mst!==undefined)mst.stop()}}catch(e){}})}).catch(function(err){Janus.error(err);callback([])})}else{Janus.warn("navigator.mediaDevices unavailable");callback([])}};Janus.attachMediaStream=function(element,stream){if(adapter.browserDetails.browser==="chrome"){var chromever=adapter.browserDetails.version;if(chromever>=43){element.srcObject=stream}else if(typeof element.src!=="undefined"){element.src=URL.createObjectURL(stream)}else{Janus.error("Error attaching stream to element")}}else if(adapter.browserDetails.browser==="safari"||window.navigator.userAgent.match(/iPad/i)||window.navigator.userAgent.match(/iPhone/i)){element.src=URL.createObjectURL(stream)}else{element.srcObject=stream}};Janus.reattachMediaStream=function(to,from){if(adapter.browserDetails.browser==="chrome"){var chromever=adapter.browserDetails.version;if(chromever>=43){to.srcObject=from.srcObject}else if(typeof to.src!=="undefined"){to.src=from.src}}else if(adapter.browserDetails.browser==="safari"||window.navigator.userAgent.match(/iPad/i)||window.navigator.userAgent.match(/iPhone/i)){to.src=from.src}else{to.srcObject=from.srcObject}};var oldOBF=window.onbeforeunload;window.onbeforeunload=function(){Janus.log("Closing window");for(var s in Janus.sessions){if(Janus.sessions[s]!==null&&Janus.sessions[s]!==undefined&&Janus.sessions[s].destroyOnUnload){Janus.log("Destroying session "+s);Janus.sessions[s].destroy({asyncRequest:false})}}if(oldOBF&&typeof oldOBF=="function")oldOBF()};Janus.initDone=true;options.callback()}};Janus.isWebrtcSupported=function(){return window.RTCPeerConnection!==undefined&&window.RTCPeerConnection!==null&&navigator.getUserMedia!==undefined&&navigator.getUserMedia!==null};Janus.randomString=function(len){var charSet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";var randomString="";for(var i=0;i<len;i++){var randomPoz=Math.floor(Math.random()*charSet.length);randomString+=charSet.substring(randomPoz,randomPoz+1)}return randomString};function Janus(gatewayCallbacks){if(Janus.initDone===undefined){gatewayCallbacks.error("Library not initialized");return{}}if(!Janus.isWebrtcSupported()){gatewayCallbacks.error("WebRTC not supported by this browser");return{}}Janus.log("Library initialized: "+Janus.initDone);gatewayCallbacks=gatewayCallbacks||{};gatewayCallbacks.success=typeof gatewayCallbacks.success=="function"?gatewayCallbacks.success:jQuery.noop;gatewayCallbacks.error=typeof gatewayCallbacks.error=="function"?gatewayCallbacks.error:jQuery.noop;gatewayCallbacks.destroyed=typeof gatewayCallbacks.destroyed=="function"?gatewayCallbacks.destroyed:jQuery.noop;if(gatewayCallbacks.server===null||gatewayCallbacks.server===undefined){gatewayCallbacks.error("Invalid gateway url");return{}}var websockets=false;var ws=null;var wsHandlers={};var wsKeepaliveTimeoutId=null;var servers=null,serversIndex=0;var server=gatewayCallbacks.server;if($.isArray(server)){Janus.log("Multiple servers provided ("+server.length+"), will use the first that works");server=null;servers=gatewayCallbacks.server;Janus.debug(servers)}else{if(server.indexOf("ws")===0){websockets=true;Janus.log("Using WebSockets to contact Janus: "+server)}else{websockets=false;Janus.log("Using REST API to contact Janus: "+server)}}var iceServers=gatewayCallbacks.iceServers;if(iceServers===undefined||iceServers===null)iceServers=[{urls:"stun:stun.l.google.com:19302"}];var iceTransportPolicy=gatewayCallbacks.iceTransportPolicy;var ipv6Support=gatewayCallbacks.ipv6;if(ipv6Support===undefined||ipv6Support===null)ipv6Support=false;var withCredentials=false;if(gatewayCallbacks.withCredentials!==undefined&&gatewayCallbacks.withCredentials!==null)withCredentials=gatewayCallbacks.withCredentials===true;var maxev=null;if(gatewayCallbacks.max_poll_events!==undefined&&gatewayCallbacks.max_poll_events!==null)maxev=gatewayCallbacks.max_poll_events;if(maxev<1)maxev=1;var token=null;if(gatewayCallbacks.token!==undefined&&gatewayCallbacks.token!==null)token=gatewayCallbacks.token;var apisecret=null;if(gatewayCallbacks.apisecret!==undefined&&gatewayCallbacks.apisecret!==null)apisecret=gatewayCallbacks.apisecret;this.destroyOnUnload=true;if(gatewayCallbacks.destroyOnUnload!==undefined&&gatewayCallbacks.destroyOnUnload!==null)this.destroyOnUnload=gatewayCallbacks.destroyOnUnload===true;var connected=false;var sessionId=null;var pluginHandles={};var that=this;var retries=0;var transactions={};createSession(gatewayCallbacks);this.getServer=function(){return server};this.isConnected=function(){return connected};this.getSessionId=function(){return sessionId};this.destroy=function(callbacks){destroySession(callbacks)};this.attach=function(callbacks){createHandle(callbacks)};function eventHandler(){if(sessionId==null)return;Janus.debug("Long poll...");if(!connected){Janus.warn("Is the gateway down? (connected=false)");return}var longpoll=server+"/"+sessionId+"?rid="+(new Date).getTime();if(maxev!==undefined&&maxev!==null)longpoll=longpoll+"&maxev="+maxev;if(token!==null&&token!==undefined)longpoll=longpoll+"&token="+token;if(apisecret!==null&&apisecret!==undefined)longpoll=longpoll+"&apisecret="+apisecret;$.ajax({type:"GET",url:longpoll,xhrFields:{withCredentials:withCredentials},cache:false,timeout:6e4,success:handleEvent,error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);retries++;if(retries>3){connected=false;gatewayCallbacks.error("Lost connection to the gateway (is it down?)");return}eventHandler()},dataType:"json"})}function handleEvent(json){retries=0;if(!websockets&&sessionId!==undefined&&sessionId!==null)setTimeout(eventHandler,200);if(!websockets&&$.isArray(json)){for(var i=0;i<json.length;i++){handleEvent(json[i])}return}if(json["janus"]==="keepalive"){Janus.vdebug("Got a keepalive on session "+sessionId);return}else if(json["janus"]==="ack"){Janus.debug("Got an ack on session "+sessionId);Janus.debug(json);var transaction=json["transaction"];if(transaction!==null&&transaction!==undefined){var reportSuccess=transactions[transaction];if(reportSuccess!==null&&reportSuccess!==undefined){reportSuccess(json)}delete transactions[transaction]}return}else if(json["janus"]==="success"){Janus.debug("Got a success on session "+sessionId);Janus.debug(json);var transaction=json["transaction"];if(transaction!==null&&transaction!==undefined){var reportSuccess=transactions[transaction];if(reportSuccess!==null&&reportSuccess!==undefined){reportSuccess(json)}delete transactions[transaction]}return}else if(json["janus"]==="webrtcup"){Janus.debug("Got a webrtcup event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.debug("This handle is not attached to this session");return}pluginHandle.webrtcState(true);return}else if(json["janus"]==="hangup"){Janus.debug("Got a hangup event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.debug("This handle is not attached to this session");return}pluginHandle.webrtcState(false,json["reason"]);pluginHandle.hangup()}else if(json["janus"]==="detached"){Janus.debug("Got a detached event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){return}pluginHandle.detached=true;pluginHandle.ondetached();pluginHandle.detach()}else if(json["janus"]==="media"){Janus.debug("Got a media event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.debug("This handle is not attached to this session");return}pluginHandle.mediaState(json["type"],json["receiving"])}else if(json["janus"]==="slowlink"){Janus.debug("Got a slowlink event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.debug("This handle is not attached to this session");return}pluginHandle.slowLink(json["uplink"],json["nacks"])}else if(json["janus"]==="error"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);Janus.debug(json);var transaction=json["transaction"];if(transaction!==null&&transaction!==undefined){var reportSuccess=transactions[transaction];if(reportSuccess!==null&&reportSuccess!==undefined){reportSuccess(json)}delete transactions[transaction]}return}else if(json["janus"]==="event"){Janus.debug("Got a plugin event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var plugindata=json["plugindata"];if(plugindata===undefined||plugindata===null){Janus.warn("Missing plugindata...");return}Janus.debug(" -- Event is coming from "+sender+" ("+plugindata["plugin"]+")");var data=plugindata["data"];Janus.debug(data);var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.warn("This handle is not attached to this session");return}var jsep=json["jsep"];if(jsep!==undefined&&jsep!==null){Janus.debug("Handling SDP as well...");Janus.debug(jsep)}var callback=pluginHandle.onmessage;if(callback!==null&&callback!==undefined){Janus.debug("Notifying application...");callback(data,jsep)}else{Janus.debug("No provided notification callback")}}else{Janus.warn("Unkown message/event '"+json["janus"]+"' on session "+sessionId);Janus.debug(json)}}function keepAlive(){if(server===null||!websockets||!connected)return;wsKeepaliveTimeoutId=setTimeout(keepAlive,3e4);var request={janus:"keepalive",session_id:sessionId,transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;ws.send(JSON.stringify(request))}function createSession(callbacks){var transaction=Janus.randomString(12);var request={janus:"create",transaction:transaction};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(server===null&&$.isArray(servers)){server=servers[serversIndex];if(server.indexOf("ws")===0){websockets=true;Janus.log("Server #"+(serversIndex+1)+": trying WebSockets to contact Janus ("+server+")")}else{websockets=false;Janus.log("Server #"+(serversIndex+1)+": trying REST API to contact Janus ("+server+")")}}if(websockets){ws=new WebSocket(server,"janus-protocol");wsHandlers={error:function(){Janus.error("Error connecting to the Janus WebSockets server... "+server);if($.isArray(servers)){serversIndex++;if(serversIndex==servers.length){callbacks.error("Error connecting to any of the provided Janus servers: Is the gateway down?");return}server=null;setTimeout(function(){createSession(callbacks)},200);return}callbacks.error("Error connecting to the Janus WebSockets server: Is the gateway down?")},open:function(){transactions[transaction]=function(json){Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error(json["error"].reason);return}wsKeepaliveTimeoutId=setTimeout(keepAlive,3e4);connected=true;sessionId=json.data["id"];Janus.log("Created session: "+sessionId);Janus.sessions[sessionId]=that;callbacks.success()};ws.send(JSON.stringify(request))},message:function(event){handleEvent(JSON.parse(event.data))},close:function(){if(server===null||!connected){return}connected=false;gatewayCallbacks.error("Lost connection to the gateway (is it down?)")}};for(var eventName in wsHandlers){ws.addEventListener(eventName,wsHandlers[eventName])}return}$.ajax({type:"POST",url:server,xhrFields:{withCredentials:withCredentials},cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error(json["error"].reason);return}connected=true;sessionId=json.data["id"];Janus.log("Created session: "+sessionId);Janus.sessions[sessionId]=that;eventHandler();callbacks.success()},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);if($.isArray(servers)){serversIndex++;if(serversIndex==servers.length){callbacks.error("Error connecting to any of the provided Janus servers: Is the gateway down?");return}server=null;setTimeout(function(){createSession(callbacks)},200);return}if(errorThrown==="")callbacks.error(textStatus+": Is the gateway down?");else callbacks.error(textStatus+": "+errorThrown)},dataType:"json"})}function destroySession(callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;var asyncRequest=true;if(callbacks.asyncRequest!==undefined&&callbacks.asyncRequest!==null)asyncRequest=callbacks.asyncRequest===true;Janus.log("Destroying session "+sessionId+" (async="+asyncRequest+")");if(!connected){Janus.warn("Is the gateway down? (connected=false)");callbacks.success();return}if(sessionId===undefined||sessionId===null){Janus.warn("No session to destroy");callbacks.success();gatewayCallbacks.destroyed();return}delete Janus.sessions[sessionId];var request={janus:"destroy",transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(websockets){request["session_id"]=sessionId;var unbindWebSocket=function(){for(var eventName in wsHandlers){ws.removeEventListener(eventName,wsHandlers[eventName])}ws.removeEventListener("message",onUnbindMessage);ws.removeEventListener("error",onUnbindError);if(wsKeepaliveTimeoutId){clearTimeout(wsKeepaliveTimeoutId)}};var onUnbindMessage=function(event){var data=JSON.parse(event.data);if(data.session_id==request.session_id&&data.transaction==request.transaction){unbindWebSocket();callbacks.success();gatewayCallbacks.destroyed()}};var onUnbindError=function(event){unbindWebSocket();callbacks.error("Failed to destroy the gateway: Is the gateway down?");gatewayCallbacks.destroyed()};ws.addEventListener("message",onUnbindMessage);ws.addEventListener("error",onUnbindError);ws.send(JSON.stringify(request));return}$.ajax({type:"POST",url:server+"/"+sessionId,async:asyncRequest,xhrFields:{withCredentials:withCredentials},cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.log("Destroyed session:");Janus.debug(json);sessionId=null;connected=false;if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason)}callbacks.success();gatewayCallbacks.destroyed()},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);sessionId=null;connected=false;callbacks.success();gatewayCallbacks.destroyed()},dataType:"json"})}function createHandle(callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;callbacks.consentDialog=typeof callbacks.consentDialog=="function"?callbacks.consentDialog:jQuery.noop;callbacks.iceState=typeof callbacks.iceState=="function"?callbacks.iceState:jQuery.noop;callbacks.mediaState=typeof callbacks.mediaState=="function"?callbacks.mediaState:jQuery.noop;callbacks.webrtcState=typeof callbacks.webrtcState=="function"?callbacks.webrtcState:jQuery.noop;callbacks.slowLink=typeof callbacks.slowLink=="function"?callbacks.slowLink:jQuery.noop;callbacks.onmessage=typeof callbacks.onmessage=="function"?callbacks.onmessage:jQuery.noop;callbacks.onlocalstream=typeof callbacks.onlocalstream=="function"?callbacks.onlocalstream:jQuery.noop;callbacks.onremotestream=typeof callbacks.onremotestream=="function"?callbacks.onremotestream:jQuery.noop;callbacks.ondata=typeof callbacks.ondata=="function"?callbacks.ondata:jQuery.noop;callbacks.ondataopen=typeof callbacks.ondataopen=="function"?callbacks.ondataopen:jQuery.noop;callbacks.oncleanup=typeof callbacks.oncleanup=="function"?callbacks.oncleanup:jQuery.noop;callbacks.ondetached=typeof callbacks.ondetached=="function"?callbacks.ondetached:jQuery.noop;if(!connected){Janus.warn("Is the gateway down? (connected=false)");callbacks.error("Is the gateway down? (connected=false)");return}var plugin=callbacks.plugin;if(plugin===undefined||plugin===null){Janus.error("Invalid plugin");callbacks.error("Invalid plugin");return}var opaqueId=callbacks.opaqueId;var transaction=Janus.randomString(12);var request={janus:"attach",plugin:plugin,opaque_id:opaqueId,transaction:transaction};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(websockets){transactions[transaction]=function(json){Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error("Ooops: "+json["error"].code+" "+json["error"].reason);return}var handleId=json.data["id"];Janus.log("Created handle: "+handleId);var pluginHandle={session:that,plugin:plugin,id:handleId,detached:false,webrtcStuff:{started:false,myStream:null,streamExternal:false,remoteStream:null,mySdp:null,pc:null,dataChannel:null,dtmfSender:null,trickle:true,iceDone:false,sdpSent:false,volume:{value:null,timer:null},bitrate:{value:null,bsnow:null,bsbefore:null,tsnow:null,tsbefore:null,timer:null}},getId:function(){return handleId},getPlugin:function(){return plugin},getVolume:function(){return getVolume(handleId)},isAudioMuted:function(){return isMuted(handleId,false)},muteAudio:function(){return mute(handleId,false,true)},unmuteAudio:function(){return mute(handleId,false,false)},isVideoMuted:function(){return isMuted(handleId,true)},muteVideo:function(){return mute(handleId,true,true)},unmuteVideo:function(){return mute(handleId,true,false)},getBitrate:function(){return getBitrate(handleId)},send:function(callbacks){sendMessage(handleId,callbacks)},data:function(callbacks){sendData(handleId,callbacks)},dtmf:function(callbacks){sendDtmf(handleId,callbacks)},consentDialog:callbacks.consentDialog,iceState:callbacks.iceState,mediaState:callbacks.mediaState,webrtcState:callbacks.webrtcState,slowLink:callbacks.slowLink,onmessage:callbacks.onmessage,createOffer:function(callbacks){prepareWebrtc(handleId,callbacks)},createAnswer:function(callbacks){prepareWebrtc(handleId,callbacks)},handleRemoteJsep:function(callbacks){prepareWebrtcPeer(handleId,callbacks)},onlocalstream:callbacks.onlocalstream,onremotestream:callbacks.onremotestream,ondata:callbacks.ondata,ondataopen:callbacks.ondataopen,oncleanup:callbacks.oncleanup,ondetached:callbacks.ondetached,hangup:function(sendRequest){cleanupWebrtc(handleId,sendRequest===true)},detach:function(callbacks){destroyHandle(handleId,callbacks)}};pluginHandles[handleId]=pluginHandle;callbacks.success(pluginHandle)};request["session_id"]=sessionId;ws.send(JSON.stringify(request));return}$.ajax({type:"POST",url:server+"/"+sessionId,xhrFields:{withCredentials:withCredentials},cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error("Ooops: "+json["error"].code+" "+json["error"].reason);return}var handleId=json.data["id"];Janus.log("Created handle: "+handleId);var pluginHandle={session:that,plugin:plugin,id:handleId,detached:false,webrtcStuff:{started:false,myStream:null,streamExternal:false,remoteStream:null,mySdp:null,pc:null,dataChannel:null,dtmfSender:null,trickle:true,iceDone:false,sdpSent:false,volume:{value:null,timer:null},bitrate:{value:null,bsnow:null,bsbefore:null,tsnow:null,tsbefore:null,timer:null}},getId:function(){return handleId},getPlugin:function(){return plugin},getVolume:function(){return getVolume(handleId)},isAudioMuted:function(){return isMuted(handleId,false)},muteAudio:function(){return mute(handleId,false,true)},unmuteAudio:function(){return mute(handleId,false,false)},isVideoMuted:function(){return isMuted(handleId,true)},muteVideo:function(){return mute(handleId,true,true)},unmuteVideo:function(){return mute(handleId,true,false)},getBitrate:function(){return getBitrate(handleId)},send:function(callbacks){sendMessage(handleId,callbacks)},data:function(callbacks){sendData(handleId,callbacks)},dtmf:function(callbacks){sendDtmf(handleId,callbacks)},consentDialog:callbacks.consentDialog,iceState:callbacks.iceState,mediaState:callbacks.mediaState,webrtcState:callbacks.webrtcState,slowLink:callbacks.slowLink,onmessage:callbacks.onmessage,createOffer:function(callbacks){prepareWebrtc(handleId,callbacks)},createAnswer:function(callbacks){prepareWebrtc(handleId,callbacks)},handleRemoteJsep:function(callbacks){prepareWebrtcPeer(handleId,callbacks)},onlocalstream:callbacks.onlocalstream,onremotestream:callbacks.onremotestream,ondata:callbacks.ondata,ondataopen:callbacks.ondataopen,oncleanup:callbacks.oncleanup,ondetached:callbacks.ondetached,hangup:function(sendRequest){cleanupWebrtc(handleId,sendRequest===true)},detach:function(callbacks){destroyHandle(handleId,callbacks)}};pluginHandles[handleId]=pluginHandle;callbacks.success(pluginHandle)},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown)},dataType:"json"})}function sendMessage(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;if(!connected){Janus.warn("Is the gateway down? (connected=false)");callbacks.error("Is the gateway down? (connected=false)");return}var message=callbacks.message;var jsep=callbacks.jsep;var transaction=Janus.randomString(12);var request={janus:"message",body:message,transaction:transaction};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(jsep!==null&&jsep!==undefined)request.jsep=jsep;Janus.debug("Sending message to plugin (handle="+handleId+"):");Janus.debug(request);if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;transactions[transaction]=function(json){Janus.debug("Message sent!");Janus.debug(json);if(json["janus"]==="success"){var plugindata=json["plugindata"];if(plugindata===undefined||plugindata===null){Janus.warn("Request succeeded, but missing plugindata...");callbacks.success();return}Janus.log("Synchronous transaction successful ("+plugindata["plugin"]+")");var data=plugindata["data"];Janus.debug(data);callbacks.success(data);return}else if(json["janus"]!=="ack"){if(json["error"]!==undefined&&json["error"]!==null){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error(json["error"].code+" "+json["error"].reason)}else{Janus.error("Unknown error");callbacks.error("Unknown error")}return}callbacks.success()};ws.send(JSON.stringify(request));return}$.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,xhrFields:{withCredentials:withCredentials},cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.debug("Message sent!");Janus.debug(json);if(json["janus"]==="success"){var plugindata=json["plugindata"];if(plugindata===undefined||plugindata===null){Janus.warn("Request succeeded, but missing plugindata...");callbacks.success();return}Janus.log("Synchronous transaction successful ("+plugindata["plugin"]+")");var data=plugindata["data"];Janus.debug(data);callbacks.success(data);return}else if(json["janus"]!=="ack"){if(json["error"]!==undefined&&json["error"]!==null){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error(json["error"].code+" "+json["error"].reason)}else{Janus.error("Unknown error");callbacks.error("Unknown error")}return}callbacks.success()},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);callbacks.error(textStatus+": "+errorThrown)},dataType:"json"})}function sendTrickleCandidate(handleId,candidate){if(!connected){Janus.warn("Is the gateway down? (connected=false)");return}var request={janus:"trickle",candidate:candidate,transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;Janus.vdebug("Sending trickle candidate (handle="+handleId+"):");Janus.vdebug(request);if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;ws.send(JSON.stringify(request));return}$.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,xhrFields:{withCredentials:withCredentials},cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.vdebug("Candidate sent!");Janus.vdebug(json);if(json["janus"]!=="ack"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);return}},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown)},dataType:"json"})}function sendData(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;var text=callbacks.text;if(text===null||text===undefined){Janus.warn("Invalid text");callbacks.error("Invalid text");return}Janus.log("Sending string on data channel: "+text);config.dataChannel.send(text);callbacks.success()}function sendDtmf(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(config.dtmfSender===null||config.dtmfSender===undefined){if(config.myStream!==undefined&&config.myStream!==null){var tracks=config.myStream.getAudioTracks();if(tracks!==null&&tracks!==undefined&&tracks.length>0){var local_audio_track=tracks[0];config.dtmfSender=config.pc.createDTMFSender(local_audio_track);Janus.log("Created DTMF Sender");config.dtmfSender.ontonechange=function(tone){Janus.debug("Sent DTMF tone: "+tone.tone)}}}if(config.dtmfSender===null||config.dtmfSender===undefined){Janus.warn("Invalid DTMF configuration");callbacks.error("Invalid DTMF configuration");return}}var dtmf=callbacks.dtmf;if(dtmf===null||dtmf===undefined){Janus.warn("Invalid DTMF parameters");callbacks.error("Invalid DTMF parameters");return}var tones=dtmf.tones;if(tones===null||tones===undefined){Janus.warn("Invalid DTMF string");callbacks.error("Invalid DTMF string");return}var duration=dtmf.duration;if(duration===null||duration===undefined)duration=500;var gap=dtmf.gap;if(gap===null||gap===undefined)gap=50;Janus.debug("Sending DTMF string "+tones+" (duration "+duration+"ms, gap "+gap+"ms)");config.dtmfSender.insertDTMF(tones,duration,gap)}function destroyHandle(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;Janus.warn(callbacks);var asyncRequest=true;if(callbacks.asyncRequest!==undefined&&callbacks.asyncRequest!==null)asyncRequest=callbacks.asyncRequest===true;Janus.log("Destroying handle "+handleId+" (async="+asyncRequest+")");cleanupWebrtc(handleId);if(pluginHandles[handleId].detached){delete pluginHandles[handleId];callbacks.success();return}if(!connected){Janus.warn("Is the gateway down? (connected=false)");callbacks.error("Is the gateway down? (connected=false)");return}var request={janus:"detach",transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;ws.send(JSON.stringify(request));delete pluginHandles[handleId];callbacks.success();return}$.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,async:asyncRequest,xhrFields:{withCredentials:withCredentials},cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.log("Destroyed handle:");Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason)}delete pluginHandles[handleId];callbacks.success()},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);delete pluginHandles[handleId];callbacks.success()},dataType:"json"})}function streamsDone(handleId,jsep,media,callbacks,stream){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle")
-;callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.debug("streamsDone:",stream);config.myStream=stream;var pc_config={iceServers:iceServers,iceTransportPolicy:iceTransportPolicy};var pc_constraints={optional:[{DtlsSrtpKeyAgreement:true}]};if(ipv6Support===true){pc_constraints.optional.push({googIPv6:true})}if(adapter.browserDetails.browser==="edge"){pc_config.bundlePolicy="max-bundle"}Janus.log("Creating PeerConnection");Janus.debug(pc_constraints);config.pc=new RTCPeerConnection(pc_config,pc_constraints);Janus.debug(config.pc);if(config.pc.getStats){config.volume.value=0;config.bitrate.value="0 kbits/sec"}Janus.log("Preparing local SDP and gathering candidates (trickle="+config.trickle+")");config.pc.oniceconnectionstatechange=function(e){if(config.pc)pluginHandle.iceState(config.pc.iceConnectionState)};config.pc.onicecandidate=function(event){if(event.candidate==null||adapter.browserDetails.browser==="edge"&&event.candidate.candidate.indexOf("endOfCandidates")>0){Janus.log("End of candidates.");config.iceDone=true;if(config.trickle===true){sendTrickleCandidate(handleId,{completed:true})}else{sendSDP(handleId,callbacks)}}else{var candidate={candidate:event.candidate.candidate,sdpMid:event.candidate.sdpMid,sdpMLineIndex:event.candidate.sdpMLineIndex};if(config.trickle===true){sendTrickleCandidate(handleId,candidate)}}};if(stream!==null&&stream!==undefined){Janus.log("Adding local stream");config.pc.addStream(stream);pluginHandle.onlocalstream(stream)}config.pc.ontrack=function(remoteStream){Janus.log("Handling Remote Stream");Janus.debug(remoteStream);config.remoteStream=remoteStream;pluginHandle.onremotestream(remoteStream.streams[0])};if(isDataEnabled(media)){Janus.log("Creating data channel");var onDataChannelMessage=function(event){Janus.log("Received message on data channel: "+event.data);pluginHandle.ondata(event.data)};var onDataChannelStateChange=function(){var dcState=config.dataChannel!==null?config.dataChannel.readyState:"null";Janus.log("State change on data channel: "+dcState);if(dcState==="open"){pluginHandle.ondataopen()}};var onDataChannelError=function(error){Janus.error("Got error on data channel:",error)};config.dataChannel=config.pc.createDataChannel("JanusDataChannel",{ordered:false});config.dataChannel.onmessage=onDataChannelMessage;config.dataChannel.onopen=onDataChannelStateChange;config.dataChannel.onclose=onDataChannelStateChange;config.dataChannel.onerror=onDataChannelError}if(jsep===null||jsep===undefined){createOffer(handleId,media,callbacks)}else{if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");createAnswer(handleId,media,callbacks)},callbacks.error)}}function prepareWebrtc(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:webrtcError;var jsep=callbacks.jsep;var media=callbacks.media;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(config.pc!==undefined&&config.pc!==null){Janus.log("Updating existing media session");if(jsep===null||jsep===undefined){createOffer(handleId,media,callbacks)}else{if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");createAnswer(handleId,media,callbacks)},callbacks.error)}return}if(callbacks.stream!==null&&callbacks.stream!==undefined){var stream=callbacks.stream;Janus.log("MediaStream provided by the application");Janus.debug(stream);config.streamExternal=true;streamsDone(handleId,jsep,media,callbacks,stream);return}config.trickle=isTrickleEnabled(callbacks.trickle);if(isAudioSendEnabled(media)||isVideoSendEnabled(media)){var constraints={mandatory:{},optional:[]};pluginHandle.consentDialog(true);var audioSupport=isAudioSendEnabled(media);if(audioSupport===true&&media!=undefined&&media!=null){if(typeof media.audio==="object"){audioSupport=media.audio}}var videoSupport=isVideoSendEnabled(media);if(videoSupport===true&&media!=undefined&&media!=null){if(media.video&&media.video!="screen"&&media.video!="window"){var width=0;var height=0,maxHeight=0;if(media.video==="lowres"){height=240;maxHeight=240;width=320}else if(media.video==="lowres-16:9"){height=180;maxHeight=180;width=320}else if(media.video==="hires"||media.video==="hires-16:9"){height=720;maxHeight=720;width=1280;if(navigator.mozGetUserMedia){var firefoxVer=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(firefoxVer<38){Janus.warn(media.video+" unsupported, falling back to stdres (old Firefox)");height=480;maxHeight=480;width=640}}}else if(media.video==="stdres"){height=480;maxHeight=480;width=640}else if(media.video==="stdres-16:9"){height=360;maxHeight=360;width=640}else{Janus.log("Default video setting ("+media.video+") is stdres 4:3");height=480;maxHeight=480;width=640}Janus.log("Adding media constraint "+media.video);if(navigator.mozGetUserMedia){var firefoxVer=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(firefoxVer<38){videoSupport={require:["height","width"],height:{max:maxHeight,min:height},width:{max:width,min:width}}}else{videoSupport={height:{ideal:height},width:{ideal:width}}}}else{videoSupport={mandatory:{maxHeight:maxHeight,minHeight:height,maxWidth:width,minWidth:width},optional:[]}}if(typeof media.video==="object"){videoSupport=media.video}Janus.debug(videoSupport)}else if(media.video==="screen"||media.video==="window"){if(!media.screenshareFrameRate){media.screenshareFrameRate=3}if(window.location.protocol!=="https:"){Janus.warn("Screen sharing only works on HTTPS, try the https:// version of this page");pluginHandle.consentDialog(false);callbacks.error("Screen sharing only works on HTTPS, try the https:// version of this page");return}var cache={};function callbackUserMedia(error,stream){pluginHandle.consentDialog(false);if(error){callbacks.error({code:error.code,name:error.name,message:error.message})}else{streamsDone(handleId,jsep,media,callbacks,stream)}}function getScreenMedia(constraints,gsmCallback){Janus.log("Adding media constraint (screen capture)");Janus.debug(constraints);navigator.mediaDevices.getUserMedia(constraints).then(function(stream){gsmCallback(null,stream)}).catch(function(error){pluginHandle.consentDialog(false);gsmCallback(error)})}if(adapter.browserDetails.browser==="chrome"){var chromever=adapter.browserDetails.version;var maxver=33;if(window.navigator.userAgent.match("Linux"))maxver=35;if(chromever>=26&&chromever<=maxver){constraints={video:{mandatory:{googLeakyBucket:true,maxWidth:window.screen.width,maxHeight:window.screen.height,minFrameRate:media.screenshareFrameRate,maxFrameRate:media.screenshareFrameRate,chromeMediaSource:"screen"}},audio:isAudioSendEnabled(media)};getScreenMedia(constraints,callbackUserMedia)}else{var pending=window.setTimeout(function(){error=new Error("NavigatorUserMediaError");error.name='The required Chrome extension is not installed: click <a href="#">here</a> to install it. (NOTE: this will need you to refresh the page)';pluginHandle.consentDialog(false);return callbacks.error(error)},1e3);cache[pending]=[callbackUserMedia,null];window.postMessage({type:"janusGetScreen",id:pending},"*")}}else if(window.navigator.userAgent.match("Firefox")){var ffver=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(ffver>=33){constraints={video:{mozMediaSource:media.video,mediaSource:media.video},audio:isAudioSendEnabled(media)};getScreenMedia(constraints,function(err,stream){callbackUserMedia(err,stream);if(!err){var lastTime=stream.currentTime;var polly=window.setInterval(function(){if(!stream)window.clearInterval(polly);if(stream.currentTime==lastTime){window.clearInterval(polly);if(stream.onended){stream.onended()}}lastTime=stream.currentTime},500)}})}else{var error=new Error("NavigatorUserMediaError");error.name="Your version of Firefox does not support screen sharing, please install Firefox 33 (or more recent versions)";pluginHandle.consentDialog(false);callbacks.error(error);return}}window.addEventListener("message",function(event){if(event.origin!=window.location.origin)return;if(event.data.type=="janusGotScreen"&&cache[event.data.id]){var data=cache[event.data.id];var callback=data[0];delete cache[event.data.id];if(event.data.sourceId===""){var error=new Error("NavigatorUserMediaError");error.name="You cancelled the request for permission, giving up...";pluginHandle.consentDialog(false);callbacks.error(error)}else{constraints={audio:isAudioSendEnabled(media),video:{mandatory:{chromeMediaSource:"desktop",maxWidth:window.screen.width,maxHeight:window.screen.height,minFrameRate:media.screenshareFrameRate,maxFrameRate:media.screenshareFrameRate},optional:[{googLeakyBucket:true},{googTemporalLayeredScreencast:true}]}};constraints.video.mandatory.chromeMediaSourceId=event.data.sourceId;getScreenMedia(constraints,callback)}}else if(event.data.type=="janusGetScreenPending"){window.clearTimeout(event.data.id)}});return}}if(media===null||media===undefined||media.video!=="screen"){navigator.mediaDevices.enumerateDevices().then(function(devices){var audioExist=devices.some(function(device){return device.kind==="audioinput"}),videoExist=devices.some(function(device){return device.kind==="videoinput"});var audioSend=isAudioSendEnabled(media);var videoSend=isVideoSendEnabled(media);var needAudioDevice=isAudioSendRequired(media);var needVideoDevice=isVideoSendRequired(media);if(audioSend||videoSend||needAudioDevice||needVideoDevice){var haveAudioDevice=audioSend?audioExist:false;var haveVideoDevice=videoSend?videoExist:false;if(!haveAudioDevice&&!haveVideoDevice){pluginHandle.consentDialog(false);callbacks.error("No capture device found");return false}else if(!haveAudioDevice&&needAudioDevice){pluginHandle.consentDialog(false);callbacks.error("Audio capture is required, but no capture device found");return false}else if(!haveVideoDevice&&needVideoDevice){pluginHandle.consentDialog(false);callbacks.error("Video capture is required, but no capture device found");return false}}navigator.mediaDevices.getUserMedia({audio:audioExist?audioSupport:false,video:videoExist?videoSupport:false}).then(function(stream){pluginHandle.consentDialog(false);streamsDone(handleId,jsep,media,callbacks,stream)}).catch(function(error){pluginHandle.consentDialog(false);callbacks.error({code:error.code,name:error.name,message:error.message})})}).catch(function(error){pluginHandle.consentDialog(false);callbacks.error("enumerateDevices error",error)})}}else{streamsDone(handleId,jsep,media,callbacks)}}function prepareWebrtcPeer(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:webrtcError;var jsep=callbacks.jsep;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(jsep!==undefined&&jsep!==null){if(config.pc===null){Janus.warn("Wait, no PeerConnection?? if this is an answer, use createAnswer and not handleRemoteJsep");callbacks.error("No PeerConnection: if this is an answer, use createAnswer and not handleRemoteJsep");return}if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");callbacks.success()},callbacks.error)}else{callbacks.error("Invalid JSEP")}}function createOffer(handleId,media,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.log("Creating offer (iceDone="+config.iceDone+")");var mediaConstraints=null;if(adapter.browserDetails.browser=="firefox"||adapter.browserDetails.browser=="edge"){mediaConstraints={offerToReceiveAudio:isAudioRecvEnabled(media),offerToReceiveVideo:isVideoRecvEnabled(media)}}else{mediaConstraints={mandatory:{OfferToReceiveAudio:isAudioRecvEnabled(media),OfferToReceiveVideo:isVideoRecvEnabled(media)}}}Janus.debug(mediaConstraints);config.pc.createOffer(function(offer){Janus.debug(offer);if(config.mySdp===null||config.mySdp===undefined){Janus.log("Setting local description");config.mySdp=offer.sdp;config.pc.setLocalDescription(offer)}if(!config.iceDone&&!config.trickle){Janus.log("Waiting for all candidates...");return}if(config.sdpSent){Janus.log("Offer already sent, not sending it again");return}Janus.log("Offer ready");Janus.debug(callbacks);config.sdpSent=true;var jsep={type:offer.type,sdp:offer.sdp};callbacks.success(jsep)},callbacks.error,mediaConstraints)}function createAnswer(handleId,media,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.log("Creating answer (iceDone="+config.iceDone+")");var mediaConstraints=null;if(adapter.browserDetails.browser=="firefox"||adapter.browserDetails.browser=="edge"){mediaConstraints={offerToReceiveAudio:isAudioRecvEnabled(media),offerToReceiveVideo:isVideoRecvEnabled(media)}}else{mediaConstraints={mandatory:{OfferToReceiveAudio:isAudioRecvEnabled(media),OfferToReceiveVideo:isVideoRecvEnabled(media)}}}Janus.debug(mediaConstraints);config.pc.createAnswer(function(answer){Janus.debug(answer);if(config.mySdp===null||config.mySdp===undefined){Janus.log("Setting local description");config.mySdp=answer.sdp;config.pc.setLocalDescription(answer)}if(!config.iceDone&&!config.trickle){Janus.log("Waiting for all candidates...");return}if(config.sdpSent){Janus.log("Answer already sent, not sending it again");return}config.sdpSent=true;var jsep={type:answer.type,sdp:answer.sdp};callbacks.success(jsep)},callbacks.error,mediaConstraints)}function sendSDP(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle, not sending anything");return}var config=pluginHandle.webrtcStuff;Janus.log("Sending offer/answer SDP...");if(config.mySdp===null||config.mySdp===undefined){Janus.warn("Local SDP instance is invalid, not sending anything...");return}config.mySdp={type:config.pc.localDescription.type,sdp:config.pc.localDescription.sdp};if(config.sdpSent){Janus.log("Offer/Answer SDP already sent, not sending it again");return}if(config.trickle===false)config.mySdp["trickle"]=false;Janus.debug(callbacks);config.sdpSent=true;callbacks.success(config.mySdp)}function getVolume(handleId){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return 0}var config=pluginHandle.webrtcStuff;if(config.pc.getStats&&adapter.browserDetails.browser=="chrome"){if(config.remoteStream===null||config.remoteStream===undefined){Janus.warn("Remote stream unavailable");return 0}if(config.volume.timer===null||config.volume.timer===undefined){Janus.log("Starting volume monitor");config.volume.timer=setInterval(function(){config.pc.getStats(function(stats){var results=stats.result();for(var i=0;i<results.length;i++){var res=results[i];if(res.type=="ssrc"&&res.stat("audioOutputLevel")){config.volume.value=res.stat("audioOutputLevel")}}})},200);return 0}return config.volume.value}else{Janus.log("Getting the remote volume unsupported by browser");return 0}}function isMuted(handleId,video){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return true}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined){Janus.warn("Invalid PeerConnection");return true}if(config.myStream===undefined||config.myStream===null){Janus.warn("Invalid local MediaStream");return true}if(video){if(config.myStream.getVideoTracks()===null||config.myStream.getVideoTracks()===undefined||config.myStream.getVideoTracks().length===0){Janus.warn("No video track");return true}return!config.myStream.getVideoTracks()[0].enabled}else{if(config.myStream.getAudioTracks()===null||config.myStream.getAudioTracks()===undefined||config.myStream.getAudioTracks().length===0){Janus.warn("No audio track");return true}return!config.myStream.getAudioTracks()[0].enabled}}function mute(handleId,video,mute){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return false}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined){Janus.warn("Invalid PeerConnection");return false}if(config.myStream===undefined||config.myStream===null){Janus.warn("Invalid local MediaStream");return false}if(video){if(config.myStream.getVideoTracks()===null||config.myStream.getVideoTracks()===undefined||config.myStream.getVideoTracks().length===0){Janus.warn("No video track");return false}config.myStream.getVideoTracks()[0].enabled=mute?false:true;return true}else{if(config.myStream.getAudioTracks()===null||config.myStream.getAudioTracks()===undefined||config.myStream.getAudioTracks().length===0){Janus.warn("No audio track");return false}config.myStream.getAudioTracks()[0].enabled=mute?false:true;return true}}function getBitrate(handleId){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return"Invalid handle"}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined)return"Invalid PeerConnection";if(config.pc.getStats&&adapter.browserDetails.browser=="chrome"){if(config.remoteStream===null||config.remoteStream===undefined){Janus.warn("Remote stream unavailable");return"Remote stream unavailable"}if(config.bitrate.timer===null||config.bitrate.timer===undefined){Janus.log("Starting bitrate timer (Chrome)");config.bitrate.timer=setInterval(function(){config.pc.getStats(function(stats){var results=stats.result();for(var i=0;i<results.length;i++){var res=results[i];if(res.type=="ssrc"&&res.stat("googFrameHeightReceived")){config.bitrate.bsnow=res.stat("bytesReceived");config.bitrate.tsnow=res.timestamp;if(config.bitrate.bsbefore===null||config.bitrate.tsbefore===null){config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}else{var bitRate=Math.round((config.bitrate.bsnow-config.bitrate.bsbefore)*8/(config.bitrate.tsnow-config.bitrate.tsbefore));config.bitrate.value=bitRate+" kbits/sec";config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}}}})},1e3);return"0 kbits/sec"}return config.bitrate.value}else if(config.pc.getStats&&adapter.browserDetails.browser=="firefox"){if(config.remoteStream===null||config.remoteStream===undefined||config.remoteStream.streams[0]===null||config.remoteStream.streams[0]===undefined){Janus.warn("Remote stream unavailable");return"Remote stream unavailable"}var videoTracks=config.remoteStream.streams[0].getVideoTracks();if(videoTracks===null||videoTracks===undefined||videoTracks.length<1){Janus.warn("No video track");return"No video track"}if(config.bitrate.timer===null||config.bitrate.timer===undefined){Janus.log("Starting bitrate timer (Firefox)");config.bitrate.timer=setInterval(function(){var cb=function(res){if(res===null||res===undefined||res.inbound_rtp_video_1==null||res.inbound_rtp_video_1==null){config.bitrate.value="Missing inbound_rtp_video_1";return}config.bitrate.bsnow=res.inbound_rtp_video_1.bytesReceived;config.bitrate.tsnow=res.inbound_rtp_video_1.timestamp;if(config.bitrate.bsbefore===null||config.bitrate.tsbefore===null){config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}else{var bitRate=Math.round((config.bitrate.bsnow-config.bitrate.bsbefore)*8/(config.bitrate.tsnow-config.bitrate.tsbefore));config.bitrate.value=bitRate+" kbits/sec";config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}};config.pc.getStats(videoTracks[0],function(stats){cb(stats)},cb)},1e3);return"0 kbits/sec"}return config.bitrate.value}else{Janus.warn("Getting the video bitrate unsupported by browser");return"Feature unsupported by browser"}}function webrtcError(error){Janus.error("WebRTC error:",error)}function cleanupWebrtc(handleId,hangupRequest){Janus.log("Cleaning WebRTC stuff");var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined){return}var config=pluginHandle.webrtcStuff;if(config!==null&&config!==undefined){if(hangupRequest===true){var request={janus:"hangup",transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;Janus.debug("Sending hangup request (handle="+handleId+"):");Janus.debug(request);if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;ws.send(JSON.stringify(request))}else{$.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,xhrFields:{withCredentials:withCredentials},cache:false,contentType:"application/json",data:JSON.stringify(request),dataType:"json"})}}config.remoteStream=null;if(config.volume.timer)clearInterval(config.volume.timer);config.volume.value=null;if(config.bitrate.timer)clearInterval(config.bitrate.timer);config.bitrate.timer=null;config.bitrate.bsnow=null;config.bitrate.bsbefore=null;config.bitrate.tsnow=null;config.bitrate.tsbefore=null;config.bitrate.value=null;try{if(!config.streamExternal&&config.myStream!==null&&config.myStream!==undefined){Janus.log("Stopping local stream");config.myStream.stop()}}catch(e){}try{if(!config.streamExternal&&config.myStream!==null&&config.myStream!==undefined){Janus.log("Stopping local stream tracks");var tracks=config.myStream.getTracks();for(var i in tracks){var mst=tracks[i];Janus.log(mst);if(mst!==null&&mst!==undefined)mst.stop()}}}catch(e){}config.streamExternal=false;config.myStream=null;try{config.pc.close()}catch(e){}config.pc=null;config.mySdp=null;config.iceDone=false;config.sdpSent=false;config.dataChannel=null;config.dtmfSender=null}pluginHandle.oncleanup()}function isAudioSendEnabled(media){Janus.debug("isAudioSendEnabled:",media);if(media===undefined||media===null)return true;if(media.audio===false)return false;if(media.audioSend===undefined||media.audioSend===null)return true;return media.audioSend===true}function isAudioSendRequired(media){Janus.debug("isAudioSendRequired:",media);if(media===undefined||media===null)return false;if(media.audio===false||media.audioSend===false)return false;if(media.failIfNoAudio===undefined||media.failIfNoAudio===null)return false;return media.failIfNoAudio===true}function isAudioRecvEnabled(media){Janus.debug("isAudioRecvEnabled:",media);if(media===undefined||media===null)return true;if(media.audio===false)return false;if(media.audioRecv===undefined||media.audioRecv===null)return true;return media.audioRecv===true}function isVideoSendEnabled(media){Janus.debug("isVideoSendEnabled:",media);if(media===undefined||media===null)return true;if(media.video===false)return false;if(media.videoSend===undefined||media.videoSend===null)return true;return media.videoSend===true}function isVideoSendRequired(media){Janus.debug("isVideoSendRequired:",media);if(media===undefined||media===null)return false;if(media.video===false||media.videoSend===false)return false;if(media.failIfNoVideo===undefined||media.failIfNoVideo===null)return false;return media.failIfNoVideo===true}function isVideoRecvEnabled(media){Janus.debug("isVideoRecvEnabled:",media);if(media===undefined||media===null)return true;if(media.video===false)return false;if(media.videoRecv===undefined||media.videoRecv===null)return true;return media.videoRecv===true}function isDataEnabled(media){Janus.debug("isDataEnabled:",media);if(adapter.browserDetails.browser=="edge"){Janus.warn("Edge doesn't support data channels yet");return false}if(media===undefined||media===null)return false;return media.data===true}function isTrickleEnabled(trickle){Janus.debug("isTrickleEnabled:",trickle);if(trickle===undefined||trickle===null)return true;return trickle===true}}
+;callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.debug("streamsDone:",stream);config.myStream=stream;var pc_config={iceServers:iceServers,iceTransportPolicy:iceTransportPolicy};var pc_constraints={optional:[{DtlsSrtpKeyAgreement:true}]};if(ipv6Support===true){pc_constraints.optional.push({googIPv6:true})}if(adapter.browserDetails.browser==="edge"){pc_config.bundlePolicy="max-bundle"}Janus.log("Creating PeerConnection");Janus.debug(pc_constraints);config.pc=new RTCPeerConnection(pc_config,pc_constraints);Janus.debug(config.pc);if(config.pc.getStats){config.volume.value=0;config.bitrate.value="0 kbits/sec"}Janus.log("Preparing local SDP and gathering candidates (trickle="+config.trickle+")");config.pc.oniceconnectionstatechange=function(e){if(config.pc)pluginHandle.iceState(config.pc.iceConnectionState)};config.pc.onicecandidate=function(event){if(event.candidate==null||adapter.browserDetails.browser==="edge"&&event.candidate.candidate.indexOf("endOfCandidates")>0){Janus.log("End of candidates.");config.iceDone=true;if(config.trickle===true){sendTrickleCandidate(handleId,{completed:true})}else{sendSDP(handleId,callbacks)}}else{var candidate={candidate:event.candidate.candidate,sdpMid:event.candidate.sdpMid,sdpMLineIndex:event.candidate.sdpMLineIndex};if(config.trickle===true){sendTrickleCandidate(handleId,candidate)}}};if(stream!==null&&stream!==undefined){Janus.log("Adding local stream");config.pc.addStream(stream);pluginHandle.onlocalstream(stream)}config.pc.onaddstream=function(remoteStream){Janus.log("Handling Remote Stream");Janus.debug(remoteStream);config.remoteStream=remoteStream;pluginHandle.onremotestream(remoteStream.stream)};if(isDataEnabled(media)){Janus.log("Creating data channel");var onDataChannelMessage=function(event){Janus.log("Received message on data channel: "+event.data);pluginHandle.ondata(event.data)};var onDataChannelStateChange=function(){var dcState=config.dataChannel!==null?config.dataChannel.readyState:"null";Janus.log("State change on data channel: "+dcState);if(dcState==="open"){pluginHandle.ondataopen()}};var onDataChannelError=function(error){Janus.error("Got error on data channel:",error)};config.dataChannel=config.pc.createDataChannel("JanusDataChannel",{ordered:false});config.dataChannel.onmessage=onDataChannelMessage;config.dataChannel.onopen=onDataChannelStateChange;config.dataChannel.onclose=onDataChannelStateChange;config.dataChannel.onerror=onDataChannelError}if(jsep===null||jsep===undefined){createOffer(handleId,media,callbacks)}else{if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");createAnswer(handleId,media,callbacks)},callbacks.error)}}function prepareWebrtc(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:webrtcError;var jsep=callbacks.jsep;var media=callbacks.media;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(config.pc!==undefined&&config.pc!==null){Janus.log("Updating existing media session");if(jsep===null||jsep===undefined){createOffer(handleId,media,callbacks)}else{if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");createAnswer(handleId,media,callbacks)},callbacks.error)}return}if(callbacks.stream!==null&&callbacks.stream!==undefined){var stream=callbacks.stream;Janus.log("MediaStream provided by the application");Janus.debug(stream);config.streamExternal=true;streamsDone(handleId,jsep,media,callbacks,stream);return}config.trickle=isTrickleEnabled(callbacks.trickle);if(isAudioSendEnabled(media)||isVideoSendEnabled(media)){var constraints={mandatory:{},optional:[]};pluginHandle.consentDialog(true);var audioSupport=isAudioSendEnabled(media);if(audioSupport===true&&media!=undefined&&media!=null){if(typeof media.audio==="object"){audioSupport=media.audio}}var videoSupport=isVideoSendEnabled(media);if(videoSupport===true&&media!=undefined&&media!=null){if(media.video&&media.video!="screen"&&media.video!="window"){var width=0;var height=0,maxHeight=0;if(media.video==="lowres"){height=240;maxHeight=240;width=320}else if(media.video==="lowres-16:9"){height=180;maxHeight=180;width=320}else if(media.video==="hires"||media.video==="hires-16:9"){height=720;maxHeight=720;width=1280;if(navigator.mozGetUserMedia){var firefoxVer=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(firefoxVer<38){Janus.warn(media.video+" unsupported, falling back to stdres (old Firefox)");height=480;maxHeight=480;width=640}}}else if(media.video==="stdres"){height=480;maxHeight=480;width=640}else if(media.video==="stdres-16:9"){height=360;maxHeight=360;width=640}else{Janus.log("Default video setting ("+media.video+") is stdres 4:3");height=480;maxHeight=480;width=640}Janus.log("Adding media constraint "+media.video);if(navigator.mozGetUserMedia){var firefoxVer=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(firefoxVer<38){videoSupport={require:["height","width"],height:{max:maxHeight,min:height},width:{max:width,min:width}}}else{videoSupport={height:{ideal:height},width:{ideal:width}}}}else{videoSupport={mandatory:{maxHeight:maxHeight,minHeight:height,maxWidth:width,minWidth:width},optional:[]}}if(typeof media.video==="object"){videoSupport=media.video}Janus.debug(videoSupport)}else if(media.video==="screen"||media.video==="window"){if(!media.screenshareFrameRate){media.screenshareFrameRate=3}if(window.location.protocol!=="https:"){Janus.warn("Screen sharing only works on HTTPS, try the https:// version of this page");pluginHandle.consentDialog(false);callbacks.error("Screen sharing only works on HTTPS, try the https:// version of this page");return}var cache={};function callbackUserMedia(error,stream){pluginHandle.consentDialog(false);if(error){callbacks.error({code:error.code,name:error.name,message:error.message})}else{streamsDone(handleId,jsep,media,callbacks,stream)}}function getScreenMedia(constraints,gsmCallback){Janus.log("Adding media constraint (screen capture)");Janus.debug(constraints);navigator.mediaDevices.getUserMedia(constraints).then(function(stream){gsmCallback(null,stream)}).catch(function(error){pluginHandle.consentDialog(false);gsmCallback(error)})}if(adapter.browserDetails.browser==="chrome"){var chromever=adapter.browserDetails.version;var maxver=33;if(window.navigator.userAgent.match("Linux"))maxver=35;if(chromever>=26&&chromever<=maxver){constraints={video:{mandatory:{googLeakyBucket:true,maxWidth:window.screen.width,maxHeight:window.screen.height,minFrameRate:media.screenshareFrameRate,maxFrameRate:media.screenshareFrameRate,chromeMediaSource:"screen"}},audio:isAudioSendEnabled(media)};getScreenMedia(constraints,callbackUserMedia)}else{var pending=window.setTimeout(function(){error=new Error("NavigatorUserMediaError");error.name='The required Chrome extension is not installed: click <a href="#">here</a> to install it. (NOTE: this will need you to refresh the page)';pluginHandle.consentDialog(false);return callbacks.error(error)},1e3);cache[pending]=[callbackUserMedia,null];window.postMessage({type:"janusGetScreen",id:pending},"*")}}else if(window.navigator.userAgent.match("Firefox")){var ffver=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(ffver>=33){constraints={video:{mozMediaSource:media.video,mediaSource:media.video},audio:isAudioSendEnabled(media)};getScreenMedia(constraints,function(err,stream){callbackUserMedia(err,stream);if(!err){var lastTime=stream.currentTime;var polly=window.setInterval(function(){if(!stream)window.clearInterval(polly);if(stream.currentTime==lastTime){window.clearInterval(polly);if(stream.onended){stream.onended()}}lastTime=stream.currentTime},500)}})}else{var error=new Error("NavigatorUserMediaError");error.name="Your version of Firefox does not support screen sharing, please install Firefox 33 (or more recent versions)";pluginHandle.consentDialog(false);callbacks.error(error);return}}window.addEventListener("message",function(event){if(event.origin!=window.location.origin)return;if(event.data.type=="janusGotScreen"&&cache[event.data.id]){var data=cache[event.data.id];var callback=data[0];delete cache[event.data.id];if(event.data.sourceId===""){var error=new Error("NavigatorUserMediaError");error.name="You cancelled the request for permission, giving up...";pluginHandle.consentDialog(false);callbacks.error(error)}else{constraints={audio:isAudioSendEnabled(media),video:{mandatory:{chromeMediaSource:"desktop",maxWidth:window.screen.width,maxHeight:window.screen.height,minFrameRate:media.screenshareFrameRate,maxFrameRate:media.screenshareFrameRate},optional:[{googLeakyBucket:true},{googTemporalLayeredScreencast:true}]}};constraints.video.mandatory.chromeMediaSourceId=event.data.sourceId;getScreenMedia(constraints,callback)}}else if(event.data.type=="janusGetScreenPending"){window.clearTimeout(event.data.id)}});return}}if(media===null||media===undefined||media.video!=="screen"){navigator.mediaDevices.enumerateDevices().then(function(devices){var audioExist=devices.some(function(device){return device.kind==="audioinput"}),videoExist=devices.some(function(device){return device.kind==="videoinput"});var audioSend=isAudioSendEnabled(media);var videoSend=isVideoSendEnabled(media);var needAudioDevice=isAudioSendRequired(media);var needVideoDevice=isVideoSendRequired(media);if(audioSend||videoSend||needAudioDevice||needVideoDevice){var haveAudioDevice=audioSend?audioExist:false;var haveVideoDevice=videoSend?videoExist:false;if(!haveAudioDevice&&!haveVideoDevice){pluginHandle.consentDialog(false);callbacks.error("No capture device found");return false}else if(!haveAudioDevice&&needAudioDevice){pluginHandle.consentDialog(false);callbacks.error("Audio capture is required, but no capture device found");return false}else if(!haveVideoDevice&&needVideoDevice){pluginHandle.consentDialog(false);callbacks.error("Video capture is required, but no capture device found");return false}}navigator.mediaDevices.getUserMedia({audio:audioExist?audioSupport:false,video:videoExist?videoSupport:false}).then(function(stream){pluginHandle.consentDialog(false);streamsDone(handleId,jsep,media,callbacks,stream)}).catch(function(error){pluginHandle.consentDialog(false);callbacks.error({code:error.code,name:error.name,message:error.message})})}).catch(function(error){pluginHandle.consentDialog(false);callbacks.error("enumerateDevices error",error)})}}else{streamsDone(handleId,jsep,media,callbacks)}}function prepareWebrtcPeer(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:webrtcError;var jsep=callbacks.jsep;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(jsep!==undefined&&jsep!==null){if(config.pc===null){Janus.warn("Wait, no PeerConnection?? if this is an answer, use createAnswer and not handleRemoteJsep");callbacks.error("No PeerConnection: if this is an answer, use createAnswer and not handleRemoteJsep");return}if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");callbacks.success()},callbacks.error)}else{callbacks.error("Invalid JSEP")}}function createOffer(handleId,media,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.log("Creating offer (iceDone="+config.iceDone+")");var mediaConstraints=null;if(adapter.browserDetails.browser=="firefox"||adapter.browserDetails.browser=="edge"){mediaConstraints={offerToReceiveAudio:isAudioRecvEnabled(media),offerToReceiveVideo:isVideoRecvEnabled(media)}}else{mediaConstraints={mandatory:{OfferToReceiveAudio:isAudioRecvEnabled(media),OfferToReceiveVideo:isVideoRecvEnabled(media)}}}Janus.debug(mediaConstraints);config.pc.createOffer(function(offer){Janus.debug(offer);if(config.mySdp===null||config.mySdp===undefined){Janus.log("Setting local description");config.mySdp=offer.sdp;config.pc.setLocalDescription(offer)}if(!config.iceDone&&!config.trickle){Janus.log("Waiting for all candidates...");return}if(config.sdpSent){Janus.log("Offer already sent, not sending it again");return}Janus.log("Offer ready");Janus.debug(callbacks);config.sdpSent=true;var jsep={type:offer.type,sdp:offer.sdp};callbacks.success(jsep)},callbacks.error,mediaConstraints)}function createAnswer(handleId,media,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.log("Creating answer (iceDone="+config.iceDone+")");var mediaConstraints=null;if(adapter.browserDetails.browser=="firefox"||adapter.browserDetails.browser=="edge"){mediaConstraints={offerToReceiveAudio:isAudioRecvEnabled(media),offerToReceiveVideo:isVideoRecvEnabled(media)}}else{mediaConstraints={mandatory:{OfferToReceiveAudio:isAudioRecvEnabled(media),OfferToReceiveVideo:isVideoRecvEnabled(media)}}}Janus.debug(mediaConstraints);config.pc.createAnswer(function(answer){Janus.debug(answer);if(config.mySdp===null||config.mySdp===undefined){Janus.log("Setting local description");config.mySdp=answer.sdp;config.pc.setLocalDescription(answer)}if(!config.iceDone&&!config.trickle){Janus.log("Waiting for all candidates...");return}if(config.sdpSent){Janus.log("Answer already sent, not sending it again");return}config.sdpSent=true;var jsep={type:answer.type,sdp:answer.sdp};callbacks.success(jsep)},callbacks.error,mediaConstraints)}function sendSDP(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:jQuery.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:jQuery.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle, not sending anything");return}var config=pluginHandle.webrtcStuff;Janus.log("Sending offer/answer SDP...");if(config.mySdp===null||config.mySdp===undefined){Janus.warn("Local SDP instance is invalid, not sending anything...");return}config.mySdp={type:config.pc.localDescription.type,sdp:config.pc.localDescription.sdp};if(config.sdpSent){Janus.log("Offer/Answer SDP already sent, not sending it again");return}if(config.trickle===false)config.mySdp["trickle"]=false;Janus.debug(callbacks);config.sdpSent=true;callbacks.success(config.mySdp)}function getVolume(handleId){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return 0}var config=pluginHandle.webrtcStuff;if(config.pc.getStats&&adapter.browserDetails.browser=="chrome"){if(config.remoteStream===null||config.remoteStream===undefined){Janus.warn("Remote stream unavailable");return 0}if(config.volume.timer===null||config.volume.timer===undefined){Janus.log("Starting volume monitor");config.volume.timer=setInterval(function(){config.pc.getStats(function(stats){var results=stats.result();for(var i=0;i<results.length;i++){var res=results[i];if(res.type=="ssrc"&&res.stat("audioOutputLevel")){config.volume.value=res.stat("audioOutputLevel")}}})},200);return 0}return config.volume.value}else{Janus.log("Getting the remote volume unsupported by browser");return 0}}function isMuted(handleId,video){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return true}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined){Janus.warn("Invalid PeerConnection");return true}if(config.myStream===undefined||config.myStream===null){Janus.warn("Invalid local MediaStream");return true}if(video){if(config.myStream.getVideoTracks()===null||config.myStream.getVideoTracks()===undefined||config.myStream.getVideoTracks().length===0){Janus.warn("No video track");return true}return!config.myStream.getVideoTracks()[0].enabled}else{if(config.myStream.getAudioTracks()===null||config.myStream.getAudioTracks()===undefined||config.myStream.getAudioTracks().length===0){Janus.warn("No audio track");return true}return!config.myStream.getAudioTracks()[0].enabled}}function mute(handleId,video,mute){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return false}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined){Janus.warn("Invalid PeerConnection");return false}if(config.myStream===undefined||config.myStream===null){Janus.warn("Invalid local MediaStream");return false}if(video){if(config.myStream.getVideoTracks()===null||config.myStream.getVideoTracks()===undefined||config.myStream.getVideoTracks().length===0){Janus.warn("No video track");return false}config.myStream.getVideoTracks()[0].enabled=mute?false:true;return true}else{if(config.myStream.getAudioTracks()===null||config.myStream.getAudioTracks()===undefined||config.myStream.getAudioTracks().length===0){Janus.warn("No audio track");return false}config.myStream.getAudioTracks()[0].enabled=mute?false:true;return true}}function getBitrate(handleId){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return"Invalid handle"}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined)return"Invalid PeerConnection";if(config.pc.getStats&&adapter.browserDetails.browser=="chrome"){if(config.remoteStream===null||config.remoteStream===undefined){Janus.warn("Remote stream unavailable");return"Remote stream unavailable"}if(config.bitrate.timer===null||config.bitrate.timer===undefined){Janus.log("Starting bitrate timer (Chrome)");config.bitrate.timer=setInterval(function(){config.pc.getStats(function(stats){var results=stats.result();for(var i=0;i<results.length;i++){var res=results[i];if(res.type=="ssrc"&&res.stat("googFrameHeightReceived")){config.bitrate.bsnow=res.stat("bytesReceived");config.bitrate.tsnow=res.timestamp;if(config.bitrate.bsbefore===null||config.bitrate.tsbefore===null){config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}else{var bitRate=Math.round((config.bitrate.bsnow-config.bitrate.bsbefore)*8/(config.bitrate.tsnow-config.bitrate.tsbefore));config.bitrate.value=bitRate+" kbits/sec";config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}}}})},1e3);return"0 kbits/sec"}return config.bitrate.value}else if(config.pc.getStats&&adapter.browserDetails.browser=="firefox"){if(config.remoteStream===null||config.remoteStream===undefined||config.remoteStream.streams[0]===null||config.remoteStream.streams[0]===undefined){Janus.warn("Remote stream unavailable");return"Remote stream unavailable"}var videoTracks=config.remoteStream.streams[0].getVideoTracks();if(videoTracks===null||videoTracks===undefined||videoTracks.length<1){Janus.warn("No video track");return"No video track"}if(config.bitrate.timer===null||config.bitrate.timer===undefined){Janus.log("Starting bitrate timer (Firefox)");config.bitrate.timer=setInterval(function(){var cb=function(res){if(res===null||res===undefined||res.inbound_rtp_video_1==null||res.inbound_rtp_video_1==null){config.bitrate.value="Missing inbound_rtp_video_1";return}config.bitrate.bsnow=res.inbound_rtp_video_1.bytesReceived;config.bitrate.tsnow=res.inbound_rtp_video_1.timestamp;if(config.bitrate.bsbefore===null||config.bitrate.tsbefore===null){config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}else{var bitRate=Math.round((config.bitrate.bsnow-config.bitrate.bsbefore)*8/(config.bitrate.tsnow-config.bitrate.tsbefore));config.bitrate.value=bitRate+" kbits/sec";config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}};config.pc.getStats(videoTracks[0],function(stats){cb(stats)},cb)},1e3);return"0 kbits/sec"}return config.bitrate.value}else{Janus.warn("Getting the video bitrate unsupported by browser");return"Feature unsupported by browser"}}function webrtcError(error){Janus.error("WebRTC error:",error)}function cleanupWebrtc(handleId,hangupRequest){Janus.log("Cleaning WebRTC stuff");var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined){return}var config=pluginHandle.webrtcStuff;if(config!==null&&config!==undefined){if(hangupRequest===true){var request={janus:"hangup",transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;Janus.debug("Sending hangup request (handle="+handleId+"):");Janus.debug(request);if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;ws.send(JSON.stringify(request))}else{$.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,xhrFields:{withCredentials:withCredentials},cache:false,contentType:"application/json",data:JSON.stringify(request),dataType:"json"})}}config.remoteStream=null;if(config.volume.timer)clearInterval(config.volume.timer);config.volume.value=null;if(config.bitrate.timer)clearInterval(config.bitrate.timer);config.bitrate.timer=null;config.bitrate.bsnow=null;config.bitrate.bsbefore=null;config.bitrate.tsnow=null;config.bitrate.tsbefore=null;config.bitrate.value=null;try{if(!config.streamExternal&&config.myStream!==null&&config.myStream!==undefined){Janus.log("Stopping local stream");config.myStream.stop()}}catch(e){}try{if(!config.streamExternal&&config.myStream!==null&&config.myStream!==undefined){Janus.log("Stopping local stream tracks");var tracks=config.myStream.getTracks();for(var i in tracks){var mst=tracks[i];Janus.log(mst);if(mst!==null&&mst!==undefined)mst.stop()}}}catch(e){}config.streamExternal=false;config.myStream=null;try{config.pc.close()}catch(e){}config.pc=null;config.mySdp=null;config.iceDone=false;config.sdpSent=false;config.dataChannel=null;config.dtmfSender=null}pluginHandle.oncleanup()}function isAudioSendEnabled(media){Janus.debug("isAudioSendEnabled:",media);if(media===undefined||media===null)return true;if(media.audio===false)return false;if(media.audioSend===undefined||media.audioSend===null)return true;return media.audioSend===true}function isAudioSendRequired(media){Janus.debug("isAudioSendRequired:",media);if(media===undefined||media===null)return false;if(media.audio===false||media.audioSend===false)return false;if(media.failIfNoAudio===undefined||media.failIfNoAudio===null)return false;return media.failIfNoAudio===true}function isAudioRecvEnabled(media){Janus.debug("isAudioRecvEnabled:",media);if(media===undefined||media===null)return true;if(media.audio===false)return false;if(media.audioRecv===undefined||media.audioRecv===null)return true;return media.audioRecv===true}function isVideoSendEnabled(media){Janus.debug("isVideoSendEnabled:",media);if(media===undefined||media===null)return true;if(media.video===false)return false;if(media.videoSend===undefined||media.videoSend===null)return true;return media.videoSend===true}function isVideoSendRequired(media){Janus.debug("isVideoSendRequired:",media);if(media===undefined||media===null)return false;if(media.video===false||media.videoSend===false)return false;if(media.failIfNoVideo===undefined||media.failIfNoVideo===null)return false;return media.failIfNoVideo===true}function isVideoRecvEnabled(media){Janus.debug("isVideoRecvEnabled:",media);if(media===undefined||media===null)return true;if(media.video===false)return false;if(media.videoRecv===undefined||media.videoRecv===null)return true;return media.videoRecv===true}function isDataEnabled(media){Janus.debug("isDataEnabled:",media);if(adapter.browserDetails.browser=="edge"){Janus.warn("Edge doesn't support data channels yet");return false}if(media===undefined||media===null)return false;return media.data===true}function isTrickleEnabled(trickle){Janus.debug("isTrickleEnabled:",trickle);if(trickle===undefined||trickle===null)return true;return trickle===true}}
diff --git a/js/lib/janus.nojquery.min.js b/js/lib/janus.nojquery.min.js
index 5adb9e5..573f13a 100644
--- a/js/lib/janus.nojquery.min.js
+++ b/js/lib/janus.nojquery.min.js
@@ -1,2 +1,2 @@
Janus.sessions={};Janus.extensionId="hapfgfdkleiggjjpfpenajgdnfckjpaj";Janus.isExtensionEnabled=function(){if(window.navigator.userAgent.match("Chrome")){var chromever=parseInt(window.navigator.userAgent.match(/Chrome\/(.*) /)[1],10);var maxver=33;if(window.navigator.userAgent.match("Linux"))maxver=35;if(chromever>=26&&chromever<=maxver){return true}return document.getElementById("janus-extension-installed")!==null}else{return true}};Janus.noop=function(){};Janus.init=function(options){options=options||{};options.callback=typeof options.callback=="function"?options.callback:Janus.noop;if(Janus.initDone===true){options.callback()}else{if(typeof console=="undefined"||typeof console.log=="undefined")console={log:function(){}};Janus.trace=Janus.noop;Janus.debug=Janus.noop;Janus.vdebug=Janus.noop;Janus.log=Janus.noop;Janus.warn=Janus.noop;Janus.error=Janus.noop;if(options.debug===true||options.debug==="all"){Janus.trace=console.trace.bind(console);Janus.debug=console.debug.bind(console);Janus.vdebug=console.debug.bind(console);Janus.log=console.log.bind(console);Janus.warn=console.warn.bind(console);Janus.error=console.error.bind(console)}else if(Array.isArray(options.debug)){for(var i in options.debug){var d=options.debug[i];switch(d){case"trace":Janus.trace=console.trace.bind(console);break;case"debug":Janus.debug=console.debug.bind(console);break;case"vdebug":Janus.vdebug=console.debug.bind(console);break;case"log":Janus.log=console.log.bind(console);break;case"warn":Janus.warn=console.warn.bind(console);break;case"error":Janus.error=console.error.bind(console);break;default:console.error("Unknown debugging option '"+d+"' (supported: 'trace', 'debug', 'vdebug', 'log', warn', 'error')");break}}}Janus.log("Initializing library");Janus.listDevices=function(callback){callback=typeof callback=="function"?callback:Janus.noop;if(navigator.mediaDevices){navigator.mediaDevices.getUserMedia({audio:true,video:true}).then(function(stream){navigator.mediaDevices.enumerateDevices().then(function(devices){Janus.debug(devices);callback(devices);try{stream.stop()}catch(e){}try{var tracks=stream.getTracks();for(var i in tracks){var mst=tracks[i];if(mst!==null&&mst!==undefined)mst.stop()}}catch(e){}})}).catch(function(err){Janus.error(err);callback([])})}else{Janus.warn("navigator.mediaDevices unavailable");callback([])}};Janus.attachMediaStream=function(element,stream){if(adapter.browserDetails.browser==="chrome"){var chromever=adapter.browserDetails.version;if(chromever>=43){element.srcObject=stream}else if(typeof element.src!=="undefined"){element.src=URL.createObjectURL(stream)}else{Janus.error("Error attaching stream to element")}}else if(adapter.browserDetails.browser==="safari"||window.navigator.userAgent.match(/iPad/i)||window.navigator.userAgent.match(/iPhone/i)){element.src=URL.createObjectURL(stream)}else{element.srcObject=stream}};Janus.reattachMediaStream=function(to,from){if(adapter.browserDetails.browser==="chrome"){var chromever=adapter.browserDetails.version;if(chromever>=43){to.srcObject=from.srcObject}else if(typeof to.src!=="undefined"){to.src=from.src}}else if(adapter.browserDetails.browser==="safari"||window.navigator.userAgent.match(/iPad/i)||window.navigator.userAgent.match(/iPhone/i)){to.src=from.src}else{to.srcObject=from.srcObject}};Janus.ajax=function(params){if(params===null||params===undefined)return;params.success=typeof params.success=="function"?params.success:Janus.noop;params.error=typeof params.error=="function"?params.error:Janus.noop;if(params.url===null||params.url===undefined){Janus.error("Missing url",params.url);params.error(null,-1,"Missing url");return}params.async=params.async===null||params.async===undefined?true:params.async===true;Janus.log(params);var XHR=new XMLHttpRequest;XHR.open(params.type,params.url,params.async);if(params.contentType!==null&&params.contentType!==undefined)XHR.setRequestHeader("Content-type",params.contentType);if(params.withCredentials!==null&&params.withCredentials!==undefined)XHR.withCredentials=params.withCredentials;if(params.async){XHR.onreadystatechange=function(){if(XHR.readyState!=4)return;if(XHR.status!==200){params.error(XHR,XHR.status!==0?XHR.status:"error","");return}try{params.success(JSON.parse(XHR.responseText))}catch(e){params.error(XHR,XHR.status,"Could not parse response, error: "+e+", text: "+XHR.responseText)}}}try{XHR.send(params.data);if(!params.async){if(XHR.status!==200){params.error(XHR,XHR.status!==0?XHR.status:"error","");return}try{params.success(JSON.parse(XHR.responseText))}catch(e){params.error(XHR,XHR.status,"Could not parse response, error: "+e+", text: "+XHR.responseText)}}}catch(e){params.error(XHR,"error","")}};var oldOBF=window.onbeforeunload;window.onbeforeunload=function(){Janus.log("Closing window");for(var s in Janus.sessions){if(Janus.sessions[s]!==null&&Janus.sessions[s]!==undefined&&Janus.sessions[s].destroyOnUnload){Janus.log("Destroying session "+s);Janus.sessions[s].destroy({asyncRequest:false})}}if(oldOBF&&typeof oldOBF=="function")oldOBF()};Janus.initDone=true;options.callback()}};Janus.isWebrtcSupported=function(){return window.RTCPeerConnection!==undefined&&window.RTCPeerConnection!==null&&navigator.getUserMedia!==undefined&&navigator.getUserMedia!==null};Janus.randomString=function(len){var charSet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";var randomString="";for(var i=0;i<len;i++){var randomPoz=Math.floor(Math.random()*charSet.length);randomString+=charSet.substring(randomPoz,randomPoz+1)}return randomString};function Janus(gatewayCallbacks){if(Janus.initDone===undefined){gatewayCallbacks.error("Library not initialized");return{}}if(!Janus.isWebrtcSupported()){gatewayCallbacks.error("WebRTC not supported by this browser");return{}}Janus.log("Library initialized: "+Janus.initDone);gatewayCallbacks=gatewayCallbacks||{};gatewayCallbacks.success=typeof gatewayCallbacks.success=="function"?gatewayCallbacks.success:Janus.noop;gatewayCallbacks.error=typeof gatewayCallbacks.error=="function"?gatewayCallbacks.error:Janus.noop;gatewayCallbacks.destroyed=typeof gatewayCallbacks.destroyed=="function"?gatewayCallbacks.destroyed:Janus.noop;if(gatewayCallbacks.server===null||gatewayCallbacks.server===undefined){gatewayCallbacks.error("Invalid gateway url");return{}}var websockets=false;var ws=null;var wsHandlers={};var wsKeepaliveTimeoutId=null;var servers=null,serversIndex=0;var server=gatewayCallbacks.server;if(Array.isArray(server)){Janus.log("Multiple servers provided ("+server.length+"), will use the first that works");server=null;servers=gatewayCallbacks.server;Janus.debug(servers)}else{if(server.indexOf("ws")===0){websockets=true;Janus.log("Using WebSockets to contact Janus: "+server)}else{websockets=false;Janus.log("Using REST API to contact Janus: "+server)}}var iceServers=gatewayCallbacks.iceServers;if(iceServers===undefined||iceServers===null)iceServers=[{urls:"stun:stun.l.google.com:19302"}];var iceTransportPolicy=gatewayCallbacks.iceTransportPolicy;var ipv6Support=gatewayCallbacks.ipv6;if(ipv6Support===undefined||ipv6Support===null)ipv6Support=false;var withCredentials=false;if(gatewayCallbacks.withCredentials!==undefined&&gatewayCallbacks.withCredentials!==null)withCredentials=gatewayCallbacks.withCredentials===true;var maxev=null;if(gatewayCallbacks.max_poll_events!==undefined&&gatewayCallbacks.max_poll_events!==null)maxev=gatewayCallbacks.max_poll_events;if(maxev<1)maxev=1;var token=null;if(gatewayCallbacks.token!==undefined&&gatewayCallbacks.token!==null)token=gatewayCallbacks.token;var apisecret=null;if(gatewayCallbacks.apisecret!==undefined&&gatewayCallbacks.apisecret!==null)apisecret=gatewayCallbacks.apisecret;this.destroyOnUnload=true;if(gatewayCallbacks.destroyOnUnload!==undefined&&gatewayCallbacks.destroyOnUnload!==null)this.destroyOnUnload=gatewayCallbacks.destroyOnUnload===true;var connected=false;var sessionId=null;var pluginHandles={};var that=this;var retries=0;var transactions={};createSession(gatewayCallbacks);this.getServer=function(){return server};this.isConnected=function(){return connected};this.getSessionId=function(){return sessionId};this.destroy=function(callbacks){destroySession(callbacks,true)};this.attach=function(callbacks){createHandle(callbacks)};function eventHandler(){if(sessionId==null)return;Janus.debug("Long poll...");if(!connected){Janus.warn("Is the gateway down? (connected=false)");return}var longpoll=server+"/"+sessionId+"?rid="+(new Date).getTime();if(maxev!==undefined&&maxev!==null)longpoll=longpoll+"&maxev="+maxev;if(token!==null&&token!==undefined)longpoll=longpoll+"&token="+token;if(apisecret!==null&&apisecret!==undefined)longpoll=longpoll+"&apisecret="+apisecret;Janus.ajax({type:"GET",url:longpoll,withCredentials:withCredentials,cache:false,timeout:6e4,success:handleEvent,error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);retries++;if(retries>3){connected=false;gatewayCallbacks.error("Lost connection to the gateway (is it down?)");return}eventHandler()},dataType:"json"})}function handleEvent(json){retries=0;if(!websockets&&sessionId!==undefined&&sessionId!==null)setTimeout(eventHandler,200);if(!websockets&&Array.isArray(json)){for(var i=0;i<json.length;i++){handleEvent(json[i])}return}if(json["janus"]==="keepalive"){Janus.vdebug("Got a keepalive on session "+sessionId);return}else if(json["janus"]==="ack"){Janus.debug("Got an ack on session "+sessionId);Janus.debug(json);var transaction=json["transaction"];if(transaction!==null&&transaction!==undefined){var reportSuccess=transactions[transaction];if(reportSuccess!==null&&reportSuccess!==undefined){reportSuccess(json)}delete transactions[transaction]}return}else if(json["janus"]==="success"){Janus.debug("Got a success on session "+sessionId);Janus.debug(json);var transaction=json["transaction"];if(transaction!==null&&transaction!==undefined){var reportSuccess=transactions[transaction];if(reportSuccess!==null&&reportSuccess!==undefined){reportSuccess(json)}delete transactions[transaction]}return}else if(json["janus"]==="webrtcup"){Janus.debug("Got a webrtcup event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.debug("This handle is not attached to this session");return}pluginHandle.webrtcState(true);return}else if(json["janus"]==="hangup"){Janus.debug("Got a hangup event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.debug("This handle is not attached to this session");return}pluginHandle.webrtcState(false,json["reason"]);pluginHandle.hangup()}else if(json["janus"]==="detached"){Janus.debug("Got a detached event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){return}pluginHandle.detached=true;pluginHandle.ondetached();pluginHandle.detach()}else if(json["janus"]==="media"){Janus.debug("Got a media event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.debug("This handle is not attached to this session");return}pluginHandle.mediaState(json["type"],json["receiving"])}else if(json["janus"]==="slowlink"){Janus.debug("Got a slowlink event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.debug("This handle is not attached to this session");return}pluginHandle.slowLink(json["uplink"],json["nacks"])}else if(json["janus"]==="error"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);Janus.debug(json);var transaction=json["transaction"];if(transaction!==null&&transaction!==undefined){var reportSuccess=transactions[transaction];if(reportSuccess!==null&&reportSuccess!==undefined){reportSuccess(json)}delete transactions[transaction]}return}else if(json["janus"]==="event"){Janus.debug("Got a plugin event on session "+sessionId);Janus.debug(json);var sender=json["sender"];if(sender===undefined||sender===null){Janus.warn("Missing sender...");return}var plugindata=json["plugindata"];if(plugindata===undefined||plugindata===null){Janus.warn("Missing plugindata...");return}Janus.debug(" -- Event is coming from "+sender+" ("+plugindata["plugin"]+")");var data=plugindata["data"];Janus.debug(data);var pluginHandle=pluginHandles[sender];if(pluginHandle===undefined||pluginHandle===null){Janus.warn("This handle is not attached to this session");return}var jsep=json["jsep"];if(jsep!==undefined&&jsep!==null){Janus.debug("Handling SDP as well...");Janus.debug(jsep)}var callback=pluginHandle.onmessage;if(callback!==null&&callback!==undefined){Janus.debug("Notifying application...");callback(data,jsep)}else{Janus.debug("No provided notification callback")}}else{Janus.warn("Unkown message/event '"+json["janus"]+"' on session "+sessionId);Janus.debug(json)}}function keepAlive(){if(server===null||!websockets||!connected)return;wsKeepaliveTimeoutId=setTimeout(keepAlive,3e4);var request={janus:"keepalive",session_id:sessionId,transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;ws.send(JSON.stringify(request))}function createSession(callbacks){var transaction=Janus.randomString(12);var request={janus:"create",transaction:transaction};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(server===null&&Array.isArray(servers)){server=servers[serversIndex];if(server.indexOf("ws")===0){websockets=true;Janus.log("Server #"+(serversIndex+1)+": trying WebSockets to contact Janus ("+server+")")}else{websockets=false;Janus.log("Server #"+(serversIndex+1)+": trying REST API to contact Janus ("+server+")")}}if(websockets){ws=new WebSocket(server,"janus-protocol");wsHandlers={error:function(){Janus.error("Error connecting to the Janus WebSockets server... "+server);if(Array.isArray(servers)){serversIndex++;if(serversIndex==servers.length){callbacks.error("Error connecting to any of the provided Janus servers: Is the gateway down?");return}server=null;setTimeout(function(){createSession(callbacks)},200);return}callbacks.error("Error connecting to the Janus WebSockets server: Is the gateway down?")},open:function(){transactions[transaction]=function(json){Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error(json["error"].reason);return}wsKeepaliveTimeoutId=setTimeout(keepAlive,3e4);connected=true;sessionId=json.data["id"];Janus.log("Created session: "+sessionId);Janus.sessions[sessionId]=that;callbacks.success()};ws.send(JSON.stringify(request))},message:function(event){try{handleEvent(JSON.parse(event.data))}catch(e){Janus.error("Error processing event:",e)}},close:function(){if(server===null||!connected){return}connected=false;gatewayCallbacks.error("Lost connection to the gateway (is it down?)")}};for(var eventName in wsHandlers){ws.addEventListener(eventName,wsHandlers[eventName])}return}Janus.ajax({type:"POST",url:server,withCredentials:withCredentials,cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error(json["error"].reason);return}connected=true;sessionId=json.data["id"];Janus.log("Created session: "+sessionId);Janus.sessions[sessionId]=that;eventHandler();callbacks.success()},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);if(Array.isArray(servers)){serversIndex++;if(serversIndex==servers.length){callbacks.error("Error connecting to any of the provided Janus servers: Is the gateway down?");return}server=null;setTimeout(function(){createSession(callbacks)},200);return}if(errorThrown==="")callbacks.error(textStatus+": Is the gateway down?");else callbacks.error(textStatus+": "+errorThrown)},dataType:"json"})}function destroySession(callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;var asyncRequest=true;if(callbacks.asyncRequest!==undefined&&callbacks.asyncRequest!==null)asyncRequest=callbacks.asyncRequest===true;Janus.log("Destroying session "+sessionId+" (async="+asyncRequest+")");if(!connected){Janus.warn("Is the gateway down? (connected=false)");callbacks.success();return}if(sessionId===undefined||sessionId===null){Janus.warn("No session to destroy");callbacks.success();gatewayCallbacks.destroyed();return}delete Janus.sessions[sessionId];var request={janus:"destroy",transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(websockets){request["session_id"]=sessionId;var unbindWebSocket=function(){for(var eventName in wsHandlers){ws.removeEventListener(eventName,wsHandlers[eventName])}ws.removeEventListener("message",onUnbindMessage);ws.removeEventListener("error",onUnbindError);if(wsKeepaliveTimeoutId){clearTimeout(wsKeepaliveTimeoutId)}};var onUnbindMessage=function(event){var data=JSON.parse(event.data);if(data.session_id==request.session_id&&data.transaction==request.transaction){unbindWebSocket();callbacks.success();gatewayCallbacks.destroyed()}};var onUnbindError=function(event){unbindWebSocket();callbacks.error("Failed to destroy the gateway: Is the gateway down?");gatewayCallbacks.destroyed()};ws.addEventListener("message",onUnbindMessage);ws.addEventListener("error",onUnbindError);ws.send(JSON.stringify(request));return}Janus.ajax({type:"POST",url:server+"/"+sessionId,async:asyncRequest,withCredentials:withCredentials,cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.log("Destroyed session:");Janus.debug(json);sessionId=null;connected=false;if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason)}callbacks.success();gatewayCallbacks.destroyed()},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);sessionId=null;connected=false;callbacks.success();gatewayCallbacks.destroyed()},dataType:"json"})}function createHandle(callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;callbacks.consentDialog=typeof callbacks.consentDialog=="function"?callbacks.consentDialog:Janus.noop;callbacks.iceState=typeof callbacks.iceState=="function"?callbacks.iceState:Janus.noop;callbacks.mediaState=typeof callbacks.mediaState=="function"?callbacks.mediaState:Janus.noop;callbacks.webrtcState=typeof callbacks.webrtcState=="function"?callbacks.webrtcState:Janus.noop;callbacks.slowLink=typeof callbacks.slowLink=="function"?callbacks.slowLink:Janus.noop;callbacks.onmessage=typeof callbacks.onmessage=="function"?callbacks.onmessage:Janus.noop;callbacks.onlocalstream=typeof callbacks.onlocalstream=="function"?callbacks.onlocalstream:Janus.noop;callbacks.onremotestream=typeof callbacks.onremotestream=="function"?callbacks.onremotestream:Janus.noop;callbacks.ondata=typeof callbacks.ondata=="function"?callbacks.ondata:Janus.noop;callbacks.ondataopen=typeof callbacks.ondataopen=="function"?callbacks.ondataopen:Janus.noop;callbacks.oncleanup=typeof callbacks.oncleanup=="function"?callbacks.oncleanup:Janus.noop;callbacks.ondetached=typeof callbacks.ondetached=="function"?callbacks.ondetached:Janus.noop;if(!connected){Janus.warn("Is the gateway down? (connected=false)");callbacks.error("Is the gateway down? (connected=false)");return}var plugin=callbacks.plugin;if(plugin===undefined||plugin===null){Janus.error("Invalid plugin");callbacks.error("Invalid plugin");return}var opaqueId=callbacks.opaqueId;var transaction=Janus.randomString(12);var request={janus:"attach",plugin:plugin,opaque_id:opaqueId,transaction:transaction};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(websockets){transactions[transaction]=function(json){Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error("Ooops: "+json["error"].code+" "+json["error"].reason);return}var handleId=json.data["id"];Janus.log("Created handle: "+handleId);var pluginHandle={session:that,plugin:plugin,id:handleId,detached:false,webrtcStuff:{started:false,myStream:null,streamExternal:false,remoteStream:null,mySdp:null,pc:null,dataChannel:null,dtmfSender:null,trickle:true,iceDone:false,sdpSent:false,volume:{value:null,timer:null},bitrate:{value:null,bsnow:null,bsbefore:null,tsnow:null,tsbefore:null,timer:null}},getId:function(){return handleId},getPlugin:function(){return plugin},getVolume:function(){return getVolume(handleId)},isAudioMuted:function(){return isMuted(handleId,false)},muteAudio:function(){return mute(handleId,false,true)},unmuteAudio:function(){return mute(handleId,false,false)},isVideoMuted:function(){return isMuted(handleId,true)},muteVideo:function(){return mute(handleId,true,true)},unmuteVideo:function(){return mute(handleId,true,false)},getBitrate:function(){return getBitrate(handleId)},send:function(callbacks){sendMessage(handleId,callbacks)},data:function(callbacks){sendData(handleId,callbacks)},dtmf:function(callbacks){sendDtmf(handleId,callbacks)},consentDialog:callbacks.consentDialog,iceState:callbacks.iceState,mediaState:callbacks.mediaState,webrtcState:callbacks.webrtcState,slowLink:callbacks.slowLink,onmessage:callbacks.onmessage,createOffer:function(callbacks){prepareWebrtc(handleId,callbacks)},createAnswer:function(callbacks){prepareWebrtc(handleId,callbacks)},handleRemoteJsep:function(callbacks){prepareWebrtcPeer(handleId,callbacks)},onlocalstream:callbacks.onlocalstream,onremotestream:callbacks.onremotestream,ondata:callbacks.ondata,ondataopen:callbacks.ondataopen,oncleanup:callbacks.oncleanup,ondetached:callbacks.ondetached,hangup:function(sendRequest){cleanupWebrtc(handleId,sendRequest===true)},detach:function(callbacks){destroyHandle(handleId,callbacks)}};pluginHandles[handleId]=pluginHandle;callbacks.success(pluginHandle)};request["session_id"]=sessionId;ws.send(JSON.stringify(request));return}Janus.ajax({type:"POST",url:server+"/"+sessionId,withCredentials:withCredentials,cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error("Ooops: "+json["error"].code+" "+json["error"].reason);return}var handleId=json.data["id"];Janus.log("Created handle: "+handleId);var pluginHandle={session:that,plugin:plugin,id:handleId,detached:false,webrtcStuff:{started:false,myStream:null,streamExternal:false,remoteStream:null,mySdp:null,pc:null,dataChannel:null,dtmfSender:null,trickle:true,iceDone:false,sdpSent:false,volume:{value:null,timer:null},bitrate:{value:null,bsnow:null,bsbefore:null,tsnow:null,tsbefore:null,timer:null}},getId:function(){return handleId},getPlugin:function(){return plugin},getVolume:function(){return getVolume(handleId)},isAudioMuted:function(){return isMuted(handleId,false)},muteAudio:function(){return mute(handleId,false,true)},unmuteAudio:function(){return mute(handleId,false,false)},isVideoMuted:function(){return isMuted(handleId,true)},muteVideo:function(){return mute(handleId,true,true)},unmuteVideo:function(){return mute(handleId,true,false)},getBitrate:function(){return getBitrate(handleId)},send:function(callbacks){sendMessage(handleId,callbacks)},data:function(callbacks){sendData(handleId,callbacks)},dtmf:function(callbacks){sendDtmf(handleId,callbacks)},consentDialog:callbacks.consentDialog,iceState:callbacks.iceState,mediaState:callbacks.mediaState,webrtcState:callbacks.webrtcState,slowLink:callbacks.slowLink,onmessage:callbacks.onmessage,createOffer:function(callbacks){prepareWebrtc(handleId,callbacks)},createAnswer:function(callbacks){prepareWebrtc(handleId,callbacks)},handleRemoteJsep:function(callbacks){prepareWebrtcPeer(handleId,callbacks)},onlocalstream:callbacks.onlocalstream,onremotestream:callbacks.onremotestream,ondata:callbacks.ondata,ondataopen:callbacks.ondataopen,oncleanup:callbacks.oncleanup,ondetached:callbacks.ondetached,hangup:function(sendRequest){cleanupWebrtc(handleId,sendRequest===true)},detach:function(callbacks){destroyHandle(handleId,callbacks)}};pluginHandles[handleId]=pluginHandle;callbacks.success(pluginHandle)},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown)},dataType:"json"})}function sendMessage(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;if(!connected){Janus.warn("Is the gateway down? (connected=false)");callbacks.error("Is the gateway down? (connected=false)");return}var message=callbacks.message;var jsep=callbacks.jsep;var transaction=Janus.randomString(12);var request={janus:"message",body:message,transaction:transaction};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(jsep!==null&&jsep!==undefined)request.jsep=jsep;Janus.debug("Sending message to plugin (handle="+handleId+"):");Janus.debug(request);if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;transactions[transaction]=function(json){Janus.debug("Message sent!");Janus.debug(json);if(json["janus"]==="success"){var plugindata=json["plugindata"];if(plugindata===undefined||plugindata===null){Janus.warn("Request succeeded, but missing plugindata...");callbacks.success();return}Janus.log("Synchronous transaction successful ("+plugindata["plugin"]+")");var data=plugindata["data"];Janus.debug(data);callbacks.success(data);return}else if(json["janus"]!=="ack"){if(json["error"]!==undefined&&json["error"]!==null){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error(json["error"].code+" "+json["error"].reason)}else{Janus.error("Unknown error");callbacks.error("Unknown error")}return}callbacks.success()};ws.send(JSON.stringify(request));return}Janus.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,withCredentials:withCredentials,cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.debug("Message sent!");Janus.debug(json);if(json["janus"]==="success"){var plugindata=json["plugindata"];if(plugindata===undefined||plugindata===null){Janus.warn("Request succeeded, but missing plugindata...");callbacks.success();return}Janus.log("Synchronous transaction successful ("+plugindata["plugin"]+")");var data=plugindata["data"];Janus.debug(data);callbacks.success(data);return}else if(json["janus"]!=="ack"){if(json["error"]!==undefined&&json["error"]!==null){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);callbacks.error(json["error"].code+" "+json["error"].reason)}else{Janus.error("Unknown error");callbacks.error("Unknown error")}return}callbacks.success()},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);callbacks.error(textStatus+": "+errorThrown)},dataType:"json"})}function sendTrickleCandidate(handleId,candidate){if(!connected){Janus.warn("Is the gateway down? (connected=false)");return}var request={janus:"trickle",candidate:candidate,transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;Janus.vdebug("Sending trickle candidate (handle="+handleId+"):");Janus.vdebug(request);if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;ws.send(JSON.stringify(request));return}Janus.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,withCredentials:withCredentials,cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.vdebug("Candidate sent!");Janus.vdebug(json);if(json["janus"]!=="ack"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason);return}},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown)},dataType:"json"})}function sendData(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;var text=callbacks.text;if(text===null||text===undefined){Janus.warn("Invalid text");callbacks.error("Invalid text");return}Janus.log("Sending string on data channel: "+text);config.dataChannel.send(text);callbacks.success()}function sendDtmf(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(config.dtmfSender===null||config.dtmfSender===undefined){if(config.myStream!==undefined&&config.myStream!==null){var tracks=config.myStream.getAudioTracks();if(tracks!==null&&tracks!==undefined&&tracks.length>0){var local_audio_track=tracks[0];config.dtmfSender=config.pc.createDTMFSender(local_audio_track);Janus.log("Created DTMF Sender");config.dtmfSender.ontonechange=function(tone){Janus.debug("Sent DTMF tone: "+tone.tone)}}}if(config.dtmfSender===null||config.dtmfSender===undefined){Janus.warn("Invalid DTMF configuration");callbacks.error("Invalid DTMF configuration");return}}var dtmf=callbacks.dtmf;if(dtmf===null||dtmf===undefined){Janus.warn("Invalid DTMF parameters");callbacks.error("Invalid DTMF parameters");return}var tones=dtmf.tones;if(tones===null||tones===undefined){Janus.warn("Invalid DTMF string");callbacks.error("Invalid DTMF string");return}var duration=dtmf.duration;if(duration===null||duration===undefined)duration=500;var gap=dtmf.gap;if(gap===null||gap===undefined)gap=50;Janus.debug("Sending DTMF string "+tones+" (duration "+duration+"ms, gap "+gap+"ms)");config.dtmfSender.insertDTMF(tones,duration,gap)}function destroyHandle(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;var asyncRequest=true;if(callbacks.asyncRequest!==undefined&&callbacks.asyncRequest!==null)asyncRequest=callbacks.asyncRequest===true;Janus.log("Destroying handle "+handleId+" (sync="+asyncRequest+")");cleanupWebrtc(handleId);if(pluginHandles[handleId].detached){
-delete pluginHandles[handleId];callbacks.success();return}if(!connected){Janus.warn("Is the gateway down? (connected=false)");callbacks.error("Is the gateway down? (connected=false)");return}var request={janus:"detach",transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;ws.send(JSON.stringify(request));delete pluginHandles[handleId];callbacks.success();return}Janus.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,async:asyncRequest,withCredentials:withCredentials,cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.log("Destroyed handle:");Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason)}delete pluginHandles[handleId];callbacks.success()},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);delete pluginHandles[handleId];callbacks.success()},dataType:"json"})}function streamsDone(handleId,jsep,media,callbacks,stream){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.debug("streamsDone:",stream);config.myStream=stream;var pc_config={iceServers:iceServers,iceTransportPolicy:iceTransportPolicy};var pc_constraints={optional:[{DtlsSrtpKeyAgreement:true}]};if(ipv6Support===true){pc_constraints.optional.push({googIPv6:true})}if(adapter.browserDetails.browser==="edge"){pc_config.bundlePolicy="max-bundle"}Janus.log("Creating PeerConnection");Janus.debug(pc_constraints);config.pc=new RTCPeerConnection(pc_config,pc_constraints);Janus.debug(config.pc);if(config.pc.getStats){config.volume.value=0;config.bitrate.value="0 kbits/sec"}Janus.log("Preparing local SDP and gathering candidates (trickle="+config.trickle+")");config.pc.oniceconnectionstatechange=function(e){if(config.pc)pluginHandle.iceState(config.pc.iceConnectionState)};config.pc.onicecandidate=function(event){if(event.candidate==null||adapter.browserDetails.browser==="edge"&&event.candidate.candidate.indexOf("endOfCandidates")>0){Janus.log("End of candidates.");config.iceDone=true;if(config.trickle===true){sendTrickleCandidate(handleId,{completed:true})}else{sendSDP(handleId,callbacks)}}else{var candidate={candidate:event.candidate.candidate,sdpMid:event.candidate.sdpMid,sdpMLineIndex:event.candidate.sdpMLineIndex};if(config.trickle===true){sendTrickleCandidate(handleId,candidate)}}};if(stream!==null&&stream!==undefined){Janus.log("Adding local stream");config.pc.addStream(stream);pluginHandle.onlocalstream(stream)}config.pc.ontrack=function(remoteStream){Janus.log("Handling Remote Stream");Janus.debug(remoteStream);config.remoteStream=remoteStream;pluginHandle.onremotestream(remoteStream.streams[0])};if(isDataEnabled(media)){Janus.log("Creating data channel");var onDataChannelMessage=function(event){Janus.log("Received message on data channel: "+event.data);pluginHandle.ondata(event.data)};var onDataChannelStateChange=function(){var dcState=config.dataChannel!==null?config.dataChannel.readyState:"null";Janus.log("State change on data channel: "+dcState);if(dcState==="open"){pluginHandle.ondataopen()}};var onDataChannelError=function(error){Janus.error("Got error on data channel:",error)};config.dataChannel=config.pc.createDataChannel("JanusDataChannel",{ordered:false});config.dataChannel.onmessage=onDataChannelMessage;config.dataChannel.onopen=onDataChannelStateChange;config.dataChannel.onclose=onDataChannelStateChange;config.dataChannel.onerror=onDataChannelError}if(jsep===null||jsep===undefined){createOffer(handleId,media,callbacks)}else{if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");createAnswer(handleId,media,callbacks)},callbacks.error)}}function prepareWebrtc(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:webrtcError;var jsep=callbacks.jsep;var media=callbacks.media;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(config.pc!==undefined&&config.pc!==null){Janus.log("Updating existing media session");if(jsep===null||jsep===undefined){createOffer(handleId,media,callbacks)}else{if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");createAnswer(handleId,media,callbacks)},callbacks.error)}return}if(callbacks.stream!==null&&callbacks.stream!==undefined){var stream=callbacks.stream;Janus.log("MediaStream provided by the application");Janus.debug(stream);config.streamExternal=true;streamsDone(handleId,jsep,media,callbacks,stream);return}config.trickle=isTrickleEnabled(callbacks.trickle);if(isAudioSendEnabled(media)||isVideoSendEnabled(media)){var constraints={mandatory:{},optional:[]};pluginHandle.consentDialog(true);var audioSupport=isAudioSendEnabled(media);if(audioSupport===true&&media!=undefined&&media!=null){if(typeof media.audio==="object"){audioSupport=media.audio}}var videoSupport=isVideoSendEnabled(media);if(videoSupport===true&&media!=undefined&&media!=null){if(media.video&&media.video!="screen"&&media.video!="window"){var width=0;var height=0,maxHeight=0;if(media.video==="lowres"){height=240;maxHeight=240;width=320}else if(media.video==="lowres-16:9"){height=180;maxHeight=180;width=320}else if(media.video==="hires"||media.video==="hires-16:9"){height=720;maxHeight=720;width=1280;if(navigator.mozGetUserMedia){var firefoxVer=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(firefoxVer<38){Janus.warn(media.video+" unsupported, falling back to stdres (old Firefox)");height=480;maxHeight=480;width=640}}}else if(media.video==="stdres"){height=480;maxHeight=480;width=640}else if(media.video==="stdres-16:9"){height=360;maxHeight=360;width=640}else{Janus.log("Default video setting ("+media.video+") is stdres 4:3");height=480;maxHeight=480;width=640}Janus.log("Adding media constraint "+media.video);if(navigator.mozGetUserMedia){var firefoxVer=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(firefoxVer<38){videoSupport={require:["height","width"],height:{max:maxHeight,min:height},width:{max:width,min:width}}}else{videoSupport={height:{ideal:height},width:{ideal:width}}}}else{videoSupport={mandatory:{maxHeight:maxHeight,minHeight:height,maxWidth:width,minWidth:width},optional:[]}}if(typeof media.video==="object"){videoSupport=media.video}Janus.debug(videoSupport)}else if(media.video==="screen"||media.video==="window"){if(!media.screenshareFrameRate){media.screenshareFrameRate=3}if(window.location.protocol!=="https:"){Janus.warn("Screen sharing only works on HTTPS, try the https:// version of this page");pluginHandle.consentDialog(false);callbacks.error("Screen sharing only works on HTTPS, try the https:// version of this page");return}var cache={};function callbackUserMedia(error,stream){pluginHandle.consentDialog(false);if(error){callbacks.error({code:error.code,name:error.name,message:error.message})}else{streamsDone(handleId,jsep,media,callbacks,stream)}}function getScreenMedia(constraints,gsmCallback){Janus.log("Adding media constraint (screen capture)");Janus.debug(constraints);navigator.mediaDevices.getUserMedia(constraints).then(function(stream){gsmCallback(null,stream)}).catch(function(error){pluginHandle.consentDialog(false);gsmCallback(error)})}if(adapter.browserDetails.browser==="chrome"){var chromever=adapter.browserDetails.version;var maxver=33;if(window.navigator.userAgent.match("Linux"))maxver=35;if(chromever>=26&&chromever<=maxver){constraints={video:{mandatory:{googLeakyBucket:true,maxWidth:window.screen.width,maxHeight:window.screen.height,minFrameRate:media.screenshareFrameRate,maxFrameRate:media.screenshareFrameRate,chromeMediaSource:"screen"}},audio:isAudioSendEnabled(media)};getScreenMedia(constraints,callbackUserMedia)}else{var pending=window.setTimeout(function(){error=new Error("NavigatorUserMediaError");error.name='The required Chrome extension is not installed: click <a href="#">here</a> to install it. (NOTE: this will need you to refresh the page)';pluginHandle.consentDialog(false);return callbacks.error(error)},1e3);cache[pending]=[callbackUserMedia,null];window.postMessage({type:"janusGetScreen",id:pending},"*")}}else if(window.navigator.userAgent.match("Firefox")){var ffver=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(ffver>=33){constraints={video:{mozMediaSource:media.video,mediaSource:media.video},audio:isAudioSendEnabled(media)};getScreenMedia(constraints,function(err,stream){callbackUserMedia(err,stream);if(!err){var lastTime=stream.currentTime;var polly=window.setInterval(function(){if(!stream)window.clearInterval(polly);if(stream.currentTime==lastTime){window.clearInterval(polly);if(stream.onended){stream.onended()}}lastTime=stream.currentTime},500)}})}else{var error=new Error("NavigatorUserMediaError");error.name="Your version of Firefox does not support screen sharing, please install Firefox 33 (or more recent versions)";pluginHandle.consentDialog(false);callbacks.error(error);return}}window.addEventListener("message",function(event){if(event.origin!=window.location.origin)return;if(event.data.type=="janusGotScreen"&&cache[event.data.id]){var data=cache[event.data.id];var callback=data[0];delete cache[event.data.id];if(event.data.sourceId===""){var error=new Error("NavigatorUserMediaError");error.name="You cancelled the request for permission, giving up...";pluginHandle.consentDialog(false);callbacks.error(error)}else{constraints={audio:isAudioSendEnabled(media),video:{mandatory:{chromeMediaSource:"desktop",maxWidth:window.screen.width,maxHeight:window.screen.height,minFrameRate:media.screenshareFrameRate,maxFrameRate:media.screenshareFrameRate},optional:[{googLeakyBucket:true},{googTemporalLayeredScreencast:true}]}};constraints.video.mandatory.chromeMediaSourceId=event.data.sourceId;getScreenMedia(constraints,callback)}}else if(event.data.type=="janusGetScreenPending"){window.clearTimeout(event.data.id)}});return}}if(media===null||media===undefined||media.video!=="screen"){navigator.mediaDevices.enumerateDevices().then(function(devices){var audioExist=devices.some(function(device){return device.kind==="audioinput"}),videoExist=devices.some(function(device){return device.kind==="videoinput"});var audioSend=isAudioSendEnabled(media);var videoSend=isVideoSendEnabled(media);var needAudioDevice=isAudioSendRequired(media);var needVideoDevice=isVideoSendRequired(media);if(audioSend||videoSend||needAudioDevice||needVideoDevice){var haveAudioDevice=audioSend?audioExist:false;var haveVideoDevice=videoSend?videoExist:false;if(!haveAudioDevice&&!haveVideoDevice){pluginHandle.consentDialog(false);callbacks.error("No capture device found");return false}else if(!haveAudioDevice&&needAudioDevice){pluginHandle.consentDialog(false);callbacks.error("Audio capture is required, but no capture device found");return false}else if(!haveVideoDevice&&needVideoDevice){pluginHandle.consentDialog(false);callbacks.error("Video capture is required, but no capture device found");return false}}navigator.mediaDevices.getUserMedia({audio:audioExist?audioSupport:false,video:videoExist?videoSupport:false}).then(function(stream){pluginHandle.consentDialog(false);streamsDone(handleId,jsep,media,callbacks,stream)}).catch(function(error){pluginHandle.consentDialog(false);callbacks.error({code:error.code,name:error.name,message:error.message})})}).catch(function(error){pluginHandle.consentDialog(false);callbacks.error("enumerateDevices error",error)})}}else{streamsDone(handleId,jsep,media,callbacks)}}function prepareWebrtcPeer(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:webrtcError;var jsep=callbacks.jsep;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(jsep!==undefined&&jsep!==null){if(config.pc===null){Janus.warn("Wait, no PeerConnection?? if this is an answer, use createAnswer and not handleRemoteJsep");callbacks.error("No PeerConnection: if this is an answer, use createAnswer and not handleRemoteJsep");return}if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");callbacks.success()},callbacks.error)}else{callbacks.error("Invalid JSEP")}}function createOffer(handleId,media,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.log("Creating offer (iceDone="+config.iceDone+")");var mediaConstraints=null;if(adapter.browserDetails.browser=="firefox"||adapter.browserDetails.browser=="edge"){mediaConstraints={offerToReceiveAudio:isAudioRecvEnabled(media),offerToReceiveVideo:isVideoRecvEnabled(media)}}else{mediaConstraints={mandatory:{OfferToReceiveAudio:isAudioRecvEnabled(media),OfferToReceiveVideo:isVideoRecvEnabled(media)}}}Janus.debug(mediaConstraints);config.pc.createOffer(function(offer){Janus.debug(offer);if(config.mySdp===null||config.mySdp===undefined){Janus.log("Setting local description");config.mySdp=offer.sdp;config.pc.setLocalDescription(offer)}if(!config.iceDone&&!config.trickle){Janus.log("Waiting for all candidates...");return}if(config.sdpSent){Janus.log("Offer already sent, not sending it again");return}Janus.log("Offer ready");Janus.debug(callbacks);config.sdpSent=true;var jsep={type:offer.type,sdp:offer.sdp};callbacks.success(jsep)},callbacks.error,mediaConstraints)}function createAnswer(handleId,media,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.log("Creating answer (iceDone="+config.iceDone+")");var mediaConstraints=null;if(adapter.browserDetails.browser=="firefox"||adapter.browserDetails.browser=="edge"){mediaConstraints={offerToReceiveAudio:isAudioRecvEnabled(media),offerToReceiveVideo:isVideoRecvEnabled(media)}}else{mediaConstraints={mandatory:{OfferToReceiveAudio:isAudioRecvEnabled(media),OfferToReceiveVideo:isVideoRecvEnabled(media)}}}Janus.debug(mediaConstraints);config.pc.createAnswer(function(answer){Janus.debug(answer);if(config.mySdp===null||config.mySdp===undefined){Janus.log("Setting local description");config.mySdp=answer.sdp;config.pc.setLocalDescription(answer)}if(!config.iceDone&&!config.trickle){Janus.log("Waiting for all candidates...");return}if(config.sdpSent){Janus.log("Answer already sent, not sending it again");return}config.sdpSent=true;var jsep={type:answer.type,sdp:answer.sdp};callbacks.success(jsep)},callbacks.error,mediaConstraints)}function sendSDP(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle, not sending anything");return}var config=pluginHandle.webrtcStuff;Janus.log("Sending offer/answer SDP...");if(config.mySdp===null||config.mySdp===undefined){Janus.warn("Local SDP instance is invalid, not sending anything...");return}config.mySdp={type:config.pc.localDescription.type,sdp:config.pc.localDescription.sdp};if(config.sdpSent){Janus.log("Offer/Answer SDP already sent, not sending it again");return}if(config.trickle===false)config.mySdp["trickle"]=false;Janus.debug(callbacks);config.sdpSent=true;callbacks.success(config.mySdp)}function getVolume(handleId){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return 0}var config=pluginHandle.webrtcStuff;if(config.pc.getStats&&adapter.browserDetails.browser=="chrome"){if(config.remoteStream===null||config.remoteStream===undefined){Janus.warn("Remote stream unavailable");return 0}if(config.volume.timer===null||config.volume.timer===undefined){Janus.log("Starting volume monitor");config.volume.timer=setInterval(function(){config.pc.getStats(function(stats){var results=stats.result();for(var i=0;i<results.length;i++){var res=results[i];if(res.type=="ssrc"&&res.stat("audioOutputLevel")){config.volume.value=res.stat("audioOutputLevel")}}})},200);return 0}return config.volume.value}else{Janus.log("Getting the remote volume unsupported by browser");return 0}}function isMuted(handleId,video){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return true}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined){Janus.warn("Invalid PeerConnection");return true}if(config.myStream===undefined||config.myStream===null){Janus.warn("Invalid local MediaStream");return true}if(video){if(config.myStream.getVideoTracks()===null||config.myStream.getVideoTracks()===undefined||config.myStream.getVideoTracks().length===0){Janus.warn("No video track");return true}return!config.myStream.getVideoTracks()[0].enabled}else{if(config.myStream.getAudioTracks()===null||config.myStream.getAudioTracks()===undefined||config.myStream.getAudioTracks().length===0){Janus.warn("No audio track");return true}return!config.myStream.getAudioTracks()[0].enabled}}function mute(handleId,video,mute){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return false}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined){Janus.warn("Invalid PeerConnection");return false}if(config.myStream===undefined||config.myStream===null){Janus.warn("Invalid local MediaStream");return false}if(video){if(config.myStream.getVideoTracks()===null||config.myStream.getVideoTracks()===undefined||config.myStream.getVideoTracks().length===0){Janus.warn("No video track");return false}config.myStream.getVideoTracks()[0].enabled=mute?false:true;return true}else{if(config.myStream.getAudioTracks()===null||config.myStream.getAudioTracks()===undefined||config.myStream.getAudioTracks().length===0){Janus.warn("No audio track");return false}config.myStream.getAudioTracks()[0].enabled=mute?false:true;return true}}function getBitrate(handleId){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return"Invalid handle"}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined)return"Invalid PeerConnection";if(config.pc.getStats&&adapter.browserDetails.browser=="chrome"){if(config.remoteStream===null||config.remoteStream===undefined){Janus.warn("Remote stream unavailable");return"Remote stream unavailable"}if(config.bitrate.timer===null||config.bitrate.timer===undefined){Janus.log("Starting bitrate timer (Chrome)");config.bitrate.timer=setInterval(function(){config.pc.getStats(function(stats){var results=stats.result();for(var i=0;i<results.length;i++){var res=results[i];if(res.type=="ssrc"&&res.stat("googFrameHeightReceived")){config.bitrate.bsnow=res.stat("bytesReceived");config.bitrate.tsnow=res.timestamp;if(config.bitrate.bsbefore===null||config.bitrate.tsbefore===null){config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}else{var bitRate=Math.round((config.bitrate.bsnow-config.bitrate.bsbefore)*8/(config.bitrate.tsnow-config.bitrate.tsbefore));config.bitrate.value=bitRate+" kbits/sec";config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}}}})},1e3);return"0 kbits/sec"}return config.bitrate.value}else if(config.pc.getStats&&adapter.browserDetails.browser=="firefox"){if(config.remoteStream===null||config.remoteStream===undefined||config.remoteStream.streams[0]===null||config.remoteStream.streams[0]===undefined){Janus.warn("Remote stream unavailable");return"Remote stream unavailable"}var videoTracks=config.remoteStream.streams[0].getVideoTracks();if(videoTracks===null||videoTracks===undefined||videoTracks.length<1){Janus.warn("No video track");return"No video track"}if(config.bitrate.timer===null||config.bitrate.timer===undefined){Janus.log("Starting bitrate timer (Firefox)");config.bitrate.timer=setInterval(function(){var cb=function(res){if(res===null||res===undefined||res.inbound_rtp_video_1==null||res.inbound_rtp_video_1==null){config.bitrate.value="Missing inbound_rtp_video_1";return}config.bitrate.bsnow=res.inbound_rtp_video_1.bytesReceived;config.bitrate.tsnow=res.inbound_rtp_video_1.timestamp;if(config.bitrate.bsbefore===null||config.bitrate.tsbefore===null){config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}else{var bitRate=Math.round((config.bitrate.bsnow-config.bitrate.bsbefore)*8/(config.bitrate.tsnow-config.bitrate.tsbefore));config.bitrate.value=bitRate+" kbits/sec";config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}};config.pc.getStats(videoTracks[0],function(stats){cb(stats)},cb)},1e3);return"0 kbits/sec"}return config.bitrate.value}else{Janus.warn("Getting the video bitrate unsupported by browser");return"Feature unsupported by browser"}}function webrtcError(error){Janus.error("WebRTC error:",error)}function cleanupWebrtc(handleId,hangupRequest){Janus.log("Cleaning WebRTC stuff");var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined){return}var config=pluginHandle.webrtcStuff;if(config!==null&&config!==undefined){if(hangupRequest===true){var request={janus:"hangup",transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;Janus.debug("Sending hangup request (handle="+handleId+"):");Janus.debug(request);if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;ws.send(JSON.stringify(request))}else{Janus.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,withCredentials:withCredentials,cache:false,contentType:"application/json",data:JSON.stringify(request),dataType:"json"})}}config.remoteStream=null;if(config.volume.timer)clearInterval(config.volume.timer);config.volume.value=null;if(config.bitrate.timer)clearInterval(config.bitrate.timer);config.bitrate.timer=null;config.bitrate.bsnow=null;config.bitrate.bsbefore=null;config.bitrate.tsnow=null;config.bitrate.tsbefore=null;config.bitrate.value=null;try{if(!config.streamExternal&&config.myStream!==null&&config.myStream!==undefined){Janus.log("Stopping local stream");config.myStream.stop()}}catch(e){}try{if(!config.streamExternal&&config.myStream!==null&&config.myStream!==undefined){Janus.log("Stopping local stream tracks");var tracks=config.myStream.getTracks();for(var i in tracks){var mst=tracks[i];Janus.log(mst);if(mst!==null&&mst!==undefined)mst.stop()}}}catch(e){}config.streamExternal=false;config.myStream=null;try{config.pc.close()}catch(e){}config.pc=null;config.mySdp=null;config.iceDone=false;config.sdpSent=false;config.dataChannel=null;config.dtmfSender=null}pluginHandle.oncleanup()}function isAudioSendEnabled(media){Janus.debug("isAudioSendEnabled:",media);if(media===undefined||media===null)return true;if(media.audio===false)return false;if(media.audioSend===undefined||media.audioSend===null)return true;return media.audioSend===true}function isAudioSendRequired(media){Janus.debug("isAudioSendRequired:",media);if(media===undefined||media===null)return false;if(media.audio===false||media.audioSend===false)return false;if(media.failIfNoAudio===undefined||media.failIfNoAudio===null)return false;return media.failIfNoAudio===true}function isAudioRecvEnabled(media){Janus.debug("isAudioRecvEnabled:",media);if(media===undefined||media===null)return true;if(media.audio===false)return false;if(media.audioRecv===undefined||media.audioRecv===null)return true;return media.audioRecv===true}function isVideoSendEnabled(media){Janus.debug("isVideoSendEnabled:",media);if(media===undefined||media===null)return true;if(media.video===false)return false;if(media.videoSend===undefined||media.videoSend===null)return true;return media.videoSend===true}function isVideoSendRequired(media){Janus.debug("isVideoSendRequired:",media);if(media===undefined||media===null)return false;if(media.video===false||media.videoSend===false)return false;if(media.failIfNoVideo===undefined||media.failIfNoVideo===null)return false;return media.failIfNoVideo===true}function isVideoRecvEnabled(media){Janus.debug("isVideoRecvEnabled:",media);if(media===undefined||media===null)return true;if(media.video===false)return false;if(media.videoRecv===undefined||media.videoRecv===null)return true;return media.videoRecv===true}function isDataEnabled(media){Janus.debug("isDataEnabled:",media);if(adapter.browserDetails.browser=="edge"){Janus.warn("Edge doesn't support data channels yet");return false}if(media===undefined||media===null)return false;return media.data===true}function isTrickleEnabled(trickle){Janus.debug("isTrickleEnabled:",trickle);if(trickle===undefined||trickle===null)return true;return trickle===true}}
+delete pluginHandles[handleId];callbacks.success();return}if(!connected){Janus.warn("Is the gateway down? (connected=false)");callbacks.error("Is the gateway down? (connected=false)");return}var request={janus:"detach",transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;ws.send(JSON.stringify(request));delete pluginHandles[handleId];callbacks.success();return}Janus.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,async:asyncRequest,withCredentials:withCredentials,cache:false,contentType:"application/json",data:JSON.stringify(request),success:function(json){Janus.log("Destroyed handle:");Janus.debug(json);if(json["janus"]!=="success"){Janus.error("Ooops: "+json["error"].code+" "+json["error"].reason)}delete pluginHandles[handleId];callbacks.success()},error:function(XMLHttpRequest,textStatus,errorThrown){Janus.error(textStatus+": "+errorThrown);delete pluginHandles[handleId];callbacks.success()},dataType:"json"})}function streamsDone(handleId,jsep,media,callbacks,stream){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.debug("streamsDone:",stream);config.myStream=stream;var pc_config={iceServers:iceServers,iceTransportPolicy:iceTransportPolicy};var pc_constraints={optional:[{DtlsSrtpKeyAgreement:true}]};if(ipv6Support===true){pc_constraints.optional.push({googIPv6:true})}if(adapter.browserDetails.browser==="edge"){pc_config.bundlePolicy="max-bundle"}Janus.log("Creating PeerConnection");Janus.debug(pc_constraints);config.pc=new RTCPeerConnection(pc_config,pc_constraints);Janus.debug(config.pc);if(config.pc.getStats){config.volume.value=0;config.bitrate.value="0 kbits/sec"}Janus.log("Preparing local SDP and gathering candidates (trickle="+config.trickle+")");config.pc.oniceconnectionstatechange=function(e){if(config.pc)pluginHandle.iceState(config.pc.iceConnectionState)};config.pc.onicecandidate=function(event){if(event.candidate==null||adapter.browserDetails.browser==="edge"&&event.candidate.candidate.indexOf("endOfCandidates")>0){Janus.log("End of candidates.");config.iceDone=true;if(config.trickle===true){sendTrickleCandidate(handleId,{completed:true})}else{sendSDP(handleId,callbacks)}}else{var candidate={candidate:event.candidate.candidate,sdpMid:event.candidate.sdpMid,sdpMLineIndex:event.candidate.sdpMLineIndex};if(config.trickle===true){sendTrickleCandidate(handleId,candidate)}}};if(stream!==null&&stream!==undefined){Janus.log("Adding local stream");config.pc.addStream(stream);pluginHandle.onlocalstream(stream)}config.pc.onaddstream=function(remoteStream){Janus.log("Handling Remote Stream");Janus.debug(remoteStream);config.remoteStream=remoteStream;pluginHandle.onremotestream(remoteStream.stream)};if(isDataEnabled(media)){Janus.log("Creating data channel");var onDataChannelMessage=function(event){Janus.log("Received message on data channel: "+event.data);pluginHandle.ondata(event.data)};var onDataChannelStateChange=function(){var dcState=config.dataChannel!==null?config.dataChannel.readyState:"null";Janus.log("State change on data channel: "+dcState);if(dcState==="open"){pluginHandle.ondataopen()}};var onDataChannelError=function(error){Janus.error("Got error on data channel:",error)};config.dataChannel=config.pc.createDataChannel("JanusDataChannel",{ordered:false});config.dataChannel.onmessage=onDataChannelMessage;config.dataChannel.onopen=onDataChannelStateChange;config.dataChannel.onclose=onDataChannelStateChange;config.dataChannel.onerror=onDataChannelError}if(jsep===null||jsep===undefined){createOffer(handleId,media,callbacks)}else{if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");createAnswer(handleId,media,callbacks)},callbacks.error)}}function prepareWebrtc(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:webrtcError;var jsep=callbacks.jsep;var media=callbacks.media;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(config.pc!==undefined&&config.pc!==null){Janus.log("Updating existing media session");if(jsep===null||jsep===undefined){createOffer(handleId,media,callbacks)}else{if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");createAnswer(handleId,media,callbacks)},callbacks.error)}return}if(callbacks.stream!==null&&callbacks.stream!==undefined){var stream=callbacks.stream;Janus.log("MediaStream provided by the application");Janus.debug(stream);config.streamExternal=true;streamsDone(handleId,jsep,media,callbacks,stream);return}config.trickle=isTrickleEnabled(callbacks.trickle);if(isAudioSendEnabled(media)||isVideoSendEnabled(media)){var constraints={mandatory:{},optional:[]};pluginHandle.consentDialog(true);var audioSupport=isAudioSendEnabled(media);if(audioSupport===true&&media!=undefined&&media!=null){if(typeof media.audio==="object"){audioSupport=media.audio}}var videoSupport=isVideoSendEnabled(media);if(videoSupport===true&&media!=undefined&&media!=null){if(media.video&&media.video!="screen"&&media.video!="window"){var width=0;var height=0,maxHeight=0;if(media.video==="lowres"){height=240;maxHeight=240;width=320}else if(media.video==="lowres-16:9"){height=180;maxHeight=180;width=320}else if(media.video==="hires"||media.video==="hires-16:9"){height=720;maxHeight=720;width=1280;if(navigator.mozGetUserMedia){var firefoxVer=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(firefoxVer<38){Janus.warn(media.video+" unsupported, falling back to stdres (old Firefox)");height=480;maxHeight=480;width=640}}}else if(media.video==="stdres"){height=480;maxHeight=480;width=640}else if(media.video==="stdres-16:9"){height=360;maxHeight=360;width=640}else{Janus.log("Default video setting ("+media.video+") is stdres 4:3");height=480;maxHeight=480;width=640}Janus.log("Adding media constraint "+media.video);if(navigator.mozGetUserMedia){var firefoxVer=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(firefoxVer<38){videoSupport={require:["height","width"],height:{max:maxHeight,min:height},width:{max:width,min:width}}}else{videoSupport={height:{ideal:height},width:{ideal:width}}}}else{videoSupport={mandatory:{maxHeight:maxHeight,minHeight:height,maxWidth:width,minWidth:width},optional:[]}}if(typeof media.video==="object"){videoSupport=media.video}Janus.debug(videoSupport)}else if(media.video==="screen"||media.video==="window"){if(!media.screenshareFrameRate){media.screenshareFrameRate=3}if(window.location.protocol!=="https:"){Janus.warn("Screen sharing only works on HTTPS, try the https:// version of this page");pluginHandle.consentDialog(false);callbacks.error("Screen sharing only works on HTTPS, try the https:// version of this page");return}var cache={};function callbackUserMedia(error,stream){pluginHandle.consentDialog(false);if(error){callbacks.error({code:error.code,name:error.name,message:error.message})}else{streamsDone(handleId,jsep,media,callbacks,stream)}}function getScreenMedia(constraints,gsmCallback){Janus.log("Adding media constraint (screen capture)");Janus.debug(constraints);navigator.mediaDevices.getUserMedia(constraints).then(function(stream){gsmCallback(null,stream)}).catch(function(error){pluginHandle.consentDialog(false);gsmCallback(error)})}if(adapter.browserDetails.browser==="chrome"){var chromever=adapter.browserDetails.version;var maxver=33;if(window.navigator.userAgent.match("Linux"))maxver=35;if(chromever>=26&&chromever<=maxver){constraints={video:{mandatory:{googLeakyBucket:true,maxWidth:window.screen.width,maxHeight:window.screen.height,minFrameRate:media.screenshareFrameRate,maxFrameRate:media.screenshareFrameRate,chromeMediaSource:"screen"}},audio:isAudioSendEnabled(media)};getScreenMedia(constraints,callbackUserMedia)}else{var pending=window.setTimeout(function(){error=new Error("NavigatorUserMediaError");error.name='The required Chrome extension is not installed: click <a href="#">here</a> to install it. (NOTE: this will need you to refresh the page)';pluginHandle.consentDialog(false);return callbacks.error(error)},1e3);cache[pending]=[callbackUserMedia,null];window.postMessage({type:"janusGetScreen",id:pending},"*")}}else if(window.navigator.userAgent.match("Firefox")){var ffver=parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1],10);if(ffver>=33){constraints={video:{mozMediaSource:media.video,mediaSource:media.video},audio:isAudioSendEnabled(media)};getScreenMedia(constraints,function(err,stream){callbackUserMedia(err,stream);if(!err){var lastTime=stream.currentTime;var polly=window.setInterval(function(){if(!stream)window.clearInterval(polly);if(stream.currentTime==lastTime){window.clearInterval(polly);if(stream.onended){stream.onended()}}lastTime=stream.currentTime},500)}})}else{var error=new Error("NavigatorUserMediaError");error.name="Your version of Firefox does not support screen sharing, please install Firefox 33 (or more recent versions)";pluginHandle.consentDialog(false);callbacks.error(error);return}}window.addEventListener("message",function(event){if(event.origin!=window.location.origin)return;if(event.data.type=="janusGotScreen"&&cache[event.data.id]){var data=cache[event.data.id];var callback=data[0];delete cache[event.data.id];if(event.data.sourceId===""){var error=new Error("NavigatorUserMediaError");error.name="You cancelled the request for permission, giving up...";pluginHandle.consentDialog(false);callbacks.error(error)}else{constraints={audio:isAudioSendEnabled(media),video:{mandatory:{chromeMediaSource:"desktop",maxWidth:window.screen.width,maxHeight:window.screen.height,minFrameRate:media.screenshareFrameRate,maxFrameRate:media.screenshareFrameRate},optional:[{googLeakyBucket:true},{googTemporalLayeredScreencast:true}]}};constraints.video.mandatory.chromeMediaSourceId=event.data.sourceId;getScreenMedia(constraints,callback)}}else if(event.data.type=="janusGetScreenPending"){window.clearTimeout(event.data.id)}});return}}if(media===null||media===undefined||media.video!=="screen"){navigator.mediaDevices.enumerateDevices().then(function(devices){var audioExist=devices.some(function(device){return device.kind==="audioinput"}),videoExist=devices.some(function(device){return device.kind==="videoinput"});var audioSend=isAudioSendEnabled(media);var videoSend=isVideoSendEnabled(media);var needAudioDevice=isAudioSendRequired(media);var needVideoDevice=isVideoSendRequired(media);if(audioSend||videoSend||needAudioDevice||needVideoDevice){var haveAudioDevice=audioSend?audioExist:false;var haveVideoDevice=videoSend?videoExist:false;if(!haveAudioDevice&&!haveVideoDevice){pluginHandle.consentDialog(false);callbacks.error("No capture device found");return false}else if(!haveAudioDevice&&needAudioDevice){pluginHandle.consentDialog(false);callbacks.error("Audio capture is required, but no capture device found");return false}else if(!haveVideoDevice&&needVideoDevice){pluginHandle.consentDialog(false);callbacks.error("Video capture is required, but no capture device found");return false}}navigator.mediaDevices.getUserMedia({audio:audioExist?audioSupport:false,video:videoExist?videoSupport:false}).then(function(stream){pluginHandle.consentDialog(false);streamsDone(handleId,jsep,media,callbacks,stream)}).catch(function(error){pluginHandle.consentDialog(false);callbacks.error({code:error.code,name:error.name,message:error.message})})}).catch(function(error){pluginHandle.consentDialog(false);callbacks.error("enumerateDevices error",error)})}}else{streamsDone(handleId,jsep,media,callbacks)}}function prepareWebrtcPeer(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:webrtcError;var jsep=callbacks.jsep;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;if(jsep!==undefined&&jsep!==null){if(config.pc===null){Janus.warn("Wait, no PeerConnection?? if this is an answer, use createAnswer and not handleRemoteJsep");callbacks.error("No PeerConnection: if this is an answer, use createAnswer and not handleRemoteJsep");return}if(adapter.browserDetails.browser==="edge"){jsep.sdp+="a=end-of-candidates\r\n"}config.pc.setRemoteDescription(new RTCSessionDescription(jsep),function(){Janus.log("Remote description accepted!");callbacks.success()},callbacks.error)}else{callbacks.error("Invalid JSEP")}}function createOffer(handleId,media,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.log("Creating offer (iceDone="+config.iceDone+")");var mediaConstraints=null;if(adapter.browserDetails.browser=="firefox"||adapter.browserDetails.browser=="edge"){mediaConstraints={offerToReceiveAudio:isAudioRecvEnabled(media),offerToReceiveVideo:isVideoRecvEnabled(media)}}else{mediaConstraints={mandatory:{OfferToReceiveAudio:isAudioRecvEnabled(media),OfferToReceiveVideo:isVideoRecvEnabled(media)}}}Janus.debug(mediaConstraints);config.pc.createOffer(function(offer){Janus.debug(offer);if(config.mySdp===null||config.mySdp===undefined){Janus.log("Setting local description");config.mySdp=offer.sdp;config.pc.setLocalDescription(offer)}if(!config.iceDone&&!config.trickle){Janus.log("Waiting for all candidates...");return}if(config.sdpSent){Janus.log("Offer already sent, not sending it again");return}Janus.log("Offer ready");Janus.debug(callbacks);config.sdpSent=true;var jsep={type:offer.type,sdp:offer.sdp};callbacks.success(jsep)},callbacks.error,mediaConstraints)}function createAnswer(handleId,media,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");callbacks.error("Invalid handle");return}var config=pluginHandle.webrtcStuff;Janus.log("Creating answer (iceDone="+config.iceDone+")");var mediaConstraints=null;if(adapter.browserDetails.browser=="firefox"||adapter.browserDetails.browser=="edge"){mediaConstraints={offerToReceiveAudio:isAudioRecvEnabled(media),offerToReceiveVideo:isVideoRecvEnabled(media)}}else{mediaConstraints={mandatory:{OfferToReceiveAudio:isAudioRecvEnabled(media),OfferToReceiveVideo:isVideoRecvEnabled(media)}}}Janus.debug(mediaConstraints);config.pc.createAnswer(function(answer){Janus.debug(answer);if(config.mySdp===null||config.mySdp===undefined){Janus.log("Setting local description");config.mySdp=answer.sdp;config.pc.setLocalDescription(answer)}if(!config.iceDone&&!config.trickle){Janus.log("Waiting for all candidates...");return}if(config.sdpSent){Janus.log("Answer already sent, not sending it again");return}config.sdpSent=true;var jsep={type:answer.type,sdp:answer.sdp};callbacks.success(jsep)},callbacks.error,mediaConstraints)}function sendSDP(handleId,callbacks){callbacks=callbacks||{};callbacks.success=typeof callbacks.success=="function"?callbacks.success:Janus.noop;callbacks.error=typeof callbacks.error=="function"?callbacks.error:Janus.noop;var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle, not sending anything");return}var config=pluginHandle.webrtcStuff;Janus.log("Sending offer/answer SDP...");if(config.mySdp===null||config.mySdp===undefined){Janus.warn("Local SDP instance is invalid, not sending anything...");return}config.mySdp={type:config.pc.localDescription.type,sdp:config.pc.localDescription.sdp};if(config.sdpSent){Janus.log("Offer/Answer SDP already sent, not sending it again");return}if(config.trickle===false)config.mySdp["trickle"]=false;Janus.debug(callbacks);config.sdpSent=true;callbacks.success(config.mySdp)}function getVolume(handleId){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return 0}var config=pluginHandle.webrtcStuff;if(config.pc.getStats&&adapter.browserDetails.browser=="chrome"){if(config.remoteStream===null||config.remoteStream===undefined){Janus.warn("Remote stream unavailable");return 0}if(config.volume.timer===null||config.volume.timer===undefined){Janus.log("Starting volume monitor");config.volume.timer=setInterval(function(){config.pc.getStats(function(stats){var results=stats.result();for(var i=0;i<results.length;i++){var res=results[i];if(res.type=="ssrc"&&res.stat("audioOutputLevel")){config.volume.value=res.stat("audioOutputLevel")}}})},200);return 0}return config.volume.value}else{Janus.log("Getting the remote volume unsupported by browser");return 0}}function isMuted(handleId,video){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return true}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined){Janus.warn("Invalid PeerConnection");return true}if(config.myStream===undefined||config.myStream===null){Janus.warn("Invalid local MediaStream");return true}if(video){if(config.myStream.getVideoTracks()===null||config.myStream.getVideoTracks()===undefined||config.myStream.getVideoTracks().length===0){Janus.warn("No video track");return true}return!config.myStream.getVideoTracks()[0].enabled}else{if(config.myStream.getAudioTracks()===null||config.myStream.getAudioTracks()===undefined||config.myStream.getAudioTracks().length===0){Janus.warn("No audio track");return true}return!config.myStream.getAudioTracks()[0].enabled}}function mute(handleId,video,mute){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return false}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined){Janus.warn("Invalid PeerConnection");return false}if(config.myStream===undefined||config.myStream===null){Janus.warn("Invalid local MediaStream");return false}if(video){if(config.myStream.getVideoTracks()===null||config.myStream.getVideoTracks()===undefined||config.myStream.getVideoTracks().length===0){Janus.warn("No video track");return false}config.myStream.getVideoTracks()[0].enabled=mute?false:true;return true}else{if(config.myStream.getAudioTracks()===null||config.myStream.getAudioTracks()===undefined||config.myStream.getAudioTracks().length===0){Janus.warn("No audio track");return false}config.myStream.getAudioTracks()[0].enabled=mute?false:true;return true}}function getBitrate(handleId){var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined||pluginHandle.webrtcStuff===null||pluginHandle.webrtcStuff===undefined){Janus.warn("Invalid handle");return"Invalid handle"}var config=pluginHandle.webrtcStuff;if(config.pc===null||config.pc===undefined)return"Invalid PeerConnection";if(config.pc.getStats&&adapter.browserDetails.browser=="chrome"){if(config.remoteStream===null||config.remoteStream===undefined){Janus.warn("Remote stream unavailable");return"Remote stream unavailable"}if(config.bitrate.timer===null||config.bitrate.timer===undefined){Janus.log("Starting bitrate timer (Chrome)");config.bitrate.timer=setInterval(function(){config.pc.getStats(function(stats){var results=stats.result();for(var i=0;i<results.length;i++){var res=results[i];if(res.type=="ssrc"&&res.stat("googFrameHeightReceived")){config.bitrate.bsnow=res.stat("bytesReceived");config.bitrate.tsnow=res.timestamp;if(config.bitrate.bsbefore===null||config.bitrate.tsbefore===null){config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}else{var bitRate=Math.round((config.bitrate.bsnow-config.bitrate.bsbefore)*8/(config.bitrate.tsnow-config.bitrate.tsbefore));config.bitrate.value=bitRate+" kbits/sec";config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}}}})},1e3);return"0 kbits/sec"}return config.bitrate.value}else if(config.pc.getStats&&adapter.browserDetails.browser=="firefox"){if(config.remoteStream===null||config.remoteStream===undefined||config.remoteStream.streams[0]===null||config.remoteStream.streams[0]===undefined){Janus.warn("Remote stream unavailable");return"Remote stream unavailable"}var videoTracks=config.remoteStream.streams[0].getVideoTracks();if(videoTracks===null||videoTracks===undefined||videoTracks.length<1){Janus.warn("No video track");return"No video track"}if(config.bitrate.timer===null||config.bitrate.timer===undefined){Janus.log("Starting bitrate timer (Firefox)");config.bitrate.timer=setInterval(function(){var cb=function(res){if(res===null||res===undefined||res.inbound_rtp_video_1==null||res.inbound_rtp_video_1==null){config.bitrate.value="Missing inbound_rtp_video_1";return}config.bitrate.bsnow=res.inbound_rtp_video_1.bytesReceived;config.bitrate.tsnow=res.inbound_rtp_video_1.timestamp;if(config.bitrate.bsbefore===null||config.bitrate.tsbefore===null){config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}else{var bitRate=Math.round((config.bitrate.bsnow-config.bitrate.bsbefore)*8/(config.bitrate.tsnow-config.bitrate.tsbefore));config.bitrate.value=bitRate+" kbits/sec";config.bitrate.bsbefore=config.bitrate.bsnow;config.bitrate.tsbefore=config.bitrate.tsnow}};config.pc.getStats(videoTracks[0],function(stats){cb(stats)},cb)},1e3);return"0 kbits/sec"}return config.bitrate.value}else{Janus.warn("Getting the video bitrate unsupported by browser");return"Feature unsupported by browser"}}function webrtcError(error){Janus.error("WebRTC error:",error)}function cleanupWebrtc(handleId,hangupRequest){Janus.log("Cleaning WebRTC stuff");var pluginHandle=pluginHandles[handleId];if(pluginHandle===null||pluginHandle===undefined){return}var config=pluginHandle.webrtcStuff;if(config!==null&&config!==undefined){if(hangupRequest===true){var request={janus:"hangup",transaction:Janus.randomString(12)};if(token!==null&&token!==undefined)request["token"]=token;if(apisecret!==null&&apisecret!==undefined)request["apisecret"]=apisecret;Janus.debug("Sending hangup request (handle="+handleId+"):");Janus.debug(request);if(websockets){request["session_id"]=sessionId;request["handle_id"]=handleId;ws.send(JSON.stringify(request))}else{Janus.ajax({type:"POST",url:server+"/"+sessionId+"/"+handleId,withCredentials:withCredentials,cache:false,contentType:"application/json",data:JSON.stringify(request),dataType:"json"})}}config.remoteStream=null;if(config.volume.timer)clearInterval(config.volume.timer);config.volume.value=null;if(config.bitrate.timer)clearInterval(config.bitrate.timer);config.bitrate.timer=null;config.bitrate.bsnow=null;config.bitrate.bsbefore=null;config.bitrate.tsnow=null;config.bitrate.tsbefore=null;config.bitrate.value=null;try{if(!config.streamExternal&&config.myStream!==null&&config.myStream!==undefined){Janus.log("Stopping local stream");config.myStream.stop()}}catch(e){}try{if(!config.streamExternal&&config.myStream!==null&&config.myStream!==undefined){Janus.log("Stopping local stream tracks");var tracks=config.myStream.getTracks();for(var i in tracks){var mst=tracks[i];Janus.log(mst);if(mst!==null&&mst!==undefined)mst.stop()}}}catch(e){}config.streamExternal=false;config.myStream=null;try{config.pc.close()}catch(e){}config.pc=null;config.mySdp=null;config.iceDone=false;config.sdpSent=false;config.dataChannel=null;config.dtmfSender=null}pluginHandle.oncleanup()}function isAudioSendEnabled(media){Janus.debug("isAudioSendEnabled:",media);if(media===undefined||media===null)return true;if(media.audio===false)return false;if(media.audioSend===undefined||media.audioSend===null)return true;return media.audioSend===true}function isAudioSendRequired(media){Janus.debug("isAudioSendRequired:",media);if(media===undefined||media===null)return false;if(media.audio===false||media.audioSend===false)return false;if(media.failIfNoAudio===undefined||media.failIfNoAudio===null)return false;return media.failIfNoAudio===true}function isAudioRecvEnabled(media){Janus.debug("isAudioRecvEnabled:",media);if(media===undefined||media===null)return true;if(media.audio===false)return false;if(media.audioRecv===undefined||media.audioRecv===null)return true;return media.audioRecv===true}function isVideoSendEnabled(media){Janus.debug("isVideoSendEnabled:",media);if(media===undefined||media===null)return true;if(media.video===false)return false;if(media.videoSend===undefined||media.videoSend===null)return true;return media.videoSend===true}function isVideoSendRequired(media){Janus.debug("isVideoSendRequired:",media);if(media===undefined||media===null)return false;if(media.video===false||media.videoSend===false)return false;if(media.failIfNoVideo===undefined||media.failIfNoVideo===null)return false;return media.failIfNoVideo===true}function isVideoRecvEnabled(media){Janus.debug("isVideoRecvEnabled:",media);if(media===undefined||media===null)return true;if(media.video===false)return false;if(media.videoRecv===undefined||media.videoRecv===null)return true;return media.videoRecv===true}function isDataEnabled(media){Janus.debug("isDataEnabled:",media);if(adapter.browserDetails.browser=="edge"){Janus.warn("Edge doesn't support data channels yet");return false}if(media===undefined||media===null)return false;return media.data===true}function isTrickleEnabled(trickle){Janus.debug("isTrickleEnabled:",trickle);if(trickle===undefined||trickle===null)return true;return trickle===true}}