UNPKG

awrtc_browser

Version:

Compatible browser implementation to the Unity asset WebRTC Video Chat. Try examples in build folder

314 lines (308 loc) 13.6 kB
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title></title> <script src="./bundle/awrtc.js"></script> </head> <body> <div id="callapp1"> <h1>Callapp1:</h1> URL to connect: <p class="callapp_url"> </p> <input type="checkbox" name="audio" class="callapp_send_audio" checked autocomplete="off"> Audio <input type="checkbox" name="video" class="callapp_send_video" checked autocomplete="off"> Video <input type= "text" class="callapp_address" autocomplete="off"> <button class="callapp_button"> Join </button> <div class="callapp_local_video">local video</div> <div class="callapp_remote_video">remote video</div> </div> <script> //everything below is just the callapp.ts compiled to js via "npm run tsc -- -p src/apps" //but without the "export" key word //the inclusion of the ./bundle/awrtc.js above will make the interface available via //the global variable "awrtc" class CallApp { constructor() { this.mNetConfig = new awrtc.NetworkConfig(); this.mCall = null; //update loop this.mIntervalId = -1; this.mLocalVideo = null; this.mRemoteVideo = {}; this.mIsRunning = false; this.Ui_OnStartStopButtonClicked = () => { if (this.mIsRunning) { this.Stop(); } else { this.Start(this.mAddress, this.mAudio, this.mVideo); } }; this.Ui_OnUpdate = () => { console.debug("OnUiUpdate"); this.mAddress = this.mUiAddress.value; this.mAudio = this.mUiAudio.checked; this.mVideo = this.mUiVideo.checked; this.mUiUrl.innerHTML = this.GetUrl(); }; this.mNetConfig.IceServers = [ { urls: "stun:stun.because-why-not.com:443" }, { urls: "stun:stun.l.google.com:19302" } ]; //use for testing conferences //this.mNetConfig.IsConference = true; //this.mNetConfig.SignalingUrl = "wss://signaling.because-why-not.com/testshared"; this.mNetConfig.IsConference = false; this.mNetConfig.SignalingUrl = "wss://signaling.because-why-not.com/callapp"; } GetParameterByName(name) { var url = window.location.href; name = name.replace(/[\[\]]/g, "\\$&"); var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, " ")); } tobool(value, defaultval) { if (value === true || value === "true") return true; if (value === false || value === "false") return false; return defaultval; } Start(address, audio, video) { if (this.mCall != null) this.Stop(); this.mIsRunning = true; this.Ui_OnStart(); console.log("start"); console.log("Using signaling server url: " + this.mNetConfig.SignalingUrl); //create media configuration var config = new awrtc.MediaConfig(); config.Audio = audio; config.Video = video; config.IdealWidth = 640; config.IdealHeight = 480; config.IdealFps = 30; //For usage in HTML set FrameUpdates to false and wait for MediaUpdate to //get the VideoElement. By default awrtc would deliver frames individually //for use in Unity WebGL console.log("requested config:" + JSON.stringify(config)); //setup our high level call class. this.mCall = new awrtc.BrowserWebRtcCall(this.mNetConfig); //handle events (get triggered after Configure / Listen call) //+ugly lambda to avoid loosing "this" reference this.mCall.addEventListener((sender, args) => { this.OnNetworkEvent(sender, args); }); //As the system is designed for realtime graphics we have to call the Update method. Events are only //triggered during this Update call! this.mIntervalId = setInterval(() => { this.Update(); }, 50); //configure media. This will request access to media and can fail if the user doesn't have a proper device or //blocks access this.mCall.Configure(config); //Try to listen to the address //Conference mode = everyone listening will connect to each other //Call mode -> If the address is free it will wait for someone else to connect // -> If the address is used then it will fail to listen and then try to connect via Call(address); this.mCall.Listen(address); } Stop() { this.Cleanup(); } Cleanup() { if (this.mCall != null) { this.mCall.Dispose(); this.mCall = null; clearInterval(this.mIntervalId); this.mIntervalId = -1; this.mIsRunning = false; this.mLocalVideo = null; this.mRemoteVideo = {}; } this.Ui_OnCleanup(); } Update() { if (this.mCall != null) this.mCall.Update(); } OnNetworkEvent(sender, args) { //User gave access to requested camera/ microphone if (args.Type == awrtc.CallEventType.ConfigurationComplete) { console.log("configuration complete"); } else if (args.Type == awrtc.CallEventType.MediaUpdate) { let margs = args; if (this.mLocalVideo == null && margs.ConnectionId == awrtc.ConnectionId.INVALID) { var videoElement = margs.VideoElement; this.mLocalVideo = videoElement; this.Ui_OnLocalVideo(videoElement); console.log("local video added resolution:" + videoElement.videoWidth + videoElement.videoHeight + " fps: ??"); } else if (margs.ConnectionId != awrtc.ConnectionId.INVALID && this.mRemoteVideo[margs.ConnectionId.id] == null) { var videoElement = margs.VideoElement; this.mRemoteVideo[margs.ConnectionId.id] = videoElement; this.Ui_OnRemoteVideo(videoElement, margs.ConnectionId); console.log("remote video added resolution:" + videoElement.videoWidth + videoElement.videoHeight + " fps: ??"); } } else if (args.Type == awrtc.CallEventType.ListeningFailed) { //First attempt of this example is to try to listen on a certain address //for conference calls this should always work (expect the internet is dead) if (this.mNetConfig.IsConference == false) { //no conference call and listening failed? someone might have claimed the address. //Try to connect to existing call this.mCall.Call(this.mAddress); } else { let errorMsg = "Listening failed. Offline? Server dead?"; console.error(errorMsg); this.Ui_OnError(errorMsg); this.Cleanup(); return; } } else if (args.Type == awrtc.CallEventType.ConnectionFailed) { //Outgoing call failed entirely. This can mean there is no address to connect to, //server is offline, internet is dead, firewall blocked access, ... let errorMsg = "Connection failed. Offline? Server dead? "; console.error(errorMsg); this.Ui_OnError(errorMsg); this.Cleanup(); return; } else if (args.Type == awrtc.CallEventType.CallEnded) { //call ended or was disconnected var callEndedEvent = args; console.log("call ended with id " + callEndedEvent.ConnectionId.id); delete this.mRemoteVideo[callEndedEvent.ConnectionId.id]; this.Ui_OnLog("Disconnected from user with id " + callEndedEvent.ConnectionId.id); //check if this was the last user if (this.mNetConfig.IsConference == false && Object.keys(this.mRemoteVideo).length == 0) { //1 to 1 call and only user left -> quit this.Cleanup(); return; } } else if (args.Type == awrtc.CallEventType.Message) { //no ui for this yet. simply echo messages for testing let messageArgs = args; this.mCall.Send(messageArgs.Content, messageArgs.Reliable, messageArgs.ConnectionId); } else if (args.Type == awrtc.CallEventType.DataMessage) { //no ui for this yet. simply echo messages for testing let messageArgs = args; this.mCall.SendData(messageArgs.Content, messageArgs.Reliable, messageArgs.ConnectionId); } else { console.log("Unhandled event: " + args.Type); } } setupUi(parent) { this.mUiAddress = parent.querySelector(".callapp_address"); this.mUiAudio = parent.querySelector(".callapp_send_audio"); this.mUiVideo = parent.querySelector(".callapp_send_video"); this.mUiUrl = parent.querySelector(".callapp_url"); this.mUiButton = parent.querySelector(".callapp_button"); this.mUiLocalVideoParent = parent.querySelector(".callapp_local_video"); this.mUiRemoteVideoParent = parent.querySelector(".callapp_remote_video"); this.mUiAudio.onclick = this.Ui_OnUpdate; this.mUiVideo.onclick = this.Ui_OnUpdate; this.mUiAddress.onkeyup = this.Ui_OnUpdate; this.mUiButton.onclick = this.Ui_OnStartStopButtonClicked; //set default value + make string "true"/"false" to proper booleans this.mAudio = this.GetParameterByName("audio"); this.mAudio = this.tobool(this.mAudio, true); this.mVideo = this.GetParameterByName("video"); this.mVideo = this.tobool(this.mVideo, true); this.mAutostart = this.GetParameterByName("autostart"); this.mAutostart = this.tobool(this.mAutostart, false); this.mAddress = this.GetParameterByName("a"); //if autostart is set but no address is given -> create one and reopen the page if (this.mAddress === null && this.mAutostart == true) { this.mAddress = this.GenerateRandomKey(); window.location.href = this.GetUrlParams(); } else { if (this.mAddress === null) this.mAddress = this.GenerateRandomKey(); this.Ui_Update(); } //used for interacting with the Unity CallApp //current hack to get the html element delivered. by default this //just the image is copied and given as array //Lazy frames will be the default soon though if (this.mAutostart) { console.log("Starting automatically ... "); this.Start(this.mAddress, this.mAudio, this.mVideo); } console.log("address: " + this.mAddress + " audio: " + this.mAudio + " video: " + this.mVideo + " autostart: " + this.mAutostart); } Ui_OnStart() { this.mUiButton.textContent = "Stop"; } Ui_OnCleanup() { this.mUiButton.textContent = "Join"; while (this.mUiLocalVideoParent.hasChildNodes()) { this.mUiLocalVideoParent.removeChild(this.mUiLocalVideoParent.firstChild); } while (this.mUiRemoteVideoParent.hasChildNodes()) { this.mUiRemoteVideoParent.removeChild(this.mUiRemoteVideoParent.firstChild); } } Ui_OnLog(msg) { } Ui_OnError(msg) { } Ui_OnLocalVideo(video) { this.mUiLocalVideoParent.appendChild(document.createElement("br")); this.mUiLocalVideoParent.appendChild(video); } Ui_OnRemoteVideo(video, id) { this.mUiRemoteVideoParent.appendChild(document.createElement("br")); this.mUiRemoteVideoParent.appendChild(new Text("connection " + id.id)); this.mUiRemoteVideoParent.appendChild(document.createElement("br")); this.mUiRemoteVideoParent.appendChild(video); } Ui_Update() { console.log("UpdateUi"); this.mUiAddress.value = this.mAddress; this.mUiAudio.checked = this.mAudio; this.mUiVideo.checked = this.mVideo; this.mUiUrl.innerHTML = this.GetUrl(); } GenerateRandomKey() { var result = ""; for (var i = 0; i < 7; i++) { result += String.fromCharCode(65 + Math.round(Math.random() * 25)); } return result; } GetUrlParams() { return "?a=" + this.mAddress + "&audio=" + this.mAudio + "&video=" + this.mVideo + "&" + "autostart=" + true; } GetUrl() { return location.protocol + '//' + location.host + location.pathname + this.GetUrlParams(); } } function callapp(parent) { let callApp; console.log("init callapp"); if (parent == null) { console.log("parent was null"); parent = document.body; } awrtc.SLog.SetLogLevel(awrtc.SLogLevel.Info); callApp = new CallApp(); callApp.setupUi(parent); } callapp(document.querySelector("#callapp1")); </script> </body> </html>