YUVCanvas.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. // 通用模块定义
  2. (function (root, factory) {
  3. if (typeof define === 'function' && define.amd) {
  4. define([], factory);
  5. } else if (typeof exports === 'object') {
  6. //节点
  7. module.exports = factory();
  8. } else {
  9. // 浏览器全局(根是窗口)
  10. root.YUVCanvas = factory();
  11. }
  12. }(this, function () {
  13. //此类可用于将输出图片从H264bsdDecoder呈现到画布元素。
  14. //
  15. //*如果可用,则使用WebGL呈现内容。
  16. function YUVCanvas(parOptions) {
  17. parOptions = parOptions || {};
  18. this.canvasElement = parOptions.canvas || document.createElement("canvas");
  19. this.contextOptions = parOptions.contextOptions;
  20. this.type = parOptions.type || "yuv420";
  21. this.customYUV444 = parOptions.customYUV444;
  22. this.conversionType = parOptions.conversionType || "rec601";
  23. this.width = parOptions.width || 640;
  24. this.height = parOptions.height || 320;
  25. this.animationTime = parOptions.animationTime || 0;
  26. this.canvasElement.width = this.width;
  27. this.canvasElement.height = this.height;
  28. this.initContextGL();
  29. if(this.contextGL) {
  30. this.initProgram();
  31. this.initBuffers();
  32. this.initTextures();
  33. };
  34. /**
  35. * 使用WebGL绘制下一个输出图片
  36. */
  37. if (this.type === "yuv420"){
  38. this.drawNextOuptutPictureGL = function(par) {
  39. var gl = this.contextGL;
  40. var texturePosBuffer = this.texturePosBuffer;
  41. var uTexturePosBuffer = this.uTexturePosBuffer;
  42. var vTexturePosBuffer = this.vTexturePosBuffer;
  43. var yTextureRef = this.yTextureRef;
  44. var uTextureRef = this.uTextureRef;
  45. var vTextureRef = this.vTextureRef;
  46. var yData = par.yData;
  47. var uData = par.uData;
  48. var vData = par.vData;
  49. var width = this.width;
  50. var height = this.height;
  51. var yDataPerRow = par.yDataPerRow || width;
  52. var yRowCnt = par.yRowCnt || height;
  53. var uDataPerRow = par.uDataPerRow || (width / 2);
  54. var uRowCnt = par.uRowCnt || (height / 2);
  55. var vDataPerRow = par.vDataPerRow || uDataPerRow;
  56. var vRowCnt = par.vRowCnt || uRowCnt;
  57. gl.viewport(0, 0, width, height);
  58. var tTop = 0;
  59. var tLeft = 0;
  60. var tBottom = height / yRowCnt;
  61. var tRight = width / yDataPerRow;
  62. var texturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]);
  63. gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer);
  64. gl.bufferData(gl.ARRAY_BUFFER, texturePosValues, gl.DYNAMIC_DRAW);
  65. if (this.customYUV444){
  66. tBottom = height / uRowCnt;
  67. tRight = width / uDataPerRow;
  68. }else{
  69. tBottom = (height / 2) / uRowCnt;
  70. tRight = (width / 2) / uDataPerRow;
  71. };
  72. var uTexturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]);
  73. gl.bindBuffer(gl.ARRAY_BUFFER, uTexturePosBuffer);
  74. gl.bufferData(gl.ARRAY_BUFFER, uTexturePosValues, gl.DYNAMIC_DRAW);
  75. if (this.customYUV444){
  76. tBottom = height / vRowCnt;
  77. tRight = width / vDataPerRow;
  78. }else{
  79. tBottom = (height / 2) / vRowCnt;
  80. tRight = (width / 2) / vDataPerRow;
  81. };
  82. var vTexturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]);
  83. gl.bindBuffer(gl.ARRAY_BUFFER, vTexturePosBuffer);
  84. gl.bufferData(gl.ARRAY_BUFFER, vTexturePosValues, gl.DYNAMIC_DRAW);
  85. gl.activeTexture(gl.TEXTURE0);
  86. gl.bindTexture(gl.TEXTURE_2D, yTextureRef);
  87. gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, yDataPerRow, yRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, yData);
  88. gl.activeTexture(gl.TEXTURE1);
  89. gl.bindTexture(gl.TEXTURE_2D, uTextureRef);
  90. gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, uDataPerRow, uRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, uData);
  91. gl.activeTexture(gl.TEXTURE2);
  92. gl.bindTexture(gl.TEXTURE_2D, vTextureRef);
  93. gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, vDataPerRow, vRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, vData);
  94. gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  95. };
  96. }else if (this.type === "yuv422"){
  97. this.drawNextOuptutPictureGL = function(par) {
  98. var gl = this.contextGL;
  99. var texturePosBuffer = this.texturePosBuffer;
  100. var textureRef = this.textureRef;
  101. var data = par.data;
  102. var width = this.width;
  103. var height = this.height;
  104. var dataPerRow = par.dataPerRow || (width * 2);
  105. var rowCnt = par.rowCnt || height;
  106. gl.viewport(0, 0, width, height);
  107. var tTop = 0;
  108. var tLeft = 0;
  109. var tBottom = height / rowCnt;
  110. var tRight = width / (dataPerRow / 2);
  111. var texturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]);
  112. gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer);
  113. gl.bufferData(gl.ARRAY_BUFFER, texturePosValues, gl.DYNAMIC_DRAW);
  114. gl.uniform2f(gl.getUniformLocation(this.shaderProgram, 'resolution'), dataPerRow, height);
  115. gl.activeTexture(gl.TEXTURE0);
  116. gl.bindTexture(gl.TEXTURE_2D, textureRef);
  117. gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, dataPerRow, rowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data);
  118. gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  119. };
  120. };
  121. };
  122. /**
  123. * 如果画布支持WebGL,则返回true
  124. */
  125. YUVCanvas.prototype.isWebGL = function() {
  126. return this.contextGL;
  127. };
  128. /**
  129. * 从canvas元素创建GL上下文
  130. */
  131. YUVCanvas.prototype.initContextGL = function() {
  132. var canvas = this.canvasElement;
  133. var gl = null;
  134. var validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"];
  135. var nameIndex = 0;
  136. while(!gl && nameIndex < validContextNames.length) {
  137. var contextName = validContextNames[nameIndex];
  138. try {
  139. if (this.contextOptions){
  140. gl = canvas.getContext(contextName, this.contextOptions);
  141. }else{
  142. gl = canvas.getContext(contextName);
  143. };
  144. } catch (e) {
  145. gl = null;
  146. }
  147. if(!gl || typeof gl.getParameter !== "function") {
  148. gl = null;
  149. }
  150. ++nameIndex;
  151. };
  152. this.contextGL = gl;
  153. };
  154. /**
  155. * 初始化GL着色器程序
  156. */
  157. YUVCanvas.prototype.initProgram = function() {
  158. var gl = this.contextGL;
  159. // 所有类型的顶点着色器都相同
  160. var vertexShaderScript;
  161. var fragmentShaderScript;
  162. if (this.type === "yuv420"){
  163. vertexShaderScript = [
  164. 'attribute vec4 vertexPos;',
  165. 'attribute vec4 texturePos;',
  166. 'attribute vec4 uTexturePos;',
  167. 'attribute vec4 vTexturePos;',
  168. 'varying vec2 textureCoord;',
  169. 'varying vec2 uTextureCoord;',
  170. 'varying vec2 vTextureCoord;',
  171. 'void main()',
  172. '{',
  173. ' gl_Position = vertexPos;',
  174. ' textureCoord = texturePos.xy;',
  175. ' uTextureCoord = uTexturePos.xy;',
  176. ' vTextureCoord = vTexturePos.xy;',
  177. '}'
  178. ].join('\n');
  179. fragmentShaderScript = [
  180. 'precision highp float;',
  181. 'varying highp vec2 textureCoord;',
  182. 'varying highp vec2 uTextureCoord;',
  183. 'varying highp vec2 vTextureCoord;',
  184. 'uniform sampler2D ySampler;',
  185. 'uniform sampler2D uSampler;',
  186. 'uniform sampler2D vSampler;',
  187. 'uniform mat4 YUV2RGB;',
  188. 'void main(void) {',
  189. ' highp float y = texture2D(ySampler, textureCoord).r;',
  190. ' highp float u = texture2D(uSampler, uTextureCoord).r;',
  191. ' highp float v = texture2D(vSampler, vTextureCoord).r;',
  192. ' gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;',
  193. '}'
  194. ].join('\n');
  195. }else if (this.type === "yuv422"){
  196. vertexShaderScript = [
  197. 'attribute vec4 vertexPos;',
  198. 'attribute vec4 texturePos;',
  199. 'varying vec2 textureCoord;',
  200. 'void main()',
  201. '{',
  202. ' gl_Position = vertexPos;',
  203. ' textureCoord = texturePos.xy;',
  204. '}'
  205. ].join('\n');
  206. fragmentShaderScript = [
  207. 'precision highp float;',
  208. 'varying highp vec2 textureCoord;',
  209. 'uniform sampler2D sampler;',
  210. 'uniform highp vec2 resolution;',
  211. 'uniform mat4 YUV2RGB;',
  212. 'void main(void) {',
  213. ' highp float texPixX = 1.0 / resolution.x;',
  214. ' highp float logPixX = 2.0 / resolution.x;', // half the resolution of the texture
  215. ' highp float logHalfPixX = 4.0 / resolution.x;', // half of the logical resolution so every 4th pixel
  216. ' highp float steps = floor(textureCoord.x / logPixX);',
  217. ' highp float uvSteps = floor(textureCoord.x / logHalfPixX);',
  218. ' highp float y = texture2D(sampler, vec2((logPixX * steps) + texPixX, textureCoord.y)).r;',
  219. ' highp float u = texture2D(sampler, vec2((logHalfPixX * uvSteps), textureCoord.y)).r;',
  220. ' highp float v = texture2D(sampler, vec2((logHalfPixX * uvSteps) + texPixX + texPixX, textureCoord.y)).r;',
  221. //' highp float y = texture2D(sampler, textureCoord).r;',
  222. //' gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;',
  223. ' gl_FragColor = vec4(y, u, v, 1.0) * YUV2RGB;',
  224. '}'
  225. ].join('\n');
  226. };
  227. var YUV2RGB = [];
  228. if (this.conversionType == "rec709") {
  229. // ITU-T Rec. 709
  230. YUV2RGB = [
  231. 1.16438, 0.00000, 1.79274, -0.97295,
  232. 1.16438, -0.21325, -0.53291, 0.30148,
  233. 1.16438, 2.11240, 0.00000, -1.13340,
  234. 0, 0, 0, 1,
  235. ];
  236. } else {
  237. // assume ITU-T Rec. 601
  238. YUV2RGB = [
  239. 1.16438, 0.00000, 1.59603, -0.87079,
  240. 1.16438, -0.39176, -0.81297, 0.52959,
  241. 1.16438, 2.01723, 0.00000, -1.08139,
  242. 0, 0, 0, 1
  243. ];
  244. };
  245. var vertexShader = gl.createShader(gl.VERTEX_SHADER);
  246. gl.shaderSource(vertexShader, vertexShaderScript);
  247. gl.compileShader(vertexShader);
  248. if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  249. console.log('Vertex shader failed to compile: ' + gl.getShaderInfoLog(vertexShader));
  250. }
  251. var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
  252. gl.shaderSource(fragmentShader, fragmentShaderScript);
  253. gl.compileShader(fragmentShader);
  254. if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  255. console.log('Fragment shader failed to compile: ' + gl.getShaderInfoLog(fragmentShader));
  256. }
  257. var program = gl.createProgram();
  258. gl.attachShader(program, vertexShader);
  259. gl.attachShader(program, fragmentShader);
  260. gl.linkProgram(program);
  261. if(!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  262. console.log('Program failed to compile: ' + gl.getProgramInfoLog(program));
  263. }
  264. gl.useProgram(program);
  265. var YUV2RGBRef = gl.getUniformLocation(program, 'YUV2RGB');
  266. gl.uniformMatrix4fv(YUV2RGBRef, false, YUV2RGB);
  267. this.shaderProgram = program;
  268. };
  269. /**
  270. * 初始化顶点缓冲区并附加到着色器程序
  271. */
  272. YUVCanvas.prototype.initBuffers = function() {
  273. var gl = this.contextGL;
  274. var program = this.shaderProgram;
  275. var vertexPosBuffer = gl.createBuffer();
  276. gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
  277. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), gl.STATIC_DRAW);
  278. var vertexPosRef = gl.getAttribLocation(program, 'vertexPos');
  279. gl.enableVertexAttribArray(vertexPosRef);
  280. gl.vertexAttribPointer(vertexPosRef, 2, gl.FLOAT, false, 0, 0);
  281. if (this.animationTime){
  282. var animationTime = this.animationTime;
  283. var timePassed = 0;
  284. var stepTime = 15;
  285. var aniFun = function(){
  286. timePassed += stepTime;
  287. var mul = ( 1 * timePassed ) / animationTime;
  288. if (timePassed >= animationTime){
  289. mul = 1;
  290. }else{
  291. setTimeout(aniFun, stepTime);
  292. };
  293. var neg = -1 * mul;
  294. var pos = 1 * mul;
  295. var vertexPosBuffer = gl.createBuffer();
  296. gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
  297. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([pos, pos, neg, pos, pos, neg, neg, neg]), gl.STATIC_DRAW);
  298. var vertexPosRef = gl.getAttribLocation(program, 'vertexPos');
  299. gl.enableVertexAttribArray(vertexPosRef);
  300. gl.vertexAttribPointer(vertexPosRef, 2, gl.FLOAT, false, 0, 0);
  301. try{
  302. gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  303. }catch(e){};
  304. };
  305. aniFun();
  306. };
  307. var texturePosBuffer = gl.createBuffer();
  308. gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer);
  309. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW);
  310. var texturePosRef = gl.getAttribLocation(program, 'texturePos');
  311. gl.enableVertexAttribArray(texturePosRef);
  312. gl.vertexAttribPointer(texturePosRef, 2, gl.FLOAT, false, 0, 0);
  313. this.texturePosBuffer = texturePosBuffer;
  314. if (this.type === "yuv420"){
  315. var uTexturePosBuffer = gl.createBuffer();
  316. gl.bindBuffer(gl.ARRAY_BUFFER, uTexturePosBuffer);
  317. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW);
  318. var uTexturePosRef = gl.getAttribLocation(program, 'uTexturePos');
  319. gl.enableVertexAttribArray(uTexturePosRef);
  320. gl.vertexAttribPointer(uTexturePosRef, 2, gl.FLOAT, false, 0, 0);
  321. this.uTexturePosBuffer = uTexturePosBuffer;
  322. var vTexturePosBuffer = gl.createBuffer();
  323. gl.bindBuffer(gl.ARRAY_BUFFER, vTexturePosBuffer);
  324. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW);
  325. var vTexturePosRef = gl.getAttribLocation(program, 'vTexturePos');
  326. gl.enableVertexAttribArray(vTexturePosRef);
  327. gl.vertexAttribPointer(vTexturePosRef, 2, gl.FLOAT, false, 0, 0);
  328. this.vTexturePosBuffer = vTexturePosBuffer;
  329. };
  330. };
  331. /**
  332. * 初始化GL纹理并附加两个着色器应用程序
  333. */
  334. YUVCanvas.prototype.initTextures = function() {
  335. var gl = this.contextGL;
  336. var program = this.shaderProgram;
  337. if (this.type === "yuv420"){
  338. var yTextureRef = this.initTexture();
  339. var ySamplerRef = gl.getUniformLocation(program, 'ySampler');
  340. gl.uniform1i(ySamplerRef, 0);
  341. this.yTextureRef = yTextureRef;
  342. var uTextureRef = this.initTexture();
  343. var uSamplerRef = gl.getUniformLocation(program, 'uSampler');
  344. gl.uniform1i(uSamplerRef, 1);
  345. this.uTextureRef = uTextureRef;
  346. var vTextureRef = this.initTexture();
  347. var vSamplerRef = gl.getUniformLocation(program, 'vSampler');
  348. gl.uniform1i(vSamplerRef, 2);
  349. this.vTextureRef = vTextureRef;
  350. }else if (this.type === "yuv422"){
  351. // only one texture for 422
  352. var textureRef = this.initTexture();
  353. var samplerRef = gl.getUniformLocation(program, 'sampler');
  354. gl.uniform1i(samplerRef, 0);
  355. this.textureRef = textureRef;
  356. };
  357. };
  358. /**
  359. * 创建和配置单个纹理
  360. */
  361. YUVCanvas.prototype.initTexture = function() {
  362. var gl = this.contextGL;
  363. var textureRef = gl.createTexture();
  364. gl.bindTexture(gl.TEXTURE_2D, textureRef);
  365. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  366. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  367. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  368. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  369. gl.bindTexture(gl.TEXTURE_2D, null);
  370. return textureRef;
  371. };
  372. //将图片数据绘制到画布上。
  373. //如果此对象使用WebGL,则数据必须是I420格式的ArrayBuffer,
  374. //否则,数据必须是RGBA格式的ArrayBuffer。
  375. YUVCanvas.prototype.drawNextOutputPicture = function(width, height, croppingParams, data) {
  376. var gl = this.contextGL;
  377. if(gl) {
  378. this.drawNextOuptutPictureGL(width, height, croppingParams, data);
  379. } else {
  380. this.drawNextOuptutPictureRGBA(width, height, croppingParams, data);
  381. }
  382. };
  383. /**
  384. *在二维画布上使用ARGB数据绘制下一个输出图片.
  385. */
  386. YUVCanvas.prototype.drawNextOuptutPictureRGBA = function(width, height, croppingParams, data) {
  387. var canvas = this.canvasElement;
  388. var croppingParams = null;
  389. var argbData = data;
  390. var ctx = canvas.getContext('2d');
  391. var imageData = ctx.getImageData(0, 0, width, height);
  392. imageData.data.set(argbData);
  393. if(croppingParams === null) {
  394. ctx.putImageData(imageData, 0, 0);
  395. } else {
  396. ctx.putImageData(imageData, -croppingParams.left, -croppingParams.top, 0, 0, croppingParams.width, croppingParams.height);
  397. }
  398. };
  399. return YUVCanvas;
  400. }));