flash-source-buffer.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. /**
  2. * @file flash-source-buffer.js
  3. */
  4. 'use strict';
  5. Object.defineProperty(exports, '__esModule', {
  6. value: true
  7. });
  8. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  9. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  10. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  12. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  13. var _globalWindow = require('global/window');
  14. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  15. var _videoJs = require('video.js');
  16. var _videoJs2 = _interopRequireDefault(_videoJs);
  17. var _muxJsLibFlv = require('mux.js/lib/flv');
  18. var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
  19. var _removeCuesFromTrack = require('./remove-cues-from-track');
  20. var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
  21. var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
  22. var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
  23. var _addTextTrackData = require('./add-text-track-data');
  24. var _flashTransmuxerWorker = require('./flash-transmuxer-worker');
  25. var _flashTransmuxerWorker2 = _interopRequireDefault(_flashTransmuxerWorker);
  26. var _webwackify = require('webwackify');
  27. var _webwackify2 = _interopRequireDefault(_webwackify);
  28. var _flashConstants = require('./flash-constants');
  29. var _flashConstants2 = _interopRequireDefault(_flashConstants);
  30. var resolveFlashTransmuxWorker = function resolveFlashTransmuxWorker() {
  31. var result = undefined;
  32. try {
  33. result = require.resolve('./flash-transmuxer-worker');
  34. } catch (e) {
  35. // no result
  36. }
  37. return result;
  38. };
  39. /**
  40. * A wrapper around the setTimeout function that uses
  41. * the flash constant time between ticks value.
  42. *
  43. * @param {Function} func the function callback to run
  44. * @private
  45. */
  46. var scheduleTick = function scheduleTick(func) {
  47. // Chrome doesn't invoke requestAnimationFrame callbacks
  48. // in background tabs, so use setTimeout.
  49. _globalWindow2['default'].setTimeout(func, _flashConstants2['default'].TIME_BETWEEN_CHUNKS);
  50. };
  51. /**
  52. * Generates a random string of max length 6
  53. *
  54. * @return {String} the randomly generated string
  55. * @function generateRandomString
  56. * @private
  57. */
  58. var generateRandomString = function generateRandomString() {
  59. return Math.random().toString(36).slice(2, 8);
  60. };
  61. /**
  62. * Round a number to a specified number of places much like
  63. * toFixed but return a number instead of a string representation.
  64. *
  65. * @param {Number} num A number
  66. * @param {Number} places The number of decimal places which to
  67. * round
  68. * @private
  69. */
  70. var toDecimalPlaces = function toDecimalPlaces(num, places) {
  71. if (typeof places !== 'number' || places < 0) {
  72. places = 0;
  73. }
  74. var scale = Math.pow(10, places);
  75. return Math.round(num * scale) / scale;
  76. };
  77. /**
  78. * A SourceBuffer implementation for Flash rather than HTML.
  79. *
  80. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  81. * @param {Object} mediaSource the flash media source
  82. * @class FlashSourceBuffer
  83. * @extends videojs.EventTarget
  84. */
  85. var FlashSourceBuffer = (function (_videojs$EventTarget) {
  86. _inherits(FlashSourceBuffer, _videojs$EventTarget);
  87. function FlashSourceBuffer(mediaSource) {
  88. var _this = this;
  89. _classCallCheck(this, FlashSourceBuffer);
  90. _get(Object.getPrototypeOf(FlashSourceBuffer.prototype), 'constructor', this).call(this);
  91. var encodedHeader = undefined;
  92. // Start off using the globally defined value but refine
  93. // as we append data into flash
  94. this.chunkSize_ = _flashConstants2['default'].BYTES_PER_CHUNK;
  95. // byte arrays queued to be appended
  96. this.buffer_ = [];
  97. // the total number of queued bytes
  98. this.bufferSize_ = 0;
  99. // to be able to determine the correct position to seek to, we
  100. // need to retain information about the mapping between the
  101. // media timeline and PTS values
  102. this.basePtsOffset_ = NaN;
  103. this.mediaSource_ = mediaSource;
  104. this.audioBufferEnd_ = NaN;
  105. this.videoBufferEnd_ = NaN;
  106. // indicates whether the asynchronous continuation of an operation
  107. // is still being processed
  108. // see https://w3c.github.io/media-source/#widl-SourceBuffer-updating
  109. this.updating = false;
  110. this.timestampOffset_ = 0;
  111. encodedHeader = _globalWindow2['default'].btoa(String.fromCharCode.apply(null, Array.prototype.slice.call(_muxJsLibFlv2['default'].getFlvHeader())));
  112. // create function names with added randomness for the global callbacks flash will use
  113. // to get data from javascript into the swf. Random strings are added as a safety
  114. // measure for pages with multiple players since these functions will be global
  115. // instead of per instance. When making a call to the swf, the browser generates a
  116. // try catch code snippet, but just takes the function name and writes out an unquoted
  117. // call to that function. If the player id has any special characters, this will result
  118. // in an error, so safePlayerId replaces all special characters to '_'
  119. var safePlayerId = this.mediaSource_.player_.id().replace(/[^a-zA-Z0-9]/g, '_');
  120. this.flashEncodedHeaderName_ = 'vjs_flashEncodedHeader_' + safePlayerId + generateRandomString();
  121. this.flashEncodedDataName_ = 'vjs_flashEncodedData_' + safePlayerId + generateRandomString();
  122. _globalWindow2['default'][this.flashEncodedHeaderName_] = function () {
  123. delete _globalWindow2['default'][_this.flashEncodedHeaderName_];
  124. return encodedHeader;
  125. };
  126. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedHeaderName_);
  127. this.transmuxer_ = (0, _webwackify2['default'])(_flashTransmuxerWorker2['default'], resolveFlashTransmuxWorker());
  128. this.transmuxer_.postMessage({ action: 'init', options: {} });
  129. this.transmuxer_.onmessage = function (event) {
  130. if (event.data.action === 'data') {
  131. _this.receiveBuffer_(event.data.segment);
  132. }
  133. };
  134. this.one('updateend', function () {
  135. _this.mediaSource_.tech_.trigger('loadedmetadata');
  136. });
  137. Object.defineProperty(this, 'timestampOffset', {
  138. get: function get() {
  139. return this.timestampOffset_;
  140. },
  141. set: function set(val) {
  142. if (typeof val === 'number' && val >= 0) {
  143. this.timestampOffset_ = val;
  144. // We have to tell flash to expect a discontinuity
  145. this.mediaSource_.swfObj.vjs_discontinuity();
  146. // the media <-> PTS mapping must be re-established after
  147. // the discontinuity
  148. this.basePtsOffset_ = NaN;
  149. this.audioBufferEnd_ = NaN;
  150. this.videoBufferEnd_ = NaN;
  151. this.transmuxer_.postMessage({ action: 'reset' });
  152. }
  153. }
  154. });
  155. Object.defineProperty(this, 'buffered', {
  156. get: function get() {
  157. if (!this.mediaSource_ || !this.mediaSource_.swfObj || !('vjs_getProperty' in this.mediaSource_.swfObj)) {
  158. return _videoJs2['default'].createTimeRange();
  159. }
  160. var buffered = this.mediaSource_.swfObj.vjs_getProperty('buffered');
  161. if (buffered && buffered.length) {
  162. buffered[0][0] = toDecimalPlaces(buffered[0][0], 3);
  163. buffered[0][1] = toDecimalPlaces(buffered[0][1], 3);
  164. }
  165. return _videoJs2['default'].createTimeRanges(buffered);
  166. }
  167. });
  168. // On a seek we remove all text track data since flash has no concept
  169. // of a buffered-range and everything else is reset on seek
  170. this.mediaSource_.player_.on('seeked', function () {
  171. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.metadataTrack_);
  172. if (_this.inbandTextTracks_) {
  173. for (var track in _this.inbandTextTracks_) {
  174. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.inbandTextTracks_[track]);
  175. }
  176. }
  177. });
  178. var onHlsReset = this.onHlsReset_.bind(this);
  179. // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
  180. // resets its state and flushes the buffer
  181. this.mediaSource_.player_.tech_.on('hls-reset', onHlsReset);
  182. this.mediaSource_.player_.tech_.hls.on('dispose', function () {
  183. _this.transmuxer_.terminate();
  184. _this.mediaSource_.player_.tech_.off('hls-reset', onHlsReset);
  185. });
  186. }
  187. /**
  188. * Append bytes to the sourcebuffers buffer, in this case we
  189. * have to append it to swf object.
  190. *
  191. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
  192. * @param {Array} bytes
  193. */
  194. _createClass(FlashSourceBuffer, [{
  195. key: 'appendBuffer',
  196. value: function appendBuffer(bytes) {
  197. var error = undefined;
  198. if (this.updating) {
  199. error = new Error('SourceBuffer.append() cannot be called ' + 'while an update is in progress');
  200. error.name = 'InvalidStateError';
  201. error.code = 11;
  202. throw error;
  203. }
  204. this.updating = true;
  205. this.mediaSource_.readyState = 'open';
  206. this.trigger({ type: 'update' });
  207. this.transmuxer_.postMessage({
  208. action: 'push',
  209. data: bytes.buffer,
  210. byteOffset: bytes.byteOffset,
  211. byteLength: bytes.byteLength
  212. }, [bytes.buffer]);
  213. this.transmuxer_.postMessage({ action: 'flush' });
  214. }
  215. /**
  216. * Reset the parser and remove any data queued to be sent to the SWF.
  217. *
  218. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
  219. */
  220. }, {
  221. key: 'abort',
  222. value: function abort() {
  223. this.buffer_ = [];
  224. this.bufferSize_ = 0;
  225. this.mediaSource_.swfObj.vjs_abort();
  226. // report any outstanding updates have ended
  227. if (this.updating) {
  228. this.updating = false;
  229. this.trigger({ type: 'updateend' });
  230. }
  231. }
  232. /**
  233. * Flash cannot remove ranges already buffered in the NetStream
  234. * but seeking clears the buffer entirely. For most purposes,
  235. * having this operation act as a no-op is acceptable.
  236. *
  237. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
  238. * @param {Double} start start of the section to remove
  239. * @param {Double} end end of the section to remove
  240. */
  241. }, {
  242. key: 'remove',
  243. value: function remove(start, end) {
  244. (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
  245. if (this.inbandTextTracks_) {
  246. for (var track in this.inbandTextTracks_) {
  247. (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]);
  248. }
  249. }
  250. this.trigger({ type: 'update' });
  251. this.trigger({ type: 'updateend' });
  252. }
  253. /**
  254. * Receive a buffer from the flv.
  255. *
  256. * @param {Object} segment
  257. * @private
  258. */
  259. }, {
  260. key: 'receiveBuffer_',
  261. value: function receiveBuffer_(segment) {
  262. var _this2 = this;
  263. // create an in-band caption track if one is present in the segment
  264. (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
  265. (0, _addTextTrackData.addTextTrackData)(this, segment.captions, segment.metadata);
  266. // Do this asynchronously since convertTagsToData_ can be time consuming
  267. scheduleTick(function () {
  268. var flvBytes = _this2.convertTagsToData_(segment);
  269. if (_this2.buffer_.length === 0) {
  270. scheduleTick(_this2.processBuffer_.bind(_this2));
  271. }
  272. if (flvBytes) {
  273. _this2.buffer_.push(flvBytes);
  274. _this2.bufferSize_ += flvBytes.byteLength;
  275. }
  276. });
  277. }
  278. /**
  279. * Append a portion of the current buffer to the SWF.
  280. *
  281. * @private
  282. */
  283. }, {
  284. key: 'processBuffer_',
  285. value: function processBuffer_() {
  286. var _this3 = this;
  287. var chunkSize = _flashConstants2['default'].BYTES_PER_CHUNK;
  288. if (!this.buffer_.length) {
  289. if (this.updating !== false) {
  290. this.updating = false;
  291. this.trigger({ type: 'updateend' });
  292. }
  293. // do nothing if the buffer is empty
  294. return;
  295. }
  296. // concatenate appends up to the max append size
  297. var chunk = this.buffer_[0].subarray(0, chunkSize);
  298. // requeue any bytes that won't make it this round
  299. if (chunk.byteLength < chunkSize || this.buffer_[0].byteLength === chunkSize) {
  300. this.buffer_.shift();
  301. } else {
  302. this.buffer_[0] = this.buffer_[0].subarray(chunkSize);
  303. }
  304. this.bufferSize_ -= chunk.byteLength;
  305. // base64 encode the bytes
  306. var binary = [];
  307. var length = chunk.byteLength;
  308. for (var i = 0; i < length; i++) {
  309. binary.push(String.fromCharCode(chunk[i]));
  310. }
  311. var b64str = _globalWindow2['default'].btoa(binary.join(''));
  312. _globalWindow2['default'][this.flashEncodedDataName_] = function () {
  313. // schedule another processBuffer to process any left over data or to
  314. // trigger updateend
  315. scheduleTick(_this3.processBuffer_.bind(_this3));
  316. delete _globalWindow2['default'][_this3.flashEncodedDataName_];
  317. return b64str;
  318. };
  319. // Notify the swf that segment data is ready to be appended
  320. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedDataName_);
  321. }
  322. /**
  323. * Turns an array of flv tags into a Uint8Array representing the
  324. * flv data. Also removes any tags that are before the current
  325. * time so that playback begins at or slightly after the right
  326. * place on a seek
  327. *
  328. * @private
  329. * @param {Object} segmentData object of segment data
  330. */
  331. }, {
  332. key: 'convertTagsToData_',
  333. value: function convertTagsToData_(segmentData) {
  334. var segmentByteLength = 0;
  335. var tech = this.mediaSource_.tech_;
  336. var videoTargetPts = 0;
  337. var segment = undefined;
  338. var videoTags = segmentData.tags.videoTags;
  339. var audioTags = segmentData.tags.audioTags;
  340. // Establish the media timeline to PTS translation if we don't
  341. // have one already
  342. if (isNaN(this.basePtsOffset_) && (videoTags.length || audioTags.length)) {
  343. // We know there is at least one video or audio tag, but since we may not have both,
  344. // we use pts: Infinity for the missing tag. The will force the following Math.min
  345. // call will to use the proper pts value since it will always be less than Infinity
  346. var firstVideoTag = videoTags[0] || { pts: Infinity };
  347. var firstAudioTag = audioTags[0] || { pts: Infinity };
  348. this.basePtsOffset_ = Math.min(firstAudioTag.pts, firstVideoTag.pts);
  349. }
  350. if (tech.seeking()) {
  351. // Do not use previously saved buffer end values while seeking since buffer
  352. // is cleared on all seeks
  353. this.videoBufferEnd_ = NaN;
  354. this.audioBufferEnd_ = NaN;
  355. }
  356. if (isNaN(this.videoBufferEnd_)) {
  357. if (tech.buffered().length) {
  358. videoTargetPts = tech.buffered().end(0) - this.timestampOffset;
  359. }
  360. // Trim to currentTime if seeking
  361. if (tech.seeking()) {
  362. videoTargetPts = Math.max(videoTargetPts, tech.currentTime() - this.timestampOffset);
  363. }
  364. // PTS values are represented in milliseconds
  365. videoTargetPts *= 1e3;
  366. videoTargetPts += this.basePtsOffset_;
  367. } else {
  368. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  369. // could append an overlapping segment, in which case there is a high likelyhood
  370. // a tag could have a matching pts to videoBufferEnd_, which would cause
  371. // that tag to get appended by the tag.pts >= targetPts check below even though it
  372. // is a duplicate of what was previously appended
  373. videoTargetPts = this.videoBufferEnd_ + 0.1;
  374. }
  375. // filter complete GOPs with a presentation time less than the seek target/end of buffer
  376. var currentIndex = videoTags.length;
  377. // if the last tag is beyond videoTargetPts, then do not search the list for a GOP
  378. // since our videoTargetPts lies in a future segment
  379. if (currentIndex && videoTags[currentIndex - 1].pts >= videoTargetPts) {
  380. // Start by walking backwards from the end of the list until we reach a tag that
  381. // is equal to or less than videoTargetPts
  382. while (--currentIndex) {
  383. var currentTag = videoTags[currentIndex];
  384. if (currentTag.pts > videoTargetPts) {
  385. continue;
  386. }
  387. // if we see a keyFrame or metadata tag once we've gone below videoTargetPts,
  388. // exit the loop as this is the start of the GOP that we want to append
  389. if (currentTag.keyFrame || currentTag.metaDataTag) {
  390. break;
  391. }
  392. }
  393. // We need to check if there are any metadata tags that come before currentIndex
  394. // as those will be metadata tags associated with the GOP we are appending
  395. // There could be 0 to 2 metadata tags that come before the currentIndex depending
  396. // on what videoTargetPts is and whether the transmuxer prepended metadata tags to this
  397. // key frame
  398. while (currentIndex) {
  399. var nextTag = videoTags[currentIndex - 1];
  400. if (!nextTag.metaDataTag) {
  401. break;
  402. }
  403. currentIndex--;
  404. }
  405. }
  406. var filteredVideoTags = videoTags.slice(currentIndex);
  407. var audioTargetPts = undefined;
  408. if (isNaN(this.audioBufferEnd_)) {
  409. audioTargetPts = videoTargetPts;
  410. } else {
  411. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  412. // could append an overlapping segment, in which case there is a high likelyhood
  413. // a tag could have a matching pts to videoBufferEnd_, which would cause
  414. // that tag to get appended by the tag.pts >= targetPts check below even though it
  415. // is a duplicate of what was previously appended
  416. audioTargetPts = this.audioBufferEnd_ + 0.1;
  417. }
  418. if (filteredVideoTags.length) {
  419. // If targetPts intersects a GOP and we appended the tags for the GOP that came
  420. // before targetPts, we want to make sure to trim audio tags at the pts
  421. // of the first video tag to avoid brief moments of silence
  422. audioTargetPts = Math.min(audioTargetPts, filteredVideoTags[0].pts);
  423. }
  424. // skip tags with a presentation time less than the seek target/end of buffer
  425. currentIndex = 0;
  426. while (currentIndex < audioTags.length) {
  427. if (audioTags[currentIndex].pts >= audioTargetPts) {
  428. break;
  429. }
  430. currentIndex++;
  431. }
  432. var filteredAudioTags = audioTags.slice(currentIndex);
  433. // update the audio and video buffer ends
  434. if (filteredAudioTags.length) {
  435. this.audioBufferEnd_ = filteredAudioTags[filteredAudioTags.length - 1].pts;
  436. }
  437. if (filteredVideoTags.length) {
  438. this.videoBufferEnd_ = filteredVideoTags[filteredVideoTags.length - 1].pts;
  439. }
  440. var tags = this.getOrderedTags_(filteredVideoTags, filteredAudioTags);
  441. if (tags.length === 0) {
  442. return;
  443. }
  444. // If we are appending data that comes before our target pts, we want to tell
  445. // the swf to adjust its notion of current time to account for the extra tags
  446. // we are appending to complete the GOP that intersects with targetPts
  447. if (tags[0].pts < videoTargetPts && tech.seeking()) {
  448. var fudgeFactor = 1 / 30;
  449. var currentTime = tech.currentTime();
  450. var diff = (videoTargetPts - tags[0].pts) / 1e3;
  451. var adjustedTime = currentTime - diff;
  452. if (adjustedTime < fudgeFactor) {
  453. adjustedTime = 0;
  454. }
  455. try {
  456. this.mediaSource_.swfObj.vjs_adjustCurrentTime(adjustedTime);
  457. } catch (e) {
  458. // no-op for backwards compatability of swf. If adjustCurrentTime fails,
  459. // the swf may incorrectly report currentTime and buffered ranges
  460. // but should not affect playback over than the time displayed on the
  461. // progress bar is inaccurate
  462. }
  463. }
  464. // concatenate the bytes into a single segment
  465. for (var i = 0; i < tags.length; i++) {
  466. segmentByteLength += tags[i].bytes.byteLength;
  467. }
  468. segment = new Uint8Array(segmentByteLength);
  469. for (var i = 0, j = 0; i < tags.length; i++) {
  470. segment.set(tags[i].bytes, j);
  471. j += tags[i].bytes.byteLength;
  472. }
  473. return segment;
  474. }
  475. /**
  476. * Assemble the FLV tags in decoder order.
  477. *
  478. * @private
  479. * @param {Array} videoTags list of video tags
  480. * @param {Array} audioTags list of audio tags
  481. */
  482. }, {
  483. key: 'getOrderedTags_',
  484. value: function getOrderedTags_(videoTags, audioTags) {
  485. var tag = undefined;
  486. var tags = [];
  487. while (videoTags.length || audioTags.length) {
  488. if (!videoTags.length) {
  489. // only audio tags remain
  490. tag = audioTags.shift();
  491. } else if (!audioTags.length) {
  492. // only video tags remain
  493. tag = videoTags.shift();
  494. } else if (audioTags[0].dts < videoTags[0].dts) {
  495. // audio should be decoded next
  496. tag = audioTags.shift();
  497. } else {
  498. // video should be decoded next
  499. tag = videoTags.shift();
  500. }
  501. tags.push(tag);
  502. }
  503. return tags;
  504. }
  505. }, {
  506. key: 'onHlsReset_',
  507. value: function onHlsReset_() {
  508. this.transmuxer_.postMessage({ action: 'resetCaptions' });
  509. }
  510. }]);
  511. return FlashSourceBuffer;
  512. })(_videoJs2['default'].EventTarget);
  513. exports['default'] = FlashSourceBuffer;
  514. module.exports = exports['default'];