blob: ff2e649c4a52665528bc4039c4bec4ee70870a05 [file] [log] [blame]
/*
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;
}));