Commit 3a88e44c by Christoph

added awrtc

parent 128d3c11
build/apps
build/awrtc
build/bundle
node_modules
\ No newline at end of file
BSD 3-Clause License BSD 3-Clause License
Copyright (c) 2019, because-why-not Copyright (c) 2019, because-why-not.com Limited
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
......
# awrtc_browser # awrtc_browser
npm install
npm run build
After that you can try the file ./build/callapp.html for the typesript based example or ./build/callapp_js.html for javascript example.
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jasmine Spec Runner v2.4.1</title>
<link rel="shortcut icon" type="image/png" href="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine_favicon.png">
<link rel="stylesheet" href="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
<script src="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script src="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
<!-- include source files here... -->
<script src="./build/bundle/test.js"></script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="./bundle/apps.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"> Start / Stop </button>
<div class="callapp_local_video">local video</div>
<div class="callapp_remote_video">remote video</div>
</div>
<!--
<div id="callapp2">
<h1>Callapp2:</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"> Start / Stop </button>
<div class="callapp_local_video">local video</div>
<div class="callapp_remote_video">remote video</div>
</div>
-->
<script>
apps.callapp(document.querySelector("#callapp1"));
//apps.callapp(document.querySelector("#callapp2"));
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="./bundle/apps.js"></script>
<button onclick="apps.WebRtcNetwork_minimal()">WebRtcNetwork_minimal</button><br>
<button onclick="apps.BrowserWebRtcCall_minimal()">BrowserWebRtcCall_minimal</button><br>
</head>
<body>
<script>
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="./bundle/apps.js"></script>
<button onclick="apps.BrowserMediaNetwork_TestLocalCamera()">BrowserMediaNetwork_TestLocalCamera</button><br>
<button onclick="apps.BrowserMediaNetwork_frameaccess()">BrowserMediaNetwork_frameaccess</button><br>
<button onclick="apps.WebsocketNetwork_sharedaddress()">WebsocketNetwork_sharedaddress</button><br>
<button onclick="apps.WebsocketNetwork_test1()">WebsocketNetwork_test1</button><br>
<button onclick="apps.CAPIWebRtcNetwork_testapp()">CAPIWebRtcNetwork_testapp</button><br>
<button onclick="apps.CAPIMediaNetwork_testapp()">CAPIMediaNetwork_testapp</button><br>
<button onclick="apps.CAPIMediaStreamAPI()">CAPIMediaStreamAPI</button><br>
</head>
<body>
<script>
</script>
</body>
</html>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "awrtc_browser",
"version": "0.98.3",
"description": "",
"author": "because-why-not.com Limited",
"license": "BSD-3-Clause",
"dependencies": {},
"scripts": {
"tsc": "tsc",
"webpack": "webpack",
"build": "webpack && tsc -p ./src/awrtc",
"clean": "shx rm -rf ./build/awrtc ./build/bundle"
},
"devDependencies": {
"@types/jasmine": "^2.8.9",
"jasmine": "^2.4.1",
"shx": "^0.3.2",
"source-map-loader": "^0.2.4",
"ts-loader": "^5.2.2",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"typescript": "^3.1.3",
"uglify-js": "^2.7.0",
"webpack": "^4.23.1",
"webpack-cli": "^3.1.2",
"webrtc-adapter": "^6.4.6"
}
}
/*
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 * as awrtc from "../awrtc";
/**
* Contains default values / servers used for example and test apps.
*
* Note that these servers might not be online forever. Feel free to
* run your own servers and update the url's below.
*/
export class DefaultValues
{
private static SignalingUrl= "ws://signaling.because-why-not.com";
private static SecureSignalingUrl= "wss://signaling.because-why-not.com";
private static get SignalingBase():string
{
if (window.location.protocol != "https:") {
return DefaultValues.SignalingUrl;
} else
{
return DefaultValues.SecureSignalingUrl;
}
}
/**
* Returns the signaling server URL using ws for http pages and
* wss for https. The server url here ends with "test" to avoid
* clashes with existing callapp.
*/
public static get Signaling():string
{
return DefaultValues.SignalingBase + "/test";
}
/**
* Returns the signaling server URL using ws for http pages and
* wss for https. The server url here ends with "testshared" to avoid
* clashes with existing conference app.
* This url of the server usually allows shared addresses for
* n to n connections / conference calls.
*/
public static get SignalingShared():string
{
return DefaultValues.SignalingBase + "/testshared";
}
private static get StunServer() : RTCIceServer
{
let res : RTCIceServer = {
urls: "stun:stun.l.google.com:19302"
};
return res;
}
/**
* Returns ice servers used for testing.
* Might only return the free google stun server. Without an
* additional turn server connections might fail due to firewall.
* Server might be unavailable in China.
*/
public static get IceServers(): RTCIceServer[]
{
return [DefaultValues.StunServer];
}
}
//
export function GetParameterByName(name : string, url?:string) {
if (!url) 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, " "));
}
//Returns a random string
export function GetRandomKey(): string {
var result = "";
for (var i = 0; i < 7; i++) {
result += String.fromCharCode(65 + Math.round(Math.random() * 25));
}
return result;
}
\ No newline at end of file
/*
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 * as apps from "./index"
import * as awrtc from "../awrtc/index"
(window as any).awrtc = awrtc;
(window as any).apps = apps;
\ No newline at end of file
/*
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.
*/
export * from "./apphelpers"
export * from "./testapps"
export * from "./examples"
export * from "./callapp"
\ No newline at end of file
{
"extends": "../awrtc/tsconfig_base",
"compilerOptions": {
"declaration": false,
"target": "es6",
"module": "es2015",
"outDir": "../../build/apps",
"lib": [
"es2015",
"dom"
]
},
"files": [
"callapp.ts"
]
}
/*
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.
*/
//obsolete. not needed for unity build anymore
//special entry point only needed for backwards compatibility
//it will merge awrtc namespace into window so old code still works
//that accesses objects directly instead using the global awrtc object
//the index will include all external modules
import * as awrtc from "./index"
//we merge awrtc into the global namespace
Object.assign(window, awrtc);
//for less risky global access
(window as any).awrtc = awrtc;
console.debug("loading awrtc modules completed");
\ No newline at end of file
/*
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.
*/
console.debug("loading awrtc modules ...");
//this should trigger webpack to include
//the webrtc adapter.js. It changes the
//global WebRTC calls and adds backwards
//and browser compatibility.
declare function require(moduleName: string)
let adapter = require("webrtc-adapter");
export * from "./network/index"
export * from "./media/index"
//for simplicity browser and unity are merged here
//it could as well be built and deployed separately
export * from "./media_browser/index"
export * from "./unity/index"
console.debug("loading awrtc modules completed");
\ No newline at end of file
/*
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 } from "../network/index";
import { IFrameData } from "./RawFrame";
export interface CallEventHandler {
(sender: any, args: CallEventArgs): void;
}
/// <summary>
/// Type of the event.
/// </summary>
export enum CallEventType {
/// <summary>
/// Used if the event value wasn't initialized
/// </summary>
Invalid = 0,
/// <summary>
/// The call object is successfully connected to the server waiting for another user
/// to connect.
/// </summary>
WaitForIncomingCall = 1,
/// <summary>
/// A call was accepted
/// </summary>
CallAccepted = 2,
/// <summary>
/// The call ended
/// </summary>
CallEnded = 3,
/**
* Backwards compatibility. Use MediaUpdate
*/
FrameUpdate = 4,
/// <summary>
/// Text message arrived
/// </summary>
Message = 5,
/// <summary>
/// Connection failed. Might be due to an server, network error or the address didn't exist
/// Using ErrorEventArgs
/// </summary>
ConnectionFailed = 6,
/// <summary>
/// Listening failed. Address might be in use or due to server/network error
/// Using ErrorEventArgs
/// </summary>
ListeningFailed = 7,
/// <summary>
/// Event triggered after the local media was successfully configured.
/// If requested the call object will have access to the users camera and/or audio now and
/// the local camera frames can be received in events.
/// </summary>
ConfigurationComplete = 8,
/// <summary>
/// Configuration failed. This happens if the configuration requested features
/// the system doesn't support e.g. no camera, camera doesn't support the requested resolution
/// or the user didn't allow the website to access the camera/microphone in WebGL mode.
/// </summary>
ConfigurationFailed = 9,
/// <summary>
/// Reliable or unreliable data msg arrived
/// </summary>
DataMessage = 10,
/**
*
*/
MediaUpdate = 20,
}
export class CallEventArgs {
private mType = CallEventType.Invalid;
public get Type(): CallEventType {
return this.mType;
}
public constructor(type: CallEventType) {
this.mType = type;
}
}
export class CallAcceptedEventArgs extends CallEventArgs
{
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
public constructor(connectionId: ConnectionId) {
super(CallEventType.CallAccepted);
this.mConnectionId = connectionId;
}
}
export class CallEndedEventArgs extends CallEventArgs {
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId{
return this.mConnectionId;
}
public constructor(connectionId: ConnectionId) {
super(CallEventType.CallEnded);
this.mConnectionId = connectionId;
}
}
export enum CallErrorType {
Unknown
}
export class ErrorEventArgs extends CallEventArgs {
private mErrorMessage: string;
public get ErrorMessage(): string {
return this.mErrorMessage;
}
private mErrorType: CallErrorType = CallErrorType.Unknown;
public get ErrorType(): CallErrorType {
return this.mErrorType;
}
public constructor(eventType: CallEventType, type?: CallErrorType, errorMessage?: string) {
super(eventType);
this.mErrorType = type;
this.mErrorMessage = errorMessage;
if (this.mErrorMessage == null) {
switch (eventType) {
//use some generic error messages as the underlaying system doesn't report the errors yet.
case CallEventType.ConnectionFailed:
this.mErrorMessage = "Connection failed.";
break;
case CallEventType.ListeningFailed:
this.mErrorMessage = "Failed to allow incoming connections. Address already in use or server connection failed.";
break;
default:
this.mErrorMessage = "Unknown error.";
break;
}
}
}
}
export class WaitForIncomingCallEventArgs extends CallEventArgs
{
private mAddress: string;
public get Address(): string {
return this.mAddress;
}
public constructor(address: string) {
super(CallEventType.WaitForIncomingCall);
this.mAddress = address;
}
}
export class MessageEventArgs extends CallEventArgs {
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
private mContent: string;
public get Content(): string {
return this.mContent;
}
private mReliable: boolean;
public get Reliable(): boolean {
return this.mReliable;
}
public constructor(id: ConnectionId, message: string, reliable: boolean) {
super(CallEventType.Message);
this.mConnectionId = id;
this.mContent = message;
this.mReliable = reliable;
}
}
export class DataMessageEventArgs extends CallEventArgs {
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
private mContent: Uint8Array;
public get Content(): Uint8Array {
return this.mContent;
}
private mReliable: boolean;
public get Reliable(): boolean {
return this.mReliable;
}
public constructor(id: ConnectionId, message: Uint8Array, reliable: boolean) {
super(CallEventType.DataMessage);
this.mConnectionId = id;
this.mContent = message;
this.mReliable = reliable;
}
}
/**
* Replaces the FrameUpdateEventArgs. Instead of
* giving access to video frames only this gives access to
* video html tag once it is created.
* TODO: Add audio + video tracks + flag that indicates added, updated or removed
* after renegotiation is added.
*/
export class MediaUpdatedEventArgs extends CallEventArgs
{
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
/// <summary>
/// False if the frame is from a local camera. True if it is received from
/// via network.
/// </summary>
public get IsRemote(): boolean {
return this.mConnectionId.id != ConnectionId.INVALID.id;
}
private mVideoElement:HTMLVideoElement;
public get VideoElement():HTMLVideoElement
{
return this.mVideoElement;
}
public constructor(conId: ConnectionId, videoElement:HTMLVideoElement)
{
super(CallEventType.MediaUpdate);
this.mConnectionId = conId;
this.mVideoElement = videoElement;
}
}
/// <summary>
/// Will be replaced with MediaUpdatedEventArgs.
/// It doesn't make a lot of sense in HTML only
/// </summary>
export class FrameUpdateEventArgs extends CallEventArgs {
private mFrame: IFrameData;
/// <summary>
/// Raw image data. Note that the byte array contained in RawFrame will be reused
/// for the next frames received. Only valid until the next call of ICall.Update
/// </summary>
public get Frame(): IFrameData {
return this.mFrame;
}
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
/// <summary>
/// False if the frame is from a local camera. True if it is received from
/// via network.
/// </summary>
public get IsRemote(): boolean {
return this.mConnectionId.id != ConnectionId.INVALID.id;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="conId"></param>
/// <param name="frame"></param>
public constructor(conId: ConnectionId, frame: IFrameData)
{
super(CallEventType.FrameUpdate);
this.mConnectionId = conId;
this.mFrame = frame;
}
}
\ No newline at end of file
/*
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 { CallEventHandler } from "./CallEventArgs";
import { ConnectionId } from "../network/index";
import { MediaConfig } from "./MediaConfig";
/** Mostly the same as the C# side ICall interface.
*
* Usage of this interface usually follows a specific pattern:
* 1. Create a platform specific instance via a factory with a specific
* NetworkConfig
* 2. Register an event handler at CallEvent and call Update regularly
* (ideally once for each frame shown to the user in realtime
* applcations so 30-60 times per second)
* 3. Call configure with your own MediaConfig instance defining what
* features you need.
* 4. Wait for a ConfigurationComplete (or failed) event. During this
* time the platform might ask the user the allow access to the devices.
* 5. Either call Listen with an address to wait for an incoming connection
* or use Call to conect another ICall that already listens on that address.
* 6. Wait for CallAccepted and other events. The call is now active and
* you can use Send messages, change volume, ...
* 7. Call Dispose to cleanup
*
* Do not forget to call Dispose method after you finished the call or the connection
* might run forever in the background!
*
* See example apps and guides for more information.
*/
export interface ICall {
addEventListener(listener: CallEventHandler): void;
removeEventListener(listener: CallEventHandler): void;
Call(address: string): void;
Configure(config: MediaConfig): void;
Listen(address: string): void;
Send(message: string, reliable?:boolean, id? :ConnectionId): void
SendData(message: Uint8Array, reliable: boolean, id: ConnectionId): void
Update(): void;
Dispose(): void;
}
\ No newline at end of file
/*
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, IWebRtcNetwork } from "../network/index";
import { MediaConfig } from "./MediaConfig";
import { IFrameData } from "./RawFrame";
export enum MediaConfigurationState {
Invalid = 0,
NoConfiguration = 1,
InProgress = 2,
Successful = 3,
Failed = 4
}
export enum MediaEventType
{
Invalid = 0,
StreamAdded = 20
}
/**
* Will replace frame event / configuration system in the future.
*
* So far it only delivers HTMLVideoElements once connection and
* all tracks are ready and it plays.
*
* This is all temporary and will be updated soon to handle
* all events from configuration of local streams to frame updates and
* renegotation.
*
*/
export class MediaEvent
{
private mEventType = MediaEventType.Invalid;
public get EventType()
{
return this.mEventType;
}
private mConnectionId = ConnectionId.INVALID;
public get ConnectionId()
{
return this.mConnectionId;
}
private mArgs:any;
public get Args():any
{
return this.mArgs;
}
public constructor(type:MediaEventType, id: ConnectionId, args:any)
{
this.mEventType = type;
this.mConnectionId = id;
this.mArgs = args;
}
}
/**Interface adds media functionality to IWebRtcNetwork.
* It is used to ensure compatibility to all other platforms.
*/
export interface IMediaNetwork extends IWebRtcNetwork
{
Configure(config: MediaConfig): void;
GetConfigurationState(): MediaConfigurationState;
GetConfigurationError(): string;
ResetConfiguration(): void;
TryGetFrame(id: ConnectionId): IFrameData;
PeekFrame(id: ConnectionId): IFrameData;
SetVolume(volume: number, id: ConnectionId): void;
HasAudioTrack(id: ConnectionId): boolean;
HasVideoTrack(id: ConnectionId): boolean;
//Only used for browser specific events for now
//Not part of the C# api yet.
DequeueMediaEvent(): MediaEvent;
}
\ No newline at end of file
/*
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.
*/
/// <summary>
/// Configuration for the WebRtcCall class.
///
/// Allows to turn on / off video and audio + configure the used servers to initialize the connection and
/// avoid firewalls.
/// </summary>
export class MediaConfig {
private mAudio: boolean = true;
public get Audio(): boolean {
return this.mAudio;
}
public set Audio(value: boolean) {
this.mAudio = value;
}
private mVideo: boolean = true;
public get Video(): boolean {
return this.mVideo;
}
public set Video(value: boolean) {
this.mVideo = value;
}
private mVideoDeviceName : string = "";
public get VideoDeviceName(): string {
return this.mVideoDeviceName;
}
public set VideoDeviceName(value: string) {
this.mVideoDeviceName = value;
}
private mMinWidth = -1;
public get MinWidth(): number {
return this.mMinWidth;
}
public set MinWidth(value: number) {
this.mMinWidth = value;
}
private mMinHeight = -1;
public get MinHeight(): number {
return this.mMinHeight;
}
public set MinHeight(value: number) {
this.mMinHeight = value;
}
private mMaxWidth = -1;
public get MaxWidth(): number {
return this.mMaxWidth;
}
public set MaxWidth(value: number) {
this.mMaxWidth = value;
}
private mMaxHeight = -1;
public get MaxHeight(): number {
return this.mMaxHeight;
}
public set MaxHeight(value: number) {
this.mMaxHeight = value;
}
private mIdealWidth = -1;
public get IdealWidth(): number {
return this.mIdealWidth;
}
public set IdealWidth(value: number) {
this.mIdealWidth = value;
}
private mIdealHeight = -1;
public get IdealHeight(): number {
return this.mIdealHeight;
}
public set IdealHeight(value: number) {
this.mIdealHeight = value;
}
private mMinFps = -1;
public get MinFps(): number {
return this.mMinFps;
}
public set MinFps(value: number) {
this.mMinFps = value;
}
private mMaxFps = -1;
public get MaxFps(): number {
return this.mMaxFps;
}
public set MaxFps(value: number) {
this.mMaxFps = value;
}
private mIdealFps = -1;
public get IdealFps(): number {
return this.mIdealFps;
}
public set IdealFps(value: number) {
this.mIdealFps = value;
}
private mFrameUpdates = false;
/** false - frame updates aren't generated. Useful for browser mode
* true - library will deliver frames as ByteArray
*/
public get FrameUpdates(): boolean {
return this.mFrameUpdates;
}
public set FrameUpdates(value: boolean) {
this.mFrameUpdates = value;
}
}
\ No newline at end of file
/*
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.
*/
export class NetworkConfig {
private mIceServers = new Array<RTCIceServer>();
public get IceServers(): RTCIceServer[] {
return this.mIceServers;
}
public set IceServers(value: RTCIceServer[]) {
this.mIceServers = value;
}
private mSignalingUrl = "ws://because-why-not.com:12776";
public get SignalingUrl() {
return this.mSignalingUrl;
}
public set SignalingUrl(value: string) {
this.mSignalingUrl = value;
}
private mIsConference = false;
public get IsConference(): boolean {
return this.mIsConference;
}
public set IsConference(value:boolean) {
this.mIsConference = value;
}
}
\ No newline at end of file
/*
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 { SLog } from "../network/Helper";
import { BrowserMediaStream } from "../media_browser/index";
export enum FramePixelFormat {
Invalid = 0,
Format32bppargb
}
//replace with interface after typescript 2.0 update (properties in interfaces aren't supported yet)
export class IFrameData {
public get Format(): FramePixelFormat{
return FramePixelFormat.Format32bppargb;
}
public get Buffer(): Uint8Array {
return null;
}
public get Width(): number {
return -1;
}
public get Height(): number {
return -1;
}
public constructor() { }
}
//Container for the raw bytes of the current frame + height and width.
//Format is currently fixed based on the browser getImageData format
export class RawFrame extends IFrameData{
private mBuffer: Uint8Array = null;
public get Buffer(): Uint8Array {
return this.mBuffer;
}
private mWidth: number;
public get Width(): number {
return this.mWidth;
}
private mHeight: number;
public get Height(): number {
return this.mHeight;
}
constructor(buffer: Uint8Array, width: number, height: number) {
super();
this.mBuffer = buffer;
this.mWidth = width;
this.mHeight = height;
}
}
/**
* This class is suppose to increase the speed of the java script implementation.
* Instead of creating RawFrames every Update call (because the real fps are unknown currently) it will
* only create a lazy frame which will delay the creation of the RawFrame until the user actually tries
* to access any data.
* Thus if the game slows down or the user doesn't access any data the expensive copy is avoided.
*/
export class LazyFrame extends IFrameData{
private mFrameGenerator: BrowserMediaStream;
public get FrameGenerator() {
return this.mFrameGenerator;
}
private mRawFrame: RawFrame;
public get Buffer(): Uint8Array {
this.GenerateFrame();
if (this.mRawFrame == null)
return null;
return this.mRawFrame.Buffer;
}
public get Width(): number {
this.GenerateFrame();
if (this.mRawFrame == null)
return -1;
return this.mRawFrame.Width;
}
public get Height(): number {
this.GenerateFrame();
if (this.mRawFrame == null)
return -1;
return this.mRawFrame.Height;
}
constructor(frameGenerator: BrowserMediaStream) {
super();
this.mFrameGenerator = frameGenerator;
}
//Called before access of any frame data triggering the creation of the raw frame data
private GenerateFrame() {
if (this.mRawFrame == null) {
try {
this.mRawFrame = this.mFrameGenerator.CreateFrame();
} catch (exception) {
this.mRawFrame = null;
SLog.LogWarning("frame skipped in GenerateFrame due to exception: " + JSON.stringify(exception));
}
}
}
}
\ No newline at end of file
/*
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.
*/
export * from './AWebRtcCall'
export * from './CallEventArgs'
export * from './ICall'
export * from './IMediaNetwork'
export * from './MediaConfig'
export * from './NetworkConfig'
export * from './RawFrame'
\ No newline at end of file
/*
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 { AWebRtcCall } from "../media/AWebRtcCall";
import { NetworkConfig } from "../media/NetworkConfig";
import { IMediaNetwork } from "../media/IMediaNetwork";
import { BrowserMediaNetwork } from "./BrowserMediaNetwork";
/**Browser version of the C# version of WebRtcCall.
*
* See ICall interface for detailed documentation.
* BrowserWebRtcCall mainly exists to allow other versions
* in the future that might build on a different IMediaNetwork
* interface (Maybe something running inside Webassembly?).
*/
export class BrowserWebRtcCall extends AWebRtcCall {
public constructor(config: NetworkConfig) {
super(config);
this.Initialize(this.CreateNetwork());
}
private CreateNetwork(): IMediaNetwork {
return new BrowserMediaNetwork(this.mNetworkConfig);
}
protected DisposeInternal(disposing: boolean): void {
super.DisposeInternal(disposing);
if (disposing) {
if (this.mNetwork != null)
this.mNetwork.Dispose();
this.mNetwork = null;
}
}
}
\ No newline at end of file
/*
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 { SLog } from "../network/index";
export class DeviceInfo
{
public deviceId:string = null;
public defaultLabel:string = null;
public label:string = null;
public isLabelGuessed:boolean = true;
}
export interface DeviceApiOnChanged {
(): void;
}
export class DeviceApi
{
private static sLastUpdate = 0;
public static get LastUpdate() :number
{
return DeviceApi.sLastUpdate;
}
public static get HasInfo()
{
return DeviceApi.sLastUpdate > 0;
}
private static sDeviceInfo: { [id: string] : DeviceInfo; } = {};
private static sVideoDeviceCounter = 1;
private static sAccessStream:MediaStream = null;
private static sUpdateEvents: Array<DeviceApiOnChanged> = [];
public static AddOnChangedHandler(evt: DeviceApiOnChanged)
{
DeviceApi.sUpdateEvents.push(evt);
}
public static RemOnChangedHandler(evt: DeviceApiOnChanged)
{
let index = DeviceApi.sUpdateEvents.indexOf(evt);
if(index >= 0)
DeviceApi.sUpdateEvents.splice(index, 1);
}
private static TriggerChangedEvent()
{
for(let v of DeviceApi.sUpdateEvents)
{
try{
v();
}catch(e)
{
SLog.LE("Error in DeviceApi user event handler: " + e);
console.exception(e);
}
}
}
private static InternalOnEnum = (devices:MediaDeviceInfo[])=>
{
DeviceApi.sLastUpdate = new Date().getTime();
let newDeviceInfo: { [id: string] : DeviceInfo; } = {};
for(let info of devices)
{
if(info.kind != "videoinput")
continue;
let newInfo = new DeviceInfo();
newInfo.deviceId = info.deviceId;
let knownInfo:DeviceInfo= null;
if(newInfo.deviceId in DeviceApi.Devices)
{
//known device. reuse the default label
knownInfo = DeviceApi.Devices[newInfo.deviceId];
}
//check if we gave this device a default label already
//this is used to identify it via a user readable name in case
//we update multiple times with proper labels / default labels
if(knownInfo != null)
{
newInfo.defaultLabel = knownInfo.defaultLabel;
}else
{
newInfo.defaultLabel = info.kind + " " + DeviceApi.sVideoDeviceCounter;;
DeviceApi.sVideoDeviceCounter++;
}
//check if we know a proper label or got one this update
if(knownInfo != null && knownInfo.isLabelGuessed == false)
{
//already have one
newInfo.label = knownInfo.label;
newInfo.isLabelGuessed = false;
}else if(info.label)
{
//got a new one
newInfo.label = info.label;
newInfo.isLabelGuessed = false;
}else{
//no known label -> just use the default one
newInfo.label = newInfo.defaultLabel;
newInfo.isLabelGuessed = true;
}
newDeviceInfo[newInfo.deviceId] = newInfo;
}
DeviceApi.sDeviceInfo = newDeviceInfo;
if(DeviceApi.sAccessStream)
{
DeviceApi.sAccessStream.stop();
DeviceApi.sAccessStream = null;
}
DeviceApi.TriggerChangedEvent();
}
public static get Devices()
{
return DeviceApi.sDeviceInfo;
}
public static Reset()
{
DeviceApi.sUpdateEvents = [];
DeviceApi.sLastUpdate = 0;
DeviceApi.sDeviceInfo = {};
DeviceApi.sVideoDeviceCounter = 1;
DeviceApi.sAccessStream = null;
}
private static InternalOnError = (err:DOMError)=>
{
SLog.LE(err);
}
private static InternalOnStream = (stream:MediaStream)=>
{
DeviceApi.sAccessStream = stream;
DeviceApi.Update();
}
/**Updates the device list based on the current
* access. Given devices numbers if the name isn't known.
*/
public static Update():void
{
navigator.mediaDevices.enumerateDevices()
.then(DeviceApi.InternalOnEnum)
.catch(DeviceApi.InternalOnError);
}
/**Asks the user for access first to get the full
* device names.
*/
public static RequestUpdate():void
{
let constraints = {video:true};
navigator.mediaDevices.getUserMedia(constraints)
.then(DeviceApi.InternalOnStream)
.catch(DeviceApi.InternalOnError);
}
public static GetDeviceId(label:string):string {
let devs = DeviceApi.Devices;
for (var key in devs) {
let dev = devs[key];
if(dev.label == label || dev.defaultLabel == label || dev.deviceId == label){
return dev.deviceId;
}
}
return null;
}
}
\ No newline at end of file
/*
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 { WebRtcDataPeer, SLog } from "../network/index";
import { BrowserMediaStream } from "./BrowserMediaStream";
import { IFrameData } from "../media/RawFrame";
//TODO: Not part of typescript as it is obsolete but
//chrome might still need this
export interface RTCMediaStreamEvent extends Event {
stream: MediaStream;
}
export interface RTCPeerConnectionObsolete extends RTCPeerConnection
{
onaddstream: ((this: RTCPeerConnection, streamEvent: RTCMediaStreamEvent) => any) | null;
addStream(stream: MediaStream): void;
}
export class MediaPeer extends WebRtcDataPeer
{
private mRemoteStream: BrowserMediaStream = null;
//quick workaround to allow html user to get the HTMLVideoElement once it is
//created. Might be done via events later to make wrapping to unity/emscripten possible
public InternalStreamAdded: (peer:MediaPeer, stream: BrowserMediaStream) => void = null;
//true - will use obsolete onstream / add stream
//false - will use ontrack / addtrack (seems to work fine now even on chrome)
public static sUseObsolete = false;
protected OnSetup(): void {
super.OnSetup();
//TODO: test in different browsers if boolean works now
//this is unclear in the API. according to typescript they are boolean, in native code they are int
//and some browser failed in the past if boolean was used ...
this.mOfferOptions = { "offerToReceiveAudio": true, "offerToReceiveVideo": true };
if(MediaPeer.sUseObsolete) {
SLog.LW("Using obsolete onaddstream as not all browsers support ontrack");
(this.mPeer as RTCPeerConnectionObsolete).onaddstream = (streamEvent: RTCMediaStreamEvent) => { this.OnAddStream(streamEvent); };
}
else{
this.mPeer.ontrack = (ev:RTCTrackEvent)=>{this.OnTrack(ev);}
}
}
protected OnCleanup() {
super.OnCleanup();
if (this.mRemoteStream != null) {
this.mRemoteStream.Dispose();
this.mRemoteStream = null;
}
}
private OnAddStream(streamEvent: RTCMediaStreamEvent) {
this.SetupStream(streamEvent.stream);
}
private OnTrack(ev:RTCTrackEvent){
if(ev && ev.streams && ev.streams.length > 0)
{
//this is getting called twice if audio and video is active
if(this.mRemoteStream == null)
this.SetupStream(ev.streams[0]);
}else{
SLog.LE("Unexpected RTCTrackEvent: " + JSON.stringify(ev));
}
}
private SetupStream(stream:MediaStream)
{
this.mRemoteStream = new BrowserMediaStream(stream);
//trigger events once the stream has its meta data available
this.mRemoteStream.InternalStreamAdded = (stream) =>{
if(this.InternalStreamAdded != null)
{
this.InternalStreamAdded(this, stream);
}
};
}
public TryGetRemoteFrame(): IFrameData
{
if (this.mRemoteStream == null)
return null;
return this.mRemoteStream.TryGetFrame();
}
public PeekFrame(): IFrameData {
if (this.mRemoteStream == null)
return null;
return this.mRemoteStream.PeekFrame();
}
public AddLocalStream(stream: MediaStream) {
if(MediaPeer.sUseObsolete) {
(this.mPeer as RTCPeerConnectionObsolete).addStream(stream);
}
else{
for(let v of stream.getTracks())
this.mPeer.addTrack(v, stream);
}
}
public Update() {
super.Update();
if (this.mRemoteStream != null) {
this.mRemoteStream.Update();
}
}
public SetVolume(volume: number): void {
if (this.mRemoteStream != null)
this.mRemoteStream.SetVolume(volume);
}
public HasAudioTrack(): boolean {
if (this.mRemoteStream != null)
return this.mRemoteStream.HasAudioTrack();
return false;
}
public HasVideoTrack(): boolean {
if (this.mRemoteStream != null)
return this.mRemoteStream.HasVideoTrack();
return false;
}
}
\ No newline at end of file
/*
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.
*/
export * from './BrowserMediaNetwork'
export * from './BrowserWebRtcCall'
export * from './BrowserMediaStream'
export * from './MediaPeer'
export * from './DeviceApi'
/*
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.
*/
/**Contains some helper classes to keep the typescript implementation
* similar to the C# implementation.
*
*/
export class Queue<T> {
private mArr: Array<T> = new Array<T>();
constructor() {
}
public Enqueue(val: T) {
this.mArr.push(val);
}
public TryDequeue(outp: Output<T> ): boolean{
var res = false
if (this.mArr.length > 0) {
outp.val = this.mArr.shift();
res = true;
}
return res;
}
public Dequeue(): T {
if (this.mArr.length > 0) {
return this.mArr.shift();
} else {
return null;
}
}
public Peek(): T {
if (this.mArr.length > 0) {
return this.mArr[0];
} else {
return null;
}
}
public Count(): number{
return this.mArr.length;
}
public Clear():void
{
this.mArr = new Array<T>();
}
}
export class List<T> {
private mArr: Array<T> = new Array<T>();
public get Internal() : Array<T>
{
return this.mArr;
}
constructor() {
}
public Add(val: T) {
this.mArr.push(val);
}
public get Count(): number {
return this.mArr.length;
}
}
export class Output<T>
{
public val : T;
}
export class Debug {
public static Log(s: any) {
SLog.Log(s);
}
public static LogError(s: any) {
SLog.LogError(s);
}
public static LogWarning(s: any) {
SLog.LogWarning(s);
}
}
export abstract class Encoder {
public abstract GetBytes(text: string): Uint8Array;
public abstract GetString(buffer: Uint8Array): string;
}
export class UTF16Encoding extends Encoder{
constructor() {
super();
}
public GetBytes(text: string): Uint8Array {
return this.stringToBuffer(text);
}
public GetString(buffer: Uint8Array): string {
return this.bufferToString(buffer);
}
private bufferToString(buffer: Uint8Array): string {
let arr = new Uint16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 2);
return String.fromCharCode.apply(null, arr);
}
private stringToBuffer(str: string): Uint8Array {
let buf = new ArrayBuffer(str.length * 2);
let bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
let result = new Uint8Array(buf);
return result;
}
}
export class Encoding {
public static get UTF16() {
return new UTF16Encoding();
}
constructor() {
}
}
export class Random {
public static getRandomInt(min, max): number {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
}
export class Helper {
public static tryParseInt(value : string): number {
try {
if (/^(\-|\+)?([0-9]+)$/.test(value)) {
let result = Number(value);
if (isNaN(result) == false)
return result;
}
} catch ( e) {
}
return null;
}
}
export enum SLogLevel
{
None = 0,
Errors = 1,
Warnings = 2,
Info = 3
}
//Simplified logger
export class SLog {
private static sLogLevel: SLogLevel = SLogLevel.Warnings;
public static SetLogLevel(level: SLogLevel)
{
SLog.sLogLevel = level;
}
public static L(msg: any): void {
SLog.Log(msg);
}
public static LW(msg: any): void {
SLog.LogWarning(msg);
}
public static LE(msg: any): void {
SLog.LogError(msg);
}
public static Log(msg: any): void {
if(SLog.sLogLevel >= SLogLevel.Info)
console.log(msg);
}
public static LogWarning(msg: any): void {
if(SLog.sLogLevel >= SLogLevel.Warnings)
console.warn(msg);
}
public static LogError(msg: any) {
if(SLog.sLogLevel >= SLogLevel.Errors)
console.error(msg);
}
}
\ No newline at end of file
/*
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} from "./index"
import { Queue } from "./Helper";
interface IIdNetworkDictionary {
[id: number]: LocalNetwork;
}
interface IAddressNetworkDictionary {
[address: string]: LocalNetwork;
}
/**Helper to simulate the WebsocketNetwork or WebRtcNetwork
* within a local application without
* any actual network components.
*
* This implementation might lack some features.
*/
export class LocalNetwork{
private static sNextId:number = 1;
private static mServers = {} as IAddressNetworkDictionary;
private mId:number;
private mNextNetworkId = new ConnectionId(1);
private mServerAddress : string = null;
private mEvents = new Queue<NetworkEvent>();
private mConnectionNetwork = {} as IIdNetworkDictionary;
private mIsDisposed = false;
public constructor() {
this.mId = LocalNetwork.sNextId;
LocalNetwork.sNextId++;
}
public get IsServer() {
return this.mServerAddress != null;
}
public StartServer(serverAddress: string = null): void
{
if (serverAddress == null)
serverAddress = "" + this.mId;
if (serverAddress in LocalNetwork.mServers) {
this.Enqueue(NetEventType.ServerInitFailed, ConnectionId.INVALID, serverAddress);
return;
}
LocalNetwork.mServers[serverAddress] = this;
this.mServerAddress = serverAddress;
this.Enqueue(NetEventType.ServerInitialized, ConnectionId.INVALID, serverAddress);
}
public StopServer() : void
{
if (this.IsServer) {
this.Enqueue(NetEventType.ServerClosed, ConnectionId.INVALID, this.mServerAddress);
delete LocalNetwork.mServers[this.mServerAddress];
this.mServerAddress = null;
}
}
public Connect(address: string): ConnectionId
{
var connectionId = this.NextConnectionId();
var sucessful = false;
if (address in LocalNetwork.mServers) {
let server = LocalNetwork.mServers[address];
if (server != null) {
server.ConnectClient(this);
//add the server as local connection
this.mConnectionNetwork[connectionId.id] = LocalNetwork.mServers[address];
this.Enqueue(NetEventType.NewConnection, connectionId, null);
sucessful = true;
}
}
if (sucessful == false) {
this.Enqueue(NetEventType.ConnectionFailed, connectionId, "Couldn't connect to the given server with id " + address);
}
return connectionId;
}
public Shutdown() : void
{
for(var id in this.mConnectionNetwork) //can be changed while looping?
{
this.Disconnect(new ConnectionId(+id));
}
//this.mConnectionNetwork.Clear();
this.StopServer();
}
public Dispose() : void{
if (this.mIsDisposed == false) {
this.Shutdown();
}
}
public SendData(userId: ConnectionId, data: Uint8Array, reliable: boolean): boolean
{
if (userId.id in this.mConnectionNetwork)
{
let net = this.mConnectionNetwork[userId.id];
net.ReceiveData(this, data, reliable);
return true;
}
return false;
}
public Update(): void
{
//work around for the GarbageCollection bug
//usually weak references are removed during garbage collection but that
//fails sometimes as others weak references get null to even though
//the objects still exist!
this.CleanupWreakReferences();
}
public Dequeue(): NetworkEvent
{
return this.mEvents.Dequeue();
}
public Peek(): NetworkEvent
{
return this.mEvents.Peek();
}
public Flush(): void
{
}
public Disconnect(id: ConnectionId): void
{
if (id.id in this.mConnectionNetwork) {
let other = this.mConnectionNetwork[id.id];
if (other != null) {
other.InternalDisconnectNetwork(this);
this.InternalDisconnect(id);
}
else {
//this is suppose to never happen but it does
//if a server is destroyed by the garbage collector the client
//weak reference appears to be NULL even though it still exists
//bug?
this.CleanupWreakReferences();
}
}
}
private FindConnectionId(network: LocalNetwork): ConnectionId
{
for(var kvp in this.mConnectionNetwork)
{
let network = this.mConnectionNetwork[kvp];
if (network != null) {
return new ConnectionId(+kvp);
}
}
return ConnectionId.INVALID;
}
private NextConnectionId(): ConnectionId
{
let res = this.mNextNetworkId;
this.mNextNetworkId = new ConnectionId(res.id + 1);
return res;
}
private ConnectClient(client: LocalNetwork): void
{
//if (this.IsServer == false)
// throw new InvalidOperationException();
let nextId = this.NextConnectionId();
//server side only
this.mConnectionNetwork[nextId.id] = client;
this.Enqueue(NetEventType.NewConnection, nextId, null);
}
private Enqueue(type: NetEventType, id: ConnectionId, data: any): void
{
let ev = new NetworkEvent(type, id, data);
this.mEvents.Enqueue(ev);
}
private ReceiveData(network: LocalNetwork, data: Uint8Array, reliable : boolean): void
{
let userId = this.FindConnectionId(network);
let buffer = new Uint8Array(data.length);
for (let i = 0; i < buffer.length; i++) {
buffer[i] = data[i];
}
let type = NetEventType.UnreliableMessageReceived;
if (reliable)
type = NetEventType.ReliableMessageReceived;
this.Enqueue(type, userId, buffer);
}
private InternalDisconnect(id: ConnectionId): void
{
if (id.id in this.mConnectionNetwork) {
this.Enqueue(NetEventType.Disconnected, id, null);
delete this.mConnectionNetwork[id.id];
}
}
private InternalDisconnectNetwork(ln: LocalNetwork): void
{
//if it can't be found it will return invalid which is ignored in internal disconnect
this.InternalDisconnect(this.FindConnectionId(ln));
}
private CleanupWreakReferences(): void
{
//foreach(var kvp in mConnectionNetwork.Keys.ToList())
//{
// var val = mConnectionNetwork[kvp];
// if (val.Get() == null) {
// InternalDisconnect(kvp);
// }
//}
}
}
\ No newline at end of file
/*
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.
*/
export * from './INetwork'
export * from './Helper'
export * from './WebRtcPeer'
export * from './WebRtcNetwork'
export * from './WebsocketNetwork'
export * from './LocalNetwork'
\ No newline at end of file
{
"extends": "./tsconfig_base",
"compilerOptions": {
"declaration": true,
"target": "es5",
"module": "es2015",
"outDir": "../../build/awrtc"
}
}
{
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"declaration": false,
"baseUrl": ".",
"esModuleInterop": true
},
"exclude": [
"node_modules",
"wwwroot"
],
"files": [
"./network/Helper.ts",
"./network/INetwork.ts",
"./network/WebRtcNetwork.ts",
"./network/WebRtcPeer.ts",
"./network/WebsocketNetwork.ts",
"./network/LocalNetwork.ts",
"./network/index.ts",
"./media/CallEventArgs.ts",
"./media/ICall.ts",
"./media/IMediaNetwork.ts",
"./media/MediaConfig.ts",
"./media/NetworkConfig.ts",
"./media/RawFrame.ts",
"./media/AWebRtcCall.ts",
"./media/index.ts",
"./media_browser/BrowserMediaNetwork.ts",
"./media_browser/BrowserWebRtcCall.ts",
"./media_browser/BrowserMediaStream.ts",
"./media_browser/MediaPeer.ts",
"./media_browser/index.ts",
"./unity/CAPI.ts",
"./unity/index.ts",
"./index.ts"
]
}
\ No newline at end of file
/*
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.
*/
export * from "./CAPI"
/*
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.
*/
//current setup needs to load everything as a module
export function some_random_export_1()
{
}
describe("BrowserApiTest_MediaStreamApi", () => {
beforeEach(()=>{
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
it("devices", (done) => {
navigator.mediaDevices.enumerateDevices()
.then(function(devices)
{
expect(devices).not.toBeNull();
devices.forEach(function(device) {
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
done();
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
fail();
});
});
it("devices2", (done) => {
let gStream;
let constraints = {video:{deviceId:undefined}, audio:{deviceId:undefined}} as MediaStreamConstraints;
navigator.mediaDevices.getUserMedia(constraints)
.then((stream)=>{
//if this stream stops the access to labels disapears again after
//a few ms (tested in firefox)
gStream = stream;
navigator.mediaDevices.enumerateDevices()
.then(function(devices)
{
expect(devices).not.toBeNull();
devices.forEach(function(device) {
expect(device.label).not.toBeNull();
expect(device.label).not.toBe("");
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
gStream.stop();
done();
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
fail();
});
})
.catch((err)=>{
console.log(err.name + ": " + err.message);
fail();
});
});
it("devices3", (done) => {
let gStream;
let constraints = {video: true, audio:false} as MediaStreamConstraints;
navigator.mediaDevices.getUserMedia(constraints)
.then((stream)=>{
//if this stream stops the access to labels disapears again after
//a few ms (tested in firefox)
gStream = stream;
navigator.mediaDevices.enumerateDevices()
.then(function(devices)
{
expect(devices).not.toBeNull();
devices.forEach(function(device) {
expect(device.label).not.toBeNull();
expect(device.label).not.toBe("");
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
gStream.stop();
done();
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
fail();
});
})
.catch((err)=>{
console.log(err.name + ": " + err.message);
fail();
});
});
});
/*
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 {ICall, NetworkConfig, ConnectionId,
MediaConfig, BrowserWebRtcCall, CallEventType,
DataMessageEventArgs, MessageEventArgs,
CallAcceptedEventArgs, CallEventArgs } from "../awrtc/index";
export class CallTestHelper
{
static CreateCall(video:boolean, audio: boolean) : ICall {
var nconfig = new NetworkConfig();
nconfig.SignalingUrl = "wss://signaling.because-why-not.com:443/test";
var call = new BrowserWebRtcCall(nconfig);
return call;
}
}
describe("CallTest", () => {
var originalTimeout;
beforeEach(() => {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;
});
afterEach(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL =originalTimeout;
});
it("CallTest normal", () => {
expect(true).toBe(true);
});
it("CallTest async", (done) => {
setTimeout(()=>{
expect(true).toBe(true);
done();
}, 1000);
});
it("Send test", (done) => {
var call1 : ICall = null;
var call2 : ICall = null;
let call1ToCall2:ConnectionId;
let call2ToCall1:ConnectionId;
var address = "webunittest";
var teststring1 = "teststring1";
var teststring2 = "teststring2";
var testdata1 = new Uint8Array([1, 2]);
var testdata2 = new Uint8Array([3, 4]);
call1 = CallTestHelper.CreateCall(false, false);
expect(call1).not.toBeNull();
call2 = CallTestHelper.CreateCall(false, false);
expect(call2).not.toBeNull();
expect(true).toBe(true);
var mconfig = new MediaConfig();
mconfig.Audio = false;
mconfig.Video = false;
call1.addEventListener((sender: any, args: CallEventArgs)=>{
if(args.Type == CallEventType.ConfigurationComplete)
{
console.debug("call1 ConfigurationComplete");
call2.Configure(mconfig);
}else if(args.Type == CallEventType.WaitForIncomingCall)
{
console.debug("call1 WaitForIncomingCall");
call2.Call(address);
}else if(args.Type == CallEventType.CallAccepted)
{
let ar = args as CallAcceptedEventArgs;
call1ToCall2 = ar.ConnectionId;
//wait for message
}else if(args.Type == CallEventType.Message)
{
console.debug("call1 Message");
var margs = args as MessageEventArgs;
expect(margs.Content).toBe(teststring1);
expect(margs.Reliable).toBe(true);
call1.Send(teststring2, false, call1ToCall2);
}else if(args.Type == CallEventType.DataMessage)
{
console.debug("call1 DataMessage");
var dargs = args as DataMessageEventArgs;
expect(dargs.Reliable).toBe(true);
var recdata = dargs.Content;
expect(testdata1[0]).toBe(recdata[0]);
expect(testdata1[1]).toBe(recdata[1]);
console.debug("call1 send DataMessage");
call1.SendData(testdata2, false, call1ToCall2)
}else{
console.error("unexpected event: " + args.Type);
expect(true).toBe(false);
}
});
call2.addEventListener((sender: any, args: CallEventArgs)=>{
if(args.Type == CallEventType.ConfigurationComplete)
{
console.debug("call2 ConfigurationComplete");
call1.Listen(address);
}else if(args.Type == CallEventType.CallAccepted)
{
let ar = args as CallAcceptedEventArgs;
call2ToCall1 = ar.ConnectionId;
expect(call2ToCall1).toBeDefined();
call2.Send(teststring1);
}else if(args.Type == CallEventType.Message)
{
console.debug("call2 Message");
var margs = args as MessageEventArgs;
expect(margs.Content).toBe(teststring2);
expect(margs.Reliable).toBe(false);
console.debug("call2 send DataMessage " + call2ToCall1.id);
call2.SendData(testdata1, true, call2ToCall1)
}else if(args.Type == CallEventType.DataMessage)
{
console.debug("call2 DataMessage");
var dargs = args as DataMessageEventArgs;
expect(dargs.Reliable).toBe(false);
var recdata = dargs.Content;
expect(testdata2[0]).toBe(recdata[0]);
expect(testdata2[1]).toBe(recdata[1]);
done();
}else{
console.error("unexpected event: " + args.Type);
expect(true).toBe(false);
}
});
setInterval(()=>{
call1.Update();
call2.Update();
}, 50);
call1.Configure(mconfig);
});
});
\ No newline at end of file
/*
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.
*/
//current setup needs to load everything as a module
import {DeviceApi, CAPI_DeviceApi_Update,
CAPI_DeviceApi_RequestUpdate, CAPI_DeviceApi_Devices_Length,
CAPI_DeviceApi_Devices_Get} from "../awrtc/index"
export function DeviceApiTest_export()
{
}
describe("DeviceApiTest", () => {
beforeEach(()=>{
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
DeviceApi.Reset();
});
function printall()
{
console.log("current DeviceApi.Devices:");
for(let k in DeviceApi.Devices)
{
let v = DeviceApi.Devices[k];
console.log(v.deviceId + " defaultLabel:" + v.defaultLabel + " label:" + v.label + " guessed:" + v.isLabelGuessed);
}
}
it("update", (done) => {
let update1complete = false;
let update2complete = false;
let deviceCount = 0;
expect(Object.keys(DeviceApi.Devices).length).toBe(0);
//first without device labels
let updatecall1 = ()=>{
expect(update1complete).toBe(false);
expect(update2complete).toBe(false);
console.debug("updatecall1");
printall();
let devices1 = DeviceApi.Devices;
deviceCount = Object.keys(devices1).length;
expect(deviceCount).toBeGreaterThan(0);
let key1 = Object.keys(devices1)[0];
expect(devices1[key1].label).toBe("videoinput 1");
expect(devices1[key1].isLabelGuessed).toBe(true);
if(deviceCount > 1)
{
let key2 = Object.keys(devices1)[1];
expect(devices1[key2].label).toBe("videoinput 2");
expect(devices1[key2].isLabelGuessed).toBe(true);
}
DeviceApi.RemOnChangedHandler(updatecall1);
//second call with device labels
let updatecall2 = ()=>{
console.debug("updatecall2");
printall();
//check if the handler work properly
expect(update1complete).toBe(true);
expect(update2complete).toBe(false);
//sadly can't simulate fixed device names for testing
let devices2 = DeviceApi.Devices;
expect(Object.keys(devices2).length).toBe(deviceCount);
let key2 = Object.keys(devices2)[0];
//should have original label now
expect(devices2[key1].label).not.toBe("videodevice 1");
//and not be guessed anymore
expect(devices2[key1].isLabelGuessed).toBe(false);
update2complete = true;
DeviceApi.Reset();
expect(Object.keys(DeviceApi.Devices).length).toBe(0);
done();
}
update1complete = true;
DeviceApi.AddOnChangedHandler(updatecall2);
DeviceApi.RequestUpdate();
};
DeviceApi.AddOnChangedHandler(updatecall1);
DeviceApi.Update();
});
it("capi_update", (done) => {
let update1complete = false;
let update2complete = false;
let deviceCount = 0;
expect(CAPI_DeviceApi_Devices_Length()).toBe(0);
CAPI_DeviceApi_Update();
setTimeout(()=>{
expect(CAPI_DeviceApi_Devices_Length()).not.toBe(0);
expect(CAPI_DeviceApi_Devices_Length()).toBe(Object.keys(DeviceApi.Devices).length);
let keys = Object.keys(DeviceApi.Devices);
let counter = 0;
for(let k of keys)
{
let expectedVal = DeviceApi.Devices[k].label;
let actual = CAPI_DeviceApi_Devices_Get(counter);
expect(actual).toBe(expectedVal);
counter++;
}
done();
}, 100);
});
});
/*
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 { LocalNetwork, IBasicNetwork } from "../awrtc/index";
import { IBasicNetworkTest } from "helper/IBasicNetworkTest";
export class LocalNetworkTest extends IBasicNetworkTest {
public setup(): void {
super.setup();
//special tests
}
public _CreateNetworkImpl(): IBasicNetwork {
return new LocalNetwork();
}
}
describe("LocalNetworkTest", () => {
it("TestEnvironment", () => {
expect(null).toBeNull();
});
var test = new LocalNetworkTest();
test.setup();
});
/*
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 { BrowserMediaNetwork, NetworkConfig, MediaConfig,
ConnectionId, MediaEvent, MediaEventType,
MediaConfigurationState, NetEventType } from "../awrtc/index";
export class MediaNetworkTest{
createdNetworks:Array<BrowserMediaNetwork> = [];
createDefault() : BrowserMediaNetwork
{
let netConfig = new NetworkConfig();
netConfig.SignalingUrl = null;
let createdNetwork = new BrowserMediaNetwork(netConfig);
this.createdNetworks.push(createdNetwork);
return createdNetwork;
}
public setup(): void {
beforeEach(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
})
afterEach(() => {
for(let net of this.createdNetworks)
net.Dispose();
this.createdNetworks = new Array<BrowserMediaNetwork>();
})
it("FrameUpdates", (done) => {
let mediaConfig = new MediaConfig();
let network = this.createDefault();
network.Configure(mediaConfig);
setInterval(()=>{
network.Update();
let localFrame = network.TryGetFrame(ConnectionId.INVALID);
if(localFrame != null)
{
expect(localFrame.Height).toBeGreaterThan(0);
expect(localFrame.Width).toBeGreaterThan(0);
expect(localFrame.Buffer).not.toBeNull();
done();
}
network.Flush();
}, 10);
});
it("MediaEvent", (done) => {
let mediaConfig = new MediaConfig();
let network = this.createDefault();
network.Configure(mediaConfig);
setInterval(()=>{
network.Update();
let evt : MediaEvent = null;
while((evt = network.DequeueMediaEvent()) != null)
{
expect(evt.EventType).toBe(MediaEventType.StreamAdded);
expect(evt.Args.videoHeight).toBeGreaterThan(0);
expect(evt.Args.videoWidth).toBeGreaterThan(0);
done();
}
network.Flush();
}, 10);
});
it("MediaEventRemote", (done) => {
let testaddress = "testaddress" + Math.random();
let sender = this.createDefault();
let receiver = this.createDefault();
let configureComplete = false;
let senderFrame = false;
let receiverFrame = false;
sender.Configure(new MediaConfig());
setInterval(()=>{
sender.Update();
receiver.Update();
if(configureComplete == false)
{
let state = sender.GetConfigurationState();
if(state == MediaConfigurationState.Successful)
{
configureComplete = true;
sender.StartServer(testaddress);
}else if(state == MediaConfigurationState.Failed)
{
fail();
}
}
let sndEvt = sender.Dequeue();
if(sndEvt != null)
{
console.log("sender event: " + sndEvt);
if(sndEvt.Type == NetEventType.ServerInitialized)
{
receiver.Connect(testaddress);
}
}
let recEvt = receiver.Dequeue();
if(recEvt != null)
{
console.log("receiver event: " + recEvt);
}
let evt : MediaEvent = null;
while((evt = sender.DequeueMediaEvent()) != null)
{
expect(evt.EventType).toBe(MediaEventType.StreamAdded);
expect(evt.Args.videoHeight).toBeGreaterThan(0);
expect(evt.Args.videoWidth).toBeGreaterThan(0);
senderFrame = true;
console.log("sender received first frame");
}
while((evt = receiver.DequeueMediaEvent()) != null)
{
expect(evt.EventType).toBe(MediaEventType.StreamAdded);
expect(evt.Args.videoHeight).toBeGreaterThan(0);
expect(evt.Args.videoWidth).toBeGreaterThan(0);
receiverFrame = true;
console.log("receiver received first frame");
}
sender.Flush();
receiver.Flush();
if(senderFrame && receiverFrame)
done();
}, 10);
});
}
}
describe("MediaNetworkTest", () => {
it("TestEnvironment", () => {
expect(null).toBeNull();
});
var test = new MediaNetworkTest();
test.setup();
});
/*
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 { WebsocketTest } from "WebsocketNetworkTest";
import { IBasicNetworkTest } from "helper/IBasicNetworkTest";
import { NetworkEvent, IBasicNetwork, NetEventType, WebsocketNetwork,
ConnectionId, SignalingConfig, LocalNetwork, WebRtcNetwork }
from "../awrtc/index";
export class WebRtcNetworkTest extends IBasicNetworkTest {
public static sUrl = 'ws://localhost:12776/test';
public static sUrlShared = 'ws://localhost:12776/testshared';
public static sDefaultIceServer = { urls: ["stun:stun.l.google.com:19302"] } as RTCIceServer;
private mUrl = WebsocketTest.sUrl;
//allows each test to overwrite the default behaviour
private mUseWebsockets = false;
//will set use websocket flag for each test
public static mAlwaysUseWebsockets = false;
public setup(): void {
beforeEach(() => {
this.mUrl = WebsocketTest.sUrl;
this.mUseWebsockets = WebRtcNetworkTest.mAlwaysUseWebsockets;
})
it("SharedAddress", (done) => {
//turn off websockets and use shared websockets for this test as local network doesn't support shared mode
this.mUseWebsockets = true;
this.mUrl = WebsocketTest.sUrlShared;
var sharedAddress = "sharedtestaddress";
var evt: NetworkEvent;
var net1: IBasicNetwork;
var net2: IBasicNetwork;
this.thenAsync((finished) => {
net1 = this._CreateNetwork();
net1.StartServer(sharedAddress);
this.waitForEvent(net1, finished);
});
this.thenAsync((finished) => {
evt = net1.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitialized);
net2 = this._CreateNetwork() as WebsocketNetwork;
net2.StartServer(sharedAddress);
this.waitForEvent(net2, finished);
});
this.thenAsync((finished) => {
evt = net2.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitialized);
this.waitForEvent(net1, finished);
});
this.thenAsync((finished) => {
evt = net1.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
this.waitForEvent(net2, finished);
});
this.then(() => {
evt = net2.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
done();
});
this.start();
});
//connect using only direct local connections (give no ice servers)
it("ConnectLocalOnly", (done) => {
var srv: IBasicNetwork;
var address: string;
var clt: IBasicNetwork;
var cltId: ConnectionId;
var evt: NetworkEvent;
this.thenAsync((finished) => {
srv = this._CreateNetwork();
this._CreateServerNetwork((rsrv, raddress) => {
srv = rsrv;
address = raddress;
finished();
});
});
this.thenAsync((finished) => {
clt = this._CreateNetwork();
cltId = clt.Connect(address);
this.waitForEvent(clt, finished);
});
this.then(() => {
evt = clt.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
expect(evt.ConnectionId.id).toBe(cltId.id);
});
this.thenAsync((finished) => {
this.waitForEvent(srv, finished);
});
this.then(() => {
evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
expect(evt.ConnectionId.id).not.toBe(ConnectionId.INVALID.id);
done();
});
this.start();
});
super.setup();
//special tests
}
public _CreateNetworkImpl(): IBasicNetwork {
let rtcConfig: RTCConfiguration = { iceServers: [WebRtcNetworkTest.sDefaultIceServer]};
var sigConfig: SignalingConfig;
if (this.mUseWebsockets) {
sigConfig = new SignalingConfig(new WebsocketNetwork(this.mUrl));
}
else {
sigConfig = new SignalingConfig(new LocalNetwork());
}
return new WebRtcNetwork(sigConfig, rtcConfig);
}
}
describe("WebRtcNetworkTest", () => {
it("TestEnvironment", () => {
expect(null).toBeNull();
});
var test = new WebRtcNetworkTest();
test.mDefaultWaitTimeout = 5000;
test.setup();
});
/*
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 { NetworkEvent, WebsocketNetwork, NetEventType,
WebsocketConnectionStatus, ConnectionId, IBasicNetwork }
from "../awrtc/index";
import { IBasicNetworkTest } from "helper/IBasicNetworkTest";
export class WebsocketTest extends IBasicNetworkTest {
//replace with valid url that has a server behind it
//public static sUrl = 'ws://localhost:12776/test';
//public static sUrlShared = 'ws://localhost:12776/testshared';
public static sUrl = 'ws://signaling.because-why-not.com';
public static sUrlShared = 'ws://signaling.because-why-not.com/testshared';
//any url to simulate offline server
public static sBadUrl = 'ws://localhost:13776';
private mUrl;
public setup(): void {
super.setup();
//special tests
beforeEach(() => {
this.mUrl = WebsocketTest.sUrl;
});
it("SharedAddress", (done) => {
this.mUrl = WebsocketTest.sUrlShared;
var sharedAddress = "sharedtestaddress";
var evt: NetworkEvent;
var net1: WebsocketNetwork;
var net2: WebsocketNetwork;
this.thenAsync((finished) => {
net1 = this._CreateNetwork() as WebsocketNetwork;
net1.StartServer(sharedAddress);
this.waitForEvent(net1, finished);
});
this.thenAsync((finished) => {
evt = net1.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitialized);
net2 = this._CreateNetwork() as WebsocketNetwork;
net2.StartServer(sharedAddress);
this.waitForEvent(net2, finished);
});
this.thenAsync((finished) => {
evt = net2.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitialized);
this.waitForEvent(net1, finished);
});
this.thenAsync((finished) => {
evt = net1.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
this.waitForEvent(net2, finished);
});
this.then(() => {
evt = net2.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
done();
});
this.start();
});
it("BadUrlStartServer", (done) => {
this.mUrl = WebsocketTest.sBadUrl;
var evt: NetworkEvent;
var srv: WebsocketNetwork;
this.thenAsync((finished) => {
srv = this._CreateNetwork() as WebsocketNetwork;
expect(srv.getStatus()).toBe(WebsocketConnectionStatus.NotConnected);
srv.StartServer();
expect(srv.getStatus()).toBe(WebsocketConnectionStatus.Connecting);
this.waitForEvent(srv, finished);
});
this.then(() => {
evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitFailed);
expect(srv.getStatus()).toBe(WebsocketConnectionStatus.NotConnected);
done();
});
this.start();
});
it("BadUrlConnect", (done) => {
this.mUrl = WebsocketTest.sBadUrl;
var evt: NetworkEvent;
var clt: WebsocketNetwork;
var cltId: ConnectionId;
this.thenAsync((finished) => {
clt = this._CreateNetwork() as WebsocketNetwork;
expect(clt.getStatus()).toBe(WebsocketConnectionStatus.NotConnected);
cltId = clt.Connect("invalid address");
expect(clt.getStatus()).toBe(WebsocketConnectionStatus.Connecting);
this.waitForEvent(clt, finished);
});
this.then(() => {
evt = clt.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ConnectionFailed);
expect(clt.getStatus()).toBe(WebsocketConnectionStatus.NotConnected);
done();
});
this.start();
});
it("WebsocketState", (done) => {
var srv: WebsocketNetwork;
var address: string;
var srvToCltId: ConnectionId;
var clt: WebsocketNetwork;
var cltToSrvId: ConnectionId;
var evt: NetworkEvent;
this.thenAsync((finished) => {
this._CreateServerClient((rsrv, raddress, rsrvToCltId, rclt, rcltToSrvId) => {
srv = rsrv as WebsocketNetwork;
address = raddress;
srvToCltId = rsrvToCltId;
clt = rclt as WebsocketNetwork;
cltToSrvId = rcltToSrvId;
finished();
});
});
this.thenAsync((finished) => {
//both should be connected
expect(srv.getStatus()).toBe(WebsocketConnectionStatus.Connected);
expect(clt.getStatus()).toBe(WebsocketConnectionStatus.Connected);
srv.Disconnect(srvToCltId);
this.waitForEvent(srv, finished);
});
this.thenAsync((finished) => {
evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.Disconnected);
this.waitForEvent(clt, finished);
});
this.thenAsync((finished) => {
evt = clt.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.Disconnected);
expect(cltToSrvId.id).toBe(evt.ConnectionId.id);
//after disconnect the client doesn't have any active connections -> expect disconnected
expect(srv.getStatus()).toBe(WebsocketConnectionStatus.Connected);
expect(clt.getStatus()).toBe(WebsocketConnectionStatus.NotConnected);
srv.StopServer();
this.waitForEvent(srv, finished);
});
this.thenAsync((finished) => {
evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerClosed);
expect(srv.getStatus()).toBe(WebsocketConnectionStatus.NotConnected);
srv.StartServer(address);
this.waitForEvent(srv, finished);
});
this.thenAsync((finished) => {
evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitialized);
this._Connect(srv, address, clt, (srvToCltIdOut, cltToSrvIdOut) => {
finished();
});
});
this.then(() => {
done();
});
this.start();
});
}
public _CreateNetworkImpl(): IBasicNetwork {
//let url = 'ws://because-why-not.com:12776';
return new WebsocketNetwork(this.mUrl);
}
}
describe("WebsocketNetworkTest", () => {
it("TestEnvironment", () => {
expect(null).toBeNull();
});
beforeEach(() => {
});
var test = new WebsocketTest();
test.setup();
});
/*
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 { IBasicNetwork, NetworkEvent, ConnectionId, NetEventType }
from "../../awrtc/network/index";
export interface Task {
(): void;
}
export interface AsyncTask {
(finished: Task): void;
}
export class TestTaskRunner {
public _toDoList = new Array<AsyncTask>();
public then(syncTask: Task) {
var wrap = (finished: Task) => {
syncTask();
finished();
};
this._toDoList.push(wrap);
}
public thenAsync(task: AsyncTask) {
this._toDoList.push(task);
}
public start() {
var task = this._toDoList.shift();
this._run(task);
}
public stop() {
}
private _run(task: AsyncTask): void {
task(() => {
if (this._toDoList.length > 0) {
setTimeout(() => {
this._run(this._toDoList.shift());
}, 1);
}
});
}
}
export abstract class BasicNetworkTestBase {
private mTestRunner = new TestTaskRunner();
private mCreatedNetworks = new Array<IBasicNetwork>();
public mDefaultWaitTimeout = 5000;
public abstract _CreateNetworkImpl(): IBasicNetwork;
public setup(): void {
beforeEach(() => {
this.mTestRunner.stop();
this.mTestRunner = new TestTaskRunner();
this.mCreatedNetworks = new Array<IBasicNetwork>();
});
}
public _CreateNetwork(): IBasicNetwork {
let net = this._CreateNetworkImpl();
this.mCreatedNetworks.push(net);
return net;
}
public then(syncTask: Task) {
this.mTestRunner.then(syncTask);
}
public thenAsync(task: AsyncTask) {
this.mTestRunner.thenAsync(task);
}
public start() {
this.mTestRunner.start();
}
//public waitForEvent(net: IBasicNetwork) {
// var wrap = (finished: Task) => {
// var timeout = 1000;
// var interval = 100;
// var intervalHandle;
// intervalHandle = setInterval(() => {
// this.UpdateAll();
// this.FlushAll();
// timeout -= interval;
// if (net.Peek() != null) {
// clearInterval(intervalHandle);
// finished();
// } else if (timeout <= 0) {
// clearInterval(intervalHandle);
// finished();
// }
// }, interval);
// };
// this.mTestRunner.thenAsync(wrap);
//}
public waitForEvent(net: IBasicNetwork, finished : Task, timeout?:number) {
if (timeout == null)
timeout = this.mDefaultWaitTimeout;
var interval = 50;
var intervalHandle;
intervalHandle = setInterval(() => {
this.UpdateAll();
this.FlushAll();
timeout -= interval;
if (net.Peek() != null) {
clearInterval(intervalHandle);
finished();
} else if (timeout <= 0) {
clearInterval(intervalHandle);
finished();
}
}, interval);
}
public UpdateAll(): void {
for (let v of this.mCreatedNetworks) {
v.Update();
}
}
public FlushAll(): void {
for (let v of this.mCreatedNetworks) {
v.Flush();
}
}
public ShutdownAll(): void {
for (let v of this.mCreatedNetworks) {
v.Shutdown();
}
this.mCreatedNetworks = new Array<IBasicNetwork>();
}
public _CreateServerNetwork(result: (IBasicNetwork, string) => void)
{
var srv = this._CreateNetwork();
srv.StartServer();
this.waitForEvent(srv, () => {
var evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitialized);
expect(evt.Info).not.toBeNull();
var address = evt.Info;
result(srv, address);
});
}
public _Connect(srv: IBasicNetwork, address: string, clt: IBasicNetwork, result: (srvToCltId: ConnectionId, cltToSrvId: ConnectionId) => void) {
var evt: NetworkEvent;
var cltToSrvId = clt.Connect(address);
var srvToCltId: ConnectionId;
this.waitForEvent(clt, () => {
evt = clt.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
expect(evt.ConnectionId.id).toBe(cltToSrvId.id);
this.waitForEvent(srv, () => {
evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
expect(evt.ConnectionId.id).not.toBe(ConnectionId.INVALID.id);
srvToCltId = evt.ConnectionId;
result(srvToCltId, cltToSrvId);
});
});
}
public _CreateServerClient(result: (srv: IBasicNetwork, address: string, srvToCltId: ConnectionId, clt: IBasicNetwork, cltToSrvId: ConnectionId) => void) {
let srv: IBasicNetwork;
let address: string;
let srvToCltId: ConnectionId;
let clt: IBasicNetwork;
let cltToSrvId: ConnectionId;
this._CreateServerNetwork((rsrv, raddress) => {
srv = rsrv;
address = raddress;
clt = this._CreateNetwork();
this._Connect(srv, address, clt, (rsrvToCltId, rcltToSrvId) => {
srvToCltId = rsrvToCltId;
cltToSrvId = rcltToSrvId;
result(srv, address, srvToCltId, clt, cltToSrvId);
});
});
}
}
\ No newline at end of file
/*
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.
*/
export * from "./LocalNetworkTest"
export * from "./WebRtcNetworkTest"
export * from "./WebsocketNetworkTest"
export * from "./CallTest"
export * from "./MediaNetworkTest"
export * from "./BrowserApiTest"
export * from "./DeviceApiTest"
{
"extends": "../awrtc/tsconfig_base",
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"target": "es5",
"module": "es2015",
"declaration": false,
"outDir": "../build/test",
"baseUrl": "."
},
"files": [
"./helper/BasicNetworkTestBase.ts",
"./helper/IBasicNetworkTest.ts",
"WebsocketNetworkTest.ts",
"WebRtcNetworkTest.ts",
"CallTest.ts",
"LocalNetworkTest.ts",
"MediaNetworkTest.ts"
]
}
/*
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.
*/
const path = require('path');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
//AWRTC bundle for unity / direct java script usage
function build_awrtc_config()
{
return {
entry: './src/awrtc/index.ts',
output: {
filename: 'awrtc.js',
path: path.resolve(__dirname, 'build/bundle'),
library: "awrtc",
libraryTarget: 'umd'
},
mode:"development",
devtool: "eval-source-map", //unity can't handle separate source maps
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
plugins: [new TsconfigPathsPlugin({ configFile: "./src/awrtc/tsconfig.json" })]
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader"
},
{
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader"
}
]
},
}
}
configAwrtcDebug = build_awrtc_config();
configAwrtcRelease = build_awrtc_config();
configAwrtcRelease.mode = "production";
configAwrtcRelease.output.filename = "awrtc.min.js";
configAwrtcRelease.devtool = false;
//jasmine unit test bundle
configTest =
{
entry: './src/test/test_entry.ts',
output: {
filename: 'test.js',
path: path.resolve(__dirname, 'build/bundle')
},
mode:"development",
devtool: "source-map",
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
plugins: [new TsconfigPathsPlugin({ configFile: "./src/test/tsconfig.json" })]
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader"
},
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
]
},
};
//bundle of awrtc + several example + test apps
function default_examples_config()
{
return {
entry: './src/apps/entry.ts',
output: {
filename: 'apps.js',
path: path.resolve(__dirname, 'build/bundle')
},
mode:"development",
devtool: "source-map",
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
plugins: [new TsconfigPathsPlugin({ configFile: "./src/apps/tsconfig.json" })]
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader"
},
{
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader"
}
]
},
}
}
examplesConfigDebug = default_examples_config();
examplesConfigRelease = default_examples_config();
examplesConfigRelease.mode = "production";
examplesConfigRelease.output.filename = "apps.min.js";
examplesConfigRelease.devtool = false;
module.exports =
[
configAwrtcDebug,
configAwrtcRelease,
configTest,
examplesConfigDebug,
examplesConfigRelease
];
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment