You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
9.0 KiB
336 lines
9.0 KiB
1 month ago
|
/*
|
||
|
|
||
|
|
||
|
usage:
|
||
|
|
||
|
p = new Player({
|
||
|
useWorker: <bool>,
|
||
|
workerFile: <defaults to "Decoder.js"> // give path to Decoder.js
|
||
|
webgl: true | false | "auto" // defaults to "auto"
|
||
|
});
|
||
|
|
||
|
// canvas property represents the canvas node
|
||
|
// put it somewhere in the dom
|
||
|
p.canvas;
|
||
|
|
||
|
p.webgl; // contains the used rendering mode. if you pass auto to webgl you can see what auto detection resulted in
|
||
|
|
||
|
p.decode(<binary>);
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
// universal module definition
|
||
|
(function (root, factory) {
|
||
|
if (typeof define === 'function' && define.amd) {
|
||
|
// AMD. Register as an anonymous module.
|
||
|
define(["./Decoder", "./YUVCanvas"], factory);
|
||
|
} else if (typeof exports === 'object') {
|
||
|
// Node. Does not work with strict CommonJS, but
|
||
|
// only CommonJS-like environments that support module.exports,
|
||
|
// like Node.
|
||
|
module.exports = factory(require("./Decoder"), require("./YUVCanvas"));
|
||
|
} else {
|
||
|
// Browser globals (root is window)
|
||
|
root.Player = factory(root.Decoder, root.YUVCanvas);
|
||
|
}
|
||
|
}(this, function (Decoder, WebGLCanvas) {
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
var nowValue = Decoder.nowValue;
|
||
|
|
||
|
|
||
|
var Player = function(parOptions){
|
||
|
var self = this;
|
||
|
this._config = parOptions || {};
|
||
|
|
||
|
this.render = true;
|
||
|
if (this._config.render === false){
|
||
|
this.render = false;
|
||
|
};
|
||
|
|
||
|
this.nowValue = nowValue;
|
||
|
|
||
|
this._config.workerFile = this._config.workerFile || "Decoder.js";
|
||
|
if (this._config.preserveDrawingBuffer){
|
||
|
this._config.contextOptions = this._config.contextOptions || {};
|
||
|
this._config.contextOptions.preserveDrawingBuffer = true;
|
||
|
};
|
||
|
|
||
|
var webgl = "auto";
|
||
|
if (this._config.webgl === true){
|
||
|
webgl = true;
|
||
|
}else if (this._config.webgl === false){
|
||
|
webgl = false;
|
||
|
};
|
||
|
|
||
|
if (webgl == "auto"){
|
||
|
webgl = true;
|
||
|
try{
|
||
|
if (!window.WebGLRenderingContext) {
|
||
|
// the browser doesn't even know what WebGL is
|
||
|
webgl = false;
|
||
|
} else {
|
||
|
var canvas = document.createElement('canvas');
|
||
|
var ctx = canvas.getContext("webgl");
|
||
|
if (!ctx) {
|
||
|
// browser supports WebGL but initialization failed.
|
||
|
webgl = false;
|
||
|
};
|
||
|
};
|
||
|
}catch(e){
|
||
|
webgl = false;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
this.webgl = webgl;
|
||
|
|
||
|
// choose functions
|
||
|
if (this.webgl){
|
||
|
this.createCanvasObj = this.createCanvasWebGL;
|
||
|
this.renderFrame = this.renderFrameWebGL;
|
||
|
}else{
|
||
|
this.createCanvasObj = this.createCanvasRGB;
|
||
|
this.renderFrame = this.renderFrameRGB;
|
||
|
};
|
||
|
|
||
|
|
||
|
var lastWidth;
|
||
|
var lastHeight;
|
||
|
var onPictureDecoded = function(buffer, width, height, infos) {
|
||
|
self.onPictureDecoded(buffer, width, height, infos);
|
||
|
|
||
|
var startTime = nowValue();
|
||
|
|
||
|
if (!buffer || !self.render) {
|
||
|
return;
|
||
|
};
|
||
|
|
||
|
self.renderFrame({
|
||
|
canvasObj: self.canvasObj,
|
||
|
data: buffer,
|
||
|
width: width,
|
||
|
height: height
|
||
|
});
|
||
|
|
||
|
if (self.onRenderFrameComplete){
|
||
|
self.onRenderFrameComplete({
|
||
|
data: buffer,
|
||
|
width: width,
|
||
|
height: height,
|
||
|
infos: infos,
|
||
|
canvasObj: self.canvasObj
|
||
|
});
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
// provide size
|
||
|
|
||
|
if (!this._config.size){
|
||
|
this._config.size = {};
|
||
|
};
|
||
|
this._config.size.width = this._config.size.width || 200;
|
||
|
this._config.size.height = this._config.size.height || 200;
|
||
|
|
||
|
if (this._config.useWorker){
|
||
|
var worker = new Worker(this._config.workerFile);
|
||
|
this.worker = worker;
|
||
|
worker.addEventListener('message', function(e) {
|
||
|
var data = e.data;
|
||
|
if (data.consoleLog){
|
||
|
console.log(data.consoleLog);
|
||
|
return;
|
||
|
};
|
||
|
|
||
|
onPictureDecoded.call(self, new Uint8Array(data.buf, 0, data.length), data.width, data.height, data.infos);
|
||
|
|
||
|
}, false);
|
||
|
|
||
|
worker.postMessage({type: "Broadway.js - Worker init", options: {
|
||
|
rgb: !webgl,
|
||
|
memsize: this.memsize,
|
||
|
reuseMemory: this._config.reuseMemory ? true : false
|
||
|
}});
|
||
|
|
||
|
if (this._config.transferMemory){
|
||
|
this.decode = function(parData, parInfo){
|
||
|
// no copy
|
||
|
// instead we are transfering the ownership of the buffer
|
||
|
// dangerous!!!
|
||
|
|
||
|
worker.postMessage({buf: parData.buffer, offset: parData.byteOffset, length: parData.length, info: parInfo}, [parData.buffer]); // Send data to our worker.
|
||
|
};
|
||
|
|
||
|
}else{
|
||
|
this.decode = function(parData, parInfo){
|
||
|
// Copy the sample so that we only do a structured clone of the
|
||
|
// region of interest
|
||
|
var copyU8 = new Uint8Array(parData.length);
|
||
|
copyU8.set( parData, 0, parData.length );
|
||
|
worker.postMessage({buf: copyU8.buffer, offset: 0, length: parData.length, info: parInfo}, [copyU8.buffer]); // Send data to our worker.
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
if (this._config.reuseMemory){
|
||
|
this.recycleMemory = function(parArray){
|
||
|
//this.beforeRecycle();
|
||
|
worker.postMessage({reuse: parArray.buffer}, [parArray.buffer]); // Send data to our worker.
|
||
|
//this.afterRecycle();
|
||
|
};
|
||
|
}
|
||
|
|
||
|
}else{
|
||
|
|
||
|
this.decoder = new Decoder({
|
||
|
rgb: !webgl
|
||
|
});
|
||
|
this.decoder.onPictureDecoded = onPictureDecoded;
|
||
|
|
||
|
this.decode = function(parData, parInfo){
|
||
|
self.decoder.decode(parData, parInfo);
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
if (this.render){
|
||
|
this.canvasObj = this.createCanvasObj({
|
||
|
contextOptions: this._config.contextOptions
|
||
|
});
|
||
|
this.canvas = this.canvasObj.canvas;
|
||
|
};
|
||
|
|
||
|
this.domNode = this.canvas;
|
||
|
|
||
|
lastWidth = this._config.size.width;
|
||
|
lastHeight = this._config.size.height;
|
||
|
|
||
|
};
|
||
|
|
||
|
Player.prototype = {
|
||
|
|
||
|
onPictureDecoded: function(buffer, width, height, infos){},
|
||
|
|
||
|
// call when memory of decoded frames is not used anymore
|
||
|
recycleMemory: function(buf){
|
||
|
},
|
||
|
/*beforeRecycle: function(){},
|
||
|
afterRecycle: function(){},*/
|
||
|
|
||
|
// for both functions options is:
|
||
|
//
|
||
|
// width
|
||
|
// height
|
||
|
// enableScreenshot
|
||
|
//
|
||
|
// returns a object that has a property canvas which is a html5 canvas
|
||
|
createCanvasWebGL: function(options){
|
||
|
var canvasObj = this._createBasicCanvasObj(options);
|
||
|
canvasObj.contextOptions = options.contextOptions;
|
||
|
return canvasObj;
|
||
|
},
|
||
|
|
||
|
createCanvasRGB: function(options){
|
||
|
var canvasObj = this._createBasicCanvasObj(options);
|
||
|
return canvasObj;
|
||
|
},
|
||
|
|
||
|
// part that is the same for webGL and RGB
|
||
|
_createBasicCanvasObj: function(options){
|
||
|
options = options || {};
|
||
|
|
||
|
var obj = {};
|
||
|
var width = options.width;
|
||
|
if (!width){
|
||
|
width = this._config.size.width;
|
||
|
};
|
||
|
var height = options.height;
|
||
|
if (!height){
|
||
|
height = this._config.size.height;
|
||
|
};
|
||
|
obj.canvas = document.createElement('canvas');
|
||
|
obj.canvas.width = width;
|
||
|
obj.canvas.height = height;
|
||
|
obj.canvas.style.backgroundColor = "#0D0E1B";
|
||
|
|
||
|
|
||
|
return obj;
|
||
|
},
|
||
|
|
||
|
// options:
|
||
|
//
|
||
|
// canvas
|
||
|
// data
|
||
|
renderFrameWebGL: function(options){
|
||
|
|
||
|
var canvasObj = options.canvasObj;
|
||
|
|
||
|
var width = options.width || canvasObj.canvas.width;
|
||
|
var height = options.height || canvasObj.canvas.height;
|
||
|
|
||
|
if (canvasObj.canvas.width !== width || canvasObj.canvas.height !== height || !canvasObj.webGLCanvas){
|
||
|
canvasObj.canvas.width = width;
|
||
|
canvasObj.canvas.height = height;
|
||
|
canvasObj.webGLCanvas = new WebGLCanvas({
|
||
|
canvas: canvasObj.canvas,
|
||
|
contextOptions: canvasObj.contextOptions,
|
||
|
width: width,
|
||
|
height: height
|
||
|
});
|
||
|
};
|
||
|
|
||
|
var ylen = width * height;
|
||
|
var uvlen = (width / 2) * (height / 2);
|
||
|
|
||
|
canvasObj.webGLCanvas.drawNextOutputPicture({
|
||
|
yData: options.data.subarray(0, ylen),
|
||
|
uData: options.data.subarray(ylen, ylen + uvlen),
|
||
|
vData: options.data.subarray(ylen + uvlen, ylen + uvlen + uvlen)
|
||
|
});
|
||
|
|
||
|
var self = this;
|
||
|
self.recycleMemory(options.data);
|
||
|
|
||
|
},
|
||
|
renderFrameRGB: function(options){
|
||
|
var canvasObj = options.canvasObj;
|
||
|
|
||
|
var width = options.width || canvasObj.canvas.width;
|
||
|
var height = options.height || canvasObj.canvas.height;
|
||
|
|
||
|
if (canvasObj.canvas.width !== width || canvasObj.canvas.height !== height){
|
||
|
canvasObj.canvas.width = width;
|
||
|
canvasObj.canvas.height = height;
|
||
|
};
|
||
|
|
||
|
var ctx = canvasObj.ctx;
|
||
|
var imgData = canvasObj.imgData;
|
||
|
|
||
|
if (!ctx){
|
||
|
canvasObj.ctx = canvasObj.canvas.getContext('2d');
|
||
|
ctx = canvasObj.ctx;
|
||
|
|
||
|
canvasObj.imgData = ctx.createImageData(width, height);
|
||
|
imgData = canvasObj.imgData;
|
||
|
};
|
||
|
|
||
|
imgData.data.set(options.data);
|
||
|
ctx.putImageData(imgData, 0, 0);
|
||
|
var self = this;
|
||
|
self.recycleMemory(options.data);
|
||
|
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
return Player;
|
||
|
|
||
|
}));
|
||
|
|