BrowserCodeReader.js 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077
  1. "use strict";
  2. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  3. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  4. return new (P || (P = Promise))(function (resolve, reject) {
  5. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  6. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  7. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  8. step((generator = generator.apply(thisArg, _arguments || [])).next());
  9. });
  10. };
  11. var __generator = (this && this.__generator) || function (thisArg, body) {
  12. var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
  13. return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
  14. function verb(n) { return function (v) { return step([n, v]); }; }
  15. function step(op) {
  16. if (f) throw new TypeError("Generator is already executing.");
  17. while (_) try {
  18. if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
  19. if (y = 0, t) op = [op[0] & 2, t.value];
  20. switch (op[0]) {
  21. case 0: case 1: t = op; break;
  22. case 4: _.label++; return { value: op[1], done: false };
  23. case 5: _.label++; y = op[1]; op = [0]; continue;
  24. case 7: op = _.ops.pop(); _.trys.pop(); continue;
  25. default:
  26. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
  27. if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
  28. if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
  29. if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
  30. if (t[2]) _.ops.pop();
  31. _.trys.pop(); continue;
  32. }
  33. op = body.call(thisArg, _);
  34. } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
  35. if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
  36. }
  37. };
  38. var __values = (this && this.__values) || function(o) {
  39. var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
  40. if (m) return m.call(o);
  41. if (o && typeof o.length === "number") return {
  42. next: function () {
  43. if (o && i >= o.length) o = void 0;
  44. return { value: o && o[i++], done: !o };
  45. }
  46. };
  47. throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
  48. };
  49. Object.defineProperty(exports, "__esModule", { value: true });
  50. exports.BrowserCodeReader = void 0;
  51. var ArgumentException_1 = require("../core/ArgumentException");
  52. var BinaryBitmap_1 = require("../core/BinaryBitmap");
  53. var ChecksumException_1 = require("../core/ChecksumException");
  54. var HybridBinarizer_1 = require("../core/common/HybridBinarizer");
  55. var FormatException_1 = require("../core/FormatException");
  56. var NotFoundException_1 = require("../core/NotFoundException");
  57. var HTMLCanvasElementLuminanceSource_1 = require("./HTMLCanvasElementLuminanceSource");
  58. var VideoInputDevice_1 = require("./VideoInputDevice");
  59. /**
  60. * @deprecated Moving to @zxing/browser
  61. *
  62. * Base class for browser code reader.
  63. */
  64. var BrowserCodeReader = /** @class */ (function () {
  65. /**
  66. * Creates an instance of BrowserCodeReader.
  67. * @param {Reader} reader The reader instance to decode the barcode
  68. * @param {number} [timeBetweenScansMillis=500] the time delay between subsequent successful decode tries
  69. *
  70. * @memberOf BrowserCodeReader
  71. */
  72. function BrowserCodeReader(reader, timeBetweenScansMillis, _hints) {
  73. if (timeBetweenScansMillis === void 0) { timeBetweenScansMillis = 500; }
  74. this.reader = reader;
  75. this.timeBetweenScansMillis = timeBetweenScansMillis;
  76. this._hints = _hints;
  77. /**
  78. * This will break the loop.
  79. */
  80. this._stopContinuousDecode = false;
  81. /**
  82. * This will break the loop.
  83. */
  84. this._stopAsyncDecode = false;
  85. /**
  86. * Delay time between decode attempts made by the scanner.
  87. */
  88. this._timeBetweenDecodingAttempts = 0;
  89. }
  90. Object.defineProperty(BrowserCodeReader.prototype, "hasNavigator", {
  91. /**
  92. * If navigator is present.
  93. */
  94. get: function () {
  95. return typeof navigator !== 'undefined';
  96. },
  97. enumerable: false,
  98. configurable: true
  99. });
  100. Object.defineProperty(BrowserCodeReader.prototype, "isMediaDevicesSuported", {
  101. /**
  102. * If mediaDevices under navigator is supported.
  103. */
  104. get: function () {
  105. return this.hasNavigator && !!navigator.mediaDevices;
  106. },
  107. enumerable: false,
  108. configurable: true
  109. });
  110. Object.defineProperty(BrowserCodeReader.prototype, "canEnumerateDevices", {
  111. /**
  112. * If enumerateDevices under navigator is supported.
  113. */
  114. get: function () {
  115. return !!(this.isMediaDevicesSuported && navigator.mediaDevices.enumerateDevices);
  116. },
  117. enumerable: false,
  118. configurable: true
  119. });
  120. Object.defineProperty(BrowserCodeReader.prototype, "timeBetweenDecodingAttempts", {
  121. /** Time between two decoding tries in milli seconds. */
  122. get: function () {
  123. return this._timeBetweenDecodingAttempts;
  124. },
  125. /**
  126. * Change the time span the decoder waits between two decoding tries.
  127. *
  128. * @param {number} millis Time between two decoding tries in milli seconds.
  129. */
  130. set: function (millis) {
  131. this._timeBetweenDecodingAttempts = millis < 0 ? 0 : millis;
  132. },
  133. enumerable: false,
  134. configurable: true
  135. });
  136. Object.defineProperty(BrowserCodeReader.prototype, "hints", {
  137. /**
  138. * Sets the hints.
  139. */
  140. get: function () {
  141. return this._hints;
  142. },
  143. /**
  144. * Sets the hints.
  145. */
  146. set: function (hints) {
  147. this._hints = hints || null;
  148. },
  149. enumerable: false,
  150. configurable: true
  151. });
  152. /**
  153. * Lists all the available video input devices.
  154. */
  155. BrowserCodeReader.prototype.listVideoInputDevices = function () {
  156. return __awaiter(this, void 0, void 0, function () {
  157. var devices, videoDevices, devices_1, devices_1_1, device, kind, deviceId, label, groupId, videoDevice;
  158. var e_1, _a;
  159. return __generator(this, function (_b) {
  160. switch (_b.label) {
  161. case 0:
  162. if (!this.hasNavigator) {
  163. throw new Error("Can't enumerate devices, navigator is not present.");
  164. }
  165. if (!this.canEnumerateDevices) {
  166. throw new Error("Can't enumerate devices, method not supported.");
  167. }
  168. return [4 /*yield*/, navigator.mediaDevices.enumerateDevices()];
  169. case 1:
  170. devices = _b.sent();
  171. videoDevices = [];
  172. try {
  173. for (devices_1 = __values(devices), devices_1_1 = devices_1.next(); !devices_1_1.done; devices_1_1 = devices_1.next()) {
  174. device = devices_1_1.value;
  175. kind = device.kind === 'video' ? 'videoinput' : device.kind;
  176. if (kind !== 'videoinput') {
  177. continue;
  178. }
  179. deviceId = device.deviceId || device.id;
  180. label = device.label || "Video device " + (videoDevices.length + 1);
  181. groupId = device.groupId;
  182. videoDevice = { deviceId: deviceId, label: label, kind: kind, groupId: groupId };
  183. videoDevices.push(videoDevice);
  184. }
  185. }
  186. catch (e_1_1) { e_1 = { error: e_1_1 }; }
  187. finally {
  188. try {
  189. if (devices_1_1 && !devices_1_1.done && (_a = devices_1.return)) _a.call(devices_1);
  190. }
  191. finally { if (e_1) throw e_1.error; }
  192. }
  193. return [2 /*return*/, videoDevices];
  194. }
  195. });
  196. });
  197. };
  198. /**
  199. * Obtain the list of available devices with type 'videoinput'.
  200. *
  201. * @returns {Promise<VideoInputDevice[]>} an array of available video input devices
  202. *
  203. * @memberOf BrowserCodeReader
  204. *
  205. * @deprecated Use `listVideoInputDevices` instead.
  206. */
  207. BrowserCodeReader.prototype.getVideoInputDevices = function () {
  208. return __awaiter(this, void 0, void 0, function () {
  209. var devices;
  210. return __generator(this, function (_a) {
  211. switch (_a.label) {
  212. case 0: return [4 /*yield*/, this.listVideoInputDevices()];
  213. case 1:
  214. devices = _a.sent();
  215. return [2 /*return*/, devices.map(function (d) { return new VideoInputDevice_1.VideoInputDevice(d.deviceId, d.label); })];
  216. }
  217. });
  218. });
  219. };
  220. /**
  221. * Let's you find a device using it's Id.
  222. */
  223. BrowserCodeReader.prototype.findDeviceById = function (deviceId) {
  224. return __awaiter(this, void 0, void 0, function () {
  225. var devices;
  226. return __generator(this, function (_a) {
  227. switch (_a.label) {
  228. case 0: return [4 /*yield*/, this.listVideoInputDevices()];
  229. case 1:
  230. devices = _a.sent();
  231. if (!devices) {
  232. return [2 /*return*/, null];
  233. }
  234. return [2 /*return*/, devices.find(function (x) { return x.deviceId === deviceId; })];
  235. }
  236. });
  237. });
  238. };
  239. /**
  240. * Decodes the barcode from the device specified by deviceId while showing the video in the specified video element.
  241. *
  242. * @param deviceId the id of one of the devices obtained after calling getVideoInputDevices. Can be undefined, in this case it will decode from one of the available devices, preffering the main camera (environment facing) if available.
  243. * @param video the video element in page where to show the video while decoding. Can be either an element id or directly an HTMLVideoElement. Can be undefined, in which case no video will be shown.
  244. * @returns The decoding result.
  245. *
  246. * @memberOf BrowserCodeReader
  247. *
  248. * @deprecated Use `decodeOnceFromVideoDevice` instead.
  249. */
  250. BrowserCodeReader.prototype.decodeFromInputVideoDevice = function (deviceId, videoSource) {
  251. return __awaiter(this, void 0, void 0, function () {
  252. return __generator(this, function (_a) {
  253. switch (_a.label) {
  254. case 0: return [4 /*yield*/, this.decodeOnceFromVideoDevice(deviceId, videoSource)];
  255. case 1: return [2 /*return*/, _a.sent()];
  256. }
  257. });
  258. });
  259. };
  260. /**
  261. * In one attempt, tries to decode the barcode from the device specified by deviceId while showing the video in the specified video element.
  262. *
  263. * @param deviceId the id of one of the devices obtained after calling getVideoInputDevices. Can be undefined, in this case it will decode from one of the available devices, preffering the main camera (environment facing) if available.
  264. * @param video the video element in page where to show the video while decoding. Can be either an element id or directly an HTMLVideoElement. Can be undefined, in which case no video will be shown.
  265. * @returns The decoding result.
  266. *
  267. * @memberOf BrowserCodeReader
  268. */
  269. BrowserCodeReader.prototype.decodeOnceFromVideoDevice = function (deviceId, videoSource) {
  270. return __awaiter(this, void 0, void 0, function () {
  271. var videoConstraints, constraints;
  272. return __generator(this, function (_a) {
  273. switch (_a.label) {
  274. case 0:
  275. this.reset();
  276. if (!deviceId) {
  277. videoConstraints = { facingMode: 'environment' };
  278. }
  279. else {
  280. videoConstraints = { deviceId: { exact: deviceId } };
  281. }
  282. constraints = { video: videoConstraints };
  283. return [4 /*yield*/, this.decodeOnceFromConstraints(constraints, videoSource)];
  284. case 1: return [2 /*return*/, _a.sent()];
  285. }
  286. });
  287. });
  288. };
  289. /**
  290. * In one attempt, tries to decode the barcode from a stream obtained from the given constraints while showing the video in the specified video element.
  291. *
  292. * @param constraints the media stream constraints to get s valid media stream to decode from
  293. * @param video the video element in page where to show the video while decoding. Can be either an element id or directly an HTMLVideoElement. Can be undefined, in which case no video will be shown.
  294. * @returns The decoding result.
  295. *
  296. * @memberOf BrowserCodeReader
  297. */
  298. BrowserCodeReader.prototype.decodeOnceFromConstraints = function (constraints, videoSource) {
  299. return __awaiter(this, void 0, void 0, function () {
  300. var stream;
  301. return __generator(this, function (_a) {
  302. switch (_a.label) {
  303. case 0: return [4 /*yield*/, navigator.mediaDevices.getUserMedia(constraints)];
  304. case 1:
  305. stream = _a.sent();
  306. return [4 /*yield*/, this.decodeOnceFromStream(stream, videoSource)];
  307. case 2: return [2 /*return*/, _a.sent()];
  308. }
  309. });
  310. });
  311. };
  312. /**
  313. * In one attempt, tries to decode the barcode from a stream obtained from the given constraints while showing the video in the specified video element.
  314. *
  315. * @param {MediaStream} [constraints] the media stream constraints to get s valid media stream to decode from
  316. * @param {string|HTMLVideoElement} [video] the video element in page where to show the video while decoding. Can be either an element id or directly an HTMLVideoElement. Can be undefined, in which case no video will be shown.
  317. * @returns {Promise<Result>} The decoding result.
  318. *
  319. * @memberOf BrowserCodeReader
  320. */
  321. BrowserCodeReader.prototype.decodeOnceFromStream = function (stream, videoSource) {
  322. return __awaiter(this, void 0, void 0, function () {
  323. var video, result;
  324. return __generator(this, function (_a) {
  325. switch (_a.label) {
  326. case 0:
  327. this.reset();
  328. return [4 /*yield*/, this.attachStreamToVideo(stream, videoSource)];
  329. case 1:
  330. video = _a.sent();
  331. return [4 /*yield*/, this.decodeOnce(video)];
  332. case 2:
  333. result = _a.sent();
  334. return [2 /*return*/, result];
  335. }
  336. });
  337. });
  338. };
  339. /**
  340. * Continuously decodes the barcode from the device specified by device while showing the video in the specified video element.
  341. *
  342. * @param {string|null} [deviceId] the id of one of the devices obtained after calling getVideoInputDevices. Can be undefined, in this case it will decode from one of the available devices, preffering the main camera (environment facing) if available.
  343. * @param {string|HTMLVideoElement|null} [video] the video element in page where to show the video while decoding. Can be either an element id or directly an HTMLVideoElement. Can be undefined, in which case no video will be shown.
  344. * @returns {Promise<void>}
  345. *
  346. * @memberOf BrowserCodeReader
  347. *
  348. * @deprecated Use `decodeFromVideoDevice` instead.
  349. */
  350. BrowserCodeReader.prototype.decodeFromInputVideoDeviceContinuously = function (deviceId, videoSource, callbackFn) {
  351. return __awaiter(this, void 0, void 0, function () {
  352. return __generator(this, function (_a) {
  353. switch (_a.label) {
  354. case 0: return [4 /*yield*/, this.decodeFromVideoDevice(deviceId, videoSource, callbackFn)];
  355. case 1: return [2 /*return*/, _a.sent()];
  356. }
  357. });
  358. });
  359. };
  360. /**
  361. * Continuously tries to decode the barcode from the device specified by device while showing the video in the specified video element.
  362. *
  363. * @param {string|null} [deviceId] the id of one of the devices obtained after calling getVideoInputDevices. Can be undefined, in this case it will decode from one of the available devices, preffering the main camera (environment facing) if available.
  364. * @param {string|HTMLVideoElement|null} [video] the video element in page where to show the video while decoding. Can be either an element id or directly an HTMLVideoElement. Can be undefined, in which case no video will be shown.
  365. * @returns {Promise<void>}
  366. *
  367. * @memberOf BrowserCodeReader
  368. */
  369. BrowserCodeReader.prototype.decodeFromVideoDevice = function (deviceId, videoSource, callbackFn) {
  370. return __awaiter(this, void 0, void 0, function () {
  371. var videoConstraints, constraints;
  372. return __generator(this, function (_a) {
  373. switch (_a.label) {
  374. case 0:
  375. if (!deviceId) {
  376. videoConstraints = { facingMode: 'environment' };
  377. }
  378. else {
  379. videoConstraints = { deviceId: { exact: deviceId } };
  380. }
  381. constraints = { video: videoConstraints };
  382. return [4 /*yield*/, this.decodeFromConstraints(constraints, videoSource, callbackFn)];
  383. case 1: return [2 /*return*/, _a.sent()];
  384. }
  385. });
  386. });
  387. };
  388. /**
  389. * Continuously tries to decode the barcode from a stream obtained from the given constraints while showing the video in the specified video element.
  390. *
  391. * @param {MediaStream} [constraints] the media stream constraints to get s valid media stream to decode from
  392. * @param {string|HTMLVideoElement} [video] the video element in page where to show the video while decoding. Can be either an element id or directly an HTMLVideoElement. Can be undefined, in which case no video will be shown.
  393. * @returns {Promise<Result>} The decoding result.
  394. *
  395. * @memberOf BrowserCodeReader
  396. */
  397. BrowserCodeReader.prototype.decodeFromConstraints = function (constraints, videoSource, callbackFn) {
  398. return __awaiter(this, void 0, void 0, function () {
  399. var stream;
  400. return __generator(this, function (_a) {
  401. switch (_a.label) {
  402. case 0: return [4 /*yield*/, navigator.mediaDevices.getUserMedia(constraints)];
  403. case 1:
  404. stream = _a.sent();
  405. return [4 /*yield*/, this.decodeFromStream(stream, videoSource, callbackFn)];
  406. case 2: return [2 /*return*/, _a.sent()];
  407. }
  408. });
  409. });
  410. };
  411. /**
  412. * In one attempt, tries to decode the barcode from a stream obtained from the given constraints while showing the video in the specified video element.
  413. *
  414. * @param {MediaStream} [constraints] the media stream constraints to get s valid media stream to decode from
  415. * @param {string|HTMLVideoElement} [video] the video element in page where to show the video while decoding. Can be either an element id or directly an HTMLVideoElement. Can be undefined, in which case no video will be shown.
  416. * @returns {Promise<Result>} The decoding result.
  417. *
  418. * @memberOf BrowserCodeReader
  419. */
  420. BrowserCodeReader.prototype.decodeFromStream = function (stream, videoSource, callbackFn) {
  421. return __awaiter(this, void 0, void 0, function () {
  422. var video;
  423. return __generator(this, function (_a) {
  424. switch (_a.label) {
  425. case 0:
  426. this.reset();
  427. return [4 /*yield*/, this.attachStreamToVideo(stream, videoSource)];
  428. case 1:
  429. video = _a.sent();
  430. return [4 /*yield*/, this.decodeContinuously(video, callbackFn)];
  431. case 2: return [2 /*return*/, _a.sent()];
  432. }
  433. });
  434. });
  435. };
  436. /**
  437. * Breaks the decoding loop.
  438. */
  439. BrowserCodeReader.prototype.stopAsyncDecode = function () {
  440. this._stopAsyncDecode = true;
  441. };
  442. /**
  443. * Breaks the decoding loop.
  444. */
  445. BrowserCodeReader.prototype.stopContinuousDecode = function () {
  446. this._stopContinuousDecode = true;
  447. };
  448. /**
  449. * Sets the new stream and request a new decoding-with-delay.
  450. *
  451. * @param stream The stream to be shown in the video element.
  452. * @param decodeFn A callback for the decode method.
  453. */
  454. BrowserCodeReader.prototype.attachStreamToVideo = function (stream, videoSource) {
  455. return __awaiter(this, void 0, void 0, function () {
  456. var videoElement;
  457. return __generator(this, function (_a) {
  458. switch (_a.label) {
  459. case 0:
  460. videoElement = this.prepareVideoElement(videoSource);
  461. this.addVideoSource(videoElement, stream);
  462. this.videoElement = videoElement;
  463. this.stream = stream;
  464. return [4 /*yield*/, this.playVideoOnLoadAsync(videoElement)];
  465. case 1:
  466. _a.sent();
  467. return [2 /*return*/, videoElement];
  468. }
  469. });
  470. });
  471. };
  472. /**
  473. *
  474. * @param videoElement
  475. */
  476. BrowserCodeReader.prototype.playVideoOnLoadAsync = function (videoElement) {
  477. var _this = this;
  478. return new Promise(function (resolve, reject) {
  479. return _this.playVideoOnLoad(videoElement, function () { return resolve(); });
  480. });
  481. };
  482. /**
  483. * Binds listeners and callbacks to the videoElement.
  484. *
  485. * @param element
  486. * @param callbackFn
  487. */
  488. BrowserCodeReader.prototype.playVideoOnLoad = function (element, callbackFn) {
  489. var _this = this;
  490. this.videoEndedListener = function () { return _this.stopStreams(); };
  491. this.videoCanPlayListener = function () { return _this.tryPlayVideo(element); };
  492. element.addEventListener('ended', this.videoEndedListener);
  493. element.addEventListener('canplay', this.videoCanPlayListener);
  494. element.addEventListener('playing', callbackFn);
  495. // if canplay was already fired, we won't know when to play, so just give it a try
  496. this.tryPlayVideo(element);
  497. };
  498. /**
  499. * Checks if the given video element is currently playing.
  500. */
  501. BrowserCodeReader.prototype.isVideoPlaying = function (video) {
  502. return (video.currentTime > 0 &&
  503. !video.paused &&
  504. !video.ended &&
  505. video.readyState > 2);
  506. };
  507. /**
  508. * Just tries to play the video and logs any errors.
  509. * The play call is only made is the video is not already playing.
  510. */
  511. BrowserCodeReader.prototype.tryPlayVideo = function (videoElement) {
  512. return __awaiter(this, void 0, void 0, function () {
  513. var _a;
  514. return __generator(this, function (_b) {
  515. switch (_b.label) {
  516. case 0:
  517. if (this.isVideoPlaying(videoElement)) {
  518. console.warn('Trying to play video that is already playing.');
  519. return [2 /*return*/];
  520. }
  521. _b.label = 1;
  522. case 1:
  523. _b.trys.push([1, 3, , 4]);
  524. return [4 /*yield*/, videoElement.play()];
  525. case 2:
  526. _b.sent();
  527. return [3 /*break*/, 4];
  528. case 3:
  529. _a = _b.sent();
  530. console.warn('It was not possible to play the video.');
  531. return [3 /*break*/, 4];
  532. case 4: return [2 /*return*/];
  533. }
  534. });
  535. });
  536. };
  537. /**
  538. * Searches and validates a media element.
  539. */
  540. BrowserCodeReader.prototype.getMediaElement = function (mediaElementId, type) {
  541. var mediaElement = document.getElementById(mediaElementId);
  542. if (!mediaElement) {
  543. throw new ArgumentException_1.default("element with id '" + mediaElementId + "' not found");
  544. }
  545. if (mediaElement.nodeName.toLowerCase() !== type.toLowerCase()) {
  546. throw new ArgumentException_1.default("element with id '" + mediaElementId + "' must be an " + type + " element");
  547. }
  548. return mediaElement;
  549. };
  550. /**
  551. * Decodes the barcode from an image.
  552. *
  553. * @param {(string|HTMLImageElement)} [source] The image element that can be either an element id or the element itself. Can be undefined in which case the decoding will be done from the imageUrl parameter.
  554. * @param {string} [url]
  555. * @returns {Promise<Result>} The decoding result.
  556. *
  557. * @memberOf BrowserCodeReader
  558. */
  559. BrowserCodeReader.prototype.decodeFromImage = function (source, url) {
  560. if (!source && !url) {
  561. throw new ArgumentException_1.default('either imageElement with a src set or an url must be provided');
  562. }
  563. if (url && !source) {
  564. return this.decodeFromImageUrl(url);
  565. }
  566. return this.decodeFromImageElement(source);
  567. };
  568. /**
  569. * Decodes the barcode from a video.
  570. *
  571. * @param {(string|HTMLImageElement)} [source] The image element that can be either an element id or the element itself. Can be undefined in which case the decoding will be done from the imageUrl parameter.
  572. * @param {string} [url]
  573. * @returns {Promise<Result>} The decoding result.
  574. *
  575. * @memberOf BrowserCodeReader
  576. */
  577. BrowserCodeReader.prototype.decodeFromVideo = function (source, url) {
  578. if (!source && !url) {
  579. throw new ArgumentException_1.default('Either an element with a src set or an URL must be provided');
  580. }
  581. if (url && !source) {
  582. return this.decodeFromVideoUrl(url);
  583. }
  584. return this.decodeFromVideoElement(source);
  585. };
  586. /**
  587. * Decodes continuously the barcode from a video.
  588. *
  589. * @param {(string|HTMLImageElement)} [source] The image element that can be either an element id or the element itself. Can be undefined in which case the decoding will be done from the imageUrl parameter.
  590. * @param {string} [url]
  591. * @returns {Promise<Result>} The decoding result.
  592. *
  593. * @memberOf BrowserCodeReader
  594. *
  595. * @experimental
  596. */
  597. BrowserCodeReader.prototype.decodeFromVideoContinuously = function (source, url, callbackFn) {
  598. if (undefined === source && undefined === url) {
  599. throw new ArgumentException_1.default('Either an element with a src set or an URL must be provided');
  600. }
  601. if (url && !source) {
  602. return this.decodeFromVideoUrlContinuously(url, callbackFn);
  603. }
  604. return this.decodeFromVideoElementContinuously(source, callbackFn);
  605. };
  606. /**
  607. * Decodes something from an image HTML element.
  608. */
  609. BrowserCodeReader.prototype.decodeFromImageElement = function (source) {
  610. if (!source) {
  611. throw new ArgumentException_1.default('An image element must be provided.');
  612. }
  613. this.reset();
  614. var element = this.prepareImageElement(source);
  615. this.imageElement = element;
  616. var task;
  617. if (this.isImageLoaded(element)) {
  618. task = this.decodeOnce(element, false, true);
  619. }
  620. else {
  621. task = this._decodeOnLoadImage(element);
  622. }
  623. return task;
  624. };
  625. /**
  626. * Decodes something from an image HTML element.
  627. */
  628. BrowserCodeReader.prototype.decodeFromVideoElement = function (source) {
  629. var element = this._decodeFromVideoElementSetup(source);
  630. return this._decodeOnLoadVideo(element);
  631. };
  632. /**
  633. * Decodes something from an image HTML element.
  634. */
  635. BrowserCodeReader.prototype.decodeFromVideoElementContinuously = function (source, callbackFn) {
  636. var element = this._decodeFromVideoElementSetup(source);
  637. return this._decodeOnLoadVideoContinuously(element, callbackFn);
  638. };
  639. /**
  640. * Sets up the video source so it can be decoded when loaded.
  641. *
  642. * @param source The video source element.
  643. */
  644. BrowserCodeReader.prototype._decodeFromVideoElementSetup = function (source) {
  645. if (!source) {
  646. throw new ArgumentException_1.default('A video element must be provided.');
  647. }
  648. this.reset();
  649. var element = this.prepareVideoElement(source);
  650. // defines the video element before starts decoding
  651. this.videoElement = element;
  652. return element;
  653. };
  654. /**
  655. * Decodes an image from a URL.
  656. */
  657. BrowserCodeReader.prototype.decodeFromImageUrl = function (url) {
  658. if (!url) {
  659. throw new ArgumentException_1.default('An URL must be provided.');
  660. }
  661. this.reset();
  662. var element = this.prepareImageElement();
  663. this.imageElement = element;
  664. var decodeTask = this._decodeOnLoadImage(element);
  665. element.src = url;
  666. return decodeTask;
  667. };
  668. /**
  669. * Decodes an image from a URL.
  670. */
  671. BrowserCodeReader.prototype.decodeFromVideoUrl = function (url) {
  672. if (!url) {
  673. throw new ArgumentException_1.default('An URL must be provided.');
  674. }
  675. this.reset();
  676. // creates a new element
  677. var element = this.prepareVideoElement();
  678. var decodeTask = this.decodeFromVideoElement(element);
  679. element.src = url;
  680. return decodeTask;
  681. };
  682. /**
  683. * Decodes an image from a URL.
  684. *
  685. * @experimental
  686. */
  687. BrowserCodeReader.prototype.decodeFromVideoUrlContinuously = function (url, callbackFn) {
  688. if (!url) {
  689. throw new ArgumentException_1.default('An URL must be provided.');
  690. }
  691. this.reset();
  692. // creates a new element
  693. var element = this.prepareVideoElement();
  694. var decodeTask = this.decodeFromVideoElementContinuously(element, callbackFn);
  695. element.src = url;
  696. return decodeTask;
  697. };
  698. BrowserCodeReader.prototype._decodeOnLoadImage = function (element) {
  699. var _this = this;
  700. return new Promise(function (resolve, reject) {
  701. _this.imageLoadedListener = function () {
  702. return _this.decodeOnce(element, false, true).then(resolve, reject);
  703. };
  704. element.addEventListener('load', _this.imageLoadedListener);
  705. });
  706. };
  707. BrowserCodeReader.prototype._decodeOnLoadVideo = function (videoElement) {
  708. return __awaiter(this, void 0, void 0, function () {
  709. return __generator(this, function (_a) {
  710. switch (_a.label) {
  711. case 0:
  712. // plays the video
  713. return [4 /*yield*/, this.playVideoOnLoadAsync(videoElement)];
  714. case 1:
  715. // plays the video
  716. _a.sent();
  717. return [4 /*yield*/, this.decodeOnce(videoElement)];
  718. case 2:
  719. // starts decoding after played the video
  720. return [2 /*return*/, _a.sent()];
  721. }
  722. });
  723. });
  724. };
  725. BrowserCodeReader.prototype._decodeOnLoadVideoContinuously = function (videoElement, callbackFn) {
  726. return __awaiter(this, void 0, void 0, function () {
  727. return __generator(this, function (_a) {
  728. switch (_a.label) {
  729. case 0:
  730. // plays the video
  731. return [4 /*yield*/, this.playVideoOnLoadAsync(videoElement)];
  732. case 1:
  733. // plays the video
  734. _a.sent();
  735. // starts decoding after played the video
  736. this.decodeContinuously(videoElement, callbackFn);
  737. return [2 /*return*/];
  738. }
  739. });
  740. });
  741. };
  742. BrowserCodeReader.prototype.isImageLoaded = function (img) {
  743. // During the onload event, IE correctly identifies any images that
  744. // weren’t downloaded as not complete. Others should too. Gecko-based
  745. // browsers act like NS4 in that they report this incorrectly.
  746. if (!img.complete) {
  747. return false;
  748. }
  749. // However, they do have two very useful properties: naturalWidth and
  750. // naturalHeight. These give the true size of the image. If it failed
  751. // to load, either of these should be zero.
  752. if (img.naturalWidth === 0) {
  753. return false;
  754. }
  755. // No other way of checking: assume it’s ok.
  756. return true;
  757. };
  758. BrowserCodeReader.prototype.prepareImageElement = function (imageSource) {
  759. var imageElement;
  760. if (typeof imageSource === 'undefined') {
  761. imageElement = document.createElement('img');
  762. imageElement.width = 200;
  763. imageElement.height = 200;
  764. }
  765. if (typeof imageSource === 'string') {
  766. imageElement = this.getMediaElement(imageSource, 'img');
  767. }
  768. if (imageSource instanceof HTMLImageElement) {
  769. imageElement = imageSource;
  770. }
  771. return imageElement;
  772. };
  773. /**
  774. * Sets a HTMLVideoElement for scanning or creates a new one.
  775. *
  776. * @param videoSource The HTMLVideoElement to be set.
  777. */
  778. BrowserCodeReader.prototype.prepareVideoElement = function (videoSource) {
  779. var videoElement;
  780. if (!videoSource && typeof document !== 'undefined') {
  781. videoElement = document.createElement('video');
  782. videoElement.width = 200;
  783. videoElement.height = 200;
  784. }
  785. if (typeof videoSource === 'string') {
  786. videoElement = (this.getMediaElement(videoSource, 'video'));
  787. }
  788. if (videoSource instanceof HTMLVideoElement) {
  789. videoElement = videoSource;
  790. }
  791. // Needed for iOS 11
  792. videoElement.setAttribute('autoplay', 'true');
  793. videoElement.setAttribute('muted', 'true');
  794. videoElement.setAttribute('playsinline', 'true');
  795. return videoElement;
  796. };
  797. /**
  798. * Tries to decode from the video input until it finds some value.
  799. */
  800. BrowserCodeReader.prototype.decodeOnce = function (element, retryIfNotFound, retryIfChecksumOrFormatError) {
  801. var _this = this;
  802. if (retryIfNotFound === void 0) { retryIfNotFound = true; }
  803. if (retryIfChecksumOrFormatError === void 0) { retryIfChecksumOrFormatError = true; }
  804. this._stopAsyncDecode = false;
  805. var loop = function (resolve, reject) {
  806. if (_this._stopAsyncDecode) {
  807. reject(new NotFoundException_1.default('Video stream has ended before any code could be detected.'));
  808. _this._stopAsyncDecode = undefined;
  809. return;
  810. }
  811. try {
  812. var result = _this.decode(element);
  813. resolve(result);
  814. }
  815. catch (e) {
  816. var ifNotFound = retryIfNotFound && e instanceof NotFoundException_1.default;
  817. var isChecksumOrFormatError = e instanceof ChecksumException_1.default || e instanceof FormatException_1.default;
  818. var ifChecksumOrFormat = isChecksumOrFormatError && retryIfChecksumOrFormatError;
  819. if (ifNotFound || ifChecksumOrFormat) {
  820. // trying again
  821. return setTimeout(loop, _this._timeBetweenDecodingAttempts, resolve, reject);
  822. }
  823. reject(e);
  824. }
  825. };
  826. return new Promise(function (resolve, reject) { return loop(resolve, reject); });
  827. };
  828. /**
  829. * Continuously decodes from video input.
  830. */
  831. BrowserCodeReader.prototype.decodeContinuously = function (element, callbackFn) {
  832. var _this = this;
  833. this._stopContinuousDecode = false;
  834. var loop = function () {
  835. if (_this._stopContinuousDecode) {
  836. _this._stopContinuousDecode = undefined;
  837. return;
  838. }
  839. try {
  840. var result = _this.decode(element);
  841. callbackFn(result, null);
  842. setTimeout(loop, _this.timeBetweenScansMillis);
  843. }
  844. catch (e) {
  845. callbackFn(null, e);
  846. var isChecksumOrFormatError = e instanceof ChecksumException_1.default || e instanceof FormatException_1.default;
  847. var isNotFound = e instanceof NotFoundException_1.default;
  848. if (isChecksumOrFormatError || isNotFound) {
  849. // trying again
  850. setTimeout(loop, _this._timeBetweenDecodingAttempts);
  851. }
  852. }
  853. };
  854. loop();
  855. };
  856. /**
  857. * Gets the BinaryBitmap for ya! (and decodes it)
  858. */
  859. BrowserCodeReader.prototype.decode = function (element) {
  860. // get binary bitmap for decode function
  861. var binaryBitmap = this.createBinaryBitmap(element);
  862. return this.decodeBitmap(binaryBitmap);
  863. };
  864. /**
  865. * Creates a binaryBitmap based in some image source.
  866. *
  867. * @param mediaElement HTML element containing drawable image source.
  868. */
  869. BrowserCodeReader.prototype.createBinaryBitmap = function (mediaElement) {
  870. var ctx = this.getCaptureCanvasContext(mediaElement);
  871. if (mediaElement instanceof HTMLVideoElement) {
  872. this.drawFrameOnCanvas(mediaElement);
  873. }
  874. else {
  875. this.drawImageOnCanvas(mediaElement);
  876. }
  877. var canvas = this.getCaptureCanvas(mediaElement);
  878. var luminanceSource = new HTMLCanvasElementLuminanceSource_1.HTMLCanvasElementLuminanceSource(canvas);
  879. var hybridBinarizer = new HybridBinarizer_1.default(luminanceSource);
  880. return new BinaryBitmap_1.default(hybridBinarizer);
  881. };
  882. /**
  883. *
  884. */
  885. BrowserCodeReader.prototype.getCaptureCanvasContext = function (mediaElement) {
  886. if (!this.captureCanvasContext) {
  887. var elem = this.getCaptureCanvas(mediaElement);
  888. var ctx = void 0;
  889. try {
  890. ctx = elem.getContext('2d', { willReadFrequently: true });
  891. }
  892. catch (e) {
  893. ctx = elem.getContext('2d');
  894. }
  895. this.captureCanvasContext = ctx;
  896. }
  897. return this.captureCanvasContext;
  898. };
  899. /**
  900. *
  901. */
  902. BrowserCodeReader.prototype.getCaptureCanvas = function (mediaElement) {
  903. if (!this.captureCanvas) {
  904. var elem = this.createCaptureCanvas(mediaElement);
  905. this.captureCanvas = elem;
  906. }
  907. return this.captureCanvas;
  908. };
  909. /**
  910. * Overwriting this allows you to manipulate the next frame in anyway you want before decode.
  911. */
  912. BrowserCodeReader.prototype.drawFrameOnCanvas = function (srcElement, dimensions, canvasElementContext) {
  913. if (dimensions === void 0) { dimensions = {
  914. sx: 0,
  915. sy: 0,
  916. sWidth: srcElement.videoWidth,
  917. sHeight: srcElement.videoHeight,
  918. dx: 0,
  919. dy: 0,
  920. dWidth: srcElement.videoWidth,
  921. dHeight: srcElement.videoHeight,
  922. }; }
  923. if (canvasElementContext === void 0) { canvasElementContext = this.captureCanvasContext; }
  924. canvasElementContext.drawImage(srcElement, dimensions.sx, dimensions.sy, dimensions.sWidth, dimensions.sHeight, dimensions.dx, dimensions.dy, dimensions.dWidth, dimensions.dHeight);
  925. };
  926. /**
  927. * Ovewriting this allows you to manipulate the snapshot image in anyway you want before decode.
  928. */
  929. BrowserCodeReader.prototype.drawImageOnCanvas = function (srcElement, dimensions, canvasElementContext) {
  930. if (dimensions === void 0) { dimensions = {
  931. sx: 0,
  932. sy: 0,
  933. sWidth: srcElement.naturalWidth,
  934. sHeight: srcElement.naturalHeight,
  935. dx: 0,
  936. dy: 0,
  937. dWidth: srcElement.naturalWidth,
  938. dHeight: srcElement.naturalHeight,
  939. }; }
  940. if (canvasElementContext === void 0) { canvasElementContext = this.captureCanvasContext; }
  941. canvasElementContext.drawImage(srcElement, dimensions.sx, dimensions.sy, dimensions.sWidth, dimensions.sHeight, dimensions.dx, dimensions.dy, dimensions.dWidth, dimensions.dHeight);
  942. };
  943. /**
  944. * Call the encapsulated readers decode
  945. */
  946. BrowserCodeReader.prototype.decodeBitmap = function (binaryBitmap) {
  947. return this.reader.decode(binaryBitmap, this._hints);
  948. };
  949. /**
  950. * 🖌 Prepares the canvas for capture and scan frames.
  951. */
  952. BrowserCodeReader.prototype.createCaptureCanvas = function (mediaElement) {
  953. if (typeof document === 'undefined') {
  954. this._destroyCaptureCanvas();
  955. return null;
  956. }
  957. var canvasElement = document.createElement('canvas');
  958. var width;
  959. var height;
  960. if (typeof mediaElement !== 'undefined') {
  961. if (mediaElement instanceof HTMLVideoElement) {
  962. width = mediaElement.videoWidth;
  963. height = mediaElement.videoHeight;
  964. }
  965. else if (mediaElement instanceof HTMLImageElement) {
  966. width = mediaElement.naturalWidth || mediaElement.width;
  967. height = mediaElement.naturalHeight || mediaElement.height;
  968. }
  969. }
  970. canvasElement.style.width = width + 'px';
  971. canvasElement.style.height = height + 'px';
  972. canvasElement.width = width;
  973. canvasElement.height = height;
  974. return canvasElement;
  975. };
  976. /**
  977. * Stops the continuous scan and cleans the stream.
  978. */
  979. BrowserCodeReader.prototype.stopStreams = function () {
  980. if (this.stream) {
  981. this.stream.getVideoTracks().forEach(function (t) { return t.stop(); });
  982. this.stream = undefined;
  983. }
  984. if (this._stopAsyncDecode === false) {
  985. this.stopAsyncDecode();
  986. }
  987. if (this._stopContinuousDecode === false) {
  988. this.stopContinuousDecode();
  989. }
  990. };
  991. /**
  992. * Resets the code reader to the initial state. Cancels any ongoing barcode scanning from video or camera.
  993. *
  994. * @memberOf BrowserCodeReader
  995. */
  996. BrowserCodeReader.prototype.reset = function () {
  997. // stops the camera, preview and scan 🔴
  998. this.stopStreams();
  999. // clean and forget about HTML elements
  1000. this._destroyVideoElement();
  1001. this._destroyImageElement();
  1002. this._destroyCaptureCanvas();
  1003. };
  1004. BrowserCodeReader.prototype._destroyVideoElement = function () {
  1005. if (!this.videoElement) {
  1006. return;
  1007. }
  1008. // first gives freedon to the element 🕊
  1009. if (typeof this.videoEndedListener !== 'undefined') {
  1010. this.videoElement.removeEventListener('ended', this.videoEndedListener);
  1011. }
  1012. if (typeof this.videoPlayingEventListener !== 'undefined') {
  1013. this.videoElement.removeEventListener('playing', this.videoPlayingEventListener);
  1014. }
  1015. if (typeof this.videoCanPlayListener !== 'undefined') {
  1016. this.videoElement.removeEventListener('loadedmetadata', this.videoCanPlayListener);
  1017. }
  1018. // then forgets about that element 😢
  1019. this.cleanVideoSource(this.videoElement);
  1020. this.videoElement = undefined;
  1021. };
  1022. BrowserCodeReader.prototype._destroyImageElement = function () {
  1023. if (!this.imageElement) {
  1024. return;
  1025. }
  1026. // first gives freedon to the element 🕊
  1027. if (undefined !== this.imageLoadedListener) {
  1028. this.imageElement.removeEventListener('load', this.imageLoadedListener);
  1029. }
  1030. // then forget about that element 😢
  1031. this.imageElement.src = undefined;
  1032. this.imageElement.removeAttribute('src');
  1033. this.imageElement = undefined;
  1034. };
  1035. /**
  1036. * Cleans canvas references 🖌
  1037. */
  1038. BrowserCodeReader.prototype._destroyCaptureCanvas = function () {
  1039. // then forget about that element 😢
  1040. this.captureCanvasContext = undefined;
  1041. this.captureCanvas = undefined;
  1042. };
  1043. /**
  1044. * Defines what the videoElement src will be.
  1045. *
  1046. * @param videoElement
  1047. * @param stream
  1048. */
  1049. BrowserCodeReader.prototype.addVideoSource = function (videoElement, stream) {
  1050. // Older browsers may not have `srcObject`
  1051. try {
  1052. // @note Throws Exception if interrupted by a new loaded request
  1053. videoElement.srcObject = stream;
  1054. }
  1055. catch (err) {
  1056. // @note Avoid using this in new browsers, as it is going away.
  1057. // @ts-ignore
  1058. videoElement.src = URL.createObjectURL(stream);
  1059. }
  1060. };
  1061. /**
  1062. * Unbinds a HTML video src property.
  1063. *
  1064. * @param videoElement
  1065. */
  1066. BrowserCodeReader.prototype.cleanVideoSource = function (videoElement) {
  1067. try {
  1068. videoElement.srcObject = null;
  1069. }
  1070. catch (err) {
  1071. videoElement.src = '';
  1072. }
  1073. this.videoElement.removeAttribute('src');
  1074. };
  1075. return BrowserCodeReader;
  1076. }());
  1077. exports.BrowserCodeReader = BrowserCodeReader;