Commit 641b1d97 by Christoph

V1.985 + major videoinput update & optimization

* videoinput updated with optimization & scaling support & addcanvas support for c# * support copy video images to webgl textures * added a webgl rendering example based on mozillas webgl samples * V1.985 * updated dependencies
parent 2fba5d08
This source diff could not be displayed because it is too large. You can view the blob instead.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebGL Demo</title>
<link rel="stylesheet" href="../webgl.css" type="text/css">
</head>
<body>
<canvas id="glcanvas" width="640" height="480"></canvas>
</body>
<script src="../bundle/awrtc.js"></script>
<script src="gl-matrix.js"></script>
<script src="webgl-demo_changed.js"></script>
<script>
let canvas = document.querySelector("#glcanvas");
let nconfig = new awrtc.NetworkConfig();
let call = new awrtc.BrowserWebRtcCall(nconfig);
call.addEventListener((sender, args) => {
if(args.Type === awrtc.CallEventType.FrameUpdate)
{
let gl = canvas.getContext("webgl");
if(args.Frame.Width != globalTextureWidth || args.Frame.Height != globalTextureHeight)
{
const pixel = new Uint8Array(args.Frame.Width * args.Frame.Height * 3 );
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, args.Frame.Width, args.Frame.Height, 0, gl.RGB, gl.UNSIGNED_BYTE, pixel);
globalTextureWidth = args.Frame.Width ;
globalTextureHeight = args.Frame.Height ;
}
args.Frame.ToTexture(gl, globalTextureId);
}
});
//As the system is designed for realtime graphics we have to call the Update method. Events are only
//triggered during this Update call!
let intervalId = setInterval(() => {
call.Update();
}, 50);
let config = new awrtc.MediaConfig();
config.Audio = false;
config.Video = true;
config.FrameUpdates = true;
config.IdealWidth = 640;
config.IdealHeight = 480;
config.IdealFps = 30;
console.log("requested config:" + JSON.stringify(config));
call.Configure(config);
</script>
</html>
\ No newline at end of file
Slightly changed WebGL example from mozzila to test the frame copy from WebRTC -> VideoElement -> WebGL Texture
Source:
https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL
https://github.com/mdn/webgl-examples/tree/gh-pages/tutorial/sample6
\ No newline at end of file
var globalTextureId = -1;
var globalTextureWidth = -1;
var globalTextureHeight = -1;
var cubeRotation = 0.0;
main();
//
// Start here
//
function main() {
const canvas = document.querySelector('#glcanvas');
const gl = canvas.getContext('webgl');
// If we don't have a GL context, give up now
if (!gl) {
alert('Unable to initialize WebGL. Your browser or machine may not support it.');
return;
}
// Vertex shader program
const vsSource = `
attribute vec4 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying highp vec2 vTextureCoord;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
vTextureCoord = aTextureCoord;
}
`;
// Fragment shader program
const fsSource = `
varying highp vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void) {
gl_FragColor = texture2D(uSampler, vTextureCoord);
}
`;
// Initialize a shader program; this is where all the lighting
// for the vertices and so forth is established.
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
// Collect all the info needed to use the shader program.
// Look up which attributes our shader program is using
// for aVertexPosition, aTextureCoord and also
// look up uniform locations.
const programInfo = {
program: shaderProgram,
attribLocations: {
vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'),
},
uniformLocations: {
projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
uSampler: gl.getUniformLocation(shaderProgram, 'uSampler'),
},
};
// Here's where we call the routine that builds all the
// objects we'll be drawing.
const buffers = initBuffers(gl);
loadTexture(gl, 'cubetexture.png');
var then = 0;
// Draw the scene repeatedly
function render(now) {
now *= 0.001; // convert to seconds
const deltaTime = now - then;
then = now;
drawScene(gl, programInfo, buffers, globalTextureId, deltaTime);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
//
// initBuffers
//
// Initialize the buffers we'll need. For this demo, we just
// have one object -- a simple three-dimensional cube.
//
function initBuffers(gl) {
// Create a buffer for the cube's vertex positions.
const positionBuffer = gl.createBuffer();
// Select the positionBuffer as the one to apply buffer
// operations to from here out.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Now create an array of positions for the cube.
const positions = [
// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
];
// Now pass the list of positions into WebGL to build the
// shape. We do this by creating a Float32Array from the
// JavaScript array, then use it to fill the current buffer.
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// Now set up the texture coordinates for the faces.
const textureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
const textureCoordinates = [
// Front
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
// Back
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
// Top
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
// Bottom
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
// Right
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
// Left
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
gl.STATIC_DRAW);
// Build the element array buffer; this specifies the indices
// into the vertex arrays for each face's vertices.
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
// This array defines each face as two triangles, using the
// indices into the vertex array to specify each triangle's
// position.
const indices = [
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // back
8, 9, 10, 8, 10, 11, // top
12, 13, 14, 12, 14, 15, // bottom
16, 17, 18, 16, 18, 19, // right
20, 21, 22, 20, 22, 23, // left
];
// Now send the element array to GL
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
new Uint16Array(indices), gl.STATIC_DRAW);
return {
position: positionBuffer,
textureCoord: textureCoordBuffer,
indices: indexBuffer,
};
}
//
// Initialize a texture and load an image.
// When the image finished loading copy it into the texture.
//
function loadTexture(gl, url) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Because images have to be download over the internet
// they might take a moment until they are ready.
// Until then put a single pixel in the texture so we can
// use it immediately. When the image has finished downloading
// we'll update the texture with the contents of the image.
const level = 0;
const internalFormat = gl.RGBA;
const width = 1;
const height = 1;
const border = 0;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
width, height, border, srcFormat, srcType,
pixel);
const image = new Image();
image.onload = function() {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
srcFormat, srcType, image);
// WebGL1 has different requirements for power of 2 images
// vs non power of 2 images so check if the image is a
// power of 2 in both dimensions.
//if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
// // Yes, it's a power of 2. Generate mips.
// gl.generateMipmap(gl.TEXTURE_2D);
//} else {
// No, it's not a power of 2. Turn of mips and set
// wrapping to clamp to edge
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
//}
globalTextureWidth = image.width;
globalTextureHeight = image.height;
};
image.src = url;
globalTextureId = texture;
return texture;
}
function isPowerOf2(value) {
return (value & (value - 1)) == 0;
}
//
// Draw the scene.
//
function drawScene(gl, programInfo, buffers, texture, deltaTime) {
gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
// Clear the canvas before we start drawing on it.
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Create a perspective matrix, a special matrix that is
// used to simulate the distortion of perspective in a camera.
// Our field of view is 45 degrees, with a width/height
// ratio that matches the display size of the canvas
// and we only want to see objects between 0.1 units
// and 100 units away from the camera.
const fieldOfView = 45 * Math.PI / 180; // in radians
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.1;
const zFar = 100.0;
const projectionMatrix = mat4.create();
// note: glmatrix.js always has the first argument
// as the destination to receive the result.
mat4.perspective(projectionMatrix,
fieldOfView,
aspect,
zNear,
zFar);
// Set the drawing position to the "identity" point, which is
// the center of the scene.
const modelViewMatrix = mat4.create();
// Now move the drawing position a bit to where we want to
// start drawing the square.
mat4.translate(modelViewMatrix, // destination matrix
modelViewMatrix, // matrix to translate
[-0.0, 0.0, -6.0]); // amount to translate
mat4.rotate(modelViewMatrix, // destination matrix
modelViewMatrix, // matrix to rotate
cubeRotation, // amount to rotate in radians
[0, 0, 1]); // axis to rotate around (Z)
mat4.rotate(modelViewMatrix, // destination matrix
modelViewMatrix, // matrix to rotate
cubeRotation * .7,// amount to rotate in radians
[0, 1, 0]); // axis to rotate around (X)
// Tell WebGL how to pull out the positions from the position
// buffer into the vertexPosition attribute
{
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
programInfo.attribLocations.vertexPosition);
}
// Tell WebGL how to pull out the texture coordinates from
// the texture coordinate buffer into the textureCoord attribute.
{
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord);
gl.vertexAttribPointer(
programInfo.attribLocations.textureCoord,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
programInfo.attribLocations.textureCoord);
}
// Tell WebGL which indices to use to index the vertices
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
// Tell WebGL to use our program when drawing
gl.useProgram(programInfo.program);
// Set the shader uniforms
gl.uniformMatrix4fv(
programInfo.uniformLocations.projectionMatrix,
false,
projectionMatrix);
gl.uniformMatrix4fv(
programInfo.uniformLocations.modelViewMatrix,
false,
modelViewMatrix);
// Specify the texture to map onto the faces.
// Tell WebGL we want to affect texture unit 0
gl.activeTexture(gl.TEXTURE0);
// Bind the texture to texture unit 0
gl.bindTexture(gl.TEXTURE_2D, texture);
// Tell the shader we bound the texture to texture unit 0
gl.uniform1i(programInfo.uniformLocations.uSampler, 0);
{
const vertexCount = 36;
const type = gl.UNSIGNED_SHORT;
const offset = 0;
gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
}
// Update the rotation for the next draw
cubeRotation += deltaTime / 4;
}
//
// Initialize a shader program, so WebGL knows how to draw our data
//
function initShaderProgram(gl, vsSource, fsSource) {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
// Create the shader program
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
// If creating the shader program failed, alert
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
return null;
}
return shaderProgram;
}
//
// creates a shader of the given type, uploads the source and
// compiles it.
//
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
// Send the source to the shader object
gl.shaderSource(shader, source);
// Compile the shader program
gl.compileShader(shader);
// See if it compiled successfully
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
// Karma configuration
// Generated on Mon Jun 24 2019 19:59:32 GMT+1200 (New Zealand Standard Time)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'build/bundle/*.js'
],
// list of files / patterns to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['FirefoxCustom'],
customLaunchers: {
FirefoxCustom: {
base: 'Firefox',
prefs: {
'media.navigator.permission.disabled': true,
'media.navigator.streams.fake' : true
}
}
},
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
{ {
"name": "awrtc_browser", "name": "awrtc_browser",
"version": "0.984.4", "version": "0.985.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
"@types/jasmine": { "@types/jasmine": {
"version": "2.8.16", "version": "2.8.16",
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz",
...@@ -17,178 +23,177 @@ ...@@ -17,178 +23,177 @@
"dev": true "dev": true
}, },
"@webassemblyjs/ast": { "@webassemblyjs/ast": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
"integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/helper-module-context": "1.8.5", "@webassemblyjs/helper-module-context": "1.9.0",
"@webassemblyjs/helper-wasm-bytecode": "1.8.5", "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
"@webassemblyjs/wast-parser": "1.8.5" "@webassemblyjs/wast-parser": "1.9.0"
} }
}, },
"@webassemblyjs/floating-point-hex-parser": { "@webassemblyjs/floating-point-hex-parser": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz",
"integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-api-error": { "@webassemblyjs/helper-api-error": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
"integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-buffer": { "@webassemblyjs/helper-buffer": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
"integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-code-frame": { "@webassemblyjs/helper-code-frame": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz",
"integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/wast-printer": "1.8.5" "@webassemblyjs/wast-printer": "1.9.0"
} }
}, },
"@webassemblyjs/helper-fsm": { "@webassemblyjs/helper-fsm": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz",
"integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-module-context": { "@webassemblyjs/helper-module-context": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz",
"integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.9.0"
"mamacro": "^0.0.3"
} }
}, },
"@webassemblyjs/helper-wasm-bytecode": { "@webassemblyjs/helper-wasm-bytecode": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
"integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
"dev": true "dev": true
}, },
"@webassemblyjs/helper-wasm-section": { "@webassemblyjs/helper-wasm-section": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
"integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.9.0",
"@webassemblyjs/helper-buffer": "1.8.5", "@webassemblyjs/helper-buffer": "1.9.0",
"@webassemblyjs/helper-wasm-bytecode": "1.8.5", "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
"@webassemblyjs/wasm-gen": "1.8.5" "@webassemblyjs/wasm-gen": "1.9.0"
} }
}, },
"@webassemblyjs/ieee754": { "@webassemblyjs/ieee754": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
"integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@xtuc/ieee754": "^1.2.0" "@xtuc/ieee754": "^1.2.0"
} }
}, },
"@webassemblyjs/leb128": { "@webassemblyjs/leb128": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
"integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"@webassemblyjs/utf8": { "@webassemblyjs/utf8": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
"integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
"dev": true "dev": true
}, },
"@webassemblyjs/wasm-edit": { "@webassemblyjs/wasm-edit": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
"integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.9.0",
"@webassemblyjs/helper-buffer": "1.8.5", "@webassemblyjs/helper-buffer": "1.9.0",
"@webassemblyjs/helper-wasm-bytecode": "1.8.5", "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
"@webassemblyjs/helper-wasm-section": "1.8.5", "@webassemblyjs/helper-wasm-section": "1.9.0",
"@webassemblyjs/wasm-gen": "1.8.5", "@webassemblyjs/wasm-gen": "1.9.0",
"@webassemblyjs/wasm-opt": "1.8.5", "@webassemblyjs/wasm-opt": "1.9.0",
"@webassemblyjs/wasm-parser": "1.8.5", "@webassemblyjs/wasm-parser": "1.9.0",
"@webassemblyjs/wast-printer": "1.8.5" "@webassemblyjs/wast-printer": "1.9.0"
} }
}, },
"@webassemblyjs/wasm-gen": { "@webassemblyjs/wasm-gen": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
"integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.9.0",
"@webassemblyjs/helper-wasm-bytecode": "1.8.5", "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
"@webassemblyjs/ieee754": "1.8.5", "@webassemblyjs/ieee754": "1.9.0",
"@webassemblyjs/leb128": "1.8.5", "@webassemblyjs/leb128": "1.9.0",
"@webassemblyjs/utf8": "1.8.5" "@webassemblyjs/utf8": "1.9.0"
} }
}, },
"@webassemblyjs/wasm-opt": { "@webassemblyjs/wasm-opt": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
"integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.9.0",
"@webassemblyjs/helper-buffer": "1.8.5", "@webassemblyjs/helper-buffer": "1.9.0",
"@webassemblyjs/wasm-gen": "1.8.5", "@webassemblyjs/wasm-gen": "1.9.0",
"@webassemblyjs/wasm-parser": "1.8.5" "@webassemblyjs/wasm-parser": "1.9.0"
} }
}, },
"@webassemblyjs/wasm-parser": { "@webassemblyjs/wasm-parser": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
"integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.9.0",
"@webassemblyjs/helper-api-error": "1.8.5", "@webassemblyjs/helper-api-error": "1.9.0",
"@webassemblyjs/helper-wasm-bytecode": "1.8.5", "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
"@webassemblyjs/ieee754": "1.8.5", "@webassemblyjs/ieee754": "1.9.0",
"@webassemblyjs/leb128": "1.8.5", "@webassemblyjs/leb128": "1.9.0",
"@webassemblyjs/utf8": "1.8.5" "@webassemblyjs/utf8": "1.9.0"
} }
}, },
"@webassemblyjs/wast-parser": { "@webassemblyjs/wast-parser": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz",
"integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.9.0",
"@webassemblyjs/floating-point-hex-parser": "1.8.5", "@webassemblyjs/floating-point-hex-parser": "1.9.0",
"@webassemblyjs/helper-api-error": "1.8.5", "@webassemblyjs/helper-api-error": "1.9.0",
"@webassemblyjs/helper-code-frame": "1.8.5", "@webassemblyjs/helper-code-frame": "1.9.0",
"@webassemblyjs/helper-fsm": "1.8.5", "@webassemblyjs/helper-fsm": "1.9.0",
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"@webassemblyjs/wast-printer": { "@webassemblyjs/wast-printer": {
"version": "1.8.5", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
"integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.9.0",
"@webassemblyjs/wast-parser": "1.8.5", "@webassemblyjs/wast-parser": "1.9.0",
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
...@@ -215,9 +220,9 @@ ...@@ -215,9 +220,9 @@
} }
}, },
"acorn": { "acorn": {
"version": "6.4.0", "version": "6.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
"integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
"dev": true "dev": true
}, },
"after": { "after": {
...@@ -227,9 +232,9 @@ ...@@ -227,9 +232,9 @@
"dev": true "dev": true
}, },
"ajv": { "ajv": {
"version": "6.11.0", "version": "6.12.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
"integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
...@@ -342,6 +347,14 @@ ...@@ -342,6 +347,14 @@
"bn.js": "^4.0.0", "bn.js": "^4.0.0",
"inherits": "^2.0.1", "inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0" "minimalistic-assert": "^1.0.0"
},
"dependencies": {
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
}
} }
}, },
"assert": { "assert": {
...@@ -523,9 +536,9 @@ ...@@ -523,9 +536,9 @@
"dev": true "dev": true
}, },
"bn.js": { "bn.js": {
"version": "4.11.8", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.1.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", "integrity": "sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA==",
"dev": true "dev": true
}, },
"body-parser": { "body-parser": {
...@@ -636,21 +649,49 @@ ...@@ -636,21 +649,49 @@
"requires": { "requires": {
"bn.js": "^4.1.0", "bn.js": "^4.1.0",
"randombytes": "^2.0.1" "randombytes": "^2.0.1"
},
"dependencies": {
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
}
} }
}, },
"browserify-sign": { "browserify-sign": {
"version": "4.0.4", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.1.0.tgz",
"integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "integrity": "sha512-VYxo7cDCeYUoBZ0ZCy4UyEUCP3smyBd4DRQM5nrFS1jJjPJjX7rP3oLRpPoWfkhQfyJ0I9ZbHbKafrFD/SGlrg==",
"dev": true, "dev": true,
"requires": { "requires": {
"bn.js": "^4.1.1", "bn.js": "^5.1.1",
"browserify-rsa": "^4.0.0", "browserify-rsa": "^4.0.1",
"create-hash": "^1.1.0", "create-hash": "^1.2.0",
"create-hmac": "^1.1.2", "create-hmac": "^1.1.7",
"elliptic": "^6.0.0", "elliptic": "^6.5.2",
"inherits": "^2.0.1", "inherits": "^2.0.4",
"parse-asn1": "^5.0.0" "parse-asn1": "^5.1.5",
"readable-stream": "^3.6.0"
},
"dependencies": {
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
} }
}, },
"browserify-zlib": { "browserify-zlib": {
...@@ -673,28 +714,6 @@ ...@@ -673,28 +714,6 @@
"isarray": "^1.0.0" "isarray": "^1.0.0"
} }
}, },
"buffer-alloc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
"dev": true,
"requires": {
"buffer-alloc-unsafe": "^1.1.0",
"buffer-fill": "^1.0.0"
}
},
"buffer-alloc-unsafe": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
"dev": true
},
"buffer-fill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=",
"dev": true
},
"buffer-from": { "buffer-from": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
...@@ -720,9 +739,9 @@ ...@@ -720,9 +739,9 @@
"dev": true "dev": true
}, },
"cacache": { "cacache": {
"version": "12.0.3", "version": "12.0.4",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
"integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"bluebird": "^3.5.5", "bluebird": "^3.5.5",
...@@ -757,24 +776,9 @@ ...@@ -757,24 +776,9 @@
} }
}, },
"graceful-fs": { "graceful-fs": {
"version": "4.2.3", "version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"requires": {
"yallist": "^3.0.2"
}
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true "dev": true
} }
} }
...@@ -830,9 +834,9 @@ ...@@ -830,9 +834,9 @@
} }
}, },
"chokidar": { "chokidar": {
"version": "3.3.1", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
"integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"anymatch": "~3.1.1", "anymatch": "~3.1.1",
...@@ -842,7 +846,7 @@ ...@@ -842,7 +846,7 @@
"is-binary-path": "~2.1.0", "is-binary-path": "~2.1.0",
"is-glob": "~4.0.1", "is-glob": "~4.0.1",
"normalize-path": "~3.0.0", "normalize-path": "~3.0.0",
"readdirp": "~3.3.0" "readdirp": "~3.4.0"
}, },
"dependencies": { "dependencies": {
"braces": { "braces": {
...@@ -1088,6 +1092,14 @@ ...@@ -1088,6 +1092,14 @@
"requires": { "requires": {
"bn.js": "^4.1.0", "bn.js": "^4.1.0",
"elliptic": "^6.0.0" "elliptic": "^6.0.0"
},
"dependencies": {
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
}
} }
}, },
"create-hash": { "create-hash": {
...@@ -1272,6 +1284,14 @@ ...@@ -1272,6 +1284,14 @@
"bn.js": "^4.1.0", "bn.js": "^4.1.0",
"miller-rabin": "^4.0.0", "miller-rabin": "^4.0.0",
"randombytes": "^2.0.0" "randombytes": "^2.0.0"
},
"dependencies": {
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
}
} }
}, },
"dom-serialize": { "dom-serialize": {
...@@ -1323,6 +1343,14 @@ ...@@ -1323,6 +1343,14 @@
"inherits": "^2.0.1", "inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0", "minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.0" "minimalistic-crypto-utils": "^1.0.0"
},
"dependencies": {
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
}
} }
}, },
"emoji-regex": { "emoji-regex": {
...@@ -1687,9 +1715,9 @@ ...@@ -1687,9 +1715,9 @@
"dev": true "dev": true
}, },
"figgy-pudding": { "figgy-pudding": {
"version": "3.5.1", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
"integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
"dev": true "dev": true
}, },
"fill-range": { "fill-range": {
...@@ -1763,9 +1791,9 @@ ...@@ -1763,9 +1791,9 @@
} }
}, },
"flatted": { "flatted": {
"version": "2.0.1", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
"integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
"dev": true "dev": true
}, },
"flush-write-stream": { "flush-write-stream": {
...@@ -1779,9 +1807,9 @@ ...@@ -1779,9 +1807,9 @@
} }
}, },
"follow-redirects": { "follow-redirects": {
"version": "1.10.0", "version": "1.11.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.10.0.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz",
"integrity": "sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ==", "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^3.0.0" "debug": "^3.0.0"
...@@ -1868,9 +1896,9 @@ ...@@ -1868,9 +1896,9 @@
"dev": true "dev": true
}, },
"fsevents": { "fsevents": {
"version": "2.1.2", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"dev": true, "dev": true,
"optional": true "optional": true
}, },
...@@ -1910,9 +1938,9 @@ ...@@ -1910,9 +1938,9 @@
} }
}, },
"glob-parent": { "glob-parent": {
"version": "5.1.0", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
"integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-glob": "^4.0.1" "is-glob": "^4.0.1"
...@@ -2021,13 +2049,39 @@ ...@@ -2021,13 +2049,39 @@
} }
}, },
"hash-base": { "hash-base": {
"version": "3.0.4", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
"integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
"dev": true, "dev": true,
"requires": { "requires": {
"inherits": "^2.0.1", "inherits": "^2.0.4",
"safe-buffer": "^5.0.1" "readable-stream": "^3.6.0",
"safe-buffer": "^5.2.0"
},
"dependencies": {
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
"dev": true
}
} }
}, },
"hash.js": { "hash.js": {
...@@ -2328,13 +2382,10 @@ ...@@ -2328,13 +2382,10 @@
"dev": true "dev": true
}, },
"isbinaryfile": { "isbinaryfile": {
"version": "3.0.3", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.6.tgz",
"integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", "integrity": "sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg==",
"dev": true, "dev": true
"requires": {
"buffer-alloc": "^1.2.0"
}
}, },
"isexe": { "isexe": {
"version": "2.0.0", "version": "2.0.0",
...@@ -2401,12 +2452,11 @@ ...@@ -2401,12 +2452,11 @@
} }
}, },
"karma": { "karma": {
"version": "4.4.1", "version": "5.0.5",
"resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", "resolved": "https://registry.npmjs.org/karma/-/karma-5.0.5.tgz",
"integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", "integrity": "sha512-Q4Su7kNwkTgqS+KbSCYgH0p4a/0JIxVLyp7qKNV7vgPNhIF4kIoh0GlUfMKpw67BrR3hgPQSJoxgF7xnzUtPpg==",
"dev": true, "dev": true,
"requires": { "requires": {
"bluebird": "^3.3.0",
"body-parser": "^1.16.1", "body-parser": "^1.16.1",
"braces": "^3.0.2", "braces": "^3.0.2",
"chokidar": "^3.0.0", "chokidar": "^3.0.0",
...@@ -2418,22 +2468,37 @@ ...@@ -2418,22 +2468,37 @@
"glob": "^7.1.1", "glob": "^7.1.1",
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
"http-proxy": "^1.13.0", "http-proxy": "^1.13.0",
"isbinaryfile": "^3.0.0", "isbinaryfile": "^4.0.2",
"lodash": "^4.17.14", "lodash": "^4.17.14",
"log4js": "^4.0.0", "log4js": "^4.0.0",
"mime": "^2.3.1", "mime": "^2.3.1",
"minimatch": "^3.0.2", "minimatch": "^3.0.2",
"optimist": "^0.6.1",
"qjobs": "^1.1.4", "qjobs": "^1.1.4",
"range-parser": "^1.2.0", "range-parser": "^1.2.0",
"rimraf": "^2.6.0", "rimraf": "^2.6.0",
"safe-buffer": "^5.0.1",
"socket.io": "2.1.1", "socket.io": "2.1.1",
"source-map": "^0.6.1", "source-map": "^0.6.1",
"tmp": "0.0.33", "tmp": "0.0.33",
"useragent": "2.3.0" "ua-parser-js": "0.7.21",
"yargs": "^15.3.1"
}, },
"dependencies": { "dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
},
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"braces": { "braces": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
...@@ -2443,6 +2508,38 @@ ...@@ -2443,6 +2508,38 @@
"fill-range": "^7.0.1" "fill-range": "^7.0.1"
} }
}, },
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"fill-range": { "fill-range": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
...@@ -2452,18 +2549,78 @@ ...@@ -2452,18 +2549,78 @@
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
} }
}, },
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"is-number": { "is-number": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true "dev": true
}, },
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"requires": {
"p-locate": "^4.1.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"requires": {
"p-limit": "^2.2.0"
}
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
}, },
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
},
"to-regex-range": { "to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
...@@ -2472,6 +2629,36 @@ ...@@ -2472,6 +2629,36 @@
"requires": { "requires": {
"is-number": "^7.0.0" "is-number": "^7.0.0"
} }
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dev": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
},
"yargs": {
"version": "15.3.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
"integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
"dev": true,
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.1"
}
} }
} }
}, },
...@@ -2504,9 +2691,9 @@ ...@@ -2504,9 +2691,9 @@
} }
}, },
"kind-of": { "kind-of": {
"version": "6.0.2", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true "dev": true
}, },
"lazy-cache": { "lazy-cache": {
...@@ -2594,13 +2781,12 @@ ...@@ -2594,13 +2781,12 @@
"dev": true "dev": true
}, },
"lru-cache": { "lru-cache": {
"version": "4.1.5", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true, "dev": true,
"requires": { "requires": {
"pseudomap": "^1.0.2", "yallist": "^3.0.2"
"yallist": "^2.1.2"
} }
}, },
"make-dir": { "make-dir": {
...@@ -2613,12 +2799,6 @@ ...@@ -2613,12 +2799,6 @@
"semver": "^5.6.0" "semver": "^5.6.0"
} }
}, },
"mamacro": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz",
"integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==",
"dev": true
},
"map-age-cleaner": { "map-age-cleaner": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
...@@ -2710,27 +2890,35 @@ ...@@ -2710,27 +2890,35 @@
"requires": { "requires": {
"bn.js": "^4.0.0", "bn.js": "^4.0.0",
"brorand": "^1.0.1" "brorand": "^1.0.1"
},
"dependencies": {
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
}
} }
}, },
"mime": { "mime": {
"version": "2.4.4", "version": "2.4.5",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz",
"integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==",
"dev": true "dev": true
}, },
"mime-db": { "mime-db": {
"version": "1.43.0", "version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
"dev": true "dev": true
}, },
"mime-types": { "mime-types": {
"version": "2.1.26", "version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"dev": true, "dev": true,
"requires": { "requires": {
"mime-db": "1.43.0" "mime-db": "1.44.0"
} }
}, },
"mimic-fn": { "mimic-fn": {
...@@ -2761,9 +2949,9 @@ ...@@ -2761,9 +2949,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "0.0.10", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true "dev": true
}, },
"mississippi": { "mississippi": {
...@@ -2806,20 +2994,12 @@ ...@@ -2806,20 +2994,12 @@
} }
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "^1.2.5"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}
} }
}, },
"move-concurrently": { "move-concurrently": {
...@@ -3018,16 +3198,6 @@ ...@@ -3018,16 +3198,6 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"dev": true,
"requires": {
"minimist": "~0.0.1",
"wordwrap": "~0.0.2"
}
},
"os-browserify": { "os-browserify": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
...@@ -3210,9 +3380,9 @@ ...@@ -3210,9 +3380,9 @@
} }
}, },
"picomatch": { "picomatch": {
"version": "2.2.1", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"dev": true "dev": true
}, },
"pify": { "pify": {
...@@ -3260,12 +3430,6 @@ ...@@ -3260,12 +3430,6 @@
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true "dev": true
}, },
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
"dev": true
},
"public-encrypt": { "public-encrypt": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
...@@ -3278,6 +3442,14 @@ ...@@ -3278,6 +3442,14 @@
"parse-asn1": "^5.0.0", "parse-asn1": "^5.0.0",
"randombytes": "^2.0.1", "randombytes": "^2.0.1",
"safe-buffer": "^5.1.2" "safe-buffer": "^5.1.2"
},
"dependencies": {
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
}
} }
}, },
"pump": { "pump": {
...@@ -3396,12 +3568,12 @@ ...@@ -3396,12 +3568,12 @@
} }
}, },
"readdirp": { "readdirp": {
"version": "3.3.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
"integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"picomatch": "^2.0.7" "picomatch": "^2.2.1"
} }
}, },
"rechoir": { "rechoir": {
...@@ -3709,17 +3881,17 @@ ...@@ -3709,17 +3881,17 @@
}, },
"dependencies": { "dependencies": {
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true "dev": true
} }
} }
}, },
"signal-exit": { "signal-exit": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
"dev": true "dev": true
}, },
"snapdragon": { "snapdragon": {
...@@ -3969,9 +4141,9 @@ ...@@ -3969,9 +4141,9 @@
} }
}, },
"source-map-support": { "source-map-support": {
"version": "0.5.16", "version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true, "dev": true,
"requires": { "requires": {
"buffer-from": "^1.0.0", "buffer-from": "^1.0.0",
...@@ -4172,9 +4344,9 @@ ...@@ -4172,9 +4344,9 @@
"dev": true "dev": true
}, },
"terser": { "terser": {
"version": "4.6.3", "version": "4.6.13",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.13.tgz",
"integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", "integrity": "sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw==",
"dev": true, "dev": true,
"requires": { "requires": {
"commander": "^2.20.0", "commander": "^2.20.0",
...@@ -4345,9 +4517,9 @@ ...@@ -4345,9 +4517,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.5",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true "dev": true
} }
} }
...@@ -4364,9 +4536,9 @@ ...@@ -4364,9 +4536,9 @@
} }
}, },
"tslib": { "tslib": {
"version": "1.11.0", "version": "1.11.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.2.tgz",
"integrity": "sha512-BmndXUtiTn/VDDrJzQE7Mm22Ix3PxgLltW9bSNLoeCY31gnG2OPx0QqJnuc9oMIKioYrz487i6K9o4Pdn0j+Kg==", "integrity": "sha512-tTSkux6IGPnUGUd1XAZHcpu85MOkIl5zX49pO+jfsie3eP0B6pyhOlLXm3cAC6T7s+euSDDUUV+Acop5WmtkVg==",
"dev": true "dev": true
}, },
"tty-browserify": { "tty-browserify": {
...@@ -4392,9 +4564,15 @@ ...@@ -4392,9 +4564,15 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.8.2", "version": "3.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
"integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==", "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
"dev": true
},
"ua-parser-js": {
"version": "0.7.21",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
...@@ -4548,16 +4726,6 @@ ...@@ -4548,16 +4726,6 @@
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
"dev": true "dev": true
}, },
"useragent": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz",
"integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==",
"dev": true,
"requires": {
"lru-cache": "4.1.x",
"tmp": "0.0.x"
}
},
"util": { "util": {
"version": "0.11.1", "version": "0.11.1",
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
...@@ -4598,12 +4766,12 @@ ...@@ -4598,12 +4766,12 @@
"dev": true "dev": true
}, },
"watchpack": { "watchpack": {
"version": "1.6.0", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz",
"integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==",
"dev": true, "dev": true,
"requires": { "requires": {
"chokidar": "^2.0.2", "chokidar": "^2.1.8",
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
"neo-async": "^2.5.0" "neo-async": "^2.5.0"
}, },
...@@ -4656,560 +4824,11 @@ ...@@ -4656,560 +4824,11 @@
} }
}, },
"fsevents": { "fsevents": {
"version": "1.2.11", "version": "1.2.13",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"dev": true, "dev": true,
"optional": true, "optional": true
"requires": {
"node-pre-gyp": "*"
},
"dependencies": {
"abbrev": {
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
"bundled": true,
"dev": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
}
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"chownr": {
"version": "1.1.3",
"bundled": true,
"dev": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"debug": {
"version": "3.2.6",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ms": "^2.1.1"
}
},
"deep-extend": {
"version": "0.6.0",
"bundled": true,
"dev": true,
"optional": true
},
"delegates": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"detect-libc": {
"version": "1.0.3",
"bundled": true,
"dev": true,
"optional": true
},
"fs-minipass": {
"version": "1.2.7",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minipass": "^2.6.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"gauge": {
"version": "2.7.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.0",
"object-assign": "^4.1.0",
"signal-exit": "^3.0.0",
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wide-align": "^1.1.0"
}
},
"glob": {
"version": "7.1.6",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"has-unicode": {
"version": "2.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"iconv-lite": {
"version": "0.4.24",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-walk": {
"version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimatch": "^3.0.4"
}
},
"inflight": {
"version": "1.0.6",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"bundled": true,
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
"bundled": true,
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
},
"isarray": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true,
"optional": true
},
"minipass": {
"version": "2.9.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
},
"minizlib": {
"version": "1.3.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minipass": "^2.9.0"
}
},
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.1.2",
"bundled": true,
"dev": true,
"optional": true
},
"needle": {
"version": "2.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"debug": "^3.2.6",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
}
},
"node-pre-gyp": {
"version": "0.14.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4.4.2"
}
},
"nopt": {
"version": "4.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
}
},
"npm-bundled": {
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"npm-normalize-package-bin": "^1.0.1"
}
},
"npm-normalize-package-bin": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.4.7",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1"
}
},
"npmlog": {
"version": "4.1.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
"gauge": "~2.7.3",
"set-blocking": "~2.0.0"
}
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
"bundled": true,
"dev": true,
"optional": true
},
"once": {
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
},
"os-homedir": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"osenv": {
"version": "0.1.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
}
},
"path-is-absolute": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"process-nextick-args": {
"version": "2.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"rc": {
"version": "1.2.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"bundled": true,
"dev": true,
"optional": true
}
}
},
"readable-stream": {
"version": "2.3.6",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"rimraf": {
"version": "2.7.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"glob": "^7.1.3"
}
},
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
"bundled": true,
"dev": true,
"optional": true
},
"sax": {
"version": "1.2.4",
"bundled": true,
"dev": true,
"optional": true
},
"semver": {
"version": "5.7.1",
"bundled": true,
"dev": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"bundled": true,
"dev": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"string-width": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
}
},
"string_decoder": {
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "~5.1.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
},
"strip-json-comments": {
"version": "2.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"tar": {
"version": "4.4.13",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.8.6",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
}
},
"util-deprecate": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"wide-align": {
"version": "1.1.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"string-width": "^1.0.2 || 2"
}
},
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"yallist": {
"version": "3.1.1",
"bundled": true,
"dev": true,
"optional": true
}
}
}, },
"glob-parent": { "glob-parent": {
"version": "3.1.0", "version": "3.1.0",
...@@ -5255,16 +4874,16 @@ ...@@ -5255,16 +4874,16 @@
} }
}, },
"webpack": { "webpack": {
"version": "4.41.6", "version": "4.43.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.6.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz",
"integrity": "sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA==", "integrity": "sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.9.0",
"@webassemblyjs/helper-module-context": "1.8.5", "@webassemblyjs/helper-module-context": "1.9.0",
"@webassemblyjs/wasm-edit": "1.8.5", "@webassemblyjs/wasm-edit": "1.9.0",
"@webassemblyjs/wasm-parser": "1.8.5", "@webassemblyjs/wasm-parser": "1.9.0",
"acorn": "^6.2.1", "acorn": "^6.4.1",
"ajv": "^6.10.2", "ajv": "^6.10.2",
"ajv-keywords": "^3.4.1", "ajv-keywords": "^3.4.1",
"chrome-trace-event": "^1.0.2", "chrome-trace-event": "^1.0.2",
...@@ -5275,13 +4894,13 @@ ...@@ -5275,13 +4894,13 @@
"loader-utils": "^1.2.3", "loader-utils": "^1.2.3",
"memory-fs": "^0.4.1", "memory-fs": "^0.4.1",
"micromatch": "^3.1.10", "micromatch": "^3.1.10",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.3",
"neo-async": "^2.6.1", "neo-async": "^2.6.1",
"node-libs-browser": "^2.2.1", "node-libs-browser": "^2.2.1",
"schema-utils": "^1.0.0", "schema-utils": "^1.0.0",
"tapable": "^1.1.3", "tapable": "^1.1.3",
"terser-webpack-plugin": "^1.4.3", "terser-webpack-plugin": "^1.4.3",
"watchpack": "^1.6.0", "watchpack": "^1.6.1",
"webpack-sources": "^1.4.1" "webpack-sources": "^1.4.1"
}, },
"dependencies": { "dependencies": {
...@@ -5317,12 +4936,6 @@ ...@@ -5317,12 +4936,6 @@
"json5": "^1.0.1" "json5": "^1.0.1"
} }
}, },
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"tapable": { "tapable": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
...@@ -5356,6 +4969,12 @@ ...@@ -5356,6 +4969,12 @@
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true "dev": true
}, },
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"chalk": { "chalk": {
"version": "2.4.2", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
...@@ -5415,12 +5034,6 @@ ...@@ -5415,12 +5034,6 @@
"json5": "^1.0.1" "json5": "^1.0.1"
} }
}, },
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"supports-color": { "supports-color": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
...@@ -5448,6 +5061,16 @@ ...@@ -5448,6 +5061,16 @@
"y18n": "^4.0.0", "y18n": "^4.0.0",
"yargs-parser": "^13.1.0" "yargs-parser": "^13.1.0"
} }
},
"yargs-parser": {
"version": "13.1.2",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
} }
} }
}, },
...@@ -5562,9 +5185,9 @@ ...@@ -5562,9 +5185,9 @@
"dev": true "dev": true
}, },
"yallist": { "yallist": {
"version": "2.1.2", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true "dev": true
}, },
"yargs": { "yargs": {
...@@ -5580,9 +5203,9 @@ ...@@ -5580,9 +5203,9 @@
} }
}, },
"yargs-parser": { "yargs-parser": {
"version": "13.1.1", "version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"camelcase": "^5.0.0", "camelcase": "^5.0.0",
......
{ {
"name": "awrtc_browser", "name": "awrtc_browser",
"version": "0.984.4", "version": "1.985.0",
"description": "", "description": "",
"author": "because-why-not.com Limited", "author": "because-why-not.com Limited",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": {}, "dependencies": {},
"main": "build/awrtc/index.js", "main": "build/bundle/awrtc.js",
"types": "build/awrtc/index.d.ts", "types": "build/awrtc/index.d.ts",
"scripts": { "scripts": {
"tsc": "tsc", "tsc": "tsc",
...@@ -14,11 +14,19 @@ ...@@ -14,11 +14,19 @@
"watch": "webpack --watch", "watch": "webpack --watch",
"clean": "shx rm -rf ./build/awrtc ./build/bundle" "clean": "shx rm -rf ./build/awrtc ./build/bundle"
}, },
"files": [
"build",
"src"
],
"repository": {
"type": "git",
"url": "https://github.com/because-why-not/awrtc_browser"
},
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.8.16", "@types/jasmine": "^2.8.16",
"jasmine": "^2.99.0", "jasmine": "^2.99.0",
"jasmine-core": "^3.5.0", "jasmine-core": "^3.5.0",
"karma": "^4.4.1", "karma": "^5.0.5",
"karma-chrome-launcher": "^2.2.0", "karma-chrome-launcher": "^2.2.0",
"karma-firefox-launcher": "^1.3.0", "karma-firefox-launcher": "^1.3.0",
"karma-jasmine": "^2.0.1", "karma-jasmine": "^2.0.1",
...@@ -26,9 +34,9 @@ ...@@ -26,9 +34,9 @@
"source-map-loader": "^0.2.4", "source-map-loader": "^0.2.4",
"ts-loader": "^5.4.5", "ts-loader": "^5.4.5",
"tsconfig-paths-webpack-plugin": "^3.2.0", "tsconfig-paths-webpack-plugin": "^3.2.0",
"typescript": "^3.8.2", "typescript": "^3.8.3",
"uglify-js": "^2.8.29", "uglify-js": "^2.8.29",
"webpack": "^4.41.6", "webpack": "^4.43.0",
"webpack-cli": "^3.3.11", "webpack-cli": "^3.3.11",
"webrtc-adapter": "^6.4.8" "webrtc-adapter": "^6.4.8"
} }
......
...@@ -256,7 +256,6 @@ class MinimalCall ...@@ -256,7 +256,6 @@ class MinimalCall
//other. //other.
export function BrowserWebRtcCall_minimal() { export function BrowserWebRtcCall_minimal() {
awrtc.BrowserMediaStream.sUseLazyFrames = true;
let netConfig = new awrtc.NetworkConfig(); let netConfig = new awrtc.NetworkConfig();
netConfig.IsConference = false; netConfig.IsConference = false;
netConfig.SignalingUrl = DefaultValues.Signaling; netConfig.SignalingUrl = DefaultValues.Signaling;
......
...@@ -29,7 +29,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -29,7 +29,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
import * as awrtc from "../awrtc/index" import * as awrtc from "../awrtc/index"
import {DefaultValues, GetRandomKey} from "./apphelpers" import {DefaultValues, GetRandomKey} from "./apphelpers"
import { DeviceApi, DeviceInfo } from "../awrtc/index"; import { DeviceApi, DeviceInfo, BrowserMediaStream } from "../awrtc/index";
//This file only contains badly maintained //This file only contains badly maintained
//test apps. Use only experimentation. //test apps. Use only experimentation.
...@@ -387,26 +387,34 @@ class FpsCounter ...@@ -387,26 +387,34 @@ class FpsCounter
lastRefresh = 0; lastRefresh = 0;
fps = 0; fps = 0;
counter = 0; counter = 0;
isNew = false;
public get Fps() public get Fps()
{ {
return Math.round(this.fps); return Math.round(this.fps);
} }
public get Counter() public get IsNew() : boolean
{ {
return this.counter; if(this.isNew){
this.isNew = false;
return true;
}
return false;
} }
Update():void Update():void
{ {
this.counter++; this.counter++;
let diff = new Date().getTime() - this.lastRefresh; let diff = new Date().getTime() - this.lastRefresh;
if(diff > 1000)
let refresh_time = 2000;
if(diff > refresh_time)
{ {
this.fps = this.counter / (diff / 1000); this.fps = this.counter / (diff / 1000);
this.counter = 0; this.counter = 0;
this.lastRefresh = new Date().getTime(); this.lastRefresh = new Date().getTime();
this.isNew = true;
} }
} }
} }
...@@ -415,7 +423,7 @@ class FpsCounter ...@@ -415,7 +423,7 @@ class FpsCounter
//and accesses the resulting frame data directly //and accesses the resulting frame data directly
export function BrowserMediaNetwork_frameaccess() { export function BrowserMediaNetwork_frameaccess() {
//BrowserMediaStream.DEFAULT_FRAMERATE = 60;
//awrtc.BrowserMediaStream.DEBUG_SHOW_ELEMENTS = true; //awrtc.BrowserMediaStream.DEBUG_SHOW_ELEMENTS = true;
let address = GetRandomKey(); let address = GetRandomKey();
...@@ -427,8 +435,15 @@ export function BrowserMediaNetwork_frameaccess() { ...@@ -427,8 +435,15 @@ export function BrowserMediaNetwork_frameaccess() {
let network2 = new awrtc.BrowserMediaNetwork(networkConfig); let network2 = new awrtc.BrowserMediaNetwork(networkConfig);
let mediaConfig1 = new awrtc.MediaConfig(); let mediaConfig1 = new awrtc.MediaConfig();
mediaConfig1.Audio = true; mediaConfig1.Audio = false;
mediaConfig1.Video = true; mediaConfig1.Video = true;
/*
mediaConfig1.IdealWidth = 320;
mediaConfig1.IdealHeight = 240;
//fps seems to be ignored by browsers even if
//the camera specifically supports that setting
mediaConfig1.IdealFps = 15;
*/
let mediaConfig2 = new awrtc.MediaConfig(); let mediaConfig2 = new awrtc.MediaConfig();
mediaConfig2.Audio = false; mediaConfig2.Audio = false;
mediaConfig2.Video = false; mediaConfig2.Video = false;
...@@ -436,6 +451,7 @@ export function BrowserMediaNetwork_frameaccess() { ...@@ -436,6 +451,7 @@ export function BrowserMediaNetwork_frameaccess() {
let localFps = new FpsCounter(); let localFps = new FpsCounter();
let remoteFps = new FpsCounter(); let remoteFps = new FpsCounter();
let loopRate = new FpsCounter();
...@@ -466,15 +482,17 @@ export function BrowserMediaNetwork_frameaccess() { ...@@ -466,15 +482,17 @@ export function BrowserMediaNetwork_frameaccess() {
setInterval(() => { setInterval(() => {
network1.Update(); network1.Update();
loopRate.Update();
if(loopRate.IsNew)
console.log("Loop rate: " + loopRate.Fps);
let frame1: awrtc.IFrameData = null; let frame1: awrtc.IFrameData = null;
let frame2: awrtc.IFrameData = null; let frame2: awrtc.IFrameData = null;
frame1 = network1.TryGetFrame(awrtc.ConnectionId.INVALID); frame1 = network1.TryGetFrame(awrtc.ConnectionId.INVALID);
if (frame1 != null) if (frame1 != null)
{ {
localFps.Update(); localFps.Update();
if(localFps.Counter % 30 == 0) if(localFps.IsNew)
console.log("local1 width" + frame1.Width + " height:" + frame1.Height + "fps: " + localFps.Fps + " data:" + frame1.Buffer[0]); console.log("local1 width" + frame1.Width + " height:" + frame1.Height + "fps: " + localFps.Fps + " data:" + frame1.Buffer[0]);
} }
...@@ -515,7 +533,7 @@ export function BrowserMediaNetwork_frameaccess() { ...@@ -515,7 +533,7 @@ export function BrowserMediaNetwork_frameaccess() {
if (frame2 != null) if (frame2 != null)
{ {
remoteFps.Update(); remoteFps.Update();
if(remoteFps.Counter % 30 == 0) if(remoteFps.IsNew)
console.log("remote2 width" + frame2.Width + " height:" + frame2.Height + "fps: " + remoteFps.Fps + " data:" + frame2.Buffer[0]); console.log("remote2 width" + frame2.Width + " height:" + frame2.Height + "fps: " + remoteFps.Fps + " data:" + frame2.Buffer[0]);
} }
} }
......
import * as awrtc from "../awrtc/index" import * as awrtc from "../awrtc/index"
/** /**
* Copy of the CallApp to test custom video input * Copy of the CallApp to test custom video input
*/ */
export class VideoInputApp export class VideoInputApp {
{
public static sVideoDevice = null; public static sVideoDevice = null;
private mAddress; private mAddress;
private mNetConfig = new awrtc.NetworkConfig(); private mNetConfig = new awrtc.NetworkConfig();
private mCall : awrtc.BrowserWebRtcCall = null; private mCall: awrtc.BrowserWebRtcCall = null;
//update loop //update loop
private mIntervalId:any = -1; private mIntervalId: any = -1;
private mLocalVideo: HTMLVideoElement = null; private mLocalVideo: HTMLVideoElement = null;
private mRemoteVideo = {}; private mRemoteVideo = {};
private mIsRunning = false; private mIsRunning = false;
public constructor() public constructor() {
{ this.mNetConfig.IceServers = [
this.mNetConfig.IceServers = [ { urls: "stun:stun.because-why-not.com:443" },
{urls: "stun:stun.because-why-not.com:443"}, { urls: "stun:stun.l.google.com:19302" }
{urls: "stun:stun.l.google.com:19302"}
]; ];
//use for testing conferences //use for testing conferences
//this.mNetConfig.IsConference = true; //this.mNetConfig.IsConference = true;
...@@ -32,8 +29,8 @@ export class VideoInputApp ...@@ -32,8 +29,8 @@ export class VideoInputApp
this.mNetConfig.SignalingUrl = "wss://signaling.because-why-not.com/callapp"; this.mNetConfig.SignalingUrl = "wss://signaling.because-why-not.com/callapp";
} }
private GetParameterByName(name) { private GetParameterByName(name) {
var url = window.location.href; var url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&"); name = name.replace(/[\[\]]/g, "\\$&");
...@@ -44,25 +41,23 @@ export class VideoInputApp ...@@ -44,25 +41,23 @@ export class VideoInputApp
return ''; return '';
return decodeURIComponent(results[2].replace(/\+/g, " ")); return decodeURIComponent(results[2].replace(/\+/g, " "));
} }
private tobool(value, defaultval) private tobool(value, defaultval) {
{ if (value === true || value === "true")
if(value === true || value === "true")
return true; return true;
if(value === false || value === "false") if (value === false || value === "false")
return false; return false;
return defaultval; return defaultval;
} }
public Start(address, audio, video) : void
{
if(this.mCall != null)
public Start(address, audio, video): void {
if (this.mCall != null)
this.Stop(); this.Stop();
this.mIsRunning = true; this.mIsRunning = true;
this.Ui_OnStart() this.Ui_OnStart()
console.log("start"); console.log("start");
...@@ -75,11 +70,10 @@ export class VideoInputApp ...@@ -75,11 +70,10 @@ export class VideoInputApp
config.IdealWidth = 640; config.IdealWidth = 640;
config.IdealHeight = 480; config.IdealHeight = 480;
config.IdealFps = 30; config.IdealFps = 30;
if(VideoInputApp.sVideoDevice !== null) if (VideoInputApp.sVideoDevice !== null) {
{
config.VideoDeviceName = VideoInputApp.sVideoDevice; config.VideoDeviceName = VideoInputApp.sVideoDevice;
} }
//For usage in HTML set FrameUpdates to false and wait for MediaUpdate to //For usage in HTML set FrameUpdates to false and wait for MediaUpdate to
//get the VideoElement. By default awrtc would deliver frames individually //get the VideoElement. By default awrtc would deliver frames individually
//for use in Unity WebGL //for use in Unity WebGL
...@@ -89,7 +83,7 @@ export class VideoInputApp ...@@ -89,7 +83,7 @@ export class VideoInputApp
//handle events (get triggered after Configure / Listen call) //handle events (get triggered after Configure / Listen call)
//+ugly lambda to avoid loosing "this" reference //+ugly lambda to avoid loosing "this" reference
this.mCall.addEventListener((sender, args)=>{ this.mCall.addEventListener((sender, args) => {
this.OnNetworkEvent(sender, args); this.OnNetworkEvent(sender, args);
}); });
...@@ -97,8 +91,9 @@ export class VideoInputApp ...@@ -97,8 +91,9 @@ export class VideoInputApp
//As the system is designed for realtime graphics we have to call the Update method. Events are only //As the system is designed for realtime graphics we have to call the Update method. Events are only
//triggered during this Update call! //triggered during this Update call!
this.mIntervalId = setInterval(()=>{ this.mIntervalId = setInterval(() => {
this.Update(); this.Update();
}, 50); }, 50);
...@@ -111,20 +106,18 @@ export class VideoInputApp ...@@ -111,20 +106,18 @@ export class VideoInputApp
//Call mode -> If the address is free it will wait for someone else to connect //Call mode -> If the address is free it will wait for someone else to connect
// -> If the address is used then it will fail to listen and then try to connect via Call(address); // -> If the address is used then it will fail to listen and then try to connect via Call(address);
this.mCall.Listen(address); this.mCall.Listen(address);
} }
public Stop(): void public Stop(): void {
{
this.Cleanup(); this.Cleanup();
} }
private Cleanup():void{ private Cleanup(): void {
if(this.mCall != null) if (this.mCall != null) {
{
this.mCall.Dispose(); this.mCall.Dispose();
this.mCall = null; this.mCall = null;
clearInterval(this.mIntervalId); clearInterval(this.mIntervalId);
...@@ -136,34 +129,33 @@ export class VideoInputApp ...@@ -136,34 +129,33 @@ export class VideoInputApp
this.Ui_OnCleanup(); this.Ui_OnCleanup();
} }
private Update():void private Update(): void {
{ if (this.mCall != null)
if(this.mCall != null)
this.mCall.Update(); this.mCall.Update();
} }
private OnNetworkEvent(sender: any, args: awrtc.CallEventArgs):void{ private OnNetworkEvent(sender: any, args: awrtc.CallEventArgs): void {
//User gave access to requested camera/ microphone //User gave access to requested camera/ microphone
if (args.Type == awrtc.CallEventType.ConfigurationComplete){ if (args.Type == awrtc.CallEventType.ConfigurationComplete) {
console.log("configuration complete"); console.log("configuration complete");
} }
else if (args.Type == awrtc.CallEventType.MediaUpdate) { else if (args.Type == awrtc.CallEventType.MediaUpdate) {
let margs = args as awrtc.MediaUpdatedEventArgs; let margs = args as awrtc.MediaUpdatedEventArgs;
if (this.mLocalVideo == null && margs.ConnectionId == awrtc.ConnectionId.INVALID) { if (this.mLocalVideo == null && margs.ConnectionId == awrtc.ConnectionId.INVALID) {
var videoElement = margs.VideoElement; var videoElement = margs.VideoElement;
this.mLocalVideo = videoElement; this.mLocalVideo = videoElement;
this.Ui_OnLocalVideo(videoElement); this.Ui_OnLocalVideo(videoElement);
console.log("local video added resolution:" + videoElement.videoWidth + videoElement.videoHeight + " fps: ??"); console.log("local video added resolution:" + videoElement.videoWidth + videoElement.videoHeight + " fps: ??");
} }
else if (margs.ConnectionId != awrtc.ConnectionId.INVALID && this.mRemoteVideo[margs.ConnectionId.id] == null) { else if (margs.ConnectionId != awrtc.ConnectionId.INVALID && this.mRemoteVideo[margs.ConnectionId.id] == null) {
var videoElement = margs.VideoElement; var videoElement = margs.VideoElement;
this.mRemoteVideo[margs.ConnectionId.id] = videoElement; this.mRemoteVideo[margs.ConnectionId.id] = videoElement;
this.Ui_OnRemoteVideo(videoElement, margs.ConnectionId); this.Ui_OnRemoteVideo(videoElement, margs.ConnectionId);
console.log("remote video added resolution:" + videoElement.videoWidth + videoElement.videoHeight + " fps: ??"); console.log("remote video added resolution:" + videoElement.videoWidth + videoElement.videoHeight + " fps: ??");
} }
} }
else if (args.Type == awrtc.CallEventType.ListeningFailed) { else if (args.Type == awrtc.CallEventType.ListeningFailed) {
...@@ -198,8 +190,7 @@ export class VideoInputApp ...@@ -198,8 +190,7 @@ export class VideoInputApp
delete this.mRemoteVideo[callEndedEvent.ConnectionId.id]; delete this.mRemoteVideo[callEndedEvent.ConnectionId.id];
this.Ui_OnLog("Disconnected from user with id " + callEndedEvent.ConnectionId.id); this.Ui_OnLog("Disconnected from user with id " + callEndedEvent.ConnectionId.id);
//check if this was the last user //check if this was the last user
if(this.mNetConfig.IsConference == false && Object.keys(this.mRemoteVideo).length == 0) if (this.mNetConfig.IsConference == false && Object.keys(this.mRemoteVideo).length == 0) {
{
//1 to 1 call and only user left -> quit //1 to 1 call and only user left -> quit
this.Cleanup(); this.Cleanup();
return; return;
...@@ -240,15 +231,14 @@ export class VideoInputApp ...@@ -240,15 +231,14 @@ export class VideoInputApp
private mUiLocalVideoParent: HTMLElement; private mUiLocalVideoParent: HTMLElement;
private mUiRemoteVideoParent: HTMLElement; private mUiRemoteVideoParent: HTMLElement;
public setupUi(parent : HTMLElement) public setupUi(parent: HTMLElement) {
{
this.mUiAddress = parent.querySelector<HTMLInputElement>(".callapp_address"); this.mUiAddress = parent.querySelector<HTMLInputElement>(".callapp_address");
this.mUiAudio = parent.querySelector<HTMLInputElement>(".callapp_send_audio"); this.mUiAudio = parent.querySelector<HTMLInputElement>(".callapp_send_audio");
this.mUiVideo = parent.querySelector<HTMLInputElement>(".callapp_send_video"); this.mUiVideo = parent.querySelector<HTMLInputElement>(".callapp_send_video");
this.mUiUrl = parent.querySelector<HTMLParagraphElement>(".callapp_url"); this.mUiUrl = parent.querySelector<HTMLParagraphElement>(".callapp_url");
this.mUiButton = parent.querySelector<HTMLInputElement>(".callapp_button"); this.mUiButton = parent.querySelector<HTMLInputElement>(".callapp_button");
this.mUiLocalVideoParent = parent.querySelector<HTMLParagraphElement>(".callapp_local_video"); this.mUiLocalVideoParent = parent.querySelector<HTMLParagraphElement>(".callapp_local_video");
this.mUiRemoteVideoParent = parent.querySelector<HTMLParagraphElement>(".callapp_remote_video"); this.mUiRemoteVideoParent = parent.querySelector<HTMLParagraphElement>(".callapp_remote_video");
this.mUiAudio.onclick = this.Ui_OnUpdate; this.mUiAudio.onclick = this.Ui_OnUpdate;
this.mUiVideo.onclick = this.Ui_OnUpdate; this.mUiVideo.onclick = this.Ui_OnUpdate;
this.mUiAddress.onkeyup = this.Ui_OnUpdate; this.mUiAddress.onkeyup = this.Ui_OnUpdate;
...@@ -256,11 +246,11 @@ export class VideoInputApp ...@@ -256,11 +246,11 @@ export class VideoInputApp
//set default value + make string "true"/"false" to proper booleans //set default value + make string "true"/"false" to proper booleans
this.mAudio = this.GetParameterByName("audio"); this.mAudio = this.GetParameterByName("audio");
this.mAudio = this.tobool(this.mAudio , true) this.mAudio = this.tobool(this.mAudio, true)
this.mVideo = this.GetParameterByName("video"); this.mVideo = this.GetParameterByName("video");
this.mVideo = this.tobool(this.mVideo , true); this.mVideo = this.tobool(this.mVideo, true);
this.mAutostart = this.GetParameterByName("autostart"); this.mAutostart = this.GetParameterByName("autostart");
this.mAutostart = this.tobool(this.mAutostart, false); this.mAutostart = this.tobool(this.mAutostart, false);
this.mAddress = this.GetParameterByName("a"); this.mAddress = this.GetParameterByName("a");
...@@ -271,11 +261,10 @@ export class VideoInputApp ...@@ -271,11 +261,10 @@ export class VideoInputApp
this.mAddress = this.GenerateRandomKey(); this.mAddress = this.GenerateRandomKey();
window.location.href = this.GetUrlParams(); window.location.href = this.GetUrlParams();
} }
else else {
{ if (this.mAddress === null)
if(this.mAddress === null)
this.mAddress = this.GenerateRandomKey(); this.mAddress = this.GenerateRandomKey();
this.Ui_Update(); this.Ui_Update();
} }
//used for interacting with the Unity CallApp //used for interacting with the Unity CallApp
...@@ -285,74 +274,70 @@ export class VideoInputApp ...@@ -285,74 +274,70 @@ export class VideoInputApp
//Lazy frames will be the default soon though //Lazy frames will be the default soon though
if(this.mAutostart) if (this.mAutostart) {
{
console.log("Starting automatically ... ") console.log("Starting automatically ... ")
this.Start(this.mAddress, this.mAudio , this.mVideo ); this.Start(this.mAddress, this.mAudio, this.mVideo);
} }
console.log("address: " + this.mAddress + " audio: " + this.mAudio + " video: " + this.mVideo + " autostart: " + this.mAutostart); console.log("address: " + this.mAddress + " audio: " + this.mAudio + " video: " + this.mVideo + " autostart: " + this.mAutostart);
} }
private Ui_OnStart(){ private Ui_OnStart() {
this.mUiButton.textContent = "Stop"; this.mUiButton.textContent = "Stop";
} }
private Ui_OnCleanup() private Ui_OnCleanup() {
{
this.mUiButton.textContent = "Join"; this.mUiButton.textContent = "Join";
while (this.mUiLocalVideoParent.hasChildNodes()) { while (this.mUiLocalVideoParent.hasChildNodes()) {
this.mUiLocalVideoParent.removeChild(this.mUiLocalVideoParent.firstChild); this.mUiLocalVideoParent.removeChild(this.mUiLocalVideoParent.firstChild);
} }
while (this.mUiRemoteVideoParent.hasChildNodes()) { while (this.mUiRemoteVideoParent.hasChildNodes()) {
this.mUiRemoteVideoParent.removeChild(this.mUiRemoteVideoParent.firstChild); this.mUiRemoteVideoParent.removeChild(this.mUiRemoteVideoParent.firstChild);
} }
} }
private Ui_OnLog(msg:string){ private Ui_OnLog(msg: string) {
} }
private Ui_OnError(msg:string){ private Ui_OnError(msg: string) {
} }
private Ui_OnLocalVideo(video : HTMLVideoElement){ private Ui_OnLocalVideo(video: HTMLVideoElement) {
this.mUiLocalVideoParent.appendChild( document.createElement("br")); this.mUiLocalVideoParent.appendChild(document.createElement("br"));
this.mUiLocalVideoParent.appendChild(video); this.mUiLocalVideoParent.appendChild(video);
} }
private Ui_OnRemoteVideo(video : HTMLVideoElement, id: awrtc.ConnectionId){ private Ui_OnRemoteVideo(video: HTMLVideoElement, id: awrtc.ConnectionId) {
this.mUiRemoteVideoParent.appendChild( document.createElement("br")); this.mUiRemoteVideoParent.appendChild(document.createElement("br"));
this.mUiRemoteVideoParent.appendChild(new Text("connection " + id.id)); this.mUiRemoteVideoParent.appendChild(new Text("connection " + id.id));
this.mUiRemoteVideoParent.appendChild( document.createElement("br")); this.mUiRemoteVideoParent.appendChild(document.createElement("br"));
this.mUiRemoteVideoParent.appendChild(video); this.mUiRemoteVideoParent.appendChild(video);
} }
public Ui_OnStartStopButtonClicked = ()=>{ public Ui_OnStartStopButtonClicked = () => {
if(this.mIsRunning) { if (this.mIsRunning) {
this.Stop(); this.Stop();
}else{ } else {
this.Start(this.mAddress, this.mAudio, this.mVideo); this.Start(this.mAddress, this.mAudio, this.mVideo);
} }
} }
public Ui_OnUpdate = ()=> public Ui_OnUpdate = () => {
{
console.debug("OnUiUpdate"); console.debug("OnUiUpdate");
this.mAddress = this.mUiAddress.value; this.mAddress = this.mUiAddress.value;
this.mAudio = this.mUiAudio.checked; this.mAudio = this.mUiAudio.checked;
this.mVideo = this.mUiVideo.checked; this.mVideo = this.mUiVideo.checked;
this.mUiUrl.innerHTML = this.GetUrl(); this.mUiUrl.innerHTML = this.GetUrl();
} }
public Ui_Update() : void public Ui_Update(): void {
{
console.log("UpdateUi"); console.log("UpdateUi");
this.mUiAddress.value = this.mAddress; this.mUiAddress.value = this.mAddress;
this.mUiAudio.checked = this.mAudio ; this.mUiAudio.checked = this.mAudio;
this.mUiVideo.checked = this.mVideo ; this.mUiVideo.checked = this.mVideo;
this.mUiUrl.innerHTML = this.GetUrl(); this.mUiUrl.innerHTML = this.GetUrl();
} }
private GenerateRandomKey() { private GenerateRandomKey() {
var result = ""; var result = "";
for (var i = 0; i < 7; i++) { for (var i = 0; i < 7; i++) {
...@@ -361,7 +346,7 @@ export class VideoInputApp ...@@ -361,7 +346,7 @@ export class VideoInputApp
return result; return result;
} }
private GetUrlParams() { private GetUrlParams() {
return "?a=" + this.mAddress + "&audio=" + this.mAudio + "&video=" + this.mVideo + "&" + "autostart=" + true; return "?a=" + this.mAddress + "&audio=" + this.mAudio + "&video=" + this.mVideo + "&" + "autostart=" + true;
} }
private GetUrl() { private GetUrl() {
return location.protocol + '//' + location.host + location.pathname + this.GetUrlParams(); return location.protocol + '//' + location.host + location.pathname + this.GetUrlParams();
...@@ -369,12 +354,10 @@ export class VideoInputApp ...@@ -369,12 +354,10 @@ export class VideoInputApp
} }
export function videoinputapp(parent: HTMLElement, canvas: HTMLCanvasElement) export function videoinputapp(parent: HTMLElement, canvas: HTMLCanvasElement) {
{ let callApp: VideoInputApp;
let callApp : VideoInputApp;
console.log("init callapp"); console.log("init callapp");
if(parent == null) if (parent == null) {
{
console.log("parent was null"); console.log("parent was null");
parent = document.body; parent = document.body;
} }
...@@ -382,7 +365,11 @@ export function videoinputapp(parent: HTMLElement, canvas: HTMLCanvasElement) ...@@ -382,7 +365,11 @@ export function videoinputapp(parent: HTMLElement, canvas: HTMLCanvasElement)
callApp = new VideoInputApp(); callApp = new VideoInputApp();
const media = new awrtc.Media(); const media = new awrtc.Media();
const devname = "canvas"; const devname = "canvas";
awrtc.Media.SharedInstance.VideoInput.AddCanvasDevice(devname, canvas); awrtc.Media.SharedInstance.VideoInput.AddCanvasDevice(canvas, devname, canvas.width / 2, canvas.height / 2, 30);
setInterval(() => {
awrtc.Media.SharedInstance.VideoInput.UpdateFrame(devname);
}, 50);
VideoInputApp.sVideoDevice = devname; VideoInputApp.sVideoDevice = devname;
callApp.setupUi(parent); callApp.setupUi(parent);
......
...@@ -59,6 +59,15 @@ export class IFrameData { ...@@ -59,6 +59,15 @@ export class IFrameData {
} }
public constructor() { } public constructor() { }
public ToTexture(gl: WebGL2RenderingContext, texture: WebGLTexture) : boolean{
return false;
}
/*
public ToTexture2(gl: WebGL2RenderingContext) : WebGLTexture{
return null;
}
*/
} }
//Container for the raw bytes of the current frame + height and width. //Container for the raw bytes of the current frame + height and width.
...@@ -96,6 +105,10 @@ export class RawFrame extends IFrameData{ ...@@ -96,6 +105,10 @@ export class RawFrame extends IFrameData{
* only create a lazy frame which will delay the creation of the RawFrame until the user actually tries * only create a lazy frame which will delay the creation of the RawFrame until the user actually tries
* to access any data. * to access any data.
* Thus if the game slows down or the user doesn't access any data the expensive copy is avoided. * Thus if the game slows down or the user doesn't access any data the expensive copy is avoided.
*
* This comes with the downside of risking a change in Width / Height at the moment. In theory the video could
* change the resolution causing the values of Width / Height to change over time before Buffer is accessed to create
* a copy that will be save to use. This should be ok as long as the frame is used at the time it is received.
*/ */
export class LazyFrame extends IFrameData{ export class LazyFrame extends IFrameData{
...@@ -113,20 +126,42 @@ export class LazyFrame extends IFrameData{ ...@@ -113,20 +126,42 @@ export class LazyFrame extends IFrameData{
return this.mRawFrame.Buffer; return this.mRawFrame.Buffer;
} }
/**Returns the expected width of the frame.
* Watch out this might change inbetween frames!
*
*/
public get Width(): number { public get Width(): number {
if (this.mRawFrame == null)
{
return this.mFrameGenerator.VideoElement.videoWidth;
}else{
return this.mRawFrame.Width;
}
/*
this.GenerateFrame(); this.GenerateFrame();
if (this.mRawFrame == null) if (this.mRawFrame == null)
return -1; return -1;
return this.mRawFrame.Width; return this.mRawFrame.Width;
*/
} }
/**Returns the expected height of the frame.
* Watch out this might change inbetween frames!
*
*/
public get Height(): number { public get Height(): number {
if (this.mRawFrame == null)
{
return this.mFrameGenerator.VideoElement.videoHeight;
}else{
return this.mRawFrame.Height;
}
/*
this.GenerateFrame(); this.GenerateFrame();
if (this.mRawFrame == null) if (this.mRawFrame == null)
return -1; return -1;
return this.mRawFrame.Height; return this.mRawFrame.Height;
*/
} }
...@@ -135,6 +170,37 @@ export class LazyFrame extends IFrameData{ ...@@ -135,6 +170,37 @@ export class LazyFrame extends IFrameData{
this.mFrameGenerator = frameGenerator; this.mFrameGenerator = frameGenerator;
} }
/**Intendet for use via the Unity plugin.
* Will copy the image directly into a texture to avoid overhead of a CPU side copy.
*
* The given texture should have the correct size before calling this method.
*
* @param gl
* @param texture
*/
public ToTexture(gl: WebGL2RenderingContext, texture: WebGLTexture) : boolean{
gl.bindTexture(gl.TEXTURE_2D, texture);
/*
const level = 0;
const internalFormat = gl.RGBA;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, this.mFrameGenerator.VideoElement);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
*/
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGB, gl.UNSIGNED_BYTE, this.mFrameGenerator.VideoElement);
return true;
}
/*
public ToTexture2(gl: WebGL2RenderingContext) : WebGLTexture{
let tex = gl.createTexture()
this.ToTexture(gl, tex)
return;
}
*/
//Called before access of any frame data triggering the creation of the raw frame data //Called before access of any frame data triggering the creation of the raw frame data
private GenerateFrame() { private GenerateFrame() {
......
...@@ -99,7 +99,6 @@ export class BrowserMediaNetwork extends WebRtcNetwork implements IMediaNetwork ...@@ -99,7 +99,6 @@ export class BrowserMediaNetwork extends WebRtcNetwork implements IMediaNetwork
{ {
let promise : Promise<MediaStream> = null; let promise : Promise<MediaStream> = null;
promise = Media.SharedInstance.getUserMedia(config); promise = Media.SharedInstance.getUserMedia(config);
promise.then((stream) => { //user gave permission promise.then((stream) => { //user gave permission
......
...@@ -31,6 +31,32 @@ import { IFrameData, RawFrame, LazyFrame } from "../media/RawFrame"; ...@@ -31,6 +31,32 @@ import { IFrameData, RawFrame, LazyFrame } from "../media/RawFrame";
import { SLog } from "../network/Helper"; import { SLog } from "../network/Helper";
/**
* Mostly used for debugging at the moment. Browser API doesn't seem to have a standard way to
* determine if a frame was updated. This class currently uses several different methods based
* on availability
*
*/
enum FrameEventMethod{
/**We use a set default framerate. FPS is unknown and we can't recognize if a frame was updated.
* Used for remote video tracks on firefox as the "framerate" property will not be set.
*/
DEFAULT_FALLBACK = "DEFAULT_FALLBACK",
/**
* Using the tracks meta data to decide the framerate. We might drop frames or deliver them twice
* because we can't tell when exactly they are updated.
* Some video devices also claim 30 FPS but generate less causing us to waste performance copying the same image
* multipel times
*
* This system works with local video in firefox
*/
TRACK = "TRACK",
/**
* uses frame numbers returned by the browser. This works for webkit based browsers only so far.
* Firefox is either missing the needed properties or they return always 0
*/
EXACT = "EXACT"
}
/**Internal use only. /**Internal use only.
* Bundles all functionality related to MediaStream, Tracks and video processing. * Bundles all functionality related to MediaStream, Tracks and video processing.
...@@ -46,15 +72,14 @@ export class BrowserMediaStream { ...@@ -46,15 +72,14 @@ export class BrowserMediaStream {
//for debugging. Will attach the HTMLVideoElement used to play the local and remote //for debugging. Will attach the HTMLVideoElement used to play the local and remote
//video streams to the document. //video streams to the document.
public static DEBUG_SHOW_ELEMENTS = false; public static DEBUG_SHOW_ELEMENTS = false;
//TODO: remove this flag. it is now always using lazy frames
public static sUseLazyFrames = true;
//Gives each FrameBuffer and its HTMLVideoElement a fixed id for debugging purposes. //Gives each FrameBuffer and its HTMLVideoElement a fixed id for debugging purposes.
public static sNextInstanceId = 1; public static sNextInstanceId = 1;
public static VERBOSE = false;
private mStream: MediaStream; private mStream: MediaStream;
...@@ -74,12 +99,15 @@ export class BrowserMediaStream { ...@@ -74,12 +99,15 @@ export class BrowserMediaStream {
//Framerate used as a workaround if //Framerate used as a workaround if
//the actual framerate is unknown due to browser restrictions //the actual framerate is unknown due to browser restrictions
public static DEFAULT_FRAMERATE = 25; public static DEFAULT_FRAMERATE = 30;
private mMsPerFrame = 1.0 / BrowserMediaStream.DEFAULT_FRAMERATE * 1000; private mMsPerFrame = 1.0 / BrowserMediaStream.DEFAULT_FRAMERATE * 1000;
private mFrameRateKnown = false;
private mFrameEventMethod = FrameEventMethod.DEFAULT_FALLBACK;
//Time the last frame was generated //Time the last frame was generated
private mLastFrameTime = 0; private mLastFrameTime = 0;
private mNextFrameTime = 0;
/** Number of the last frame (not yet supported in all browsers) /** Number of the last frame (not yet supported in all browsers)
* if it remains at <= 0 then we just generate frames based on * if it remains at <= 0 then we just generate frames based on
...@@ -98,37 +126,56 @@ export class BrowserMediaStream { ...@@ -98,37 +126,56 @@ export class BrowserMediaStream {
this.mInstanceId = BrowserMediaStream.sNextInstanceId; this.mInstanceId = BrowserMediaStream.sNextInstanceId;
BrowserMediaStream.sNextInstanceId++; BrowserMediaStream.sNextInstanceId++;
if (this.mStream.getVideoTracks().length > 0) this.mMsPerFrame = 1.0 / BrowserMediaStream.DEFAULT_FRAMERATE * 1000;
{ this.mFrameEventMethod = FrameEventMethod.DEFAULT_FALLBACK;
this.mHasVideo = true;
let vtrack = this.mStream.getVideoTracks()[0];
let settings = vtrack.getSettings();
let fps = settings.frameRate;
if(fps)
{
this.mMsPerFrame = 1.0 / fps * 1000;
this.mFrameRateKnown = true;
}
}
this.SetupElements(); this.SetupElements();
} }
private CheckFrameRate():void private CheckFrameRate():void
{ {
//in chrome the track itself might miss the framerate but if(this.mVideoElement)
//we still know when it updates trough webkitDecodedFrameCount
if(this.mVideoElement && typeof (this.mVideoElement as any).webkitDecodedFrameCount !== "undefined")
{
this.mFrameRateKnown = true;
}
if(this.mFrameRateKnown === false)
{ {
//firefox and co won't tell us the FPS for remote stream if (this.mStream.getVideoTracks().length > 0)
SLog.LW("Framerate unknown. Using default framerate of " + BrowserMediaStream.DEFAULT_FRAMERATE); {
this.mHasVideo = true;
let vtrack = this.mStream.getVideoTracks()[0];
let settings = vtrack.getSettings();
let fps = settings.frameRate;
if(fps)
{
if(BrowserMediaStream.VERBOSE)
{
console.log("Track FPS: " + fps);
}
this.mMsPerFrame = 1.0 / fps * 1000;
this.mFrameEventMethod = FrameEventMethod.TRACK;
}
}
//try to get the video fps via the track
//fails on firefox if the track comes from a remote source
if(this.GetFrameNumber() != -1)
{
if(BrowserMediaStream.VERBOSE)
{
console.log("Get frame available.");
}
//browser returns exact frame information
this.mFrameEventMethod = FrameEventMethod.EXACT;
}
//failed to determine any frame rate. This happens on firefox with
//remote tracks
if(this.mFrameEventMethod === FrameEventMethod.DEFAULT_FALLBACK)
{
//firefox and co won't tell us the FPS for remote stream
SLog.LW("Framerate unknown for stream " + this.mInstanceId + ". Using default framerate of " + BrowserMediaStream.DEFAULT_FRAMERATE);
}
} }
} }
public SetupElements() {
private SetupElements() {
this.mVideoElement = this.SetupVideoElement(); this.mVideoElement = this.SetupVideoElement();
//TOOD: investigate bug here //TOOD: investigate bug here
...@@ -138,7 +185,7 @@ export class BrowserMediaStream { ...@@ -138,7 +185,7 @@ export class BrowserMediaStream {
//with 720p. (video device "BisonCam, NB Pro" on MSI laptop) //with 720p. (video device "BisonCam, NB Pro" on MSI laptop)
SLog.L("video element created. video tracks: " + this.mStream.getVideoTracks().length); SLog.L("video element created. video tracks: " + this.mStream.getVideoTracks().length);
this.mVideoElement.onloadedmetadata = (e) => { this.mVideoElement.onloadedmetadata = (e) => {
//console.log("onloadedmetadata");
//we might have shutdown everything by now already //we might have shutdown everything by now already
if(this.mVideoElement == null) if(this.mVideoElement == null)
return; return;
...@@ -162,7 +209,12 @@ export class BrowserMediaStream { ...@@ -162,7 +209,12 @@ export class BrowserMediaStream {
this.CheckFrameRate(); this.CheckFrameRate();
SLog.L("Resolution: " + this.mVideoElement.videoWidth + "x" + this.mVideoElement.videoHeight); let video_log = "Resolution: " + this.mVideoElement.videoWidth + "x" + this.mVideoElement.videoHeight
+ " fps method: " + this.mFrameEventMethod + " " + Math.round(1000/(this.mMsPerFrame));
SLog.L(video_log);
if(BrowserMediaStream.VERBOSE){
console.log(video_log)
}
//now create canvas after the meta data of the video are known //now create canvas after the meta data of the video are known
if (this.mHasVideo) { if (this.mHasVideo) {
this.mCanvasElement = this.SetupCanvas(); this.mCanvasElement = this.SetupCanvas();
...@@ -199,26 +251,42 @@ export class BrowserMediaStream { ...@@ -199,26 +251,42 @@ export class BrowserMediaStream {
let frameNumber; let frameNumber;
if(this.mVideoElement) if(this.mVideoElement)
{ {
//to find out if we got a new frame if((this.mVideoElement as any).webkitDecodedFrameCount)
//chrome has webkitDecodedFrameCount {
//firefox mozDecodedFrames, mozParsedFrames, mozPresentedFrames seems to be always 0 so far frameNumber = (this.mVideoElement as any).webkitDecodedFrameCount;
//mozPaintedFrames turned out useless as it only updates if the tag is visible }
//no idea about all others /*
// None of these work and future versions might return numbers that are only
frameNumber = (this.mVideoElement as any).webkitDecodedFrameCount updated once a second or so. For now it is best to ignore these.
//|| this.mVideoElement.currentTime can't be used updates every call
|| -1; TODO: Check if any of these will work in the future. this.mVideoElement.getVideoPlaybackQuality().totalVideoFrames;
might also help in the future (so far always 0)
this.mVideoElement.currentTime also won't work because this is updated faster than the framerate (would result in >100+ framerate)
else if((this.mVideoElement as any).mozParsedFrames)
{
frameNumber = (this.mVideoElement as any).mozParsedFrames;
}else if((this.mVideoElement as any).mozDecodedFrames)
{
frameNumber = (this.mVideoElement as any).mozDecodedFrames;
}else if((this.mVideoElement as any).decodedFrameCount)
{
frameNumber = (this.mVideoElement as any).decodedFrameCount;
}
*/
else
{
frameNumber = -1;
}
}else{ }else{
frameNumber = -1; frameNumber = -1;
} }
return frameNumber; return frameNumber;
} }
//TODO: Buffering
public TryGetFrame(): IFrameData public TryGetFrame(): IFrameData
{ {
//make sure we get the newest frame //make sure we get the newest frame
this.EnsureLatestFrame(); //this.EnsureLatestFrame();
//remove the buffered frame if any //remove the buffered frame if any
var result = this.mBufferedFrame; var result = this.mBufferedFrame;
...@@ -230,7 +298,7 @@ export class BrowserMediaStream { ...@@ -230,7 +298,7 @@ export class BrowserMediaStream {
this.mVideoElement.muted = mute; this.mVideoElement.muted = mute;
} }
public PeekFrame(): IFrameData { public PeekFrame(): IFrameData {
this.EnsureLatestFrame(); //this.EnsureLatestFrame();
return this.mBufferedFrame; return this.mBufferedFrame;
} }
...@@ -240,7 +308,7 @@ export class BrowserMediaStream { ...@@ -240,7 +308,7 @@ export class BrowserMediaStream {
private EnsureLatestFrame():boolean private EnsureLatestFrame():boolean
{ {
if (this.HasNewerFrame()) { if (this.HasNewerFrame()) {
this.FrameToBuffer(); this.GenerateFrame();
return true; return true;
} }
return false; return false;
...@@ -258,6 +326,7 @@ export class BrowserMediaStream { ...@@ -258,6 +326,7 @@ export class BrowserMediaStream {
{ {
if(this.mLastFrameNumber > 0) if(this.mLastFrameNumber > 0)
{ {
this.mFrameEventMethod = FrameEventMethod.EXACT;
//we are getting frame numbers. use those to //we are getting frame numbers. use those to
//check if we have a new one //check if we have a new one
if(this.GetFrameNumber() > this.mLastFrameNumber) if(this.GetFrameNumber() > this.mLastFrameNumber)
...@@ -268,10 +337,8 @@ export class BrowserMediaStream { ...@@ -268,10 +337,8 @@ export class BrowserMediaStream {
else else
{ {
//many browsers do not share the frame info //many browsers do not share the frame info
//so far we just generate 30 FPS as a work around
let now = new Date().getTime(); let now = new Date().getTime();
let div = now - this.mLastFrameTime; if (this.mNextFrameTime <= now) {
if (div >= this.mMsPerFrame) {
{ {
return true; return true;
} }
...@@ -284,8 +351,7 @@ export class BrowserMediaStream { ...@@ -284,8 +351,7 @@ export class BrowserMediaStream {
public Update(): void { public Update(): void {
//moved to avoid creating buffered frames if not needed this.EnsureLatestFrame();
//this.EnsureLatestFrame();
} }
public DestroyCanvas(): void { public DestroyCanvas(): void {
...@@ -319,11 +385,12 @@ export class BrowserMediaStream { ...@@ -319,11 +385,12 @@ export class BrowserMediaStream {
this.mCanvasElement.width = this.mVideoElement.videoWidth; this.mCanvasElement.width = this.mVideoElement.videoWidth;
this.mCanvasElement.height = this.mVideoElement.videoHeight; this.mCanvasElement.height = this.mVideoElement.videoHeight;
let ctx = this.mCanvasElement.getContext("2d"); let ctx = this.mCanvasElement.getContext("2d");
/*
var fillBackgroundFirst = true; var fillBackgroundFirst = true;
if (fillBackgroundFirst) { if (fillBackgroundFirst) {
ctx.clearRect(0, 0, this.mCanvasElement.width, this.mCanvasElement.height); ctx.clearRect(0, 0, this.mCanvasElement.width, this.mCanvasElement.height);
} }
*/
ctx.drawImage(this.mVideoElement, 0, 0); ctx.drawImage(this.mVideoElement, 0, 0);
try { try {
...@@ -359,10 +426,21 @@ export class BrowserMediaStream { ...@@ -359,10 +426,21 @@ export class BrowserMediaStream {
} }
} }
private FrameToBuffer(): void //Old buffed frame was replaced with a wrapepr that avoids buffering internally
//Only point of generate frame is now to ensure a consistent framerate
private GenerateFrame(): void
{ {
this.mLastFrameTime = new Date().getTime();
this.mLastFrameNumber = this.GetFrameNumber(); this.mLastFrameNumber = this.GetFrameNumber();
let now = new Date().getTime();
//js timing is very inaccurate. reduce time until next frame if we are
//late with this one.
let diff = now - this.mNextFrameTime;
let delta = (this.mMsPerFrame - diff);
delta = Math.min(this.mMsPerFrame, Math.max(1, delta))
this.mLastFrameTime = now;
this.mNextFrameTime = now + delta;
//console.log("last frame , new frame", this.mLastFrameTime, this.mNextFrameTime, delta);
this.mBufferedFrame = new LazyFrame(this); this.mBufferedFrame = new LazyFrame(this);
} }
......
...@@ -352,9 +352,14 @@ export class DeviceApi ...@@ -352,9 +352,14 @@ export class DeviceApi
{ {
deviceId = DeviceApi.GetDeviceId(config.VideoDeviceName); deviceId = DeviceApi.GetDeviceId(config.VideoDeviceName);
SLog.L("using device " + config.VideoDeviceName); SLog.L("using device " + config.VideoDeviceName);
if(deviceId !== null) if(deviceId === "")
{ {
//SLog.L("using device id " + deviceId); //Workaround for Chrome 81: If no camera access is allowed chrome returns the deviceId ""
//thus we can only request any video device. We can't select a specific one
deviceId = null;
}else if(deviceId !== null)
{
//all good
} }
else{ else{
SLog.LE("Failed to find deviceId for label " + config.VideoDeviceName); SLog.LE("Failed to find deviceId for label " + config.VideoDeviceName);
......
...@@ -34,26 +34,35 @@ export class Media{ ...@@ -34,26 +34,35 @@ export class Media{
return real_devices.concat(virtual_devices); return real_devices.concat(virtual_devices);
} }
public static IsNameSet(videoDeviceName: string) : boolean{
if(videoDeviceName !== null && videoDeviceName !== "" )
{
return true;
}
return false;
}
public getUserMedia(config: MediaConfig): Promise<MediaStream>{ public getUserMedia(config: MediaConfig): Promise<MediaStream>{
if(config.VideoDeviceName !== null if(config.Video && Media.IsNameSet(config.VideoDeviceName)
&& config.VideoDeviceName !== ""
&& this.videoInput != null && this.videoInput != null
&& this.videoInput.HasDevice(config.VideoDeviceName)) && this.videoInput.HasDevice(config.VideoDeviceName))
{ {
return new Promise<MediaStream>((resolve, reject) => {
try{ let res = Promise.resolve().then(async ()=>{
const res :MediaStream = this.videoInput.GetStream(config.VideoDeviceName); let stream = this.videoInput.GetStream(config.VideoDeviceName);
resolve(res) if(config.Audio)
}catch(err)
{ {
reject(err); let constraints = {} as MediaStreamConstraints
} constraints.audio = true;
}); let audio_stream = await DeviceApi.getBrowserUserMedia(constraints);
stream.addTrack(audio_stream.getTracks()[0])
}
return stream;
})
return res;
} }
return DeviceApi.getAssetUserMedia(config); return DeviceApi.getAssetUserMedia(config);
......
interface CanvasMap{ /**TS version of the C++ / C# side Native VideoInput API
[key:string] : HTMLCanvasElement; *
} *
* In addition it also supports adding a HTMLCanvasElement as a video device. This can be
* a lot faster in the browser than the C / C++ style UpdateFrame methods that use raw byte arrays
* or pointers to deliver an image.
*
* Note there are currently three distinct ways how this is used:
* 1. Using AddCanvasDevice without scaling (wdith = 0, height =0 or the same as the canvas)
* In this mode the MediaStream will be returned from the canvas. Drawing calls from the canvas
* turn into video frames of the video without any manual UpdateFrame calls
*
* 2. Using AddCanvasDevice with scaling by setting a width / height different from the canvas.
* In this mode the user draws to the canvas and every time UpdateFrame is called a scaled frame
* is created that will turn into video frames. Lower UpdateFrame calls will reduce the framerate
* even if the original canvas us used a higher framerate.
* This mode should result in lower data usage.
*
* 3. Using AddDevice and UpdateFrame to deliver raw byte array frames. This is a compatibility mode
* that works similar to the C / C++ and C# API. An internal canvas is created and updated based on
* the data the user delivers. This mode makes sense if you generate custom data that doesn't have
* a canvas as its source.
* This mode can be quite slow and inefficient.
*
* TODO:
* - Using AddDevice with one resolution & UpdateFrame with another might not support scaling yet but
* activating the 2nd canvas for scaling might
* reduce the performance even more. Check if there is a better solution and if scaling is even needed.
* It could easily be added by calling initScaling but it must be known if scaling is required before
* the device is selected by the user. Given that scaling can reduce the performance doing so by default
* might cause problems for some users.
*
* - UpdateFrame rotation and firstRowIsBottom aren't supported yet. Looks like they aren't needed for
* WebGL anyway. Looks like frames here always start with the top line and rotation is automatically
* handled by the browser.
*
*/
export class VideoInput {
private canvasDevices: CanvasMap = {};
constructor() {
}
/**Adds a canvas to use as video source for streaming.
*
* Make sure canvas.getContext is at least called once before calling this method.
*
* @param canvas
* @param deviceName
* @param width
* @param height
* @param fps
*/
public AddCanvasDevice(canvas: HTMLCanvasElement, deviceName: string, width: number, height: number, fps: number) {
export class VideoInput{
private canvasDevices : CanvasMap = {};
constructor(){ let cdev = CanvasDevice.CreateExternal(canvas, fps);
if (width != canvas.width || height != canvas.height) {
//console.warn("testing scaling");
cdev.initScaling(width, height);
}
this.canvasDevices[deviceName] = cdev;
} }
/**For internal use.
public AddCanvasDevice(deviceName: string, canvas : HTMLCanvasElement){ * Allows to check if the device already exists.
this.canvasDevices[deviceName] = canvas; *
* @param dev
*/
public HasDevice(dev: string): boolean {
return dev in this.canvasDevices;
} }
public RemCanvasDevice(deviceName: string){ /**For internal use.
delete this.canvasDevices[deviceName]; * Lists all registered devices.
*
*/
public GetDeviceNames(): Array<string> {
return Object.keys(this.canvasDevices);
} }
public HasDevice(dev: string): boolean{ /**For internal use.
return dev in this.canvasDevices; * Returns a MediaStream for the given device.
} *
* @param dev
*/
public GetStream(dev: string): MediaStream | null {
if (this.HasDevice(dev)) {
let device = this.canvasDevices[dev];
public GetStream(dev: string) : MediaStream | null
{
if(this.HasDevice(dev)){
let canvas = this.canvasDevices[dev];
//watch out: This can trigger an exception if getContext has never been called before. //watch out: This can trigger an exception if getContext has never been called before.
//There doesn't seem to way to detect this beforehand though //There doesn't seem to way to detect this beforehand though
let stream = (canvas as any).captureStream(); let stream = device.captureStream();
return stream; return stream;
} }
return null; return null;
} }
public GetDeviceNames() : Array<string>{
return Object.keys(this.canvasDevices); /**C# API: public void AddDevice(string name, int width, int height, int fps);
*
* Adds a device that will be accessible via the given name. Width / Height determines
* the size of the canvas that is used to stream the video.
*
*
* @param name unique name for the canvas
* @param width width of the canvase used for the stream
* @param height height of the canvase used for the stream
* @param fps Expected FPS used by the stream. 0 or undefined to let the browser decide (likely based on actual draw calls)
*/
public AddDevice(name: string, width: number, height: number, fps?: number): void {
let cdev = CanvasDevice.CreateInternal(width, height, fps);
this.canvasDevices[name] = cdev;
}
private RemCanvasDevice(deviceName: string) {
let cdev = this.canvasDevices[deviceName];
if (cdev) {
delete this.canvasDevices[deviceName];
}
}
//C# API: public void RemoveDevice(string name);
public RemoveDevice(name: string): void {
this.RemCanvasDevice(name);
}
public UpdateFrame(name: string): boolean;
public UpdateFrame(name: string, dataPtr: Uint8ClampedArray, width: number, height: number, type: VideoInputType): boolean
public UpdateFrame(name: string, dataPtr: Uint8ClampedArray, width: number, height: number, type: VideoInputType, rotation: number, firstRowIsBottom: boolean): boolean
/**
* Use UpdateFrame with name only to trigger a new frame without changing the content (e.g. if AddCanvasDevice was used to add the device and it needs scaling)
* Use UpdateFrame with image data if you added the device via AddDevice and want to updat its content
*
*
*
* @param name name of the device
* @param dataPtr array to the image data
* @param width must be the exact width of the image in dataPtr
* @param height must be the exact height of the image in dataPtr
* @param type must be ARGB at the moment
* @param rotation not yet supported
* @param firstRowIsBottom not yet supported
*/
public UpdateFrame(name: string, dataPtr?: Uint8ClampedArray, width?: number, height?: number, type: VideoInputType = VideoInputType.ARGB, rotation: number = 0, firstRowIsBottom: boolean = true): boolean {
if (this.HasDevice(name)) {
let device = this.canvasDevices[name];
if (device.IsExternal() || dataPtr == null) {
//can't change external images / no data available. just generate a new frame without new data
device.UpdateFrame();
} else {
var data = new ImageData(dataPtr, width, height);
device.UpdateFrame(data);
}
return true;
}
return false;
}
}
interface FancyHTMLCanvasElement extends HTMLCanvasElement {
captureStream(fps?: number): MediaStream;
}
/**Wraps around a canvas object to use as a source for MediaStream.
* It supports streaming via a second canvas that is used to scale the image
* before streaming. For scaling UpdateFrame needs to be called one a frame.
* Without scaling the browser will detect changes in the original canvas
* and automatically update the stream
*
*/
class CanvasDevice {
/**Main canvas. This is actively drawn onto by the user (external)
* or by this class.
*
*/
private canvas: FancyHTMLCanvasElement;
/**false = we own the canvas and can change its settings e.g. via VideoInput
* true = externally used canvas. Can't change width / height or any other settings
*/
private external_canvas = false;
/** FPS used for the canvas captureStream.
* 0 or undefined to let the browser handle it automatically via captureStream()
*/
private fps?: number;
/**Canvas element to handle scaling.
* Remains null if initScaling is never called and width / height is expected to
* fit the canvas.
*
*/
private scaling_canvas: FancyHTMLCanvasElement = null;
//private scaling_interval = -1;
private is_capturing = false;
public getStreamingCanvas() {
if (this.scaling_canvas == null)
return this.canvas;
return this.scaling_canvas;
}
public captureStream() {
if (this.is_capturing == false && this.scaling_canvas) {
//scaling is active.
this.startScaling();
}
this.is_capturing = true;
if (this.fps && this.fps > 0) {
return this.getStreamingCanvas().captureStream(this.fps);
}
return this.getStreamingCanvas().captureStream();
}
private constructor(c: HTMLCanvasElement, external_canvas: boolean, fps?: number) {
this.canvas = c as FancyHTMLCanvasElement;
this.external_canvas = external_canvas;
this.fps = fps;
}
public static CreateInternal(width: number, height: number, fps?: number) {
const c = CanvasDevice.MakeCanvas(width, height);
return new CanvasDevice(c, false, fps);
}
public static CreateExternal(c: HTMLCanvasElement, fps?: number) {
return new CanvasDevice(c, true, fps);
}
/**Adds scaling support to this canvas device.
*
* @param width
* @param height
*/
public initScaling(width: number, height: number) {
this.scaling_canvas = document.createElement("canvas") as FancyHTMLCanvasElement;
this.scaling_canvas.width = width;
this.scaling_canvas.height = height;
this.scaling_canvas.getContext("2d");
}
/**Used to update the frame data if the canvas is managed internally.
* Use without image data to just trigger the scaling / generation of a new frame if the canvas is drawn to externally.
*
* If the canvas is managed externally and scaling is not required this method won't do anything. A new frame is instead
* generated automatically based on the browser & canvas drawing operations.
*/
public UpdateFrame(data?: ImageData): void {
if (data) {
let ctx = this.canvas.getContext("2d");
//TODO: This doesn't seem to support scaling out of the box
//we might need to combien this with the scaling system as well
//in case users deliver different resolutions than the device is setup for
ctx.putImageData(data, 0, 0);
}
this.scaleNow();
}
/**Called the first time we need the scaled image to ensure
* the buffers are all filled.
*/
private startScaling() {
this.scaleNow();
}
private scaleNow() {
if (this.scaling_canvas != null) {
let ctx = this.scaling_canvas.getContext("2d");
//ctx.fillStyle = "#FF0000";
//ctx.fillRect(0, 0, this.scaling_canvas.width, this.scaling_canvas.height);
//ctx.clearRect(0, 0, this.scaling_canvas.width, this.scaling_canvas.height)
ctx.drawImage(this.canvas, 0, 0, this.scaling_canvas.width, this.scaling_canvas.height);
}
} }
public IsExternal(): boolean {
return this.external_canvas;
}
private static MakeCanvas(width: number, height: number): FancyHTMLCanvasElement {
let canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext("2d");
//make red for debugging purposes
ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height);
return canvas as FancyHTMLCanvasElement;
}
}
interface CanvasMap {
[key: string]: CanvasDevice;
}
} /** Only one format supported by browsers so far.
\ No newline at end of file * Maybe more can be added in the future.
*/
export enum VideoInputType {
ARGB
}
...@@ -32,7 +32,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -32,7 +32,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
import {SLog, WebRtcNetwork, SignalingConfig, NetworkEvent, ConnectionId, LocalNetwork, WebsocketNetwork} from "../network/index" import {SLog, WebRtcNetwork, SignalingConfig, NetworkEvent, ConnectionId, LocalNetwork, WebsocketNetwork} from "../network/index"
import { MediaConfigurationState, NetworkConfig, MediaConfig } from "../media/index"; import { MediaConfigurationState, NetworkConfig, MediaConfig } from "../media/index";
import { BrowserMediaStream, BrowserMediaNetwork, DeviceApi, BrowserWebRtcCall, Media } from "../media_browser/index"; import { BrowserMediaStream, BrowserMediaNetwork, DeviceApi, BrowserWebRtcCall, Media, VideoInputType } from "../media_browser/index";
var CAPI_InitMode = { var CAPI_InitMode = {
...@@ -425,9 +425,6 @@ export function CAPI_MediaNetwork_TryGetFrame(lIndex: number, lConnectionId: num ...@@ -425,9 +425,6 @@ export function CAPI_MediaNetwork_TryGetFrame(lIndex: number, lConnectionId: num
if (frame == null || frame.Buffer == null) { if (frame == null || frame.Buffer == null) {
return false; return false;
} else { } else {
//TODO: copy frame over
lWidthInt32Array[lWidthIntArrayIndex] = frame.Width; lWidthInt32Array[lWidthIntArrayIndex] = frame.Width;
lHeightInt32Array[lHeightIntArrayIndex] = frame.Height; lHeightInt32Array[lHeightIntArrayIndex] = frame.Height;
...@@ -438,6 +435,61 @@ export function CAPI_MediaNetwork_TryGetFrame(lIndex: number, lConnectionId: num ...@@ -438,6 +435,61 @@ export function CAPI_MediaNetwork_TryGetFrame(lIndex: number, lConnectionId: num
} }
} }
export function CAPI_MediaNetwork_TryGetFrame_ToTexture(lIndex: number, lConnectionId: number,
lWidth: number,
lHeight: number,
gl:WebGL2RenderingContext, texture:WebGLTexture): boolean
{
//console.log("CAPI_MediaNetwork_TryGetFrame_ToTexture");
let mediaNetwork = gCAPI_WebRtcNetwork_Instances[lIndex] as BrowserMediaNetwork;
let frame = mediaNetwork.TryGetFrame(new ConnectionId(lConnectionId));
if (frame == null ) {
return false;
} else if (frame.Width != lWidth || frame.Height != lHeight) {
SLog.LW("CAPI_MediaNetwork_TryGetFrame_ToTexture failed. Width height expected: " + frame.Width + "x" + frame.Height + " but received " + lWidth + "x" + lHeight);
return false;
}else {
frame.ToTexture(gl, texture);
return true;
}
}
/*
export function CAPI_MediaNetwork_TryGetFrame_ToTexture2(lIndex: number, lConnectionId: number,
lWidthInt32Array: Int32Array, lWidthIntArrayIndex: number,
lHeightInt32Array: Int32Array, lHeightIntArrayIndex: number,
gl:WebGL2RenderingContext): WebGLTexture
{
//console.log("CAPI_MediaNetwork_TryGetFrame_ToTexture");
let mediaNetwork = gCAPI_WebRtcNetwork_Instances[lIndex] as BrowserMediaNetwork;
let frame = mediaNetwork.TryGetFrame(new ConnectionId(lConnectionId));
if (frame == null) {
return false;
} else {
lWidthInt32Array[lWidthIntArrayIndex] = frame.Width;
lHeightInt32Array[lHeightIntArrayIndex] = frame.Height;
let texture = frame.ToTexture2(gl);
return texture;
}
}
*/
export function CAPI_MediaNetwork_TryGetFrame_Resolution(lIndex: number, lConnectionId: number,
lWidthInt32Array: Int32Array, lWidthIntArrayIndex: number,
lHeightInt32Array: Int32Array, lHeightIntArrayIndex: number): boolean
{
let mediaNetwork = gCAPI_WebRtcNetwork_Instances[lIndex] as BrowserMediaNetwork;
let frame = mediaNetwork.PeekFrame(new ConnectionId(lConnectionId));
if (frame == null) {
return false;
} else {
lWidthInt32Array[lWidthIntArrayIndex] = frame.Width;
lHeightInt32Array[lHeightIntArrayIndex] = frame.Height;
return true;
}
}
//Returns the frame buffer size or -1 if no frame is available //Returns the frame buffer size or -1 if no frame is available
export function CAPI_MediaNetwork_TryGetFrameDataLength(lIndex: number, connectionId: number) : number { export function CAPI_MediaNetwork_TryGetFrameDataLength(lIndex: number, connectionId: number) : number {
let mediaNetwork = gCAPI_WebRtcNetwork_Instances[lIndex] as BrowserMediaNetwork; let mediaNetwork = gCAPI_WebRtcNetwork_Instances[lIndex] as BrowserMediaNetwork;
...@@ -496,29 +548,11 @@ export function CAPI_DeviceApi_LastUpdate():number ...@@ -496,29 +548,11 @@ export function CAPI_DeviceApi_LastUpdate():number
{ {
return DeviceApi.LastUpdate; return DeviceApi.LastUpdate;
} }
/*
export function CAPI_DeviceApi_Devices_Length():number{
return Object.keys(DeviceApi.Devices).length;
}
export function CAPI_DeviceApi_Devices_Get(index:number):string{
let keys = Object.keys(DeviceApi.Devices);
if(keys.length > index)
{
let key = keys[index];
return DeviceApi.Devices[key].label;
}
else
{
SLog.LE("Requested device with index " + index + " does not exist.");
return "";
}
}
*/
export function CAPI_DeviceApi_Devices_Length():number{ export function CAPI_Media_GetVideoDevices_Length():number{
return Media.SharedInstance.GetVideoDevices().length; return Media.SharedInstance.GetVideoDevices().length;
} }
export function CAPI_DeviceApi_Devices_Get(index:number):string{ export function CAPI_Media_GetVideoDevices(index:number):string{
const devs = Media.SharedInstance.GetVideoDevices(); const devs = Media.SharedInstance.GetVideoDevices();
if(devs.length > index) if(devs.length > index)
{ {
...@@ -530,4 +564,48 @@ export function CAPI_DeviceApi_Devices_Get(index:number):string{ ...@@ -530,4 +564,48 @@ export function CAPI_DeviceApi_Devices_Get(index:number):string{
//it needs to be "" to behave the same to the C++ API. std::string can't be null //it needs to be "" to behave the same to the C++ API. std::string can't be null
return ""; return "";
} }
} }
\ No newline at end of file
export function CAPI_VideoInput_AddCanvasDevice(query:string, name:string, width: number, height: number, fps: number): boolean{
let canvas = document.querySelector(query) as HTMLCanvasElement;
if(canvas){
console.debug("CAPI_VideoInput_AddCanvasDevice", {query, name, width, height, fps});
if(width <= 0 || height <= 0){
width = canvas.width;
height = canvas.height;
}
Media.SharedInstance.VideoInput.AddCanvasDevice(canvas as HTMLCanvasElement, name, width, height, fps);//, width, height, fps);
return true;
}
return false;
}
export function CAPI_VideoInput_AddDevice(name:string, width: number, height: number, fps: number){
Media.SharedInstance.VideoInput.AddDevice(name, width, height, fps);
}
export function CAPI_VideoInput_RemoveDevice(name:string){
Media.SharedInstance.VideoInput.RemoveDevice(name);
}
export function CAPI_VideoInput_UpdateFrame(name:string,
lBufferUint8Array: Uint8Array, lBufferUint8ArrayOffset: number, lBufferUint8ArrayLength: number,
width: number, height: number,
rotation: number, firstRowIsBottom: boolean) : boolean
{
let dataPtrClamped : Uint8ClampedArray = null;
if(lBufferUint8Array && lBufferUint8ArrayLength > 0){
dataPtrClamped = new Uint8ClampedArray(lBufferUint8Array.buffer, lBufferUint8ArrayOffset, lBufferUint8ArrayLength);
}
return Media.SharedInstance.VideoInput.UpdateFrame(name, dataPtrClamped, width, height, VideoInputType.ARGB, rotation, firstRowIsBottom);
}
//TODO: This needs a proper implementation
//so far only works if unity is the only canvas and uses webgl2
export function GetUnityCanvas() : HTMLCanvasElement
{
return document.querySelector("canvas");
}
export function GetUnityContext() : WebGL2RenderingContext
{
return GetUnityCanvas().getContext("webgl2");
}
...@@ -28,19 +28,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ...@@ -28,19 +28,6 @@ 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. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
import { Media } from "../media_browser/Media"; import { Media } from "../media_browser/Media";
import { GetUnityCanvas } from "./CAPI";
export * from "./CAPI" export * from "./CAPI"
//add the canvas to video input for testing.
//done via timeout to avoid returning possible errors to unity loading routine
setTimeout(()=>{
console.debug("trying to add canvas to video input");
const canvas = document.querySelector("canvas");
if(canvas)
{
Media.SharedInstance.VideoInput.AddCanvasDevice("canvas", canvas);
console.debug("Canvas added. Make sure to turn off unity local video if streaming from a canvas. Copying images from the canvas to Unity will heavily slow down the app!");
}else{
console.error("Adding canvas failed. No canvas found");
}
}, 10);
\ No newline at end of file
...@@ -29,8 +29,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -29,8 +29,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
//current setup needs to load everything as a module //current setup needs to load everything as a module
import {DeviceApi, CAPI_DeviceApi_Update, import {DeviceApi, CAPI_DeviceApi_Update,
CAPI_DeviceApi_RequestUpdate, CAPI_DeviceApi_Devices_Length, CAPI_DeviceApi_RequestUpdate, CAPI_Media_GetVideoDevices_Length,
CAPI_DeviceApi_Devices_Get, CAPI_Media_GetVideoDevices,
MediaConfig, MediaConfig,
Media} from "../awrtc/index" Media} from "../awrtc/index"
...@@ -134,11 +134,11 @@ describe("DeviceApiTest", () => { ...@@ -134,11 +134,11 @@ describe("DeviceApiTest", () => {
let update2complete = false; let update2complete = false;
let deviceCount = 0; let deviceCount = 0;
const devices_length_unitialized = CAPI_DeviceApi_Devices_Length(); const devices_length_unitialized = CAPI_Media_GetVideoDevices_Length();
expect(devices_length_unitialized).toBe(0); expect(devices_length_unitialized).toBe(0);
DeviceApi.AddOnChangedHandler(()=>{ DeviceApi.AddOnChangedHandler(()=>{
let dev_length = CAPI_DeviceApi_Devices_Length(); let dev_length = CAPI_Media_GetVideoDevices_Length();
expect(dev_length).not.toBe(0); expect(dev_length).not.toBe(0);
expect(dev_length).toBe(Object.keys(DeviceApi.Devices).length); expect(dev_length).toBe(Object.keys(DeviceApi.Devices).length);
...@@ -147,7 +147,7 @@ describe("DeviceApiTest", () => { ...@@ -147,7 +147,7 @@ describe("DeviceApiTest", () => {
for(let k of keys) for(let k of keys)
{ {
let expectedVal = DeviceApi.Devices[k].label; let expectedVal = DeviceApi.Devices[k].label;
let actual = CAPI_DeviceApi_Devices_Get(counter); let actual = CAPI_Media_GetVideoDevices(counter);
expect(actual).toBe(expectedVal); expect(actual).toBe(expectedVal);
counter++; counter++;
...@@ -237,7 +237,7 @@ describe("DeviceApiTest", () => { ...@@ -237,7 +237,7 @@ describe("DeviceApiTest", () => {
expect(DeviceApi.GetVideoDevices().length).toBe(0); expect(DeviceApi.GetVideoDevices().length).toBe(0);
await DeviceApi.UpdateAsync(); await DeviceApi.UpdateAsync();
expect(DeviceApi.GetVideoDevices().length).toBeGreaterThan(0); expect(DeviceApi.GetVideoDevices().length).toBeGreaterThan(0);
expect(DeviceApi.GetVideoDevices().length).toBe(CAPI_DeviceApi_Devices_Length()); expect(DeviceApi.GetVideoDevices().length).toBe(CAPI_Media_GetVideoDevices_Length());
done(); done();
}); });
......
import { VideoInput, Media, DeviceApi, MediaConfig, CAPI_DeviceApi_Devices_Length, CAPI_DeviceApi_Devices_Get } from "../awrtc/index"; import { VideoInput, Media, DeviceApi, MediaConfig, CAPI_Media_GetVideoDevices_Length, CAPI_Media_GetVideoDevices, BrowserMediaStream, WaitForIncomingCallEventArgs } from "../awrtc/index";
import { MakeTestCanvas } from "VideoInputTest"; import { MakeTestCanvas } from "VideoInputTest";
export function MediaTest_export() export function MediaTest_export()
...@@ -69,7 +69,7 @@ describe("MediaTest", () => { ...@@ -69,7 +69,7 @@ describe("MediaTest", () => {
config.Video = true; config.Video = true;
const canvas = MakeTestCanvas(); const canvas = MakeTestCanvas();
media.VideoInput.AddCanvasDevice(name, canvas); media.VideoInput.AddCanvasDevice(canvas, name, canvas.width, canvas.height, 30);
const streamCamera = await media.getUserMedia(config); const streamCamera = await media.getUserMedia(config);
expect(streamCamera).not.toBeNull(); expect(streamCamera).not.toBeNull();
...@@ -88,22 +88,261 @@ describe("MediaTest", () => { ...@@ -88,22 +88,261 @@ describe("MediaTest", () => {
expect(streamCanvas2.getVideoTracks().length).toBe(1); expect(streamCanvas2.getVideoTracks().length).toBe(1);
done(); done();
}); });
it("GetUserMedia_videoinput_and_audio", async () => {
const name = "test_canvas";
const media = new Media();
const config = new MediaConfig();
config.Audio = true;
config.Video = true;
const canvas = MakeTestCanvas();
media.VideoInput.AddCanvasDevice(canvas, name, canvas.width, canvas.height, 30);
config.VideoDeviceName = name;
let stream : MediaStream = null;
try{
stream = await media.getUserMedia(config);
}catch(err){
console.error(err);
fail(err);
}
expect(stream).not.toBeNull();
expect(stream.getAudioTracks().length).toBe(1);
expect(stream.getVideoTracks().length).toBe(1);
config.VideoDeviceName = "invalid name";
stream = null;
let error_result : string = null
try{
stream = await media.getUserMedia(config);
}catch(err){
error_result = err;
}
expect(error_result).not.toBeNull();
expect(stream).toBeNull();
}, 15000);
//CAPI needs to be changed to use Media only instead the device API //CAPI needs to be changed to use Media only instead the device API
it("MediaCapiVideoInput", async (done) => { it("MediaCapiVideoInput", async (done) => {
//empty normal device api //empty normal device api
DeviceApi.Reset(); DeviceApi.Reset();
expect(CAPI_DeviceApi_Devices_Length()).toBe(0); expect(CAPI_Media_GetVideoDevices_Length()).toBe(0);
const name = "test_canvas"; const name = "test_canvas";
const canvas = MakeTestCanvas(); const canvas = MakeTestCanvas();
Media.SharedInstance.VideoInput.AddCanvasDevice(name, canvas); Media.SharedInstance.VideoInput.AddCanvasDevice(canvas, name, canvas.width, canvas.height, 30);
expect(CAPI_DeviceApi_Devices_Length()).toBe(1); expect(CAPI_Media_GetVideoDevices_Length()).toBe(1);
expect(CAPI_DeviceApi_Devices_Get(0)).toBe(name); expect(CAPI_Media_GetVideoDevices(0)).toBe(name);
done(); done();
}); });
});
describe("MediaStreamTest", () => {
beforeEach((done)=>{
let handler = ()=>{
DeviceApi.RemOnChangedHandler(handler);
done();
};
DeviceApi.AddOnChangedHandler(handler);
DeviceApi.Update();
Media.ResetSharedInstance();
});
class TestStreamContainer
{
public canvas: HTMLCanvasElement;
public stream : MediaStream;
public constructor()
{
let canvas = document.createElement("canvas");
canvas.width = 4;
canvas.height = 4;
let ctx = canvas.getContext("2d");
//make blue for debugging purposes
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
this.canvas = canvas;
this.stream = (canvas as any).captureStream() as MediaStream;
}
public MakeFrame(color : string){
let ctx = this.canvas.getContext("2d");
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
//make blue for debugging purposes
ctx.fillStyle = color;
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function MakeTestStreamContainer()
{
return new TestStreamContainer();
}
//TODO: need proper way to wait and check with async/ await
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function WaitFor(){
}
it("buffer_and_trygetframe", async(done) => {
const testcontainer = MakeTestStreamContainer();
const stream = new BrowserMediaStream(testcontainer.stream);
//frames are not available at the start until fully loaded
let frame = stream.TryGetFrame();
expect(frame).toBeNull();
await sleep(100);
stream.Update();
//waited for the internals to get initialized. We should have a frame now
frame = stream.TryGetFrame();
expect(frame).not.toBeNull();;
//and a buffer
let buffer = frame.Buffer;
expect(buffer).not.toBeNull();;
//expected to be blue
let r = buffer[0];
let g = buffer[1];
let b = buffer[2];
let a = buffer[3];
expect(r).toBe(0);
expect(g).toBe(0);
expect(b).toBe(255);
expect(a).toBe(255);
//we removed the frame now. this should be null
frame = stream.TryGetFrame();
expect(frame).toBeNull();
//make a new frame with different color
testcontainer.MakeFrame("#FFFF00");
await sleep(100);
stream.Update();
//get new frame
frame = stream.TryGetFrame();
expect(frame).not.toBeNull();;
buffer = frame.Buffer;
expect(buffer).not.toBeNull();;
//should be different color now
r = buffer[0];
g = buffer[1];
b = buffer[2];
a = buffer[3];
expect(r).toBe(255);
expect(g).toBe(255);
expect(b).toBe(0);
expect(a).toBe(255);
//done
done();
});
function createTexture(gl: WebGL2RenderingContext) : WebGLTexture
{
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Because images have to be download over the internet
// they might take a moment until they are ready.
// Until then put a single pixel in the texture so we can
// use it immediately. When the image has finished downloading
// we'll update the texture with the contents of the image.
const level = 0;
const internalFormat = gl.RGBA;
const width = 1;
const height = 1;
const border = 0;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
width, height, border, srcFormat, srcType,
pixel);
return texture;
}
it("texture", async(done) => {
//blue test container to stream from
const testcontainer = MakeTestStreamContainer();
const stream = new BrowserMediaStream(testcontainer.stream);
//document.body.appendChild(testcontainer.canvas);
//waited for the internals to get initialized. We should have a frame now
await sleep(100);
stream.Update();
let frame = stream.PeekFrame()
expect(frame).not.toBeNull();
//create another canvas but with WebGL context
//this is where we copy the texture to
let canvas = document.createElement("canvas");
canvas.width = testcontainer.canvas.width;
canvas.height = testcontainer.canvas.height;
//document.body.appendChild(canvas);
let gl = canvas.getContext("webgl2");
//testing only. draw this one red
gl.clearColor(1,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);
//create new texture and copy the image into it
let texture = createTexture(gl);
let res = frame.ToTexture(gl, texture);
expect(res).toBe(true);
//we attach our test texture to a frame buffer, then read from it to copy the data back from the GPU
//into an array dst_buffer
const dst_buffer = new Uint8Array(testcontainer.canvas.width * testcontainer.canvas.height * 4);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.readPixels(0, 0, testcontainer.canvas.width, testcontainer.canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, dst_buffer);
//check if we have the expected blue color we use to setup the testcontainer canvas
let r = dst_buffer[0];
let g = dst_buffer[1];
let b = dst_buffer[2];
let a = dst_buffer[3];
expect(r).toBe(0);
expect(g).toBe(0);
expect(b).toBe(255);
expect(a).toBe(255);
//TODO: could compare whole src / dst buffer to check if something is cut off
//const compare_buffer = frame.Buffer;
done();
});
}); });
\ No newline at end of file
import { VideoInput } from "../awrtc/index"; import { VideoInput, VideoInputType } from "../awrtc/index";
export function VideoInputTest_export()
{
export function VideoInputTest_export() {
} }
export function MakeTestCanvas() : HTMLCanvasElement{ export function MakeTestCanvas(w?: number, h?: number): HTMLCanvasElement {
if (w == null)
w = 4;
if (h == null)
h = 4;
let canvas = document.createElement("canvas"); let canvas = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
let ctx = canvas.getContext("2d"); let ctx = canvas.getContext("2d");
//make blue for debugging purposes //make blue for debugging purposes
ctx.fillStyle = "blue"; ctx.fillStyle = "blue";
...@@ -16,16 +19,65 @@ export function MakeTestCanvas() : HTMLCanvasElement{ ...@@ -16,16 +19,65 @@ export function MakeTestCanvas() : HTMLCanvasElement{
return canvas; return canvas;
} }
export function MakeBrokenTestCanvas() : HTMLCanvasElement{ export function MakeBrokenTestCanvas(): HTMLCanvasElement {
let canvas = document.createElement("canvas"); let canvas = document.createElement("canvas");
return canvas; return canvas;
} }
/**Create test image with pattern
* Black White
* White Black
*
* So each corner can be tested for correct results.
*
* @param src_width
* @param src_height
*/
export function MakeTestImage(src_width: number, src_height: number): ImageData {
let src_size = src_width * src_height * 4;
let src_data = new Uint8ClampedArray(src_size);
for (let y = 0; y < src_height; y++) {
for (let x = 0; x < src_width; x++) {
let pos = y * src_width + x;
let xp = x >= src_width / 2;
let yp = y >= src_height / 2;
let val = 0;
if (xp || yp)
val = 255;
if (xp && yp)
val = 0;
src_data[pos * 4 + 0] = val;
src_data[pos * 4 + 1] = val;
src_data[pos * 4 + 2] = val;
src_data[pos * 4 + 3] = 255;
}
}
var src_img = new ImageData(src_data, src_width, src_height);
return src_img;
}
export function ExtractData(video: HTMLVideoElement): ImageData {
var canvas = document.createElement("canvas");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
let dst_context = canvas.getContext('2d')
dst_context.drawImage(video, 0, 0, canvas.width, canvas.height);
let dst_img = dst_context.getImageData(0, 0, canvas.width, canvas.height);
return dst_img
}
describe("VideoInputTest", () => { describe("VideoInputTest", () => {
beforeEach(()=>{ beforeEach(() => {
}); });
it("AddRem", () => { it("AddRem", () => {
let name = "test_canvas"; let name = "test_canvas";
...@@ -33,10 +85,10 @@ describe("VideoInputTest", () => { ...@@ -33,10 +85,10 @@ describe("VideoInputTest", () => {
let canvas = document.createElement("canvas") let canvas = document.createElement("canvas")
expect(vi.HasDevice(name)).toBe(false); expect(vi.HasDevice(name)).toBe(false);
vi.AddCanvasDevice(name, canvas); vi.AddCanvasDevice(canvas, name, canvas.width, canvas.height, 30);
expect(vi.HasDevice(name)).toBe(true); expect(vi.HasDevice(name)).toBe(true);
vi.RemCanvasDevice(name); vi.RemoveDevice(name);
expect(vi.HasDevice(name)).toBe(false); expect(vi.HasDevice(name)).toBe(false);
}); });
it("GetDeviceNames", () => { it("GetDeviceNames", () => {
...@@ -49,60 +101,236 @@ describe("VideoInputTest", () => { ...@@ -49,60 +101,236 @@ describe("VideoInputTest", () => {
expect(names.length).toBe(0); expect(names.length).toBe(0);
vi.AddCanvasDevice(name, canvas); vi.AddCanvasDevice(canvas, name, canvas.width, canvas.height, 30);
names = vi.GetDeviceNames(); names = vi.GetDeviceNames();
expect(names).toBeTruthy(); expect(names).toBeTruthy();
expect(names.length).toBe(1); expect(names.length).toBe(1);
expect(names[0]).toBe(name); expect(names[0]).toBe(name);
vi.AddCanvasDevice(name, canvas); vi.AddCanvasDevice(canvas, name, canvas.width, canvas.height, 30);
names = vi.GetDeviceNames(); names = vi.GetDeviceNames();
expect(names).toBeTruthy(); expect(names).toBeTruthy();
expect(names.length).toBe(1); expect(names.length).toBe(1);
expect(names[0]).toBe(name); expect(names[0]).toBe(name);
vi.AddCanvasDevice(name2, canvas); vi.AddCanvasDevice(canvas, name2, canvas.width, canvas.height, 30);
names = vi.GetDeviceNames(); names = vi.GetDeviceNames();
expect(names).toBeTruthy(); expect(names).toBeTruthy();
expect(names.length).toBe(2); expect(names.length).toBe(2);
expect(names.sort()).toEqual([name, name2].sort()); expect(names.sort()).toEqual([name, name2].sort());
}); });
it("GetStream", () => { it("GetStream", () => {
let name = "test_canvas"; let name = "test_canvas";
let vi = new VideoInput(); let vi = new VideoInput();
let canvas = MakeTestCanvas(); let canvas = MakeTestCanvas();
let stream = vi.GetStream(name); let stream = vi.GetStream(name);
expect(stream).toBeNull(); expect(stream).toBeNull();
vi.AddCanvasDevice(name, canvas); vi.AddCanvasDevice(canvas, name, canvas.width, canvas.height, 30);
stream = vi.GetStream(name); stream = vi.GetStream(name);
expect(stream).toBeTruthy(); expect(stream).toBeTruthy();
}); });
it("AddCanvasDevice_no_scaling", (done) => {
let name = "test_canvas";
let vi = new VideoInput();
const src_width = 40;
const src_height = 30;
let canvas = MakeTestCanvas(src_width, src_height);
vi.AddCanvasDevice(canvas, name, canvas.width, canvas.height, 30);
let stream = vi.GetStream(name);
expect(stream).toBeTruthy();
let videoOutput = document.createElement("video")
videoOutput.onloadedmetadata = () => {
expect(videoOutput.videoWidth).toBe(src_width)
expect(videoOutput.videoHeight).toBe(src_height)
done()
}
videoOutput.srcObject = stream;
}, 1000);
it("AddCanvasDevice_scaling", (done) => {
let debug = false;
let name = "test_canvas";
let vi = new VideoInput();
const src_width = 64;
const src_height = 64;
const dst_width = 32;
const dst_height = 32;
let canvas = MakeTestCanvas(src_width, src_height);
let srcContext = canvas.getContext("2d");
var src_img = MakeTestImage(src_width, src_height);
srcContext.putImageData(src_img, 0, 0)
if (debug)
document.body.appendChild(canvas);
vi.AddCanvasDevice(canvas, name, dst_width, dst_height, 30);
let stream = vi.GetStream(name);
expect(stream).toBeTruthy();
let videoOutput = document.createElement("video")
if (debug)
document.body.appendChild(videoOutput);
videoOutput.onloadedmetadata = () => {
expect(videoOutput.videoWidth).toBe(dst_width)
expect(videoOutput.videoHeight).toBe(dst_height)
let dst_img_data = ExtractData(videoOutput)
//upper left
expect(dst_img_data.data[0]).toBe(0);
//upper right
expect(dst_img_data.data[((dst_width - 1) * 4)]).toBe(255);
//lower left
expect(dst_img_data.data[((dst_height - 1) * dst_width) * 4]).toBe(255);
//lower right
expect(dst_img_data.data[(dst_height * dst_width - 1) * 4]).toBe(0);
vi.RemoveDevice(name);
done()
}
videoOutput.srcObject = stream;
}, 1000);
//not yet clear how this can be handled //not yet clear how this can be handled
//this test will trigger an error in firefox //this test will trigger an error in firefox
it("GetStream_no_context", () => { xit("GetStream_no_context", () => {
let name = "test_canvas"; let name = "test_canvas";
let vi = new VideoInput(); let vi = new VideoInput();
let canvas = MakeBrokenTestCanvas(); let canvas = MakeBrokenTestCanvas();
//if we try to record from a canvas before
//a context was accessed it will fail.
//uncommenting this line fixes the bug
//but this is out of our control / within user code
//let ctx = canvas.getContext("2d"); //let ctx = canvas.getContext("2d");
let stream = vi.GetStream(name); let stream = vi.GetStream(name);
expect(stream).toBeNull(); expect(stream).toBeNull();
vi.AddCanvasDevice(name, canvas); vi.AddCanvasDevice(canvas, name, canvas.width, canvas.height, 30);
stream = vi.GetStream(name); stream = vi.GetStream(name);
expect(stream).toBeTruthy(); expect(stream).toBeTruthy();
}); });
//not yet clear how this can be handled
//this test will trigger an error in firefox
it("AddRemDevice", () => {
let name = "test_canvas";
const w = 640;
const h = 480;
const fps = 30;
let vi = new VideoInput();
let stream = vi.GetStream(name);
expect(stream).toBeNull();
vi.AddDevice(name, w, h, fps);
let res = vi.GetDeviceNames().indexOf(name);
expect(res).toBe(0);
vi.RemoveDevice(name);
let res2 = vi.GetDeviceNames().indexOf(name);
expect(res2).toBe(-1);
});
it("Device_int_array", () => {
let name = "test_canvas";
const w = 2;
const h = 2;
const fps = 30;
let arr = new Uint8ClampedArray([
1, 2, 3, 255,
4, 5, 6, 255,
7, 8, 9, 255,
10, 11, 12, 255,
13, 14, 15, 255
]);
let vi = new VideoInput();
vi.AddDevice(name, w, h, fps);
let stream = vi.GetStream(name);
expect(stream).toBeTruthy();
const clamped = new Uint8ClampedArray(arr.buffer, 4, 4 * 4);
const res = vi.UpdateFrame(name, clamped, w, h, VideoInputType.ARGB, 0, false);
expect(res).toBe(true);
let result_canvas = (vi as any).canvasDevices[name].canvas as HTMLCanvasElement;
expect(result_canvas.width).toBe(w);
expect(result_canvas.height).toBe(h);
let result_img = result_canvas.getContext("2d").getImageData(0, 0, result_canvas.width, result_canvas.height);
const result_arr = new Uint8Array(result_img.data.buffer);
const base_arr = new Uint8Array(arr.buffer, 4, 4 * 4);
expect(base_arr).toEqual(result_arr);
});
it("Device_full", () => {
let src_canvas = MakeTestCanvas();
let src_ctx = src_canvas.getContext("2d");
src_ctx.fillStyle = "yellow";
src_ctx.fillRect(0, 0, src_canvas.width, src_canvas.height);
let name = "test_canvas";
const w = 2;
const h = 2;
const fps = 30;
src_canvas.width = w;
src_canvas.height = h;
let vi = new VideoInput();
let src_img = src_ctx.getImageData(0, 0, src_canvas.width, src_canvas.height);
vi.AddDevice(name, w, h, fps);
let stream = vi.GetStream(name);
expect(stream).toBeTruthy();
const res = vi.UpdateFrame(name, src_img.data, src_img.width, src_img.height, VideoInputType.ARGB, 0, false);
expect(res).toBe(true);
//test if the internal array was set correctly
let result_canvas = (vi as any).canvasDevices[name].canvas as HTMLCanvasElement;
expect(result_canvas.width).toBe(src_canvas.width);
expect(result_canvas.height).toBe(src_canvas.height);
let result_img = result_canvas.getContext("2d").getImageData(0, 0, result_canvas.width, result_canvas.height);
expect(result_img.width).toBe(src_img.width);
expect(result_img.height).toBe(src_img.height);
expect(result_img.data).toEqual(src_img.data);
});
}); });
\ 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