UNPKG

awrtc_browser

Version:

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

352 lines (347 loc) 16 kB
/* Copyright (c) 2019, because-why-not.com Limited All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //import {ConnectionId, NetworkEvent, NetEventType, IBasicNetwork} from './INetwork' import { SignalingInfo, WebRtcPeerState, WebRtcDataPeer, NetworkEvent, NetEventType, ConnectionId } from "./index"; import { Queue, SLog, Output } from "./Helper"; export var WebRtcNetworkServerState; (function (WebRtcNetworkServerState) { WebRtcNetworkServerState[WebRtcNetworkServerState["Invalid"] = 0] = "Invalid"; WebRtcNetworkServerState[WebRtcNetworkServerState["Offline"] = 1] = "Offline"; WebRtcNetworkServerState[WebRtcNetworkServerState["Starting"] = 2] = "Starting"; WebRtcNetworkServerState[WebRtcNetworkServerState["Online"] = 3] = "Online"; })(WebRtcNetworkServerState || (WebRtcNetworkServerState = {})); /// <summary> /// Native version of WebRtc /// /// Make sure to use Shutdown before unity quits! (unity will probably get stuck without it) /// /// /// </summary> var WebRtcNetwork = /** @class */ (function () { //public function WebRtcNetwork(signalingConfig, lRtcConfig) { this.mTimeout = 60000; this.mInSignaling = {}; this.mNextId = new ConnectionId(1); this.mSignaling = null; this.mEvents = new Queue(); this.mIdToConnection = {}; //must be the same as the hashmap and later returned read only (avoids copies) this.mConnectionIds = new Array(); this.mServerState = WebRtcNetworkServerState.Offline; this.mIsDisposed = false; this.mSignaling = signalingConfig; this.mSignalingNetwork = this.mSignaling.GetNetwork(); this.mRtcConfig = lRtcConfig; } Object.defineProperty(WebRtcNetwork.prototype, "IdToConnection", { get: function () { return this.mIdToConnection; }, enumerable: true, configurable: true }); //only for internal use WebRtcNetwork.prototype.GetConnections = function () { return this.mConnectionIds; }; //just for debugging / testing WebRtcNetwork.prototype.SetLog = function (logDel) { this.mLogDelegate = logDel; }; WebRtcNetwork.prototype.StartServer = function (address) { this.StartServerInternal(address); }; WebRtcNetwork.prototype.StartServerInternal = function (address) { this.mServerState = WebRtcNetworkServerState.Starting; this.mSignalingNetwork.StartServer(address); }; WebRtcNetwork.prototype.StopServer = function () { if (this.mServerState == WebRtcNetworkServerState.Starting) { this.mSignalingNetwork.StopServer(); //removed. the underlaying sygnaling network should set those values //this.mServerState = WebRtcNetworkServerState.Offline; //this.mEvents.Enqueue(new NetworkEvent(NetEventType.ServerInitFailed, ConnectionId.INVALID, null)); } else if (this.mServerState == WebRtcNetworkServerState.Online) { //dont wait for confirmation this.mSignalingNetwork.StopServer(); //removed. the underlaying sygnaling network should set those values //this.mServerState = WebRtcNetworkServerState.Offline; //this.mEvents.Enqueue(new NetworkEvent(NetEventType.ServerClosed, ConnectionId.INVALID, null)); } }; WebRtcNetwork.prototype.Connect = function (address) { return this.AddOutgoingConnection(address); }; WebRtcNetwork.prototype.Update = function () { this.CheckSignalingState(); this.UpdateSignalingNetwork(); this.UpdatePeers(); }; WebRtcNetwork.prototype.Dequeue = function () { if (this.mEvents.Count() > 0) return this.mEvents.Dequeue(); return null; }; WebRtcNetwork.prototype.Peek = function () { if (this.mEvents.Count() > 0) return this.mEvents.Peek(); return null; }; WebRtcNetwork.prototype.Flush = function () { this.mSignalingNetwork.Flush(); }; WebRtcNetwork.prototype.SendData = function (id, data /*, offset : number, length : number*/, reliable) { if (id == null || data == null || data.length == 0) return; var peer = this.mIdToConnection[id.id]; if (peer) { return peer.SendData(data, /* offset, length,*/ reliable); } else { SLog.LogWarning("unknown connection id"); return false; } }; WebRtcNetwork.prototype.GetBufferedAmount = function (id, reliable) { var peer = this.mIdToConnection[id.id]; if (peer) { return peer.GetBufferedAmount(reliable); } else { SLog.LogWarning("unknown connection id"); return -1; } }; WebRtcNetwork.prototype.Disconnect = function (id) { var peer = this.mIdToConnection[id.id]; if (peer) { this.HandleDisconnect(id); } }; WebRtcNetwork.prototype.Shutdown = function () { //bugfix. Make copy before the loop as Disconnect changes the original mConnectionIds array var ids = this.mConnectionIds.slice(); for (var _i = 0, ids_1 = ids; _i < ids_1.length; _i++) { var id = ids_1[_i]; this.Disconnect(id); } this.StopServer(); this.mSignalingNetwork.Shutdown(); }; WebRtcNetwork.prototype.DisposeInternal = function () { if (this.mIsDisposed == false) { this.Shutdown(); this.mIsDisposed = true; } }; WebRtcNetwork.prototype.Dispose = function () { this.DisposeInternal(); }; //protected WebRtcNetwork.prototype.CreatePeer = function (peerId, rtcConfig) { var peer = new WebRtcDataPeer(peerId, rtcConfig); return peer; }; //private WebRtcNetwork.prototype.CheckSignalingState = function () { var connected = new Array(); var failed = new Array(); //update the signaling channels for (var key in this.mInSignaling) { var peer = this.mInSignaling[key]; peer.Update(); var timeAlive = peer.SignalingInfo.GetCreationTimeMs(); var msg = new Output(); while (peer.DequeueSignalingMessage(msg)) { var buffer = this.StringToBuffer(msg.val); this.mSignalingNetwork.SendData(new ConnectionId(+key), buffer, true); } if (peer.GetState() == WebRtcPeerState.Connected) { connected.push(peer.SignalingInfo.ConnectionId); } else if (peer.GetState() == WebRtcPeerState.SignalingFailed || timeAlive > this.mTimeout) { failed.push(peer.SignalingInfo.ConnectionId); } } for (var _i = 0, connected_1 = connected; _i < connected_1.length; _i++) { var v = connected_1[_i]; this.ConnectionEstablished(v); } for (var _a = 0, failed_1 = failed; _a < failed_1.length; _a++) { var v = failed_1[_a]; this.SignalingFailed(v); } }; WebRtcNetwork.prototype.UpdateSignalingNetwork = function () { //update the signaling system this.mSignalingNetwork.Update(); var evt; while ((evt = this.mSignalingNetwork.Dequeue()) != null) { if (evt.Type == NetEventType.ServerInitialized) { this.mServerState = WebRtcNetworkServerState.Online; this.mEvents.Enqueue(new NetworkEvent(NetEventType.ServerInitialized, ConnectionId.INVALID, evt.RawData)); } else if (evt.Type == NetEventType.ServerInitFailed) { this.mServerState = WebRtcNetworkServerState.Offline; this.mEvents.Enqueue(new NetworkEvent(NetEventType.ServerInitFailed, ConnectionId.INVALID, evt.RawData)); } else if (evt.Type == NetEventType.ServerClosed) { this.mServerState = WebRtcNetworkServerState.Offline; this.mEvents.Enqueue(new NetworkEvent(NetEventType.ServerClosed, ConnectionId.INVALID, evt.RawData)); } else if (evt.Type == NetEventType.NewConnection) { //check if new incoming connection or an outgoing was established var peer = this.mInSignaling[evt.ConnectionId.id]; if (peer) { peer.StartSignaling(); } else { this.AddIncomingConnection(evt.ConnectionId); } } else if (evt.Type == NetEventType.ConnectionFailed) { //Outgoing connection failed this.SignalingFailed(evt.ConnectionId); } else if (evt.Type == NetEventType.Disconnected) { var peer = this.mInSignaling[evt.ConnectionId.id]; if (peer) { peer.SignalingInfo.SignalingDisconnected(); } //if signaling was completed this isn't a problem //SignalingDisconnected(evt.ConnectionId); //do nothing. either webrtc has enough information to connect already //or it will wait forever for the information -> after 30 sec we give up } else if (evt.Type == NetEventType.ReliableMessageReceived) { var peer = this.mInSignaling[evt.ConnectionId.id]; if (peer) { var msg = this.BufferToString(evt.MessageData); peer.AddSignalingMessage(msg); } else { SLog.LogWarning("Signaling message from unknown connection received"); } } } }; WebRtcNetwork.prototype.UpdatePeers = function () { //every peer has a queue storing incoming messages to avoid multi threading problems -> handle it now var disconnected = new Array(); for (var key in this.mIdToConnection) { var peer = this.mIdToConnection[key]; peer.Update(); var ev = new Output(); while (peer.DequeueEvent(/*out*/ ev)) { this.mEvents.Enqueue(ev.val); } if (peer.GetState() == WebRtcPeerState.Closed) { disconnected.push(peer.ConnectionId); } } for (var _i = 0, disconnected_1 = disconnected; _i < disconnected_1.length; _i++) { var key_1 = disconnected_1[_i]; this.HandleDisconnect(key_1); } }; WebRtcNetwork.prototype.AddOutgoingConnection = function (address) { var signalingConId = this.mSignalingNetwork.Connect(address); SLog.L("new outgoing connection"); var info = new SignalingInfo(signalingConId, false, Date.now()); var peer = this.CreatePeer(this.NextConnectionId(), this.mRtcConfig); peer.SetSignalingInfo(info); this.mInSignaling[signalingConId.id] = peer; return peer.ConnectionId; }; WebRtcNetwork.prototype.AddIncomingConnection = function (signalingConId) { SLog.L("new incoming connection"); var info = new SignalingInfo(signalingConId, true, Date.now()); var peer = this.CreatePeer(this.NextConnectionId(), this.mRtcConfig); peer.SetSignalingInfo(info); this.mInSignaling[signalingConId.id] = peer; //passive way of starting signaling -> send out random number. if the other one does the same //the one with the highest number starts signaling peer.NegotiateSignaling(); return peer.ConnectionId; }; WebRtcNetwork.prototype.ConnectionEstablished = function (signalingConId) { var peer = this.mInSignaling[signalingConId.id]; delete this.mInSignaling[signalingConId.id]; this.mSignalingNetwork.Disconnect(signalingConId); this.mConnectionIds.push(peer.ConnectionId); this.mIdToConnection[peer.ConnectionId.id] = peer; this.mEvents.Enqueue(new NetworkEvent(NetEventType.NewConnection, peer.ConnectionId, null)); }; WebRtcNetwork.prototype.SignalingFailed = function (signalingConId) { var peer = this.mInSignaling[signalingConId.id]; if (peer) { //connection was still believed to be in signaling -> notify the user of the event delete this.mInSignaling[signalingConId.id]; this.mEvents.Enqueue(new NetworkEvent(NetEventType.ConnectionFailed, peer.ConnectionId, null)); if (peer.SignalingInfo.IsSignalingConnected()) { this.mSignalingNetwork.Disconnect(signalingConId); } peer.Dispose(); } }; WebRtcNetwork.prototype.HandleDisconnect = function (id) { var peer = this.mIdToConnection[id.id]; if (peer) { peer.Dispose(); } //search for the index to remove the id (user might provide a different object with the same id //don't use indexOf! var index = this.mConnectionIds.findIndex(function (e) { return e.id == id.id; }); if (index != -1) { this.mConnectionIds.splice(index, 1); delete this.mIdToConnection[id.id]; } var ev = new NetworkEvent(NetEventType.Disconnected, id, null); this.mEvents.Enqueue(ev); }; WebRtcNetwork.prototype.NextConnectionId = function () { var id = new ConnectionId(this.mNextId.id); this.mNextId.id++; return id; }; WebRtcNetwork.prototype.StringToBuffer = function (str) { var buf = new ArrayBuffer(str.length * 2); var bufView = new Uint16Array(buf); for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } var result = new Uint8Array(buf); return result; }; WebRtcNetwork.prototype.BufferToString = function (buffer) { var arr = new Uint16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 2); return String.fromCharCode.apply(null, arr); }; return WebRtcNetwork; }()); export { WebRtcNetwork }; //# sourceMappingURL=WebRtcNetwork.js.map