PageSourceSearch

https://www.saalbach.com/cache-buster-1777443972626/static/js/libs/videojs-livestream/videojs-contrib-hls.js

js saalbach.com collected 2026-09-24 09:40:34 UTC 685,596 bytes, 20,719 lines download raw bytes

1/**
2 * videojs-contrib-hls
3 * @version 5.11.0
4 * @copyright 2017 Brightcove, Inc
5 * @license Apache-2.0
6 */
7(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.videojsContribHls = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
8/**
9 * @file ad-cue-tags.js
10 */
11'use strict';
12
13Object.defineProperty(exports, '__esModule', {
14  value: true
15});
16
17var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
18
19function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
20
21var _globalWindow = require('global/window');
22
23var _globalWindow2 = _interopRequireDefault(_globalWindow);
24
25/**
26 * Searches for an ad cue that overlaps with the given mediaTime
27 */
28var findAdCue = function findAdCue(track, mediaTime) {
29  var cues = track.cues;
30
31  for (var i = 0; i < cues.length; i++) {
32    var cue = cues[i];
33
34    if (mediaTime >= cue.adStartTime && mediaTime <= cue.adEndTime) {
35      return cue;
36    }
37  }
38  return null;
39};
40
41var updateAdCues = function updateAdCues(media, track) {
42  var offset = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2];
43
44  if (!media.segments) {
45    return;
46  }
47
48  var mediaTime = offset;
49  var cue = undefined;
50
51  for (var i = 0; i < media.segments.length; i++) {
52    var segment = media.segments[i];
53
54    if (!cue) {
55      // Since the cues will span for at least the segment duration, adding a fudge
56      // factor of half segment duration will prevent duplicate cues from being
57      // created when timing info is not exact (e.g. cue start time initialized
58      // at 10.006677, but next call mediaTime is 10.003332 )
59      cue = findAdCue(track, mediaTime + segment.duration / 2);
60    }
61
62    if (cue) {
63      if ('cueIn' in segment) {
64        // Found a CUE-IN so end the cue
65        cue.endTime = mediaTime;
66        cue.adEndTime = mediaTime;
67        mediaTime += segment.duration;
68        cue = null;
69        continue;
70      }
71
72      if (mediaTime < cue.endTime) {
73        // Already processed this mediaTime for this cue
74        mediaTime += segment.duration;
75        continue;
76      }
77
78      // otherwise extend cue until a CUE-IN is found
79      cue.endTime += segment.duration;
80    } else {
81      if ('cueOut' in segment) {
82        cue = new _globalWindow2['default'].VTTCue(mediaTime, mediaTime + segment.duration, segment.cueOut);
83        cue.adStartTime = mediaTime;
84        // Assumes tag format to be
85        // #EXT-X-CUE-OUT:30
86        cue.adEndTime = mediaTime + parseFloat(segment.cueOut);
87        track.addCue(cue);
88      }
89
90      if ('cueOutCont' in segment) {
91        // Entered into the middle of an ad cue
92        var adOffset = undefined;
93        var adTotal = undefined;
94
95        // Assumes tag formate to be
96        // #EXT-X-CUE-OUT-CONT:10/30
97
98        var _segment$cueOutCont$split$map = segment.cueOutCont.split('/').map(parseFloat);
99
100        var _segment$cueOutCont$split$map2 = _slicedToArray(_segment$cueOutCont$split$map, 2);
101
102        adOffset = _segment$cueOutCont$split$map2[0];
103        adTotal = _segment$cueOutCont$split$map2[1];
104
105        cue = new _globalWindow2['default'].VTTCue(mediaTime, mediaTime + segment.duration, '');
106        cue.adStartTime = mediaTime - adOffset;
107        cue.adEndTime = cue.adStartTime + adTotal;
108        track.addCue(cue);
109      }
110    }
111    mediaTime += segment.duration;
112  }
113};
114
115exports['default'] = {
116  updateAdCues: updateAdCues,
117  findAdCue: findAdCue
118};
119module.exports = exports['default'];
120},{"global/window":32}],2:[function(require,module,exports){
121/**
122 * @file bin-utils.js
123 */
124
125/**
126 * convert a TimeRange to text
127 *
128 * @param {TimeRange} range the timerange to use for conversion
129 * @param {Number} i the iterator on the range to convert
130 */
131'use strict';
132
133Object.defineProperty(exports, '__esModule', {
134  value: true
135});
136var textRange = function textRange(range, i) {
137  return range.start(i) + '-' + range.end(i);
138};
139
140/**
141 * format a number as hex string
142 *
143 * @param {Number} e The number
144 * @param {Number} i the iterator
145 */
146var formatHexString = function formatHexString(e, i) {
147  var value = e.toString(16);
148
149  return '00'.substring(0, 2 - value.length) + value + (i % 2 ? ' ' : '');
150};
151var formatAsciiString = function formatAsciiString(e) {
152  if (e >= 0x20 && e < 0x7e) {
153    return String.fromCharCode(e);
154  }
155  return '.';
156};
157
158/**
159 * Creates an object for sending to a web worker modifying properties that are TypedArrays
160 * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
161 *
162 * @param {Object} message
163 *        Object of properties and values to send to the web worker
164 * @return {Object}
165 *         Modified message with TypedArray values expanded
166 * @function createTransferableMessage
167 */
168var createTransferableMessage = function createTransferableMessage(message) {
169  var transferable = {};
170
171  Object.keys(message).forEach(function (key) {
172    var value = message[key];
173
174    if (ArrayBuffer.isView(value)) {
175      transferable[key] = {
176        bytes: value.buffer,
177        byteOffset: value.byteOffset,
178        byteLength: value.byteLength
179      };
180    } else {
181      transferable[key] = value;
182    }
183  });
184
185  return transferable;
186};
187
188/**
189 * Returns a unique string identifier for a media initialization
190 * segment.
191 */
192var initSegmentId = function initSegmentId(initSegment) {
193  var byterange = initSegment.byterange || {
194    length: Infinity,
195    offset: 0
196  };
197
198  return [byterange.length, byterange.offset, initSegment.resolvedUri].join(',');
199};
200
201/**
202 * utils to help dump binary data to the console
203 */
204var utils = {
205  hexDump: function hexDump(data) {
206    var bytes = Array.prototype.slice.call(data);
207    var step = 16;
208    var result = '';
209    var hex = undefined;
210    var ascii = undefined;
211
212    for (var j = 0;
212 j < bytes.length / step; j++) {
213      hex = bytes.slice(j * step, j * step + step).map(formatHexString).join('');
214      ascii = bytes.slice(j * step, j * step + step).map(formatAsciiString).join('');
215      result += hex + ' ' + ascii + '\n';
216    }
217    return result;
218  },
219  tagDump: function tagDump(tag) {
220    return utils.hexDump(tag.bytes);
221  },
222  textRanges: function textRanges(ranges) {
223    var result = '';
224    var i = undefined;
225
226    for (i = 0; i < ranges.length; i++) {
227      result += textRange(ranges, i) + ' ';
228    }
229    return result;
230  },
231  createTransferableMessage: createTransferableMessage,
232  initSegmentId: initSegmentId
233};
234
235exports['default'] = utils;
236module.exports = exports['default'];
237},{}],3:[function(require,module,exports){
238"use strict";
239
240Object.defineProperty(exports, "__esModule", {
241  value: true
242});
243exports["default"] = {
244  GOAL_BUFFER_LENGTH: 30,
245  MAX_GOAL_BUFFER_LENGTH: 60,
246  GOAL_BUFFER_LENGTH_RATE: 1,
247  // A fudge factor to apply to advertised playlist bitrates to account for
248  // temporary flucations in client bandwidth
249  BANDWIDTH_VARIANCE: 1.2,
250  // How much of the buffer must be filled before we consider upswitching
251  BUFFER_LOW_WATER_LINE: 0,
252  MAX_BUFFER_LOW_WATER_LINE: 30,
253  BUFFER_LOW_WATER_LINE_RATE: 1
254};
255module.exports = exports["default"];
256},{}],4:[function(require,module,exports){
257'use strict';
258
259Object.defineProperty(exports, '__esModule', {
260  value: true
261});
262
263function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
264
265var _globalWindow = require('global/window');
266
267var _globalWindow2 = _interopRequireDefault(_globalWindow);
268
269var _aesDecrypter = require('aes-decrypter');
270
271var _binUtils = require('./bin-utils');
272
273/**
274 * Our web worker interface so that things can talk to aes-decrypter
275 * that will be running in a web worker. the scope is passed to this by
276 * webworkify.
277 *
278 * @param {Object} self
279 *        the scope for the web worker
280 */
281var DecrypterWorker = function DecrypterWorker(self) {
282  self.onmessage = function (event) {
283    var data = event.data;
284    var encrypted = new Uint8Array(data.encrypted.bytes, data.encrypted.byteOffset, data.encrypted.byteLength);
285    var key = new Uint32Array(data.key.bytes, data.key.byteOffset, data.key.byteLength / 4);
286    var iv = new Uint32Array(data.iv.bytes, data.iv.byteOffset, data.iv.byteLength / 4);
287
288    /* eslint-disable no-new, handle-callback-err */
289    new _aesDecrypter.Decrypter(encrypted, key, iv, function (err, bytes) {
290      _globalWindow2['default'].postMessage((0, _binUtils.createTransferableMessage)({
291        source: data.source,
292        decrypted: bytes
293      }), [bytes.buffer]);
294    });
295    /* eslint-enable */
296  };
297};
298
299exports['default'] = function (self) {
300  return new DecrypterWorker(self);
301};
302
303module.exports = exports['default'];
304},{"./bin-utils":2,"aes-decrypter":25,"global/window":32}],5:[function(require,module,exports){
305(function (global){
306/**
307 * @file master-playlist-controller.js
308 */
309'use strict';
310
311Object.defineProperty(exports, '__esModule', {
312  value: true
313});
314
315var _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; }; })();
316
317var _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); } } };
318
319function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
320
321function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
322
323function _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, { c
323onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
324
325var _playlistLoader = require('./playlist-loader');
326
327var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
328
329var _segmentLoader = require('./segment-loader');
330
331var _segmentLoader2 = _interopRequireDefault(_segmentLoader);
332
333var _vttSegmentLoader = require('./vtt-segment-loader');
334
335var _vttSegmentLoader2 = _interopRequireDefault(_vttSegmentLoader);
336
337var _ranges = require('./ranges');
338
339var _ranges2 = _interopRequireDefault(_ranges);
340
341var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
342
343var _videoJs2 = _interopRequireDefault(_videoJs);
344
345var _adCueTags = require('./ad-cue-tags');
346
347var _adCueTags2 = _interopRequireDefault(_adCueTags);
348
349var _syncController = require('./sync-controller');
350
351var _syncController2 = _interopRequireDefault(_syncController);
352
353var _videojsContribMediaSourcesEs5CodecUtils = require('videojs-contrib-media-sources/es5/codec-utils');
354
355var _webworkify = require('webworkify');
356
357var _webworkify2 = _interopRequireDefault(_webworkify);
358
359var _decrypterWorker = require('./decrypter-worker');
360
361var _decrypterWorker2 = _interopRequireDefault(_decrypterWorker);
362
363var _config = require('./config');
364
365var _config2 = _interopRequireDefault(_config);
366
367var _utilCodecsJs = require('./util/codecs.js');
368
369var _mediaGroups = require('./media-groups');
370
371var ABORT_EARLY_BLACKLIST_SECONDS = 60 * 2;
372
373var Hls = undefined;
374
375// Default codec parameters if none were provided for video and/or audio
376var defaultCodecs = {
377  videoCodec: 'avc1',
378  videoObjectTypeIndicator: '.4d400d',
379  // AAC-LC
380  audioProfile: '2'
381};
382
383// SegmentLoader stats that need to have each loader's
384// values summed to calculate the final value
385var loaderStats = ['mediaRequests', 'mediaRequestsAborted', 'mediaRequestsTimedout', 'mediaRequestsErrored', 'mediaTransferDuration', 'mediaBytesTransferred'];
386var sumLoaderStat = function sumLoaderStat(stat) {
387  return this.audioSegmentLoader_[stat] + this.mainSegmentLoader_[stat];
388};
389
390/**
391 * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
392 * standard `avc1.<hhhhhh>`.
393 *
394 * @param codecString {String} the codec string
395 * @return {String} the codec string with old apple-style codecs replaced
396 *
397 * @private
398 */
399var mapLegacyAvcCodecs_ = function mapLegacyAvcCodecs_(codecString) {
400  return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) {
401    return (0, _videojsContribMediaSourcesEs5CodecUtils.translateLegacyCodecs)([match])[0];
402  });
403};
404
405exports.mapLegacyAvcCodecs_ = mapLegacyAvcCodecs_;
406/**
407 * Build a media mime-type string from a set of parameters
408 * @param {String} type either 'audio' or 'video'
409 * @param {String} container either 'mp2t' or 'mp4'
410 * @param {Array} codecs an array of codec strings to add
411 * @return {String} a valid media mime-type
412 */
413var makeMimeTypeString = function makeMimeTypeString(type, container, codecs) {
414  // The codecs array is filtered so that falsey values are
415  // dropped and don't cause Array#join to create spurious
416  // commas
417  return type + '/' + container + '; codecs="' + codecs.filter(function (c) {
418    return !!c;
419  }).join(', ') + '"';
420};
421
422/**
423 * Returns the type container based on information in the playlist
424 * @param {Playlist} media the current media playlist
425 * @return {String} a valid media container type
426 */
427var getContainerType = function getContainerType(media) {
428  // An initialization segment means the media playlist is an iframe
429  // playlist or is using the mp4 container. We don't currently
430  // support iframe playlists, so assume this is signalling mp4
431  // fragments.
432  if (media.segments && media.segments.length && media.segments[0].map) {
433    return 'mp4';
434  }
435  return 'mp2t';
436};
437
438/**
439 * Returns a set of codec strings parsed from the playlist or the default
440 * codec strings if no codecs were specified in the playlist
441 * @param {Playlist} media the current media playlist
442 * @return {Object} an object with the video and audio codecs
443 */
444var getCodecs = function getCodecs(media) {
445  // if the codecs were explicitly specified, use them instead of the
446  // defaults
447  var mediaAttributes = media.attributes || {};
448
449  if (mediaAttributes.CODECS) {
450    return (0, _utilCodecsJs.parseCodecs)(mediaAttributes.CODECS);
451  }
452  return defaultCodecs;
453};
454
455/**
456 * Calculates the MIME type strings for a working configuration of
457 * SourceBuffers to play variant streams in a master playlist. If
458 * there is no possible working configuration, an empty array will be
459 * returned.
460 *
461 * @param master {Object} the m3u8 object for the master playlist
462 * @param media {Object} the m3u8 object for the variant playlist
463 * @return {Array} the MIME type strings. If the array has more than
464 * one entry, the first element should be applied to the video
465 * SourceBuffer and the second to the audio SourceBuffer.
466 *
467 * @private
468 */
469var mimeTypesForPlaylist_ = function mimeTypesForPlaylist_(master, media) {
470  var containerType = getContainerType(media);
471  var codecInfo = getCodecs(media);
472  var mediaAttributes = media.attributes || {};
473  // Default condition for a traditional HLS (no demuxed audio/video)
474  var isMuxed = true;
475  var isMaat = false;
476
477  if (!media) {
478    // Not enough information
479    return [];
480  }
481
482  if (master.mediaGroups.AUDIO && mediaAttributes.AUDIO) {
483    var audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO];
484
485    // Handle the case where we are in a multiple-audio track scenario
486    if (audioGroup) {
487      isMaat = true;
488      // Start with the everything demuxed then...
489      isMuxed = false;
490      // ...check to see if any audio group tracks are muxed (ie. lacking a uri)
491      for (var groupId in audioGroup) {
492        if (!audioGroup[groupId].uri) {
493          isMuxed = true;
494          break;
495        }
496      }
497    }
498  }
499
500  // HLS with multiple-audio tracks must always get an audio codec.
501  // Put another way, there is no way to have a video-only multiple-audio HLS!
502  if (isMaat && !codecInfo.audioProfile) {
503    _videoJs2['default'].log.warn('Multiple audio tracks present but no audio codec string is specified. ' + 'Attempting to use the default audio codec (mp4a.40.2)');
504    codecInfo.audioProfile = defaultCodecs.audioProfile;
505  }
506
507  // Generate the final codec strings from the codec object generated above
508  var codecStrings = {};
509
510  if (codecInfo.videoCodec) {
511    codecStrings.video = '' + codecInfo.videoCodec + codecInfo.videoObjectTypeIndicator;
512  }
513
514  if (codecInfo.audioProfile) {
515    codecStrings.audio = 'mp4a.40.' + codecInfo.audioProfile;
516  }
517
518  // Finally, make and return an array with proper mime-types depending on
519  // the configuration
520  var justAudio = makeMimeTypeString('audio', containerType, [codecStrings.audio]);
521  var justVideo = makeMimeTypeString('video', containerType, [codecStrings.video]);
522  var bothVideoAudio = makeMimeTypeString('video', containerType, [codecStrings.video, codecStrings.audio]);
523
524  if (isMaat) {
525    if (!isMuxed && codecStrings.video) {
526      return [justVideo, justAudio];
527    }
528    // There exists the possiblity that this will return a `video/container`
529    // mime-type for the first entry in the array even when there is only audio.
530    // This doesn't appear to be a problem and simplifies the code.
531    return [bothVideoAudio, justAudio];
532  }
533
534  // If there is ano video codec at all, always just return a single
535  // audio/<container> mime-type
536  if (!codecStrings.video) {
537    return [justAudio];
538  }
539
540  // When not using separate audio media groups, audio and video is
541  // *always* muxed
542  return [bothVideoAudio];
543};
544
545exports.mimeTypesForPlaylist_ = mimeTypesForPlaylist_;
546/**
547 * the master playlist controller controller all interactons
548 * between playlists and segmentloaders. At this time this mainly
549 * involves a master playlist and a series of audio playlists
550 * if they are available
551 *
552 * @class MasterPlaylistController
553 * @extends videojs.EventTarget
554 */
555
556var MasterPlaylistController = (function (_videojs$EventTarget) {
557  _inherits(MasterPlaylistController, _videojs$EventTarget);
558
559  function MasterPlaylistController(options) {
560    var _this = this;
561
562    _classCallCheck(this, MasterPlaylistController);
563
564    _get(Object.getPrototypeOf(MasterPlaylistController.prototype), 'constructor', this).call(this);
565
566    var url = options.url;
567    var withCredentials = options.withCredentials;
568    var mode = options.mode;
569    var tech = options.tech;
570    var bandwidth = options.bandwidth;
571    var externHls = options.externHls;
572    var useCueTags = options.useCueTags;
573    var blacklistDuration = options.blacklistDuration;
574    var enableLowInitialPlaylist = options.enableLowInitialPlaylist;
575
576    if (!url) {
577      throw new Error('A non-empty playlist URL is required');
578    }
579
580    Hls = externHls;
581
582    this.withCredentials = withCredentials;
583    this.tech_ = tech;
584    this.hls_ = tech.hls;
585    this.mode_ = mode;
586    this.useCueTags_ = useCueTags;
587    this.blacklistDuration = blacklistDuration;
588    this.enableLowInitialPlaylist = enableLowInitialPlaylist;
589    if (this.useCueTags_) {
590      this.cueTagsTrack_ = this.tech_.addTextTrack('metadata', 'ad-cues');
591      this.cueTagsTrack_.inBandMetadataTrackDispatchType = '';
592    }
593
594    this.requestOptions_ = {
595      withCredentials: this.withCredentials,
596      timeout: null
597    };
598
599    this.mediaTypes_ = (0, _mediaGroups.createMediaTypes)();
600
601    this.mediaSource = new _videoJs2['default'].MediaSource({ mode: mode });
602
603    // load the media source into the player
604    this.mediaSource.addEventListener('sourceopen', this.handleSourceOpen_.bind(this));
605
606    this.seekable_ = _videoJs2['default'].createTimeRanges();
607    this.hasPlayed_ = function () {
608      return false;
609    };
610
611    this.syncController_ = new _syncController2['default'](options);
612    this.segmentMetadataTrack_ = tech.addRemoteTextTrack({
613      kind: 'metadata',
614      label: 'segment-metadata'
615    }, false).track;
616
617    this.decrypter_ = (0, _webworkify2['default'])(_decrypterWorker2['default']);
618
619    var segmentLoaderSettings = {
620      hls: this.hls_,
621      mediaSource: this.mediaSource,
622      currentTime: this.tech_.currentTime.bind(this.tech_),
623      seekable: function seekable() {
624        return _this.seekable();
625      },
626      seeking: function seeking() {
627        return _this.tech_.seeking();
628      },
629      duration: function duration() {
630        return _this.mediaSource.duration;
631      },
632      hasPlayed: function hasPlayed() {
633        return _this.hasPlayed_();
634      },
635      goalBufferLength: function goalBufferLength() {
636        return _this.goalBufferLength();
637      },
638      bandwidth: bandwidth,
639      syncController: this.syncController_,
640      decrypter: this.decrypter_
641    };
642
643    // setup playlist loaders
644    this.masterPlaylistLoader_ = new _playlistLoader2['default'](url, this.hls_, this.withCredentials);
645    this.setupMasterPlaylistLoaderListeners_();
646
647    // setup segment loaders
648    // combined audio/video or just video when alternate audio track is selected
649    this.mainSegmentLoader_ = new _segmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderSettings, {
650      segmentMetadataTrack: this.segmentMetadataTrack_,
651      loaderType: 'main'
652    }), options);
653
654    // alternate audio track
655    this.audioSegmentLoader_ = new _segmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderSettings, {
656      loaderType: 'audio'
657    }), options);
658
659    this.subtitleSegmentLoader_ = new _vttSegmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderSettings, {
660      loaderType: 'vtt'
661    }), options);
662
663    this.setupSegmentLoaderListeners_();
664
665    // Create SegmentLoader stat-getters
vendor: 7,535 bytes, lines 666-875
666    loaderStats.forEach(function (stat) {
667      _this[stat + '_'] = sumLoaderStat.bind(_this, stat);
668    });
669
670    this.masterPlaylistLoader_.load();
671  }
672
673  /**
674   * Register event handlers on the master playlist loader. A helper
675   * function for construction time.
676   *
677   * @private
678   */
679
680  _createClass(MasterPlaylistController, [{
681    key: 'setupMasterPlaylistLoaderListeners_',
682    value: function setupMasterPlaylistLoaderListeners_() {
683      var _this2 = this;
684
685      this.masterPlaylistLoader_.on('loadedmetadata', function () {
686        var media = _this2.masterPlaylistLoader_.media();
687        var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
688
689        // If we don't have any more available playlists, we don't want to
690        // timeout the request.
691        if (_this2.masterPlaylistLoader_.isLowestEnabledRendition_()) {
692          _this2.requestOptions_.timeout = 0;
693        } else {
694          _this2.requestOptions_.timeout = requestTimeout;
695        }
696
697        // if this isn't a live video and preload permits, start
698        // downloading segments
699        if (media.endList && _this2.tech_.preload() !== 'none') {
700          _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
701          _this2.mainSegmentLoader_.load();
702        }
703
704        (0, _mediaGroups.setupMediaGroups)({
705          segmentLoaders: {
706            AUDIO: _this2.audioSegmentLoader_,
707            SUBTITLES: _this2.subtitleSegmentLoader_,
708            main: _this2.mainSegmentLoader_
709          },
710          tech: _this2.tech_,
711          requestOptions: _this2.requestOptions_,
712          masterPlaylistLoader: _this2.masterPlaylistLoader_,
713          mode: _this2.mode_,
714          hls: _this2.hls_,
715          master: _this2.master(),
716          mediaTypes: _this2.mediaTypes_,
717          blacklistCurrentPlaylist: _this2.blacklistCurrentPlaylist.bind(_this2)
718        });
719
720        _this2.triggerPresenceUsage_(_this2.master(), media);
721
722        try {
723          _this2.setupSourceBuffers_();
724        } catch (e) {
725          _videoJs2['default'].log.warn('Failed to create SourceBuffers', e);
726          return _this2.mediaSource.endOfStream('decode');
727        }
728        _this2.setupFirstPlay();
729
730        _this2.trigger('selectedinitialmedia');
731      });
732
733      this.masterPlaylistLoader_.on('loadedplaylist', function () {
734        var updatedPlaylist = _this2.masterPlaylistLoader_.media();
735
736        if (!updatedPlaylist) {
737          var selectedMedia = undefined;
738
739          if (_this2.enableLowInitialPlaylist) {
740            selectedMedia = _this2.selectInitialPlaylist();
741          }
742
743          if (!selectedMedia) {
744            selectedMedia = _this2.selectPlaylist();
745          }
746
747          _this2.initialMedia_ = selectedMedia;
748          _this2.masterPlaylistLoader_.media(_this2.initialMedia_);
749          return;
750        }
751
752        if (_this2.useCueTags_) {
753          _this2.updateAdCues_(updatedPlaylist);
754        }
755
756        // TODO: Create a new event on the PlaylistLoader that signals
757        // that the segments have changed in some way and use that to
758        // update the SegmentLoader instead of doing it twice here and
759        // on `mediachange`
760        _this2.mainSegmentLoader_.playlist(updatedPlaylist, _this2.requestOptions_);
761        _this2.updateDuration();
762
763        // If the player isn't paused, ensure that the segment loader is running,
764        // as it is possible that it was temporarily stopped while waiting for
765        // a playlist (e.g., in case the playlist errored and we re-requested it).
766        if (!_this2.tech_.paused()) {
767          _this2.mainSegmentLoader_.load();
768        }
769
770        if (!updatedPlaylist.endList) {
771          (function () {
772            var addSeekableRange = function addSeekableRange() {
773              var seekable = _this2.seekable();
774
775              if (seekable.length !== 0) {
776                _this2.mediaSource.addSeekableRange_(seekable.start(0), seekable.end(0));
777              }
778            };
779
780            if (_this2.duration() !== Infinity) {
781              (function () {
782                var onDurationchange = function onDurationchange() {
783                  if (_this2.duration() === Infinity) {
784                    addSeekableRange();
785                  } else {
786                    _this2.tech_.one('durationchange', onDurationchange);
787                  }
788                };
789
790                _this2.tech_.one('durationchange', onDurationchange);
791              })();
792            } else {
793              addSeekableRange();
794            }
795          })();
796        }
797      });
798
799      this.masterPlaylistLoader_.on('error', function () {
800        _this2.blacklistCurrentPlaylist(_this2.masterPlaylistLoader_.error);
801      });
802
803      this.masterPlaylistLoader_.on('mediachanging', function () {
804        _this2.mainSegmentLoader_.abort();
805        _this2.mainSegmentLoader_.pause();
806      });
807
808      this.masterPlaylistLoader_.on('mediachange', function () {
809        var media = _this2.masterPlaylistLoader_.media();
810        var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
811
812        // If we don't have any more available playlists, we don't want to
813        // timeout the request.
814        if (_this2.masterPlaylistLoader_.isLowestEnabledRendition_()) {
815          _this2.requestOptions_.timeout = 0;
816        } else {
817          _this2.requestOptions_.timeout = requestTimeout;
818        }
819
820        // TODO: Create a new event on the PlaylistLoader that signals
821        // that the segments have changed in some way and use that to
822        // update the SegmentLoader instead of doing it twice here and
823        // on `loadedplaylist`
824        _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
825        _this2.mainSegmentLoader_.load();
826
827        _this2.tech_.trigger({
828          type: 'mediachange',
829          bubbles: true
830        });
831      });
832
833      this.masterPlaylistLoader_.on('playlistunchanged', function () {
834        var updatedPlaylist = _this2.masterPlaylistLoader_.media();
835        var playlistOutdated = _this2.stuckAtPlaylistEnd_(updatedPlaylist);
836
837        if (playlistOutdated) {
838          // Playlist has stopped updating and we're stuck at its end. Try to
839          // blacklist it and switch to another playlist in the hope that that
840          // one is updating (and give the player a chance to re-adjust to the
841          // safe live point).
842          _this2.blacklistCurrentPlaylist({
843            message: 'Playlist no longer updating.'
844          });
845          // useful for monitoring QoS
846          _this2.tech_.trigger('playliststuck');
847        }
848      });
849
850      this.masterPlaylistLoader_.on('renditiondisabled', function () {
851        _this2.tech_.trigger({ type: 'usage', name: 'hls-rendition-disabled' });
852      });
853      this.masterPlaylistLoader_.on('renditionenabled', function () {
854        _this2.tech_.trigger({ type: 'usage', name: 'hls-rendition-enabled' });
855      });
856    }
857
858    /**
859     * A helper function for triggerring presence usage events once per source
860     *
861     * @private
862     */
863  }, {
864    key: 'triggerPresenceUsage_',
865    value: function triggerPresenceUsage_(master, media) {
866      var mediaGroups = master.mediaGroups || {};
867      var defaultDemuxed = true;
868      var audioGroupKeys = Object.keys(mediaGroups.AUDIO);
869
870      for (var mediaGroup in mediaGroups.AUDIO) {
871        for (var label in mediaGroups.AUDIO[mediaGroup]) {
872          var properties = mediaGroups.AUDIO[mediaGroup][label];
873
874          if (!properties.uri) {
875            defaultDemuxed = false;
876          }
877        }
878      }
879
880      if (defaultDemuxed) {
881        this.tech_.trigger({ type: 'usage', name: 'hls-demuxed' });
882      }
883
884      if (Object.keys(mediaGroups.SUBTITLES).length) {
885        this.tech_.trigger({ type: 'usage', name: 'hls-webvtt' });
886      }
887
888      if (Hls.Playlist.isAes(media)) {
889        this.tech_.trigger({ type: 'usage', name: 'hls-aes' });
890      }
891
892      if (Hls.Playlist.isFmp4(media)) {
893        this.tech_.trigger({ type: 'usage', name: 'hls-fmp4' });
894      }
895
896      if (audioGroupKeys.length && Object.keys(mediaGroups.AUDIO[audioGroupKeys[0]]).length > 1) {
897        this.tech_.trigger({ type: 'usage', name: 'hls-alternate-audio' });
898      }
899
900      if (this.useCueTags_) {
901        this.tech_.trigger({ type: 'usage', name: 'hls-playlist-cue-tags' });
902      }
903    }
904
905    /**
906     * Register event handlers on the segment loaders. A helper function
907     * for construction time.
908     *
909     * @private
910     */
911  }, {
912    key: 'setupSegmentLoaderListeners_',
913    value: function setupSegmentLoaderListeners_() {
914      var _this3 = this;
915
916      this.mainSegmentLoader_.on('bandwidthupdate', function () {
917        var nextPlaylist = _this3.selectPlaylist();
918        var currentPlaylist = _this3.masterPlaylistLoader_.media();
919        var buffered = _this3.tech_.buffered();
920        var forwardBuffer = buffered.length ? buffered.end(buffered.length - 1) - _this3.tech_.currentTime() : 0;
921
922        var bufferLowWaterLine = _this3.bufferLowWaterLine();
923
924        // If the playlist is live, then we want to not take low water line into account.
925        // This is because in LIVE, the player plays 3 segments from the end of the
926        // playlist, and if `BUFFER_LOW_WATER_LINE` is greater than the duration availble
927        // in those segments, a viewer will never experience a rendition upswitch.
928        if (!currentPlaylist.endList ||
929        // For the same reason as LIVE, we ignore the low water line when the VOD
930        // duration is below the max potential low water line
931        _this3.duration() < _config2['default'].MAX_BUFFER_LOW_WATER_LINE ||
932        // we want to switch down to lower resolutions quickly to continue playback, but
933        nextPlaylist.attributes.BANDWIDTH < currentPlaylist.attributes.BANDWIDTH ||
934        // ensure we have some buffer before we switch up to prevent us running out of
935        // buffer while loading a higher rendition.
936        forwardBuffer >= bufferLowWaterLine) {
937          _this3.masterPlaylistLoader_.media(nextPlaylist);
938        }
939
940        _this3.tech_.trigger('bandwidthupdate');
941      });
942      this.mainSegmentLoader_.on('progress', function () {
943        _this3.trigger('progress');
944      });
945
946      this.mainSegmentLoader_.on('error', function () {
947        _this3.blacklistCurrentPlaylist(_this3.mainSegmentLoader_.error());
948      });
949
950      this.mainSegmentLoader_.on('syncinfoupdate', function () {
951        _this3.onSyncInfoUpdate_();
952      });
953
954      this.mainSegmentLoader_.on('timestampoffset', function () {
955        _this3.tech_.trigger({ type: 'usage', name: 'hls-timestamp-offset' });
956      });
957      this.audioSegmentLoader_.on('syncinfoupdate', function () {
958        _this3.onSyncInfoUpdate_();
959      });
960
961      this.mainSegmentLoader_.on('ended', function () {
962        _this3.onEndOfStream();
963      });
964
965      this.mainSegmentLoader_.on('earlyabort', function () {
966        _this3.blacklistCurrentPlaylist({
967          message: 'Aborted early because there isn\'t enough bandwidth to complete the ' + 'request without rebuffering.'
968        }, ABORT_EARLY_BLACKLIST_SECONDS);
969      });
970
971      this.mainSegmentLoader_.on('reseteverything', function () {
972        _this3.tech_.trigger('hls-reset');
973      });
974
975      this.audioSegmentLoader_.on('ended', function () {
976        _this3.onEndOfStream();
977      });
978    }
979  }, {
980    key: 'mediaSecondsLoaded_',
981    value: function mediaSecondsLoaded_() {
982      return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded + this.mainSegmentLoader_.mediaSecondsLoaded);
983    }
984
985    /**
986     * Call load on our SegmentLoaders
987     */
988  }, {
989    key: 'load',
990    value: function load() {
991      this.mainSegmentLoader_.load();
992      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
993        this.audioSegmentLoader_.load();
994      }
995      if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
996        this.subtitleSegmentLoader_.load();
997      }
998    }
999
1000    /**
1001     * Re-tune playback quality level for the current player
1002     * conditions. This method may perform destructive actions, like
1003     * removing already buffered content, to readjust the currently
1004     * active playlist quickly.
1005     *
1006     * @private
1007     */
1008  }, {
1009    key: 'fastQualityChange_',
1010    value: function fastQualityChange_() {
1011      var media = this.selectPlaylist();
1012
1013      if (media !== this.masterPlaylistLoader_.media()) {
1014        this.masterPlaylistLoader_.media(media);
1015
1016        this.mainSegmentLoader_.resetLoader();
1017        // don't need to reset audio as it is reset when media changes
1018      }
1019    }
1020
1021    /**
1022     * Begin playback.
1023     */
1024  }, {
1025    key: 'play',
1026    value: function play() {
1027      if (this.setupFirstPlay()) {
1028        return;
1029      }
1030
1031      if (this.tech_.ended()) {
1032        this.tech_.setCurrentTime(0);
1033      }
1034
1035      if (this.hasPlayed_()) {
1036        this.load();
1037      }
1038
1039      var seekable = this.tech_.seekable();
1040
1041      // if the viewer has paused and we fell out of the live window,
1042      // seek forward to the live point
1043      if (this.tech_.duration() === Infinity) {
1044        if (this.tech_.currentTime() < seekable.start(0)) {
1045          return this.tech_.setCurrentTime(seekable.end(seekable.length - 1));
1046        }
1047      }
1048    }
1049
1050    /**
1051     * Seek to the latest media position if this is a live video and the
1052     * player and video are loaded and initialized.
1053     */
1054  }, {
1055    key: 'setupFirstPlay',
1056    value: function setupFirstPlay() {
1057      var seekable = undefined;
1058      var media = this.masterPlaylistLoader_.media();
1059
1060      // check that everything is ready to begin buffering in the live
1061      // scenario
1062      // 1) the active media playlist is available
1063      if (media &&
1064      // 2) the player is not paused
1065      !this.tech_.paused() &&
1066      // 3) the player has not started playing
1067      !this.hasPlayed_()) {
1068
1069        // when the video is a live stream
1070        if (!media.endList) {
1071          this.trigger('firstplay');
1072
1073          // seek to the latest media position for live videos
1074          seekable = this.seekable();
1075          if (seekable.length) {
1076            this.tech_.setCurrentTime(seekable.end(0));
1077          }
1078        }
1079        this.hasPlayed_ = function () {
1080          return true;
1081        };
1082        // now that we are ready, load the segment
1083        this.load();
1084        return true;
1085      }
1086      return false;
1087    }
1088
1089    /**
1090     * handle the sourceopen event on the MediaSource
1091     *
1092     * @private
1093     */
1094  }, {
1095    key: 'handleSourceOpen_',
1096    value: function handleSourceOpen_() {
1097      // Only attempt to create the source buffer if none already exist.
1098      // handleSourceOpen is also called when we are "re-opening" a source buffer
1099      // after `endOfStream` has been called (in response to a seek for instance)
1100      try {
1101        this.setupSourceBuffers_();
1102      } catch (e) {
1103        _videoJs2['default'].log.warn('Failed to create Source Buffers', e);
1104        return this.mediaSource.endOfStream('decode');
1105      }
1106
1107      // if autoplay is enabled, begin playback. This is duplicative of
1108      // code in video.js but is required because play() must be invoked
1109      // *after* the media source has opened.
1110      if (this.tech_.autoplay()) {
1111        this.tech_.play();
1112      }
1113
1114      this.trigger('sourceopen');
1115    }
1116
1117    /**
1118     * Calls endOfStream on the media source when all active stream types have called
1119     * endOfStream
1120     *
1121     * @param {string} streamType
1122     *        Stream type of the segment loader that called endOfStream
1123     * @private
1124     */
1125  }, {
1126    key: 'onEndOfStream',
1127    value: function onEndOfStream() {
1128      var isEndOfStream = this.mainSegmentLoader_.ended_;
1129
1130      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
1131        // if the audio playlist loader exists, then alternate audio is active, so we need
1132        // to wait for both the main and audio segment loaders to call endOfStream
1133        isEndOfStream = isEndOfStream && this.audioSegmentLoader_.ended_;
1134      }
1135
1136      if (isEndOfStream) {
1137        this.mediaSource.endOfStream();
1138      }
1139    }
1140
1141    /**
1142     * Check if a playlist has stopped being updated
1143     * @param {Object} playlist the media playlist object
1144     * @return {boolean} whether the playlist has stopped being updated or not
1145     */
1146  }, {
1147    key: 'stuckAtPlaylistEnd_',
1148    value: function stuckAtPlaylistEnd_(playlist) {
1149      var seekable = this.seekable();
1150
1151      if (!seekable.length) {
1152        // playlist doesn't have enough information to determine whether we are stuck
1153        return false;
1154      }
1155
1156      var expired = this.syncController_.getExpiredTime(playlist, this.mediaSource.duration);
1157
1158      if (expired === null) {
1159        return false;
1160      }
1161
1162      // does not use the safe live end to calculate playlist end, since we
1163      // don't want to say we are stuck while there is still content
1164      var absolutePlaylistEnd = Hls.Playlist.playlistEnd(playlist, expired);
1165      var currentTime = this.tech_.currentTime();
1166      var buffered = this.tech_.buffered();
1167
1168      if (!buffered.length) {
1169        // return true if the playhead reached the absolute end of the playlist
1170        return absolutePlaylistEnd - currentTime <= _ranges2['default'].TIME_FUDGE_FACTOR;
1171      }
1172      var bufferedEnd = buffered.end(buffered.length - 1);
1173
1174      // return true if there is too little buffer left and
1175      // buffer has reached absolute end of playlist
1176      return bufferedEnd - currentTime <= _ranges2['default'].TIME_FUDGE_FACTOR && absolutePlaylistEnd - bufferedEnd <= _ranges2['default'].TIME_FUDGE_FACTOR;
1177    }
1178
1179    /**
1180     * Blacklists a playlist when an error occurs for a set amount of time
1181     * making it unavailable for selection by the rendition selection algorithm
1182     * and then forces a new playlist (rendition) selection.
1183     *
1184     * @param {Object=} error an optional error that may include the playlist
1185     * to blacklist
1186     * @param {Number=} blacklistDuration an optional number of seconds to blacklist the
1187     * playlist
1188     */
1189  }, {
1190    key: 'blacklistCurrentPlaylist',
1191    value: function blacklistCurrentPlaylist(error, blacklistDuration) {
1192      if (error === undefined) error = {};
1193
1194      var currentPlaylist = undefined;
1195      var nextPlaylist = undefined;
1196
1197      // If the `error` was generated by the playlist loader, it will contain
1198      // the playlist we were trying to load (but failed) and that should be
1199      // blacklisted instead of the currently selected playlist which is likely
1200      // out-of-date in this scenario
1201      currentPlaylist = error.playlist || this.masterPlaylistLoader_.media();
1202
1203      // If there is no current playlist, then an error occurred while we were
1204      // trying to load the master OR while we were disposing of the tech
1205      if (!currentPlaylist) {
1206        this.error = error;
1207
1208        try {
1209          return this.mediaSource.endOfStream('network');
1210        } catch (e) {
1211          return this.trigger('error');
1212        }
1213      }
1214
1215      var isFinalRendition = this.masterPlaylistLoader_.isFinalRendition_();
1216
1217      if (isFinalRendition) {
vendor: 6,883 bytes, lines 1218-1436
1218        // Never blacklisting this playlist because it's final rendition
1219        _videoJs2['default'].log.warn('Problem encountered with the current ' + 'HLS playlist. Trying again since it is the final playlist.');
1220
1221        this.tech_.trigger('retryplaylist');
1222        return this.masterPlaylistLoader_.load(isFinalRendition);
1223      }
1224      // Blacklist this playlist
1225      currentPlaylist.excludeUntil = Date.now() + (blacklistDuration ? blacklistDuration : this.blacklistDuration) * 1000;
1226      this.tech_.trigger('blacklistplaylist');
1227      this.tech_.trigger({ type: 'usage', name: 'hls-rendition-blacklisted' });
1228
1229      // Select a new playlist
1230      nextPlaylist = this.selectPlaylist();
1231      _videoJs2['default'].log.warn('Problem encountered with the current HLS playlist.' + (error.message ? ' ' + error.message : '') + ' Switching to another playlist.');
1232
1233      return this.masterPlaylistLoader_.media(nextPlaylist);
1234    }
1235
1236    /**
1237     * Pause all segment loaders
1238     */
1239  }, {
1240    key: 'pauseLoading',
1241    value: function pauseLoading() {
1242      this.mainSegmentLoader_.pause();
1243      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
1244        this.audioSegmentLoader_.pause();
1245      }
1246      if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
1247        this.subtitleSegmentLoader_.pause();
1248      }
1249    }
1250
1251    /**
1252     * set the current time on all segment loaders
1253     *
1254     * @param {TimeRange} currentTime the current time to set
1255     * @return {TimeRange} the current time
1256     */
1257  }, {
1258    key: 'setCurrentTime',
1259    value: function setCurrentTime(currentTime) {
1260      var buffered = _ranges2['default'].findRange(this.tech_.buffered(), currentTime);
1261
1262      if (!(this.masterPlaylistLoader_ && this.masterPlaylistLoader_.media())) {
1263        // return immediately if the metadata is not ready yet
1264        return 0;
1265      }
1266
1267      // it's clearly an edge-case but don't thrown an error if asked to
1268      // seek within an empty playlist
1269      if (!this.masterPlaylistLoader_.media().segments) {
1270        return 0;
1271      }
1272
1273      // In flash playback, the segment loaders should be reset on every seek, even
1274      // in buffer seeks
1275      var isFlash = this.mode_ === 'flash' || this.mode_ === 'auto' && !_videoJs2['default'].MediaSource.supportsNativeMediaSources();
1276
1277      // if the seek location is already buffered, continue buffering as
1278      // usual
1279      if (buffered && buffered.length && !isFlash) {
1280        return currentTime;
1281      }
1282
1283      // cancel outstanding requests so we begin buffering at the new
1284      // location
1285      this.mainSegmentLoader_.resetEverything();
1286      this.mainSegmentLoader_.abort();
1287      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
1288        this.audioSegmentLoader_.resetEverything();
1289        this.audioSegmentLoader_.abort();
1290      }
1291      if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
1292        this.subtitleSegmentLoader_.resetEverything();
1293        this.subtitleSegmentLoader_.abort();
1294      }
1295
1296      // start segment loader loading in case they are paused
1297      this.load();
1298    }
1299
1300    /**
1301     * get the current duration
1302     *
1303     * @return {TimeRange} the duration
1304     */
1305  }, {
1306    key: 'duration',
1307    value: function duration() {
1308      if (!this.masterPlaylistLoader_) {
1309        return 0;
1310      }
1311
1312      if (this.mediaSource) {
1313        return this.mediaSource.duration;
1314      }
1315
1316      return Hls.Playlist.duration(this.masterPlaylistLoader_.media());
1317    }
1318
1319    /**
1320     * check the seekable range
1321     *
1322     * @return {TimeRange} the seekable range
1323     */
1324  }, {
1325    key: 'seekable',
1326    value: function seekable() {
1327      return this.seekable_;
1328    }
1329  }, {
1330    key: 'onSyncInfoUpdate_',
1331    value: function onSyncInfoUpdate_() {
1332      var mainSeekable = undefined;
1333      var audioSeekable = undefined;
1334
1335      if (!this.masterPlaylistLoader_) {
1336        return;
1337      }
1338
1339      var media = this.masterPlaylistLoader_.media();
1340
1341      if (!media) {
1342        return;
1343      }
1344
1345      var expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
1346
1347      if (expired === null) {
1348        // not enough information to update seekable
1349        return;
1350      }
1351
1352      mainSeekable = Hls.Playlist.seekable(media, expired);
1353
1354      if (mainSeekable.length === 0) {
1355        return;
1356      }
1357
1358      if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
1359        media = this.mediaTypes_.AUDIO.activePlaylistLoader.media();
1360        expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
1361
1362        if (expired === null) {
1363          return;
1364        }
1365
1366        audioSeekable = Hls.Playlist.seekable(media, expired);
1367
1368        if (audioSeekable.length === 0) {
1369          return;
1370        }
1371      }
1372
1373      if (!audioSeekable) {
1374        // seekable has been calculated based on buffering video data so it
1375        // can be returned directly
1376        this.seekable_ = mainSeekable;
1377      } else if (audioSeekable.start(0) > mainSeekable.end(0) || mainSeekable.start(0) > audioSeekable.end(0)) {
1378        // seekables are pretty far off, rely on main
1379        this.seekable_ = mainSeekable;
1380      } else {
1381        this.seekable_ = _videoJs2['default'].createTimeRanges([[audioSeekable.start(0) > mainSeekable.start(0) ? audioSeekable.start(0) : mainSeekable.start(0), audioSeekable.end(0) < mainSeekable.end(0) ? audioSeekable.end(0) : mainSeekable.end(0)]]);
1382      }
1383
1384      this.tech_.trigger('seekablechanged');
1385    }
1386
1387    /**
1388     * Update the player duration
1389     */
1390  }, {
1391    key: 'updateDuration',
1392    value: function updateDuration() {
1393      var _this4 = this;
1394
1395      var oldDuration = this.mediaSource.duration;
1396      var newDuration = Hls.Playlist.duration(this.masterPlaylistLoader_.media());
1397      var buffered = this.tech_.buffered();
1398      var setDuration = function setDuration() {
1399        _this4.mediaSource.duration = newDuration;
1400        _this4.tech_.trigger('durationchange');
1401
1402        _this4.mediaSource.removeEventListener('sourceopen', setDuration);
1403      };
1404
1405      if (buffered.length > 0) {
1406        newDuration = Math.max(newDuration, buffered.end(buffered.length - 1));
1407      }
1408
1409      // if the duration has changed, invalidate the cached value
1410      if (oldDuration !== newDuration) {
1411        // update the duration
1412        if (this.mediaSource.readyState !== 'open') {
1413          this.mediaSource.addEventListener('sourceopen', setDuration);
1414        } else {
1415          setDuration();
1416        }
1417      }
1418    }
1419
1420    /**
1421     * dispose of the MasterPlaylistController and everything
1422     * that it controls
1423     */
1424  }, {
1425    key: 'dispose',
1426    value: function dispose() {
1427      var _this5 = this;
1428
1429      this.decrypter_.terminate();
1430      this.masterPlaylistLoader_.dispose();
1431      this.mainSegmentLoader_.dispose();
1432
1433      ['AUDIO', 'SUBTITLES'].forEach(function (type) {
1434        var groups = _this5.mediaTypes_[type].groups;
1435
1436        for (var id in groups) {
1437          groups[id].forEach(function (group) {
1438            if (group.playlistLoader) {
1439              group.playlistLoader.dispose();
1440            }
1441          });
1442        }
1443      });
1444
1445      this.audioSegmentLoader_.dispose();
1446      this.subtitleSegmentLoader_.dispose();
1447    }
1448
1449    /**
1450     * return the master playlist object if we have one
1451     *
1452     * @return {Object} the master playlist object that we parsed
1453     */
1454  }, {
1455    key: 'master',
1456    value: function master() {
1457      return this.masterPlaylistLoader_.master;
1458    }
1459
1460    /**
1461     * return the currently selected playlist
1462     *
1463     * @return {Object} the currently selected playlist object that we parsed
1464     */
1465  }, {
1466    key: 'media',
1467    value: function media() {
1468      // playlist loader will not return media if it has not been fully loaded
1469      return this.masterPlaylistLoader_.media() || this.initialMedia_;
1470    }
1471
1472    /**
1473     * setup our internal source buffers on our segment Loaders
1474     *
1475     * @private
1476     */
1477  }, {
1478    key: 'setupSourceBuffers_',
1479    value: function setupSourceBuffers_() {
1480      var media = this.masterPlaylistLoader_.media();
1481      var mimeTypes = undefined;
1482
1483      // wait until a media playlist is available and the Media Source is
1484      // attached
1485      if (!media || this.mediaSource.readyState !== 'open') {
1486        return;
1487      }
1488
1489      mimeTypes = mimeTypesForPlaylist_(this.masterPlaylistLoader_.master, media);
1490      if (mimeTypes.length < 1) {
1491        this.error = 'No compatible SourceBuffer configuration for the variant stream:' + media.resolvedUri;
1492        return this.mediaSource.endOfStream('decode');
1493      }
1494      this.mainSegmentLoader_.mimeType(mimeTypes[0]);
1495      if (mimeTypes[1]) {
1496        this.audioSegmentLoader_.mimeType(mimeTypes[1]);
1497      }
1498
1499      // exclude any incompatible variant streams from future playlist
1500      // selection
1501      this.excludeIncompatibleVariants_(media);
1502    }
1503
1504    /**
1505     * Blacklist playlists that are known to be codec or
1506     * stream-incompatible with the SourceBuffer configuration. For
1507     * instance, Media Source Extensions would cause the video element to
1508     * stall waiting for video data if you switched from a variant with
1509     * video and audio to an audio-only one.
1510     *
1511     * @param {Object} media a media playlist compatible with the current
1512     * set of SourceBuffers. Variants in the current master playlist that
1513     * do not appear to have compatible codec or stream configurations
1514     * will be excluded from the default playlist selection algorithm
1515     * indefinitely.
1516     * @private
1517     */
1518  }, {
1519    key: 'excludeIncompatibleVariants_',
1520    value: function excludeIncompatibleVariants_(media) {
1521      var master = this.masterPlaylistLoader_.master;
1522      var codecCount = 2;
1523      var videoCodec = null;
1524      var codecs = undefined;
1525
1526      if (media.attributes.CODECS) {
1527        codecs = (0, _utilCodecsJs.parseCodecs)(media.attributes.CODECS);
1528        videoCodec = codecs.videoCodec;
1529        codecCount = codecs.codecCount;
1530      }
1531      master.playlists.forEach(function (variant) {
1532        var variantCodecs = {
1533          codecCount: 2,
1534          videoCodec: null
1535        };
1536
1537        if (variant.attributes.CODECS) {
1538          var codecString = variant.attributes.CODECS;
1539
1540          variantCodecs = (0, _utilCodecsJs.parseCodecs)(codecString);
1541
1542          if (window.MediaSource && window.MediaSource.isTypeSupported && !window.MediaSource.isTypeSupported('video/mp4; codecs="' + mapLegacyAvcCodecs_(codecString) + '"')) {
1543            variant.excludeUntil = Infinity;
1544          }
1545        }
1546
1547        // if the streams differ in the presence or absence of audio or
1548        // video, they are incompatible
1549        if (variantCodecs.codecCount !== codecCount) {
1550          variant.excludeUntil = Infinity;
1551        }
1552
1553        // if h.264 is specified on the current playlist, some flavor of
1554        // it must be specified on all compatible variants
1555        if (variantCodecs.videoCodec !== videoCodec) {
1556          variant.excludeUntil = Infinity;
1557        }
1558      });
1559    }
1560  }, {
1561    key: 'updateAdCues_',
1562    value: function updateAdCues_(media) {
1563      var offset = 0;
1564      var seekable = this.seekable();
1565
1566      if (seekable.length) {
1567        offset = seekable.start(0);
1568      }
1569
1570      _adCueTags2['default'].updateAdCues(media, this.cueTagsTrack_, offset);
1571    }
1572
1573    /**
1574     * Calculates the desired forward buffer length based on current time
1575     *
1576     * @return {Number} Desired forward buffer length in seconds
1577     */
1578  }, {
1579    key: 'goalBufferLength',
1580    value: function goalBufferLength() {
1581      var currentTime = this.tech_.currentTime();
1582      var initial = _config2['default'].GOAL_BUFFER_LENGTH;
1583      var rate = _config2['default'].GOAL_BUFFER_LENGTH_RATE;
1584      var max = Math.max(initial, _config2['default'].MAX_GOAL_BUFFER_LENGTH);
1585
1586      return Math.min(initial + currentTime * rate, max);
1587    }
1588
1589    /**
1590     * Calculates the desired buffer low water line based on current time
1591     *
1592     * @return {Number} Desired buffer low water line in seconds
1593     */
1594  }, {
1595    key: 'bufferLowWaterLine',
1596    value: function bufferLowWaterLine() {
1597      var currentTime = this.tech_.currentTime();
1598      var initial = _config2['default'].BUFFER_LOW_WATER_LINE;
1599      var rate = _config2['default'].BUFFER_LOW_WATER_LINE_RATE;
1600      var max = Math.max(initial, _config2['default'].MAX_BUFFER_LOW_WATER_LINE);
1601
1602      return Math.min(initial + currentTime * rate, max);
1603    }
1604  }]);
1605
1606  return MasterPlaylistController;
1607})(_videoJs2['default'].EventTarget);
1608
1609exports.MasterPlaylistController = MasterPlaylistController;
1610}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
1611},{"./ad-cue-tags":1,"./config":3,"./decrypter-worker":4,"./media-groups":6,"./playlist-loader":9,"./ranges":12,"./segment-loader":16,"./sync-controller":18,"./util/codecs.js":19,"./vtt-segment-loader":20,"videojs-contrib-media-sources/es5/codec-utils":65,"webworkify":76}],6:[function(require,module,exports){
1612(function (global){
1613'use strict';
1614
1615Object.defineProperty(exports, '__esModule', {
1616  value: true
1617});
1618
1619function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
1620
1621var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
1622
1623var _videoJs2 = _interopRequireDefault(_videoJs);
1624
1625var _playlistLoader = require('./playlist-loader');
1626
1627var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
1628
1629var noop = function noop() {};
1630
1631/**
1632 * Convert the properties of an HLS track into an audioTrackKind.
1633 *
1634 * @private
1635 */
1636var audioTrackKind_ = function audioTrackKind_(properties) {
1637  var kind = properties['default'] ? 'main' : 'alternative';
1638
1639  if (properties.characteristics && properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) {
1640    kind = 'main-desc';
1641  }
1642
1643  return kind;
1644};
1645
1646/**
1647 * Pause provided segment loader and playlist loader if active
1648 *
1649 * @param {SegmentLoader} segmentLoader
1650 *        SegmentLoader to pause
1651 * @param {Object} mediaType
1652 *        Active media type
1653 * @function stopLoaders
1654 */
1655var stopLoaders = function stopLoaders(segmentLoader, mediaType) {
1656  segmentLoader.abort();
1657  segmentLoader.pause();
1658
1659  if (mediaType && mediaType.activePlaylistLoader) {
1660    mediaType.activePlaylistLoader.pause();
1661    mediaType.activePlaylistLoader = null;
1662  }
1663};
1664
1665exports.stopLoaders = stopLoaders;
1666/**
1667 * Start loading provided segment loader and playlist loader
1668 *
1669 * @param {PlaylistLoader} playlistLoader
1670 *        PlaylistLoader to start loading
1671 * @param {Object} mediaType
1672 *        Active media type
1673 * @function startLoaders
1674 */
1675var startLoaders = function startLoaders(playlistLoader, mediaType) {
1676  // Segment loader will be started after `loadedmetadata` or `loadedplaylist` from the
1677  // playlist loader
1678  mediaType.activePlaylistLoader = playlistLoader;
1679  playlistLoader.load();
1680};
1681
1682exports.startLoaders = startLoaders;
1683/**
1684 * Returns a function to be called when the media group changes. It performs a
1685 * non-destructive (preserve the buffer) resync of the SegmentLoader. This is because a
1686 * change of group is merely a rendition switch of the same content at another encoding,
1687 * rather than a change of content, such as switching audio from English to Spanish.
1688 *
1689 * @param {String} type
1690 *        MediaGroup type
1691 * @param {Object} settings
1692 *        Object containing required information for media groups
1693 * @return {Function}
1694 *         Handler for a non-destructive resync of SegmentLoader when the active media
1695 *         group changes.
1696 * @function onGroupChanged
1697 */
1698var onGroupChanged = function onGroupChanged(type, settings) {
1699  return function () {
1700    var _settings$segmentLoaders = settings.segmentLoaders;
1701    var segmentLoader = _settings$segmentLoaders[type];
1702    var mainSegmentLoader = _settings$segmentLoaders.main;
1703    var mediaType = settings.mediaTypes[type];
1704
1705    var activeTrack = mediaType.activeTrack();
1706    var activeGroup = mediaType.activeGroup(activeTrack);
1707    var previousActiveLoader = mediaType.activePlaylistLoader;
1708
1709    stopLoaders(segmentLoader, mediaType);
1710
1711    if (!activeGroup) {
1712      // there is no group active
1713      return;
1714    }
1715
1716    if (!activeGroup.playlistLoader) {
1717      if (previousActiveLoader) {
1718        // The previous group had a playlist loader but the new active group does not
1719        // this means we are switching from demuxed to muxed audio. In this case we want to
1720        // do a destructive reset of the main segment loader and n
1720ot restart the audio
1721        // loaders.
1722        mainSegmentLoader.resetEverything();
1723      }
1724      return;
1725    }
1726
1727    // Non-destructive resync
1728    segmentLoader.resyncLoader();
1729
1730    startLoaders(activeGroup.playlistLoader, mediaType);
1731  };
1732};
1733
1734exports.onGroupChanged = onGroupChanged;
1735/**
1736 * Returns a function to be called when the media track changes. It performs a
1737 * destructive reset of the SegmentLoader to ensure we start loading as close to
1738 * currentTime as possible.
1739 *
1740 * @param {String} type
1741 *        MediaGroup type
1742 * @param {Object} settings
1743 *        Object containing required information for media groups
1744 * @return {Function}
1745 *         Handler for a destructive reset of SegmentLoader when the active media
1746 *         track changes.
1747 * @function onTrackChanged
1748 */
1749var onTrackChanged = function onTrackChanged(type, settings) {
1750  return function () {
1751    var _settings$segmentLoaders2 = settings.segmentLoaders;
1752    var segmentLoader = _settings$segmentLoaders2[type];
1753    var mainSegmentLoader = _settings$segmentLoaders2.main;
1754    var mediaType = settings.mediaTypes[type];
1755
1756    var activeTrack = mediaType.activeTrack();
1757    var activeGroup = mediaType.activeGroup(activeTrack);
1758    var previousActiveLoader = mediaType.activePlaylistLoader;
1759
1760    stopLoaders(segmentLoader, mediaType);
1761
1762    if (!activeGroup) {
1763      // there is no group active so we do not want to restart loaders
1764      return;
1765    }
1766
1767    if (!activeGroup.playlistLoader) {
1768      // when switching from demuxed audio/video to muxed audio/video (noted by no playlist
1769      // loader for the audio group), we want to do a destructive reset of the main segment
1770      // loader and not restart the audio loaders
1771      mainSegmentLoader.resetEverything();
1772      return;
1773    }
1774
1775    if (previousActiveLoader === activeGroup.playlistLoader) {
1776      // Nothing has actually changed. This can happen because track change events can fire
1777      // multiple times for a "single" change. One for enabling the new active track, and
1778      // one for disabling the track that was active
1779      startLoaders(activeGroup.playlistLoader, mediaType);
1780      return;
1781    }
1782
1783    if (segmentLoader.track) {
1784      // For WebVTT, set the new text track in the segmentloader
1785      segmentLoader.track(activeTrack);
1786    }
1787
1788    // destructive reset
1789    segmentLoader.resetEverything();
1790
1791    startLoaders(activeGroup.playlistLoader, mediaType);
1792  };
1793};
1794
1795exports.onTrackChanged = onTrackChanged;
1796var onError = {
1797  /**
1798   * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
1799   * an error.
1800   *
1801   * @param {String} type
1802   *        MediaGroup type
1803   * @param {Object} settings
1804   *        Object containing required information for media groups
1805   * @return {Function}
1806   *         Error handler. Logs warning (or error if the playlist is blacklisted) to
1807   *         console and switches back to default audio track.
1808   * @function onError.AUDIO
1809   */
1810  AUDIO: function AUDIO(type, settings) {
1811    return function () {
1812      var segmentLoader = settings.segmentLoaders[type];
1813      var mediaType = settings.mediaTypes[type];
1814      var blacklistCurrentPlaylist = settings.blacklistCurrentPlaylist;
1815
1816      stopLoaders(segmentLoader, mediaType);
1817
1818      // switch back to default audio track
1819      var activeTrack = mediaType.activeTrack();
1820      var activeGroup = mediaType.activeGroup();
1821      var id = (activeGroup.filter(function (group) {
1822        return group['default'];
1823      })[0] || activeGroup[0]).id;
1824      var defaultTrack = mediaType.tracks[id];
1825
1826      if (activeTrack === defaultTrack) {
1827        // Default track encountered an error. All we can do now is blacklist the current
1828        // rendition and hope another will switch audio groups
1829        blacklistCurrentPlaylist({
1830          message: 'Problem encountered loading the default audio track.'
1831        });
1832        return;
1833      }
1834
1835      _videoJs2['default'].log.warn('Problem encountered loading the alternate audio track.' + 'Switching back to default.');
1836
1837      for (var trackId in mediaType.tracks) {
1838        mediaType.tracks[trackId].enabled = mediaType.tracks[trackId] === defaultTrack;
1839      }
1840
1841      mediaType.onTrackChanged();
1842    };
1843  },
1844  /**
1845   * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
1846   * an error.
1847   *
1848   * @param {String} type
1849   *        MediaGroup type
1850   * @param {Object} settings
1851   *        Object containing required information for media groups
1852   * @return {Function}
1853   *         Error handler. Logs warning to console and disables the active subtitle track
1854   * @function onError.SUBTITLES
1855   */
1856  SUBTITLES: function SUBTITLES(type, settings) {
1857    return function () {
1858      var segmentLoader = settings.segmentLoaders[type];
1859      var mediaType = settings.mediaTypes[type];
1860
1861      _videoJs2['default'].log.warn('Problem encountered loading the subtitle track.' + 'Disabling subtitle track.');
1862
1863      stopLoaders(segmentLoader, mediaType);
1864
1865      var track = mediaType.activeTrack();
1866
1867      if (track) {
1868        track.mode = 'disabled';
1869      }
1870
1871      mediaType.onTrackChanged();
1872    };
1873  }
1874};
1875
1876exports.onError = onError;
1877var setupListeners = {
1878  /**
1879   * Setup event listeners for audio playlist loader
1880   *
1881   * @param {String} type
1882   *        MediaGroup type
1883   * @param {PlaylistLoader|null} playlistLoader
1884   *        PlaylistLoader to register listeners on
1885   * @param {Object} settings
1886   *        Object containing required information for media groups
1887   * @function setupListeners.AUDIO
1888   */
1889  AUDIO: function AUDIO(type, playlistLoader, settings) {
1890    if (!playlistLoader) {
1891      // no playlist loader means audio will be muxed with the video
1892      return;
1893    }
1894
1895    var tech = settings.tech;
1896    var requestOptions = settings.requestOptions;
1897    var segmentLoader = settings.segmentLoaders[type];
1898
1899    playlistLoader.on('loadedmetadata', function () {
1900      var media = playlistLoader.media();
1901
1902      segmentLoader.playlist(media, requestOptions);
1903
1904      // if the video is already playing, or if this isn't a live video and preload
1905      // permits, start downloading segments
1906      if (!tech.paused() || media.endList && tech.preload() !== 'none') {
1907        segmentLoader.load();
1908      }
1909    });
1910
1911    playlistLoader.on('loadedplaylist', function () {
1912      segmentLoader.playlist(playlistLoader.media(), requestOptions);
1913
1914      // If the player isn't paused, ensure that the segment loader is running
1915      if (!tech.paused()) {
1916        segmentLoader.load();
1917      }
1918    });
1919
1920    playlistLoader.on('error', onError[type](type, settings));
1921  },
1922  /**
1923   * Setup event listeners for subtitle playlist loader
1924   *
1925   * @param {String} type
1926   *        MediaGroup type
1927   * @param {PlaylistLoader|null} playlistLoader
1928   *        PlaylistLoader to register listeners on
1929   * @param {Object} settings
1930   *        Object containing required information for media groups
1931   * @function setupListeners.SUBTITLES
1932   */
1933  SUBTITLES: function SUBTITLES(type, playlistLoader, settings) {
1934    var tech = settings.tech;
1935    var requestOptions = settings.requestOptions;
1936    var segmentLoader = settings.segmentLoaders[type];
1937    var mediaType = settings.mediaTypes[type];
1938
1939    playlistLoader.on('loadedmetadata', function () {
1940      var media = playlistLoader.media();
1941
1942      segmentLoader.playlist(media, requestOptions);
1943      segmentLoader.track(mediaType.activeTrack());
1944
1945      // if the video is already playing, or if this isn't a live video and preload
1946      // permits, start downloading segments
1947      if (!tech.paused() || media.endList && tech.preload() !== 'none') {
1948        segmentLoader.load();
1949      }
1950    });
1951
1952    playlistLoader.on('loadedplaylist', function () {
1953      segmentLoader.playlist(playlistLoader.media(), requestOptions);
1954
1955      // If the player isn't paused, ensure that the segment loader is running
1956      if (!tech.paused()) {
1957        segmentLoader.load();
1958      }
1959    });
1960
1961    playlistLoader.on('error', onError[type](type, settings));
1962  }
1963};
1964
1965exports.setupListeners = setupListeners;
1966var initialize = {
1967  /**
1968   * Setup PlaylistLoaders and AudioTracks for the audio groups
1969   *
1970   * @param {String} type
1971   *        MediaGroup type
1972   * @param {Object} settings
1973   *        Object containing required information for media groups
1974   * @function initialize.AUDIO
1975   */
1976  'AUDIO': function AUDIO(type, settings) {
1977    var mode = settings.mode;
1978    var hls = settings.hls;
1979    var segmentLoader = settings.segmentLoaders[type];
1980    var withCredentials = settings.requestOptions.withCredentials;
1981    var mediaGroups = settings.master.mediaGroups;
1982    var _settings$mediaTypes$type = settings.mediaTypes[type];
1983    var groups = _settings$mediaTypes$type.groups;
1984    var tracks = _settings$mediaTypes$type.tracks;
1985
1986    // force a default if we have none or we are not
1987    // in html5 mode (the only mode to support more than one
1988    // audio track)
1989    if (!mediaGroups[type] || Object.keys(mediaGroups[type]).length === 0 || mode !== 'html5') {
1990      mediaGroups[type] = { main: { 'default': { 'default': true } } };
1991    }
1992
1993    for (var groupId in mediaGroups[type]) {
1994      if (!groups[groupId]) {
1995        groups[groupId] = [];
1996      }
1997
1998      for (var variantLabel in mediaGroups[type][groupId]) {
1999        var properties = mediaGroups[type][groupId][variantLabel];
2000        var playlistLoader = undefined;
2001
2002        if (properties.resolvedUri) {
2003          playlistLoader = new _playlistLoader2['default'](properties.resolvedUri, hls, withCredentials);
2004        } else {
2005          // no resolvedUri means the audio is muxed with the video when using this
2006          // audio track
2007          playlistLoader = null;
2008        }
2009
2010        properties = _videoJs2['default'].mergeOptions({ id: variantLabel, playlistLoader: playlistLoader }, properties);
2011
2012        setupListeners[type](type, properties.playlistLoader, settings);
2013
2014        groups[groupId].push(properties);
2015
2016        if (typeof tracks[variantLabel] === 'undefined') {
2017          var track = new _videoJs2['default'].AudioTrack({
2018            id: variantLabel,
2019            kind: audioTrackKind_(properties),
2020            enabled: false,
2021            language: properties.language,
2022            'default': properties['default'],
2023            label: variantLabel
2024          });
2025
2026          tracks[variantLabel] = track;
2027        }
2028      }
2029    }
2030
2031    // setup single error event handler for the segment loader
2032    segmentLoader.on('error', onError[type](type, settings));
2033  },
2034  /**
2035   * Setup PlaylistLoaders and TextTracks for the subtitle groups
2036   *
2037   * @param {String} type
2038   *        MediaGroup type
2039   * @param {Object} settings
2040   *        Object containing required information for media groups
2041   * @function initialize.SUBTITLES
2042   */
2043  'SUBTITLES': function SUBTITLES(type, settings) {
2044    var tech = settings.tech;
2045    var hls = settings.hls;
2046    var segmentLoader = settings.segmentLoaders[type];
2047    var withCredentials = settings.requestOptions.withCredentials;
2048    var mediaGroups = settings.master.mediaGroups;
2049    var _settings$mediaTypes$type2 = settings.mediaTypes[type];
2050    var groups = _settings$mediaTypes$type2.groups;
2051    var tracks = _settings$mediaTypes$type2.tracks;
2052
2053    for (var groupId in mediaGroups[type]) {
2054      if (!groups[groupId]) {
2055        groups[groupId] = [];
2056      }
2057
2058      for (var variantLabel in mediaGroups[type][groupId]) {
2059        if (mediaGroups[type][groupId][variantLabel].forced) {
2060          // Subtitle playlists with the forced attribute are not selectable in Safari.
2061          // According to Apple's HLS Authoring Specification:
2062          //   If content has forced subtitles and regular subtitles in a given language,
2063          //   the regular subtitles track in that language MUST contain both the forced
2064          //   subtitles and the regular subtitles for that language.
2065          // Because of this requirement and that Safari does not add forced subtitles,
2066          // forced subtitles are skipped here to maintain consistent experience across
2067          // all platforms
2068          continue;
2069        }
2070
2071        var properties = mediaGroups[type][groupId][variantLabel];
2072
2073        properties = _videoJs2['default'].mergeOptions({
2074          id: variantLabel,
2075          playlistLoader: new _playlistLoader2['default'](properties.resolvedUri, hls, withCredentials)
2076        }, properties);
2077
2078        setupListeners[type](type, properties.playlistLoader, settings);
2079
2080        groups[groupId].push(properties);
2081
2082        if (typeof tracks[variantLabel] === 'undefined') {
2083          var track = tech.addRemoteTextTrack({
2084            id: variantLabel,
2085            kind: 'subtitles',
2086            enabled: false,
2087            language: properties.language,
2088            label: variantLabel
2089          }, false).track;
2090
2091          tracks[variantLabel] = track;
2092        }
2093      }
2094    }
2095
2096    // setup single error event handler for the segment loader
2097    segmentLoader.on('error', onError[type](type, settings));
2098  },
2099  /**
2100   * Setup TextTracks for the closed-caption groups
2101   *
2102   * @param {String} type
2103   *        MediaGroup type
2104   * @param {Object} settings
2105   *        Object containing required information for media groups
2106   * @function initialize['CLOSED-CAPTIONS']
2107   */
2108  'CLOSED-CAPTIONS': function CLOSEDCAPTIONS(type, settings) {
2109    var tech = settings.tech;
2110    var mediaGroups = settings.master.mediaGroups;
2111    var _settings$mediaTypes$type3 = settings.mediaTypes[type];
2112    var groups = _settings$mediaTypes$type3.groups;
2113    var tracks = _settings$mediaTypes$type3.tracks;
2114
2115    for (var groupId in mediaGroups[type]) {
2116      if (!groups[groupId]) {
2117        groups[groupId] = [];
2118      }
2119
2120      for (var variantLabel in mediaGroups[type][groupId]) {
2121        var properties = mediaGroups[type][groupId][variantLabel];
2122
2123        // We only support CEA608 captions for now, so ignore anything that
2124        // doesn't use a CCx INSTREAM-ID
2125        if (!properties.instreamId.match(/CC\d/)) {
2126          continue;
2127        }
2128
2129        // No PlaylistLoader is required for Closed-Captions because the captions are
2130        // embedded within the video stream
2131        groups[groupId].push(_videoJs2['default'].mergeOptions({ id: variantLabel }, properties));
2132
2133        if (typeof tracks[variantLabel] === 'undefined') {
2134          var track = tech.addRemoteTextTrack({
2135            id: properties.instreamId,
2136            kind: 'captions',
2137            enabled: false,
2138            language: properties.language,
2139            label: variantLabel
2140          }, false).track;
2141
2142          tracks[variantLabel] = track;
2143        }
2144      }
2145    }
2146  }
2147};
2148
2149exports.initialize = initialize;
2150/**
2151 * Returns a function used to get the active group of the provided type
2152 *
2153 * @param {String} type
2154 *        MediaGroup type
2155 * @param {Object} settings
2156 *        Object containing required information for media groups
2157 * @return {Function}
2158 *         Function that returns the active media group for the provided type. Takes an
2159 *         optional parameter {TextTrack} track. If no track is provided, a list of all
2160 *         variants in the group, otherwise the variant corresponding to the provided
2161 *         track is returned.
2162 * @function activeGroup
2163 */
2164var activeGroup = function activeGroup(type, settings) {
2165  return function (track) {
2166    var masterPlaylistLoader = settings.masterPlaylistLoader;
2167    var groups = settings.mediaTypes[type].groups;
2168
2169    var media = masterPlaylistLoader.media();
2170
2171    if (!media) {
2172      return null;
2173    }
2174
2175    var variants = null;
2176
2177    if (media.attributes[type]) {
2178      variants = groups[media.attributes[type]];
2179    }
2180
2181    variants = variants || groups.main;
2182
2183    if (typeof track === 'undefined') {
2184      return variants;
2185    }
2186
2187    if (track === null) {
2188      // An active track was specified so a corresponding group is expected. track === null
2189      // means no track is currently active so there is no corresponding group
2190      return null;
2191    }
2192
2193    return variants.filter(function (props) {
2194      return props.id === track.id;
2195    })[0] || null;
2196  };
2197};
2198
2199exports.activeGroup = activeGroup;
2200var activeTrack = {
2201  /**
2202   * Returns a function used to get the active track of type provided
2203   *
2204   * @param {String} type
2205   *        MediaGroup type
2206   * @param {Object} settings
2207   *        Object containing required information for media groups
2208   * @return {Function}
2209   *         Function that returns the active media track for the provided type. Returns
2210   *         null if no track is active
2211   * @function activeTrack.AUDIO
2212   */
2213  AUDIO: function AUDIO(type, settings) {
2214    return function () {
2215      var tracks = settings.mediaTypes[type].tracks;
2216
2217      for (var id in tracks) {
2218        if (tracks[id].enabled) {
2219          return tracks[id];
2220        }
2221      }
2222
2223      return null;
2224    };
2225  },
2226  /**
2227   * Returns a function used to get the active track of type provided
2228   *
2229   * @param {String} type
2230   *        MediaGroup type
2231   * @param {Object} settings
2232   *        Object containing required information for media groups
2233   * @return {Function}
2234   *         Function that returns the active media track for the provided type. Returns
2235   *         null if no track is active
2236   * @function activeTrack.SUBTITLES
2237   */
2238  SUBTITLES: function SUBTITLES(type, settings) {
2239    return function () {
2240      var tracks = settings.mediaTypes[type].tracks;
2241
2242      for (var id in tracks) {
2243        if (tracks[id].mode === 'showing') {
2244          return tracks[id];
2245        }
2246      }
2247
2248      return null;
2249    };
2250  }
2251};
2252
2253exports.activeTrack = activeTrack;
2254/**
2255 * Setup PlaylistLoaders and Tracks for media groups (Audio, Subtitles,
2256 * Closed-Captions) specified in the master manifest.
2257 *
2258 * @param {Object} settings
2259 *        Object containing required information for setting up the media groups
2260 * @param {SegmentLoader} settings.segmentLoaders.AUDIO
2261 *        Audio segment loader
2262 * @param {SegmentLoader} settings.segmentLoaders.SUBTITLES
2263 *        Subtitle segment loader
2264 * @param {SegmentLoader} settings.segmentLoaders.main
2265 *        Main segment loader
2266 * @param {Tech} settings.tech
2267 *        The tech of the player
2268 * @param {Object} settings.requestOptions
2269 *        XHR request options used by the segment loaders
2270 * @param {PlaylistLoader} settings.masterPlaylistLoader
2271 *        PlaylistLoader for the master source
2272 * @param {String} mode
2273 *        Mode of the hls source handler. Can be 'auto', 'html5', or 'flash'
2274 * @param {HlsHandler} settings.hls
2275 *        HLS SourceHandler
2276 * @param {Object} settings.master
2277 *        The parsed master manifest
2278 * @param {Object} settings.mediaTypes
2279 *        Object to store the loaders, tracks, and utility methods for each media type
2280 * @param {Function} settings.blacklistCurrentPlaylist
2281 *        Blacklists the current rendition and forces a rendition switch.
2282 * @function setupMediaGroups
2283 */
2284var setupMediaGroups = function setupMediaGroups(settings) {
2285  ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
2286    initialize[type](type, settings);
2287  });
2288
2289  var mediaTypes = settings.mediaTypes;
2290  var masterPlaylistLoader = settings.masterPlaylistLoader;
2291  var tech = settings.tech;
2292  var hls = settings.hls;
2293
2294  // setup active group and track getters and change event handlers
2295  ['AUDIO', 'SUBTITLES'].forEach(function (type) {
2296    mediaTypes[type].activeGroup = activeGroup(type, settings);
2297    mediaTypes[type].activeTrack = activeTrack[type](type, settings);
2298    mediaTypes[type].onGroupChanged = onGroupChanged(type, settings);
2299    mediaTypes[type].onTrackChanged = onTrackChanged(type, settings);
2300  });
2301
2302  // DO NOT enable the default subtitle or caption track.
2303  // DO enable the default audio track
2304  var audioGroup = mediaTypes.AUDIO.activeGroup();
2305  var groupId = (audioGroup.filter(function (group) {
2306    return group['default'];
2307  })[0] || audioGroup[0]).id;
2308
2309  mediaTypes.AUDIO.tracks[groupId].enabled = true;
2310  mediaTypes.AUDIO.onTrackChanged();
2311
2312  masterPlaylistLoader.on('mediachange', function () {
2313    ['AUDIO', 'SUBTITLES'].forEach(function (type) {
2314      return mediaTypes[type].onGroupChanged();
2315    });
2316  });
2317
2318  // custom audio track change event handler for usage event
2319  var onAudioTrackChanged = function onAudioTrackChanged() {
2320    mediaTypes.AUDIO.onTrackChanged();
2321    tech.trigger({ type: 'usage', name: 'hls-audio-change' });
2322  };
2323
2324  tech.audioTracks().addEventListener('change', onAudioTrackChanged);
2325  tech.remoteTextTracks().addEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
2326
2327  hls.on('dispose', function () {
2328    tech.audioTracks().removeEventListener('change', onAudioTrackChanged);
2329    tech.remoteTextTracks().removeEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
2330  });
2331
2332  // clear existing audio tracks and add the ones we just created
2333  tech.clearTracks('audio');
2334
2335  for (var id in mediaTypes.AUDIO.tracks) {
2336    tech.audioTracks().addTrack(mediaTypes.AUDIO.tracks[id]);
2337  }
2338};
2339
2340exports.setupMediaGroups = setupMediaGroups;
2341/**
2342 * Creates skeleton object used to store the loaders, tracks, and utility methods for each
2343 * media type
2344 *
2345 * @return {Object}
2346 *         Object to store the loaders, tracks, and utility methods for each media type
2347 * @function createMediaTypes
2348 */
2349var createMediaTypes = function createMediaTypes() {
2350  var mediaTypes = {};
2351
2352  ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
2353    mediaTypes[type] = {
2354      groups: {},
2355      tracks: {},
2356      activePlaylistLoader: null,
2357      activeGroup: noop,
2358      activeTrack: noop,
2359      onGroupChanged: noop,
2360      onTrackChanged: noop
2361    };
2362  });
2363
2364  return mediaTypes;
2365};
2366exports.createMediaTypes = createMediaTypes;
2367}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2368},{"./playlist-loader":9}],7:[function(require,module,exports){
2369(function (global){
2370'use strict';
2371
2372Object.defineProperty(exports, '__esModule', {
2373  value: true
2374});
2375
2376function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
2377
2378var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
2379
2380var _videoJs2 = _interopRequireDefault(_videoJs);
2381
2382var _binUtils = require('./bin-utils');
2383
2384var REQUEST_ERRORS = {
2385  FAILURE: 2,
2386  TIMEOUT: -101,
2387  ABORTED: -102
2388};
2389
2390exports.REQUEST_ERRORS = REQUEST_ERRORS;
2391/**
2392 * Turns segment byterange into a string suitable for use in
2393 * HTTP Range requests
2394 *
2395 * @param {Object} byterange - an object with two values defining the start and end
2396 *                             of a byte-range
2397 */
2398var byterangeStr = function byterangeStr(byterange) {
2399  var byterangeStart = undefined;
2400  var byterangeEnd = undefined;
2401
2402  // `byterangeEnd` is one less than `offset + length` because the HTTP range
2403  // header uses inclusive ranges
2404  byterangeEnd = byterange.offset + byterange.length - 1;
2405  byterangeStart = byterange.offset;
2406  return 'bytes=' + byterangeStart + '-' + byterangeEnd;
2407};
2408
2409/**
2410 * Defines headers for use in the xhr request for a particular segment.
2411 *
2412 * @param {Object} segment - a simplified copy of the segmentInfo object
2413 *                           from SegmentLoader
2414 */
2415var segmentXhrHeaders = function segmentXhrHeaders(segment) {
2416  var headers = {};
2417
2418  if (segment.byterange) {
2419    headers.Range = byterangeStr(segment.byterange);
2420  }
2421  return headers;
2422};
2423
2424/**
2425 * Abort all requests
2426 *
2427 * @param {Object} activeXhrs - an object that tracks all XHR requests
2428 */
2429var abortAll = function abortAll(activeXhrs) {
vendor: 4,369 bytes, lines 2430-2577
2430  activeXhrs.forEach(function (xhr) {
2431    xhr.abort();
2432  });
2433};
2434
2435/**
2436 * Gather important bandwidth stats once a request has completed
2437 *
2438 * @param {Object} request - the XHR request from which to gather stats
2439 */
2440var getRequestStats = function getRequestStats(request) {
2441  return {
2442    bandwidth: request.bandwidth,
2443    bytesReceived: request.bytesReceived || 0,
2444    roundTripTime: request.roundTripTime || 0
2445  };
2446};
2447
2448/**
2449 * If possible gather bandwidth stats as a request is in
2450 * progress
2451 *
2452 * @param {Event} progressEvent - an event object from an XHR's progress event
2453 */
2454var getProgressStats = function getProgressStats(progressEvent) {
2455  var request = progressEvent.target;
2456  var roundTripTime = Date.now() - request.requestTime;
2457  var stats = {
2458    bandwidth: Infinity,
2459    bytesReceived: 0,
2460    roundTripTime: roundTripTime || 0
2461  };
2462
2463  stats.bytesReceived = progressEvent.loaded;
2464  // This can result in Infinity if stats.roundTripTime is 0 but that is ok
2465  // because we should only use bandwidth stats on progress to determine when
2466  // abort a request early due to insufficient bandwidth
2467  stats.bandwidth = Math.floor(stats.bytesReceived / stats.roundTripTime * 8 * 1000);
2468
2469  return stats;
2470};
2471
2472/**
2473 * Handle all error conditions in one place and return an object
2474 * with all the information
2475 *
2476 * @param {Error|null} error - if non-null signals an error occured with the XHR
2477 * @param {Object} request -  the XHR request that possibly generated the error
2478 */
2479var handleErrors = function handleErrors(error, request) {
2480  if (request.timedout) {
2481    return {
2482      status: request.status,
2483      message: 'HLS request timed-out at URL: ' + request.uri,
2484      code: REQUEST_ERRORS.TIMEOUT,
2485      xhr: request
2486    };
2487  }
2488
2489  if (request.aborted) {
2490    return {
2491      status: request.status,
2492      message: 'HLS request aborted at URL: ' + request.uri,
2493      code: REQUEST_ERRORS.ABORTED,
2494      xhr: request
2495    };
2496  }
2497
2498  if (error) {
2499    return {
2500      status: request.status,
2501      message: 'HLS request errored at URL: ' + request.uri,
2502      code: REQUEST_ERRORS.FAILURE,
2503      xhr: request
2504    };
2505  }
2506
2507  return null;
2508};
2509
2510/**
2511 * Handle responses for key data and convert the key data to the correct format
2512 * for the decryption step later
2513 *
2514 * @param {Object} segment - a simplified copy of the segmentInfo object
2515 *                           from SegmentLoader
2516 * @param {Function} finishProcessingFn - a callback to execute to continue processing
2517 *                                        this request
2518 */
2519var handleKeyResponse = function handleKeyResponse(segment, finishProcessingFn) {
2520  return function (error, request) {
2521    var response = request.response;
2522    var errorObj = handleErrors(error, request);
2523
2524    if (errorObj) {
2525      return finishProcessingFn(errorObj, segment);
2526    }
2527
2528    if (response.byteLength !== 16) {
2529      return finishProcessingFn({
2530        status: request.status,
2531        message: 'Invalid HLS key at URL: ' + request.uri,
2532        code: REQUEST_ERRORS.FAILURE,
2533        xhr: request
2534      }, segment);
2535    }
2536
2537    var view = new DataView(response);
2538
2539    segment.key.bytes = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]);
2540    return finishProcessingFn(null, segment);
2541  };
2542};
2543
2544/**
2545 * Handle init-segment responses
2546 *
2547 * @param {Object} segment - a simplified copy of the segmentInfo object
2548 *                           from SegmentLoader
2549 * @param {Function} finishProcessingFn - a callback to execute to continue processing
2550 *                                        this request
2551 */
2552var handleInitSegmentResponse = function handleInitSegmentResponse(segment, finishProcessingFn) {
2553  return function (error, request) {
2554    var response = request.response;
2555    var errorObj = handleErrors(error, request);
2556
2557    if (errorObj) {
2558      return finishProcessingFn(errorObj, segment);
2559    }
2560
2561    // stop processing if received empty content
2562    if (response.byteLength === 0) {
2563      return finishProcessingFn({
2564        status: request.status,
2565        message: 'Empty HLS segment content at URL: ' + request.uri,
2566        code: REQUEST_ERRORS.FAILURE,
2567        xhr: request
2568      }, segment);
2569    }
2570
2571    segment.map.bytes = new Uint8Array(request.response);
2572    return finishProcessingFn(null, segment);
2573  };
2574};
2575
2576/**
2577 * Response handler for segment-requests being sure to set the correct
2578 * property depending on whether the segment is encryped or not
2579 * Also records and keeps track of stats that are used for ABR purposes
2580 *
2581 * @param {Object} segment - a simplified copy of the segmentInfo object
2582 *                           from SegmentLoader
2583 * @param {Function} finishProcessingFn - a callback to execute to continue processing
2584 *                                        this request
2585 */
2586var handleSegmentResponse = function handleSegmentResponse(segment, finishProcessingFn) {
2587  return function (error, request) {
2588    var response = request.response;
2589    var errorObj = handleErrors(error, request);
2590
2591    if (errorObj) {
2592      return finishProcessingFn(errorObj, segment);
2593    }
2594
2595    // stop processing if received empty content
2596    if (response.byteLength === 0) {
2597      return finishProcessingFn({
2598        status: request.status,
2599        message: 'Empty HLS segment content at URL: ' + request.uri,
2600        code: REQUEST_ERRORS.FAILURE,
2601        xhr: request
2602      }, segment);
2603    }
2604
2605    segment.stats = getRequestStats(request);
2606
2607    if (segment.key) {
2608      segment.encryptedBytes = new Uint8Array(request.response);
2609    } else {
2610      segment.bytes = new Uint8Array(request.response);
2611    }
2612
2613    return finishProcessingFn(null, segment);
2614  };
2615};
2616
2617/**
2618 * Decrypt the segment via the decryption web worker
2619 *
2620 * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
2621 * @param {Object} segment - a simplified copy of the segmentInfo object
2622 *                           from SegmentLoader
2623 * @param {Function} doneFn - a callback that is executed after decryption has completed
2624 */
2625var decryptSegment = function decryptSegment(decrypter, segment, doneFn) {
2626  var decryptionHandler = function decryptionHandler(event) {
2627    if (event.data.source === segment.requestId) {
2628      decrypter.removeEventListener('message', decryptionHandler);
2629      var decrypted = event.data.decrypted;
2630
2631      segment.bytes = new Uint8Array(decrypted.bytes, decrypted.byteOffset, decrypted.byteLength);
2632      return doneFn(null, segment);
2633    }
2634  };
2635
2636  decrypter.addEventListener('message', decryptionHandler);
2637
2638  // this is an encrypted segment
2639  // incrementally decrypt the segment
2640  decrypter.postMessage((0, _binUtils.createTransferableMessage)({
2641    source: segment.requestId,
2642    encrypted: segment.encryptedBytes,
2643    key: segment.key.bytes,
2644    iv: segment.key.iv
2645  }), [segment.encryptedBytes.buffer, segment.key.bytes.buffer]);
2646};
2647
2648/**
2649 * The purpose of this function is to get the most pertinent error from the
2650 * array of errors.
2651 * For instance if a timeout and two aborts occur, then the aborts were
2652 * likely triggered by the timeout so return that error object.
2653 */
2654var getMostImportantError = function getMostImportantError(errors) {
2655  return errors.reduce(function (prev, err) {
2656    return err.code > prev.code ? err : prev;
2657  });
2658};
2659
2660/**
2661 * This function waits for all XHRs to finish (with either success or failure)
2662 * before continueing processing via it's callback. The function gathers errors
2663 * from each request into a single errors array so that the error status for
2664 * each request can be examined later.
2665 *
2666 * @param {Object} activeXhrs - an object that tracks all XHR requests
2667 * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
2668 * @param {Function} doneFn - a callback that is executed after all resources have been
2669 *                            downloaded and any decryption completed
2670 */
2671var waitForCompletion = function waitForCompletion(activeXhrs, decrypter, doneFn) {
2672  var errors = [];
2673  var count = 0;
2674
2675  return function (error, segment) {
2676    if (error) {
2677      // If there are errors, we have to abort any outstanding requests
2678      abortAll(activeXhrs);
2679      errors.push(error);
2680    }
2681    count += 1;
2682
2683    if (count === activeXhrs.length) {
2684      // Keep track of when *all* of the requests have completed
2685      segment.endOfAllRequests = Date.now();
2686
2687      if (errors.length > 0) {
2688        var worstError = getMostImportantError(errors);
2689
2690        return doneFn(worstError, segment);
2691      }
2692      if (segment.encryptedBytes) {
2693        return decryptSegment(decrypter, segment, doneFn);
2694      }
2695      // Otherwise, everything is ready just continue
2696      return doneFn(null, segment);
2697    }
2698  };
2699};
2700
2701/**
2702 * Simple progress event callback handler that gathers some stats before
2703 * executing a provided callback with the `segment` object
2704 *
2705 * @param {Object} segment - a simplified copy of the segmentInfo object
2706 *                           from SegmentLoader
2707 * @param {Function} progressFn - a callback that is executed each time a progress event
2708 *                                is received
2709 * @param {Event} event - the progress event object from XMLHttpRequest
2710 */
2711var handleProgress = function handleProgress(segment, progressFn) {
2712  return function (event) {
2713    segment.stats = _videoJs2['default'].mergeOptions(segment.stats, getProgressStats(event));
2714
2715    // record the time that we receive the first byte of data
2716    if (!segment.stats.firstBytesReceivedAt && segment.stats.bytesReceived) {
2717      segment.stats.firstBytesReceivedAt = Date.now();
2718    }
2719
2720    return progressFn(event, segment);
2721  };
2722};
2723
2724/**
2725 * Load all resources and does any processing necessary for a media-segment
2726 *
2727 * Features:
2728 *   decrypts the media-segment if it has a key uri and an iv
2729 *   aborts *all* requests if *any* one request fails
2730 *
2731 * The segment object, at minimum, has the following format:
2732 * {
2733 *   resolvedUri: String,
2734 *   [byterange]: {
2735 *     offset: Number,
2736 *     length: Number
2737 *   },
2738 *   [key]: {
2739 *     resolvedUri: String
2740 *     [byterange]: {
2741 *       offset: Number,
2742 *       length: Number
2743 *     },
2744 *     iv: {
2745 *       bytes: Uint32Array
2746 *     }
2747 *   },
2748 *   [map]: {
2749 *     resolvedUri: String,
2750 *     [byterange]: {
2751 *       offset: Number,
2752 *       length: Number
2753 *     },
2754 *     [bytes]: Uint8Array
2755 *   }
2756 * }
2757 * ...where [name] denotes optional properties
2758 *
2759 * @param {Function} xhr - an instance of the xhr wrapper in xhr.js
2760 * @param {Object} xhrOptions - the base options to provide to all xhr requests
2761 * @param {WebWorker} decryptionWorker - a WebWorker interface to AES-128
2762 *                                       decryption routines
2763 * @param {Object} segment - a simplified copy of the segmentInfo object
2764 *                           from SegmentLoader
2765 * @param {Function} progressFn - a callback that receives progress events from the main
2766 *                                segment's xhr request
2767 * @param {Function} doneFn - a callback that is executed only once all requests have
2768 *                            succeeded or failed
2769 * @returns {Function} a function that, when invoked, immediately aborts all
2770 *                     outstanding requests
2771 */
2772var mediaSegmentRequest = function mediaSegmentRequest(xhr, xhrOptions, decryptionWorker, segment, progressFn, doneFn) {
2773  var activeXhrs = [];
2774  var finishProcessingFn = waitForCompletion(activeXhrs, decryptionWorker, doneFn);
2775
2776  // optionally, request the decryption key
2777  if (segment.key) {
2778    var keyRequestOptions = _videoJs2['default'].mergeOptions(xhrOptions, {
2779      uri: segment.key.resolvedUri,
2780      responseType: 'arraybuffer'
2781    });
2782    var keyRequestCallback = handleKeyResponse(segment, finishProcessingFn);
2783    var keyXhr = xhr(keyRequestOptions, keyRequestCallback);
2784
2785    activeXhrs.push(keyXhr);
2786  }
2787
2788  // optionally, request the associated media init segment
2789  if (segment.map && !segment.map.bytes) {
2790    var initSegmentOptions = _videoJs2['default'].mergeOptions(xhrOptions, {
2791      uri: segment.map.resolvedUri,
2792      responseType: 'arraybuffer',
2793      headers: segmentXhrHeaders(segment.map)
2794    });
2795    var initSegmentRequestCallback = handleInitSegmentResponse(segment, finishProcessingFn);
2796    var initSegmentXhr = xhr(initSegmentOptions, initSegmentRequestCallback);
2797
2798    activeXhrs.push(initSegmentXhr);
2799  }
2800
2801  var segmentRequestOptions = _videoJs2['default'].mergeOptions(xhrOptions, {
2802    uri: segment.resolvedUri,
2803    responseType: 'arraybuffer',
2804    headers: segmentXhrHeaders(segment)
2805  });
2806  var segmentRequestCallback = handleSegmentResponse(segment, finishProcessingFn);
2807  var segmentXhr = xhr(segmentRequestOptions, segmentRequestCallback);
2808
2809  segmentXhr.addEventListener('progress', handleProgress(segment, progressFn));
2810  activeXhrs.push(segmentXhr);
2811
2812  return function () {
2813    return abortAll(activeXhrs);
2814  };
2815};
2816exports.mediaSegmentRequest = mediaSegmentRequest;
2817}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2818},{"./bin-utils":2}],8:[function(require,module,exports){
2819(function (global){
2820/**
2821 * @file playback-watcher.js
2822 *
2823 * Playback starts, and now my watch begins. It shall not end until my death. I shall
2824 * take no wait, hold no uncleared timeouts, father no bad seeks. I shall wear no crowns
2825 * and win no glory. I shall live and die at my post. I am the corrector of the underflow.
2826 * I am the watcher of gaps. I am the shield that guards the realms of seekable. I pledge
2827 * my life and honor to the Playback Watch, for this Player and all the Players to come.
2828 */
2829
2830'use strict';
2831
2832Object.defineProperty(exports, '__esModule', {
2833  value: true
2834});
2835
2836var _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; }; })();
2837
2838function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
2839
2840function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
2841
2842var _globalWindow = require('global/window');
2843
2844var _globalWindow2 = _interopRequireDefault(_globalWindow);
2845
2846var _ranges = require('./ranges');
2847
2848var _ranges2 = _interopRequireDefault(_ranges);
2849
2850var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
2851
2852var _videoJs2 = _interopRequireDefault(_videoJs);
2853
2854// Set of events that reset the playback-watcher time check logic and clear the timeout
2855var timerCancelEvents = ['seeking', 'seeked', 'pause', 'playing', 'error'];
2856
2857/**
2858 * @class PlaybackWatcher
2859 */
2860
2861var PlaybackWatcher = (function () {
2862  /**
2863   * Represents an PlaybackWatcher object.
2864   * @constructor
2865   * @param {object} options an object that includes the tech and settings
2866   */
2867
2868  function PlaybackWatcher(options) {
2869    var _this = this;
2870
2871    _classCallCheck(this, PlaybackWatcher);
2872
2873    this.tech_ = options.tech;
2874    this.seekable = options.seekable;
2875
2876    this.consecutiveUpdates = 0;
2877    this.lastRecordedTime = null;
2878    this.timer_ = null;
2879    this.checkCurrentTimeTimeout_ = null;
2880
2881    if (options.debug) {
2882      this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'playback-watcher ->');
2883    }
2884    this.logger_('initialize');
2885
2886    var canPlayHandler = function canPlayHandler() {
2887      return _this.monitorCurrentTime_();
2888    };
2889    var waitingHandler = function waitingHandler() {
2890      return _this.techWaiting_();
2891    };
2892    var cancelTimerHandler = function cancelTimerHandler() {
2893      return _this.cancelTimer_();
2894    };
2895    var fixesBadSeeksHandler = function fixesBadSeeksHandler() {
2896      return _this.fixesBadSeeks_();
2897    };
2898
2899    this.tech_.on('seekablechanged', fixesBadSeeksHandler);
2900    this.tech_.on('waiting', waitingHandler);
2901    this.tech_.on(timerCancelEvents, cancelTimerHandler);
2902    this.tech_.on('canplay', canPlayHandler);
2903
2904    // Define the dispose function to clean up our events
2905    this.dispose = function () {
2906      _this.logger_('dispose');
2907      _this.tech_.off('seekablechanged', fixesBadSeeksHandler);
2908      _this.tech_.off('waiting', waitingHandler);
2909      _this.tech_.off(timerCancelEvents, cancelTimerHandler);
2910      _this.tech_.off('canplay', canPlayHandler);
2911      if (_this.checkCurrentTimeTimeout_) {
2912        _globalWindow2['default'].clearTimeout(_this.checkCurrentTimeTimeout_);
2913      }
2914      _this.cancelTimer_();
2915    };
2916  }
2917
2918  /**
2919   * Periodically check current time to see if playback stopped
2920   *
2921   * @private
2922   */
2923
2924  _createClass(PlaybackWatcher, [{
2925    key: 'monitorCurrentTime_',
2926    value: function monitorCurrentTime_() {
2927      this.checkCurrentTime_();
2928
2929      if (this.checkCurrentTimeTimeout_) {
2930        _globalWindow2['default'].clearTimeout(this.checkCurrentTimeTimeout_);
2931      }
2932
2933      // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
2934      this.checkCurrentTimeTimeout_ = _globalWindow2['default'].setTimeout(this.monitorCurrentTime_.bind(this), 250);
2935    }
2936
2937    /**
2938     * The purpose of this function is to emulate the "waiting" event on
2939     * browsers that do not emit it when they are waiting for more
2940     * data to continue playback
2941     *
2942     * @private
2943     */
2944  }, {
2945    key: 'checkCurrentTime_',
2946    value: function checkCurrentTime_() {
2947      if (this.tech_.seeking() && this.fixesBadSeeks_()) {
2948        this.consecutiveUpdates = 0;
2949        this.lastRecordedTime = this.tech_.currentTime();
2950        return;
2951      }
2952
2953      if (this.tech_.paused() || this.tech_.seeking()) {
2954        return;
2955      }
2956
2957      var currentTime = this.tech_.currentTime();
2958      var buffered = this.tech_.buffered();
2959
2960      if (this.lastRecordedTime === currentTime && (!buffered.length || currentTime + 0.1 >= buffered.end(buffered.length - 1))) {
2961        // If current time is at the end of the final buffered region, then any playback
2962        // stall is most likely caused by buffering in a low bandwidth environment. The tech
2963        // should fire a `waiting` event in this scenario, but due to browser and tech
vendor: 6,639 bytes, lines 2964-3140
2964        // inconsistencies (e.g. The Flash tech does not fire a `waiting` event when the end
2965        // of the buffer is reached and has fallen off the live window). Calling
2966        // `techWaiting_` here allows us to simulate responding to a native `waiting` event
2967        // when the tech fails to emit one.
2968        return this.techWaiting_();
2969      }
2970
2971      if (this.consecutiveUpdates >= 5 && currentTime === this.lastRecordedTime) {
2972        this.consecutiveUpdates++;
2973        this.waiting_();
2974      } else if (currentTime === this.lastRecordedTime) {
2975        this.consecutiveUpdates++;
2976      } else {
2977        this.consecutiveUpdates = 0;
2978        this.lastRecordedTime = currentTime;
2979      }
2980    }
2981
2982    /**
2983     * Cancels any pending timers and resets the 'timeupdate' mechanism
2984     * designed to detect that we are stalled
2985     *
2986     * @private
2987     */
2988  }, {
2989    key: 'cancelTimer_',
2990    value: function cancelTimer_() {
2991      this.consecutiveUpdates = 0;
2992
2993      if (this.timer_) {
2994        this.logger_('cancelTimer_');
2995        clearTimeout(this.timer_);
2996      }
2997
2998      this.timer_ = null;
2999    }
3000
3001    /**
3002     * Fixes situations where there's a bad seek
3003     *
3004     * @return {Boolean} whether an action was taken to fix the seek
3005     * @private
3006     */
3007  }, {
3008    key: 'fixesBadSeeks_',
3009    value: function fixesBadSeeks_() {
3010      var seeking = this.tech_.seeking();
3011      var seekable = this.seekable();
3012      var currentTime = this.tech_.currentTime();
3013      var seekTo = undefined;
3014
3015      if (seeking && this.afterSeekableWindow_(seekable, currentTime)) {
3016        var seekableEnd = seekable.end(seekable.length - 1);
3017
3018        // sync to live point (if VOD, our seekable was updated and we're simply adjusting)
3019        seekTo = seekableEnd;
3020      }
3021
3022      if (seeking && this.beforeSeekableWindow_(seekable, currentTime)) {
3023        var seekableStart = seekable.start(0);
3024
3025        // sync to the beginning of the live window
3026        // provide a buffer of .1 seconds to handle rounding/imprecise numbers
3027        seekTo = seekableStart + 0.1;
3028      }
3029
3030      if (typeof seekTo !== 'undefined') {
3031        this.logger_('Trying to seek outside of seekable at time ' + currentTime + ' with ' + ('seekable range ' + _ranges2['default'].printableRange(seekable) + '. Seeking to ') + (seekTo + '.'));
3032
3033        this.tech_.setCurrentTime(seekTo);
3034        return true;
3035      }
3036
3037      return false;
3038    }
3039
3040    /**
3041     * Handler for situations when we determine the player is waiting.
3042     *
3043     * @private
3044     */
3045  }, {
3046    key: 'waiting_',
3047    value: function waiting_() {
3048      if (this.techWaiting_()) {
3049        return;
3050      }
3051
3052      // All tech waiting checks failed. Use last resort correction
3053      var currentTime = this.tech_.currentTime();
3054      var buffered = this.tech_.buffered();
3055      var currentRange = _ranges2['default'].findRange(buffered, currentTime);
3056
3057      // Sometimes the player can stall for unknown reasons within a contiguous buffered
3058      // region with no indication that anything is amiss (seen in Firefox). Seeking to
3059      // currentTime is usually enough to kickstart the player. This checks that the player
3060      // is currently within a buffered region before attempting a corrective seek.
3061      // Chrome does not appear to continue `timeupdate` events after a `waiting` event
3062      // until there is ~ 3 seconds of forward buffer available. PlaybackWatcher should also
3063      // make sure there is ~3 seconds of forward buffer before taking any corrective action
3064      // to avoid triggering an `unknownwaiting` event when the network is slow.
3065      if (currentRange.length && currentTime + 3 <= currentRange.end(0)) {
3066        this.cancelTimer_();
3067        this.tech_.setCurrentTime(currentTime);
3068
3069        this.logger_('Stopped at ' + currentTime + ' while inside a buffered region ' + ('[' + currentRange.start(0) + ' -> ' + currentRange.end(0) + ']. Attempting to resume ') + 'playback by seeking to the current time.');
3070
3071        // unknown waiting corrections may be useful for monitoring QoS
3072        this.tech_.trigger({ type: 'usage', name: 'hls-unknown-waiting' });
3073        return;
3074      }
3075    }
3076
3077    /**
3078     * Handler for situations when the tech fires a `waiting` event
3079     *
3080     * @return {Boolean}
3081     *         True if an action (or none) was needed to correct the waiting. False if no
3082     *         checks passed
3083     * @private
3084     */
3085  }, {
3086    key: 'techWaiting_',
3087    value: function techWaiting_() {
3088      var seekable = this.seekable();
3089      var currentTime = this.tech_.currentTime();
3090
3091      if (this.tech_.seeking() && this.fixesBadSeeks_()) {
3092        // Tech is seeking or bad seek fixed, no action needed
3093        return true;
3094      }
3095
3096      if (this.tech_.seeking() || this.timer_ !== null) {
3097        // Tech is seeking or already waiting on another action, no action needed
3098        return true;
3099      }
3100
3101      if (this.beforeSeekableWindow_(seekable, currentTime)) {
3102        var livePoint = seekable.end(seekable.length - 1);
3103
3104        this.logger_('Fell out of live window at time ' + currentTime + '. Seeking to ' + ('live point (seekable end) ' + livePoint));
3105        this.cancelTimer_();
3106        this.tech_.setCurrentTime(livePoint);
3107
3108        // live window resyncs may be useful for monitoring QoS
3109        this.tech_.trigger({ type: 'usage', name: 'hls-live-resync' });
3110        return true;
3111      }
3112
3113      var buffered = this.tech_.buffered();
3114      var nextRange = _ranges2['default'].findNextRange(buffered, currentTime);
3115
3116      if (this.videoUnderflow_(nextRange, buffered, currentTime)) {
3117        // Even though the video underflowed and was stuck in a gap, the audio overplayed
3118        // the gap, leading currentTime into a buffered range. Seeking to currentTime
3119        // allows the video to catch up to the audio position without losing any audio
3120        // (only suffering ~3 seconds of frozen video and a pause in audio playback).
3121        this.cancelTimer_();
3122        this.tech_.setCurrentTime(currentTime);
3123
3124        // video underflow may be useful for monitoring QoS
3125        this.tech_.trigger({ type: 'usage', name: 'hls-video-underflow' });
3126        return true;
3127      }
3128
3129      // check for gap
3130      if (nextRange.length > 0) {
3131        var difference = nextRange.start(0) - currentTime;
3132
3133        this.logger_('Stopped at ' + currentTime + ', setting timer for ' + difference + ', seeking ' + ('to ' + nextRange.start(0)));
3134
3135        this.timer_ = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime);
3136        return true;
3137      }
3138
3139      // All checks failed. Returning false to indicate failure to correct waiting
3140      return false;
3141    }
3142  }, {
3143    key: 'afterSeekableWindow_',
3144    value: function afterSeekableWindow_(seekable, currentTime) {
3145      if (!seekable.length) {
3146        // we can't make a solid case if there's no seekable, default to false
3147        return false;
3148      }
3149
3150      // provide a buffer of .1 seconds to handle rounding/imprecise numbers
3151      if (currentTime > seekable.end(seekable.length - 1) + 0.1) {
3152        return true;
3153      }
3154
3155      return false;
3156    }
3157  }, {
3158    key: 'beforeSeekableWindow_',
3159    value: function beforeSeekableWindow_(seekable, currentTime) {
3160      if (seekable.length &&
3161      // can't fall before 0 and 0 seekable start identifies VOD stream
3162      seekable.start(0) > 0 &&
3163      // provide a buffer of .1 seconds to handle rounding/imprecise numbers
3164      currentTime < seekable.start(0) - 0.1) {
3165        return true;
3166      }
3167
3168      return false;
3169    }
3170  }, {
3171    key: 'videoUnderflow_',
3172    value: function videoUnderflow_(nextRange, buffered, currentTime) {
3173      if (nextRange.length === 0) {
3174        // Even if there is no available next range, there is still a possibility we are
3175        // stuck in a gap due to video underflow.
3176        var gap = this.gapFromVideoUnderflow_(buffered, currentTime);
3177
3178        if (gap) {
3179          this.logger_('Encountered a gap in video from ' + gap.start + ' to ' + gap.end + '. ' + ('Seeking to current time ' + currentTime));
3180
3181          return true;
3182        }
3183      }
3184
3185      return false;
3186    }
3187
3188    /**
3189     * Timer callback. If playback still has not proceeded, then we seek
3190     * to the start of the next buffered region.
3191     *
3192     * @private
3193     */
3194  }, {
3195    key: 'skipTheGap_',
3196    value: function skipTheGap_(scheduledCurrentTime) {
3197      var buffered = this.tech_.buffered();
3198      var currentTime = this.tech_.currentTime();
3199      var nextRange = _ranges2['default'].findNextRange(buffered, currentTime);
3200
3201      this.cancelTimer_();
3202
3203      if (nextRange.length === 0 || currentTime !== scheduledCurrentTime) {
3204        return;
3205      }
3206
3207      this.logger_('skipTheGap_:', 'currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0));
3208
3209      // only seek if we still have not played
3210      this.tech_.setCurrentTime(nextRange.start(0) + _ranges2['default'].TIME_FUDGE_FACTOR);
3211
3212      this.tech_.trigger({ type: 'usage', name: 'hls-gap-skip' });
3213    }
3214  }, {
3215    key: 'gapFromVideoUnderflow_',
3216    value: function gapFromVideoUnderflow_(buffered, currentTime) {
3217      // At least in Chrome, if there is a gap in the video buffer, the audio will continue
3218      // playing for ~3 seconds after the video gap starts. This is done to account for
3219      // video buffer underflow/underrun (note that this is not done when there is audio
3220      // buffer underflow/underrun -- in that case the video will stop as soon as it
3221      // encounters the gap, as audio stalls are more noticeable/jarring to a user than
3222      // video stalls). The player's time will reflect the playthrough of audio, so the
3223      // time will appear as if we are in a buffered region, even if we are stuck in a
3224      // "gap."
3225      //
3226      // Example:
3227      // video buffer:   0 => 10.1, 10.2 => 20
3228      // audio buffer:   0 => 20
3229      // overall buffer: 0 => 10.1, 10.2 => 20
3230      // current time: 13
3231      //
3232      // Chrome's video froze at 10 seconds, where the video buffer encountered the gap,
3233      // however, the audio continued playing until it reached ~3 seconds past the gap
3234      // (13 seconds), at which point it stops as well. Since current time is past the
3235      // gap, findNextRange will return no ranges.
3236      //
3237      // To check for this issue, we see if there is a gap that starts somewhere within
3238      // a 3 second range (3 seconds +/- 1 second) back from our current time.
3239      var gaps = _ranges2['default'].findGaps(buffered);
3240
3241      for (var i = 0; i < gaps.length; i++) {
3242        var start = gaps.start(i);
3243        var end = gaps.end(i);
3244
3245        // gap is starts no more than 4 seconds back
3246        if (currentTime - start < 4 && currentTime - start > 2) {
3247          return {
3248            start: start,
3249            end: end
3250          };
3251        }
3252      }
3253
3254      return null;
3255    }
3256
3257    /**
3258     * A debugging logger noop that is set to console.log only if debugging
3259     * is enabled globally
3260     *
3261     * @private
3262     */
3263  }, {
3264    key: 'logger_',
3265    value: function logger_() {}
3266  }]);
3267
3268  return PlaybackWatcher;
3269})();
3270
3271exports['default'] = PlaybackWatcher;
3272module.exports = exports['default'];
3273}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
3274},{"./ranges":12,"global/window":32}],9:[function(require,module,exports){
3275(function (global){
3276/**
3277 * @file playlist-loader.js
3278 *
3279 * A state machine that manages the loading, caching, and updating of
3280 * M3U8 playlists.
3281 *
3282 */
3283'use strict';
3284
3285Object.defineProperty(exports, '__esModule', {
3286  value: true
3287});
3288
3289function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
3290
3291var _resolveUrl = require('./resolve-url');
3292
3293var _resolveUrl2 = _interopRequireDefault(_resolveUrl);
3294
3295var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
3296
3297var _playlistJs = require('./playlist.js');
3298
3299var _m3u8Parser = require('m3u8-parser');
3300
3301var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
3302
3303var _globalWindow = require('global/window');
3304
3305var _globalWindow2 = _interopRequireDefault(_globalWindow);
3306
3307/**
3308  * Returns a new array of segments that is the result of merging
3309  * properties from an older list of segments onto an updated
3310  * list. No properties on the updated playlist will be overridden.
3311  *
3312  * @param {Array} original the outdated list of segments
3313  * @param {Array} update the updated list of segments
3314  * @param {Number=} offset the index of the first update
3315  * segment in the original segment list. For non-live playlists,
3316  * this should always be zero and does not need to be
3317  * specified. For live playlists, it should be the difference
3318  * between the media sequence numbers in the original and updated
3319  * playlists.
3320  * @return a list of merged segment objects
3321  */
3322var updateSegments = function updateSegments(original, update, offset) {
3323  var result = update.slice();
3324  var length = undefined;
3325  var i = undefined;
3326
3327  offset = offset || 0;
3328  length = Math.min(original.length, update.length + offset);
3329
3330  for (i = offset; i < length; i++) {
3331    result[i - offset] = (0, _videoJs.mergeOptions)(original[i], result[i - offset]);
3332  }
3333  return result;
3334};
3335
3336/**
3337  * Returns a new master playlist that is the result of merging an
3338  * updated media playlist into the original version. If the
3339  * updated media playlist does not match any of the playlist
3340  * entries in the original master playlist, null is returned.
3341  *
3342  * @param {Object} master a parsed master M3U8 object
3343  * @param {Object} media a parsed media M3U8 object
3344  * @return {Object} a new object that represents the original
3345  * master playlist with the updated media playlist merged in, or
3346  * null if the merge produced no change.
3347  */
3348var updateMaster = function updateMaster(master, media) {
3349  var changed = false;
3350  var result = (0, _videoJs.mergeOptions)(master, {});
3351  var i = master.playlists.length;
3352  var playlist = undefined;
3353  var segment = undefined;
3354  var j = undefined;
3355
3356  while (i--) {
3357    playlist = result.playlists[i];
3358    if (playlist.uri === media.uri) {
3359      // consider the playlist unchanged if the number of segments
3360      // are equal and the media sequence number is unchanged
3361      if (playlist.segments && media.segments && playlist.segments.length === media.segments.length && playlist.mediaSequence === media.mediaSequence) {
3362        continue;
3363      }
3364
3365      result.playlists[i] = (0, _videoJs.mergeOptions)(playlist, media);
3366      result.playlists[media.uri] = result.playlists[i];
3367
3368      // if the update could overlap existing segment information,
3369      // merge the two lists
3370      if (playlist.segments) {
3371        result.playlists[i].segments = updateSegments(playlist.segments, media.segments, media.mediaSequence - playlist.mediaSequence);
3372      }
3373      // resolve any missing segment and key URIs
3374      j = 0;
3375      if (result.playlists[i].segments) {
3376        j = result.playlists[i].segments.length;
3377      }
3378      while (j--) {
3379        segment = result.playlists[i].segments[j];
3380        if (!segment.resolvedUri) {
3381          segment.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.uri);
3382        }
3383        if (segment.key && !segment.key.resolvedUri) {
3384          segment.key.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.key.uri);
3385        }
3386        if (segment.map && !segment.map.resolvedUri) {
3387          segment.map.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.map.uri);
3388        }
3389      }
3390      changed = true;
3391    }
3392  }
3393  return changed ? result : null;
3394};
3395
3396/**
3397 * Load a playlist from a remote location
3398 *
3399 * @class PlaylistLoader
3400 * @extends Stream
3401 * @param {String} srcUrl the url to start with
3402 * @param {Boolean} withCredentials the withCredentials xhr option
3403 * @constructor
3404 */
3405var PlaylistLoader = function PlaylistLoader(srcUrl, hls, withCredentials) {
3406  var _this = this;
3407
3408  /* eslint-disable consistent-this */
3409  var loader = this;
3410  /* eslint-enable consistent-this */
3411  var mediaUpdateTimeout = undefined;
3412  var request = undefined;
3413  var playlistRequestError = undefined;
3414  var haveMetadata = undefined;
3415
3416  PlaylistLoader.prototype.constructor.call(this);
3417
3418  this.hls_ = hls;
3419
3420  if (!srcUrl) {
3421    throw new Error('A non-empty playlist URL is required');
3422  }
3423
3424  playlistRequestError = function (xhr, url, startingState) {
3425    loader.setBandwidth(request || xhr);
3426
3427    // any in-flight request is now finished
3428    request = null;
3429
3430    if (startingState) {
3431      loader.state = startingState;
3432    }
3433
3434    loader.error = {
3435      playlist: loader.master.playlists[url],
3436      status: xhr.status,
3437      message: 'HLS playlist request error at URL: ' + url,
3438      responseText: xhr.responseText,
3439      code: xhr.status >= 500 ? 4 : 2
3440    };
3441
3442    loader.trigger('error');
3443  };
3444
3445  // update the playlist loader's state in response to a new or
3446  // updated playlist.
3447  haveMetadata = function (xhr, url) {
3448    var parser = undefined;
3449    var refreshDelay = undefined;
3450    var update = undefined;
3451
3452    loader.setBandwidth(request || xhr);
3453
3454    // any in-flight request is now finished
3455    request = null;
3456
3457    loader.state = 'HAVE_METADATA';
3458
3459    parser = new _m3u8Parser2['default'].Parser();
3460    parser.push(xhr.responseText);
3461    parser.end();
3462    parser.manifest.uri = url;
3463    // m3u8-parser does not attach an attributes property to media playlists so make
3464    // sure that the property is attached to avoid undefined reference errors
3465    parser.manifest.attributes = parser.manifest.attributes || {};
3466
3467    // merge this playlist into the master
3468    update = updateMaster(loader.master, parser.manifest);
3469    refreshDelay = (parser.manifest.targetDuration || 10) * 1000;
3470    loader.targetDuration = parser.manifest.targetDuration;
3471    if (update) {
3472      loader.master = update;
3473      loader.media_ = loader.master.playlists[parser.manifest.uri];
3474    } else {
3475      // if the playlist is unchanged since the last reload,
3476      // try again after half the target duration
3477      refreshDelay /= 2;
3478      loader.trigger('playlistunchanged');
3479    }
3480
3481    // refresh live playlists after a target duration passes
3482    if (!loader.media().endList) {
3483      _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
3484      mediaUpdateTimeout = _globalWindow2['default'].setTimeout(function () {
3485        loader.trigger('mediaupdatetimeout');
3486      }, refreshDelay);
3487    }
3488
3489    loader.trigger('loadedplaylist');
3490  };
3491
3492  // initialize the loader state
3493  loader.state = 'HAVE_NOTHING';
3494
3495  /**
3496   * Abort any outstanding work and clean up.
3497   */
3498  loader.dispose = function () {
3499    loader.stopRequest();
3500    _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
3501    loader.off();
3502  };
3503
3504  loader.stopRequest = function () {
3505    if (request) {
3506      var oldRequest = request;
3507
3508      request = null;
3509      oldRequest.onreadystatechange = null;
3510      oldRequest.abort();
3511    }
3512  };
3513
3514  /**
3515   * Returns the number of enabled playlists on the master playlist object
3516   *
3517   * @return {Number} number of eneabled playlists
3518   */
3519  loader.enabledPlaylists_ = function () {
3520    return loader.master.playlists.filter(_playlistJs.isEnabled).length;
3521  };
3522
3523  /**
3524   * Returns whether the current playlist is the lowest rendition
3525   *
3526   * @return {Boolean} true if on lowest rendition
3527   */
3528  loader.isLowestEnabledRendition_ = function () {
3529    if (loader.master.playlists.length === 1) {
3530      return true;
3531    }
3532
3533    var media = loader.media();
3534
3535    var currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE;
3536
3537    return loader.master.playlists.filter(function (playlist) {
3538      var enabled = (0, _playlistJs.isEnabled)(playlist);
3539
3540      if (!enabled) {
3541        return false;
3542      }
3543
3544      return (playlist.attributes.BANDWIDTH || 0) < currentBandwidth;
3545    }).length === 0;
3546  };
3547
3548  /**
3549   * Returns whether the current playlist is the final available rendition
3550   *
3551   * @return {Boolean} true if on final rendition
3552   */
3553  loader.isFinalRendition_ = function () {
3554    return loader.master.playlists.filter(_playlistJs.isEnabled).length === 1;
3555  };
3556
3557  /**
3558   * When called without any arguments, returns the currently
3559   * active media playlist. When called with a single argument,
3560   * triggers the playlist loader to asynchronously switch to the
3561   * specified media playlist. Calling this method while the
3562   * loader is in the HAVE_NOTHING causes an error to be emitted
3563   * but otherwise has no effect.
3564   *
3565   * @param {Object=} playlist the parsed media playlist
3566   * object to switch to
3567   * @return {Playlist} the current loaded media
3568   */
3569  loader.media = function (playlist) {
3570    var startingState = loader.state;
3571    var mediaChange = undefined;
3572
3573    // getter
3574    if (!playlist) {
3575      return loader.media_;
3576    }
3577
3578    // setter
3579    if (loader.state === 'HAVE_NOTHING') {
3580      throw new Error('Cannot switch media playlist from ' + loader.state);
3581    }
3582
3583    // find the playlist object if the target playlist has been
3584    // specified by URI
3585    if (typeof playlist === 'string') {
3586      if (!loader.master.playlists[playlist]) {
3587        throw new Error('Unknown playlist URI: ' + playlist);
3588      }
3589      playlist = loader.master.playlists[playlist];
3590    }
3591
3592    mediaChange = !loader.media_ || playlist.uri !== loader.media_.uri;
3593
3594    // switch to fully loaded playlists immediately
3595    if (loader.master.playlists[playlist.uri].endList) {
3596      // abort outstanding playlist requests
3597      if (request) {
3598        request.onreadystatechange = null;
3599        request.abort();
3600        request = null;
3601      }
3602      loader.state = 'HAVE_METADATA';
3603      loader.media_ = playlist;
3604
3605      // trigger media change if the active media has been updated
3606      if (mediaChange) {
3607        loader.trigger('mediachanging');
3608        loader.trigger('mediachange');
3609      }
3610      return;
3611    }
3612
3613    // switching to the active playlist is a no-op
3614    if (!mediaChange) {
3615      return;
3616    }
3617
3618    loader.state = 'SWITCHING_MEDIA';
3619
3620    // there is already an outstanding playlist request
3621    if (request) {
3622      if ((0, _resolveUrl2['default'])(loader.master.uri, playlist.uri) === request.url) {
3623        // requesting to switch to the same playlist multiple times
3624        // has no effect after the first
3625        return;
3626      }
3627      request.onreadystatechange = null;
3628      request.abort();
3629      request = null;
3630    }
3631
3632    // request the new playlist
3633    if (this.media_) {
3634      this.trigger('mediachanging');
3635    }
3636
3637    request = this.hls_.xhr({
3638      uri: (0, _resolveUrl2['default'])(loader.master.uri, playlist.uri),
3639      withCredentials: withCredentials
3640    }, function (error, req) {
3641      // disposed
3642      if (!request) {
3643        return;
3644      }
3645
3646      if (error) {
3647        return playlistRequestError(request, playlist.uri, startingState);
3648      }
3649
3650      haveMetadata(req, playlist.uri);
3651
3652      // fire loadedmetadata the first time a media playlist is loaded
3653      if (startingState === 'HAVE_MASTER') {
3654        loader.trigger('loadedmetadata');
3655      } else {
3656        loader.trigger('mediachange');
3657      }
3658    });
3659  };
3660
3661  /**
3662   * set the bandwidth on an xhr to the bandwidth on the playlist
3663   */
3664  loader.setBandwidth = function (xhr) {
3665    loader.bandwidth = xhr.bandwidth;
3666  };
3667
3668  // live playlist staleness timeout
3669  loader.on('mediaupdatetimeout', function () {
3670    if (loader.state !== 'HAVE_METADATA') {
3671      // only refresh the media playlist if no other activity is going on
3672      return;
3673    }
3674
3675    loader.state = 'HAVE_CURRENT_METADATA';
3676
3677    request = this.hls_.xhr({
3678      uri: (0, _resolveUrl2['default'])(loader.master.uri, loader.media().uri),
3679      withCredentials: withCredentials
3680    }, function (error, req) {
3681      // disposed
3682      if (!request) {
3683        return;
3684      }
3685
3686      if (error) {
3687        return playlistRequestError(request, loader.media().uri, 'HAVE_METADATA');
3688      }
3689
3690      haveMetadata(request, loader.media().uri);
3691    });
3692  });
3693
3694  /**
3695   * pause loading of the playlist
3696   */
3697  loader.pause = function () {
3698    loader.stopRequest();
3699    _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
3700    if (loader.state === 'HAVE_NOTHING') {
3701      // If we pause the loader before any data has been retrieved, its as if we never
3702      // started, so reset to an unstarted state.
3703      loader.started = false;
3704    }
3705    // Need to restore state now that no activity is happening
3706    if (loader.state === 'SWITCHING_MEDIA') {
3707      // if the loader was in the process of switching media, it should either return to
3708      // HAVE_MASTER or HAVE_METADATA depending on if the loader has loaded a media
3709      // playlist yet. This is determined by the existence of loader.media_
3710      if (loader.media_) {
3711        loader.state = 'HAVE_METADATA';
3712      } else {
3713        loader.state = 'HAVE_MASTER';
3714      }
3715    } else if (loader.state === 'HAVE_CURRENT_METADATA') {
3716      loader.state = 'HAVE_METADATA';
3717    }
3718  };
3719
3720  /**
3721   * start loading of the playlist
3722   */
3723  loader.load = function (isFinalRendition) {
3724    var media = loader.media();
3725
3726    _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
3727
3728    if (isFinalRendition) {
3729      var refreshDelay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
3730
3731      mediaUpdateTimeout = _globalWindow2['default'].setTimeout(loader.load.bind(null, false), refreshDelay);
3732      return;
3733    }
3734
3735    if (!loader.started) {
3736      loader.start();
3737      return;
3738    }
3739
3740    if (media && !media.endList) {
3741      loader.trigger('mediaupdatetimeout');
3742    } else {
3743      loader.trigger('loadedplaylist');
3744    }
3745  };
3746
3747  /**
3748   * start loading of the playlist
3749   */
3750  loader.start = function () {
3751    loader.started = true;
3752
3753    // request the specified URL
3754    request = _this.hls_.xhr({
3755      uri: srcUrl,
3756      withCredentials: withCredentials
3757    }, function (error, req) {
3758      var parser = undefined;
3759      var playlist = undefined;
3760      var i = undefined;
3761
3762      // disposed
3763      if (!request) {
3764        return;
3765      }
3766
3767      // clear the loader's request reference
3768      request = null;
3769
3770      if (error) {
3771        loader.error = {
3772          status: req.status,
3773          message: 'HLS playlist request error at URL: ' + srcUrl,
3774          responseText: req.responseText,
3775          // MEDIA_ERR_NETWORK
3776          code: 2
3777        };
3778        if (loader.state === 'HAVE_NOTHING') {
3779          loader.started = false;
3780        }
3781        return loader.trigger('error');
3782      }
3783
3784      parser = new _m3u8Parser2['default'].Parser();
3785      parser.push(req.responseText);
3786      parser.end();
3787
3788      loader.state = 'HAVE_MASTER';
3789
3790      parser.manifest.uri = srcUrl;
3791
3792      // loaded a master playlist
3793      if (parser.manifest.playlists) {
3794        loader.master = parser.manifest;
3795
3796        // setup by-URI lookups and resolve media playlist URIs
3797        i = loader.master.playlists.length;
3798        while (i--) {
3799          playlist = loader.master.playlists[i];
3800          loader.master.playlists[playlist.uri] = playlist;
3801          playlist.resolvedUri = (0, _resolveUrl2['default'])(loader.master.uri, playlist.uri);
3802
3803          if (!playlist.attributes) {
3804            // Although the spec states an #EXT-X-STREAM-INF tag MUST have a
3805            // BANDWIDTH attribute, we can play the stream without it. This means a poorly
3806            // formatted master playlist may not have an attribute list. An attributes
3807            // property is added here to prevent undefined references when we encounter
3808            // this scenario.
3809            playlist.attributes = {};
3810
3811            _videoJs.log.warn('Invalid playlist STREAM-INF detected. Missing BANDWIDTH attribute.');
3812          }
3813        }
3814
3815        // resolve any media group URIs
3816        ['AUDIO', 'SUBTITLES'].forEach(function (mediaType) {
3817          for (var groupKey in loader.master.mediaGroups[mediaType]) {
3818            for (var labelKey in loader.master.mediaGroups[mediaType][groupKey]) {
3819              var mediaProperties = loader.master.mediaGroups[mediaType][groupKey][labelKey];
3820
3821              if (mediaProperties.uri) {
3822                mediaProperties.resolvedUri = (0, _resolveUrl2['default'])(loader.master.uri, mediaProperties.uri);
3823              }
3824            }
3825          }
3826        });
3827
3828        loader.trigger('loadedplaylist');
3829        if (!request) {
3830          // no media playlist was specifically selected so start
3831          // from the first listed one
3832          loader.media(parser.manifest.playlists[0]);
3833        }
3834        return;
3835      }
3836
3837      // loaded a media playlist
3838      // infer a master playlist if none was previously requested
3839      loader.master = {
3840        mediaGroups: {
3841          'AUDIO': {},
3842          'VIDEO': {},
3843          'CLOSED-CAPTIONS': {},
3844          'SUBTITLES': {}
3845        },
3846        uri: _globalWindow2['default'].location.href,
3847        playlists: [{
3848          uri: srcUrl
3849        }]
3850      };
3851      loader.master.playlists[srcUrl] = loader.master.playlists[0];
3852      loader.master.playlists[0].resolvedUri = srcUrl;
3853      // m3u8-parser does not attach an attributes property to media playlists so make
3854      // sure that the property is attached to avoid undefined reference errors
3855      loader.master.playlists[0].attributes = loader.master.playlists[0].attributes || {};
3856      haveMetadata(req, srcUrl);
3857      return loader.trigger('loadedmetadata');
3858    });
3859  };
3860};
3861
3862PlaylistLoader.prototype = new _videoJs.EventTarget();
3863
3864exports['default'] = PlaylistLoader;
3865module.exports = exports['default'];
3866}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
3867},{"./playlist.js":11,"./resolve-url":15,"global/window":32,"m3u8-parser":33}],10:[function(require,module,exports){
3868'use strict';
3869
3870Object.defineProperty(exports, '__esModule', {
3871  value: true
3872});
3873
3874function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
3875
3876var _config = require('./config');
3877
3878var _config2 = _interopRequireDefault(_config);
3879
3880var _playlist = require('./playlist');
3881
3882var _playlist2 = _interopRequireDefault(_playlist);
3883
3884var _utilCodecsJs = require('./util/codecs.js');
3885
3886// Utilities
3887
3888/**
3889 * Returns the CSS value for the specified property on an element
3890 * using `getComputedStyle`. Firefox has a long-standing issue where
3891 * getComputedStyle() may return null when running in an iframe with
3892 * `display: none`.
3893 *
3894 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
3895 * @param {HTMLElement} el the htmlelement to work on
3896 * @param {string} the proprety to get the style for
3897 */
3898var safeGetComputedStyle = function safeGetComputedStyle(el, property) {
3899  var result = undefined;
3900
3901  if (!el) {
3902    return '';
3903  }
3904
3905  result = window.getComputedStyle(el);
3906  if (!result) {
3907    return '';
3908  }
3909
3910  return result[property];
3911};
3912
3913/**
3914 * Resuable stable sort function
3915 *
3916 * @param {Playlists} array
3917 * @param {Function}
3917 sortFn Different comparators
3918 * @function stableSort
3919 */
3920var stableSort = function stableSort(array, sortFn) {
3921  var newArray = array.slice();
3922
3923  array.sort(function (left, right) {
3924    var cmp = sortFn(left, right);
3925
3926    if (cmp === 0) {
3927      return newArray.indexOf(left) - newArray.indexOf(right);
3928    }
3929    return cmp;
3930  });
3931};
3932
3933/**
3934 * A comparator function to sort two playlist object by bandwidth.
3935 *
3936 * @param {Object} left a media playlist object
3937 * @param {Object} right a media playlist object
3938 * @return {Number} Greater than zero if the bandwidth attribute of
3939 * left is greater than the corresponding attribute of right. Less
3940 * than zero if the bandwidth of right is greater than left and
3941 * exactly zero if the two are equal.
3942 */
3943var comparePlaylistBandwidth = function comparePlaylistBandwidth(left, right) {
3944  var leftBandwidth = undefined;
3945  var rightBandwidth = undefined;
3946
3947  if (left.attributes.BANDWIDTH) {
3948    leftBandwidth = left.attributes.BANDWIDTH;
3949  }
3950  leftBandwidth = leftBandwidth || window.Number.MAX_VALUE;
3951  if (right.attributes.BANDWIDTH) {
3952    rightBandwidth = right.attributes.BANDWIDTH;
3953  }
3954  rightBandwidth = rightBandwidth || window.Number.MAX_VALUE;
3955
3956  return leftBandwidth - rightBandwidth;
3957};
3958
3959exports.comparePlaylistBandwidth = comparePlaylistBandwidth;
3960/**
3961 * A comparator function to sort two playlist object by resolution (width).
3962 * @param {Object} left a media playlist object
3963 * @param {Object} right a media playlist object
3964 * @return {Number} Greater than zero if the resolution.width attribute of
3965 * left is greater than the corresponding attribute of right. Less
3966 * than zero if the resolution.width of right is greater than left and
3967 * exactly zero if the two are equal.
3968 */
3969var comparePlaylistResolution = function comparePlaylistResolution(left, right) {
3970  var leftWidth = undefined;
3971  var rightWidth = undefined;
3972
3973  if (left.attributes.RESOLUTION && left.attributes.RESOLUTION.width) {
3974    leftWidth = left.attributes.RESOLUTION.width;
3975  }
3976
3977  leftWidth = leftWidth || window.Number.MAX_VALUE;
3978
3979  if (right.attributes.RESOLUTION && right.attributes.RESOLUTION.width) {
3980    rightWidth = right.attributes.RESOLUTION.width;
3981  }
3982
3983  rightWidth = rightWidth || window.Number.MAX_VALUE;
3984
3985  // NOTE - Fallback to bandwidth sort as appropriate in cases where multiple renditions
3986  // have the same media dimensions/ resolution
3987  if (leftWidth === rightWidth && left.attributes.BANDWIDTH && right.attributes.BANDWIDTH) {
3988    return left.attributes.BANDWIDTH - right.attributes.BANDWIDTH;
3989  }
3990  return leftWidth - rightWidth;
3991};
3992
3993exports.comparePlaylistResolution = comparePlaylistResolution;
3994/**
3995 * Chooses the appropriate media playlist based on bandwidth and player size
3996 *
3997 * @param {Object} master
3998 *        Object representation of the master manifest
3999 * @param {Number} playerBandwidth
4000 *        Current calculated bandwidth of the player
4001 * @param {Number} playerWidth
4002 *        Current width of the player element
4003 * @param {Number} playerHeight
4004 *        Current height of the player element
4005 * @return {Playlist} the highest bitrate playlist less than the
4006 * currently detected bandwidth, accounting for some amount of
4007 * bandwidth variance
4008 */
4009var simpleSelector = function simpleSelector(master, playerBandwidth, playerWidth, playerHeight) {
4010  // convert the playlists to an intermediary representation to make comparisons easier
4011  var sortedPlaylistReps = master.playlists.map(function (playlist) {
4012    var width = undefined;
4013    var height = undefined;
4014    var bandwidth = undefined;
4015
4016    width = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.width;
4017    height = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.height;
4018    bandwidth = playlist.attributes.BANDWIDTH;
4019
4020    bandwidth = bandwidth || window.Number.MAX_VALUE;
4021
4022    return {
4023      bandwidth: bandwidth,
4024      width: width,
4025      height: height,
4026      playlist: playlist
4027    };
4028  });
4029
4030  stableSort(sortedPlaylistReps, function (left, right) {
4031    return left.bandwidth - right.bandwidth;
4032  });
4033
4034  // filter out any playlists that have been excluded due to
4035  // incompatible configurations or playback errors
4036  sortedPlaylistReps = sortedPlaylistReps.filter(function (rep) {
4037    return _playlist2['default'].isEnabled(rep.playlist);
4038  });
4039
4040  // filter out any variant that has greater effective bitrate
4041  // than the current estimated bandwidth
4042  var bandwidthPlaylistReps = sortedPlaylistReps.filter(function (rep) {
4043    return rep.bandwidth * _config2['default'].BANDWIDTH_VARIANCE < playerBandwidth;
4044  });
4045
4046  var highestRemainingBandwidthRep = bandwidthPlaylistReps[bandwidthPlaylistReps.length - 1];
4047
4048  // get all of the renditions with the same (highest) bandwidth
4049  // and then taking the very first element
4050  var bandwidthBestRep = bandwidthPlaylistReps.filter(function (rep) {
4051    return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
4052  })[0];
4053
4054  // filter out playlists without resolution information
4055  var haveResolution = bandwidthPlaylistReps.filter(function (rep) {
4056    return rep.width && rep.height;
4057  });
4058
4059  // sort variants by resolution
4060  stableSort(haveResolution, function (left, right) {
4061    return left.width - right.width;
4062  });
4063
4064  // if we have the exact resolution as the player use it
4065  var resolutionBestRepList = haveResolution.filter(function (rep) {
4066    return rep.width === playerWidth && rep.height === playerHeight;
4067  });
4068
4069  highestRemainingBandwidthRep = resolutionBestRepList[resolutionBestRepList.length - 1];
4070  // ensure that we pick the highest bandwidth variant that have exact resolution
4071  var resolutionBestRep = resolutionBestRepList.filter(function (rep) {
4072    return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
4073  })[0];
4074
4075  var resolutionPlusOneList = undefined;
4076  var resolutionPlusOneSmallest = undefined;
4077  var resolutionPlusOneRep = undefined;
4078
4079  // find the smallest variant that is larger than the player
4080  // if there is no match of exact resolution
4081  if (!resolutionBestRep) {
4082    resolutionPlusOneList = haveResolution.filter(function (rep) {
4083      return rep.width > playerWidth || rep.height > playerHeight;
4084    });
4085
4086    // find all the variants have the same smallest resolution
4087    resolutionPlusOneSmallest = resolutionPlusOneList.filter(function (rep) {
4088      return rep.width === resolutionPlusOneList[0].width && rep.height === resolutionPlusOneList[0].height;
4089    });
4090
4091    // ensure that we also pick the highest bandwidth variant that
4092    // is just-larger-than the video player
4093    highestRemainingBandwidthRep = resolutionPlusOneSmallest[resolutionPlusOneSmallest.length - 1];
4094    resolutionPlusOneRep = resolutionPlusOneSmallest.filter(function (rep) {
4095      return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
4096    })[0];
4097  }
4098
4099  // fallback chain of variants
4100  return (resolutionPlusOneRep || resolutionBestRep || bandwidthBestRep || sortedPlaylistReps[0]).playlist;
4101};
4102
4103exports.simpleSelector = simpleSelector;
4104// Playlist Selectors
4105
4106/**
4107 * Chooses the appropriate media playlist based on the most recent
4108 * bandwidth estimate and the player size.
4109 *
4110 * Expects to be called within the context of an instance of HlsHandler
4111 *
4112 * @return {Playlist} the highest bitrate playlist less than the
4113 * currently detected bandwidth, accounting for some amount of
4114 * bandwidth variance
4115 */
4116var lastBandwidthSelector = function lastBandwidthSelector() {
4117  return simpleSelector(this.playlists.master, this.systemBandwidth, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10), parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10));
4118};
4119
4120exports.lastBandwidthSelector = lastBandwidthSelector;
4121/**
4122 * Chooses the appropriate media playlist based on an
4123 * exponential-weighted moving average of the bandwidth after
4124 * filtering for player size.
4125 *
4126 * Expects to be called within the context of an instance of HlsHandler
4127 *
4128 * @param {Number} decay - a number between 0 and 1. Higher values of
4129 * this parameter will cause previous bandwidth estimates to lose
4130 * significance more quickly.
4131 * @return {Function} a function which can be invoked to create a new
4132 * playlist selector function.
4133 * @see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
4134 */
4135var movingAverageBandwidthSelector = function movingAverageBandwidthSelector(decay) {
4136  var average = -1;
4137
4138  if (decay < 0 || decay > 1) {
4139    throw new Error('Moving average bandwidth decay must be between 0 and 1.');
4140  }
4141
4142  return function () {
4143    if (average < 0) {
4144      average = this.systemBandwidth;
4145    }
4146
4147    average = decay * this.systemBandwidth + (1 - decay) * average;
4148    return simpleSelector(this.playlists.master, average, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10), parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10));
4149  };
4150};
4151
4152exports.movingAverageBandwidthSelector = movingAverageBandwidthSelector;
4153/**
4154 * Chooses the appropriate media playlist based on the potential to rebuffer
4155 *
4156 * @param {Object} settings
4157 *        Object of information required to use this selector
4158 * @param {Object} settings.master
4159 *        Object representation of the master manifest
4160 * @param {Number} settings.currentTime
4161 *        The current time of the player
4162 * @param {Number} settings.bandwidth
4163 *        Current measured bandwidth
4164 * @param {Number} settings.duration
4165 *        Duration of the media
4166 * @param {Number} settings.segmentDuration
4167 *        Segment duration to be used in round trip time calculations
4168 * @param {Number} settings.timeUntilRebuffer
4169 *        Time left in seconds until the player has to rebuffer
4170 * @param {Number} settings.currentTimeline
4171 *        The current timeline segments are being loaded from
4172 * @param {SyncController} settings.syncController
4173 *        SyncController for determining if we have a sync point for a given playlist
4174 * @return {Object|null}
4175 *         {Object} return.playlist
4176 *         The highest bandwidth playlist with the least amount of rebuffering
4177 *         {Number} return.rebufferingImpact
4178 *         The amount of time in seconds switching to this playlist will rebuffer. A
4179 *         negative value means that switching will cause zero rebuffering.
4180 */
4181var minRebufferMaxBandwidthSelector = function minRebufferMaxBandwidthSelector(settings) {
4182  var master = settings.master;
4183  var currentTime = settings.currentTime;
4184  var bandwidth = settings.bandwidth;
4185  var duration = settings.duration;
4186  var segmentDuration = settings.segmentDuration;
4187  var timeUntilRebuffer = settings.timeUntilRebuffer;
4188  var currentTimeline = settings.currentTimeline;
4189  var syncController = settings.syncController;
4190
4191  var bandwidthPlaylists = master.playlists.filter(function (playlist) {
4192    return _playlist2['default'].isEnabled(playlist) && _playlist2['default'].hasAttribute('BANDWIDTH', playlist);
4193  });
4194
4195  var rebufferingEstimates = bandwidthPlaylists.map(function (playlist) {
4196    var syncPoint = syncController.getSyncPoint(playlist, duration, currentTimeline, currentTime);
4197    // If there is no sync point for this playlist, switching to it will require a
4198    // sync request first. This will double the request time
4199    var numRequests = syncPoint ? 1 : 2;
4200    var requestTimeEstimate = _playlist2['default'].estimateSegmentRequestTime(segmentDuration, bandwidth, playlist);
4201    var rebufferingImpact = requestTimeEstimate * numRequests - timeUntilRebuffer;
4202
4203    return {
4204      playlist: playlist,
4205      rebufferingImpact: rebufferingImpact
4206    };
4207  });
4208
4209  var noRebufferingPlaylists = rebufferingEstimates.filter(function (estimate) {
4210    return estimate.rebufferingImpact <= 0;
4211  });
4212
4213  // Sort by bandwidth DESC
4214  stableSort(noRebufferingPlaylists, function (a, b) {
4215    return comparePlaylistBandwidth(b.playlist, a.playlist);
4216  });
4217
4218  if (noRebufferingPlaylists.length) {
4219    return noRebufferingPlaylists[0];
4220  }
4221
4222  stableSort(rebufferingEstimates, function (a, b) {
4223    return a.rebufferingImpact - b.rebufferingImpact;
4224  });
4225
4226  return rebufferingEstimates[0] || null;
4227};
4228
4229exports.minRebufferMaxBandwidthSelector = minRebufferMaxBandwidthSelector;
4230/**
4231 * Chooses the appropriate media playlist, which in this case is the lowest bitrate
4232 * one with video.  If no renditions with video exist, return the lowest audio rendition.
4233 *
4234 * Expects to be called within the context of an instance of HlsHandler
4235 *
4236 * @return {Object|null}
4237 *         {Object} return.playlist
4238 *         The lowest bitrate playlist that contains a video codec.  If no such rendition
4239 *         exists pick the lowest audio rendition.
4240 */
4241var lowestBitrateCompatibleVariantSelector = function lowestBitrateCompatibleVariantSelector() {
4242  // filter out any playlists that have been excluded due to
4243  // incompatible configurations or playback errors
4244  var playlists = this.playlists.master.playlists.filter(_playlist2['default'].isEnabled);
4245
4246  // Sort ascending by bitrate
4247  stableSort(playlists, function (a, b) {
4248    return comparePlaylistBandwidth(a, b);
4249  });
4250
4251  // Parse and assume that playlists with no video codec have no video
4252  // (this is not necessarily true, although it is generally true).
4253  //
4254  // If an entire manifest has no valid videos everything will get filtered
4255  // out.
4256  var playlistsWithVideo = playlists.filter(function (playlist) {
4257    return (0, _utilCodecsJs.parseCodecs)(playlist.attributes.CODECS).videoCodec;
4258  });
4259
4260  return playlistsWithVideo[0] || null;
4261};
4262exports.lowestBitrateCompatibleVariantSelector = lowestBitrateCompatibleVariantSelector;
4263},{"./config":3,"./playlist":11,"./util/codecs.js":19}],11:[function(require,module,exports){
4264(function (global){
4265/**
4266 * @file playlist.js
4267 *
4268 * Playlist related utilities.
4269 */
4270'use strict';
4271
4272Object.defineProperty(exports, '__esModule', {
4273  value: true
4274});
4275
4276function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
4277
4278var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
4279
4280var _globalWindow = require('global/window');
4281
4282var _globalWindow2 = _interopRequireDefault(_globalWindow);
4283
4284var Playlist = {
4285  /**
4286   * The number of segments that are unsafe to start playback at in
4287   * a live stream. Changing this value can cause playback stalls.
4288   * See HTTP Live Streaming, "Playing the Media Playlist File"
4289   * https://tools.ietf.org/html/draft-pantos-http-live-streaming-18#section-6.3.3
4290   */
4291  UNSAFE_LIVE_SEGMENTS: 3
4292};
4293
4294/**
4295 * walk backward until we find a duration we can use
4296 * or return a failure
4297 *
4298 * @param {Playlist} playlist the playlist to walk through
4299 * @param {Number} endSequence the mediaSequence to stop walking on
4300 */
4301
4302var backwardDuration = function backwardDuration(playlist, endSequence) {
4303  var result = 0;
4304  var i = endSequence - playlist.mediaSequence;
4305  // if a start time is available for segment immediately following
4306  // the interval, use it
4307  var segment = playlist.segments[i];
4308
4309  // Walk backward until we find the latest segment with timeline
4310  // information that is earlier than endSequence
4311  if (segment) {
4312    if (typeof segment.start !== 'undefined') {
4313      return { result: segment.start, precise: true };
4314    }
4315    if (typeof segment.end !== 'undefined') {
4316      return {
4317        result: segment.end - segment.duration,
4318        precise: true
4319      };
4320    }
4321  }
4322  while (i--) {
4323    segment = playlist.segments[i];
4324    if (typeof segment.end !== 'undefined') {
4325      return { result: result + segment.end, precise: true };
4326    }
4327
4328    result += segment.duration;
4329
4330    if (typeof segment.start !== 'undefined') {
4331      return { result: result + segment.start, precise: true };
4332    }
4333  }
4334  return { result: result, precise: false };
4335};
4336
4337/**
4338 * walk forward until we find a duration we can use
4339 * or return a failure
4340 *
4341 * @param {Playlist} playlist the playlist to walk through
4342 * @param {Number} endSequence the mediaSequence to stop walking on
4343 */
4344var forwardDuration = function forwardDuration(playlist, endSequence) {
4345  var result = 0;
4346  var segment = undefined;
4347  var i = endSequence - playlist.mediaSequence;
4348  // Walk forward until we find the earliest segment with timeline
4349  // information
4350
4351  for (; i < playlist.segments.length; i++) {
4352    segment = playlist.segments[i];
4353    if (typeof segment.start !== 'undefined') {
4354      return {
4355        result: segment.start - result,
4356        precise: true
4357      };
4358    }
4359
4360    result += segment.duration;
4361
4362    if (typeof segment.end !== 'undefined') {
4363      return {
4364        result: segment.end - result,
4365        precise: true
4366      };
4367    }
4368  }
4369  // indicate we didn't find a useful duration estimate
4370  return { result: -1, precise: false };
4371};
4372
4373/**
4374  * Calculate the media duration from the segments associated with a
4375  * playlist. The duration of a subinterval of the available segments
4376  * may be calculated by specifying an end index.
4377  *
4378  * @param {Object} playlist a media playlist object
4379  * @param {Number=} endSequence an exclusive upper boundary
4380  * for the playlist.  Defaults to playlist length.
4381  * @param {Number} expired the amount of time that has dropped
4382  * off the front of the playlist in a live scenario
4383  * @return {Number} the duration between the first available segment
4384  * and end index.
4385  */
4386var intervalDuration = function intervalDuration(playlist, endSequence, expired) {
4387  var backward = undefined;
4388  var forward = undefined;
4389
4390  if (typeof endSequence === 'undefined') {
4391    endSequence = playlist.mediaSequence + playlist.segments.length;
4392  }
4393
4394  if (endSequence < playlist.mediaSequence) {
4395    return 0;
4396  }
4397
4398  // do a backward walk to estimate the duration
4399  backward = backwardDuration(playlist, endSequence);
4400  if (backward.precise) {
4401    // if we were able to base our duration estimate on timing
4402    // information provided directly from the Media Source, return
4403    // it
4404    return backward.result;
4405  }
4406
4407  // walk forward to see if a precise duration estimate can be made
4408  // that way
4409  forward = forwardDuration(playlist, endSequence);
4410  if (forward.precise) {
4411    // we found a segment that has been buffered and so it's
4412    // position is known precisely
4413    return forward.result;
4414  }
4415
4416  // return the less-precise, playlist-based duration estimate
4417  return backward.result + expired;
4418};
4419
4420/**
4421  * Calculates the duration of a playlist. If a start and end index
4422  * are specified, the duration will be for the subset of the media
4423  * timeline between those two indices. The total duration for live
4424  * playlists is always Infinity.
4425  *
4426  * @param {Object} playlist a media playlist object
4427  * @param {Number=} endSequence an exclusive upper
4428  * boundary for the playlist. Defaults to the playlist media
4429  * sequence number plus its length.
4430  * @param {Number=} expired the amount of time that has
4431  * dropped off the front of the playlist in a live scenario
4432  * @return {Number} the duration between the start index and end
4433  * index.
4434  */
4435var duration = function duration(playlist, endSequence, expired) {
4436  if (!playlist) {
4437    return 0;
4438  }
4439
4440  if (typeof expired !== 'number') {
4441    expired = 0;
4442  }
4443
4444  // if a slice of the total duration is not requested, use
4445  // playlist-level duration indicators when they're present
4446  if (typeof endSequence === 'undefined') {
4447    // if present, use the duration specified in the playlist
4448    if (playlist.totalDuration) {
4449      return playlist.totalDuration;
4450    }
4451
4452    // duration should be Infinity for live playlists
4453    if (!playlist.endList) {
4454      return _globalWindow2['default'].Infinity;
4455    }
4456  }
4457
4458  // calculate the total duration based on the segment durations
4459  return intervalDuration(playlist, endSequence, expired);
4460};
4461
4462exports.duration = duration;
4463/**
4464  * Calculate the time between two indexes in the current playlist
4465  * neight the start- nor the end-index need to be within the current
4466  * playlist in which case, the targetDuration of the playlist is used
4467  * to approximate the durations of the segments
4468  *
4469  * @param {Object} playlist a media playlist object
4470  * @param {Number} startIndex
4471  * @param {Number} endIndex
4472  * @return {Number} the number of seconds between startIndex and endIndex
4473  */
4474var sumDurations = function sumDurations(playlist, startIndex, endIndex) {
4475  var durations = 0;
4476
4477  if (startIndex > endIndex) {
4478    var _ref = [endIndex, startIndex];
4479    startIndex = _ref[0];
4480    endIndex = _ref[1];
4481  }
4482
4483  if (startIndex < 0) {
4484    for (var i = startIndex; i < Math.min(0, endIndex); i++) {
4485      durations += playlist.targetDuration;
4486    }
4487    startIndex = 0;
4488  }
4489
4490  for (var i = startIndex; i < endIndex; i++) {
4491    durations += playlist.segments[i].duration;
4492  }
4493
4494  return durations;
4495};
4496
4497exports.sumDurations = sumDurations;
4498/**
4499 * Calculates the playlist end time
4500 *
4501 * @param {Object} playlist a media playlist object
4502 * @param {Number=} expired the amount of time that has
4503 *                  dropped off the front of the playlist in a live scenario
4504 * @param {Boolean|false} useSafeLiveEnd a boolean value indicating whether or not the
4505 *                        playlist end calculation should consider the safe live end
4506 *                        (truncate the playlist end by three segments). This is normally
4507 *                        used for calculating the end of the playlist's seekable range.
4508 * @returns {Number} the end time of playlist
4509 * @function playlistEnd
4510 */
4511var playlistEnd = function playlistEnd(playlist, expired, useSafeLiveEnd) {
4512  if (!playlist || !playlist.segments) {
4513    return null;
4514  }
4515  if (playlist.endList) {
4516    return duration(playlist);
4517  }
4518
4519  if (expired === null) {
4520    return null;
4521  }
4522
4523  expired = expired || 0;
4524
4525  var endSequence = useSafeLiveEnd ? Math.max(0, playlist.segments.length - Playlist.UNSAFE_LIVE_SEGMENTS) : Math.max(0, playlist.segments.length);
4526
4527  return intervalDuration(playlist, playlist.mediaSequence + endSequence, expired);
4528};
4529
4530exports.playlistEnd = playlistEnd;
4531/**
4532  * Calculates the interval of time that is currently seekable in a
4533  * playlist. The returned time ranges are relative to the earliest
4534  * moment in the specified playlist that is still available. A full
4535  * seekable implementation for live streams would need to offset
4536  * these values by the duration of content that has expired from the
4537  * stream.
4538  *
4539  * @param {Object} playlist a media playlist object
4540  * dropped off the front of the playlist in a live scenario
4541  * @param {Number=} expired the amount of time that has
4542  * dropped off the front of the playlist in a live scenario
4543  * @return {TimeRanges}
4543 the periods of time that are valid targets
4544  * for seeking
4545  */
4546var seekable = function seekable(playlist, expired) {
4547  var useSafeLiveEnd = true;
4548  var seekableStart = expired || 0;
4549  var seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd);
4550
4551  if (seekableEnd === null) {
4552    return (0, _videoJs.createTimeRange)();
4553  }
4554  return (0, _videoJs.createTimeRange)(seekableStart, seekableEnd);
4555};
4556
4557exports.seekable = seekable;
4558var isWholeNumber = function isWholeNumber(num) {
4559  return num - Math.floor(num) === 0;
4560};
4561
4562var roundSignificantDigit = function roundSignificantDigit(increment, num) {
4563  // If we have a whole number, just add 1 to it
4564  if (isWholeNumber(num)) {
4565    return num + increment * 0.1;
4566  }
4567
4568  var numDecimalDigits = num.toString().split('.')[1].length;
4569
4570  for (var i = 1; i <= numDecimalDigits; i++) {
4571    var scale = Math.pow(10, i);
4572    var temp = num * scale;
4573
4574    if (isWholeNumber(temp) || i === numDecimalDigits) {
4575      return (temp + increment) / scale;
4576    }
4577  }
4578};
4579
4580var ceilLeastSignificantDigit = roundSignificantDigit.bind(null, 1);
4581var floorLeastSignificantDigit = roundSignificantDigit.bind(null, -1);
4582
4583/**
4584 * Determine the index and estimated starting time of the segment that
4585 * contains a specified playback position in a media playlist.
4586 *
4587 * @param {Object} playlist the media playlist to query
4588 * @param {Number} currentTime The number of seconds since the earliest
4589 * possible position to determine the containing segment for
4590 * @param {Number} startIndex
4591 * @param {Number} startTime
4592 * @return {Object}
4593 */
4594var getMediaInfoForTime = function getMediaInfoForTime(playlist, currentTime, startIndex, startTime) {
4595  var i = undefined;
4596  var segment = undefined;
4597  var numSegments = playlist.segments.length;
4598
4599  var time = currentTime - startTime;
4600
4601  if (time < 0) {
4602    // Walk backward from startIndex in the playlist, adding durations
4603    // until we find a segment that contains `time` and return it
4604    if (startIndex > 0) {
4605      for (i = startIndex - 1; i >= 0; i--) {
4606        segment = playlist.segments[i];
4607        time += floorLeastSignificantDigit(segment.duration);
4608        if (time > 0) {
4609          return {
4610            mediaIndex: i,
4611            startTime: startTime - sumDurations(playlist, startIndex, i)
4612          };
4613        }
4614      }
4615    }
4616    // We were unable to find a good segment within the playlist
4617    // so select the first segment
4618    return {
4619      mediaIndex: 0,
4620      startTime: currentTime
4621    };
4622  }
4623
4624  // When startIndex is negative, we first walk forward to first segment
4625  // adding target durations. If we "run out of time" before getting to
4626  // the first segment, return the first segment
4627  if (startIndex < 0) {
4628    for (i = startIndex; i < 0; i++) {
4629      time -= playlist.targetDuration;
4630      if (time < 0) {
4631        return {
4632          mediaIndex: 0,
4633          startTime: currentTime
4634        };
4635      }
4636    }
4637    startIndex = 0;
4638  }
4639
4640  // Walk forward from startIndex in the playlist, subtracting durations
4641  // until we find a segment that contains `time` and return it
4642  for (i = startIndex; i < numSegments; i++) {
4643    segment = playlist.segments[i];
4644    time -= ceilLeastSignificantDigit(segment.duration);
4645    if (time < 0) {
4646      return {
4647        mediaIndex: i,
4648        startTime: startTime + sumDurations(playlist, startIndex, i)
4649      };
4650    }
4651  }
4652
4653  // We are out of possible candidates so load the last one...
4654  return {
4655    mediaIndex: numSegments - 1,
4656    startTime: currentTime
4657  };
4658};
4659
4660exports.getMediaInfoForTime = getMediaInfoForTime;
4661/**
4662 * Check whether the playlist is blacklisted or not.
4663 *
4664 * @param {Object} playlist the media playlist object
4665 * @return {boolean} whether the playlist is blacklisted or not
4666 * @function isBlacklisted
4667 */
4668var isBlacklisted = function isBlacklisted(playlist) {
4669  return playlist.excludeUntil && playlist.excludeUntil > Date.now();
4670};
4671
4672exports.isBlacklisted = isBlacklisted;
4673/**
4674 * Check whether the playlist is enabled or not.
4675 *
4676 * @param {Object} playlist the media playlist object
4677 * @return {boolean} whether the playlist is enabled or not
4678 * @function isEnabled
4679 */
4680var isEnabled = function isEnabled(playlist) {
4681  var blacklisted = isBlacklisted(playlist);
4682
4683  return !playlist.disabled && !blacklisted;
4684};
4685
4686exports.isEnabled = isEnabled;
4687/**
4688 * Returns whether the current playlist is an AES encrypted HLS stream
4689 *
4690 * @return {Boolean} true if it's an AES encrypted HLS stream
4691 */
4692var isAes = function isAes(media) {
4693  for (var i = 0; i < media.segments.length; i++) {
4694    if (media.segments[i].key) {
4695      return true;
4696    }
4697  }
4698  return false;
4699};
4700
4701exports.isAes = isAes;
4702/**
4703 * Returns whether the current playlist contains fMP4
4704 *
4705 * @return {Boolean} true if the playlist contains fMP4
4706 */
4707var isFmp4 = function isFmp4(media) {
4708  for (var i = 0; i < media.segments.length; i++) {
4709    if (media.segments[i].map) {
4710      return true;
4711    }
4712  }
4713  return false;
4714};
4715
4716exports.isFmp4 = isFmp4;
4717/**
4718 * Checks if the playlist has a value for the specified attribute
4719 *
4720 * @param {String} attr
4721 *        Attribute to check for
4722 * @param {Object} playlist
4723 *        The media playlist object
4724 * @return {Boolean}
4725 *         Whether the playlist contains a value for the attribute or not
4726 * @function hasAttribute
4727 */
4728var hasAttribute = function hasAttribute(attr, playlist) {
4729  return playlist.attributes && playlist.attributes[attr];
4730};
4731
4732exports.hasAttribute = hasAttribute;
4733/**
4734 * Estimates the time required to complete a segment download from the specified playlist
4735 *
4736 * @param {Number} segmentDuration
4737 *        Duration of requested segment
4738 * @param {Number} bandwidth
4739 *        Current measured bandwidth of the player
4740 * @param {Object} playlist
4741 *        The media playlist object
4742 * @param {Number=} bytesReceived
4743 *        Number of bytes already received for the request. Defaults to 0
4744 * @return {Number|NaN}
4745 *         The estimated time to request the segment. NaN if bandwidth information for
4746 *         the given playlist is unavailable
4747 * @function estimateSegmentRequestTime
4748 */
4749var estimateSegmentRequestTime = function estimateSegmentRequestTime(segmentDuration, bandwidth, playlist) {
4750  var bytesReceived = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3];
4751
4752  if (!hasAttribute('BANDWIDTH', playlist)) {
4753    return NaN;
4754  }
4755
4756  var size = segmentDuration * playlist.attributes.BANDWIDTH;
4757
4758  return (size - bytesReceived * 8) / bandwidth;
4759};
4760
4761exports.estimateSegmentRequestTime = estimateSegmentRequestTime;
4762Playlist.duration = duration;
4763Playlist.seekable = seekable;
4764Playlist.getMediaInfoForTime = getMediaInfoForTime;
4765Playlist.isEnabled = isEnabled;
4766Playlist.isBlacklisted = isBlacklisted;
4767Playlist.playlistEnd = playlistEnd;
4768Playlist.isAes = isAes;
4769Playlist.isFmp4 = isFmp4;
4770Playlist.hasAttribute = hasAttribute;
4771Playlist.estimateSegmentRequestTime = estimateSegmentRequestTime;
4772
4773// exports
4774exports['default'] = Playlist;
4775}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
4776},{"global/window":32}],12:[function(require,module,exports){
4777(function (global){
4778/**
4779 * ranges
4780 *
4781 * Utilities for working with TimeRanges.
4782 *
4783 */
4784
4785'use strict';
4786
4787Object.defineProperty(exports, '__esModule', {
4788  value: true
4789});
4790
4791var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
4792
4793function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
4794
4795var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
4796
4797var _videoJs2 = _interopRequireDefault(_videoJs);
4798
4799// Fudge factor to account for TimeRanges rounding
4800var TIME_FUDGE_FACTOR = 1 / 30;
4801
4802/**
4803 * Clamps a value to within a range
4804 * @param {Number} num - the value to clamp
4805 * @param {Number} start - the start of the range to clamp within, inclusive
4806 * @param {Number} end - the end of the range to clamp within, inclusive
4807 * @return {Number}
4808 */
4809var clamp = function clamp(num, _ref) {
4810  var _ref2 = _slicedToArray(_ref, 2);
4811
4812  var start = _ref2[0];
4813  var end = _ref2[1];
4814
4815  return Math.min(Math.max(start, num), end);
4816};
4817var filterRanges = function filterRanges(timeRanges, predicate) {
4818  var results = [];
4819  var i = undefined;
4820
4821  if (timeRanges && timeRanges.length) {
4822    // Search for ranges that match the predicate
4823    for (i = 0; i < timeRanges.length; i++) {
4824      if (predicate(timeRanges.start(i), timeRanges.end(i))) {
4825        results.push([timeRanges.start(i), timeRanges.end(i)]);
4826      }
4827    }
4828  }
4829
4830  return _videoJs2['default'].createTimeRanges(results);
4831};
4832
4833/**
4834 * Attempts to find the buffered TimeRange that contains the specified
4835 * time.
4836 * @param {TimeRanges} buffered - the TimeRanges object to query
4837 * @param {number} time  - the time to filter on.
4838 * @returns {TimeRanges} a new TimeRanges object
4839 */
4840var findRange = function findRange(buffered, time) {
4841  return filterRanges(buffered, function (start, end) {
4842    return start - TIME_FUDGE_FACTOR <= time && end + TIME_FUDGE_FACTOR >= time;
4843  });
4844};
4845
4846/**
4847 * Returns the TimeRanges that begin later than the specified time.
4848 * @param {TimeRanges} timeRanges - the TimeRanges object to query
4849 * @param {number} time - the time to filter on.
4850 * @returns {TimeRanges} a new TimeRanges object.
4851 */
4852var findNextRange = function findNextRange(timeRanges, time) {
4853  return filterRanges(timeRanges, function (start) {
4854    return start - TIME_FUDGE_FACTOR >= time;
4855  });
4856};
4857
4858/**
4859 * Returns gaps within a list of TimeRanges
4860 * @param {TimeRanges} buffered - the TimeRanges object
4861 * @return {TimeRanges} a TimeRanges object of gaps
4862 */
4863var findGaps = function findGaps(buffered) {
4864  if (buffered.length < 2) {
4865    return _videoJs2['default'].createTimeRanges();
4866  }
4867
4868  var ranges = [];
4869
4870  for (var i = 1; i < buffered.length; i++) {
4871    var start = buffered.end(i - 1);
4872    var end = buffered.start(i);
4873
4874    ranges.push([start, end]);
4875  }
4876
4877  return _videoJs2['default'].createTimeRanges(ranges);
4878};
4879
4880/**
4881 * Search for a likely end time for the segment that was just appened
4882 * based on the state of the `buffered` property before and after the
4883 * append. If we fin only one such uncommon end-point return it.
4884 * @param {TimeRanges} original - the buffered time ranges before the update
4885 * @param {TimeRanges} update - the buffered time ranges after the update
4886 * @returns {Number|null} the end time added between `original` and `update`,
4887 * or null if one cannot be unambiguously determined.
4888 */
4889var findSoleUncommonTimeRangesEnd = function findSoleUncommonTimeRangesEnd(original, update) {
4890  var i = undefined;
4891  var start = undefined;
4892  var end = undefined;
4893  var result = [];
4894  var edges = [];
4895
4896  // In order to qualify as a possible candidate, the end point must:
4897  //  1) Not have already existed in the `original` ranges
4898  //  2) Not result from the shrinking of a range that already existed
4899  //     in the `original` ranges
4900  //  3) Not be contained inside of a range that existed in `original`
4901  var overlapsCurrentEnd = function overlapsCurrentEnd(span) {
4902    return span[0] <= end && span[1] >= end;
4903  };
4904
4905  if (original) {
4906    // Save all the edges in the `original` TimeRanges object
4907    for (i = 0; i < original.length; i++) {
4908      start = original.start(i);
4909      end = original.end(i);
4910
4911      edges.push([start, end]);
4912    }
4913  }
4914
4915  if (update) {
4916    // Save any end-points in `update` that are not in the `original`
4917    // TimeRanges object
4918    for (i = 0; i < update.length; i++) {
4919      start = update.start(i);
4920      end = update.end(i);
4921
4922      if (edges.some(overlapsCurrentEnd)) {
4923        continue;
4924      }
4925
4926      // at this point it must be a unique non-shrinking end edge
4927      result.push(end);
4928    }
4929  }
4930
4931  // we err on the side of caution and return null if didn't find
4932  // exactly *one* differing end edge in the search above
4933  if (result.length !== 1) {
4934    return null;
4935  }
4936
4937  return result[0];
4938};
4939
4940/**
4941 * Calculate the intersection of two TimeRanges
4942 * @param {TimeRanges} bufferA
4943 * @param {TimeRanges} bufferB
4944 * @returns {TimeRanges} The interesection of `bufferA` with `bufferB`
4945 */
4946var bufferIntersection = function bufferIntersection(bufferA, bufferB) {
4947  var start = null;
4948  var end = null;
4949  var arity = 0;
4950  var extents = [];
4951  var ranges = [];
4952
4953  if (!bufferA || !bufferA.length || !bufferB || !bufferB.length) {
4954    return _videoJs2['default'].createTimeRange();
4955  }
4956
4957  // Handle the case where we have both buffers and create an
4958  // intersection of the two
4959  var count = bufferA.length;
4960
4961  // A) Gather up all start and end times
4962  while (count--) {
4963    extents.push({ time: bufferA.start(count), type: 'start' });
4964    extents.push({ time: bufferA.end(count), type: 'end' });
4965  }
4966  count = bufferB.length;
4967  while (count--) {
4968    extents.push({ time: bufferB.start(count), type: 'start' });
4969    extents.push({ time: bufferB.end(count), type: 'end' });
4970  }
4971  // B) Sort them by time
4972  extents.sort(function (a, b) {
4973    return a.time - b.time;
4974  });
4975
4976  // C) Go along one by one incrementing arity for start and decrementing
4977  //    arity for ends
4978  for (count = 0; count < extents.length; count++) {
4979    if (extents[count].type === 'start') {
4980      arity++;
4981
4982      // D) If arity is ever incremented to 2 we are entering an
4983      //    overlapping range
4984      if (arity === 2) {
4985        start = extents[count].time;
4986      }
4987    } else if (extents[count].type === 'end') {
4988      arity--;
4989
4990      // E) If arity is ever decremented to 1 we leaving an
4991      //    overlapping range
4992      if (arity === 1) {
4993        end = extents[count].time;
4994      }
4995    }
4996
4997    // F) Record overlapping ranges
4998    if (start !== null && end !== null) {
4999      ranges.push([start, end]);
5000      start = null;
5001      end = null;
5002    }
5003  }
5004
5005  return _videoJs2['default'].createTimeRanges(ranges);
5006};
5007
5008/**
5009 * Calculates the percentage of `segmentRange` that overlaps the
5010 * `buffered` time ranges.
5011 * @param {TimeRanges} segmentRange - the time range that the segment
5012 * covers adjusted according to currentTime
5013 * @param {TimeRanges} referenceRange - the original time range that the
5014 * segment covers
5015 * @param {Number} currentTime - time in seconds where the current playback
5016 * is at
5017 * @param {TimeRanges} buffered - the currently buffered time ranges
5018 * @returns {Number} percent of the segment currently buffered
5019 */
5020var calculateBufferedPercent = function calculateBufferedPercent(adjustedRange, referenceRange, currentTime, buffered) {
5021  var referenceDuration = referenceRange.end(0) - referenceRange.start(0);
5022  var adjustedDuration = adjustedRange.end(0) - adjustedRange.start(0);
5023  var bufferMissingFromAdjusted = referenceDuration - adjustedDuration;
5024  var adjustedIntersection = bufferIntersection(adjustedRange, buffered);
5025  var referenceIntersection = bufferIntersection(referenceRange, buffered);
5026  var adjustedOverlap = 0;
5027  var referenceOverlap = 0;
5028
5029  var count = adjustedIntersection.length;
5030
5031  while (count--) {
5032    adjustedOverlap += adjustedIntersection.end(count) - adjustedIntersection.start(count);
5033
5034    // If the current overlap segment starts at currentTime, then increase the
5035    // overlap duration so that it actually starts at the beginning of referenceRange
5036    // by including the difference between the two Range's durations
5037    // This is a work around for the way Flash has no buffer before currentTime
5038    if (adjustedIntersection.start(count) === currentTime) {
5039      adjustedOverlap += bufferMissingFromAdjusted;
5040    }
5041  }
5042
5043  count = referenceIntersection.length;
5044
5045  while (count--) {
5046    referenceOverlap += referenceIntersection.end(count) - referenceIntersection.start(count);
5047  }
5048
5049  // Use whichever value is larger for the percentage-buffered since that value
5050  // is likely more accurate because the only way
5051  return Math.max(adjustedOverlap, referenceOverlap) / referenceDuration * 100;
5052};
5053
5054/**
5055 * Return the amount of a range specified by the startOfSegment and segmentDuration
5056 * overlaps the current buffered content.
5057 *
5058 * @param {Number} startOfSegment - the time where the segment begins
5059 * @param {Number} segmentDuration - the duration of the segment in seconds
5060 * @param {Number} currentTime - time in seconds where the current playback
5061 * is at
5062 * @param {TimeRanges} buffered - the state of the buffer
5063 * @returns {Number} percentage of the segment's time range that is
5064 * already in `buffered`
5065 */
5066var getSegmentBufferedPercent = function getSegmentBufferedPercent(startOfSegment, segmentDuration, currentTime, buffered) {
5067  var endOfSegment = startOfSegment + segmentDuration;
5068
5069  // The entire time range of the segment
5070  var originalSegmentRange = _videoJs2['default'].createTimeRanges([[startOfSegment, endOfSegment]]);
5071
5072  // The adjusted segment time range that is setup such that it starts
5073  // no earlier than currentTime
5074  // Flash has no notion of a back-buffer so adjustedSegmentRange adjusts
5075  // for that and the function will still return 100% if a only half of a
5076  // segment is actually in the buffer as long as the currentTime is also
5077  // half-way through the segment
5078  var adjustedSegmentRange = _videoJs2['default'].createTimeRanges([[clamp(startOfSegment, [currentTime, endOfSegment]), endOfSegment]]);
5079
5080  // This condition happens when the currentTime is beyond the segment's
5081  // end time
5082  if (adjustedSegmentRange.start(0) === adjustedSegmentRange.end(0)) {
5083    return 0;
5084  }
5085
5086  var percent = calculateBufferedPercent(adjustedSegmentRange, originalSegmentRange, currentTime, buffered);
5087
5088  // If the segment is reported as having a zero duration, return 0%
5089  // since it is likely that we will need to fetch the segment
5090  if (isNaN(percent) || percent === Infinity || percent === -Infinity) {
5091    return 0;
5092  }
5093
5094  return percent;
5095};
5096
5097/**
5098 * Gets a human readable string for a TimeRange
5099 *
5100 * @param {TimeRange} range
5101 * @returns {String} a human readable string
5102 */
5103var printableRange = function printableRange(range) {
5104  var strArr = [];
5105
5106  if (!range || !range.length) {
5107    return '';
5108  }
5109
5110  for (var i = 0; i < range.length; i++) {
5111    strArr.push(range.start(i) + ' => ' + range.end(i));
5112  }
5113
5114  return strArr.join(', ');
5115};
5116
5117/**
5118 * Calculates the amount of time left in seconds until the player hits the end of the
5119 * buffer and causes a rebuffer
5120 *
5121 * @param {TimeRange} buffered
5122 *        The state of the buffer
5123 * @param {Numnber} currentTime
5124 *        The current time of the player
5125 * @param {Number} playbackRate
5126 *        The current playback rate of the player. Defaults to 1.
5127 * @return {Number}
5128 *         Time until the player has to start rebuffering in seconds.
5129 * @function timeUntilRebuffer
5130 */
5131var timeUntilRebuffer = function timeUntilRebuffer(buffered, currentTime) {
5132  var playbackRate = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2];
5133
5134  var bufferedEnd = buffered.length ? buffered.end(buffered.length - 1) : 0;
5135
5136  return (bufferedEnd - currentTime) / playbackRate;
5137};
5138
5139exports['default'] = {
5140  findRange: findRange,
5141  findNextRange: findNextRange,
5142  findGaps: findGaps,
5143  findSoleUncommonTimeRangesEnd: findSoleUncommonTimeRangesEnd,
5144  getSegmentBufferedPercent: getSegmentBufferedPercent,
5145  TIME_FUDGE_FACTOR: TIME_FUDGE_FACTOR,
5146  printableRange: printableRange,
5147  timeUntilRebuffer: timeUntilRebuffer
5148};
5149module.exports = exports['default'];
5150}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
5151},{}],13:[function(require,module,exports){
5152(function (global){
5153'use strict';
5154
5155Object.defineProperty(exports, '__esModule', {
5156  value: true
5157});
5158
5159function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
5160
5161var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
5162
5163var _videoJs2 = _interopRequireDefault(_videoJs);
5164
5165var defaultOptions = {
5166  errorInterval: 30,
5167  getSource: function getSource(next) {
5168    var tech = this.tech({ IWillNotUseThisInPlugins: true });
5169    var sourceObj = tech.currentSource_;
5170
5171    return next(sourceObj);
5172  }
5173};
5174
5175/**
5176 * Main entry point for the plugin
5177 *
5178 * @param {Player} player a reference to a videojs Player instance
5179 * @param {Object} [options] an object with plugin options
5180 * @private
5181 */
5182var initPlugin = function initPlugin(player, options) {
5183  var lastCalled = 0;
5184  var seekTo = 0;
5185  var localOptions = _videoJs2['default'].mergeOptions(defaultOptions, options);
5186
5187  player.ready(function () {
5188    player.trigger({ type: 'usage', name: 'hls-error-reload-initialized' });
5189  });
5190
5191  /**
5192   * Player modifications to perform that must wait until `loadedmetadata`
5193   * has been triggered
5194   *
5195   * @private
5196   */
5197  var loadedMetadataHandler = function loadedMetadataHandler() {
5198    if (seekTo) {
5199      player.currentTime(seekTo);
5200    }
5201  };
5202
5203  /**
5204   * Set the source on the player element, play, and seek if necessary
5205   *
5206   * @param {Object} sourceObj An object specifying the source url and mime-type to play
5207   * @private
5208   */
5209  var setSource = function setSource(sourceObj) {
5210    if (sourceObj === null || sourceObj === undefined) {
5211      return;
5212    }
5213    seekTo = player.duration() !== Infinity && player.currentTime() || 0;
5214
5215    player.one('loadedmetadata', loadedMetadataHandler);
5216
5217    player.src(sourceObj);
5218    player.trigger({ type: 'usage', name: 'hls-error-reload' });
5219    player.play();
5220  };
5221
5222  /**
5223   * Attempt to get a source from either the built-in getSource function
5224   * or a custom function provided via the options
5225   *
5226   * @private
5227   */
5228  var errorHandler = function errorHandler() {
5229    // Do not attempt to reload the source if a source-reload occurred before
5230    // 'errorInterval' time has elapsed since the last source-reload
5231    if (Date.now() - lastCalled < localOptions.errorInterval * 1000) {
5232      player.trigger({ type: 'usage', name: 'hls-error-reload-canceled' });
5233      return;
5234    }
5235
5236    if (!localOptions.getSource || typeof localOptions.getSource !== 'function') {
5237      _videoJs2['default'].log.error('ERROR: reloadSourceOnError - The option getSource must be a function!');
5238      return;
5239    }
5240    lastCalled = Date.now();
5241
5242    return localOptions.getSource.call(player, setSource);
5243  };
5244
5245  /**
5246   * Unbind any event handlers that were bound by the plugin
5247   *
5248   * @private
5249   */
5250  var cleanupEvents = function cleanupEvents() {
5251    player.off('loadedmetadata', loadedMetadataHandler);
5252    player.off('error', errorHandler);
5253    player.off('dispose', cleanupEvents);
5254  };
5255
5256  /**
5257   * Cleanup before re-initializing the plugin
5258   *
5259   * @param {Object} [newOptions] an object with plugin options
5260   * @private
5261   */
5262  var reinitPlugin = function reinitPlugin(newOptions) {
5263    cleanupEvents();
5264    initPlugin(player, newOptions);
5265  };
5266
5267  player.on('error', errorHandler);
5268  player.on('dispose', cleanupEvents);
5269
5270  // Overwrite the plugin function so that we can correctly cleanup before
5271  // initializing the plugin
5272  player.reloadSourceOnError = reinitPlugin;
5273};
5274
5275/**
5276 * Reload the source when an error is detected as long as there
5277 * wasn't an error previously within the last 30 seconds
5278 *
5279 * @param {Object} [options] an object with plugin options
5280 */
5281var reloadSourceOnError = function reloadSourceOnError(options) {
5282  initPlugin(this, options);
5283};
5284
5285exports['default'] = reloadSourceOnError;
5286module.exports = exports['default'];
5287}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
5288},{}],14:[function(require,module,exports){
5289'use strict';
5290
5291Object.defineProperty(exports, '__esModule', {
5292  value: true
5293});
5294
5295function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
5296
5297var _playlistJs = require('./playlist.js');
5298
5299/**
5300 * Enable/disable playlist function. It is intended to have the first two
5301 * arguments partially-applied in order to create the final per-playlist
5302 * function.
5303 *
5304 * @param {PlaylistLoader} playlist - The rendition or media-playlist
5305 * @param {Function} changePlaylistFn - A function to be called after a
5306 * playlist's enabled-state has been changed. Will NOT be called if a
5307 * playlist's enabled-state is unchanged
5308 * @param {Boolean=} enable - Value to set the playlist enabled-state to
5309 * or if undefined returns the current enabled-state for the playlist
5310 * @return {Boolean} The current enabled-state of the playlist
5311 */
5312var enableFunction = function enableFunction(loader, playlistUri, changePlaylistFn, enable) {
5313  var playlist = loader.master.playlists[playlistUri];
5314  var blacklisted = (0, _playlistJs.isBlacklisted)(playlist);
5315  var currentlyEnabled = (0, _playlistJs.isEnabled)(playlist);
5316
5317  if (typeof enable === 'undefined') {
5318    return currentlyEnabled;
5319  }
5320
5321  if (enable) {
5322    delete playlist.disabled;
5323  } else {
5324    playlist.disabled = true;
5325  }
5326
5327  if (enable !== currentlyEnabled && !blacklisted) {
5328    // Ensure the outside world knows about our changes
5329    changePlaylistFn();
5330    if (enable) {
5331      loader.trigger('renditionenabled');
5332    } else {
5333      loader.trigger('renditiondisabled');
5334    }
5335  }
5336  return enable;
5337};
5338
5339/**
5340 * The representation object encapsulates the publicly visible information
5341 * in a media playlist along with a setter/getter-type function (enabled)
5342 * for changing the enabled-state of a particular playlist entry
5343 *
5344 * @class Representation
5345 */
5346
5347var Representation = function Representation(hlsHandler, playlist, id) {
5348  _classCallCheck(this, Representation);
5349
5350  // Get a reference to a bound version of fastQualityChange_
5351  var fastChangeFunction = hlsHandler.masterPlaylistController_.fastQualityChange_.bind(hlsHandler.masterPlaylistController_);
5352
5353  // some playlist attributes are optional
5354  if (playlist.attributes.RESOLUTION) {
5355    var resolution = playlist.attributes.RESOLUTION;
5356
5357    this.width = resolution.width;
5358    this.height = resolution.height;
5359  }
5360
5361  this.bandwidth = playlist.attributes.BANDWIDTH;
5362
5363  // The id is simply the ordinality of the media playlist
5364  // within the master playlist
5365  this.id = id;
5366
5367  // Partially-apply the enableFunction to create a playlist-
5368  // specific variant
5369  this.enabled = enableFunction.bind(this, hlsHandler.playlists, playlist.uri, fastChangeFunction);
5370}
5371
5372/**
5373 * A mixin function that adds the `representations` api to an instance
5374 * of the HlsHandler class
5375 * @param {HlsHandler} hlsHandler - An instance of HlsHandler to add the
5376 * representation API into
5377 */
5378;
5379
5380var renditionSelectionMixin = function renditionSelectionMixin(hlsHandler) {
5381  var playlists = hlsHandler.playlists;
5382
5383  // Add a single API-specific function to the HlsHandler instance
5384  hlsHandler.representations = function () {
5385    return playlists.master.playlists.filter(function (media) {
5386      return !(0, _playlistJs.isBlacklisted)(media);
5387    }).map(function (e, i) {
5388      return new Representation(hlsHandler, e, e.uri);
5389    });
5390  };
5391};
5392
5393exports['default'] = renditionSelectionMixin;
5394module.exports = exports['default'];
5395},{"./playlist.js":11}],15:[function(require,module,exports){
5396/**
5397 * @file resolve-url.js
5398 */
5399
5400'use strict';
5401
5402Object.defineProperty(exports, '__esModule', {
5403  value: true
5404});
5405
5406function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
5407
5408var _urlToolkit = require('url-toolkit');
5409
5410var _urlToolkit2 = _interopRequireDefault(_urlToolkit);
5411
5412var _globalWindow = require('global/window');
5413
5414var _globalWindow2 = _interopRequireDefault(_globalWindow);
5415
5416var resolveUrl = function resolveUrl(baseURL, relativeURL) {
5417  // return early if we don't need to resolve
5418  if (/^[a-z]+:/i.test(relativeURL)) {
5419    return relativeURL;
5420  }
5421
5422  // if the base URL is relative then combine with the current location
5423  if (!/\/\//i.test(baseURL)) {
5424    baseURL = _urlToolkit2['default'].buildAbsoluteURL(_globalWindow2['default'].location.href, baseURL);
5425  }
5426
5427  return _urlToolkit2['default'].buildAbsoluteURL(baseURL, relativeURL);
5428};
5429
5430exports['default'] = resolveUrl;
5431module.exports = exports['default'];
5432},{"global/window":32,"url-toolkit":63}],16:[function(require,module,exports){
5433(function (global){
5434/**
5435 * @file segment-loader.js
5436 */
5437'use strict';
5438
5439Object.defineProperty(exports, '__esModule', {
5440  value: true
5441});
5442
5443var _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; }; })();
5444
5445var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { var object = _x4, property = _x5, receiver = _x6; _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 { _x4 = parent; _x5 = property; _x6 = 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); } } };
5446
5447function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
5448
5449function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
5450
5451function _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, { c
5451onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
5452
5453var _playlist = require('./playlist');
5454
5455var _playlist2 = _interopRequireDefault(_playlist);
5456
5457var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
5458
5459var _videoJs2 = _interopRequireDefault(_videoJs);
5460
5461var _sourceUpdater = require('./source-updater');
5462
5463var _sourceUpdater2 = _interopRequireDefault(_sourceUpdater);
5464
5465var _config = require('./config');
5466
5467var _config2 = _interopRequireDefault(_config);
5468
5469var _globalWindow = require('global/window');
5470
5471var _globalWindow2 = _interopRequireDefault(_globalWindow);
5472
5473var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs = require('videojs-contrib-media-sources/es5/remove-cues-from-track.js');
5474
5475var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2 = _interopRequireDefault(_videojsContribMediaSourcesEs5RemoveCuesFromTrackJs);
5476
5477var _binUtils = require('./bin-utils');
5478
5479var _mediaSegmentRequest = require('./media-segment-request');
5480
5481var _ranges = require('./ranges');
5482
5483var _playlistSelectors = require('./playlist-selectors');
5484
5485// in ms
5486var CHECK_BUFFER_DELAY = 500;
5487
5488/**
5489 * Determines if we should call endOfStream on the media source based
5490 * on the state of the buffer or if appened segment was the final
5491 * segment in the playlist.
5492 *
5493 * @param {Object} playlist a media playlist object
5494 * @param {Object} mediaSource the MediaSource object
5495 * @param {Number} segmentIndex the index of segment we last appended
5496 * @returns {Boolean} do we need to call endOfStream on the MediaSource
5497 */
5498var detectEndOfStream = function detectEndOfStream(playlist, mediaSource, segmentIndex) {
5499  if (!playlist || !mediaSource) {
5500    return false;
5501  }
5502
5503  var segments = playlist.segments;
5504
5505  // determine a few boolean values to help make the branch below easier
5506  // to read
5507  var appendedLastSegment = segmentIndex === segments.length;
5508
5509  // if we've buffered to the end of the video, we need to call endOfStream
5510  // so that MediaSources can trigger the `ended` event when it runs out of
5511  // buffered data instead of waiting for me
5512  return playlist.endList && mediaSource.readyState === 'open' && appendedLastSegment;
5513};
5514
5515var finite = function finite(num) {
5516  return typeof num === 'number' && isFinite(num);
5517};
5518
5519var illegalMediaSwitch = function illegalMediaSwitch(loaderType, startingMedia, newSegmentMedia) {
5520  // Although these checks should most likely cover non 'main' types, for now it narrows
5521  // the scope of our checks.
5522  if (loaderType !== 'main' || !startingMedia || !newSegmentMedia) {
5523    return null;
5524  }
5525
5526  if (!newSegmentMedia.containsAudio && !newSegmentMedia.containsVideo) {
5527    return 'Neither audio nor video found in segment.';
5528  }
5529
5530  if (startingMedia.containsVideo && !newSegmentMedia.containsVideo) {
5531    return 'Only audio found in segment when we expected video.' + ' We can\'t switch to audio only from a stream that had video.' + ' To get rid of this message, please add codec information to the manifest.';
5532  }
5533
5534  if (!startingMedia.containsVideo && newSegmentMedia.containsVideo) {
5535    return 'Video found in segment when we expected only audio.' + ' We can\'t switch to a stream with video from an audio only stream.' + ' To get rid of this message, please add codec information to the manifest.';
5536  }
5537
5538  return null;
5539};
5540
5541exports.illegalMediaSwitch = illegalMediaSwitch;
5542/**
5543 * An object that manages segment loading and appending.
5544 *
5545 * @class SegmentLoader
5546 * @param {Object} options required and optional options
5547 * @extends videojs.EventTarget
5548 */
5549
5550var SegmentLoader = (function (_videojs$EventTarget) {
5551  _inherits(SegmentLoader, _videojs$EventTarget);
5552
5553  function SegmentLoader(settings) {
5554    var _this = this;
5555
5556    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
5557
5558    _classCallCheck(this, SegmentLoader);
5559
5560    _get(Object.getPrototypeOf(SegmentLoader.prototype), 'constructor', this).call(this);
5561    // check pre-conditions
5562    if (!settings) {
5563      throw new TypeError('Initialization settings are required');
5564    }
5565    if (typeof settings.currentTime !== 'function') {
5566      throw new TypeError('No currentTime getter specified');
5567    }
5568    if (!settings.mediaSource) {
5569      throw new TypeError('No MediaSource specified');
5570    }
5571    // public properties
5572    this.state = 'INIT';
5573    this.bandwidth = settings.bandwidth;
5574    this.throughput = { rate: 0, count: 0 };
5575    this.roundTrip = NaN;
5576    this.resetStats_();
5577    this.mediaIndex = null;
5578
5579    // private settings
5580    this.hasPlayed_ = settings.hasPlayed;
5581    this.currentTime_ = settings.currentTime;
5582    this.seekable_ = settings.seekable;
5583    this.seeking_ = settings.seeking;
5584    this.duration_ = settings.duration;
5585    this.mediaSource_ = settings.mediaSource;
5586    this.hls_ = settings.hls;
5587    this.loaderType_ = settings.loaderType;
5588    this.startingMedia_ = void 0;
5589    this.segmentMetadataTrack_ = settings.segmentMetadataTrack;
5590    this.goalBufferLength_ = settings.goalBufferLength;
5591
5592    // private instance variables
5593    this.checkBufferTimeout_ = null;
5594    this.error_ = void 0;
5595    this.currentTimeline_ = -1;
5596    this.pendingSegment_ = null;
5597    this.mimeType_ = null;
5598    this.sourceUpdater_ = null;
5599    this.xhrOptions_ = null;
5600
5601    // Fragmented mp4 playback
5602    this.activeInitSegmentId_ = null;
5603    this.initSegments_ = {};
5604
5605    this.decrypter_ = settings.decrypter;
5606
5607    // Manages the tracking and generation of sync-points, mappings
5608    // between a time in the display time and a segment index within
5609    // a playlist
5610    this.syncController_ = settings.syncController;
5611    this.syncPoint_ = {
5612      segmentIndex: 0,
5613      time: 0
5614    };
5615
5616    this.syncController_.on('syncinfoupdate', function () {
5617      return _this.trigger('syncinfoupdate');
5618    });
5619
5620    this.mediaSource_.addEventListener('sourceopen', function () {
5621      return _this.ended_ = false;
5622    });
5623
5624    // ...for determining the fetch location
5625    this.fetchAtBuffer_ = false;
5626
5627    if (options.debug) {
5628      this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'segment-loader', this.loaderType_, '->');
5629    }
5630  }
5631
5632  /**
5633   * reset all of our media stats
5634   *
5635   * @private
5636   */
5637
5638  _createClass(SegmentLoader, [{
5639    key: 'resetStats_',
5640    value: function resetStats_() {
5641      this.mediaBytesTransferred = 0;
5642      this.mediaRequests = 0;
5643      this.mediaRequestsAborted = 0;
5644      this.mediaRequestsTimedout = 0;
5645      this.mediaRequestsErrored = 0;
5646      this.mediaTransferDuration = 0;
5647      this.mediaSecondsLoaded = 0;
5648    }
5649
5650    /**
5651     * dispose of the SegmentLoader and reset to the default state
5652     */
5653  }, {
5654    key: 'dispose',
5655    value: function dispose() {
5656      this.state = 'DISPOSED';
5657      this.pause();
5658      this.abort_();
5659      if (this.sourceUpdater_) {
5660        this.sourceUpdater_.dispose();
5661      }
5662      this.resetStats_();
5663    }
5664
5665    /**
5666     * abort anything that is currently doing on with the SegmentLoader
5667     * and reset to a default state
5668     */
5669  }, {
5670    key: 'abort',
5671    value: function abort() {
5672      if (this.state !== 'WAITING') {
5673        if (this.pendingSegment_) {
5674          this.pendingSegment_ = null;
5675        }
5676        return;
5677      }
5678
5679      this.abort_();
5680
5681      // We aborted the requests we were waiting on, so reset the loader's state to READY
5682      // since we are no longer "waiting" on any requests. XHR callback is not always run
5683      // when the request is aborted. This will prevent the loader from being stuck in the
5684      // WAITING state indefinitely.
5685      this.state = 'READY';
5686
5687      // don't wait for buffer check timeouts to begin fetching the
5688      // next segment
5689      if (!this.paused()) {
5690        this.monitorBuffer_();
5691      }
5692    }
5693
5694    /**
5695     * abort all pending xhr requests and null any pending segements
5696     *
5697     * @private
5698     */
5699  }, {
5700    key: 'abort_',
5701    value: function abort_() {
5702      if (this.pendingSegment_) {
5703        this.pendingSegment_.abortRequests();
5704      }
5705
5706      // clear out the segment being processed
5707      this.pendingSegment_ = null;
5708    }
5709
5710    /**
5711     * set an error on the segment loader and null out any pending segements
5712     *
5713     * @param {Error} error the error to set on the SegmentLoader
5714     * @return {Error} the error that was set or that is currently set
5715     */
5716  }, {
5717    key: 'error',
5718    value: function error(_error) {
5719      if (typeof _error !== 'undefined') {
5720        this.error_ = _error;
5721      }
5722
5723      this.pendingSegment_ = null;
5724      return this.error_;
5725    }
5726  }, {
5727    key: 'endOfStream',
5728    value: function endOfStream() {
5729      this.ended_ = true;
5730      this.pause();
5731      this.trigger('ended');
5732    }
5733
5734    /**
5735     * Indicates which time ranges are buffered
5736     *
5737     * @return {TimeRange}
5738     *         TimeRange object representing the current buffered ranges
5739     */
5740  }, {
5741    key: 'buffered_',
5742    value: function buffered_() {
5743      if (!this.sourceUpdater_) {
5744        return _videoJs2['default'].createTimeRanges();
5745      }
5746
5747      return this.sourceUpdater_.buffered();
5748    }
5749
5750    /**
5751     * Gets and sets init segment for the provided map
5752     *
5753     * @param {Object} map
5754     *        The map object representing the init segment to get or set
5755     * @param {Boolean=} set
5756     *        If true, the init segment for the provided map should be saved
5757     * @return {Object}
5758     *         map object for desired init segment
5759     */
5760  }, {
5761    key: 'initSegment',
5762    value: function initSegment(map) {
5763      var set = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
5764
5765      if (!map) {
5766        return null;
5767      }
5768
5769      var id = (0, _binUtils.initSegmentId)(map);
5770      var storedMap = this.initSegments_[id];
5771
5772      if (set && !storedMap && map.bytes) {
5773        this.initSegments_[id] = storedMap = {
5774          resolvedUri: map.resolvedUri,
5775          byterange: map.byterange,
5776          bytes: map.bytes
5777        };
5778      }
5779
5780      return storedMap || map;
5781    }
5782
5783    /**
5784     * Returns true if all configuration required for loading is present, otherwise false.
5785     *
5786     * @return {Boolean} True if the all configuration is ready for loading
5787     * @private
5788     */
5789  }, {
5790    key: 'couldBeginLoading_',
5791    value: function couldBeginLoading_() {
5792      return this.playlist_ && (
5793      // the source updater is created when init_ is called, so either having a
5794      // source updater or being in the INIT state with a mimeType is enough
5795      // to say we have all the needed configuration to start loading.
5796      this.sourceUpdater_ || this.mimeType_ && this.state === 'INIT') && !this.paused();
5797    }
5798
5799    /**
5800     * load a playlist and start to fill the buffer
5801     */
5802  }, {
5803    key: 'load',
5804    value: function load() {
5805      // un-pause
5806      this.monitorBuffer_();
5807
5808      // if we don't have a playlist yet, keep waiting for one to be
5809      // specified
5810      if (!this.playlist_) {
5811        return;
5812      }
5813
5814      // not sure if this is the best place for this
5815      this.syncController_.setDateTimeMapping(this.playlist_);
5816
5817      // if all the configuration is ready, initialize and begin loading
5818      if (this.state === 'INIT' && this.couldBeginLoading_()) {
5819        return this.init_();
5820      }
5821
5822      // if we're in the middle of processing a segment already, don't
5823      // kick off an additional segment request
5824      if (!this.couldBeginLoading_() || this.state !== 'READY' && this.state !== 'INIT') {
5825        return;
5826      }
5827
5828      this.state = 'READY';
5829    }
5830
5831    /**
5832     * Once all the starting parameters have been specified, begin
5833     * operation. This method should only be invoked from the INIT
5834     * state.
5835     *
5836     * @private
5837     */
5838  }, {
5839    key: 'init_',
5840    value: function init_() {
5841      this.state = 'READY';
5842      this.sourceUpdater_ = new _sourceUpdater2['default'](this.mediaSource_, this.mimeType_);
5843      this.resetEverything();
5844      return this.monitorBuffer_();
5845    }
5846
5847    /**
5848     * set a playlist on the segment loader
5849     *
5850     * @param {PlaylistLoader} media the playlist to set on the segment loader
5851     */
5852  }, {
5853    key: 'playlist',
5854    value: function playlist(newPlaylist) {
5855      var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
5856
5857      if (!newPlaylist) {
5858        return;
5859      }
5860
5861      var oldPlaylist = this.playlist_;
5862      var segmentInfo = this.pendingSegment_;
5863
5864      this.playlist_ = newPlaylist;
5865      this.xhrOptions_ = options;
5866
5867      // when we haven't started playing yet, the start of a live playlist
5868      // is always our zero-time so force a sync update each time the playlist
5869      // is refreshed from the server
5870      if (!this.hasPlayed_()) {
5871        newPlaylist.syncInfo = {
5872          mediaSequence: newPlaylist.mediaSequence,
5873          time: 0
5874        };
5875      }
5876
5877      // in VOD, this is always a rendition switch (or we updated our syncInfo above)
5878      // in LIVE, we always want to update with new playlists (including refreshes)
5879      this.trigger('syncinfoupdate');
5880
5881      // if we were unpaused but waiting for a playlist, start
5882      // buffering now
5883      if (this.state === 'INIT' && this.couldBeginLoading_()) {
5884        return this.init_();
5885      }
5886
5887      if (!oldPlaylist || oldPlaylist.uri !== newPlaylist.uri) {
5888        if (this.mediaIndex !== null) {
5889          // we must "resync" the segment loader when we switch renditions and
5890          // the segment loader is already synced to the previous rendition
5891          this.resyncLoader();
5892        }
5893
5894        // the rest of this function depends on `oldPlaylist` being defined
5895        return;
5896      }
5897
5898      // we reloaded the same playlist so we are in a live scenario
5899      // and we will likely need to adjust the mediaIndex
5900      var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
5901
5902      this.logger_('mediaSequenceDiff', mediaSequenceDiff);
5903
5904      // update the mediaIndex on the SegmentLoader
5905      // this is important because we can abort a request and this value must be
5906      // equal to the last appended mediaIndex
5907      if (this.mediaIndex !== null) {
5908        this.mediaIndex -= mediaSequenceDiff;
5909      }
5910
5911      // update the mediaIndex on the SegmentInfo object
5912      // this is important because we will update this.mediaIndex with this value
5913      // in `handleUpdateEnd_` after the segment has been successfully appended
5914      if (segmentInfo) {
5915        segmentInfo.mediaIndex -= mediaSequenceDiff;
5916
5917        // we need to update the referenced segment so that timing information is
5918        // saved for the new playlist's segment, however, if the segment fell off the
5919        // playlist, we can leave the old reference and just lose the timing info
5920        if (segmentInfo.mediaIndex >= 0) {
5921          segmentInfo.segment = newPlaylist.segments[segmentInfo.mediaIndex];
5922        }
5923      }
5924
5925      this.syncController_.saveExpiredSegmentInfo(oldPlaylist, newPlaylist);
5926    }
5927
5928    /**
5929     * Prevent the loader from fetching additional segments. If there
5930     * is a segment request outstanding, it will finish processing
5931     * before the loader halts. A segment loader can be unpaused by
5932     * calling load().
5933     */
5934  }, {
5935    key: 'pause',
5936    value: function pause() {
5937      if (this.checkBufferTimeout_) {
5938        _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
5939
5940        this.checkBufferTimeout_ = null;
5941      }
5942    }
5943
5944    /**
5945     * Returns whether the segment loader is fetching additional
5946     * segments when given the opportunity. This property can be
5947     * modified through calls to pause() and load().
5948     */
5949  }, {
5950    key: 'paused',
5951    value: function paused() {
5952      return this.checkBufferTimeout_ === null;
5953    }
5954
5955    /**
5956     * create/set the following mimetype on the SourceBuffer through a
5957     * SourceUpdater
5958     *
5959     * @param {String} mimeType the mime type string to use
5960     */
5961  }, {
5962    key: 'mimeType',
5963    value: function mimeType(_mimeType) {
5964      if (this.mimeType_) {
5965        return;
5966      }
5967
5968      this.mimeType_ = _mimeType;
5969      // if we were unpaused but waiting for a sourceUpdater, start
5970      // buffering now
5971      if (this.state === 'INIT' && this.couldBeginLoading_()) {
5972        this.init_();
5973      }
5974    }
5975
5976    /**
5977     * Delete all the buffered data and reset the SegmentLoader
5978     */
5979  }, {
5980    key: 'resetEverything',
5981    value: function resetEverything() {
5982      this.ended_ = false;
5983      this.resetLoader();
5984      this.remove(0, this.duration_());
5985      this.trigger('reseteverything');
5986    }
5987
5988    /**
5989     * Force the SegmentLoader to resync and start loading around the currentTime instead
5990     * of starting at the end of the buffer
5991     *
5992     * Useful for fast quality changes
5993     */
5994  }, {
5995    key: 'resetLoader',
5996    value: function resetLoader() {
5997      this.fetchAtBuffer_ = false;
5998      this.resyncLoader();
5999    }
6000
6001    /**
6002     * Force the SegmentLoader to restart synchronization and make a conservative guess
6003     * before returning to the simple walk-forward method
6004     */
6005  }, {
6006    key: 'resyncLoader',
6007    value: function resyncLoader() {
6008      this.mediaIndex = null;
6009      this.syncPoint_ = null;
6010      this.abort();
6011    }
6012
6013    /**
6014     * Remove any data in the source buffer between start and end times
6015     * @param {Number} start - the start time of the region to remove from the buffer
6016     * @param {Number} end - the end time of the region to remove from the buffer
6017     */
6018  }, {
6019    key: 'remove',
6020    value: function remove(start, end) {
6021      if (this.sourceUpdater_) {
6022        this.sourceUpdater_.remove(start, end);
6023      }
6024      (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.segmentMetadataTrack_);
6025    }
6026
6027    /**
6028     * (re-)schedule monitorBufferTick_ to run as soon as possible
6029     *
6030     * @private
6031     */
6032  }, {
6033    key: 'monitorBuffer_',
6034    value: function monitorBuffer_() {
6035      if (this.checkBufferTimeout_) {
6036        _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
6037      }
6038
6039      this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), 1);
6040    }
6041
6042    /**
6043     * As long as the SegmentLoader is in the READY state, periodically
6044     * invoke fillBuffer_().
6045     *
6046     * @private
6047     */
6048  }, {
6049    key: 'monitorBufferTick_',
6050    value: function monitorBufferTick_() {
6051      if (this.state === 'READY') {
6052        this.fillBuffer_();
6053      }
6054
6055      if (this.checkBufferTimeout_) {
6056        _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
6057      }
6058
6059      this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), CHECK_BUFFER_DELAY);
6060    }
6061
6062    /**
6063     * fill the buffer with segements unless the sourceBuffers are
6064     * currently updating
6065     *
6066     * Note: this function should only ever be called by monitorBuffer_
6067     * and never directly
6068     *
6069     * @private
6070     */
6071  }, {
6072    key: 'fillBuffer_',
6073    value: function fillBuffer_() {
6074      if (this.sourceUpdater_.updating()) {
6075        return;
6076      }
6077
6078      if (!this.syncPoint_) {
6079        this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
6080      }
6081
6082      // see if we need to begin loading immediately
6083      var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
6084
6085      if (!segmentInfo) {
6086        return;
6087      }
6088
6089      var isEndOfStream = detectEndOfStream(this.playlist_, this.mediaSource_, segmentInfo.mediaIndex);
6090
6091      if (isEndOfStream) {
6092        this.endOfStream();
6093        return;
6094      }
6095
6096      if (segmentInfo.mediaIndex === this.playlist_.segments.length - 1 && this.mediaSource_.readyState === 'ended' && !this.seeking_()) {
6097        return;
6098      }
6099
6100      // We will need to change timestampOffset of the sourceBuffer if either of
6101      // the following conditions are true:
6102      // - The segment.timeline !== this.currentTimeline
6103      //   (we are crossing a discontinuity somehow)
6104      // - The "timestampOffset" for the start of this segment is less than
6105      //   the currently set timestampOffset
6106      if (segmentInfo.timeline !== this.currentTimeline_ || segmentInfo.startOfSegment !== null && segmentInfo.startOfSegment < this.sourceUpdater_.timestampOffset()) {
6107        this.syncController_.reset();
6108        segmentInfo.timestampOffset = segmentInfo.startOfSegment;
6109      }
6110
6111      this.loadSegment_(segmentInfo);
6112    }
6113
6114    /**
6115     * Determines what segment request should be made, given current playback
6116     * state.
6117     *
6118     * @param {TimeRanges} buffered - the state of the buffer
6119     * @param {Object} playlist - the playlist object to fetch segments from
6120     * @param {Number} mediaIndex - the previous mediaIndex fetched or null
6121     * @param {Boolean} hasPlayed - a flag indicating whether we have played or not
6122     * @param {Number} currentTime - the playback position in seconds
6123     * @param {Object} syncPoint - a segment info object that describes the
6124     * @returns {Object} a segment request object that describes the segment to load
6125     */
6126  }, {
6127    key: 'checkBuffer_',
6128    value: function checkBuffer_(buffered, playlist, mediaIndex, hasPlayed, currentTime, syncPoint) {
6129      var lastBufferedEnd = 0;
6130      var startOfSegment = undefined;
6131
6132      if (buffered.length) {
6133        lastBufferedEnd = buffered.end(buffered.length - 1);
6134      }
6135
6136      var bufferedTime = Math.max(0, lastBufferedEnd - currentTime);
6137
6138      if (!playlist.segments.length) {
6139        return null;
6140      }
6141
6142      // if there is plenty of content buffered, and the video has
6143      // been played before relax for awhile
6144      if (bufferedTime >= this.goalBufferLength_()) {
6145        return null;
6146      }
6147
6148      // if the video has not yet played once, and we already have
6149      // one segment downloaded do nothing
6150      if (!hasPlayed && bufferedTime >= 1) {
6151        return null;
6152      }
6153
6154      this.logger_('checkBuffer_', 'mediaIndex:', mediaIndex, 'hasPlayed:', hasPlayed, 'currentTime:', currentTime, 'syncPoint:', syncPoint, 'fetchAtBuffer:', this.fetchAtBuffer_, 'bufferedTime:', bufferedTime);
6155
6156      // When the syncPoint is null, there is no way of determining a good
6157      // conservative segment index to fetch from
6158      // The best thing to do here is to get the kind of sync-point data by
6159      // making a request
6160      if (syncPoint === null) {
6161        mediaIndex = this.getSyncSegmentCandidate_(playlist);
6162        this.logger_('getSync', 'mediaIndex:', mediaIndex);
6163        return this.generateSegmentInfo_(playlist, mediaIndex, null, true);
6164      }
6165
6166      // Under normal playback conditions fetching is a simple walk forward
6167      if (mediaIndex !== null) {
6168        this.logger_('walkForward', 'mediaIndex:', mediaIndex + 1);
6169        var segment = playlist.segments[mediaIndex];
6170
6171        if (segment && segment.end) {
6172          startOfSegment = segment.end;
6173        } else {
6174          startOfSegment = lastBufferedEnd;
6175        }
6176        return this.generateSegmentInfo_(playlist, mediaIndex + 1, startOfSegment, false);
6177      }
6178
6179      // There is a sync-point but the lack of a mediaIndex indicates that
6180      // we need to make a good conservative guess about which segment to
6181      // fetch
6182      if (this.fetchAtBuffer_) {
6183        // Find the segment containing the end of the buffer
6184        var mediaSourceInfo = _playlist2['default'].getMediaInfoForTime(playlist, lastBufferedEnd, syncPoint.segmentIndex, syncPoint.time);
6185
6186        mediaIndex = mediaSourceInfo.mediaIndex;
6187        startOfSegment = mediaSourceInfo.startTime;
6188      } else {
6189        // Find the segment containing currentTime
6190        var mediaSourceInfo = _playlist2['default'].getMediaInfoForTime(playlist, currentTime, syncPoint.segmentIndex, syncPoint.time);
6191
6192        mediaIndex = mediaSourceInfo.mediaIndex;
6193        startOfSegment = mediaSourceInfo.startTime;
6194      }
6195      this.logger_('getMediaIndexForTime', 'mediaIndex:', mediaIndex, 'startOfSegment:', startOfSegment);
6196
6197      return this.generateSegmentInfo_(playlist, mediaIndex, startOfSegment, false);
6198    }
6199
6200    /**
6201     * The segment loader has no recourse except to fetch a segment in the
6202     * current playlist and use the internal timestamps in that segment to
6203     * generate a syncPoint. This function returns a good candidate index
6204     * for that process.
6205     *
6206     * @param {Object} playlist - the playlist object to look for a
6207     * @returns {Number} An index of a segment from the playlist to load
6208     */
6209  }, {
6210    key: 'getSyncSegmentCandidate_',
6211    value: function getSyncSegmentCandidate_(playlist) {
6212      var _this2 = this;
6213
6214      if (this.currentTimeline_ === -1) {
6215        return 0;
6216      }
6217
6218      var segmentIndexArray = playlist.segments.map(function (s, i) {
6219        return {
6220          timeline: s.timeline,
6221          segmentIndex: i
6222        };
6223      }).filter(function (s) {
6224        return s.timeline === _this2.currentTimeline_;
6225      });
6226
6227      if (segmentIndexArray.length) {
6228        return segmentIndexArray[Math.min(segmentIndexArray.length - 1, 1)].segmentIndex;
6229      }
6230
6231      return Math.max(playlist.segments.length - 1, 0);
6232    }
6233  }, {
6234    key: 'generateSegmentInfo_',
6235    value: function generateSegmentInfo_(playlist, mediaIndex, startOfSegment, isSyncRequest) {
6236      if (mediaIndex < 0 || mediaIndex >= playlist.segments.length) {
6237        return null;
6238      }
6239
6240      var segment = playlist.segments[mediaIndex];
6241
6242      return {
6243        requestId: 'segment-loader-' + Math.random(),
6244        // resolve the segment URL relative to the playlist
6245        uri: segment.resolvedUri,
6246        // the segment's mediaIndex at the time it was requested
6247        mediaIndex: mediaIndex,
6248        // whether or not to update the SegmentLoader's state with this
6249        // segment's mediaIndex
6250        isSyncRequest: isSyncRequest,
6251        startOfSegment: startOfSegment,
6252        // the segment's playlist
6253        playlist: playlist,
6254        // unencrypted bytes of the segment
6255        bytes: null,
6256        // when a key is defined for this segment, the encrypted bytes
6257        encryptedBytes: null,
6258        // The target timestampOffset for this segment when we append it
6259        // to the source buffer
6260        timestampOffset: null,
6261        // The timeline that the segment is in
6262        timeline: segment.timeline,
6263        // The expected duration of the segment in seconds
6264        duration: segment.duration,
6265        // retain the segment in case the playlist updates while doing an async process
6266        segment: segment
6267      };
6268    }
6269
6270    /**
6271     * Determines if the network has enough bandwidth to complete the current segment
6272     * request in a timely manner. If not, the request will be aborted early and bandwidth
6273     * updated to trigger a playlist switch.
6274     *
6275     * @param {Object} stats
6276     *        Object containing stats about the request timing and size
6277     * @return {Boolean} True if the request was aborted, false otherwise
6278     * @private
6279     */
6280  }, {
6281    key: 'abortRequestEarly_',
6282    value: function abortRequestEarly_(stats) {
6283      if (this.hls_.tech_.paused() ||
6284      // Don't abort if the current playlist is on the lowestEnabledRendition
6285      // TODO: Replace using timeout with a boolean indicating whether this playlist is
6286      //       the lowestEnabledRendition.
6287      !this.xhrOptions_.timeout ||
6288      // Don't abort if we have no bandwidth information to estimate segment sizes
6289      !this.playlist_.attributes.BANDWIDTH) {
6290        return false;
6291      }
6292
6293      // Wait at least 1 second since the first byte of data has been received before
6294      // using the calculated bandwidth from the progress event to allow the bitrate
6295      // to stabilize
6296      if (Date.now() - (stats.firstBytesReceivedAt || Date.now()) < 1000) {
6297        return false;
6298      }
6299
6300      var currentTime = this.currentTime_();
6301      var measuredBandwidth = stats.bandwidth;
6302      var segmentDuration = this.pendingSegment_.duration;
6303
6304      var requestTimeRemaining = _playlist2['default'].estimateSegmentRequestTime(segmentDuration, measuredBandwidth, this.playlist_, stats.bytesReceived);
6305
6306      // Subtract 1 from the timeUntilRebuffer so we still consider an early abort
6307      // if we are only left with less than 1 second when the request completes.
6308      // A negative timeUntilRebuffering indicates we are already rebuffering
6309      var timeUntilRebuffer = (0, _ranges.timeUntilRebuffer)(this.buffered_(), currentTime, this.hls_.tech_.playbackRate()) - 1;
6310
6311      // Only consider aborting early if the estimated time to finish the download
6312      // is larger than the estimated time until the player runs out of forward buffer
6313      if (requestTimeRemaining <= timeUntilRebuffer) {
6314        return false;
6315      }
6316
6317      var switchCandidate = (0, _playlistSelectors.minRebufferMaxBandwidthSelector)({
6318        master: this.hls_.playlists.master,
6319        currentTime: currentTime,
6320        bandwidth: measuredBandwidth,
6321        duration: this.duration_(),
6322        segmentDuration: segmentDuration,
6323        timeUntilRebuffer: timeUntilRebuffer,
6324        currentTimeline: this.currentTimeline_,
6325        syncController: this.syncController_
6326      });
6327
6328      if (!switchCandidate) {
6329        return;
6330      }
6331
6332      var rebufferingImpact = requestTimeRemaining - timeUntilRebuffer;
6333
6334      var timeSavedBySwitching = rebufferingImpact - switchCandidate.rebufferingImpact;
6335
6336      var minimumTimeSaving = 0.5;
6337
6338      // If we are already rebuffering, increase the amount of variance we add to the
6339      // potential round trip time of the new request so that we are not too aggressive
6340      // with switching to a playlist that might save us a fraction of a second.
6341      if (timeUntilRebuffer <= _ranges.TIME_FUDGE_FACTOR) {
6342        minimumTimeSaving = 1;
6343      }
6344
6345      if (!switchCandidate.playlist || switchCandidate.playlist.uri === this.playlist_.uri || timeSavedBySwitching < minimumTimeSaving) {
6346        return false;
6347      }
6348
6349      // set the bandwidth to that of the desired playlist being sure to scale by
6350      // BANDWIDTH_VARIANCE and add one so the playlist selector does not exclude it
6351      // don't trigger a bandwidthupdate as the bandwidth is artifial
6352      this.bandwidth = switchCandidate.playlist.attributes.BANDWIDTH * _config2['default'].BANDWIDTH_VARIANCE + 1;
6353      this.abort();
6354      this.trigger('earlyabort');
6355      return true;
6356    }
6357
6358    /**
6359     * XHR `progress` event handler
6360     *
6361     * @param {Event}
6362     *        The XHR `progress` event
6363     * @param {Object} simpleSegment
6364     *        A simplified segment object copy
6365     * @private
6366     */
6367  }, {
6368    key: 'handleProgress_',
6369    value: function handleProgress_(event, simpleSegment) {
6370      if (!this.pendingSegment_ || simpleSegment.requestId !== this.pendingSegment_.requestId || this.abortRequestEarly_(simpleSegment.stats)) {
6371        return;
6372      }
6373
6374      this.trigger('progress');
6375    }
6376
6377    /**
6378     * load a specific segment from a request into the buffer
6379     *
6380     * @private
6381     */
6382  }, {
6383    key: 'loadSegment_',
6384    value: function loadSegment_(segmentInfo) {
6385      this.state = 'WAITING';
6386      this.pendingSegment_ = segmentInfo;
6387      this.trimBackBuffer_(segmentInfo);
6388
6389      segmentInfo.abortRequests = (0, _mediaSegmentRequest.mediaSegmentRequest)(this.hls_.xhr, this.xhrOptions_, this.decrypter_, this.createSimplifiedSegmentObj_(segmentInfo),
6390      // progress callback
6391      this.handleProgress_.bind(this), this.segmentRequestFinished_.bind(this));
6392    }
6393
6394    /**
6395     * trim the back buffer so that we don't have too much data
6396     * in the source buffer
6397     *
6398     * @private
6399     *
6400     * @param {Object} segmentInfo - the current segment
6401     */
6402  }, {
6403    key: 'trimBackBuffer_',
6404    value: function trimBackBuffer_(segmentInfo) {
6405      var seekable = this.seekable_();
6406      var currentTime = this.currentTime_();
6407      var removeToTime = 0;
6408
6409      // Chrome has a hard limit of 150MB of
6410      // buffer and a very conservative "garbage collector"
6411      // We manually clear out the old buffer to ensure
6412      // we don't trigger the QuotaExceeded error
6413      // on the source buffer during subsequent appends
6414
6415      // If we have a seekable range use that as the limit for what can be removed safely
6416      // otherwise remove anything older than 30 seconds before the current play head
6417      if (seekable.length && seekable.start(0) > 0 && seekable.start(0) < currentTime) {
6418        removeToTime = seekable.start(0);
6419      } else {
6420        removeToTime = currentTime - 30;
6421      }
6422
6423      if (removeToTime > 0) {
6424        this.remove(0, removeToTime);
6425      }
6426    }
6427
6428    /**
6429     * created a simplified copy of the segment object with just the
6430     * information necessary to perform the XHR and decryption
6431     *
6432     * @private
6433     *
6434     * @param {Object} segmentInfo - the current segment
6435     * @returns {Object}
vendor: 9,589 bytes, lines 6435-6708
6435 a simplified segment object copy
6436     */
6437  }, {
6438    key: 'createSimplifiedSegmentObj_',
6439    value: function createSimplifiedSegmentObj_(segmentInfo) {
6440      var segment = segmentInfo.segment;
6441      var simpleSegment = {
6442        resolvedUri: segment.resolvedUri,
6443        byterange: segment.byterange,
6444        requestId: segmentInfo.requestId
6445      };
6446
6447      if (segment.key) {
6448        // if the media sequence is greater than 2^32, the IV will be incorrect
6449        // assuming 10s segments, that would be about 1300 years
6450        var iv = segment.key.iv || new Uint32Array([0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence]);
6451
6452        simpleSegment.key = {
6453          resolvedUri: segment.key.resolvedUri,
6454          iv: iv
6455        };
6456      }
6457
6458      if (segment.map) {
6459        simpleSegment.map = this.initSegment(segment.map);
6460      }
6461
6462      return simpleSegment;
6463    }
6464
6465    /**
6466     * Handle the callback from the segmentRequest function and set the
6467     * associated SegmentLoader state and errors if necessary
6468     *
6469     * @private
6470     */
6471  }, {
6472    key: 'segmentRequestFinished_',
6473    value: function segmentRequestFinished_(error, simpleSegment) {
6474      // every request counts as a media request even if it has been aborted
6475      // or canceled due to a timeout
6476      this.mediaRequests += 1;
6477
6478      if (simpleSegment.stats) {
6479        this.mediaBytesTransferred += simpleSegment.stats.bytesReceived;
6480        this.mediaTransferDuration += simpleSegment.stats.roundTripTime;
6481      }
6482
6483      // The request was aborted and the SegmentLoader has already been reset
6484      if (!this.pendingSegment_) {
6485        this.mediaRequestsAborted += 1;
6486        return;
6487      }
6488
6489      // the request was aborted and the SegmentLoader has already started
6490      // another request. this can happen when the timeout for an aborted
6491      // request triggers due to a limitation in the XHR library
6492      // do not count this as any sort of request or we risk double-counting
6493      if (simpleSegment.requestId !== this.pendingSegment_.requestId) {
6494        return;
6495      }
6496
6497      // an error occurred from the active pendingSegment_ so reset everything
6498      if (error) {
6499        this.pendingSegment_ = null;
6500        this.state = 'READY';
6501
6502        // the requests were aborted just record the aborted stat and exit
6503        // this is not a true error condition and nothing corrective needs
6504        // to be done
6505        if (error.code === _mediaSegmentRequest.REQUEST_ERRORS.ABORTED) {
6506          this.mediaRequestsAborted += 1;
6507          return;
6508        }
6509
6510        this.pause();
6511
6512        // the error is really just that at least one of the requests timed-out
6513        // set the bandwidth to a very low value and trigger an ABR switch to
6514        // take emergency action
6515        if (error.code === _mediaSegmentRequest.REQUEST_ERRORS.TIMEOUT) {
6516          this.mediaRequestsTimedout += 1;
6517          this.bandwidth = 1;
6518          this.roundTrip = NaN;
6519          this.trigger('bandwidthupdate');
6520          return;
6521        }
6522
6523        // if control-flow has arrived here, then the error is real
6524        // emit an error event to blacklist the current playlist
6525        this.mediaRequestsErrored += 1;
6526        this.error(error);
6527        this.trigger('error');
6528        return;
6529      }
6530
6531      // the response was a success so set any bandwidth stats the request
6532      // generated for ABR purposes
6533      this.bandwidth = simpleSegment.stats.bandwidth;
6534      this.roundTrip = simpleSegment.stats.roundTripTime;
6535
6536      // if this request included an initialization segment, save that data
6537      // to the initSegment cache
6538      if (simpleSegment.map) {
6539        simpleSegment.map = this.initSegment(simpleSegment.map, true);
6540      }
6541
6542      this.processSegmentResponse_(simpleSegment);
6543    }
6544
6545    /**
6546     * Move any important data from the simplified segment object
6547     * back to the real segment object for future phases
6548     *
6549     * @private
6550     */
6551  }, {
6552    key: 'processSegmentResponse_',
6553    value: function processSegmentResponse_(simpleSegment) {
6554      var segmentInfo = this.pendingSegment_;
6555
6556      segmentInfo.bytes = simpleSegment.bytes;
6557      if (simpleSegment.map) {
6558        segmentInfo.segment.map.bytes = simpleSegment.map.bytes;
6559      }
6560
6561      segmentInfo.endOfAllRequests = simpleSegment.endOfAllRequests;
6562      this.handleSegment_();
6563    }
6564
6565    /**
6566     * append a decrypted segement to the SourceBuffer through a SourceUpdater
6567     *
6568     * @private
6569     */
6570  }, {
6571    key: 'handleSegment_',
6572    value: function handleSegment_() {
6573      var _this3 = this;
6574
6575      if (!this.pendingSegment_) {
6576        this.state = 'READY';
6577        return;
6578      }
6579
6580      var segmentInfo = this.pendingSegment_;
6581      var segment = segmentInfo.segment;
6582      var timingInfo = this.syncController_.probeSegmentInfo(segmentInfo);
6583
6584      // When we have our first timing info, determine what media types this loader is
6585      // dealing with. Although we're maintaining extra state, it helps to preserve the
6586      // separation of segment loader from the actual source buffers.
6587      if (typeof this.startingMedia_ === 'undefined' && timingInfo && (
6588      // Guard against cases where we're not getting timing info at all until we are
6589      // certain that all streams will provide it.
6590      timingInfo.containsAudio || timingInfo.containsVideo)) {
6591        this.startingMedia_ = {
6592          containsAudio: timingInfo.containsAudio,
6593          containsVideo: timingInfo.containsVideo
6594        };
6595      }
6596
6597      var illegalMediaSwitchError = illegalMediaSwitch(this.loaderType_, this.startingMedia_, timingInfo);
6598
6599      if (illegalMediaSwitchError) {
6600        this.error({
6601          message: illegalMediaSwitchError
6602        });
6603        this.trigger('error');
6604        return;
6605      }
6606
6607      this.state = 'APPENDING';
6608
6609      if (segmentInfo.isSyncRequest) {
6610        this.trigger('syncinfoupdate');
6611        this.pendingSegment_ = null;
6612        this.state = 'READY';
6613        return;
6614      }
6615
6616      if (segmentInfo.timestampOffset !== null && segmentInfo.timestampOffset !== this.sourceUpdater_.timestampOffset()) {
6617        this.sourceUpdater_.timestampOffset(segmentInfo.timestampOffset);
6618        // fired when a timestamp offset is set in HLS (can also identify discontinuities)
6619        this.trigger('timestampoffset');
6620      }
6621
6622      // if the media initialization segment is changing, append it
6623      // before the content segment
6624      if (segment.map) {
6625        (function () {
6626          var initId = (0, _binUtils.initSegmentId)(segment.map);
6627
6628          if (!_this3.activeInitSegmentId_ || _this3.activeInitSegmentId_ !== initId) {
6629            var initSegment = _this3.initSegment(segment.map);
6630
6631            _this3.sourceUpdater_.appendBuffer(initSegment.bytes, function () {
6632              _this3.activeInitSegmentId_ = initId;
6633            });
6634          }
6635        })();
6636      }
6637
6638      segmentInfo.byteLength = segmentInfo.bytes.byteLength;
6639      if (typeof segment.start === 'number' && typeof segment.end === 'number') {
6640        this.mediaSecondsLoaded += segment.end - segment.start;
6641      } else {
6642        this.mediaSecondsLoaded += segment.duration;
6643      }
6644
6645      this.sourceUpdater_.appendBuffer(segmentInfo.bytes, this.handleUpdateEnd_.bind(this));
6646    }
6647
6648    /**
6649     * callback to run when appendBuffer is finished. detects if we are
6650     * in a good state to do things with the data we got, or if we need
6651     * to wait for more
6652     *
6653     * @private
6654     */
6655  }, {
6656    key: 'handleUpdateEnd_',
6657    value: function handleUpdateEnd_() {
6658      this.logger_('handleUpdateEnd_', 'segmentInfo:', this.pendingSegment_);
6659
6660      if (!this.pendingSegment_) {
6661        this.state = 'READY';
6662        if (!this.paused()) {
6663          this.monitorBuffer_();
6664        }
6665        return;
6666      }
6667
6668      var segmentInfo = this.pendingSegment_;
6669      var segment = segmentInfo.segment;
6670      var isWalkingForward = this.mediaIndex !== null;
6671
6672      this.pendingSegment_ = null;
6673      this.recordThroughput_(segmentInfo);
6674      this.addSegmentMetadataCue_(segmentInfo);
6675
6676      this.state = 'READY';
6677
6678      this.mediaIndex = segmentInfo.mediaIndex;
6679      this.fetchAtBuffer_ = true;
6680      this.currentTimeline_ = segmentInfo.timeline;
6681
6682      // We must update the syncinfo to recalculate the seekable range before
6683      // the following conditional otherwise it may consider this a bad "guess"
6684      // and attempt to resync when the post-update seekable window and live
6685      // point would mean that this was the perfect segment to fetch
6686      this.trigger('syncinfoupdate');
6687
6688      // If we previously appended a segment that ends more than 3 targetDurations before
6689      // the currentTime_ that means that our conservative guess was too conservative.
6690      // In that case, reset the loader state so that we try to use any information gained
6691      // from the previous request to create a new, more accurate, sync-point.
6692      if (segment.end && this.currentTime_() - segment.end > segmentInfo.playlist.targetDuration * 3) {
6693        this.resetEverything();
6694        return;
6695      }
6696
6697      // Don't do a rendition switch unless we have enough time to get a sync segment
6698      // and conservatively guess
6699      if (isWalkingForward) {
6700        this.trigger('bandwidthupdate');
6701      }
6702      this.trigger('progress');
6703
6704      // any time an update finishes and the last segment is in the
6705      // buffer, end the stream. this ensures the "ended" event will
6706      // fire if playback reaches that point.
6707      var isEndOfStream = detectEndOfStream(segmentInfo.playlist, this.mediaSource_, segmentInfo.mediaIndex + 1);
6708
6709      if (isEndOfStream) {
6710        this.endOfStream();
6711      }
6712
6713      if (!this.paused()) {
6714        this.monitorBuffer_();
6715      }
6716    }
6717
6718    /**
6719     * Records the current throughput of the decrypt, transmux, and append
6720     * portion of the semgment pipeline. `throughput.rate` is a the cumulative
6721     * moving average of the throughput. `throughput.count` is the number of
6722     * data points in the average.
6723     *
6724     * @private
6725     * @param {Object} segmentInfo the object returned by loadSegment
6726     */
6727  }, {
6728    key: 'recordThroughput_',
6729    value: function recordThroughput_(segmentInfo) {
6730      var rate = this.throughput.rate;
6731      // Add one to the time to ensure that we don't accidentally attempt to divide
6732      // by zero in the case where the throughput is ridiculously high
6733      var segmentProcessingTime = Date.now() - segmentInfo.endOfAllRequests + 1;
6734      // Multiply by 8000 to convert from bytes/millisecond to bits/second
6735      var segmentProcessingThroughput = Math.floor(segmentInfo.byteLength / segmentProcessingTime * 8 * 1000);
6736
6737      // This is just a cumulative moving average calculation:
6738      //   newAvg = oldAvg + (sample - oldAvg) / (sampleCount + 1)
6739      this.throughput.rate += (segmentProcessingThroughput - rate) / ++this.throughput.count;
6740    }
6741
6742    /**
6743     * A debugging logger noop that is set to console.log only if debugging
6744     * is enabled globally
6745     *
6746     * @private
6747     */
6748  }, {
6749    key: 'logger_',
6750    value: function logger_() {}
6751
6752    /**
6753     * Adds a cue to the segment-metadata track with some metadata information about the
6754     * segment
6755     *
6756     * @private
6757     * @param {Object} segmentInfo
6758     *        the object returned by loadSegment
6759     * @method addSegmentMetadataCue_
6760     */
6761  }, {
6762    key: 'addSegmentMetadataCue_',
6763    value: function addSegmentMetadataCue_(segmentInfo) {
6764      if (!this.segmentMetadataTrack_) {
6765        return;
6766      }
6767
6768      var segment = segmentInfo.segment;
6769      var start = segment.start;
6770      var end = segment.end;
6771
6772      // Do not try adding the cue if the start and end times are invalid.
6773      if (!finite(start) || !finite(end)) {
6774        return;
6775      }
6776
6777      (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.segmentMetadataTrack_);
6778
6779      var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue;
6780      var value = {
6781        uri: segmentInfo.uri,
6782        timeline: segmentInfo.timeline,
6783        playlist: segmentInfo.playlist.uri,
6784        start: start,
6785        end: end
6786      };
6787      var data = JSON.stringify(value);
6788      var cue = new Cue(start, end, data);
6789
6790      // Attach the metadata to the value property of the cue to keep consistency between
6791      // the differences of WebKitDataCue in safari and VTTCue in other browsers
6792      cue.value = value;
6793
6794      this.segmentMetadataTrack_.addCue(cue);
6795    }
6796  }]);
6797
6798  return SegmentLoader;
6799})(_videoJs2['default'].EventTarget);
6800
6801exports['default'] = SegmentLoader;
6802}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
6803},{"./bin-utils":2,"./config":3,"./media-segment-request":7,"./playlist":11,"./playlist-selectors":10,"./ranges":12,"./source-updater":17,"global/window":32,"videojs-contrib-media-sources/es5/remove-cues-from-track.js":72}],17:[function(require,module,exports){
6804(function (global){
6805/**
6806 * @file source-updater.js
6807 */
6808'use strict';
6809
6810Object.defineProperty(exports, '__esModule', {
6811  value: true
6812});
6813
6814var _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; }; })();
6815
6816function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
6817
6818function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
6819
6820var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
6821
6822var _videoJs2 = _interopRequireDefault(_videoJs);
6823
6824var noop = function noop() {};
6825
6826/**
6827 * A queue of callbacks to be serialized and applied when a
6828 * MediaSource and its associated SourceBuffers are not in the
6829 * updating state. It is used by the segment loader to update the
6830 * underlying SourceBuffers when new data is loaded, for instance.
6831 *
6832 * @class SourceUpdater
6833 * @param {MediaSource} mediaSource the MediaSource to create the
6834 * SourceBuffer from
6835 * @param {String} mimeType the desired MIME type of the underlying
6836 * SourceBuffer
6837 */
6838
6839var SourceUpdater = (function () {
6840  function SourceUpdater(mediaSource, mimeType) {
6841    var _this = this;
6842
6843    _classCallCheck(this, SourceUpdater);
6844
6845    var createSourceBuffer = function createSourceBuffer() {
6846      _this.sourceBuffer_ = mediaSource.addSourceBuffer(mimeType);
6847
6848      // run completion handlers and process callbacks as updateend
6849      // events fire
6850      _this.onUpdateendCallback_ = function () {
6851        var pendingCallback = _this.pendingCallback_;
6852
6853        _this.pendingCallback_ = null;
6854
6855        if (pendingCallback) {
6856          pendingCallback();
6857        }
6858
6859        _this.runCallback_();
6860      };
6861
6862      _this.sourceBuffer_.addEventListener('updateend', _this.onUpdateendCallback_);
6863
6864      _this.runCallback_();
6865    };
6866
6867    this.callbacks_ = [];
6868    this.pendingCallback_ = null;
6869    this.timestampOffset_ = 0;
6870    this.mediaSource = mediaSource;
6871    this.processedAppend_ = false;
6872
6873    if (mediaSource.readyState === 'closed') {
6874      mediaSource.addEventListener('sourceopen', createSourceBuffer);
6875    } else {
6876      createSourceBuffer();
6877    }
6878  }
6879
6880  /**
6881   * Aborts the current segment and resets the segment parser.
6882   *
6883   * @param {Function} done function to call when done
6884   * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
6885   */
6886
6887  _createClass(SourceUpdater, [{
6888    key: 'abort',
6889    value: function abort(done) {
6890      var _this2 = this;
6891
6892      if (this.processedAppend_) {
6893        this.queueCallback_(function () {
6894          _this2.sourceBuffer_.abort();
6895        }, done);
6896      }
6897    }
6898
6899    /**
6900     * Queue an update to append an ArrayBuffer.
6901     *
6902     * @param {ArrayBuffer} bytes
6903     * @param {Function} done the function to call when done
6904     * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
6905     */
6906  }, {
6907    key: 'appendBuffer',
6908    value: function appendBuffer(bytes, done) {
6909      var _this3 = this;
6910
6911      this.processedAppend_ = true;
6912
6913      this.queueCallback_(function () {
6914        _this3.sourceBuffer_.appendBuffer(bytes);
6915      }, done);
6916    }
6917
6918    /**
6919     * Indicates what TimeRanges are buffered in the managed SourceBuffer.
6920     *
6921     * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
6922     */
6923  }, {
6924    key: 'buffered',
6925    value: function buffered() {
6926      if (!this.sourceBuffer_) {
6927        return _videoJs2['default'].createTimeRanges();
6928      }
6929      return this.sourceBuffer_.buffered;
6930    }
6931
6932    /**
6933     * Queue an update to remove a time range from the buffer.
6934     *
6935     * @param {Number} start where to start the removal
6936     * @param {Number} end where to end the removal
6937     * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
6938     */
6939  }, {
6940    key: 'remove',
6941    value: function remove(start, end) {
6942      var _this4 = this;
6943
6944      if (this.processedAppend_) {
6945        this.queueCallback_(function () {
6946          _this4.sourceBuffer_.remove(start, end);
6947        }, noop);
6948      }
6949    }
6950
6951    /**
6952     * Whether the underlying sourceBuffer is updating or not
6953     *
6954     * @return {Boolean} the updating status of the SourceBuffer
6955     */
6956  }, {
6957    key: 'updating',
6958    value: function updating() {
6959      return !this.sourceBuffer_ || this.sourceBuffer_.updating || this.pendingCallback_;
6960    }
6961
6962    /**
6963     * Set/get the timestampoffset on the SourceBuffer
6964     *
6965     * @return {Number} the timestamp offset
6966     */
6967  }, {
6968    key: 'timestampOffset',
6969    value: function timestampOffset(offset) {
6970      var _this5 = this;
6971
6972      if (typeof offset !== 'undefined') {
6973        this.queueCallback_(function () {
6974          _this5.sourceBuffer_.timestampOffset = offset;
6975        });
6976        this.timestampOffset_ = offset;
6977      }
6978      return this.timestampOffset_;
6979    }
6980
6981    /**
6982     * Queue a callback to run
6983     */
6984  }, {
6985    key: 'queueCallback_',
6986    value: function queueCallback_(callback, done) {
6987      this.callbacks_.push([callback.bind(this), done]);
6988      this.runCallback_();
6989    }
6990
6991    /**
6992     * Run a queued callback
6993     */
6994  }, {
6995    key: 'runCallback_',
6996    value: function runCallback_() {
6997      var callbacks = undefined;
6998
6999      if (!this.updating() && this.callbacks_.length) {
7000        callbacks = this.callbacks_.shift();
7001        this.pendingCallback_ = callbacks[1];
7002        callbacks[0]();
7003      }
7004    }
7005
7006    /**
7007     * dispose of the source updater and the underlying sourceBuffer
7008     */
7009  }, {
7010    key: 'dispose',
7011    value: function dispose() {
7012      this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
7013      if (this.sourceBuffer_ && this.mediaSource.readyState === 'open') {
7014        this.sourceBuffer_.abort();
7015      }
7016    }
7017  }]);
7018
7019  return SourceUpdater;
7020})();
7021
7022exports['default'] = SourceUpdater;
7023module.exports = exports['default'];
7024}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
7025},{}],18:[function(require,module,exports){
7026(function (global){
7027/**
7028 * @file sync-controller.js
7029 */
7030
7031'use strict';
7032
7033Object.defineProperty(exports, '__esModule', {
7034  value: true
7035});
7036
7037var _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; }; })();
7038
7039var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _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 { _x2 = parent; _x3 = property; _x4 = 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); } } };
7040
7041function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7042
7043function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
7044
7045function _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, { c
7045onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
7046
7047var _muxJsLibMp4Probe = require('mux.js/lib/mp4/probe');
7048
7049var _muxJsLibMp4Probe2 = _interopRequireDefault(_muxJsLibMp4Probe);
7050
7051var _muxJsLibToolsTsInspectorJs = require('mux.js/lib/tools/ts-inspector.js');
7052
7053var _playlist = require('./playlist');
7054
7055var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
7056
7057var _videoJs2 = _interopRequireDefault(_videoJs);
7058
7059var syncPointStrategies = [
7060// Stategy "VOD": Handle the VOD-case where the sync-point is *always*
7061//                the equivalence display-time 0 === segment-index 0
7062{
7063  name: 'VOD',
7064  run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
7065    if (duration !== Infinity) {
7066      var syncPoint = {
7067        time: 0,
7068        segmentIndex: 0
7069      };
7070
7071      return syncPoint;
7072    }
7073    return null;
7074  }
7075},
7076// Stategy "ProgramDateTime": We have a program-date-time tag in this playlist
7077{
7078  name: 'ProgramDateTime',
7079  run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
7080    if (syncController.datetimeToDisplayTime && playlist.dateTimeObject) {
7081      var playlistTime = playlist.dateTimeObject.getTime() / 1000;
7082      var playlistStart = playlistTime + syncController.datetimeToDisplayTime;
7083      var syncPoint = {
7084        time: playlistStart,
7085        segmentIndex: 0
7086      };
7087
7088      return syncPoint;
7089    }
7090    return null;
7091  }
7092},
7093// Stategy "Segment": We have a known time mapping for a timeline and a
7094//                    segment in the current timeline with timing data
7095{
7096  name: 'Segment',
7097  run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
7098    var segments = playlist.segments || [];
7099    var syncPoint = null;
7100    var lastDistance = null;
7101
7102    currentTime = currentTime || 0;
7103
7104    for (var i = 0; i < segments.length; i++) {
7105      var segment = segments[i];
7106
7107      if (segment.timeline === currentTimeline && typeof segment.start !== 'undefined') {
7108        var distance = Math.abs(currentTime - segment.start);
7109
7110        // Once the distance begins to increase, we have passed
7111        // currentTime and can stop looking for better candidates
7112        if (lastDistance !== null && lastDistance < distance) {
7113          break;
7114        }
7115
7116        if (!syncPoint || lastDistance === null || lastDistance >= distance) {
7117          lastDistance = distance;
7118          syncPoint = {
7119            time: segment.start,
7120            segmentIndex: i
7121          };
7122        }
7123      }
7124    }
7125    return syncPoint;
7126  }
7127},
7128// Stategy "Discontinuity": We have a discontinuity with a known
7129//                          display-time
7130{
7131  name: 'Discontinuity',
7132  run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
7133    var syncPoint = null;
7134
7135    currentTime = currentTime || 0;
7136
7137    if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
7138      var lastDistance = null;
7139
7140      for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
7141        var segmentIndex = playlist.discontinuityStarts[i];
7142        var discontinuity = playlist.discontinuitySequence + i + 1;
7143        var discontinuitySync = syncController.discontinuities[discontinuity];
7144
7145        if (discontinuitySync) {
7146          var distance = Math.abs(currentTime - discontinuitySync.time);
7147
7148          // Once the distance begins to increase, we have passed
7149          // currentTime and can stop looking for better candidates
7150          if (lastDistance !== null && lastDistance < distance) {
7151            break;
7152          }
7153
7154          if (!syncPoint || lastDistance === null || lastDistance >= distance) {
7155            lastDistance = distance;
7156            syncPoint = {
7157              time: discontinuitySync.time,
7158              segmentIndex: segmentIndex
7159            };
7160          }
7161        }
7162      }
7163    }
7164    return syncPoint;
7165  }
7166},
7167// Stategy "Playlist": We have a playlist with a known mapping of
7168//                     segment index to display time
7169{
7170  name: 'Playlist',
7171  run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
7172    if (playlist.syncInfo) {
7173      var syncPoint = {
7174        time: playlist.syncInfo.time,
7175        segmentIndex: playlist.syncInfo.mediaSequence - playlist.mediaSequence
7176      };
7177
7178      return syncPoint;
7179    }
7180    return null;
7181  }
7182}];
7183
7184exports.syncPointStrategies = syncPointStrategies;
7185
7186var SyncController = (function (_videojs$EventTarget) {
7187  _inherits(SyncController, _videojs$EventTarget);
7188
7189  function SyncController() {
7190    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
7191
7192    _classCallCheck(this, SyncController);
7193
7194    _get(Object.getPrototypeOf(SyncController.prototype), 'constructor', this).call(this);
7195    // Segment Loader state variables...
7196    // ...for synching across variants
7197    this.inspectCache_ = undefined;
7198
7199    // ...for synching across variants
7200    this.timelines = [];
7201    this.discontinuities = [];
7202    this.datetimeToDisplayTime = null;
7203
7204    if (options.debug) {
7205      this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'sync-controller ->');
7206    }
7207  }
7208
7209  /**
7210   * Find a sync-point for the playlist specified
7211   *
7212   * A sync-point is defined as a known mapping from display-time to
7213   * a segment-index in the current playlist.
7214   *
7215   * @param {Playlist} playlist
7216   *        The playlist that needs a sync-point
7217   * @param {Number} duration
7218   *        Duration of the MediaSource (Infinite if playing a live source)
7219   * @param {Number} currentTimeline
7220   *        The last timeline from which a segment was loaded
7221   * @returns {Object}
7222   *          A sync-point object
7223   */
7224
7225  _createClass(SyncController, [{
7226    key: 'getSyncPoint',
7227    value: function getSyncPoint(playlist, duration, currentTimeline, currentTime) {
7228      var syncPoints = this.runStrategies_(playlist, duration, currentTimeline, currentTime);
7229
7230      if (!syncPoints.length) {
7231        // Signal that we need to attempt to get a sync-point manually
7232        // by fetching a segment in the playlist and constructing
7233        // a sync-point from that information
7234        return null;
7235      }
7236
7237      // Now find the sync-point that is closest to the currentTime because
7238      // that should result in the most accurate guess about which segment
7239      // to fetch
7240      return this.selectSyncPoint_(syncPoints, { key: 'time', value: currentTime });
7241    }
7242
7243    /**
7244     * Calculate the amount of time that has expired off the playlist during playback
7245     *
7246     * @param {Playlist} playlist
7247     *        Playlist object to calculate expired from
7248     * @param {Number} duration
7249     *        Duration of the MediaSource (Infinity if playling a live source)
7250     * @returns {Number|null}
7251     *          The amount of time that has expired off the playlist during playback. Null
7252     *          if no sync-points for the playlist can be found.
7253     */
7254  }, {
7255    key: 'getExpiredTime',
7256    value: function getExpiredTime(playlist, duration) {
7257      if (!playlist || !playlist.segments) {
7258        return null;
7259      }
7260
7261      var syncPoints = this.runStrategies_(playlist, duration, playlist.discontinuitySequence, 0);
7262
vendor: 10,174 bytes, lines 7263-7554
7263      // Without sync-points, there is not enough information to determine the expired time
7264      if (!syncPoints.length) {
7265        return null;
7266      }
7267
7268      var syncPoint = this.selectSyncPoint_(syncPoints, {
7269        key: 'segmentIndex',
7270        value: 0
7271      });
7272
7273      // If the sync-point is beyond the start of the playlist, we want to subtract the
7274      // duration from index 0 to syncPoint.segmentIndex instead of adding.
7275      if (syncPoint.segmentIndex > 0) {
7276        syncPoint.time *= -1;
7277      }
7278
7279      return Math.abs(syncPoint.time + (0, _playlist.sumDurations)(playlist, syncPoint.segmentIndex, 0));
7280    }
7281
7282    /**
7283     * Runs each sync-point strategy and returns a list of sync-points returned by the
7284     * strategies
7285     *
7286     * @private
7287     * @param {Playlist} playlist
7288     *        The playlist that needs a sync-point
7289     * @param {Number} duration
7290     *        Duration of the MediaSource (Infinity if playing a live source)
7291     * @param {Number} currentTimeline
7292     *        The last timeline from which a segment was loaded
7293     * @returns {Array}
7294     *          A list of sync-point objects
7295     */
7296  }, {
7297    key: 'runStrategies_',
7298    value: function runStrategies_(playlist, duration, currentTimeline, currentTime) {
7299      var syncPoints = [];
7300
7301      // Try to find a sync-point in by utilizing various strategies...
7302      for (var i = 0; i < syncPointStrategies.length; i++) {
7303        var strategy = syncPointStrategies[i];
7304        var syncPoint = strategy.run(this, playlist, duration, currentTimeline, currentTime);
7305
7306        if (syncPoint) {
7307          syncPoint.strategy = strategy.name;
7308          syncPoints.push({
7309            strategy: strategy.name,
7310            syncPoint: syncPoint
7311          });
7312          this.logger_('syncPoint found via <' + strategy.name + '>:', syncPoint);
7313        }
7314      }
7315
7316      return syncPoints;
7317    }
7318
7319    /**
7320     * Selects the sync-point nearest the specified target
7321     *
7322     * @private
7323     * @param {Array} syncPoints
7324     *        List of sync-points to select from
7325     * @param {Object} target
7326     *        Object specifying the property and value we are targeting
7327     * @param {String} target.key
7328     *        Specifies the property to target. Must be either 'time' or 'segmentIndex'
7329     * @param {Number} target.value
7330     *        The value to target for the specified key.
7331     * @returns {Object}
7332     *          The sync-point nearest the target
7333     */
7334  }, {
7335    key: 'selectSyncPoint_',
7336    value: function selectSyncPoint_(syncPoints, target) {
7337      var bestSyncPoint = syncPoints[0].syncPoint;
7338      var bestDistance = Math.abs(syncPoints[0].syncPoint[target.key] - target.value);
7339      var bestStrategy = syncPoints[0].strategy;
7340
7341      for (var i = 1; i < syncPoints.length; i++) {
7342        var newDistance = Math.abs(syncPoints[i].syncPoint[target.key] - target.value);
7343
7344        if (newDistance < bestDistance) {
7345          bestDistance = newDistance;
7346          bestSyncPoint = syncPoints[i].syncPoint;
7347          bestStrategy = syncPoints[i].strategy;
7348        }
7349      }
7350
7351      this.logger_('syncPoint with strategy <' + bestStrategy + '> chosen: ', bestSyncPoint);
7352      return bestSyncPoint;
7353    }
7354
7355    /**
7356     * Save any meta-data present on the segments when segments leave
7357     * the live window to the playlist to allow for synchronization at the
7358     * playlist level later.
7359     *
7360     * @param {Playlist} oldPlaylist - The previous active playlist
7361     * @param {Playlist} newPlaylist - The updated and most current playlist
7362     */
7363  }, {
7364    key: 'saveExpiredSegmentInfo',
7365    value: function saveExpiredSegmentInfo(oldPlaylist, newPlaylist) {
7366      var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
7367
7368      // When a segment expires from the playlist and it has a start time
7369      // save that information as a possible sync-point reference in future
7370      for (var i = mediaSequenceDiff - 1; i >= 0; i--) {
7371        var lastRemovedSegment = oldPlaylist.segments[i];
7372
7373        if (lastRemovedSegment && typeof lastRemovedSegment.start !== 'undefined') {
7374          newPlaylist.syncInfo = {
7375            mediaSequence: oldPlaylist.mediaSequence + i,
7376            time: lastRemovedSegment.start
7377          };
7378          this.logger_('playlist sync:', newPlaylist.syncInfo);
7379          this.trigger('syncinfoupdate');
7380          break;
7381        }
7382      }
7383    }
7384
7385    /**
7386     * Save the mapping from playlist's ProgramDateTime to display. This should
7387     * only ever happen once at the start of playback.
7388     *
7389     * @param {Playlist} playlist - The currently active playlist
7390     */
7391  }, {
7392    key: 'setDateTimeMapping',
7393    value: function setDateTimeMapping(playlist) {
7394      if (!this.datetimeToDisplayTime && playlist.dateTimeObject) {
7395        var playlistTimestamp = playlist.dateTimeObject.getTime() / 1000;
7396
7397        this.datetimeToDisplayTime = -playlistTimestamp;
7398      }
7399    }
7400
7401    /**
7402     * Reset the state of the inspection cache when we do a rendition
7403     * switch
7404     */
7405  }, {
7406    key: 'reset',
7407    value: function reset() {
7408      this.inspectCache_ = undefined;
7409    }
7410
7411    /**
7412     * Probe or inspect a fmp4 or an mpeg2-ts segment to determine the start
7413     * and end of the segment in it's internal "media time". Used to generate
7414     * mappings from that internal "media time" to the display time that is
7415     * shown on the player.
7416     *
7417     * @param {SegmentInfo} segmentInfo - The current active request information
7418     */
7419  }, {
7420    key: 'probeSegmentInfo',
7421    value: function probeSegmentInfo(segmentInfo) {
7422      var segment = segmentInfo.segment;
7423      var playlist = segmentInfo.playlist;
7424      var timingInfo = undefined;
7425
7426      if (segment.map) {
7427        timingInfo = this.probeMp4Segment_(segmentInfo);
7428      } else {
7429        timingInfo = this.probeTsSegment_(segmentInfo);
7430      }
7431
7432      if (timingInfo) {
7433        if (this.calculateSegmentTimeMapping_(segmentInfo, timingInfo)) {
7434          this.saveDiscontinuitySyncInfo_(segmentInfo);
7435
7436          // If the playlist does not have sync information yet, record that information
7437          // now with segment timing information
7438          if (!playlist.syncInfo) {
7439            playlist.syncInfo = {
7440              mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
7441              time: segment.start
7442            };
7443          }
7444        }
7445      }
7446
7447      return timingInfo;
7448    }
7449
7450    /**
7451     * Probe an fmp4 or an mpeg2-ts segment to determine the start of the segment
7452     * in it's internal "media time".
7453     *
7454     * @private
7455     * @param {SegmentInfo} segmentInfo - The current active request information
7456     * @return {object} The start and end time of the current segment in "media time"
7457     */
7458  }, {
7459    key: 'probeMp4Segment_',
7460    value: function probeMp4Segment_(segmentInfo) {
7461      var segment = segmentInfo.segment;
7462      var timescales = _muxJsLibMp4Probe2['default'].timescale(segment.map.bytes);
7463      var startTime = _muxJsLibMp4Probe2['default'].startTime(timescales, segmentInfo.bytes);
7464
7465      if (segmentInfo.timestampOffset !== null) {
7466        segmentInfo.timestampOffset -= startTime;
7467      }
7468
7469      return {
7470        start: startTime,
7471        end: startTime + segment.duration
7472      };
7473    }
7474
7475    /**
7476     * Probe an mpeg2-ts segment to determine the start and end of the segment
7477     * in it's internal "media time".
7478     *
7479     * @private
7480     * @param {SegmentInfo} segmentInfo - The current active request information
7481     * @return {object} The start and end time of the current segment in "media time"
7482     */
7483  }, {
7484    key: 'probeTsSegment_',
7485    value: function probeTsSegment_(segmentInfo) {
7486      var timeInfo = (0, _muxJsLibToolsTsInspectorJs.inspect)(segmentInfo.bytes, this.inspectCache_);
7487      var segmentStartTime = undefined;
7488      var segmentEndTime = undefined;
7489
7490      if (!timeInfo) {
7491        return null;
7492      }
7493
7494      if (timeInfo.video && timeInfo.video.length === 2) {
7495        this.inspectCache_ = timeInfo.video[1].dts;
7496        segmentStartTime = timeInfo.video[0].dtsTime;
7497        segmentEndTime = timeInfo.video[1].dtsTime;
7498      } else if (timeInfo.audio && timeInfo.audio.length === 2) {
7499        this.inspectCache_ = timeInfo.audio[1].dts;
7500        segmentStartTime = timeInfo.audio[0].dtsTime;
7501        segmentEndTime = timeInfo.audio[1].dtsTime;
7502      }
7503
7504      return {
7505        start: segmentStartTime,
7506        end: segmentEndTime,
7507        containsVideo: timeInfo.video && timeInfo.video.length === 2,
7508        containsAudio: timeInfo.audio && timeInfo.audio.length === 2
7509      };
7510    }
7511  }, {
7512    key: 'timestampOffsetForTimeline',
7513    value: function timestampOffsetForTimeline(timeline) {
7514      if (typeof this.timelines[timeline] === 'undefined') {
7515        return null;
7516      }
7517      return this.timelines[timeline].time;
7518    }
7519
7520    /**
7521     * Use the "media time" for a segment to generate a mapping to "display time" and
7522     * save that display time to the segment.
7523     *
7524     * @private
7525     * @param {SegmentInfo} segmentInfo
7526     *        The current active request information
7527     * @param {object} timingInfo
7528     *        The start and end time of the current segment in "media time"
7529     * @returns {Boolean}
7530     *          Returns false if segment time mapping could not be calculated
7531     */
7532  }, {
7533    key: 'calculateSegmentTimeMapping_',
7534    value: function calculateSegmentTimeMapping_(segmentInfo, timingInfo) {
7535      var segment = segmentInfo.segment;
7536      var mappingObj = this.timelines[segmentInfo.timeline];
7537
7538      if (segmentInfo.timestampOffset !== null) {
7539        this.logger_('tsO:', segmentInfo.timestampOffset);
7540
7541        mappingObj = {
7542          time: segmentInfo.startOfSegment,
7543          mapping: segmentInfo.startOfSegment - timingInfo.start
7544        };
7545        this.timelines[segmentInfo.timeline] = mappingObj;
7546        this.trigger('timestampoffset');
7547
7548        segment.start = segmentInfo.startOfSegment;
7549        segment.end = timingInfo.end + mappingObj.mapping;
7550      } else if (mappingObj) {
7551        segment.start = timingInfo.start + mappingObj.mapping;
7552        segment.end = timingInfo.end + mappingObj.mapping;
7553      } else {
7554        return false;
7555      }
7556
7557      return true;
7558    }
7559
7560    /**
7561     * Each time we have discontinuity in the playlist, attempt to calculate the location
7562     * in display of the start of the discontinuity and save that. We also save an accuracy
7563     * value so that we save values with the most accuracy (closest to 0.)
7564     *
7565     * @private
7566     * @param {SegmentInfo} segmentInfo - The current active request information
7567     */
7568  }, {
7569    key: 'saveDiscontinuitySyncInfo_',
7570    value: function saveDiscontinuitySyncInfo_(segmentInfo) {
7571      var playlist = segmentInfo.playlist;
7572      var segment = segmentInfo.segment;
7573
7574      // If the current segment is a discontinuity then we know exactly where
7575      // the start of the range and it's accuracy is 0 (greater accuracy values
7576      // mean more approximation)
7577      if (segment.discontinuity) {
7578        this.discontinuities[segment.timeline] = {
7579          time: segment.start,
7580          accuracy: 0
7581        };
7582      } else if (playlist.discontinuityStarts.length) {
7583        // Search for future discontinuities that we can provide better timing
7584        // information for and save that information for sync purposes
7585        for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
7586          var segmentIndex = playlist.discontinuityStarts[i];
7587          var discontinuity = playlist.discontinuitySequence + i + 1;
7588          var mediaIndexDiff = segmentIndex - segmentInfo.mediaIndex;
7589          var accuracy = Math.abs(mediaIndexDiff);
7590
7591          if (!this.discontinuities[discontinuity] || this.discontinuities[discontinuity].accuracy > accuracy) {
7592            var time = undefined;
7593
7594            if (mediaIndexDiff < 0) {
7595              time = segment.start - (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex, segmentIndex);
7596            } else {
7597              time = segment.end + (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex + 1, segmentIndex);
7598            }
7599
7600            this.discontinuities[discontinuity] = {
7601              time: time,
7602              accuracy: accuracy
7603            };
7604          }
7605        }
7606      }
7607    }
7608
7609    /**
7610     * A debugging logger noop that is set to console.log only if debugging
7611     * is enabled globally
7612     *
7613     * @private
7614     */
7615  }, {
7616    key: 'logger_',
7617    value: function logger_() {}
7618  }]);
7619
7620  return SyncController;
7621})(_videoJs2['default'].EventTarget);
7622
7623exports['default'] = SyncController;
7624}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
7625},{"./playlist":11,"mux.js/lib/mp4/probe":57,"mux.js/lib/tools/ts-inspector.js":59}],19:[function(require,module,exports){
7626
7627/**
7628 * @file - codecs.js - Handles tasks regarding codec strings such as translating them to
7629 * codec strings, or translating codec strings into objects that can be examined.
7630 */
7631
7632/**
7633 * Parses a codec string to retrieve the number of codecs specified,
7634 * the video codec and object type indicator, and the audio profile.
7635 */
7636
7637'use strict';
7638
7639Object.defineProperty(exports, '__esModule', {
7640  value: true
7641});
7642var parseCodecs = function parseCodecs() {
7643  var codecs = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
7644
7645  var result = {
7646    codecCount: 0
7647  };
7648  var parsed = undefined;
7649
7650  result.codecCount = codecs.split(',').length;
7651  result.codecCount = result.codecCount || 2;
7652
7653  // parse the video codec
7654  parsed = /(^|\s|,)+(avc1)([^ ,]*)/i.exec(codecs);
7655  if (parsed) {
7656    result.videoCodec = parsed[2];
7657    result.videoObjectTypeIndicator = parsed[3];
7658  }
7659
7660  // parse the last field of the audio codec
7661  result.audioProfile = /(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(codecs);
7662  result.audioProfile = result.audioProfile && result.audioProfile[2];
7663
7664  return result;
7665};
7666exports.parseCodecs = parseCodecs;
7667},{}],20:[function(require,module,exports){
7668(function (global){
7669/**
7670 * @file vtt-segment-loader.js
7671 */
7672'use strict';
7673
7674Object.defineProperty(exports, '__esModule', {
7675  value: true
7676});
7677
7678var _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; }; })();
7679
7680var _get = function get(_x3, _x4, _x5) { var _again = true; _function: while (_again) { var object = _x3, property = _x4, receiver = _x5; _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 { _x3 = parent; _x4 = property; _x5 = 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); } } };
7681
7682function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7683
7684function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
7685
7686function _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, { c
7686onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
7687
7688var _segmentLoader = require('./segment-loader');
7689
7690var _segmentLoader2 = _interopRequireDefault(_segmentLoader);
7691
7692var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
7693
7694var _videoJs2 = _interopRequireDefault(_videoJs);
7695
7696var _globalWindow = require('global/window');
7697
7698var _globalWindow2 = _interopRequireDefault(_globalWindow);
7699
7700var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs = require('videojs-contrib-media-sources/es5/remove-cues-from-track.js');
7701
7702var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2 = _interopRequireDefault(_videojsContribMediaSourcesEs5RemoveCuesFromTrackJs);
7703
7704var _binUtils = require('./bin-utils');
7705
7706var VTT_LINE_TERMINATORS = new Uint8Array('\n\n'.split('').map(function (char) {
7707  return char.charCodeAt(0);
7708}));
7709
7710var uintToString = function uintToString(uintArray) {
7711  return String.fromCharCode.apply(null, uintArray);
7712};
7713
7714/**
7715 * An object that manages segment loading and appending.
7716 *
7717 * @class VTTSegmentLoader
7718 * @param {Object} options required and optional options
7719 * @extends videojs.EventTarget
7720 */
7721
7722var VTTSegmentLoader = (function (_SegmentLoader) {
7723  _inherits(VTTSegmentLoader, _SegmentLoader);
7724
7725  function VTTSegmentLoader(settings) {
7726    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
7727
7728    _classCallCheck(this, VTTSegmentLoader);
7729
7730    _get(Object.getPrototypeOf(VTTSegmentLoader.prototype), 'constructor', this).call(this, settings, options);
7731
7732    // SegmentLoader requires a MediaSource be specified or it will throw an error;
7733    // however, VTTSegmentLoader has no need of a media source, so delete the reference
7734    this.mediaSource_ = null;
7735
7736    this.subtitlesTrack_ = null;
7737  }
7738
7739  /**
7740   * Indicates which time ranges are buffered
7741   *
7742   * @return {TimeRange}
7743   *         TimeRange object representing the current buffered ranges
7744   */
7745
7746  _createClass(VTTSegmentLoader, [{
7747    key: 'buffered_',
7748    value: function buffered_() {
7749      if (!this.subtitlesTrack_ || !this.subtitlesTrack_.cues.length) {
7750        return _videoJs2['default'].createTimeRanges();
7751      }
7752
7753      var cues = this.subtitlesTrack_.cues;
7754      var start = cues[0].startTime;
7755      var end = cues[cues.length - 1].startTime;
7756
7757      return _videoJs2['default'].createTimeRanges([[start, end]]);
7758    }
7759
7760    /**
7761     * Gets and sets init segment for the provided map
7762     *
7763     * @param {Object} map
7764     *        The map object representing the init segment to get or set
7765     * @param {Boolean=} set
7766     *        If true, the init segment for the provided map should be saved
7767     * @return {Object}
7768     *         map object for desired init segment
7769     */
7770  }, {
7771    key: 'initSegment',
7772    value: function initSegment(map) {
7773      var set = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
7774
7775      if (!map) {
7776        return null;
7777      }
7778
7779      var id = (0, _binUtils.initSegmentId)(map);
7780      var storedMap = this.initSegments_[id];
7781
7782      if (set && !storedMap && map.bytes) {
7783        // append WebVTT line terminators to the media initialization segment if it exists
7784        // to follow the WebVTT spec (https://w3c.github.io/webvtt/#file-structure) that
7785        // requires two or more WebVTT line terminators between the WebVTT header and the
7786        // rest of the file
7787        var combinedByteLength = VTT_LINE_TERMINATORS.byteLength + map.bytes.byteLength;
7788        var combinedSegment = new Uint8Array(combinedByteLength);
7789
7790        combinedSegment.set(map.bytes);
7791        combinedSegment.set(VTT_LINE_TERMINATORS, map.bytes.byteLength);
7792
7793        this.initSegments_[id] = storedMap = {
7794          resolvedUri: map.resolvedUri,
7795          byterange: map.byterange,
7796          bytes: combinedSegment
7797        };
7798      }
7799
7800      return storedMap || map;
7801    }
7802
7803    /**
7804     * Returns true if all configuration required for loading is present, otherwise false.
7805     *
7806     * @return {Boolean} True if the all configuration is ready for loading
7807     * @private
7808     */
7809  }, {
7810    key: 'couldBeginLoading_',
7811    value: function couldBeginLoading_() {
7812      return this.playlist_ && this.subtitlesTrack_ && !this.paused();
7813    }
7814
7815    /**
7816     * Once all the starting parameters have been specified, begin
7817     * operation. This method should only be invoked from the INIT
7818     * state.
7819     *
7820     * @private
7821     */
7822  }, {
7823    key: 'init_',
7824    value: function init_() {
7825      this.state = 'READY';
7826      this.resetEverything();
7827      return this.monitorBuffer_();
7828    }
7829
7830    /**
7831     * Set a subtitle track on the segment loader to add subtitles to
7832     *
7833     * @param {TextTrack=} track
7834     *        The text track to add loaded subtitles to
7835     * @return {TextTrack}
7836     *        Returns the subtitles track
7837     */
7838  }, {
7839    key: 'track',
7840    value: function track(_track) {
7841      if (typeof _track === 'undefined') {
7842        return this.subtitlesTrack_;
7843      }
7844
7845      this.subtitlesTrack_ = _track;
7846
7847      // if we were unpaused but waiting for a sourceUpdater, start
7848      // buffering now
7849      if (this.state === 'INIT' && this.couldBeginLoading_()) {
7850        this.init_();
7851      }
7852
7853      return this.subtitlesTrack_;
7854    }
7855
7856    /**
7857     * Remove any data in the source buffer between start and end times
7858     * @param {Number} start - the start time of the region to remove from the buffer
7859     * @param {Number} end - the end time of the region to remove from the buffer
7860     */
7861  }, {
7862    key: 'remove',
7863    value: function remove(start, end) {
7864      (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.subtitlesTrack_);
7865    }
7866
7867    /**
7868     * fill the buffer with segements unless the sourceBuffers are
7869     * currently updating
7870     *
7871     * Note: this function should only ever be called by monitorBuffer_
7872     * and never directly
7873     *
7874     * @private
7875     */
7876  }, {
7877    key: 'fillBuffer_',
7878    value: function fillBuffer_() {
7879      var _this = this;
7880
7881      if (!this.syncPoint_) {
7882        this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
7883      }
7884
7885      // see if we need to begin loading immediately
7886      var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
7887
7888      segmentInfo = this.skipEmptySegments_(segmentInfo);
7889
7890      if (!segmentInfo) {
7891        return;
7892      }
7893
7894      if (this.syncController_.timestampOffsetForTimeline(segmentInfo.timeline) === null) {
7895        // We don't have the timestamp offset that we need to sync subtitles.
7896        // Rerun on a timestamp offset or user interaction.
7897        var checkTimestampOffset = function checkTimestampOffset() {
7898          _this.state = 'READY';
7899          if (!_this.paused()) {
7900            // if not paused, queue a buffer check as soon as possible
7901            _this.monitorBuffer_();
7902          }
7903        };
7904
7905        this.syncController_.one('timestampoffset', checkTimestampOffset);
7906        this.state = 'WAITING_ON_TIMELINE';
7907        return;
7908      }
7909
7910      this.loadSegment_(segmentInfo);
7911    }
7912
7913    /**
7914     * Prevents the segment loader from requesting segments we know contain no subtitles
7915     * by walking forward until we find the next segment that we don't know 
7915whether it is
7916     * empty or not.
7917     *
7918     * @param {Object} segmentInfo
7919     *        a segment info object that describes the current segment
7920     * @return {Object}
7921     *         a segment info object that describes the current segment
7922     */
7923  }, {
7924    key: 'skipEmptySegments_',
7925    value: function skipEmptySegments_(segmentInfo) {
7926      while (segmentInfo && segmentInfo.segment.empty) {
7927        segmentInfo = this.generateSegmentInfo_(segmentInfo.playlist, segmentInfo.mediaIndex + 1, segmentInfo.startOfSegment + segmentInfo.duration, segmentInfo.isSyncRequest);
7928      }
7929      return segmentInfo;
7930    }
7931
7932    /**
7933     * append a decrypted segement to the SourceBuffer through a SourceUpdater
7934     *
7935     * @private
7936     */
7937  }, {
7938    key: 'handleSegment_',
7939    value: function handleSegment_() {
7940      var _this2 = this;
7941
7942      if (!this.pendingSegment_ || !this.subtitlesTrack_) {
7943        this.state = 'READY';
7944        return;
7945      }
7946
7947      this.state = 'APPENDING';
7948
7949      var segmentInfo = this.pendingSegment_;
7950      var segment = segmentInfo.segment;
7951
7952      // Make sure that vttjs has loaded, otherwise, wait till it finished loading
7953      if (typeof _globalWindow2['default'].WebVTT !== 'function' && this.subtitlesTrack_ && this.subtitlesTrack_.tech_) {
7954        var _ret = (function () {
7955
7956          var loadHandler = function loadHandler() {
7957            _this2.handleSegment_();
7958          };
7959
7960          _this2.state = 'WAITING_ON_VTTJS';
7961          _this2.subtitlesTrack_.tech_.one('vttjsloaded', loadHandler);
7962          _this2.subtitlesTrack_.tech_.one('vttjserror', function () {
7963            _this2.subtitlesTrack_.tech_.off('vttjsloaded', loadHandler);
7964            _this2.error({
7965              message: 'Error loading vtt.js'
7966            });
7967            _this2.state = 'READY';
7968            _this2.pause();
7969            _this2.trigger('error');
7970          });
7971
7972          return {
7973            v: undefined
7974          };
7975        })();
7976
7977        if (typeof _ret === 'object') return _ret.v;
7978      }
7979
7980      segment.requested = true;
7981
7982      try {
7983        this.parseVTTCues_(segmentInfo);
7984      } catch (e) {
7985        this.error({
7986          message: e.message
7987        });
7988        this.state = 'READY';
7989        this.pause();
7990        return this.trigger('error');
7991      }
7992
7993      this.updateTimeMapping_(segmentInfo, this.syncController_.timelines[segmentInfo.timeline], this.playlist_);
7994
7995      if (segmentInfo.isSyncRequest) {
7996        this.trigger('syncinfoupdate');
7997        this.pendingSegment_ = null;
7998        this.state = 'READY';
7999        return;
8000      }
8001
8002      segmentInfo.byteLength = segmentInfo.bytes.byteLength;
8003
8004      this.mediaSecondsLoaded += segment.duration;
8005
8006      if (segmentInfo.cues.length) {
8007        // remove any overlapping cues to prevent doubling
8008        this.remove(segmentInfo.cues[0].endTime, segmentInfo.cues[segmentInfo.cues.length - 1].endTime);
8009      }
8010
8011      segmentInfo.cues.forEach(function (cue) {
8012        _this2.subtitlesTrack_.addCue(cue);
8013      });
8014
8015      this.handleUpdateEnd_();
8016    }
8017
8018    /**
8019     * Uses the WebVTT parser to parse the segment response
8020     *
8021     * @param {Object} segmentInfo
8022     *        a segment info object that describes the current segment
8023     * @private
8024     */
8025  }, {
8026    key: 'parseVTTCues_',
8027    value: function parseVTTCues_(segmentInfo) {
8028      var decoder = undefined;
8029      var decodeBytesToString = false;
8030
8031      if (typeof _globalWindow2['default'].TextDecoder === 'function') {
8032        decoder = new _globalWindow2['default'].TextDecoder('utf8');
8033      } else {
8034        decoder = _globalWindow2['default'].WebVTT.StringDecoder();
8035        decodeBytesToString = true;
8036      }
8037
8038      var parser = new _globalWindow2['default'].WebVTT.Parser(_globalWindow2['default'], _globalWindow2['default'].vttjs, decoder);
8039
8040      segmentInfo.cues = [];
8041      segmentInfo.timestampmap = { MPEGTS: 0, LOCAL: 0 };
8042
8043      parser.oncue = segmentInfo.cues.push.bind(segmentInfo.cues);
8044      parser.ontimestampmap = function (map) {
8045        return segmentInfo.timestampmap = map;
8046      };
8047      parser.onparsingerror = function (error) {
8048        _videoJs2['default'].log.warn('Error encountered when parsing cues: ' + error.message);
8049      };
8050
8051      if (segmentInfo.segment.map) {
8052        var mapData = segmentInfo.segment.map.bytes;
8053
8054        if (decodeBytesToString) {
8055          mapData = uintToString(mapData);
8056        }
8057
8058        parser.parse(mapData);
8059      }
8060
8061      var segmentData = segmentInfo.bytes;
8062
8063      if (decodeBytesToString) {
8064        segmentData = uintToString(segmentData);
8065      }
8066
8067      parser.parse(segmentData);
8068      parser.flush();
8069    }
8070
8071    /**
8072     * Updates the start and end times of any cues parsed by the WebVTT parser using
8073     * the information parsed from the X-TIMESTAMP-MAP header and a TS to media time mapping
8074     * from the SyncController
8075     *
8076     * @param {Object} segmentInfo
8077     *        a segment info object that describes the current segment
8078     * @param {Object} mappingObj
8079     *        object containing a mapping from TS to media time
8080     * @param {Object} playlist
8081     *        the playlist object containing the segment
8082     * @private
8083     */
8084  }, {
8085    key: 'updateTimeMapping_',
8086    value: function updateTimeMapping_(segmentInfo, mappingObj, playlist) {
8087      var segment = segmentInfo.segment;
8088
8089      if (!mappingObj) {
8090        // If the sync controller does not have a mapping of TS to Media Time for the
8091        // timeline, then we don't have enough information to update the cue
8092        // start/end times
8093        return;
8094      }
8095
8096      if (!segmentInfo.cues.length) {
8097        // If there are no cues, we also do not have enough information to figure out
8098        // segment timing. Mark that the segment contains no cues so we don't re-request
8099        // an empty segment.
8100        segment.empty = true;
8101        return;
8102      }
8103
8104      var timestampmap = segmentInfo.timestampmap;
8105      var diff = timestampmap.MPEGTS / 90000 - timestampmap.LOCAL + mappingObj.mapping;
8106
8107      segmentInfo.cues.forEach(function (cue) {
8108        // First convert cue time to TS time using the timestamp-map provided within the vtt
8109        cue.startTime += diff;
8110        cue.endTime += diff;
8111      });
8112
8113      if (!playlist.syncInfo) {
8114        var firstStart = segmentInfo.cues[0].startTime;
8115        var lastStart = segmentInfo.cues[segmentInfo.cues.length - 1].startTime;
8116
8117        playlist.syncInfo = {
8118          mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
8119          time: Math.min(firstStart, lastStart - segment.duration)
8120        };
8121      }
8122    }
8123  }]);
8124
8125  return VTTSegmentLoader;
8126})(_segmentLoader2['default']);
8127
8128exports['default'] = VTTSegmentLoader;
8129module.exports = exports['default'];
8130}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
8131},{"./bin-utils":2,"./segment-loader":16,"global/window":32,"videojs-contrib-media-sources/es5/remove-cues-from-track.js":72}],21:[function(require,module,exports){
8132(function (global){
8133/**
8134 * @file xhr.js
8135 */
8136
8137/**
8138 * A wrapper for videojs.xhr that tracks bandwidth.
8139 *
8140 * @param {Object} options options for the XHR
8141 * @param {Function} callback the callback to call when done
8142 * @return {Request} the xhr request that is going to be made
8143 */
8144'use strict';
8145
8146Object.defineProperty(exports, '__esModule', {
8147  value: true
8148});
8149
8150function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8151
8152var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
8153
8154var _videoJs2 = _interopRequireDefault(_videoJs);
8155
8156var xhrFactory = function xhrFactory() {
8157  var xhr = function XhrFunction(options, callback) {
8158    // Add a default timeout for all hls requests
8159    options = (0, _videoJs.mergeOptions)({
8160      timeout: 45e3
8161    }, options);
8162
8163    // Allow an optional user-specified function to modify the option
8164    // object before we construct the xhr request
8165    var beforeRequest = XhrFunction.beforeRequest || _videoJs2['default'].Hls.xhr.beforeRequest;
8166
8167    if (beforeRequest && typeof beforeRequest === 'function') {
8168      var newOptions = beforeRequest(options);
8169
8170      if (newOptions) {
8171        options = newOptions;
8172      }
8173    }
8174
8175    var request = (0, _videoJs.xhr)(options, function (error, response) {
8176      var reqResponse = request.response;
8177
8178      if (!error && reqResponse) {
8179        request.responseTime = Date.now();
8180        request.roundTripTime = request.responseTime - request.requestTime;
8181        request.bytesReceived = reqResponse.byteLength || reqResponse.length;
8182        if (!request.bandwidth) {
8183          request.bandwidth = Math.floor(request.bytesReceived / request.roundTripTime * 8 * 1000);
8184        }
8185      }
8186
8187      // videojs.xhr now uses a specific code on the error
8188      // object to signal that a request has timed out instead
8189      // of setting a boolean on the request object
8190      if (error && error.code === 'ETIMEDOUT') {
8191        request.timedout = true;
8192      }
8193
8194      // videojs.xhr no longer considers status codes outside of 200 and 0
8195      // (for file uris) to be errors, but the old XHR did, so emulate that
8196      // behavior. Status 206 may be used in response to byterange requests.
8197      if (!error && !request.aborted && response.statusCode !== 200 && response.statusCode !== 206 && response.statusCode !== 0) {
8198        error = new Error('XHR Failed with a response of: ' + (request && (reqResponse || request.responseText)));
8199      }
8200
8201      callback(error, request);
8202    });
8203    var originalAbort = request.abort;
8204
8205    request.abort = function () {
8206      request.aborted = true;
8207      return originalAbort.apply(request, arguments);
8208    };
8209    request.uri = options.uri;
8210    request.requestTime = Date.now();
8211    return request;
8212  };
8213
8214  return xhr;
8215};
8216
8217exports['default'] = xhrFactory;
8218module.exports = exports['default'];
8219}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
8220},{}],22:[function(require,module,exports){
8221/**
8222 * @file aes.js
8223 *
8224 * This file contains an adaptation of the AES decryption algorithm
8225 * from the Standford Javascript Cryptography Library. That work is
8226 * covered by the following copyright and permissions notice:
8227 *
8228 * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
8229 * All rights reserved.
8230 *
8231 * Redistribution and use in source and binary forms, with or without
8232 * modification, are permitted provided that the following conditions are
8233 * met:
8234 *
8235 * 1. Redistributions of source code must retain the above copyright
8236 *    notice, this list of conditions and the following disclaimer.
8237 *
8238 * 2. Redistributions in binary form must reproduce the above
8239 *    copyright notice, this list of conditions and the following
8240 *    disclaimer in the documentation and/or other materials provided
8241 *    with the distribution.
8242 *
8243 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
8244 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8245 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
8246 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
8247 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
8248 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
8249 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
8250 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
8251 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
8252 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
8253 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8254 *
8255 * The views and conclusions contained in the software and documentation
8256 * are those of the authors and should not be interpreted as representing
8257 * official policies, either expressed or implied, of the authors.
8258 */
8259
8260/**
8261 * Expand the S-box tables.
8262 *
8263 * @private
8264 */
8265'use strict';
8266
8267Object.defineProperty(exports, '__esModule', {
8268  value: true
8269});
8270
8271var _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; }; })();
8272
8273function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
8274
8275var precompute = function precompute() {
8276  var tables = [[[], [], [], [], []], [[], [], [], [], []]];
8277  var encTable = tables[0];
8278  var decTable = tables[1];
8279  var sbox = encTable[4];
8280  var sboxInv = decTable[4];
8281  var i = undefined;
8282  var x = undefined;
8283  var xInv = undefined;
8284  var d = [];
8285  var th = [];
8286  var x2 = undefined;
8287  var x4 = undefined;
8288  var x8 = undefined;
8289  var s = undefined;
8290  var tEnc = undefined;
8291  var tDec = undefined;
8292
8293  // Compute double and third tables
8294  for (i = 0; i < 256; i++) {
8295    th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
8296  }
8297
8298  for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
8299    // Compute sbox
8300    s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
8301    s = s >> 8 ^ s & 255 ^ 99;
8302    sbox[x] = s;
8303    sboxInv[s] = x;
8304
8305    // Compute MixColumns
8306    x8 = d[x4 = d[x2 = d[x]]];
8307    tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
8308    tEnc = d[s] * 0x101 ^ s * 0x1010100;
8309
8310    for (i = 0; i < 4; i++) {
8311      encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
8312      decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
8313    }
8314  }
8315
8316  // Compactify. Considerable speedup on Firefox.
8317  for (i = 0; i < 5; i++) {
8318    encTable[i] = encTable[i].slice(0);
8319    decTable[i] = decTable[i].slice(0);
8320  }
8321  return tables;
8322};
8323var aesTables = null;
8324
8325/**
8326 * Schedule out an AES key for both encryption and decryption. This
8327 * is a low-level class. Use a cipher mode to do bulk encryption.
8328 *
8329 * @class AES
8330 * @param key {Array} The key as an array of 4, 6 or 8 words.
8331 */
8332
8333var AES = (function () {
8334  function AES(key) {
8335    _classCallCheck(this, AES);
8336
8337    /**
8338     * The expanded S-box and inverse S-box tables. These will be computed
8339     * on the client so that we don't have to send them down the wire.
8340     *
8341     * There are two tables, _tables[0] is for encryption and
8342     * _tables[1] is for decryption.
8343     *
8344     * The first 4 sub-tables are the expanded S-box with MixColumns. The
8345     * last (_tables[01][4]) is the S-box itself.
8346     *
8347     * @private
8348     */
8349    // if we have yet to precompute the S-box tables
8350    // do so now
8351    if (!aesTables) {
8352      aesTables = precompute();
8353    }
8354    // then make a copy of that object for use
8355    this._tables = [[aesTables[0][0].slice(), aesTables[0][1].slice(), aesTables[0][2].slice(), aesTables[0][3].slice(), aesTables[0][4].slice()], [aesTables[1][0].slice(), aesTables[1][1].slice(), aesTables[1][2].slice(), aesTables[1][3].slice(), aesTables[1][4].slice()]];
8356    var i = undefined;
8357    var j = undefined;
8358    var tmp = undefined;
8359    var encKey = undefined;
8360    var decKey = undefined;
8361    var sbox = this._tables[0][4];
8362    var decTable = this._tables[1];
8363    var keyLen = key.length;
8364    var rcon = 1;
8365
8366    if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
8367      throw new Error('Invalid aes key size');
8368    }
8369
8370    encKey = key.slice(0);
8371    decKey = [];
8372    this._key = [encKey, decKey];
8373
8374    // schedule encryption keys
8375    for (i = keyLen; i < 4 * keyLen + 28; i++) {
8376      tmp = encKey[i - 1];
8377
8378      // apply sbox
8379      if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
8380        tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];
8381
8382        // shift rows and add rcon
8383        if (i % keyLen === 0) {
8384          tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
8385          rcon = rcon << 1 ^ (rcon >> 7) * 283;
8386        }
8387      }
8388
8389      encKey[i] = encKey[i - keyLen] ^ tmp;
8390    }
8391
8392    // schedule decryption keys
8393    for (j = 0; i; j++, i--) {
8394      tmp = encKey[j & 3 ? i : i - 4];
8395      if (i <= 4 || j < 4) {
8396        decKey[j] = tmp;
8397      } else {
8398        decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
8399      }
8400    }
8401  }
8402
8403  /**
8404   * Decrypt 16 bytes, specified as four 32-bit words.
8405   *
8406   * @param {Number} encrypted0 the first word to decrypt
8407   * @param {Number} encrypted1 the second word to decrypt
8408   * @param {Number} encrypted2 the third word to decrypt
8409   * @param {Number} encrypted3 the fourth word to decrypt
8410   * @param {Int32Array} out the array to write the decrypted words
8411   * into
8412   * @param {Number} offset the offset into the output array to start
8413   * writing results
8414   * @return {Array} The plaintext.
8415   */
8416
8417  _createClass(AES, [{
8418    key: 'decrypt',
8419    value: function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
8420      var key = this._key[1];
8421      // state variables a,b,c,d are loaded with pre-whitened data
8422      var a = encrypted0 ^ key[0];
8423      var b = encrypted3 ^ key[1];
8424      var c = encrypted2 ^ key[2];
8425      var d = encrypted1 ^ key[3];
8426      var a2 = undefined;
8427      var b2 = undefined;
8428      var c2 = undefined;
8429
8430      // key.length === 2 ?
8431      var nInnerRounds = key.length / 4 - 2;
8432      var i = undefined;
8433      var kIndex = 4;
8434      var table = this._tables[1];
8435
8436      // load up the tables
8437      var table0 = table[0];
8438      var table1 = table[1];
8439      var table2 = table[2];
8440      var table3 = table[3];
8441      var sbox = table[4];
8442
8443      // Inner rounds. Cribbed from OpenSSL.
8444      for (i = 0; i < nInnerRounds; i++) {
8445        a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
8446        b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
8447        c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
8448        d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
8449        kIndex += 4;
8450        a = a2;b = b2;c = c2;
8451      }
8452
8453      // Last round.
8454      for (i = 0; i < 4; i++) {
8455        out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
8456        a2 = a;a = b;b = c;c = d;d = a2;
8457      }
8458    }
8459  }]);
8460
8461  return AES;
8462})();
8463
8464exports['default'] = AES;
8465module.exports = exports['default'];
8466},{}],23:[function(require,module,exports){
8467/**
8468 * @file async-stream.js
8469 */
8470'use strict';
8471
8472Object.defineProperty(exports, '__esModule', {
8473  value: true
8474});
8475
8476var _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; }; })();
8477
8478var _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); } } };
8479
8480function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8481
8482function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
8483
8484function _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, { c
8484onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
8485
8486var _stream = require('./stream');
8487
8488var _stream2 = _interopRequireDefault(_stream);
8489
8490/**
8491 * A wrapper around the Stream class to use setTiemout
8492 * and run stream "jobs" Asynchronously
8493 *
8494 * @class AsyncStream
8495 * @extends Stream
8496 */
8497
8498var AsyncStream = (function (_Stream) {
8499  _inherits(AsyncStream, _Stream);
8500
8501  function AsyncStream() {
8502    _classCallCheck(this, AsyncStream);
8503
8504    _get(Object.getPrototypeOf(AsyncStream.prototype), 'constructor', this).call(this, _stream2['default']);
8505    this.jobs = [];
8506    this.delay = 1;
8507    this.timeout_ = null;
8508  }
8509
8510  /**
8511   * process an async job
8512   *
8513   * @private
8514   */
8515
8516  _createClass(AsyncStream, [{
8517    key: 'processJob_',
8518    value: function processJob_() {
8519      this.jobs.shift()();
8520      if (this.jobs.length) {
8521        this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
8522      } else {
8523        this.timeout_ = null;
8524      }
8525    }
8526
8527    /**
8528     * push a job into the stream
8529     *
8530     * @param {Function} job the job to push into the stream
8531     */
8532  }, {
8533    key: 'push',
8534    value: function push(job) {
8535      this.jobs.push(job);
8536      if (!this.timeout_) {
8537        this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
8538      }
8539    }
8540  }]);
8541
8542  return AsyncStream;
8543})(_stream2['default']);
8544
8545exports['default'] = AsyncStream;
8546module.exports = exports['default'];
8547},{"./stream":26}],24:[function(require,module,exports){
8548/**
8549 * @file decrypter.js
8550 *
8551 * An asynchronous implementation of AES-128 CBC decryption with
8552 * PKCS#7 padding.
8553 */
8554
8555'use strict';
8556
8557Object.defineProperty(exports, '__esModule', {
8558  value: true
8559});
8560
8561var _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; }; })();
8562
8563function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8564
8565function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
8566
8567var _aes = require('./aes');
8568
8569var _aes2 = _interopRequireDefault(_aes);
8570
8571var _asyncStream = require('./async-stream');
8572
8573var _asyncStream2 = _interopRequireDefault(_asyncStream);
8574
8575var _pkcs7 = require('pkcs7');
8576
8577/**
8578 * Convert network-order (big-endian) bytes into their little-endian
8579 * representation.
8580 */
8581var ntoh = function ntoh(word) {
8582  return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
8583};
8584
8585/**
8586 * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
8587 *
8588 * @param {Uint8Array} encrypted the encrypted bytes
8589 * @param {Uint32Array} key the bytes of the decryption key
8590 * @param {Uint32Array} initVector the initialization vector (IV) to
8591 * use for the first round of CBC.
8592 * @return {Uint8Array} the decrypted bytes
8593 *
8594 * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
8595 * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
8596 * @see https://tools.ietf.org/html/rfc2315
8597 */
8598var decrypt = function decrypt(encrypted, key, initVector) {
8599  // word-level access to the encrypted bytes
8600  var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
8601
8602  var decipher = new _aes2['default'](Array.prototype.slice.call(key));
8603
8604  // byte and word-level access for the decrypted output
8605  var decrypted = new Uint8Array(encrypted.byteLength);
8606  var decrypted32 = new Int32Array(decrypted.buffer);
8607
8608  // temporary variables for working with the IV, encrypted, and
8609  // decrypted data
8610  var init0 = undefined;
8611  var init1 = undefined;
8612  var init2 = undefined;
8613  var init3 = undefined;
8614  var encrypted0 = undefined;
8615  var encrypted1 = undefined;
8616  var encrypted2 = undefined;
8617  var encrypted3 = undefined;
8618
8619  // iteration variable
8620  var wordIx = undefined;
8621
8622  // pull out the words of the IV to ensure we don't modify the
8623  // passed-in reference and easier access
8624  init0 = initVector[0];
8625  init1 = initVector[1];
8626  init2 = initVector[2];
8627  init3 = initVector[3];
8628
8629  // decrypt four word sequences, applying cipher-block chaining (CBC)
8630  // to each decrypted block
8631  for (wordIx = 0;
8631 wordIx < encrypted32.length; wordIx += 4) {
8632    // convert big-endian (network order) words into little-endian
8633    // (javascript order)
8634    encrypted0 = ntoh(encrypted32[wordIx]);
8635    encrypted1 = ntoh(encrypted32[wordIx + 1]);
8636    encrypted2 = ntoh(encrypted32[wordIx + 2]);
8637    encrypted3 = ntoh(encrypted32[wordIx + 3]);
8638
8639    // decrypt the block
8640    decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx);
8641
8642    // XOR with the IV, and restore network byte-order to obtain the
8643    // plaintext
8644    decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
8645    decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
8646    decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
8647    decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
8648
8649    // setup the IV for the next round
8650    init0 = encrypted0;
8651    init1 = encrypted1;
8652    init2 = encrypted2;
8653    init3 = encrypted3;
8654  }
8655
8656  return decrypted;
8657};
8658
8659exports.decrypt = decrypt;
8660/**
8661 * The `Decrypter` class that manages decryption of AES
8662 * data through `AsyncStream` objects and the `decrypt`
8663 * function
8664 *
8665 * @param {Uint8Array} encrypted the encrypted bytes
8666 * @param {Uint32Array} key the bytes of the decryption key
8667 * @param {Uint32Array} initVector the initialization vector (IV) to
8668 * @param {Function} done the function to run when done
8669 * @class Decrypter
8670 */
8671
8672var Decrypter = (function () {
8673  function Decrypter(encrypted, key, initVector, done) {
8674    _classCallCheck(this, Decrypter);
8675
8676    var step = Decrypter.STEP;
8677    var encrypted32 = new Int32Array(encrypted.buffer);
8678    var decrypted = new Uint8Array(encrypted.byteLength);
8679    var i = 0;
8680
8681    this.asyncStream_ = new _asyncStream2['default']();
8682
8683    // split up the encryption job and do the individual chunks asynchronously
8684    this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
8685    for (i = step; i < encrypted32.length; i += step) {
8686      initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
8687      this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
8688    }
8689    // invoke the done() callback when everything is finished
8690    this.asyncStream_.push(function () {
8691      // remove pkcs#7 padding from the decrypted bytes
8692      done(null, (0, _pkcs7.unpad)(decrypted));
8693    });
8694  }
8695
8696  /**
8697   * a getter for step the maximum number of bytes to process at one time
8698   *
8699   * @return {Number} the value of step 32000
8700   */
8701
8702  _createClass(Decrypter, [{
8703    key: 'decryptChunk_',
8704
8705    /**
8706     * @private
8707     */
8708    value: function decryptChunk_(encrypted, key, initVector, decrypted) {
8709      return function () {
8710        var bytes = decrypt(encrypted, key, initVector);
8711
8712        decrypted.set(bytes, encrypted.byteOffset);
8713      };
8714    }
8715  }], [{
8716    key: 'STEP',
8717    get: function get() {
8718      // 4 * 8000;
8719      return 32000;
8720    }
8721  }]);
8722
8723  return Decrypter;
8724})();
8725
8726exports.Decrypter = Decrypter;
8727exports['default'] = {
8728  Decrypter: Decrypter,
8729  decrypt: decrypt
8730};
8731},{"./aes":22,"./async-stream":23,"pkcs7":28}],25:[function(require,module,exports){
8732/**
8733 * @file index.js
8734 *
8735 * Index module to easily import the primary components of AES-128
8736 * decryption. Like this:
8737 *
8738 * ```js
8739 * import {Decrypter, decrypt, AsyncStream} from 'aes-decrypter';
8740 * ```
8741 */
8742'use strict';
8743
8744Object.defineProperty(exports, '__esModule', {
8745  value: true
8746});
8747
8748function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8749
8750var _decrypter = require('./decrypter');
8751
8752var _asyncStream = require('./async-stream');
8753
8754var _asyncStream2 = _interopRequireDefault(_asyncStream);
8755
8756exports['default'] = {
8757  decrypt: _decrypter.decrypt,
8758  Decrypter: _decrypter.Decrypter,
8759  AsyncStream: _asyncStream2['default']
8760};
8761module.exports = exports['default'];
8762},{"./async-stream":23,"./decrypter":24}],26:[function(require,module,exports){
8763/**
8764 * @file stream.js
8765 */
8766/**
8767 * A lightweight readable stream implemention that handles event dispatching.
8768 *
8769 * @class Stream
8770 */
8771'use strict';
8772
8773Object.defineProperty(exports, '__esModule', {
8774  value: true
8775});
8776
8777var _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; }; })();
8778
8779function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
8780
8781var Stream = (function () {
8782  function Stream() {
8783    _classCallCheck(this, Stream);
8784
8785    this.listeners = {};
8786  }
8787
8788  /**
8789   * Add a listener for a specified event type.
8790   *
8791   * @param {String} type the event name
8792   * @param {Function} listener the callback to be invoked when an event of
8793   * the specified type occurs
8794   */
8795
8796  _createClass(Stream, [{
8797    key: 'on',
8798    value: function on(type, listener) {
8799      if (!this.listeners[type]) {
8800        this.listeners[type] = [];
8801      }
8802      this.listeners[type].push(listener);
8803    }
8804
8805    /**
8806     * Remove a listener for a specified event type.
8807     *
8808     * @param {String} type the event name
8809     * @param {Function} listener  a function previously registered for this
8810     * type of event through `on`
8811     * @return {Boolean} if we could turn it off or not
8812     */
8813  }, {
8814    key: 'off',
8815    value: function off(type, listener) {
8816      var index = undefined;
8817
8818      if (!this.listeners[type]) {
8819        return false;
8820      }
8821      index = this.listeners[type].indexOf(listener);
8822      this.listeners[type].splice(index, 1);
8823      return index > -1;
8824    }
8825
8826    /**
8827     * Trigger an event of the specified type on this stream. Any additional
8828     * arguments to this function are passed as parameters to event listeners.
8829     *
8830     * @param {String} type the event name
8831     */
8832  }, {
8833    key: 'trigger',
8834    value: function trigger(type) {
8835      var callbacks = undefined;
8836      var i = undefined;
8837      var length = undefined;
8838      var args = undefined;
8839
8840      callbacks = this.listeners[type];
8841      if (!callbacks) {
8842        return;
8843      }
8844      // Slicing the arguments on every invocation of this method
8845      // can add a significant amount of overhead. Avoid the
8846      // intermediate object creation for the common case of a
8847      // single callback argument
8848      if (arguments.length === 2) {
8849        length = callbacks.length;
8850        for (i = 0; i < length; ++i) {
8851          callbacks[i].call(this, arguments[1]);
8852        }
8853      } else {
8854        args = Array.prototype.slice.call(arguments, 1);
8855        length = callbacks.length;
8856        for (i = 0; i < length; ++i) {
8857          callbacks[i].apply(this, args);
8858        }
8859      }
8860    }
8861
8862    /**
8863     * Destroys the stream and cleans up.
8864     */
8865  }, {
8866    key: 'dispose',
8867    value: function dispose() {
8868      this.listeners = {};
8869    }
8870
8871    /**
8872     * Forwards all `data` events on this stream to the destination stream. The
8873     * destination stream should provide a method `push` to receive the data
8874     * events as they arrive.
8875     *
8876     * @param {Stream} destination the stream that will receive all `data` events
8877     * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
8878     */
8879  }, {
8880    key: 'pipe',
8881    value: function pipe(destination) {
8882      this.on('data', function (data) {
8883        destination.push(data);
8884      });
8885    }
8886  }]);
8887
8888  return Stream;
8889})();
8890
8891exports['default'] = Stream;
8892module.exports = exports['default'];
8893},{}],27:[function(require,module,exports){
8894/*
8895 * pkcs7.pad
8896 * https://github.com/brightcove/pkcs7
8897 *
8898 * Copyright (c) 2014 Brightcove
8899 * Licensed under the apache2 license.
8900 */
8901
8902'use strict';
8903
8904var PADDING;
8905
8906/**
8907 * Returns a new Uint8Array that is padded with PKCS#7 padding.
8908 * @param plaintext {Uint8Array} the input bytes before encryption
8909 * @return {Uint8Array} the padded bytes
8910 * @see http://tools.ietf.org/html/rfc5652
8911 */
8912module.exports = function pad(plaintext) {
8913  var padding = PADDING[(plaintext.byteLength % 16) || 0],
8914      result = new Uint8Array(plaintext.byteLength + padding.length);
8915  result.set(plaintext);
8916  result.set(padding, plaintext.byteLength);
8917  return result;
8918};
8919
8920// pre-define the padding values
8921PADDING = [
8922  [16, 16, 16, 16,
8923   16, 16, 16, 16,
8924   16, 16, 16, 16,
8925   16, 16, 16, 16],
8926
8927  [15, 15, 15, 15,
8928   15, 15, 15, 15,
8929   15, 15, 15, 15,
8930   15, 15, 15],
8931
8932  [14, 14, 14, 14,
8933   14, 14, 14, 14,
8934   14, 14, 14, 14,
8935   14, 14],
8936
8937  [13, 13, 13, 13,
8938   13, 13, 13, 13,
8939   13, 13, 13, 13,
8940   13],
8941
8942  [12, 12, 12, 12,
8943   12, 12, 12, 12,
8944   12, 12, 12, 12],
8945
8946  [11, 11, 11, 11,
8947   11, 11, 11, 11,
8948   11, 11, 11],
8949
8950  [10, 10, 10, 10,
8951   10, 10, 10, 10,
8952   10, 10],
8953
8954  [9, 9, 9, 9,
8955   9, 9, 9, 9,
8956   9],
8957
8958  [8, 8, 8, 8,
8959   8, 8, 8, 8],
8960
8961  [7, 7, 7, 7,
8962   7, 7, 7],
8963
8964  [6, 6, 6, 6,
8965   6, 6],
8966
8967  [5, 5, 5, 5,
8968   5],
8969
8970  [4, 4, 4, 4],
8971
8972  [3, 3, 3],
8973
8974  [2, 2],
8975
8976  [1]
8977];
8978
8979},{}],28:[function(require,module,exports){
8980/*
8981 * pkcs7
8982 * https://github.com/brightcove/pkcs7
8983 *
8984 * Copyright (c) 2014 Brightcove
8985 * Licensed under the apache2 license.
8986 */
8987
8988'use strict';
8989
8990exports.pad = require('./pad.js');
8991exports.unpad = require('./unpad.js');
8992
8993},{"./pad.js":27,"./unpad.js":29}],29:[function(require,module,exports){
8994/*
8995 * pkcs7.unpad
8996 * https://github.com/brightcove/pkcs7
8997 *
8998 * Copyright (c) 2014 Brightcove
8999 * Licensed under the apache2 license.
9000 */
9001
9002'use strict';
9003
9004/**
9005 * Returns the subarray of a Uint8Array without PKCS#7 padding.
9006 * @param padded {Uint8Array} unencrypted bytes that have been padded
9007 * @return {Uint8Array} the unpadded bytes
9008 * @see http://tools.ietf.org/html/rfc5652
9009 */
9010module.exports = function unpad(padded) {
9011  return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
9012};
9013
9014},{}],30:[function(require,module,exports){
9015
9016},{}],31:[function(require,module,exports){
9017(function (global){
9018var topLevel = typeof global !== 'undefined' ? global :
9019    typeof window !== 'undefined' ? window : {}
9020var minDoc = require('min-document');
9021
9022var doccy;
9023
9024if (typeof document !== 'undefined') {
9025    doccy = document;
9026} else {
9027    doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
9028
9029    if (!doccy) {
9030        doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
9031    }
9032}
9033
9034module.exports = doccy;
9035
9036}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
9037},{"min-document":30}],32:[function(require,module,exports){
9038(function (global){
9039var win;
9040
9041if (typeof window !== "undefined") {
9042    win = window;
9043} else if (typeof global !== "undefined") {
9044    win = global;
9045} else if (typeof self !== "undefined"){
9046    win = self;
9047} else {
9048    win = {};
9049}
9050
9051module.exports = win;
9052
9053}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
9054},{}],33:[function(require,module,exports){
9055'use strict';
9056
9057var _lineStream = require('./line-stream');
9058
9059var _lineStream2 = _interopRequireDefault(_lineStream);
9060
9061var _parseStream = require('./parse-stream');
9062
9063var _parseStream2 = _interopRequireDefault(_parseStream);
9064
9065var _parser = require('./parser');
9066
9067var _parser2 = _interopRequireDefault(_parser);
9068
9069function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
9070
9071module.exports = {
9072  LineStream: _lineStream2['default'],
9073  ParseStream: _parseStream2['default'],
9074  Parser: _parser2['default']
9075}; /**
9076    * @file m3u8/index.js
9077    *
9078    * Utilities for parsing M3U8 files. If the entire manifest is available,
9079    * `Parser` will create an object representation with enough detail for managing
9080    * playback. `ParseStream` and `LineStream` are lower-level parsing primitives
9081    * that do not assume the entirety of the manifest is ready and expose a
9082    * ReadableStream-like interface.
9083    */
9084},{"./line-stream":34,"./parse-stream":35,"./parser":36}],34:[function(require,module,exports){
9085'use strict';
9086
9087Object.defineProperty(exports, "__esModule", {
9088  value: true
9089});
9090
9091var _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; }; }();
9092
9093var _stream = require('./stream');
9094
9095var _stream2 = _interopRequireDefault(_stream);
9096
9097function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
9098
9099function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
9100
9101function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
9102
9103function _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; } /**
9104                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * @file m3u8/line-stream.js
9105                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                */
9106
9107
9108/**
9109 * A stream that buffers string input and generates a `data` event for each
9110 * line.
9111 *
9112 * @class LineStream
9113 * @extends Stream
9114 */
9115var LineStream = function (_Stream) {
9116  _inherits(LineStream, _Stream);
9117
9118  function LineStream() {
9119    _classCallCheck(this, LineStream);
9120
9121    var _this = _possibleConstructorReturn(this, (LineStream.__proto__ || Object.getPrototypeOf(LineStream)).call(this));
9122
9123    _this.buffer = '';
9124    return _this;
9125  }
9126
9127  /**
9128   * Add new data to be parsed.
9129   *
9130   * @param {String} data the text to process
9131   */
9132
9133
9134  _createClass(LineStream, [{
9135    key: 'push',
9136    value: function push(data) {
9137      var nextNewline = void 0;
9138
9139      this.buffer += data;
9140      nextNewline = this.buffer.indexOf('\n');
9141
9142      for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) {
9143        this.trigger('data', this.buffer.substring(0, nextNewline));
9144        this.buffer = this.buffer.substring(nextNewline + 1);
9145      }
9146    }
9147  }]);
9148
9149  return LineStream;
9150}(_stream2['default']);
9151
9152exports['default'] = LineStream;
9153},{"./stream":37}],35:[function(require,module,exports){
9154'use strict';
9155
9156Object.defineProperty(exports, "__esModule", {
9157  value: true
9158});
9159
9160var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); }
vendor: 60,067 bytes, lines 9160-11012
9160 else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
9161
9162var _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; }; }();
9163
9164var _stream = require('./stream');
9165
9166var _stream2 = _interopRequireDefault(_stream);
9167
9168function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
9169
9170function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
9171
9172function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
9173
9174function _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; } /**
9175                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * @file m3u8/parse-stream.js
9176                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                */
9177
9178
9179/**
9180 * "forgiving" attribute list psuedo-grammar:
9181 * attributes -> keyvalue (',' keyvalue)*
9182 * keyvalue   -> key '=' value
9183 * key        -> [^=]*
9184 * value      -> '"' [^"]* '"' | [^,]*
9185 */
9186var attributeSeparator = function attributeSeparator() {
9187  var key = '[^=]*';
9188  var value = '"[^"]*"|[^,]*';
9189  var keyvalue = '(?:' + key + ')=(?:' + value + ')';
9190
9191  return new RegExp('(?:^|,)(' + keyvalue + ')');
9192};
9193
9194/**
9195 * Parse attributes from a line given the seperator
9196 *
9197 * @param {String} attributes the attibute line to parse
9198 */
9199var parseAttributes = function parseAttributes(attributes) {
9200  // split the string using attributes as the separator
9201  var attrs = attributes.split(attributeSeparator());
9202  var result = {};
9203  var i = attrs.length;
9204  var attr = void 0;
9205
9206  while (i--) {
9207    // filter out unmatched portions of the string
9208    if (attrs[i] === '') {
9209      continue;
9210    }
9211
9212    // split the key and value
9213    attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1);
9214    // trim whitespace and remove optional quotes around the value
9215    attr[0] = attr[0].replace(/^\s+|\s+$/g, '');
9216    attr[1] = attr[1].replace(/^\s+|\s+$/g, '');
9217    attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1');
9218    result[attr[0]] = attr[1];
9219  }
9220  return result;
9221};
9222
9223/**
9224 * A line-level M3U8 parser event stream. It expects to receive input one
9225 * line at a time and performs a context-free parse of its contents. A stream
9226 * interpretation of a manifest can be useful if the manifest is expected to
9227 * be too large to fit comfortably into memory or the entirety of the input
9228 * is not immediately available. Otherwise, it's probably much easier to work
9229 * with a regular `Parser` object.
9230 *
9231 * Produces `data` events with an object that captures the parser's
9232 * interpretation of the input. That object has a property `tag` that is one
9233 * of `uri`, `comment`, or `tag`. URIs only have a single additional
9234 * property, `line`, which captures the entirety of the input without
9235 * interpretation. Comments similarly have a single additional property
9236 * `text` which is the input without the leading `#`.
9237 *
9238 * Tags always have a property `tagType` which is the lower-cased version of
9239 * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
9240 * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
9241 * tags are given the tag type `unknown` and a single additional property
9242 * `data` with the remainder of the input.
9243 *
9244 * @class ParseStream
9245 * @extends Stream
9246 */
9247
9248var ParseStream = function (_Stream) {
9249  _inherits(ParseStream, _Stream);
9250
9251  function ParseStream() {
9252    _classCallCheck(this, ParseStream);
9253
9254    return _possibleConstructorReturn(this, (ParseStream.__proto__ || Object.getPrototypeOf(ParseStream)).call(this));
9255  }
9256
9257  /**
9258   * Parses an additional line of input.
9259   *
9260   * @param {String} line a single line of an M3U8 file to parse
9261   */
9262
9263
9264  _createClass(ParseStream, [{
9265    key: 'push',
9266    value: function push(line) {
9267      var match = void 0;
9268      var event = void 0;
9269
9270      // strip whitespace
9271      line = line.replace(/^[\u0000\s]+|[\u0000\s]+$/g, '');
9272      if (line.length === 0) {
9273        // ignore empty lines
9274        return;
9275      }
9276
9277      // URIs
9278      if (line[0] !== '#') {
9279        this.trigger('data', {
9280          type: 'uri',
9281          uri: line
9282        });
9283        return;
9284      }
9285
9286      // Comments
9287      if (line.indexOf('#EXT') !== 0) {
9288        this.trigger('data', {
9289          type: 'comment',
9290          text: line.slice(1)
9291        });
9292        return;
9293      }
9294
9295      // strip off any carriage returns here so the regex matching
9296      // doesn't have to account for them.
9297      line = line.replace('\r', '');
9298
9299      // Tags
9300      match = /^#EXTM3U/.exec(line);
9301      if (match) {
9302        this.trigger('data', {
9303          type: 'tag',
9304          tagType: 'm3u'
9305        });
9306        return;
9307      }
9308      match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(line);
9309      if (match) {
9310        event = {
9311          type: 'tag',
9312          tagType: 'inf'
9313        };
9314        if (match[1]) {
9315          event.duration = parseFloat(match[1]);
9316        }
9317        if (match[2]) {
9318          event.title = match[2];
9319        }
9320        this.trigger('data', event);
9321        return;
9322      }
9323      match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(line);
9324      if (match) {
9325        event = {
9326          type: 'tag',
9327          tagType: 'targetduration'
9328        };
9329        if (match[1]) {
9330          event.duration = parseInt(match[1], 10);
9331        }
9332        this.trigger('data', event);
9333        return;
9334      }
9335      match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(line);
9336      if (match) {
9337        event = {
9338          type: 'tag',
9339          tagType: 'totalduration'
9340        };
9341        if (match[1]) {
9342          event.duration = parseInt(match[1], 10);
9343        }
9344        this.trigger('data', event);
9345        return;
9346      }
9347      match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(line);
9348      if (match) {
9349        event = {
9350          type: 'tag',
9351          tagType: 'version'
9352        };
9353        if (match[1]) {
9354          event.version = parseInt(match[1], 10);
9355        }
9356        this.trigger('data', event);
9357        return;
9358      }
9359      match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
9360      if (match) {
9361        event = {
9362          type: 'tag',
9363          tagType: 'media-sequence'
9364        };
9365        if (match[1]) {
9366          event.number = parseInt(match[1], 10);
9367        }
9368        this.trigger('data', event);
9369        return;
9370      }
9371      match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
9372      if (match) {
9373        event = {
9374          type: 'tag',
9375          tagType: 'discontinuity-sequence'
9376        };
9377        if (match[1]) {
9378          event.number = parseInt(match[1], 10);
9379        }
9380        this.trigger('data', event);
9381        return;
9382      }
9383      match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(line);
9384      if (match) {
9385        event = {
9386          type: 'tag',
9387          tagType: 'playlist-type'
9388        };
9389        if (match[1]) {
9390          event.playlistType = match[1];
9391        }
9392        this.trigger('data', event);
9393        return;
9394      }
9395      match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(line);
9396      if (match) {
9397        event = {
9398          type: 'tag',
9399          tagType: 'byterange'
9400        };
9401        if (match[1]) {
9402          event.length = parseInt(match[1], 10);
9403        }
9404        if (match[2]) {
9405          event.offset = parseInt(match[2], 10);
9406        }
9407        this.trigger('data', event);
9408        return;
9409      }
9410      match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(line);
9411      if (match) {
9412        event = {
9413          type: 'tag',
9414          tagType: 'allow-cache'
9415        };
9416        if (match[1]) {
9417          event.allowed = !/NO/.test(match[1]);
9418        }
9419        this.trigger('data', event);
9420        return;
9421      }
9422      match = /^#EXT-X-MAP:?(.*)$/.exec(line);
9423      if (match) {
9424        event = {
9425          type: 'tag',
9426          tagType: 'map'
9427        };
9428
9429        if (match[1]) {
9430          var attributes = parseAttributes(match[1]);
9431
9432          if (attributes.URI) {
9433            event.uri = attributes.URI;
9434          }
9435          if (attributes.BYTERANGE) {
9436            var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'),
9437                _attributes$BYTERANGE2 = _slicedToArray(_attributes$BYTERANGE, 2),
9438                length = _attributes$BYTERANGE2[0],
9439                offset = _attributes$BYTERANGE2[1];
9440
9441            event.byterange = {};
9442            if (length) {
9443              event.byterange.length = parseInt(length, 10);
9444            }
9445            if (offset) {
9446              event.byterange.offset = parseInt(offset, 10);
9447            }
9448          }
9449        }
9450
9451        this.trigger('data', event);
9452        return;
9453      }
9454      match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(line);
9455      if (match) {
9456        event = {
9457          type: 'tag',
9458          tagType: 'stream-inf'
9459        };
9460        if (match[1]) {
9461          event.attributes = parseAttributes(match[1]);
9462
9463          if (event.attributes.RESOLUTION) {
9464            var split = event.attributes.RESOLUTION.split('x');
9465            var resolution = {};
9466
9467            if (split[0]) {
9468              resolution.width = parseInt(split[0], 10);
9469            }
9470            if (split[1]) {
9471              resolution.height = parseInt(split[1], 10);
9472            }
9473            event.attributes.RESOLUTION = resolution;
9474          }
9475          if (event.attributes.BANDWIDTH) {
9476            event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
9477          }
9478          if (event.attributes['PROGRAM-ID']) {
9479            event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
9480          }
9481        }
9482        this.trigger('data', event);
9483        return;
9484      }
9485      match = /^#EXT-X-MEDIA:?(.*)$/.exec(line);
9486      if (match) {
9487        event = {
9488          type: 'tag',
9489          tagType: 'media'
9490        };
9491        if (match[1]) {
9492          event.attributes = parseAttributes(match[1]);
9493        }
9494        this.trigger('data', event);
9495        return;
9496      }
9497      match = /^#EXT-X-ENDLIST/.exec(line);
9498      if (match) {
9499        this.trigger('data', {
9500          type: 'tag',
9501          tagType: 'endlist'
9502        });
9503        return;
9504      }
9505      match = /^#EXT-X-DISCONTINUITY/.exec(line);
9506      if (match) {
9507        this.trigger('data', {
9508          type: 'tag',
9509          tagType: 'discontinuity'
9510        });
9511        return;
9512      }
9513      match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(line);
9514      if (match) {
9515        event = {
9516          type: 'tag',
9517          tagType: 'program-date-time'
9518        };
9519        if (match[1]) {
9520          event.dateTimeString = match[1];
9521          event.dateTimeObject = new Date(match[1]);
9522        }
9523        this.trigger('data', event);
9524        return;
9525      }
9526      match = /^#EXT-X-KEY:?(.*)$/.exec(line);
9527      if (match) {
9528        event = {
9529          type: 'tag',
9530          tagType: 'key'
9531        };
9532        if (match[1]) {
9533          event.attributes = parseAttributes(match[1]);
9534          // parse the IV string into a Uint32Array
9535          if (event.attributes.IV) {
9536            if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') {
9537              event.attributes.IV = event.attributes.IV.substring(2);
9538            }
9539
9540            event.attributes.IV = event.attributes.IV.match(/.{8}/g);
9541            event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16);
9542            event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16);
9543            event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16);
9544            event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16);
9545            event.attributes.IV = new Uint32Array(event.attributes.IV);
9546          }
9547        }
9548        this.trigger('data', event);
9549        return;
9550      }
9551      match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(line);
9552      if (match) {
9553        event = {
9554          type: 'tag',
9555          tagType: 'cue-out-cont'
9556        };
9557        if (match[1]) {
9558          event.data = match[1];
9559        } else {
9560          event.data = '';
9561        }
9562        this.trigger('data', event);
9563        return;
9564      }
9565      match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(line);
9566      if (match) {
9567        event = {
9568          type: 'tag',
9569          tagType: 'cue-out'
9570        };
9571        if (match[1]) {
9572          event.data = match[1];
9573        } else {
9574          event.data = '';
9575        }
9576        this.trigger('data', event);
9577        return;
9578      }
9579      match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(line);
9580      if (match) {
9581        event = {
9582          type: 'tag',
9583          tagType: 'cue-in'
9584        };
9585        if (match[1]) {
9586          event.data = match[1];
9587        } else {
9588          event.data = '';
9589        }
9590        this.trigger('data', event);
9591        return;
9592      }
9593
9594      // unknown tag type
9595      this.trigger('data', {
9596        type: 'tag',
9597        data: line.slice(4)
9598      });
9599    }
9600  }]);
9601
9602  return ParseStream;
9603}(_stream2['default']);
9604
9605exports['default'] = ParseStream;
9606},{"./stream":37}],36:[function(require,module,exports){
9607'use strict';
9608
9609Object.defineProperty(exports, "__esModule", {
9610  value: true
9611});
9612
9613var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9614
9615var _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; }; }();
9616
9617var _stream = require('./stream');
9618
9619var _stream2 = _interopRequireDefault(_stream);
9620
9621var _lineStream = require('./line-stream');
9622
9623var _lineStream2 = _interopRequireDefault(_lineStream);
9624
9625var _parseStream = require('./parse-stream');
9626
9627var _parseStream2 = _interopRequireDefault(_parseStream);
9628
9629function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
9630
9631function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
9632
9633function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
9634
9635function _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; } /**
9636                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * @file m3u8/parser.js
9637                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                */
9638
9639
9640/**
9641 * A parser for M3U8 files. The current interpretation of the input is
9642 * exposed as a property `manifest` on parser objects. It's just two lines to
9643 * create and parse a manifest once you have the contents available as a string:
9644 *
9645 * ```js
9646 * var parser = new m3u8.Parser();
9647 * parser.push(xhr.responseText);
9648 * ```
9649 *
9650 * New input can later be applied to update the manifest object by calling
9651 * `push` again.
9652 *
9653 * The parser attempts to create a usable manifest object even if the
9654 * underlying input is somewhat nonsensical. It emits `info` and `warning`
9655 * events during the parse if it encounters input that seems invalid or
9656 * requires some property of the manifest object to be defaulted.
9657 *
9658 * @class Parser
9659 * @extends Stream
9660 */
9661var Parser = function (_Stream) {
9662  _inherits(Parser, _Stream);
9663
9664  function Parser() {
9665    _classCallCheck(this, Parser);
9666
9667    var _this = _possibleConstructorReturn(this, (Parser.__proto__ || Object.getPrototypeOf(Parser)).call(this));
9668
9669    _this.lineStream = new _lineStream2['default']();
9670    _this.parseStream = new _parseStream2['default']();
9671    _this.lineStream.pipe(_this.parseStream);
9672    /* eslint-disable consistent-this */
9673    var self = _this;
9674    /* eslint-enable consistent-this */
9675    var uris = [];
9676    var currentUri = {};
9677    // if specified, the active EXT-X-MAP definition
9678    var currentMap = void 0;
9679    // if specified, the active decryption key
9680    var _key = void 0;
9681    var noop = function noop() {};
9682    var defaultMediaGroups = {
9683      'AUDIO': {},
9684      'VIDEO': {},
9685      'CLOSED-CAPTIONS': {},
9686      'SUBTITLES': {}
9687    };
9688    // group segments into numbered timelines delineated by discontinuities
9689    var currentTimeline = 0;
9690
9691    // the manifest is empty until the parse stream begins delivering data
9692    _this.manifest = {
9693      allowCache: true,
9694      discontinuityStarts: [],
9695      segments: []
9696    };
9697
9698    // update the manifest with the m3u8 entry from the parse stream
9699    _this.parseStream.on('data', function (entry) {
9700      var mediaGroup = void 0;
9701      var rendition = void 0;
9702
9703      ({
9704        tag: function tag() {
9705          // switch based on the tag type
9706          (({
9707            'allow-cache': function allowCache() {
9708              this.manifest.allowCache = entry.allowed;
9709              if (!('allowed' in entry)) {
9710                this.trigger('info', {
9711                  message: 'defaulting allowCache to YES'
9712                });
9713                this.manifest.allowCache = true;
9714              }
9715            },
9716            byterange: function byterange() {
9717              var byterange = {};
9718
9719              if ('length' in entry) {
9720                currentUri.byterange = byterange;
9721                byterange.length = entry.length;
9722
9723                if (!('offset' in entry)) {
9724                  this.trigger('info', {
9725                    message: 'defaulting offset to zero'
9726                  });
9727                  entry.offset = 0;
9728                }
9729              }
9730              if ('offset' in entry) {
9731                currentUri.byterange = byterange;
9732                byterange.offset = entry.offset;
9733              }
9734            },
9735            endlist: function endlist() {
9736              this.manifest.endList = true;
9737            },
9738            inf: function inf() {
9739              if (!('mediaSequence' in this.manifest)) {
9740                this.manifest.mediaSequence = 0;
9741                this.trigger('info', {
9742                  message: 'defaulting media sequence to zero'
9743                });
9744              }
9745              if (!('discontinuitySequence' in this.manifest)) {
9746                this.manifest.discontinuitySequence = 0;
9747                this.trigger('info', {
9748                  message: 'defaulting discontinuity sequence to zero'
9749                });
9750              }
9751              if (entry.duration > 0) {
9752                currentUri.duration = entry.duration;
9753              }
9754
9755              if (entry.duration === 0) {
9756                currentUri.duration = 0.01;
9757                this.trigger('info', {
9758                  message: 'updating zero segment duration to a small value'
9759                });
9760              }
9761
9762              this.manifest.segments = uris;
9763            },
9764            key: function key() {
9765              if (!entry.attributes) {
9766                this.trigger('warn', {
9767                  message: 'ignoring key declaration without attribute list'
9768                });
9769                return;
9770              }
9771              // clear the active encryption key
9772              if (entry.attributes.METHOD === 'NONE') {
9773                _key = null;
9774                return;
9775              }
9776              if (!entry.attributes.URI) {
9777                this.trigger('warn', {
9778                  message: 'ignoring key declaration without URI'
9779                });
9780                return;
9781              }
9782              if (!entry.attributes.METHOD) {
9783                this.trigger('warn', {
9784                  message: 'defaulting key method to AES-128'
9785                });
9786              }
9787
9788              // setup an encryption key for upcoming segments
9789              _key = {
9790                method: entry.attributes.METHOD || 'AES-128',
9791                uri: entry.attributes.URI
9792              };
9793
9794              if (typeof entry.attributes.IV !== 'undefined') {
9795                _key.iv = entry.attributes.IV;
9796              }
9797            },
9798            'media-sequence': function mediaSequence() {
9799              if (!isFinite(entry.number)) {
9800                this.trigger('warn', {
9801                  message: 'ignoring invalid media sequence: ' + entry.number
9802                });
9803                return;
9804              }
9805              this.manifest.mediaSequence = entry.number;
9806            },
9807            'discontinuity-sequence': function discontinuitySequence() {
9808              if (!isFinite(entry.number)) {
9809                this.trigger('warn', {
9810                  message: 'ignoring invalid discontinuity sequence: ' + entry.number
9811                });
9812                return;
9813              }
9814              this.manifest.discontinuitySequence = entry.number;
9815              currentTimeline = entry.number;
9816            },
9817            'playlist-type': function playlistType() {
9818              if (!/VOD|EVENT/.test(entry.playlistType)) {
9819                this.trigger('warn', {
9820                  message: 'ignoring unknown playlist type: ' + entry.playlist
9821                });
9822                return;
9823              }
9824              this.manifest.playlistType = entry.playlistType;
9825            },
9826            map: function map() {
9827              currentMap = {};
9828              if (entry.uri) {
9829                currentMap.uri = entry.uri;
9830              }
9831              if (entry.byterange) {
9832                currentMap.byterange = entry.byterange;
9833              }
9834            },
9835            'stream-inf': function streamInf() {
9836              this.manifest.playlists = uris;
9837              this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
9838
9839              if (!entry.attributes) {
9840                this.trigger('warn', {
9841                  message: 'ignoring empty stream-inf attributes'
9842                });
9843                return;
9844              }
9845
9846              if (!currentUri.attributes) {
9847                currentUri.attributes = {};
9848              }
9849              _extends(currentUri.attributes, entry.attributes);
9850            },
9851            media: function media() {
9852              this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
9853
9854              if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) {
9855                this.trigger('warn', {
9856                  message: 'ignoring incomplete or missing media group'
9857                });
9858                return;
9859              }
9860
9861              // find the media group, creating defaults as necessary
9862              var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE];
9863
9864              mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {};
9865              mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']];
9866
9867              // collect the rendition metadata
9868              rendition = {
9869                'default': /yes/i.test(entry.attributes.DEFAULT)
9870              };
9871              if (rendition['default']) {
9872                rendition.autoselect = true;
9873              } else {
9874                rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT);
9875              }
9876              if (entry.attributes.LANGUAGE) {
9877                rendition.language = entry.attributes.LANGUAGE;
9878              }
9879              if (entry.attributes.URI) {
9880                rendition.uri = entry.attributes.URI;
9881              }
9882              if (entry.attributes['INSTREAM-ID']) {
9883                rendition.instreamId = entry.attributes['INSTREAM-ID'];
9884              }
9885              if (entry.attributes.CHARACTERISTICS) {
9886                rendition.characteristics = entry.attributes.CHARACTERISTICS;
9887              }
9888              if (entry.attributes.FORCED) {
9889                rendition.forced = /yes/i.test(entry.attributes.FORCED);
9890              }
9891
9892              // insert the new rendition
9893              mediaGroup[entry.attributes.NAME] = rendition;
9894            },
9895            discontinuity: function discontinuity() {
9896              currentTimeline += 1;
9897              currentUri.discontinuity = true;
9898              this.manifest.discontinuityStarts.push(uris.length);
9899            },
9900            'program-date-time': function programDateTime() {
9901              this.manifest.dateTimeString = entry.dateTimeString;
9902              this.manifest.dateTimeObject = entry.dateTimeObject;
9903            },
9904            targetduration: function targetduration() {
9905              if (!isFinite(entry.duration) || entry.duration < 0) {
9906                this.trigger('warn', {
9907                  message: 'ignoring invalid target duration: ' + entry.duration
9908                });
9909                return;
9910              }
9911              this.manifest.targetDuration = entry.duration;
9912            },
9913            totalduration: function totalduration() {
9914              if (!isFinite(entry.duration) || entry.duration < 0) {
9915                this.trigger('warn', {
9916                  message: 'ignoring invalid total duration: ' + entry.duration
9917                });
9918                return;
9919              }
9920              this.manifest.totalDuration = entry.duration;
9921            },
9922            'cue-out': function cueOut() {
9923              currentUri.cueOut = entry.data;
9924            },
9925            'cue-out-cont': function cueOutCont() {
9926              currentUri.cueOutCont = entry.data;
9927            },
9928            'cue-in': function cueIn() {
9929              currentUri.cueIn = entry.data;
9930            }
9931          })[entry.tagType] || noop).call(self);
9932        },
9933        uri: function uri() {
9934          currentUri.uri = entry.uri;
9935          uris.push(currentUri);
9936
9937          // if no explicit duration was declared, use the target duration
9938          if (this.manifest.targetDuration && !('duration' in currentUri)) {
9939            this.trigger('warn', {
9940              message: 'defaulting segment duration to the target duration'
9941            });
9942            currentUri.duration = this.manifest.targetDuration;
9943          }
9944          // annotate with encryption information, if necessary
9945          if (_key) {
9946            currentUri.key = _key;
9947          }
9948          currentUri.timeline = currentTimeline;
9949          // annotate with initialization segment information, if necessary
9950          if (currentMap) {
9951            currentUri.map = currentMap;
9952          }
9953
9954          // prepare for the next URI
9955          currentUri = {};
9956        },
9957        comment: function comment() {
9958          // comments are not important for playback
9959        }
9960      })[entry.type].call(self);
9961    });
9962
9963    return _this;
9964  }
9965
9966  /**
9967   * Parse the input string and update the manifest object.
9968   *
9969   * @param {String} chunk a potentially incomplete portion of the manifest
9970   */
9971
9972
9973  _createClass(Parser, [{
9974    key: 'push',
9975    value: function push(chunk) {
9976      this.lineStream.push(chunk);
9977    }
9978
9979    /**
9980     * Flush any remaining input. This can be handy if the last line of an M3U8
9981     * manifest did not contain a trailing newline but the file has been
9982     * completely received.
9983     */
9984
9985  }, {
9986    key: 'end',
9987    value: function end() {
9988      // flush any buffered input
9989      this.lineStream.push('\n');
9990    }
9991  }]);
9992
9993  return Parser;
9994}(_stream2['default']);
9995
9996exports['default'] = Parser;
9997},{"./line-stream":34,"./parse-stream":35,"./stream":37}],37:[function(require,module,exports){
9998'use strict';
9999
10000Object.defineProperty(exports, "__esModule", {
10001  value: true
10002});
10003
10004var _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; }; }();
10005
10006function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
10007
10008/**
10009 * @file stream.js
10010 */
10011/**
10012 * A lightweight readable stream implemention that handles event dispatching.
10013 *
10014 * @class Stream
10015 */
10016var Stream = function () {
10017  function Stream() {
10018    _classCallCheck(this, Stream);
10019
10020    this.listeners = {};
10021  }
10022
10023  /**
10024   * Add a listener for a specified event type.
10025   *
10026   * @param {String} type the event name
10027   * @param {Function} listener the callback to be invoked when an event of
10028   * the specified type occurs
10029   */
10030
10031
10032  _createClass(Stream, [{
10033    key: 'on',
10034    value: function on(type, listener) {
10035      if (!this.listeners[type]) {
10036        this.listeners[type] = [];
10037      }
10038      this.listeners[type].push(listener);
10039    }
10040
10041    /**
10042     * Remove a listener for a specified event type.
10043     *
10044     * @param {String} type the event name
10045     * @param {Function} listener  a function previously registered for this
10046     * type of event through `on`
10047     * @return {Boolean} if we could turn it off or not
10048     */
10049
10050  }, {
10051    key: 'off',
10052    value: function off(type, listener) {
10053      if (!this.listeners[type]) {
10054        return false;
10055      }
10056
10057      var index = this.listeners[type].indexOf(listener);
10058
10059      this.listeners[type].splice(index, 1);
10060      return index > -1;
10061    }
10062
10063    /**
10064     * Trigger an event of the specified type on this stream. Any additional
10065     * arguments to this function are passed as parameters to event listeners.
10066     *
10067     * @param {String} type the event name
10068     */
10069
10070  }, {
10071    key: 'trigger',
10072    value: function trigger(type) {
10073      var callbacks = this.listeners[type];
10074      var i = void 0;
10075      var length = void 0;
10076      var args = void 0;
10077
10078      if (!callbacks) {
10079        return;
10080      }
10081      // Slicing the arguments on every invocation of this method
10082      // can add a significant amount of overhead. Avoid the
10083      // intermediate object creation for the common case of a
10084      // single callback argument
10085      if (arguments.length === 2) {
10086        length = callbacks.length;
10087        for (i = 0; i < length; ++i) {
10088          callbacks[i].call(this, arguments[1]);
10089        }
10090      } else {
10091        args = Array.prototype.slice.call(arguments, 1);
10092        length = callbacks.length;
10093        for (i = 0; i < length; ++i) {
10094          callbacks[i].apply(this, args);
10095        }
10096      }
10097    }
10098
10099    /**
10100     * Destroys the stream and cleans up.
10101     */
10102
10103  }, {
10104    key: 'dispose',
10105    value: function dispose() {
10106      this.listeners = {};
10107    }
10108    /**
10109     * Forwards all `data` events on this stream to the destination stream. The
10110     * destination stream should provide a method `push` to receive the data
10111     * events as they arrive.
10112     *
10113     * @param {Stream} destination the stream that will receive all `data` events
10114     * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
10115     */
10116
10117  }, {
10118    key: 'pipe',
10119    value: function pipe(destination) {
10120      this.on('data', function (data) {
10121        destination.push(data);
10122      });
10123    }
10124  }]);
10125
10126  return Stream;
10127}();
10128
10129exports['default'] = Stream;
10130},{}],38:[function(require,module,exports){
10131/**
10132 * mux.js
10133 *
10134 * Copyright (c) 2016 Brightcove
10135 * All rights reserved.
10136 *
10137 * A stream-based aac to mp4 converter. This utility can be used to
10138 * deliver mp4s to a SourceBuffer on platforms that support native
10139 * Media Source Extensions.
10140 */
10141'use strict';
10142var Stream = require('../utils/stream.js');
10143
10144// Constants
10145var AacStream;
10146
10147/**
10148 * Splits an incoming stream of binary data into ADTS and ID3 Frames.
10149 */
10150
10151AacStream = function() {
10152  var
10153    everything = new Uint8Array(),
10154    timeStamp = 0;
10155
10156  AacStream.prototype.init.call(this);
10157
10158  this.setTimestamp = function(timestamp) {
10159    timeStamp = timestamp;
10160  };
10161
10162  this.parseId3TagSize = function(header, byteIndex) {
10163    var
10164      returnSize = (header[byteIndex + 6] << 21) |
10165                   (header[byteIndex + 7] << 14) |
10166                   (header[byteIndex + 8] << 7) |
10167                   (header[byteIndex + 9]),
10168      flags = header[byteIndex + 5],
10169      footerPresent = (flags & 16) >> 4;
10170
10171    if (footerPresent) {
10172      return returnSize + 20;
10173    }
10174    return returnSize + 10;
10175  };
10176
10177  this.parseAdtsSize = function(header, byteIndex) {
10178    var
10179      lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
10180      middle = header[byteIndex + 4] << 3,
10181      highTwo = header[byteIndex + 3] & 0x3 << 11;
10182
10183    return (highTwo | middle) | lowThree;
10184  };
10185
10186  this.push = function(bytes) {
10187    var
10188      frameSize = 0,
10189      byteIndex = 0,
10190      bytesLeft,
10191      chunk,
10192      packet,
10193      tempLength;
10194
10195    // If there are bytes remaining from the last segment, prepend them to the
10196    // bytes that were pushed in
10197    if (everything.length) {
10198      tempLength = everything.length;
10199      everything = new Uint8Array(bytes.byteLength + tempLength);
10200      everything.set(everything.subarray(0, tempLength));
10201      everything.set(bytes, tempLength);
10202    } else {
10203      everything = bytes;
10204    }
10205
10206    while (everything.length - byteIndex >= 3) {
10207      if ((everything[byteIndex] === 'I'.charCodeAt(0)) &&
10208          (everything[byteIndex + 1] === 'D'.charCodeAt(0)) &&
10209          (everything[byteIndex + 2] === '3'.charCodeAt(0))) {
10210
10211        // Exit early because we don't have enough to parse
10212        // the ID3 tag header
10213        if (everything.length - byteIndex < 10) {
10214          break;
10215        }
10216
10217        // check framesize
10218        frameSize = this.parseId3TagSize(everything, byteIndex);
10219
10220        // Exit early if we don't have enough in the buffer
10221        // to emit a full packet
10222        if (frameSize > everything.length) {
10223          break;
10224        }
10225        chunk = {
10226          type: 'timed-metadata',
10227          data: everything.subarray(byteIndex, byteIndex + frameSize)
10228        };
10229        this.trigger('data', chunk);
10230        byteIndex += frameSize;
10231        continue;
10232      } else if ((everything[byteIndex] & 0xff === 0xff) &&
10233                 ((everything[byteIndex + 1] & 0xf0) === 0xf0)) {
10234
10235        // Exit early because we don't have enough to parse
10236        // the ADTS frame header
10237        if (everything.length - byteIndex < 7) {
10238          break;
10239        }
10240
10241        frameSize = this.parseAdtsSize(everything, byteIndex);
10242
10243        // Exit early if we don't have enough in the buffer
10244        // to emit a full packet
10245        if (frameSize > everything.length) {
10246          break;
10247        }
10248
10249        packet = {
10250          type: 'audio',
10251          data: everything.subarray(byteIndex, byteIndex + frameSize),
10252          pts: timeStamp,
10253          dts: timeStamp
10254        };
10255        this.trigger('data', packet);
10256        byteIndex += frameSize;
10257        continue;
10258      }
10259      byteIndex++;
10260    }
10261    bytesLeft = everything.length - byteIndex;
10262
10263    if (bytesLeft > 0) {
10264      everything = everything.subarray(byteIndex);
10265    } else {
10266      everything = new Uint8Array();
10267    }
10268  };
10269};
10270
10271AacStream.prototype = new Stream();
10272
10273module.exports = AacStream;
10274
10275},{"../utils/stream.js":62}],39:[function(require,module,exports){
10276/**
10277 * mux.js
10278 *
10279 * Copyright (c) 2016 Brightcove
10280 * All rights reserved.
10281 *
10282 * Utilities to detect basic properties and metadata about Aac data.
10283 */
10284'use strict';
10285
10286var ADTS_SAMPLING_FREQUENCIES = [
10287  96000,
10288  88200,
10289  64000,
10290  48000,
10291  44100,
10292  32000,
10293  24000,
10294  22050,
10295  16000,
10296  12000,
10297  11025,
10298  8000,
10299  7350
10300];
10301
10302var parseSyncSafeInteger = function(data) {
10303  return (data[0] << 21) |
10304          (data[1] << 14) |
10305          (data[2] << 7) |
10306          (data[3]);
10307};
10308
10309// return a percent-encoded representation of the specified byte range
10310// @see http://en.wikipedia.org/wiki/Percent-encoding
10311var percentEncode = function(bytes, start, end) {
10312  var i, result = '';
10313  for (i = start; i < end; i++) {
10314    result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
10315  }
10316  return result;
10317};
10318
10319// return the string representation of the specified byte range,
10320// interpreted as ISO-8859-1.
10321var parseIso88591 = function(bytes, start, end) {
10322  return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
10323};
10324
10325var parseId3TagSize = function(header, byteIndex) {
10326  var
10327    returnSize = (header[byteIndex + 6] << 21) |
10328                 (header[byteIndex + 7] << 14) |
10329                 (header[byteIndex + 8] << 7) |
10330                 (header[byteIndex + 9]),
10331    flags = header[byteIndex + 5],
10332    footerPresent = (flags & 16) >> 4;
10333
10334  if (footerPresent) {
10335    return returnSize + 20;
10336  }
10337  return returnSize + 10;
10338};
10339
10340var parseAdtsSize = function(header, byteIndex) {
10341  var
10342    lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
10343    middle = header[byteIndex + 4] << 3,
10344    highTwo = header[byteIndex + 3] & 0x3 << 11;
10345
10346  return (highTwo | middle) | lowThree;
10347};
10348
10349var parseType = function(header, byteIndex) {
10350  if ((header[byteIndex] === 'I'.charCodeAt(0)) &&
10351      (header[byteIndex + 1] === 'D'.charCodeAt(0)) &&
10352      (header[byteIndex + 2] === '3'.charCodeAt(0))) {
10353    return 'timed-metadata';
10354  } else if ((header[byteIndex] & 0xff === 0xff) &&
10355             ((header[byteIndex + 1] & 0xf0) === 0xf0)) {
10356    return 'audio';
10357  }
10358  return null;
10359};
10360
10361var parseSampleRate = function(packet) {
10362  var i = 0;
10363
10364  while (i + 5 < packet.length) {
10365    if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
10366      // If a valid header was not found,  jump one forward and attempt to
10367      // find a valid ADTS header starting at the next byte
10368      i++;
10369      continue;
10370    }
10371    return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2];
10372  }
10373
10374  return null;
10375};
10376
10377var parseAacTimestamp = function(packet) {
10378  var frameStart, frameSize, frame, frameHeader;
10379
10380  // find the start of the first frame and the end of the tag
10381  frameStart = 10;
10382  if (packet[5] & 0x40) {
10383    // advance the frame start past the extended header
10384    frameStart += 4; // header size field
10385    frameStart += parseSyncSafeInteger(packet.subarray(10, 14));
10386  }
10387
10388  // parse one or more ID3 frames
10389  // http://id3.org/id3v2.3.0#ID3v2_frame_overview
10390  do {
10391    // determine the number of bytes in this frame
10392    frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8));
10393    if (frameSize < 1) {
10394      return null;
10395    }
10396    frameHeader = String.fromCharCode(packet[frameStart],
10397                                      packet[frameStart + 1],
10398                                      packet[frameStart + 2],
10399                                      packet[frameStart + 3]);
10400
10401    if (frameHeader === 'PRIV') {
10402      frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
10403
10404      for (var i = 0; i < frame.byteLength; i++) {
10405        if (frame[i] === 0) {
10406          var owner = parseIso88591(frame, 0, i);
10407          if (owner === 'com.apple.streaming.transportStreamTimestamp') {
10408            var d = frame.subarray(i + 1);
10409            var size = ((d[3] & 0x01)  << 30) |
10410                       (d[4]  << 22) |
10411                       (d[5] << 14) |
10412                       (d[6] << 6) |
10413                       (d[7] >>> 2);
10414            size *= 4;
10415            size += d[7] & 0x03;
10416
10417            return size;
10418          }
10419          break;
10420        }
10421      }
10422    }
10423
10424    frameStart += 10; // advance past the frame header
10425    frameStart += frameSize; // advance past the frame body
10426  } while (frameStart < packet.byteLength);
10427  return null;
10428};
10429
10430module.exports = {
10431  parseId3TagSize: parseId3TagSize,
10432  parseAdtsSize: parseAdtsSize,
10433  parseType: parseType,
10434  parseSampleRate: parseSampleRate,
10435  parseAacTimestamp: parseAacTimestamp
10436};
10437
10438},{}],40:[function(require,module,exports){
10439'use strict';
10440
10441var Stream = require('../utils/stream.js');
10442
10443var AdtsStream;
10444
10445var
10446  ADTS_SAMPLING_FREQUENCIES = [
10447    96000,
10448    88200,
10449    64000,
10450    48000,
10451    44100,
10452    32000,
10453    24000,
10454    22050,
10455    16000,
10456    12000,
10457    11025,
10458    8000,
10459    7350
10460  ];
10461
10462/*
10463 * Accepts a ElementaryStream and emits data events with parsed
10464 * AAC Audio Frames of the individual packets. Input audio in ADTS
10465 * format is unpacked and re-emitted as AAC frames.
10466 *
10467 * @see http://wiki.multimedia.cx/index.php?title=ADTS
10468 * @see http://wiki.multimedia.cx/?title=Understanding_AAC
10469 */
10470AdtsStream = function() {
10471  var buffer;
10472
10473  AdtsStream.prototype.init.call(this);
10474
10475  this.push = function(packet) {
10476    var
10477      i = 0,
10478      frameNum = 0,
10479      frameLength,
10480      protectionSkipBytes,
10481      frameEnd,
10482      oldBuffer,
10483      sampleCount,
10484      adtsFrameDuration;
10485
10486    if (packet.type !== 'audio') {
10487      // ignore non-audio data
10488      return;
10489    }
10490
10491    // Prepend any data in the buffer to the input data so that we can parse
10492    // aac frames the cross a PES packet boundary
10493    if (buffer) {
10494      oldBuffer = buffer;
10495      buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
10496      buffer.set(oldBuffer);
10497      buffer.set(packet.data, oldBuffer.byteLength);
10498    } else {
10499      buffer = packet.data;
10500    }
10501
10502    // unpack any ADTS frames which have been fully received
10503    // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
10504    while (i + 5 < buffer.length) {
10505
10506      // Loook for the start of an ADTS header..
10507      if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
10508        // If a valid header was not found,  jump one forward and attempt to
10509        // find a valid ADTS header starting at the next byte
10510        i++;
10511        continue;
10512      }
10513
10514      // The protection skip bit tells us if we have 2 bytes of CRC data at the
10515      // end of the ADTS header
10516      protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
10517
10518      // Frame length is a 13 bit integer starting 16 bits from the
10519      // end of the sync sequence
10520      frameLength = ((buffer[i + 3] & 0x03) << 11) |
10521        (buffer[i + 4] << 3) |
10522        ((buffer[i + 5] & 0xe0) >> 5);
10523
10524      sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
10525      adtsFrameDuration = (sampleCount * 90000) /
10526        ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
10527
10528      frameEnd = i + frameLength;
10529
10530      // If we don't have enough data to actually finish this ADTS frame, return
10531      // and wait for more data
10532      if (buffer.byteLength < frameEnd) {
10533        return;
10534      }
10535
10536      // Otherwise, deliver the complete AAC frame
10537      this.trigger('data', {
10538        pts: packet.pts + (frameNum * adtsFrameDuration),
10539        dts: packet.dts + (frameNum * adtsFrameDuration),
10540        sampleCount: sampleCount,
10541        audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
10542        channelcount: ((buffer[i + 2] & 1) << 2) |
10543          ((buffer[i + 3] & 0xc0) >>> 6),
10544        samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
10545        samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
10546        // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
10547        samplesize: 16,
10548        data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
10549      });
10550
10551      // If the buffer is empty, clear it and return
10552      if (buffer.byteLength === frameEnd) {
10553        buffer = undefined;
10554        return;
10555      }
10556
10557      frameNum++;
10558
10559      // Remove the finished frame from the buffer and start the process again
10560      buffer = buffer.subarray(frameEnd);
10561    }
10562  };
10563  this.flush = function() {
10564    this.trigger('done');
10565  };
10566};
10567
10568AdtsStream.prototype = new Stream();
10569
10570module.exports = AdtsStream;
10571
10572},{"../utils/stream.js":62}],41:[function(require,module,exports){
10573'use strict';
10574
10575var Stream = require('../utils/stream.js');
10576var ExpGolomb = require('../utils/exp-golomb.js');
10577
10578var H264Stream, NalByteStream;
10579var PROFILES_WITH_OPTIONAL_SPS_DATA;
10580
10581/**
10582 * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
10583 */
10584NalByteStream = function() {
10585  var
10586    syncPoint = 0,
10587    i,
10588    buffer;
10589  NalByteStream.prototype.init.call(this);
10590
10591  this.push = function(data) {
10592    var swapBuffer;
10593
10594    if (!buffer) {
10595      buffer = data.data;
10596    } else {
10597      swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
10598      swapBuffer.set(buffer);
10599      swapBuffer.set(data.data, buffer.byteLength);
10600      buffer = swapBuffer;
10601    }
10602
10603    // Rec. ITU-T H.264, Annex B
10604    // scan for NAL unit boundaries
10605
10606    // a match looks like this:
10607    // 0 0 1 .. NAL .. 0 0 1
10608    // ^ sync point        ^ i
10609    // or this:
10610    // 0 0 1 .. NAL .. 0 0 0
10611    // ^ sync point        ^ i
10612
10613    // advance the sync point to a NAL start, if necessary
10614    for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
10615      if (buffer[syncPoint + 2] === 1) {
10616        // the sync point is properly aligned
10617        i = syncPoint + 5;
10618        break;
10619      }
10620    }
10621
10622    while (i < buffer.byteLength) {
10623      // look at the current byte to determine if we've hit the end of
10624      // a NAL unit boundary
10625      switch (buffer[i]) {
10626      case 0:
10627        // skip past non-sync sequences
10628        if (buffer[i - 1] !== 0) {
10629          i += 2;
10630          break;
10631        } else if (buffer[i - 2] !== 0) {
10632          i++;
10633          break;
10634        }
10635
10636        // deliver the NAL unit if it isn't empty
10637        if (syncPoint + 3 !== i - 2) {
10638          this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
10639        }
10640
10641        // drop trailing zeroes
10642        do {
10643          i++;
10644        } while (buffer[i] !== 1 && i < buffer.length);
10645        syncPoint = i - 2;
10646        i += 3;
10647        break;
10648      case 1:
10649        // skip past non-sync sequences
10650        if (buffer[i - 1] !== 0 ||
10651            buffer[i - 2] !== 0) {
10652          i += 3;
10653          break;
10654        }
10655
10656        // deliver the NAL unit
10657        this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
10658        syncPoint = i - 2;
10659        i += 3;
10660        break;
10661      default:
10662        // the current byte isn't a one or zero, so it cannot be part
10663        // of a sync sequence
10664        i += 3;
10665        break;
10666      }
10667    }
10668    // filter out the NAL units that were delivered
10669    buffer = buffer.subarray(syncPoint);
10670    i -= syncPoint;
10671    syncPoint = 0;
10672  };
10673
10674  this.flush = function() {
10675    // deliver the last buffered NAL unit
10676    if (buffer && buffer.byteLength > 3) {
10677      this.trigger('data', buffer.subarray(syncPoint + 3));
10678    }
10679    // reset the stream state
10680    buffer = null;
10681    syncPoint = 0;
10682    this.trigger('done');
10683  };
10684};
10685NalByteStream.prototype = new Stream();
10686
10687// values of profile_idc that indicate additional fields are included in the SPS
10688// see Recommendation ITU-T H.264 (4/2013),
10689// 7.3.2.1.1 Sequence parameter set data syntax
10690PROFILES_WITH_OPTIONAL_SPS_DATA = {
10691  100: true,
10692  110: true,
10693  122: true,
10694  244: true,
10695  44: true,
10696  83: true,
10697  86: true,
10698  118: true,
10699  128: true,
10700  138: true,
10701  139: true,
10702  134: true
10703};
10704
10705/**
10706 * Accepts input from a ElementaryStream and produces H.264 NAL unit data
10707 * events.
10708 */
10709H264Stream = function() {
10710  var
10711    nalByteStream = new NalByteStream(),
10712    self,
10713    trackId,
10714    currentPts,
10715    currentDts,
10716
10717    discardEmulationPreventionBytes,
10718    readSequenceParameterSet,
10719    skipScalingList;
10720
10721  H264Stream.prototype.init.call(this);
10722  self = this;
10723
10724  this.push = function(packet) {
10725    if (packet.type !== 'video') {
10726      return;
10727    }
10728    trackId = packet.trackId;
10729    currentPts = packet.pts;
10730    currentDts = packet.dts;
10731
10732    nalByteStream.push(packet);
10733  };
10734
10735  nalByteStream.on('data', function(data) {
10736    var
10737      event = {
10738        trackId: trackId,
10739        pts: currentPts,
10740        dts: currentDts,
10741        data: data
10742      };
10743
10744    switch (data[0] & 0x1f) {
10745    case 0x05:
10746      event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
10747      break;
10748    case 0x06:
10749      event.nalUnitType = 'sei_rbsp';
10750      event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
10751      break;
10752    case 0x07:
10753      event.nalUnitType = 'seq_parameter_set_rbsp';
10754      event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
10755      event.config = readSequenceParameterSet(event.escapedRBSP);
10756      break;
10757    case 0x08:
10758      event.nalUnitType = 'pic_parameter_set_rbsp';
10759      break;
10760    case 0x09:
10761      event.nalUnitType = 'access_unit_delimiter_rbsp';
10762      break;
10763
10764    default:
10765      break;
10766    }
10767    self.trigger('data', event);
10768  });
10769  nalByteStream.on('done', function() {
10770    self.trigger('done');
10771  });
10772
10773  this.flush = function() {
10774    nalByteStream.flush();
10775  };
10776
10777  /**
10778   * Advance the ExpGolomb decoder past a scaling list. The scaling
10779   * list is optionally transmitted as part of a sequence parameter
10780   * set and is not relevant to transmuxing.
10781   * @param count {number} the number of entries in this scaling list
10782   * @param expGolombDecoder {object} an ExpGolomb pointed to the
10783   * start of a scaling list
10784   * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
10785   */
10786  skipScalingList = function(count, expGolombDecoder) {
10787    var
10788      lastScale = 8,
10789      nextScale = 8,
10790      j,
10791      deltaScale;
10792
10793    for (j = 0; j < count; j++) {
10794      if (nextScale !== 0) {
10795        deltaScale = expGolombDecoder.readExpGolomb();
10796        nextScale = (lastScale + deltaScale + 256) % 256;
10797      }
10798
10799      lastScale = (nextScale === 0) ? lastScale : nextScale;
10800    }
10801  };
10802
10803  /**
10804   * Expunge any "Emulation Prevention" bytes from a "Raw Byte
10805   * Sequence Payload"
10806   * @param data {Uint8Array} the bytes of a RBSP from a NAL
10807   * unit
10808   * @return {Uint8Array} the RBSP without any Emulation
10809   * Prevention Bytes
10810   */
10811  discardEmulationPreventionBytes = function(data) {
10812    var
10813      length = data.byteLength,
10814      emulationPreventionBytesPositions = [],
10815      i = 1,
10816      newLength, newData;
10817
10818    // Find all `Emulation Prevention Bytes`
10819    while (i < length - 2) {
10820      if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
10821        emulationPreventionBytesPositions.push(i + 2);
10822        i += 2;
10823      } else {
10824        i++;
10825      }
10826    }
10827
10828    // If no Emulation Prevention Bytes were found just return the original
10829    // array
10830    if (emulationPreventionBytesPositions.length === 0) {
10831      return data;
10832    }
10833
10834    // Create a new array to hold the NAL unit data
10835    newLength = length - emulationPreventionBytesPositions.length;
10836    newData = new Uint8Array(newLength);
10837    var sourceIndex = 0;
10838
10839    for (i = 0; i < newLength; sourceIndex++, i++) {
10840      if (sourceIndex === emulationPreventionBytesPositions[0]) {
10841        // Skip this byte
10842        sourceIndex++;
10843        // Remove this position index
10844        emulationPreventionBytesPositions.shift();
10845      }
10846      newData[i] = data[sourceIndex];
10847    }
10848
10849    return newData;
10850  };
10851
10852  /**
10853   * Read a sequence parameter set and return some interesting video
10854   * properties. A sequence parameter set is the H264 metadata that
10855   * describes the properties of upcoming video frames.
10856   * @param data {Uint8Array} the bytes of a sequence parameter set
10857   * @return {object} an object with configuration parsed from the
10858   * sequence parameter set, including the dimensions of the
10859   * associated video frames.
10860   */
10861  readSequenceParameterSet = function(data) {
10862    var
10863      frameCropLeftOffset = 0,
10864      frameCropRightOffset = 0,
10865      frameCropTopOffset = 0,
10866      frameCropBottomOffset = 0,
10867      sarScale = 1,
10868      expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
10869      chromaFormatIdc, picOrderCntType,
10870      numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
10871      picHeightInMapUnitsMinus1,
10872      frameMbsOnlyFlag,
10873      scalingListCount,
10874      sarRatio,
10875      aspectRatioIdc,
10876      i;
10877
10878    expGolombDecoder = new ExpGolomb(data);
10879    profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
10880    profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
10881    levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
10882    expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
10883
10884    // some profiles have more optional data we don't need
10885    if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
10886      chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
10887      if (chromaFormatIdc === 3) {
10888        expGolombDecoder.skipBits(1); // separate_colour_plane_flag
10889      }
10890      expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
10891      expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
10892      expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
10893      if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
10894        scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
10895        for (i = 0; i < scalingListCount; i++) {
10896          if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
10897            if (i < 6) {
10898              skipScalingList(16, expGolombDecoder);
10899            } else {
10900              skipScalingList(64, expGolombDecoder);
10901            }
10902          }
10903        }
10904      }
10905    }
10906
10907    expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
10908    picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
10909
10910    if (picOrderCntType === 0) {
10911      expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
10912    } else if (picOrderCntType === 1) {
10913      expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
10914      expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
10915      expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
10916      numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
10917      for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
10918        expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
10919      }
10920    }
10921
10922    expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
10923    expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
10924
10925    picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
10926    picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
10927
10928    frameMbsOnlyFlag = expGolombDecoder.readBits(1);
10929    if (frameMbsOnlyFlag === 0) {
10930      expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
10931    }
10932
10933    expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
10934    if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
10935      frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
10936      frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
10937      frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
10938      frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
10939    }
10940    if (expGolombDecoder.readBoolean()) {
10941      // vui_parameters_present_flag
10942      if (expGolombDecoder.readBoolean()) {
10943        // aspect_ratio_info_present_flag
10944        aspectRatioIdc = expGolombDecoder.readUnsignedByte();
10945        switch (aspectRatioIdc) {
10946          case 1: sarRatio = [1, 1]; break;
10947          case 2: sarRatio = [12, 11]; break;
10948          case 3: sarRatio = [10, 11]; break;
10949          case 4: sarRatio = [16, 11]; break;
10950          case 5: sarRatio = [40, 33]; break;
10951          case 6: sarRatio = [24, 11]; break;
10952          case 7: sarRatio = [20, 11]; break;
10953          case 8: sarRatio = [32, 11]; break;
10954          case 9: sarRatio = [80, 33]; break;
10955          case 10: sarRatio = [18, 11]; break;
10956          case 11: sarRatio = [15, 11]; break;
10957          case 12: sarRatio = [64, 33]; break;
10958          case 13: sarRatio = [160, 99]; break;
10959          case 14: sarRatio = [4, 3]; break;
10960          case 15: sarRatio = [3, 2]; break;
10961          case 16: sarRatio = [2, 1]; break;
10962          case 255: {
10963            sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
10964                        expGolombDecoder.readUnsignedByte(),
10965                        expGolombDecoder.readUnsignedByte() << 8 |
10966                        expGolombDecoder.readUnsignedByte() ];
10967            break;
10968          }
10969        }
10970        if (sarRatio) {
10971          sarScale = sarRatio[0] / sarRatio[1];
10972        }
10973      }
10974    }
10975    return {
10976      profileIdc: profileIdc,
10977      levelIdc: levelIdc,
10978      profileCompatibility: profileCompatibility,
10979      width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
10980      height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2)
10981    };
10982  };
10983
10984};
10985H264Stream.prototype = new Stream();
10986
10987module.exports = {
10988  H264Stream: H264Stream,
10989  NalByteStream: NalByteStream
10990};
10991
10992},{"../utils/exp-golomb.js":61,"../utils/stream.js":62}],42:[function(require,module,exports){
10993var highPrefix = [33, 16, 5, 32, 164, 27];
10994var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
10995var zeroFill = function(count) {
10996  var a = [];
10997  while (count--) {
10998    a.push(0);
10999  }
11000  return a;
11001};
11002
11003var makeTable = function(metaTable) {
11004  return Object.keys(metaTable).reduce(function(obj, key) {
11005    obj[key] = new Uint8Array(metaTable[key].reduce(function(arr, part) {
11006      return arr.concat(part);
11007    }, []));
11008    return obj;
11009  }, {});
11010};
11011
11012// Frames-of-silence to use for filling in missing AAC frames
vendor: 4,575 bytes, lines 11013-11141
11013var coneOfSilence = {
11014  96000: [highPrefix, [227, 64], zeroFill(154), [56]],
11015  88200: [highPrefix, [231], zeroFill(170), [56]],
11016  64000: [highPrefix, [248, 192], zeroFill(240), [56]],
11017  48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
11018  44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
11019  32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
11020  24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
11021  16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
11022  12000: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 253, 128], zeroFill(259), [56]],
11023  11025: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 255, 192], zeroFill(268), [55, 175, 128], zeroFill(108), [112]],
11024  8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
11025};
11026
11027module.exports = makeTable(coneOfSilence);
11028
11029},{}],43:[function(require,module,exports){
11030'use strict';
11031
11032var Stream = require('../utils/stream.js');
11033
11034/**
11035 * The final stage of the transmuxer that emits the flv tags
11036 * for audio, video, and metadata. Also tranlates in time and
11037 * outputs caption data and id3 cues.
11038 */
11039var CoalesceStream = function(options) {
11040  // Number of Tracks per output segment
11041  // If greater than 1, we combine multiple
11042  // tracks into a single segment
11043  this.numberOfTracks = 0;
11044  this.metadataStream = options.metadataStream;
11045
11046  this.videoTags = [];
11047  this.audioTags = [];
11048  this.videoTrack = null;
11049  this.audioTrack = null;
11050  this.pendingCaptions = [];
11051  this.pendingMetadata = [];
11052  this.pendingTracks = 0;
11053  this.processedTracks = 0;
11054
11055  CoalesceStream.prototype.init.call(this);
11056
11057  // Take output from multiple
11058  this.push = function(output) {
11059    // buffer incoming captions until the associated video segment
11060    // finishes
11061    if (output.text) {
11062      return this.pendingCaptions.push(output);
11063    }
11064    // buffer incoming id3 tags until the final flush
11065    if (output.frames) {
11066      return this.pendingMetadata.push(output);
11067    }
11068
11069    if (output.track.type === 'video') {
11070      this.videoTrack = output.track;
11071      this.videoTags = output.tags;
11072      this.pendingTracks++;
11073    }
11074    if (output.track.type === 'audio') {
11075      this.audioTrack = output.track;
11076      this.audioTags = output.tags;
11077      this.pendingTracks++;
11078    }
11079  };
11080};
11081
11082CoalesceStream.prototype = new Stream();
11083CoalesceStream.prototype.flush = function(flushSource) {
11084  var
11085    id3,
11086    caption,
11087    i,
11088    timelineStartPts,
11089    event = {
11090      tags: {},
11091      captions: [],
11092      captionStreams: {},
11093      metadata: []
11094    };
11095
11096  if (this.pendingTracks < this.numberOfTracks) {
11097    if (flushSource !== 'VideoSegmentStream' &&
11098        flushSource !== 'AudioSegmentStream') {
11099      // Return because we haven't received a flush from a data-generating
11100      // portion of the segment (meaning that we have only recieved meta-data
11101      // or captions.)
11102      return;
11103    } else if (this.pendingTracks === 0) {
11104      // In the case where we receive a flush without any data having been
11105      // received we consider it an emitted track for the purposes of coalescing
11106      // `done` events.
11107      // We do this for the case where there is an audio and video track in the
11108      // segment but no audio data. (seen in several playlists with alternate
11109      // audio tracks and no audio present in the main TS segments.)
11110      this.processedTracks++;
11111
11112      if (this.processedTracks < this.numberOfTracks) {
11113        return;
11114      }
11115    }
11116  }
11117
11118  this.processedTracks += this.pendingTracks;
11119  this.pendingTracks = 0;
11120
11121  if (this.processedTracks < this.numberOfTracks) {
11122    return;
11123  }
11124
11125  if (this.videoTrack) {
11126    timelineStartPts = this.videoTrack.timelineStartInfo.pts;
11127  } else if (this.audioTrack) {
11128    timelineStartPts = this.audioTrack.timelineStartInfo.pts;
11129  }
11130
11131  event.tags.videoTags = this.videoTags;
11132  event.tags.audioTags = this.audioTags;
11133
11134  // Translate caption PTS times into second offsets into the
11135  // video timeline for the segment, and add track info
11136  for (i = 0; i < this.pendingCaptions.length; i++) {
11137    caption = this.pendingCaptions[i];
11138    caption.startTime = caption.startPts - timelineStartPts;
11139    caption.startTime /= 90e3;
11140    caption.endTime = caption.endPts - timelineStartPts;
11141    caption.endTime /= 90e3;
vendor: 28,064 bytes, lines 11142-12104
11142    event.captionStreams[caption.stream] = true;
11143    event.captions.push(caption);
11144  }
11145
11146  // Translate ID3 frame PTS times into second offsets into the
11147  // video timeline for the segment
11148  for (i = 0; i < this.pendingMetadata.length; i++) {
11149    id3 = this.pendingMetadata[i];
11150    id3.cueTime = id3.pts - timelineStartPts;
11151    id3.cueTime /= 90e3;
11152    event.metadata.push(id3);
11153  }
11154  // We add this to every single emitted segment even though we only need
11155  // it for the first
11156  event.metadata.dispatchType = this.metadataStream.dispatchType;
11157
11158  // Reset stream state
11159  this.videoTrack = null;
11160  this.audioTrack = null;
11161  this.videoTags = [];
11162  this.audioTags = [];
11163  this.pendingCaptions.length = 0;
11164  this.pendingMetadata.length = 0;
11165  this.pendingTracks = 0;
11166  this.processedTracks = 0;
11167
11168  // Emit the final segment
11169  this.trigger('data', event);
11170
11171  this.trigger('done');
11172};
11173
11174module.exports = CoalesceStream;
11175
11176},{"../utils/stream.js":62}],44:[function(require,module,exports){
11177'use strict';
11178
11179var FlvTag = require('./flv-tag.js');
11180
11181// For information on the FLV format, see
11182// http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf.
11183// Technically, this function returns the header and a metadata FLV tag
11184// if duration is greater than zero
11185// duration in seconds
11186// @return {object} the bytes of the FLV header as a Uint8Array
11187var getFlvHeader = function(duration, audio, video) { // :ByteArray {
11188  var
11189    headBytes = new Uint8Array(3 + 1 + 1 + 4),
11190    head = new DataView(headBytes.buffer),
11191    metadata,
11192    result,
11193    metadataLength;
11194
11195  // default arguments
11196  duration = duration || 0;
11197  audio = audio === undefined ? true : audio;
11198  video = video === undefined ? true : video;
11199
11200  // signature
11201  head.setUint8(0, 0x46); // 'F'
11202  head.setUint8(1, 0x4c); // 'L'
11203  head.setUint8(2, 0x56); // 'V'
11204
11205  // version
11206  head.setUint8(3, 0x01);
11207
11208  // flags
11209  head.setUint8(4, (audio ? 0x04 : 0x00) | (video ? 0x01 : 0x00));
11210
11211  // data offset, should be 9 for FLV v1
11212  head.setUint32(5, headBytes.byteLength);
11213
11214  // init the first FLV tag
11215  if (duration <= 0) {
11216    // no duration available so just write the first field of the first
11217    // FLV tag
11218    result = new Uint8Array(headBytes.byteLength + 4);
11219    result.set(headBytes);
11220    result.set([0, 0, 0, 0], headBytes.byteLength);
11221    return result;
11222  }
11223
11224  // write out the duration metadata tag
11225  metadata = new FlvTag(FlvTag.METADATA_TAG);
11226  metadata.pts = metadata.dts = 0;
11227  metadata.writeMetaDataDouble('duration', duration);
11228  metadataLength = metadata.finalize().length;
11229  result = new Uint8Array(headBytes.byteLength + metadataLength);
11230  result.set(headBytes);
11231  result.set(head.byteLength, metadataLength);
11232
11233  return result;
11234};
11235
11236module.exports = getFlvHeader;
11237
11238},{"./flv-tag.js":45}],45:[function(require,module,exports){
11239/**
11240 * An object that stores the bytes of an FLV tag and methods for
11241 * querying and manipulating that data.
11242 * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
11243 */
11244'use strict';
11245
11246var FlvTag;
11247
11248// (type:uint, extraData:Boolean = false) extends ByteArray
11249FlvTag = function(type, extraData) {
11250  var
11251    // Counter if this is a metadata tag, nal start marker if this is a video
11252    // tag. unused if this is an audio tag
11253    adHoc = 0, // :uint
11254
11255    // The default size is 16kb but this is not enough to hold iframe
11256    // data and the resizing algorithm costs a bit so we create a larger
11257    // starting buffer for video tags
11258    bufferStartSize = 16384,
11259
11260    // checks whether the FLV tag has enough capacity to accept the proposed
11261    // write and re-allocates the internal buffers if necessary
11262    prepareWrite = function(flv, count) {
11263      var
11264        bytes,
11265        minLength = flv.position + count;
11266      if (minLength < flv.bytes.byteLength) {
11267        // there's enough capacity so do nothing
11268        return;
11269      }
11270
11271      // allocate a new buffer and copy over the data that will not be modified
11272      bytes = new Uint8Array(minLength * 2);
11273      bytes.set(flv.bytes.subarray(0, flv.position), 0);
11274      flv.bytes = bytes;
11275      flv.view = new DataView(flv.bytes.buffer);
11276    },
11277
11278    // commonly used metadata properties
11279    widthBytes = FlvTag.widthBytes || new Uint8Array('width'.length),
11280    heightBytes = FlvTag.heightBytes || new Uint8Array('height'.length),
11281    videocodecidBytes = FlvTag.videocodecidBytes || new Uint8Array('videocodecid'.length),
11282    i;
11283
11284  if (!FlvTag.widthBytes) {
11285    // calculating the bytes of common metadata names ahead of time makes the
11286    // corresponding writes faster because we don't have to loop over the
11287    // characters
11288    // re-test with test/perf.html if you're planning on changing this
11289    for (i = 0; i < 'width'.length; i++) {
11290      widthBytes[i] = 'width'.charCodeAt(i);
11291    }
11292    for (i = 0; i < 'height'.length; i++) {
11293      heightBytes[i] = 'height'.charCodeAt(i);
11294    }
11295    for (i = 0; i < 'videocodecid'.length; i++) {
11296      videocodecidBytes[i] = 'videocodecid'.charCodeAt(i);
11297    }
11298
11299    FlvTag.widthBytes = widthBytes;
11300    FlvTag.heightBytes = heightBytes;
11301    FlvTag.videocodecidBytes = videocodecidBytes;
11302  }
11303
11304  this.keyFrame = false; // :Boolean
11305
11306  switch (type) {
11307  case FlvTag.VIDEO_TAG:
11308    this.length = 16;
11309    // Start the buffer at 256k
11310    bufferStartSize *= 6;
11311    break;
11312  case FlvTag.AUDIO_TAG:
11313    this.length = 13;
11314    this.keyFrame = true;
11315    break;
11316  case FlvTag.METADATA_TAG:
11317    this.length = 29;
11318    this.keyFrame = true;
11319    break;
11320  default:
11321    throw new Error('Unknown FLV tag type');
11322  }
11323
11324  this.bytes = new Uint8Array(bufferStartSize);
11325  this.view = new DataView(this.bytes.buffer);
11326  this.bytes[0] = type;
11327  this.position = this.length;
11328  this.keyFrame = extraData; // Defaults to false
11329
11330  // presentation timestamp
11331  this.pts = 0;
11332  // decoder timestamp
11333  this.dts = 0;
11334
11335  // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0)
11336  this.writeBytes = function(bytes, offset, length) {
11337    var
11338      start = offset || 0,
11339      end;
11340    length = length || bytes.byteLength;
11341    end = start + length;
11342
11343    prepareWrite(this, length);
11344    this.bytes.set(bytes.subarray(start, end), this.position);
11345
11346    this.position += length;
11347    this.length = Math.max(this.length, this.position);
11348  };
11349
11350  // ByteArray#writeByte(value:int):void
11351  this.writeByte = function(byte) {
11352    prepareWrite(this, 1);
11353    this.bytes[this.position] = byte;
11354    this.position++;
11355    this.length = Math.max(this.length, this.position);
11356  };
11357
11358  // ByteArray#writeShort(value:int):void
11359  this.writeShort = function(short) {
11360    prepareWrite(this, 2);
11361    this.view.setUint16(this.position, short);
11362    this.position += 2;
11363    this.length = Math.max(this.length, this.position);
11364  };
11365
11366  // Negative index into array
11367  // (pos:uint):int
11368  this.negIndex = function(pos) {
11369    return this.bytes[this.length - pos];
11370  };
11371
11372  // The functions below ONLY work when this[0] == VIDEO_TAG.
11373  // We are not going to check for that because we dont want the overhead
11374  // (nal:ByteArray = null):int
11375  this.nalUnitSize = function() {
11376    if (adHoc === 0) {
11377      return 0;
11378    }
11379
11380    return this.length - (adHoc + 4);
11381  };
11382
11383  this.startNalUnit = function() {
11384    // remember position and add 4 bytes
11385    if (adHoc > 0) {
11386      throw new Error('Attempted to create new NAL wihout closing the old one');
11387    }
11388
11389    // reserve 4 bytes for nal unit size
11390    adHoc = this.length;
11391    this.length += 4;
11392    this.position = this.length;
11393  };
11394
11395  // (nal:ByteArray = null):void
11396  this.endNalUnit = function(nalContainer) {
11397    var
11398      nalStart, // :uint
11399      nalLength; // :uint
11400
11401    // Rewind to the marker and write the size
11402    if (this.length === adHoc + 4) {
11403      // we started a nal unit, but didnt write one, so roll back the 4 byte size value
11404      this.length -= 4;
11405    } else if (adHoc > 0) {
11406      nalStart = adHoc + 4;
11407      nalLength = this.length - nalStart;
11408
11409      this.position = adHoc;
11410      this.view.setUint32(this.position, nalLength);
11411      this.position = this.length;
11412
11413      if (nalContainer) {
11414        // Add the tag to the NAL unit
11415        nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength));
11416      }
11417    }
11418
11419    adHoc = 0;
11420  };
11421
11422  /**
11423   * Write out a 64-bit floating point valued metadata property. This method is
11424   * called frequently during a typical parse and needs to be fast.
11425   */
11426  // (key:String, val:Number):void
11427  this.writeMetaDataDouble = function(key, val) {
11428    var i;
11429    prepareWrite(this, 2 + key.length + 9);
11430
11431    // write size of property name
11432    this.view.setUint16(this.position, key.length);
11433    this.position += 2;
11434
11435    // this next part looks terrible but it improves parser throughput by
11436    // 10kB/s in my testing
11437
11438    // write property name
11439    if (key === 'width') {
11440      this.bytes.set(widthBytes, this.position);
11441      this.position += 5;
11442    } else if (key === 'height') {
11443      this.bytes.set(heightBytes, this.position);
11444      this.position += 6;
11445    } else if (key === 'videocodecid') {
11446      this.bytes.set(videocodecidBytes, this.position);
11447      this.position += 12;
11448    } else {
11449      for (i = 0; i < key.length; i++) {
11450        this.bytes[this.position] = key.charCodeAt(i);
11451        this.position++;
11452      }
11453    }
11454
11455    // skip null byte
11456    this.position++;
11457
11458    // write property value
11459    this.view.setFloat64(this.position, val);
11460    this.position += 8;
11461
11462    // update flv tag length
11463    this.length = Math.max(this.length, this.position);
11464    ++adHoc;
11465  };
11466
11467  // (key:String, val:Boolean):void
11468  this.writeMetaDataBoolean = function(key, val) {
11469    var i;
11470    prepareWrite(this, 2);
11471    this.view.setUint16(this.position, key.length);
11472    this.position += 2;
11473    for (i = 0; i < key.length; i++) {
11474      // if key.charCodeAt(i) >= 255, handle error
11475      prepareWrite(this, 1);
11476      this.bytes[this.position] = key.charCodeAt(i);
11477      this.position++;
11478    }
11479    prepareWrite(this, 2);
11480    this.view.setUint8(this.position, 0x01);
11481    this.position++;
11482    this.view.setUint8(this.position, val ? 0x01 : 0x00);
11483    this.position++;
11484    this.length = Math.max(this.length, this.position);
11485    ++adHoc;
11486  };
11487
11488  // ():ByteArray
11489  this.finalize = function() {
11490    var
11491      dtsDelta, // :int
11492      len; // :int
11493
11494    switch (this.bytes[0]) {
11495      // Video Data
11496    case FlvTag.VIDEO_TAG:
11497       // We only support AVC, 1 = key frame (for AVC, a seekable
11498       // frame), 2 = inter frame (for AVC, a non-seekable frame)
11499      this.bytes[11] = ((this.keyFrame || extraData) ? 0x10 : 0x20) | 0x07;
11500      this.bytes[12] = extraData ?  0x00 : 0x01;
11501
11502      dtsDelta = this.pts - this.dts;
11503      this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16;
11504      this.bytes[14] = (dtsDelta & 0x0000FF00) >>>  8;
11505      this.bytes[15] = (dtsDelta & 0x000000FF) >>>  0;
11506      break;
11507
11508    case FlvTag.AUDIO_TAG:
11509      this.bytes[11] = 0xAF; // 44 kHz, 16-bit stereo
11510      this.bytes[12] = extraData ? 0x00 : 0x01;
11511      break;
11512
11513    case FlvTag.METADATA_TAG:
11514      this.position = 11;
11515      this.view.setUint8(this.position, 0x02); // String type
11516      this.position++;
11517      this.view.setUint16(this.position, 0x0A); // 10 Bytes
11518      this.position += 2;
11519      // set "onMetaData"
11520      this.bytes.set([0x6f, 0x6e, 0x4d, 0x65,
11521                      0x74, 0x61, 0x44, 0x61,
11522                      0x74, 0x61], this.position);
11523      this.position += 10;
11524      this.bytes[this.position] = 0x08; // Array type
11525      this.position++;
11526      this.view.setUint32(this.position, adHoc);
11527      this.position = this.length;
11528      this.bytes.set([0, 0, 9], this.position);
11529      this.position += 3; // End Data Tag
11530      this.length = this.position;
11531      break;
11532    }
11533
11534    len = this.length - 11;
11535
11536    // write the DataSize field
11537    this.bytes[ 1] = (len & 0x00FF0000) >>> 16;
11538    this.bytes[ 2] = (len & 0x0000FF00) >>>  8;
11539    this.bytes[ 3] = (len & 0x000000FF) >>>  0;
11540    // write the Timestamp
11541    this.bytes[ 4] = (this.dts & 0x00FF0000) >>> 16;
11542    this.bytes[ 5] = (this.dts & 0x0000FF00) >>>  8;
11543    this.bytes[ 6] = (this.dts & 0x000000FF) >>>  0;
11544    this.bytes[ 7] = (this.dts & 0xFF000000) >>> 24;
11545    // write the StreamID
11546    this.bytes[ 8] = 0;
11547    this.bytes[ 9] = 0;
11548    this.bytes[10] = 0;
11549
11550    // Sometimes we're at the end of the view and have one slot to write a
11551    // uint32, so, prepareWrite of count 4, since, view is uint8
11552    prepareWrite(this, 4);
11553    this.view.setUint32(this.length, this.length);
11554    this.length += 4;
11555    this.position += 4;
11556
11557    // trim down the byte buffer to what is actually being used
11558    this.bytes = this.bytes.subarray(0, this.length);
11559    this.frameTime = FlvTag.frameTime(this.bytes);
11560    // if bytes.bytelength isn't equal to this.length, handle error
11561    return this;
11562  };
11563};
11564
11565FlvTag.AUDIO_TAG = 0x08; // == 8, :uint
11566FlvTag.VIDEO_TAG = 0x09; // == 9, :uint
11567FlvTag.METADATA_TAG = 0x12; // == 18, :uint
11568
11569// (tag:ByteArray):Boolean {
11570FlvTag.isAudioFrame = function(tag) {
11571  return FlvTag.AUDIO_TAG === tag[0];
11572};
11573
11574// (tag:ByteArray):Boolean {
11575FlvTag.isVideoFrame = function(tag) {
11576  return FlvTag.VIDEO_TAG === tag[0];
11577};
11578
11579// (tag:ByteArray):Boolean {
11580FlvTag.isMetaData = function(tag) {
11581  return FlvTag.METADATA_TAG === tag[0];
11582};
11583
11584// (tag:ByteArray):Boolean {
11585FlvTag.isKeyFrame = function(tag) {
11586  if (FlvTag.isVideoFrame(tag)) {
11587    return tag[11] === 0x17;
11588  }
11589
11590  if (FlvTag.isAudioFrame(tag)) {
11591    return true;
11592  }
11593
11594  if (FlvTag.isMetaData(tag)) {
11595    return true;
11596  }
11597
11598  return false;
11599};
11600
11601// (tag:ByteArray):uint {
11602FlvTag.frameTime = function(tag) {
11603  var pts = tag[ 4] << 16; // :uint
11604  pts |= tag[ 5] <<  8;
11605  pts |= tag[ 6] <<  0;
11606  pts |= tag[ 7] << 24;
11607  return pts;
11608};
11609
11610module.exports = FlvTag;
11611
11612},{}],46:[function(require,module,exports){
11613module.exports = {
11614  tag: require('./flv-tag'),
11615  Transmuxer: require('./transmuxer'),
11616  getFlvHeader: require('./flv-header')
11617};
11618
11619},{"./flv-header":44,"./flv-tag":45,"./transmuxer":48}],47:[function(require,module,exports){
11620'use strict';
11621
11622var TagList = function() {
11623  var self = this;
11624
11625  this.list = [];
11626
11627  this.push = function(tag) {
11628    this.list.push({
11629      bytes: tag.bytes,
11630      dts: tag.dts,
11631      pts: tag.pts,
11632      keyFrame: tag.keyFrame,
11633      metaDataTag: tag.metaDataTag
11634    });
11635  };
11636
11637  Object.defineProperty(this, 'length', {
11638    get: function() {
11639      return self.list.length;
11640    }
11641  });
11642};
11643
11644module.exports = TagList;
11645
11646},{}],48:[function(require,module,exports){
11647'use strict';
11648
11649var Stream = require('../utils/stream.js');
11650var FlvTag = require('./flv-tag.js');
11651var m2ts = require('../m2ts/m2ts.js');
11652var AdtsStream = require('../codecs/adts.js');
11653var H264Stream = require('../codecs/h264').H264Stream;
11654var CoalesceStream = require('./coalesce-stream.js');
11655var TagList = require('./tag-list.js');
11656
11657var
11658  Transmuxer,
11659  VideoSegmentStream,
11660  AudioSegmentStream,
11661  collectTimelineInfo,
11662  metaDataTag,
11663  extraDataTag;
11664
11665/**
11666 * Store information about the start and end of the tracka and the
11667 * duration for each frame/sample we process in order to calculate
11668 * the baseMediaDecodeTime
11669 */
11670collectTimelineInfo = function(track, data) {
11671  if (typeof data.pts === 'number') {
11672    if (track.timelineStartInfo.pts === undefined) {
11673      track.timelineStartInfo.pts = data.pts;
11674    } else {
11675      track.timelineStartInfo.pts =
11676        Math.min(track.timelineStartInfo.pts, data.pts);
11677    }
11678  }
11679
11680  if (typeof data.dts === 'number') {
11681    if (track.timelineStartInfo.dts === undefined) {
11682      track.timelineStartInfo.dts = data.dts;
11683    } else {
11684      track.timelineStartInfo.dts =
11685        Math.min(track.timelineStartInfo.dts, data.dts);
11686    }
11687  }
11688};
11689
11690metaDataTag = function(track, pts) {
11691  var
11692    tag = new FlvTag(FlvTag.METADATA_TAG); // :FlvTag
11693
11694  tag.dts = pts;
11695  tag.pts = pts;
11696
11697  tag.writeMetaDataDouble('videocodecid', 7);
11698  tag.writeMetaDataDouble('width', track.width);
11699  tag.writeMetaDataDouble('height', track.height);
11700
11701  return tag;
11702};
11703
11704extraDataTag = function(track, pts) {
11705  var
11706    i,
11707    tag = new FlvTag(FlvTag.VIDEO_TAG, true);
11708
11709  tag.dts = pts;
11710  tag.pts = pts;
11711
11712  tag.writeByte(0x01);// version
11713  tag.writeByte(track.profileIdc);// profile
11714  tag.writeByte(track.profileCompatibility);// compatibility
11715  tag.writeByte(track.levelIdc);// level
11716  tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
11717  tag.writeByte(0xE0 | 0x01); // reserved (3 bits), num of SPS (5 bits)
11718  tag.writeShort(track.sps[0].length); // data of SPS
11719  tag.writeBytes(track.sps[0]); // SPS
11720
11721  tag.writeByte(track.pps.length); // num of PPS (will there ever be more that 1 PPS?)
11722  for (i = 0; i < track.pps.length; ++i) {
11723    tag.writeShort(track.pps[i].length); // 2 bytes for length of PPS
11724    tag.writeBytes(track.pps[i]); // data of PPS
11725  }
11726
11727  return tag;
11728};
11729
11730/**
11731 * Constructs a single-track, media segment from AAC data
11732 * events. The output of this stream can be fed to flash.
11733 */
11734AudioSegmentStream = function(track) {
11735  var
11736    adtsFrames = [],
11737    videoKeyFrames = [],
11738    oldExtraData;
11739
11740  AudioSegmentStream.prototype.init.call(this);
11741
11742  this.push = function(data) {
11743    collectTimelineInfo(track, data);
11744
11745    if (track) {
11746      track.audioobjecttype = data.audioobjecttype;
11747      track.channelcount = data.channelcount;
11748      track.samplerate = data.samplerate;
11749      track.samplingfrequencyindex = data.samplingfrequencyindex;
11750      track.samplesize = data.samplesize;
11751      track.extraData = (track.audioobjecttype << 11) |
11752                        (track.samplingfrequencyindex << 7) |
11753                        (track.channelcount << 3);
11754    }
11755
11756    data.pts = Math.round(data.pts / 90);
11757    data.dts = Math.round(data.dts / 90);
11758
11759    // buffer audio data until end() is called
11760    adtsFrames.push(data);
11761  };
11762
11763  this.flush = function() {
11764    var currentFrame, adtsFrame, lastMetaPts, tags = new TagList();
11765    // return early if no audio data has been observed
11766    if (adtsFrames.length === 0) {
11767      this.trigger('done', 'AudioSegmentStream');
11768      return;
11769    }
11770
11771    lastMetaPts = -Infinity;
11772
11773    while (adtsFrames.length) {
11774      currentFrame = adtsFrames.shift();
11775
11776      // write out a metadata frame at every video key frame
11777      if (videoKeyFrames.length && currentFrame.pts >= videoKeyFrames[0]) {
11778        lastMetaPts = videoKeyFrames.shift();
11779        this.writeMetaDataTags(tags, lastMetaPts);
11780      }
11781
11782      // also write out metadata tags every 1 second so that the decoder
11783      // is re-initialized quickly after seeking into a different
11784      // audio configuration.
11785      if (track.extraData !== oldExtraData || currentFrame.pts - lastMetaPts >= 1000) {
11786        this.writeMetaDataTags(tags, currentFrame.pts);
11787        oldExtraData = track.extraData;
11788        lastMetaPts = currentFrame.pts;
11789      }
11790
11791      adtsFrame = new FlvTag(FlvTag.AUDIO_TAG);
11792      adtsFrame.pts = currentFrame.pts;
11793      adtsFrame.dts = currentFrame.dts;
11794
11795      adtsFrame.writeBytes(currentFrame.data);
11796
11797      tags.push(adtsFrame.finalize());
11798    }
11799
11800    videoKeyFrames.length = 0;
11801    oldExtraData = null;
11802    this.trigger('data', {track: track, tags: tags.list});
11803
11804    this.trigger('done', 'AudioSegmentStream');
11805  };
11806
11807  this.writeMetaDataTags = function(tags, pts) {
11808    var adtsFrame;
11809
11810    adtsFrame = new FlvTag(FlvTag.METADATA_TAG);
11811    // For audio, DTS is always the same as PTS. We want to set the DTS
11812    // however so we can compare with video DTS to determine approximate
11813    // packet order
11814    adtsFrame.pts = pts;
11815    adtsFrame.dts = pts;
11816
11817    // AAC is always 10
11818    adtsFrame.writeMetaDataDouble('audiocodecid', 10);
11819    adtsFrame.writeMetaDataBoolean('stereo', track.channelcount === 2);
11820    adtsFrame.writeMetaDataDouble('audiosamplerate', track.samplerate);
11821    // Is AAC always 16 bit?
11822    adtsFrame.writeMetaDataDouble('audiosamplesize', 16);
11823
11824    tags.push(adtsFrame.finalize());
11825
11826    adtsFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
11827    // For audio, DTS is always the same as PTS. We want to set the DTS
11828    // however so we can compare with video DTS to determine approximate
11829    // packet order
11830    adtsFrame.pts = pts;
11831    adtsFrame.dts = pts;
11832
11833    adtsFrame.view.setUint16(adtsFrame.position, track.extraData);
11834    adtsFrame.position += 2;
11835    adtsFrame.length = Math.max(adtsFrame.length, adtsFrame.position);
11836
11837    tags.push(adtsFrame.finalize());
11838  };
11839
11840  this.onVideoKeyFrame = function(pts) {
11841    videoKeyFrames.push(pts);
11842  };
11843};
11844AudioSegmentStream.prototype = new Stream();
11845
11846/**
11847 * Store FlvTags for the h264 stream
11848 * @param track {object} track metadata configuration
11849 */
11850VideoSegmentStream = function(track) {
11851  var
11852    nalUnits = [],
11853    config,
11854    h264Frame;
11855  VideoSegmentStream.prototype.init.call(this);
11856
11857  this.finishFrame = function(tags, frame) {
11858    if (!frame) {
11859      return;
11860    }
11861    // Check if keyframe and the length of tags.
11862    // This makes sure we write metadata on the first frame of a segment.
11863    if (config && track && track.newMetadata &&
11864        (frame.keyFrame || tags.length === 0)) {
11865      // Push extra data on every IDR frame in case we did a stream change + seek
11866      var metaTag = metaDataTag(config, frame.dts).finalize();
11867      var extraTag = extraDataTag(track, frame.dts).finalize();
11868
11869      metaTag.metaDataTag = extraTag.metaDataTag = true;
11870
11871      tags.push(metaTag);
11872      tags.push(extraTag);
11873      track.newMetadata = false;
11874
11875      this.trigger('keyframe', frame.dts);
11876    }
11877
11878    frame.endNalUnit();
11879    tags.push(frame.finalize());
11880    h264Frame = null;
11881  };
11882
11883  this.push = function(data) {
11884    collectTimelineInfo(track, data);
11885
11886    data.pts = Math.round(data.pts / 90);
11887    data.dts = Math.round(data.dts / 90);
11888
11889    // buffer video until flush() is called
11890    nalUnits.push(data);
11891  };
11892
11893  this.flush = function() {
11894    var
11895      currentNal,
11896      tags = new TagList();
11897
11898    // Throw away nalUnits at the start of the byte stream until we find
11899    // the first AUD
11900    while (nalUnits.length) {
11901      if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
11902        break;
11903      }
11904      nalUnits.shift();
11905    }
11906
11907    // return early if no video data has been observed
11908    if (nalUnits.length === 0) {
11909      this.trigger('done', 'VideoSegmentStream');
11910      return;
11911    }
11912
11913    while (nalUnits.length) {
11914      currentNal = nalUnits.shift();
11915
11916      // record the track config
11917      if (currentNal.nalUnitType === 'seq_parameter_set_rbsp') {
11918        track.newMetadata = true;
11919        config = currentNal.config;
11920        track.width = config.width;
11921        track.height = config.height;
11922        track.sps = [currentNal.data];
11923        track.profileIdc = config.profileIdc;
11924        track.levelIdc = config.levelIdc;
11925        track.profileCompatibility = config.profileCompatibility;
11926        h264Frame.endNalUnit();
11927      } else if (currentNal.nalUnitType === 'pic_parameter_set_rbsp') {
11928        track.newMetadata = true;
11929        track.pps = [currentNal.data];
11930        h264Frame.endNalUnit();
11931      } else if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
11932        if (h264Frame) {
11933          this.finishFrame(tags, h264Frame);
11934        }
11935        h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
11936        h264Frame.pts = currentNal.pts;
11937        h264Frame.dts = currentNal.dts;
11938      } else {
11939        if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
11940          // the current sample is a key frame
11941          h264Frame.keyFrame = true;
11942        }
11943        h264Frame.endNalUnit();
11944      }
11945      h264Frame.startNalUnit();
11946      h264Frame.writeBytes(currentNal.data);
11947    }
11948    if (h264Frame) {
11949      this.finishFrame(tags, h264Frame);
11950    }
11951
11952    this.trigger('data', {track: track, tags: tags.list});
11953
11954    // Continue with the flush process now
11955    this.trigger('done', 'VideoSegmentStream');
11956  };
11957};
11958
11959VideoSegmentStream.prototype = new Stream();
11960
11961/**
11962 * An object that incrementally transmuxes MPEG2 Trasport Stream
11963 * chunks into an FLV.
11964 */
11965Transmuxer = function(options) {
11966  var
11967    self = this,
11968
11969    packetStream, parseStream, elementaryStream,
11970    videoTimestampRolloverStream, audioTimestampRolloverStream,
11971    timedMetadataTimestampRolloverStream,
11972    adtsStream, h264Stream,
11973    videoSegmentStream, audioSegmentStream, captionStream,
11974    coalesceStream;
11975
11976  Transmuxer.prototype.init.call(this);
11977
11978  options = options || {};
11979
11980  // expose the metadata stream
11981  this.metadataStream = new m2ts.MetadataStream();
11982
11983  options.metadataStream = this.metadataStream;
11984
11985  // set up the parsing pipeline
11986  packetStream = new m2ts.TransportPacketStream();
11987  parseStream = new m2ts.TransportParseStream();
11988  elementaryStream = new m2ts.ElementaryStream();
11989  videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
11990  audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
11991  timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
11992
11993  adtsStream = new AdtsStream();
11994  h264Stream = new H264Stream();
11995  coalesceStream = new CoalesceStream(options);
11996
11997  // disassemble MPEG2-TS packets into elementary streams
11998  packetStream
11999    .pipe(parseStream)
12000    .pipe(elementaryStream);
12001
12002  // !!THIS ORDER IS IMPORTANT!!
12003  // demux the streams
12004  elementaryStream
12005    .pipe(videoTimestampRolloverStream)
12006    .pipe(h264Stream);
12007  elementaryStream
12008    .pipe(audioTimestampRolloverStream)
12009    .pipe(adtsStream);
12010
12011  elementaryStream
12012    .pipe(timedMetadataTimestampRolloverStream)
12013    .pipe(this.metadataStream)
12014    .pipe(coalesceStream);
12015  // if CEA-708 parsing is available, hook up a caption stream
12016  captionStream = new m2ts.CaptionStream();
12017  h264Stream.pipe(captionStream)
12018    .pipe(coalesceStream);
12019
12020  // hook up the segment streams once track metadata is delivered
12021  elementaryStream.on('data', function(data) {
12022    var i, videoTrack, audioTrack;
12023
12024    if (data.type === 'metadata') {
12025      i = data.tracks.length;
12026
12027      // scan the tracks listed in the metadata
12028      while (i--) {
12029        if (data.tracks[i].type === 'video') {
12030          videoTrack = data.tracks[i];
12031        } else if (data.tracks[i].type === 'audio') {
12032          audioTrack = data.tracks[i];
12033        }
12034      }
12035
12036      // hook up the video segment stream to the first track with h264 data
12037      if (videoTrack && !videoSegmentStream) {
12038        coalesceStream.numberOfTracks++;
12039        videoSegmentStream = new VideoSegmentStream(videoTrack);
12040
12041        // Set up the final part of the video pipeline
12042        h264Stream
12043          .pipe(videoSegmentStream)
12044          .pipe(coalesceStream);
12045      }
12046
12047      if (audioTrack && !audioSegmentStream) {
12048        // hook up the audio segment stream to the first track with aac data
12049        coalesceStream.numberOfTracks++;
12050        audioSegmentStream = new AudioSegmentStream(audioTrack);
12051
12052        // Set up the final part of the audio pipeline
12053        adtsStream
12054          .pipe(audioSegmentStream)
12055          .pipe(coalesceStream);
12056
12057        if (videoSegmentStream) {
12058          videoSegmentStream.on('keyframe', audioSegmentStream.onVideoKeyFrame);
12059        }
12060      }
12061    }
12062  });
12063
12064  // feed incoming data to the front of the parsing pipeline
12065  this.push = function(data) {
12066    packetStream.push(data);
12067  };
12068
12069  // flush any buffered data
12070  this.flush = function() {
12071    // Start at the top of the pipeline and flush all pending work
12072    packetStream.flush();
12073  };
12074
12075  // Caption data has to be reset when seeking outside buffered range
12076  this.resetCaptions = function() {
12077    captionStream.reset();
12078  };
12079
12080  // Re-emit any data coming from the coalesce stream to the outside world
12081  coalesceStream.on('data', function(event) {
12082    self.trigger('data', event);
12083  });
12084
12085  // Let the consumer know we have finished flushing the entire pipeline
12086  coalesceStream.on('done', function() {
12087    self.trigger('done');
12088  });
12089};
12090Transmuxer.prototype = new Stream();
12091
12092// forward compatibility
12093module.exports = Transmuxer;
12094
12095},{"../codecs/adts.js":40,"../codecs/h264":41,"../m2ts/m2ts.js":50,"../utils/stream.js":62,"./coalesce-stream.js":43,"./flv-tag.js":45,"./tag-list.js":47}],49:[function(require,module,exports){
12096/**
12097 * mux.js
12098 *
12099 * Copyright (c) 2015 Brightcove
12100 * All rights reserved.
12101 *
12102 * Reads in-band caption information from a video elementary
12103 * stream. Captions must follow the CEA-708 standard for injection
12104 * into an MPEG-2 transport streams.
vendor: 7,004 bytes, lines 12105-12353
12105 * @see https://en.wikipedia.org/wiki/CEA-708
12106 * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
12107 */
12108
12109'use strict';
12110
12111// -----------------
12112// Link To Transport
12113// -----------------
12114
12115// Supplemental enhancement information (SEI) NAL units have a
12116// payload type field to indicate how they are to be
12117// interpreted. CEAS-708 caption content is always transmitted with
12118// payload type 0x04.
12119var USER_DATA_REGISTERED_ITU_T_T35 = 4,
12120    RBSP_TRAILING_BITS = 128,
12121    Stream = require('../utils/stream');
12122
12123/**
12124  * Parse a supplemental enhancement information (SEI) NAL unit.
12125  * Stops parsing once a message of type ITU T T35 has been found.
12126  *
12127  * @param bytes {Uint8Array} the bytes of a SEI NAL unit
12128  * @return {object} the parsed SEI payload
12129  * @see Rec. ITU-T H.264, 7.3.2.3.1
12130  */
12131var parseSei = function(bytes) {
12132  var
12133    i = 0,
12134    result = {
12135      payloadType: -1,
12136      payloadSize: 0
12137    },
12138    payloadType = 0,
12139    payloadSize = 0;
12140
12141  // go through the sei_rbsp parsing each each individual sei_message
12142  while (i < bytes.byteLength) {
12143    // stop once we have hit the end of the sei_rbsp
12144    if (bytes[i] === RBSP_TRAILING_BITS) {
12145      break;
12146    }
12147
12148    // Parse payload type
12149    while (bytes[i] === 0xFF) {
12150      payloadType += 255;
12151      i++;
12152    }
12153    payloadType += bytes[i++];
12154
12155    // Parse payload size
12156    while (bytes[i] === 0xFF) {
12157      payloadSize += 255;
12158      i++;
12159    }
12160    payloadSize += bytes[i++];
12161
12162    // this sei_message is a 608/708 caption so save it and break
12163    // there can only ever be one caption message in a frame's sei
12164    if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
12165      result.payloadType = payloadType;
12166      result.payloadSize = payloadSize;
12167      result.payload = bytes.subarray(i, i + payloadSize);
12168      break;
12169    }
12170
12171    // skip the payload and parse the next message
12172    i += payloadSize;
12173    payloadType = 0;
12174    payloadSize = 0;
12175  }
12176
12177  return result;
12178};
12179
12180// see ANSI/SCTE 128-1 (2013), section 8.1
12181var parseUserData = function(sei) {
12182  // itu_t_t35_contry_code must be 181 (United States) for
12183  // captions
12184  if (sei.payload[0] !== 181) {
12185    return null;
12186  }
12187
12188  // itu_t_t35_provider_code should be 49 (ATSC) for captions
12189  if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
12190    return null;
12191  }
12192
12193  // the user_identifier should be "GA94" to indicate ATSC1 data
12194  if (String.fromCharCode(sei.payload[3],
12195                          sei.payload[4],
12196                          sei.payload[5],
12197                          sei.payload[6]) !== 'GA94') {
12198    return null;
12199  }
12200
12201  // finally, user_data_type_code should be 0x03 for caption data
12202  if (sei.payload[7] !== 0x03) {
12203    return null;
12204  }
12205
12206  // return the user_data_type_structure and strip the trailing
12207  // marker bits
12208  return sei.payload.subarray(8, sei.payload.length - 1);
12209};
12210
12211// see CEA-708-D, section 4.4
12212var parseCaptionPackets = function(pts, userData) {
12213  var results = [], i, count, offset, data;
12214
12215  // if this is just filler, return immediately
12216  if (!(userData[0] & 0x40)) {
12217    return results;
12218  }
12219
12220  // parse out the cc_data_1 and cc_data_2 fields
12221  count = userData[0] & 0x1f;
12222  for (i = 0; i < count; i++) {
12223    offset = i * 3;
12224    data = {
12225      type: userData[offset + 2] & 0x03,
12226      pts: pts
12227    };
12228
12229    // capture cc data when cc_valid is 1
12230    if (userData[offset + 2] & 0x04) {
12231      data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
12232      results.push(data);
12233    }
12234  }
12235  return results;
12236};
12237
12238var packetDropper = {
12239  push: function() {}
12240};
12241
12242var CaptionStream = function() {
12243
12244  CaptionStream.prototype.init.call(this);
12245
12246  this.captionPackets_ = [];
12247
12248  this.ccStreams_ = [
12249    new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
12250    new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
12251    new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
12252    new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
12253  ];
12254
12255  this.reset();
12256
12257  // forward data and done events from CCs to this CaptionStream
12258  this.ccStreams_.forEach(function(cc) {
12259    cc.on('data', this.trigger.bind(this, 'data'));
12260    cc.on('done', this.trigger.bind(this, 'done'));
12261  }, this);
12262
12263};
12264
12265CaptionStream.prototype = new Stream();
12266CaptionStream.prototype.push = function(event) {
12267  var sei, userData;
12268
12269  // only examine SEI NALs
12270  if (event.nalUnitType !== 'sei_rbsp') {
12271    return;
12272  }
12273
12274  // parse the sei
12275  sei = parseSei(event.escapedRBSP);
12276
12277  // ignore everything but user_data_registered_itu_t_t35
12278  if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
12279    return;
12280  }
12281
12282  // parse out the user data payload
12283  userData = parseUserData(sei);
12284
12285  // ignore unrecognized userData
12286  if (!userData) {
12287    return;
12288  }
12289
12290  // Sometimes, the same segment # will be downloaded twice. To stop the
12291  // caption data from being processed twice, we track the latest dts we've
12292  // received and ignore everything with a dts before that. However, since
12293  // data for a specific dts can be split across 2 packets on either side of
12294  // a segment boundary, we need to make sure we *don't* ignore the second
12295  // dts packet we receive that has dts === this.latestDts_. And thus, the
12296  // ignoreNextEqualDts_ flag was born.
12297  if (event.dts < this.latestDts_) {
12298    // We've started getting older data, so set the flag.
12299    this.ignoreNextEqualDts_ = true;
12300    return;
12301  } else if ((event.dts === this.latestDts_) && (this.ignoreNextEqualDts_)) {
12302    // We've received the last duplicate packet, time to start processing again
12303    this.ignoreNextEqualDts_ = false;
12304    return;
12305  }
12306
12307  // parse out CC data packets and save them for later
12308  this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
12309  this.latestDts_ = event.dts;
12310};
12311
12312CaptionStream.prototype.flush = function() {
12313  // make sure we actually parsed captions before proceeding
12314  if (!this.captionPackets_.length) {
12315    this.ccStreams_.forEach(function(cc) {
12316      cc.flush();
12317    }, this);
12318    return;
12319  }
12320
12321  // In Chrome, the Array#sort function is not stable so add a
12322  // presortIndex that we can use to ensure we get a stable-sort
12323  this.captionPackets_.forEach(function(elem, idx) {
12324    elem.presortIndex = idx;
12325  });
12326
12327  // sort caption byte-pairs based on their PTS values
12328  this.captionPackets_.sort(function(a, b) {
12329    if (a.pts === b.pts) {
12330      return a.presortIndex - b.presortIndex;
12331    }
12332    return a.pts - b.pts;
12333  });
12334
12335  this.captionPackets_.forEach(function(packet) {
12336    if (packet.type < 2) {
12337      // Dispatch packet to the right Cea608Stream
12338      this.dispatchCea608Packet(packet);
12339    }
12340    // this is where an 'else' would go for a dispatching packets
12341    // to a theoretical Cea708Stream that handles SERVICEn data
12342  }, this);
12343
12344  this.captionPackets_.length = 0;
12345  this.ccStreams_.forEach(function(cc) {
12346    cc.flush();
12347  }, this);
12348  return;
12349};
12350
12351CaptionStream.prototype.reset = function() {
12352  this.latestDts_ = null;
12353  this.ignoreNextEqualDts_ = false;
12354  this.activeCea608Channel_ = null;
12355  // Since we don't know which channel is active until we get a control
12356  // code that sets it, we start off with CEA608 handlers that just drop
12357  // all the packets.
12358  this.activeCea608Streams_ = [
12359    packetDropper,
12360    packetDropper
12361  ];
12362  this.ccStreams_.forEach(function(ccStream) {
12363    ccStream.reset();
12364  });
12365};
12366
12367CaptionStream.prototype.dispatchCea608Packet = function(packet) {
12368  if (this.setsChannel1Active(packet) && this.activeCea608Channel_ !== 1) {
12369    this.activeCea608Channel_ = 1;
12370    this.activeCea608Streams_ = [this.ccStreams_[0], this.ccStreams_[2]]; // CC1, CC3
12371  } else if (this.setsChannel2Active(packet) && this.activeCea608Channel_ !== 2) {
12372    this.activeCea608Channel_ = 2;
12373    this.activeCea608Streams_ = [this.ccStreams_[1], this.ccStreams_[3]]; // CC2, CC4
12374  }
12375  // If we haven't set the active streams yet, this next call just returns
12376  // immediately.
12377  this.activeCea608Streams_[packet.type].push(packet);
12378};
12379
12380CaptionStream.prototype.setsChannel1Active = function(packet) {
12381  return ((packet.ccData & 0x7800) === 0x1000);
12382};
12383CaptionStream.prototype.setsChannel2Active = function(packet) {
12384  return ((packet.ccData & 0x7800) === 0x1800);
12385};
12386
12387// ----------------------
12388// Session to Application
12389// ----------------------
12390
12391var CHARACTER_TRANSLATION = {
12392  0x2a: 0xe1,     // á
12393  0x5c: 0xe9,     // é
12394  0x5e: 0xed,     // í
12395  0x5f: 0xf3,     // ó
12396  0x60: 0xfa,     // ú
12397  0x7b: 0xe7,     // ç
12398  0x7c: 0xf7,     // ÷
12399  0x7d: 0xd1,     // Ñ
12400  0x7e: 0xf1,     // ñ
12401  0x7f: 0x2588,   // █
12402  0x0130: 0xae,   // ®
12403  0x0131: 0xb0,   // °
12404  0x0132: 0xbd,   // ½
12405  0x0133: 0xbf,   // ¿
12406  0x0134: 0x2122, // ™
12407  0x0135: 0xa2,   // ¢
12408  0x0136: 0xa3,   // £
12409  0x0137: 0x266a, // ♪
12410  0x0138: 0xe0,   // à
12411  0x0139: 0xa0,   //
12412  0x013a: 0xe8,   // è
12413  0x013b: 0xe2,   // â
12414  0x013c: 0xea,   // ê
12415  0x013d: 0xee,   // î
12416  0x013e: 0xf4,   // ô
12417  0x013f: 0xfb,   // û
12418  0x0220: 0xc1,   // Á
12419  0x0221: 0xc9,   // É
12420  0x0222: 0xd3,   // Ó
12421  0x0223: 0xda,   // Ú
12422  0x0224: 0xdc,   // Ü
12423  0x0225: 0xfc,   // ü
12424  0x0226: 0x2018, // ‘
12425  0x0227: 0xa1,   // ¡
12426  0x0228: 0x2a,   // *
12427  0x0229: 0x27,   // '
12428  0x022a: 0x2014, // —
12429  0x022b: 0xa9,   // ©
12430  0x022c: 0x2120, // ℠
12431  0x022d: 0x2022, // •
12432  0x022e: 0x201c, // “
12433  0x022f: 0x201d, // ”
12434  0x0230: 0xc0,   // À
12435  0x0231: 0xc2,   // Â
12436  0x0232: 0xc7,   // Ç
12437  0x0233: 0xc8,   // È
12438  0x0234: 0xca,   // Ê
12439  0x0235: 0xcb,   // Ë
12440  0x0236: 0xeb,   // ë
12441  0x0237: 0xce,   // Î
12442  0x0238: 0xcf,   // Ï
12443  0x0239: 0xef,   // ï
12444  0x023a: 0xd4,   // Ô
12445  0x023b: 0xd9,   // Ù
12446  0x023c: 0xf9,   // ù
12447  0x023d: 0xdb,   // Û
12448  0x023e: 0xab,   // «
12449  0x023f: 0xbb,   // »
12450  0x0320: 0xc3,   // Ã
12451  0x0321: 0xe3,   // ã
12452  0x0322: 0xcd,   // Í
12453  0x0323: 0xcc,   // Ì
12454  0x0324: 0xec,   // ì
12455  0x0325: 0xd2,   // Ò
12456  0x0326: 0xf2,   // ò
12457  0x0327: 0xd5,   // Õ
12458  0x0328: 0xf5,   // õ
12459  0x0329: 0x7b,   // {
12460  0x032a: 0x7d,   // }
12461  0x032b: 0x5c,   // \
12462  0x032c: 0x5e,   // ^
12463  0x032d: 0x5f,   // _
12464  0x032e: 0x7c,   // |
12465  0x032f: 0x7e,   // ~
12466  0x0330: 0xc4,   // Ä
12467  0x0331: 0xe4,   // ä
12468  0x0332: 0xd6,   // Ö
12469  0x0333: 0xf6,   // ö
12470  0x0334: 0xdf,   // ß
12471  0x0335: 0xa5,   // ¥
12472  0x0336: 0xa4,   // ¤
12473  0x0337: 0x2502, // │
12474  0x0338: 0xc5,   // Å
12475  0x0339: 0xe5,   // å
12476  0x033a: 0xd8,   // Ø
12477  0x033b: 0xf8,   // ø
12478  0x033c: 0x250c, // ┌
12479  0x033d: 0x2510, // ┐
12480  0x033e: 0x2514, // └
12481  0x033f: 0x2518  // ┘
12482};
12483
12484var getCharFromCode = function(code) {
12485  if (code === null) {
12486    return '';
12487  }
12488  code = CHARACTER_TRANSLATION[code] || code;
12489  return String.fromCharCode(code);
12490};
12491
12492// the index of the last row in a CEA-608 display buffer
12493var BOTTOM_ROW = 14;
12494
12495// This array is used for mapping PACs -> row #, since there's no way of
12496// getting it through bit logic.
12497var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620,
12498            0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420];
12499
12500// CEA-608 captions are rendered onto a 34x15 matrix of character
12501// cells. The "bottom" row is the last element in the outer array.
12502var createDisplayBuffer = function() {
12503  var result = [], i = BOTTOM_ROW + 1;
12504  while (i--) {
12505    result.push('');
12506  }
12507  return result;
12508};
12509
12510var Cea608Stream = function(field, dataChannel) {
12511  Cea608Stream.prototype.init.call(this);
12512
12513  this.field_ = field || 0;
12514  this.dataChannel_ = dataChannel || 0;
12515
12516  this.name_ = 'CC' + (((this.field_ << 1) | this.dataChannel_) + 1);
12517
12518  this.setConstants();
12519  this.reset();
12520
12521  this.push = function(packet) {
12522    var data, swap, char0, char1, text;
12523    // remove the parity bits
12524    data = packet.ccData & 0x7f7f;
12525
12526    // ignore duplicate control codes; the spec demands they're sent twice
12527    if (data === this.lastControlCode_) {
12528      this.lastControlCode_ = null;
12529      return;
12530    }
12531
12532    // Store control codes
12533    if ((data & 0xf000) === 0x1000) {
12534      this.lastControlCode_ = data;
12535    } else if (data !== this.PADDING_) {
12536      this.lastControlCode_ = null;
12537    }
12538
12539    char0 = data >>> 8;
12540    char1 = data & 0xff;
vendor: 4,062 bytes, lines 12541-12647
12541
12542    if (data === this.PADDING_) {
12543      return;
12544
12545    } else if (data === this.RESUME_CAPTION_LOADING_) {
12546      this.mode_ = 'popOn';
12547
12548    } else if (data === this.END_OF_CAPTION_) {
12549      this.clearFormatting(packet.pts);
12550      // if a caption was being displayed, it's gone now
12551      this.flushDisplayed(packet.pts);
12552
12553      // flip memory
12554      swap = this.displayed_;
12555      this.displayed_ = this.nonDisplayed_;
12556      this.nonDisplayed_ = swap;
12557
12558      // start measuring the time to display the caption
12559      this.startPts_ = packet.pts;
12560
12561    } else if (data === this.ROLL_UP_2_ROWS_) {
12562      this.topRow_ = BOTTOM_ROW - 1;
12563      this.mode_ = 'rollUp';
12564    } else if (data === this.ROLL_UP_3_ROWS_) {
12565      this.topRow_ = BOTTOM_ROW - 2;
12566      this.mode_ = 'rollUp';
12567    } else if (data === this.ROLL_UP_4_ROWS_) {
12568      this.topRow_ = BOTTOM_ROW - 3;
12569      this.mode_ = 'rollUp';
12570    } else if (data === this.CARRIAGE_RETURN_) {
12571      this.clearFormatting(packet.pts);
12572      this.flushDisplayed(packet.pts);
12573      this.shiftRowsUp_();
12574      this.startPts_ = packet.pts;
12575
12576    } else if (data === this.BACKSPACE_) {
12577      if (this.mode_ === 'popOn') {
12578        this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
12579      } else {
12580        this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
12581      }
12582    } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
12583      this.flushDisplayed(packet.pts);
12584      this.displayed_ = createDisplayBuffer();
12585    } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
12586      this.nonDisplayed_ = createDisplayBuffer();
12587
12588    } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
12589      this.mode_ = 'paintOn';
12590
12591    // Append special characters to caption text
12592    } else if (this.isSpecialCharacter(char0, char1)) {
12593      // Bitmask char0 so that we can apply character transformations
12594      // regardless of field and data channel.
12595      // Then byte-shift to the left and OR with char1 so we can pass the
12596      // entire character code to `getCharFromCode`.
12597      char0 = (char0 & 0x03) << 8;
12598      text = getCharFromCode(char0 | char1);
12599      this[this.mode_](packet.pts, text);
12600      this.column_++;
12601
12602    // Append extended characters to caption text
12603    } else if (this.isExtCharacter(char0, char1)) {
12604      // Extended characters always follow their "non-extended" equivalents.
12605      // IE if a "è" is desired, you'll always receive "eè"; non-compliant
12606      // decoders are supposed to drop the "è", while compliant decoders
12607      // backspace the "e" and insert "è".
12608
12609      // Delete the previous character
12610      if (this.mode_ === 'popOn') {
12611        this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
12612      } else {
12613        this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
12614      }
12615
12616      // Bitmask char0 so that we can apply character transformations
12617      // regardless of field and data channel.
12618      // Then byte-shift to the left and OR with char1 so we can pass the
12619      // entire character code to `getCharFromCode`.
12620      char0 = (char0 & 0x03) << 8;
12621      text = getCharFromCode(char0 | char1);
12622      this[this.mode_](packet.pts, text);
12623      this.column_++;
12624
12625    // Process mid-row codes
12626    } else if (this.isMidRowCode(char0, char1)) {
12627      // Attributes are not additive, so clear all formatting
12628      this.clearFormatting(packet.pts);
12629
12630      // According to the standard, mid-row codes
12631      // should be replaced with spaces, so add one now
12632      this[this.mode_](packet.pts, ' ');
12633      this.column_++;
12634
12635      if ((char1 & 0xe) === 0xe) {
12636        this.addFormatting(packet.pts, ['i']);
12637      }
12638
12639      if ((char1 & 0x1) === 0x1) {
12640        this.addFormatting(packet.pts, ['u']);
12641      }
12642
12643    // Detect offset control codes and adjust cursor
12644    } else if (this.isOffsetControlCode(char0, char1)) {
12645      // Cursor position is set by indent PAC (see below) in 4-column
12646      // increments, with an additional offset code of 1-3 to reach any
12647      // of the 32 columns specified by CEA-608. So all we need to do
vendor: 46,419 bytes, lines 12648-14093
12648      // here is increment the column cursor by the given offset.
12649      this.column_ += (char1 & 0x03);
12650
12651    // Detect PACs (Preamble Address Codes)
12652    } else if (this.isPAC(char0, char1)) {
12653
12654      // There's no logic for PAC -> row mapping, so we have to just
12655      // find the row code in an array and use its index :(
12656      var row = ROWS.indexOf(data & 0x1f20);
12657
12658      if (row !== this.row_) {
12659        // formatting is only persistent for current row
12660        this.clearFormatting(packet.pts);
12661        this.row_ = row;
12662      }
12663      // All PACs can apply underline, so detect and apply
12664      // (All odd-numbered second bytes set underline)
12665      if ((char1 & 0x1) && (this.formatting_.indexOf('u') === -1)) {
12666          this.addFormatting(packet.pts, ['u']);
12667      }
12668
12669      if ((data & 0x10) === 0x10) {
12670        // We've got an indent level code. Each successive even number
12671        // increments the column cursor by 4, so we can get the desired
12672        // column position by bit-shifting to the right (to get n/2)
12673        // and multiplying by 4.
12674        this.column_ = ((data & 0xe) >> 1) * 4;
12675      }
12676
12677      if (this.isColorPAC(char1)) {
12678        // it's a color code, though we only support white, which
12679        // can be either normal or italicized. white italics can be
12680        // either 0x4e or 0x6e depending on the row, so we just
12681        // bitwise-and with 0xe to see if italics should be turned on
12682        if ((char1 & 0xe) === 0xe) {
12683          this.addFormatting(packet.pts, ['i']);
12684        }
12685      }
12686
12687    // We have a normal character in char0, and possibly one in char1
12688    } else if (this.isNormalChar(char0)) {
12689      if (char1 === 0x00) {
12690        char1 = null;
12691      }
12692      text = getCharFromCode(char0);
12693      text += getCharFromCode(char1);
12694      this[this.mode_](packet.pts, text);
12695      this.column_ += text.length;
12696
12697    } // finish data processing
12698
12699  };
12700};
12701Cea608Stream.prototype = new Stream();
12702// Trigger a cue point that captures the current state of the
12703// display buffer
12704Cea608Stream.prototype.flushDisplayed = function(pts) {
12705  var content = this.displayed_
12706    // remove spaces from the start and end of the string
12707    .map(function(row) {
12708      return row.trim();
12709    })
12710    // combine all text rows to display in one cue
12711    .join('\n')
12712    // and remove blank rows from the start and end, but not the middle
12713    .replace(/^\n+|\n+$/g, '');
12714
12715  if (content.length) {
12716    this.trigger('data', {
12717      startPts: this.startPts_,
12718      endPts: pts,
12719      text: content,
12720      stream: this.name_
12721    });
12722  }
12723};
12724
12725/**
12726 * Zero out the data, used for startup and on seek
12727 */
12728Cea608Stream.prototype.reset = function() {
12729  this.mode_ = 'popOn';
12730  // When in roll-up mode, the index of the last row that will
12731  // actually display captions. If a caption is shifted to a row
12732  // with a lower index than this, it is cleared from the display
12733  // buffer
12734  this.topRow_ = 0;
12735  this.startPts_ = 0;
12736  this.displayed_ = createDisplayBuffer();
12737  this.nonDisplayed_ = createDisplayBuffer();
12738  this.lastControlCode_ = null;
12739
12740  // Track row and column for proper line-breaking and spacing
12741  this.column_ = 0;
12742  this.row_ = BOTTOM_ROW;
12743
12744  // This variable holds currently-applied formatting
12745  this.formatting_ = [];
12746};
12747
12748/**
12749 * Sets up control code and related constants for this instance
12750 */
12751Cea608Stream.prototype.setConstants = function() {
12752  // The following attributes have these uses:
12753  // ext_ :    char0 for mid-row codes, and the base for extended
12754  //           chars (ext_+0, ext_+1, and ext_+2 are char0s for
12755  //           extended codes)
12756  // control_: char0 for control codes, except byte-shifted to the
12757  //           left so that we can do this.control_ | CONTROL_CODE
12758  // offset_:  char0 for tab offset codes
12759  //
12760  // It's also worth noting that control codes, and _only_ control codes,
12761  // differ between field 1 and field2. Field 2 control codes are always
12762  // their field 1 value plus 1. That's why there's the "| field" on the
12763  // control value.
12764  if (this.dataChannel_ === 0) {
12765    this.BASE_     = 0x10;
12766    this.EXT_      = 0x11;
12767    this.CONTROL_  = (0x14 | this.field_) << 8;
12768    this.OFFSET_   = 0x17;
12769  } else if (this.dataChannel_ === 1) {
12770    this.BASE_     = 0x18;
12771    this.EXT_      = 0x19;
12772    this.CONTROL_  = (0x1c | this.field_) << 8;
12773    this.OFFSET_   = 0x1f;
12774  }
12775
12776  // Constants for the LSByte command codes recognized by Cea608Stream. This
12777  // list is not exhaustive. For a more comprehensive listing and semantics see
12778  // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
12779  // Padding
12780  this.PADDING_                    = 0x0000;
12781  // Pop-on Mode
12782  this.RESUME_CAPTION_LOADING_     = this.CONTROL_ | 0x20;
12783  this.END_OF_CAPTION_             = this.CONTROL_ | 0x2f;
12784  // Roll-up Mode
12785  this.ROLL_UP_2_ROWS_             = this.CONTROL_ | 0x25;
12786  this.ROLL_UP_3_ROWS_             = this.CONTROL_ | 0x26;
12787  this.ROLL_UP_4_ROWS_             = this.CONTROL_ | 0x27;
12788  this.CARRIAGE_RETURN_            = this.CONTROL_ | 0x2d;
12789  // paint-on mode (not supported)
12790  this.RESUME_DIRECT_CAPTIONING_   = this.CONTROL_ | 0x29;
12791  // Erasure
12792  this.BACKSPACE_                  = this.CONTROL_ | 0x21;
12793  this.ERASE_DISPLAYED_MEMORY_     = this.CONTROL_ | 0x2c;
12794  this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
12795};
12796
12797/**
12798 * Detects if the 2-byte packet data is a special character
12799 *
12800 * Special characters have a second byte in the range 0x30 to 0x3f,
12801 * with the first byte being 0x11 (for data channel 1) or 0x19 (for
12802 * data channel 2).
12803 *
12804 * @param  {Integer} char0 The first byte
12805 * @param  {Integer} char1 The second byte
12806 * @return {Boolean}       Whether the 2 bytes are an special character
12807 */
12808Cea608Stream.prototype.isSpecialCharacter = function(char0, char1) {
12809  return (char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f);
12810};
12811
12812/**
12813 * Detects if the 2-byte packet data is an extended character
12814 *
12815 * Extended characters have a second byte in the range 0x20 to 0x3f,
12816 * with the first byte being 0x12 or 0x13 (for data channel 1) or
12817 * 0x1a or 0x1b (for data channel 2).
12818 *
12819 * @param  {Integer} char0 The first byte
12820 * @param  {Integer} char1 The second byte
12821 * @return {Boolean}       Whether the 2 bytes are an extended character
12822 */
12823Cea608Stream.prototype.isExtCharacter = function(char0, char1) {
12824  return ((char0 === (this.EXT_ + 1) || char0 === (this.EXT_ + 2)) &&
12825    (char1 >= 0x20 && char1 <= 0x3f));
12826};
12827
12828/**
12829 * Detects if the 2-byte packet is a mid-row code
12830 *
12831 * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
12832 * the first byte being 0x11 (for data channel 1) or 0x19 (for data
12833 * channel 2).
12834 *
12835 * @param  {Integer} char0 The first byte
12836 * @param  {Integer} char1 The second byte
12837 * @return {Boolean}       Whether the 2 bytes are a mid-row code
12838 */
12839Cea608Stream.prototype.isMidRowCode = function(char0, char1) {
12840  return (char0 === this.EXT_ && (char1 >= 0x20 && char1 <= 0x2f));
12841};
12842
12843/**
12844 * Detects if the 2-byte packet is an offset control code
12845 *
12846 * Offset control codes have a second byte in the range 0x21 to 0x23,
12847 * with the first byte being 0x17 (for data channel 1) or 0x1f (for
12848 * data channel 2).
12849 *
12850 * @param  {Integer} char0 The first byte
12851 * @param  {Integer} char1 The second byte
12852 * @return {Boolean}       Whether the 2 bytes are an offset control code
12853 */
12854Cea608Stream.prototype.isOffsetControlCode = function(char0, char1) {
12855  return (char0 === this.OFFSET_ && (char1 >= 0x21 && char1 <= 0x23));
12856};
12857
12858/**
12859 * Detects if the 2-byte packet is a Preamble Address Code
12860 *
12861 * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
12862 * or 0x18 to 0x1f (for data channel 2), with the second byte in the
12863 * range 0x40 to 0x7f.
12864 *
12865 * @param  {Integer} char0 The first byte
12866 * @param  {Integer} char1 The second byte
12867 * @return {Boolean}       Whether the 2 bytes are a PAC
12868 */
12869Cea608Stream.prototype.isPAC = function(char0, char1) {
12870  return (char0 >= this.BASE_ && char0 < (this.BASE_ + 8) &&
12871    (char1 >= 0x40 && char1 <= 0x7f));
12872};
12873
12874/**
12875 * Detects if a packet's second byte is in the range of a PAC color code
12876 *
12877 * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
12878 * 0x60 to 0x6f.
12879 *
12880 * @param  {Integer} char1 The second byte
12881 * @return {Boolean}       Whether the byte is a color PAC
12882 */
12883Cea608Stream.prototype.isColorPAC = function(char1) {
12884  return ((char1 >= 0x40 && char1 <= 0x4f) || (char1 >= 0x60 && char1 <= 0x7f));
12885};
12886
12887/**
12888 * Detects if a single byte is in the range of a normal character
12889 *
12890 * Normal text bytes are in the range 0x20 to 0x7f.
12891 *
12892 * @param  {Integer} char  The byte
12893 * @return {Boolean}       Whether the byte is a normal character
12894 */
12895Cea608Stream.prototype.isNormalChar = function(char) {
12896  return (char >= 0x20 && char <= 0x7f);
12897};
12898
12899// Adds the opening HTML tag for the passed character to the caption text,
12900// and keeps track of it for later closing
12901Cea608Stream.prototype.addFormatting = function(pts, format) {
12902  this.formatting_ = this.formatting_.concat(format);
12903  var text = format.reduce(function(text, format) {
12904    return text + '<' + format + '>';
12905  }, '');
12906  this[this.mode_](pts, text);
12907};
12908
12909// Adds HTML closing tags for current formatting to caption text and
12910// clears remembered formatting
12911Cea608Stream.prototype.clearFormatting = function(pts) {
12912  if (!this.formatting_.length) {
12913    return;
12914  }
12915  var text = this.formatting_.reverse().reduce(function(text, format) {
12916    return text + '</' + format + '>';
12917  }, '');
12918  this.formatting_ = [];
12919  this[this.mode_](pts, text);
12920};
12921
12922// Mode Implementations
12923Cea608Stream.prototype.popOn = function(pts, text) {
12924  var baseRow = this.nonDisplayed_[this.row_];
12925
12926  // buffer characters
12927  baseRow += text;
12928  this.nonDisplayed_[this.row_] = baseRow;
12929};
12930
12931Cea608Stream.prototype.rollUp = function(pts, text) {
12932  var baseRow = this.displayed_[BOTTOM_ROW];
12933
12934  baseRow += text;
12935  this.displayed_[BOTTOM_ROW] = baseRow;
12936
12937};
12938
12939Cea608Stream.prototype.shiftRowsUp_ = function() {
12940  var i;
12941  // clear out inactive rows
12942  for (i = 0; i < this.topRow_; i++) {
12943    this.displayed_[i] = '';
12944  }
12945  // shift displayed rows up
12946  for (i = this.topRow_; i < BOTTOM_ROW; i++) {
12947    this.displayed_[i] = this.displayed_[i + 1];
12948  }
12949  // clear out the bottom row
12950  this.displayed_[BOTTOM_ROW] = '';
12951};
12952
12953// paintOn mode is not implemented
12954Cea608Stream.prototype.paintOn = function() {};
12955
12956// exports
12957module.exports = {
12958  CaptionStream: CaptionStream,
12959  Cea608Stream: Cea608Stream
12960};
12961
12962},{"../utils/stream":62}],50:[function(require,module,exports){
12963/**
12964 * mux.js
12965 *
12966 * Copyright (c) 2015 Brightcove
12967 * All rights reserved.
12968 *
12969 * A stream-based mp2t to mp4 converter. This utility can be used to
12970 * deliver mp4s to a SourceBuffer on platforms that support native
12971 * Media Source Extensions.
12972 */
12973'use strict';
12974var Stream = require('../utils/stream.js'),
12975  CaptionStream = require('./caption-stream'),
12976  StreamTypes = require('./stream-types'),
12977  TimestampRolloverStream = require('./timestamp-rollover-stream').TimestampRolloverStream;
12978
12979var m2tsStreamTypes = require('./stream-types.js');
12980
12981// object types
12982var TransportPacketStream, TransportParseStream, ElementaryStream;
12983
12984// constants
12985var
12986  MP2T_PACKET_LENGTH = 188, // bytes
12987  SYNC_BYTE = 0x47;
12988
12989/**
12990 * Splits an incoming stream of binary data into MPEG-2 Transport
12991 * Stream packets.
12992 */
12993TransportPacketStream = function() {
12994  var
12995    buffer = new Uint8Array(MP2T_PACKET_LENGTH),
12996    bytesInBuffer = 0;
12997
12998  TransportPacketStream.prototype.init.call(this);
12999
13000   // Deliver new bytes to the stream.
13001
13002  this.push = function(bytes) {
13003    var
13004      startIndex = 0,
13005      endIndex = MP2T_PACKET_LENGTH,
13006      everything;
13007
13008    // If there are bytes remaining from the last segment, prepend them to the
13009    // bytes that were pushed in
13010    if (bytesInBuffer) {
13011      everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
13012      everything.set(buffer.subarray(0, bytesInBuffer));
13013      everything.set(bytes, bytesInBuffer);
13014      bytesInBuffer = 0;
13015    } else {
13016      everything = bytes;
13017    }
13018
13019    // While we have enough data for a packet
13020    while (endIndex < everything.byteLength) {
13021      // Look for a pair of start and end sync bytes in the data..
13022      if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
13023        // We found a packet so emit it and jump one whole packet forward in
13024        // the stream
13025        this.trigger('data', everything.subarray(startIndex, endIndex));
13026        startIndex += MP2T_PACKET_LENGTH;
13027        endIndex += MP2T_PACKET_LENGTH;
13028        continue;
13029      }
13030      // If we get here, we have somehow become de-synchronized and we need to step
13031      // forward one byte at a time until we find a pair of sync bytes that denote
13032      // a packet
13033      startIndex++;
13034      endIndex++;
13035    }
13036
13037    // If there was some data left over at the end of the segment that couldn't
13038    // possibly be a whole packet, keep it because it might be the start of a packet
13039    // that continues in the next segment
13040    if (startIndex < everything.byteLength) {
13041      buffer.set(everything.subarray(startIndex), 0);
13042      bytesInBuffer = everything.byteLength - startIndex;
13043    }
13044  };
13045
13046  this.flush = function() {
13047    // If the buffer contains a whole packet when we are being flushed, emit it
13048    // and empty the buffer. Otherwise hold onto the data because it may be
13049    // important for decoding the next segment
13050    if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
13051      this.trigger('data', buffer);
13052      bytesInBuffer = 0;
13053    }
13054    this.trigger('done');
13055  };
13056};
13057TransportPacketStream.prototype = new Stream();
13058
13059/**
13060 * Accepts an MP2T TransportPacketStream and emits data events with parsed
13061 * forms of the individual transport stream packets.
13062 */
13063TransportParseStream = function() {
13064  var parsePsi, parsePat, parsePmt, self;
13065  TransportParseStream.prototype.init.call(this);
13066  self = this;
13067
13068  this.packetsWaitingForPmt = [];
13069  this.programMapTable = undefined;
13070
13071  parsePsi = function(payload, psi) {
13072    var offset = 0;
13073
13074    // PSI packets may be split into multiple sections and those
13075    // sections may be split into multiple packets. If a PSI
13076    // section starts in this packet, the payload_unit_start_indicator
13077    // will be true and the first byte of the payload will indicate
13078    // the offset from the current position to the start of the
13079    // section.
13080    if (psi.payloadUnitStartIndicator) {
13081      offset += payload[offset] + 1;
13082    }
13083
13084    if (psi.type === 'pat') {
13085      parsePat(payload.subarray(offset), psi);
13086    } else {
13087      parsePmt(payload.subarray(offset), psi);
13088    }
13089  };
13090
13091  parsePat = function(payload, pat) {
13092    pat.section_number = payload[7]; // eslint-disable-line camelcase
13093    pat.last_section_number = payload[8]; // eslint-disable-line camelcase
13094
13095    // skip the PSI header and parse the first PMT entry
13096    self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
13097    pat.pmtPid = self.pmtPid;
13098  };
13099
13100  /**
13101   * Parse out the relevant fields of a Program Map Table (PMT).
13102   * @param payload {Uint8Array} the PMT-specific portion of an MP2T
13103   * packet. The first byte in this array should be the table_id
13104   * field.
13105   * @param pmt {object} the object that should be decorated with
13106   * fields parsed from the PMT.
13107   */
13108  parsePmt = function(payload, pmt) {
13109    var sectionLength, tableEnd, programInfoLength, offset;
13110
13111    // PMTs can be sent ahead of the time when they should actually
13112    // take effect. We don't believe this should ever be the case
13113    // for HLS but we'll ignore "forward" PMT declarations if we see
13114    // them. Future PMT declarations have the current_next_indicator
13115    // set to zero.
13116    if (!(payload[5] & 0x01)) {
13117      return;
13118    }
13119
13120    // overwrite any existing program map table
13121    self.programMapTable = {};
13122
13123    // the mapping table ends at the end of the current section
13124    sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
13125    tableEnd = 3 + sectionLength - 4;
13126
13127    // to determine where the table is, we have to figure out how
13128    // long the program info descriptors are
13129    programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];
13130
13131    // advance the offset to the first entry in the mapping table
13132    offset = 12 + programInfoLength;
13133    while (offset < tableEnd) {
13134      // add an entry that maps the elementary_pid to the stream_type
13135      self.programMapTable[(payload[offset + 1] & 0x1F) << 8 | payload[offset + 2]] = payload[offset];
13136
13137      // move to the next table entry
13138      // skip past the elementary stream descriptors, if present
13139      offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
13140    }
13141
13142    // record the map on the packet as well
13143    pmt.programMapTable = self.programMapTable;
13144
13145    // if there are any packets waiting for a PMT to be found, process them now
13146    while (self.packetsWaitingForPmt.length) {
13147      self.processPes_.apply(self, self.packetsWaitingForPmt.shift());
13148    }
13149  };
13150
13151  /**
13152   * Deliver a new MP2T packet to the stream.
13153   */
13154  this.push = function(packet) {
13155    var
13156      result = {},
13157      offset = 4;
13158
13159    result.payloadUnitStartIndicator = !!(packet[1] & 0x40);
13160
13161    // pid is a 13-bit field starting at the last bit of packet[1]
13162    result.pid = packet[1] & 0x1f;
13163    result.pid <<= 8;
13164    result.pid |= packet[2];
13165
13166    // if an adaption field is present, its length is specified by the
13167    // fifth byte of the TS packet header. The adaptation field is
13168    // used to add stuffing to PES packets that don't fill a complete
13169    // TS packet, and to specify some forms of timing and control data
13170    // that we do not currently use.
13171    if (((packet[3] & 0x30) >>> 4) > 0x01) {
13172      offset += packet[offset] + 1;
13173    }
13174
13175    // parse the rest of the packet based on the type
13176    if (result.pid === 0) {
13177      result.type = 'pat';
13178      parsePsi(packet.subarray(offset), result);
13179      this.trigger('data', result);
13180    } else if (result.pid === this.pmtPid) {
13181      result.type = 'pmt';
13182      parsePsi(packet.subarray(offset), result);
13183      this.trigger('data', result);
13184    } else if (this.programMapTable === undefined) {
13185      // When we have not seen a PMT yet, defer further processing of
13186      // PES packets until one has been parsed
13187      this.packetsWaitingForPmt.push([packet, offset, result]);
13188    } else {
13189      this.processPes_(packet, offset, result);
13190    }
13191  };
13192
13193  this.processPes_ = function(packet, offset, result) {
13194    result.streamType = this.programMapTable[result.pid];
13195    result.type = 'pes';
13196    result.data = packet.subarray(offset);
13197
13198    this.trigger('data', result);
13199  };
13200
13201};
13202TransportParseStream.prototype = new Stream();
13203TransportParseStream.STREAM_TYPES  = {
13204  h264: 0x1b,
13205  adts: 0x0f
13206};
13207
13208/**
13209 * Reconsistutes program elementary stream (PES) packets from parsed
13210 * transport stream packets. That is, if you pipe an
13211 * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
13212 * events will be events which capture the bytes for individual PES
13213 * packets plus relevant metadata that has been extracted from the
13214 * container.
13215 */
13216ElementaryStream = function() {
13217  var
13218    self = this,
13219    // PES packet fragments
13220    video = {
13221      data: [],
13222      size: 0
13223    },
13224    audio = {
13225      data: [],
13226      size: 0
13227    },
13228    timedMetadata = {
13229      data: [],
13230      size: 0
13231    },
13232    parsePes = function(payload, pes) {
13233      var ptsDtsFlags;
13234
13235      // get the packet length, this will be 0 for video
13236      pes.packetLength = 6 + ((payload[4] << 8) | payload[5]);
13237
13238      // find out if this packets starts a new keyframe
13239      pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
13240      // PES packets may be annotated with a PTS value, or a PTS value
13241      // and a DTS value. Determine what combination of values is
13242      // available to work with.
13243      ptsDtsFlags = payload[7];
13244
13245      // PTS and DTS are normally stored as a 33-bit number.  Javascript
13246      // performs all bitwise operations on 32-bit integers but javascript
13247      // supports a much greater range (52-bits) of integer using standard
13248      // mathematical operations.
13249      // We construct a 31-bit value using bitwise operators over the 31
13250      // most significant bits and then multiply by 4 (equal to a left-shift
13251      // of 2) before we add the final 2 least significant bits of the
13252      // timestamp (equal to an OR.)
13253      if (ptsDtsFlags & 0xC0) {
13254        // the PTS and DTS are not written out directly. For information
13255        // on how they are encoded, see
13256        // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
13257        pes.pts = (payload[9] & 0x0E) << 27 |
13258          (payload[10] & 0xFF) << 20 |
13259          (payload[11] & 0xFE) << 12 |
13260          (payload[12] & 0xFF) <<  5 |
13261          (payload[13] & 0xFE) >>>  3;
13262        pes.pts *= 4; // Left shift by 2
13263        pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
13264        pes.dts = pes.pts;
13265        if (ptsDtsFlags & 0x40) {
13266          pes.dts = (payload[14] & 0x0E) << 27 |
13267            (payload[15] & 0xFF) << 20 |
13268            (payload[16] & 0xFE) << 12 |
13269            (payload[17] & 0xFF) << 5 |
13270            (payload[18] & 0xFE) >>> 3;
13271          pes.dts *= 4; // Left shift by 2
13272          pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
13273        }
13274      }
13275      // the data section starts immediately after the PES header.
13276      // pes_header_data_length specifies the number of header bytes
13277      // that follow the last byte of the field.
13278      pes.data = payload.subarray(9 + payload[8]);
13279    },
13280    flushStream = function(stream, type, forceFlush) {
13281      var
13282        packetData = new Uint8Array(stream.size),
13283        event = {
13284          type: type
13285        },
13286        i = 0,
13287        offset = 0,
13288        packetFlushable = false,
13289        fragment;
13290
13291      // do nothing if there is not enough buffered data for a complete
13292      // PES header
13293      if (!stream.data.length || stream.size < 9) {
13294        return;
13295      }
13296      event.trackId = stream.data[0].pid;
13297
13298      // reassemble the packet
13299      for (i = 0; i < stream.data.length; i++) {
13300        fragment = stream.data[i];
13301
13302        packetData.set(fragment.data, offset);
13303        offset += fragment.data.byteLength;
13304      }
13305
13306      // parse assembled packet's PES header
13307      parsePes(packetData, event);
13308
13309      // non-video PES packets MUST have a non-zero PES_packet_length
13310      // check that they match before we do a flush
13311      packetFlushable = type === 'video' || event.packetLength === stream.size;
13312
13313      // flush pending packets if the conditions are right
13314      if (forceFlush || packetFlushable) {
13315        stream.size = 0;
13316        stream.data.length = 0;
13317      }
13318
13319      // only emit packets that are complete. this is to avoid assembling
13320      // incomplete PES packets due to poor segmentation
13321      if (packetFlushable) {
13322        self.trigger('data', event);
13323      }
13324    };
13325
13326  ElementaryStream.prototype.init.call(this);
13327
13328  this.push = function(data) {
13329    ({
13330      pat: function() {
13331        // we have to wait for the PMT to arrive as well before we
13332        // have any meaningful metadata
13333      },
13334      pes: function() {
13335        var stream, streamType;
13336
13337        switch (data.streamType) {
13338        case StreamTypes.H264_STREAM_TYPE:
13339        case m2tsStreamTypes.H264_STREAM_TYPE:
13340          stream = video;
13341          streamType = 'video';
13342          break;
13343        case StreamTypes.ADTS_STREAM_TYPE:
13344          stream = audio;
13345          streamType = 'audio';
13346          break;
13347        case StreamTypes.METADATA_STREAM_TYPE:
13348          stream = timedMetadata;
13349          streamType = 'timed-metadata';
13350          break;
13351        default:
13352          // ignore unknown stream types
13353          return;
13354        }
13355
13356        // if a new packet is starting, we can flush the completed
13357        // packet
13358        if (data.payloadUnitStartIndicator) {
13359          flushStream(stream, streamType, true);
13360        }
13361
13362        // buffer this fragment until we are sure we've received the
13363        // complete payload
13364        stream.data.push(data);
13365        stream.size += data.data.byteLength;
13366      },
13367      pmt: function() {
13368        var
13369          event = {
13370            type: 'metadata',
13371            tracks: []
13372          },
13373          programMapTable = data.programMapTable,
13374          k,
13375          track;
13376
13377        // translate streams to tracks
13378        for (k in programMapTable) {
13379          if (programMapTable.hasOwnProperty(k)) {
13380            track = {
13381              timelineStartInfo: {
13382                baseMediaDecodeTime: 0
13383              }
13384            };
13385            track.id = +k;
13386            if (programMapTable[k] === m2tsStreamTypes.H264_STREAM_TYPE) {
13387              track.codec = 'avc';
13388              track.type = 'video';
13389            } else if (programMapTable[k] === m2tsStreamTypes.ADTS_STREAM_TYPE) {
13390              track.codec = 'adts';
13391              track.type = 'audio';
13392            }
13393            event.tracks.push(track);
13394          }
13395        }
13396        self.trigger('data', event);
13397      }
13398    })[data.type]();
13399  };
13400
13401  /**
13402   * Flush any remaining input. Video PES packets may be of variable
13403   * length. Normally, the start of a new video packet can trigger the
13404   * finalization of the previous packet. That is not possible if no
13405   * more video is forthcoming, however. In that case, some other
13406   * mechanism (like the end of the file) has to be employed. When it is
13407   * clear that no additional data is forthcoming, calling this method
13408   * will flush the buffered packets.
13409   */
13410  this.flush = function() {
13411    // !!THIS ORDER IS IMPORTANT!!
13412    // video first then audio
13413    flushStream(video, 'video');
13414    flushStream(audio, 'audio');
13415    flushStream(timedMetadata, 'timed-metadata');
13416    this.trigger('done');
13417  };
13418};
13419ElementaryStream.prototype = new Stream();
13420
13421var m2ts = {
13422  PAT_PID: 0x0000,
13423  MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
13424  TransportPacketStream: TransportPacketStream,
13425  TransportParseStream: TransportParseStream,
13426  ElementaryStream: ElementaryStream,
13427  TimestampRolloverStream: TimestampRolloverStream,
13428  CaptionStream: CaptionStream.CaptionStream,
13429  Cea608Stream: CaptionStream.Cea608Stream,
13430  MetadataStream: require('./metadata-stream')
13431};
13432
13433for (var type in StreamTypes) {
13434  if (StreamTypes.hasOwnProperty(type)) {
13435    m2ts[type] = StreamTypes[type];
13436  }
13437}
13438
13439module.exports = m2ts;
13440
13441},{"../utils/stream.js":62,"./caption-stream":49,"./metadata-stream":51,"./stream-types":53,"./stream-types.js":53,"./timestamp-rollover-stream":54}],51:[function(require,module,exports){
13442/**
13443 * Accepts program elementary stream (PES) data events and parses out
13444 * ID3 metadata from them, if present.
13445 * @see http://id3.org/id3v2.3.0
13446 */
13447'use strict';
13448var
13449  Stream = require('../utils/stream'),
13450  StreamTypes = require('./stream-types'),
13451  // return a percent-encoded representation of the specified byte range
13452  // @see http://en.wikipedia.org/wiki/Percent-encoding
13453  percentEncode = function(bytes, start, end) {
13454    var i, result = '';
13455    for (i = start; i < end; i++) {
13456      result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
13457    }
13458    return result;
13459  },
13460  // return the string representation of the specified byte range,
13461  // interpreted as UTf-8.
13462  parseUtf8 = function(bytes, start, end) {
13463    return decodeURIComponent(percentEncode(bytes, start, end));
13464  },
13465  // return the string representation of the specified byte range,
13466  // interpreted as ISO-8859-1.
13467  parseIso88591 = function(bytes, start, end) {
13468    return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
13469  },
13470  parseSyncSafeInteger = function(data) {
13471    return (data[0] << 21) |
13472            (data[1] << 14) |
13473            (data[2] << 7) |
13474            (data[3]);
13475  },
13476  tagParsers = {
13477    TXXX: function(tag) {
13478      var i;
13479      if (tag.data[0] !== 3) {
13480        // ignore frames with unrecognized character encodings
13481        return;
13482      }
13483
13484      for (i = 1; i < tag.data.length; i++) {
13485        if (tag.data[i] === 0) {
13486          // parse the text fields
13487          tag.description = parseUtf8(tag.data, 1, i);
13488          // do not include the null terminator in the tag value
13489          tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
13490          break;
13491        }
13492      }
13493      tag.data = tag.value;
13494    },
13495    WXXX: function(tag) {
13496      var i;
13497      if (tag.data[0] !== 3) {
13498        // ignore frames with unrecognized character encodings
13499        return;
13500      }
13501
13502      for (i = 1; i < tag.data.length; i++) {
13503        if (tag.data[i] === 0) {
13504          // parse the description and URL fields
13505          tag.description = parseUtf8(tag.data, 1, i);
13506          tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
13507          break;
13508        }
13509      }
13510    },
13511    PRIV: function(tag) {
13512      var i;
13513
13514      for (i = 0; i < tag.data.length; i++) {
13515        if (tag.data[i] === 0) {
13516          // parse the description and URL fields
13517          tag.owner = parseIso88591(tag.data, 0, i);
13518          break;
13519        }
13520      }
13521      tag.privateData = tag.data.subarray(i + 1);
13522      tag.data = tag.privateData;
13523    }
13524  },
13525  MetadataStream;
13526
13527MetadataStream = function(options) {
13528  var
13529    settings = {
13530      debug: !!(options && options.debug),
13531
13532      // the bytes of the program-level descriptor field in MP2T
13533      // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
13534      // program element descriptors"
13535      descriptor: options && options.descriptor
13536    },
13537    // the total size in bytes of the ID3 tag being parsed
13538    tagSize = 0,
13539    // tag data that is not complete enough to be parsed
13540    buffer = [],
13541    // the total number of bytes currently in the buffer
13542    bufferSize = 0,
13543    i;
13544
13545  MetadataStream.prototype.init.call(this);
13546
13547  // calculate the text track in-band metadata track dispatch type
13548  // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
13549  this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
13550  if (settings.descriptor) {
13551    for (i = 0; i < settings.descriptor.length; i++) {
13552      this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
13553    }
13554  }
13555
13556  this.push = function(chunk) {
13557    var tag, frameStart, frameSize, frame, i, frameHeader;
13558    if (chunk.type !== 'timed-metadata') {
13559      return;
13560    }
13561
13562    // if data_alignment_indicator is set in the PES header,
13563    // we must have the start of a new ID3 tag. Assume anything
13564    // remaining in the buffer was malformed and throw it out
13565    if (chunk.dataAlignmentIndicator) {
13566      bufferSize = 0;
13567      buffer.length = 0;
13568    }
13569
13570    // ignore events that don't look like ID3 data
13571    if (buffer.length === 0 &&
13572        (chunk.data.length < 10 ||
13573          chunk.data[0] !== 'I'.charCodeAt(0) ||
13574          chunk.data[1] !== 'D'.charCodeAt(0) ||
13575          chunk.data[2] !== '3'.charCodeAt(0))) {
13576      if (settings.debug) {
13577        // eslint-disable-next-line no-console
13578        console.log('Skipping unrecognized metadata packet');
13579      }
13580      return;
13581    }
13582
13583    // add this chunk to the data we've collected so far
13584
13585    buffer.push(chunk);
13586    bufferSize += chunk.data.byteLength;
13587
13588    // grab the size of the entire frame from the ID3 header
13589    if (buffer.length === 1) {
13590      // the frame size is transmitted as a 28-bit integer in the
13591      // last four bytes of the ID3 header.
13592      // The most significant bit of each byte is dropped and the
13593      // results concatenated to recover the actual value.
13594      tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
13595
13596      // ID3 reports the tag size excluding the header but it's more
13597      // convenient for our comparisons to include it
13598      tagSize += 10;
13599    }
13600
13601    // if the entire frame has not arrived, wait for more data
13602    if (bufferSize < tagSize) {
13603      return;
13604    }
13605
13606    // collect the entire frame so it can be parsed
13607    tag = {
13608      data: new Uint8Array(tagSize),
13609      frames: [],
13610      pts: buffer[0].pts,
13611      dts: buffer[0].dts
13612    };
13613    for (i = 0; i < tagSize;) {
13614      tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
13615      i += buffer[0].data.byteLength;
13616      bufferSize -= buffer[0].data.byteLength;
13617      buffer.shift();
13618    }
13619
13620    // find the start of the first frame and the end of the tag
13621    frameStart = 10;
13622    if (tag.data[5] & 0x40) {
13623      // advance the frame start past the extended header
13624      frameStart += 4; // header size field
13625      frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
13626
13627      // clip any padding off the end
13628      tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
13629    }
13630
13631    // parse one or more ID3 frames
13632    // http://id3.org/id3v2.3.0#ID3v2_frame_overview
13633    do {
13634      // determine the number of bytes in this frame
13635      frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
13636      if (frameSize < 1) {
13637         // eslint-disable-next-line no-console
13638        return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
13639      }
13640      frameHeader = String.fromCharCode(tag.data[frameStart],
13641                                        tag.data[frameStart + 1],
13642                                        tag.data[frameStart + 2],
13643                                        tag.data[frameStart + 3]);
13644
13645
13646      frame = {
13647        id: frameHeader,
13648        data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
13649      };
13650      frame.key = frame.id;
13651      if (tagParsers[frame.id]) {
13652        tagParsers[frame.id](frame);
13653
13654        // handle the special PRIV frame used to indicate the start
13655        // time for raw AAC data
13656        if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
13657          var
13658            d = frame.data,
13659            size = ((d[3] & 0x01)  << 30) |
13660                   (d[4]  << 22) |
13661                   (d[5] << 14) |
13662                   (d[6] << 6) |
13663                   (d[7] >>> 2);
13664
13665          size *= 4;
13666          size += d[7] & 0x03;
13667          frame.timeStamp = size;
13668          // in raw AAC, all subsequent data will be timestamped based
13669          // on the value of this frame
13670          // we couldn't have known the appropriate pts and dts before
13671          // parsing this ID3 tag so set those values now
13672          if (tag.pts === undefined && tag.dts === undefined) {
13673            tag.pts = frame.timeStamp;
13674            tag.dts = frame.timeStamp;
13675          }
13676          this.trigger('timestamp', frame);
13677        }
13678      }
13679      tag.frames.push(frame);
13680
13681      frameStart += 10; // advance past the frame header
13682      frameStart += frameSize; // advance past the frame body
13683    } while (frameStart < tagSize);
13684    this.trigger('data', tag);
13685  };
13686};
13687MetadataStream.prototype = new Stream();
13688
13689module.exports = MetadataStream;
13690
13691},{"../utils/stream":62,"./stream-types":53}],52:[function(require,module,exports){
13692/**
13693 * mux.js
13694 *
13695 * Copyright (c) 2016 Brightcove
13696 * All rights reserved.
13697 *
13698 * Utilities to detect basic properties and metadata about TS Segments.
13699 */
13700'use strict';
13701
13702var StreamTypes = require('./stream-types.js');
13703
13704var parsePid = function(packet) {
13705  var pid = packet[1] & 0x1f;
13706  pid <<= 8;
13707  pid |= packet[2];
13708  return pid;
13709};
13710
13711var parsePayloadUnitStartIndicator = function(packet) {
13712  return !!(packet[1] & 0x40);
13713};
13714
13715var parseAdaptionField = function(packet) {
13716  var offset = 0;
13717  // if an adaption field is present, its length is specified by the
13718  // fifth byte of the TS packet header. The adaptation field is
13719  // used to add stuffing to PES packets that don't fill a complete
13720  // TS packet, and to specify some forms of timing and control data
13721  // that we do not currently use.
13722  if (((packet[3] & 0x30) >>> 4) > 0x01) {
13723    offset += packet[4] + 1;
13724  }
13725  return offset;
13726};
13727
13728var parseType = function(packet, pmtPid) {
13729  var pid = parsePid(packet);
13730  if (pid === 0) {
13731    return 'pat';
13732  } else if (pid === pmtPid) {
13733    return 'pmt';
13734  } else if (pmtPid) {
13735    return 'pes';
13736  }
13737  return null;
13738};
13739
13740var parsePat = function(packet) {
13741  var pusi = parsePayloadUnitStartIndicator(packet);
13742  var offset = 4 + parseAdaptionField(packet);
13743
13744  if (pusi) {
13745    offset += packet[offset] + 1;
13746  }
13747
13748  return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
13749};
13750
13751var parsePmt = function(packet) {
13752  var programMapTable = {};
13753  var pusi = parsePayloadUnitStartIndicator(packet);
13754  var payloadOffset = 4 + parseAdaptionField(packet);
13755
13756  if (pusi) {
13757    payloadOffset += packet[payloadOffset] + 1;
13758  }
13759
13760  // PMTs can be sent ahead of the time when they should actually
13761  // take effect. We don't believe this should ever be the case
13762  // for HLS but we'll ignore "forward" PMT declarations if we see
13763  // them. Future PMT declarations have the current_next_indicator
13764  // set to zero.
13765  if (!(packet[payloadOffset + 5] & 0x01)) {
13766    return;
13767  }
13768
13769  var sectionLength, tableEnd, programInfoLength;
13770  // the mapping table ends at the end of the current section
13771  sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
13772  tableEnd = 3 + sectionLength - 4;
13773
13774  // to determine where the table is, we have to figure out how
13775  // long the program info descriptors are
13776  programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11];
13777
13778  // advance the offset to the first entry in the mapping table
13779  var offset = 12 + programInfoLength;
13780  while (offset < tableEnd) {
13781    var i = payloadOffset + offset;
13782    // add an entry that maps the elementary_pid to the stream_type
13783    programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i];
13784
13785    // move to the next table entry
13786    // skip past the elementary stream descriptors, if present
13787    offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
13788  }
13789  return programMapTable;
13790};
13791
13792var parsePesType = function(packet, programMapTable) {
13793  var pid = parsePid(packet);
13794  var type = programMapTable[pid];
13795  switch (type) {
13796    case StreamTypes.H264_STREAM_TYPE:
13797      return 'video';
13798    case StreamTypes.ADTS_STREAM_TYPE:
13799      return 'audio';
13800    case StreamTypes.METADATA_STREAM_TYPE:
13801      return 'timed-metadata';
13802    default:
13803      return null;
13804  }
13805};
13806
13807var parsePesTime = function(packet) {
13808  var pusi = parsePayloadUnitStartIndicator(packet);
13809  if (!pusi) {
13810    return null;
13811  }
13812
13813  var offset = 4 + parseAdaptionField(packet);
13814
13815  if (offset >= packet.byteLength) {
13816    // From the H 222.0 MPEG-TS spec
13817    // "For transport stream packets carrying PES packets, stuffing is needed when there
13818    //  is insufficient PES packet data to completely fill the transport stream packet
13819    //  payload bytes. Stuffing is accomplished by defining an adaptation field longer than
13820    //  the sum of the lengths of the data elements in it, so that the payload bytes
13821    //  remaining after the adaptation field exactly accommodates the available PES packet
13822    //  data."
13823    //
13824    // If the offset is >= the length of the packet, then the packet contains no data
13825    // and instead is just adaption field stuffing bytes
13826    return null;
13827  }
13828
13829  var pes = null;
13830  var ptsDtsFlags;
13831
13832  // PES packets may be annotated with a PTS value, or a PTS value
13833  // and a DTS value. Determine what combination of values is
13834  // available to work with.
13835  ptsDtsFlags = packet[offset + 7];
13836
13837  // PTS and DTS are normally stored as a 33-bit number.  Javascript
13838  // performs all bitwise operations on 32-bit integers but javascript
13839  // supports a much greater range (52-bits) of integer using standard
13840  // mathematical operations.
13841  // We construct a 31-bit value using bitwise operators over the 31
13842  // most significant bits and then multiply by 4 (equal to a left-shift
13843  // of 2) before we add the final 2 least significant bits of the
13844  // timestamp (equal to an OR.)
13845  if (ptsDtsFlags & 0xC0) {
13846    pes = {};
13847    // the PTS and DTS are not written out directly. For information
13848    // on how they are encoded, see
13849    // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
13850    pes.pts = (packet[offset + 9] & 0x0E) << 27 |
13851      (packet[offset + 10] & 0xFF) << 20 |
13852      (packet[offset + 11] & 0xFE) << 12 |
13853      (packet[offset + 12] & 0xFF) <<  5 |
13854      (packet[offset + 13] & 0xFE) >>>  3;
13855    pes.pts *= 4; // Left shift by 2
13856    pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
13857    pes.dts = pes.pts;
13858    if (ptsDtsFlags & 0x40) {
13859      pes.dts = (packet[offset + 14] & 0x0E) << 27 |
13860        (packet[offset + 15] & 0xFF) << 20 |
13861        (packet[offset + 16] & 0xFE) << 12 |
13862        (packet[offset + 17] & 0xFF) << 5 |
13863        (packet[offset + 18] & 0xFE) >>> 3;
13864      pes.dts *= 4; // Left shift by 2
13865      pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
13866    }
13867  }
13868  return pes;
13869};
13870
13871var parseNalUnitType = function(type) {
13872  switch (type) {
13873    case 0x05:
13874      return 'slice_layer_without_partitioning_rbsp_idr';
13875    case 0x06:
13876      return 'sei_rbsp';
13877    case 0x07:
13878      return 'seq_parameter_set_rbsp';
13879    case 0x08:
13880      return 'pic_parameter_set_rbsp';
13881    case 0x09:
13882      return 'access_unit_delimiter_rbsp';
13883    default:
13884      return null;
13885  }
13886};
13887
13888var videoPacketContainsKeyFrame = function(packet) {
13889  var offset = 4 + parseAdaptionField(packet);
13890  var frameBuffer = packet.subarray(offset);
13891  var frameI = 0;
13892  var frameSyncPoint = 0;
13893  var foundKeyFrame = false;
13894  var nalType;
13895
13896  // advance the sync point to a NAL start, if necessary
13897  for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
13898    if (frameBuffer[frameSyncPoint + 2] === 1) {
13899      // the sync point is properly aligned
13900      frameI = frameSyncPoint + 5;
13901      break;
13902    }
13903  }
13904
13905  while (frameI < frameBuffer.byteLength) {
13906    // look at the current byte to determine if we've hit the end of
13907    // a NAL unit boundary
13908    switch (frameBuffer[frameI]) {
13909    case 0:
13910      // skip past non-sync sequences
13911      if (frameBuffer[frameI - 1] !== 0) {
13912        frameI += 2;
13913        break;
13914      } else if (frameBuffer[frameI - 2] !== 0) {
13915        frameI++;
13916        break;
13917      }
13918
13919      if (frameSyncPoint + 3 !== frameI - 2) {
13920        nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
13921        if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
13922          foundKeyFrame = true;
13923        }
13924      }
13925
13926      // drop trailing zeroes
13927      do {
13928        frameI++;
13929      } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
13930      frameSyncPoint = frameI - 2;
13931      frameI += 3;
13932      break;
13933    case 1:
13934      // skip past non-sync sequences
13935      if (frameBuffer[frameI - 1] !== 0 ||
13936          frameBuffer[frameI - 2] !== 0) {
13937        frameI += 3;
13938        break;
13939      }
13940
13941      nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
13942      if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
13943        foundKeyFrame = true;
13944      }
13945      frameSyncPoint = frameI - 2;
13946      frameI += 3;
13947      break;
13948    default:
13949      // the current byte isn't a one or zero, so it cannot be part
13950      // of a sync sequence
13951      frameI += 3;
13952      break;
13953    }
13954  }
13955  frameBuffer = frameBuffer.subarray(frameSyncPoint);
13956  frameI -= frameSyncPoint;
13957  frameSyncPoint = 0;
13958  // parse the final nal
13959  if (frameBuffer && frameBuffer.byteLength > 3) {
13960    nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
13961    if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
13962      foundKeyFrame = true;
13963    }
13964  }
13965
13966  return foundKeyFrame;
13967};
13968
13969
13970module.exports = {
13971  parseType: parseType,
13972  parsePat: parsePat,
13973  parsePmt: parsePmt,
13974  parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
13975  parsePesType: parsePesType,
13976  parsePesTime: parsePesTime,
13977  videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
13978};
13979
13980},{"./stream-types.js":53}],53:[function(require,module,exports){
13981'use strict';
13982
13983module.exports = {
13984  H264_STREAM_TYPE: 0x1B,
13985  ADTS_STREAM_TYPE: 0x0F,
13986  METADATA_STREAM_TYPE: 0x15
13987};
13988
13989},{}],54:[function(require,module,exports){
13990/**
13991 * mux.js
13992 *
13993 * Copyright (c) 2016 Brightcove
13994 * All rights reserved.
13995 *
13996 * Accepts program elementary stream (PES) data events and corrects
13997 * decode and presentation time stamps to account for a rollover
13998 * of the 33 bit value.
13999 */
14000
14001'use strict';
14002
14003var Stream = require('../utils/stream');
14004
14005var MAX_TS = 8589934592;
14006
14007var RO_THRESH = 4294967296;
14008
14009var handleRollover = function(value, reference) {
14010  var direction = 1;
14011
14012  if (value > reference) {
14013    // If the current timestamp value is greater than our reference timestamp and we detect a
14014    // timestamp rollover, this means the roll over is happening in the opposite direction.
14015    // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
14016    // point will be set to a small number, e.g. 1. The user then seeks backwards over the
14017    // rollover point. In loading this segment, the timestamp values will be very large,
14018    // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
14019    // the time stamp to be `value - 2^33`.
14020    direction = -1;
14021  }
14022
14023  // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
14024  // cause an incorrect adjustment.
14025  while (Math.abs(reference - value) > RO_THRESH) {
14026    value += (direction * MAX_TS);
14027  }
14028
14029  return value;
14030};
14031
14032var TimestampRolloverStream = function(type) {
14033  var lastDTS, referenceDTS;
14034
14035  TimestampRolloverStream.prototype.init.call(this);
14036
14037  this.type_ = type;
14038
14039  this.push = function(data) {
14040    if (data.type !== this.type_) {
14041      return;
14042    }
14043
14044    if (referenceDTS === undefined) {
14045      referenceDTS = data.dts;
14046    }
14047
14048    data.dts = handleRollover(data.dts, referenceDTS);
14049    data.pts = handleRollover(data.pts, referenceDTS);
14050
14051    lastDTS = data.dts;
14052
14053    this.trigger('data', data);
14054  };
14055
14056  this.flush = function() {
14057    referenceDTS = lastDTS;
14058    this.trigger('done');
14059  };
14060
14061  this.discontinuity = function() {
14062    referenceDTS = void 0;
14063    lastDTS = void 0;
14064  };
14065
14066};
14067
14068TimestampRolloverStream.prototype = new Stream();
14069
14070module.exports = {
14071  TimestampRolloverStream: TimestampRolloverStream,
14072  handleRollover: handleRollover
14073};
14074
14075},{"../utils/stream":62}],55:[function(require,module,exports){
14076module.exports = {
14077  generator: require('./mp4-generator'),
14078  Transmuxer: require('./transmuxer').Transmuxer,
14079  AudioSegmentStream: require('./transmuxer').AudioSegmentStream,
14080  VideoSegmentStream: require('./transmuxer').VideoSegmentStream
14081};
14082
14083},{"./mp4-generator":56,"./transmuxer":58}],56:[function(require,module,exports){
14084/**
14085 * mux.js
14086 *
14087 * Copyright (c) 2015 Brightcove
14088 * All rights reserved.
14089 *
14090 * Functions that generate fragmented MP4s suitable for use with Media
14091 * Source Extensions.
14092 */
14093'use strict';
vendor: 21,889 bytes, lines 14093-14854
14093
14094
14095var UINT32_MAX = Math.pow(2, 32) - 1;
14096
14097var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd,
14098    trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex,
14099    trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR,
14100    AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS;
14101
14102// pre-calculate constants
14103(function() {
14104  var i;
14105  types = {
14106    avc1: [], // codingname
14107    avcC: [],
14108    btrt: [],
14109    dinf: [],
14110    dref: [],
14111    esds: [],
14112    ftyp: [],
14113    hdlr: [],
14114    mdat: [],
14115    mdhd: [],
14116    mdia: [],
14117    mfhd: [],
14118    minf: [],
14119    moof: [],
14120    moov: [],
14121    mp4a: [], // codingname
14122    mvex: [],
14123    mvhd: [],
14124    sdtp: [],
14125    smhd: [],
14126    stbl: [],
14127    stco: [],
14128    stsc: [],
14129    stsd: [],
14130    stsz: [],
14131    stts: [],
14132    styp: [],
14133    tfdt: [],
14134    tfhd: [],
14135    traf: [],
14136    trak: [],
14137    trun: [],
14138    trex: [],
14139    tkhd: [],
14140    vmhd: []
14141  };
14142
14143  // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
14144  // don't throw an error
14145  if (typeof Uint8Array === 'undefined') {
14146    return;
14147  }
14148
14149  for (i in types) {
14150    if (types.hasOwnProperty(i)) {
14151      types[i] = [
14152        i.charCodeAt(0),
14153        i.charCodeAt(1),
14154        i.charCodeAt(2),
14155        i.charCodeAt(3)
14156      ];
14157    }
14158  }
14159
14160  MAJOR_BRAND = new Uint8Array([
14161    'i'.charCodeAt(0),
14162    's'.charCodeAt(0),
14163    'o'.charCodeAt(0),
14164    'm'.charCodeAt(0)
14165  ]);
14166  AVC1_BRAND = new Uint8Array([
14167    'a'.charCodeAt(0),
14168    'v'.charCodeAt(0),
14169    'c'.charCodeAt(0),
14170    '1'.charCodeAt(0)
14171  ]);
14172  MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
14173  VIDEO_HDLR = new Uint8Array([
14174    0x00, // version 0
14175    0x00, 0x00, 0x00, // flags
14176    0x00, 0x00, 0x00, 0x00, // pre_defined
14177    0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
14178    0x00, 0x00, 0x00, 0x00, // reserved
14179    0x00, 0x00, 0x00, 0x00, // reserved
14180    0x00, 0x00, 0x00, 0x00, // reserved
14181    0x56, 0x69, 0x64, 0x65,
14182    0x6f, 0x48, 0x61, 0x6e,
14183    0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
14184  ]);
14185  AUDIO_HDLR = new Uint8Array([
14186    0x00, // version 0
14187    0x00, 0x00, 0x00, // flags
14188    0x00, 0x00, 0x00, 0x00, // pre_defined
14189    0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
14190    0x00, 0x00, 0x00, 0x00, // reserved
14191    0x00, 0x00, 0x00, 0x00, // reserved
14192    0x00, 0x00, 0x00, 0x00, // reserved
14193    0x53, 0x6f, 0x75, 0x6e,
14194    0x64, 0x48, 0x61, 0x6e,
14195    0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
14196  ]);
14197  HDLR_TYPES = {
14198    video: VIDEO_HDLR,
14199    audio: AUDIO_HDLR
14200  };
14201  DREF = new Uint8Array([
14202    0x00, // version 0
14203    0x00, 0x00, 0x00, // flags
14204    0x00, 0x00, 0x00, 0x01, // entry_count
14205    0x00, 0x00, 0x00, 0x0c, // entry_size
14206    0x75, 0x72, 0x6c, 0x20, // 'url' type
14207    0x00, // version 0
14208    0x00, 0x00, 0x01 // entry_flags
14209  ]);
14210  SMHD = new Uint8Array([
14211    0x00,             // version
14212    0x00, 0x00, 0x00, // flags
14213    0x00, 0x00,       // balance, 0 means centered
14214    0x00, 0x00        // reserved
14215  ]);
14216  STCO = new Uint8Array([
14217    0x00, // version
14218    0x00, 0x00, 0x00, // flags
14219    0x00, 0x00, 0x00, 0x00 // entry_count
14220  ]);
14221  STSC = STCO;
14222  STSZ = new Uint8Array([
14223    0x00, // version
14224    0x00, 0x00, 0x00, // flags
14225    0x00, 0x00, 0x00, 0x00, // sample_size
14226    0x00, 0x00, 0x00, 0x00 // sample_count
14227  ]);
14228  STTS = STCO;
14229  VMHD = new Uint8Array([
14230    0x00, // version
14231    0x00, 0x00, 0x01, // flags
14232    0x00, 0x00, // graphicsmode
14233    0x00, 0x00,
14234    0x00, 0x00,
14235    0x00, 0x00 // opcolor
14236  ]);
14237}());
14238
14239box = function(type) {
14240  var
14241    payload = [],
14242    size = 0,
14243    i,
14244    result,
14245    view;
14246
14247  for (i = 1; i < arguments.length; i++) {
14248    payload.push(arguments[i]);
14249  }
14250
14251  i = payload.length;
14252
14253  // calculate the total size we need to allocate
14254  while (i--) {
14255    size += payload[i].byteLength;
14256  }
14257  result = new Uint8Array(size + 8);
14258  view = new DataView(result.buffer, result.byteOffset, result.byteLength);
14259  view.setUint32(0, result.byteLength);
14260  result.set(type, 4);
14261
14262  // copy the payload into the result
14263  for (i = 0, size = 8; i < payload.length; i++) {
14264    result.set(payload[i], size);
14265    size += payload[i].byteLength;
14266  }
14267  return result;
14268};
14269
14270dinf = function() {
14271  return box(types.dinf, box(types.dref, DREF));
14272};
14273
14274esds = function(track) {
14275  return box(types.esds, new Uint8Array([
14276    0x00, // version
14277    0x00, 0x00, 0x00, // flags
14278
14279    // ES_Descriptor
14280    0x03, // tag, ES_DescrTag
14281    0x19, // length
14282    0x00, 0x00, // ES_ID
14283    0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
14284
14285    // DecoderConfigDescriptor
14286    0x04, // tag, DecoderConfigDescrTag
14287    0x11, // length
14288    0x40, // object type
14289    0x15,  // streamType
14290    0x00, 0x06, 0x00, // bufferSizeDB
14291    0x00, 0x00, 0xda, 0xc0, // maxBitrate
14292    0x00, 0x00, 0xda, 0xc0, // avgBitrate
14293
14294    // DecoderSpecificInfo
14295    0x05, // tag, DecoderSpecificInfoTag
14296    0x02, // length
14297    // ISO/IEC 14496-3, AudioSpecificConfig
14298    // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
14299    (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1),
14300    (track.samplingfrequencyindex << 7) | (track.channelcount << 3),
14301    0x06, 0x01, 0x02 // GASpecificConfig
14302  ]));
14303};
14304
14305ftyp = function() {
14306  return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
14307};
14308
14309hdlr = function(type) {
14310  return box(types.hdlr, HDLR_TYPES[type]);
14311};
14312mdat = function(data) {
14313  return box(types.mdat, data);
14314};
14315mdhd = function(track) {
14316  var result = new Uint8Array([
14317    0x00,                   // version 0
14318    0x00, 0x00, 0x00,       // flags
14319    0x00, 0x00, 0x00, 0x02, // creation_time
14320    0x00, 0x00, 0x00, 0x03, // modification_time
14321    0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
14322
14323    (track.duration >>> 24) & 0xFF,
14324    (track.duration >>> 16) & 0xFF,
14325    (track.duration >>>  8) & 0xFF,
14326    track.duration & 0xFF,  // duration
14327    0x55, 0xc4,             // 'und' language (undetermined)
14328    0x00, 0x00
14329  ]);
14330
14331  // Use the sample rate from the track metadata, when it is
14332  // defined. The sample rate can be parsed out of an ADTS header, for
14333  // instance.
14334  if (track.samplerate) {
14335    result[12] = (track.samplerate >>> 24) & 0xFF;
14336    result[13] = (track.samplerate >>> 16) & 0xFF;
14337    result[14] = (track.samplerate >>>  8) & 0xFF;
14338    result[15] = (track.samplerate)        & 0xFF;
14339  }
14340
14341  return box(types.mdhd, result);
14342};
14343mdia = function(track) {
14344  return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
14345};
14346mfhd = function(sequenceNumber) {
14347  return box(types.mfhd, new Uint8Array([
14348    0x00,
14349    0x00, 0x00, 0x00, // flags
14350    (sequenceNumber & 0xFF000000) >> 24,
14351    (sequenceNumber & 0xFF0000) >> 16,
14352    (sequenceNumber & 0xFF00) >> 8,
14353    sequenceNumber & 0xFF // sequence_number
14354  ]));
14355};
14356minf = function(track) {
14357  return box(types.minf,
14358             track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD),
14359             dinf(),
14360             stbl(track));
14361};
14362moof = function(sequenceNumber, tracks) {
14363  var
14364    trackFragments = [],
14365    i = tracks.length;
14366  // build traf boxes for each track fragment
14367  while (i--) {
14368    trackFragments[i] = traf(tracks[i]);
14369  }
14370  return box.apply(null, [
14371    types.moof,
14372    mfhd(sequenceNumber)
14373  ].concat(trackFragments));
14374};
14375/**
14376 * Returns a movie box.
14377 * @param tracks {array} the tracks associated with this movie
14378 * @see ISO/IEC 14496-12:2012(E), section 8.2.1
14379 */
14380moov = function(tracks) {
14381  var
14382    i = tracks.length,
14383    boxes = [];
14384
14385  while (i--) {
14386    boxes[i] = trak(tracks[i]);
14387  }
14388
14389  return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
14390};
14391mvex = function(tracks) {
14392  var
14393    i = tracks.length,
14394    boxes = [];
14395
14396  while (i--) {
14397    boxes[i] = trex(tracks[i]);
14398  }
14399  return box.apply(null, [types.mvex].concat(boxes));
14400};
14401mvhd = function(duration) {
14402  var
14403    bytes = new Uint8Array([
14404      0x00, // version 0
14405      0x00, 0x00, 0x00, // flags
14406      0x00, 0x00, 0x00, 0x01, // creation_time
14407      0x00, 0x00, 0x00, 0x02, // modification_time
14408      0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
14409      (duration & 0xFF000000) >> 24,
14410      (duration & 0xFF0000) >> 16,
14411      (duration & 0xFF00) >> 8,
14412      duration & 0xFF, // duration
14413      0x00, 0x01, 0x00, 0x00, // 1.0 rate
14414      0x01, 0x00, // 1.0 volume
14415      0x00, 0x00, // reserved
14416      0x00, 0x00, 0x00, 0x00, // reserved
14417      0x00, 0x00, 0x00, 0x00, // reserved
14418      0x00, 0x01, 0x00, 0x00,
14419      0x00, 0x00, 0x00, 0x00,
14420      0x00, 0x00, 0x00, 0x00,
14421      0x00, 0x00, 0x00, 0x00,
14422      0x00, 0x01, 0x00, 0x00,
14423      0x00, 0x00, 0x00, 0x00,
14424      0x00, 0x00, 0x00, 0x00,
14425      0x00, 0x00, 0x00, 0x00,
14426      0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
14427      0x00, 0x00, 0x00, 0x00,
14428      0x00, 0x00, 0x00, 0x00,
14429      0x00, 0x00, 0x00, 0x00,
14430      0x00, 0x00, 0x00, 0x00,
14431      0x00, 0x00, 0x00, 0x00,
14432      0x00, 0x00, 0x00, 0x00, // pre_defined
14433      0xff, 0xff, 0xff, 0xff // next_track_ID
14434    ]);
14435  return box(types.mvhd, bytes);
14436};
14437
14438sdtp = function(track) {
14439  var
14440    samples = track.samples || [],
14441    bytes = new Uint8Array(4 + samples.length),
14442    flags,
14443    i;
14444
14445  // leave the full box header (4 bytes) all zero
14446
14447  // write the sample table
14448  for (i = 0; i < samples.length; i++) {
14449    flags = samples[i].flags;
14450
14451    bytes[i + 4] = (flags.dependsOn << 4) |
14452      (flags.isDependedOn << 2) |
14453      (flags.hasRedundancy);
14454  }
14455
14456  return box(types.sdtp,
14457             bytes);
14458};
14459
14460stbl = function(track) {
14461  return box(types.stbl,
14462             stsd(track),
14463             box(types.stts, STTS),
14464             box(types.stsc, STSC),
14465             box(types.stsz, STSZ),
14466             box(types.stco, STCO));
14467};
14468
14469(function() {
14470  var videoSample, audioSample;
14471
14472  stsd = function(track) {
14473
14474    return box(types.stsd, new Uint8Array([
14475      0x00, // version 0
14476      0x00, 0x00, 0x00, // flags
14477      0x00, 0x00, 0x00, 0x01
14478    ]), track.type === 'video' ? videoSample(track) : audioSample(track));
14479  };
14480
14481  videoSample = function(track) {
14482    var
14483      sps = track.sps || [],
14484      pps = track.pps || [],
14485      sequenceParameterSets = [],
14486      pictureParameterSets = [],
14487      i;
14488
14489    // assemble the SPSs
14490    for (i = 0; i < sps.length; i++) {
14491      sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
14492      sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength
14493      sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
14494    }
14495
14496    // assemble the PPSs
14497    for (i = 0; i < pps.length; i++) {
14498      pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
14499      pictureParameterSets.push((pps[i].byteLength & 0xFF));
14500      pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
14501    }
14502
14503    return box(types.avc1, new Uint8Array([
14504      0x00, 0x00, 0x00,
14505      0x00, 0x00, 0x00, // reserved
14506      0x00, 0x01, // data_reference_index
14507      0x00, 0x00, // pre_defined
14508      0x00, 0x00, // reserved
14509      0x00, 0x00, 0x00, 0x00,
14510      0x00, 0x00, 0x00, 0x00,
14511      0x00, 0x00, 0x00, 0x00, // pre_defined
14512      (track.width & 0xff00) >> 8,
14513      track.width & 0xff, // width
14514      (track.height & 0xff00) >> 8,
14515      track.height & 0xff, // height
14516      0x00, 0x48, 0x00, 0x00, // horizresolution
14517      0x00, 0x48, 0x00, 0x00, // vertresolution
14518      0x00, 0x00, 0x00, 0x00, // reserved
14519      0x00, 0x01, // frame_count
14520      0x13,
14521      0x76, 0x69, 0x64, 0x65,
14522      0x6f, 0x6a, 0x73, 0x2d,
14523      0x63, 0x6f, 0x6e, 0x74,
14524      0x72, 0x69, 0x62, 0x2d,
14525      0x68, 0x6c, 0x73, 0x00,
14526      0x00, 0x00, 0x00, 0x00,
14527      0x00, 0x00, 0x00, 0x00,
14528      0x00, 0x00, 0x00, // compressorname
14529      0x00, 0x18, // depth = 24
14530      0x11, 0x11 // pre_defined = -1
14531    ]), box(types.avcC, new Uint8Array([
14532      0x01, // configurationVersion
14533      track.profileIdc, // AVCProfileIndication
14534      track.profileCompatibility, // profile_compatibility
14535      track.levelIdc, // AVCLevelIndication
14536      0xff // lengthSizeMinusOne, hard-coded to 4 bytes
14537    ].concat([
14538      sps.length // numOfSequenceParameterSets
14539    ]).concat(sequenceParameterSets).concat([
14540      pps.length // numOfPictureParameterSets
14541    ]).concat(pictureParameterSets))), // "PPS"
14542            box(types.btrt, new Uint8Array([
14543              0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
14544              0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
14545              0x00, 0x2d, 0xc6, 0xc0
14546            ])) // avgBitrate
14547              );
14548  };
14549
14550  audioSample = function(track) {
14551    return box(types.mp4a, new Uint8Array([
14552
14553      // SampleEntry, ISO/IEC 14496-12
14554      0x00, 0x00, 0x00,
14555      0x00, 0x00, 0x00, // reserved
14556      0x00, 0x01, // data_reference_index
14557
14558      // AudioSampleEntry, ISO/IEC 14496-12
14559      0x00, 0x00, 0x00, 0x00, // reserved
14560      0x00, 0x00, 0x00, 0x00, // reserved
14561      (track.channelcount & 0xff00) >> 8,
14562      (track.channelcount & 0xff), // channelcount
14563
14564      (track.samplesize & 0xff00) >> 8,
14565      (track.samplesize & 0xff), // samplesize
14566      0x00, 0x00, // pre_defined
14567      0x00, 0x00, // reserved
14568
14569      (track.samplerate & 0xff00) >> 8,
14570      (track.samplerate & 0xff),
14571      0x00, 0x00 // samplerate, 16.16
14572
14573      // MP4AudioSampleEntry, ISO/IEC 14496-14
14574    ]), esds(track));
14575  };
14576}());
14577
14578tkhd = function(track) {
14579  var result = new Uint8Array([
14580    0x00, // version 0
14581    0x00, 0x00, 0x07, // flags
14582    0x00, 0x00, 0x00, 0x00, // creation_time
14583    0x00, 0x00, 0x00, 0x00, // modification_time
14584    (track.id & 0xFF000000) >> 24,
14585    (track.id & 0xFF0000) >> 16,
14586    (track.id & 0xFF00) >> 8,
14587    track.id & 0xFF, // track_ID
14588    0x00, 0x00, 0x00, 0x00, // reserved
14589    (track.duration & 0xFF000000) >> 24,
14590    (track.duration & 0xFF0000) >> 16,
14591    (track.duration & 0xFF00) >> 8,
14592    track.duration & 0xFF, // duration
14593    0x00, 0x00, 0x00, 0x00,
14594    0x00, 0x00, 0x00, 0x00, // reserved
14595    0x00, 0x00, // layer
14596    0x00, 0x00, // alternate_group
14597    0x01, 0x00, // non-audio track volume
14598    0x00, 0x00, // reserved
14599    0x00, 0x01, 0x00, 0x00,
14600    0x00, 0x00, 0x00, 0x00,
14601    0x00, 0x00, 0x00, 0x00,
14602    0x00, 0x00, 0x00, 0x00,
14603    0x00, 0x01, 0x00, 0x00,
14604    0x00, 0x00, 0x00, 0x00,
14605    0x00, 0x00, 0x00, 0x00,
14606    0x00, 0x00, 0x00, 0x00,
14607    0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
14608    (track.width & 0xFF00) >> 8,
14609    track.width & 0xFF,
14610    0x00, 0x00, // width
14611    (track.height & 0xFF00) >> 8,
14612    track.height & 0xFF,
14613    0x00, 0x00 // height
14614  ]);
14615
14616  return box(types.tkhd, result);
14617};
14618
14619/**
14620 * Generate a track fragment (traf) box. A traf box collects metadata
14621 * about tracks in a movie fragment (moof) box.
14622 */
14623traf = function(track) {
14624  var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun,
14625      sampleDependencyTable, dataOffset,
14626      upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
14627
14628  trackFragmentHeader = box(types.tfhd, new Uint8Array([
14629    0x00, // version 0
14630    0x00, 0x00, 0x3a, // flags
14631    (track.id & 0xFF000000) >> 24,
14632    (track.id & 0xFF0000) >> 16,
14633    (track.id & 0xFF00) >> 8,
14634    (track.id & 0xFF), // track_ID
14635    0x00, 0x00, 0x00, 0x01, // sample_description_index
14636    0x00, 0x00, 0x00, 0x00, // default_sample_duration
14637    0x00, 0x00, 0x00, 0x00, // default_sample_size
14638    0x00, 0x00, 0x00, 0x00  // default_sample_flags
14639  ]));
14640
14641  upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
14642  lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
14643
14644  trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([
14645    0x01, // version 1
14646    0x00, 0x00, 0x00, // flags
14647    // baseMediaDecodeTime
14648    (upperWordBaseMediaDecodeTime >>> 24) & 0xFF,
14649    (upperWordBaseMediaDecodeTime >>> 16) & 0xFF,
14650    (upperWordBaseMediaDecodeTime >>>  8) & 0xFF,
14651    upperWordBaseMediaDecodeTime & 0xFF,
14652    (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF,
14653    (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF,
14654    (lowerWordBaseMediaDecodeTime >>>  8) & 0xFF,
14655    lowerWordBaseMediaDecodeTime & 0xFF
14656  ]));
14657
14658  // the data offset specifies the number of bytes from the start of
14659  // the containing moof to the first payload byte of the associated
14660  // mdat
14661  dataOffset = (32 + // tfhd
14662                20 + // tfdt
14663                8 +  // traf header
14664                16 + // mfhd
14665                8 +  // moof header
14666                8);  // mdat header
14667
14668  // audio tracks require less metadata
14669  if (track.type === 'audio') {
14670    trackFragmentRun = trun(track, dataOffset);
14671    return box(types.traf,
14672               trackFragmentHeader,
14673               trackFragmentDecodeTime,
14674               trackFragmentRun);
14675  }
14676
14677  // video tracks should contain an independent and disposable samples
14678  // box (sdtp)
14679  // generate one and adjust offsets to match
14680  sampleDependencyTable = sdtp(track);
14681  trackFragmentRun = trun(track,
14682                          sampleDependencyTable.length + dataOffset);
14683  return box(types.traf,
14684             trackFragmentHeader,
14685             trackFragmentDecodeTime,
14686             trackFragmentRun,
14687             sampleDependencyTable);
14688};
14689
14690/**
14691 * Generate a track box.
14692 * @param track {object} a track definition
14693 * @return {Uint8Array} the track box
14694 */
14695trak = function(track) {
14696  track.duration = track.duration || 0xffffffff;
14697  return box(types.trak,
14698             tkhd(track),
14699             mdia(track));
14700};
14701
14702trex = function(track) {
14703  var result = new Uint8Array([
14704    0x00, // version 0
14705    0x00, 0x00, 0x00, // flags
14706    (track.id & 0xFF000000) >> 24,
14707    (track.id & 0xFF0000) >> 16,
14708    (track.id & 0xFF00) >> 8,
14709    (track.id & 0xFF), // track_ID
14710    0x00, 0x00, 0x00, 0x01, // default_sample_description_index
14711    0x00, 0x00, 0x00, 0x00, // default_sample_duration
14712    0x00, 0x00, 0x00, 0x00, // default_sample_size
14713    0x00, 0x01, 0x00, 0x01 // default_sample_flags
14714  ]);
14715  // the last two bytes of default_sample_flags is the sample
14716  // degradation priority, a hint about the importance of this sample
14717  // relative to others. Lower the degradation priority for all sample
14718  // types other than video.
14719  if (track.type !== 'video') {
14720    result[result.length - 1] = 0x00;
14721  }
14722
14723  return box(types.trex, result);
14724};
14725
14726(function() {
14727  var audioTrun, videoTrun, trunHeader;
14728
14729  // This method assumes all samples are uniform. That is, if a
14730  // duration is present for the first sample, it will be present for
14731  // all subsequent samples.
14732  // see ISO/IEC 14496-12:2012, Section 8.8.8.1
14733  trunHeader = function(samples, offset) {
14734    var durationPresent = 0, sizePresent = 0,
14735        flagsPresent = 0, compositionTimeOffset = 0;
14736
14737    // trun flag constants
14738    if (samples.length) {
14739      if (samples[0].duration !== undefined) {
14740        durationPresent = 0x1;
14741      }
14742      if (samples[0].size !== undefined) {
14743        sizePresent = 0x2;
14744      }
14745      if (samples[0].flags !== undefined) {
14746        flagsPresent = 0x4;
14747      }
14748      if (samples[0].compositionTimeOffset !== undefined) {
14749        compositionTimeOffset = 0x8;
14750      }
14751    }
14752
14753    return [
14754      0x00, // version 0
14755      0x00,
14756      durationPresent | sizePresent | flagsPresent | compositionTimeOffset,
14757      0x01, // flags
14758      (samples.length & 0xFF000000) >>> 24,
14759      (samples.length & 0xFF0000) >>> 16,
14760      (samples.length & 0xFF00) >>> 8,
14761      samples.length & 0xFF, // sample_count
14762      (offset & 0xFF000000) >>> 24,
14763      (offset & 0xFF0000) >>> 16,
14764      (offset & 0xFF00) >>> 8,
14765      offset & 0xFF // data_offset
14766    ];
14767  };
14768
14769  videoTrun = function(track, offset) {
14770    var bytes, samples, sample, i;
14771
14772    samples = track.samples || [];
14773    offset += 8 + 12 + (16 * samples.length);
14774
14775    bytes = trunHeader(samples, offset);
14776
14777    for (i = 0; i < samples.length; i++) {
14778      sample = samples[i];
14779      bytes = bytes.concat([
14780        (sample.duration & 0xFF000000) >>> 24,
14781        (sample.duration & 0xFF0000) >>> 16,
14782        (sample.duration & 0xFF00) >>> 8,
14783        sample.duration & 0xFF, // sample_duration
14784        (sample.size & 0xFF000000) >>> 24,
14785        (sample.size & 0xFF0000) >>> 16,
14786        (sample.size & 0xFF00) >>> 8,
14787        sample.size & 0xFF, // sample_size
14788        (sample.flags.isLeading << 2) | sample.flags.dependsOn,
14789        (sample.flags.isDependedOn << 6) |
14790          (sample.flags.hasRedundancy << 4) |
14791          (sample.flags.paddingValue << 1) |
14792          sample.flags.isNonSyncSample,
14793        sample.flags.degradationPriority & 0xF0 << 8,
14794        sample.flags.degradationPriority & 0x0F, // sample_flags
14795        (sample.compositionTimeOffset & 0xFF000000) >>> 24,
14796        (sample.compositionTimeOffset & 0xFF0000) >>> 16,
14797        (sample.compositionTimeOffset & 0xFF00) >>> 8,
14798        sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
14799      ]);
14800    }
14801    return box(types.trun, new Uint8Array(bytes));
14802  };
14803
14804  audioTrun = function(track, offset) {
14805    var bytes, samples, sample, i;
14806
14807    samples = track.samples || [];
14808    offset += 8 + 12 + (8 * samples.length);
14809
14810    bytes = trunHeader(samples, offset);
14811
14812    for (i = 0; i < samples.length; i++) {
14813      sample = samples[i];
14814      bytes = bytes.concat([
14815        (sample.duration & 0xFF000000) >>> 24,
14816        (sample.duration & 0xFF0000) >>> 16,
14817        (sample.duration & 0xFF00) >>> 8,
14818        sample.duration & 0xFF, // sample_duration
14819        (sample.size & 0xFF000000) >>> 24,
14820        (sample.size & 0xFF0000) >>> 16,
14821        (sample.size & 0xFF00) >>> 8,
14822        sample.size & 0xFF]); // sample_size
14823    }
14824
14825    return box(types.trun, new Uint8Array(bytes));
14826  };
14827
14828  trun = function(track, offset) {
14829    if (track.type === 'audio') {
14830      return audioTrun(track, offset);
14831    }
14832
14833    return videoTrun(track, offset);
14834  };
14835}());
14836
14837module.exports = {
14838  ftyp: ftyp,
14839  mdat: mdat,
14840  moof: moof,
14841  moov: moov,
14842  initSegment: function(tracks) {
14843    var
14844      fileType = ftyp(),
14845      movie = moov(tracks),
14846      result;
14847
14848    result = new Uint8Array(fileType.byteLength + movie.byteLength);
14849    result.set(fileType);
14850    result.set(movie, fileType.byteLength);
14851    return result;
14852  }
14853};
14854
14855},{}],57:[function(require,module,exports){
14856/**
14857 * mux.js
14858 *
14859 * Copyright (c) 2015 Brightcove
14860 * All rights reserved.
14861 *
14862 * Utilities to detect basic properties and metadata about MP4s.
14863 */
14864'use strict';
14865
14866var findBox, parseType, timescale, startTime;
14867
14868// Find the data for a box specified by its path
14869findBox = function(data, path) {
14870  var results = [],
14871      i, size, type, end, subresults;
14872
14873  if (!path.length) {
14874    // short-circuit the search for empty paths
14875    return null;
14876  }
14877
14878  for (i = 0; i < data.byteLength;) {
14879    size  = data[i]     << 24;
14880    size |= data[i + 1] << 16;
14881    size |= data[i + 2] << 8;
14882    size |= data[i + 3];
14883
14884    type = parseType(data.subarray(i + 4, i + 8));
14885
14886    end = size > 1 ? i + size : data.byteLength;
14887
14888    if (type === path[0]) {
14889      if (path.length === 1) {
14890        // this is the end of the path and we've found the box we were
14891        // looking for
14892        results.push(data.subarray(i + 8, end));
14893      } else {
14894        // recursively search for the next box along the path
14895        subresults = findBox(data.subarray(i + 8, end), path.slice(1));
14896        if (subresults.length) {
14897          results = results.concat(subresults);
14898        }
14899      }
14900    }
14901    i = end;
14902  }
14903
14904  // we've finished searching all of data
14905  return results;
14906};
14907
14908/**
14909 * Returns the string representation of an ASCII encoded four byte buffer.
14910 * @param buffer {Uint8Array} a four-byte buffer to translate
14911 * @return {string} the corresponding string
14912 */
14913parseType = function(buffer) {
14914  var result = '';
14915  result += String.fromCharCode(buffer[0]);
14916  result += String.fromCharCode(buffer[1]);
14917  result += String.fromCharCode(buffer[2]);
14918  result += String.fromCharCode(buffer[3]);
14919  return result;
14920};
14921
14922/**
14923 * Parses an MP4 initialization segment and extracts the timescale
14924 * values for any declared tracks. Timescale values indicate the
14925 * number of clock ticks per second to assume for time-based values
14926 * elsewhere in the MP4.
14927 *
14928 * To determine the start time of an MP4, you need two pieces of
14929 * information: the timescale unit and the earliest base media decode
14930 * time. Multiple timescales can be specified within an MP4 but the
14931 * base media decode time is always expressed in the timescale from
14932 * the media header box for the track:
14933 * ```
14934 * moov > trak > mdia > mdhd.timescale
14935 * ```
14936 * @param init {Uint8Array} the bytes of the init segment
14937 * @return {object} a hash of track ids to timescale values or null if
14938 * the init segment is malformed.
14939 */
14940timescale = function(init) {
14941  var
14942    result = {},
14943    traks = findBox(init, ['moov', 'trak']);
14944
14945  // mdhd timescale
14946  return traks.reduce(function(result, trak) {
14947    var tkhd, version, index, id, mdhd;
14948
14949    tkhd = findBox(trak, ['tkhd'])[0];
14950    if (!tkhd) {
14951      return null;
14952    }
14953    version = tkhd[0];
14954    index = version === 0 ? 12 : 20;
14955    id = tkhd[index]     << 24 |
14956         tkhd[index + 1] << 16 |
14957         tkhd[index + 2] <<  8 |
14958         tkhd[index + 3];
14959
14960    mdhd = findBox(trak, ['mdia', 'mdhd'])[0];
14961    if (!mdhd) {
14962      return null;
14963    }
14964    version = mdhd[0];
14965    index = version === 0 ? 12 : 20;
14966    result[id] = mdhd[index]     << 24 |
14967                 mdhd[index + 1] << 16 |
14968                 mdhd[index + 2] <<  8 |
14969                 mdhd[index + 3];
14970    return result;
14971  }, result);
14972};
14973
14974/**
14975 * Determine the base media decode start time, in seconds, for an MP4
14976 * fragment. If multiple fragments are specified, the earliest time is
14977 * returned.
14978 *
14979 * The base media decode time can be parsed from track fragment
14980 * metadata:
14981 * ```
14982 * moof > traf > tfdt.baseMediaDecodeTime
14983 * ```
14984 * It requires the timescale value from the mdhd to interpret.
14985 *
14986 * @param timescale {object} a hash of track ids to timescale values.
14987 * @return {number} the earliest base media decode start time for the
14988 * fragment, in seconds
14989 */
14990startTime = function(timescale, fragment) {
14991  var trafs, baseTimes, result;
14992
14993  // we need info from two childrend of each track fragment box
14994  trafs = findBox(fragment, ['moof', 'traf']);
14995
14996  // determine the start times for each track
14997  baseTimes = [].concat.apply([], trafs.map(function(traf) {
14998    return findBox(traf, ['tfhd']).map(function(tfhd) {
14999      var id, scale, baseTime;
15000
15001      // get the track id from the tfhd
15002      id = tfhd[4] << 24 |
15003           tfhd[5] << 16 |
15004           tfhd[6] << 8 |
15005           tfhd[7];
15006      // assume a 90kHz clock if no timescale was specified
15007      scale = timescale[id] || 90e3;
15008
15009      // get the base media decode time from the tfdt
15010      baseTime = findBox(traf, ['tfdt']).map(function(tfdt) {
15011        var version, result;
15012
15013        version = tfdt[0];
15014        result = tfdt[4] << 24 |
15015                 tfdt[5] << 16 |
15016                 tfdt[6] <<  8 |
15017                 tfdt[7];
15018        if (version ===  1) {
15019          result *= Math.pow(2, 32);
15020          result += tfdt[8]  << 24 |
15021                    tfdt[9]  << 16 |
15022                    tfdt[10] <<  8 |
15023                    tfdt[11];
15024        }
15025        return result;
15026      })[0];
15027      baseTime = baseTime || Infinity;
15028
15029      // convert base time to seconds
15030      return baseTime / scale;
15031    });
15032  }));
15033
15034  // return the minimum
15035  result = Math.min.apply(null, baseTimes);
15036  return isFinite(result) ? result : 0;
15037};
15038
15039module.exports = {
15040  parseType: parseType,
15041  timescale: timescale,
15042  startTime: startTime
15043};
15044
15045},{}],58:[function(require,module,exports){
15046/**
15047 * mux.js
15048 *
15049 * Copyright (c) 2015 Brightcove
15050 * All rights reserved.
15051 *
15052 * A stream-based mp2t to mp4 converter. This utility can be used to
15053 * deliver mp4s to a SourceBuffer on platforms that support native
15054 * Media Source Extensions.
15055 */
15056'use strict';
15057
15058var Stream = require('../utils/stream.js');
15059var mp4 = require('./mp4-generator.js');
15060var m2ts = require('../m2ts/m2ts.js');
15061var AdtsStream = require('../codecs/adts.js');
15062var H264Stream = require('../codecs/h264').H264Stream;
15063var AacStream = require('../aac');
15064var coneOfSilence = require('../data/silence');
15065var clock = require('../utils/clock');
15066
15067// constants
15068var AUDIO_PROPERTIES = [
15069  'audioobjecttype',
15070  'channelcount',
15071  'samplerate',
15072  'samplingfrequencyindex',
15073  'samplesize'
15074];
15075
15076var VIDEO_PROPERTIES = [
15077  'width',
15078  'height',
15079  'profileIdc',
15080  'levelIdc',
15081  'profileCompatibility'
15082];
15083
15084var ONE_SECOND_IN_TS = 90000; // 90kHz clock
15085
15086// object types
15087var VideoSegmentStream, AudioSegmentStream, Transmuxer, CoalesceStream;
15088
15089// Helper functions
15090var
15091  createDefaultSample,
15092  isLikelyAacData,
15093  collectDtsInfo,
15094  clearDtsInfo,
15095  calculateTrackBaseMediaDecodeTime,
15096  arrayEquals,
15097  sumFrameByteLengths;
15098
15099/**
15100 * Default sample object
15101 * see ISO/IEC 14496-12:2012, section 8.6.4.3
15102 */
15103createDefaultSample = function() {
15104  return {
15105    size: 0,
15106    flags: {
15107      isLeading: 0,
15108      dependsOn: 1,
15109      isDependedOn: 0,
15110      hasRedundancy: 0,
15111      degradationPriority: 0
15112    }
15113  };
15114};
15115
15116isLikelyAacData = function(data) {
15117  if ((data[0] === 'I'.charCodeAt(0)) &&
15118      (data[1] === 'D'.charCodeAt(0)) &&
15119      (data[2] === '3'.charCodeAt(0))) {
15120    return true;
15121  }
15122  return false;
vendor: 58,390 bytes, lines 15123-17058
15123};
15124
15125/**
15126 * Compare two arrays (even typed) for same-ness
15127 */
15128arrayEquals = function(a, b) {
15129  var
15130    i;
15131
15132  if (a.length !== b.length) {
15133    return false;
15134  }
15135
15136  // compare the value of each element in the array
15137  for (i = 0; i < a.length; i++) {
15138    if (a[i] !== b[i]) {
15139      return false;
15140    }
15141  }
15142
15143  return true;
15144};
15145
15146/**
15147 * Sum the `byteLength` properties of the data in each AAC frame
15148 */
15149sumFrameByteLengths = function(array) {
15150  var
15151    i,
15152    currentObj,
15153    sum = 0;
15154
15155  // sum the byteLength's all each nal unit in the frame
15156  for (i = 0; i < array.length; i++) {
15157    currentObj = array[i];
15158    sum += currentObj.data.byteLength;
15159  }
15160
15161  return sum;
15162};
15163
15164/**
15165 * Constructs a single-track, ISO BMFF media segment from AAC data
15166 * events. The output of this stream can be fed to a SourceBuffer
15167 * configured with a suitable initialization segment.
15168 */
15169AudioSegmentStream = function(track) {
15170  var
15171    adtsFrames = [],
15172    sequenceNumber = 0,
15173    earliestAllowedDts = 0,
15174    audioAppendStartTs = 0,
15175    videoBaseMediaDecodeTime = Infinity;
15176
15177  AudioSegmentStream.prototype.init.call(this);
15178
15179  this.push = function(data) {
15180    collectDtsInfo(track, data);
15181
15182    if (track) {
15183      AUDIO_PROPERTIES.forEach(function(prop) {
15184        track[prop] = data[prop];
15185      });
15186    }
15187
15188    // buffer audio data until end() is called
15189    adtsFrames.push(data);
15190  };
15191
15192  this.setEarliestDts = function(earliestDts) {
15193    earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
15194  };
15195
15196  this.setVideoBaseMediaDecodeTime = function(baseMediaDecodeTime) {
15197    videoBaseMediaDecodeTime = baseMediaDecodeTime;
15198  };
15199
15200  this.setAudioAppendStart = function(timestamp) {
15201    audioAppendStartTs = timestamp;
15202  };
15203
15204  this.flush = function() {
15205    var
15206      frames,
15207      moof,
15208      mdat,
15209      boxes;
15210
15211    // return early if no audio data has been observed
15212    if (adtsFrames.length === 0) {
15213      this.trigger('done', 'AudioSegmentStream');
15214      return;
15215    }
15216
15217    frames = this.trimAdtsFramesByEarliestDts_(adtsFrames);
15218    track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
15219
15220    this.prefixWithSilence_(track, frames);
15221
15222    // we have to build the index from byte locations to
15223    // samples (that is, adts frames) in the audio data
15224    track.samples = this.generateSampleTable_(frames);
15225
15226    // concatenate the audio data to constuct the mdat
15227    mdat = mp4.mdat(this.concatenateFrameData_(frames));
15228
15229    adtsFrames = [];
15230
15231    moof = mp4.moof(sequenceNumber, [track]);
15232    boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
15233
15234    // bump the sequence number for next time
15235    sequenceNumber++;
15236
15237    boxes.set(moof);
15238    boxes.set(mdat, moof.byteLength);
15239
15240    clearDtsInfo(track);
15241
15242    this.trigger('data', {track: track, boxes: boxes});
15243    this.trigger('done', 'AudioSegmentStream');
15244  };
15245
15246  // Possibly pad (prefix) the audio track with silence if appending this track
15247  // would lead to the introduction of a gap in the audio buffer
15248  this.prefixWithSilence_ = function(track, frames) {
15249    var
15250      baseMediaDecodeTimeTs,
15251      frameDuration = 0,
15252      audioGapDuration = 0,
15253      audioFillFrameCount = 0,
15254      audioFillDuration = 0,
15255      silentFrame,
15256      i;
15257
15258    if (!frames.length) {
15259      return;
15260    }
15261
15262    baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate);
15263    // determine frame clock duration based on sample rate, round up to avoid overfills
15264    frameDuration = Math.ceil(ONE_SECOND_IN_TS / (track.samplerate / 1024));
15265
15266    if (audioAppendStartTs && videoBaseMediaDecodeTime) {
15267      // insert the shortest possible amount (audio gap or audio to video gap)
15268      audioGapDuration =
15269        baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime);
15270      // number of full frames in the audio gap
15271      audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
15272      audioFillDuration = audioFillFrameCount * frameDuration;
15273    }
15274
15275    // don't attempt to fill gaps smaller than a single frame or larger
15276    // than a half second
15277    if (audioFillFrameCount < 1 || audioFillDuration > ONE_SECOND_IN_TS / 2) {
15278      return;
15279    }
15280
15281    silentFrame = coneOfSilence[track.samplerate];
15282
15283    if (!silentFrame) {
15284      // we don't have a silent frame pregenerated for the sample rate, so use a frame
15285      // from the content instead
15286      silentFrame = frames[0].data;
15287    }
15288
15289    for (i = 0; i < audioFillFrameCount; i++) {
15290      frames.splice(i, 0, {
15291        data: silentFrame
15292      });
15293    }
15294
15295    track.baseMediaDecodeTime -=
15296      Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
15297  };
15298
15299  // If the audio segment extends before the earliest allowed dts
15300  // value, remove AAC frames until starts at or after the earliest
15301  // allowed DTS so that we don't end up with a negative baseMedia-
15302  // DecodeTime for the audio track
15303  this.trimAdtsFramesByEarliestDts_ = function(adtsFrames) {
15304    if (track.minSegmentDts >= earliestAllowedDts) {
15305      return adtsFrames;
15306    }
15307
15308    // We will need to recalculate the earliest segment Dts
15309    track.minSegmentDts = Infinity;
15310
15311    return adtsFrames.filter(function(currentFrame) {
15312      // If this is an allowed frame, keep it and record it's Dts
15313      if (currentFrame.dts >= earliestAllowedDts) {
15314        track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
15315        track.minSegmentPts = track.minSegmentDts;
15316        return true;
15317      }
15318      // Otherwise, discard it
15319      return false;
15320    });
15321  };
15322
15323  // generate the track's raw mdat data from an array of frames
15324  this.generateSampleTable_ = function(frames) {
15325    var
15326      i,
15327      currentFrame,
15328      samples = [];
15329
15330    for (i = 0; i < frames.length; i++) {
15331      currentFrame = frames[i];
15332      samples.push({
15333        size: currentFrame.data.byteLength,
15334        duration: 1024 // For AAC audio, all samples contain 1024 samples
15335      });
15336    }
15337    return samples;
15338  };
15339
15340  // generate the track's sample table from an array of frames
15341  this.concatenateFrameData_ = function(frames) {
15342    var
15343      i,
15344      currentFrame,
15345      dataOffset = 0,
15346      data = new Uint8Array(sumFrameByteLengths(frames));
15347
15348    for (i = 0; i < frames.length; i++) {
15349      currentFrame = frames[i];
15350
15351      data.set(currentFrame.data, dataOffset);
15352      dataOffset += currentFrame.data.byteLength;
15353    }
15354    return data;
15355  };
15356};
15357
15358AudioSegmentStream.prototype = new Stream();
15359
15360/**
15361 * Constructs a single-track, ISO BMFF media segment from H264 data
15362 * events. The output of this stream can be fed to a SourceBuffer
15363 * configured with a suitable initialization segment.
15364 * @param track {object} track metadata configuration
15365 */
15366VideoSegmentStream = function(track) {
15367  var
15368    sequenceNumber = 0,
15369    nalUnits = [],
15370    config,
15371    pps;
15372
15373  VideoSegmentStream.prototype.init.call(this);
15374
15375  delete track.minPTS;
15376
15377  this.gopCache_ = [];
15378
15379  this.push = function(nalUnit) {
15380    collectDtsInfo(track, nalUnit);
15381
15382    // record the track config
15383    if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
15384      config = nalUnit.config;
15385      track.sps = [nalUnit.data];
15386
15387      VIDEO_PROPERTIES.forEach(function(prop) {
15388        track[prop] = config[prop];
15389      }, this);
15390    }
15391
15392    if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' &&
15393        !pps) {
15394      pps = nalUnit.data;
15395      track.pps = [nalUnit.data];
15396    }
15397
15398    // buffer video until flush() is called
15399    nalUnits.push(nalUnit);
15400  };
15401
15402  this.flush = function() {
15403    var
15404      frames,
15405      gopForFusion,
15406      gops,
15407      moof,
15408      mdat,
15409      boxes;
15410
15411    // Throw away nalUnits at the start of the byte stream until
15412    // we find the first AUD
15413    while (nalUnits.length) {
15414      if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
15415        break;
15416      }
15417      nalUnits.shift();
15418    }
15419
15420    // Return early if no video data has been observed
15421    if (nalUnits.length === 0) {
15422      this.resetStream_();
15423      this.trigger('done', 'VideoSegmentStream');
15424      return;
15425    }
15426
15427    // Organize the raw nal-units into arrays that represent
15428    // higher-level constructs such as frames and gops
15429    // (group-of-pictures)
15430    frames = this.groupNalsIntoFrames_(nalUnits);
15431    gops = this.groupFramesIntoGops_(frames);
15432
15433    // If the first frame of this fragment is not a keyframe we have
15434    // a problem since MSE (on Chrome) requires a leading keyframe.
15435    //
15436    // We have two approaches to repairing this situation:
15437    // 1) GOP-FUSION:
15438    //    This is where we keep track of the GOPS (group-of-pictures)
15439    //    from previous fragments and attempt to find one that we can
15440    //    prepend to the current fragment in order to create a valid
15441    //    fragment.
15442    // 2) KEYFRAME-PULLING:
15443    //    Here we search for the first keyframe in the fragment and
15444    //    throw away all the frames between the start of the fragment
15445    //    and that keyframe. We then extend the duration and pull the
15446    //    PTS of the keyframe forward so that it covers the time range
15447    //    of the frames that were disposed of.
15448    //
15449    // #1 is far prefereable over #2 which can cause "stuttering" but
15450    // requires more things to be just right.
15451    if (!gops[0][0].keyFrame) {
15452      // Search for a gop for fusion from our gopCache
15453      gopForFusion = this.getGopForFusion_(nalUnits[0], track);
15454
15455      if (gopForFusion) {
15456        gops.unshift(gopForFusion);
15457        // Adjust Gops' metadata to account for the inclusion of the
15458        // new gop at the beginning
15459        gops.byteLength += gopForFusion.byteLength;
15460        gops.nalCount += gopForFusion.nalCount;
15461        gops.pts = gopForFusion.pts;
15462        gops.dts = gopForFusion.dts;
15463        gops.duration += gopForFusion.duration;
15464      } else {
15465        // If we didn't find a candidate gop fall back to keyrame-pulling
15466        gops = this.extendFirstKeyFrame_(gops);
15467      }
15468    }
15469    collectDtsInfo(track, gops);
15470
15471    // First, we have to build the index from byte locations to
15472    // samples (that is, frames) in the video data
15473    track.samples = this.generateSampleTable_(gops);
15474
15475    // Concatenate the video data and construct the mdat
15476    mdat = mp4.mdat(this.concatenateNalData_(gops));
15477
15478    // save all the nals in the last GOP into the gop cache
15479    this.gopCache_.unshift({
15480      gop: gops.pop(),
15481      pps: track.pps,
15482      sps: track.sps
15483    });
15484
15485    // Keep a maximum of 6 GOPs in the cache
15486    this.gopCache_.length = Math.min(6, this.gopCache_.length);
15487
15488    // Clear nalUnits
15489    nalUnits = [];
15490
15491    track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
15492
15493    this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
15494    this.trigger('timelineStartInfo', track.timelineStartInfo);
15495
15496    moof = mp4.moof(sequenceNumber, [track]);
15497
15498    // it would be great to allocate this array up front instead of
15499    // throwing away hundreds of media segment fragments
15500    boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
15501
15502    // Bump the sequence number for next time
15503    sequenceNumber++;
15504
15505    boxes.set(moof);
15506    boxes.set(mdat, moof.byteLength);
15507
15508    this.trigger('data', {track: track, boxes: boxes});
15509
15510    this.resetStream_();
15511
15512    // Continue with the flush process now
15513    this.trigger('done', 'VideoSegmentStream');
15514  };
15515
15516  this.resetStream_ = function() {
15517    clearDtsInfo(track);
15518
15519    // reset config and pps because they may differ across segments
15520    // for instance, when we are rendition switching
15521    config = undefined;
15522    pps = undefined;
15523  };
15524
15525  // Search for a candidate Gop for gop-fusion from the gop cache and
15526  // return it or return null if no good candidate was found
15527  this.getGopForFusion_ = function(nalUnit) {
15528    var
15529      halfSecond = 45000, // Half-a-second in a 90khz clock
15530      allowableOverlap = 10000, // About 3 frames @ 30fps
15531      nearestDistance = Infinity,
15532      dtsDistance,
15533      nearestGopObj,
15534      currentGop,
15535      currentGopObj,
15536      i;
15537
15538    // Search for the GOP nearest to the beginning of this nal unit
15539    for (i = 0; i < this.gopCache_.length; i++) {
15540      currentGopObj = this.gopCache_[i];
15541      currentGop = currentGopObj.gop;
15542
15543      // Reject Gops with different SPS or PPS
15544      if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) ||
15545          !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
15546        continue;
15547      }
15548
15549      // Reject Gops that would require a negative baseMediaDecodeTime
15550      if (currentGop.dts < track.timelineStartInfo.dts) {
15551        continue;
15552      }
15553
15554      // The distance between the end of the gop and the start of the nalUnit
15555      dtsDistance = (nalUnit.dts - currentGop.dts) - currentGop.duration;
15556
15557      // Only consider GOPS that start before the nal unit and end within
15558      // a half-second of the nal unit
15559      if (dtsDistance >= -allowableOverlap &&
15560          dtsDistance <= halfSecond) {
15561
15562        // Always use the closest GOP we found if there is more than
15563        // one candidate
15564        if (!nearestGopObj ||
15565            nearestDistance > dtsDistance) {
15566          nearestGopObj = currentGopObj;
15567          nearestDistance = dtsDistance;
15568        }
15569      }
15570    }
15571
15572    if (nearestGopObj) {
15573      return nearestGopObj.gop;
15574    }
15575    return null;
15576  };
15577
15578  this.extendFirstKeyFrame_ = function(gops) {
15579    var currentGop;
15580
15581    if (!gops[0][0].keyFrame && gops.length > 1) {
15582      // Remove the first GOP
15583      currentGop = gops.shift();
15584
15585      gops.byteLength -=  currentGop.byteLength;
15586      gops.nalCount -= currentGop.nalCount;
15587
15588      // Extend the first frame of what is now the
15589      // first gop to cover the time period of the
15590      // frames we just removed
15591      gops[0][0].dts = currentGop.dts;
15592      gops[0][0].pts = currentGop.pts;
15593      gops[0][0].duration += currentGop.duration;
15594    }
15595
15596    return gops;
15597  };
15598
15599  // Convert an array of nal units into an array of frames with each frame being
15600  // composed of the nal units that make up that frame
15601  // Also keep track of cummulative data about the frame from the nal units such
15602  // as the frame duration, starting pts, etc.
15603  this.groupNalsIntoFrames_ = function(nalUnits) {
15604    var
15605      i,
15606      currentNal,
15607      currentFrame = [],
15608      frames = [];
15609
15610    currentFrame.byteLength = 0;
15611
15612    for (i = 0; i < nalUnits.length; i++) {
15613      currentNal = nalUnits[i];
15614
15615      // Split on 'aud'-type nal units
15616      if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
15617        // Since the very first nal unit is expected to be an AUD
15618        // only push to the frames array when currentFrame is not empty
15619        if (currentFrame.length) {
15620          currentFrame.duration = currentNal.dts - currentFrame.dts;
15621          frames.push(currentFrame);
15622        }
15623        currentFrame = [currentNal];
15624        currentFrame.byteLength = currentNal.data.byteLength;
15625        currentFrame.pts = currentNal.pts;
15626        currentFrame.dts = currentNal.dts;
15627      } else {
15628        // Specifically flag key frames for ease of use later
15629        if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
15630          currentFrame.keyFrame = true;
15631        }
15632        currentFrame.duration = currentNal.dts - currentFrame.dts;
15633        currentFrame.byteLength += currentNal.data.byteLength;
15634        currentFrame.push(currentNal);
15635      }
15636    }
15637
15638    // For the last frame, use the duration of the previous frame if we
15639    // have nothing better to go on
15640    if (frames.length &&
15641        (!currentFrame.duration ||
15642         currentFrame.duration <= 0)) {
15643      currentFrame.duration = frames[frames.length - 1].duration;
15644    }
15645
15646    // Push the final frame
15647    frames.push(currentFrame);
15648    return frames;
15649  };
15650
15651  // Convert an array of frames into an array of Gop with each Gop being composed
15652  // of the frames that make up that Gop
15653  // Also keep track of cummulative data about the Gop from the frames such as the
15654  // Gop duration, starting pts, etc.
15655  this.groupFramesIntoGops_ = function(frames) {
15656    var
15657      i,
15658      currentFrame,
15659      currentGop = [],
15660      gops = [];
15661
15662    // We must pre-set some of the values on the Gop since we
15663    // keep running totals of these values
15664    currentGop.byteLength = 0;
15665    currentGop.nalCount = 0;
15666    currentGop.duration = 0;
15667    currentGop.pts = frames[0].pts;
15668    currentGop.dts = frames[0].dts;
15669
15670    // store some metadata about all the Gops
15671    gops.byteLength = 0;
15672    gops.nalCount = 0;
15673    gops.duration = 0;
15674    gops.pts = frames[0].pts;
15675    gops.dts = frames[0].dts;
15676
15677    for (i = 0; i < frames.length; i++) {
15678      currentFrame = frames[i];
15679
15680      if (currentFrame.keyFrame) {
15681        // Since the very first frame is expected to be an keyframe
15682        // only push to the gops array when currentGop is not empty
15683        if (currentGop.length) {
15684          gops.push(currentGop);
15685          gops.byteLength += currentGop.byteLength;
15686          gops.nalCount += currentGop.nalCount;
15687          gops.duration += currentGop.duration;
15688        }
15689
15690        currentGop = [currentFrame];
15691        currentGop.nalCount = currentFrame.length;
15692        currentGop.byteLength = currentFrame.byteLength;
15693        currentGop.pts = currentFrame.pts;
15694        currentGop.dts = currentFrame.dts;
15695        currentGop.duration = currentFrame.duration;
15696      } else {
15697        currentGop.duration += currentFrame.duration;
15698        currentGop.nalCount += currentFrame.length;
15699        currentGop.byteLength += currentFrame.byteLength;
15700        currentGop.push(currentFrame);
15701      }
15702    }
15703
15704    if (gops.length && currentGop.duration <= 0) {
15705      currentGop.duration = gops[gops.length - 1].duration;
15706    }
15707    gops.byteLength += currentGop.byteLength;
15708    gops.nalCount += currentGop.nalCount;
15709    gops.duration += currentGop.duration;
15710
15711    // push the final Gop
15712    gops.push(currentGop);
15713    return gops;
15714  };
15715
15716  // generate the track's sample table from an array of gops
15717  this.generateSampleTable_ = function(gops, baseDataOffset) {
15718    var
15719      h, i,
15720      sample,
15721      currentGop,
15722      currentFrame,
15723      dataOffset = baseDataOffset || 0,
15724      samples = [];
15725
15726    for (h = 0; h < gops.length; h++) {
15727      currentGop = gops[h];
15728
15729      for (i = 0; i < currentGop.length; i++) {
15730        currentFrame = currentGop[i];
15731
15732        sample = createDefaultSample();
15733
15734        sample.dataOffset = dataOffset;
15735        sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts;
15736        sample.duration = currentFrame.duration;
15737        sample.size = 4 * currentFrame.length; // Space for nal unit size
15738        sample.size += currentFrame.byteLength;
15739
15740        if (currentFrame.keyFrame) {
15741          sample.flags.dependsOn = 2;
15742        }
15743
15744        dataOffset += sample.size;
15745
15746        samples.push(sample);
15747      }
15748    }
15749    return samples;
15750  };
15751
15752  // generate the track's raw mdat data from an array of gops
15753  this.concatenateNalData_ = function(gops) {
15754    var
15755      h, i, j,
15756      currentGop,
15757      currentFrame,
15758      currentNal,
15759      dataOffset = 0,
15760      nalsByteLength = gops.byteLength,
15761      numberOfNals = gops.nalCount,
15762      totalByteLength = nalsByteLength + 4 * numberOfNals,
15763      data = new Uint8Array(totalByteLength),
15764      view = new DataView(data.buffer);
15765
15766    // For each Gop..
15767    for (h = 0; h < gops.length; h++) {
15768      currentGop = gops[h];
15769
15770      // For each Frame..
15771      for (i = 0; i < currentGop.length; i++) {
15772        currentFrame = currentGop[i];
15773
15774        // For each NAL..
15775        for (j = 0; j < currentFrame.length; j++) {
15776          currentNal = currentFrame[j];
15777
15778          view.setUint32(dataOffset, currentNal.data.byteLength);
15779          dataOffset += 4;
15780          data.set(currentNal.data, dataOffset);
15781          dataOffset += currentNal.data.byteLength;
15782        }
15783      }
15784    }
15785    return data;
15786  };
15787};
15788
15789VideoSegmentStream.prototype = new Stream();
15790
15791/**
15792 * Store information about the start and end of the track and the
15793 * duration for each frame/sample we process in order to calculate
15794 * the baseMediaDecodeTime
15795 */
15796collectDtsInfo = function(track, data) {
15797  if (typeof data.pts === 'number') {
15798    if (track.timelineStartInfo.pts === undefined) {
15799      track.timelineStartInfo.pts = data.pts;
15800    }
15801
15802    if (track.minSegmentPts === undefined) {
15803      track.minSegmentPts = data.pts;
15804    } else {
15805      track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
15806    }
15807
15808    if (track.maxSegmentPts === undefined) {
15809      track.maxSegmentPts = data.pts;
15810    } else {
15811      track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
15812    }
15813  }
15814
15815  if (typeof data.dts === 'number') {
15816    if (track.timelineStartInfo.dts === undefined) {
15817      track.timelineStartInfo.dts = data.dts;
15818    }
15819
15820    if (track.minSegmentDts === undefined) {
15821      track.minSegmentDts = data.dts;
15822    } else {
15823      track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
15824    }
15825
15826    if (track.maxSegmentDts === undefined) {
15827      track.maxSegmentDts = data.dts;
15828    } else {
15829      track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
15830    }
15831  }
15832};
15833
15834/**
15835 * Clear values used to calculate the baseMediaDecodeTime between
15836 * tracks
15837 */
15838clearDtsInfo = function(track) {
15839  delete track.minSegmentDts;
15840  delete track.maxSegmentDts;
15841  delete track.minSegmentPts;
15842  delete track.maxSegmentPts;
15843};
15844
15845/**
15846 * Calculate the track's baseMediaDecodeTime based on the earliest
15847 * DTS the transmuxer has ever seen and the minimum DTS for the
15848 * current track
15849 */
15850calculateTrackBaseMediaDecodeTime = function(track) {
15851  var
15852    baseMediaDecodeTime,
15853    scale,
15854    // Calculate the distance, in time, that this segment starts from the start
15855    // of the timeline (earliest time seen since the transmuxer initialized)
15856    timeSinceStartOfTimeline = track.minSegmentDts - track.timelineStartInfo.dts;
15857
15858  // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
15859  // we want the start of the first segment to be placed
15860  baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime;
15861
15862  // Add to that the distance this segment is from the very first
15863  baseMediaDecodeTime += timeSinceStartOfTimeline;
15864
15865  // baseMediaDecodeTime must not become negative
15866  baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
15867
15868  if (track.type === 'audio') {
15869    // Audio has a different clock equal to the sampling_rate so we need to
15870    // scale the PTS values into the clock rate of the track
15871    scale = track.samplerate / ONE_SECOND_IN_TS;
15872    baseMediaDecodeTime *= scale;
15873    baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
15874  }
15875
15876  return baseMediaDecodeTime;
15877};
15878
15879/**
15880 * A Stream that can combine multiple streams (ie. audio & video)
15881 * into a single output segment for MSE. Also supports audio-only
15882 * and video-only streams.
15883 */
15884CoalesceStream = function(options, metadataStream) {
15885  // Number of Tracks per output segment
15886  // If greater than 1, we combine multiple
15887  // tracks into a single segment
15888  this.numberOfTracks = 0;
15889  this.metadataStream = metadataStream;
15890
15891  if (typeof options.remux !== 'undefined') {
15892    this.remuxTracks = !!options.remux;
15893  } else {
15894    this.remuxTracks = true;
15895  }
15896
15897  this.pendingTracks = [];
15898  this.videoTrack = null;
15899  this.pendingBoxes = [];
15900  this.pendingCaptions = [];
15901  this.pendingMetadata = [];
15902  this.pendingBytes = 0;
15903  this.emittedTracks = 0;
15904
15905  CoalesceStream.prototype.init.call(this);
15906
15907  // Take output from multiple
15908  this.push = function(output) {
15909    // buffer incoming captions until the associated video segment
15910    // finishes
15911    if (output.text) {
15912      return this.pendingCaptions.push(output);
15913    }
15914    // buffer incoming id3 tags until the final flush
15915    if (output.frames) {
15916      return this.pendingMetadata.push(output);
15917    }
15918
15919    // Add this track to the list of pending tracks and store
15920    // important information required for the construction of
15921    // the final segment
15922    this.pendingTracks.push(output.track);
15923    this.pendingBoxes.push(output.boxes);
15924    this.pendingBytes += output.boxes.byteLength;
15925
15926    if (output.track.type === 'video') {
15927      this.videoTrack = output.track;
15928    }
15929    if (output.track.type === 'audio') {
15930      this.audioTrack = output.track;
15931    }
15932  };
15933};
15934
15935CoalesceStream.prototype = new Stream();
15936CoalesceStream.prototype.flush = function(flushSource) {
15937  var
15938    offset = 0,
15939    event = {
15940      captions: [],
15941      captionStreams: {},
15942      metadata: [],
15943      info: {}
15944    },
15945    caption,
15946    id3,
15947    initSegment,
15948    timelineStartPts = 0,
15949    i;
15950
15951  if (this.pendingTracks.length < this.numberOfTracks) {
15952    if (flushSource !== 'VideoSegmentStream' &&
15953        flushSource !== 'AudioSegmentStream') {
15954      // Return because we haven't received a flush from a data-generating
15955      // portion of the segment (meaning that we have only recieved meta-data
15956      // or captions.)
15957      return;
15958    } else if (this.remuxTracks) {
15959      // Return until we have enough tracks from the pipeline to remux (if we
15960      // are remuxing audio and video into a single MP4)
15961      return;
15962    } else if (this.pendingTracks.length === 0) {
15963      // In the case where we receive a flush without any data having been
15964      // received we consider it an emitted track for the purposes of coalescing
15965      // `done` events.
15966      // We do this for the case where there is an audio and video track in the
15967      // segment but no audio data. (seen in several playlists with alternate
15968      // audio tracks and no audio present in the main TS segments.)
15969      this.emittedTracks++;
15970
15971      if (this.emittedTracks >= this.numberOfTracks) {
15972        this.trigger('done');
15973        this.emittedTracks = 0;
15974      }
15975      return;
15976    }
15977  }
15978
15979  if (this.videoTrack) {
15980    timelineStartPts = this.videoTrack.timelineStartInfo.pts;
15981    VIDEO_PROPERTIES.forEach(function(prop) {
15982      event.info[prop] = this.videoTrack[prop];
15983    }, this);
15984  } else if (this.audioTrack) {
15985    timelineStartPts = this.audioTrack.timelineStartInfo.pts;
15986    AUDIO_PROPERTIES.forEach(function(prop) {
15987      event.info[prop] = this.audioTrack[prop];
15988    }, this);
15989  }
15990
15991  if (this.pendingTracks.length === 1) {
15992    event.type = this.pendingTracks[0].type;
15993  } else {
15994    event.type = 'combined';
15995  }
15996
15997  this.emittedTracks += this.pendingTracks.length;
15998
15999  initSegment = mp4.initSegment(this.pendingTracks);
16000
16001  // Create a new typed array to hold the init segment
16002  event.initSegment = new Uint8Array(initSegment.byteLength);
16003
16004  // Create an init segment containing a moov
16005  // and track definitions
16006  event.initSegment.set(initSegment);
16007
16008  // Create a new typed array to hold the moof+mdats
16009  event.data = new Uint8Array(this.pendingBytes);
16010
16011  // Append each moof+mdat (one per track) together
16012  for (i = 0; i < this.pendingBoxes.length; i++) {
16013    event.data.set(this.pendingBoxes[i], offset);
16014    offset += this.pendingBoxes[i].byteLength;
16015  }
16016
16017  // Translate caption PTS times into second offsets into the
16018  // video timeline for the segment, and add track info
16019  for (i = 0; i < this.pendingCaptions.length; i++) {
16020    caption = this.pendingCaptions[i];
16021    caption.startTime = (caption.startPts - timelineStartPts);
16022    caption.startTime /= 90e3;
16023    caption.endTime = (caption.endPts - timelineStartPts);
16024    caption.endTime /= 90e3;
16025    event.captionStreams[caption.stream] = true;
16026    event.captions.push(caption);
16027  }
16028
16029  // Translate ID3 frame PTS times into second offsets into the
16030  // video timeline for the segment
16031  for (i = 0; i < this.pendingMetadata.length; i++) {
16032    id3 = this.pendingMetadata[i];
16033    id3.cueTime = (id3.pts - timelineStartPts);
16034    id3.cueTime /= 90e3;
16035    event.metadata.push(id3);
16036  }
16037  // We add this to every single emitted segment even though we only need
16038  // it for the first
16039  event.metadata.dispatchType = this.metadataStream.dispatchType;
16040
16041  // Reset stream state
16042  this.pendingTracks.length = 0;
16043  this.videoTrack = null;
16044  this.pendingBoxes.length = 0;
16045  this.pendingCaptions.length = 0;
16046  this.pendingBytes = 0;
16047  this.pendingMetadata.length = 0;
16048
16049  // Emit the built segment
16050  this.trigger('data', event);
16051
16052  // Only emit `done` if all tracks have been flushed and emitted
16053  if (this.emittedTracks >= this.numberOfTracks) {
16054    this.trigger('done');
16055    this.emittedTracks = 0;
16056  }
16057};
16058/**
16059 * A Stream that expects MP2T binary data as input and produces
16060 * corresponding media segments, suitable for use with Media Source
16061 * Extension (MSE) implementations that support the ISO BMFF byte
16062 * stream format, like Chrome.
16063 */
16064Transmuxer = function(options) {
16065  var
16066    self = this,
16067    hasFlushed = true,
16068    videoTrack,
16069    audioTrack;
16070
16071  Transmuxer.prototype.init.call(this);
16072
16073  options = options || {};
16074  this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
16075  this.transmuxPipeline_ = {};
16076
16077  this.setupAacPipeline = function() {
16078    var pipeline = {};
16079    this.transmuxPipeline_ = pipeline;
16080
16081    pipeline.type = 'aac';
16082    pipeline.metadataStream = new m2ts.MetadataStream();
16083
16084    // set up the parsing pipeline
16085    pipeline.aacStream = new AacStream();
16086    pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
16087    pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
16088    pipeline.adtsStream = new AdtsStream();
16089    pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
16090    pipeline.headOfPipeline = pipeline.aacStream;
16091
16092    pipeline.aacStream
16093      .pipe(pipeline.audioTimestampRolloverStream)
16094      .pipe(pipeline.adtsStream);
16095    pipeline.aacStream
16096      .pipe(pipeline.timedMetadataTimestampRolloverStream)
16097      .pipe(pipeline.metadataStream)
16098      .pipe(pipeline.coalesceStream);
16099
16100    pipeline.metadataStream.on('timestamp', function(frame) {
16101      pipeline.aacStream.setTimestamp(frame.timeStamp);
16102    });
16103
16104    pipeline.aacStream.on('data', function(data) {
16105      if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
16106        audioTrack = audioTrack || {
16107          timelineStartInfo: {
16108            baseMediaDecodeTime: self.baseMediaDecodeTime
16109          },
16110          codec: 'adts',
16111          type: 'audio'
16112        };
16113        // hook up the audio segment stream to the first track with aac data
16114        pipeline.coalesceStream.numberOfTracks++;
16115        pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
16116        // Set up the final part of the audio pipeline
16117        pipeline.adtsStream
16118          .pipe(pipeline.audioSegmentStream)
16119          .pipe(pipeline.coalesceStream);
16120      }
16121    });
16122
16123    // Re-emit any data coming from the coalesce stream to the outside world
16124    pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
16125    // Let the consumer know we have finished flushing the entire pipeline
16126    pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
16127  };
16128
16129  this.setupTsPipeline = function() {
16130    var pipeline = {};
16131    this.transmuxPipeline_ = pipeline;
16132
16133    pipeline.type = 'ts';
16134    pipeline.metadataStream = new m2ts.MetadataStream();
16135
16136    // set up the parsing pipeline
16137    pipeline.packetStream = new m2ts.TransportPacketStream();
16138    pipeline.parseStream = new m2ts.TransportParseStream();
16139    pipeline.elementaryStream = new m2ts.ElementaryStream();
16140    pipeline.videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
16141    pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
16142    pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
16143    pipeline.adtsStream = new AdtsStream();
16144    pipeline.h264Stream = new H264Stream();
16145    pipeline.captionStream = new m2ts.CaptionStream();
16146    pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
16147    pipeline.headOfPipeline = pipeline.packetStream;
16148
16149    // disassemble MPEG2-TS packets into elementary streams
16150    pipeline.packetStream
16151      .pipe(pipeline.parseStream)
16152      .pipe(pipeline.elementaryStream);
16153
16154    // !!THIS ORDER IS IMPORTANT!!
16155    // demux the streams
16156    pipeline.elementaryStream
16157      .pipe(pipeline.videoTimestampRolloverStream)
16158      .pipe(pipeline.h264Stream);
16159    pipeline.elementaryStream
16160      .pipe(pipeline.audioTimestampRolloverStream)
16161      .pipe(pipeline.adtsStream);
16162
16163    pipeline.elementaryStream
16164      .pipe(pipeline.timedMetadataTimestampRolloverStream)
16165      .pipe(pipeline.metadataStream)
16166      .pipe(pipeline.coalesceStream);
16167
16168    // Hook up CEA-608/708 caption stream
16169    pipeline.h264Stream.pipe(pipeline.captionStream)
16170      .pipe(pipeline.coalesceStream);
16171
16172    pipeline.elementaryStream.on('data', function(data) {
16173      var i;
16174
16175      if (data.type === 'metadata') {
16176        i = data.tracks.length;
16177
16178        // scan the tracks listed in the metadata
16179        while (i--) {
16180          if (!videoTrack && data.tracks[i].type === 'video') {
16181            videoTrack = data.tracks[i];
16182            videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
16183          } else if (!audioTrack && data.tracks[i].type === 'audio') {
16184            audioTrack = data.tracks[i];
16185            audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
16186          }
16187        }
16188
16189        // hook up the video segment stream to the first track with h264 data
16190        if (videoTrack && !pipeline.videoSegmentStream) {
16191          pipeline.coalesceStream.numberOfTracks++;
16192          pipeline.videoSegmentStream = new VideoSegmentStream(videoTrack);
16193
16194          pipeline.videoSegmentStream.on('timelineStartInfo', function(timelineStartInfo) {
16195          // When video emits timelineStartInfo data after a flush, we forward that
16196          // info to the AudioSegmentStream, if it exists, because video timeline
16197          // data takes precedence.
16198            if (audioTrack) {
16199              audioTrack.timelineStartInfo = timelineStartInfo;
16200              // On the first segment we trim AAC frames that exist before the
16201              // very earliest DTS we have seen in video because Chrome will
16202              // interpret any video track with a baseMediaDecodeTime that is
16203              // non-zero as a gap.
16204              pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
16205            }
16206          });
16207
16208          pipeline.videoSegmentStream.on('baseMediaDecodeTime', function(baseMediaDecodeTime) {
16209            if (audioTrack) {
16210              pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
16211            }
16212          });
16213
16214          // Set up the final part of the video pipeline
16215          pipeline.h264Stream
16216            .pipe(pipeline.videoSegmentStream)
16217            .pipe(pipeline.coalesceStream);
16218        }
16219
16220        if (audioTrack && !pipeline.audioSegmentStream) {
16221          // hook up the audio segment stream to the first track with aac data
16222          pipeline.coalesceStream.numberOfTracks++;
16223          pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
16224
16225          // Set up the final part of the audio pipeline
16226          pipeline.adtsStream
16227            .pipe(pipeline.audioSegmentStream)
16228            .pipe(pipeline.coalesceStream);
16229        }
16230      }
16231    });
16232
16233    // Re-emit any data coming from the coalesce stream to the outside world
16234    pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
16235    // Let the consumer know we have finished flushing the entire pipeline
16236    pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
16237  };
16238
16239  // hook up the segment streams once track metadata is delivered
16240  this.setBaseMediaDecodeTime = function(baseMediaDecodeTime) {
16241    var pipeline = this.transmuxPipeline_;
16242
16243    this.baseMediaDecodeTime = baseMediaDecodeTime;
16244    if (audioTrack) {
16245      audioTrack.timelineStartInfo.dts = undefined;
16246      audioTrack.timelineStartInfo.pts = undefined;
16247      clearDtsInfo(audioTrack);
16248      audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
16249      if (pipeline.audioTimestampRolloverStream) {
16250        pipeline.audioTimestampRolloverStream.discontinuity();
16251      }
16252    }
16253    if (videoTrack) {
16254      if (pipeline.videoSegmentStream) {
16255        pipeline.videoSegmentStream.gopCache_ = [];
16256        pipeline.videoTimestampRolloverStream.discontinuity();
16257      }
16258      videoTrack.timelineStartInfo.dts = undefined;
16259      videoTrack.timelineStartInfo.pts = undefined;
16260      clearDtsInfo(videoTrack);
16261      pipeline.captionStream.reset();
16262      videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
16263    }
16264
16265    if (pipeline.timedMetadataTimestampRolloverStream) {
16266      pipeline.timedMetadataTimestampRolloverStream.discontinuity();
16267    }
16268  };
16269
16270  this.setAudioAppendStart = function(timestamp) {
16271    if (audioTrack) {
16272      this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
16273    }
16274  };
16275
16276  // feed incoming data to the front of the parsing pipeline
16277  this.push = function(data) {
16278    if (hasFlushed) {
16279      var isAac = isLikelyAacData(data);
16280
16281      if (isAac && this.transmuxPipeline_.type !== 'aac') {
16282        this.setupAacPipeline();
16283      } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
16284        this.setupTsPipeline();
16285      }
16286      hasFlushed = false;
16287    }
16288    this.transmuxPipeline_.headOfPipeline.push(data);
16289  };
16290
16291  // flush any buffered data
16292  this.flush = function() {
16293      hasFlushed = true;
16294    // Start at the top of the pipeline and flush all pending work
16295    this.transmuxPipeline_.headOfPipeline.flush();
16296  };
16297
16298  // Caption data has to be reset when seeking outside buffered range
16299  this.resetCaptions = function() {
16300    if (this.transmuxPipeline_.captionStream) {
16301      this.transmuxPipeline_.captionStream.reset();
16302    }
16303  };
16304
16305};
16306Transmuxer.prototype = new Stream();
16307
16308module.exports = {
16309  Transmuxer: Transmuxer,
16310  VideoSegmentStream: VideoSegmentStream,
16311  AudioSegmentStream: AudioSegmentStream,
16312  AUDIO_PROPERTIES: AUDIO_PROPERTIES,
16313  VIDEO_PROPERTIES: VIDEO_PROPERTIES
16314};
16315
16316},{"../aac":38,"../codecs/adts.js":40,"../codecs/h264":41,"../data/silence":42,"../m2ts/m2ts.js":50,"../utils/clock":60,"../utils/stream.js":62,"./mp4-generator.js":56}],59:[function(require,module,exports){
16317/**
16318 * mux.js
16319 *
16320 * Copyright (c) 2016 Brightcove
16321 * All rights reserved.
16322 *
16323 * Parse mpeg2 transport stream packets to extract basic timing information
16324 */
16325'use strict';
16326
16327var StreamTypes = require('../m2ts/stream-types.js');
16328var handleRollover = require('../m2ts/timestamp-rollover-stream.js').handleRollover;
16329var probe = {};
16330probe.ts = require('../m2ts/probe.js');
16331probe.aac = require('../aac/probe.js');
16332
16333
16334var
16335  PES_TIMESCALE = 90000,
16336  MP2T_PACKET_LENGTH = 188, // bytes
16337  SYNC_BYTE = 0x47;
16338
16339var isLikelyAacData = function(data) {
16340  if ((data[0] === 'I'.charCodeAt(0)) &&
16341      (data[1] === 'D'.charCodeAt(0)) &&
16342      (data[2] === '3'.charCodeAt(0))) {
16343    return true;
16344  }
16345  return false;
16346};
16347
16348/**
16349 * walks through segment data looking for pat and pmt packets to parse out
16350 * program map table information
16351 */
16352var parsePsi_ = function(bytes, pmt) {
16353  var
16354    startIndex = 0,
16355    endIndex = MP2T_PACKET_LENGTH,
16356    packet, type;
16357
16358  while (endIndex < bytes.byteLength) {
16359    // Look for a pair of start and end sync bytes in the data..
16360    if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
16361      // We found a packet
16362      packet = bytes.subarray(startIndex, endIndex);
16363      type = probe.ts.parseType(packet, pmt.pid);
16364
16365      switch (type) {
16366        case 'pat':
16367          if (!pmt.pid) {
16368            pmt.pid = probe.ts.parsePat(packet);
16369          }
16370          break;
16371        case 'pmt':
16372          if (!pmt.table) {
16373            pmt.table = probe.ts.parsePmt(packet);
16374          }
16375          break;
16376        default:
16377          break;
16378      }
16379
16380      // Found the pat and pmt, we can stop walking the segment
16381      if (pmt.pid && pmt.table) {
16382        return;
16383      }
16384
16385      startIndex += MP2T_PACKET_LENGTH;
16386      endIndex += MP2T_PACKET_LENGTH;
16387      continue;
16388    }
16389
16390    // If we get here, we have somehow become de-synchronized and we need to step
16391    // forward one byte at a time until we find a pair of sync bytes that denote
16392    // a packet
16393    startIndex++;
16394    endIndex++;
16395  }
16396};
16397
16398/**
16399 * walks through the segment data from the start and end to get timing information
16400 * for the first and last audio pes packets
16401 */
16402var parseAudioPes_ = function(bytes, pmt, result) {
16403  var
16404    startIndex = 0,
16405    endIndex = MP2T_PACKET_LENGTH,
16406    packet, type, pesType, pusi, parsed;
16407
16408  var endLoop = false;
16409
16410  // Start walking from start of segment to get first audio packet
16411  while (endIndex < bytes.byteLength) {
16412    // Look for a pair of start and end sync bytes in the data..
16413    if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
16414      // We found a packet
16415      packet = bytes.subarray(startIndex, endIndex);
16416      type = probe.ts.parseType(packet, pmt.pid);
16417
16418      switch (type) {
16419        case 'pes':
16420          pesType = probe.ts.parsePesType(packet, pmt.table);
16421          pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
16422          if (pesType === 'audio' && pusi) {
16423            parsed = probe.ts.parsePesTime(packet);
16424            if (parsed) {
16425              parsed.type = 'audio';
16426              result.audio.push(parsed);
16427              endLoop = true;
16428            }
16429          }
16430          break;
16431        default:
16432          break;
16433      }
16434
16435      if (endLoop) {
16436        break;
16437      }
16438
16439      startIndex += MP2T_PACKET_LENGTH;
16440      endIndex += MP2T_PACKET_LENGTH;
16441      continue;
16442    }
16443
16444    // If we get here, we have somehow become de-synchronized and we need to step
16445    // forward one byte at a time until we find a pair of sync bytes that denote
16446    // a packet
16447    startIndex++;
16448    endIndex++;
16449  }
16450
16451  // Start walking from end of segment to get last audio packet
16452  endIndex = bytes.byteLength;
16453  startIndex = endIndex - MP2T_PACKET_LENGTH;
16454  endLoop = false;
16455  while (startIndex >= 0) {
16456    // Look for a pair of start and end sync bytes in the data..
16457    if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
16458      // We found a packet
16459      packet = bytes.subarray(startIndex, endIndex);
16460      type = probe.ts.parseType(packet, pmt.pid);
16461
16462      switch (type) {
16463        case 'pes':
16464          pesType = probe.ts.parsePesType(packet, pmt.table);
16465          pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
16466          if (pesType === 'audio' && pusi) {
16467            parsed = probe.ts.parsePesTime(packet);
16468            if (parsed) {
16469              parsed.type = 'audio';
16470              result.audio.push(parsed);
16471              endLoop = true;
16472            }
16473          }
16474          break;
16475        default:
16476          break;
16477      }
16478
16479      if (endLoop) {
16480        break;
16481      }
16482
16483      startIndex -= MP2T_PACKET_LENGTH;
16484      endIndex -= MP2T_PACKET_LENGTH;
16485      continue;
16486    }
16487
16488    // If we get here, we have somehow become de-synchronized and we need to step
16489    // forward one byte at a time until we find a pair of sync bytes that denote
16490    // a packet
16491    startIndex--;
16492    endIndex--;
16493  }
16494};
16495
16496/**
16497 * walks through the segment data from the start and end to get timing information
16498 * for the first and last video pes packets as well as timing information for the first
16499 * key frame.
16500 */
16501var parseVideoPes_ = function(bytes, pmt, result) {
16502  var
16503    startIndex = 0,
16504    endIndex = MP2T_PACKET_LENGTH,
16505    packet, type, pesType, pusi, parsed, frame, i, pes;
16506
16507  var endLoop = false;
16508
16509  var currentFrame = {
16510    data: [],
16511    size: 0
16512  };
16513
16514  // Start walking from start of segment to get first video packet
16515  while (endIndex < bytes.byteLength) {
16516    // Look for a pair of start and end sync bytes in the data..
16517    if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
16518      // We found a packet
16519      packet = bytes.subarray(startIndex, endIndex);
16520      type = probe.ts.parseType(packet, pmt.pid);
16521
16522      switch (type) {
16523        case 'pes':
16524          pesType = probe.ts.parsePesType(packet, pmt.table);
16525          pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
16526          if (pesType === 'video') {
16527            if (pusi && !endLoop) {
16528              parsed = probe.ts.parsePesTime(packet);
16529              if (parsed) {
16530                parsed.type = 'video';
16531                result.video.push(parsed);
16532                endLoop = true;
16533              }
16534            }
16535            if (!result.firstKeyFrame) {
16536              if (pusi) {
16537                if (currentFrame.size !== 0) {
16538                  frame = new Uint8Array(currentFrame.size);
16539                  i = 0;
16540                  while (currentFrame.data.length) {
16541                    pes = currentFrame.data.shift();
16542                    frame.set(pes, i);
16543                    i += pes.byteLength;
16544                  }
16545                  if (probe.ts.videoPacketContainsKeyFrame(frame)) {
16546                    result.firstKeyFrame = probe.ts.parsePesTime(frame);
16547                    result.firstKeyFrame.type = 'video';
16548                  }
16549                  currentFrame.size = 0;
16550                }
16551              }
16552              currentFrame.data.push(packet);
16553              currentFrame.size += packet.byteLength;
16554            }
16555          }
16556          break;
16557        default:
16558          break;
16559      }
16560
16561      if (endLoop && result.firstKeyFrame) {
16562        break;
16563      }
16564
16565      startIndex += MP2T_PACKET_LENGTH;
16566      endIndex += MP2T_PACKET_LENGTH;
16567      continue;
16568    }
16569
16570    // If we get here, we have somehow become de-synchronized and we need to step
16571    // forward one byte at a time until we find a pair of sync bytes that denote
16572    // a packet
16573    startIndex++;
16574    endIndex++;
16575  }
16576
16577  // Start walking from end of segment to get last video packet
16578  endIndex = bytes.byteLength;
16579  startIndex = endIndex - MP2T_PACKET_LENGTH;
16580  endLoop = false;
16581  while (startIndex >= 0) {
16582    // Look for a pair of start and end sync bytes in the data..
16583    if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
16584      // We found a packet
16585      packet = bytes.subarray(startIndex, endIndex);
16586      type = probe.ts.parseType(packet, pmt.pid);
16587
16588      switch (type) {
16589        case 'pes':
16590          pesType = probe.ts.parsePesType(packet, pmt.table);
16591          pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
16592          if (pesType === 'video' && pusi) {
16593              parsed = probe.ts.parsePesTime(packet);
16594              if (parsed) {
16595                parsed.type = 'video';
16596                result.video.push(parsed);
16597                endLoop = true;
16598              }
16599          }
16600          break;
16601        default:
16602          break;
16603      }
16604
16605      if (endLoop) {
16606        break;
16607      }
16608
16609      startIndex -= MP2T_PACKET_LENGTH;
16610      endIndex -= MP2T_PACKET_LENGTH;
16611      continue;
16612    }
16613
16614    // If we get here, we have somehow become de-synchronized and we need to step
16615    // forward one byte at a time until we find a pair of sync bytes that denote
16616    // a packet
16617    startIndex--;
16618    endIndex--;
16619  }
16620};
16621
16622/**
16623 * Adjusts the timestamp information for the segment to account for
16624 * rollover and convert to seconds based on pes packet timescale (90khz clock)
16625 */
16626var adjustTimestamp_ = function(segmentInfo, baseTimestamp) {
16627  if (segmentInfo.audio && segmentInfo.audio.length) {
16628    var audioBaseTimestamp = baseTimestamp;
16629    if (typeof audioBaseTimestamp === 'undefined') {
16630      audioBaseTimestamp = segmentInfo.audio[0].dts;
16631    }
16632    segmentInfo.audio.forEach(function(info) {
16633      info.dts = handleRollover(info.dts, audioBaseTimestamp);
16634      info.pts = handleRollover(info.pts, audioBaseTimestamp);
16635      // time in seconds
16636      info.dtsTime = info.dts / PES_TIMESCALE;
16637      info.ptsTime = info.pts / PES_TIMESCALE;
16638    });
16639  }
16640
16641  if (segmentInfo.video && segmentInfo.video.length) {
16642    var videoBaseTimestamp = baseTimestamp;
16643    if (typeof videoBaseTimestamp === 'undefined') {
16644      videoBaseTimestamp = segmentInfo.video[0].dts;
16645    }
16646    segmentInfo.video.forEach(function(info) {
16647      info.dts = handleRollover(info.dts, videoBaseTimestamp);
16648      info.pts = handleRollover(info.pts, videoBaseTimestamp);
16649      // time in seconds
16650      info.dtsTime = info.dts / PES_TIMESCALE;
16651      info.ptsTime = info.pts / PES_TIMESCALE;
16652    });
16653    if (segmentInfo.firstKeyFrame) {
16654      var frame = segmentInfo.firstKeyFrame;
16655      frame.dts = handleRollover(frame.dts, videoBaseTimestamp);
16656      frame.pts = handleRollover(frame.pts, videoBaseTimestamp);
16657      // time in seconds
16658      frame.dtsTime = frame.dts / PES_TIMESCALE;
16659      frame.ptsTime = frame.dts / PES_TIMESCALE;
16660    }
16661  }
16662};
16663
16664/**
16665 * inspects the aac data stream for start and end time information
16666 */
16667var inspectAac_ = function(bytes) {
16668  var
16669    endLoop = false,
16670    audioCount = 0,
16671    sampleRate = null,
16672    timestamp = null,
16673    frameSize = 0,
16674    byteIndex = 0,
16675    packet;
16676
16677  while (bytes.length - byteIndex >= 3) {
16678    var type = probe.aac.parseType(bytes, byteIndex);
16679    switch (type) {
16680      case 'timed-metadata':
16681        // Exit early because we don't have enough to parse
16682        // the ID3 tag header
16683        if (bytes.length - byteIndex < 10) {
16684          endLoop = true;
16685          break;
16686        }
16687
16688        frameSize = probe.aac.parseId3TagSize(bytes, byteIndex);
16689
16690        // Exit early if we don't have enough in the buffer
16691        // to emit a full packet
16692        if (frameSize > bytes.length) {
16693          endLoop = true;
16694          break;
16695        }
16696        if (timestamp === null) {
16697          packet = bytes.subarray(byteIndex, byteIndex + frameSize);
16698          timestamp = probe.aac.parseAacTimestamp(packet);
16699        }
16700        byteIndex += frameSize;
16701        break;
16702      case 'audio':
16703        // Exit early because we don't have enough to parse
16704        // the ADTS frame header
16705        if (bytes.length - byteIndex < 7) {
16706          endLoop = true;
16707          break;
16708        }
16709
16710        frameSize = probe.aac.parseAdtsSize(bytes, byteIndex);
16711
16712        // Exit early if we don't have enough in the buffer
16713        // to emit a full packet
16714        if (frameSize > bytes.length) {
16715          endLoop = true;
16716          break;
16717        }
16718        if (sampleRate === null) {
16719          packet = bytes.subarray(byteIndex, byteIndex + frameSize);
16720          sampleRate = probe.aac.parseSampleRate(packet);
16721        }
16722        audioCount++;
16723        byteIndex += frameSize;
16724        break;
16725      default:
16726        byteIndex++;
16727        break;
16728    }
16729    if (endLoop) {
16730      return null;
16731    }
16732  }
16733  if (sampleRate === null || timestamp === null) {
16734    return null;
16735  }
16736
16737  var audioTimescale = PES_TIMESCALE / sampleRate;
16738
16739  var result = {
16740    audio: [
16741      {
16742        type: 'audio',
16743        dts: timestamp,
16744        pts: timestamp
16745      },
16746      {
16747        type: 'audio',
16748        dts: timestamp + (audioCount * 1024 * audioTimescale),
16749        pts: timestamp + (audioCount * 1024 * audioTimescale)
16750      }
16751    ]
16752  };
16753
16754  return result;
16755};
16756
16757/**
16758 * inspects the transport stream segment data for start and end time information
16759 * of the audio and video tracks (when present) as well as the first key frame's
16760 * start time.
16761 */
16762var inspectTs_ = function(bytes) {
16763  var pmt = {
16764    pid: null,
16765    table: null
16766  };
16767
16768  var result = {};
16769
16770  parsePsi_(bytes, pmt);
16771
16772  for (var pid in pmt.table) {
16773    if (pmt.table.hasOwnProperty(pid)) {
16774      var type = pmt.table[pid];
16775      switch (type) {
16776        case StreamTypes.H264_STREAM_TYPE:
16777          result.video = [];
16778          parseVideoPes_(bytes, pmt, result);
16779          if (result.video.length === 0) {
16780            delete result.video;
16781          }
16782          break;
16783        case StreamTypes.ADTS_STREAM_TYPE:
16784          result.audio = [];
16785          parseAudioPes_(bytes, pmt, result);
16786          if (result.audio.length === 0) {
16787            delete result.audio;
16788          }
16789          break;
16790        default:
16791          break;
16792      }
16793    }
16794  }
16795  return result;
16796};
16797
16798/**
16799 * Inspects segment byte data and returns an object with start and end timing information
16800 *
16801 * @param {Uint8Array} bytes The segment byte data
16802 * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame
16803 *  timestamps for rollover. This value must be in 90khz clock.
16804 * @return {Object} Object containing start and end frame timing info of segment.
16805 */
16806var inspect = function(bytes, baseTimestamp) {
16807  var isAacData = isLikelyAacData(bytes);
16808
16809  var result;
16810
16811  if (isAacData) {
16812    result = inspectAac_(bytes);
16813  } else {
16814    result = inspectTs_(bytes);
16815  }
16816
16817  if (!result || (!result.audio && !result.video)) {
16818    return null;
16819  }
16820
16821  adjustTimestamp_(result, baseTimestamp);
16822
16823  return result;
16824};
16825
16826module.exports = {
16827  inspect: inspect
16828};
16829
16830},{"../aac/probe.js":39,"../m2ts/probe.js":52,"../m2ts/stream-types.js":53,"../m2ts/timestamp-rollover-stream.js":54}],60:[function(require,module,exports){
16831var
16832  ONE_SECOND_IN_TS = 90000, // 90kHz clock
16833  secondsToVideoTs,
16834  secondsToAudioTs,
16835  videoTsToSeconds,
16836  audioTsToSeconds,
16837  audioTsToVideoTs,
16838  videoTsToAudioTs;
16839
16840secondsToVideoTs = function(seconds) {
16841  return seconds * ONE_SECOND_IN_TS;
16842};
16843
16844secondsToAudioTs = function(seconds, sampleRate) {
16845  return seconds * sampleRate;
16846};
16847
16848videoTsToSeconds = function(timestamp) {
16849  return timestamp / ONE_SECOND_IN_TS;
16850};
16851
16852audioTsToSeconds = function(timestamp, sampleRate) {
16853  return timestamp / sampleRate;
16854};
16855
16856audioTsToVideoTs = function(timestamp, sampleRate) {
16857  return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
16858};
16859
16860videoTsToAudioTs = function(timestamp, sampleRate) {
16861  return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
16862};
16863
16864module.exports = {
16865  secondsToVideoTs: secondsToVideoTs,
16866  secondsToAudioTs: secondsToAudioTs,
16867  videoTsToSeconds: videoTsToSeconds,
16868  audioTsToSeconds: audioTsToSeconds,
16869  audioTsToVideoTs: audioTsToVideoTs,
16870  videoTsToAudioTs: videoTsToAudioTs
16871};
16872
16873},{}],61:[function(require,module,exports){
16874'use strict';
16875
16876var ExpGolomb;
16877
16878/**
16879 * Parser for exponential Golomb codes, a variable-bitwidth number encoding
16880 * scheme used by h264.
16881 */
16882ExpGolomb = function(workingData) {
16883  var
16884    // the number of bytes left to examine in workingData
16885    workingBytesAvailable = workingData.byteLength,
16886
16887    // the current word being examined
16888    workingWord = 0, // :uint
16889
16890    // the number of bits left to examine in the current word
16891    workingBitsAvailable = 0; // :uint;
16892
16893  // ():uint
16894  this.length = function() {
16895    return (8 * workingBytesAvailable);
16896  };
16897
16898  // ():uint
16899  this.bitsAvailable = function() {
16900    return (8 * workingBytesAvailable) + workingBitsAvailable;
16901  };
16902
16903  // ():void
16904  this.loadWord = function() {
16905    var
16906      position = workingData.byteLength - workingBytesAvailable,
16907      workingBytes = new Uint8Array(4),
16908      availableBytes = Math.min(4, workingBytesAvailable);
16909
16910    if (availableBytes === 0) {
16911      throw new Error('no bytes available');
16912    }
16913
16914    workingBytes.set(workingData.subarray(position,
16915                                          position + availableBytes));
16916    workingWord = new DataView(workingBytes.buffer).getUint32(0);
16917
16918    // track the amount of workingData that has been processed
16919    workingBitsAvailable = availableBytes * 8;
16920    workingBytesAvailable -= availableBytes;
16921  };
16922
16923  // (count:int):void
16924  this.skipBits = function(count) {
16925    var skipBytes; // :int
16926    if (workingBitsAvailable > count) {
16927      workingWord          <<= count;
16928      workingBitsAvailable -= count;
16929    } else {
16930      count -= workingBitsAvailable;
16931      skipBytes = Math.floor(count / 8);
16932
16933      count -= (skipBytes * 8);
16934      workingBytesAvailable -= skipBytes;
16935
16936      this.loadWord();
16937
16938      workingWord <<= count;
16939      workingBitsAvailable -= count;
16940    }
16941  };
16942
16943  // (size:int):uint
16944  this.readBits = function(size) {
16945    var
16946      bits = Math.min(workingBitsAvailable, size), // :uint
16947      valu = workingWord >>> (32 - bits); // :uint
16948    // if size > 31, handle error
16949    workingBitsAvailable -= bits;
16950    if (workingBitsAvailable > 0) {
16951      workingWord <<= bits;
16952    } else if (workingBytesAvailable > 0) {
16953      this.loadWord();
16954    }
16955
16956    bits = size - bits;
16957    if (bits > 0) {
16958      return valu << bits | this.readBits(bits);
16959    }
16960    return valu;
16961  };
16962
16963  // ():uint
16964  this.skipLeadingZeros = function() {
16965    var leadingZeroCount; // :uint
16966    for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
16967      if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) {
16968        // the first bit of working word is 1
16969        workingWord <<= leadingZeroCount;
16970        workingBitsAvailable -= leadingZeroCount;
16971        return leadingZeroCount;
16972      }
16973    }
16974
16975    // we exhausted workingWord and still have not found a 1
16976    this.loadWord();
16977    return leadingZeroCount + this.skipLeadingZeros();
16978  };
16979
16980  // ():void
16981  this.skipUnsignedExpGolomb = function() {
16982    this.skipBits(1 + this.skipLeadingZeros());
16983  };
16984
16985  // ():void
16986  this.skipExpGolomb = function() {
16987    this.skipBits(1 + this.skipLeadingZeros());
16988  };
16989
16990  // ():uint
16991  this.readUnsignedExpGolomb = function() {
16992    var clz = this.skipLeadingZeros(); // :uint
16993    return this.readBits(clz + 1) - 1;
16994  };
16995
16996  // ():int
16997  this.readExpGolomb = function() {
16998    var valu = this.readUnsignedExpGolomb(); // :int
16999    if (0x01 & valu) {
17000      // the number is odd if the low order bit is set
17001      return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
17002    }
17003    return -1 * (valu >>> 1); // divide by two then make it negative
17004  };
17005
17006  // Some convenience functions
17007  // :Boolean
17008  this.readBoolean = function() {
17009    return this.readBits(1) === 1;
17010  };
17011
17012  // ():int
17013  this.readUnsignedByte = function() {
17014    return this.readBits(8);
17015  };
17016
17017  this.loadWord();
17018};
17019
17020module.exports = ExpGolomb;
17021
17022},{}],62:[function(require,module,exports){
17023/**
17024 * mux.js
17025 *
17026 * Copyright (c) 2014 Brightcove
17027 * All rights reserved.
17028 *
17029 * A lightweight readable stream implemention that handles event dispatching.
17030 * Objects that inherit from streams should call init in their constructors.
17031 */
17032'use strict';
17033
17034var Stream = function() {
17035  this.init = function() {
17036    var listeners = {};
17037    /**
17038     * Add a listener for a specified event type.
17039     * @param type {string} the event name
17040     * @param listener {function} the callback to be invoked when an event of
17041     * the specified type occurs
17042     */
17043    this.on = function(type, listener) {
17044      if (!listeners[type]) {
17045        listeners[type] = [];
17046      }
17047      listeners[type] = listeners[type].concat(listener);
17048    };
17049    /**
17050     * Remove a listener for a specified event type.
17051     * @param type {string} the event name
17052     * @param listener {function} a function previously registered for this
17053     * type of event through `on`
17054     */
17055    this.off = function(type, listener) {
17056      var index;
17057      if (!listeners[type]) {
17058        return false;
17059      }
17060      index = listeners[type].indexOf(listener);
17061      listeners[type] = listeners[type].slice();
17062      listeners[type].splice(index, 1);
17063      return index > -1;
17064    };
17065    /**
17066     * Trigger an event of the specified type on this stream. Any additional
17067     * arguments to this function are passed as parameters to event listeners.
17068     * @param type {string} the event name
17069     */
17070    this.trigger = function(type) {
17071      var callbacks, i, length, args;
17072      callbacks = listeners[type];
17073      if (!callbacks) {
17074        return;
17075      }
17076      // Slicing the arguments on every invocation of this method
17077      // can add a significant amount of overhead. Avoid the
17078      // intermediate object creation for the common case of a
17079      // single callback argument
17080      if (arguments.length === 2) {
17081        length = callbacks.length;
17082        for (i = 0; i < length; ++i) {
17083          callbacks[i].call(this, arguments[1]);
17084        }
17085      } else {
17086        args = [];
17087        i = arguments.length;
17088        for (i = 1; i < arguments.length; ++i) {
17089          args.push(arguments[i]);
17090        }
17091        length = callbacks.length;
17092        for (i = 0; i < length; ++i) {
17093          callbacks[i].apply(this, args);
17094        }
17095      }
17096    };
17097    /**
17098     * Destroys the stream and cleans up.
17099     */
17100    this.dispose = function() {
17101      listeners = {};
17102    };
17103  };
17104};
17105
17106/**
17107 * Forwards all `data` events on this stream to the destination stream. The
17108 * destination stream should provide a method `push` to receive the data
17109 * events as they arrive.
17110 * @param destination {stream} the stream that will receive all `data` events
17111 * @param autoFlush {boolean} if false, we will not call `flush` on the destination
17112 *                            when the current stream emits a 'done' event
17113 * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
17114 */
17115Stream.prototype.pipe = function(destination) {
17116  this.on('data', function(data) {
17117    destination.push(data);
17118  });
17119
17120  this.on('done', function(flushSource) {
17121    destination.flush(flushSource);
17122  });
17123
17124  return destination;
17125};
17126
17127// Default stream functions that are expected to be overridden to perform
17128// actual work. These are provided by the prototype as a sort of no-op
17129// implementation so that we don't have to check for their existence in the
17130// `pipe` function above.
17131Stream.prototype.push = function(data) {
17132  this.trigger('data', data);
17133};
17134
17135Stream.prototype.flush = function(flushSource) {
17136  this.trigger('done', flushSource);
17137};
17138
17139module.exports = Stream;
17140
17141},{}],63:[function(require,module,exports){
17142/* jshint ignore:start */
17143(function(root) { 
17144/* jshint ignore:end */
17145  var URLToolkit = {
17146    // build an absolute URL from a relative one using the provided baseURL
17147    // if relativeURL is an absolute URL it will be returned as is.
17148    buildAbsoluteURL: function(baseURL, relativeURL) {
17149      // remove any remaining space and CRLF
17150      relativeURL = relativeURL.trim();
17151      if (/^[a-z]+:/i.test(relativeURL)) {
17152        // complete url, not relative
17153        return relativeURL;
17154      }
17155
17156      var relativeURLQuery = null;
17157      var relativeURLHash = null;
17158
17159      var relativeURLHashSplit = /^([^#]*)(.*)$/.exec(relativeURL);
17160      if (relativeURLHashSplit) {
17161        relativeURLHash = relativeURLHashSplit[2];
17162        relativeURL = relativeURLHashSplit[1];
17163      }
17164      var relativeURLQuerySplit = /^([^\?]*)(.*)$/.exec(relativeURL);
17165      if (relativeURLQuerySplit) {
17166        relativeURLQuery = relativeURLQuerySplit[2];
17167        relativeURL = relativeURLQuerySplit[1];
17168      }
17169
17170      var baseURLHashSplit = /^([^#]*)(.*)$/.exec(baseURL);
17171      if (baseURLHashSplit) {
17172        baseURL = baseURLHashSplit[1];
17173      }
17174      var baseURLQuerySplit = /^([^\?]*)(.*)$/.exec(baseURL);
17175      if (baseURLQuerySplit) {
17176        baseURL = baseURLQuerySplit[1];
17177      }
17178
17179      var baseURLDomainSplit = /^(([a-z]+:)?\/\/[^:\/]+(:[0-9]+)?)?(\/?.*)$/i.exec(baseURL);
17180      if (!baseURLDomainSplit) {
17181        throw new Error('Error trying to parse base URL.');
17182      }
17183      
17184      // e.g. 'http:', 'https:', ''
17185      var baseURLProtocol = baseURLDomainSplit[2] || '';
17186      // e.g. 'http://example.com', '//example.com', ''
17187      var baseURLProtocolDomain = baseURLDomainSplit[1] || '';
17188      // e.g. '/a/b/c/playlist.m3u8', 'a/b/c/playlist.m3u8'
17189      var baseURLPath = baseURLDomainSplit[4];
17190      if (baseURLPath.indexOf('/') !== 0 && baseURLProtocolDomain !== '') {
17191        // this handles a base url of http://example.com (missing last slash)
17192        baseURLPath = '/'+baseURLPath;
17193      }
17194
17195      var builtURL = null;
17196      if (/^\/\//.test(relativeURL)) {
17197        // relative url starts wth '//' so copy protocol (which may be '' if baseUrl didn't provide one)
17198        builtURL = baseURLProtocol+'//'+URLToolkit.buildAbsolutePath('', relativeURL.substring(2));
17199      }
17200      else if (/^\//.test(relativeURL)) {
17201        // relative url starts with '/' so start from root of domain
17202        builtURL = baseURLProtocolDomain+'/'+URLToolkit.buildAbsolutePath('', relativeURL.substring(1));
17203      }
17204      else {
17205        builtURL = URLToolkit.buildAbsolutePath(baseURLProtocolDomain+baseURLPath, relativeURL);
17206      }
17207
17208      // put the query and hash parts back
17209      if (relativeURLQuery) {
17210        builtURL += relativeURLQuery;
17211      }
17212      if (relativeURLHash) {
17213        builtURL += relativeURLHash;
17214      }
17215      return builtURL;
17216    },
17217
17218    // build an absolute path using the provided basePath
17219    // adapted from https://developer.mozilla.org/en-US/docs/Web/API/document/cookie#Using_relative_URLs_in_the_path_parameter
17220    // this does not handle the case where relativePath is "/" or "//". These 
17220cases should be handled outside this.
17221    buildAbsolutePath: function(basePath, relativePath) {
17222      var sRelPath = relativePath;
17223      var nUpLn, sDir = '', sPath = basePath.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, '$1'));
17224      for (var nEnd, nStart = 0; nEnd = sPath.indexOf('/../', nStart), nEnd > -1; nStart = nEnd + nUpLn) {
17225        nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length;
17226        sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp('(?:\\\/+[^\\\/]*){0,' + ((nUpLn - 1) / 3) + '}$'), '/');
17227      }
17228      return sDir + sPath.substr(nStart);
17229    }
17230  };
17231
17232/* jshint ignore:start */
17233  if(typeof exports === 'object' && typeof module === 'object')
17234    module.exports = URLToolkit;
17235  else if(typeof define === 'function' && define.amd)
17236    define([], function() { return URLToolkit; });
17237  else if(typeof exports === 'object')
17238    exports["URLToolkit"] = URLToolkit;
17239  else
17240    root["URLToolkit"] = URLToolkit;
17241})(this);
17242/* jshint ignore:end */
17243
17244},{}],64:[function(require,module,exports){
17245(function (global){
17246/**
17247 * @file add-text-track-data.js
17248 */
17249'use strict';
17250
17251Object.defineProperty(exports, '__esModule', {
17252  value: true
17253});
17254
17255function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
17256
17257var _globalWindow = require('global/window');
17258
17259var _globalWindow2 = _interopRequireDefault(_globalWindow);
17260
17261var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
17262
17263var _videoJs2 = _interopRequireDefault(_videoJs);
17264
17265/**
17266 * Define properties on a cue for backwards compatability,
17267 * but warn the user that the way that they are using it
17268 * is depricated and will be removed at a later date.
17269 *
17270 * @param {Cue} cue the cue to add the properties on
17271 * @private
17272 */
17273var deprecateOldCue = function deprecateOldCue(cue) {
17274  Object.defineProperties(cue.frame, {
17275    id: {
17276      get: function get() {
17277        _videoJs2['default'].log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
17278        return cue.value.key;
17279      }
17280    },
17281    value: {
17282      get: function get() {
17283        _videoJs2['default'].log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
17284        return cue.value.data;
17285      }
17286    },
17287    privateData: {
17288      get: function get() {
17289        _videoJs2['default'].log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
17290        return cue.value.data;
17291      }
17292    }
17293  });
17294};
17295
17296var durationOfVideo = function durationOfVideo(duration) {
17297  var dur = undefined;
17298
17299  if (isNaN(duration) || Math.abs(duration) === Infinity) {
17300    dur = Number.MAX_VALUE;
17301  } else {
17302    dur = duration;
17303  }
17304  return dur;
17305};
17306/**
17307 * Add text track data to a source handler given the captions and
17308 * metadata from the buffer.
17309 *
17310 * @param {Object} sourceHandler the flash or virtual source buffer
17311 * @param {Array} captionArray an array of caption data
17312 * @param {Array} metadataArray an array of meta data
17313 * @private
17314 */
17315var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
17316  var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue;
17317
17318  if (captionArray) {
17319    captionArray.forEach(function (caption) {
17320      var track = caption.stream;
17321
17322      this.inbandTextTracks_[track].addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
17323    }, sourceHandler);
17324  }
17325
17326  if (metadataArray) {
17327    (function () {
17328      var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
17329
17330      metadataArray.forEach(function (metadata) {
17331        var time = metadata.cueTime + this.timestampOffset;
17332
17333        metadata.frames.forEach(function (frame) {
17334          var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
17335
17336          cue.frame = frame;
17337          cue.value = frame;
17338          deprecateOldCue(cue);
17339
17340          this.metadataTrack_.addCue(cue);
17341        }, this);
17342      }, sourceHandler);
17343
17344      // Updating the metadeta cues so that
17345      // the endTime of each cue is the startTime of the next cue
17346      // the endTime of last cue is the duration of the video
17347      if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
17348        (function () {
17349          var cues = sourceHandler.metadataTrack_.cues;
17350          var cuesArray = [];
17351
17352          // Create a copy of the TextTrackCueList...
17353          // ...disregarding cues with a falsey value
17354          for (var i = 0; i < cues.length; i++) {
17355            if (cues[i]) {
17356              cuesArray.push(cues[i]);
17357            }
17358          }
17359
17360          // Group cues by their startTime value
17361          var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
17362            var timeSlot = obj[cue.startTime] || [];
17363
17364            timeSlot.push(cue);
17365            obj[cue.startTime] = timeSlot;
17366
17367            return obj;
17368          }, {});
17369
17370          // Sort startTimes by ascending order
17371          var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
17372            return Number(a) - Number(b);
17373          });
17374
17375          // Map each cue group's endTime to the next group's startTime
17376          sortedStartTimes.forEach(function (startTime, idx) {
17377            var cueGroup = cuesGroupedByStartTime[startTime];
17378            var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration;
17379
17380            // Map each cue's endTime the next group's startTime
17381            cueGroup.forEach(function (cue) {
17382              cue.endTime = nextTime;
17383            });
17384          });
17385        })();
17386      }
17387    })();
17388  }
17389};
17390
17391exports['default'] = {
17392  addTextTrackData: addTextTrackData,
17393  durationOfVideo: durationOfVideo
17394};
17395module.exports = exports['default'];
17396}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
17397},{"global/window":32}],65:[function(require,module,exports){
17398/**
17399 * @file codec-utils.js
17400 */
17401
17402/**
17403 * Check if a codec string refers to an audio codec.
17404 *
17405 * @param {String} codec codec string to check
17406 * @return {Boolean} if this is an audio codec
17407 * @private
17408 */
17409'use strict';
17410
17411Object.defineProperty(exports, '__esModule', {
17412  value: true
17413});
17414var isAudioCodec = function isAudioCodec(codec) {
17415  return (/mp4a\.\d+.\d+/i.test(codec)
17416  );
17417};
17418
17419/**
17420 * Check if a codec string refers to a video codec.
17421 *
17422 * @param {String} codec codec string to check
17423 * @return {Boolean} if this is a video codec
17424 * @private
17425 */
17426var isVideoCodec = function isVideoCodec(codec) {
17427  return (/avc1\.[\da-f]+/i.test(codec)
17428  );
17429};
17430
17431/**
17432 * Parse a content type header into a type and parameters
17433 * object
17434 *
17435 * @param {String} type the content type header
17436 * @return {Object} the parsed content-type
17437 * @private
17438 */
17439var parseContentType = function parseContentType(type) {
17440  var object = { type: '', parameters: {} };
17441  var parameters = type.trim().split(';');
17442
17443  // first parameter should always be content-type
17444  object.type = parameters.shift().trim();
17445  parameters.forEach(function (parameter) {
17446    var pair = parameter.trim().split('=');
17447
17448    if (pair.length > 1) {
17449      var _name = pair[0].replace(/"/g, '').trim();
17450      var value = pair[1].replace(/"/g, '').trim();
17451
17452      object.parameters[_name] = value;
17453    }
17454  });
17455
17456  return object;
17457};
17458
17459/**
17460 * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
17461 * `avc1.<hhhhhh>`
17462 *
17463 * @param {Array} codecs an array of codec strings to fix
17464 * @return {Array} the translated codec array
17465 * @private
17466 */
17467var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
17468  return codecs.map(function (codec) {
17469    return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
17470      var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
17471      var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
17472
17473      return 'avc1.' + profileHex + '00' + avcLevelHex;
17474    });
17475  });
17476};
17477
17478exports['default'] = {
17479  isAudioCodec: isAudioCodec,
17480  parseContentType: parseContentType,
17481  isVideoCodec: isVideoCodec,
17482  translateLegacyCodecs: translateLegacyCodecs
17483};
17484module.exports = exports['default'];
17485},{}],66:[function(require,module,exports){
17486/**
17487 * @file create-text-tracks-if-necessary.js
17488 */
17489
17490/**
17491 * Create text tracks on video.js if they exist on a segment.
17492 *
17493 * @param {Object} sourceBuffer the VSB or FSB
17494 * @param {Object} mediaSource the HTML or Flash media source
17495 * @param {Object} segment the segment that may contain the text track
17496 * @private
17497 */
17498'use strict';
17499
17500Object.defineProperty(exports, '__esModule', {
17501  value: true
17502});
17503var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
17504  var player = mediaSource.player_;
17505
17506  // create an in-band caption track if one is present in the segment
17507  if (segment.captions && segment.captions.length) {
17508    if (!sourceBuffer.inbandTextTracks_) {
17509      sourceBuffer.inbandTextTracks_ = {};
17510    }
17511
17512    for (var trackId in segment.captionStreams) {
17513      if (!sourceBuffer.inbandTextTracks_[trackId]) {
17514        player.tech_.trigger({ type: 'usage', name: 'hls-608' });
17515        var track = player.textTracks().getTrackById(trackId);
17516
17517        if (track) {
17518          // Resuse an existing track with a CC# id because this was
17519          // very likely created by videojs-contrib-hls from information
17520          // in the m3u8 for us to use
17521          sourceBuffer.inbandTextTracks_[trackId] = track;
17522        } else {
17523          // Otherwise, create a track with the default `CC#` label and
17524          // without a language
17525          sourceBuffer.inbandTextTracks_[trackId] = player.addRemoteTextTrack({
17526            kind: 'captions',
17527            id: trackId,
17528            label: trackId
17529          }, false).track;
17530        }
17531      }
17532    }
17533  }
17534
17535  if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
17536    sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
17537      kind: 'metadata',
17538      label: 'Timed Metadata'
17539    }, false).track;
17540    sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
17541  }
17542};
17543
17544exports['default'] = createTextTracksIfNecessary;
17545module.exports = exports['default'];
17546},{}],67:[function(require,module,exports){
17547/**
17548 * @file flash-constants.js
17549 */
17550/**
17551 * The maximum size in bytes for append operations to the video.js
17552 * SWF. Calling through to Flash blocks and can be expensive so
17553 * we chunk data and pass through 4KB at a time, yielding to the
17554 * browser between chunks. This gives a theoretical maximum rate of
17555 * 1MB/s into Flash. Any higher and we begin to drop frames and UI
17556 * responsiveness suffers.
17557 *
17558 * @private
17559 */
17560"use strict";
17561
17562Object.defineProperty(exports, "__esModule", {
17563  value: true
17564});
17565var flashConstants = {
17566  // times in milliseconds
17567  TIME_BETWEEN_CHUNKS: 1,
17568  BYTES_PER_CHUNK: 1024 * 32
17569};
17570
17571exports["default"] = flashConstants;
17572module.exports = exports["default"];
17573},{}],68:[function(require,module,exports){
17574(function (global){
17575/**
17576 * @file flash-media-source.js
17577 */
17578'use strict';
17579
17580Object.defineProperty(exports, '__esModule', {
17581  value: true
17582});
17583
17584var _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; }; })();
17585
17586var _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); } } };
17587
17588function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
17589
17590function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
17591
17592function _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, { c
17592onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
17593
17594var _globalDocument = require('global/document');
17595
17596var _globalDocument2 = _interopRequireDefault(_globalDocument);
17597
17598var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
17599
17600var _videoJs2 = _interopRequireDefault(_videoJs);
17601
17602var _flashSourceBuffer = require('./flash-source-buffer');
17603
17604var _flashSourceBuffer2 = _interopRequireDefault(_flashSourceBuffer);
17605
17606var _flashConstants = require('./flash-constants');
17607
17608var _flashConstants2 = _interopRequireDefault(_flashConstants);
17609
17610var _codecUtils = require('./codec-utils');
17611
17612/**
17613 * A flash implmentation of HTML MediaSources and a polyfill
17614 * for browsers that don't support native or HTML MediaSources..
17615 *
17616 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
17617 * @class FlashMediaSource
17618 * @extends videojs.EventTarget
17619 */
17620
17621var FlashMediaSource = (function (_videojs$EventTarget) {
17622  _inherits(FlashMediaSource, _videojs$EventTarget);
17623
17624  function FlashMediaSource() {
17625    var _this = this;
17626
17627    _classCallCheck(this, FlashMediaSource);
17628
17629    _get(Object.getPrototypeOf(FlashMediaSource.prototype), 'constructor', this).call(this);
17630    this.sourceBuffers = [];
17631    this.readyState = 'closed';
17632
17633    this.on(['sourceopen', 'webkitsourceopen'], function (event) {
17634      // find the swf where we will push media data
17635      _this.swfObj = _globalDocument2['default'].getElementById(event.swfId);
17636      _this.player_ = (0, _videoJs2['default'])(_this.swfObj.parentNode);
17637      _this.tech_ = _this.swfObj.tech;
17638      _this.readyState = 'open';
17639
17640      _this.tech_.on('seeking', function () {
17641        var i = _this.sourceBuffers.length;
17642
17643        while (i--) {
17644          _this.sourceBuffers[i].abort();
17645        }
17646      });
17647
17648      // trigger load events
17649      if (_this.swfObj) {
17650        _this.swfObj.vjs_load();
17651      }
17652    });
17653  }
17654
17655  /**
17656    * Set or return the presentation duration.
17657    *
17658    * @param {Double} value the duration of the media in seconds
17659    * @param {Double} the current presentation duration
17660    * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
17661    */
17662
17663  /**
17664   * We have this function so that the html and flash interfaces
17665   * are the same.
17666   *
17667   * @private
17668   */
17669
17670  _createClass(FlashMediaSource, [{
17671    key: 'addSeekableRange_',
17672    value: function addSeekableRange_() {}
17673    // intentional no-op
17674
17675    /**
17676     * Create a new flash source buffer and add it to our flash media source.
17677     *
17678     * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
17679     * @param {String} type the content-type of the source
17680     * @return {Object} the flash source buffer
17681     */
17682
17683  }, {
17684    key: 'addSourceBuffer',
17685    value: function addSourceBuffer(type) {
17686      var parsedType = (0, _codecUtils.parseContentType)(type);
17687      var sourceBuffer = undefined;
17688
17689      // if this is an FLV type, we'll push data to flash
17690      if (parsedType.type === 'video/mp2t' || parsedType.type === 'audio/mp2t') {
17691        // Flash source buffers
17692        sourceBuffer = new _flashSourceBuffer2['default'](this);
17693      } else {
17694        throw new Error('NotSupportedError (Video.js)');
17695      }
17696
17697      this.sourceBuffers.push(sourceBuffer);
17698      return sourceBuffer;
17699    }
17700
17701    /**
17702     * Signals the end of the stream.
17703     *
17704     * @link https://w3c.github.io/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
17705     * @param {String=} error Signals that a playback error
17706     * has occurred. If specified, it must be either "network" or
17707     * "decode".
17708     */
17709  }, {
17710    key: 'endOfStream',
17711    value: function endOfStream(error) {
17712      if (error === 'network') {
17713        // MEDIA_ERR_NETWORK
17714        this.tech_.error(2);
17715      } else if (error === 'decode') {
17716        // MEDIA_ERR_DECODE
17717        this.tech_.error(3);
17718      }
17719      if (this.readyState !== 'ended') {
17720        this.readyState = 'ended';
17721        this.swfObj.vjs_endOfStream();
17722      }
17723    }
17724  }]);
17725
17726  return FlashMediaSource;
17727})(_videoJs2['default'].EventTarget);
17728
17729exports['default'] = FlashMediaSource;
17730try {
17731  Object.defineProperty(FlashMediaSource.prototype, 'duration', {
17732    /**
17733     * Return the presentation duration.
17734     *
17735     * @return {Double} the duration of the media in seconds
17736     * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
17737     */
17738    get: function get() {
17739      if (!this.swfObj) {
17740        return NaN;
17741      }
17742      // get the current duration from the SWF
17743      return this.swfObj.vjs_getProperty('duration');
17744    },
17745    /**
17746     * Set the presentation duration.
17747     *
17748     * @param {Double} value the duration of the media in seconds
17749     * @return {Double} the duration of the media in seconds
17750     * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
17751     */
17752    set: function set(value) {
17753      var i = undefined;
17754      var oldDuration = this.swfObj.vjs_getProperty('duration');
17755
17756      this.swfObj.vjs_setProperty('duration', value);
17757
17758      if (value < oldDuration) {
17759        // In MSE, this triggers the range removal algorithm which causes
17760        // an update to occur
17761        for (i = 0; i < this.sourceBuffers.length; i++) {
17762          this.sourceBuffers[i].remove(value, oldDuration);
17763        }
17764      }
17765
17766      return value;
17767    }
17768  });
17769} catch (e) {
17770  // IE8 throws if defineProperty is called on a non-DOM node. We
17771  // don't support IE8 but we shouldn't throw an error if loaded
17772  // there.
17773  FlashMediaSource.prototype.duration = NaN;
17774}
17775
17776for (var property in _flashConstants2['default']) {
17777  FlashMediaSource[property] = _flashConstants2['default'][property];
17778}
17779module.exports = exports['default'];
17780}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
17781},{"./codec-utils":65,"./flash-constants":67,"./flash-source-buffer":69,"global/document":31}],69:[function(require,module,exports){
17782(function (global){
17783/**
17784 * @file flash-source-buffer.js
17785 */
17786'use strict';
17787
17788Object.defineProperty(exports, '__esModule', {
17789  value: true
17790});
17791
17792var _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; }; })();
17793
17794var _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); } } };
17795
17796function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
17797
17798function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
17799
17800function _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, { c
17800onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
17801
17802var _globalWindow = require('global/window');
17803
17804var _globalWindow2 = _interopRequireDefault(_globalWindow);
17805
17806var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
17807
17808var _videoJs2 = _interopRequireDefault(_videoJs);
17809
17810var _muxJsLibFlv = require('mux.js/lib/flv');
17811
17812var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
17813
17814var _removeCuesFromTrack = require('./remove-cues-from-track');
17815
17816var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
17817
17818var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
17819
17820var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
17821
17822var _addTextTrackData = require('./add-text-track-data');
17823
17824var _flashTransmuxerWorker = require('./flash-transmuxer-worker');
17825
17826var _flashTransmuxerWorker2 = _interopRequireDefault(_flashTransmuxerWorker);
17827
17828var _webworkify = require('webworkify');
17829
17830var _webworkify2 = _interopRequireDefault(_webworkify);
17831
17832var _flashConstants = require('./flash-constants');
17833
17834var _flashConstants2 = _interopRequireDefault(_flashConstants);
17835
17836/**
17837 * A wrapper around the setTimeout function that uses
17838 * the flash constant time between ticks value.
17839 *
17840 * @param {Function} func the function callback to run
17841 * @private
17842 */
17843var scheduleTick = function scheduleTick(func) {
17844  // Chrome doesn't invoke requestAnimationFrame callbacks
17845  // in background tabs, so use setTimeout.
17846  _globalWindow2['default'].setTimeout(func, _flashConstants2['default'].TIME_BETWEEN_CHUNKS);
17847};
17848
17849/**
17850 * Generates a random string of max length 6
17851 *
17852 * @return {String} the randomly generated string
17853 * @function generateRandomString
17854 * @private
17855 */
17856var generateRandomString = function generateRandomString() {
17857  return Math.random().toString(36).slice(2, 8);
17858};
17859
17860/**
17861 * Round a number to a specified number of places much like
17862 * toFixed but return a number instead of a string representation.
17863 *
17864 * @param {Number} num A number
17865 * @param {Number} places The number of decimal places which to
17866 * round
17867 * @private
17868 */
17869var toDecimalPlaces = function toDecimalPlaces(num, places) {
17870  if (typeof places !== 'number' || places < 0) {
17871    places = 0;
17872  }
17873
17874  var scale = Math.pow(10, places);
17875
17876  return Math.round(num * scale) / scale;
17877};
17878
17879/**
17880 * A SourceBuffer implementation for Flash rather than HTML.
17881 *
17882 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
17883 * @param {Object} mediaSource the flash media source
17884 * @class FlashSourceBuffer
17885 * @extends videojs.EventTarget
17886 */
17887
17888var FlashSourceBuffer = (function (_videojs$EventTarget) {
17889  _inherits(FlashSourceBuffer, _videojs$EventTarget);
17890
17891  function FlashSourceBuffer(mediaSource) {
17892    var _this = this;
17893
17894    _classCallCheck(this, FlashSourceBuffer);
17895
17896    _get(Object.getPrototypeOf(FlashSourceBuffer.prototype), 'constructor', this).call(this);
17897    var encodedHeader = undefined;
17898
17899    // Start off using the globally defined value but refine
17900    // as we append data into flash
17901    this.chunkSize_ = _flashConstants2['default'].BYTES_PER_CHUNK;
17902
17903    // byte arrays queued to be appended
17904    this.buffer_ = [];
17905
17906    // the total number of queued bytes
17907    this.bufferSize_ = 0;
17908
17909    // to be able to determine the correct position to seek to, we
17910    // need to retain information about the mapping between the
17911    // media timeline and PTS values
17912    this.basePtsOffset_ = NaN;
17913
17914    this.mediaSource_ = mediaSource;
17915
17916    this.audioBufferEnd_ = NaN;
17917    this.videoBufferEnd_ = NaN;
17918
17919    // indicates whether the asynchronous continuation of an operation
17920    // is still being processed
17921    // see https://w3c.github.io/media-source/#widl-SourceBuffer-updating
17922    this.updating = false;
17923    this.timestampOffset_ = 0;
17924
17925    encodedHeader = _globalWindow2['default'].btoa(String.fromCharCode.apply(null, Array.prototype.slice.call(_muxJsLibFlv2['default'].getFlvHeader())));
17926
17927    // create function names with added randomness for the global callbacks flash will use
17928    // to get data from javascript into the swf. Random strings are added as a safety
17929    // measure for pages with multiple players since these functions will be global
17930    // instead of per instance. When making a call to the swf, the browser generates a
17931    // try catch code snippet, but just takes the function name and writes out an unquoted
17932    // call to that function. If the player id has any special characters, this will result
17933    // in an error, so safePlayerId replaces all special characters to '_'
17934    var safePlayerId = this.mediaSource_.player_.id().replace(/[^a-zA-Z0-9]/g, '_');
17935
17936    this.flashEncodedHeaderName_ = 'vjs_flashEncodedHeader_' + s
17936afePlayerId + generateRandomString();
17937    this.flashEncodedDataName_ = 'vjs_flashEncodedData_' + safePlayerId + generateRandomString();
17938
17939    _globalWindow2['default'][this.flashEncodedHeaderName_] = function () {
17940      delete _globalWindow2['default'][_this.flashEncodedHeaderName_];
17941      return encodedHeader;
17942    };
17943
17944    this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedHeaderName_);
17945
17946    this.transmuxer_ = (0, _webworkify2['default'])(_flashTransmuxerWorker2['default']);
17947    this.transmuxer_.postMessage({ action: 'init', options: {} });
17948    this.transmuxer_.onmessage = function (event) {
17949      if (event.data.action === 'data') {
17950        _this.receiveBuffer_(event.data.segment);
17951      }
17952    };
17953
17954    this.one('updateend', function () {
17955      _this.mediaSource_.tech_.trigger('loadedmetadata');
17956    });
17957
17958    Object.defineProperty(this, 'timestampOffset', {
17959      get: function get() {
17960        return this.timestampOffset_;
17961      },
17962      set: function set(val) {
17963        if (typeof val === 'number' && val >= 0) {
17964          this.timestampOffset_ = val;
17965          // We have to tell flash to expect a discontinuity
17966          this.mediaSource_.swfObj.vjs_discontinuity();
17967          // the media <-> PTS mapping must be re-established after
17968          // the discontinuity
17969          this.basePtsOffset_ = NaN;
17970          this.audioBufferEnd_ = NaN;
17971          this.videoBufferEnd_ = NaN;
17972
17973          this.transmuxer_.postMessage({ action: 'reset' });
17974        }
17975      }
17976    });
17977
17978    Object.defineProperty(this, 'buffered', {
17979      get: function get() {
17980        if (!this.mediaSource_ || !this.mediaSource_.swfObj || !('vjs_getProperty' in this.mediaSource_.swfObj)) {
17981          return _videoJs2['default'].createTimeRange();
17982        }
17983
17984        var buffered = this.mediaSource_.swfObj.vjs_getProperty('buffered');
17985
17986        if (buffered && buffered.length) {
17987          buffered[0][0] = toDecimalPlaces(buffered[0][0], 3);
17988          buffered[0][1] = toDecimalPlaces(buffered[0][1], 3);
17989        }
17990        return _videoJs2['default'].createTimeRanges(buffered);
17991      }
17992    });
17993
17994    // On a seek we remove all text track data since flash has no concept
17995    // of a buffered-range and everything else is reset on seek
17996    this.mediaSource_.player_.on('seeked', function () {
17997      (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.metadataTrack_);
17998      if (_this.inbandTextTracks_) {
17999        for (var track in _this.inbandTextTracks_) {
18000          (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.inbandTextTracks_[track]);
18001        }
18002      }
18003    });
18004
18005    var onHlsReset = this.onHlsReset_.bind(this);
18006
18007    this.mediaSource_.player_.tech_.on('hls-reset', onHlsReset);
18008
18009    this.mediaSource_.player_.tech_.hls.on('dispose', function () {
18010      _this.transmuxer_.terminate();
18011      _this.mediaSource_.player_.tech_.off('hls-reset', onHlsReset);
18012    });
18013  }
18014
18015  /**
18016   * Append bytes to the sourcebuffers buffer, in this case we
18017   * have to append it to swf object.
18018   *
18019   * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
18020   * @param {Array} bytes
18021   */
18022
18023  _createClass(FlashSourceBuffer, [{
18024    key: 'appendBuffer',
18025    value: function appendBuffer(bytes) {
18026      var error = undefined;
18027
18028      if (this.updating) {
18029        error = new Error('SourceBuffer.append() cannot be called ' + 'while an update is in progress');
18030        error.name = 'InvalidStateError';
18031        error.code = 11;
18032        throw error;
18033      }
18034      this.updating = true;
18035      this.mediaSource_.readyState = 'open';
18036      this.trigger({ type: 'update' });
18037
18038      this.transmuxer_.postMessage({
18039        action: 'push',
18040        data: bytes.buffer,
18041        byteOffset: bytes.byteOffset,
18042        byteLength: bytes.byteLength
18043      }, [bytes.buffer]);
18044      this.transmuxer_.postMessage({ action: 'flush' });
18045    }
18046
18047    /**
18048     * Reset the parser and remove any data queued to be sent to the SWF.
18049     *
18050     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
18051     */
18052  }, {
18053    key: 'abort',
18054    value: function abort() {
18055      this.buffer_ = [];
18056      this.bufferSize_ = 0;
18057      this.mediaSource_.swfObj.vjs_abort();
18058
18059      // report any outstanding updates have ended
18060      if (this.updating) {
18061        this.updating = false;
18062        this.trigger({ type: 'updateend' });
18063      }
18064    }
18065
18066    /**
18067     * Flash cannot remove ranges already buffered in the NetStream
18068     * but seeking clears the buffer entirely. For most purposes,
18069     * having this operation act as a no-op is acceptable.
18070     *
18071     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
18072     * @param {Double} start start of the section to remove
18073     * @param {Double} end end of the section to remove
18074     */
18075  }, {
18076    key: 'remove',
18077    value: function remove(start, end) {
18078      (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
18079      if (this.inbandTextTracks_) {
18080        for (var track in this.inbandTextTracks_) {
18081          (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]);
18082        }
18083      }
18084      this.trigger({ type: 'update' });
18085      this.trigger({ type: 'updateend' });
18086    }
18087
18088    /**
18089     * Receive a buffer from the flv.
18090     *
18091     * @param {Object} segment
18092     * @private
18093     */
18094  }, {
18095    key: 'receiveBuffer_',
18096    value: function receiveBuffer_(segment) {
18097      var _this2 = this;
18098
18099      // create an in-band caption track if one is present in the segment
18100      (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
18101      (0, _addTextTrackData.addTextTrackData)(this, segment.captions, segment.metadata);
18102
18103      // Do this asynchronously since convertTagsToData_ can be time consuming
18104      scheduleTick(function () {
18105        var flvBytes = _this2.convertTagsToData_(segment);
18106
18107        if (_this2.buffer_.length === 0) {
18108          scheduleTick(_this2.processBuffer_.bind(_this2));
18109        }
18110
18111        if (flvBytes) {
18112          _this2.buffer_.push(flvBytes);
18113          _this2.bufferSize_ += flvBytes.byteLength;
18114        }
18115      });
18116    }
18117
18118    /**
18119     * Append a portion of the current buffer to the SWF.
18120     *
18121     * @private
18122     */
18123  }, {
18124    key: 'processBuffer_',
18125    value: function processBuffer_() {
18126      var _this3 = this;
18127
18128      var chunkSize = _flashConstants2['default'].BYTES_PER_CHUNK;
18129
18130      if (!this.buffer_.length) {
18131        if (this.updating !== false) {
18132          this.updating = false;
18133          this.trigger({ type: 'updateend' });
18134        }
18135        // do nothing if the buffer is empty
18136        return;
18137      }
18138
18139      // concatenate appends up to the max append size
18140      var chunk = this.buffer_[0].subarray(0, chunkSize);
18141
18142      // requeue any bytes that won't make it this round
18143      if (chunk.byteLength < chunkSize || this.buffer_[0].byteLength === chunkSize) {
18144        this.buffer_.shift();
18145      } else {
18146        this.buffer_[0] = this.buffer_[0].subarray(chunkSize);
18147      }
18148
18149      this.bufferSize_ -= chunk.byteLength;
18150
18151      // base64 encode the bytes
18152      var binary = [];
18153      var length = chunk.byteLength;
18154
18155      for (var i = 0; i < length; i++) {
18156        binary.push(String.fromCharCode(chunk[i]));
18157      }
18158      var b64str = _globalWindow2['default'].btoa(binary.join(''));
18159
18160      _globalWindow2['default'][this.flashEncodedDataName_] = function () {
18161        // schedule another processBuffer to process any left over data or to
18162        // trigger updateend
18163        scheduleTick(_this3.processBuffer_.bind(_this3));
18164        delete _globalWindow2['default'][_this3.flashEncodedDataName_];
18165        return b64str;
18166      };
18167
18168      // Notify the swf that segment data is ready to be appended
18169      this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedDataName_);
18170    }
18171
18172    /**
18173     * Turns an array of flv tags into a Uint8Array representing the
18174     * flv data. Also removes any tags that are before the current
18175     * time so that playback begins at or slightly after the right
18176     * place on a seek
18177     *
18178     * @private
18179     * @param {Object} segmentData object of segment data
18180     */
18181  }, {
18182    key: 'convertTagsToData_',
18183    value: function convertTagsToData_(segmentData) {
18184      var segmentByteLength = 0;
18185      var tech = this.mediaSource_.tech_;
18186      var videoTargetPts = 0;
18187      var segment = undefined;
18188      var videoTags = segmentData.tags.videoTags;
18189      var audioTags = segmentData.tags.audioTags;
18190
18191      // Establish the media timeline to PTS translation if we don't
18192      // have one already
18193      if (isNaN(this.basePtsOffset_) && (videoTags.length || audioTags.length)) {
18194        // We know there is at least one video or audio tag, but since we may 
18194not have both,
18195        // we use pts: Infinity for the missing tag. The will force the following Math.min
18196        // call will to use the proper pts value since it will always be less than Infinity
18197        var firstVideoTag = videoTags[0] || { pts: Infinity };
18198        var firstAudioTag = audioTags[0] || { pts: Infinity };
18199
18200        this.basePtsOffset_ = Math.min(firstAudioTag.pts, firstVideoTag.pts);
18201      }
18202
18203      if (tech.seeking()) {
18204        // Do not use previously saved buffer end values while seeking since buffer
18205        // is cleared on all seeks
18206        this.videoBufferEnd_ = NaN;
18207        this.audioBufferEnd_ = NaN;
18208      }
18209
18210      if (isNaN(this.videoBufferEnd_)) {
18211        if (tech.buffered().length) {
18212          videoTargetPts = tech.buffered().end(0) - this.timestampOffset;
18213        }
18214
18215        // Trim to currentTime if seeking
18216        if (tech.seeking()) {
18217          videoTargetPts = Math.max(videoTargetPts, tech.currentTime() - this.timestampOffset);
18218        }
18219
18220        // PTS values are represented in milliseconds
18221        videoTargetPts *= 1e3;
18222        videoTargetPts += this.basePtsOffset_;
18223      } else {
18224        // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
18225        // could append an overlapping segment, in which case there is a high likelyhood
18226        // a tag could have a matching pts to videoBufferEnd_, which would cause
18227        // that tag to get appended by the tag.pts >= targetPts check below even though it
18228        // is a duplicate of what was previously appended
18229        videoTargetPts = this.videoBufferEnd_ + 0.1;
18230      }
18231
18232      // filter complete GOPs with a presentation time less than the seek target/end of buffer
18233      var currentIndex = videoTags.length;
18234
18235      // if the last tag is beyond videoTargetPts, then do not search the list for a GOP
18236      // since our videoTargetPts lies in a future segment
18237      if (currentIndex && videoTags[currentIndex - 1].pts >= videoTargetPts) {
18238        // Start by walking backwards from the end of the list until we reach a tag that
18239        // is equal to or less than videoTargetPts
18240        while (--currentIndex) {
18241          var currentTag = videoTags[currentIndex];
18242
18243          if (currentTag.pts > videoTargetPts) {
18244            continue;
18245          }
18246
18247          // if we see a keyFrame or metadata tag once we've gone below videoTargetPts,
18248          // exit the loop as this is the start of the GOP that we want to append
18249          if (currentTag.keyFrame || currentTag.metaDataTag) {
18250            break;
18251          }
18252        }
18253
18254        // We need to check if there are any metadata tags that come before currentIndex
18255        // as those will be metadata tags associated with the GOP we are appending
18256        // There could be 0 to 2 metadata tags that come before the currentIndex depending
18257        // on what videoTargetPts is and whether the transmuxer prepended metadata tags to this
18258        // key frame
18259        while (currentIndex) {
18260          var nextTag = videoTags[currentIndex - 1];
18261
18262          if (!nextTag.metaDataTag) {
18263            break;
18264          }
18265
18266          currentIndex--;
18267        }
18268      }
18269
18270      var filteredVideoTags = videoTags.slice(currentIndex);
18271
18272      var audioTargetPts = undefined;
18273
18274      if (isNaN(this.audioBufferEnd_)) {
18275        audioTargetPts = videoTargetPts;
18276      } else {
18277        // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
18278        // could append an overlapping segment, in which case there is a high likelyhood
18279        // a tag could have a matching pts to videoBufferEnd_, which would cause
18280        // that tag to get appended by the tag.pts >= targetPts check below even though it
18281        // is a duplicate of what was previously appended
18282        audioTargetPts = this.audioBufferEnd_ + 0.1;
18283      }
18284
18285      if (filteredVideoTags.length) {
18286        // If targetPts intersects a GOP and we appended the tags for the GOP that came
18287        // before targetPts, we want to make sure to trim audio tags at the pts
18288        // of the first video tag to avoid brief moments of silence
18289        audioTargetPts = Math.min(audioTargetPts, filteredVideoTags[0].pts);
18290      }
18291
18292      // skip tags with a presentation time less than the seek target/end of buffer
18293      currentIndex = 0;
18294
18295      while (currentIndex < audioTags.length) {
18296        if (audioTags[currentIndex].pts >= audioTargetPts) {
18297          break;
18298        }
18299
18300        currentIndex++;
18301      }
18302
18303      var filteredAudioTags = audioTags.slice(currentIndex);
18304
18305      // update the audio and video buffer ends
18306      if (filteredAudioTags.length) {
18307        this.audioBufferEnd_ = filteredAudioTags[filteredAudioTags.length - 1].pts;
18308      }
18309      if (filteredVideoTags.length) {
18310        this.videoBufferEnd_ = filteredVideoTags[filteredVideoTags.length - 1].pts;
18311      }
18312
18313      var tags = this.getOrderedTags_(filteredVideoTags, filteredAudioTags);
18314
18315      if (tags.length === 0) {
18316        return;
18317      }
18318
18319      // If we are appending data that comes before our target pts, we want to tell
18320      // the swf to adjust its notion of current time to account for the extra tags
18321      // we are appending to complete the GOP that intersects with targetPts
18322      if (tags[0].pts < videoTargetPts && tech.seeking()) {
18323        var fudgeFactor = 1 / 30;
18324        var currentTime = tech.currentTime();
18325        var diff = (videoTargetPts - tags[0].pts) / 1e3;
18326        var adjustedTime = currentTime - diff;
18327
18328        if (adjustedTime < fudgeFactor) {
18329          adjustedTime = 0;
18330        }
18331
18332        try {
18333          this.mediaSource_.swfObj.vjs_adjustCurrentTime(adjustedTime);
18334        } catch (e) {
18335          // no-op for backwards compatability of swf. If adjustCurrentTime fails,
18336          // the swf may incorrectly report currentTime and buffered ranges
18337          // but should not affect playback over than the time displayed on the
18338          // progress bar is inaccurate
18339        }
18340      }
18341
18342      // concatenate the bytes into a single segment
18343      for (var i = 0; i < tags.length; i++) {
18344        segmentByteLength += tags[i].bytes.byteLength;
18345      }
18346      segment = new Uint8Array(segmentByteLength);
18347      for (var i = 0, j = 0; i < tags.length; i++) {
18348        segment.set(tags[i].bytes, j);
18349        j += tags[i].bytes.byteLength;
18350      }
18351
18352      return segment;
18353    }
18354
18355    /**
18356     * Assemble the FLV tags in decoder order.
18357     *
18358     * @private
18359     * @param {Array} videoTags list of video tags
18360     * @param {Array} audioTags list of audio tags
18361     */
18362  }, {
18363    key: 'getOrderedTags_',
18364    value: function getOrderedTags_(videoTags, audioTags) {
18365      var tag = undefined;
18366      var tags = [];
18367
18368      while (videoTags.length || audioTags.length) {
18369        if (!videoTags.length) {
18370          // only audio tags remain
18371          tag = audioTags.shift();
18372        } else if (!audioTags.length) {
18373          // only video tags remain
18374          tag = videoTags.shift();
18375        } else if (audioTags[0].dts < videoTags[0].dts) {
18376          // audio should be decoded next
18377          tag = audioTags.shift();
18378        } else {
18379          // video should be decoded next
18380          tag = videoTags.shift();
18381        }
18382
18383        tags.push(tag);
18384      }
18385
18386      return tags;
18387    }
18388  }, {
18389    key: 'onHlsReset_',
18390    value: function onHlsReset_() {
18391      this.transmuxer_.postMessage({ action: 'resetCaptions' });
18392    }
18393  }]);
18394
18395  return FlashSourceBuffer;
18396})(_videoJs2['default'].EventTarget);
18397
18398exports['default'] = FlashSourceBuffer;
18399module.exports = exports['default'];
18400}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
18401},{"./add-text-track-data":64,"./create-text-tracks-if-necessary":66,"./flash-constants":67,"./flash-transmuxer-worker":70,"./remove-cues-from-track":72,"global/window":32,"mux.js/lib/flv":46,"webworkify":76}],70:[function(require,module,exports){
18402/**
18403 * @file flash-transmuxer-worker.js
18404 */
18405'use strict';
18406
18407Object.defineProperty(exports, '__esModule', {
18408  value: true
18409});
18410
18411var _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; }; })();
18412
18413function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
18414
18415function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
18416
18417var _globalWindow = require('global/window');
18418
18419var _globalWindow2 = _interopRequireDefault(_globalWindow);
18420
18421var _muxJsLibFlv = require('mux.js/lib/flv');
18422
18423var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
18424
18425/**
18426 * Re-emits transmuxer events by converting them into messages to the
18427 * world outside the worker.
18428 *
18429 * @param {Object} transmuxer the transmuxer to wire events on
18430 * @private
18431 */
18432var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
18433  transmuxer.on('data', function (segment) {
18434    _globalWindow2['default'].postMessage({
18435      action: 'data',
18436      segment: segment
18437    });
18438  });
18439
18440  transmuxer.on('done', function (data) {
18441    _globalWindow2['default'].postMessage({ action: 'done' });
18442  });
18443};
18444
18445/**
18446 * All incoming messages route through this hash. If no function exists
18447 * to handle an incoming message, then we ignore the message.
18448 *
18449 * @class MessageHandlers
18450 * @param {Object} options the options to initialize with
18451 */
18452
18453var MessageHandlers = (function () {
18454  function MessageHandlers(options) {
18455    _classCallCheck(this, MessageHandlers);
18456
18457    this.options = options || {};
18458    this.init();
18459  }
18460
18461  /**
18462   * Our web wroker interface so that things can talk to mux.js
18463   * that will be running in a web worker. The scope is passed to this by
18464   * webworkify.
18465   *
18466   * @param {Object} self the scope for the web worker
18467   */
18468
18469  /**
18470   * initialize our web worker and wire all the events.
18471   */
18472
18473  _createClass(MessageHandlers, [{
18474    key: 'init',
18475    value: function init() {
18476      if (this.transmuxer) {
18477        this.transmuxer.dispose();
18478      }
18479      this.transmuxer = new _muxJsLibFlv2['default'].Transmuxer(this.options);
18480      wireTransmuxerEvents(this.transmuxer);
18481    }
18482
18483    /**
18484     * Adds data (a ts segment) to the start of the transmuxer pipeline for
18485     * processing.
18486     *
18487     * @param {ArrayBuffer} data data to push into the muxer
18488     */
18489  }, {
18490    key: 'push',
18491    value: function push(data) {
18492      // Cast array buffer to correct type for transmuxer
18493      var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
18494
18495      this.transmuxer.push(segment);
18496    }
18497
18498    /**
18499     * Recreate the transmuxer so that the next segment added via `push`
18500     * start with a fresh transmuxer.
18501     */
18502  }, {
18503    key: 'reset',
18504    value: function reset() {
18505      this.init();
18506    }
18507
18508    /**
18509     * Forces the pipeline to finish processing the last segment and emit its
18510     * results.
18511     */
18512  }, {
18513    key: 'flush',
18514    value: function flush() {
18515      this.transmuxer.flush();
18516    }
18517  }, {
18518    key: 'resetCaptions',
18519    value: function resetCaptions() {
18520      this.transmuxer.resetCaptions();
18521    }
18522  }]);
18523
18524  return MessageHandlers;
18525})();
18526
18527var FlashTransmuxerWorker = function FlashTransmuxerWorker(self) {
18528  self.onmessage = function (event) {
18529    if (event.data.action === 'init' && event.data.options) {
18530      this.messageHandlers = new MessageHandlers(event.data.options);
18531      return;
18532    }
18533
18534    if (!this.messageHandlers) {
18535      this.messageHandlers = new MessageHandlers();
18536    }
18537
18538    if (event.data && event.data.action && event.data.action !== 'init') {
18539      if (this.messageHandlers[event.data.action]) {
18540        this.messageHandlers[event.data.action](event.data);
18541      }
18542    }
18543  };
18544};
18545
18546exports['default'] = function (self) {
18547  return new FlashTransmuxerWorker(self);
18548};
18549
18550module.exports = exports['default'];
18551},{"global/window":32,"mux.js/lib/flv":46}],71:[function(require,module,exports){
18552(function (global){
18553/**
18554 * @file html-media-source.js
18555 */
18556'use strict';
18557
18558Object.defineProperty(exports, '__esModule', {
18559  value: true
18560});
18561
18562var _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; }; })();
18563
18564var _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); } } };
18565
18566function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
18567
18568function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
18569
18570function _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, { c
18570onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
18571
18572var _globalWindow = require('global/window');
18573
18574var _globalWindow2 = _interopRequireDefault(_globalWindow);
18575
18576var _globalDocument = require('global/document');
18577
18578var _globalDocument2 = _interopRequireDefault(_globalDocument);
18579
18580var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
18581
18582var _videoJs2 = _interopRequireDefault(_videoJs);
18583
18584var _virtualSourceBuffer = require('./virtual-source-buffer');
18585
18586var _virtualSourceBuffer2 = _interopRequireDefault(_virtualSourceBuffer);
18587
18588var _addTextTrackData = require('./add-text-track-data');
18589
18590var _codecUtils = require('./codec-utils');
18591
18592/**
18593 * Our MediaSource implementation in HTML, mimics native
18594 * MediaSource where/if possible.
18595 *
18596 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
18597 * @class HtmlMediaSource
18598 * @extends videojs.EventTarget
18599 */
18600
18601var HtmlMediaSource = (function (_videojs$EventTarget) {
18602  _inherits(HtmlMediaSource, _videojs$EventTarget);
18603
18604  function HtmlMediaSource() {
18605    var _this = this;
18606
18607    _classCallCheck(this, HtmlMediaSource);
18608
18609    _get(Object.getPrototypeOf(HtmlMediaSource.prototype), 'constructor', this).call(this);
18610    var property = undefined;
18611
18612    this.nativeMediaSource_ = new _globalWindow2['default'].MediaSource();
18613    // delegate to the native MediaSource's methods by default
18614    for (property in this.nativeMediaSource_) {
18615      if (!(property in HtmlMediaSource.prototype) && typeof this.nativeMediaSource_[property] === 'function') {
18616        this[property] = this.nativeMediaSource_[property].bind(this.nativeMediaSource_);
18617      }
18618    }
18619
18620    // emulate `duration` and `seekable` until seeking can be
18621    // handled uniformly for live streams
18622    // see https://github.com/w3c/media-source/issues/5
18623    this.duration_ = NaN;
18624    Object.defineProperty(this, 'duration', {
18625      get: function get() {
18626        if (this.duration_ === Infinity) {
18627          return this.duration_;
18628        }
18629        return this.nativeMediaSource_.duration;
18630      },
18631      set: function set(duration) {
18632        this.duration_ = duration;
18633        if (duration !== Infinity) {
18634          this.nativeMediaSource_.duration = duration;
18635          return;
18636        }
18637      }
18638    });
18639    Object.defineProperty(this, 'seekable', {
18640      get: function get() {
18641        if (this.duration_ === Infinity) {
18642          return _videoJs2['default'].createTimeRanges([[0, this.nativeMediaSource_.duration]]);
18643        }
18644        return this.nativeMediaSource_.seekable;
18645      }
18646    });
18647
18648    Object.defineProperty(this, 'readyState', {
18649      get: function get() {
18650        return this.nativeMediaSource_.readyState;
18651      }
18652    });
18653
18654    Object.defineProperty(this, 'activeSourceBuffers', {
18655      get: function get() {
18656        return this.activeSourceBuffers_;
18657      }
18658    });
18659
18660    // the list of virtual and native SourceBuffers created by this
18661    // MediaSource
18662    this.sourceBuffers = [];
18663
18664    this.activeSourceBuffers_ = [];
18665
18666    /**
18667     * update the list of active source buffers based upon various
18668     * imformation from HLS and video.js
18669     *
18670     * @private
18671     */
18672    this.updateActiveSourceBuffers_ = function () {
18673      // Retain the reference but empty the array
18674      _this.activeSourceBuffers_.length = 0;
18675
18676      // If there is only one source buffer, then it will always be active and audio will
18677      // be disabled based on the codec of the source buffer
18678      if (_this.sourceBuffers.length === 1) {
18679        var sourceBuffer = _this.sourceBuffers[0];
18680
18681        sourceBuffer.appendAudioInitSegment_ = true;
18682        sourceBuffer.audioDisabled_ = !sourceBuffer.audioCodec_;
18683        _this.activeSourceBuffers_.push(sourceBuffer);
18684        return;
18685      }
18686
18687      // There are 2 source buffers, a combined (possibly video only) source buffer and
18688      // and an audio only source buffer.
18689      // By default, the audio in the combined virtual source buffer is enabled
18690      // and the audio-only source buffer (if it exists) is disabled.
18691      var disableCombined = false;
18692      var disableAudioOnly = true;
18693
18694      // TODO: maybe we can store the sourcebuffers on the track objects?
18695      // safari may do something like this
18696      for (var i = 0; i < _this.player_.audioTracks().length; i++) {
18697        var track = _this.player_.audioTracks()[i];
18698
18699        if (track.enabled && track.kind !== 'main') {
18700          // The enabled track is an alternate audio track so disable the audio in
18701          // the combined source buffer and enable the audio-only source buffer.
18702          disableCombined = true;
18703          disableAudioOnly = false;
18704          break;
18705        }
18706      }
18707
18708      _this.sourceBuffers.forEach(function (sourceBuffer) {
18709        /* eslinst-disable */
18710        // TODO once codecs are required, we can switch to using the codecs to determine
18711        //      what stream is the video stream, rather than relying on videoTracks
18712        /* eslinst-enable */
18713
18714        sourceBuffer.appendAudioInitSegment_ = true;
18715
18716        if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
18717          // combined
18718          sourceBuffer.audioDisabled_ = disableCombined;
18719        } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
18720          // If the "combined" source buffer is video only, then we do not want
18721          // disable the audio-only source buffer (this is mostly for demuxed
18722          // audio and video hls)
18723          sourceBuffer.audioDisabled_ = true;
18724          disableAudioOnly = false;
18725        } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
18726          // audio only
18727          sourceBuffer.audioDisabled_ = disableAudioOnly;
18728          if (disableAudioOnly) {
18729            return;
18730          }
18731        }
18732
18733        _this.activeSourceBuffers_.push(sourceBuffer);
18734      });
18735    };
18736
18737    this.onPlayerMediachange_ = function () {
18738      _this.sourceBuffers.forEach(function (sourceBuffer) {
18739        sourceBuffer.appendAudioInitSegment_ = true;
18740      });
18741    };
18742
18743    this.onHlsReset_ = function () {
18744      _this.sourceBuffers.forEach(function (sourceBuffer) {
18745        if (sourceBuffer.transmuxer_) {
18746          sourceBuffer.transmuxer_.postMessage({ action: 'resetCaptions' });
18747        }
18748      });
18749    };
18750
18751    // Re-emit MediaSource events on the polyfill
18752    ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
18753      this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
18754    }, this);
18755
18756    // capture the associated player when the MediaSource is
18757    // successfully attached
18758    this.on('sourceopen', function (event) {
18759      // Get the player this MediaSource is attached to
18760      var video = _globalDocument2['default'].querySelector('[src="' + _this.url_ + '"]');
18761
18762      if (!video) {
18763        return;
18764      }
18765
18766      _this.player_ = (0, _videoJs2['default'])(video.parentNode);
18767
18768      _this.player_.tech_.on('hls-reset', _this.onHlsReset_);
18769
18770      if (_this.player_.audioTracks && _this.player_.audioTracks()) {
18771        _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
18772        _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
18773        _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
18774      }
18775
18776      _this.player_.on('mediachange', _this.onPlayerMediachange_);
18777    });
18778
18779    this.on('sourceended', function (event) {
18780      var duration = (0, _addTextTrackData.durationOfVideo)(_this.duration);
18781
18782      for (var i = 0; i < _this.sourceBuffers.length; i++) {
18783        var sourcebuffer = _this.sourceBuffers[i];
18784        var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
18785
18786        if (cues && cues.length) {
18787          cues[cues.length - 1].endTime = duration;
18788        }
18789      }
18790    });
18791
18792    // explicitly terminate any WebWorkers that were created
18793    // by SourceHandlers
18794    this.on('sourceclose', function (event) {
18795      this.sourceBuffers.forEach(function (sourceBuffer) {
18796        if (sourceBuffer.transmuxer_) {
18797          sourceBuffer.transmuxer_.terminate();
18798        }
18799      });
18800
18801      this.sourceBuffers.length = 0;
18802      if (!this.player_) {
18803        return;
18804      }
18805
18806      if (this.player_.audioTracks && this.player_.audioTracks()) {
18807        this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
18808        this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
18809        this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
18810      }
18811
18812      // We can only change this if the player hasn't been disposed of yet
18813      // because `off` eventually tries to use the el_ property. If it has
18814      // been disposed of, then don't worry about it because there are no
18815      // event handlers left to unbind anyway
18816      if (this.player_.el_) {
18817        this.player_.off('mediachange', this.onPlayerMediachange_);
18818        this.player_.tech_.off('hls-reset', this.onHlsReset_);
18819      }
18820    });
18821  }
18822
18823  /**
18824   * Add a range that that can now be seeked to.
18825   *
18826   * @param {Double} start where to start the addition
18827   * @param {Double} end where to end the addition
18828   * @private
18829   */
18830
18831  _createClass(HtmlMediaSource, [{
18832    key: 'addSeekableRange_',
18833    value: function addSeekableRange_(start, end) {
18834      var error = undefined;
18835
18836      if (this.duration !== Infinity) {
18837        error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
18838        error.name = 'InvalidStateError';
18839        error.code = 11;
18840        throw error;
18841      }
18842
18843      if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
18844        this.nativeMediaSource_.duration = end;
18845      }
18846    }
18847
18848    /**
18849     * Add a source buffer to the media source.
18850     *
18851     * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
18852     * @param {String} type the content-type of the content
18853     * @return {Object} the created source buffer
18854     */
18855  }, {
18856    key: 'addSourceBuffer',
18857    value: function addSourceBuffer(type) {
18858      var buffer = undefined;
18859      var parsedType = (0, _codecUtils.parseContentType)(type);
18860
18861      // Create a VirtualSourceBuffer to transmux MPEG-2 transport
18862      // stream segments into fragmented MP4s
18863      if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
18864        var codecs = [];
18865
18866        if (parsedType.parameters && parsedType.parameters.codecs) {
18867          codecs = parsedType.parameters.codecs.split(',');
18868          codecs = (0, _codecUtils.translateLegacyCodecs)(codecs);
18869          codecs = codecs.filter(function (codec) {
18870            return (0, _codecUtils.isAudioCodec)(codec) || (0, _codecUtils.isVideoCodec)(codec);
18871          });
18872        }
18873
18874        if (codecs.length === 0) {
18875          codecs = ['avc1.4d400d', 'mp4a.40.2'];
18876        }
18877
18878        buffer = new _virtualSourceBuffer2['default'](this, codecs);
18879
18880        if (this.sourceBuffers.length !== 0) {
18881          // If another VirtualSourceBuffer already exists, then we are creating a
18882          // SourceBuffer for an alternate audio track and therefore we know that
18883          // the source has both an audio and video track.
18884          // That means we should trigger the manual creation of the real
18885          // SourceBuffers instead of waiting for the transmuxer to return data
18886          this.sourceBuffers[0].createRealSourceBuffers_();
18887          buffer.createRealSourceBuffers_();
18888
18889          // Automatically disable the audio on the first source buffer if
18890          // a second source buffer is ever created
18891          this.sourceBuffers[0].audioDisabled_ = true;
18892        }
18893      } else {
18894        // delegate to the native implementation
18895        buffer = this.nativeMediaSource_.addSourceBuffer(type);
18896      }
18897
18898      this.sourceBuffers.push(buffer);
18899      return buffer;
18900    }
18901  }]);
18902
18903  return HtmlMediaSource;
18904})(_videoJs2['default'].EventTarget);
18905
18906exports['default'] = HtmlMediaSource;
18907module.exports = exports['default'];
18908}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
18909},{"./add-text-track-data":64,"./codec-utils":65,"./virtual-source-buffer":75,"global/document":31,"global/window":32}],72:[function(require,module,exports){
18910/**
18911 * @file remove-cues-from-track.js
18912 */
18913
18914/**
18915 * Remove cues from a track on video.js.
18916 *
18917 * @param {Double} start start of where we should remove the cue
18918 * @param {Double} end end of where the we should remove the cue
18919 * @param {Object} track the text track to remove the cues from
18920 * @private
18921 */
18922"use strict";
18923
18924Object.defineProperty(exports, "__esModule", {
18925  value: true
18926});
18927var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
18928  var i = undefined;
18929  var cue = undefined;
18930
18931  if (!track) {
18932    return;
18933  }
18934
18935  if (!track.cues) {
18936    return;
18937  }
18938
18939  i = track.cues.length;
18940
18941  while (i--) {
18942    cue = track.cues[i];
18943
18944    // Remove any overlapping cue
18945    if (cue.startTime <= end && cue.endTime >= start) {
18946      track.removeCue(cue);
18947    }
18948  }
18949};
18950
18951exports["default"] = removeCuesFromTrack;
18952module.exports = exports["default"];
18953},{}],73:[function(require,module,exports){
18954/**
18955 * @file transmuxer-worker.js
18956 */
18957
18958/**
18959 * videojs-contrib-media-sources
18960 *
18961 * Copyright (c) 2015 Brightcove
18962 * All rights reserved.
18963 *
18964 * Handles communication between the browser-world and the mux.js
18965 * transmuxer running inside of a WebWorker by exposing a simple
18966 * message-based interface to a Transmuxer object.
18967 */
18968'use strict';
18969
18970Object.defineProperty(exports, '__esModule', {
18971  value: true
18972});
18973
18974var _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; }; })();
18975
18976function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
18977
18978function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
18979
18980var _globalWindow = require('global/window');
18981
18982var _globalWindow2 = _interopRequireDefault(_globalWindow);
18983
18984var _muxJsLibMp4 = require('mux.js/lib/mp4');
18985
18986var _muxJsLibMp42 = _interopRequireDefault(_muxJsLibMp4);
18987
18988/**
18989 * Re-emits transmuxer events by converting them into messages to the
18990 * world outside the worker.
18991 *
18992 * @param {Object} transmuxer the transmuxer to wire events on
18993 * @private
18994 */
18995var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
18996  transmuxer.on('data', function (segment) {
18997    // transfer ownership of the underlying ArrayBuffer
18998    // instead of doing a copy to save memory
18999    // ArrayBuffers are transferable but generic TypedArrays are not
19000    // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
19001    var initArray = segment.initSegment;
19002
19003    segment.initSegment = {
19004      data: initArray.buffer,
19005      byteOffset: initArray.byteOffset,
19006      byteLength: initArray.byteLength
19007    };
19008
19009    var typedArray = segment.data;
19010
19011    segment.data = typedArray.buffer;
19012    _globalWindow2['default'].postMessage({
19013      action: 'data',
19014      segment: segment,
19015      byteOffset: typedArray.byteOffset,
19016      byteLength: typedArray.byteLength
19017    }, [segment.data]);
19018  });
19019
19020  if (transmuxer.captionStream) {
19021    transmuxer.captionStream.on('data', function (caption) {
19022      _globalWindow2['default'].postMessage({
19023        action: 'caption',
19024        data: caption
19025      });
19026    });
19027  }
19028
19029  transmuxer.on('done', function (data) {
19030    _globalWindow2['default'].postMessage({ action: 'done' });
19031  });
19032};
19033
19034/**
19035 * All incoming messages route through this hash. If no function exists
19036 * to handle an incoming message, then we ignore the message.
19037 *
19038 * @class MessageHandlers
19039 * @param {Object} options the options to initialize with
19040 */
19041
19042var MessageHandlers = (function () {
19043  function MessageHandlers(options) {
19044    _classCallCheck(this, MessageHandlers);
19045
19046    this.options = options || {};
19047    this.init();
19048  }
19049
19050  /**
19051   * Our web wroker interface so that things can talk to mux.js
19052   * that will be running in a web worker. the scope is passed to this by
19053   * webworkify.
19054   *
19055   * @param {Object} self the scope for the web worker
19056   */
19057
19058  /**
19059   * initialize our web worker and wire all the events.
19060   */
19061
19062  _createClass(MessageHandlers, [{
19063    key: 'init',
19064    value: function init() {
19065      if (this.transmuxer) {
19066        this.transmuxer.dispose();
19067      }
19068      this.transmuxer = new _muxJsLibMp42['default'].Transmuxer(this.options);
19069      wireTransmuxerEvents(this.transmuxer);
19070    }
19071
19072    /**
19073     * Adds data (a ts segment) to the start of the transmuxer pipeline for
19074     * processing.
19075     *
19076     * @param {ArrayBuffer} data data to push into the muxer
19077     */
19078  }, {
19079    key: 'push',
19080    value: function push(data) {
19081      // Cast array buffer to correct type for transmuxer
19082      var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
19083
19084      this.transmuxer.push(segment);
19085    }
19086
19087    /**
19088     * Recreate the transmuxer so that the next segment added via `push`
19089     * start with a fresh transmuxer.
19090     */
19091  }, {
19092    key: 'reset',
19093    value: function reset() {
19094      this.init();
19095    }
19096
19097    /**
19098     * Set the value that will be used as the `baseMediaDecodeTime` time for the
19099     * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
19100     * set relative to the first based on the PTS values.
19101     *
19102     * @param {Object} data used to set the timestamp offset in the muxer
19103     */
19104  }, {
19105    key: 'setTimestampOffset',
19106    value: function setTimestampOffset(data) {
19107      var timestampOffset = data.timestampOffset || 0;
19108
19109      this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
19110    }
19111  }, {
19112    key: 'setAudioAppendStart',
19113    value: function setAudioAppendStart(data) {
19114      this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
19115    }
19116
19117    /**
19118     * Forces the pipeline to finish processing the last segment and emit it's
19119     * results.
19120     *
19121     * @param {Object} data event data, not really used
19122     */
19123  }, {
19124    key: 'flush',
19125    value: function flush(data) {
19126      this.transmuxer.flush();
19127    }
19128  }, {
19129    key: 'resetCaptions',
19130    value: function resetCaptions() {
19131      this.transmuxer.resetCaptions();
19132    }
19133  }]);
19134
19135  return MessageHandlers;
19136})();
19137
19138var TransmuxerWorker = function TransmuxerWorker(self) {
19139  self.onmessage = function (event) {
19140    if (event.data.action === 'init' && event.data.options) {
19141      this.messageHandlers = new MessageHandlers(event.data.options);
19142      return;
19143    }
19144
19145    if (!this.messageHandlers) {
19146      this.messageHandlers = new MessageHandlers();
19147    }
19148
19149    if (event.data && event.data.action && event.data.action !== 'init') {
19150      if (this.messageHandlers[event.data.action]) {
19151        this.messageHandlers[event.data.action](event.data);
19152      }
19153    }
19154  };
19155};
19156
19157exports['default'] = function (self) {
19158  return new TransmuxerWorker(self);
19159};
19160
19161module.exports = exports['default'];
19162},{"global/window":32,"mux.js/lib/mp4":55}],74:[function(require,module,exports){
19163(function (global){
19164/**
19165 * @file videojs-contrib-media-sources.js
19166 */
19167'use strict';
19168
19169Object.defineProperty(exports, '__esModule', {
19170  value: true
19171});
19172
19173function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
19174
19175var _globalWindow = require('global/window');
19176
19177var _globalWindow2 = _interopRequireDefault(_globalWindow);
19178
19179var _flashMediaSource = require('./flash-media-source');
19180
19181var _flashMediaSource2 = _interopRequireDefault(_flashMediaSource);
19182
19183var _htmlMediaSource = require('./html-media-source');
19184
19185var _htmlMediaSource2 = _interopRequireDefault(_htmlMediaSource);
19186
19187var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
19188
19189var _videoJs2 = _interopRequireDefault(_videoJs);
19190
19191var urlCount = 0;
19192
19193// ------------
19194// Media Source
19195// ------------
19196
19197var defaults = {
19198  // how to determine the MediaSource implementation to use. There
19199  // are three available modes:
19200  // - auto: use native MediaSources where available and Flash
19201  //   everywhere else
19202  // - html5: always use native MediaSources
19203  // - flash: always use the Flash MediaSource polyfill
19204  mode: 'auto'
19205};
19206
19207// store references to the media sources so they can be connected
19208// to a video element (a swf object)
19209// TODO: can we store this somewhere local to this module?
19210_videoJs2['default'].mediaSources = {};
19211
19212/**
19213 * Provide a method for a swf object to notify JS that a
19214 * media source is now open.
19215 *
19216 * @param {String} msObjectURL string referencing the MSE Object URL
19217 * @param {String} swfId the swf id
19218 */
19219var open = function open(msObjectURL, swfId) {
19220  var mediaSource = _videoJs2['default'].mediaSources[msObjectURL];
19221
19222  if (mediaSource) {
19223    mediaSource.trigger({ type: 'sourceopen', swfId: swfId });
19224  } else {
19225    throw new Error('Media Source not found (Video.js)');
19226  }
19227};
19228
19229/**
19230 * Check to see if the native MediaSource object exists and supports
19231 * an MP4 container with both H.264 video and AAC-LC audio.
19232 *
19233 * @return {Boolean} if  native media sources are supported
19234 */
19235var supportsNativeMediaSources = function supportsNativeMediaSources() {
19236  return !!_globalWindow2['default'].MediaSource && !!_globalWindow2['default'].MediaSource.isTypeSupported && _globalWindow2['default'].MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
19237};
19238
19239/**
19240 * An emulation of the MediaSource API so that we can support
19241 * native and non-native functionality such as flash and
19242 * video/mp2t videos. returns an instance of HtmlMediaSource or
19243 * FlashMediaSource depending on what is supported and what options
19244 * are passed in.
19245 *
19246 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
19247 * @param {Object} options options to use during setup.
19248 */
19249var MediaSource = function MediaSource(options) {
19250  var settings = _videoJs2['default'].mergeOptions(defaults, options);
19251
19252  this.MediaSource = {
19253    open: open,
19254    supportsNativeMediaSources: supportsNativeMediaSources
19255  };
19256
19257  // determine whether HTML MediaSources should be used
19258  if (settings.mode === 'html5' || settings.mode === 'auto' && supportsNativeMediaSources()) {
19259    return new _htmlMediaSource2['default']();
19260  } else if (_videoJs2['default'].getTech('Flash')) {
19261    return new _flashMediaSource2['default']();
19262  }
19263
19264  throw new Error('Cannot use Flash or Html5 to create a MediaSource for this video');
19265};
19266
19267exports.MediaSource = MediaSource;
19268MediaSource.open = open;
19269MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
19270
19271/**
19272 * A wrapper around the native URL for our MSE object
19273 * implementation, this object is exposed under videojs.URL
19274 *
19275 * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
19276 */
19277var URL = {
19278  /**
19279   * A wrapper around the native createObjectURL for our objects.
19280   * This function maps a native or emulated mediaSource to a blob
19281   * url so that it can be loaded into video.js
19282   *
19283   * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
19284   * @param {MediaSource} object the object to create a blob url to
19285   */
19286  createObjectURL: function createObjectURL(object) {
19287    var objectUrlPrefix = 'blob:vjs-media-source/';
19288    var url = undefined;
19289
19290    // use the native MediaSource to generate an object URL
19291    if (object instanceof _htmlMediaSource2['default']) {
19292      url = _globalWindow2['default'].URL.createObjectURL(object.nativeMediaSource_);
19293      object.url_ = url;
19294      return url;
19295    }
19296    // if the object isn't an emulated MediaSource, delegate to the
19297    // native implementation
19298    if (!(object instanceof _flashMediaSource2['default'])) {
19299      url = _globalWindow2['default'].URL.createObjectURL(object);
19300      object.url_ = url;
19301      return url;
19302    }
19303
19304    // build a URL that can be used to map back to the emulated
19305    // MediaSource
19306    url = objectUrlPrefix + urlCount;
19307
19308    urlCount++;
19309
19310    // setup the mapping back to object
19311    _videoJs2['default'].mediaSources[url] = object;
19312
19313    return url;
19314  }
19315};
19316
19317exports.URL = URL;
19318_videoJs2['default'].MediaSource = MediaSource;
19319_videoJs2['default'].URL = URL;
19320}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
19321},{"./flash-media-source":68,"./html-media-source":71,"global/window":32}],75:[function(require,module,exports){
19322(function (global){
19323/**
19324 * @file virtual-source-buffer.js
19325 */
19326'use strict';
19327
19328Object.defineProperty(exports, '__esModule', {
19329  value: true
19330});
19331
19332var _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; }; })();
19333
19334var _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); } } };
19335
19336function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
19337
19338function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
19339
19340function _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, { c
19340onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
19341
19342var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
19343
19344var _videoJs2 = _interopRequireDefault(_videoJs);
19345
19346var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
19347
19348var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
19349
19350var _removeCuesFromTrack = require('./remove-cues-from-track');
19351
19352var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
19353
19354var _addTextTrackData = require('./add-text-track-data');
19355
19356var _webworkify = require('webworkify');
19357
19358var _webworkify2 = _interopRequireDefault(_webworkify);
19359
19360var _transmuxerWorker = require('./transmuxer-worker');
19361
19362var _transmuxerWorker2 = _interopRequireDefault(_transmuxerWorker);
19363
19364var _codecUtils = require('./codec-utils');
19365
19366// We create a wrapper around the SourceBuffer so that we can manage the
19367// state of the `updating` property manually. We have to do this because
19368// Firefox changes `updating` to false long before triggering `updateend`
19369// events and that was causing strange problems in videojs-contrib-hls
19370var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) {
19371  var sourceBuffer = mediaSource.addSourceBuffer(mimeType);
19372  var wrapper = Object.create(null);
19373
19374  wrapper.updating = false;
19375  wrapper.realBuffer_ = sourceBuffer;
19376
19377  var _loop = function (key) {
19378    if (typeof sourceBuffer[key] === 'function') {
19379      wrapper[key] = function () {
19380        return sourceBuffer[key].apply(sourceBuffer, arguments);
19381      };
19382    } else if (typeof wrapper[key] === 'undefined') {
19383      Object.defineProperty(wrapper, key, {
19384        get: function get() {
19385          return sourceBuffer[key];
19386        },
19387        set: function set(v) {
19388          return sourceBuffer[key] = v;
19389        }
19390      });
19391    }
19392  };
19393
19394  for (var key in sourceBuffer) {
19395    _loop(key);
19396  }
19397
19398  return wrapper;
19399};
19400
19401/**
19402 * VirtualSourceBuffers exist so that we can transmux non native formats
19403 * into a native format, but keep the same api as a native source buffer.
19404 * It creates a transmuxer, that works in its own thread (a web worker) and
19405 * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
19406 * then send all of that data to the naive sourcebuffer so that it is
19407 * indestinguishable from a natively supported format.
19408 *
19409 * @param {HtmlMediaSource} mediaSource the parent mediaSource
19410 * @param {Array} codecs array of codecs that we will be dealing with
19411 * @class VirtualSourceBuffer
19412 * @extends video.js.EventTarget
19413 */
19414
19415var VirtualSourceBuffer = (function (_videojs$EventTarget) {
19416  _inherits(VirtualSourceBuffer, _videojs$EventTarget);
19417
19418  function VirtualSourceBuffer(mediaSource, codecs) {
19419    var _this = this;
19420
19421    _classCallCheck(this, VirtualSourceBuffer);
19422
19423    _get(Object.getPrototypeOf(VirtualSourceBuffer.prototype), 'constructor', this).call(this, _videoJs2['default'].EventTarget);
19424    this.timestampOffset_ = 0;
19425    this.pendingBuffers_ = [];
19426    this.bufferUpdating_ = false;
19427
19428    this.mediaSource_ = mediaSource;
19429    this.codecs_ = codecs;
19430    this.audioCodec_ = null;
19431    this.videoCodec_ = null;
19432    this.audioDisabled_ = false;
19433    this.appendAudioInitSegment_ = true;
19434
19435    var options = {
19436      remux: false
19437    };
19438
19439    this.codecs_.forEach(function (codec) {
19440      if ((0, _codecUtils.isAudioCodec)(codec)) {
19441        _this.audioCodec_ = codec;
19442      } else if ((0, _codecUtils.isVideoCodec)(codec)) {
19443        _this.videoCodec_ = codec;
19444      }
19445    });
19446
19447    // append muxed segments to their respective native buffers as
19448    // soon as they are available
19449    this.transmuxer_ = (0, _webworkify2['default'])(_transmuxerWorker2['default']);
19450    this.transmuxer_.postMessage({ action: 'init', options: options });
19451
19452    this.transmuxer_.onmessage = function (event) {
19453      if (event.data.action === 'data') {
19454        return _this.data_(event);
19455      }
19456
19457      if (event.data.action === 'done') {
19458        return _this.done_(event);
19459      }
19460    };
19461
19462    // this timestampOffset is a property with the side-effect of resetting
19463    // baseMediaDecodeTime in the transmuxer on the setter
19464    Object.defineProperty(this, 'timestampOffset', {
19465      get: function get() {
19466        return this.timestampOffset_;
19467      },
19468      set: function set(val) {
19469        if (typeof val === 'number' && val >= 0) {
19470          this.timestampOffset_ = val;
19471          this.appendAudioInitSegment_ = true;
19472
19473          // We have to tell the transmuxer to set the baseMediaDecodeTime to
19474          // the desired timestampOffset for the next segment
19475          this.transmuxer_.postMessage({
19476            action: 'setTimestampOffset',
19477            timestampOffset: val
19478          });
19479        }
19480      }
19481    });
19482
19483    // setting the append window affects both source buffers
19484    Object.defineProperty(this, 'appendWindowStart', {
19485      get: function get() {
19486        return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
19487      },
19488      set: function set(start) {
19489        if (this.videoBuffer_) {
19490          this.videoBuffer_.appendWindowStart = start;
19491        }
19492        if (this.audioBuffer_) {
19493          this.audioBuffer_.appendWindowStart = start;
19494        }
19495      }
19496    });
19497
19498    // this buffer is "updating" if either of its native buffers are
19499    Object.defineProperty(this, 'updating', {
19500      get: function get() {
19501        return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
19502      }
19503    });
19504
19505    // the buffered property is the intersection of the buffered
19506    // ranges of the native source buffers
19507    Object.defineProperty(this, 'buffered', {
19508      get: function get() {
19509        var start = null;
19510        var end = null;
19511        var arity = 0;
19512        var extents = [];
19513        var ranges = [];
19514
19515        // neither buffer has been created yet
19516        if (!this.videoBuffer_ && !this.audioBuffer_) {
19517          return _videoJs2['default'].createTimeRange();
19518        }
19519
19520        // only one buffer is configured
19521        if (!this.videoBuffer_) {
19522          return this.audioBuffer_.buffered;
19523        }
19524        if (!this.audioBuffer_) {
19525          return this.videoBuffer_.buffered;
19526        }
19527
19528        // both buffers are configured
19529        if (this.audioDisabled_) {
19530          return this.videoBuffer_.buffered;
19531        }
19532
19533        // both buffers are empty
19534        if (this.videoBuffer_.buffered.length === 0 && this.audioBuffer_.buffered.length === 0) {
19535          return _videoJs2['default'].createTimeRange();
19536        }
19537
19538        // Handle the case where we have both buffers and create an
19539        // intersection of the two
19540        var videoBuffered = this.videoBuffer_.buffered;
19541        var audioBuffered = this.audioBuffer_.buffered;
19542        var count = videoBuffered.length;
19543
19544        // A) Gather up all start and end times
19545        while (count--) {
19546          extents.push({ time: videoBuffered.start(count), type: 'start' });
19547          extents.push({ time: videoBuffered.end(count), type: 'end' });
19548        }
19549        count = audioBuffered.length;
19550        while (count--) {
19551          extents.push({ time: audioBuffered.start(count), type: 'start' });
19552          extents.push({ time: audioBuffered.end(count), type: 'end' });
19553        }
19554        // B) Sort them by time
19555        extents.sort(function (a, b) {
19556          return a.time - b.time;
19557        });
19558
19559        // C) Go along one by one incrementing arity for start and decrementing
19560        //    arity for ends
19561        for (count = 0; count < extents.length; count++) {
19562          if (extents[count].type === 'start') {
19563            arity++;
19564
19565            // D) If arity is ever incremented to 2 we are entering an
19566            //    overlapping range
19567            if (arity === 2) {
19568              start = extents[count].time;
19569            }
19570          } else if (extents[count].type === 'end') {
19571            arity--;
19572
19573            // E) If arity is ever decremented to 1 we leaving an
19574            //    overlapping range
19575            if (arity === 1) {
19576              end = extents[count].time;
19577            }
19578          }
19579
19580          // F) Record overlapping ranges
19581          if (start !== null && end !== null) {
19582            ranges.push([start, end]);
19583            start = null;
19584            end = null;
19585          }
19586        }
19587
19588        return _videoJs2['default'].createTimeRanges(ranges);
19589      }
19590    });
19591  }
19592
19593  /**
19594   * When we get a data event from the transmuxer
19595   * we call this function and handle the data that
19596   * was sent to us
19597   *
19598   * @private
19599   * @param {Event} event the data event from the transmuxer
19600   */
19601
19602  _createClass(VirtualSourceBuffer, [{
19603    key: 'data_',
19604    value: function data_(event) {
19605      var segment = event.data.segment;
19606
19607      // Cast ArrayBuffer to TypedArray
19608      segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
19609
19610      segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
19611
19612      (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
19613
19614      // Add the segments to the pendingBuffers array
19615      this.pendingBuffers_.push(segment);
19616      return;
19617    }
19618
19619    /**
19620     * When we get a done event from the transmuxer
19621     * we call this function and we process all
19622     * of the pending data that we have been saving in the
19623     * data_ function
19624     *
19625     * @private
19626     * @param {Event} event the done event from the transmuxer
19627     */
19628  }, {
19629    key: 'done_',
19630    value: function done_(event) {
19631      // Don't process and append data if the mediaSource is closed
19632      if (this.mediaSource_.readyState === 'closed') {
19633        this.pendingBuffers_.length = 0;
19634        return;
19635      }
19636
19637      // All buffers should have been flushed from the muxer
19638      // start processing anything we have received
19639      this.processPendingSegments_();
19640      return;
19641    }
19642
19643    /**
19644     * Create our internal native audio/video source buffers and add
19645     * event handlers to them with the following conditions:
19646     * 1. they do not already exist on the mediaSource
19647     * 2. this VSB has a codec for them
19648     *
19649     * @private
19650     */
vendor: 3,306 bytes, lines 19650-19739
19650
19651  }, {
19652    key: 'createRealSourceBuffers_',
19653    value: function createRealSourceBuffers_() {
19654      var _this2 = this;
19655
19656      var types = ['audio', 'video'];
19657
19658      types.forEach(function (type) {
19659        // Don't create a SourceBuffer of this type if we don't have a
19660        // codec for it
19661        if (!_this2[type + 'Codec_']) {
19662          return;
19663        }
19664
19665        // Do nothing if a SourceBuffer of this type already exists
19666        if (_this2[type + 'Buffer_']) {
19667          return;
19668        }
19669
19670        var buffer = null;
19671
19672        // If the mediasource already has a SourceBuffer for the codec
19673        // use that
19674        if (_this2.mediaSource_[type + 'Buffer_']) {
19675          buffer = _this2.mediaSource_[type + 'Buffer_'];
19676          // In multiple audio track cases, the audio source buffer is disabled
19677          // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier
19678          // than createRealSourceBuffers_ is called to create the second
19679          // VirtualSourceBuffer because that happens as a side-effect of
19680          // videojs-contrib-hls starting the audioSegmentLoader. As a result,
19681          // the audioBuffer is essentially "ownerless" and no one will toggle
19682          // the `updating` state back to false once the `updateend` event is received
19683          //
19684          // Setting `updating` to false manually will work around this
19685          // situation and allow work to continue
19686          buffer.updating = false;
19687        } else {
19688          var codecProperty = type + 'Codec_';
19689          var mimeType = type + '/mp4;codecs="' + _this2[codecProperty] + '"';
19690
19691          buffer = makeWrappedSourceBuffer(_this2.mediaSource_.nativeMediaSource_, mimeType);
19692
19693          _this2.mediaSource_[type + 'Buffer_'] = buffer;
19694        }
19695
19696        _this2[type + 'Buffer_'] = buffer;
19697
19698        // Wire up the events to the SourceBuffer
19699        ['update', 'updatestart', 'updateend'].forEach(function (event) {
19700          buffer.addEventListener(event, function () {
19701            // if audio is disabled
19702            if (type === 'audio' && _this2.audioDisabled_) {
19703              return;
19704            }
19705
19706            if (event === 'updateend') {
19707              _this2[type + 'Buffer_'].updating = false;
19708            }
19709
19710            var shouldTrigger = types.every(function (t) {
19711              // skip checking audio's updating status if audio
19712              // is not enabled
19713              if (t === 'audio' && _this2.audioDisabled_) {
19714                return true;
19715              }
19716              // if the other type if updating we don't trigger
19717              if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
19718                return false;
19719              }
19720              return true;
19721            });
19722
19723            if (shouldTrigger) {
19724              return _this2.trigger(event);
19725            }
19726          });
19727        });
19728      });
19729    }
19730
19731    /**
19732     * Emulate the native mediasource function, but our function will
19733     * send all of the proposed segments to the transmuxer so that we
19734     * can transmux them before we append them to our internal
19735     * native source buffers in the correct format.
19736     *
19737     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
19738     * @param {Uint8Array} segment the segment to append to the buffer
19739     */
vendor: 4,661 bytes, lines 19740-19880
19740  }, {
19741    key: 'appendBuffer',
19742    value: function appendBuffer(segment) {
19743      // Start the internal "updating" state
19744      this.bufferUpdating_ = true;
19745
19746      if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
19747        var audioBuffered = this.audioBuffer_.buffered;
19748
19749        this.transmuxer_.postMessage({
19750          action: 'setAudioAppendStart',
19751          appendStart: audioBuffered.end(audioBuffered.length - 1)
19752        });
19753      }
19754
19755      this.transmuxer_.postMessage({
19756        action: 'push',
19757        // Send the typed-array of data as an ArrayBuffer so that
19758        // it can be sent as a "Transferable" and avoid the costly
19759        // memory copy
19760        data: segment.buffer,
19761
19762        // To recreate the original typed-array, we need information
19763        // about what portion of the ArrayBuffer it was a view into
19764        byteOffset: segment.byteOffset,
19765        byteLength: segment.byteLength
19766      }, [segment.buffer]);
19767      this.transmuxer_.postMessage({ action: 'flush' });
19768    }
19769
19770    /**
19771     * Emulate the native mediasource function and remove parts
19772     * of the buffer from any of our internal buffers that exist
19773     *
19774     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
19775     * @param {Double} start position to start the remove at
19776     * @param {Double} end position to end the remove at
19777     */
19778  }, {
19779    key: 'remove',
19780    value: function remove(start, end) {
19781      if (this.videoBuffer_) {
19782        this.videoBuffer_.updating = true;
19783        this.videoBuffer_.remove(start, end);
19784      }
19785      if (!this.audioDisabled_ && this.audioBuffer_) {
19786        this.audioBuffer_.updating = true;
19787        this.audioBuffer_.remove(start, end);
19788      }
19789
19790      // Remove Metadata Cues (id3)
19791      (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
19792
19793      // Remove Any Captions
19794      if (this.inbandTextTracks_) {
19795        for (var track in this.inbandTextTracks_) {
19796          (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]);
19797        }
19798      }
19799    }
19800
19801    /**
19802     * Process any segments that the muxer has output
19803     * Concatenate segments together based on type and append them into
19804     * their respective sourceBuffers
19805     *
19806     * @private
19807     */
19808  }, {
19809    key: 'processPendingSegments_',
19810    value: function processPendingSegments_() {
19811      var sortedSegments = {
19812        video: {
19813          segments: [],
19814          bytes: 0
19815        },
19816        audio: {
19817          segments: [],
19818          bytes: 0
19819        },
19820        captions: [],
19821        metadata: []
19822      };
19823
19824      // Sort segments into separate video/audio arrays and
19825      // keep track of their total byte lengths
19826      sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
19827        var type = segment.type;
19828        var data = segment.data;
19829        var initSegment = segment.initSegment;
19830
19831        segmentObj[type].segments.push(data);
19832        segmentObj[type].bytes += data.byteLength;
19833
19834        segmentObj[type].initSegment = initSegment;
19835
19836        // Gather any captions into a single array
19837        if (segment.captions) {
19838          segmentObj.captions = segmentObj.captions.concat(segment.captions);
19839        }
19840
19841        if (segment.info) {
19842          segmentObj[type].info = segment.info;
19843        }
19844
19845        // Gather any metadata into a single array
19846        if (segment.metadata) {
19847          segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
19848        }
19849
19850        return segmentObj;
19851      }, sortedSegments);
19852
19853      // Create the real source buffers if they don't exist by now since we
19854      // finally are sure what tracks are contained in the source
19855      if (!this.videoBuffer_ && !this.audioBuffer_) {
19856        // Remove any codecs that may have been specified by default but
19857        // are no longer applicable now
19858        if (sortedSegments.video.bytes === 0) {
19859          this.videoCodec_ = null;
19860        }
19861        if (sortedSegments.audio.bytes === 0) {
19862          this.audioCodec_ = null;
19863        }
19864
19865        this.createRealSourceBuffers_();
19866      }
19867
19868      if (sortedSegments.audio.info) {
19869        this.mediaSource_.trigger({ type: 'audioinfo', info: sortedSegments.audio.info });
19870      }
19871      if (sortedSegments.video.info) {
19872        this.mediaSource_.trigger({ type: 'videoinfo', info: sortedSegments.video.info });
19873      }
19874
19875      if (this.appendAudioInitSegment_) {
19876        if (!this.audioDisabled_ && this.audioBuffer_) {
19877          sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
19878          sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
19879        }
19880        this.appendAudioInitSegment_ = false;
19881      }
19882
19883      // Merge multiple video and audio segments into one and append
19884      if (this.videoBuffer_) {
19885        sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
19886        sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
19887        this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
19888        // TODO: are video tracks the only ones with text tracks?
19889        (0, _addTextTrackData.addTextTrackData)(this, sortedSegments.captions, sortedSegments.metadata);
19890      }
19891
19892      if (!this.audioDisabled_ && this.audioBuffer_) {
19893        this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
19894      }
19895
19896      this.pendingBuffers_.length = 0;
19897
19898      // We are no longer in the internal "updating" state
19899      this.bufferUpdating_ = false;
19900    }
19901
19902    /**
19903     * Combine all segments into a single Uint8Array and then append them
19904     * to the destination buffer
19905     *
19906     * @param {Object} segmentObj
19907     * @param {SourceBuffer} destinationBuffer native source buffer to append data to
19908     * @private
19909     */
19910  }, {
19911    key: 'concatAndAppendSegments_',
19912    value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
19913      var offset = 0;
19914      var tempBuffer = undefined;
19915
19916      if (segmentObj.bytes) {
19917        tempBuffer = new Uint8Array(segmentObj.bytes);
19918
19919        // Combine the individual segments into one large typed-array
19920        segmentObj.segments.forEach(function (segment) {
19921          tempBuffer.set(segment, offset);
19922          offset += segment.byteLength;
19923        });
19924
19925        try {
19926          destinationBuffer.updating = true;
19927          destinationBuffer.appendBuffer(tempBuffer);
19928        } catch (error) {
19929          if (this.mediaSource_.player_) {
19930            this.mediaSource_.player_.error({
19931              code: -3,
19932              type: 'APPEND_BUFFER_ERR',
19933              message: error.message,
19934              originalError: error
19935            });
19936          }
19937        }
19938      }
19939    }
19940
19941    /**
19942     * Emulate the native mediasource function. abort any soureBuffer
19943     * actions and throw out any un-appended data.
19944     *
19945     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
19946     */
19947  }, {
19948    key: 'abort',
19949    value: function abort() {
19950      if (this.videoBuffer_) {
19951        this.videoBuffer_.abort();
19952      }
19953      if (!this.audioDisabled_ && this.audioBuffer_) {
19954        this.audioBuffer_.abort();
19955      }
19956      if (this.transmuxer_) {
19957        this.transmuxer_.postMessage({ action: 'reset' });
19958      }
19959      this.pendingBuffers_.length = 0;
19960      this.bufferUpdating_ = false;
19961    }
19962  }]);
19963
19964  return VirtualSourceBuffer;
19965})(_videoJs2['default'].EventTarget);
19966
19967exports['default'] = VirtualSourceBuffer;
19968module.exports = exports['default'];
19969}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
19970},{"./add-text-track-data":64,"./codec-utils":65,"./create-text-tracks-if-necessary":66,"./remove-cues-from-track":72,"./transmuxer-worker":73,"webworkify":76}],76:[function(require,module,exports){
19971var bundleFn = arguments[3];
19972var sources = arguments[4];
19973var cache = arguments[5];
19974
19975var stringify = JSON.stringify;
19976
19977module.exports = function (fn) {
19978    var keys = [];
19979    var wkey;
19980    var cacheKeys = Object.keys(cache);
19981    
19982    for (var i = 0, l = cacheKeys.length; i < l; i++) {
19983        var key = cacheKeys[i];
19984        if (cache[key].exports === fn) {
19985            wkey = key;
19986            break;
19987        }
19988    }
19989    
19990    if (!wkey) {
19991        wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
19992        var wcache = {};
19993        for (var i = 0, l = cacheKeys.length; i < l; i++) {
19994            var key = cacheKeys[i];
19995            wcache[key] = key;
19996        }
19997        sources[wkey] = [
19998            Function(['require','module','exports'], '(' + fn + ')(self)'),
19999            wcache
20000        ];
20001    }
20002    var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
20003    
20004    var scache = {}; scache[wkey] = wkey;
20005    sources[skey] = [
20006        Function(['require'],'require(' + stringify(wkey) + ')(self)'),
20007        scache
20008    ];
20009    
20010    var src = '(' + bundleFn + ')({'
20011        + Object.keys(sources).map(function (key) {
20012            return stringify(key) + ':['
20013                + sources[key][0]
20014                + ',' + stringify(sources[key][1]) + ']'
20015            ;
20016        }).join(',')
20017        + '},{},[' + stringify(skey) + '])'
20018    ;
20019    
20020    var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
20021    
20022    return new Worker(URL.createObjectURL(
20023        new Blob([src], { type: 'text/javascript' })
20024    ));
20025};
20026
20027},{}],77:[function(require,module,exports){
20028(function (global){
20029/**
20030 * @file videojs-contrib-hls.js
20031 *
20032 * The main file for the HLS project.
20033 * License: https://github.com/videojs/videojs-contrib-hls/blob/master/LICENSE
20034 */
20035'use strict';
20036
20037var _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; }; })();
20038
20039var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { var object = _x4, property = _x5, receiver = _x6; _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 { _x4 = parent; _x5 = property; _x6 = 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); } } };
20040
20041function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
20042
20043function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
20044
20045function _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, { c
20045onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
20046
20047var _globalDocument = require('global/document');
20048
20049var _globalDocument2 = _interopRequireDefault(_globalDocument);
20050
20051var _playlistLoader = require('./playlist-loader');
20052
20053var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
20054
20055var _playlist = require('./playlist');
20056
20057var _playlist2 = _interopRequireDefault(_playlist);
20058
20059var _xhr = require('./xhr');
20060
20061var _xhr2 = _interopRequireDefault(_xhr);
20062
20063var _aesDecrypter = require('aes-decrypter');
20064
20065var _binUtils = require('./bin-utils');
20066
20067var _binUtils2 = _interopRequireDefault(_binUtils);
20068
20069var _videojsContribMediaSources = require('videojs-contrib-media-sources');
20070
20071var _m3u8Parser = require('m3u8-parser');
20072
20073var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
20074
20075var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
20076
20077var _videoJs2 = _interopRequireDefault(_videoJs);
20078
20079var _masterPlaylistController = require('./master-playlist-controller');
20080
20081var _config = require('./config');
20082
20083var _config2 = _interopRequireDefault(_config);
20084
20085var _renditionMixin = require('./rendition-mixin');
20086
20087var _renditionMixin2 = _interopRequireDefault(_renditionMixin);
20088
20089var _globalWindow = require('global/window');
20090
20091var _globalWindow2 = _interopRequireDefault(_globalWindow);
20092
20093var _playbackWatcher = require('./playback-watcher');
20094
20095var _playbackWatcher2 = _interopRequireDefault(_playbackWatcher);
20096
20097var _reloadSourceOnError = require('./reload-source-on-error');
20098
20099var _reloadSourceOnError2 = _interopRequireDefault(_reloadSourceOnError);
20100
20101var _playlistSelectorsJs = require('./playlist-selectors.js');
20102
20103var Hls = {
20104  PlaylistLoader: _playlistLoader2['default'],
20105  Playlist: _playlist2['default'],
20106  Decrypter: _aesDecrypter.Decrypter,
20107  AsyncStream: _aesDecrypter.AsyncStream,
20108  decrypt: _aesDecrypter.decrypt,
20109  utils: _binUtils2['default'],
20110
20111  STANDARD_PLAYLIST_SELECTOR: _playlistSelectorsJs.lastBandwidthSelector,
20112  INITIAL_PLAYLIST_SELECTOR: _playlistSelectorsJs.lowestBitrateCompatibleVariantSelector,
20113  comparePlaylistBandwidth: _playlistSelectorsJs.comparePlaylistBandwidth,
20114  comparePlaylistResolution: _playlistSelectorsJs.comparePlaylistResolution,
20115
20116  xhr: (0, _xhr2['default'])()
20117};
20118
20119// 0.5 MB/s
20120var INITIAL_BANDWIDTH = 4194304;
20121
20122// Define getter/setters for config properites
20123['GOAL_BUFFER_LENGTH', 'MAX_GOAL_BUFFER_LENGTH', 'GOAL_BUFFER_LENGTH_RATE', 'BUFFER_LOW_WATER_LINE', 'MAX_BUFFER_LOW_WATER_LINE', 'BUFFER_LOW_WATER_LINE_RATE', 'BANDWIDTH_VARIANCE'].forEach(function (prop) {
20124  Object.defineProperty(Hls, prop, {
20125    get: function get() {
20126      _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
20127      return _config2['default'][prop];
20128    },
20129    set: function set(value) {
20130      _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
20131
20132      if (typeof value !== 'number' || value < 0) {
20133        _videoJs2['default'].log.warn('value of Hls.' + prop + ' must be greater than or equal to 0');
20134        return;
20135      }
20136
20137      _config2['default'][prop] = value;
20138    }
20139  });
20140});
20141
20142/**
20143 * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
20144 *
20145 * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
20146 * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
20147 * @function handleHlsMediaChange
20148 */
20149var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
20150  var newPlaylist = playlistLoader.media();
20151  var selectedIndex = -1;
20152
20153  for (var i = 0; i < qualityLevels.length; i++) {
20154    if (qualityLevels[i].id === newPlaylist.uri) {
20155      selectedIndex = i;
20156      break;
20157    }
20158  }
20159
20160  qualityLevels.selectedIndex_ = selectedIndex;
20161  qualityLevels.trigger({
20162    selectedIndex: selectedIndex,
20163    type: 'change'
20164  });
20165};
20166
20167/**
20168 * Adds quality levels to list once playlist metadata is available
20169 *
20170 * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
20171 * @param {Object} hls Hls object to listen to for media events.
20172 * @function handleHlsLoadedMetadata
20173 */
20174var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
20175  hls.representations().forEach(function (rep) {
20176    qualityLevels.addQualityLevel(rep);
20177  });
20178  handleHlsMediaChange(qualityLevels, hls.playlists);
20179};
20180
20181// HLS is a source handler, not a tech. Make sure attempts to use it
20182// as one do not cause exceptions.
20183Hls.canPlaySource = function () {
20184  return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
20185};
20186
20187/**
20188 * Whether the browser has built-in HLS support.
20189 */
20190Hls.supportsNativeHls = (function () {
20191  var video = _globalDocument2['default'].createElement('video');
20192
20193  // native HLS is definitely not supported if HTML5 video isn't
20194  if (!_videoJs2['default'].getTech('Html5').isSupported()) {
20195    return false;
20196  }
20197
20198  // HLS manifests can go by many mime-types
20199  var canPlay = [
20200  // Apple santioned
20201  'application/vnd.apple.mpegurl',
20202  // Apple sanctioned for backwards compatibility
20203  'audio/mpegurl',
20204  // Very common
20205  'audio/x-mpegurl',
20206  // Very common
20207  'application/x-mpegurl',
20208  // Included for completeness
20209  'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
20210
20211  return canPlay.some(function (canItPlay) {
20212    return (/maybe|probably/i.test(video.canPlayType(canItPlay))
20213    );
20214  });
20215})();
20216
20217/**
20218 * HLS is a source handler, not a tech. Make sure attempts to use it
20219 * as one do not cause exceptions.
20220 */
20221Hls.isSupported = function () {
20222  return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
20223};
20224
20225var Component = _videoJs2['default'].getComponent('Component');
20226
20227/**
20228 * The Hls Handler object, where we orchestrate all of the parts
20229 * of HLS to interact with video.js
20230 *
20231 * @class HlsHandler
20232 * @extends videojs.Component
20233 * @param {Object} source the soruce object
20234 * @param {Tech} tech the parent tech object
20235 * @param {Object} options optional and required options
20236 */
20237
20238var HlsHandler = (function (_Component) {
20239  _inherits(HlsHandler, _Component);
20240
20241  function HlsHandler(source, tech, options) {
20242    var _this = this;
20243
20244    _classCallCheck(this, HlsHandler);
20245
20246    _get(Object.getPrototypeOf(HlsHandler.prototype), 'constructor', this).call(this, tech, options.hls);
20247
20248    // tech.player() is deprecated but setup a reference to HLS for
20249    // backwards-compatibility
20250    if (tech.options_ && tech.options_.playerId) {
20251      var _player = (0, _videoJs2['default'])(tech.options_.playerId);
20252
20253      if (!_player.hasOwnProperty('hls')) {
20254        Object.defineProperty(_player, 'hls', {
20255          get: function get() {
20256            _videoJs2['default'].log.warn('player.hls is deprecated. Use player.tech_.hls instead.');
20257            tech.trigger({ type: 'usage', name: 'hls-player-access' });
20258            return _this;
20259          }
20260        });
20261      }
20262    }
20263
20264    this.tech_ = tech;
20265    this.source_ = source;
20266    this.stats = {};
20267    this.ignoreNextSeekingEvent_ = false;
20268    this.setOptions_();
20269
20270    // overriding native HLS only works if audio tracks have been emulated
20271    // error early if we're misconfigured:
20272    if (this.options_.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
20273      throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
20274    }
20275
20276    // listen for fullscreenchange events for this player so that we
20277    // can adjust our quality selection quickly
20278    this.on(_globalDocument2['default'], ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
20279      var fullscreenElement = _globalDocument2['default'].fullscreenElement || _globalDocument2['default'].webkitFullscreenElement || _globalDocument2['default'].mozFullScreenElement || _globalDocument2['default'].msFullscreenElement;
20280
20281      if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) {
20282        _this.masterPlaylistController_.fastQualityChange_();
20283      }
20284    });
20285
20286    this.on(this.tech_, 'seeking', function () {
20287      if (this.ignoreNextSeekingEvent_) {
20288        this.ignoreNextSeekingEvent_ = false;
20289        return;
20290      }
20291
20292      this.setCurrentTime(this.tech_.currentTime());
20293    });
20294    this.on(this.tech_, 'error', function () {
20295      if (this.masterPlaylistController_) {
20296        this.masterPlaylistController_.pauseLoading();
20297      }
20298    });
20299
20300    this.on(this.tech_, 'play', this.play);
20301  }
20302
20303  /**
20304   * The Source Handler object, which informs video.js what additional
20305   * MIME types are supported and sets up playback. It is registered
20306   * automatically to the appropriate tech based on the capabilities of
20307   * the browser it is running in. It is not necessary to use or modify
20308   * this object in normal usage.
20309   */
20310
20311  _createClass(HlsHandler, [{
20312    key: 'setOptions_',
20313    value: function setOptions_() {
20314      var _this2 = this;
20315
20316      // defaults
20317      this.options_.withCredentials = this.options_.withCredentials || false;
20318
20319      if (typeof this.options_.blacklistDuration !== 'number') {
20320        this.options_.blacklistDuration = 5 * 60;
20321      }
20322
20323      // start playlist selection at a reasonable bandwidth for
20324      // broadband internet (0.5 MB/s) or mobile (0.0625 MB/s)
20325      if (typeof this.options_.bandwidth !== 'number') {
20326        this.options_.bandwidth = INITIAL_BANDWIDTH;
20327      }
20328
20329      // If the bandwidth number is unchanged from the initial setting
20330      // then this takes precedence over the enableLowInitialPlaylist option
20331      this.options_.enableLowInitialPlaylist = this.options_.enableLowInitialPlaylist && this.options_.bandwidth === INITIAL_BANDWIDTH;
20332
20333      // grab options passed to player.src
20334      ['withCredentials', 'bandwidth'].forEach(function (option) {
20335        if (typeof _this2.source_[option] !== 'undefined') {
20336          _this2.options_[option] = _this2.source_[option];
20337        }
20338      });
20339
20340      this.bandwidth = this.options_.bandwidth;
20341    }
20342
20343    /**
20344     * called when player.src gets called, handle a new source
20345     *
20346     * @param {Object} src the source object to handle
20347     */
20348  }, {
20349    key: 'src',
20350    value: function src(_src) {
20351      var _this3 = this;
20352
20353      // do nothing if the src is falsey
20354      if (!_src) {
20355        return;
20356      }
20357      this.setOptions_();
20358      // add master playlist controller options
20359      this.options_.url = this.source_.src;
20360      this.options_.tech = this.tech_;
20361      this.options_.externHls = Hls;
20362
20363      this.masterPlaylistController_ = new _masterPlaylistController.MasterPlaylistController(this.options_);
20364      this.playbackWatcher_ = new _playbackWatcher2['default'](_videoJs2['default'].mergeOptions(this.options_, {
20365        seekable: function seekable() {
20366          return _this3.seekable();
20367        }
20368      }));
20369
20370      this.masterPlaylistController_.on('error', function () {
20371        var player = _videoJs2['default'].players[_this3.tech_.options_.playerId];
20372
20373        player.error(_this3.masterPlaylistController_.error);
20374      });
20375
20376      // `this` in selectPlaylist should be the HlsHandler for backwards
20377      // compatibility with < v2
20378      this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls.STANDARD_PLAYLIST_SELECTOR.bind(this);
20379
20380      this.masterPlaylistController_.selectInitialPlaylist = Hls.INITIAL_PLAYLIST_SELECTOR.bind(this);
20381
20382      // re-expose some internal objects for backwards compatibility with < v2
20383      this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
20384      this.mediaSource = this.masterPlaylistController_.mediaSource;
20385
20386      // Proxy assignment of some properties to the master playlist
20387      // controller. Using a custom property for backwards compatibility
20388      // with < v2
20389      Object.defineProperties(this, {
20390        selectPlaylist: {
20391          get: function get() {
20392            return this.masterPlaylistController_.selectPlaylist;
20393          },
20394          set: function set(selectPlaylist) {
20395            this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
20396          }
20397        },
20398        throughput: {
20399          get: function get() {
20400            return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
20401          },
20402          set: function set(throughput) {
20403            this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput;
20404            // By setting `count` to 1 the throughput value becomes the starting value
20405            // for the cumulative average
20406            this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
20407          }
20408        },
20409        bandwidth: {
20410          get: function get() {
20411            return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
20412          },
20413          set: function set(bandwidth) {
20414            this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth;
20415            // setting the bandwidth manually resets the throughput counter
20416            // `count` is set to zero that current value of `rate` isn't included
20417            // in the cumulative average
20418            this.masterPlaylistController_.mainSegmentLoader_.throughput = {
20419              rate: 0,
20420              count: 0
20421            };
20422          }
20423        },
20424        /**
20425         * `systemBandwidth` is a combination of two serial processes bit-rates. The first
20426         * is the network bitrate provided by `bandwidth` and the second is the bitrate of
20427         * the entire process after that - decryption, transmuxing, and appending - provided
20428         * by `throughput`.
20429         *
20430         * Since the two process are serial, the overall system bandwidth is given by:
20431         *   sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
20432         */
20433        systemBandwidth: {
20434          get: function get() {
20435            var invBandwidth = 1 / (this.bandwidth || 1);
20436            var invThroughput = undefined;
20437
20438            if (this.throughput > 0) {
20439              invThroughput = 1 / this.throughput;
20440            } else {
20441              invThroughput = 0;
20442            }
20443
20444            var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
20445
20446            return systemBitrate;
20447          },
20448          set: function set() {
20449            _videoJs2['default'].log.error('The "systemBandwidth" property is read-only');
20450          }
20451        }
20452      });
20453
20454      Object.defineProperties(this.stats, {
20455        bandwidth: {
20456          get: function get() {
20457            return _this3.bandwidth || 0;
20458          },
20459          enumerable: true
20460        },
20461        mediaRequests: {
20462          get: function get() {
20463            return _this3.masterPlaylistController_.mediaRequests_() || 0;
20464          },
20465          enumerable: true
20466        },
20467        mediaRequestsAborted: {
20468          get: function get() {
20469            return _this3.masterPlaylistController_.mediaRequestsAborted_() || 0;
20470          },
20471          enumerable: true
20472        },
20473        mediaRequestsTimedout: {
20474          get: function get() {
20475            return _this3.masterPlaylistController_.mediaRequestsTimedout_() || 0;
20476          },
20477          enumerable: true
20478        },
20479        mediaRequestsErrored: {
20480          get: function get() {
20481            return _this3.masterPlaylistController_.mediaRequestsErrored_() || 0;
20482          },
20483          enumerable: true
20484        },
20485        mediaTransferDuration: {
20486          get: function get() {
20487            return _this3.masterPlaylistController_.mediaTransferDuration_() || 0;
20488          },
20489          enumerable: true
20490        },
20491        mediaBytesTransferred: {
20492          get: function get() {
20493            return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0;
20494          },
20495          enumerable: true
20496        },
20497        mediaSecondsLoaded: {
20498          get: function get() {
20499            return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0;
20500          },
20501          enumerable: true
20502        }
20503      });
20504
20505      this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
20506
20507      this.masterPlaylistController_.on('selectedinitialmedia', function () {
20508        // Add the manual rendition mix-in to HlsHandler
20509        (0, _renditionMixin2['default'])(_this3);
20510      });
20511
20512      // the bandwidth of the primary segment loader is our best
20513      // estimate of overall bandwidth
20514      this.on(this.masterPlaylistController_, 'progress', function () {
20515        this.tech_.trigger('progress');
20516      });
20517
20518      // In the live case, we need to ignore the very first `seeking` event since
20519      // that will be the result of the seek-to-live behavior
20520      this.on(this.masterPlaylistController_, 'firstplay', function () {
20521        this.ignoreNextSeekingEvent_ = true;
20522      });
20523
20524      this.tech_.ready(function () {
20525        return _this3.setupQualityLevels_();
20526      });
20527
20528      // do nothing if the tech has been disposed already
20529      // this can occur if someone sets the src in player.ready(), for instance
20530      if (!this.tech_.el()) {
20531        return;
20532      }
20533
20534      this.tech_.src(_videoJs2['default'].URL.createObjectURL(this.masterPlaylistController_.mediaSource));
20535    }
20536
20537    /**
20538     * Initializes the quality levels and sets listeners to update them.
20539     *
20540     * @method setupQualityLevels_
20541     * @private
20542     */
20543  }, {
20544    key: 'setupQualityLevels_',
20545    value: function setupQualityLevels_() {
20546      var _this4 = this;
20547
20548      var player = _videoJs2['default'].players[this.tech_.options_.playerId];
20549
20550      if (player && player.qualityLevels) {
vendor: 1,476 bytes, lines 20551-20610
20551        this.qualityLevels_ = player.qualityLevels();
20552
20553        this.masterPlaylistController_.on('selectedinitialmedia', function () {
20554          handleHlsLoadedMetadata(_this4.qualityLevels_, _this4);
20555        });
20556
20557        this.playlists.on('mediachange', function () {
20558          handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists);
20559        });
20560      }
20561    }
20562
20563    /**
20564     * Begin playing the video.
20565     */
20566  }, {
20567    key: 'play',
20568    value: function play() {
20569      this.masterPlaylistController_.play();
20570    }
20571
20572    /**
20573     * a wrapper around the function in MasterPlaylistController
20574     */
20575  }, {
20576    key: 'setCurrentTime',
20577    value: function setCurrentTime(currentTime) {
20578      this.masterPlaylistController_.setCurrentTime(currentTime);
20579    }
20580
20581    /**
20582     * a wrapper around the function in MasterPlaylistController
20583     */
20584  }, {
20585    key: 'duration',
20586    value: function duration() {
20587      return this.masterPlaylistController_.duration();
20588    }
20589
20590    /**
20591     * a wrapper around the function in MasterPlaylistController
20592     */
20593  }, {
20594    key: 'seekable',
20595    value: function seekable() {
20596      return this.masterPlaylistController_.seekable();
20597    }
20598
20599    /**
20600    * Abort all outstanding work and cleanup.
20601    */
20602  }, {
20603    key: 'dispose',
20604    value: function dispose() {
20605      if (this.playbackWatcher_) {
20606        this.playbackWatcher_.dispose();
20607      }
20608      if (this.masterPlaylistController_) {
20609        this.masterPlaylistController_.dispose();
20610      }
20611      if (this.qualityLevels_) {
20612        this.qualityLevels_.dispose();
20613      }
20614      _get(Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this);
20615    }
20616  }]);
20617
20618  return HlsHandler;
20619})(Component);
20620
20621var HlsSourceHandler = function HlsSourceHandler(mode) {
20622  return {
20623    canHandleSource: function canHandleSource(srcObj) {
20624      var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
20625
20626      var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options);
20627
20628      // this forces video.js to skip this tech/mode if its not the one we have been
20629      // overriden to use, by returing that we cannot handle the source.
20630      if (localOptions.hls && localOptions.hls.mode && localOptions.hls.mode !== mode) {
20631        return false;
20632      }
20633      return HlsSourceHandler.canPlayType(srcObj.type, localOptions);
20634    },
20635    handleSource: function handleSource(source, tech) {
20636      var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
20637
20638      var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options, { hls: { mode: mode } });
20639
20640      if (mode === 'flash') {
20641        // We need to trigger this asynchronously to give others the chance
20642        // to bind to the event when a source is set at player creation
20643        tech.setTimeout(function () {
20644          tech.trigger('loadstart');
20645        }, 1);
20646      }
20647
20648      tech.hls = new HlsHandler(source, tech, localOptions);
20649      tech.hls.xhr = (0, _xhr2['default'])();
20650
20651      tech.hls.src(source.src);
20652      return tech.hls;
20653    },
20654    canPlayType: function canPlayType(type) {
20655      var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
20656
20657      var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options);
20658
20659      if (HlsSourceHandler.canPlayType(type, localOptions)) {
20660        return 'maybe';
20661      }
20662      return '';
20663    }
20664  };
20665};
20666
20667HlsSourceHandler.canPlayType = function (type, options) {
20668  // No support for IE 10 or below
20669  if (_videoJs2['default'].browser.IE_VERSION && _videoJs2['default'].browser.IE_VERSION <= 10) {
20670    return false;
20671  }
20672
20673  var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
20674
20675  // favor native HLS support if it's available
20676  if (!options.hls.overrideNative && Hls.supportsNativeHls) {
20677    return false;
20678  }
20679  return mpegurlRE.test(type);
20680};
20681
20682if (typeof _videoJs2['default'].MediaSource === 'undefined' || typeof _videoJs2['default'].URL === 'undefined') {
20683  _videoJs2['default'].MediaSource = _videojsContribMediaSources.MediaSource;
20684  _videoJs2['default'].URL = _videojsContribMediaSources.URL;
20685}
20686
20687var flashTech = _videoJs2['default'].getTech('Flash');
20688
20689// register source handlers with the appropriate techs
20690if (_videojsContribMediaSources.MediaSource.supportsNativeMediaSources()) {
20691  _videoJs2['default'].getTech('Html5').registerSourceHandler(HlsSourceHandler('html5'), 0);
20692}
20693if (_globalWindow2['default'].Uint8Array && flashTech) {
20694  flashTech.registerSourceHandler(HlsSourceHandler('flash'));
20695}
20696
20697_videoJs2['default'].HlsHandler = HlsHandler;
20698_videoJs2['default'].HlsSourceHandler = HlsSourceHandler;
20699_videoJs2['default'].Hls = Hls;
20700if (!_videoJs2['default'].use) {
20701  _videoJs2['default'].registerComponent('Hls', Hls);
20702}
20703_videoJs2['default'].m3u8 = _m3u8Parser2['default'];
20704_videoJs2['default'].options.hls = _videoJs2['default'].options.hls || {};
20705
20706if (_videoJs2['default'].registerPlugin) {
20707  _videoJs2['default'].registerPlugin('reloadSourceOnError', _reloadSourceOnError2['default']);
20708} else {
20709  _videoJs2['default'].plugin('reloadSourceOnError', _reloadSourceOnError2['default']);
20710}
20711
20712module.exports = {
20713  Hls: Hls,
20714  HlsHandler: HlsHandler,
20715  HlsSourceHandler: HlsSourceHandler
20716};
20717}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
20718},{"./bin-utils":2,"./config":3,"./master-playlist-controller":5,"./playback-watcher":8,"./playlist":11,"./playlist-loader":9,"./playlist-selectors.js":10,"./reload-source-on-error":13,"./rendition-mixin":14,"./xhr":21,"aes-decrypter":25,"global/document":31,"global/window":32,"m3u8-parser":33,"videojs-contrib-media-sources":74}]},{},[77])(77)
20719});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.