vap.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global.howLongUntilLunch = factory());
  5. }(this, (function () { 'use strict';
  6. var classCallCheck = function (instance, Constructor) {
  7. if (!(instance instanceof Constructor)) {
  8. throw new TypeError("Cannot call a class as a function");
  9. }
  10. };
  11. var createClass = function () {
  12. function defineProperties(target, props) {
  13. for (var i = 0; i < props.length; i++) {
  14. var descriptor = props[i];
  15. descriptor.enumerable = descriptor.enumerable || false;
  16. descriptor.configurable = true;
  17. if ("value" in descriptor) descriptor.writable = true;
  18. Object.defineProperty(target, descriptor.key, descriptor);
  19. }
  20. }
  21. return function (Constructor, protoProps, staticProps) {
  22. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  23. if (staticProps) defineProperties(Constructor, staticProps);
  24. return Constructor;
  25. };
  26. }();
  27. var get = function get(object, property, receiver) {
  28. if (object === null) object = Function.prototype;
  29. var desc = Object.getOwnPropertyDescriptor(object, property);
  30. if (desc === undefined) {
  31. var parent = Object.getPrototypeOf(object);
  32. if (parent === null) {
  33. return undefined;
  34. } else {
  35. return get(parent, property, receiver);
  36. }
  37. } else if ("value" in desc) {
  38. return desc.value;
  39. } else {
  40. var getter = desc.get;
  41. if (getter === undefined) {
  42. return undefined;
  43. }
  44. return getter.call(receiver);
  45. }
  46. };
  47. var inherits = function (subClass, superClass) {
  48. if (typeof superClass !== "function" && superClass !== null) {
  49. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  50. }
  51. subClass.prototype = Object.create(superClass && superClass.prototype, {
  52. constructor: {
  53. value: subClass,
  54. enumerable: false,
  55. writable: true,
  56. configurable: true
  57. }
  58. });
  59. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  60. };
  61. var possibleConstructorReturn = function (self, call) {
  62. if (!self) {
  63. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  64. }
  65. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  66. };
  67. var slicedToArray = function () {
  68. function sliceIterator(arr, i) {
  69. var _arr = [];
  70. var _n = true;
  71. var _d = false;
  72. var _e = undefined;
  73. try {
  74. for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
  75. _arr.push(_s.value);
  76. if (i && _arr.length === i) break;
  77. }
  78. } catch (err) {
  79. _d = true;
  80. _e = err;
  81. } finally {
  82. try {
  83. if (!_n && _i["return"]) _i["return"]();
  84. } finally {
  85. if (_d) throw _e;
  86. }
  87. }
  88. return _arr;
  89. }
  90. return function (arr, i) {
  91. if (Array.isArray(arr)) {
  92. return arr;
  93. } else if (Symbol.iterator in Object(arr)) {
  94. return sliceIterator(arr, i);
  95. } else {
  96. throw new TypeError("Invalid attempt to destructure non-iterable instance");
  97. }
  98. };
  99. }();
  100. /*
  101. * Tencent is pleased to support the open source community by making vap available.
  102. *
  103. * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  104. *
  105. * Licensed under the MIT License (the "License"); you may not use this file except in
  106. * compliance with the License. You may obtain a copy of the License at
  107. *
  108. * http://opensource.org/licenses/MIT
  109. *
  110. * Unless required by applicable law or agreed to in writing, software distributed under the License is
  111. * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  112. * either express or implied. See the License for the specific language governing permissions and
  113. * limitations under the License.
  114. */
  115. var FrameParser = function () {
  116. function FrameParser(source, headData) {
  117. classCallCheck(this, FrameParser);
  118. this.config = source || {};
  119. this.headData = headData;
  120. this.frame = [];
  121. this.textureMap = {};
  122. }
  123. createClass(FrameParser, [{
  124. key: 'init',
  125. value: function init() {
  126. var _this2 = this;
  127. return Promise.resolve().then(function () {
  128. _this2.initCanvas();
  129. return _this2.parseSrc(_this2.config);
  130. }).then(function () {
  131. _this2.canvas.parentNode.removeChild(_this2.canvas);
  132. _this2.frame = _this2.config.frame || [];
  133. return _this2;
  134. });
  135. }
  136. }, {
  137. key: 'initCanvas',
  138. value: function initCanvas() {
  139. var canvas = document.createElement('canvas');
  140. var ctx = canvas.getContext('2d');
  141. canvas.style.display = 'none';
  142. document.body.appendChild(canvas);
  143. this.ctx = ctx;
  144. this.canvas = canvas;
  145. }
  146. }, {
  147. key: 'loadImg',
  148. value: function loadImg(url) {
  149. return new Promise(function (resolve, reject) {
  150. // console.log('load img:', url)
  151. var img = new Image();
  152. img.crossOrigin = 'anonymous';
  153. img.onload = function () {
  154. resolve(this);
  155. };
  156. img.onerror = function (e) {
  157. console.error('frame 资源加载失败:' + url);
  158. reject(new Error('frame 资源加载失败:' + url));
  159. };
  160. img.src = url;
  161. });
  162. }
  163. }, {
  164. key: 'parseSrc',
  165. value: function parseSrc(dataJson) {
  166. var _this = this;
  167. var src = this.srcData = {};
  168. return Promise.all((dataJson.src || []).map(function (item) {
  169. return Promise.resolve().then(function () {
  170. item.img = null;
  171. if (!_this.headData[item.srcTag.slice(1, item.srcTag.length - 1)]) {
  172. console.warn('vap: \u878D\u5408\u4FE1\u606F\u6CA1\u6709\u4F20\u5165\uFF1A' + item.srcTag);
  173. } else {
  174. return Promise.resolve().then(function () {
  175. if (item.srcType === 'txt') {
  176. item.textStr = item.srcTag.replace(/\[(.*)\]/, function ($0, $1) {
  177. return _this.headData[$1];
  178. });
  179. item.img = _this.makeTextImg(item);
  180. } else {
  181. if (item.srcType === 'img') {
  182. item.imgUrl = item.srcTag.replace(/\[(.*)\]/, function ($0, $1) {
  183. return _this.headData[$1];
  184. });
  185. return Promise.resolve().then(function () {
  186. return _this.loadImg(item.imgUrl + '?t=' + Date.now());
  187. }).then(function (_resp) {
  188. item.img = _resp;
  189. }).catch(function (e) {
  190. return Promise.resolve();
  191. });
  192. }
  193. }
  194. }).then(function () {
  195. if (item.img) {
  196. src[item.srcId] = item;
  197. }
  198. });
  199. }
  200. }).then(function () {});
  201. }));
  202. }
  203. /**
  204. * 文字转换图片
  205. * @param {*} param0
  206. */
  207. }, {
  208. key: 'makeTextImg',
  209. value: function makeTextImg(_ref) {
  210. var textStr = _ref.textStr,
  211. w = _ref.w,
  212. h = _ref.h,
  213. color = _ref.color,
  214. style = _ref.style;
  215. var ctx = this.ctx;
  216. ctx.canvas.width = w;
  217. ctx.canvas.height = h;
  218. var fontSize = Math.min(parseInt(w / textStr.length, 10), h - 8); // 需留一定间隙
  219. var font = [fontSize + 'px', 'Arial'];
  220. if (style === 'b') {
  221. font.unshift('bold');
  222. }
  223. ctx.font = font.join(' ');
  224. ctx.textBaseline = 'middle';
  225. ctx.textAlign = 'center';
  226. ctx.fillStyle = color;
  227. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  228. ctx.fillText(textStr, w / 2, h / 2);
  229. // console.log('frame : ' + textStr, ctx.canvas.toDataURL('image/png'))
  230. return ctx.getImageData(0, 0, w, h);
  231. }
  232. }, {
  233. key: 'getFrame',
  234. value: function getFrame(frame) {
  235. return this.frame.find(function (item) {
  236. return item.i === frame;
  237. });
  238. }
  239. }]);
  240. return FrameParser;
  241. }();
  242. /*
  243. * Tencent is pleased to support the open source community by making vap available.
  244. *
  245. * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  246. *
  247. * Licensed under the MIT License (the "License"); you may not use this file except in
  248. * compliance with the License. You may obtain a copy of the License at
  249. *
  250. * http://opensource.org/licenses/MIT
  251. *
  252. * Unless required by applicable law or agreed to in writing, software distributed under the License is
  253. * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  254. * either express or implied. See the License for the specific language governing permissions and
  255. * limitations under the License.
  256. */
  257. function createShader(gl, type, source) {
  258. var shader = gl.createShader(type);
  259. gl.shaderSource(shader, source);
  260. gl.compileShader(shader);
  261. // if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  262. // console.error(gl.getShaderInfoLog(shader))
  263. // }
  264. return shader;
  265. }
  266. function createProgram(gl, vertexShader, fragmentShader) {
  267. var program = gl.createProgram();
  268. gl.attachShader(program, vertexShader);
  269. gl.attachShader(program, fragmentShader);
  270. gl.linkProgram(program);
  271. // if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  272. // console.error(gl.getProgramInfoLog(program))
  273. // }
  274. gl.useProgram(program);
  275. return program;
  276. }
  277. function createTexture(gl, index, imgData) {
  278. var texture = gl.createTexture();
  279. var textrueIndex = gl.TEXTURE0 + index;
  280. gl.activeTexture(textrueIndex);
  281. gl.bindTexture(gl.TEXTURE_2D, texture);
  282. // gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
  283. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  284. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  285. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  286. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  287. if (imgData) {
  288. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imgData);
  289. }
  290. return texture;
  291. }
  292. /*
  293. * Tencent is pleased to support the open source community by making vap available.
  294. *
  295. * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  296. *
  297. * Licensed under the MIT License (the "License"); you may not use this file except in
  298. * compliance with the License. You may obtain a copy of the License at
  299. *
  300. * http://opensource.org/licenses/MIT
  301. *
  302. * Unless required by applicable law or agreed to in writing, software distributed under the License is
  303. * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  304. * either express or implied. See the License for the specific language governing permissions and
  305. * limitations under the License.
  306. */
  307. var VapVideo = function () {
  308. function VapVideo(options) {
  309. classCallCheck(this, VapVideo);
  310. if (!options.container || !options.src) {
  311. return console.warn('[Alpha video]: options container and src cannot be empty!');
  312. }
  313. this.options = Object.assign({
  314. // 视频url
  315. src: '',
  316. // 循环播放
  317. loop: false,
  318. fps: 20,
  319. // 视频宽度
  320. width: 375,
  321. // 视频高度
  322. height: 375,
  323. // 容器
  324. container: null,
  325. // 是否预加载视频资源
  326. precache: false,
  327. // 是否静音播放
  328. mute: false,
  329. config: ''
  330. }, options);
  331. this.fps = 20;
  332. this.requestAnim = this.requestAnimFunc(this.fps);
  333. this.container = this.options.container;
  334. if (!this.options.src || !this.options.config || !this.options.container) {
  335. console.error('参数出错:src(视频地址)、config(配置文件地址)、container(dom容器)');
  336. } else {
  337. // 创建video
  338. this.initVideo();
  339. }
  340. }
  341. createClass(VapVideo, [{
  342. key: 'precacheSource',
  343. value: function precacheSource(source) {
  344. var URL = window.webkitURL || window.URL;
  345. return new Promise(function (resolve, reject) {
  346. var xhr = new XMLHttpRequest();
  347. xhr.open("GET", source, true);
  348. xhr.responseType = "blob";
  349. xhr.onload = function () {
  350. if (xhr.status === 200 || xhr.status === 304) {
  351. var res = xhr.response;
  352. if (/iphone|ipad|ipod/i.test(navigator.userAgent)) {
  353. var fileReader = new FileReader();
  354. fileReader.onloadend = function () {
  355. var raw = atob(fileReader.result.slice(fileReader.result.indexOf(",") + 1));
  356. var buf = Array(raw.length);
  357. for (var d = 0; d < raw.length; d++) {
  358. buf[d] = raw.charCodeAt(d);
  359. }
  360. var arr = new Uint8Array(buf);
  361. var blob = new Blob([arr], { type: "video/mp4" });
  362. resolve(URL.createObjectURL(blob));
  363. };
  364. fileReader.readAsDataURL(xhr.response);
  365. } else {
  366. resolve(URL.createObjectURL(res));
  367. }
  368. } else {
  369. reject(new Error("http response invalid" + xhr.status));
  370. }
  371. };
  372. xhr.send();
  373. });
  374. }
  375. }, {
  376. key: 'initVideo',
  377. value: function initVideo() {
  378. var _this = this;
  379. var options = this.options;
  380. // 创建video
  381. var video = this.video = document.createElement('video');
  382. video.crossOrigin = 'anonymous';
  383. video.autoplay = false;
  384. video.preload = 'auto';
  385. video.autoload = true;
  386. if (options.mute) {
  387. video.muted = true;
  388. video.volume = 0;
  389. }
  390. video.style.display = 'none';
  391. video.loop = !!options.loop;
  392. if (options.precache) {
  393. this.precacheSource(options.src).then(function (blob) {
  394. console.log("sample precached.");
  395. video.src = blob;
  396. document.body.appendChild(video);
  397. }).catch(function (e) {
  398. console.error(e);
  399. });
  400. } else {
  401. video.src = options.src;
  402. // 这里要插在body上,避免container移动带来无法播放的问题
  403. document.body.appendChild(this.video);
  404. video.load();
  405. }
  406. // 绑定事件
  407. this.events = {};['playing', 'pause', 'ended', 'error'].forEach(function (item) {
  408. _this.on(item, _this['on' + item].bind(_this));
  409. });
  410. }
  411. }, {
  412. key: 'drawFrame',
  413. value: function drawFrame() {
  414. this._drawFrame = this._drawFrame || this.drawFrame.bind(this);
  415. this.animId = this.requestAnim(this._drawFrame);
  416. }
  417. }, {
  418. key: 'play',
  419. value: function play() {
  420. var _this2 = this;
  421. var prom = this.video && this.video.play();
  422. if (prom && prom.then) {
  423. prom.catch(function (e) {
  424. if (!_this2.video) {
  425. return;
  426. }
  427. _this2.video.muted = true;
  428. _this2.video.volume = 0;
  429. _this2.video.play().catch(function (e) {
  430. (_this2.events.error || []).forEach(function (item) {
  431. item(e);
  432. });
  433. });
  434. });
  435. }
  436. }
  437. }, {
  438. key: 'requestAnimFunc',
  439. value: function requestAnimFunc() {
  440. var me = this;
  441. if (window.requestAnimationFrame) {
  442. var index = -1;
  443. return function (cb) {
  444. index++;
  445. return requestAnimationFrame(function () {
  446. if (!(index % (60 / me.fps))) {
  447. return cb();
  448. }
  449. me.animId = me.requestAnim(cb);
  450. });
  451. };
  452. }
  453. return function (cb) {
  454. return setTimeout(cb, 1000 / me.fps);
  455. };
  456. }
  457. }, {
  458. key: 'cancelRequestAnimation',
  459. value: function cancelRequestAnimation() {
  460. if (window.cancelAnimationFrame) {
  461. cancelAnimationFrame(this.animId);
  462. }
  463. clearTimeout(this.animId);
  464. }
  465. }, {
  466. key: 'destroy',
  467. value: function destroy() {
  468. if (this.video) {
  469. this.video.parentNode && this.video.parentNode.removeChild(this.video);
  470. this.video = null;
  471. }
  472. this.cancelRequestAnimation(this.animId);
  473. }
  474. }, {
  475. key: 'clear',
  476. value: function clear() {
  477. this.destroy();
  478. }
  479. }, {
  480. key: 'on',
  481. value: function on(event, callback) {
  482. var cbs = this.events[event] || [];
  483. cbs.push(callback);
  484. this.events[event] = cbs;
  485. this.video.addEventListener(event, callback);
  486. return this;
  487. }
  488. }, {
  489. key: 'onplaying',
  490. value: function onplaying() {
  491. if (!this.firstPlaying) {
  492. this.firstPlaying = true;
  493. this.drawFrame();
  494. }
  495. }
  496. }, {
  497. key: 'onpause',
  498. value: function onpause() {}
  499. }, {
  500. key: 'onended',
  501. value: function onended() {
  502. this.destroy();
  503. }
  504. }, {
  505. key: 'onerror',
  506. value: function onerror(err) {
  507. console.error('[Alpha video]: play error: ', err);
  508. this.destroy();
  509. }
  510. }]);
  511. return VapVideo;
  512. }();
  513. /*
  514. * Tencent is pleased to support the open source community by making vap available.
  515. *
  516. * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  517. *
  518. * Licensed under the MIT License (the "License"); you may not use this file except in
  519. * compliance with the License. You may obtain a copy of the License at
  520. *
  521. * http://opensource.org/licenses/MIT
  522. *
  523. * Unless required by applicable law or agreed to in writing, software distributed under the License is
  524. * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  525. * either express or implied. See the License for the specific language governing permissions and
  526. * limitations under the License.
  527. */
  528. var clearTimer = null;
  529. var instances = {};
  530. var PER_SIZE = 9;
  531. function computeCoord(x, y, w, h, vw, vh) {
  532. // leftX rightX bottomY topY
  533. return [x / vw, (x + w) / vw, (vh - y - h) / vh, (vh - y) / vh];
  534. }
  535. var WebglRenderVap = function (_VapVideo) {
  536. inherits(WebglRenderVap, _VapVideo);
  537. function WebglRenderVap(options) {
  538. classCallCheck(this, WebglRenderVap);
  539. var _this = possibleConstructorReturn(this, (WebglRenderVap.__proto__ || Object.getPrototypeOf(WebglRenderVap)).call(this, options));
  540. _this.insType = _this.options.type;
  541. if (instances[_this.insType]) {
  542. _this.instance = instances[_this.insType];
  543. } else {
  544. _this.instance = instances[_this.insType] = {};
  545. }
  546. _this.textures = [];
  547. _this.buffers = [];
  548. _this.shaders = [];
  549. _this.init();
  550. return _this;
  551. }
  552. createClass(WebglRenderVap, [{
  553. key: 'init',
  554. value: function init() {
  555. var _this3 = this;
  556. return Promise.resolve().then(function () {
  557. _this3.setCanvas();
  558. if (_this3.options.config) {
  559. return Promise.resolve().then(function () {
  560. return new FrameParser(_this3.options.config, _this3.options).init();
  561. }).then(function (_resp) {
  562. _this3.vapFrameParser = _resp;
  563. _this3.resources = _this3.vapFrameParser.srcData;
  564. }).catch(function (e) {
  565. console.error('[Alpha video] parse vap frame error.', e);
  566. });
  567. }
  568. }).then(function () {
  569. _this3.resources = _this3.resources || {};
  570. _this3.initWebGL();
  571. _this3.play();
  572. });
  573. }
  574. }, {
  575. key: 'setCanvas',
  576. value: function setCanvas() {
  577. var canvas = this.instance.canvas;
  578. var _options = this.options,
  579. width = _options.width,
  580. height = _options.height;
  581. if (!canvas) {
  582. canvas = this.instance.canvas = document.createElement('canvas');
  583. }
  584. canvas.width = width;
  585. canvas.height = height;
  586. this.container.appendChild(canvas);
  587. }
  588. }, {
  589. key: 'initWebGL',
  590. value: function initWebGL() {
  591. var canvas = this.instance.canvas;
  592. var _instance = this.instance,
  593. gl = _instance.gl,
  594. vertexShader = _instance.vertexShader,
  595. fragmentShader = _instance.fragmentShader,
  596. program = _instance.program;
  597. if (!canvas) {
  598. return;
  599. }
  600. if (!gl) {
  601. this.instance.gl = gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
  602. gl.enable(gl.BLEND);
  603. gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  604. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
  605. }
  606. if (gl) {
  607. gl.viewport(0, 0, canvas.width, canvas.height);
  608. if (!vertexShader) {
  609. vertexShader = this.instance.vertexShader = this.initVertexShader();
  610. }
  611. if (!fragmentShader) {
  612. fragmentShader = this.instance.fragmentShader = this.initFragmentShader();
  613. }
  614. if (!program) {
  615. program = this.instance.program = createProgram(gl, vertexShader, fragmentShader);
  616. }
  617. this.program = program;
  618. this.initTexture();
  619. this.initVideoTexture();
  620. return gl;
  621. }
  622. }
  623. /**
  624. * 顶点着色器
  625. */
  626. }, {
  627. key: 'initVertexShader',
  628. value: function initVertexShader() {
  629. var gl = this.instance.gl;
  630. return createShader(gl, gl.VERTEX_SHADER, 'attribute vec2 a_position; // \u63A5\u53D7\u9876\u70B9\u5750\u6807\n attribute vec2 a_texCoord; // \u63A5\u53D7\u7EB9\u7406\u5750\u6807\n attribute vec2 a_alpha_texCoord; // \u63A5\u53D7\u7EB9\u7406\u5750\u6807\n varying vec2 v_alpha_texCoord; // \u63A5\u53D7\u7EB9\u7406\u5750\u6807\n varying vec2 v_texcoord; // \u4F20\u9012\u7EB9\u7406\u5750\u6807\u7ED9\u7247\u5143\u7740\u8272\u5668\n void main(void){\n gl_Position = vec4(a_position, 0.0, 1.0); // \u8BBE\u7F6E\u5750\u6807\n v_texcoord = a_texCoord; // \u8BBE\u7F6E\u7EB9\u7406\u5750\u6807\n v_alpha_texCoord = a_alpha_texCoord; // \u8BBE\u7F6E\u7EB9\u7406\u5750\u6807\n }');
  631. }
  632. /**
  633. * 片元着色器
  634. */
  635. }, {
  636. key: 'initFragmentShader',
  637. value: function initFragmentShader() {
  638. var gl = this.instance.gl;
  639. var bgColor = 'vec4(texture2D(u_image_video, v_texcoord).rgb, texture2D(u_image_video,v_alpha_texCoord).r);';
  640. var textureSize = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) - 1;
  641. // const textureSize =0
  642. var sourceTexure = '';
  643. var sourceUniform = '';
  644. if (textureSize > 0) {
  645. var imgColor = [];
  646. for (var i = 0; i < textureSize; i++) {
  647. imgColor.push('if(ndx == ' + i + '){\n color = texture2D(textures[' + i + '],uv);\n }');
  648. }
  649. sourceUniform = '\n uniform sampler2D u_image[' + textureSize + '];\n uniform float image_pos[' + textureSize * PER_SIZE + '];\n vec4 getSampleFromArray(sampler2D textures[' + textureSize + '], int ndx, vec2 uv) {\n vec4 color;\n ' + imgColor.join(' else ') + '\n return color;\n }\n ';
  650. sourceTexure = '\n vec4 srcColor,maskColor;\n vec2 srcTexcoord,maskTexcoord;\n int srcIndex;\n float x1,x2,y1,y2,mx1,mx2,my1,my2; //\u663E\u793A\u7684\u533A\u57DF\n\n for(int i=0;i<' + textureSize * PER_SIZE + ';i+= ' + PER_SIZE + '){\n if ((int(image_pos[i]) > 0)) {\n srcIndex = int(image_pos[i]);\n \n x1 = image_pos[i+1];\n x2 = image_pos[i+2];\n y1 = image_pos[i+3];\n y2 = image_pos[i+4];\n \n mx1 = image_pos[i+5];\n mx2 = image_pos[i+6];\n my1 = image_pos[i+7];\n my2 = image_pos[i+8];\n \n \n if (v_texcoord.s>x1 && v_texcoord.s<x2 && v_texcoord.t>y1 && v_texcoord.t<y2) {\n srcTexcoord = vec2((v_texcoord.s-x1)/(x2-x1),(v_texcoord.t-y1)/(y2-y1));\n maskTexcoord = vec2(mx1+srcTexcoord.s*(mx2-mx1),my1+srcTexcoord.t*(my2-my1));\n srcColor = getSampleFromArray(u_image,srcIndex,srcTexcoord);\n maskColor = texture2D(u_image_video, maskTexcoord);\n srcColor.a = srcColor.a*(maskColor.r);\n \n bgColor = vec4(srcColor.rgb*srcColor.a,srcColor.a) + (1.0-srcColor.a)*bgColor;\n \n } \n }\n }\n ';
  651. }
  652. var fragmentSharder = '\n precision lowp float;\n varying vec2 v_texcoord;\n varying vec2 v_alpha_texCoord;\n uniform sampler2D u_image_video;\n ' + sourceUniform + '\n \n void main(void) {\n vec4 bgColor = ' + bgColor + '\n ' + sourceTexure + '\n // bgColor = texture2D(u_image[0], v_texcoord);\n gl_FragColor = bgColor;\n }\n ';
  653. return createShader(gl, gl.FRAGMENT_SHADER, fragmentSharder);
  654. }
  655. }, {
  656. key: 'initTexture',
  657. value: function initTexture() {
  658. var gl = this.instance.gl;
  659. var i = 1;
  660. if (!this.vapFrameParser || !this.vapFrameParser.srcData) {
  661. return;
  662. }
  663. var resources = this.vapFrameParser.srcData;
  664. for (var key in resources) {
  665. var resource = resources[key];
  666. this.textures.push(createTexture(gl, i, resource.img));
  667. var _sampler = gl.getUniformLocation(this.program, 'u_image[' + i + ']');
  668. gl.uniform1i(_sampler, i);
  669. this.vapFrameParser.textureMap[resource.srcId] = i++;
  670. }
  671. var dumpTexture = gl.createTexture();
  672. gl.activeTexture(gl.TEXTURE0);
  673. gl.bindTexture(gl.TEXTURE_2D, dumpTexture);
  674. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
  675. this.videoTexture = createTexture(gl, i);
  676. var sampler = gl.getUniformLocation(this.program, 'u_image_video');
  677. gl.uniform1i(sampler, i);
  678. }
  679. }, {
  680. key: 'initVideoTexture',
  681. value: function initVideoTexture() {
  682. var gl = this.instance.gl;
  683. var vertexBuffer = gl.createBuffer();
  684. this.buffers.push(vertexBuffer);
  685. if (!this.vapFrameParser || !this.vapFrameParser.config || !this.vapFrameParser.config.info) {
  686. return;
  687. }
  688. var info = this.vapFrameParser.config.info;
  689. var ver = [];
  690. var vW = info.videoW,
  691. vH = info.videoH;
  692. var _info$rgbFrame = slicedToArray(info.rgbFrame, 4),
  693. rgbX = _info$rgbFrame[0],
  694. rgbY = _info$rgbFrame[1],
  695. rgbW = _info$rgbFrame[2],
  696. rgbH = _info$rgbFrame[3];
  697. var _info$aFrame = slicedToArray(info.aFrame, 4),
  698. aX = _info$aFrame[0],
  699. aY = _info$aFrame[1],
  700. aW = _info$aFrame[2],
  701. aH = _info$aFrame[3];
  702. var rgbCoord = computeCoord(rgbX, rgbY, rgbW, rgbH, vW, vH);
  703. var aCoord = computeCoord(aX, aY, aW, aH, vW, vH);
  704. ver.push.apply(ver, [-1, 1, rgbCoord[0], rgbCoord[3], aCoord[0], aCoord[3]]);
  705. ver.push.apply(ver, [1, 1, rgbCoord[1], rgbCoord[3], aCoord[1], aCoord[3]]);
  706. ver.push.apply(ver, [-1, -1, rgbCoord[0], rgbCoord[2], aCoord[0], aCoord[2]]);
  707. ver.push.apply(ver, [1, -1, rgbCoord[1], rgbCoord[2], aCoord[1], aCoord[2]]);
  708. var view = new Float32Array(ver);
  709. gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  710. gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW);
  711. this.aPosition = gl.getAttribLocation(this.program, 'a_position');
  712. gl.enableVertexAttribArray(this.aPosition);
  713. this.aTexCoord = gl.getAttribLocation(this.program, 'a_texCoord');
  714. gl.enableVertexAttribArray(this.aTexCoord);
  715. this.aAlphaTexCoord = gl.getAttribLocation(this.program, 'a_alpha_texCoord');
  716. gl.enableVertexAttribArray(this.aAlphaTexCoord);
  717. // 将缓冲区对象分配给a_position变量、a_texCoord变量
  718. var size = view.BYTES_PER_ELEMENT;
  719. gl.vertexAttribPointer(this.aPosition, 2, gl.FLOAT, false, size * 6, 0); // 顶点着色器位置
  720. gl.vertexAttribPointer(this.aTexCoord, 2, gl.FLOAT, false, size * 6, size * 2); // rgb像素位置
  721. gl.vertexAttribPointer(this.aAlphaTexCoord, 2, gl.FLOAT, false, size * 6, size * 4); // rgb像素位置
  722. }
  723. }, {
  724. key: 'drawFrame',
  725. value: function drawFrame() {
  726. var _this2 = this;
  727. var gl = this.instance.gl;
  728. if (!gl) {
  729. get(WebglRenderVap.prototype.__proto__ || Object.getPrototypeOf(WebglRenderVap.prototype), 'drawFrame', this).call(this);
  730. return;
  731. }
  732. gl.clear(gl.COLOR_BUFFER_BIT);
  733. if (this.vapFrameParser) {
  734. var frame = Math.floor(this.video.currentTime * this.options.fps);
  735. var frameData = this.vapFrameParser.getFrame(frame);
  736. var posArr = [];
  737. if (frameData && frameData.obj) {
  738. frameData.obj.forEach(function (frame, index) {
  739. posArr[posArr.length] = +_this2.vapFrameParser.textureMap[frame.srcId];
  740. var info = _this2.vapFrameParser.config.info;
  741. var vW = info.videoW,
  742. vH = info.videoH;
  743. var _frame$frame = slicedToArray(frame.frame, 4),
  744. x = _frame$frame[0],
  745. y = _frame$frame[1],
  746. w = _frame$frame[2],
  747. h = _frame$frame[3];
  748. var _frame$mFrame = slicedToArray(frame.mFrame, 4),
  749. mX = _frame$mFrame[0],
  750. mY = _frame$mFrame[1],
  751. mW = _frame$mFrame[2],
  752. mH = _frame$mFrame[3];
  753. var coord = computeCoord(x, y, w, h, vW, vH);
  754. var mCoord = computeCoord(mX, mY, mW, mH, vW, vH);
  755. posArr = posArr.concat(coord).concat(mCoord);
  756. });
  757. }
  758. //
  759. var size = (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) - 1) * PER_SIZE;
  760. posArr = posArr.concat(new Array(size - posArr.length).fill(0));
  761. this._imagePos = this._imagePos || gl.getUniformLocation(this.program, 'image_pos');
  762. gl.uniform1fv(this._imagePos, new Float32Array(posArr));
  763. }
  764. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, this.video); // 指定二维纹理方式
  765. gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  766. get(WebglRenderVap.prototype.__proto__ || Object.getPrototypeOf(WebglRenderVap.prototype), 'drawFrame', this).call(this);
  767. }
  768. }, {
  769. key: 'destroy',
  770. value: function destroy() {
  771. var _instance2 = this.instance,
  772. canvas = _instance2.canvas,
  773. gl = _instance2.gl;
  774. if (this.textures && this.textures.length) {
  775. for (var i = 0; i < this.textures.length; i++) {
  776. gl.deleteTexture(this.textures[i]);
  777. }
  778. }
  779. if (canvas) {
  780. canvas.parentNode && canvas.parentNode.removeChild(canvas);
  781. }
  782. // glUtil.cleanWebGL(gl, this.shaders, this.program, this.textures, this.buffers)
  783. get(WebglRenderVap.prototype.__proto__ || Object.getPrototypeOf(WebglRenderVap.prototype), 'destroy', this).call(this);
  784. this.clearMemoryCache();
  785. }
  786. }, {
  787. key: 'clearMemoryCache',
  788. value: function clearMemoryCache() {
  789. if (clearTimer) {
  790. clearTimeout(clearTimer);
  791. }
  792. clearTimer = setTimeout(function () {
  793. instances = {};
  794. }, 30 * 60 * 1000);
  795. }
  796. }]);
  797. return WebglRenderVap;
  798. }(VapVideo);
  799. /*
  800. * Tencent is pleased to support the open source community by making vap available.
  801. *
  802. * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  803. *
  804. * Licensed under the MIT License (the "License"); you may not use this file except in
  805. * compliance with the License. You may obtain a copy of the License at
  806. *
  807. * http://opensource.org/licenses/MIT
  808. *
  809. * Unless required by applicable law or agreed to in writing, software distributed under the License is
  810. * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  811. * either express or implied. See the License for the specific language governing permissions and
  812. * limitations under the License.
  813. */
  814. var isCanWebGL = void 0;
  815. /**
  816. * @param options
  817. * @constructor
  818. * @return {null}
  819. */
  820. function index (options) {
  821. if (canWebGL()) {
  822. return new WebglRenderVap(Object.assign({}, options));
  823. } else {
  824. throw new Error('your browser not support webgl');
  825. }
  826. }
  827. function canWebGL() {
  828. if (typeof isCanWebGL !== 'undefined') {
  829. return isCanWebGL;
  830. }
  831. try {
  832. if (!window.WebGLRenderingContext) {
  833. return false;
  834. }
  835. var canvas = document.createElement('canvas');
  836. var context = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
  837. isCanWebGL = !!context;
  838. context = null;
  839. } catch (err) {
  840. isCanWebGL = false;
  841. }
  842. return isCanWebGL;
  843. }
  844. return index;
  845. })));