Player.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /*
  2. usage:
  3. p = new Player({
  4. useWorker: <bool>,
  5. workerFile: <defaults to "Decoder.js"> // give path to Decoder.js
  6. webgl: true | false | "auto" // defaults to "auto"
  7. });
  8. // canvas property represents the canvas node
  9. // put it somewhere in the dom
  10. p.canvas;
  11. p.webgl; // contains the used rendering mode. if you pass auto to webgl you can see what auto detection resulted in
  12. p.decode(<binary>);
  13. */
  14. // universal module definition
  15. (function (root, factory) {
  16. if (typeof define === 'function' && define.amd) {
  17. // AMD. Register as an anonymous module.
  18. define(["./Decoder", "./YUVCanvas"], factory);
  19. } else if (typeof exports === 'object') {
  20. // Node. Does not work with strict CommonJS, but
  21. // only CommonJS-like environments that support module.exports,
  22. // like Node.
  23. module.exports = factory(require("./Decoder"), require("./YUVCanvas"));
  24. } else {
  25. // Browser globals (root is window)
  26. root.Player = factory(root.Decoder, root.YUVCanvas);
  27. }
  28. }(this, function (Decoder, WebGLCanvas) {
  29. "use strict";
  30. var nowValue = Decoder.nowValue;
  31. var Player = function(parOptions){
  32. var self = this;
  33. this._config = parOptions || {};
  34. this.render = true;
  35. if (this._config.render === false){
  36. this.render = false;
  37. };
  38. this.nowValue = nowValue;
  39. this._config.workerFile = this._config.workerFile || "Decoder.js";
  40. if (this._config.preserveDrawingBuffer){
  41. this._config.contextOptions = this._config.contextOptions || {};
  42. this._config.contextOptions.preserveDrawingBuffer = true;
  43. };
  44. var webgl = "auto";
  45. if (this._config.webgl === true){
  46. webgl = true;
  47. }else if (this._config.webgl === false){
  48. webgl = false;
  49. };
  50. if (webgl == "auto"){
  51. webgl = true;
  52. try{
  53. if (!window.WebGLRenderingContext) {
  54. // the browser doesn't even know what WebGL is
  55. webgl = false;
  56. } else {
  57. var canvas = document.createElement('canvas');
  58. var ctx = canvas.getContext("webgl");
  59. if (!ctx) {
  60. // browser supports WebGL but initialization failed.
  61. webgl = false;
  62. };
  63. };
  64. }catch(e){
  65. webgl = false;
  66. };
  67. };
  68. this.webgl = webgl;
  69. // choose functions
  70. if (this.webgl){
  71. this.createCanvasObj = this.createCanvasWebGL;
  72. this.renderFrame = this.renderFrameWebGL;
  73. }else{
  74. this.createCanvasObj = this.createCanvasRGB;
  75. this.renderFrame = this.renderFrameRGB;
  76. };
  77. var lastWidth;
  78. var lastHeight;
  79. var onPictureDecoded = function(buffer, width, height, infos) {
  80. self.onPictureDecoded(buffer, width, height, infos);
  81. var startTime = nowValue();
  82. if (!buffer || !self.render) {
  83. return;
  84. };
  85. self.renderFrame({
  86. canvasObj: self.canvasObj,
  87. data: buffer,
  88. width: width,
  89. height: height
  90. });
  91. if (self.onRenderFrameComplete){
  92. self.onRenderFrameComplete({
  93. data: buffer,
  94. width: width,
  95. height: height,
  96. infos: infos,
  97. canvasObj: self.canvasObj
  98. });
  99. };
  100. };
  101. // provide size
  102. if (!this._config.size){
  103. this._config.size = {};
  104. };
  105. this._config.size.width = this._config.size.width || 200;
  106. this._config.size.height = this._config.size.height || 200;
  107. if (this._config.useWorker){
  108. var worker = new Worker(this._config.workerFile);
  109. this.worker = worker;
  110. worker.addEventListener('message', function(e) {
  111. var data = e.data;
  112. if (data.consoleLog){
  113. console.log(data.consoleLog);
  114. return;
  115. };
  116. onPictureDecoded.call(self, new Uint8Array(data.buf, 0, data.length), data.width, data.height, data.infos);
  117. }, false);
  118. worker.postMessage({type: "Broadway.js - Worker init", options: {
  119. rgb: !webgl,
  120. memsize: this.memsize,
  121. reuseMemory: this._config.reuseMemory ? true : false
  122. }});
  123. if (this._config.transferMemory){
  124. this.decode = function(parData, parInfo){
  125. // no copy
  126. // instead we are transfering the ownership of the buffer
  127. // dangerous!!!
  128. worker.postMessage({buf: parData.buffer, offset: parData.byteOffset, length: parData.length, info: parInfo}, [parData.buffer]); // Send data to our worker.
  129. };
  130. }else{
  131. this.decode = function(parData, parInfo){
  132. // Copy the sample so that we only do a structured clone of the
  133. // region of interest
  134. var copyU8 = new Uint8Array(parData.length);
  135. copyU8.set( parData, 0, parData.length );
  136. worker.postMessage({buf: copyU8.buffer, offset: 0, length: parData.length, info: parInfo}, [copyU8.buffer]); // Send data to our worker.
  137. };
  138. };
  139. if (this._config.reuseMemory){
  140. this.recycleMemory = function(parArray){
  141. //this.beforeRecycle();
  142. worker.postMessage({reuse: parArray.buffer}, [parArray.buffer]); // Send data to our worker.
  143. //this.afterRecycle();
  144. };
  145. }
  146. }else{
  147. this.decoder = new Decoder({
  148. rgb: !webgl
  149. });
  150. this.decoder.onPictureDecoded = onPictureDecoded;
  151. this.decode = function(parData, parInfo){
  152. self.decoder.decode(parData, parInfo);
  153. };
  154. };
  155. if (this.render){
  156. this.canvasObj = this.createCanvasObj({
  157. contextOptions: this._config.contextOptions
  158. });
  159. this.canvas = this.canvasObj.canvas;
  160. };
  161. this.domNode = this.canvas;
  162. lastWidth = this._config.size.width;
  163. lastHeight = this._config.size.height;
  164. };
  165. Player.prototype = {
  166. onPictureDecoded: function(buffer, width, height, infos){},
  167. // call when memory of decoded frames is not used anymore
  168. recycleMemory: function(buf){
  169. },
  170. /*beforeRecycle: function(){},
  171. afterRecycle: function(){},*/
  172. // for both functions options is:
  173. //
  174. // width
  175. // height
  176. // enableScreenshot
  177. //
  178. // returns a object that has a property canvas which is a html5 canvas
  179. createCanvasWebGL: function(options){
  180. var canvasObj = this._createBasicCanvasObj(options);
  181. canvasObj.contextOptions = options.contextOptions;
  182. return canvasObj;
  183. },
  184. createCanvasRGB: function(options){
  185. var canvasObj = this._createBasicCanvasObj(options);
  186. return canvasObj;
  187. },
  188. // part that is the same for webGL and RGB
  189. _createBasicCanvasObj: function(options){
  190. options = options || {};
  191. var obj = {};
  192. var width = options.width;
  193. if (!width){
  194. width = this._config.size.width;
  195. };
  196. var height = options.height;
  197. if (!height){
  198. height = this._config.size.height;
  199. };
  200. obj.canvas = document.createElement('canvas');
  201. obj.canvas.width = width;
  202. obj.canvas.height = height;
  203. obj.canvas.style.backgroundColor = "#0D0E1B";
  204. return obj;
  205. },
  206. // options:
  207. //
  208. // canvas
  209. // data
  210. renderFrameWebGL: function(options){
  211. var canvasObj = options.canvasObj;
  212. var width = options.width || canvasObj.canvas.width;
  213. var height = options.height || canvasObj.canvas.height;
  214. if (canvasObj.canvas.width !== width || canvasObj.canvas.height !== height || !canvasObj.webGLCanvas){
  215. canvasObj.canvas.width = width;
  216. canvasObj.canvas.height = height;
  217. canvasObj.webGLCanvas = new WebGLCanvas({
  218. canvas: canvasObj.canvas,
  219. contextOptions: canvasObj.contextOptions,
  220. width: width,
  221. height: height
  222. });
  223. };
  224. var ylen = width * height;
  225. var uvlen = (width / 2) * (height / 2);
  226. canvasObj.webGLCanvas.drawNextOutputPicture({
  227. yData: options.data.subarray(0, ylen),
  228. uData: options.data.subarray(ylen, ylen + uvlen),
  229. vData: options.data.subarray(ylen + uvlen, ylen + uvlen + uvlen)
  230. });
  231. var self = this;
  232. self.recycleMemory(options.data);
  233. },
  234. renderFrameRGB: function(options){
  235. var canvasObj = options.canvasObj;
  236. var width = options.width || canvasObj.canvas.width;
  237. var height = options.height || canvasObj.canvas.height;
  238. if (canvasObj.canvas.width !== width || canvasObj.canvas.height !== height){
  239. canvasObj.canvas.width = width;
  240. canvasObj.canvas.height = height;
  241. };
  242. var ctx = canvasObj.ctx;
  243. var imgData = canvasObj.imgData;
  244. if (!ctx){
  245. canvasObj.ctx = canvasObj.canvas.getContext('2d');
  246. ctx = canvasObj.ctx;
  247. canvasObj.imgData = ctx.createImageData(width, height);
  248. imgData = canvasObj.imgData;
  249. };
  250. imgData.data.set(options.data);
  251. ctx.putImageData(imgData, 0, 0);
  252. var self = this;
  253. self.recycleMemory(options.data);
  254. }
  255. };
  256. return Player;
  257. }));