PageSourceSearch

https://tiin.vn/adsense/js/videojs-contrib-hls.js

js tiin.vn collected 2026-09-24 21:45:33 UTC 858,242 bytes, 19,145 lines download raw bytes

1/**
2 * videojs-contrib-hls
3 * @version 5.5.3
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
13        Object.defineProperty(exports, '__esModule', {
14            value: true
15        });
16
17        var _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
19        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
20
21        var _globalWindow = require('global/window');
22
23        var _globalWindow2 = _interopRequireDefault(_globalWindow);
24
25        /**
26         * Searches for an ad cue that overlaps with the given mediaTime
27         */
28        var 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
41        var 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
115        exports['default'] = {
116            updateAdCues: updateAdCues,
117            findAdCue: findAdCue
118        };
119        module.exports = exports['default'];
120    },{"global/window":30}],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
133        Object.defineProperty(exports, '__esModule', {
134            value: true
135        });
136        var 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         */
146        var formatHexString = function formatHexString(e, i) {
147            var value = e.toString(16);
148
149            return '00'.substring(0, 2 - value.length) + value + (i % 2 ? ' ' : '');
150        };
151        var 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         */
168        var 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         */
192        var 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         */
204        var 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
235        exports['default'] = utils;
236        module.exports = exports['default'];
237    },{}],3:[function(require,module,exports){
238        "use strict";
239
240        Object.defineProperty(exports, "__esModule", {
241            value: true
242        });
243        exports["default"] = {
244            GOAL_BUFFER_LENGTH: 30,
245            // A fudge factor to apply to advertised playlist bitrates to account for
246            // temporary flucations in client bandwidth
247            BANDWIDTH_VARIANCE: 1.2
248        };
249        module.exports = exports["default"];
250    },{}],4:[function(require,module,exports){
251        'use strict';
252
253        Object.defineProperty(exports, '__esModule', {
254            value: true
255        });
256
257        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
258
259        var _globalWindow = require('global/window');
260
261        var _globalWindow2 = _interopRequireDefault(_globalWindow);
262
263        var _aesDecrypter = require('aes-decrypter');
264
265        var _binUtils = require('./bin-utils');
266
267        /**
268         * Our web worker interface so that things can talk to aes-decrypter
269         * that will be running in a web worker. the scope is passed to this by
270         * webworkify.
271         *
272         * @param {Object} self
273         *        the scope for the web worker
274         */
275        var DecrypterWorker = function DecrypterWorker(self) {
276            self.onmessage = function (event) {
277                var data = event.data;
278                var encrypted = new Uint8Array(data.encrypted.bytes, data.encrypted.byteOffset, data.encrypted.byteLength);
279                var key = new Uint32Array(data.key.bytes, data.key.byteOffset, data.key.byteLength / 4);
280                var iv = new Uint32Array(data.iv.bytes, data.iv.byteOffset, data.iv.byteLength / 4);
281
282                /* eslint-disable no-new, handle-callback-err */
283                new _aesDecrypter.Decrypter(encrypted, key, iv, function (err, bytes) {
284                    _globalWindow2['default'].postMessage((0, _binUtils.createTransferableMessage)({
285                        source: data.source,
286                        decrypted: bytes
287                    }), [bytes.buffer]);
288                });
289                /* eslint-enable */
290            };
291        };
292
293        exports['default'] = function (self) {
294            return new DecrypterWorker(self);
295        };
296
297        module.exports = exports['default'];
298    },{"./bin-utils":2,"aes-decrypter":23,"global/window":30}],5:[function(require,module,exports){
299        (function (global){
300            /**
301             * @file master-playlist-controller.js
302             */
303            'use strict';
304
305            Object.defineProperty(exports, '__esModule', {
306                value: true
307            });
308
309            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
310
311            var _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); } } };
312
313            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
314
315            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
316
317            function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
317onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
318
319            var _playlistLoader = require('./playlist-loader');
320
321            var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
322
323            var _segmentLoader = require('./segment-loader');
324
325            var _segmentLoader2 = _interopRequireDefault(_segmentLoader);
326
327            var _vttSegmentLoader = require('./vtt-segment-loader');
328
329            var _vttSegmentLoader2 = _interopRequireDefault(_vttSegmentLoader);
330
331            var _ranges = require('./ranges');
332
333            var _ranges2 = _interopRequireDefault(_ranges);
334
335            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
336
337            var _videoJs2 = _interopRequireDefault(_videoJs);
338
339            var _adCueTags = require('./ad-cue-tags');
340
341            var _adCueTags2 = _interopRequireDefault(_adCueTags);
342
343            var _syncController = require('./sync-controller');
344
345            var _syncController2 = _interopRequireDefault(_syncController);
346
347            var _videojsContribMediaSourcesEs5CodecUtils = require('videojs-contrib-media-sources/es5/codec-utils');
348
349            var _webworkify = require('webworkify');
350
351            var _webworkify2 = _interopRequireDefault(_webworkify);
352
353            var _decrypterWorker = require('./decrypter-worker');
354
355            var _decrypterWorker2 = _interopRequireDefault(_decrypterWorker);
356
357            var Hls = undefined;
358
359// Default codec parameters if none were provided for video and/or audio
360            var defaultCodecs = {
361                videoCodec: 'avc1',
362                videoObjectTypeIndicator: '.4d400d',
363                // AAC-LC
364                audioProfile: '2'
365            };
366
367// SegmentLoader stats that need to have each loader's
368// values summed to calculate the final value
369            var loaderStats = ['mediaRequests', 'mediaRequestsAborted', 'mediaRequestsTimedout', 'mediaRequestsErrored', 'mediaTransferDuration', 'mediaBytesTransferred'];
370            var sumLoaderStat = function sumLoaderStat(stat) {
371                return this.audioSegmentLoader_[stat] + this.mainSegmentLoader_[stat];
372            };
373
374            /**
375             * determine if an object a is differnt from
376             * and object b. both only having one dimensional
377             * properties
378             *
379             * @param {Object} a object one
380             * @param {Object} b object two
381             * @return {Boolean} if the object has changed or not
382             */
383            var objectChanged = function objectChanged(a, b) {
384                if (typeof a !== typeof b) {
385                    return true;
386                }
387                // if we have a different number of elements
388                // something has changed
389                if (Object.keys(a).length !== Object.keys(b).length) {
390                    return true;
391                }
392
393                for (var prop in a) {
394                    if (a[prop] !== b[prop]) {
395                        return true;
396                    }
397                }
398                return false;
399            };
400
401            /**
402             * Parses a codec string to retrieve the number of codecs specified,
403             * the video codec and object type indicator, and the audio profile.
404             *
405             * @private
406             */
407            var parseCodecs = function parseCodecs(codecs) {
408                var result = {
409                    codecCount: 0
410                };
411                var parsed = undefined;
412
413                result.codecCount = codecs.split(',').length;
414                result.codecCount = result.codecCount || 2;
415
416                // parse the video codec
417                parsed = /(^|\s|,)+(avc1)([^ ,]*)/i.exec(codecs);
418                if (parsed) {
419                    result.videoCodec = parsed[2];
420                    result.videoObjectTypeIndicator = parsed[3];
421                }
422
423                // parse the last field of the audio codec
424                result.audioProfile = /(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(codecs);
425                result.audioProfile = result.audioProfile && result.audioProfile[2];
426
427                return result;
428            };
429
430            /**
431             * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
432             * standard `avc1.<hhhhhh>`.
433             *
434             * @param codecString {String} the codec string
435             * @return {String} the codec string with old apple-style codecs replaced
436             *
437             * @private
438             */
439            var mapLegacyAvcCodecs_ = function mapLegacyAvcCodecs_(codecString) {
440                return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) {
441                    return (0, _videojsContribMediaSourcesEs5CodecUtils.translateLegacyCodecs)([match])[0];
442                });
443            };
444
445            exports.mapLegacyAvcCodecs_ = mapLegacyAvcCodecs_;
446            /**
447             * Build a media mime-type string from a set of parameters
448             * @param {String} type either 'audio' or 'video'
449             * @param {String} container either 'mp2t' or 'mp4'
450             * @param {Array} codecs an array of codec strings to add
451             * @return {String} a valid media mime-type
452             */
453            var makeMimeTypeString = function makeMimeTypeString(type, container, codecs) {
454                // The codecs array is filtered so that falsey values are
455                // dropped and don't cause Array#join to create spurious
456                // commas
457                return type + '/' + container + '; codecs="' + codecs.filter(function (c) {
458                    return !!c;
459                }).join(', ') + '"';
460            };
461
462            /**
463             * Returns the type container based on information in the playlist
464             * @param {Playlist} media the current media playlist
465             * @return {String} a valid media container type
466             */
467            var getContainerType = function getContainerType(media) {
468                // An initialization segment means the media playlist is an iframe
469                // playlist or is using the mp4 container. We don't currently
470                // support iframe playlists, so assume this is signalling mp4
471                // fragments.
472                if (media.segments && media.segments.length && media.segments[0].map) {
473                    return 'mp4';
474                }
475                return 'mp2t';
476            };
477
478            /**
479             * Returns a set of codec strings parsed from the playlist or the default
480             * codec strings if no codecs were specified in the playlist
481             * @param {Playlist} media the current media playlist
482             * @return {Object} an object with the video and audio codecs
483             */
484            var getCodecs = function getCodecs(media) {
485                // if the codecs were explicitly specified, use them instead of the
486                // defaults
487                var mediaAttributes = media.attributes || {};
488
489                if (mediaAttributes.CODECS) {
490                    return parseCodecs(mediaAttributes.CODECS);
491                }
492                return defaultCodecs;
493            };
494
495            /**
496             * Calculates the MIME type strings for a working configuration of
497             * SourceBuffers to play variant streams in a master playlist. If
498             * there is no possible working configuration, an empty array will be
499             * returned.
500             *
501             * @param master {Object} the m3u8 object for the master playlist
502             * @param media {Object} the m3u8 object for the variant playlist
503             * @return {Array} the MIME type strings. If the array has more than
504             * one entry, the first element should be applied to the video
505             * SourceBuffer and the second to the audio SourceBuffer.
506             *
507             * @private
508             */
509            var mimeTypesForPlaylist_ = function mimeTypesForPlaylist_(master, media) {
510                var containerType = getContainerType(media);
511                var codecInfo = getCodecs(media);
512                var mediaAttributes = media.attributes || {};
513                // Default condition for a traditional HLS (no demuxed audio/video)
514                var isMuxed = true;
515                var isMaat = false;
516
517                if (!media) {
518                    // Not enough information
519                    return [];
520                }
521
522                if (master.mediaGroups.AUDIO && mediaAttributes.AUDIO) {
523                    var audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO];
524
525                    // Handle the case where we are in a multiple-audio track scenario
526                    if (audioGroup) {
527                        isMaat = true;
528                        // Start with the everything demuxed then...
529                        isMuxed = false;
530                        // ...check to see if any audio group tracks are muxed (ie. lacking a uri)
531                        for (var groupId in audioGroup) {
532                            if (!audioGroup[groupId].uri) {
533                                isMuxed = true;
534                                break;
535                            }
536                        }
537                    }
538                }
539
540                // HLS with multiple-audio tracks must always get an audio codec.
541                // Put another way, there is no way to have a video-only multiple-audio HLS!
542                if (isMaat && !codecInfo.audioProfile) {
543                    _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)');
544                    codecInfo.audioProfile = defaultCodecs.audioProfile;
545                }
546
547                // Generate the final codec strings from the codec object generated above
548                var codecStrings = {};
549
550                if (codecInfo.videoCodec) {
551                    codecStrings.video = '' + codecInfo.videoCodec + codecInfo.videoObjectTypeIndicator;
552                }
553
554                if (codecInfo.audioProfile) {
555                    codecStrings.audio = 'mp4a.40.' + codecInfo.audioProfile;
556                }
557
558                // Finally, make and return an array with proper mime-types depending on
559                // the configuration
560                var justAudio = makeMimeTypeString('audio', containerType, [codecStrings.audio]);
561                var justVideo = makeMimeTypeString('video', containerType, [codecStrings.video]);
562                var bothVideoAudio = makeMimeTypeString('video', containerType, [codecStrings.video, codecStrings.audio]);
563
564                if (isMaat) {
565                    if (!isMuxed && codecStrings.video) {
566                        return [justVideo, justAudio];
567                    }
568                    // There exists the possiblity that this will return a `video/container`
569                    // mime-type for the first entry in the array even when there is only audio.
570                    // This doesn't appear to be a problem and simplifies the code.
571                    return [bothVideoAudio, justAudio];
572                }
573
574                // If there is ano video codec at all, always just return a single
575                // audio/<container> mime-type
576                if (!codecStrings.video) {
577                    return [justAudio];
578                }
579
580                // When not using separate audio media groups, audio and video is
581                // *always* muxed
582                return [bothVideoAudio];
583            };
584
585            exports.mimeTypesForPlaylist_ = mimeTypesForPlaylist_;
586            /**
587             * the master playlist controller controller all interactons
588             * between playlists and segmentloaders. At this time this mainly
589             * involves a master playlist and a series of audio playlists
590             * if they are available
591             *
592             * @class MasterPlaylistController
593             * @extends videojs.EventTarget
594             */
595
596            var MasterPlaylistController = (function (_videojs$EventTarget) {
597                _inherits(MasterPlaylistController, _videojs$EventTarget);
598
599                function MasterPlaylistController(options) {
600                    var _this = this;
601
602                    _classCallCheck(this, MasterPlaylistController);
603
604                    _get(Object.getPrototypeOf(MasterPlaylistController.prototype), 'constructor', this).call(this);
605
606                    var url = options.url;
607                    var withCredentials = options.withCredentials;
608                    var mode = options.mode;
609                    var tech = options.tech;
610                    var bandwidth = options.bandwidth;
611                    var externHls = options.externHls;
612                    var useCueTags = options.useCueTags;
613                    var blacklistDuration = options.blacklistDuration;
614
615                    if (!url) {
616                        throw new Error('A non-empty playlist URL is required');
617                    }
618
619                    Hls = externHls;
620
621                    this.withCredentials = withCredentials;
622                    this.tech_ = tech;
623                    this.hls_ = tech.hls;
624                    this.mode_ = mode;
625                    this.useCueTags_ = useCueTags;
626                    this.blacklistDuration = blacklistDuration;
627                    if (this.useCueTags_) {
628                        this.cueTagsTrack_ = this.tech_.addTextTrack('metadata', 'ad-cues');
629                        this.cueTagsTrack_.inBandMetadataTrackDispatchType = '';
630                    }
631
632                    this.requestOptions_ = {
633                        withCredentials: this.withCredentials,
634                        timeout: null
635                    };
636
637                    this.audioGroups_ = {};
638                    this.subtitleGroups_ = { groups: {}, tracks: {} };
639
640                    this.mediaSource = new _videoJs2['default'].MediaSource({ mode: mode });
641                    this.audioinfo_ = null;
642                    this.mediaSource.on('audioinfo', this.handleAudioinfoUpdate_.bind(this));
643
644                    // load the media source into the player
645                    this.mediaSource.addEventListener('sourceopen', this.handleSourceOpen_.bind(this));
646
647                    this.seekable_ = _videoJs2['default'].createTimeRanges();
648                    this.hasPlayed_ = function () {
649                        return false;
650                    };
651
652                    this.syncController_ = new _syncController2['default']();
653                    this.segmentMetadataTrack_ = tech.addRemoteTextTrack({
654                        kind: 'metadata',
655                        label: 'segment-metadata'
656                    }, true).track;
657
658                    this.decrypter_ = (0, _webworkify2['default'])(_decrypterWorker2['default']);
659
660                    var segmentLoaderOptions = {
661                        hls: this.hls_,
662                        mediaSource: this.mediaSource,
663                        currentTime: this.tech_.currentTime.bind(this.tech_),
664                        seekable: function seekable() {
665                            return _this.seekable();
666                        },
667                        seeking: function seeking() {
668                            return _this.tech_.seeking();
669                        },
670                        duration: function duration() {
671                            return _this.mediaSource.duration;
672                        },
673                        hasPlayed: function hasPlayed() {
674                            return _this.hasPlayed_();
675                        },
676                        bandwidth: bandwidth,
677                        syncController: this.syncController_,
678                        decrypter: this.decrypter_
679                    };
680
681                    // setup playlist loaders
682                    this.masterPlaylistLoader_ = new _playlistLoader2['default'](url, this.hls_, this.withCredentials);
683                    this.setupMasterPlaylistLoaderListeners_();
684                    this.audioPlaylistLoader_ = null;
685                    this.subtitlePlaylistLoader_ = null;
686
687                    // setup segment loaders
688                    // combined audio/video or just video when alternate audio track is selected
689                    this.mainSegmentLoader_ = new _segmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderOptions, {
690                        segmentMetadataTrack: this.segmentMetadataTrack_,
691                        loaderType: 'main'
692                    }));
693
694                    // alternate audio track
695                    this.audioSegmentLoader_ = new _segmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderOptions, {
696                        loaderType: 'audio'
697                    }));
698
699                    this.subtitleSegmentLoader_ = new _vttSegmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderOptions, {
700                        loaderType: 'vtt'
701                    }));
702
703                    this.setupSegmentLoaderListeners_();
704
705                    // Create SegmentLoader stat-getters
706                    loaderStats.forEach(function (stat) {
707                        _this[stat + '_'] = sumLoaderStat.bind(_this, stat);
708                    });
709
710                    this.masterPlaylistLoader_.load();
711                }
712
713                /**
714                 * Register event handlers on the master playlist loader. A helper
715                 * function for construction time.
716                 *
717                 * @private
718                 */
719
720                _createClass(MasterPlaylistController, [{
721                    key: 'setupMasterPlaylistLoaderListeners_',
722                    value: function setupMasterPlaylistLoaderListeners_() {
723                        var _this2 = this;
724
725                        this.masterPlaylistLoader_.on('loadedmetadata', function () {
726                            var media = _this2.masterPlaylistLoader_.media();
727                            var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
728
729                            // If we don't have any more available playlists, we don't want to
730                            // timeout the request.
731                            if (_this2.masterPlaylistLoader_.isLowestEnabledRendition_()) {
732                                _this2.requestOptions_.timeout = 0;
733                            } else {
734                                _this2.requestOptions_.timeout = requestTimeout;
735                            }
736
737                            // if this isn't a live video and preload permits, start
738                            // downloading segments
739                            if (media.endList && _this2.tech_.preload() !== 'none') {
740                                _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
741                                _this2.mainSegmentLoader_.load();
742                            }
743
744                            _this2.fillAudioTracks_();
745                            _this2.setupAudio();
746
747                            _this2.fillSubtitleTracks_();
748                            _this2.setupSubtitles();
749
750                            try {
751                                _this2.setupSourceBuffers_();
752                            } catch (e) {
753                                _videoJs2['default'].log.warn('Failed to create SourceBuffers', e);
754                                return _this2.mediaSource.endOfStream('decode');
755                            }
756                            _this2.setupFirstPlay();
757
758                            _this2.trigger('audioupdate');
759                            _this2.trigger('selectedinitialmedia');
760                        });
761
762                        this.masterPlaylistLoader_.on('loadedplaylist', function () {
763                            var updatedPlaylist = _this2.masterPlaylistLoader_.media();
764
765                            if (!updatedPlaylist) {
766                                // select the initial variant
767                                _this2.initialMedia_ = _this2.selectPlaylist();
768                                _this2.masterPlaylistLoader_.media(_this2.initialMedia_);
769                                return;
770                            }
771
772                            if (_this2.useCueTags_) {
773                                _this2.updateAdCues_(updatedPlaylist);
774                            }
775
776                            // TODO: Create a new event on the PlaylistLoader that signals
777                            // that the segments have changed in some way and use that to
778                            // update the SegmentLoader instead of doing it twice here and
779                            // on `mediachange`
780                            _this2.mainSegmentLoader_.playlist(updatedPlaylist, _this2.requestOptions_);
781                            _this2.updateDuration();
782
783                            // If the player isn't paused, ensure that the segment loader is running,
784                            // as it is possible that it was temporarily stopped while waiting for
785                            // a playlist (e.g., in case the playlist errored and we re-requested it).
786                            if (!_this2.tech_.paused()) {
787                                _this2.mainSegmentLoader_.load();
788                            }
789
790                            if (!updatedPlaylist.endList) {
791                                (function () {
792                                    var addSeekableRange = function addSeekableRange() {
793                                        var seekable = _this2.seekable();
794
795                                        if (seekable.length !== 0) {
796                                            _this2.mediaSource.addSeekableRange_(seekable.start(0), seekable.end(0));
797                                        }
798                                    };
799
800                                    if (_this2.duration() !== Infinity) {
801                                        (function () {
802                                            var onDurationchange = function onDurationchange() {
803                                                if (_this2.duration() === Infinity) {
804                                                    addSeekableRange();
805                                                } else {
806                                                    _this2.tech_.one('durationchange', onDurationchange);
807                                                }
808                                            };
809
810                                            _this2.tech_.one('durationchange', onDurationchange);
811                                        })();
812                                    } else {
813                                        addSeekableRange();
814                                    }
815                                })();
816                            }
817                        });
818
819                        this.masterPlaylistLoader_.on('error', function () {
820                            _this2.blacklistCurrentPlaylist(_this2.masterPlaylistLoader_.error);
821                        });
822
823                        this.masterPlaylistLoader_.on('mediachanging', function () {
824                            _this2.mainSegmentLoader_.abort();
825                            _this2.mainSegmentLoader_.pause();
826                        });
827
828                        this.masterPlaylistLoader_.on('mediachange', function () {
829                            var media = _this2.masterPlaylistLoader_.media();
830                            var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
831                            var activeAudioGroup = undefined;
832                            var activeTrack = undefined;
833
834                            // If we don't have any more available playlists, we don't want to
835                            // timeout the request.
836                            if (_this2.masterPlaylistLoader_.isLowestEnabledRendition_()) {
837                                _this2.requestOptions_.timeout = 0;
838                            } else {
839                                _this2.requestOptions_.timeout = requestTimeout;
840                            }
841
842                            // TODO: Create a new event on the PlaylistLoader that signals
843                            // that the segments have changed in some way and use that to
844                            // update the SegmentLoader instead of doing it twice here and
845                            // on `loadedplaylist`
846                            _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
847                            _this2.mainSegmentLoader_.load();
848
849                            // if the audio group has changed, a new audio track has to be
850                            // enabled
851                            activeAudioGroup = _this2.activeAudioGroup();
852                            activeTrack = activeAudioGroup.filter(function (track) {
853                                return track.enabled;
854                            })[0];
855                            if (!activeTrack) {
856                                _this2.setupAudio();
857                                _this2.trigger('audioupdate');
858                            }
859                            _this2.setupSubtitles();
860
861                            _this2.tech_.trigger({
862                                type: 'mediachange',
863                                bubbles: true
864                            });
865                        });
866
867                        this.masterPlaylistLoader_.on('playlistunchanged', function () {
868                            var updatedPlaylist = _this2.masterPlaylistLoader_.media();
869                            var playlistOutdated = _this2.stuckAtPlaylistEnd_(updatedPlaylist);
870
871                            if (playlistOutdated) {
872                                // Playlist has stopped updating and we're stuck at its end. Try to
873                                // blacklist it and switch to another playlist in the hope that that
874                                // one is updating (and give the player a chance to re-adjust to the
875                                // safe live point).
876                                _this2.blacklistCurrentPlaylist({
877                                    message: 'Playlist no longer updating.'
878                                });
879                                // useful for monitoring QoS
880                                _this2.tech_.trigger('playliststuck');
881                            }
882                        });
883                    }
884
885                    /**
886                     * Register event handlers on the segment loaders. A helper function
887                     * for construction time.
888                     *
889                     * @private
890                     */
891                }, {
892                    key: 'setupSegmentLoaderListeners_',
893                    value: function setupSegmentLoaderListeners_() {
894                        var _this3 = this;
895
896                        this.mainSegmentLoader_.on('bandwidthupdate', function () {
897                            // figure out what stream the next segment should be downloaded from
898                            // with the updated bandwidth information
899                            _this3.masterPlaylistLoader_.media(_this3.selectPlaylist());
900                        });
901                        this.mainSegmentLoader_.on('progress', function () {
902                            _this3.trigger('progress');
903                        });
904
905                        this.mainSegmentLoader_.on('error', function () {
906                            _this3.blacklistCurrentPlaylist(_this3.mainSegmentLoader_.error());
907                        });
908
909                        this.mainSegmentLoader_.on('syncinfoupdate', function () {
910                            _this3.onSyncInfoUpdate_();
911                        });
912
913                        this.audioSegmentLoader_.on('syncinfoupdate', function () {
914                            _this3.onSyncInfoUpdate_();
915                        });
916
917                        this.audioSegmentLoader_.on('error', function () {
918                            _videoJs2['default'].log.warn('Problem encountered with the current alternate audio track' + '. Switching back to default.');
919                            _this3.audioSegmentLoader_.abort();
920                            _this3.audioPlaylistLoader_ = null;
921                            _this3.setupAudio();
922                        });
923
924                        this.subtitleSegmentLoader_.on('error', this.handleSubtitleError_.bind(this));
925                    }
926                }, {
927                    key: 'handleAudioinfoUpdate_',
928                    value: function handleAudioinfoUpdate_(event) {
929                        if (Hls.supportsAudioInfoChange_() || !this.audioInfo_ || !
929objectChanged(this.audioInfo_, event.info)) {
930                            this.audioInfo_ = event.info;
931                            return;
932                        }
933
934                        var error = 'had different audio properties (channels, sample rate, etc.) ' + 'or changed in some other way.  This behavior is currently ' + 'unsupported in Firefox 48 and below due to an issue: \n\n' + 'https://bugzilla.mozilla.org/show_bug.cgi?id=1247138\n\n';
935
936                        var enabledIndex = this.activeAudioGroup().map(function (track) {
937                            return track.enabled;
938                        }).indexOf(true);
939                        var enabledTrack = this.activeAudioGroup()[enabledIndex];
940                        var defaultTrack = this.activeAudioGroup().filter(function (track) {
941                            return track.properties_ && track.properties_['default'];
942                        })[0];
943
944                        // they did not switch audiotracks
945                        // blacklist the current playlist
946                        if (!this.audioPlaylistLoader_) {
947                            error = 'The rendition that we tried to switch to ' + error + 'Unfortunately that means we will have to blacklist ' + 'the current playlist and switch to another. Sorry!';
948                            this.blacklistCurrentPlaylist();
949                        } else {
950                            error = 'The audio track \'' + enabledTrack.label + '\' that we tried to ' + ('switch to ' + error + ' Unfortunately this means we will have to ') + ('return you to the main track \'' + defaultTrack.label + '\'. Sorry!');
951                            defaultTrack.enabled = true;
952                            this.activeAudioGroup().splice(enabledIndex, 1);
953                            this.trigger('audioupdate');
954                        }
955
956                        _videoJs2['default'].log.warn(error);
957                        this.setupAudio();
958                    }
959                }, {
960                    key: 'mediaSecondsLoaded_',
961                    value: function mediaSecondsLoaded_() {
962                        return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded + this.mainSegmentLoader_.mediaSecondsLoaded);
963                    }
964
965                    /**
966                     * fill our internal list of HlsAudioTracks with data from
967                     * the master playlist or use a default
968                     *
969                     * @private
970                     */
971                }, {
972                    key: 'fillAudioTracks_',
973                    value: function fillAudioTracks_() {
974                        var master = this.master();
975                        var mediaGroups = master.mediaGroups || {};
976
977                        // force a default if we have none or we are not
978                        // in html5 mode (the only mode to support more than one
979                        // audio track)
980                        if (!mediaGroups || !mediaGroups.AUDIO || Object.keys(mediaGroups.AUDIO).length === 0 || this.mode_ !== 'html5') {
981                            // "main" audio group, track name "default"
982                            mediaGroups.AUDIO = { main: { 'default': { 'default': true } } };
983                        }
984
985                        for (var mediaGroup in mediaGroups.AUDIO) {
986                            if (!this.audioGroups_[mediaGroup]) {
987                                this.audioGroups_[mediaGroup] = [];
988                            }
989
990                            for (var label in mediaGroups.AUDIO[mediaGroup]) {
991                                var properties = mediaGroups.AUDIO[mediaGroup][label];
992                                var track = new _videoJs2['default'].AudioTrack({
993                                    id: label,
994                                    kind: this.audioTrackKind_(properties),
995                                    enabled: false,
996                                    language: properties.language,
997                                    label: label
998                                });
999
1000                                track.properties_ = properties;
1001                                this.audioGroups_[mediaGroup].push(track);
1002                            }
1003                        }
1004
1005                        // enable the default active track
1006                        (this.activeAudioGroup().filter(function (audioTrack) {
1007                            return audioTrack.properties_['default'];
1008                        })[0] || this.activeAudioGroup()[0]).enabled = true;
1009                    }
1010
1011                    /**
1012                     * Convert the properties of an HLS track into an audioTrackKind.
1013                     *
1014                     * @private
1015                     */
1016                }, {
1017                    key: 'audioTrackKind_',
1018                    value: function audioTrackKind_(properties) {
1019                        var kind = properties['default'] ? 'main' : 'alternative';
1020
1021                        if (properties.characteristics && properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) {
1022                            kind = 'main-desc';
1023                        }
1024
1025                        return kind;
1026                    }
1027
1028                    /**
1029                     * fill our internal list of Subtitle Tracks with data from
1030                     * the master playlist or use a default
1031                     *
1032                     * @private
1033                     */
1034                }, {
1035                    key: 'fillSubtitleTracks_',
1036                    value: function fillSubtitleTracks_() {
1037                        var master = this.master();
1038                        var mediaGroups = master.mediaGroups || {};
1039
1040                        for (var mediaGroup in mediaGroups.SUBTITLES) {
1041                            if (!this.subtitleGroups_.groups[mediaGroup]) {
1042                                this.subtitleGroups_.groups[mediaGroup] = [];
1043                            }
1044
1045                            for (var label in mediaGroups.SUBTITLES[mediaGroup]) {
1046                                var properties = mediaGroups.SUBTITLES[mediaGroup][label];
1047
1048                                if (!properties.forced) {
1049                                    this.subtitleGroups_.groups[mediaGroup].push(_videoJs2['default'].mergeOptions({ id: label }, properties));
1050
1051                                    if (typeof this.subtitleGroups_.tracks[label] === 'undefined') {
1052                                        var track = this.tech_.addRemoteTextTrack({
1053                                            id: label,
1054                                            kind: 'subtitles',
1055                                            enabled: false,
1056                                            language: properties.language,
1057                                            label: label
1058                                        }, true).track;
1059
1060                                        this.subtitleGroups_.tracks[label] = track;
1061                                    }
1062                                }
1063                            }
1064                        }
1065
1066                        // Do not enable a default subtitle track. Wait for user interaction instead.
1067                    }
1068
1069                    /**
1070                     * Call load on our SegmentLoaders
1071                     */
1072                }, {
1073                    key: 'load',
1074                    value: function load() {
1075                        this.mainSegmentLoader_.load();
1076                        if (this.audioPlaylistLoader_) {
1077                            this.audioSegmentLoader_.load();
1078                        }
1079                        if (this.subtitlePlaylistLoader_) {
1080                            this.subtitleSegmentLoader_.load();
1081                        }
1082                    }
1083
1084                    /**
1085                     * Returns the audio group for the currently active primary
1086                     * media playlist.
1087                     */
1088                }, {
1089                    key: 'activeAudioGroup',
1090                    value: function activeAudioGroup() {
1091                        var videoPlaylist = this.masterPlaylistLoader_.media();
1092                        var result = undefined;
1093
1094                        if (videoPlaylist.attributes && videoPlaylist.attributes.AUDIO) {
1095                            result = this.audioGroups_[videoPlaylist.attributes.AUDIO];
1096                        }
1097
1098                        return result || this.audioGroups_.main;
1099                    }
1100
1101                    /**
1102                     * Returns the subtitle group for the currently active primary
1103                     * media playlist.
1104                     */
1105                }, {
1106                    key: 'activeSubtitleGroup_',
1107                    value: function activeSubtitleGroup_() {
1108                        var videoPlaylist = this.masterPlaylistLoader_.media();
1109                        var result = undefined;
1110
1111                        if (!videoPlaylist) {
1112                            return null;
1113                        }
1114
1115                        if (videoPlaylist.attributes && videoPlaylist.attributes.SUBTITLES) {
1116                            result = this.subtitleGroups_.groups[videoPlaylist.attributes.SUBTITLES];
1117                        }
1118
1119                        return result || this.subtitleGroups_.groups.main;
1120                    }
1121                }, {
1122                    key: 'activeSubtitleTrack_',
1123                    value: function activeSubtitleTrack_() {
1124                        for (var trackName in this.subtitleGroups_.tracks) {
1125                            if (this.subtitleGroups_.tracks[trackName].mode === 'showing') {
1126                                return this.subtitleGroups_.tracks[trackName];
1127                            }
1128                        }
1129
1130                        return null;
1131                    }
1132                }, {
1133                    key: 'handleSubtitleError_',
1134                    value: function handleSubtitleError_() {
1135                        _videoJs2['default'].log.warn('Problem encountered loading the subtitle track' + '. Switching back to default.');
1136
1137                        this.subtitleSegmentLoader_.abort();
1138
1139                        var track = this.activeSubtitleTrack_();
1140
1141                        if (track) {
1142                            track.mode = 'disabled';
1143                        }
1144
1145                        this.setupSubtitles();
1146                    }
1147
1148                    /**
1149                     * Determine the correct audio rendition based on the active
1150                     * AudioTrack and initialize a PlaylistLoader and SegmentLoader if
1151                     * necessary. This method is called once automatically before
1152                     * playback begins to enable the default audio track and should be
1153                     * invoked again if the track is changed.
1154                     */
1155                }, {
1156                    key: 'setupAudio',
1157                    value: function setupAudio() {
1158                        var _this4 = this;
1159
1160                        // determine whether seperate loaders are required for the audio
1161                        // rendition
1162                        var audioGroup = this.activeAudioGroup();
1163                        var track = audioGroup.filter(function (audioTrack) {
1164                            return audioTrack.enabled;
1165                        })[0];
1166
1167                        if (!track) {
1168                            track = audioGroup.filter(function (audioTrack) {
1169                                return audioTrack.properties_['default'];
1170                            })[0] || audioGroup[0];
1171                            track.enabled = true;
1172                        }
1173
1174                        // stop playlist and segment loading for audio
1175                        if (this.audioPlaylistLoader_) {
1176                            this.audioPlaylistLoader_.dispose();
1177                            this.audioPlaylistLoader_ = null;
1178                        }
1179                        this.audioSegmentLoader_.pause();
1180
1181                        if (!track.properties_.resolvedUri) {
1182                            this.mainSegmentLoader_.resetEverything();
1183                            return;
1184                        }
1185                        this.audioSegmentLoader_.resetEverything();
1186
1187                        // startup playlist and segment loaders for the enabled audio
1188                        // track
1189                        this.audioPlaylistLoader_ = new _playlistLoader2['default'](track.properties_.resolvedUri, this.hls_, this.withCredentials);
1190                        this.audioPlaylistLoader_.load();
1191
1192                        this.audioPlaylistLoader_.on('loadedmetadata', function () {
1193                            var audioPlaylist = _this4.audioPlaylistLoader_.media();
1194
1195                            _this4.audioSegmentLoader_.playlist(audioPlaylist, _this4.requestOptions_);
1196
1197                            // if the video is already playing, or if this isn't a live video and preload
1198                            // permits, start downloading segments
1199                            if (!_this4.tech_.paused() || audioPlaylist.endList && _this4.tech_.preload() !== 'none') {
1200                                _this4.audioSegmentLoader_.load();
1201                            }
1202
1203                            if (!audioPlaylist.endList) {
1204                                _this4.audioPlaylistLoader_.trigger('firstplay');
1205                            }
1206                        });
1207
1208                        this.audioPlaylistLoader_.on('loadedplaylist', function () {
1209                            var updatedPlaylist = undefined;
1210
1211                            if (_this4.audioPlaylistLoader_) {
1212                                updatedPlaylist = _this4.audioPlaylistLoader_.media();
1213                            }
1214
1215                            if (!updatedPlaylist) {
1216                                // only one playlist to select
1217                                _this4.audioPlaylistLoader_.media(_this4.audioPlaylistLoader_.playlists.master.playlists[0]);
1218                                return;
1219                            }
1220
1221                            _this4.audioSegmentLoader_.playlist(updatedPlaylist, _this4.requestOptions_);
1222                        });
1223
1224                        this.audioPlaylistLoader_.on('error', function () {
1225                            _videoJs2['default'].log.warn('Problem encountered loading the alternate audio track' + '. Switching back to default.');
1226                            _this4.audioSegmentLoader_.abort();
1227                            _this4.setupAudio();
1228                        });
1229                    }
1230
1231                    /**
1232                     * Determine the correct subtitle playlist based on the active
1233                     * SubtitleTrack and initialize a PlaylistLoader and SegmentLoader if
1234                     * necessary. This method is called once automatically before
1235                     * playback begins to enable the default subtitle track and should be
1236                     * invoked again if the track is changed.
1237                     */
1238                }, {
1239                    key: 'setupSubtitles',
1240                    value: function setupSubtitles() {
1241                        var _this5 = this;
1242
1243                        var subtitleGroup = this.activeSubtitleGroup_();
1244                        var track = this.activeSubtitleTrack_();
1245
1246                        this.subtitleSegmentLoader_.pause();
1247
1248                        if (!track) {
1249                            // stop playlist and segment loading for subtitles
1250                            if (this.subtitlePlaylistLoader_) {
1251                                this.subtitlePlaylistLoader_.dispose();
1252                                this.subtitlePlaylistLoader_ = null;
1253                            }
1254                            return;
1255                        }
1256
1257                        var properties = subtitleGroup.filter(function (subtitleProperties) {
1258                            return subtitleProperties.id === track.id;
1259                        })[0];
1260
1261                        // startup playlist and segment loaders for the enabled subtitle track
1262                        if (!this.subtitlePlaylistLoader_ ||
1263                            // if the media hasn't loaded yet, we don't have the URI to check, so it is
1264                            // easiest to simply recreate the playlist loader
1265                            !this.subtitlePlaylistLoader_.media() || this.subtitlePlaylistLoader_.media().resolvedUri !== properties.resolvedUri) {
1266
1267                            if (this.subtitlePlaylistLoader_) {
1268                                this.subtitlePlaylistLoader_.dispose();
1269                            }
1270
1271                            // reset the segment loader only when the subtitle playlist is changed instead of
1272                            // every time setupSubtitles is called since switching subtitle tracks fires
1273                            // multiple `change` events on the TextTrackList
1274                            this.subtitleSegmentLoader_.resetEverything();
1275
1276                            // can't reuse playlistloader because we're only using single renditions and not a
1277                            // proper master
1278                            this.subtitlePlaylistLoader_ = new _playlistLoader2['default'](properties.resolvedUri, this.hls_, this.withCredentials);
1279
1280                            this.subtitlePlaylistLoader_.on('loadedmetadata', function () {
1281                                var subtitlePlaylist = _this5.subtitlePlaylistLoader_.media();
1282
1283                                _this5.subtitleSegmentLoader_.playlist(subtitlePlaylist, _this5.requestOptions_);
1284                                _this5.subtitleSegmentLoader_.track(_this5.activeSubtitleTrack_());
1285
1286                                // if the video is already playing, or if this isn't a live video and preload
1287                                // permits, start downloading segments
1288                                if (!_this5.tech_.paused() || subtitlePlaylist.endList && _this5.tech_.preload() !== 'none') {
1289                                    _this5.subtitleSegmentLoader_.load();
1290                                }
1291                            });
1292
1293                            this.subtitlePlaylistLoader_.on('loadedplaylist', function () {
1294                                var updatedPlaylist = undefined;
1295
1296                                if (_this5.subtitlePlaylistLoader_) {
1297                                    updatedPlaylist = _this5.subtitlePlaylistLoader_.media();
1298                                }
1299
1300                                if (!updatedPlaylist) {
1301                                    return;
1302                                }
1303
1304                                _this5.subtitleSegmentLoader_.playlist(updatedPlaylist, _this5.requestOptions_);
1305                            });
1306
1307                            this.subtitlePlaylistLoader_.on('error', this.handleSubtitleError_.bind(this));
1308                        }
1309
1310                        if (this.subtitlePlaylistLoader_.media() && this.subtitlePlaylistLoader_.media().resolvedUri === properties.resolvedUri) {
1311                            this.subtitleSegmentLoader_.load();
1312                        } else {
1313                            this.subtitlePlaylistLoader_.load();
1314                        }
1315                    }
1316
1317                    /**
1318                     * Re-tune playback quality level for the current player
1319                     * conditions. This method may perform destructive actions, like
1320                     * removing already buffered content, to readjust the currently
1321                     * active playlist quickly.
1322                     *
1323                     * @private
1324                     */
1325                }, {
1326                    key: 'fastQualityChange_',
1327                    value: function fastQualityChange_() {
1328                        var media = this.selectPlaylist();
1329
1330                        if (media !== this.masterPlaylistLoader_.media()) {
1331                            this.masterPlaylistLoader_.media(media);
1332
1333                            this.mainSegmentLoader_.resetLoader();
1334                            // don't need to reset audio as it is reset when media changes
1335                        }
1336                    }
1337
1338                    /**
1339                     * Begin playback.
1340                     */
1341                }, {
1342                    key: 'play',
1343                    value: function play() {
1344                        if (this.setupFirstPlay()) {
1345                            return;
1346                        }
1347
1348                        if (this.tech_.ended()) {
1349                            this.tech_.setCurrentTime(0);
1350                        }
1351
1352                        if (this.hasPlayed_()) {
1353                            this.load();
1354                        }
1355
1356                        var seekable = this.tech_.seekable();
1357
1358                        // if the viewer has paused and we fell out of the live window,
1359                        // seek forward to the live point
1360                        if (this.tech_.duration() === Infinity) {
1361                            if (this.tech_.currentTime() < seekable.start(0)) {
1362                                return this.tech_.setCurrentTime(seekable.end(seekable.length - 1));
1363                            }
1364                        }
1365                    }
1366
1367                    /**
1368                     * Seek to the latest media position if this is a live video and the
1369                     * player and video are loaded and initialized.
1370                     */
1371                }, {
1372                    key: 'setupFirstPlay',
1373                    value: function setupFirstPlay() {
1374                        var seekable = undefined;
1375                        var media = this.masterPlaylistLoader_.media();
1376
1377                        // check that everything is ready to begin buffering in the live
1378                        // scenario
1379                        // 1) the active media playlist is available
1380                        if (media &&
1381                            // 2) the player is not paused
1382                            !this.tech_.paused() &&
1383                            // 3) the player has not started playing
1384                            !this.hasPlayed_()) {
1385
1386                            // when the video is a live stream
1387                            if (!media.endList) {
1388                                this.trigger('firstplay');
1389
1390                                // seek to the latest media position for live videos
1391                                seekable = this.seekable();
1392                                if (seekable.length) {
1393                                    this.tech_.setCurrentTime(seekable.end(0));
1394                                }
1395                            }
1396                            this.hasPlayed_ = function () {
1397                                return true;
1398                            };
1399                            // now that we are ready, load the segment
1400                            this.load();
1401                            return true;
1402                        }
1403                        return false;
1404                    }
1405
1406                    /**
1407                     * handle the sourceopen event on the MediaSource
1408                     *
1409                     * @private
1410                     */
1411                }, {
1412                    key: 'handleSourceOpen_',
1413                    value: function handleSourceOpen_() {
1414                        // Only attempt to create the source buffer if none already exist.
1415                        // handleSourceOpen is also called when we are "re-opening" a source buffer
1416                        // after `endOfStream` has been called (in response to a seek for instance)
1417                        try {
1418                            this.setupSourceBuffers_();
1419                        } catch (e) {
1420                            _videoJs2['default'].log.warn('Failed to create Source Buffers', e);
1421                            return this.mediaSource.endOfStream('decode');
1422                        }
1423
1424                        // if autoplay is enabled, begin playback. This is duplicative of
1425                        // code in video.js but is required because play() must be invoked
1426                        // *after* the media source has opened.
1427                        if (this.tech_.autoplay()) {
1428                            this.tech_.play();
1429                        }
1430
1431                        this.trigger('sourceopen');
1432                    }
1433
1434                    /**
1435                     * Check if a playlist has stopped being updated
1436                     * @param {Object} playlist the media playlist object
1437                     * @return {boolean} whether the playlist has stopped being updated or not
1438                     */
1439                }, {
1440                    key: 'stuckAtPlaylistEnd_',
1441                    value: function stuckAtPlaylistEnd_(playlist) {
1442                        var seekable = this.seekable();
1443
1444                        if (!seekable.length) {
1445                            // playlist doesn't have enough information to determine whether we are stuck
1446                            return false;
1447                        }
1448
1449                        var expired = this.syncController_.getExpiredTime(playlist, this.mediaSource.duration);
1450
1451                        if (expired === null) {
1452                            return false;
1453                        }
1454
1455                        // does not use the safe live end to calculate playlist end, since we
1456                        // don't want to say we are stuck while there is still content
1457                        var absolutePlaylistEnd = Hls.Playlist.playlistEnd(playlist, expired);
1458                        var currentTime = this.tech_.currentTime();
1459                        var buffered = this.tech_.buffered();
1460
1461                        if (!buffered.length) {
1462                            // return true if the playhead reached the absolute end of the playlist
1463                            return absolutePlaylistEnd - currentTime <= _ranges2['default'].TIME_FUDGE_FACTOR;
1464                        }
1465                        var bufferedEnd = buffered.end(buffered.length - 1);
1466
1467                        // return true if there is too little buffer left and
1468                        // buffer has reached absolute end of playlist
1469                        return bufferedEnd - currentTime <= _ranges2['default'].TIME_FUDGE_FACTOR && absolutePlaylistEnd - bufferedEnd <= _ranges2['default'].TIME_FUDGE_FACTOR;
1470                    }
1471
1472                    /**
1473                     * Blacklists a playlist when an error occurs for a set amount of time
1474                     * making it unavailable for selection by the rendition selection algorithm
1475                     * and then forces a new playlist (rendition) selection.
1476                     *
1477                     * @param {Object=} error an optional error that may include the playlist
1478                     * to blacklist
1479                     */
1480                }, {
1481                    key: 'blacklistCurrentPlaylist',
1482                    value: function blacklistCurrentPlaylist() {
1483                        var error = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
1484
1485                        var currentPlaylist = undefined;
1486                        var nextPlaylist = undefined;
1487
1488                        // If the `error` was generated by the playlist loader, it will contain
1489                        // the playlist we were trying to load (but failed) and that should be
1490                        // blacklisted instead of the currently selected playlist which is likely
1491                        // out-of-date in this scenario
1492                        currentPlaylist = error.playlist || this.masterPlaylistLoader_.media();
1493
1494                        // If there is no current playlist, then an error occurred while we were
1495                        // trying to load the master OR while we were disposing of the tech
1496                        if (!currentPlaylist) {
1497                            this.error = error;
1498
1499                            try {
1500                                return this.mediaSource.endOfStream('network');
1501                            } catch (e) {
1502                                return this.trigger('error');
1503                            }
1504                        }
1505
1506                        var isFinalRendition = this.masterPlaylistLoader_.isFinalRendition_();
1507
1508                        if (isFinalRendition) {
1509                            // Never blacklisting this playlist because it's final rendition
1510                            _videoJs2['default'].log.warn('Problem encountered with the current ' + 'HLS playlist. Trying again since it is the final playlist.');
1511
1512                            this.tech_.trigger('retryplaylist');
1513                            return this.masterPlaylistLoader_.load(isFinalRendition);
1514                        }
1515                        // Blacklist this playlist
1516                        currentPlaylist.excludeUntil = Date.now() + this.blacklistDuration * 1000;
1517                        this.tech_.trigger('blacklistplaylist');
1518
1519                        // Select a new playlist
1520                        nextPlaylist = this.selectPlaylist();
1521                        _videoJs2['default'].log.warn('Problem encountered with the current HLS playlist.' + (error.message ? ' ' + error.message : '') + ' Switching to another playlist.');
1522
1523                        return this.masterPlaylistLoader_.media(nextPlaylist);
1524                    }
1525
1526                    /**
1527                     * Pause all segment loaders
1528                     */
1529                }, {
1530                    key: 'pauseLoading',
1531                    value: function pauseLoading() {
1532                        this.mainSegmentLoader_.pause();
1533                        if (this.audioPlaylistLoader_) {
1534                            this.audioSegmentLoader_.pause();
1535                        }
1536                        if (this.subtitlePlaylistLoader_) {
1537                            this.subtitleSegmentLoader_.pause();
1538                        }
1539                    }
1540
1541                    /**
1542                     * set the current time on all segment loaders
1543                     *
1544                     * @param {TimeRange} currentTime the current time to set
1545                     * @return {TimeRange} the current time
1546                     */
1547                }, {
1548                    key: 'setCurrentTime',
1549                    value: function setCurrentTime(currentTime) {
1550                        var buffered = _ranges2['default'].findRange(this.tech_.buffered(), currentTime);
1551
1552                        if (!(this.masterPlaylistLoader_ && this.masterPlaylistLoader_.media())) {
1553                            // return immediately if the metadata is not ready yet
1554                            return 0;
1555                        }
1556
1557                        // it's clearly an edge-case but don't thrown an error if asked to
1558                        // seek within an empty playlist
1559                        if (!this.masterPlaylistLoader_.media().segments) {
1560                            return 0;
1561                        }
1562
1563                        // In flash playback, the segment loaders should be reset on every seek, even
1564                        // in buffer seeks
1565                        var isFlash = this.mode_ === 'flash' || this.mode_ === 'auto' && !_videoJs2['default'].MediaSource.supportsNativeMediaSources();
1566
1567                        // if the seek location is already buffered, continue buffering as
1568                        // usual
1569                        if (buffered && buffered.length && !isFlash) {
1570                            return currentTime;
1571                        }
1572
1573                        // cancel outstanding requests so we begin buffering at the new
1574                        // location
1575                        this.mainSegmentLoader_.resetEverything();
1576                        this.mainSegmentLoader_.abort();
1577                        if (this.audioPlaylistLoader_) {
1578                            this.audioSegmentLoader_.resetEverything();
1579                            this.audioSegmentLoader_.abort();
1580                        }
1581                        if (this.subtitlePlaylistLoader_) {
1582                            this.subtitleSegmentLoader_.resetEverything();
1583                            this.subtitleSegmentLoader_.abort();
1584                        }
1585
1586                        if (!this.tech_.paused()) {
1587                            this.mainSegmentLoader_.load();
1588                            if (this.audioPlaylistLoader_) {
1589                                this.audioSegmentLoader_.load();
1590                            }
1591                            if (this.subtitlePlaylistLoader_) {
1592                                this.subtitleSegmentLoader_.load();
1593                            }
1594                        }
1595                    }
1596
1597                    /**
1598                     * get the current duration
1599                     *
1600                     * @return {TimeRange} the duration
1601                     */
1602                }, {
1603                    key: 'duration',
1604                    value: function duration() {
1605                        if (!this.masterPlaylistLoader_) {
1606                            return 0;
1607                        }
1608
1609                        if (this.mediaSource) {
1610                            return this.mediaSource.duration;
1611                        }
1612
1613                        return Hls.Playlist.duration(this.masterPlaylistLoader_.media());
1614                    }
1615
1616                    /**
1617                     * check the seekable range
1618                     *
1619                     * @return {TimeRange} the seekable range
1620                     */
1621                }, {
1622                    key: 'seekable',
1623                    value: function seekable() {
1624                        return this.seekable_;
1625                    }
1626                }, {
1627                    key: 'onSyncInfoUpdate_',
1628                    value: function onSyncInfoUpdate_() {
1629                        var mainSeekable = undefined;
1630                        var audioSeekable = undefined;
1631
1632                        if (!this.masterPlaylistLoader_) {
1633                            return;
1634                        }
1635
1636                        var media = this.masterPlaylistLoader_.media();
1637
1638                        if (!media) {
1639                            return;
1640                        }
1641
1642                        var expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
1643
1644                        if (expired === null) {
1645                            // not enough information to update seekable
1646                            return;
1647                        }
1648
1649                        mainSeekable = Hls.Playlist.seekable(media, expired);
1650
1651                        if (mainSeekable.length === 0) {
1652                            return;
1653                        }
1654
1655                        if (this.audioPlaylistLoader_) {
1656                            media = this.audioPlaylistLoader_.media();
1657                            expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
1658
1659                            if (expired === null) {
1660                                return;
1661                            }
1662
1663                            audioSeekable = Hls.Playlist.seekable(media, expired);
1664
1665                            if (audioSeekable.length === 0) {
1666                                return;
1667                            }
1668                        }
1669
1670                        if (!audioSeekable) {
1671                            // seekable has been calculated based on buffering video data so it
1672                            // can be returned directly
1673                            this.seekable_ = mainSeekable;
1674                        } else if (audioSeekable.start(0) > mainSeekable.end(0) || mainSeekable.start(0) > audioSeekable.end(0)) {
1675                            // seekables are pretty far off, rely on main
1676                            this.seekable_ = mainSeekable;
1677                        } else {
1678                            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)]]);
1679                        }
1680
1681                        this.tech_.trigger('seekablechanged');
1682                    }
1683
1684                    /**
1685                     * Update the player duration
1686                     */
1687                }, {
1688                    key: 'updateDuration',
1689                    value: function updateDuration() {
1690                        var _this6 = this;
1691
1692                        var oldDuration = this.mediaSource.duration;
1693                        var newDuration = Hls.Playlist.duration(this.masterPlaylistLoader_.media());
1694                        var buffered = this.tech_.buffered();
1695                        var setDuration = function setDuration() {
1696                            _this6.mediaSource.duration = newDuration;
1697                            _this6.tech_.trigger('durationchange');
1698
1699                            _this6.mediaSource.removeEventListener('sourceopen', setDuration);
1700                        };
1701
1702                        if (buffered.length > 0) {
1703                            newDuration = Math.max(newDuration, buffered.end(buffered.length - 1));
1704                        }
1705
1706                        // if the duration has changed, invalidate the cached value
1707                        if (oldDuration !== newDuration) {
1708                            // update the duration
1709                            if (this.mediaSource.readyState !== 'open') {
1710                                this.mediaSource.addEventListener('sourceopen', setDuration);
1711                            } else {
1712                                setDuration();
1713                            }
1714                        }
1715                    }
1716
1717                    /**
1718                     * dispose of the MasterPlaylistController and everything
1719                     * that it controls
1720                     */
1721                }, {
1722                    key: 'dispose',
1723                    value: function dispose() {
1724                        this.decrypter_.terminate();
1725                        this.masterPlaylistLoader_.dispose();
1726                        this.mainSegmentLoader_.dispose();
1727
1728                        if (this.audioPlaylistLoader_) {
1729                            this.audioPlaylistLoader_.dispose();
1730                        }
1731                        if (this.subtitlePlaylistLoader_) {
1732                            this.subtitlePlaylistLoader_.dispose();
1733                        }
1734                        this.audioSegmentLoader_.dispose();
1735                        this.subtitleSegmentLoader_.dispose();
1736                    }
1737
1738                    /**
1739                     * return the master playlist object if we have one
1740                     *
1741                     * @return {Object} the master playlist object that we parsed
1742                     */
1743                }, {
1744                    key: 'master',
1745                    value: function master() {
1746                        return this.masterPlaylistLoader_.master;
1747                    }
1748
1749                    /**
1750                     * return the currently selected playlist
1751                     *
1752                     * @return {Object} the currently selected playlist object that we parsed
1753                     */
1754                }, {
1755                    key: 'media',
1756                    value: function media() {
1757                        // playlist loader will not return media if it has not been fully loaded
1758                        return this.masterPlaylistLoader_.media() || this.initialMedia_;
1759                    }
1760
1761                    /**
1762                     * setup our internal source buffers on our segment Loaders
1763                     *
1764                     * @private
1765                     */
1766                }, {
1767                    key: 'setupSourceBuffers_',
1768                    value: function setupSourceBuffers_() {
1769                        var media = this.masterPlaylistLoader_.media();
1770                        var mimeTypes = undefined;
1771
1772                        // wait until a media playlist is available and the Media Source is
1773                        // attached
1774                        if (!media || this.mediaSource.readyState !== 'open') {
1775                            return;
1776                        }
1777
1778                        mimeTypes = mimeTypesForPlaylist_(this.masterPlaylistLoader_.master, media);
1779                        if (mimeTypes.length < 1) {
1780                            this.error = 'No compatible SourceBuffer configuration for the variant stream:' + media.resolvedUri;
1781                            return this.mediaSource.endOfStream('decode');
1782                        }
1783                        this.mainSegmentLoader_.mimeType(mimeTypes[0]);
1784                        if (mimeTypes[1]) {
1785                            this.audioSegmentLoader_.mimeType(mimeTypes[1]);
1786                        }
1787
1788                        // exclude any incompatible variant streams from future playlist
1789                        // selection
1790                        this.excludeIncompatibleVariants_(media);
1791                    }
1792
1793                    /**
1794                     * Blacklist playlists that are known to be codec or
1795                     * stream-incompatible with the SourceBuffer configuration. For
1796                     * instance, Media Source Extensions would cause the video element to
1797                     * stall waiting for video data if you switched from a variant with
1798                     * video and audio to an audio-only one.
1799                     *
1800                     * @param {Object} media a media playlist compatible with the current
1801                     * set of SourceBuffers. Variants in the current master playlist that
1802                     * do not appear to have compatible codec or stream configurations
1803                     * will be excluded from the default playlist selection algorithm
1804                     * indefinitely.
1805                     * @private
1806                     */
1807                }, {
1808                    key: 'excludeIncompatibleVariants_',
1809                    value: function excludeIncompatibleVariants_(media) {
1810                        var master = this.masterPlaylistLoader_.master;
1811                        var codecCount = 2;
1812                        var videoCodec = null;
1813                        var codecs = undefined;
1814
1815                        if (media.attributes && media.attributes.CODECS) {
1816                            codecs = parseCodecs(media.attributes.CODECS);
1817                            videoCodec = codecs.videoCodec;
1818                            codecCount = codecs.codecCount;
1819                        }
1820                        master.playlists.forEach(function (variant) {
1821                            var variantCodecs = {
1822                                codecCount: 2,
1823                                videoCodec: null
1824                            };
1825
1826                            if (variant.attributes && variant.attributes.CODECS) {
1827                                var codecString = variant.attributes.CODECS;
1828
1829                                variantCodecs = parseCodecs(codecString);
1830
1831                                if (window.MediaSource && window.MediaSource.isTypeSupported && !window.MediaSource.isTypeSupported('video/mp4; codecs="' + mapLegacyAvcCodecs_(codecString) + '"')) {
1832                                    variant.excludeUntil = Infinity;
1833                                }
1834                            }
1835
1836                            // if the streams differ in the presence or absence of audio or
1837                            // video, they are incompatible
1838                            if (variantCodecs.codecCount !== codecCount) {
1839                                variant.excludeUntil = Infinity;
1840                            }
1841
1842                            // if h.264 is specified on the current playlist, some flavor of
1843                            // it must be specified on all compatible variants
1844                            if (variantCodecs.videoCodec !== videoCodec) {
1845                                variant.excludeUntil = Infinity;
1846                            }
1847                        });
1848                    }
1849                }, {
1850                    key: 'updateAdCues_',
1851                    value: function updateAdCues_(media) {
1852                        var offset = 0;
1853                        var seekable = this.seekable();
1854
1855                        if (seekable.length) {
1856                            offset = seekable.start(0);
1857                        }
1858
1859                        _adCueTags2['default'].updateAdCues(media, this.cueTagsTrack_, offset);
1860                    }
1861                }]);
1862
1863                return MasterPlaylistController;
1864            })(_videoJs2['default'].EventTarget);
1865
1866            exports.MasterPlaylistController = MasterPlaylistController;
1867        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
1868    },{"./ad-cue-tags":1,"./decrypter-worker":4,"./playlist-loader":8,"./ranges":10,"./segment-loader":14,"./sync-controller":17,"./vtt-segment-loader":18,"videojs-contrib-media-sources/es5/codec-utils":64,"webworkify":75}],6:[function(require,module,exports){
1869        (function (global){
1870            'use strict';
1871
1872            Object.defineProperty(exports, '__esModule', {
1873                value: true
1874            });
1875
1876            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
1877
1878            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
1879
1880            var _videoJs2 = _interopRequireDefault(_videoJs);
1881
1882            var _binUtils = require('./bin-utils');
1883
1884            var REQUEST_ERRORS = {
1885                FAILURE: 2,
1886                TIMEOUT: -101,
1887                ABORTED: -102
1888            };
1889
1890            exports.REQUEST_ERRORS = REQUEST_ERRORS;
1891            /**
1892             * Turns segment byterange into a string suitable for use in
1893             * HTTP Range requests
1894             *
1895             * @param {Object} byterange - an object with two values defining the start and end
1896             *                             of a byte-range
1897             */
1898            var byterangeStr = function byterangeStr(byterange) {
1899                var byterangeStart = undefined;
1900                var byterangeEnd = undefined;
1901
1902                // `byterangeEnd` is one less than `offset + length` because the HTTP range
1903                // header uses inclusive ranges
1904                byterangeEnd = byterange.offset + byterange.length - 1;
1905                byterangeStart = byterange.offset;
1906                return 'bytes=' + byterangeStart + '-' + byterangeEnd;
1907            };
1908
1909            /**
1910             * Defines headers for use in the xhr request for a particular segment.
1911             *
1912             * @param {Object} segment - a simplified copy of the segmentInfo object from SegmentLoader
1913             */
1914            var segmentXhrHeaders = function segmentXhrHeaders(segment) {
1915                var headers = {};
1916
1917                if (segment.byterange) {
1918                    headers.Range = byterangeStr(segment.byterange);
1919                }
1920                return headers;
1921            };
1922
1923            /**
1924             * Abort all requests
1925             *
1926             * @param {Object} activeXhrs - an object that tracks all XHR requests
1927             */
1928            var abortAll = function abortAll(activeXhrs) {
vendor: 6,284 bytes, lines 1929-2075
1929                activeXhrs.forEach(function (xhr) {
1930                    xhr.abort();
1931                });
1932            };
1933
1934            /**
1935             * Gather important bandwidth stats once a request has completed
1936             *
1937             * @param {Object} request - the XHR request from which to gather stats
1938             */
1939            var getRequestStats = function getRequestStats(request) {
1940                return {
1941                    bandwidth: request.bandwidth,
1942                    bytesReceived: request.bytesReceived || 0,
1943                    roundTripTime: request.roundTripTime || 0
1944                };
1945            };
1946
1947            /**
1948             * If possible gather bandwidth stats as a request is in
1949             * progress
1950             *
1951             * @param {Event} progressEvent - an event object from an XHR's progress event
1952             */
1953            var getProgressStats = function getProgressStats(progressEvent) {
1954                var request = progressEvent.target;
1955                var roundTripTime = Date.now() - request.requestTime;
1956                var stats = {
1957                    bandwidth: Infinity,
1958                    bytesReceived: 0,
1959                    roundTripTime: roundTripTime || 0
1960                };
1961
1962                if (progressEvent.lengthComputable) {
1963                    stats.bytesReceived = progressEvent.loaded;
1964                    // This can result in Infinity if stats.roundTripTime is 0 but that is ok
1965                    // because we should only use bandwidth stats on progress to determine when
1966                    // abort a request early due to insufficient bandwidth
1967                    stats.bandwidth = Math.floor(stats.bytesReceived / stats.roundTripTime * 8 * 1000);
1968                }
1969                return stats;
1970            };
1971
1972            /**
1973             * Handle all error conditions in one place and return an object
1974             * with all the information
1975             *
1976             * @param {Error|null} error - if non-null signals an error occured with the XHR
1977             * @param {Object} request -  the XHR request that possibly generated the error
1978             */
1979            var handleErrors = function handleErrors(error, request) {
1980                if (request.timedout) {
1981                    return {
1982                        status: request.status,
1983                        message: 'HLS request timed-out at URL: ' + request.uri,
1984                        code: REQUEST_ERRORS.TIMEOUT,
1985                        xhr: request
1986                    };
1987                }
1988
1989                if (request.aborted) {
1990                    return {
1991                        status: request.status,
1992                        message: 'HLS request aborted at URL: ' + request.uri,
1993                        code: REQUEST_ERRORS.ABORTED,
1994                        xhr: request
1995                    };
1996                }
1997
1998                if (error) {
1999                    return {
2000                        status: request.status,
2001                        message: 'HLS request errored at URL: ' + request.uri,
2002                        code: REQUEST_ERRORS.FAILURE,
2003                        xhr: request
2004                    };
2005                }
2006
2007                return null;
2008            };
2009
2010            /**
2011             * Handle responses for key data and convert the key data to the correct format
2012             * for the decryption step later
2013             *
2014             * @param {Object} segment - a simplified copy of the segmentInfo object from SegmentLoader
2015             * @param {Function} finishProcessingFn - a callback to execute to continue processing
2016             *                                        this request
2017             */
2018            var handleKeyResponse = function handleKeyResponse(segment, finishProcessingFn) {
2019                return function (error, request) {
2020                    var response = request.response;
2021                    var errorObj = handleErrors(error, request);
2022
2023                    if (errorObj) {
2024                        return finishProcessingFn(errorObj, segment);
2025                    }
2026
2027                    if (response.byteLength !== 16) {
2028                        return finishProcessingFn({
2029                            status: request.status,
2030                            message: 'Invalid HLS key at URL: ' + request.uri,
2031                            code: REQUEST_ERRORS.FAILURE,
2032                            xhr: request
2033                        }, segment);
2034                    }
2035
2036                    var view = new DataView(response);
2037
2038                    segment.key.bytes = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]);
2039                    return finishProcessingFn(null, segment);
2040                };
2041            };
2042
2043            /**
2044             * Handle init-segment responses
2045             *
2046             * @param {Object} segment - a simplified copy of the segmentInfo object from SegmentLoader
2047             * @param {Function} finishProcessingFn - a callback to execute to continue processing
2048             *                                        this request
2049             */
2050            var handleInitSegmentResponse = function handleInitSegmentResponse(segment, finishProcessingFn) {
2051                return function (error, request) {
2052                    var response = request.response;
2053                    var errorObj = handleErrors(error, request);
2054
2055                    if (errorObj) {
2056                        return finishProcessingFn(errorObj, segment);
2057                    }
2058
2059                    // stop processing if received empty content
2060                    if (response.byteLength === 0) {
2061                        return finishProcessingFn({
2062                            status: request.status,
2063                            message: 'Empty HLS segment content at URL: ' + request.uri,
2064                            code: REQUEST_ERRORS.FAILURE,
2065                            xhr: request
2066                        }, segment);
2067                    }
2068
2069                    segment.map.bytes = new Uint8Array(request.response);
2070                    return finishProcessingFn(null, segment);
2071                };
2072            };
2073
2074            /**
2075             * Response handler for segment-requests being sure to set the correct
2076             * property depending on whether the segment is encryped or not
2077             * Also records and keeps track of stats that are used for ABR purposes
2078             *
2079             * @param {Object} segment - a simplified copy of the segmentInfo object from SegmentLoader
2080             * @param {Function} finishProcessingFn - a callback to execute to continue processing
2081             *                                        this request
2082             */
2083            var handleSegmentResponse = function handleSegmentResponse(segment, finishProcessingFn) {
2084                return function (error, request) {
2085                    var response = request.response;
2086                    var errorObj = handleErrors(error, request);
2087
2088                    if (errorObj) {
2089                        return finishProcessingFn(errorObj, segment);
2090                    }
2091
2092                    // stop processing if received empty content
2093                    if (response.byteLength === 0) {
2094                        return finishProcessingFn({
2095                            status: request.status,
2096                            message: 'Empty HLS segment content at URL: ' + request.uri,
2097                            code: REQUEST_ERRORS.FAILURE,
2098                            xhr: request
2099                        }, segment);
2100                    }
2101
2102                    segment.stats = getRequestStats(request);
2103
2104                    if (segment.key) {
2105                        segment.encryptedBytes = new Uint8Array(request.response);
2106                    } else {
2107                        segment.bytes = new Uint8Array(request.response);
2108                    }
2109
2110                    return finishProcessingFn(null, segment);
2111                };
2112            };
2113
2114            /**
2115             * Decrypt the segment via the decryption web worker
2116             *
2117             * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
2118             * @param {Object} segment - a simplified copy of the segmentInfo object from SegmentLoader
2119             * @param {Function} doneFn - a callback that is executed after decryption has completed
2120             */
2121            var decryptSegment = function decryptSegment(decrypter, segment, doneFn) {
2122                var decryptionHandler = function decryptionHandler(event) {
2123                    if (event.data.source === segment.requestId) {
2124                        decrypter.removeEventListener('message', decryptionHandler);
2125                        var decrypted = event.data.decrypted;
2126
2127                        segment.bytes = new Uint8Array(decrypted.bytes, decrypted.byteOffset, decrypted.byteLength);
2128                        return doneFn(null, segment);
2129                    }
2130                };
2131
2132                decrypter.addEventListener('message', decryptionHandler);
2133
2134                // this is an encrypted segment
2135                // incrementally decrypt the segment
2136                decrypter.postMessage((0, _binUtils.createTransferableMessage)({
2137                    source: segment.requestId,
2138                    encrypted: segment.encryptedBytes,
2139                    key: segment.key.bytes,
2140                    iv: segment.key.iv
2141                }), [segment.encryptedBytes.buffer, segment.key.bytes.buffer]);
2142            };
2143
2144            /**
2145             * The purpose of this function is to get the most pertinent error from the
2146             * array of errors.
2147             * For instance if a timeout and two aborts occur, then the aborts were
2148             * likely triggered by the timeout so return that error object.
2149             */
2150            var getMostImportantError = function getMostImportantError(errors) {
2151                return errors.reduce(function (prev, err) {
2152                    return err.code > prev.code ? err : prev;
2153                });
2154            };
2155
2156            /**
2157             * This function waits for all XHRs to finish (with either success or failure)
2158             * before continueing processing via it's callback. The function gathers errors
2159             * from each request into a single errors array so that the error status for
2160             * each request can be examined later.
2161             *
2162             * @param {Object} activeXhrs - an object that tracks all XHR requests
2163             * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
2164             * @param {Function} doneFn - a callback that is executed after all resources have been
2165             *                            downloaded and any decryption completed
2166             */
2167            var waitForCompletion = function waitForCompletion(activeXhrs, decrypter, doneFn) {
2168                var errors = [];
2169                var count = 0;
2170
2171                return function (error, segment) {
2172                    if (error) {
2173                        // If there are errors, we have to abort any outstanding requests
2174                        abortAll(activeXhrs);
2175                        errors.push(error);
2176                    }
2177                    count += 1;
2178
2179                    if (count === activeXhrs.length) {
2180                        // Keep track of when *all* of the requests have completed
2181                        segment.endOfAllRequests = Date.now();
2182
2183                        if (errors.length > 0) {
2184                            var worstError = getMostImportantError(errors);
2185
2186                            return doneFn(worstError, segment);
2187                        }
2188                        if (segment.encryptedBytes) {
2189                            return decryptSegment(decrypter, segment, doneFn);
2190                        }
2191                        // Otherwise, everything is ready just continue
2192                        return doneFn(null, segment);
2193                    }
2194                };
2195            };
2196
2197            /**
2198             * Simple progress event callback handler that gathers some stats before
2199             * executing a provided callback with the `segment` object
2200             *
2201             * @param {Object} segment - a simplified copy of the segmentInfo object from SegmentLoader
2202             * @param {Function} progressFn - a callback that is executed each time a progress event is received
2203             * @param {Event} event - the progress event object from XMLHttpRequest
2204             */
2205            var handleProgress = function handleProgress(segment, progressFn) {
2206                return function (event) {
2207                    segment.stats = getProgressStats(event);
2208                    return progressFn(event, segment);
2209                };
2210            };
2211
2212            /**
2213             * Load all resources and does any processing necessary for a media-segment
2214             *
2215             * Features:
2216             *   decrypts the media-segment if it has a key uri and an iv
2217             *   aborts *all* requests if *any* one request fails
2218             *
2219             * The segment object, at minimum, has the following format:
2220             * {
2221             *   resolvedUri: String,
2222             *   [byterange]: {
2223             *     offset: Number,
2224             *     length: Number
2225             *   },
2226             *   [key]: {
2227             *     resolvedUri: String
2228             *     [byterange]: {
2229             *       offset: Number,
2230             *       length: Number
2231             *     },
2232             *     iv: {
2233             *       bytes: Uint32Array
2234             *     }
2235             *   },
2236             *   [map]: {
2237             *     resolvedUri: String,
2238             *     [byterange]: {
2239             *       offset: Number,
2240             *       length: Number
2241             *     },
2242             *     [bytes]: Uint8Array
2243             *   }
2244             * }
2245             * ...where [name] denotes optional properties
2246             *
2247             * @param {Function} xhr - an instance of the xhr wrapper in xhr.js
2248             * @param {Object} xhrOptions - the base options to provide to all xhr requests
2249             * @param {WebWorker} decryptionWorker - a WebWorker interface to AES-128 decryption routines
2250             * @param {Object} segment - a simplified copy of the segmentInfo object from SegmentLoader
2251             * @param {Function} progressFn - a callback that receives progress events from the main segment's xhr request
2252             * @param {Function} doneFn - a callback that is executed only once all requests have succeeded or failed
2253             * @returns {Function} a function that, when invoked, immediately aborts all outstanding requests
2254             */
2255            var mediaSegmentRequest = function mediaSegmentRequest(xhr, xhrOptions, decryptionWorker, segment, progressFn, doneFn) {
2256                var activeXhrs = [];
2257                var finishProcessingFn = waitForCompletion(activeXhrs, decryptionWorker, doneFn);
2258
2259                // optionally, request the decryption key
2260                if (segment.key) {
2261                    var keyRequestOptions = _videoJs2['default'].mergeOptions(xhrOptions, {
2262                        uri: segment.key.resolvedUri,
2263                        responseType: 'arraybuffer'
2264                    });
2265                    var keyRequestCallback = handleKeyResponse(segment, finishProcessingFn);
2266                    var keyXhr = xhr(keyRequestOptions, keyRequestCallback);
2267
2268                    activeXhrs.push(keyXhr);
2269                }
2270
2271                // optionally, request the associated media init segment
2272                if (segment.map && !segment.map.bytes) {
2273                    var initSegmentOptions = _videoJs2['default'].mergeOptions(xhrOptions, {
2274                        uri: segment.map.resolvedUri,
2275                        responseType: 'arraybuffer',
2276                        headers: segmentXhrHeaders(segment.map)
2277                    });
2278                    var initSegmentRequestCallback = handleInitSegmentResponse(segment, finishProcessingFn);
2279                    var initSegmentXhr = xhr(initSegmentOptions, initSegmentRequestCallback);
2280
2281                    activeXhrs.push(initSegmentXhr);
2282                }
2283
2284                var segmentRequestOptions = _videoJs2['default'].mergeOptions(xhrOptions, {
2285                    uri: segment.resolvedUri,
2286                    responseType: 'arraybuffer',
2287                    headers: segmentXhrHeaders(segment)
2288                });
2289                var segmentRequestCallback = handleSegmentResponse(segment, finishProcessingFn);
2290                var segmentXhr = xhr(segmentRequestOptions, segmentRequestCallback);
2291
2292                segmentXhr.addEventListener('progress', handleProgress(segment, progressFn));
2293                activeXhrs.push(segmentXhr);
2294
2295                return function () {
2296                    return abortAll(activeXhrs);
2297                };
2298            };
2299            exports.mediaSegmentRequest = mediaSegmentRequest;
2300        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2301    },{"./bin-utils":2}],7:[function(require,module,exports){
2302        (function (global){
2303            /**
2304             * @file playback-watcher.js
2305             *
2306             * Playback starts, and now my watch begins. It shall not end until my death. I shall
2307             * take no wait, hold no uncleared timeouts, father no bad seeks. I shall wear no crowns
2308             * and win no glory. I shall live and die at my post. I am the corrector of the underflow.
2309             * I am the watcher of gaps. I am the shield that guards the realms of seekable. I pledge
2310             * my life and honor to the Playback Watch, for this Player and all the Players to come.
2311             */
2312
2313            'use strict';
2314
2315            Object.defineProperty(exports, '__esModule', {
2316                value: true
2317            });
2318
2319            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
2320
2321            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
2322
2323            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
2324
2325            var _globalWindow = require('global/window');
2326
2327            var _globalWindow2 = _interopRequireDefault(_globalWindow);
2328
2329            var _ranges = require('./ranges');
2330
2331            var _ranges2 = _interopRequireDefault(_ranges);
2332
2333            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
2334
2335            var _videoJs2 = _interopRequireDefault(_videoJs);
2336
2337// Set of events that reset the playback-watcher time check logic and clear the timeout
2338            var timerCancelEvents = ['seeking', 'seeked', 'pause', 'playing', 'error'];
2339
2340            /**
2341             * @class PlaybackWatcher
2342             */
2343
2344            var PlaybackWatcher = (function () {
2345                /**
2346                 * Represents an PlaybackWatcher object.
2347                 * @constructor
2348                 * @param {object} options an object that includes the tech and settings
2349                 */
2350
2351                function PlaybackWatcher(options) {
2352                    var _this = this;
2353
2354                    _classCallCheck(this, PlaybackWatcher);
2355
2356                    this.tech_ = options.tech;
2357                    this.seekable = options.seekable;
2358
2359                    this.consecutiveUpdates = 0;
2360                    this.lastRecordedTime = null;
2361                    this.timer_ = null;
2362                    this.checkCurrentTimeTimeout_ = null;
2363
2364                    if (options.debug) {
2365                        this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'playback-watcher ->');
2366                    }
2367                    this.logger_('initialize');
2368
2369                    var waitingHandler = function waitingHandler() {
2370                        return _this.techWaiting_();
2371                    };
2372                    var cancelTimerHandler = function cancelTimerHandler() {
2373                        return _this.cancelTimer_();
2374                    };
2375                    var fixesBadSeeksHandler = function fixesBadSeeksHandler() {
2376                        return _this.fixesBadSeeks_();
2377                    };
2378
2379                    this.tech_.on('seekablechanged', fixesBadSeeksHandler);
2380                    this.tech_.on('waiting', waitingHandler);
2381                    this.tech_.on(timerCancelEvents, cancelTimerHandler);
2382                    this.monitorCurrentTime_();
2383
2384                    // Define the dispose function to clean up our events
2385                    this.dispose = function () {
2386                        _this.logger_('dispose');
2387                        _this.tech_.off('seekablechanged', fixesBadSeeksHandler);
2388                        _this.tech_.off('waiting', waitingHandler);
2389                        _this.tech_.off(timerCancelEvents, cancelTimerHandler);
2390                        if (_this.checkCurrentTimeTimeout_) {
2391                            _globalWindow2['default'].clearTimeout(_this.checkCurrentTimeTimeout_);
2392                        }
2393                        _this.cancelTimer_();
2394                    };
2395                }
2396
2397                /**
2398                 * Periodically check current time to see if playback stopped
2399                 *
2400                 * @private
2401                 */
2402
2403                _createClass(PlaybackWatcher, [{
2404                    key: 'monitorCurrentTime_',
2405                    value: function monitorCurrentTime_() {
2406                        this.checkCurrentTime_();
2407
2408                        if (this.checkCurrentTimeTimeout_) {
2409                            _globalWindow2['default'].clearTimeout(this.checkCurrentTimeTimeout_);
2410                        }
2411
2412                        // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
2413                        this.checkCurrentTimeTimeout_ = _globalWindow2['default'].setTimeout(this.monitorCurrentTime_.bind(this), 250);
2414                    }
2415
2416                    /**
2417                     * The purpose of this function is to emulate the "waiting" event on
2418                     * browsers that do not emit it when they are waiting for more
2419                     * data to continue playback
2420                     *
2421                     * @private
2422                     */
2423                }, {
2424                    key: 'checkCurrentTime_',
2425                    value: function checkCurrentTime_() {
2426                        if (this.tech_.seeking() && this.fixesBadSeeks_()) {
2427                            this.consecutiveUpdates = 0;
2428                            this.lastRecordedTime = this.tech_.currentTime();
2429                            return;
2430                        }
2431
2432                        if (this.tech_.paused() || this.tech_.seeking()) {
2433                            return;
2434                        }
2435
2436                        var currentTime = this.tech_.currentTime();
2437                        var buffered = this.tech_.buffered();
2438
2439                        if (this.lastRecordedTime === currentTime && (!buffered.length || currentTime + 0.1 >= buffered.end(buffered.length - 1))) {
2440                            // If current time is at the end of the final buffered region, then any playback
2441                            // stall is most likely caused by buffering in a low bandwidth environment. The tech
2442                            // should fire a `waiting` event in this scenario, but due to browser and tech
2443                            // inconsistencies (e.g. The Flash tech does not fire a `waiting` event when the end
2444                            // of the buffer is reached and has fallen off the live window). Calling
2445                            // `techWaiting_` here allows us to simulate responding to a native `waiting` event
2446                            // when the tech fails to emit one.
2447                            return this.techWaiting_();
2448                        }
2449
2450                        if (this.consecutiveUpdates >= 5 && currentTime === this.lastRecordedTime) {
2451                            this.consecutiveUpdates++;
2452                            this.waiting_();
2453                        } else if (currentTime === this.lastRecordedTime) {
2454                            this.consecutiveUpdates++;
2455                        } else {
2456                            this.consecutiveUpdates = 0;
2457                            this.lastRecordedTime = currentTime;
2458                        }
2459                    }
2460
2461                    /**
2462                     * Cancels any pending timers and resets the 'timeupdate' mechanism
2463                     * designed to detect that we are stalled
2464                     *
2465                     * @private
2466                     */
2467                }, {
2468                    key: 'cancelTimer_',
2469                    value: function cancelTimer_() {
2470                        this.consecutiveUpdates = 0;
2471
2472                        if (this.timer_) {
2473                            this.logger_('cancelTimer_');
2474                            clearTimeout(this.timer_);
2475                        }
2476
2477                        this.timer_ = null;
2478                    }
2479
2480                    /**
2481                     * Fixes situations where there's a bad seek
2482                     *
2483                     * @return {Boolean} whether an action was taken to fix the seek
2484                     * @private
2485                     */
2486                }, {
2487                    key: 'fixesBadSeeks_',
2488                    value: function fixesBadSeeks_() {
2489                        var seekable = this.seekable();
2490                        var currentTime = this.tech_.currentTime();
2491
2492                        if (this.tech_.seeking() && this.outsideOfSeekableWindow_(seekable, currentTime)) {
2493                            var seekableEnd = seekable.end(seekable.length - 1);
2494
2495                            // sync to live point (if VOD, our seekable was updated and we're simply adjusting)
2496                            this.logger_('Trying to seek outside of seekable at time ' + currentTime + ' with ' + ('seekable range ' + _ranges2['default'].printableRange(seekable) + '. Seeking to ') + (seekableEnd + '.'));
2497                            this.tech_.setCurrentTime(seekableEnd);
2498                            return true;
2499                        }
2500
2501                        return false;
2502                    }
2503
2504                    /**
2505                     * Handler for situations when we determine the player is waiting.
2506                     *
2507                     * @private
2508                     */
2509                }, {
2510                    key: 'waiting_',
2511                    value: function waiting_() {
2512                        if (this.techWaiting_()) {
2513                            return;
2514                        }
2515
2516                        // All tech waiting checks failed. Use last resort correction
2517                        var currentTime = this.tech_.currentTime();
2518                        var buffered = this.tech_.buffered();
2519                        var currentRange = _ranges2['default'].findRange(buffered, currentTime);
2520
2521                        // Sometimes the player can stall for unknown reasons within a contiguous buffered
2522                        // region with no indication that anything is amiss (seen in Firefox). Seeking to
2523                        // currentTime is usually enough to kickstart the player. This checks that the player
2524                        // is currently within a buffered region before attempting a corrective seek.
2525                        // Chrome does not appear to continue `timeupdate` events after a `waiting` event
2526                        // until there is ~ 3 seconds of forward buffer available. PlaybackWatcher should also
2527                        // make sure there is ~3 seconds of forward buffer before taking any corrective action
2528                        // to avoid triggering an `unknownwaiting` event when the network is slow.
2529                        if (currentRange.length && currentTime + 3 <= currentRange.end(0)) {
2530                            this.cancelTimer_();
2531                            this.tech_.setCurrentTime(currentTime);
2532
2533                            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.');
2534
2535                            // unknown waiting corrections may be useful for monitoring QoS
2536                            this.tech_.trigger('unknownwaiting');
2537                            return;
2538                        }
2539                    }
2540
2541                    /**
2542                     * Handler for situations when the tech fires a `waiting` event
2543                     *
2544                     * @return {Boolean}
2545                     *         True if an action (or none) was needed to correct the waiting. False if no
2546                     *         checks passed
2547                     * @private
2548                     */
2549                }, {
2550                    key: 'techWaiting_',
2551                    value: function techWaiting_() {
2552                        var seekable = this.seekable();
2553                        var currentTime = this.tech_.currentTime();
2554
2555                        if (this.tech_.seeking() && this.fixesBadSeeks_()) {
2556                            // Tech is seeking or bad seek fixed, no action needed
2557                            return true;
2558                        }
2559
2560                        if (this.tech_.seeking() || this.timer_ !== null) {
2561                            // Tech is seeking or already waiting on another action, no action needed
2562                            return true;
2563                        }
2564
2565                        if (this.fellOutOfLiveWindow_(seekable, currentTime)) {
2566                            var livePoint = seekable.end(seekable.length - 1);
2567
2568                            this.logger_('Fell out of live window at time ' + currentTime + '. Seeking to ' + ('live point (seekable end) ' + livePoint));
2569                            this.cancelTimer_();
2570                            this.tech_.setCurrentTime(livePoint);
2571
2572                            // live window resyncs may be useful for monitoring QoS
2573                            this.tech_.trigger('liveresync');
2574                            return true;
2575                        }
2576
2577                        var buffered = this.tech_.buffered();
2578                        var nextRange = _ranges2['default'].findNextRange(buffered, currentTime);
2579
2580                        if (this.videoUnderflow_(nextRange, buffered, currentTime)) {
2581                            // Even though the video underflowed and was stuck in a gap, the audio overplayed
2582                            // the gap, leading currentTime into a buffered range. Seeking to currentTime
2583                            // allows the video to catch up to the audio position without losing any audio
2584                            // (only suffering ~3 seconds of frozen video and a pause in audio playback).
2585                            this.cancelTimer_();
2586                            this.tech_.setCurrentTime(currentTime);
2587
2588                            // video underflow may be useful for monitoring QoS
2589                            this.tech_.trigger('videounderflow');
2590                            return true;
2591                        }
2592
2593                        // check for gap
2594                        if (nextRange.length > 0) {
2595                            var difference = nextRange.start(0) - currentTime;
2596
2597                            this.logger_('Stopped at ' + currentTime + ', setting timer for ' + difference + ', seeking ' + ('to ' + nextRange.start(0)));
2598
2599                            this.timer_ = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime);
2600                            return true;
2601                        }
2602
2603                        // All checks failed. Returning false to indicate failure to correct waiting
2604                        return false;
2605                    }
2606                }, {
2607                    key: 'outsideOfSeekableWindow_',
2608                    value: function outsideOfSeekableWindow_(seekable, currentTime) {
2609                        if (!seekable.length) {
2610                            // we can't make a solid case if there's no seekable, default to false
2611                            return false;
2612                        }
2613
2614                        // provide a buffer of .1 seconds to handle rounding/imprecise numbers
2615                        if (currentTime < seekable.start(0) - 0.1 || currentTime > seekable.end(seekable.length - 1) + 0.1) {
2616                            return true;
2617                        }
2618
2619                        return false;
2620                    }
2621                }, {
2622                    key: 'fellOutOfLiveWindow_',
2623                    value: function fellOutOfLiveWindow_(seekable, currentTime) {
2624                        if (seekable.length &&
2625                            // can't fall before 0 and 0 seekable start identifies VOD stream
2626                            seekable.start(0) > 0 && currentTime < seekable.start(0)) {
2627                            return true;
2628                        }
2629
2630                        return false;
2631                    }
2632                }, {
2633                    key: 'videoUnderflow_',
2634                    value: function videoUnderflow_(nextRange, buffered, currentTime) {
2635                        if (nextRange.length === 0) {
2636                            // Even if there is no available next range, there is still a possibility we are
2637                            // stuck in a gap due to video underflow.
2638                            var gap = this.gapFromVideoUnderflow_(buffered, currentTime);
2639
2640                            if (gap) {
2641                                this.logger_('Encountered a gap in video from ' + gap.start + ' to ' + gap.end + '. ' + ('Seeking to current time ' + currentTime));
2642
2643                                return true;
2644                            }
2645                        }
2646
2647                        return false;
2648                    }
2649
2650                    /**
2651                     * Timer callback. If playback still has not proceeded, then we seek
2652                     * to the start of the next buffered region.
2653                     *
2654                     * @private
2655                     */
2656                }, {
2657                    key: 'skipTheGap_',
2658                    value: function skipTheGap_(scheduledCurrentTime) {
2659                        var buffered = this.tech_.buffered();
2660                        var currentTime = this.tech_.currentTime();
2661                        var nextRange = _ranges2['default'].findNextRange(buffered, currentTime);
2662
2663                        this.cancelTimer_();
2664
2665                        if (nextRange.length === 0 || currentTime !== scheduledCurrentTime) {
2666                            return;
2667                        }
2668
2669                        this.logger_('skipTheGap_:', 'currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0));
2670
2671                        // only seek if we still have not played
2672                        this.tech_.setCurrentTime(nextRange.start(0) + _ranges2['default'].TIME_FUDGE_FACTOR);
2673                    }
2674                }, {
2675                    key: 'gapFromVideoUnderflow_',
2676                    value: function gapFromVideoUnderflow_(buffered, currentTime) {
2677                        // At least in Chrome, if there is a gap in the video buffer, the audio will continue
2678                        // playing for ~3 seconds after the video gap starts. This is done to account for
2679                        // video buffer underflow/underrun (note that this is not done when there is audio
2680                        // buffer underflow/underrun -- in that case the video will stop as soon as it
2681                        // encounters the gap, as audio stalls are more noticeable/jarring to a user than
2682                        // video stalls). The player's time will reflect the playthrough of audio, so the
2683                        // time will appear as if we are in a buffered region, even if we are stuck in a
2684                        // "gap."
2685                        //
2686                        // Example:
2687                        // video buffer:   0 => 10.1, 10.2 => 20
2688                        // audio buffer:   0 => 20
2689                        // overall buffer: 0 => 10.1, 10.2 => 20
2690                        // current time: 13
2691                        //
2692                        // Chrome's video froze at 10 seconds, where the video buffer encountered the gap,
2693                        // however, the audio continued playing until it reached ~3 seconds past the gap
2694                        // (13 seconds), at which point it stops as well. Since current time is past the
2695                        // gap, findNextRange will return no ranges.
2696                        //
2697                        // To check for this issue, we see if there is a gap that starts somewhere within
2698                        // a 3 second range (3 seconds +/- 1 second) back from our current time.
2699                        var gaps = _ranges2['default'].findGaps(buffered);
2700
2701                        for (var i = 0; i < gaps.length; i++) {
2702                            var start = gaps.start(i);
2703                            var end = gaps.end(i);
2704
2705                            // gap is starts no more than 4 seconds back
2706                            if (currentTime - start < 4 && currentTime - start > 2) {
2707                                return {
2708                                    start: start,
2709                                    end: end
2710                                };
2711                            }
2712                        }
2713
2714                        return null;
2715                    }
2716
2717                    /**
2718                     * A debugging logger noop that is set to console.log only if debugging
2719                     * is enabled globally
2720                     *
2721                     * @private
2722                     */
2723                }, {
2724                    key: 'logger_',
2725                    value: function logger_() {}
2726                }]);
2727
2728                return PlaybackWatcher;
2729            })();
2730
2731            exports['default'] = PlaybackWatcher;
2732            module.exports = exports['default'];
2733        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2734    },{"./ranges":10,"global/window":30}],8:[function(require,module,exports){
2735        (function (global){
2736            /**
2737             * @file playlist-loader.js
2738             *
2739             * A state machine that manages the loading, caching, and updating of
2740             * M3U8 playlists.
2741             *
2742             */
2743            'use strict';
2744
2745            Object.defineProperty(exports, '__esModule', {
2746                value: true
2747            });
2748
2749            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
2750
2751            var _resolveUrl = require('./resolve-url');
2752
2753            var _resolveUrl2 = _interopRequireDefault(_resolveUrl);
2754
2755            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
2756
2757            var _playlistJs = require('./playlist.js');
2758
2759            var _stream = require('./stream');
2760
2761            var _stream2 = _interopRequireDefault(_stream);
2762
2763            var _m3u8Parser = require('m3u8-parser');
2764
2765            var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
2766
2767            var _globalWindow = require('global/window');
2768
2769            var _globalWindow2 = _interopRequireDefault(_globalWindow);
2770
2771            /**
2772             * Returns a new array of segments that is the result of merging
2773             * properties from an older list of segments onto an updated
2774             * list. No properties on the updated playlist will be overridden.
2775             *
2776             * @param {Array} original the outdated list of segments
2777             * @param {Array} update the updated list of segments
2778             * @param {Number=} offset the index of the first update
2779             * segment in the original segment list. For non-live playlists,
2780             * this should always be zero and does not need to be
2781             * specified. For live playlists, it should be the difference
2782             * between the media sequence numbers in the original and updated
2783             * playlists.
2784             * @return a list of merged segment objects
2785             */
2786            var updateSegments = function updateSegments(original, update, offset) {
2787                var result = update.slice();
2788                var length = undefined;
2789                var i = undefined;
2790
2791                offset = offset || 0;
2792                length = Math.min(original.length, update.length + offset);
2793
2794                for (i = offset; i < length; i++) {
2795                    result[i - offset] = (0, _videoJs.mergeOptions)(original[i], result[i - offset]);
2796                }
2797                return result;
2798            };
2799
2800            /**
2801             * Returns a new master playlist that is the result of merging an
2802             * updated media playlist into the original version. If the
2803             * updated media playlist does not match any of the playlist
2804             * entries in the original master playlist, null is returned.
2805             *
2806             * @param {Object} master a parsed master M3U8 object
2807             * @param {Object} media a parsed media M3U8 object
2808             * @return {Object} a new object that represents the original
2809             * master playlist with the updated media playlist merged in, or
2810             * null if the merge produced no change.
2811             */
2812            var updateMaster = function updateMaster(master, media) {
2813                var changed = false;
2814                var result = (0, _videoJs.mergeOptions)(master, {});
2815                var i = master.playlists.length;
2816                var playlist = undefined;
2817                var segment = undefined;
2818                var j = undefined;
2819
2820                while (i--) {
2821                    playlist = result.playlists[i];
2822                    if (playlist.uri === media.uri) {
2823                        // consider the playlist unchanged if the number of segments
2824                        // are equal and the media sequence number is unchanged
2825                        if (playlist.segments && media.segments && playlist.segments.length === media.segments.length && playlist.mediaSequence === media.mediaSequence) {
2826                            continue;
2827                        }
2828
2829                        result.playlists[i] = (0, _videoJs.mergeOptions)(playlist, media);
2830                        result.playlists[media.uri] = result.playlists[i];
2831
2832                        // if the update could overlap existing segment information,
2833                        // merge the two lists
2834                        if (playlist.segments) {
2835                            result.playlists[i].segments = updateSegments(playlist.segments, media.segments, media.mediaSequence - playlist.mediaSequence);
2836                        }
2837                        // resolve any missing segment and key URIs
2838                        j = 0;
2839                        if (result.playlists[i].segments) {
2840                            j = result.playlists[i].segments.length;
2841                        }
2842                        while (j--) {
2843                            segment = result.playlists[i].segments[j];
2844                            if (!segment.resolvedUri) {
2845                                segment.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.uri);
2846                            }
2847                            if (segment.key && !segment.key.resolvedUri) {
2848                                segment.key.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.key.uri);
2849                            }
2850                            if (segment.map && !segment.map.resolvedUri) {
2851                                segment.map.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.map.uri);
2852                            }
2853                        }
2854                        changed = true;
2855                    }
2856                }
2857                return changed ? result : null;
2858            };
2859
2860            /**
2861             * Load a playlist from a remote location
2862             *
2863             * @class PlaylistLoader
2864             * @extends Stream
2865             * @param {String} srcUrl the url to start with
2866             * @param {Boolean} withCredentials the withCredentials xhr option
2867             * @constructor
2868             */
2869            var PlaylistLoader = function PlaylistLoader(srcUrl, hls, withCredentials) {
2870                var _this = this;
2871
2872                /* eslint-disable consistent-this */
2873                var loader = this;
2874                /* eslint-enable consistent-this */
2875                var dispose = undefined;
2876                var mediaUpdateTimeout = undefined;
2877                var request = undefined;
2878                var playlistRequestError = undefined;
2879                var haveMetadata = undefined;
2880
2881                PlaylistLoader.prototype.constructor.call(this);
2882
2883                this.hls_ = hls;
2884
2885                if (!srcUrl) {
2886                    throw new Error('A non-empty playlist URL is required');
2887                }
2888
2889                playlistRequestError = function (xhr, url, startingState) {
2890                    loader.setBandwidth(request || xhr);
2891
2892                    // any in-flight request is now finished
2893                    request = null;
2894
2895                    if (startingState) {
2896                        loader.state = startingState;
2897                    }
2898
2899                    loader.error = {
2900                        playlist: loader.master.playlists[url],
2901                        status: xhr.status,
2902                        message: 'HLS playlist request error at URL: ' + url,
2903                        responseText: xhr.responseText,
2904                        code: xhr.status >= 500 ? 4 : 2
2905                    };
2906
2907                    loader.trigger('error');
2908                };
2909
2910                // update the playlist loader's state in response to a new or
2911                // updated playlist.
2912                haveMetadata = function (xhr, url) {
2913                    var parser = undefined;
2914                    var refreshDelay = undefined;
2915                    var update = undefined;
2916
2917                    loader.setBandwidth(request || xhr);
2918
2919                    // any in-flight request is now finished
2920                    request = null;
2921
2922                    loader.state = 'HAVE_METADATA';
2923
2924                    parser = new _m3u8Parser2['default'].Parser();
2925                    parser.push(xhr.responseText);
2926                    parser.end();
2927                    parser.manifest.uri = url;
2928
2929                    // merge this playlist into the master
2930                    update = updateMaster(loader.master, parser.manifest);
2931                    refreshDelay = (parser.manifest.targetDuration || 10) * 1000;
2932                    loader.targetDuration = parser.manifest.targetDuration;
2933                    if (update) {
2934                        loader.master = update;
2935                        loader.media_ = loader.master.playlists[parser.manifest.uri];
2936                    } else {
2937                        // if the playlist is unchanged since the last reload,
2938                        // try again after half the target duration
2939                        refreshDelay /= 2;
2940                        loader.trigger('playlistunchanged');
2941                    }
2942
2943                    // refresh live playlists after a target duration passes
2944                    if (!loader.media().endList) {
2945                        _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
2946                        mediaUpdateTimeout = _globalWindow2['default'].setTimeout(function () {
2947                            loader.trigger('mediaupdatetimeout');
2948                        }, refreshDelay);
2949                    }
2950
2951                    loader.trigger('loadedplaylist');
2952                };
2953
2954                // initialize the loader state
2955                loader.state = 'HAVE_NOTHING';
2956
2957                // capture the prototype dispose function
2958                dispose = this.dispose;
2959
2960                /**
2961                 * Abort any outstanding work and clean up.
2962                 */
2963                loader.dispose = function () {
2964                    loader.stopRequest();
2965                    _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
2966                    dispose.call(this);
2967                };
2968
2969                loader.stopRequest = function () {
2970                    if (request) {
2971                        var oldRequest = request;
2972
2973                        request = null;
2974                        oldRequest.onreadystatechange = null;
2975                        oldRequest.abort();
2976                    }
2977                };
2978
2979                /**
2980                 * Returns the number of enabled playlists on the master playlist object
2981                 *
2982                 * @return {Number} number of eneabled playlists
2983                 */
2984                loader.enabledPlaylists_ = function () {
2985                    return loader.master.playlists.filter(_playlistJs.isEnabled).length;
2986                };
2987
2988                /**
2989                 * Returns whether the current playlist is the lowest rendition
2990                 *
2991                 * @return {Boolean} true if on lowest rendition
2992                 */
2993                loader.isLowestEnabledRendition_ = function () {
2994                    if (loader.master.playlists.length === 1) {
2995                        return true;
2996                    }
2997
2998                    var media = loader.media();
2999
3000                    var currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE;
3001
3002                    return loader.master.playlists.filter(function (playlist) {
3003                        var enabled = (0, _playlistJs.isEnabled)(playlist);
3004
3005                        if (!enabled) {
3006                            return false;
3007                        }
3008
3009                        var bandwidth = 0;
3010
3011                        if (playlist && playlist.attributes) {
3012                            bandwidth = playlist.attributes.BANDWIDTH;
3013                        }
3014                        return bandwidth < currentBandwidth;
3015                    }).length === 0;
3016                };
3017
3018                /**
3019                 * Returns whether the current playlist is the final available rendition
3020                 *
3021                 * @return {Boolean} true if on final rendition
3022                 */
3023                loader.isFinalRendition_ = function () {
3024                    return loader.master.playlists.filter(_playlistJs.isEnabled).length === 1;
3025                };
3026
3027                /**
3028                 * When called without any arguments, returns the currently
3029                 * active media playlist. When called with a single argument,
3030                 * triggers the playlist loader to asynchronously switch to the
3031                 * specified media playlist. Calling this method while the
3032                 * loader is in the HAVE_NOTHING causes an error to be emitted
3033                 * but otherwise has no effect.
3034                 *
3035                 * @param {Object=} playlist the parsed media playlist
3036                 * object to switch to
3037                 * @return {Playlist} the current loaded media
3038                 */
3039                loader.media = function (playlist) {
3040                    var startingState = loader.state;
3041                    var mediaChange = undefined;
3042
3043                    // getter
3044                    if (!playlist) {
3045                        return loader.media_;
3046                    }
3047
3048                    // setter
3049                    if (loader.state === 'HAVE_NOTHING') {
3050                        throw new Error('Cannot switch media playlist from ' + loader.state);
3051                    }
3052
3053                    // find the playlist object if the target playlist has been
3054                    // specified by URI
3055                    if (typeof playlist === 'string') {
3056                        if (!loader.master.playlists[playlist]) {
3057                            throw new Error('Unknown playlist URI: ' + playlist);
3058                        }
3059                        playlist = loader.master.playlists[playlist];
3060                    }
3061
3062                    mediaChange = !loader.media_ || playlist.uri !== loader.media_.uri;
3063
3064                    // switch to fully loaded playlists immediately
3065                    if (loader.master.playlists[playlist.uri].endList) {
3066                        // abort outstanding playlist requests
3067                        if (request) {
3068                            request.onreadystatechange = null;
3069                            request.abort();
3070                            request = null;
3071                        }
3072                        loader.state = 'HAVE_METADATA';
3073                        loader.media_ = playlist;
3074
3075                        // trigger media change if the active media has been updated
3076                        if (mediaChange) {
3077                            loader.trigger('mediachanging');
3078                            loader.trigger('mediachange');
3079                        }
3080                        return;
3081                    }
3082
3083                    // switching to the active playlist is a no-op
3084                    if (!mediaChange) {
3085                        return;
3086                    }
3087
3088                    loader.state = 'SWITCHING_MEDIA';
3089
3090                    // there is already an outstanding playlist request
3091                    if (request) {
3092                        if ((0, _resolveUrl2['default'])(loader.master.uri, playlist.uri) === request.url) {
3093                            // requesting to switch to the same playlist multiple times
3094                            // has no effect after the first
3095                            return;
3096                        }
3097                        request.onreadystatechange = null;
3098                        request.abort();
3099                        request = null;
3100                    }
3101
3102                    // request the new playlist
3103                    if (this.media_) {
3104                        this.trigger('mediachanging');
3105                    }
3106                    request = this.hls_.xhr({
3107                        uri: (0, _resolveUrl2['default'])(loader.master.uri, playlist.uri),
3108                        withCredentials: withCredentials
3109                    }, function (error, req) {
3110                        // disposed
3111                        if (!request) {
3112                            return;
3113                        }
3114
3115                        if (error) {
3116                            return playlistRequestError(request, playlist.uri, startingState);
3117                        }
3118
3119                        haveMetadata(req, playlist.uri);
3120
3121                        // fire loadedmetadata the first time a media playlist is loaded
3122                        if (startingState === 'HAVE_MASTER') {
3123                            loader.trigger('loadedmetadata');
3124                        } else {
3125                            loader.trigger('mediachange');
3126                        }
3127                    });
3128                };
3129
3130                /**
3131                 * set the bandwidth on an xhr to the bandwidth on the playlist
3132                 */
3133                loader.setBandwidth = function (xhr) {
3134                    loader.bandwidth = xhr.bandwidth;
3135                };
3136
3137                // live playlist staleness timeout
3138                loader.on('mediaupdatetimeout', function () {
3139                    if (loader.state !== 'HAVE_METADATA') {
3140                        // only refresh the media playlist if no other activity is going on
3141                        return;
3142                    }
3143
3144                    loader.state = 'HAVE_CURRENT_METADATA';
3145                    request = this.hls_.xhr({
3146                        uri: (0, _resolveUrl2['default'])(loader.master.uri, loader.media().uri),
3147                        withCredentials: withCredentials
3148                    }, function (error, req) {
3149                        // disposed
3150                        if (!request) {
3151                            return;
3152                        }
3153
3154                        if (error) {
3155                            return playlistRequestError(request, loader.media().uri, 'HAVE_METADATA');
3156                        }
3157                        haveMetadata(request, loader.media().uri);
3158                    });
3159                });
3160
3161                // setup initial sync info
3162                loader.on('firstplay', function () {
3163                    var playlist = loader.media();
3164
3165                    if (playlist) {
3166                        playlist.syncInfo = {
3167                            mediaSequence: playlist.mediaSequence,
3168                            time: 0
3169                        };
3170                    }
3171                });
3172
3173                /**
3174                 * pause loading of the playlist
3175                 */
3176                loader.pause = function () {
3177                    loader.stopRequest();
3178                    _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
3179                    if (loader.state === 'HAVE_NOTHING') {
3180                        // If we pause the loader before any data has been retrieved, its as if we never
3181                        // started, so reset to an unstarted state.
3182                        loader.started = false;
3183                    }
3184                };
3185
3186                /**
3187                 * start loading of the playlist
3188                 */
3189                loader.load = function (isFinalRendition) {
3190                    var media = loader.media();
3191
3192                    _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
3193
3194                    if (isFinalRendition) {
3195                        var refreshDelay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
3196
3197                        mediaUpdateTimeout = _globalWindow2['default'].setTimeout(loader.load.bind(null, false), refreshDelay);
3198                        return;
3199                    }
3200
3201                    if (!loader.started) {
3202                        loader.start();
3203                        return;
3204                    }
3205
3206                    if (media && !media.endList) {
3207                        loader.trigger('mediaupdatetimeout');
3208                    } else {
3209                        loader.trigger('loadedplaylist');
3210                    }
3211                };
3212
3213                /**
3214                 * start loading of the playlist
3215                 */
3216                loader.start = function () {
3217                    loader.started = true;
3218
3219                    // request the specified URL
3220                    request = _this.hls_.xhr({
3221                        uri: srcUrl,
3222                        withCredentials: withCredentials
3223                    }, function (error, req) {
3224                        var parser = undefined;
3225                        var playlist = undefined;
3226                        var i = undefined;
3227
3228                        // disposed
3229                        if (!request) {
3230                            return;
3231                        }
3232
3233                        // clear the loader's request reference
3234                        request = null;
3235
3236                        if (error) {
3237                            loader.error = {
3238                                status: req.status,
3239                                message: 'HLS playlist request error at URL: ' + srcUrl,
3240                                responseText: req.responseText,
3241                                // MEDIA_ERR_NETWORK
3242                                code: 2
3243                            };
3244                            if (loader.state === 'HAVE_NOTHING') {
3245                                loader.started = false;
3246                            }
3247                            return loader.trigger('error');
3248                        }
3249
3250                        parser = new _m3u8Parser2['default'].Parser();
3251                        parser.push(req.responseText);
3252                        parser.end();
3253
3254                        loader.state = 'HAVE_MASTER';
3255
3256                        parser.manifest.uri = srcUrl;
3257
3258                        // loaded a master playlist
3259                        if (parser.manifest.playlists) {
3260                            loader.master = parser.manifest;
3261
3262                            // setup by-URI lookups and resolve media playlist URIs
3263                            i = loader.master.playlists.length;
3264                            while (i--) {
3265                                playlist = loader.master.playlists[i];
3266                                loader.master.playlists[playlist.uri] = playlist;
3267                                playlist.resolvedUri = (0, _resolveUrl2['default'])(loader.master.uri, playlist.uri);
3268                            }
3269
3270                            // resolve any media group URIs
3271                            ['AUDIO', 'SUBTITLES'].forEach(function (mediaType) {
3272                                for (var groupKey in loader.master.mediaGroups[mediaType]) {
3273                                    for (var labelKey in loader.master.mediaGroups[mediaType][groupKey]) {
3274                                        var mediaProperties = loader.master.mediaGroups[mediaType][groupKey][labelKey];
3275
3276                                        if (mediaProperties.uri) {
3277                                            mediaProperties.resolvedUri = (0, _resolveUrl2['default'])(loader.master.uri, mediaProperties.uri);
3278                                        }
3279                                    }
3280                                }
3281                            });
3282
3283                            loader.trigger('loadedplaylist');
3284                            if (!request) {
3285                                // no media playlist was specifically selected so start
3286                                // from the first listed one
3287                                loader.media(parser.manifest.playlists[0]);
3288                            }
3289                            return;
3290                        }
3291
3292                        // loaded a media playlist
3293                        // infer a master playlist if none was previously requested
3294                        loader.master = {
3295                            mediaGroups: {
3296                                'AUDIO': {},
3297                                'VIDEO': {},
3298                                'CLOSED-CAPTIONS': {},
3299                                'SUBTITLES': {}
3300                            },
3301                            uri: _globalWindow2['default'].location.href,
3302                            playlists: [{
3303                                uri: srcUrl
3304                            }]
3305                        };
3306                        loader.master.playlists[srcUrl] = loader.master.playlists[0];
3307                        loader.master.playlists[0].resolvedUri = srcUrl;
3308                        haveMetadata(req, srcUrl);
3309                        return loader.trigger('loadedmetadata');
3310                    });
3311                };
3312            };
3313
3314            PlaylistLoader.prototype = new _stream2['default']();
3315
3316            exports['default'] = PlaylistLoader;
3317            module.exports = exports['default'];
3318        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
3319    },{"./playlist.js":9,"./resolve-url":13,"./stream":16,"global/window":30,"m3u8-parser":31}],9:[function(require,module,exports){
3320        (function (global){
3321            /**
3322             * @file playlist.js
3323             *
3324             * Playlist related utilities.
3325             */
3326            'use strict';
3327
3328            Object.defineProperty(exports, '__esModule', {
3329                value: true
3330            });
3331
3332            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
3333
3334            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
3335
3336            var _globalWindow = require('global/window');
3337
3338            var _globalWindow2 = _interopRequireDefault(_globalWindow);
3339
3340            var Playlist = {
3341                /**
3342                 * The number of segments that are unsafe to start playback at in
3343                 * a live stream. Changing this value can cause playback stalls.
3344                 * See HTTP Live Streaming, "Playing the Media Playlist File"
3345                 * https://tools.ietf.org/html/draft-pantos-http-live-streaming-18#section-6.3.3
3346                 */
3347                UNSAFE_LIVE_SEGMENTS: 3
3348            };
3349
3350            /**
3351             * walk backward until we find a duration we can use
3352             * or return a failure
3353             *
3354             * @param {Playlist} playlist the playlist to walk through
3355             * @param {Number} endSequence the mediaSequence to stop walking on
3356             */
3357
3358            var backwardDuration = function backwardDuration(playlist, endSequence) {
3359                var result = 0;
3360                var i = endSequence - playlist.mediaSequence;
3361                // if a start time is available for segment immediately following
3362                // the interval, use it
3363                var segment = playlist.segments[i];
3364
3365                // Walk backward until we find the latest segment with timeline
3366                // information that is earlier than endSequence
3367                if (segment) {
3368                    if (typeof segment.start !== 'undefined') {
3369                        return { result: segment.start, precise: true };
3370                    }
3371                    if (typeof segment.end !== 'undefined') {
3372                        return {
3373                            result: segment.end - segment.duration,
3374                            precise: true
3375                        };
3376                    }
3377                }
3378                while (i--) {
3379                    segment = playlist.segments[i];
3380                    if (typeof segment.end !== 'undefined') {
3381                        return { result: result + segment.end, precise: true };
3382                    }
3383
3384                    result += segment.duration;
3385
3386                    if (typeof segment.start !== 'undefined') {
3387                        return { result: result + segment.start, precise: true };
3388                    }
3389                }
3390                return { result: result, precise: false };
3391            };
3392
3393            /**
3394             * walk forward until we find a duration we can use
3395             * or return a failure
3396             *
3397             * @param {Playlist} playlist the playlist to walk through
3398             * @param {Number} endSequence the mediaSequence to stop walking on
3399             */
3400            var forwardDuration = function forwardDuration(playlist, endSequence) {
3401                var result = 0;
3402                var segment = undefined;
3403                var i = endSequence - playlist.mediaSequence;
3404                // Walk forward until we find the earliest segment with timeline
3405                // information
3406
3407                for (; i < playlist.segments.length; i++) {
3408                    segment = playlist.segments[i];
3409                    if (typeof segment.start !== 'undefined') {
3410                        return {
3411                            result: segment.start - result,
3412                            precise: true
3413                        };
3414                    }
3415
3416                    result += segment.duration;
3417
3418                    if (typeof segment.end !== 'undefined') {
3419                        return {
3420                            result: segment.end - result,
3421                            precise: true
3422                        };
3423                    }
3424                }
3425                // indicate we didn't find a useful duration estimate
3426                return { result: -1, precise: false };
3427            };
3428
3429            /**
3430             * Calculate the media duration from the segments associated with a
3431             * playlist. The duration of a subinterval of the available segments
3432             * may be calculated by specifying an end index.
3433             *
3434             * @param {Object} playlist a media playlist object
3435             * @param {Number=} endSequence an exclusive upper boundary
3436             * for the playlist.  Defaults to playlist length.
3437             * @param {Number} expired the amount of time that has dropped
3438             * off the front of the playlist in a live scenario
3439             * @return {Number} the duration between the first available segment
3440             * and end index.
3441             */
3442            var intervalDuration = function intervalDuration(playlist, endSequence, expired) {
3443                var backward = undefined;
3444                var forward = undefined;
3445
3446                if (typeof endSequence === 'undefined') {
3447                    endSequence = playlist.mediaSequence + playlist.segments.length;
3448                }
3449
3450                if (endSequence < playlist.mediaSequence) {
3451                    return 0;
3452                }
3453
3454                // do a backward walk to estimate the duration
3455                backward = backwardDuration(playlist, endSequence);
3456                if (backward.precise) {
3457                    // if we were able to base our duration estimate on timing
3458                    // information provided directly from the Media Source, return
3459                    // it
3460                    return backward.result;
3461                }
3462
3463                // walk forward to see if a precise duration estimate can be made
3464                // that way
3465                forward = forwardDuration(playlist, endSequence);
3466                if (forward.precise) {
3467                    // we found a segment that has been buffered and so it's
3468                    // position is known precisely
3469                    return forward.result;
3470                }
3471
3472                // return the less-precise, playlist-based duration estimate
3473                return backward.result + expired;
3474            };
3475
3476            /**
3477             * Calculates the duration of a playlist. If a start and end index
3478             * are specified, the duration will be for the subset of the media
3479             * timeline between those two indices. The total duration for live
3480             * playlists is always Infinity.
3481             *
3482             * @param {Object} playlist a media playlist object
3483             * @param {Number=} endSequence an exclusive upper
3484             * boundary for the playlist. Defaults to the playlist media
3485             * sequence number plus its length.
3486             * @param {Number=} expired the amount of time that has
3487             * dropped off the front of the playlist in a live scenario
3488             * @return {Number} the duration between the start index and end
3489             * index.
3490             */
3491            var duration = function duration(playlist, endSequence, expired) {
3492                if (!playlist) {
3493                    return 0;
3494                }
3495
3496                if (typeof expired !== 'number') {
3497                    expired = 0;
3498                }
3499
3500                // if a slice of the total duration is not requested, use
3501                // playlist-level duration indicators when they're present
3502                if (typeof endSequence === 'undefined') {
3503                    // if present, use the duration specified in the playlist
3504                    if (playlist.totalDuration) {
3505                        return playlist.totalDuration;
3506                    }
3507
3508                    // duration should be Infinity for live playlists
3509                    if (!playlist.endList) {
3510                        return _globalWindow2['default'].Infinity;
3511                    }
3512                }
3513
3514                // calculate the total duration based on the segment durations
3515                return intervalDuration(playlist, endSequence, expired);
3516            };
3517
3518            exports.duration = duration;
3519            /**
3520             * Calculate the time between two indexes in the current playlist
3521             * neight the start- nor the end-index need to be within the current
3522             * playlist in which case, the targetDuration of the playlist is used
3523             * to approximate the durations of the segments
3524             *
3525             * @param {Object} playlist a media playlist object
3526             * @param {Number} startIndex
3527             * @param {Number} endIndex
3528             * @return {Number} the number of seconds between startIndex and endIndex
3529             */
3530            var sumDurations = function sumDurations(playlist, startIndex, endIndex) {
3531                var durations = 0;
3532
3533                if (startIndex > endIndex) {
3534                    var _ref = [endIndex, startIndex];
3535                    startIndex = _ref[0];
3536                    endIndex = _ref[1];
3537                }
3538
3539                if (startIndex < 0) {
3540                    for (var i = startIndex; i < Math.min(0, endIndex); i++) {
3541                        durations += playlist.targetDuration;
3542                    }
3543                    startIndex = 0;
3544                }
3545
3546                for (var i = startIndex; i < endIndex; i++) {
3547                    durations += playlist.segments[i].duration;
3548                }
3549
3550                return durations;
3551            };
3552
3553            exports.sumDurations = sumDurations;
3554            /**
3555             * Calculates the playlist end time
3556             *
3557             * @param {Object} playlist a media playlist object
3558             * @param {Number=} expired the amount of time that has
3559             *                  dropped off the front of the playlist in a live scenario
3560             * @param {Boolean|false} useSafeLiveEnd a boolean value indicating whether or not the playlist
3561             *                        end calculation should consider the safe live end (truncate the playlist
3562             *                        end by three segments). This is normally used for calculating the end of
3563             *                        the playlist's seekable range.
3564             * @returns {Number} the end time of playlist
3565             * @function playlistEnd
3566             */
3567            var playlistEnd = function playlistEnd(playlist, expired, useSafeLiveEnd) {
3568                if (!playlist || !playlist.segments) {
3569                    return null;
3570                }
3571                if (playlist.endList) {
3572                    return duration(playlist);
3573                }
3574
3575                if (expired === null) {
3576                    return null;
3577                }
3578
3579                expired = expired || 0;
3580
3581                var endSequence = useSafeLiveEnd ? Math.max(0, playlist.segments.length - Playlist.UNSAFE_LIVE_SEGMENTS) : Math.max(0, playlist.segments.length);
3582
3583                return intervalDuration(playlist, playlist.mediaSequence + endSequence, expired);
3584            };
3585
3586            exports.playlistEnd = playlistEnd;
3587            /**
3588             * Calculates the interval of time that is currently seekable in a
3589             * playlist. The returned time ranges are relative to the earliest
3590             * moment in the specified playlist that is still available. A full
3591             * seekable implementation for live streams would need to offset
3592             * these values by the duration of content that has expired from the
3593             * stream.
3594             *
3595             * @param {Object} playlist a media playlist object
3596             * dropped off the front of the playlist in a live scenario
3597             * @param {Number=} expired the amount of time that has
3598             * dropped off the front of the playlist in a live scenario
3599             * @return {TimeRanges}
3599 the periods of time that are valid targets
3600             * for seeking
3601             */
3602            var seekable = function seekable(playlist, expired) {
3603                var useSafeLiveEnd = true;
3604                var seekableStart = expired || 0;
3605                var seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd);
3606
3607                if (seekableEnd === null) {
3608                    return (0, _videoJs.createTimeRange)();
3609                }
3610                return (0, _videoJs.createTimeRange)(seekableStart, seekableEnd);
3611            };
3612
3613            exports.seekable = seekable;
3614            var isWholeNumber = function isWholeNumber(num) {
3615                return num - Math.floor(num) === 0;
3616            };
3617
3618            var roundSignificantDigit = function roundSignificantDigit(increment, num) {
3619                // If we have a whole number, just add 1 to it
3620                if (isWholeNumber(num)) {
3621                    return num + increment * 0.1;
3622                }
3623
3624                var numDecimalDigits = num.toString().split('.')[1].length;
3625
3626                for (var i = 1; i <= numDecimalDigits; i++) {
3627                    var scale = Math.pow(10, i);
3628                    var temp = num * scale;
3629
3630                    if (isWholeNumber(temp) || i === numDecimalDigits) {
3631                        return (temp + increment) / scale;
3632                    }
3633                }
3634            };
3635
3636            var ceilLeastSignificantDigit = roundSignificantDigit.bind(null, 1);
3637            var floorLeastSignificantDigit = roundSignificantDigit.bind(null, -1);
3638
3639            /**
3640             * Determine the index and estimated starting time of the segment that
3641             * contains a specified playback position in a media playlist.
3642             *
3643             * @param {Object} playlist the media playlist to query
3644             * @param {Number} currentTime The number of seconds since the earliest
3645             * possible position to determine the containing segment for
3646             * @param {Number} startIndex
3647             * @param {Number} startTime
3648             * @return {Object}
3649             */
3650            var getMediaInfoForTime_ = function getMediaInfoForTime_(playlist, currentTime, startIndex, startTime) {
3651                var i = undefined;
3652                var segment = undefined;
3653                var numSegments = playlist.segments.length;
3654
3655                var time = currentTime - startTime;
3656
3657                if (time < 0) {
3658                    // Walk backward from startIndex in the playlist, adding durations
3659                    // until we find a segment that contains `time` and return it
3660                    if (startIndex > 0) {
3661                        for (i = startIndex - 1; i >= 0; i--) {
3662                            segment = playlist.segments[i];
3663                            time += floorLeastSignificantDigit(segment.duration);
3664                            if (time > 0) {
3665                                return {
3666                                    mediaIndex: i,
3667                                    startTime: startTime - sumDurations(playlist, startIndex, i)
3668                                };
3669                            }
3670                        }
3671                    }
3672                    // We were unable to find a good segment within the playlist
3673                    // so select the first segment
3674                    return {
3675                        mediaIndex: 0,
3676                        startTime: currentTime
3677                    };
3678                }
3679
3680                // When startIndex is negative, we first walk forward to first segment
3681                // adding target durations. If we "run out of time" before getting to
3682                // the first segment, return the first segment
3683                if (startIndex < 0) {
3684                    for (i = startIndex; i < 0; i++) {
3685                        time -= playlist.targetDuration;
3686                        if (time < 0) {
3687                            return {
3688                                mediaIndex: 0,
3689                                startTime: currentTime
3690                            };
3691                        }
3692                    }
3693                    startIndex = 0;
3694                }
3695
3696                // Walk forward from startIndex in the playlist, subtracting durations
3697                // until we find a segment that contains `time` and return it
3698                for (i = startIndex; i < numSegments; i++) {
3699                    segment = playlist.segments[i];
3700                    time -= ceilLeastSignificantDigit(segment.duration);
3701                    if (time < 0) {
3702                        return {
3703                            mediaIndex: i,
3704                            startTime: startTime + sumDurations(playlist, startIndex, i)
3705                        };
3706                    }
3707                }
3708
3709                // We are out of possible candidates so load the last one...
3710                return {
3711                    mediaIndex: numSegments - 1,
3712                    startTime: currentTime
3713                };
3714            };
3715
3716            exports.getMediaInfoForTime_ = getMediaInfoForTime_;
3717            /**
3718             * Check whether the playlist is blacklisted or not.
3719             *
3720             * @param {Object} playlist the media playlist object
3721             * @return {boolean} whether the playlist is blacklisted or not
3722             * @function isBlacklisted
3723             */
3724            var isBlacklisted = function isBlacklisted(playlist) {
3725                return playlist.excludeUntil && playlist.excludeUntil > Date.now();
3726            };
3727
3728            exports.isBlacklisted = isBlacklisted;
3729            /**
3730             * Check whether the playlist is enabled or not.
3731             *
3732             * @param {Object} playlist the media playlist object
3733             * @return {boolean} whether the playlist is enabled or not
3734             * @function isEnabled
3735             */
3736            var isEnabled = function isEnabled(playlist) {
3737                var blacklisted = isBlacklisted(playlist);
3738
3739                return !playlist.disabled && !blacklisted;
3740            };
3741
3742            exports.isEnabled = isEnabled;
3743            Playlist.duration = duration;
3744            Playlist.seekable = seekable;
3745            Playlist.getMediaInfoForTime_ = getMediaInfoForTime_;
3746            Playlist.isEnabled = isEnabled;
3747            Playlist.isBlacklisted = isBlacklisted;
3748            Playlist.playlistEnd = playlistEnd;
3749
3750// exports
3751            exports['default'] = Playlist;
3752        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
3753    },{"global/window":30}],10:[function(require,module,exports){
3754        (function (global){
3755            /**
3756             * ranges
3757             *
3758             * Utilities for working with TimeRanges.
3759             *
3760             */
3761
3762            'use strict';
3763
3764            Object.defineProperty(exports, '__esModule', {
3765                value: true
3766            });
3767
3768            var _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'); } }; })();
3769
3770            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
3771
3772            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
3773
3774            var _videoJs2 = _interopRequireDefault(_videoJs);
3775
3776// Fudge factor to account for TimeRanges rounding
3777            var TIME_FUDGE_FACTOR = 1 / 30;
3778
3779            /**
3780             * Clamps a value to within a range
3781             * @param {Number} num - the value to clamp
3782             * @param {Number} start - the start of the range to clamp within, inclusive
3783             * @param {Number} end - the end of the range to clamp within, inclusive
3784             * @return {Number}
3785             */
3786            var clamp = function clamp(num, _ref) {
3787                var _ref2 = _slicedToArray(_ref, 2);
3788
3789                var start = _ref2[0];
3790                var end = _ref2[1];
3791
3792                return Math.min(Math.max(start, num), end);
3793            };
3794            var filterRanges = function filterRanges(timeRanges, predicate) {
3795                var results = [];
3796                var i = undefined;
3797
3798                if (timeRanges && timeRanges.length) {
3799                    // Search for ranges that match the predicate
3800                    for (i = 0; i < timeRanges.length; i++) {
3801                        if (predicate(timeRanges.start(i), timeRanges.end(i))) {
3802                            results.push([timeRanges.start(i), timeRanges.end(i)]);
3803                        }
3804                    }
3805                }
3806
3807                return _videoJs2['default'].createTimeRanges(results);
3808            };
3809
3810            /**
3811             * Attempts to find the buffered TimeRange that contains the specified
3812             * time.
3813             * @param {TimeRanges} buffered - the TimeRanges object to query
3814             * @param {number} time  - the time to filter on.
3815             * @returns {TimeRanges} a new TimeRanges object
3816             */
3817            var findRange = function findRange(buffered, time) {
3818                return filterRanges(buffered, function (start, end) {
3819                    return start - TIME_FUDGE_FACTOR <= time && end + TIME_FUDGE_FACTOR >= time;
3820                });
3821            };
3822
3823            /**
3824             * Returns the TimeRanges that begin later than the specified time.
3825             * @param {TimeRanges} timeRanges - the TimeRanges object to query
3826             * @param {number} time - the time to filter on.
3827             * @returns {TimeRanges} a new TimeRanges object.
3828             */
3829            var findNextRange = function findNextRange(timeRanges, time) {
3830                return filterRanges(timeRanges, function (start) {
3831                    return start - TIME_FUDGE_FACTOR >= time;
3832                });
3833            };
3834
3835            /**
3836             * Returns gaps within a list of TimeRanges
3837             * @param {TimeRanges} buffered - the TimeRanges object
3838             * @return {TimeRanges} a TimeRanges object of gaps
3839             */
3840            var findGaps = function findGaps(buffered) {
3841                if (buffered.length < 2) {
3842                    return _videoJs2['default'].createTimeRanges();
3843                }
3844
3845                var ranges = [];
3846
3847                for (var i = 1; i < buffered.length; i++) {
3848                    var start = buffered.end(i - 1);
3849                    var end = buffered.start(i);
3850
3851                    ranges.push([start, end]);
3852                }
3853
3854                return _videoJs2['default'].createTimeRanges(ranges);
3855            };
3856
3857            /**
3858             * Search for a likely end time for the segment that was just appened
3859             * based on the state of the `buffered` property before and after the
3860             * append. If we fin only one such uncommon end-point return it.
3861             * @param {TimeRanges} original - the buffered time ranges before the update
3862             * @param {TimeRanges} update - the buffered time ranges after the update
3863             * @returns {Number|null} the end time added between `original` and `update`,
3864             * or null if one cannot be unambiguously determined.
3865             */
3866            var findSoleUncommonTimeRangesEnd = function findSoleUncommonTimeRangesEnd(original, update) {
3867                var i = undefined;
3868                var start = undefined;
3869                var end = undefined;
3870                var result = [];
3871                var edges = [];
3872
3873                // In order to qualify as a possible candidate, the end point must:
3874                //  1) Not have already existed in the `original` ranges
3875                //  2) Not result from the shrinking of a range that already existed
3876                //     in the `original` ranges
3877                //  3) Not be contained inside of a range that existed in `original`
3878                var overlapsCurrentEnd = function overlapsCurrentEnd(span) {
3879                    return span[0] <= end && span[1] >= end;
3880                };
3881
3882                if (original) {
3883                    // Save all the edges in the `original` TimeRanges object
3884                    for (i = 0; i < original.length; i++) {
3885                        start = original.start(i);
3886                        end = original.end(i);
3887
3888                        edges.push([start, end]);
3889                    }
3890                }
3891
3892                if (update) {
3893                    // Save any end-points in `update` that are not in the `original`
3894                    // TimeRanges object
3895                    for (i = 0; i < update.length; i++) {
3896                        start = update.start(i);
3897                        end = update.end(i);
3898
3899                        if (edges.some(overlapsCurrentEnd)) {
3900                            continue;
3901                        }
3902
3903                        // at this point it must be a unique non-shrinking end edge
3904                        result.push(end);
3905                    }
3906                }
3907
3908                // we err on the side of caution and return null if didn't find
3909                // exactly *one* differing end edge in the search above
3910                if (result.length !== 1) {
3911                    return null;
3912                }
3913
3914                return result[0];
3915            };
3916
3917            /**
3918             * Calculate the intersection of two TimeRanges
3919             * @param {TimeRanges} bufferA
3920             * @param {TimeRanges} bufferB
3921             * @returns {TimeRanges} The interesection of `bufferA` with `bufferB`
3922             */
3923            var bufferIntersection = function bufferIntersection(bufferA, bufferB) {
3924                var start = null;
3925                var end = null;
3926                var arity = 0;
3927                var extents = [];
3928                var ranges = [];
3929
3930                if (!bufferA || !bufferA.length || !bufferB || !bufferB.length) {
3931                    return _videoJs2['default'].createTimeRange();
3932                }
3933
3934                // Handle the case where we have both buffers and create an
3935                // intersection of the two
3936                var count = bufferA.length;
3937
3938                // A) Gather up all start and end times
3939                while (count--) {
3940                    extents.push({ time: bufferA.start(count), type: 'start' });
3941                    extents.push({ time: bufferA.end(count), type: 'end' });
3942                }
3943                count = bufferB.length;
3944                while (count--) {
3945                    extents.push({ time: bufferB.start(count), type: 'start' });
3946                    extents.push({ time: bufferB.end(count), type: 'end' });
3947                }
3948                // B) Sort them by time
3949                extents.sort(function (a, b) {
3950                    return a.time - b.time;
3951                });
3952
3953                // C) Go along one by one incrementing arity for start and decrementing
3954                //    arity for ends
3955                for (count = 0; count < extents.length; count++) {
3956                    if (extents[count].type === 'start') {
3957                        arity++;
3958
3959                        // D) If arity is ever incremented to 2 we are entering an
3960                        //    overlapping range
3961                        if (arity === 2) {
3962                            start = extents[count].time;
3963                        }
3964                    } else if (extents[count].type === 'end') {
3965                        arity--;
3966
3967                        // E) If arity is ever decremented to 1 we leav
3967ing an
3968                        //    overlapping range
3969                        if (arity === 1) {
3970                            end = extents[count].time;
3971                        }
3972                    }
3973
3974                    // F) Record overlapping ranges
3975                    if (start !== null && end !== null) {
3976                        ranges.push([start, end]);
3977                        start = null;
3978                        end = null;
3979                    }
3980                }
3981
3982                return _videoJs2['default'].createTimeRanges(ranges);
3983            };
3984
3985            /**
3986             * Calculates the percentage of `segmentRange` that overlaps the
3987             * `buffered` time ranges.
3988             * @param {TimeRanges} segmentRange - the time range that the segment
3989             * covers adjusted according to currentTime
3990             * @param {TimeRanges} referenceRange - the original time range that the
3991             * segment covers
3992             * @param {Number} currentTime - time in seconds where the current playback
3993             * is at
3994             * @param {TimeRanges} buffered - the currently buffered time ranges
3995             * @returns {Number} percent of the segment currently buffered
3996             */
3997            var calculateBufferedPercent = function calculateBufferedPercent(adjustedRange, referenceRange, currentTime, buffered) {
3998                var referenceDuration = referenceRange.end(0) - referenceRange.start(0);
3999                var adjustedDuration = adjustedRange.end(0) - adjustedRange.start(0);
4000                var bufferMissingFromAdjusted = referenceDuration - adjustedDuration;
4001                var adjustedIntersection = bufferIntersection(adjustedRange, buffered);
4002                var referenceIntersection = bufferIntersection(referenceRange, buffered);
4003                var adjustedOverlap = 0;
4004                var referenceOverlap = 0;
4005
4006                var count = adjustedIntersection.length;
4007
4008                while (count--) {
4009                    adjustedOverlap += adjustedIntersection.end(count) - adjustedIntersection.start(count);
4010
4011                    // If the current overlap segment starts at currentTime, then increase the
4012                    // overlap duration so that it actually starts at the beginning of referenceRange
4013                    // by including the difference between the two Range's durations
4014                    // This is a work around for the way Flash has no buffer before currentTime
4015                    if (adjustedIntersection.start(count) === currentTime) {
4016                        adjustedOverlap += bufferMissingFromAdjusted;
4017                    }
4018                }
4019
4020                count = referenceIntersection.length;
4021
4022                while (count--) {
4023                    referenceOverlap += referenceIntersection.end(count) - referenceIntersection.start(count);
4024                }
4025
4026                // Use whichever value is larger for the percentage-buffered since that value
4027                // is likely more accurate because the only way
4028                return Math.max(adjustedOverlap, referenceOverlap) / referenceDuration * 100;
4029            };
4030
4031            /**
4032             * Return the amount of a range specified by the startOfSegment and segmentDuration
4033             * overlaps the current buffered content.
4034             *
4035             * @param {Number} startOfSegment - the time where the segment begins
4036             * @param {Number} segmentDuration - the duration of the segment in seconds
4037             * @param {Number} currentTime - time in seconds where the current playback
4038             * is at
4039             * @param {TimeRanges} buffered - the state of the buffer
4040             * @returns {Number} percentage of the segment's time range that is
4041             * already in `buffered`
4042             */
4043            var getSegmentBufferedPercent = function getSegmentBufferedPercent(startOfSegment, segmentDuration, currentTime, buffered) {
4044                var endOfSegment = startOfSegment + segmentDuration;
4045
4046                // The entire time range of the segment
4047                var originalSegmentRange = _videoJs2['default'].createTimeRanges([[startOfSegment, endOfSegment]]);
4048
4049                // The adjusted segment time range that is setup such that it starts
4050                // no earlier than currentTime
4051                // Flash has no notion of a back-buffer so adjustedSegmentRange adjusts
4052                // for that and the function will still return 100% if a only half of a
4053                // segment is actually in the buffer as long as the currentTime is also
4054                // half-way through the segment
4055                var adjustedSegmentRange = _videoJs2['default'].createTimeRanges([[clamp(startOfSegment, [currentTime, endOfSegment]), endOfSegment]]);
4056
4057                // This condition happens when the currentTime is beyond the segment's
4058                // end time
4059                if (adjustedSegmentRange.start(0) === adjustedSegmentRange.end(0)) {
4060                    return 0;
4061                }
4062
4063                var percent = calculateBufferedPercent(adjustedSegmentRange, originalSegmentRange, currentTime, buffered);
4064
4065                // If the segment is reported as having a zero duration, return 0%
4066                // since it is likely that we will need to fetch the segment
4067                if (isNaN(percent) || percent === Infinity || percent === -Infinity) {
4068                    return 0;
4069                }
4070
4071                return percent;
4072            };
4073
4074            /**
4075             * Gets a human readable string for a TimeRange
4076             *
4077             * @param {TimeRange} range
4078             * @returns {String} a human readable string
4079             */
4080            var printableRange = function printableRange(range) {
4081                var strArr = [];
4082
4083                if (!range || !range.length) {
4084                    return '';
4085                }
4086
4087                for (var i = 0; i < range.length; i++) {
4088                    strArr.push(range.start(i) + ' => ' + range.end(i));
4089                }
4090
4091                return strArr.join(', ');
4092            };
4093
4094            exports['default'] = {
4095                findRange: findRange,
4096                findNextRange: findNextRange,
4097                findGaps: findGaps,
4098                findSoleUncommonTimeRangesEnd: findSoleUncommonTimeRangesEnd,
4099                getSegmentBufferedPercent: getSegmentBufferedPercent,
4100                TIME_FUDGE_FACTOR: TIME_FUDGE_FACTOR,
4101                printableRange: printableRange
4102            };
4103            module.exports = exports['default'];
4104        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
4105    },{}],11:[function(require,module,exports){
4106        (function (global){
4107            'use strict';
4108
4109            Object.defineProperty(exports, '__esModule', {
4110                value: true
4111            });
4112
4113            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
4114
4115            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
4116
4117            var _videoJs2 = _interopRequireDefault(_videoJs);
4118
4119            var defaultOptions = {
4120                errorInterval: 30,
4121                getSource: function getSource(next) {
4122                    var tech = this.tech({ IWillNotUseThisInPlugins: true });
4123                    var sourceObj = tech.currentSource_;
4124
4125                    return next(sourceObj);
4126                }
4127            };
4128
4129            /**
4130             * Main entry point for the plugin
4131             *
4132             * @param {Player} player a reference to a videojs Player instance
4133             * @param {Object} [options] an object with plugin options
4134             * @private
4135             */
4136            var initPlugin = function initPlugin(player, options) {
4137                var lastCalled = 0;
4138                var seekTo = 0;
4139                var localOptions = _videoJs2['default'].mergeOptions(defaultOptions, options);
4140
4141                /**
4142                 * Player modifications to perform that must wait until `loadedmetadata`
4143                 * has been triggered
4144                 *
4145                 * @private
4146                 */
4147                var loadedMetadataHandler = function loadedMetadataHandler() {
4148                    if (seekTo) {
4149                        player.currentTime(seekTo);
4150                    }
4151                };
4152
4153                /**
4154                 * Set the source on the player element, play, and seek if necessary
4155                 *
4156                 * @param {Object} sourceObj An object specifying the source url and mime-type to play
4157                 * @private
4158                 */
4159                var setSource = function setSource(sourceObj) {
4160                    if (sourceObj === null || sourceObj === undefined) {
4161                        return;
4162                    }
4163                    seekTo = player.duration() !== Infinity && player.currentTime() || 0;
4164
4165                    player.one('loadedmetadata', loadedMetadataHandler);
4166
4167                    player.src(sourceObj);
4168                    player.play();
4169                };
4170
4171                /**
4172                 * Attempt to get a source from either the built-in getSource function
4173                 * or a custom function provided via the options
4174                 *
4175                 * @private
4176                 */
4177                var errorHandler = function errorHandler() {
4178                    // Do not attempt to reload the source if a source-reload occurred before
4179                    // 'errorInterval' time has elapsed since the last source-reload
4180                    if (Date.now() - lastCalled < localOptions.errorInterval * 1000) {
4181                        return;
4182                    }
4183
4184                    if (!localOptions.getSource || typeof localOptions.getSource !== 'function') {
4185                        _videoJs2['default'].log.error('ERROR: reloadSourceOnError - The option getSource must be a function!');
4186                        return;
4187                    }
4188                    lastCalled = Date.now();
4189
4190                    return localOptions.getSource.call(player, setSource);
4191                };
4192
4193                /**
4194                 * Unbind any event handlers that were bound by the plugin
4195                 *
4196                 * @private
4197                 */
4198                var cleanupEvents = function cleanupEvents() {
4199                    player.off('loadedmetadata', loadedMetadataHandler);
4200                    player.off('error', errorHandler);
4201                    player.off('dispose', cleanupEvents);
4202                };
4203
4204                /**
4205                 * Cleanup before re-initializing the plugin
4206                 *
4207                 * @param {Object} [newOptions] an object with plugin options
4208                 * @private
4209                 */
4210                var reinitPlugin = function reinitPlugin(newOptions) {
4211                    cleanupEvents();
4212                    initPlugin(player, newOptions);
4213                };
4214
4215                player.on('error', errorHandler);
4216                player.on('dispose', cleanupEvents);
4217
4218                // Overwrite the plugin function so that we can correctly cleanup before
4219                // initializing the plugin
4220                player.reloadSourceOnError = reinitPlugin;
4221            };
4222
4223            /**
4224             * Reload the source when an error is detected as long as there
4225             * wasn't an error previously within the last 30 seconds
4226             *
4227             * @param {Object} [options] an object with plugin options
4228             */
4229            var reloadSourceOnError = function reloadSourceOnError(options) {
4230                initPlugin(this, options);
4231            };
4232
4233            exports['default'] = reloadSourceOnError;
4234            module.exports = exports['default'];
4235        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
4236    },{}],12:[function(require,module,exports){
4237        'use strict';
4238
4239        Object.defineProperty(exports, '__esModule', {
4240            value: true
4241        });
4242
4243        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
4244
4245        var _playlistJs = require('./playlist.js');
4246
4247        /**
4248         * Enable/disable playlist function. It is intended to have the first two
4249         * arguments partially-applied in order to create the final per-playlist
4250         * function.
4251         *
4252         * @param {PlaylistLoader} playlist - The rendition or media-playlist
4253         * @param {Function} changePlaylistFn - A function to be called after a
4254         * playlist's enabled-state has been changed. Will NOT be called if a
4255         * playlist's enabled-state is unchanged
4256         * @param {Boolean=} enable - Value to set the playlist enabled-state to
4257         * or if undefined returns the current enabled-state for the playlist
4258         * @return {Boolean} The current enabled-state of the playlist
4259         */
4260        var enableFunction = function enableFunction(loader, playlistUri, changePlaylistFn, enable) {
4261            var playlist = loader.master.playlists[playlistUri];
4262            var blacklisted = (0, _playlistJs.isBlacklisted)(playlist);
4263            var currentlyEnabled = (0, _playlistJs.isEnabled)(playlist);
4264
4265            if (typeof enable === 'undefined') {
4266                return currentlyEnabled;
4267            }
4268
4269            if (enable) {
4270                delete playlist.disabled;
4271            } else {
4272                playlist.disabled = true;
4273            }
4274
4275            if (enable !== currentlyEnabled && !blacklisted) {
4276                // Ensure the outside world knows about our changes
4277                changePlaylistFn();
4278            }
4279
4280            return enable;
4281        };
4282
4283        /**
4284         * The representation object encapsulates the publicly visible information
4285         * in a media playlist along with a setter/getter-type function (enabled)
4286         * for changing the enabled-state of a particular playlist entry
4287         *
4288         * @class Representation
4289         */
4290
4291        var Representation = function Representation(hlsHandler, playlist, id) {
4292                _classCallCheck(this, Representation);
4293
4294                // Get a reference to a bound version of fastQualityChange_
4295                var fastChangeFunction = hlsHandler.masterPlaylistController_.fastQualityChange_.bind(hlsHandler.masterPlaylistController_);
4296
4297                // Carefully descend into the playlist's attributes since most
4298                // properties are optional
4299                if (playlist.attributes) {
4300                    var attributes = playlist.attributes;
4301
4302                    if (attributes.RESOLUTION) {
4303                        var resolution = attributes.RESOLUTION;
4304
4305                        this.width = resolution.width;
4306                        this.height = resolution.height;
4307                    }
4308
4309                    this.bandwidth = attributes.BANDWIDTH;
4310                }
4311
4312                // The id is simply the ordinality of the media playlist
4313                // within the master playlist
4314                this.id = id;
4315
4316                // Partially-apply the enableFunction to create a playlist-
4317                // specific variant
4318                this.enabled = enableFunction.bind(this, hlsHandler.playlists, playlist.uri, fastChangeFunction);
4319            }
4320
4321            /**
4322             * A mixin function that adds the `representations` api to an instance
4323             * of the HlsHandler class
4324             * @param {HlsHandler} hlsHandler - An instance of HlsHandler to add the
4325             * representation API into
4326             */
4327        ;
4328
4329        var renditionSelectionMixin = function renditionSelectionMixin(hlsHandler) {
4330            var playlists = hlsHandler.playlists;
4331
4332            // Add a single API-specific function to the HlsHandler instance
4333            hlsHandler.representations = function () {
4334                return playlists.master.playlists.filter(function (media) {
4335                    return !(0, _playlistJs.isBlacklisted)(media);
4336                }).map(function (e, i) {
4337                    return new Representation(hlsHandler, e, e.uri);
4338                });
4339            };
4340        };
4341
4342        exports['default'] = renditionSelectionMixin;
4343        module.exports = exports['default'];
4344    },{"./playlist.js":9}],13:[function(require,module,exports){
4345        /**
4346         * @file resolve-url.js
4347         */
4348
4349        'use strict';
4350
4351        Object.defineProperty(exports, '__esModule', {
4352            value: true
4353        });
4354
4355        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
4356
4357        var _urlToolkit = require('url-toolkit');
4358
4359        var _urlToolkit2 = _interopRequireDefault(_urlToolkit);
4360
4361        var _globalWindow = require('global/window');
4362
4363        var _globalWindow2 = _interopRequireDefault(_globalWindow);
4364
4365        var resolveUrl = function resolveUrl(baseURL, relativeURL) {
4366            // return early if we don't need to resolve
4367            if (/^[a-z]+:/i.test(relativeURL)) {
4368                return relativeURL;
4369            }
4370
4371            // if the base URL is relative then combine with the current location
4372            if (!/\/\//i.test(baseURL)) {
4373                baseURL = _urlToolkit2['default'].buildAbsoluteURL(_globalWindow2['default'].location.href, baseURL);
4374            }
4375
4376            return _urlToolkit2['default'].buildAbsoluteURL(baseURL, relativeURL);
4377        };
4378
4379        exports['default'] = resolveUrl;
4380        module.exports = exports['default'];
4381    },{"global/window":30,"url-toolkit":61}],14:[function(require,module,exports){
4382        (function (global){
4383            /**
4384             * @file segment-loader.js
4385             */
4386            'use strict';
4387
4388            Object.defineProperty(exports, '__esModule', {
4389                value: true
4390            });
4391
4392            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
4393
4394            var _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); } } };
4395
4396            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
4397
4398            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
4399
4400            function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
4400onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
4401
4402            var _playlist = require('./playlist');
4403
4404            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
4405
4406            var _videoJs2 = _interopRequireDefault(_videoJs);
4407
4408            var _sourceUpdater = require('./source-updater');
4409
4410            var _sourceUpdater2 = _interopRequireDefault(_sourceUpdater);
4411
4412            var _config = require('./config');
4413
4414            var _config2 = _interopRequireDefault(_config);
4415
4416            var _globalWindow = require('global/window');
4417
4418            var _globalWindow2 = _interopRequireDefault(_globalWindow);
4419
4420            var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs = require('videojs-contrib-media-sources/es5/remove-cues-from-track.js');
4421
4422            var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2 = _interopRequireDefault(_videojsContribMediaSourcesEs5RemoveCuesFromTrackJs);
4423
4424            var _binUtils = require('./bin-utils');
4425
4426            var _mediaSegmentRequest = require('./media-segment-request');
4427
4428// in ms
4429            var CHECK_BUFFER_DELAY = 500;
4430
4431            /**
4432             * Determines if we should call endOfStream on the media source based
4433             * on the state of the buffer or if appened segment was the final
4434             * segment in the playlist.
4435             *
4436             * @param {Object} playlist a media playlist object
4437             * @param {Object} mediaSource the MediaSource object
4438             * @param {Number} segmentIndex the index of segment we last appended
4439             * @returns {Boolean} do we need to call endOfStream on the MediaSource
4440             */
4441            var detectEndOfStream = function detectEndOfStream(playlist, mediaSource, segmentIndex) {
4442                if (!playlist || !mediaSource) {
4443                    return false;
4444                }
4445
4446                var segments = playlist.segments;
4447
4448                // determine a few boolean values to help make the branch below easier
4449                // to read
4450                var appendedLastSegment = segmentIndex === segments.length;
4451
4452                // if we've buffered to the end of the video, we need to call endOfStream
4453                // so that MediaSources can trigger the `ended` event when it runs out of
4454                // buffered data instead of waiting for me
4455                return playlist.endList && mediaSource.readyState === 'open' && appendedLastSegment;
4456            };
4457
4458            /**
4459             * An object that manages segment loading and appending.
4460             *
4461             * @class SegmentLoader
4462             * @param {Object} options required and optional options
4463             * @extends videojs.EventTarget
4464             */
4465
4466            var SegmentLoader = (function (_videojs$EventTarget) {
4467                _inherits(SegmentLoader, _videojs$EventTarget);
4468
4469                function SegmentLoader(options) {
4470                    var _this = this;
4471
4472                    _classCallCheck(this, SegmentLoader);
4473
4474                    _get(Object.getPrototypeOf(SegmentLoader.prototype), 'constructor', this).call(this);
4475                    // check pre-conditions
4476                    if (!options) {
4477                        throw new TypeError('Initialization options are required');
4478                    }
4479                    if (typeof options.currentTime !== 'function') {
4480                        throw new TypeError('No currentTime getter specified');
4481                    }
4482                    if (!options.mediaSource) {
4483                        throw new TypeError('No MediaSource specified');
4484                    }
4485                    var settings = _videoJs2['default'].mergeOptions(_videoJs2['default'].options.hls, options);
4486
4487                    // public properties
4488                    this.state = 'INIT';
4489                    this.bandwidth = settings.bandwidth;
4490                    this.throughput = { rate: 0, count: 0 };
4491                    this.roundTrip = NaN;
4492                    this.resetStats_();
4493                    this.mediaIndex = null;
4494
4495                    // private settings
4496                    this.hasPlayed_ = settings.hasPlayed;
4497                    this.currentTime_ = settings.currentTime;
4498                    this.seekable_ = settings.seekable;
4499                    this.seeking_ = settings.seeking;
4500                    this.duration_ = settings.duration;
4501                    this.mediaSource_ = settings.mediaSource;
4502                    this.hls_ = settings.hls;
4503                    this.loaderType_ = settings.loaderType;
4504                    this.segmentMetadataTrack_ = settings.segmentMetadataTrack;
4505
4506                    // private instance variables
4507                    this.checkBufferTimeout_ = null;
4508                    this.error_ = void 0;
4509                    this.currentTimeline_ = -1;
4510                    this.pendingSegment_ = null;
4511                    this.mimeType_ = null;
4512                    this.sourceUpdater_ = null;
4513                    this.xhrOptions_ = null;
4514
4515                    // Fragmented mp4 playback
4516                    this.activeInitSegmentId_ = null;
4517                    this.initSegments_ = {};
4518
4519                    this.decrypter_ = settings.decrypter;
4520
4521                    // Manages the tracking and generation of sync-points, mappings
4522                    // between a time in the display time and a segment index within
4523                    // a playlist
4524                    this.syncController_ = settings.syncController;
4525                    this.syncPoint_ = {
4526                        segmentIndex: 0,
4527                        time: 0
4528                    };
4529
4530                    this.syncController_.on('syncinfoupdate', function () {
4531                        return _this.trigger('syncinfoupdate');
4532                    });
4533
4534                    // ...for determining the fetch location
4535                    this.fetchAtBuffer_ = false;
4536
4537                    if (settings.debug) {
4538                        this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'segment-loader', this.loaderType_, '->');
4539                    }
4540                }
4541
4542                /**
4543                 * reset all of our media stats
4544                 *
4545                 * @private
4546                 */
4547
4548                _createClass(SegmentLoader, [{
4549                    key: 'resetStats_',
4550                    value: function resetStats_() {
4551                        this.mediaBytesTransferred = 0;
4552                        this.mediaRequests = 0;
4553                        this.mediaRequestsAborted = 0;
4554                        this.mediaRequestsTimedout = 0;
4555                        this.mediaRequestsErrored = 0;
4556                        this.mediaTransferDuration = 0;
4557                        this.mediaSecondsLoaded = 0;
4558                    }
4559
4560                    /**
4561                     * dispose of the SegmentLoader and reset to the default state
4562                     */
4563                }, {
4564                    key: 'dispose',
4565                    value: function dispose() {
4566                        this.state = 'DISPOSED';
4567                        this.abort_();
4568                        if (this.sourceUpdater_) {
4569                            this.sourceUpdater_.dispose();
4570                        }
4571                        this.resetStats_();
4572                    }
4573
4574                    /**
4575                     * abort anything that is currently doing on with the SegmentLoader
4576                     * and reset to a default state
4577                     */
4578                }, {
4579                    key: 'abort',
4580                    value: function abort() {
4581                        if (this.state !== 'WAITING') {
4582                            if (this.pendingSegment_) {
4583                                this.pendingSegment_ = null;
4584                            }
4585                            return;
4586                        }
4587
4588                        this.abort_();
4589
4590                        // don't wait for buffer check timeouts to begin fetching the
4591                        // next segment
4592                        if (!this.paused()) {
4593                            this.state = 'READY';
4594                            this.monitorBuffer_();
4595                        }
4596                    }
4597
4598                    /**
4599                     * abort all pending xhr requests and null any pending segements
4600                     *
4601                     * @private
4602                     */
4603                }, {
4604                    key: 'abort_',
4605                    value: function abort_() {
4606                        if (this.pendingSegment_) {
4607                            this.pendingSegment_.abortRequests();
4608                        }
4609
4610                        // clear out the segment being processed
4611                        this.pendingSegment_ = null;
4612                    }
4613
4614                    /**
4615                     * set an error on the segment loader and null out any pending segements
4616                     *
4617                     * @param {Error} error the error to set on the SegmentLoader
4618                     * @return {Error} the error that was set or that is currently set
4619                     */
4620                }, {
4621                    key: 'error',
4622                    value: function error(_error) {
4623                        if (typeof _error !== 'undefined') {
4624                            this.error_ = _error;
4625                        }
4626
4627                        this.pendingSegment_ = null;
4628                        return this.error_;
4629                    }
4630
4631                    /**
4632                     * Indicates which time ranges are buffered
4633                     *
4634                     * @return {TimeRange}
4635                     *         TimeRange object representing the current buffered ranges
4636                     */
4637                }, {
4638                    key: 'buffered_',
4639                    value: function buffered_() {
4640                        if (!this.sourceUpdater_) {
4641                            return _videoJs2['default'].createTimeRanges();
4642                        }
4643
4644                        return this.sourceUpdater_.buffered();
4645                    }
4646
4647                    /**
4648                     * Gets and sets init segment for the provided map
4649                     *
4650                     * @param {Object} map
4651                     *        The map object representing the init segment to get or set
4652                     * @param {Boolean=} set
4653                     *        If true, the init segment for the provided map should be saved
4654                     * @return {Object}
4655                     *         map object for desired init segment
4656                     */
4657                }, {
4658                    key: 'initSegment',
4659                    value: function initSegment(map) {
4660                        var set = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
4661
4662                        if (!map) {
4663                            return null;
4664                        }
4665
4666                        var id = (0, _binUtils.initSegmentId)(map);
4667                        var storedMap = this.initSegments_[id];
4668
4669                        if (set && !storedMap && map.bytes) {
4670                            this.initSegments_[id] = storedMap = {
4671                                resolvedUri: map.resolvedUri,
4672                                byterange: map.byterange,
4673                                bytes: map.bytes
4674                            };
4675                        }
4676
4677                        return storedMap || map;
4678                    }
4679
4680                    /**
4681                     * Returns true if all configuration required for loading is present, otherwise false.
4682                     *
4683                     * @return {Boolean} True if the all configuration is ready for loading
4684                     * @private
4685                     */
4686                }, {
4687                    key: 'couldBeginLoading_',
4688                    value: function couldBeginLoading_() {
4689                        return this.playlist_ && (
4690                            // the source updater is created when init_ is called, so either having a
4691                            // source updater or being in the INIT state with a mimeType is enough
4692                            // to say we have all the needed configuration to start loading.
4693                            this.sourceUpdater_ || this.mimeType_ && this.state === 'INIT') && !this.paused();
4694                    }
4695
4696                    /**
4697                     * load a playlist and start to fill the buffer
4698                     */
4699                }, {
4700                    key: 'load',
4701                    value: function load() {
4702                        // un-pause
4703                        this.monitorBuffer_();
4704
4705                        // if we don't have a playlist yet, keep waiting for one to be
4706                        // specified
4707                        if (!this.playlist_) {
4708                            return;
4709                        }
4710
4711                        // not sure if this is the best place for this
4712                        this.syncController_.setDateTimeMapping(this.playlist_);
4713
4714                        // if all the configuration is ready, initialize and begin loading
4715                        if (this.state === 'INIT' && this.couldBeginLoading_()) {
4716                            return this.init_();
4717                        }
4718
4719                        // if we're in the middle of processing a segment already, don't
4720                        // kick off an additional segment request
4721                        if (!this.couldBeginLoading_() || this.state !== 'READY' && this.state !== 'INIT') {
4722                            return;
4723                        }
4724
4725                        this.state = 'READY';
4726                    }
4727
4728                    /**
4729                     * Once all the starting parameters have been specified, begin
4730                     * operation. This method should only be invoked from the INIT
4731                     * state.
4732                     *
4733                     * @private
4734                     */
4735                }, {
4736                    key: 'init_',
4737                    value: function init_() {
4738                        this.state = 'READY';
4739                        this.sourceUpdater_ = new _sourceUpdater2['default'](this.mediaSource_, this.mimeType_);
4740                        this.resetEverything();
4741                        return this.monitorBuffer_();
4742                    }
4743
4744                    /**
4745                     * set a playlist on the segment loader
4746                     *
4747                     * @param {PlaylistLoader} media the playlist to set on the segment loader
4748                     */
4749                }, {
4750                    key: 'playlist',
4751                    value: function playlist(newPlaylist) {
4752                        var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
4753
4754                        if (!newPlaylist) {
4755                            return;
4756                        }
4757
4758                        var oldPlaylist = this.playlist_;
4759                        var segmentInfo = this.pendingSegment_;
4760
4761                        this.playlist_ = newPlaylist;
4762                        this.xhrOptions_ = options;
4763
4764                        // when we haven't started playing yet, the start of a live playlist
4765                        // is always our zero-time so force a sync update each time the playlist
4766                        // is refreshed from the server
4767                        if (!this.hasPlayed_()) {
4768                            newPlaylist.syncInfo = {
4769                                mediaSequence: newPlaylist.mediaSequence,
4770                                time: 0
4771                            };
4772                        }
4773
4774                        // in VOD, this is always a rendition switch (or we updated our syncInfo above)
4775                        // in LIVE, we always want to update with new playlists (including refreshes)
4776                        this.trigger('syncinfoupdate');
4777
4778                        // if we were unpaused but waiting for a playlist, start
4779                        // buffering now
4780                        if (this.state === 'INIT' && this.couldBeginLoading_()) {
4781                            return this.init_();
4782                        }
4783
4784                        if (!oldPlaylist || oldPlaylist.uri !== newPlaylist.uri) {
4785                            if (this.mediaIndex !== null) {
4786                                // we must "resync" the segment loader when we switch renditions and
4787                                // the segment loader is already synced to the previous rendition
4788                                this.resyncLoader();
4789                            }
4790
4791                            // the rest of this function depends on `oldPlaylist` being defined
4792                            return;
4793                        }
4794
4795                        // we reloaded the same playlist so we are in a live scenario
4796                        // and we will likely need to adjust the mediaIndex
4797                        var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
4798
4799                        this.logger_('mediaSequenceDiff', mediaSequenceDiff);
4800
4801                        // update the mediaIndex on the SegmentLoader
4802                        // this is important because we can abort a request and this value must be
4803                        // equal to the last appended mediaIndex
4804                        if (this.mediaIndex !== null) {
4805                            this.mediaIndex -= mediaSequenceDiff;
4806                        }
4807
4808                        // update the mediaIndex on the SegmentInfo object
4809                        // this is important because we will update this.mediaIndex with this value
4810                        // in `handleUpdateEnd_` after the segment has been successfully appended
4811                        if (segmentInfo) {
4812                            segmentInfo.mediaIndex -= mediaSequenceDiff;
4813
4814                            // we need to update the referenced segment so that timing information is
4815                            // saved for the new playlist's segment, however, if the segment fell off the
4816                            // playlist, we can leave the old reference and just lose the timing info
4817                            if (segmentInfo.mediaIndex >= 0) {
4818                                segmentInfo.segment = newPlaylist.segments[segmentInfo.mediaIndex];
4819                            }
4820                        }
4821
4822                        this.syncController_.saveExpiredSegmentInfo(oldPlaylist, newPlaylist);
4823                    }
4824
4825                    /**
4826                     * Prevent the loader from fetching additional segments. If there
4827                     * is a segment request outstanding, it will finish processing
4828                     * before the loader halts. A segment loader can be unpaused by
4829                     * calling load().
4830                     */
4831                }, {
4832                    key: 'pause',
4833                    value: function pause() {
4834                        if (this.checkBufferTimeout_) {
4835                            _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
4836
4837                            this.checkBufferTimeout_ = null;
4838                        }
4839                    }
4840
4841                    /**
4842                     * Returns whether the segment loader is fetching additional
4843                     * segments when given the opportunity. This property can be
4844                     * modified through calls to pause() and load().
4845                     */
4846                }, {
4847                    key: 'paused',
4848                    value: function paused() {
4849                        return this.checkBufferTimeout_ === null;
4850                    }
4851
4852                    /**
4853                     * create/set the following mimetype on the SourceBuffer through a
4854                     * SourceUpdater
4855                     *
4856                     * @param {String} mimeType the mime type string to use
4857                     */
4858                }, {
4859                    key: 'mimeType',
4860                    value: function mimeType(_mimeType) {
4861                        if (this.mimeType_) {
4862                            return;
4863                        }
4864
4865                        this.mimeType_ = _mimeType;
4866                        // if we were unpaused but waiting for a sourceUpdater, start
4867                        // buffering now
4868                        if (this.state === 'INIT' && this.couldBeginLoading_()) {
4869                            this.init_();
4870                        }
4871                    }
4872
4873                    /**
4874                     * Delete all the buffered data and reset the SegmentLoader
4875                     */
4876                }, {
4877                    key: 'resetEverything',
4878                    value: function resetEverything() {
4879                        this.resetLoader();
4880                        this.remove(0, Infinity);
4881                    }
4882
4883                    /**
4884                     * Force the SegmentLoader to resync and start loading around the currentTime instead
4885                     * of starting at the end of the buffer
4886                     *
4887                     * Useful for fast quality changes
4888                     */
4889                }, {
4890                    key: 'resetLoader',
4891                    value: function resetLoader() {
4892                        this.fetchAtBuffer_ = false;
4893                        this.resyncLoader();
4894                    }
4895
4896                    /**
4897                     * Force the SegmentLoader to restart synchronization and make a conservative guess
4898                     * before returning to the simple walk-forward method
4899                     */
4900                }, {
4901                    key: 'resyncLoader',
4902                    value: function resyncLoader() {
4903                        this.mediaIndex = null;
4904                        this.syncPoint_ = null;
4905                    }
4906
4907                    /**
4908                     * Remove any data in the source buffer between start and end times
4909                     * @param {Number} start - the start time of the region to remove from the buffer
4910                     * @param {Number} end - the end time of the region to remove from the buffer
4911                     */
4912                }, {
4913                    key: 'remove',
4914                    value: function remove(start, end) {
4915                        if (this.sourceUpdater_) {
4916                            this.sourceUpdater_.remove(start, end);
4917                        }
4918                        (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.segmentMetadataTrack_);
4919                    }
4920
4921                    /**
4922                     * (re-)schedule monitorBufferTick_ to run as soon as possible
4923                     *
4924                     * @private
4925                     */
4926                }, {
4927                    key: 'monitorBuffer_',
4928                    value: function monitorBuffer_() {
4929                        if (this.checkBufferTimeout_) {
4930                            _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
4931                        }
4932
4933                        this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), 1);
4934                    }
4935
4936                    /**
4937                     * As long as the SegmentLoader is in the READY state, periodically
4938                     * invoke fillBuffer_().
4939                     *
4940                     * @private
4941                     */
4942                }, {
4943                    key: 'monitorBufferTick_',
4944                    value: function monitorBufferTick_() {
4945                        if (this.state === 'READY') {
4946                            this.fillBuffer_();
4947                        }
4948
4949                        if (this.checkBufferTimeout_) {
4950                            _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
4951                        }
4952
4953                        this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), CHECK_BUFFER_DELAY);
4954                    }
4955
4956                    /**
4957                     * fill the buffer with segements unless the sourceBuffers are
4958                     * currently updating
4959                     *
4960                     * Note: this function should only ever be called by monitorBuffer_
4961                     * and never directly
4962                     *
4963                     * @private
4964                     */
4965                }, {
4966                    key: 'fillBuffer_',
4967                    value: function fillBuffer_() {
4968                        if (this.sourceUpdater_.updating()) {
4969                            return;
4970                        }
4971
4972                        if (!this.syncPoint_) {
4973                            this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
4974                        }
4975
4976                        // see if we need to begin loading immediately
4977                        var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
4978
4979                        if (!segmentInfo) {
4980                            return;
4981                        }
4982
4983                        var isEndOfStream = detectEndOfStream(this.playlist_, this.mediaSource_, segmentInfo.mediaIndex);
4984
4985                        if (isEndOfStream) {
4986                            this.mediaSource_.endOfStream();
4987                            return;
4988                        }
4989
4990                        if (segmentInfo.mediaIndex === this.playlist_.segments.length - 1 && this.mediaSource_.readyState === 'ended' && !this.seeking_()) {
4991                            return;
4992                        }
4993
4994                        // We will need to change timestampOffset of the sourceBuffer if either of
4995                        // the following conditions are true:
4996                        // - The segment.timeline !== this.currentTimeline
4997                        //   (we are crossing a discontinuity somehow)
4998                        // - The "timestampOffset" for the start of this segment is less than
4999                        //   the currently set timestampOffset
5000                        if (segmentInfo.timeline !== this.currentTimeline_ || segmentInfo.startOfSegment !== null && segmentInfo.startOfSegment < this.sourceUpdater_.timestampOffset()) {
5001                            this.syncController_.reset();
5002                            segmentInfo.timestampOffset = segmentInfo.startOfSegment;
5003                        }
5004
5005                        this.loadSegment_(segmentInfo);
5006                    }
5007
5008                    /**
5009                     * Determines what segment request should be made, given current playback
5010                     * state.
5011                     *
5012                     * @param {TimeRanges} buffered - the state of the buffer
5013                     * @param {Object} playlist - the playlist object to fetch segments from
5014                     * @param {Number} mediaIndex - the previous mediaIndex fetched or null
5015                     * @param {Boolean} hasPlayed - a flag indicating whether we have played or not
5016                     * @param {Number} currentTime - the playback position in seconds
5017                     * @param {Object} syncPoint - a segment info object that describes the
5018                     * @returns {Object} a segment request object that describes the segment to load
5019                     */
5020                }, {
5021                    key: 'checkBuffer_',
5022                    value: function checkBuffer_(buffered, playlist, mediaIndex, hasPlayed, currentTime, syncPoint) {
5023                        var lastBufferedEnd = 0;
5024                        var startOfSegment = undefined;
5025
5026                        if (buffered.length) {
5027                            lastBufferedEnd = buffered.end(buffered.length - 1);
5028                        }
5029
5030                        var bufferedTime = Math.max(0, lastBufferedEnd - currentTime);
5031
5032                        if (!playlist.segments.length) {
5033                            return null;
5034                        }
5035
5036                        // if there is plenty of content buffered, and the video has
5037                        // been played before relax for awhile
5038                        if (bufferedTime >= _config2['default'].GOAL_BUFFER_LENGTH) {
5039                            return null;
5040                        }
5041
5042                        // if the video has not yet played once, and we already have
5043                        // one segment downloaded do nothing
5044                        if (!hasPlayed && bufferedTime >= 1) {
5045                            return null;
5046                        }
5047
5048                        this.logger_('checkBuffer_', 'mediaIndex:', mediaIndex, 'hasPlayed:', hasPlayed, 'currentTime:', currentTime, 'syncPoint:', syncPoint, 'fetchAtBuffer:', this.fetchAtBuffer_, 'bufferedTime:', bufferedTime);
5049
5050                        // When the syncPoint is null, there is no way of determining a good
5051                        // conservative segment index to fetch from
5052                        // The best thing to do here is to get the kind of sync-point data by
5053                        // making a request
5054                        if (syncPoint === null) {
5055                            mediaIndex = this.getSyncSegmentCandidate_(playlist);
5056                            this.logger_('getSync', 'mediaIndex:', mediaIndex);
5057                            return this.generateSegmentInfo_(playlist, mediaIndex, null, true);
5058                        }
5059
5060                        // Under normal playback conditions fetching is a simple walk forward
5061                        if (mediaIndex !== null) {
5062                            this.logger_('walkForward', 'mediaIndex:', mediaIndex + 1);
5063                            var segment = playlist.segments[mediaIndex];
5064
5065                            if (segment && segment.end) {
5066                                startOfSegment = segment.end;
5067                            } else {
5068                                startOfSegment = lastBufferedEnd;
5069                            }
5070                            return this.generateSegmentInfo_(playlist, mediaIndex + 1, startOfSegment, false);
5071                        }
5072
5073                        // There is a sync-point but the lack of a mediaIndex indicates that
5074                        // we need to make a good conservative guess about which segment to
5075                        // fetch
5076                        if (this.fetchAtBuffer_) {
5077                            // Find the segment containing the end of the buffer
5078                            var mediaSourceInfo = (0, _playlist.getMediaInfoForTime_)(playlist, lastBufferedEnd, syncPoint.segmentIndex, syncPoint.time);
5079
5080                            mediaIndex = mediaSourceInfo.mediaIndex;
5081                            startOfSegment = mediaSourceInfo.startTime;
5082                        } else {
5083                            // Find the segment containing currentTime
5084                            var mediaSourceInfo = (0, _playlist.getMediaInfoForTime_)(playlist, currentTime, syncPoint.segmentIndex, syncPoint.time);
5085
5086                            mediaIndex = mediaSourceInfo.mediaIndex;
5087                            startOfSegment = mediaSourceInfo.startTime;
5088                        }
5089                        this.logger_('getMediaIndexForTime', 'mediaIndex:', mediaIndex, 'startOfSegment:', startOfSegment);
5090
5091                        return this.generateSegmentInfo_(playlist, mediaIndex, startOfSegment, false);
5092                    }
5093
5094                    /**
5095                     * The segment loader has no recourse except to fetch a segment in the
5096                     * current playlist and use the internal timestamps in that segment to
5097                     * generate a syncPoint. This function returns a good candidate index
5098                     * for that process.
5099                     *
5100                     * @param {Object} playlist - the playlist object to look for a
5101                     * @returns {Number} An index of a segment from the playlist to load
5102                     */
5103                }, {
5104                    key: 'getSyncSegmentCandidate_',
5105                    value: function getSyncSegmentCandidate_(playlist) {
5106                        var _this2 = this;
5107
5108                        if (this.currentTimeline_ === -1) {
5109                            return 0;
5110                        }
5111
5112                        var segmentIndexArray = playlist.segments.map(function (s, i) {
5113                            return {
5114                                timeline: s.timeline,
5115                                segmentIndex: i
5116                            };
5117                        }).filter(function (s) {
5118                            return s.timeline === _this2.currentTimeline_;
5119                        });
5120
5121                        if (segmentIndexArray.length) {
5122                            return segmentIndexArray[Math.min(segmentIndexArray.length - 1, 1)].segmentIndex;
5123                        }
5124
5125                        return Math.max(playlist.segments.length - 1, 0);
5126                    }
5127                }, {
5128                    key: 'generateSegmentInfo_',
5129                    value: function generateSegmentInfo_(playlist, mediaIndex, startOfSegment, isSyncRequest) {
5130                        if (mediaIndex < 0 || mediaIndex >= playlist.segments.length) {
5131                            return null;
5132                        }
5133
5134                        var segment = playlist.segments[mediaIndex];
5135
5136                        return {
5137                            requestId: 'segment-loader-' + Math.random(),
5138                            // resolve the segment URL relative to the playlist
5139                            uri: segment.resolvedUri,
5140                            // the segment's mediaIndex at the time it was requested
5141                            mediaIndex: mediaIndex,
5142                            // whether or not to update the SegmentLoader's state with this
5143                            // segment's mediaIndex
5144                            isSyncRequest: isSyncRequest,
5145                            startOfSegment: startOfSegment,
5146                            // the segment's playlist
5147                            playlist: playlist,
5148                            // unencrypted bytes of the segment
5149                            bytes: null,
5150                            // when a key is defined for this segment, the encrypted bytes
5151                            encryptedBytes: null,
5152                            // The target timestampOffset for this segment when we append it
5153                            // to the source buffer
5154                            timestampOffset: null,
5155                            // The timeline that the segment is in
5156                            timeline: segment.timeline,
5157                            // The expected duration of the segment in seconds
5158                            duration: segment.duration,
5159                            // retain the segment in case the playlist updates while doing an async process
5160                            segment: segment
5161                        };
5162                    }
5163
5164                    /**
5165                     * load a specific segment from a request into the buffer
5166                     *
5167                     * @private
5168                     */
5169                }, {
5170                    key: 'loadSegment_',
5171                    value: function loadSegment_(segmentInfo) {
5172                        var _this3 = this;
5173
5174                        this.state = 'WAITING';
5175                        this.pendingSegment_ = segmentInfo;
5176                        this.trimBackBuffer_(segmentInfo);
5177
5178                        segmentInfo.abortRequests = (0, _mediaSegmentRequest.mediaSegmentRequest)(this.hls_.xhr, this.xhrOptions_, this.decrypter_, this.createSimplifiedSegmentObj_(segmentInfo),
5179                            // progress callback
5180                            function (event, segment) {
5181                                if (!_this3.pendingSegment_ || segment.requestId !== _this3.pendingSegment_.requestId) {
5182                                    return;
5183                                }
5184                                // TODO: Use progress-based bandwidth to early abort low-bandwidth situations
5185                                _this3.trigger('progress');
5186                            }, this.segmentRequestFinished_.bind(this));
5187                    }
5188
5189                    /**
5190                     * trim the back buffer so that we don't have too much data
5191                     * in the source buffer
5192                     *
5193                     * @private
5194                     *
5195                     * @param {Object} segmentInfo - the current segment
5196                     */
5197                }, {
5198                    key: 'trimBackBuffer_',
5199                    value: function trimBackBuffer_(segmentInfo) {
5200                        var seekable = this.seekable_();
5201                        var currentTime = this.currentTime_();
5202                        var removeToTime = 0;
5203
5204                        // Chrome has a hard limit of 150mb of
5205                        // buffer and a very conservative "garbage collector"
5206                        // We manually clear out the old buffer to ensure
5207                        // we don't trigger the QuotaExceeded error
5208                        // on the source buffer during subsequent appends
5209
5210                        // If we have a seekable range use that as the limit for what can be removed safely
5211                        // otherwise remove anything older than 1 minute before the current play head
5212                        if (seekable.length && seekable.start(0) > 0 && seekable.start(0) < currentTime) {
5213                            removeToTime = seekable.start(0);
5214                        } else {
5215                            removeToTime = currentTime - 60;
5216                        }
5217
5218                        if (removeToTime > 0) {
5219                            this.remove(0, removeToTime);
5220                        }
5221                    }
5222
5223                    /**
5224                     * created a simplified copy of the segment object with just the
5225                     * information necessary to perform the XHR and decryption
5226                     *
5227                     * @private
5228                     *
5229                     * @param {Object} segmentInfo - the current segment
5230                     * @returns {Object}
5230 a simplified segment object copy
5231                     */
5232                }, {
5233                    key: 'createSimplifiedSegmentObj_',
5234                    value: function createSimplifiedSegmentObj_(segmentInfo) {
5235                        var segment = segmentInfo.segment;
5236                        var simpleSegment = {
5237                            resolvedUri: segment.resolvedUri,
5238                            byterange: segment.byterange,
5239                            requestId: segmentInfo.requestId
5240                        };
5241
5242                        if (segment.key) {
5243                            // if the media sequence is greater than 2^32, the IV will be incorrect
5244                            // assuming 10s segments, that would be about 1300 years
5245                            var iv = segment.key.iv || new Uint32Array([0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence]);
5246
5247                            simpleSegment.key = {
5248                                resolvedUri: segment.key.resolvedUri,
5249                                iv: iv
5250                            };
5251                        }
5252
5253                        if (segment.map) {
5254                            simpleSegment.map = this.initSegment(segment.map);
5255                        }
5256
5257                        return simpleSegment;
5258                    }
5259
5260                    /**
5261                     * Handle the callback from the segmentRequest function and set the
5262                     * associated SegmentLoader state and errors if necessary
5263                     *
5264                     * @private
5265                     */
5266                }, {
5267                    key: 'segmentRequestFinished_',
5268                    value: function segmentRequestFinished_(error, simpleSegment) {
5269                        // every request counts as a media request even if it has been aborted
5270                        // or canceled due to a timeout
5271                        this.mediaRequests += 1;
5272
5273                        if (simpleSegment.stats) {
5274                            this.mediaBytesTransferred += simpleSegment.stats.bytesReceived;
5275                            this.mediaTransferDuration += simpleSegment.stats.roundTripTime;
5276                        }
5277
5278                        // The request was aborted and the SegmentLoader has already been reset
5279                        if (!this.pendingSegment_) {
5280                            this.mediaRequestsAborted += 1;
5281                            return;
5282                        }
5283
5284                        // the request was aborted and the SegmentLoader has already started
5285                        // another request. this can happen when the timeout for an aborted
5286                        // request triggers due to a limitation in the XHR library
5287                        // do not count this as any sort of request or we risk double-counting
5288                        if (simpleSegment.requestId !== this.pendingSegment_.requestId) {
5289                            return;
5290                        }
5291
5292                        // an error occurred from the active pendingSegment_ so reset everything
5293                        if (error) {
5294                            this.pendingSegment_ = null;
5295
5296                            // the requests were aborted just record the aborted stat and exit
5297                            // this is not a true error condition and nothing corrective needs
5298                            // to be done
5299                            if (error.code === _mediaSegmentRequest.REQUEST_ERRORS.ABORTED) {
5300                                this.mediaRequestsAborted += 1;
5301                                return;
5302                            }
5303
5304                            this.state = 'READY';
5305                            this.pause();
5306
5307                            // the error is really just that at least one of the requests timed-out
5308                            // set the bandwidth to a very low value and trigger an ABR switch to
5309                            // take emergency action
5310                            if (error.code === _mediaSegmentRequest.REQUEST_ERRORS.TIMEOUT) {
5311                                this.mediaRequestsTimedout += 1;
5312                                this.bandwidth = 1;
5313                                this.roundTrip = NaN;
5314                                this.trigger('bandwidthupdate');
5315                                return;
5316                            }
5317
5318                            // if control-flow has arrived here, then the error is real
5319                            // emit an error event to blacklist the current playlist
5320                            this.mediaRequestsErrored += 1;
5321                            this.error(error);
5322                            this.trigger('error');
5323                            return;
5324                        }
5325
5326                        // the response was a success so set any bandwidth stats the request
5327                        // generated for ABR purposes
5328                        this.bandwidth = simpleSegment.stats.bandwidth;
5329                        this.roundTrip = simpleSegment.stats.roundTripTime;
5330
5331                        // if this request included an initialization segment, save that data
5332                        // to the initSegment cache
5333                        if (simpleSegment.map) {
5334                            simpleSegment.map = this.initSegment(simpleSegment.map, true);
5335                        }
5336
5337                        this.processSegmentResponse_(simpleSegment);
5338                    }
5339
5340                    /**
5341                     * Move any important data from the simplified segment object
5342                     * back to the real segment object for future phases
5343                     *
5344                     * @private
5345                     */
5346                }, {
5347                    key: 'processSegmentResponse_',
5348                    value: function processSegmentResponse_(simpleSegment) {
5349                        var segmentInfo = this.pendingSegment_;
5350
5351                        segmentInfo.bytes = simpleSegment.bytes;
5352                        if (simpleSegment.map) {
5353                            segmentInfo.segment.map.bytes = simpleSegment.map.bytes;
5354                        }
5355
5356                        segmentInfo.endOfAllRequests = simpleSegment.endOfAllRequests;
5357                        this.handleSegment_();
5358                    }
5359
5360                    /**
5361                     * append a decrypted segement to the SourceBuffer through a SourceUpdater
5362                     *
5363                     * @private
5364                     */
5365                }, {
5366                    key: 'handleSegment_',
5367                    value: function handleSegment_() {
5368                        var _this4 = this;
5369
5370                        if (!this.pendingSegment_) {
5371                            this.state = 'READY';
5372                            return;
5373                        }
5374
5375                        this.state = 'APPENDING';
5376
5377                        var segmentInfo = this.pendingSegment_;
5378                        var segment = segmentInfo.segment;
5379
5380                        this.syncController_.probeSegmentInfo(segmentInfo);
5381
5382                        if (segmentInfo.isSyncRequest) {
5383                            this.trigger('syncinfoupdate');
5384                            this.pendingSegment_ = null;
5385                            this.state = 'READY';
5386                            return;
5387                        }
5388
5389                        if (segmentInfo.timestampOffset !== null && segmentInfo.timestampOffset !== this.sourceUpdater_.timestampOffset()) {
5390                            this.sourceUpdater_.timestampOffset(segmentInfo.timestampOffset);
5391                        }
5392
5393                        // if the media initialization segment is changing, append it
5394                        // before the content segment
5395                        if (segment.map) {
5396                            (function () {
5397                                var initId = (0, _binUtils.initSegmentId)(segment.map);
5398
5399                                if (!_this4.activeInitSegmentId_ || _this4.activeInitSegmentId_ !== initId) {
5400                                    var initSegment = _this4.initSegment(segment.map);
5401
5402                                    _this4.sourceUpdater_.appendBuffer(initSegment.bytes, function () {
5403                                        _this4.activeInitSegmentId_ = initId;
5404                                    });
5405                                }
5406                            })();
5407                        }
5408
5409                        segmentInfo.byteLength = segmentInfo.bytes.byteLength;
5410                        if (typeof segment.start === 'number' && typeof segment.end === 'number') {
5411                            this.mediaSecondsLoaded += segment.end - segment.start;
5412                        } else {
5413                            this.mediaSecondsLoaded += segment.duration;
5414                        }
5415
5416                        this.sourceUpdater_.appendBuffer(segmentInfo.bytes, this.handleUpdateEnd_.bind(this));
5417                    }
5418
5419                    /**
5420                     * callback to run when appendBuffer is finished. detects if we are
5421                     * in a good state to do things with the data we got, or if we need
5422                     * to wait for more
5423                     *
5424                     * @private
5425                     */
5426                }, {
5427                    key: 'handleUpdateEnd_',
5428                    value: function handleUpdateEnd_() {
5429                        this.logger_('handleUpdateEnd_', 'segmentInfo:', this.pendingSegment_);
5430
5431                        if (!this.pendingSegment_) {
5432                            this.state = 'READY';
5433                            if (!this.paused()) {
5434                                this.monitorBuffer_();
5435                            }
5436                            return;
5437                        }
5438
5439                        var segmentInfo = this.pendingSegment_;
5440                        var segment = segmentInfo.segment;
5441                        var isWalkingForward = this.mediaIndex !== null;
5442
5443                        this.pendingSegment_ = null;
5444                        this.recordThroughput_(segmentInfo);
5445                        this.addSegmentMetadataCue_(segmentInfo);
5446
5447                        this.state = 'READY';
5448
5449                        this.mediaIndex = segmentInfo.mediaIndex;
5450                        this.fetchAtBuffer_ = true;
5451                        this.currentTimeline_ = segmentInfo.timeline;
5452
5453                        // We must update the syncinfo to recalculate the seekable range before
5454                        // the following conditional otherwise it may consider this a bad "guess"
5455                        // and attempt to resync when the post-update seekable window and live
5456                        // point would mean that this was the perfect segment to fetch
5457                        this.trigger('syncinfoupdate');
5458
5459                        // If we previously appended a segment that ends more than 3 targetDurations before
5460                        // the currentTime_ that means that our conservative guess was too conservative.
5461                        // In that case, reset the loader state so that we try to use any information gained
5462                        // from the previous request to create a new, more accurate, sync-point.
5463                        if (segment.end && this.currentTime_() - segment.end > segmentInfo.playlist.targetDuration * 3) {
5464                            this.resetEverything();
5465                            return;
5466                        }
5467
5468                        // Don't do a rendition switch unless we have enough time to get a sync segment
5469                        // and conservatively guess
5470                        if (isWalkingForward) {
5471                            this.trigger('bandwidthupdate');
5472                        }
5473                        this.trigger('progress');
5474
5475                        // any time an update finishes and the last segment is in the
5476                        // buffer, end the stream. this ensures the "ended" event will
5477                        // fire if playback reaches that point.
5478                        var isEndOfStream = detectEndOfStream(segmentInfo.playlist, this.mediaSource_, segmentInfo.mediaIndex + 1);
5479
5480                        if (isEndOfStream) {
5481                            this.mediaSource_.endOfStream();
5482                        }
5483
5484                        if (!this.paused()) {
5485                            this.monitorBuffer_();
5486                        }
5487                    }
5488
5489                    /**
5490                     * Records the current throughput of the decrypt, transmux, and append
5491                     * portion of the semgment pipeline. `throughput.rate` is a the cumulative
5492                     * moving average of the throughput. `throughput.count` is the number of
5493                     * data points in the average.
5494                     *
5495                     * @private
5496                     * @param {Object} segmentInfo the object returned by loadSegment
5497                     */
5498                }, {
5499                    key: 'recordThroughput_',
5500                    value: function recordThroughput_(segmentInfo) {
5501                        var rate = this.throughput.rate;
5502                        // Add one to the time to ensure that we don't accidentally attempt to divide
5503                        // by zero in the case where the throughput is ridiculously high
5504                        var segmentProcessingTime = Date.now() - segmentInfo.endOfAllRequests + 1;
5505                        // Multiply by 8000 to convert from bytes/millisecond to bits/second
5506                        var segmentProcessingThroughput = Math.floor(segmentInfo.byteLength / segmentProcessingTime * 8 * 1000);
5507
5508                        // This is just a cumulative moving average calculation:
5509                        //   newAvg = oldAvg + (sample - oldAvg) / (sampleCount + 1)
5510                        this.throughput.rate += (segmentProcessingThroughput - rate) / ++this.throughput.count;
5511                    }
5512
5513                    /**
5514                     * A debugging logger noop that is set to console.log only if debugging
5515                     * is enabled globally
5516                     *
5517                     * @private
5518                     */
5519                }, {
5520                    key: 'logger_',
5521                    value: function logger_() {}
5522
5523                    /**
5524                     * Adds a cue to the segment-metadata track with some metadata information about the
5525                     * segment
5526                     *
5527                     * @private
5528                     * @param {Object} segmentInfo
5529                     *        the object returned by loadSegment
5530                     * @method addSegmentMetadataCue_
5531                     */
5532                }, {
5533                    key: 'addSegmentMetadataCue_',
5534                    value: function addSegmentMetadataCue_(segmentInfo) {
5535                        if (!this.segmentMetadataTrack_) {
5536                            return;
5537                        }
5538
5539                        var segment = segmentInfo.segment;
5540                        var start = segment.start;
5541                        var end = segment.end;
5542
5543                        (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.segmentMetadataTrack_);
5544
5545                        var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue;
5546                        var value = {
5547                            uri: segmentInfo.uri,
5548                            timeline: segmentInfo.timeline,
5549                            playlist: segmentInfo.playlist.uri,
5550                            start: start,
5551                            end: end
5552                        };
5553                        var data = JSON.stringify(value);
5554                        var cue = new Cue(start, end, data);
5555
5556                        // Attach the metadata to the value property of the cue to keep consistency between
5557                        // the differences of WebKitDataCue in safari and VTTCue in other browsers
5558                        cue.value = value;
5559
5560                        this.segmentMetadataTrack_.addCue(cue);
5561                    }
5562                }]);
5563
5564                return SegmentLoader;
5565            })(_videoJs2['default'].EventTarget);
5566
5567            exports['default'] = SegmentLoader;
5568            module.exports = exports['default'];
5569        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
5570    },{"./bin-utils":2,"./config":3,"./media-segment-request":6,"./playlist":9,"./source-updater":15,"global/window":30,"videojs-contrib-media-sources/es5/remove-cues-from-track.js":71}],15:[function(require,module,exports){
5571        (function (global){
5572            /**
5573             * @file source-updater.js
5574             */
5575            'use strict';
5576
5577            Object.defineProperty(exports, '__esModule', {
5578                value: true
5579            });
5580
5581            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
5582
5583            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
5584
5585            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
5586
5587            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
5588
5589            var _videoJs2 = _interopRequireDefault(_videoJs);
5590
5591            var noop = function noop() {};
5592
5593            /**
5594             * A queue of callbacks to be serialized and applied when a
5595             * MediaSource and its associated SourceBuffers are not in the
5596             * updating state. It is used by the segment loader to update the
5597             * underlying SourceBuffers when new data is loaded, for instance.
5598             *
5599             * @class SourceUpdater
5600             * @param {MediaSource} mediaSource the MediaSource to create the
5601             * SourceBuffer from
5602             * @param {String} mimeType the desired MIME type of the underlying
5603             * SourceBuffer
5604             */
5605
5606            var SourceUpdater = (function () {
5607                function SourceUpdater(mediaSource, mimeType) {
5608                    var _this = this;
5609
5610                    _classCallCheck(this, SourceUpdater);
5611
5612                    var createSourceBuffer = function createSourceBuffer() {
5613                        _this.sourceBuffer_ = mediaSource.addSourceBuffer(mimeType);
5614
5615                        // run completion handlers and process callbacks as updateend
5616                        // events fire
5617                        _this.onUpdateendCallback_ = function () {
5618                            var pendingCallback = _this.pendingCallback_;
5619
5620                            _this.pendingCallback_ = null;
5621
5622                            if (pendingCallback) {
5623                                pendingCallback();
5624                            }
5625
5626                            _this.runCallback_();
5627                        };
5628
5629                        _this.sourceBuffer_.addEventListener('updateend', _this.onUpdateendCallback_);
5630
5631                        _this.runCallback_();
5632                    };
5633
5634                    this.callbacks_ = [];
5635                    this.pendingCallback_ = null;
5636                    this.timestampOffset_ = 0;
5637                    this.mediaSource = mediaSource;
5638                    this.processedAppend_ = false;
5639
5640                    if (mediaSource.readyState === 'closed') {
5641                        mediaSource.addEventListener('sourceopen', createSourceBuffer);
5642                    } else {
5643                        createSourceBuffer();
5644                    }
5645                }
5646
5647                /**
5648                 * Aborts the current segment and resets the segment parser.
5649                 *
5650                 * @param {Function} done function to call when done
5651                 * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
5652                 */
5653
5654                _createClass(SourceUpdater, [{
5655                    key: 'abort',
5656                    value: function abort(done) {
5657                        var _this2 = this;
5658
5659                        if (this.processedAppend_) {
5660                            this.queueCallback_(function () {
5661                                _this2.sourceBuffer_.abort();
5662                            }, done);
5663                        }
5664                    }
5665
5666                    /**
5667                     * Queue an update to append an ArrayBuffer.
5668                     *
5669                     * @param {ArrayBuffer} bytes
5670                     * @param {Function} done the function to call when done
5671                     * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
5672                     */
5673                }, {
5674                    key: 'appendBuffer',
5675                    value: function appendBuffer(bytes, done) {
5676                        var _this3 = this;
5677
5678                        this.processedAppend_ = true;
5679
5680                        this.queueCallback_(function () {
5681                            _this3.sourceBuffer_.appendBuffer(bytes);
5682                        }, done);
5683                    }
5684
5685                    /**
5686                     * Indicates what TimeRanges are buffered in the managed SourceBuffer.
5687                     *
5688                     * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
5689                     */
5690                }, {
5691                    key: 'buffered',
5692                    value: function buffered() {
5693                        if (!this.sourceBuffer_) {
5694                            return _videoJs2['default'].createTimeRanges();
5695                        }
5696                        return this.sourceBuffer_.buffered;
5697                    }
5698
5699                    /**
5700                     * Queue an update to remove a time range from the buffer.
5701                     *
5702                     * @param {Number} start where to start the removal
5703                     * @param {Number} end where to end the removal
5704                     * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
5705                     */
5706                }, {
5707                    key: 'remove',
5708                    value: function remove(start, end) {
5709                        var _this4 = this;
5710
5711                        if (this.processedAppend_) {
5712                            this.queueCallback_(function () {
5713                                _this4.sourceBuffer_.remove(start, end);
5714                            }, noop);
5715                        }
5716                    }
5717
5718                    /**
5719                     * Whether the underlying sourceBuffer is updating or not
5720                     *
5721                     * @return {Boolean} the updating status of the SourceBuffer
5722                     */
5723                }, {
5724                    key: 'updating',
5725                    value: function updating() {
5726                        return !this.sourceBuffer_ || this.sourceBuffer_.updating || this.pendingCallback_;
5727                    }
5728
5729                    /**
5730                     * Set/get the timestampoffset on the SourceBuffer
5731                     *
5732                     * @return {Number} the timestamp offset
5733                     */
5734                }, {
5735                    key: 'timestampOffset',
5736                    value: function timestampOffset(offset) {
5737                        var _this5 = this;
5738
5739                        if (typeof offset !== 'undefined') {
5740                            this.queueCallback_(function () {
5741                                _this5.sourceBuffer_.timestampOffset = offset;
5742                            });
5743                            this.timestampOffset_ = offset;
5744                        }
5745                        return this.timestampOffset_;
5746                    }
5747
5748                    /**
5749                     * Queue a callback to run
5750                     */
5751                }, {
5752                    key: 'queueCallback_',
5753                    value: function queueCallback_(callback, done) {
5754                        this.callbacks_.push([callback.bind(this), done]);
5755                        this.runCallback_();
5756                    }
5757
5758                    /**
5759                     * Run a queued callback
5760                     */
5761                }, {
5762                    key: 'runCallback_',
5763                    value: function runCallback_() {
5764                        var callbacks = undefined;
5765
5766                        if (!this.updating() && this.callbacks_.length) {
5767                            callbacks = this.callbacks_.shift();
5768                            this.pendingCallback_ = callbacks[1];
5769                            callbacks[0]();
5770                        }
5771                    }
5772
5773                    /**
5774                     * dispose of the source updater and the underlying sourceBuffer
5775                     */
5776                }, {
5777                    key: 'dispose',
5778                    value: function dispose() {
5779                        this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
5780                        if (this.sourceBuffer_ && this.mediaSource.readyState === 'open') {
5781                            this.sourceBuffer_.abort();
5782                        }
5783                    }
5784                }]);
5785
5786                return SourceUpdater;
5787            })();
5788
5789            exports['default'] = SourceUpdater;
5790            module.exports = exports['default'];
5791        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
5792    },{}],16:[function(require,module,exports){
5793        /**
5794         * @file stream.js
5795         */
5796        /**
5797         * A lightweight readable stream implemention that handles event dispatching.
5798         *
5799         * @class Stream
5800         */
5801        'use strict';
5802
5803        Object.defineProperty(exports, '__esModule', {
5804            value: true
5805        });
5806
5807        var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
5808
5809        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
5810
5811        var Stream = (function () {
5812            function Stream() {
5813                _classCallCheck(this, Stream);
5814
5815                this.listeners = {};
5816            }
5817
5818            /**
5819             * Add a listener for a specified event type.
5820             *
5821             * @param {String} type the event name
5822             * @param {Function} listener the callback to be invoked when an event of
5823             * the specified type occurs
5824             */
5825
5826            _createClass(Stream, [{
5827                key: 'on',
5828                value: function on(type, listener) {
5829                    if (!this.listeners[type]) {
5830                        this.listeners[type] = [];
5831                    }
5832                    this.listeners[type].push(listener);
5833                }
5834
5835                /**
5836                 * Remove a listener for a specified event type.
5837                 *
5838                 * @param {String} type the event name
5839                 * @param {Function} listener  a function previously registered for this
5840                 * type of event through `on`
5841                 * @return {Boolean} if we could turn it off or not
5842                 */
5843            }, {
5844                key: 'off',
5845                value: function off(type, listener) {
5846                    var index = undefined;
5847
5848                    if (!this.listeners[type]) {
5849                        return false;
5850                    }
5851                    index = this.listeners[type].indexOf(listener);
5852                    this.listeners[type].splice(index, 1);
5853                    return index > -1;
5854                }
5855
5856                /**
5857                 * Trigger an event of the specified type on this stream. Any additional
5858                 * arguments to this function are passed as parameters to event listeners.
5859                 *
5860                 * @param {String} type the event name
5861                 */
5862            }, {
5863                key: 'trigger',
5864                value: function trigger(type) {
5865                    var callbacks = undefined;
5866                    var i = undefined;
5867                    var length = undefined;
5868                    var args = undefined;
5869
5870                    callbacks = this.listeners[type];
5871                    if (!callbacks) {
5872                        return;
5873                    }
5874                    // Slicing the arguments on every invocation of this method
5875                    // can add a significant amount of overhead. Avoid the
5876                    // intermediate object creation for the common case of a
5877                    // single callback argument
5878                    if (arguments.length === 2) {
5879                        length = callbacks.length;
5880                        for (i = 0; i < length; ++i) {
5881                            callbacks[i].call(this, arguments[1]);
5882                        }
5883                    } else {
5884                        args = Array.prototype.slice.call(arguments, 1);
5885                        length = callbacks.length;
5886                        for (i = 0; i < length; ++i) {
5887                            callbacks[i].apply(this, args);
5888                        }
5889                    }
5890                }
5891
5892                /**
5893                 * Destroys the stream and cleans up.
5894                 */
5895            }, {
5896                key: 'dispose',
5897                value: function dispose() {
5898                    this.listeners = {};
5899                }
5900
5901                /**
5902                 * Forwards all `data` events on this stream to the destination stream. The
5903                 * destination stream should provide a method `push` to receive the data
5904                 * events as they arrive.
5905                 *
5906                 * @param {Stream} destination the stream that will receive all `data` events
5907                 * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
5908                 */
5909            }, {
5910                key: 'pipe',
5911                value: function pipe(destination) {
5912                    this.on('data', function (data) {
5913                        destination.push(data);
5914                    });
5915                }
5916            }]);
5917
5918            return Stream;
5919        })();
5920
5921        exports['default'] = Stream;
5922        module.exports = exports['default'];
5923    },{}],17:[function(require,module,exports){
5924        (function (global){
5925            /**
5926             * @file sync-controller.js
5927             */
5928
5929            'use strict';
5930
5931            Object.defineProperty(exports, '__esModule', {
5932                value: true
5933            });
5934
5935            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
5936
5937            var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
5938
5939            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
5940
5941            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
5942
5943            function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
5943onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
5944
5945            var _muxJsLibMp4Probe = require('mux.js/lib/mp4/probe');
5946
5947            var _muxJsLibMp4Probe2 = _interopRequireDefault(_muxJsLibMp4Probe);
5948
5949            var _muxJsLibToolsTsInspectorJs = require('mux.js/lib/tools/ts-inspector.js');
5950
5951            var _playlist = require('./playlist');
5952
5953            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
5954
5955            var _videoJs2 = _interopRequireDefault(_videoJs);
5956
5957            var syncPointStrategies = [
5958// Stategy "VOD": Handle the VOD-case where the sync-point is *always*
5959//                the equivalence display-time 0 === segment-index 0
5960                {
5961                    name: 'VOD',
5962                    run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
5963                        if (duration !== Infinity) {
5964                            var syncPoint = {
5965                                time: 0,
5966                                segmentIndex: 0
5967                            };
5968
5969                            return syncPoint;
5970                        }
5971                        return null;
5972                    }
5973                },
5974// Stategy "ProgramDateTime": We have a program-date-time tag in this playlist
5975                {
5976                    name: 'ProgramDateTime',
5977                    run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
5978                        if (syncController.datetimeToDisplayTime && playlist.dateTimeObject) {
5979                            var playlistTime = playlist.dateTimeObject.getTime() / 1000;
5980                            var playlistStart = playlistTime + syncController.datetimeToDisplayTime;
5981                            var syncPoint = {
5982                                time: playlistStart,
5983                                segmentIndex: 0
5984                            };
5985
5986                            return syncPoint;
5987                        }
5988                        return null;
5989                    }
5990                },
5991// Stategy "Segment": We have a known time mapping for a timeline and a
5992//                    segment in the current timeline with timing data
5993                {
5994                    name: 'Segment',
5995                    run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
5996                        var segments = playlist.segments;
5997                        var syncPoint = null;
5998                        var lastDistance = null;
5999
6000                        currentTime = currentTime || 0;
6001
6002                        for (var i = 0; i < segments.length; i++) {
6003                            var segment = segments[i];
6004
6005                            if (segment.timeline === currentTimeline && typeof segment.start !== 'undefined') {
6006                                var distance = Math.abs(currentTime - segment.start);
6007
6008                                // Once the distance begins to increase, we have passed
6009                                // currentTime and can stop looking for better candidates
6010                                if (lastDistance !== null && lastDistance < distance) {
6011                                    break;
6012                                }
6013
6014                                if (!syncPoint || lastDistance === null || lastDistance >= distance) {
6015                                    lastDistance = distance;
6016                                    syncPoint = {
6017                                        time: segment.start,
6018                                        segmentIndex: i
6019                                    };
6020                                }
6021                            }
6022                        }
6023                        return syncPoint;
6024                    }
6025                },
6026// Stategy "Discontinuity": We have a discontinuity with a known
6027//                          display-time
6028                {
6029                    name: 'Discontinuity',
6030                    run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
6031                        var syncPoint = null;
6032
6033                        currentTime = currentTime || 0;
6034
6035                        if (playlist.discontinuityStarts.length) {
6036                            var lastDistance = null;
6037
6038                            for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
6039                                var segmentIndex = playlist.discontinuityStarts[i];
6040                                var discontinuity = playlist.discontinuitySequence + i + 1;
6041                                var discontinuitySync = syncController.discontinuities[discontinuity];
6042
6043                                if (discontinuitySync) {
6044                                    var distance = Math.abs(currentTime - discontinuitySync.time);
6045
6046                                    // Once the distance begins to increase, we have passed
6047                                    // currentTime and can stop looking for better candidates
6048                                    if (lastDistance !== null && lastDistance < distance) {
6049                                        break;
6050                                    }
6051
6052                                    if (!syncPoint || lastDistance === null || lastDistance >= distance) {
6053                                        lastDistance = distance;
6054                                        syncPoint = {
6055                                            time: discontinuitySync.time,
6056                                            segmentIndex: segmentIndex
6057                                        };
6058                                    }
6059                                }
6060                            }
6061                        }
6062                        return syncPoint;
6063                    }
6064                },
6065// Stategy "Playlist": We have a playlist with a known mapping of
6066//                     segment index to display time
6067                {
6068                    name: 'Playlist',
6069                    run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
6070                        if (playlist.syncInfo) {
6071                            var syncPoint = {
6072                                time: playlist.syncInfo.time,
6073                                segmentIndex: playlist.syncInfo.mediaSequence - playlist.mediaSequence
6074                            };
6075
6076                            return syncPoint;
6077                        }
6078                        return null;
6079                    }
6080                }];
6081
6082            exports.syncPointStrategies = syncPointStrategies;
6083
6084            var SyncController = (function (_videojs$EventTarget) {
6085                _inherits(SyncController, _videojs$EventTarget);
6086
6087                function SyncController() {
6088                    _classCallCheck(this, SyncController);
6089
6090                    _get(Object.getPrototypeOf(SyncController.prototype), 'constructor', this).call(this);
6091                    // Segment Loader state variables...
6092                    // ...for synching across variants
6093                    this.inspectCache_ = undefined;
6094
6095                    // ...for synching across variants
6096                    this.timelines = [];
6097                    this.discontinuities = [];
6098                    this.datetimeToDisplayTime = null;
6099
6100                    if (_videoJs2['default'].options.hls && _videoJs2['default'].options.hls.debug) {
6101                        this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'sync-controller ->');
6102                    }
6103                }
6104
6105                /**
6106                 * Find a sync-point for the playlist specified
6107                 *
6108                 * A sync-point is defined as a known mapping from display-time to
6109                 * a segment-index in the current playlist.
6110                 *
6111                 * @param {Playlist} playlist
6112                 *        The playlist that needs a sync-point
6113                 * @param {Number} duration
6114                 *        Duration of the MediaSource (Infinite if playing a live source)
6115                 * @param {Number} currentTimeline
6116                 *        The last timeline from which a segment was loaded
6117                 * @returns {Object}
6118                 *          A sync-point object
6119                 */
6120
6121                _createClass(SyncController, [{
6122                    key: 'getSyncPoint',
6123                    value: function getSyncPoint(playlist, duration, currentTimeline, currentTime) {
6124                        var syncPoints = this.runStrategies_(playlist, duration, currentTimeline, currentTime);
6125
6126                        if (!syncPoints.length) {
6127                            // Signal that we need to attempt to get a sync-point manually
6128                            // by fetching a segment in the playlist and constructing
6129                            // a sync-point from that information
6130                            return null;
6131                        }
6132
6133                        // Now find the sync-point that is closest to the currentTime because
6134                        // that should result in the most accurate guess about which segment
6135                        // to fetch
6136                        return this.selectSyncPoint_(syncPoints, { key: 'time', value: currentTime });
6137                    }
6138
6139                    /**
6140                     * Calculate the amount of time that has expired off the playlist during playback
6141                     *
6142                     * @param {Playlist} playlist
6143                     *        Playlist object to calculate expired from
6144                     * @param {Number} duration
6145                     *        Duration of the MediaSource (Infinity if playling a live source)
6146                     * @returns {Number|null}
6147                     *          The amount of time that has expired off the playlist during playback. Null
6148                     *          if no sync-points for the playlist can be found.
6149                     */
6150                }, {
6151                    key: 'getExpiredTime',
6152                    value: function getExpiredTime(playlist, duration) {
6153                        if (!playlist || !playlist.segments) {
6154                            return null;
6155                        }
6156
6157                        var syncPoints = this.runStrategies_(playlist, duration, playlist.discontinuitySequence, 0);
6158
6159                        // Without sync-points, there is not enough information to determine the expired time
6160                        if (!syncPoints.length) {
6161                            return null;
6162                        }
6163
6164                        var syncPoint = this.selectSyncPoint_(syncPoints, {
6165                            key: 'segmentIndex',
6166                            value: 0
6167                        });
6168
6169                        // If the sync-point is beyond the start of the playlist, we want to subtract the
6170                        // duration from index 0 to syncPoint.segmentIndex instead of adding.
6171                        if (syncPoint.segmentIndex > 0) {
6172                            syncPoint.time *= -1;
6173                        }
6174
6175                        return Math.abs(syncPoint.time + (0, _playlist.sumDurations)(playlist, syncPoint.segmentIndex, 0));
6176                    }
6177
6178                    /**
6179                     * Runs each sync-point strategy and returns a list of sync-points returned by the
6180                     * strategies
6181                     *
6182                     * @private
6183                     * @param {Playlist} playlist
6184                     *        The playlist that needs a sync-point
6185                     * @param {Number} duration
6186                     *        Duration of the MediaSource (Infinity if playing a live source)
6187                     * @param {Number} currentTimeline
6188                     *        The last timeline from which a segment was loaded
6189                     * @returns {Array}
6190                     *          A list of sync-point objects
6191                     */
6192                }, {
6193                    key: 'runStrategies_',
6194                    value: function runStrategies_(playlist, duration, currentTimeline, currentTime) {
6195                        var syncPoints = [];
6196
6197                        // Try to find a sync-point in by utilizing various strategies...
6198                        for (var i = 0; i < syncPointStrategies.length; i++) {
6199                            var strategy = syncPointStrategies[i];
6200                            var syncPoint = strategy.run(this, playlist, duration, currentTimeline, currentTime);
6201
6202                            if (syncPoint) {
6203                                syncPoint.strategy = strategy.name;
6204                                syncPoints.push({
6205                                    strategy: strategy.name,
6206                                    syncPoint: syncPoint
6207                                });
6208                                this.logger_('syncPoint found via <' + strategy.name + '>:', syncPoint);
6209                            }
6210                        }
6211
6212                        return syncPoints;
6213                    }
6214
6215                    /**
6216                     * Selects the sync-point nearest the specified target
6217                     *
6218                     * @private
6219                     * @param {Array} syncPoints
6220                     *        List of sync-points to select from
6221                     * @param {Object} target
6222                     *        Object specifying the property and value we are targeting
6223                     * @param {String} target.key
6224                     *        Specifies the property to target. Must be either 'time' or 'segmentIndex'
6225                     * @param {Number} target.value
6226                     *        The value to target for the specified key.
6227                     * @returns {Object}
6228                     *          The sync-point nearest the target
6229                     */
6230                }, {
6231                    key: 'selectSyncPoint_',
6232                    value: function selectSyncPoint_(syncPoints, target) {
6233                        var bestSyncPoint = syncPoints[0].syncPoint;
6234                        var bestDistance = Math.abs(syncPoints[0].syncPoint[target.key] - target.value);
6235                        var bestStrategy = syncPoints[0].strategy;
6236
6237                        for (var i = 1; i < syncPoints.length; i++) {
6238                            var newDistance = Math.abs(syncPoints[i].syncPoint[target.key] - target.value);
6239
6240                            if (newDistance < bestDistance) {
6241                                bestDistance = newDistance;
6242                                bestSyncPoint = syncPoints[i].syncPoint;
6243                                bestStrategy = syncPoints[i].strategy;
6244                            }
6245                        }
6246
6247                        this.logger_('syncPoint with strategy <' + bestStrategy + '> chosen: ', bestSyncPoint);
6248                        return bestSyncPoint;
6249                    }
6250
6251                    /**
6252                     * Save any meta-data present on the segments when segments leave
6253                     * the live window to the playlist to allow for synchronization at the
6254                     * playlist level later.
6255                     *
6256                     * @param {Playlist} oldPlaylist - The previous active playlist
6257                     * @param {Playlist} newPlaylist - The updated and most current playlist
6258                     */
6259                }, {
6260                    key: 'saveExpiredSegmentInfo',
6261                    value: function saveExpiredSegmentInfo(oldPlaylist, newPlaylist) {
6262                        var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
6263
6264                        // When a segment expires from the playlist and it has a start time
6265                        // save that information as a possible sync-point reference in future
6266                        for (var i = mediaSequenceDiff - 1; i >= 0; i--) {
6267                            var lastRemovedSegment = oldPlaylist.segments[i];
6268
6269                            if (lastRemovedSegment && typeof lastRemovedSegment.start !== 'undefined') {
6270                                newPlaylist.syncInfo = {
6271                                    mediaSequence: oldPlaylist.mediaSequence + i,
6272                                    time: lastRemovedSegment.start
6273                                };
6274                                this.logger_('playlist sync:', newPlaylist.syncInfo);
6275                                this.trigger('syncinfoupdate');
6276                                break;
6277                            }
6278                        }
6279                    }
6280
6281                    /**
6282                     * Save the mapping from playlist's ProgramDateTime to display. This should
6283                     * only ever happen once at the start of playback.
6284                     *
6285                     * @param {Playlist} playlist - The currently active playlist
6286                     */
6287                }, {
6288                    key: 'setDateTimeMapping',
6289                    value: function setDateTimeMapping(playlist) {
6290                        if (!this.datetimeToDisplayTime && playlist.dateTimeObject) {
6291                            var playlistTimestamp = playlist.dateTimeObject.getTime() / 1000;
6292
6293                            this.datetimeToDisplayTime = -playlistTimestamp;
6294                        }
6295                    }
6296
6297                    /**
6298                     * Reset the state of the inspection cache when we do a rendition
6299                     * switch
6300                     */
6301                }, {
6302                    key: 'reset',
6303                    value: function reset() {
6304                        this.inspectCache_ = undefined;
6305                    }
6306
6307                    /**
6308                     * Probe or inspect a fmp4 or an mpeg2-ts segment to determine the start
6309                     * and end of the segment in it's internal "media time". Used to generate
6310                     * mappings from that internal "media time" to the display time that is
6311                     * shown on the player.
6312                     *
6313                     * @param {SegmentInfo} segmentInfo - The current active request information
6314                     */
6315                }, {
6316                    key: 'probeSegmentInfo',
6317                    value: function probeSegmentInfo(segmentInfo) {
6318                        var segment = segmentInfo.segment;
6319                        var timingInfo = undefined;
6320
6321                        if (segment.map) {
6322                            timingInfo = this.probeMp4Segment_(segmentInfo);
6323                        } else {
6324                            timingInfo = this.probeTsSegment_(segmentInfo);
6325                        }
6326
6327                        if (timingInfo) {
6328                            if (this.calculateSegmentTimeMapping_(segmentInfo, timingInfo)) {
6329                                this.saveDiscontinuitySyncInfo_(segmentInfo);
6330                            }
6331                        }
6332                    }
6333
6334                    /**
6335                     * Probe an fmp4 or an mpeg2-ts segment to determine the start of the segment
6336                     * in it's internal "media time".
6337                     *
6338                     * @private
6339                     * @param {SegmentInfo} segmentInfo - The current active request information
6340                     * @return {object} The start and end time of the current segment in "media time"
6341                     */
6342                }, {
6343                    key: 'probeMp4Segment_',
6344                    value: function probeMp4Segment_(segmentInfo) {
6345                        var segment = segmentInfo.segment;
6346                        var timescales = _muxJsLibMp4Probe2['default'].timescale(segment.map.bytes);
6347                        var startTime = _muxJsLibMp4Probe2['default'].startTime(timescales, segmentInfo.bytes);
6348
6349                        if (segmentInfo.timestampOffset !== null) {
6350                            segmentInfo.timestampOffset -= startTime;
6351                        }
6352
6353                        return {
6354                            start: startTime,
6355                            end: startTime + segment.duration
6356                        };
6357                    }
6358
6359                    /**
6360                     * Probe an mpeg2-ts segment to determine the start and end of the segment
6361                     * in it's internal "media time".
6362                     *
6363                     * @private
6364                     * @param {SegmentInfo} segmentInfo - The current active request information
6365                     * @return {object} The start and end time of the current segment in "media time"
6366                     */
6367                }, {
6368                    key: 'probeTsSegment_',
6369                    value: function probeTsSegment_(segmentInfo) {
6370                        var timeInfo = (0, _muxJsLibToolsTsInspectorJs.inspect)(segmentInfo.bytes, this.inspectCache_);
6371                        var segmentStartTime = undefined;
6372                        var segmentEndTime = undefined;
6373
6374                        if (!timeInfo) {
6375                            return null;
6376                        }
6377
6378                        if (timeInfo.video && timeInfo.video.length === 2) {
6379                            this.inspectCache_ = timeInfo.video[1].dts;
6380                            segmentStartTime = timeInfo.video[0].dtsTime;
6381                            segmentEndTime = timeInfo.video[1].dtsTime;
6382                        } else if (timeInfo.audio && timeInfo.audio.length === 2) {
6383                            this.inspectCache_ = timeInfo.audio[1].dts;
6384                            segmentStartTime = timeInfo.audio[0].dtsTime;
6385                            segmentEndTime = timeInfo.audio[1].dtsTime;
6386                        }
6387
6388                        return {
6389                            start: segmentStartTime,
6390                            end: segmentEndTime
6391                        };
6392                    }
6393                }, {
6394                    key: 'timestampOffsetForTimeline',
6395                    value: function timestampOffsetForTimeline(timeline) {
6396                        if (typeof this.timelines[timeline] === 'undefined') {
6397                            return null;
6398                        }
6399                        return this.timelines[timeline].time;
6400                    }
6401
6402                    /**
6403                     * Use the "media time" for a segment to generate a mapping to "display time" and
6404                     * save that display time to the segment.
6405                     *
6406                     * @private
6407                     * @param {SegmentInfo} segmentInfo
6408                     *        The current active request information
6409                     * @param {object} timingInfo
6410                     *        The start and end time of the current segment in "media time"
6411                     * @returns {Boolean}
6412                     *          Returns false if segment time mapping could not be calculated
6413                     */
6414                }, {
6415                    key: 'calculateSegmentTimeMapping_',
6416                    value: function calculateSegmentTimeMapping_(segmentInfo, timingInfo) {
6417                        var segment = segmentInfo.segment;
6418                        var mappingObj = this.timelines[segmentInfo.timeline];
6419
6420                        if (segmentInfo.timestampOffset !== null) {
6421                            this.logger_('tsO:', segmentInfo.timestampOffset);
6422
6423                            mappingObj = {
6424                                time: segmentInfo.startOfSegment,
6425                                mapping: segmentInfo.startOfSegment - timingInfo.start
6426                            };
6427                            this.timelines[segmentInfo.timeline] = mappingObj;
6428                            this.trigger('timestampoffset');
6429
6430                            segment.start = segmentInfo.startOfSegment;
6431                            segment.end = timingInfo.end + mappingObj.mapping;
6432                        } else if (mappingObj) {
6433                            segment.start = timingInfo.start + mappingObj.mapping;
6434                            segment.end = timingInfo.end + mappingObj.mapping;
6435                        } else {
6436                            return false;
6437                        }
6438
6439                        return true;
6440                    }
6441
6442                    /**
6443                     * Each time we have discontinuity in the playlist, attempt to calculate the location
6444                     * in display of the start of the discontinuity and save that. We also save an accuracy
6445                     * value so that we save values with the most accuracy (closest to 0.)
6446                     *
6447                     * @private
6448                     * @param {SegmentInfo} segmentInfo - The current active request information
6449                     */
6450                }, {
6451                    key: 'saveDiscontinuitySyncInfo_',
6452                    value: function saveDiscontinuitySyncInfo_(segmentInfo) {
6453                        var playlist = segmentInfo.playlist;
6454                        var segment = segmentInfo.segment;
6455
6456                        // If the current segment is a discontinuity then we know exactly where
6457                        // the start of the range and it's accuracy is 0 (greater accuracy values
6458                        // mean more approximation)
6459                        if (segment.discontinuity) {
6460                            this.discontinuities[segment.timeline] = {
6461                                time: segment.start,
6462                                accuracy: 0
6463                            };
6464                        } else if (playlist.discontinuityStarts.length) {
6465                            // Search for future discontinuities that we can provide better timing
6466                            // information for and save that information for sync purposes
6467                            for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
6468                                var segmentIndex = playlist.discontinuityStarts[i];
6469                                var discontinuity = playlist.discontinuitySequence + i + 1;
6470                                var mediaIndexDiff = segmentIndex - segmentInfo.mediaIndex;
6471                                var accuracy = Math.abs(mediaIndexDiff);
6472
6473                                if (!this.discontinuities[discontinuity] || this.discontinuities[discontinuity].accuracy > accuracy) {
6474                                    var time = undefined;
6475
6476                                    if (mediaIndexDiff < 0) {
6477                                        time = segment.start - (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex, segmentIndex);
6478                                    } else {
6479                                        time = segment.end + (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex + 1, segmentIndex);
6480                                    }
6481
6482                                    this.discontinuities[discontinuity] = {
6483                                        time: time,
6484                                        accuracy: accuracy
6485                                    };
6486                                }
6487                            }
6488                        }
6489                    }
6490
6491                    /**
6492                     * A debugging logger noop that is set to console.log only if debugging
6493                     * is enabled globally
6494                     *
6495                     * @private
6496                     */
6497                }, {
6498                    key: 'logger_',
6499                    value: function logger_() {}
6500                }]);
6501
6502                return SyncController;
6503            })(_videoJs2['default'].EventTarget);
6504
6505            exports['default'] = SyncController;
6506        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
6507    },{"./playlist":9,"mux.js/lib/mp4/probe":55,"mux.js/lib/tools/ts-inspector.js":57}],18:[function(require,module,exports){
6508        (function (global){
6509            /**
6510             * @file vtt-segment-loader.js
6511             */
6512            'use strict';
6513
6514            Object.defineProperty(exports, '__esModule', {
6515                value: true
6516            });
6517
6518            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
6519
6520            var _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); } } };
6521
6522            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
6523
6524            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
6525
6526            function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
6526onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
6527
6528            var _segmentLoader = require('./segment-loader');
6529
6530            var _segmentLoader2 = _interopRequireDefault(_segmentLoader);
6531
6532            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
6533
6534            var _videoJs2 = _interopRequireDefault(_videoJs);
6535
6536            var _globalWindow = require('global/window');
6537
6538            var _globalWindow2 = _interopRequireDefault(_globalWindow);
6539
6540            var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs = require('videojs-contrib-media-sources/es5/remove-cues-from-track.js');
6541
6542            var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2 = _interopRequireDefault(_videojsContribMediaSourcesEs5RemoveCuesFromTrackJs);
6543
6544            var _binUtils = require('./bin-utils');
6545
6546            var VTT_LINE_TERMINATORS = new Uint8Array('\n\n'.split('').map(function (char) {
6547                return char.charCodeAt(0);
6548            }));
6549
6550            var uintToString = function uintToString(uintArray) {
6551                return String.fromCharCode.apply(null, uintArray);
6552            };
6553
6554            /**
6555             * An object that manages segment loading and appending.
6556             *
6557             * @class VTTSegmentLoader
6558             * @param {Object} options required and optional options
6559             * @extends videojs.EventTarget
6560             */
6561
6562            var VTTSegmentLoader = (function (_SegmentLoader) {
6563                _inherits(VTTSegmentLoader, _SegmentLoader);
6564
6565                function VTTSegmentLoader(options) {
6566                    _classCallCheck(this, VTTSegmentLoader);
6567
6568                    _get(Object.getPrototypeOf(VTTSegmentLoader.prototype), 'constructor', this).call(this, options);
6569
6570                    // SegmentLoader requires a MediaSource be specified or it will throw an error;
6571                    // however, VTTSegmentLoader has no need of a media source, so delete the reference
6572                    this.mediaSource_ = null;
6573
6574                    this.subtitlesTrack_ = null;
6575                }
6576
6577                /**
6578                 * Indicates which time ranges are buffered
6579                 *
6580                 * @return {TimeRange}
6581                 *         TimeRange object representing the current buffered ranges
6582                 */
6583
6584                _createClass(VTTSegmentLoader, [{
6585                    key: 'buffered_',
6586                    value: function buffered_() {
6587                        if (!this.subtitlesTrack_ || !this.subtitlesTrack_.cues.length) {
6588                            return _videoJs2['default'].createTimeRanges();
6589                        }
6590
6591                        var cues = this.subtitlesTrack_.cues;
6592                        var start = cues[0].startTime;
6593                        var end = cues[cues.length - 1].startTime;
6594
6595                        return _videoJs2['default'].createTimeRanges([[start, end]]);
6596                    }
6597
6598                    /**
6599                     * Gets and sets init segment for the provided map
6600                     *
6601                     * @param {Object} map
6602                     *        The map object representing the init segment to get or set
6603                     * @param {Boolean=} set
6604                     *        If true, the init segment for the provided map should be saved
6605                     * @return {Object}
6606                     *         map object for desired init segment
6607                     */
6608                }, {
6609                    key: 'initSegment',
6610                    value: function initSegment(map) {
6611                        var set = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
6612
6613                        if (!map) {
6614                            return null;
6615                        }
6616
6617                        var id = (0, _binUtils.initSegmentId)(map);
6618                        var storedMap = this.initSegments_[id];
6619
6620                        if (set && !storedMap && map.bytes) {
6621                            // append WebVTT line terminators to the media initialization segment if it exists
6622                            // to follow the WebVTT spec (https://w3c.github.io/webvtt/#file-structure) that
6623                            // requires two or more WebVTT line terminators between the WebVTT header and the rest
6624                            // of the file
6625                            var combinedByteLength = VTT_LINE_TERMINATORS.byteLength + map.bytes.byteLength;
6626                            var combinedSegment = new Uint8Array(combinedByteLength);
6627
6628                            combinedSegment.set(map.bytes);
6629                            combinedSegment.set(VTT_LINE_TERMINATORS, map.b
vendor: 5,269 bytes, lines 6629-6745
6629ytes.byteLength);
6630
6631                            this.initSegments_[id] = storedMap = {
6632                                resolvedUri: map.resolvedUri,
6633                                byterange: map.byterange,
6634                                bytes: combinedSegment
6635                            };
6636                        }
6637
6638                        return storedMap || map;
6639                    }
6640
6641                    /**
6642                     * Returns true if all configuration required for loading is present, otherwise false.
6643                     *
6644                     * @return {Boolean} True if the all configuration is ready for loading
6645                     * @private
6646                     */
6647                }, {
6648                    key: 'couldBeginLoading_',
6649                    value: function couldBeginLoading_() {
6650                        return this.playlist_ && this.subtitlesTrack_ && !this.paused();
6651                    }
6652
6653                    /**
6654                     * Once all the starting parameters have been specified, begin
6655                     * operation. This method should only be invoked from the INIT
6656                     * state.
6657                     *
6658                     * @private
6659                     */
6660                }, {
6661                    key: 'init_',
6662                    value: function init_() {
6663                        this.state = 'READY';
6664                        this.resetEverything();
6665                        return this.monitorBuffer_();
6666                    }
6667
6668                    /**
6669                     * Set a subtitle track on the segment loader to add subtitles to
6670                     *
6671                     * @param {TextTrack} track
6672                     *        The text track to add loaded subtitles to
6673                     */
6674                }, {
6675                    key: 'track',
6676                    value: function track(_track) {
6677                        this.subtitlesTrack_ = _track;
6678
6679                        // if we were unpaused but waiting for a sourceUpdater, start
6680                        // buffering now
6681                        if (this.state === 'INIT' && this.couldBeginLoading_()) {
6682                            this.init_();
6683                        }
6684                    }
6685
6686                    /**
6687                     * Remove any data in the source buffer between start and end times
6688                     * @param {Number} start - the start time of the region to remove from the buffer
6689                     * @param {Number} end - the end time of the region to remove from the buffer
6690                     */
6691                }, {
6692                    key: 'remove',
6693                    value: function remove(start, end) {
6694                        (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.subtitlesTrack_);
6695                    }
6696
6697                    /**
6698                     * fill the buffer with segements unless the sourceBuffers are
6699                     * currently updating
6700                     *
6701                     * Note: this function should only ever be called by monitorBuffer_
6702                     * and never directly
6703                     *
6704                     * @private
6705                     */
6706                }, {
6707                    key: 'fillBuffer_',
6708                    value: function fillBuffer_() {
6709                        var _this = this;
6710
6711                        if (!this.syncPoint_) {
6712                            this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
6713                        }
6714
6715                        // see if we need to begin loading immediately
6716                        var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
6717
6718                        segmentInfo = this.skipEmptySegments_(segmentInfo);
6719
6720                        if (!segmentInfo) {
6721                            return;
6722                        }
6723
6724                        if (this.syncController_.timestampOffsetForTimeline(segmentInfo.timeline) === null) {
6725                            // We don't have the timestamp offset that we need to sync subtitles.
6726                            // Rerun on a timestamp offset or user interaction.
6727                            var checkTimestampOffset = function checkTimestampOffset() {
6728                                _this.state = 'READY';
6729                                if (!_this.paused()) {
6730                                    // if not paused, queue a buffer check as soon as possible
6731                                    _this.monitorBuffer_();
6732                                }
6733                            };
6734
6735                            this.syncController_.one('timestampoffset', checkTimestampOffset);
6736                            this.state = 'WAITING_ON_TIMELINE';
6737                            return;
6738                        }
6739
6740                        this.loadSegment_(segmentInfo);
6741                    }
6742
6743                    /**
6744                     * Prevents the segment loader from requesting segments we know contain no subtitles
6745                     * by walking forward until we find the next segment that we don't know 
6745whether it is
6746                     * empty or not.
6747                     *
6748                     * @param {Object} segmentInfo
6749                     *        a segment info object that describes the current segment
6750                     * @return {Object}
6751                     *         a segment info object that describes the current segment
6752                     */
6753                }, {
6754                    key: 'skipEmptySegments_',
6755                    value: function skipEmptySegments_(segmentInfo) {
6756                        while (segmentInfo && segmentInfo.segment.empty) {
6757                            segmentInfo = this.generateSegmentInfo_(segmentInfo.playlist, segmentInfo.mediaIndex + 1, segmentInfo.startOfSegment + segmentInfo.duration, segmentInfo.isSyncRequest);
6758                        }
6759                        return segmentInfo;
6760                    }
6761
6762                    /**
6763                     * append a decrypted segement to the SourceBuffer through a SourceUpdater
6764                     *
6765                     * @private
6766                     */
6767                }, {
6768                    key: 'handleSegment_',
6769                    value: function handleSegment_() {
6770                        var _this2 = this;
6771
6772                        if (!this.pendingSegment_) {
6773                            this.state = 'READY';
6774                            return;
6775                        }
6776
6777                        this.state = 'APPENDING';
6778
6779                        var segmentInfo = this.pendingSegment_;
6780                        var segment = segmentInfo.segment;
6781
6782                        // Make sure that vttjs has loaded, otherwise, wait till it finished loading
6783                        if (typeof _globalWindow2['default'].WebVTT !== 'function' && this.subtitlesTrack_ && this.subtitlesTrack_.tech_) {
6784                            var _ret = (function () {
6785
6786                                var loadHandler = function loadHandler() {
6787                                    _this2.handleSegment_();
6788                                };
6789
6790                                _this2.state = 'WAITING_ON_VTTJS';
6791                                _this2.subtitlesTrack_.tech_.one('vttjsloaded', loadHandler);
6792                                _this2.subtitlesTrack_.tech_.one('vttjserror', function () {
6793                                    _this2.subtitlesTrack_.tech_.off('vttjsloaded', loadHandler);
6794                                    _this2.error({
6795                                        message: 'Error loading vtt.js'
6796                                    });
6797                                    _this2.state = 'READY';
6798                                    _this2.pause();
6799                                    _this2.trigger('error');
6800                                });
6801
6802                                return {
6803                                    v: undefined
6804                                };
6805                            })();
6806
6807                            if (typeof _ret === 'object') return _ret.v;
6808                        }
6809
6810                        segment.requested = true;
6811
6812                        try {
6813                            this.parseVTTCues_(segmentInfo);
6814                        } catch (e) {
6815                            this.error({
6816                                message: e.message
6817                            });
6818                            this.state = 'READY';
6819                            this.pause();
6820                            return this.trigger('error');
6821                        }
6822
6823                        this.updateTimeMapping_(segmentInfo, this.syncController_.timelines[segmentInfo.timeline], this.playlist_);
6824
6825                        if (segmentInfo.isSyncRequest) {
6826                            this.trigger('syncinfoupdate');
6827                            this.pendingSegment_ = null;
6828                            this.state = 'READY';
6829                            return;
6830                        }
6831
6832                        segmentInfo.byteLength = segmentInfo.bytes.byteLength;
6833
6834                        this.mediaSecondsLoaded += segment.duration;
6835
6836                        segmentInfo.cues.forEach(function (cue) {
6837                            _this2.subtitlesTrack_.addCue(cue);
6838                        });
6839
6840                        this.handleUpdateEnd_();
6841                    }
6842
6843                    /**
6844                     * Uses the WebVTT parser to parse the segment response
6845                     *
6846                     * @param {Object} segmentInfo
6847                     *        a segment info object that describes the current segment
6848                     * @private
6849                     */
6850                }, {
6851                    key: 'parseVTTCues_',
6852                    value: function parseVTTCues_(segmentInfo) {
6853                        var decoder = undefined;
6854                        var decodeBytesToString = false;
6855
6856                        if (typeof _globalWindow2['default'].TextDecoder === 'function') {
6857                            decoder = new _globalWindow2['default'].TextDecoder('utf8');
6858                        } else {
6859                            decoder = _globalWindow2['default'].WebVTT.StringDecoder();
6860                            decodeBytesToString = true;
6861                        }
6862
6863                        var parser = new _globalWindow2['default'].WebVTT.Parser(_globalWindow2['default'], _globalWindow2['default'].vttjs, decoder);
6864
6865                        segmentInfo.cues = [];
6866                        segmentInfo.timestampmap = { MPEGTS: 0, LOCAL: 0 };
6867
6868                        parser.oncue = segmentInfo.cues.push.bind(segmentInfo.cues);
6869                        parser.ontimestampmap = function (map) {
6870                            return segmentInfo.timestampmap = map;
6871                        };
6872                        parser.onparsingerror = function (error) {
6873                            _videoJs2['default'].log.warn('Error encountered when parsing cues: ' + error.message);
6874                        };
6875
6876                        if (segmentInfo.segment.map) {
6877                            var mapData = segmentInfo.segment.map.bytes;
6878
6879                            if (decodeBytesToString) {
6880                                mapData = uintToString(mapData);
6881                            }
6882
6883                            parser.parse(mapData);
6884                        }
6885
6886                        var segmentData = segmentInfo.bytes;
6887
6888                        if (decodeBytesToString) {
6889                            segmentData = uintToString(segmentData);
6890                        }
6891
6892                        parser.parse(segmentData);
6893                        parser.flush();
6894                    }
6895
6896                    /**
6897                     * Updates the start and end times of any cues parsed by the WebVTT parser using
6898                     * the information parsed from the X-TIMESTAMP-MAP header and a TS to media time mapping
6899                     * from the SyncController
6900                     *
6901                     * @param {Object} segmentInfo
6902                     *        a segment info object that describes the current segment
6903                     * @param {Object} mappingObj
6904                     *        object containing a mapping from TS to media time
6905                     * @param {Object} playlist
6906                     *        the playlist object containing the segment
6907                     * @private
6908                     */
6909                }, {
6910                    key: 'updateTimeMapping_',
6911                    value: function updateTimeMapping_(segmentInfo, mappingObj, playlist) {
6912                        var segment = segmentInfo.segment;
6913
6914                        if (!mappingObj) {
6915                            // If the sync controller does not have a mapping of TS to Media Time for the
6916                            // timeline, then we don't have enough information to update the cue
6917                            // start/end times
6918                            return;
6919                        }
6920
6921                        if (!segmentInfo.cues.length) {
6922                            // If there are no cues, we also do not have enough information to figure out
6923                            // segment timing. Mark that the segment contains 
6923no cues so we don't re-request
6924                            // an empty segment.
6925                            segment.empty = true;
6926                            return;
6927                        }
6928
6929                        var timestampmap = segmentInfo.timestampmap;
6930                        var diff = timestampmap.MPEGTS / 90000 - timestampmap.LOCAL + mappingObj.mapping;
6931
6932                        segmentInfo.cues.forEach(function (cue) {
6933                            // First convert cue time to TS time using the timestamp-map provided within the vtt
6934                            cue.startTime += diff;
6935                            cue.endTime += diff;
6936                        });
6937
6938                        if (!playlist.syncInfo) {
6939                            var firstStart = segmentInfo.cues[0].startTime;
6940                            var lastStart = segmentInfo.cues[segmentInfo.cues.length - 1].startTime;
6941
6942                            playlist.syncInfo = {
6943                                mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
6944                                time: Math.min(firstStart, lastStart - segment.duration)
6945                            };
6946                        }
6947                    }
6948                }]);
6949
6950                return VTTSegmentLoader;
6951            })(_segmentLoader2['default']);
6952
6953            exports['default'] = VTTSegmentLoader;
6954            module.exports = exports['default'];
6955        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
6956    },{"./bin-utils":2,"./segment-loader":14,"global/window":30,"videojs-contrib-media-sources/es5/remove-cues-from-track.js":71}],19:[function(require,module,exports){
6957        (function (global){
6958            /**
6959             * @file xhr.js
6960             */
6961
6962            /**
6963             * A wrapper for videojs.xhr that tracks bandwidth.
6964             *
6965             * @param {Object} options options for the XHR
6966             * @param {Function} callback the callback to call when done
6967             * @return {Request} the xhr request that is going to be made
6968             */
6969            'use strict';
6970
6971            Object.defineProperty(exports, '__esModule', {
6972                value: true
6973            });
6974
6975            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
6976
6977            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
6978
6979            var _videoJs2 = _interopRequireDefault(_videoJs);
6980
6981            var xhrFactory = function xhrFactory() {
6982                var xhr = function XhrFunction(options, callback) {
6983                    // Add a default timeout for all hls requests
6984                    options = (0, _videoJs.mergeOptions)({
6985                        timeout: 45e3
6986                    }, options);
6987
6988                    // Allow an optional user-specified function to modify the option
6989                    // object before we construct the xhr request
6990                    var beforeRequest = XhrFunction.beforeRequest || _videoJs2['default'].Hls.xhr.beforeRequest;
6991
6992                    if (beforeRequest && typeof beforeRequest === 'function') {
6993                        var newOptions = beforeRequest(options);
6994
6995                        if (newOptions) {
6996                            options = newOptions;
6997                        }
6998                    }
6999
7000                    var request = (0, _videoJs.xhr)(options, function (error, response) {
7001                        var reqResponse = request.response;
7002
7003                        if (!error && reqResponse) {
7004                            request.responseTime = Date.now();
7005                            request.roundTripTime = request.responseTime - request.requestTime;
7006                            request.bytesReceived = reqResponse.byteLength || reqResponse.length;
7007                            if (!request.bandwidth) {
7008                                request.bandwidth = Math.floor(request.bytesReceived / request.roundTripTime * 8 * 1000);
7009                            }
7010                        }
7011
7012                        // videojs.xhr now uses a specific code on the error
7013                        // object to signal that a request has t
7013imed out instead
7014                        // of setting a boolean on the request object
7015                        if (error && error.code === 'ETIMEDOUT') {
7016                            request.timedout = true;
7017                        }
7018
7019                        // videojs.xhr no longer considers status codes outside of 200 and 0
7020                        // (for file uris) to be errors, but the old XHR did, so emulate that
7021                        // behavior. Status 206 may be used in response to byterange requests.
7022                        if (!error && !request.aborted && response.statusCode !== 200 && response.statusCode !== 206 && response.statusCode !== 0) {
7023                            error = new Error('XHR Failed with a response of: ' + (request && (reqResponse || request.responseText)));
7024                        }
7025
7026                        callback(error, request);
7027                    });
7028                    var originalAbort = request.abort;
7029
7030                    request.abort = function () {
7031                        request.aborted = true;
7032                        return originalAbort.apply(request, arguments);
7033                    };
7034                    request.uri = options.uri;
7035                    request.requestTime = Date.now();
7036                    return request;
7037                };
7038
7039                return xhr;
7040            };
7041
7042            exports['default'] = xhrFactory;
7043            module.exports = exports['default'];
7044        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
7045    },{}],20:[function(require,module,exports){
7046        /**
7047         * @file aes.js
7048         *
7049         * This file contains an adaptation of the AES decryption algorithm
7050         * from the Standford Javascript Cryptography Library. That work is
7051         * covered by the following copyright and permissions notice:
7052         *
7053         * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
7054         * All rights reserved.
7055         *
7056         * Redistribution and use in source and binary forms, with or without
7057         * modification, are permitted provided that the following conditions are
7058         * met:
7059         *
7060         * 1. Redistributions of source code must retain the above copyright
7061         *    notice, this list of conditions and the following disclaimer.
7062         *
7063         * 2. Redistributions in binary form must reproduce the above
7064         *    copyright notice, this list of conditions and the following
7065         *    disclaimer in the documentation and/or other materials provided
7066         *    with the distribution.
7067         *
7068         * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
7069         * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
7070         * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
7071         * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
7072         * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
7073         * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
7074         * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
7075         * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
7076         * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
7077         * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
7078         * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7079         *
7080         * The views and conclusions contained in the software and documentation
7081         * are those of the authors and should not be interpreted as representing
7082         * official policies, either expressed or implied, of the authors.
7083         */
7084
7085        /**
7086         * Expand the S-box tables.
7087         *
7088         * @private
7089         */
7090        'use strict';
7091
7092        Object.defineProperty(exports, '__esModule', {
7093            value: true
7094        });
7095
7096        var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
7097
7098        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
7099
7100        var precompute = function precompute() {
7101            var tables = [[[], [], [], [], []], [[], [], [], [], []]];
7102            var encTable = tables[0];
7103            var decTable = tables[1];
7104            var sbox = encTable[4];
7105            var sboxInv = decTable[4];
7106            var i = undefined;
7107            var x = undefined;
7108            var xInv = undefined;
7109            var d = [];
7110            var th = [];
7111            var x2 = undefined;
7112            var x4 = undefined;
7113            var x8 = undefined;
7114            var s = undefined;
7115            var tEnc = undefined;
7116            var tDec = undefined;
7117
7118            // Compute double and third tables
7119            for (i = 0; i < 256; i++) {
7120                th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
7121            }
7122
7123            for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
7124                // Compute sbox
7125                s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
7126                s = s >> 8 ^ s & 255 ^ 99;
7127                sbox[x] = s;
7128                sboxInv[s] = x;
7129
7130                // Compute MixColumns
7131                x8 = d[x4 = d[x2 = d[x]]];
7132                tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
7133                tEnc = d[s] * 0x101 ^ s * 0x1010100;
7134
7135                for (i = 0; i < 4; i++) {
7136                    encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
7137                    decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
7138                }
7139            }
7140
7141            // Compactify. Considerable speedup on Firefox.
7142            for (i = 0; i < 5; i++) {
7143                encTable[i] = encTable[i].slice(0);
7144                decTable[i] = decTable[i].slice(0);
7145            }
7146            return tables;
7147        };
7148        var aesTables = null;
7149
7150        /**
7151         * Schedule out an AES key for both encryption and decryption. This
7152         * is a low-level class. Use a cipher mode to do bulk encryption.
7153         *
7154         * @class AES
7155         * @param key {Array} The key as an array of 4, 6 or 8 words.
7156         */
7157
7158        var AES = (function () {
7159            function AES(key) {
7160                _classCallCheck(this, AES);
7161
7162                /**
7163                 * The expanded S-box and inverse S-box tables. These will be computed
7164                 * on the client so that we don't have to send them down the wire.
7165                 *
7166                 * There are two tables, _tables[0] is for encryption and
7167                 * _tables[1] is for decryption.
7168                 *
7169                 * The first 4 sub-tables are the expanded S-box with MixColumns. The
7170                 * last (_tables[01][4]) is the S-box itself.
7171                 *
7172                 * @private
7173                 */
7174                // if we have yet to precompute the S-box tables
7175                // do so now
7176                if (!aesTables) {
7177                    aesTables = precompute();
7178                }
7179                // then make a copy of that object for use
7180                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()]];
7181                var i = undefined;
7182                var j = undefined;
7183                var tmp = undefined;
7184                var encKey = undefined;
7185                var decKey = undefined;
7186                var sbox = this._tables[0][4];
7187                var decTable = this._tables[1];
7188                var keyLen = key.length;
7189                var rcon = 1;
7190
7191                if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
7192                    throw new Error('Invalid aes key size');
7193                }
7194
7195                encKey = key.slice(0);
7196                decKey = [];
7197                this._key = [encKey, decKey];
7198
7199                // schedule encryption keys
7200                for (i = keyLen; i < 4 * keyLen + 28; i++) {
7201                    tmp = encKey[i - 1];
7202
7203                    // apply sbox
7204                    if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
7205                        tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];
7206
7207                        // shift rows and add rcon
7208                        if (i % keyLen === 0) {
7209                            tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
7210                            rcon = rcon << 1 ^ (rcon >> 7) * 283;
7211                        }
7212                    }
7213
7214                    encKey[i] = encKey[i - keyLen] ^ tmp;
7215                }
7216
7217                // schedule decryption keys
7218                for (j = 0; i; j++, i--) {
7219                    tmp = encKey[j & 3 ? i : i - 4];
7220                    if (i <= 4 || j < 4) {
7221                        decKey[j] = tmp;
7222                    } else {
7223                        decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
7224                    }
7225                }
7226            }
7227
7228            /**
7229             * Decrypt 16 bytes, specified as four 32-bit words.
7230             *
7231             * @param {Number} encrypted0 the first word to decrypt
7232             * @param {Number} encrypted1 the second word to decrypt
7233             * @param {Number} encrypted2 the third word to decrypt
7234             * @param {Number} encrypted3 the fourth word to decrypt
7235             * @param {Int32Array} out the array to write the decrypted words
7236             * into
7237             * @param {Number} offset the offset into the output array to start
7238             * writing results
7239             * @return {Array} The plaintext.
7240             */
7241
7242            _createClass(AES, [{
7243                key: 'decrypt',
7244                value: function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
7245                    var key = this._key[1];
7246                    // state variables a,b,c,d are loaded with pre-whitened data
7247                    var a = encrypted0 ^ key[0];
7248                    var b = encrypted3 ^ key[1];
7249                    var c = encrypted2 ^ key[2];
7250                    var d = encrypted1 ^ key[3];
7251                    var a2 = undefined;
7252                    var b2 = undefined;
7253                    var c2 = undefined;
7254
7255                    // key.length === 2 ?
7256                    var nInnerRounds = key.length / 4 - 2;
7257                    var i = undefined;
7258                    var kIndex = 4;
7259                    var table = this._tables[1];
7260
7261                    // load up the tables
7262                    var table0 = table[0];
7263                    var table1 = table[1];
7264                    var table2 = table[2];
7265                    var table3 = table[3];
7266                    var sbox = table[4];
7267
7268                    // Inner rounds. Cribbed from OpenSSL.
7269                    for (i = 0; i < nInnerRounds; i++) {
7270                        a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
7271                        b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
7272                        c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
7273                        d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
7274                        kIndex += 4;
7275                        a = a2;b = b2;c = c2;
7276                    }
7277
7278                    // Last round.
7279                    for (i = 0; i < 4; i++) {
7280                        out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
7281                        a2 = a;a = b;b = c;c = d;d = a2;
7282                    }
7283                }
7284            }]);
7285
7286            return AES;
7287        })();
7288
7289        exports['default'] = AES;
7290        module.exports = exports['default'];
7291    },{}],21:[function(require,module,exports){
7292        /**
7293         * @file async-stream.js
7294         */
7295        'use strict';
7296
7297        Object.defineProperty(exports, '__esModule', {
7298            value: true
7299        });
7300
7301        var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
7302
7303        var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
7304
7305        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7306
7307        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
7308
7309        function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
7309onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
7310
7311        var _stream = require('./stream');
7312
7313        var _stream2 = _interopRequireDefault(_stream);
7314
7315        /**
7316         * A wrapper around the Stream class to use setTiemout
7317         * and run stream "jobs" Asynchronously
7318         *
7319         * @class AsyncStream
7320         * @extends Stream
7321         */
7322
7323        var AsyncStream = (function (_Stream) {
7324            _inherits(AsyncStream, _Stream);
7325
7326            function AsyncStream() {
7327                _classCallCheck(this, AsyncStream);
7328
7329                _get(Object.getPrototypeOf(AsyncStream.prototype), 'constructor', this).call(this, _stream2['default']);
7330                this.jobs = [];
7331                this.delay = 1;
7332                this.timeout_ = null;
7333            }
7334
7335            /**
7336             * process an async job
7337             *
7338             * @private
7339             */
7340
7341            _createClass(AsyncStream, [{
7342                key: 'processJob_',
7343                value: function processJob_() {
7344                    this.jobs.shift()();
7345                    if (this.jobs.length) {
7346                        this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
7347                    } else {
7348                        this.timeout_ = null;
7349                    }
7350                }
7351
7352                /**
7353                 * push a job into the stream
7354                 *
7355                 * @param {Function} job the job to push into the stream
7356                 */
7357            }, {
7358                key: 'push',
7359                value: function push(job) {
7360                    this.jobs.push(job);
7361                    if (!this.timeout_) {
7362                        this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
7363                    }
7364                }
7365            }]);
7366
7367            return AsyncStream;
7368        })(_stream2['default']);
7369
7370        exports['default'] = AsyncStream;
7371        module.exports = exports['default'];
7372    },{"./stream":24}],22:[function(require,module,exports){
7373        /**
7374         * @file decrypter.js
7375         *
7376         * An asynchronous implementation of AES-128 CBC decryption with
7377         * PKCS#7 padding.
7378         */
7379
7380        'use strict';
7381
7382        Object.defineProperty(exports, '__esModule', {
7383            value: true
7384        });
7385
7386        var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
7387
7388        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7389
7390        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
7391
7392        var _aes = require('./aes');
7393
7394        var _aes2 = _interopRequireDefault(_aes);
7395
7396        var _asyncStream = require('./async-stream');
7397
7398        var _asyncStream2 = _interopRequireDefault(_asyncStream);
7399
7400        var _pkcs7 = require('pkcs7');
7401
7402        /**
7403         * Convert network-order (big-endian) bytes into their little-endian
7404         * representation.
7405         */
7406        var ntoh = function ntoh(word) {
7407            return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
7408        };
7409
7410        /**
7411         * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
7412         *
7413         * @param {Uint8Array} encrypted the encrypted bytes
7414         * @param {Uint32Array} key the bytes of the decryption key
7415         * @param {Uint32Array} initVector the initialization vector (IV) to
7416         * use for the first round of CBC.
7417         * @return {Uint8Array} the decrypted bytes
7418         *
7419         * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
7420         * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
7421         * @see https://tools.ietf.org/html/rfc2315
7422         */
7423        var decrypt = function decrypt(encrypted, key, initVector) {
7424            // word-level access to the encrypted bytes
7425            var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
7426
7427            var decipher = new _aes2['default'](Array.prototype.slice.call(key));
7428
7429            // byte and word-level access for the decrypted output
7430            var decrypted = new Uint8Array(encrypted.byteLength);
7431            var decrypted32 = new Int32Array(decrypted.buffer);
7432
7433            // temporary variables for working with the IV, encrypted, and
7434            // decrypted data
7435            var init0 = undefined;
7436            var init1 = undefined;
7437            var init2 = undefined;
7438            var init3 = undefined;
7439            var encrypted0 = undefined;
7440            var encrypted1 = undefined;
7441            var encrypted2 = undefined;
7442            var encrypted3 = undefined;
7443
7444            // iteration variable
7445            var wordIx = undefined;
7446
7447            // pull out the words of the IV to ensure we don't modify the
7448            // passed-in reference and easier access
7449            init0 = initVector[0];
7450            init1 = initVector[1];
7451            init2 = initVector[2];
7452            init3 = initVector[3];
7453
7454            // decrypt four word sequences, applying cipher-block chaining (CBC)
7455            // to each decrypted block
7456            for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
7457                // convert big-endian (network order) words into little-endian
7458                // (javascript order)
7459                encrypted0 = ntoh(encrypted32[wordIx]);
7460                encrypted1 = ntoh(encrypted32[wordIx + 1]);
7461                encrypted2 = ntoh(encrypted32[wordIx + 2]);
7462                encrypted3 = ntoh(encrypted32[wordIx + 3]);
7463
7464                // decrypt the block
7465                decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx);
7466
7467                // XOR with the IV, and restore network byte-order to obtain the
7468                // plaintext
7469                decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
7470                decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
7471                decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
7472                decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
7473
7474                // setup the IV for the next round
7475                init0 = encrypted0;
7476                init1 = encrypted1;
7477                init2 = encrypted2;
7478                init3 = encrypted3;
7479            }
7480
7481            return decrypted;
7482        };
7483
7484        exports.decrypt = decrypt;
7485        /**
7486         * The `Decrypter` class that manages decryption of AES
7487         * data through `AsyncStream` objects and the `decrypt`
7488         * function
7489         *
7490         * @param {Uint8Array} encrypted the encrypted bytes
7491         * @param {Uint32Array} key the bytes of the decryption key
7492         * @param {Uint32Array} initVector the initialization vector (IV) to
7493         * @param {Function} done the function to run when done
7494         * @class Decrypter
7495         */
7496
7497        var Decrypter = (function () {
7498            function Decrypter(encrypted, key, initVector, done) {
7499                _classCallCheck(this, Decrypter);
7500
7501                var step = Decrypter.STEP;
7502                var encrypted32 = new Int32Array(encrypted.buffer);
7503                var decrypted = new Uint8Array(encrypted.byteLength);
7504                var i = 0;
7505
7506                this.asyncStream_ = new _asyncStream2['default']();
7507
7508                // split up the encryption job and do the individual chunks asynchronously
7509                this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
7510                for (i = step; i < encrypted32.length; i += step) {
7511                    initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
7512                    this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
7513                }
7514                // invoke the done() callback when everything is finished
7515                this.asyncStream_.push(function () {
7516                    // remove pkcs#7 padding from the decrypted bytes
7517                    done(null, (0, _pkcs7.unpad)(decrypted));
7518                });
7519            }
7520
7521            /**
7522             * a getter for step the maximum number of bytes to process at one time
7523             *
7524             * @return {Number} the value of step 32000
7525             */
7526
7527            _createClass(Decrypter, [{
7528                key: 'decryptChunk_',
7529
7530                /**
7531                 * @private
7532                 */
7533                value: function decryptChunk_(encrypted, key, initVector, decrypted) {
7534                    return function () {
7535                        var bytes = decrypt(encrypted, key, initVector);
7536
7537                        decrypted.set(bytes, encrypted.byteOffset);
7538                    };
7539                }
7540            }], [{
7541                key: 'STEP',
7542                get: function get() {
7543                    // 4 * 8000;
7544                    return 32000;
7545                }
7546            }]);
7547
7548            return Decrypter;
7549        })();
7550
7551        exports.Decrypter = Decrypter;
7552        exports['default'] = {
7553            Decrypter: Decrypter,
7554            decrypt: decrypt
7555        };
7556    },{"./aes":20,"./async-stream":21,"pkcs7":26}],23:[function(require,module,exports){
7557        /**
7558         * @file index.js
7559         *
7560         * Index module to easily import the primary components of AES-128
7561         * decryption. Like this:
7562         *
7563         * ```js
7564         * import {Decrypter, decrypt, AsyncStream} from 'aes-decrypter';
7565         * ```
7566         */
7567        'use strict';
7568
7569        Object.defineProperty(exports, '__esModule', {
7570            value: true
7571        });
7572
7573        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7574
7575        var _decrypter = require('./decrypter');
7576
7577        var _asyncStream = require('./async-stream');
7578
7579        var _asyncStream2 = _interopRequireDefault(_asyncStream);
7580
7581        exports['default'] = {
7582            decrypt: _decrypter.decrypt,
7583            Decrypter: _decrypter.Decrypter,
7584            AsyncStream: _asyncStream2['default']
7585        };
7586        module.exports = exports['default'];
7587    },{"./async-stream":21,"./decrypter":22}],24:[function(require,module,exports){
7588        arguments[4][16][0].apply(exports,arguments)
7589    },{"dup":16}],25:[function(require,module,exports){
7590        /*
7591 * pkcs7.pad
7592 * https://github.com/brightcove/pkcs7
7593 *
7594 * Copyright (c) 2014 Brightcove
7595 * Licensed under the apache2 license.
7596 */
7597
7598        'use strict';
7599
7600        var PADDING;
7601
7602        /**
7603         * Returns a new Uint8Array that is padded with PKCS#7 padding.
7604         * @param plaintext {Uint8Array} the input bytes before encryption
7605         * @return {Uint8Array} the padded bytes
7606         * @see http://tools.ietf.org/html/rfc5652
7607         */
7608        module.exports = function pad(plaintext) {
7609            var padding = PADDING[(plaintext.byteLength % 16) || 0],
7610                result = new Uint8Array(plaintext.byteLength + padding.length);
7611            result.set(plaintext);
7612            result.set(padding, plaintext.byteLength);
7613            return result;
7614        };
7615
7616// pre-define the padding values
7617        PADDING = [
7618            [16, 16, 16, 16,
7619                16, 16, 16, 16,
7620                16, 16, 16, 16,
7621                16, 16, 16, 16],
7622
7623            [15, 15, 15, 15,
7624                15, 15, 15, 15,
7625                15, 15, 15, 15,
7626                15, 15, 15],
7627
7628            [14, 14, 14, 14,
7629                14, 14, 14, 14,
7630                14, 14, 14, 14,
7631                14, 14],
7632
7633            [13, 13, 13, 13,
7634                13, 13, 13, 13,
7635                13, 13, 13, 13,
7636                13],
7637
7638            [12, 12, 12, 12,
7639                12, 12, 12, 12,
7640                12, 12, 12, 12],
7641
7642            [11, 11, 11, 11,
7643                11, 11, 11, 11,
7644                11, 11, 11],
7645
7646            [10, 10, 10, 10,
7647                10, 10, 10, 10,
7648                10, 10],
7649
7650            [9, 9, 9, 9,
7651                9, 9, 9, 9,
7652                9],
7653
7654            [8, 8, 8, 8,
7655                8, 8, 8, 8],
7656
7657            [7, 7, 7, 7,
7658                7, 7, 7],
7659
7660            [6, 6, 6, 6,
7661                6, 6],
7662
7663            [5, 5, 5, 5,
7664                5],
7665
7666            [4, 4, 4, 4],
7667
7668            [3, 3, 3],
7669
7670            [2, 2],
7671
7672            [1]
7673        ];
7674
7675    },{}],26:[function(require,module,exports){
7676        /*
7677 * pkcs7
7678 * https://github.com/brightcove/pkcs7
7679 *
7680 * Copyright (c) 2014 Brightcove
7681 * Licensed under the apache2 license.
7682 */
7683
7684        'use strict';
7685
7686        exports.pad = require('./pad.js');
7687        exports.unpad = require('./unpad.js');
7688
7689    },{"./pad.js":25,"./unpad.js":27}],27:[function(require,module,exports){
7690        /*
7691 * pkcs7.unpad
7692 * https://github.com/brightcove/pkcs7
7693 *
7694 * Copyright (c) 2014 Brightcove
7695 * Licensed under the apache2 license.
7696 */
7697
7698        'use strict';
7699
7700        /**
7701         * Returns the subarray of a Uint8Array without PKCS#7 padding.
7702         * @param padded {Uint8Array} unencrypted bytes that have been padded
7703         * @return {Uint8Array} the unpadded bytes
7704         * @see http://tools.ietf.org/html/rfc5652
7705         */
7706        module.exports = function unpad(padded) {
7707            return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
7708        };
7709
7710    },{}],28:[function(require,module,exports){
7711
7712    },{}],29:[function(require,module,exports){
7713        (function (global){
7714            var topLevel = typeof global !== 'undefined' ? global :
7715                typeof window !== 'undefined' ? window : {}
7716            var minDoc = require('min-document');
7717
7718            var doccy;
7719
7720            if (typeof document !== 'undefined') {
7721                doccy = document;
7722            } else {
7723                doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
7724
7725                if (!doccy) {
7726                    doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
7727                }
7728            }
7729
7730            module.exports = doccy;
7731
7732        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
7733    },{"min-document":28}],30:[function(require,module,exports){
7734        (function (global){
7735            var win;
7736
7737            if (typeof window !== "undefined") {
7738                win = window;
7739            } else if (typeof global !== "undefined") {
7740                win = global;
7741            } else if (typeof self !== "undefined"){
7742                win = self;
7743            } else {
7744                win = {};
7745            }
7746
7747            module.exports = win;
7748
7749        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
7750    },{}],31:[function(require,module,exports){
7751        'use strict';
7752
7753        var _lineStream = require('./line-stream');
7754
7755        var _lineStream2 = _interopRequireDefault(_lineStream);
7756
7757        var _parseStream = require('./parse-stream');
7758
7759        var _parseStream2 = _interopRequireDefault(_parseStream);
7760
7761        var _parser = require('./parser');
7762
7763        var _parser2 = _interopRequireDefault(_parser);
7764
7765        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7766
7767        module.exports = {
7768            LineStream: _lineStream2['default'],
7769            ParseStream: _parseStream2['default'],
7770            Parser: _parser2['default']
7771        }; /**
7772         * @file m3u8/index.js
7773         *
7774         * Utilities for parsing M3U8 files. If the entire manifest is available,
7775         * `Parser` will create an object representation with enough detail for managing
7776         * playback. `ParseStream` and `LineStream` are lower-level parsing primitives
7777         * that do not assume the entirety of the manifest is ready and expose a
7778         * ReadableStream-like interface.
7779         */
7780    },{"./line-stream":32,"./parse-stream":33,"./parser":34}],32:[function(require,module,exports){
7781        'use strict';
7782
7783        Object.defineProperty(exports, "__esModule", {
7784            value: true
7785        });
7786
7787        var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
7788
7789        var _stream = require('./stream');
7790
7791        var _stream2 = _interopRequireDefault(_stream);
7792
7793        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7794
7795        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
7796
7797        function _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; }
7798
7799        function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
7800         * @file m3u8/line-stream.js
7801         */
7802
7803
7804        /**
7805         * A stream that buffers string input and generates a `data` event for each
7806         * line.
7807         *
7808         * @class LineStream
7809         * @extends Stream
7810         */
7811        var LineStream = function (_Stream) {
7812            _inherits(LineStream, _Stream);
7813
7814            function LineStream() {
7815                _classCallCheck(this, LineStream);
7816
7817                var _this = _possibleConstructorReturn(this, (LineStream.__proto__ || Object.getPrototypeOf(LineStream)).call(this));
7818
7819                _this.buffer = '';
7820                return _this;
7821            }
7822
7823            /**
7824             * Add new data to be parsed.
7825             *
7826             * @param {String} data the text to process
7827             */
7828
7829
7830            _createClass(LineStream, [{
7831                key: 'push',
7832                value: function push(data) {
7833                    var nextNewline = void 0;
7834
7835                    this.buffer += data;
7836                    nextNewline = this.buffer.indexOf('\n');
7837
7838                    for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) {
7839                        this.trigger('data', this.buffer.substring(0, nextNewline));
7840                        this.buffer = this.buffer.substring(nextNewline + 1);
7841                    }
7842                }
7843            }]);
7844
7845            return LineStream;
7846        }(_stream2['default']);
7847
7848        exports['default'] = LineStream;
7849    },{"./stream":35}],33:[function(require,module,exports){
7850        'use strict';
7851
7852        Object.defineProperty(exports, "__esModule", {
7853            value: true
7854        });
7855
7856        var _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); }
7856 else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
7857
7858        var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
7859
7860        var _stream = require('./stream');
7861
7862        var _stream2 = _interopRequireDefault(_stream);
7863
7864        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7865
7866        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
7867
7868        function _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; }
7869
7870        function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
7871         * @file m3u8/parse-stream.js
7872         */
7873
7874
7875        /**
7876         * "forgiving" attribute list psuedo-grammar:
7877         * attributes -> keyvalue (',' keyvalue)*
7878         * keyvalue   -> key '=' value
7879         * key        -> [^=]*
7880         * value      -> '"' [^"]* '"' | [^,]*
7881         */
7882        var attributeSeparator = function attributeSeparator() {
7883            var key = '[^=]*';
7884            var value = '"[^"]*"|[^,]*';
7885            var keyvalue = '(?:' + key + ')=(?:' + value + ')';
7886
7887            return new RegExp('(?:^|,)(' + keyvalue + ')');
7888        };
7889
7890        /**
7891         * Parse attributes from a line given the seperator
7892         *
7893         * @param {String} attributes the attibute line to parse
7894         */
7895        var parseAttributes = function parseAttributes(attributes) {
7896            // split the string using attributes as the separator
7897            var attrs = attributes.split(attributeSeparator());
7898            var result = {};
7899            var i = attrs.length;
7900            var attr = void 0;
7901
7902            while (i--) {
7903                // filter out unmatched portions of the string
7904                if (attrs[i] === '') {
7905                    continue;
7906                }
7907
7908                // split the key and value
7909                attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1);
7910                // trim whitespace and remove optional quotes around the value
7911                attr[0] = attr[0].replace(/^\s+|\s+$/g, '');
7912                attr[1] = attr[1].replace(/^\s+|\s+$/g, '');
7913                attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1');
7914                result[attr[0]] = attr[1];
7915            }
7916            return result;
7917        };
7918
7919        /**
7920         * A line-level M3U8 parser event stream. It expects to receive input one
7921         * line at a time and performs a context-free parse of its contents. A stream
7922         * interpretation of a manifest can be useful if the manifest is expected to
7923         * be too large to fit comfortably into memory or the entirety of the input
7924         * is not immediately available. Otherwise, it's probably much easier to work
7925         * with a regular `Parser` object.
7926         *
7927         * Produces `data` events with an object that captures the parser's
7928         * interpretation of the input. That object has a property `tag` that is one
7929         * of `uri`, `comment`, or `tag`. URIs only have a single additional
7930         * property, `line`, which captures the entirety of the input without
7931         * interpretation. Comments similarly have a single additional property
7932         * `text` which is the input without the leading `#`.
7933         *
7934         * Tags always have a property `tagType` which is the lower-cased version of
7935         * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
7936         * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
7937         * tags are given the tag type `unknown` and a single additional property
7938         * `data` with the remainder of the input.
7939         *
7940         * @class ParseStream
7941         * @extends Stream
7942         */
7943
vendor: 32,768 bytes, lines 7944-8612
7944        var ParseStream = function (_Stream) {
7945            _inherits(ParseStream, _Stream);
7946
7947            function ParseStream() {
7948                _classCallCheck(this, ParseStream);
7949
7950                return _possibleConstructorReturn(this, (ParseStream.__proto__ || Object.getPrototypeOf(ParseStream)).call(this));
7951            }
7952
7953            /**
7954             * Parses an additional line of input.
7955             *
7956             * @param {String} line a single line of an M3U8 file to parse
7957             */
7958
7959
7960            _createClass(ParseStream, [{
7961                key: 'push',
7962                value: function push(line) {
7963                    var match = void 0;
7964                    var event = void 0;
7965
7966                    // strip whitespace
7967                    line = line.replace(/^[\u0000\s]+|[\u0000\s]+$/g, '');
7968                    if (line.length === 0) {
7969                        // ignore empty lines
7970                        return;
7971                    }
7972
7973                    // URIs
7974                    if (line[0] !== '#') {
7975                        this.trigger('data', {
7976                            type: 'uri',
7977                            uri: line
7978                        });
7979                        return;
7980                    }
7981
7982                    // Comments
7983                    if (line.indexOf('#EXT') !== 0) {
7984                        this.trigger('data', {
7985                            type: 'comment',
7986                            text: line.slice(1)
7987                        });
7988                        return;
7989                    }
7990
7991                    // strip off any carriage returns here so the regex matching
7992                    // doesn't have to account for them.
7993                    line = line.replace('\r', '');
7994
7995                    // Tags
7996                    match = /^#EXTM3U/.exec(line);
7997                    if (match) {
7998                        this.trigger('data', {
7999                            type: 'tag',
8000                            tagType: 'm3u'
8001                        });
8002                        return;
8003                    }
8004                    match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(line);
8005                    if (match) {
8006                        event = {
8007                            type: 'tag',
8008                            tagType: 'inf'
8009                        };
8010                        if (match[1]) {
8011                            event.duration = parseFloat(match[1]);
8012                        }
8013                        if (match[2]) {
8014                            event.title = match[2];
8015                        }
8016                        this.trigger('data', event);
8017                        return;
8018                    }
8019                    match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(line);
8020                    if (match) {
8021                        event = {
8022                            type: 'tag',
8023                            tagType: 'targetduration'
8024                        };
8025                        if (match[1]) {
8026                            event.duration = parseInt(match[1], 10);
8027                        }
8028                        this.trigger('data', event);
8029                        return;
8030                    }
8031                    match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(line);
8032                    if (match) {
8033                        event = {
8034                            type: 'tag',
8035                            tagType: 'totalduration'
8036                        };
8037                        if (match[1]) {
8038                            event.duration = parseInt(match[1], 10);
8039                        }
8040                        this.trigger('data', event);
8041                        return;
8042                    }
8043                    match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(line);
8044                    if (match) {
8045                        event = {
8046                            type: 'tag',
8047                            tagType: 'version'
8048                        };
8049                        if (match[1]) {
8050                            event.version = parseInt(match[1], 10);
8051                        }
8052                        this.trigger('data', event);
8053                        return;
8054                    }
8055                    match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
8056                    if (match) {
8057                        event = {
8058                            type: 'tag',
8059                            tagType: 'media-sequence'
8060                        };
8061                        if (match[1]) {
8062                            event.number = parseInt(match[1], 10);
8063                        }
8064                        this.trigger('data', event);
8065                        return;
8066                    }
8067                    match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
8068                    if (match) {
8069                        event = {
8070                            type: 'tag',
8071                            tagType: 'discontinuity-sequence'
8072                        };
8073                        if (match[1]) {
8074                            event.number = parseInt(match[1], 10);
8075                        }
8076                        this.trigger('data', event);
8077                        return;
8078                    }
8079                    match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(line);
8080                    if (match) {
8081                        event = {
8082                            type: 'tag',
8083                            tagType: 'playlist-type'
8084                        };
8085                        if (match[1]) {
8086                            event.playlistType = match[1];
8087                        }
8088                        this.trigger('data', event);
8089                        return;
8090                    }
8091                    match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(line);
8092                    if (match) {
8093                        event = {
8094                            type: 'tag',
8095                            tagType: 'byterange'
8096                        };
8097                        if (match[1]) {
8098                            event.length = parseInt(match[1], 10);
8099                        }
8100                        if (match[2]) {
8101                            event.offset = parseInt(match[2], 10);
8102                        }
8103                        this.trigger('data', event);
8104                        return;
8105                    }
8106                    match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(line);
8107                    if (match) {
8108                        event = {
8109                            type: 'tag',
8110                            tagType: 'allow-cache'
8111                        };
8112                        if (match[1]) {
8113                            event.allowed = !/NO/.test(match[1]);
8114                        }
8115                        this.trigger('data', event);
8116                        return;
8117                    }
8118                    match = /^#EXT-X-MAP:?(.*)$/.exec(line);
8119                    if (match) {
8120                        event = {
8121                            type: 'tag',
8122                            tagType: 'map'
8123                        };
8124
8125                        if (match[1]) {
8126                            var attributes = parseAttributes(match[1]);
8127
8128                            if (attributes.URI) {
8129                                event.uri = attributes.URI;
8130                            }
8131                            if (attributes.BYTERANGE) {
8132                                var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'),
8133                                    _attributes$BYTERANGE2 = _slicedToArray(_attributes$BYTERANGE, 2),
8134                                    length = _attributes$BYTERANGE2[0],
8135                                    offset = _attributes$BYTERANGE2[1];
8136
8137                                event.byterange = {};
8138                                if (length) {
8139                                    event.byterange.length = parseInt(length, 10);
8140                                }
8141                                if (offset) {
8142                                    event.byterange.offset = parseInt(offset, 10);
8143                                }
8144                            }
8145                        }
8146
8147                        this.trigger('data', event);
8148                        return;
8149                    }
8150                    match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(line);
8151                    if (match) {
8152                        event = {
8153                            type: 'tag',
8154                            tagType: 'stream-inf'
8155                        };
8156                        if (match[1]) {
8157                            event.attributes = parseAttributes(match[1]);
8158
8159                            if (event.attributes.RESOLUTION) {
8160                                var split = event.attributes.RESOLUTION.split('x');
8161                                var resolution = {};
8162
8163                                if (split[0]) {
8164                                    resolution.width = parseInt(split[0], 10);
8165                                }
8166                                if (split[1]) {
8167                                    resolution.height = parseInt(split[1], 10);
8168                                }
8169                                event.attributes.RESOLUTION = resolution;
8170                            }
8171                            if (event.attributes.BANDWIDTH) {
8172                                event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
8173                            }
8174                            if (event.attributes['PROGRAM-ID']) {
8175                                event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
8176                            }
8177                        }
8178                        this.trigger('data', event);
8179                        return;
8180                    }
8181                    match = /^#EXT-X-MEDIA:?(.*)$/.exec(line);
8182                    if (match) {
8183                        event = {
8184                            type: 'tag',
8185                            tagType: 'media'
8186                        };
8187                        if (match[1]) {
8188                            event.attributes = parseAttributes(match[1]);
8189                        }
8190                        this.trigger('data', event);
8191                        return;
8192                    }
8193                    match = /^#EXT-X-ENDLIST/.exec(line);
8194                    if (match) {
8195                        this.trigger('data', {
8196                            type: 'tag',
8197                            tagType: 'endlist'
8198                        });
8199                        return;
8200                    }
8201                    match = /^#EXT-X-DISCONTINUITY/.exec(line);
8202                    if (match) {
8203                        this.trigger('data', {
8204                            type: 'tag',
8205                            tagType: 'discontinuity'
8206                        });
8207                        return;
8208                    }
8209                    match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(line);
8210                    if (match) {
8211                        event = {
8212                            type: 'tag',
8213                            tagType: 'program-date-time'
8214                        };
8215                        if (match[1]) {
8216                            event.dateTimeString = match[1];
8217                            event.dateTimeObject = new Date(match[1]);
8218                        }
8219                        this.trigger('data', event);
8220                        return;
8221                    }
8222                    match = /^#EXT-X-KEY:?(.*)$/.exec(line);
8223                    if (match) {
8224                        event = {
8225                            type: 'tag',
8226                            tagType: 'key'
8227                        };
8228                        if (match[1]) {
8229                            event.attributes = parseAttributes(match[1]);
8230                            // parse the IV string into a Uint32Array
8231                            if (event.attributes.IV) {
8232                                if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') {
8233                                    event.attributes.IV = event.attributes.IV.substring(2);
8234                                }
8235
8236                                event.attributes.IV = event.attributes.IV.match(/.{8}/g);
8237                                event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16);
8238                                event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16);
8239                                event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16);
8240                                event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16);
8241                                event.attributes.IV = new Uint32Array(event.attributes.IV);
8242                            }
8243                        }
8244                        this.trigger('data', event);
8245                        return;
8246                    }
8247                    match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(line);
8248                    if (match) {
8249                        event = {
8250                            type: 'tag',
8251                            tagType: 'cue-out-cont'
8252                        };
8253                        if (match[1]) {
8254                            event.data = match[1];
8255                        } else {
8256                            event.data = '';
8257                        }
8258                        this.trigger('data', event);
8259                        return;
8260                    }
8261                    match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(line);
8262                    if (match) {
8263                        event = {
8264                            type: 'tag',
8265                            tagType: 'cue-out'
8266                        };
8267                        if (match[1]) {
8268                            event.data = match[1];
8269                        } else {
8270                            event.data = '';
8271                        }
8272                        this.trigger('data', event);
8273                        return;
8274                    }
8275                    match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(line);
8276                    if (match) {
8277                        event = {
8278                            type: 'tag',
8279                            tagType: 'cue-in'
8280                        };
8281                        if (match[1]) {
8282                            event.data = match[1];
8283                        } else {
8284                            event.data = '';
8285                        }
8286                        this.trigger('data', event);
8287                        return;
8288                    }
8289
8290                    // unknown tag type
8291                    this.trigger('data', {
8292                        type: 'tag',
8293                        data: line.slice(4)
8294                    });
8295                }
8296            }]);
8297
8298            return ParseStream;
8299        }(_stream2['default']);
8300
8301        exports['default'] = ParseStream;
8302    },{"./stream":35}],34:[function(require,module,exports){
8303        'use strict';
8304
8305        Object.defineProperty(exports, "__esModule", {
8306            value: true
8307        });
8308
8309        var _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; };
8310
8311        var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8312
8313        var _stream = require('./stream');
8314
8315        var _stream2 = _interopRequireDefault(_stream);
8316
8317        var _lineStream = require('./line-stream');
8318
8319        var _lineStream2 = _interopRequireDefault(_lineStream);
8320
8321        var _parseStream = require('./parse-stream');
8322
8323        var _parseStream2 = _interopRequireDefault(_parseStream);
8324
8325        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8326
8327        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
8328
8329        function _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; }
8330
8331        function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
8332         * @file m3u8/parser.js
8333         */
8334
8335
8336        /**
8337         * A parser for M3U8 files. The current interpretation of the input is
8338         * exposed as a property `manifest` on parser objects. It's just two lines to
8339         * create and parse a manifest once you have the contents available as a string:
8340         *
8341         * ```js
8342         * var parser = new m3u8.Parser();
8343         * parser.push(xhr.responseText);
8344         * ```
8345         *
8346         * New input can later be applied to update the manifest object by calling
8347         * `push` again.
8348         *
8349         * The parser attempts to create a usable manifest object even if the
8350         * underlying input is somewhat nonsensical. It emits `info` and `warning`
8351         * events during the parse if it encounters input that seems invalid or
8352         * requires some property of the manifest object to be defaulted.
8353         *
8354         * @class Parser
8355         * @extends Stream
8356         */
8357        var Parser = function (_Stream) {
8358            _inherits(Parser, _Stream);
8359
8360            function Parser() {
8361                _classCallCheck(this, Parser);
8362
8363                var _this = _possibleConstructorReturn(this, (Parser.__proto__ || Object.getPrototypeOf(Parser)).call(this));
8364
8365                _this.lineStream = new _lineStream2['default']();
8366                _this.parseStream = new _parseStream2['default']();
8367                _this.lineStream.pipe(_this.parseStream);
8368                /* eslint-disable consistent-this */
8369                var self = _this;
8370                /* eslint-enable consistent-this */
8371                var uris = [];
8372                var currentUri = {};
8373                // if specified, the active EXT-X-MAP definition
8374                var currentMap = void 0;
8375                // if specified, the active decryption key
8376                var _key = void 0;
8377                var noop = function noop() {};
8378                var defaultMediaGroups = {
8379                    'AUDIO': {},
8380                    'VIDEO': {},
8381                    'CLOSED-CAPTIONS': {},
8382                    'SUBTITLES': {}
8383                };
8384                // group segments into numbered timelines delineated by discontinuities
8385                var currentTimeline = 0;
8386
8387                // the manifest is empty until the parse stream begins delivering data
8388                _this.manifest = {
8389                    allowCache: true,
8390                    discontinuityStarts: [],
8391                    segments: []
8392                };
8393
8394                // update the manifest with the m3u8 entry from the parse stream
8395                _this.parseStream.on('data', function (entry) {
8396                    var mediaGroup = void 0;
8397                    var rendition = void 0;
8398
8399                    ({
8400                        tag: function tag() {
8401                            // switch based on the tag type
8402                            (({
8403                                'allow-cache': function allowCache() {
8404                                    this.manifest.allowCache = entry.allowed;
8405                                    if (!('allowed' in entry)) {
8406                                        this.trigger('info', {
8407                                            message: 'defaulting allowCache to YES'
8408                                        });
8409                                        this.manifest.allowCache = true;
8410                                    }
8411                                },
8412                                byterange: function byterange() {
8413                                    var byterange = {};
8414
8415                                    if ('length' in entry) {
8416                                        currentUri.byterange = byterange;
8417                                        byterange.length = entry.length;
8418
8419                                        if (!('offset' in entry)) {
8420                                            this.trigger('info', {
8421                                                message: 'defaulting offset to zero'
8422                                            });
8423                                            entry.offset = 0;
8424                                        }
8425                                    }
8426                                    if ('offset' in entry) {
8427                                        currentUri.byterange = byterange;
8428                                        byterange.offset = entry.offset;
8429                                    }
8430                                },
8431                                endlist: function endlist() {
8432                                    this.manifest.endList = true;
8433                                },
8434                                inf: function inf() {
8435                                    if (!('mediaSequence' in this.manifest)) {
8436                                        this.manifest.mediaSequence = 0;
8437                                        this.trigger('info', {
8438                                            message: 'defaulting media sequence to zero'
8439                                        });
8440                                    }
8441                                    if (!('discontinuitySequence' in this.manifest)) {
8442                                        this.manifest.discontinuitySequence = 0;
8443                                        this.trigger('info', {
8444                                            message: 'defaulting discontinuity sequence to zero'
8445                                        });
8446                                    }
8447                                    if (entry.duration > 0) {
8448                                        currentUri.duration = entry.duration;
8449                                    }
8450
8451                                    if (entry.duration === 0) {
8452                                        currentUri.duration = 0.01;
8453                                        this.trigger('info', {
8454                                            message: 'updating zero segment duration to a small value'
8455                                        });
8456                                    }
8457
8458                                    this.manifest.segments = uris;
8459                                },
8460                                key: function key() {
8461                                    if (!entry.attributes) {
8462                                        this.trigger('warn', {
8463                                            message: 'ignoring key declaration without attribute list'
8464                                        });
8465                                        return;
8466                                    }
8467                                    // clear the active encryption key
8468                                    if (entry.attributes.METHOD === 'NONE') {
8469                                        _key = null;
8470                                        return;
8471                                    }
8472                                    if (!entry.attributes.URI) {
8473                                        this.trigger('warn', {
8474                                            message: 'ignoring key declaration without URI'
8475                                        });
8476                                        return;
8477                                    }
8478                                    if (!entry.attributes.METHOD) {
8479                                        this.trigger('warn', {
8480                                            message: 'defaulting key method to AES-128'
8481                                        });
8482                                    }
8483
8484                                    // setup an encryption key for upcoming segments
8485                                    _key = {
8486                                        method: entry.attributes.METHOD || 'AES-128',
8487                                        uri: entry.attributes.URI
8488                                    };
8489
8490                                    if (typeof entry.attributes.IV !== 'undefined') {
8491                                        _key.iv = entry.attributes.IV;
8492                                    }
8493                                },
8494                                'media-sequence': function mediaSequence() {
8495                                    if (!isFinite(entry.number)) {
8496                                        this.trigger('warn', {
8497                                            message: 'ignoring invalid media sequence: ' + entry.number
8498                                        });
8499                                        return;
8500                                    }
8501                                    this.manifest.mediaSequence = entry.number;
8502                                },
8503                                'discontinuity-sequence': function discontinuitySequence() {
8504                                    if (!isFinite(entry.number)) {
8505                                        this.trigger('warn', {
8506                                            message: 'ignoring invalid discontinuity sequence: ' + entry.number
8507                                        });
8508                                        return;
8509                                    }
8510                                    this.manifest.discontinuitySequence = entry.number;
8511                                    currentTimeline = entry.number;
8512                                },
8513                                'playlist-type': function playlistType() {
8514                                    if (!/VOD|EVENT/.test(entry.playlistType)) {
8515                                        this.trigger('warn', {
8516                                            message: 'ignoring unknown playlist type: ' + entry.playlist
8517                                        });
8518                                        return;
8519                                    }
8520                                    this.manifest.playlistType = entry.playlistType;
8521                                },
8522                                map: function map() {
8523                                    currentMap = {};
8524                                    if (entry.uri) {
8525                                        currentMap.uri = entry.uri;
8526                                    }
8527                                    if (entry.byterange) {
8528                                        currentMap.byterange = entry.byterange;
8529                                    }
8530                                },
8531                                'stream-inf': function streamInf() {
8532                                    this.manifest.playlists = uris;
8533                                    this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
8534
8535                                    if (!entry.attributes) {
8536                                        this.trigger('warn', {
8537                                            message: 'ignoring empty stream-inf attributes'
8538                                        });
8539                                        return;
8540                                    }
8541
8542                                    if (!currentUri.attributes) {
8543                                        currentUri.attributes = {};
8544                                    }
8545                                    _extends(currentUri.attributes, entry.attributes);
8546                                },
8547                                media: function media() {
8548                                    this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
8549
8550                                    if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) {
8551                                        this.trigger('warn', {
8552                                            message: 'ignoring incomplete or missing media group'
8553                                        });
8554                                        return;
8555                                    }
8556
8557                                    // find the media group, creating defaults as necessary
8558                                    var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE];
8559
8560                                    mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {};
8561                                    mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']];
8562
8563                                    // collect the rendition metadata
8564                                    rendition = {
8565                                        'default': /yes/i.test(entry.attributes.DEFAULT)
8566                                    };
8567                                    if (rendition['default']) {
8568                                        rendition.autoselect = true;
8569                                    } else {
8570                                        rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT);
8571                                    }
8572                                    if (entry.attributes.LANGUAGE) {
8573                                        rendition.language = entry.attributes.LANGUAGE;
8574                                    }
8575                                    if (entry.attributes.URI) {
8576                                        rendition.uri = entry.attributes.URI;
8577                                    }
8578                                    if (entry.attributes['INSTREAM-ID']) {
8579                                        rendition.instreamId = entry.attributes['INSTREAM-ID'];
8580                                    }
8581                                    if (entry.attributes.CHARACTERISTICS) {
8582                                        rendition.characteristics = entry.attributes.CHARACTERISTICS;
8583                                    }
8584                                    if (entry.attributes.FORCED) {
8585                                        rendition.forced = /yes/i.test(entry.attributes.FORCED);
8586                                    }
8587
8588                                    // insert the new rendition
8589                                    mediaGroup[entry.attributes.NAME] = rendition;
8590                                },
8591                                discontinuity: function discontinuity() {
8592                                    currentTimeline += 1;
8593                                    currentUri.discontinuity = true;
8594                                    this.manifest.discontinuityStarts.push(uris.length);
8595                                },
8596                                'program-date-time': function programDateTime() {
8597                                    this.manifest.dateTimeString = entry.dateTimeString;
8598                                    this.manifest.dateTimeObject = entry.dateTimeObject;
8599                                },
8600                                targetduration: function targetduration() {
8601                                    if (!isFinite(entry.duration) || entry.duration < 0) {
8602                                        this.trigger('warn', {
8603                                            message: 'ignoring invalid target duration: ' + entry.duration
8604                                        });
8605                                        return;
8606                                    }
8607                                    this.manifest.targetDuration = entry.duration;
8608                                },
8609                                totalduration: function totalduration() {
8610                                    if (!isFinite(entry.duration) || entry.duration < 0) {
8611                                        this.trigger('warn', {
8612                                            
8612message: 'ignoring invalid total duration: ' + entry.duration
8613                                        });
8614                                        return;
8615                                    }
8616                                    this.manifest.totalDuration = entry.duration;
8617                                },
8618                                'cue-out': function cueOut() {
8619                                    currentUri.cueOut = entry.data;
8620                                },
8621                                'cue-out-cont': function cueOutCont() {
8622                                    currentUri.cueOutCont = entry.data;
8623                                },
8624                                'cue-in': function cueIn() {
8625                                    currentUri.cueIn = entry.data;
8626                                }
8627                            })[entry.tagType] || noop).call(self);
8628                        },
8629                        uri: function uri() {
8630                            currentUri.uri = entry.uri;
8631                            uris.push(currentUri);
8632
8633                            // if no explicit duration was declared, use the target duration
8634                            if (this.manifest.targetDuration && !('duration' in currentUri)) {
8635                                this.trigger('warn', {
8636                                    message: 'defaulting segment duration to the target duration'
8637                                });
8638                                currentUri.duration = this.manifest.targetDuration;
8639                            }
8640                            // annotate with encryption information, if necessary
8641                            if (_key) {
8642                                currentUri.key = _key;
8643                            }
8644                            currentUri.timeline = currentTimeline;
8645                            // annotate with initialization segment information, if necessary
8646                            if (currentMap) {
8647                                currentUri.map = currentMap;
8648                            }
8649
8650                            // prepare for the next URI
8651                            currentUri = {};
8652                        },
8653                        comment: function comment() {
8654                            // comments are not important for playback
8655                        }
8656                    })[entry.type].call(self);
8657                });
8658
8659                return _this;
8660            }
8661
8662            /**
8663             * Parse the input string and update the manifest object.
8664             *
8665             * @param {String} chunk a potentially incomplete portion of the manifest
8666             */
8667
8668
8669            _createClass(Parser, [{
8670                key: 'push',
8671                value: function push(chunk) {
8672                    this.lineStream.push(chunk);
8673                }
8674
8675                /**
8676                 * Flush any remaining input. This can be handy if the last line of an M3U8
8677                 * manifest did not contain a trailing newline but the file has been
8678                 * completely received.
8679                 */
8680
8681            }, {
8682                key: 'end',
8683                value: function end() {
8684                    // flush any buffered input
8685                    this.lineStream.push('\n');
8686                }
8687            }]);
8688
8689            return Parser;
8690        }(_stream2['default']);
8691
8692        exports['default'] = Parser;
8693    },{"./line-stream":32,"./parse-stream":33,"./stream":35}],35:[function(require,module,exports){
8694        'use strict';
8695
8696        Object.defineProperty(exports, "__esModule", {
8697            value: true
8698        });
8699
8700        var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8701
8702        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
8703
8704        /**
8705         * @file stream.js
8706         */
8707        /**
8708         * A lightweight readable stream implemention that handles event dispatching.
8709         *
8710         * @class Stream
8711         */
8712        var Stream = function () {
8713            function Stream() {
8714                _classCallCheck(this, Stream);
8715
8716                this.listeners = {};
8717            }
8718
8719            /**
8720             * Add a listener for a specified event type.
8721             *
8722             * @param {String} type the event name
8723             * @param {Function} listener the callback to be invoked when an event of
8724             * the specified type occurs
8725             */
8726
8727
8728            _createClass(Stream, [{
8729                key: 'on',
8730                value: function on(type, listener) {
8731                    if (!this.listeners[type]) {
8732                        this.listeners[type] = [];
8733                    }
8734                    this.listeners[type].push(listener);
8735                }
8736
8737                /**
8738                 * Remove a listener for a specified event type.
8739                 *
8740                 * @param {String} type the event name
8741                 * @param {Function} listener  a function previously registered for this
8742                 * type of event through `on`
8743                 * @return {Boolean} if we could turn it off or not
8744                 */
8745
8746            }, {
8747                key: 'off',
8748                value: function off(type, listener) {
8749                    if (!this.listeners[type]) {
8750                        return false;
vendor: 40,732 bytes, lines 8751-9764
8751                    }
8752
8753                    var index = this.listeners[type].indexOf(listener);
8754
8755                    this.listeners[type].splice(index, 1);
8756                    return index > -1;
8757                }
8758
8759                /**
8760                 * Trigger an event of the specified type on this stream. Any additional
8761                 * arguments to this function are passed as parameters to event listeners.
8762                 *
8763                 * @param {String} type the event name
8764                 */
8765
8766            }, {
8767                key: 'trigger',
8768                value: function trigger(type) {
8769                    var callbacks = this.listeners[type];
8770                    var i = void 0;
8771                    var length = void 0;
8772                    var args = void 0;
8773
8774                    if (!callbacks) {
8775                        return;
8776                    }
8777                    // Slicing the arguments on every invocation of this method
8778                    // can add a significant amount of overhead. Avoid the
8779                    // intermediate object creation for the common case of a
8780                    // single callback argument
8781                    if (arguments.length === 2) {
8782                        length = callbacks.length;
8783                        for (i = 0; i < length; ++i) {
8784                            callbacks[i].call(this, arguments[1]);
8785                        }
8786                    } else {
8787                        args = Array.prototype.slice.call(arguments, 1);
8788                        length = callbacks.length;
8789                        for (i = 0; i < length; ++i) {
8790                            callbacks[i].apply(this, args);
8791                        }
8792                    }
8793                }
8794
8795                /**
8796                 * Destroys the stream and cleans up.
8797                 */
8798
8799            }, {
8800                key: 'dispose',
8801                value: function dispose() {
8802                    this.listeners = {};
8803                }
8804                /**
8805                 * Forwards all `data` events on this stream to the destination stream. The
8806                 * destination stream should provide a method `push` to receive the data
8807                 * events as they arrive.
8808                 *
8809                 * @param {Stream} destination the stream that will receive all `data` events
8810                 * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
8811                 */
8812
8813            }, {
8814                key: 'pipe',
8815                value: function pipe(destination) {
8816                    this.on('data', function (data) {
8817                        destination.push(data);
8818                    });
8819                }
8820            }]);
8821
8822            return Stream;
8823        }();
8824
8825        exports['default'] = Stream;
8826    },{}],36:[function(require,module,exports){
8827        /**
8828         * mux.js
8829         *
8830         * Copyright (c) 2016 Brightcove
8831         * All rights reserved.
8832         *
8833         * A stream-based aac to mp4 converter. This utility can be used to
8834         * deliver mp4s to a SourceBuffer on platforms that support native
8835         * Media Source Extensions.
8836         */
8837        'use strict';
8838        var Stream = require('../utils/stream.js');
8839
8840// Constants
8841        var AacStream;
8842
8843        /**
8844         * Splits an incoming stream of binary data into ADTS and ID3 Frames.
8845         */
8846
8847        AacStream = function() {
8848            var
8849                everything = new Uint8Array(),
8850                timeStamp = 0;
8851
8852            AacStream.prototype.init.call(this);
8853
8854            this.setTimestamp = function(timestamp) {
8855                timeStamp = timestamp;
8856            };
8857
8858            this.parseId3TagSize = function(header, byteIndex) {
8859                var
8860                    returnSize = (header[byteIndex + 6] << 21) |
8861                        (header[byteIndex + 7] << 14) |
8862                        (header[byteIndex + 8] << 7) |
8863                        (header[byteIndex + 9]),
8864                    flags = header[byteIndex + 5],
8865                    footerPresent = (flags & 16) >> 4;
8866
8867                if (footerPresent) {
8868                    return returnSize + 20;
8869                }
8870                return returnSize + 10;
8871            };
8872
8873            this.parseAdtsSize = function(header, byteIndex) {
8874                var
8875                    lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
8876                    middle = header[byteIndex + 4] << 3,
8877                    highTwo = header[byteIndex + 3] & 0x3 << 11;
8878
8879                return (highTwo | middle) | lowThree;
8880            };
8881
8882            this.push = function(bytes) {
8883                var
8884                    frameSize = 0,
8885                    byteIndex = 0,
8886                    bytesLeft,
8887                    chunk,
8888                    packet,
8889                    tempLength;
8890
8891                // If there are bytes remaining from the last segment, prepend them to the
8892                // bytes that were pushed in
8893                if (everything.length) {
8894                    tempLength = everything.length;
8895                    everything = new Uint8Array(bytes.byteLength + tempLength);
8896                    everything.set(everything.subarray(0, tempLength));
8897                    everything.set(bytes, tempLength);
8898                } else {
8899                    everything = bytes;
8900                }
8901
8902                while (everything.length - byteIndex >= 3) {
8903                    if ((everything[byteIndex] === 'I'.charCodeAt(0)) &&
8904                        (everything[byteIndex + 1] === 'D'.charCodeAt(0)) &&
8905                        (everything[byteIndex + 2] === '3'.charCodeAt(0))) {
8906
8907                        // Exit early because we don't have enough to parse
8908                        // the ID3 tag header
8909                        if (everything.length - byteIndex < 10) {
8910                            break;
8911                        }
8912
8913                        // check framesize
8914                        frameSize = this.parseId3TagSize(everything, byteIndex);
8915
8916                        // Exit early if we don't have enough in the buffer
8917                        // to emit a full packet
8918                        if (frameSize > everything.length) {
8919                            break;
8920                        }
8921                        chunk = {
8922                            type: 'timed-metadata',
8923                            data: everything.subarray(byteIndex, byteIndex + frameSize)
8924                        };
8925                        this.trigger('data', chunk);
8926                        byteIndex += frameSize;
8927                        continue;
8928                    } else if ((everything[byteIndex] & 0xff === 0xff) &&
8929                        ((everything[byteIndex + 1] & 0xf0) === 0xf0)) {
8930
8931                        // Exit early because we don't have enough to parse
8932                        // the ADTS frame header
8933                        if (everything.length - byteIndex < 7) {
8934                            break;
8935                        }
8936
8937                        frameSize = this.parseAdtsSize(everything, byteIndex);
8938
8939                        // Exit early if we don't have enough in the buffer
8940                        // to emit a full packet
8941                        if (frameSize > everything.length) {
8942                            break;
8943                        }
8944
8945                        packet = {
8946                            type: 'audio',
8947                            data: everything.subarray(byteIndex, byteIndex + frameSize),
8948                            pts: timeStamp,
8949                            dts: timeStamp
8950                        };
8951                        this.trigger('data', packet);
8952                        byteIndex += frameSize;
8953                        continue;
8954                    }
8955                    byteIndex++;
8956                }
8957                bytesLeft = everything.length - byteIndex;
8958
8959                if (bytesLeft > 0) {
8960                    everything = everything.subarray(byteIndex);
8961                } else {
8962                    everything = new Uint8Array();
8963                }
8964            };
8965        };
8966
8967        AacStream.prototype = new Stream();
8968
8969        module.exports = AacStream;
8970
8971    },{"../utils/stream.js":60}],37:[function(require,module,exports){
8972        /**
8973         * mux.js
8974         *
8975         * Copyright (c) 2016 Brightcove
8976         * All rights reserved.
8977         *
8978         * Utilities to detect basic properties and metadata about Aac data.
8979         */
8980        'use strict';
8981
8982        var ADTS_SAMPLING_FREQUENCIES = [
8983            96000,
8984            88200,
8985            64000,
8986            48000,
8987            44100,
8988            32000,
8989            24000,
8990            22050,
8991            16000,
8992            12000,
8993            11025,
8994            8000,
8995            7350
8996        ];
8997
8998        var parseSyncSafeInteger = function(data) {
8999            return (data[0] << 21) |
9000                (data[1] << 14) |
9001                (data[2] << 7) |
9002                (data[3]);
9003        };
9004
9005// return a percent-encoded representation of the specified byte range
9006// @see http://en.wikipedia.org/wiki/Percent-encoding
9007        var percentEncode = function(bytes, start, end) {
9008            var i, result = '';
9009            for (i = start; i < end; i++) {
9010                result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
9011            }
9012            return result;
9013        };
9014
9015// return the string representation of the specified byte range,
9016// interpreted as ISO-8859-1.
9017        var parseIso88591 = function(bytes, start, end) {
9018            return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
9019        };
9020
9021        var parseId3TagSize = function(header, byteIndex) {
9022            var
9023                returnSize = (header[byteIndex + 6] << 21) |
9024                    (header[byteIndex + 7] << 14) |
9025                    (header[byteIndex + 8] << 7) |
9026                    (header[byteIndex + 9]),
9027                flags = header[byteIndex + 5],
9028                footerPresent = (flags & 16) >> 4;
9029
9030            if (footerPresent) {
9031                return returnSize + 20;
9032            }
9033            return returnSize + 10;
9034        };
9035
9036        var parseAdtsSize = function(header, byteIndex) {
9037            var
9038                lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
9039                middle = header[byteIndex + 4] << 3,
9040                highTwo = header[byteIndex + 3] & 0x3 << 11;
9041
9042            return (highTwo | middle) | lowThree;
9043        };
9044
9045        var parseType = function(header, byteIndex) {
9046            if ((header[byteIndex] === 'I'.charCodeAt(0)) &&
9047                (header[byteIndex + 1] === 'D'.charCodeAt(0)) &&
9048                (header[byteIndex + 2] === '3'.charCodeAt(0))) {
9049                return 'timed-metadata';
9050            } else if ((header[byteIndex] & 0xff === 0xff) &&
9051                ((header[byteIndex + 1] & 0xf0) === 0xf0)) {
9052                return 'audio';
9053            }
9054            return null;
9055        };
9056
9057        var parseSampleRate = function(packet) {
9058            var i = 0;
9059
9060            while (i + 5 < packet.length) {
9061                if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
9062                    // If a valid header was not found,  jump one forward and attempt to
9063                    // find a valid ADTS header starting at the next byte
9064                    i++;
9065                    continue;
9066                }
9067                return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2];
9068            }
9069
9070            return null;
9071        };
9072
9073        var parseAacTimestamp = function(packet) {
9074            var frameStart, frameSize, frame, frameHeader;
9075
9076            // find the start of the first frame and the end of the tag
9077            frameStart = 10;
9078            if (packet[5] & 0x40) {
9079                // advance the frame start past the extended header
9080                frameStart += 4; // header size field
9081                frameStart += parseSyncSafeInteger(packet.subarray(10, 14));
9082            }
9083
9084            // parse one or more ID3 frames
9085            // http://id3.org/id3v2.3.0#ID3v2_frame_overview
9086            do {
9087                // determine the number of bytes in this frame
9088                frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8));
9089                if (frameSize < 1) {
9090                    return null;
9091                }
9092                frameHeader = String.fromCharCode(packet[frameStart],
9093                    packet[frameStart + 1],
9094                    packet[frameStart + 2],
9095                    packet[frameStart + 3]);
9096
9097                if (frameHeader === 'PRIV') {
9098                    frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
9099
9100                    for (var i = 0; i < frame.byteLength; i++) {
9101                        if (frame[i] === 0) {
9102                            var owner = parseIso88591(frame, 0, i);
9103                            if (owner === 'com.apple.streaming.transportStreamTimestamp') {
9104                                var d = frame.subarray(i + 1);
9105                                var size = ((d[3] & 0x01)  << 30) |
9106                                    (d[4]  << 22) |
9107                                    (d[5] << 14) |
9108                                    (d[6] << 6) |
9109                                    (d[7] >>> 2);
9110                                size *= 4;
9111                                size += d[7] & 0x03;
9112
9113                                return size;
9114                            }
9115                            break;
9116                        }
9117                    }
9118                }
9119
9120                frameStart += 10; // advance past the frame header
9121                frameStart += frameSize; // advance past the frame body
9122            } while (frameStart < packet.byteLength);
9123            return null;
9124        };
9125
9126        module.exports = {
9127            parseId3TagSize: parseId3TagSize,
9128            parseAdtsSize: parseAdtsSize,
9129            parseType: parseType,
9130            parseSampleRate: parseSampleRate,
9131            parseAacTimestamp: parseAacTimestamp
9132        };
9133
9134    },{}],38:[function(require,module,exports){
9135        'use strict';
9136
9137        var Stream = require('../utils/stream.js');
9138
9139        var AdtsStream;
9140
9141        var
9142            ADTS_SAMPLING_FREQUENCIES = [
9143                96000,
9144                88200,
9145                64000,
9146                48000,
9147                44100,
9148                32000,
9149                24000,
9150                22050,
9151                16000,
9152                12000,
9153                11025,
9154                8000,
9155                7350
9156            ];
9157
9158        /*
9159 * Accepts a ElementaryStream and emits data events with parsed
9160 * AAC Audio Frames of the individual packets. Input audio in ADTS
9161 * format is unpacked and re-emitted as AAC frames.
9162 *
9163 * @see http://wiki.multimedia.cx/index.php?title=ADTS
9164 * @see http://wiki.multimedia.cx/?title=Understanding_AAC
9165 */
9166        AdtsStream = function() {
9167            var buffer;
9168
9169            AdtsStream.prototype.init.call(this);
9170
9171            this.push = function(packet) {
9172                var
9173                    i = 0,
9174                    frameNum = 0,
9175                    frameLength,
9176                    protectionSkipBytes,
9177                    frameEnd,
9178                    oldBuffer,
9179                    sampleCount,
9180                    adtsFrameDuration;
9181
9182                if (packet.type !== 'audio') {
9183                    // ignore non-audio data
9184                    return;
9185                }
9186
9187                // Prepend any data in the buffer to the input data so that we can parse
9188                // aac frames the cross a PES packet boundary
9189                if (buffer) {
9190                    oldBuffer = buffer;
9191                    buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
9192                    buffer.set(oldBuffer);
9193                    buffer.set(packet.data, oldBuffer.byteLength);
9194                } else {
9195                    buffer = packet.data;
9196                }
9197
9198                // unpack any ADTS frames which have been fully received
9199                // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
9200                while (i + 5 < buffer.length) {
9201
9202                    // Loook for the start of an ADTS header..
9203                    if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
9204                        // If a valid header was not found,  jump one forward and attempt to
9205                        // find a valid ADTS header starting at the next byte
9206                        i++;
9207                        continue;
9208                    }
9209
9210                    // The protection skip bit tells us if we have 2 bytes of CRC data at the
9211                    // end of the ADTS header
9212                    protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
9213
9214                    // Frame length is a 13 bit integer starting 16 bits from the
9215                    // end of the sync sequence
9216                    frameLength = ((buffer[i + 3] & 0x03) << 11) |
9217                        (buffer[i + 4] << 3) |
9218                        ((buffer[i + 5] & 0xe0) >> 5);
9219
9220                    sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
9221                    adtsFrameDuration = (sampleCount * 90000) /
9222                        ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
9223
9224                    frameEnd = i + frameLength;
9225
9226                    // If we don't have enough data to actually finish this ADTS frame, return
9227                    // and wait for more data
9228                    if (buffer.byteLength < frameEnd) {
9229                        return;
9230                    }
9231
9232                    // Otherwise, deliver the complete AAC frame
9233                    this.trigger('data', {
9234                        pts: packet.pts + (frameNum * adtsFrameDuration),
9235                        dts: packet.dts + (frameNum * adtsFrameDuration),
9236                        sampleCount: sampleCount,
9237                        audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
9238                        channelcount: ((buffer[i + 2] & 1) << 2) |
9239                            ((buffer[i + 3] & 0xc0) >>> 6),
9240                        samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
9241                        samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
9242                        // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
9243                        samplesize: 16,
9244                        data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
9245                    });
9246
9247                    // If the buffer is empty, clear it and return
9248                    if (buffer.byteLength === frameEnd) {
9249                        buffer = undefined;
9250                        return;
9251                    }
9252
9253                    frameNum++;
9254
9255                    // Remove the finished frame from the buffer and start the process again
9256                    buffer = buffer.subarray(frameEnd);
9257                }
9258            };
9259            this.flush = function() {
9260                this.trigger('done');
9261            };
9262        };
9263
9264        AdtsStream.prototype = new Stream();
9265
9266        module.exports = AdtsStream;
9267
9268    },{"../utils/stream.js":60}],39:[function(require,module,exports){
9269        'use strict';
9270
9271        var Stream = require('../utils/stream.js');
9272        var ExpGolomb = require('../utils/exp-golomb.js');
9273
9274        var H264Stream, NalByteStream;
9275        var PROFILES_WITH_OPTIONAL_SPS_DATA;
9276
9277        /**
9278         * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
9279         */
9280        NalByteStream = function() {
9281            var
9282                syncPoint = 0,
9283                i,
9284                buffer;
9285            NalByteStream.prototype.init.call(this);
9286
9287            this.push = function(data) {
9288                var swapBuffer;
9289
9290                if (!buffer) {
9291                    buffer = data.data;
9292                } else {
9293                    swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
9294                    swapBuffer.set(buffer);
9295                    swapBuffer.set(data.data, buffer.byteLength);
9296                    buffer = swapBuffer;
9297                }
9298
9299                // Rec. ITU-T H.264, Annex B
9300                // scan for NAL unit boundaries
9301
9302                // a match looks like this:
9303                // 0 0 1 .. NAL .. 0 0 1
9304                // ^ sync point        ^ i
9305                // or this:
9306                // 0 0 1 .. NAL .. 0 0 0
9307                // ^ sync point        ^ i
9308
9309                // advance the sync point to a NAL start, if necessary
9310                for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
9311                    if (buffer[syncPoint + 2] === 1) {
9312                        // the sync point is properly aligned
9313                        i = syncPoint + 5;
9314                        break;
9315                    }
9316                }
9317
9318                while (i < buffer.byteLength) {
9319                    // look at the current byte to determine if we've hit the end of
9320                    // a NAL unit boundary
9321                    switch (buffer[i]) {
9322                        case 0:
9323                            // skip past non-sync sequences
9324                            if (buffer[i - 1] !== 0) {
9325                                i += 2;
9326                                break;
9327                            } else if (buffer[i - 2] !== 0) {
9328                                i++;
9329                                break;
9330                            }
9331
9332                            // deliver the NAL unit if it isn't empty
9333                            if (syncPoint + 3 !== i - 2) {
9334                                this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
9335                            }
9336
9337                            // drop trailing zeroes
9338                            do {
9339                                i++;
9340                            } while (buffer[i] !== 1 && i < buffer.length);
9341                            syncPoint = i - 2;
9342                            i += 3;
9343                            break;
9344                        case 1:
9345                            // skip past non-sync sequences
9346                            if (buffer[i - 1] !== 0 ||
9347                                buffer[i - 2] !== 0) {
9348                                i += 3;
9349                                break;
9350                            }
9351
9352                            // deliver the NAL unit
9353                            this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
9354                            syncPoint = i - 2;
9355                            i += 3;
9356                            break;
9357                        default:
9358                            // the current byte isn't a one or zero, so it cannot be part
9359                            // of a sync sequence
9360                            i += 3;
9361                            break;
9362                    }
9363                }
9364                // filter out the NAL units that were delivered
9365                buffer = buffer.subarray(syncPoint);
9366                i -= syncPoint;
9367                syncPoint = 0;
9368            };
9369
9370            this.flush = function() {
9371                // deliver the last buffered NAL unit
9372                if (buffer && buffer.byteLength > 3) {
9373                    this.trigger('data', buffer.subarray(syncPoint + 3));
9374                }
9375                // reset the stream state
9376                buffer = null;
9377                syncPoint = 0;
9378                this.trigger('done');
9379            };
9380        };
9381        NalByteStream.prototype = new Stream();
9382
9383// values of profile_idc that indicate additional fields are included in the SPS
9384// see Recommendation ITU-T H.264 (4/2013),
9385// 7.3.2.1.1 Sequence parameter set data syntax
9386        PROFILES_WITH_OPTIONAL_SPS_DATA = {
9387            100: true,
9388            110: true,
9389            122: true,
9390            244: true,
9391            44: true,
9392            83: true,
9393            86: true,
9394            118: true,
9395            128: true,
9396            138: true,
9397            139: true,
9398            134: true
9399        };
9400
9401        /**
9402         * Accepts input from a ElementaryStream and produces H.264 NAL unit data
9403         * events.
9404         */
9405        H264Stream = function() {
9406            var
9407                nalByteStream = new NalByteStream(),
9408                self,
9409                trackId,
9410                currentPts,
9411                currentDts,
9412
9413                discardEmulationPreventionBytes,
9414                readSequenceParameterSet,
9415                skipScalingList;
9416
9417            H264Stream.prototype.init.call(this);
9418            self = this;
9419
9420            this.push = function(packet) {
9421                if (packet.type !== 'video') {
9422                    return;
9423                }
9424                trackId = packet.trackId;
9425                currentPts = packet.pts;
9426                currentDts = packet.dts;
9427
9428                nalByteStream.push(packet);
9429            };
9430
9431            nalByteStream.on('data', function(data) {
9432                var
9433                    event = {
9434                        trackId: trackId,
9435                        pts: currentPts,
9436                        dts: currentDts,
9437                        data: data
9438                    };
9439
9440                switch (data[0] & 0x1f) {
9441                    case 0x05:
9442                        event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
9443                        break;
9444                    case 0x06:
9445                        event.nalUnitType = 'sei_rbsp';
9446                        event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
9447                        break;
9448                    case 0x07:
9449                        event.nalUnitType = 'seq_parameter_set_rbsp';
9450                        event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
9451                        event.config = readSequenceParameterSet(event.escapedRBSP);
9452                        break;
9453                    case 0x08:
9454                        event.nalUnitType = 'pic_parameter_set_rbsp';
9455                        break;
9456                    case 0x09:
9457                        event.nalUnitType = 'access_unit_delimiter_rbsp';
9458                        break;
9459
9460                    default:
9461                        break;
9462                }
9463                self.trigger('data', event);
9464            });
9465            nalByteStream.on('done', function() {
9466                self.trigger('done');
9467            });
9468
9469            this.flush = function() {
9470                nalByteStream.flush();
9471            };
9472
9473            /**
9474             * Advance the ExpGolomb decoder past a scaling list. The scaling
9475             * list is optionally transmitted as part of a sequence parameter
9476             * set and is not relevant to transmuxing.
9477             * @param count {number} the number of entries in this scaling list
9478             * @param expGolombDecoder {object} an ExpGolomb pointed to the
9479             * start of a scaling list
9480             * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
9481             */
9482            skipScalingList = function(count, expGolombDecoder) {
9483                var
9484                    lastScale = 8,
9485                    nextScale = 8,
9486                    j,
9487                    deltaScale;
9488
9489                for (j = 0; j < count; j++) {
9490                    if (nextScale !== 0) {
9491                        deltaScale = expGolombDecoder.readExpGolomb();
9492                        nextScale = (lastScale + deltaScale + 256) % 256;
9493                    }
9494
9495                    lastScale = (nextScale === 0) ? lastScale : nextScale;
9496                }
9497            };
9498
9499            /**
9500             * Expunge any "Emulation Prevention" bytes from a "Raw Byte
9501             * Sequence Payload"
9502             * @param data {Uint8Array} the bytes of a RBSP from a NAL
9503             * unit
9504             * @return {Uint8Array} the RBSP without any Emulation
9505             * Prevention Bytes
9506             */
9507            discardEmulationPreventionBytes = function(data) {
9508                var
9509                    length = data.byteLength,
9510                    emulationPreventionBytesPositions = [],
9511                    i = 1,
9512                    newLength, newData;
9513
9514                // Find all `Emulation Prevention Bytes`
9515                while (i < length - 2) {
9516                    if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
9517                        emulationPreventionBytesPositions.push(i + 2);
9518                        i += 2;
9519                    } else {
9520                        i++;
9521                    }
9522                }
9523
9524                // If no Emulation Prevention Bytes were found just return the original
9525                // array
9526                if (emulationPreventionBytesPositions.length === 0) {
9527                    return data;
9528                }
9529
9530                // Create a new array to hold the NAL unit data
9531                newLength = length - emulationPreventionBytesPositions.length;
9532                newData = new Uint8Array(newLength);
9533                var sourceIndex = 0;
9534
9535                for (i = 0; i < newLength; sourceIndex++, i++) {
9536                    if (sourceIndex === emulationPreventionBytesPositions[0]) {
9537                        // Skip this byte
9538                        sourceIndex++;
9539                        // Remove this position index
9540                        emulationPreventionBytesPositions.shift();
9541                    }
9542                    newData[i] = data[sourceIndex];
9543                }
9544
9545                return newData;
9546            };
9547
9548            /**
9549             * Read a sequence parameter set and return some interesting video
9550             * properties. A sequence parameter set is the H264 metadata that
9551             * describes the properties of upcoming video frames.
9552             * @param data {Uint8Array} the bytes of a sequence parameter set
9553             * @return {object} an object with configuration parsed from the
9554             * sequence parameter set, including the dimensions of the
9555             * associated video frames.
9556             */
9557            readSequenceParameterSet = function(data) {
9558                var
9559                    frameCropLeftOffset = 0,
9560                    frameCropRightOffset = 0,
9561                    frameCropTopOffset = 0,
9562                    frameCropBottomOffset = 0,
9563                    sarScale = 1,
9564                    expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
9565                    chromaFormatIdc, picOrderCntType,
9566                    numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
9567                    picHeightInMapUnitsMinus1,
9568                    frameMbsOnlyFlag,
9569                    scalingListCount,
9570                    sarRatio,
9571                    aspectRatioIdc,
9572                    i;
9573
9574                expGolombDecoder = new ExpGolomb(data);
9575                profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
9576                profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
9577                levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
9578                expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
9579
9580                // some profiles have more optional data we don't need
9581                if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
9582                    chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
9583                    if (chromaFormatIdc === 3) {
9584                        expGolombDecoder.skipBits(1); // separate_colour_plane_flag
9585                    }
9586                    expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
9587                    expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
9588                    expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
9589                    if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
9590                        scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
9591                        for (i = 0; i < scalingListCount; i++) {
9592                            if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
9593                                if (i < 6) {
9594                                    skipScalingList(16, expGolombDecoder);
9595                                } else {
9596                                    skipScalingList(64, expGolombDecoder);
9597                                }
9598                            }
9599                        }
9600                    }
9601                }
9602
9603                expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
9604                picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
9605
9606                if (picOrderCntType === 0) {
9607                    expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
9608                } else if (picOrderCntType === 1) {
9609                    expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
9610                    expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
9611                    expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
9612                    numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
9613                    for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
9614                        expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
9615                    }
9616                }
9617
9618                expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
9619                expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
9620
9621                picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
9622                picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
9623
9624                frameMbsOnlyFlag = expGolombDecoder.readBits(1);
9625                if (frameMbsOnlyFlag === 0) {
9626                    expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
9627                }
9628
9629                expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
9630                if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
9631                    frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
9632                    frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
9633                    frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
9634                    frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
9635                }
9636                if (expGolombDecoder.readBoolean()) {
9637                    // vui_parameters_present_flag
9638                    if (expGolombDecoder.readBoolean()) {
9639                        // aspect_ratio_info_present_flag
9640                        aspectRatioIdc = expGolombDecoder.readUnsignedByte();
9641                        switch (aspectRatioIdc) {
9642                            case 1: sarRatio = [1, 1]; break;
9643                            case 2: sarRatio = [12, 11]; break;
9644                            case 3: sarRatio = [10, 11]; break;
9645                            case 4: sarRatio = [16, 11]; break;
9646                            case 5: sarRatio = [40, 33]; break;
9647                            case 6: sarRatio = [24, 11]; break;
9648                            case 7: sarRatio = [20, 11]; break;
9649                            case 8: sarRatio = [32, 11]; break;
9650                            case 9: sarRatio = [80, 33]; break;
9651                            case 10: sarRatio = [18, 11]; break;
9652                            case 11: sarRatio = [15, 11]; break;
9653                            case 12: sarRatio = [64, 33]; break;
9654                            case 13: sarRatio = [160, 99]; break;
9655                            case 14: sarRatio = [4, 3]; break;
9656                            case 15: sarRatio = [3, 2]; break;
9657                            case 16: sarRatio = [2, 1]; break;
9658                            case 255: {
9659                                sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
9660                                expGolombDecoder.readUnsignedByte(),
9661                                    expGolombDecoder.readUnsignedByte() << 8 |
9662                                    expGolombDecoder.readUnsignedByte() ];
9663                                break;
9664                            }
9665                        }
9666                        if (sarRatio) {
9667                            sarScale = sarRatio[0] / sarRatio[1];
9668                        }
9669                    }
9670                }
9671                return {
9672                    profileIdc: profileIdc,
9673                    levelIdc: levelIdc,
9674                    profileCompatibility: profileCompatibility,
9675                    width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
9676                    height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2)
9677                };
9678            };
9679
9680        };
9681        H264Stream.prototype = new Stream();
9682
9683        module.exports = {
9684            H264Stream: H264Stream,
9685            NalByteStream: NalByteStream
9686        };
9687
9688    },{"../utils/exp-golomb.js":59,"../utils/stream.js":60}],40:[function(require,module,exports){
9689        var highPrefix = [33, 16, 5, 32, 164, 27];
9690        var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
9691        var zeroFill = function(count) {
9692            var a = [];
9693            while (count--) {
9694                a.push(0);
9695            }
9696            return a;
9697        };
9698
9699        var makeTable = function(metaTable) {
9700            return Object.keys(metaTable).reduce(function(obj, key) {
9701                obj[key] = new Uint8Array(metaTable[key].reduce(function(arr, part) {
9702                    return arr.concat(part);
9703                }, []));
9704                return obj;
9705            }, {});
9706        };
9707
9708// Frames-of-silence to use for filling in missing AAC frames
9709        var coneOfSilence = {
9710            96000: [highPrefix, [227, 64], zeroFill(154), [56]],
9711            88200: [highPrefix, [231], zeroFill(170), [56]],
9712            64000: [highPrefix, [248, 192], zeroFill(240), [56]],
9713            48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
9714            44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
9715            32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
9716            24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
9717            16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
9718            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]],
9719            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]],
9720            8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
9721        };
9722
9723        module.exports = makeTable(coneOfSilence);
9724
9725    },{}],41:[function(require,module,exports){
9726        'use strict';
9727
9728        var Stream = require('../utils/stream.js');
9729
9730        /**
9731         * The final stage of the transmuxer that emits the flv tags
9732         * for audio, video, and metadata. Also tranlates in time and
9733         * outputs caption data and id3 cues.
9734         */
9735        var CoalesceStream = function(options) {
9736            // Number of Tracks per output segment
9737            // If greater than 1, we combine multiple
9738            // tracks into a single segment
9739            this.numberOfTracks = 0;
9740            this.metadataStream = options.metadataStream;
9741
9742            this.videoTags = [];
9743            this.audioTags = [];
9744            this.videoTrack = null;
9745            this.audioTrack = null;
9746            this.pendingCaptions = [];
9747            this.pendingMetadata = [];
9748            this.pendingTracks = 0;
9749            this.processedTracks = 0;
9750
9751            CoalesceStream.prototype.init.call(this);
9752
9753            // Take output from multiple
9754            this.push = function(output) {
9755                // buffer incoming captions until the associated video segment
9756                // finishes
9757                if (output.text) {
9758                    return this.pendingCaptions.push(output);
9759                }
9760                // buffer incoming id3 tags until the final flush
9761                if (output.frames) {
9762                    return this.pendingMetadata.push(output);
9763                }
9764
9765                if (output.track.type === 'video') {
9766                    this.videoTrack = output.track;
9767                    this.videoTags = output.tags;
9768                    this.pendingTracks++;
9769                }
9770                if (output.track.type === 'audio') {
9771                    this.audioTrack = output.track;
9772                    this.audioTags = output.tags;
9773                    this.pendingTracks++;
9774                }
9775            };
9776        };
9777
9778        CoalesceStream.prototype = new Stream();
9779        CoalesceStream.prototype.flush = function(flushSource) {
9780            var
9781                id3,
9782                caption,
9783                i,
9784                timelineStartPts,
9785                event = {
9786                    tags: {},
9787                    captions: [],
9788                    metadata: []
9789                };
9790
9791            if (this.pendingTracks < this.numberOfTracks) {
9792                if (flushSource !== 'VideoSegmentStream' &&
9793                    flushSource !== 'AudioSegmentStream') {
9794                    // Return because we haven't received a flush from a data-generating
9795                    // portion of the segment (meaning that we have only recieved meta-data
9796                    // or captions.)
9797                    return;
9798                } else if (this.pendingTracks === 0) {
9799                    // In the case where we receive a flush without any data having been
9800                    // received we consider it an emitted track for the purposes of coalescing
9801                    // `done` events.
9802                    // We do this for the case where there is an audio and video track in the
9803                    // segment but no audio data. (seen in several playlists with alternate
9804                    // audio tracks and no audio present in the main TS segments.)
9805                    this.processedTracks++;
9806
9807                    if (this.processedTracks < this.numberOfTracks) {
9808                        return;
9809                    }
9810                }
9811            }
9812
9813            this.processedTracks += this.pendingTracks;
9814            this.pendingTracks = 0;
9815
9816            if (this.processedTracks < this.numberOfTracks) {
9817                return;
9818            }
9819
9820            if (this.videoTrack) {
9821                timelineStartPts = this.videoTrack.timelineStartInfo.pts;
9822            } else if (this.audioTrack) {
9823                timelineStartPts = this.audioTrack.timelineStartInfo.pts;
9824            }
9825
9826            event.tags.videoTags = this.videoTags;
9827            event.tags.audioTags = this.audioTags;
9828
9829            // Translate caption PTS times into second offsets into the
9830            // video timeline for the segment
9831            for (i = 0; i < this.pendingCaptions.length; i++) {
9832                caption = this.pendingCaptions[i];
9833                caption.startTime = caption.startPts - timelineStartPts;
9834                caption.startTime /= 90e3;
9835                caption.endTime = caption.endPts - timelineStartPts;
9836                caption.endTime /= 90e3;
9837                event.captions.push(caption);
9838            }
9839
9840            // Translate ID3 frame PTS times into second offsets into the
9841            // video timeline for the segment
9842            for (i = 0; i < this.pendingMetadata.length; i++) {
9843                id3 = this.pendingMetadata[i];
9844                id3.cueTime = id3.pts - timelineStartPts;
9845                id3.cueTime /= 90e3;
9846                event.metadata.push(id3);
9847            }
9848            // We add this to every single emitted segment even though we only need
9849            // it for the first
9850            event.metadata.dispatchType = this.metadataStream.dispatchType;
9851
9852            // Reset stream state
9853            this.videoTrack = null;
9854            this.audioTrack = null;
9855            this.videoTags = [];
9856            this.audioTags = [];
9857            this.pendingCaptions.length = 0;
9858            this.pendingMetadata.length = 0;
9859            this.pendingTracks = 0;
9860            this.processedTracks = 0;
9861
9862            // Emit the final segment
9863            this.trigger('data', event);
9864
9865            this.trigger('done');
9866        };
9867
9868        module.exports = CoalesceStream;
9869
9870    },{"../utils/stream.js":60}],42:[function(require,module,exports){
9871        'use strict';
9872
9873        var FlvTag = require('./flv-tag.js');
9874
9875// For information on the FLV format, see
9876// http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf.
9877// Technically, this function returns the header and a metadata FLV tag
9878// if duration is greater than zero
9879// duration in seconds
9880// @return {object} the bytes of the FLV header as a Uint8Array
9881        var getFlvHeader = function(duration, audio, video) { // :ByteArray {
9882            var
9883                headBytes = new Uint8Array(3 + 1 + 1 + 4),
vendor: 16,081 bytes, lines 9884-10292
9884                head = new DataView(headBytes.buffer),
9885                metadata,
9886                result,
9887                metadataLength;
9888
9889            // default arguments
9890            duration = duration || 0;
9891            audio = audio === undefined ? true : audio;
9892            video = video === undefined ? true : video;
9893
9894            // signature
9895            head.setUint8(0, 0x46); // 'F'
9896            head.setUint8(1, 0x4c); // 'L'
9897            head.setUint8(2, 0x56); // 'V'
9898
9899            // version
9900            head.setUint8(3, 0x01);
9901
9902            // flags
9903            head.setUint8(4, (audio ? 0x04 : 0x00) | (video ? 0x01 : 0x00));
9904
9905            // data offset, should be 9 for FLV v1
9906            head.setUint32(5, headBytes.byteLength);
9907
9908            // init the first FLV tag
9909            if (duration <= 0) {
9910                // no duration available so just write the first field of the first
9911                // FLV tag
9912                result = new Uint8Array(headBytes.byteLength + 4);
9913                result.set(headBytes);
9914                result.set([0, 0, 0, 0], headBytes.byteLength);
9915                return result;
9916            }
9917
9918            // write out the duration metadata tag
9919            metadata = new FlvTag(FlvTag.METADATA_TAG);
9920            metadata.pts = metadata.dts = 0;
9921            metadata.writeMetaDataDouble('duration', duration);
9922            metadataLength = metadata.finalize().length;
9923            result = new Uint8Array(headBytes.byteLength + metadataLength);
9924            result.set(headBytes);
9925            result.set(head.byteLength, metadataLength);
9926
9927            return result;
9928        };
9929
9930        module.exports = getFlvHeader;
9931
9932    },{"./flv-tag.js":43}],43:[function(require,module,exports){
9933        /**
9934         * An object that stores the bytes of an FLV tag and methods for
9935         * querying and manipulating that data.
9936         * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
9937         */
9938        'use strict';
9939
9940        var FlvTag;
9941
9942// (type:uint, extraData:Boolean = false) extends ByteArray
9943        FlvTag = function(type, extraData) {
9944            var
9945                // Counter if this is a metadata tag, nal start marker if this is a video
9946                // tag. unused if this is an audio tag
9947                adHoc = 0, // :uint
9948
9949                // The default size is 16kb but this is not enough to hold iframe
9950                // data and the resizing algorithm costs a bit so we create a larger
9951                // starting buffer for video tags
9952                bufferStartSize = 16384,
9953
9954                // checks whether the FLV tag has enough capacity to accept the proposed
9955                // write and re-allocates the internal buffers if necessary
9956                prepareWrite = function(flv, count) {
9957                    var
9958                        bytes,
9959                        minLength = flv.position + count;
9960                    if (minLength < flv.bytes.byteLength) {
9961                        // there's enough capacity so do nothing
9962                        return;
9963                    }
9964
9965                    // allocate a new buffer and copy over the data that will not be modified
9966                    bytes = new Uint8Array(minLength * 2);
9967                    bytes.set(flv.bytes.subarray(0, flv.position), 0);
9968                    flv.bytes = bytes;
9969                    flv.view = new DataView(flv.bytes.buffer);
9970                },
9971
9972                // commonly used metadata properties
9973                widthBytes = FlvTag.widthBytes || new Uint8Array('width'.length),
9974                heightBytes = FlvTag.heightBytes || new Uint8Array('height'.length),
9975                videocodecidBytes = FlvTag.videocodecidBytes || new Uint8Array('videocodecid'.length),
9976                i;
9977
9978            if (!FlvTag.widthBytes) {
9979                // calculating the bytes of common metadata names ahead of time makes the
9980                // corresponding writes faster because we don't have to loop over the
9981                // characters
9982                // re-test with test/perf.html if you're planning on changing this
9983                for (i = 0; i < 'width'.length; i++) {
9984                    widthBytes[i] = 'width'.charCodeAt(i);
9985                }
9986                for (i = 0; i < 'height'.length; i++) {
9987                    heightBytes[i] = 'height'.charCodeAt(i);
9988                }
9989                for (i = 0; i < 'videocodecid'.length; i++) {
9990                    videocodecidBytes[i] = 'videocodecid'.charCodeAt(i);
9991                }
9992
9993                FlvTag.widthBytes = widthBytes;
9994                FlvTag.heightBytes = heightBytes;
9995                FlvTag.videocodecidBytes = videocodecidBytes;
9996            }
9997
9998            this.keyFrame = false; // :Boolean
9999
10000            switch (type) {
10001                case FlvTag.VIDEO_TAG:
10002                    this.length = 16;
10003                    // Start the buffer at 256k
10004                    bufferStartSize *= 6;
10005                    break;
10006                case FlvTag.AUDIO_TAG:
10007                    this.length = 13;
10008                    this.keyFrame = true;
10009                    break;
10010                case FlvTag.METADATA_TAG:
10011                    this.length = 29;
10012                    this.keyFrame = true;
10013                    break;
10014                default:
10015                    throw new Error('Unknown FLV tag type');
10016            }
10017
10018            this.bytes = new Uint8Array(bufferStartSize);
10019            this.view = new DataView(this.bytes.buffer);
10020            this.bytes[0] = type;
10021            this.position = this.length;
10022            this.keyFrame = extraData; // Defaults to false
10023
10024            // presentation timestamp
10025            this.pts = 0;
10026            // decoder timestamp
10027            this.dts = 0;
10028
10029            // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0)
10030            this.writeBytes = function(bytes, offset, length) {
10031                var
10032                    start = offset || 0,
10033                    end;
10034                length = length || bytes.byteLength;
10035                end = start + length;
10036
10037                prepareWrite(this, length);
10038                this.bytes.set(bytes.subarray(start, end), this.position);
10039
10040                this.position += length;
10041                this.length = Math.max(this.length, this.position);
10042            };
10043
10044            // ByteArray#writeByte(value:int):void
10045            this.writeByte = function(byte) {
10046                prepareWrite(this, 1);
10047                this.bytes[this.position] = byte;
10048                this.position++;
10049                this.length = Math.max(this.length, this.position);
10050            };
10051
10052            // ByteArray#writeShort(value:int):void
10053            this.writeShort = function(short) {
10054                prepareWrite(this, 2);
10055                this.view.setUint16(this.position, short);
10056                this.position += 2;
10057                this.length = Math.max(this.length, this.position);
10058            };
10059
10060            // Negative index into array
10061            // (pos:uint):int
10062            this.negIndex = function(pos) {
10063                return this.bytes[this.length - pos];
10064            };
10065
10066            // The functions below ONLY work when this[0] == VIDEO_TAG.
10067            // We are not going to check for that because we dont want the overhead
10068            // (nal:ByteArray = null):int
10069            this.nalUnitSize = function() {
10070                if (adHoc === 0) {
10071                    return 0;
10072                }
10073
10074                return this.length - (adHoc + 4);
10075            };
10076
10077            this.startNalUnit = function() {
10078                // remember position and add 4 bytes
10079                if (adHoc > 0) {
10080                    throw new Error('Attempted to create new NAL wihout closing the old one');
10081                }
10082
10083                // reserve 4 bytes for nal unit size
10084                adHoc = this.length;
10085                this.length += 4;
10086                this.position = this.length;
10087            };
10088
10089            // (nal:ByteArray = null):void
10090            this.endNalUnit = function(nalContainer) {
10091                var
10092                    nalStart, // :uint
10093                    nalLength; // :uint
10094
10095                // Rewind to the marker and write the size
10096                if (this.length === adHoc + 4) {
10097                    // we started a nal unit, but didnt write one, so roll back the 4 byte size value
10098                    this.length -= 4;
10099                } else if (adHoc > 0) {
10100                    nalStart = adHoc + 4;
10101                    nalLength = this.length - nalStart;
10102
10103                    this.position = adHoc;
10104                    this.view.setUint32(this.position, nalLength);
10105                    this.position = this.length;
10106
10107                    if (nalContainer) {
10108                        // Add the tag to the NAL unit
10109                        nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength));
10110                    }
10111                }
10112
10113                adHoc = 0;
10114            };
10115
10116            /**
10117             * Write out a 64-bit floating point valued metadata property. This method is
10118             * called frequently during a typical parse and needs to be fast.
10119             */
10120            // (key:String, val:Number):void
10121            this.writeMetaDataDouble = function(key, val) {
10122                var i;
10123                prepareWrite(this, 2 + key.length + 9);
10124
10125                // write size of property name
10126                this.view.setUint16(this.position, key.length);
10127                this.position += 2;
10128
10129                // this next part looks terrible but it improves parser throughput by
10130                // 10kB/s in my testing
10131
10132                // write property name
10133                if (key === 'width') {
10134                    this.bytes.set(widthBytes, this.position);
10135                    this.position += 5;
10136                } else if (key === 'height') {
10137                    this.bytes.set(heightBytes, this.position);
10138                    this.position += 6;
10139                } else if (key === 'videocodecid') {
10140                    this.bytes.set(videocodecidBytes, this.position);
10141                    this.position += 12;
10142                } else {
10143                    for (i = 0; i < key.length; i++) {
10144                        this.bytes[this.position] = key.charCodeAt(i);
10145                        this.position++;
10146                    }
10147                }
10148
10149                // skip null byte
10150                this.position++;
10151
10152                // write property value
10153                this.view.setFloat64(this.position, val);
10154                this.position += 8;
10155
10156                // update flv tag length
10157                this.length = Math.max(this.length, this.position);
10158                ++adHoc;
10159            };
10160
10161            // (key:String, val:Boolean):void
10162            this.writeMetaDataBoolean = function(key, val) {
10163                var i;
10164                prepareWrite(this, 2);
10165                this.view.setUint16(this.position, key.length);
10166                this.position += 2;
10167                for (i = 0; i < key.length; i++) {
10168                    // if key.charCodeAt(i) >= 255, handle error
10169                    prepareWrite(this, 1);
10170                    this.bytes[this.position] = key.charCodeAt(i);
10171                    this.position++;
10172                }
10173                prepareWrite(this, 2);
10174                this.view.setUint8(this.position, 0x01);
10175                this.position++;
10176                this.view.setUint8(this.position, val ? 0x01 : 0x00);
10177                this.position++;
10178                this.length = Math.max(this.length, this.position);
10179                ++adHoc;
10180            };
10181
10182            // ():ByteArray
10183            this.finalize = function() {
10184                var
10185                    dtsDelta, // :int
10186                    len; // :int
10187
10188                switch (this.bytes[0]) {
10189                    // Video Data
10190                    case FlvTag.VIDEO_TAG:
10191                        // We only support AVC, 1 = key frame (for AVC, a seekable
10192                        // frame), 2 = inter frame (for AVC, a non-seekable frame)
10193                        this.bytes[11] = ((this.keyFrame || extraData) ? 0x10 : 0x20) | 0x07;
10194                        this.bytes[12] = extraData ?  0x00 : 0x01;
10195
10196                        dtsDelta = this.pts - this.dts;
10197                        this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16;
10198                        this.bytes[14] = (dtsDelta & 0x0000FF00) >>>  8;
10199                        this.bytes[15] = (dtsDelta & 0x000000FF) >>>  0;
10200                        break;
10201
10202                    case FlvTag.AUDIO_TAG:
10203                        this.bytes[11] = 0xAF; // 44 kHz, 16-bit stereo
10204                        this.bytes[12] = extraData ? 0x00 : 0x01;
10205                        break;
10206
10207                    case FlvTag.METADATA_TAG:
10208                        this.position = 11;
10209                        this.view.setUint8(this.position, 0x02); // String type
10210                        this.position++;
10211                        this.view.setUint16(this.position, 0x0A); // 10 Bytes
10212                        this.position += 2;
10213                        // set "onMetaData"
10214                        this.bytes.set([0x6f, 0x6e, 0x4d, 0x65,
10215                            0x74, 0x61, 0x44, 0x61,
10216                            0x74, 0x61], this.position);
10217                        this.position += 10;
10218                        this.bytes[this.position] = 0x08; // Array type
10219                        this.position++;
10220                        this.view.setUint32(this.position, adHoc);
10221                        this.position = this.length;
10222                        this.bytes.set([0, 0, 9], this.position);
10223                        this.position += 3; // End Data Tag
10224                        this.length = this.position;
10225                        break;
10226                }
10227
10228                len = this.length - 11;
10229
10230                // write the DataSize field
10231                this.bytes[ 1] = (len & 0x00FF0000) >>> 16;
10232                this.bytes[ 2] = (len & 0x0000FF00) >>>  8;
10233                this.bytes[ 3] = (len & 0x000000FF) >>>  0;
10234                // write the Timestamp
10235                this.bytes[ 4] = (this.dts & 0x00FF0000) >>> 16;
10236                this.bytes[ 5] = (this.dts & 0x0000FF00) >>>  8;
10237                this.bytes[ 6] = (this.dts & 0x000000FF) >>>  0;
10238                this.bytes[ 7] = (this.dts & 0xFF000000) >>> 24;
10239                // write the StreamID
10240                this.bytes[ 8] = 0;
10241                this.bytes[ 9] = 0;
10242                this.bytes[10] = 0;
10243
10244                // Sometimes we're at the end of the view and have one slot to write a
10245                // uint32, so, prepareWrite of count 4, since, view is uint8
10246                prepareWrite(this, 4);
10247                this.view.setUint32(this.length, this.length);
10248                this.length += 4;
10249                this.position += 4;
10250
10251                // trim down the byte buffer to what is actually being used
10252                this.bytes = this.bytes.subarray(0, this.length);
10253                this.frameTime = FlvTag.frameTime(this.bytes);
10254                // if bytes.bytelength isn't equal to this.length, handle error
10255                return this;
10256            };
10257        };
10258
10259        FlvTag.AUDIO_TAG = 0x08; // == 8, :uint
10260        FlvTag.VIDEO_TAG = 0x09; // == 9, :uint
10261        FlvTag.METADATA_TAG = 0x12; // == 18, :uint
10262
10263// (tag:ByteArray):Boolean {
10264        FlvTag.isAudioFrame = function(tag) {
10265            return FlvTag.AUDIO_TAG === tag[0];
10266        };
10267
10268// (tag:ByteArray):Boolean {
10269        FlvTag.isVideoFrame = function(tag) {
10270            return FlvTag.VIDEO_TAG === tag[0];
10271        };
10272
10273// (tag:ByteArray):Boolean {
10274        FlvTag.isMetaData = function(tag) {
10275            return FlvTag.METADATA_TAG === tag[0];
10276        };
10277
10278// (tag:ByteArray):Boolean {
10279        FlvTag.isKeyFrame = function(tag) {
10280            if (FlvTag.isVideoFrame(tag)) {
10281                return tag[11] === 0x17;
10282            }
10283
10284            if (FlvTag.isAudioFrame(tag)) {
10285                return true;
10286            }
10287
10288            if (FlvTag.isMetaData(tag)) {
10289                return true;
10290            }
10291
10292            return false;
10293        };
10294
10295// (tag:ByteArray):uint {
10296        FlvTag.frameTime = function(tag) {
10297            var pts = tag[ 4] << 16; // :uint
10298            pts |= tag[ 5] <<  8;
10299            pts |= tag[ 6] <<  0;
10300            pts |= tag[ 7] << 24;
10301            return pts;
10302        };
10303
10304        module.exports = FlvTag;
10305
10306    },{}],44:[function(require,module,exports){
10307        module.exports = {
10308            tag: require('./flv-tag'),
10309            Transmuxer: require('./transmuxer'),
10310            getFlvHeader: require('./flv-header')
10311        };
10312
10313    },{"./flv-header":42,"./flv-tag":43,"./transmuxer":46}],45:[function(require,module,exports){
10314        'use strict';
10315
10316        var TagList = function() {
10317            var self = this;
10318
10319            this.list = [];
10320
10321            this.push = function(tag) {
10322                this.list.push({
10323                    bytes: tag.bytes,
10324                    dts: tag.dts,
10325                    pts: tag.pts,
10326                    keyFrame: tag.keyFrame,
10327                    metaDataTag: tag.metaDataTag
10328                });
10329            };
10330
10331            Object.defineProperty(this, 'length', {
10332                get: function() {
10333                    return self.list.length;
10334                }
10335            });
10336        };
10337
10338        module.exports = TagList;
10339
10340    },{}],46:[function(require,module,exports){
10341        'use strict';
10342
10343        var Stream = require('../utils/stream.js');
10344        var FlvTag = require('./flv-tag.js');
10345        var m2ts = require('../m2ts/m2ts.js');
10346        var AdtsStream = require('../codecs/adts.js');
10347        var H264Stream = require('../codecs/h264').H264Stream;
10348        var CoalesceStream = require('./coalesce-stream.js');
10349        var TagList = require('./tag-list.js');
10350
10351        var
10352            Transmuxer,
10353            VideoSegmentStream,
10354            AudioSegmentStream,
10355            collectTimelineInfo,
10356            metaDataTag,
10357            extraDataTag;
10358
10359        /**
10360         * Store information about the start and end of the tracka and the
10361         * duration for each frame/sample we process in order to calculate
10362         * the baseMediaDecodeTime
10363         */
10364        collectTimelineInfo = function(track, data) {
10365            if (typeof data.pts === 'number') {
10366                if (track.timelineStartInfo.pts === undefined) {
10367                    track.timelineStartInfo.pts = data.pts;
10368                } else {
10369                    track.timelineStartInfo.pts =
10370                        Math.min(track.timelineStartInfo.pts, data.pts);
10371                }
10372            }
10373
10374            if (typeof data.dts === 'number') {
10375                if (track.timelineStartInfo.dts === undefined) {
10376                    track.timelineStartInfo.dts = data.dts;
10377                } else {
10378                    track.timelineStartInfo.dts =
10379                        Math.min(track.timelineStartInfo.dts, data.dts);
10380                }
10381            }
10382        };
10383
10384        metaDataTag = function(track, pts) {
10385            var
10386                tag = new FlvTag(FlvTag.METADATA_TAG); // :FlvTag
10387
10388            tag.dts = pts;
10389            tag.pts = pts;
10390
10391            tag.writeMetaDataDouble('videocodecid', 7);
10392            tag.writeMetaDataDouble('width', track.width);
10393            tag.writeMetaDataDouble('height', track.height);
10394
10395            return tag;
10396        };
10397
10398        extraDataTag = function(track, pts) {
10399            var
10400                i,
10401                tag = new FlvTag(FlvTag.VIDEO_TAG, true);
10402
10403            tag.dts = pts;
10404            tag.pts = pts;
10405
10406            tag.writeByte(0x01);// version
10407            tag.writeByte(track.profileIdc);// profile
10408            tag.writeByte(track.profileCompatibility);// compatibility
10409            tag.writeByte(track.levelIdc);// level
10410            tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
10411            tag.writeByte(0xE0 | 0x01); // reserved (3 bits), num of SPS (5 bits)
10412            tag.writeShort(track.sps[0].length); // data of SPS
10413            tag.writeBytes(track.sps[0]); // SPS
10414
10415            tag.writeByte(track.pps.length); // num of PPS (will there ever be more that 1 PPS?)
10416            for (i = 0; i < track.pps.length; ++i) {
10417                tag.writeShort(track.pps[i].length); // 2 bytes for length of PPS
10418                tag.writeBytes(track.pps[i]); // data of PPS
10419            }
10420
10421            return tag;
10422        };
10423
10424        /**
10425         * Constructs a single-track, media segment from AAC data
10426         * events. The output of this stream can be fed to flash.
10427         */
10428        AudioSegmentStream = function(track) {
10429            var
10430                adtsFrames = [],
10431                oldExtraData;
10432
10433            AudioSegmentStream.prototype.init.call(this);
10434
10435            this.push = function(data) {
10436                collectTimelineInfo(track, data);
10437
10438                if (track && track.channelcount === undefined) {
10439                    track.audioobjecttype = data.audioobjecttype;
10440                    track.channelcount = data.channelcount;
10441                    track.samplerate = data.samplerate;
10442                    track.samplingfrequencyindex = data.samplingfrequencyindex;
10443                    track.samplesize = data.samplesize;
10444                    track.extraData = (track.audioobjecttype << 11) |
10445                        (track.samplingfrequencyindex << 7) |
10446                        (track.channelcount << 3);
10447                }
10448
10449                data.pts = Math.round(data.pts / 90);
10450                data.dts = Math.round(data.dts / 90);
10451
10452                // buffer audio data until end() is called
10453                adtsFrames.push(data);
10454            };
10455
10456            this.flush = function() {
10457                var currentFrame, adtsFrame, lastMetaPts, tags = new TagList();
10458                // return early if no audio data has been observed
10459                if (adtsFrames.length === 0) {
10460                    this.trigger('done', 'AudioSegmentStream');
10461                    return;
10462                }
10463
10464                lastMetaPts = -Infinity;
10465
10466                while (adtsFrames.length) {
10467                    currentFrame = adtsFrames.shift();
10468
10469                    // write out metadata tags every 1 second so that the decoder
10470                    // is re-initialized quickly after seeking into a different
10471                    // audio configuration
10472                    if (track.extraData !== oldExtraData || currentFrame.pts - lastMetaPts >= 1000) {
10473                        adtsFrame = new FlvTag(FlvTag.METADATA_TAG);
10474                        adtsFrame.pts = currentFrame.pts;
10475                        adtsFrame.dts = currentFrame.dts;
10476
10477                        // AAC is always 10
10478                        adtsFrame.writeMetaDataDouble('audiocodecid', 10);
10479                        adtsFrame.writeMetaDataBoolean('stereo', track.channelcount === 2);
10480                        adtsFrame.writeMetaDataDouble('audiosamplerate', track.samplerate);
10481                        // Is AAC always 16 bit?
10482                        adtsFrame.writeMetaDataDouble('audiosamplesize', 16);
10483
10484                        tags.push(adtsFrame.finalize());
10485
10486                        oldExtraData = track.extraData;
10487
10488                        adtsFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
10489                        // For audio, DTS is always the same as PTS. We want to set the DTS
10490                        // however so we can compare with video DTS to determine approximate
10491                        // packet order
10492                        adtsFrame.pts = currentFrame.pts;
10493                        adtsFrame.dts = currentFrame.dts;
10494
10495                        adtsFrame.view.setUint16(adtsFrame.position, track.extraData);
10496                        adtsFrame.position += 2;
10497                        adtsFrame.length = Math.max(adtsFrame.length, adtsFrame.position);
10498
10499                        tags.push(adtsFrame.finalize());
10500
10501                        lastMetaPts = currentFrame.pts;
10502                    }
10503                    adtsFrame = new FlvTag(FlvTag.AUDIO_TAG);
10504                    adtsFrame.pts = currentFrame.pts;
10505                    adtsFrame.dts = currentFrame.dts;
10506
10507                    adtsFrame.writeBytes(currentFrame.data);
10508
10509                    tags.push(adtsFrame.finalize());
10510                }
10511
10512                oldExtraData = null;
10513                this.trigger('data', {track: track, tags: tags.list});
10514
10515                this.trigger('done', 'AudioSegmentStream');
10516            };
10517        };
10518        AudioSegmentStream.prototype = new Stream();
10519
10520        /**
10521         * Store FlvTags for the h264 stream
10522         * @param track {object} track metadata configuration
10523         */
10524        VideoSegmentStream = function(track) {
10525            var
10526                nalUnits = [],
10527                config,
10528                h264Frame;
10529            VideoSegmentStream.prototype.init.call(this);
10530
10531            this.finishFrame = function(tags, frame) {
10532                if (!frame) {
10533                    return;
10534                }
10535                // Check if keyframe and the length of tags.
10536                // This makes sure we write metadata on the first frame of a segment.
10537                if (config && track && track.newMetadata &&
10538                    (frame.keyFrame || tags.length === 0)) {
10539                    // Push extra data on every IDR frame in case we did a stream change + seek
10540                    var metaTag = metaDataTag(config, frame.dts).finalize();
10541                    var extraTag = extraDataTag(track, frame.dts).finalize();
10542
10543                    metaTag.metaDataTag = extraTag.metaDataTag = true;
10544
10545                    tags.push(metaTag);
10546                    tags.push(extraTag);
10547                    track.newMetadata = false;
10548                }
10549
10550                frame.endNalUnit();
10551                tags.push(frame.finalize());
10552                h264Frame = null;
10553            };
10554
10555            this.push = function(data) {
10556                collectTimelineInfo(track, data);
10557
10558                data.pts = Math.round(data.pts / 90);
10559                data.dts = Math.round(data.dts / 90);
10560
10561                // buffer video until flush() is called
10562                nalUnits.push(data);
10563            };
10564
10565            this.flush = function() {
10566                var
10567                    currentNal,
10568                    tags = new TagList();
10569
10570                // Throw away nalUnits at the start of the byte stream until we find
10571                // the first AUD
10572                while (nalUnits.length) {
10573                    if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
10574                        break;
10575                    }
10576                    nalUnits.shift();
10577                }
10578
10579                // return early if no video data has been observed
10580                if (nalUnits.length === 0) {
10581                    this.trigger('done', 'VideoSegmentStream');
10582                    return;
10583                }
10584
10585                while (nalUnits.length) {
10586                    currentNal = nalUnits.shift();
10587
10588                    // record the track config
10589                    if (currentNal.nalUnitType === 'seq_parameter_set_rbsp') {
10590                        track.newMetadata = true;
10591                        config = currentNal.config;
10592                        track.width = config.width;
10593                        track.height = config.height;
10594                        track.sps = [currentNal.data];
10595                        track.profileIdc = config.profileIdc;
10596                        track.levelIdc = config.levelIdc;
10597                        track.profileCompatibility = config.profileCompatibility;
10598                        h264Frame.endNalUnit();
10599                    } else if (currentNal.nalUnitType === 'pic_parameter_set_rbsp') {
10600                        track.newMetadata = true;
10601                        track.pps = [currentNal.data];
10602                        h264Frame.endNalUnit();
10603                    } else if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
10604                        if (h264Frame) {
10605                            this.finishFrame(tags, h264Frame);
10606                        }
10607                        h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
10608                        h264Frame.pts = currentNal.pts;
10609                        h264Frame.dts = currentNal.dts;
10610                    } else {
10611                        if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
10612                            // the current sample is a key frame
10613                            h264Frame.keyFrame = true;
10614                        }
10615                        h264Frame.endNalUnit();
10616                    }
10617                    h264Frame.startNalUnit();
10618                    h264Frame.writeBytes(currentNal.data);
10619                }
10620                if (h264Frame) {
10621                    this.finishFrame(tags, h264Frame);
10622                }
10623
10624                this.trigger('data', {track: track, tags: tags.list});
10625
10626                // Continue with the flush process now
10627                this.trigger('done', 'VideoSegmentStream');
10628            };
10629        };
10630
10631        VideoSegmentStream.prototype = new Stream();
10632
10633        /**
10634         * An object that incrementally transmuxes MPEG2 Trasport Stream
10635         * chunks into an FLV.
10636         */
10637        Transmuxer = function(options) {
10638            var
10639                self = this,
10640
10641                packetStream, parseStream, elementaryStream,
10642                videoTimestampRolloverStream, audioTimestampRolloverStream,
10643                timedMetadataTimestampRolloverStream,
10644                adtsStream, h264Stream,
10645                videoSegmentStream, audioSegmentStream, captionStream,
10646                coalesceStream;
10647
10648            Transmuxer.prototype.init.call(this);
10649
10650            options = options || {};
10651
10652            // expose the metadata stream
10653            this.metadataStream = new m2ts.MetadataStream();
10654
10655            options.metadataStream = this.metadataStream;
10656
10657            // set up the parsing pipeline
10658            packetStream = new m2ts.TransportPacketStream();
10659            parseStream = new m2ts.TransportParseStream();
10660            elementaryStream = new m2ts.ElementaryStream();
10661            videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
10662            audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
10663            timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
10664
10665            adtsStream = new AdtsStream();
10666            h264Stream = new H264Stream();
10667            coalesceStream = new CoalesceStream(options);
10668
10669            // disassemble MPEG2-TS packets into elementary streams
10670            packetStream
10671                .pipe(parseStream)
10672                .pipe(elementaryStream);
10673
10674            // !!THIS ORDER IS IMPORTANT!!
10675            // demux the streams
10676            elementaryStream
10677                .pipe(videoTimestampRolloverStream)
10678                .pipe(h264Stream);
10679            elementaryStream
10680                .pipe(audioTimestampRolloverStream)
10681                .pipe(adtsStream);
10682
10683            elementaryStream
10684                .pipe(timedMetadataTimestampRolloverStream)
10685                .pipe(this.metadataStream)
10686                .pipe(coalesceStream);
10687            // if CEA-708 parsing is available, hook up a caption stream
10688            captionStream = new m2ts.CaptionStream();
10689            h264Stream.pipe(captionStream)
10690                .pipe(coalesceStream);
10691
10692            // hook up the segment streams once track metadata is delivered
10693            elementaryStream.on('data', function(data) {
10694                var i, videoTrack, audioTrack;
10695
10696                if (data.type === 'metadata') {
10697                    i = data.tracks.length;
10698
10699                    // scan the tracks listed in the metadata
10700                    while (i--) {
10701                        if (data.tracks[i].type === 'video') {
10702                            videoTrack = data.tracks[i];
10703                        } else if (data.tracks[i].type === 'audio') {
10704                            audioTrack = data.tracks[i];
10705                        }
10706                    }
10707
10708                    // hook up the video segment stream to the first track with h264 data
10709                    if (videoTrack && !videoSegmentStream) {
10710                        coalesceStream.numberOfTracks++;
10711                        videoSegmentStream = new VideoSegmentStream(videoTrack);
10712
10713                        // Set up the final part of the video pipeline
10714                        h264Stream
10715                            .pipe(videoSegmentStream)
10716                            .pipe(coalesceStream);
10717                    }
10718
10719                    if (audioTrack && !audioSegmentStream) {
10720                        // hook up the audio segment stream to the first track with aac data
10721                        coalesceStream.numberOfTracks++;
10722                        audioSegmentStream = new AudioSegmentStream(audioTrack);
10723
10724                        // Set up the final part of the audio pipeline
10725                        adtsStream
10726                            .pipe(audioSegmentStream)
10727                            .pipe(coalesceStream);
10728                    }
10729                }
10730            });
10731
10732            // feed incoming data to the front of the parsing pipeline
10733            this.push = function(data) {
10734                packetStream.push(data);
10735            };
10736
10737            // flush any buffered data
10738            this.flush = function() {
10739                // Start at the top of the pipeline and flush all pending work
10740                packetStream.flush();
10741            };
10742
10743            // Re-emit any data coming from the coalesce stream to the outside world
10744            coalesceStream.on('data', function(event) {
10745                self.trigger('data', event);
10746            });
10747
10748            // Let the consumer know we have finished flushing the entire pipeline
10749            coalesceStream.on('done', function() {
10750                self.trigger('done');
10751            });
10752        };
10753        Transmuxer.prototype = new Stream();
10754
10755// forward compatibility
10756        module.exports = Transmuxer;
10757
10758    },{"../codecs/adts.js":38,"../codecs/h264":39,"../m2ts/m2ts.js":48,"../utils/stream.js":60,"./coalesce-stream.js":41,"./flv-tag.js":43,"./tag-list.js":45}],47:[function(require,module,exports){
10759        /**
10760         * mux.js
10761         *
10762         * Copyright (c) 2015 Brightcove
10763         * All rights reserved.
10764         *
10765         * Reads in-band caption information from a video elementary
10766         * stream. Captions must follow the CEA-708 standard for injection
10767         * into an MPEG-2 transport streams.
10768         * @see https://en.wikipedia.org/wiki/CEA-708
10769         */
10770
10771        'use strict';
10772
10773// -----------------
10774// Link To Transport
10775// -----------------
10776
10777// Supplemental enhancement information (SEI) NAL units have a
10778// payload type field to indicate how they are to be
10779// interpreted. CEAS-708 caption content is always transmitted with
10780// payload type 0x04.
10781        var USER_DATA_REGISTERED_ITU_T_T35 = 4,
10782            RBSP_TRAILING_BITS = 128,
10783            Stream = require('../utils/stream');
10784
10785        /**
10786         * Parse a supplemental enhancement information (SEI) NAL unit.
10787         * Stops parsing once a message of type ITU T T35 has been found.
10788         *
10789         * @param bytes {Uint8Array} the bytes of a SEI NAL unit
10790         * @return {object} the parsed SEI payload
10791         * @see Rec. ITU-T H.264, 7.3.2.3.1
10792         */
10793        var parseSei = function(bytes) {
10794            var
10795                i = 0,
10796                result = {
10797                    payloadType: -1,
10798                    payloadSize: 0
10799                },
10800                payloadType = 0,
10801                payloadSize = 0;
10802
10803            // go through the sei_rbsp parsing each each individual sei_message
10804            while (i < bytes.byteLength) {
10805                // stop once we have hit the end of the sei_rbsp
10806                if (bytes[i] === RBSP_TRAILING_BITS) {
10807                    break;
10808                }
10809
10810                // Parse payload type
10811                while (bytes[i] === 0xFF) {
10812                    payloadType += 255;
10813                    i++;
10814                }
10815                payloadType += bytes[i++];
10816
10817                // Parse payload size
10818                while (bytes[i] === 0xFF) {
10819                    payloadSize += 255;
10820                    i++;
10821                }
10822                payloadSize += bytes[i++];
10823
10824                // this sei_message is a 608/708 caption so save it and break
10825                // there can only ever be one caption message in a frame's sei
10826                if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
10827                    result.payloadType = payloadType;
10828                    result.payloadSize = payloadSize;
10829                    result.payload = bytes.subarray(i, i + payloadSize);
10830                    break;
10831                }
10832
10833                // skip the payload and parse the next message
10834                i += payloadSize;
10835                payloadType = 0;
10836                payloadSize = 0;
10837            }
10838
10839            return result;
10840        };
10841
10842// see ANSI/SCTE 128-1 (2013), section 8.1
10843        var parseUserData = function(sei) {
10844            // itu_t_t35_contry_code must be 181 (United States) for
10845            // captions
10846            if (sei.payload[0] !== 181) {
10847                return null;
10848            }
10849
10850            // itu_t_t35_provider_code should be 49 (ATSC) for captions
10851            if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
10852                return null;
10853            }
10854
10855            // the user_identifier should be "GA94" to indicate ATSC1 data
10856            if (String.fromCharCode(sei.payload[3],
10857                sei.payload[4],
10858                sei.payload[5],
10859                sei.payload[6]) !== 'GA94') {
10860                return null;
10861            }
10862
10863            // finally, user_data_type_code should be 0x03 for caption data
10864            if (sei.payload[7] !== 0x03) {
10865                return null;
10866            }
10867
10868            // return the user_data_type_structure and strip the trailing
10869            // marker bits
10870            return sei.payload.subarray(8, sei.payload.length - 1);
10871        };
10872
10873// see CEA-708-D, section 4.4
10874        var parseCaptionPackets = function(pts, userData) {
10875            var results = [], i, count, offset, data;
10876
10877            // if this is just filler, return immediately
10878            if (!(userData[0] & 0x40)) {
10879                return results;
10880            }
10881
10882            // parse out the cc_data_1 and cc_data_2 fields
10883            count = userData[0] & 0x1f;
10884            for (i = 0; i < count; i++) {
10885                offset = i * 3;
10886                data = {
10887                    type: userData[offset + 2] & 0x03,
10888                    pts: pts
10889                };
10890
10891                // capture cc data when cc_valid is 1
10892                if (userData[offset + 2] & 0x04) {
10893                    data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
10894                    results.push(data);
10895                }
10896            }
10897            return results;
10898        };
10899
10900        var CaptionStream = function() {
10901            CaptionStream.prototype.init.call(this);
10902
10903            this.captionPackets_ = [];
10904
10905            this.field1_ = new Cea608Stream(); // eslint-disable-line no-use-before-define
10906
10907            // forward data and done events from field1_ to this CaptionStream
10908            this.field1_.on('data', this.trigger.bind(this, 'data'));
10909            this.field1_.on('done', this.trigger.bind(this, 'done'));
10910        };
10911        CaptionStream.prototype = new Stream();
10912        CaptionStream.prototype.push = function(event) {
10913            var sei, userData;
10914
10915            // only examine SEI NALs
10916            if (event.nalUnitType !== 'sei_rbsp') {
10917                return;
10918            }
10919
10920            // parse the sei
10921            sei = parseSei(event.escapedRBSP);
10922
10923            // ignore everything but user_data_registered_itu_t_t35
10924            if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
10925                return;
10926            }
10927
10928            // parse out the user data payload
10929            userData = parseUserData(sei);
10930
10931            // ignore unrecognized userData
10932            if (!userData) {
10933                return;
10934            }
10935
10936            // parse out CC data packets and save them for later
10937            this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
10938        };
10939
10940        CaptionStream.prototype.flush = function() {
10941            // make sure we actually parsed captions before proceeding
10942            if (!this.captionPackets_.length) {
10943                this.field1_.flush();
10944                return;
10945            }
10946
10947            // In Chrome, the Array#sort function is not stable so add a
10948            // presortIndex that we can use to ensure we get a stable-sort
10949            this.captionPackets_.forEach(function(elem, idx) {
10950                elem.presortIndex = idx;
10951            });
10952
10953            // sort caption byte-pairs based on their PTS values
10954            this.captionPackets_.sort(function(a, b) {
10955                if (a.pts === b.pts) {
10956                    return a.presortIndex - b.presortIndex;
10957                }
10958                return a.pts - b.pts;
10959            });
10960
10961            // Push each caption into Cea608Stream
10962            this.captionPackets_.forEach(this.field1_.push, this.field1_);
10963
10964            this.captionPackets_.length = 0;
10965            this.field1_.flush();
10966            return;
10967        };
10968// ----------------------
10969// Session to Application
10970// ----------------------
10971
10972        var BASIC_CHARACTER_TRANSLATION = {
10973            0x2a: 0xe1,
10974            0x5c: 0xe9,
10975            0x5e: 0xed,
10976            0x5f: 0xf3,
10977            0x60: 0xfa,
10978            0x7b: 0xe7,
10979            0x7c: 0xf7,
10980            0x7d: 0xd1,
10981            0x7e: 0xf1,
10982            0x7f: 0x2588
10983        };
10984
10985        var getCharFromCode = function(code) {
10986            if (code === null) {
10987                return '';
10988            }
10989            code = BASIC_CHARACTER_TRANSLATION[code] || code;
10990            return String.fromCharCode(code);
10991        };
10992
10993// Constants for the byte codes recognized b
10993y Cea608Stream. This
10994// list is not exhaustive. For a more comprehensive listing and
10995// semantics see
10996// http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
10997        var PADDING                    = 0x0000,
10998
10999            // Pop-on Mode
11000            RESUME_CAPTION_LOADING     = 0x1420,
11001            END_OF_CAPTION             = 0x142f,
11002
11003            // Roll-up Mode
11004            ROLL_UP_2_ROWS             = 0x1425,
11005            ROLL_UP_3_ROWS             = 0x1426,
11006            ROLL_UP_4_ROWS             = 0x1427,
11007            CARRIAGE_RETURN            = 0x142d,
11008            // Erasure
11009            BACKSPACE                  = 0x1421,
11010            ERASE_DISPLAYED_MEMORY     = 0x142c,
11011            ERASE_NON_DISPLAYED_MEMORY = 0x142e;
11012
11013// the index of the last row in a CEA-608 display buffer
11014        var BOTTOM_ROW = 14;
11015// CEA-608 captions are rendered onto a 34x15 matrix of character
11016// cells. The "bottom" row is the last element in the outer array.
11017        var createDisplayBuffer = function() {
11018            var result = [], i = BOTTOM_ROW + 1;
11019            while (i--) {
11020                result.push('');
11021            }
11022            return result;
11023        };
11024
11025        var Cea608Stream = function() {
11026            Cea608Stream.prototype.init.call(this);
11027
11028            this.mode_ = 'popOn';
11029            // When in roll-up mode, the index of the last row that will
11030            // actually display captions. If a caption is shifted to a row
11031            // with a lower index than this, it is cleared from the display
11032            // buffer
11033            this.topRow_ = 0;
11034            this.startPts_ = 0;
11035            this.displayed_ = createDisplayBuffer();
11036            this.nonDisplayed_ = createDisplayBuffer();
11037            this.lastControlCode_ = null;
11038
11039            this.push = function(packet) {
11040                // Ignore other channels
11041                if (packet.type !== 0) {
11042                    return;
11043                }
11044                var data, swap, char0, char1;
11045                // remove the parity bits
11046                data = packet.ccData & 0x7f7f;
11047
11048                // ignore duplicate control codes
11049                if (data === this.lastControlCode_) {
11050                    this.lastControlCode_ = null;
11051                    return;
11052                }
11053
11054                // Store control codes
11055                if ((data & 0xf000) === 0x1000) {
11056                    this.lastControlCode_ = data;
11057                } else {
11058                    this.lastControlCode_ = null;
11059                }
11060
11061                switch (data) {
11062                    case PADDING:
11063                        break;
11064                    case RESUME_CAPTION_LOADING:
11065                        this.mode_ = 'popOn';
11066                        break;
11067                    case END_OF_CAPTION:
11068                        // if a caption was being displayed, it's gone now
11069                        this.flushDisplayed(packet.pts);
11070
11071                        // flip memory
11072                        swap = this.displayed_;
11073                        this.displayed_ = this.nonDisplayed_;
11074                        this.nonDisplayed_ = swap;
11075
11076                        // start measuring the time to display the caption
11077                        this.startPts_ = packet.pts;
11078                        break;
11079
11080                    case ROLL_UP_2_ROWS:
11081                        this.topRow_ = BOTTOM_ROW - 1;
11082                        this.mode_ = 'rollUp';
11083                        break;
11084                    case ROLL_UP_3_ROWS:
11085                        this.topRow_ = BOTTOM_ROW - 2;
11086                        this.mode_ = 'rollUp';
11087                        break;
11088                    case ROLL_UP_4_ROWS:
11089                        this.topRow_ = BOTTOM_ROW - 3;
11090                        this.mode_ = 'rollUp';
11091                        break;
11092                    case CARRIAGE_RETURN:
11093                        this.flushDisplayed(packet.pts);
11094                        this.shiftRowsUp_();
11095                        this.startPts_ = packet.pts;
11096                        break;
11097
11098                    case BACKSPACE:
11099                        if (this.mode_ === 'popOn') {
11100                            this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
11101                        } else {
11102                            this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
11103                        }
11104                        break;
11105                    case ERASE_DISPLAYED_MEMORY:
11106                        this.flushDisplayed(packet.pts);
11107                        this.displayed_ = createDisplayBuffer();
11108                        break;
11109                    case ERASE_NON_DISPLAYED_MEMORY:
11110                        this.nonDisplayed_ = createDisplayBuffer();
11111                        break;
11112                    default:
11113                        char0 = data >>> 8;
11114                        char1 = data & 0xff;
11115
11116                        // Look for a Channel 1 Preamble Address Code
11117                        if (char0 >= 0x10 && char0 <= 0x17 &&
11118                            char1 >= 0x40 && char1 <= 0x7F &&
11119                            (char0 !== 0x10 || char1 < 0x60)) {
11120                            // Follow Safari's lead and replace the PAC with a space
11121                            char0 = 0x20;
11122                            // we only want one space so make the second character null
11123                            // which will get become '' in getCharFromCode
11124                            char1 = null;
11125                        }
11126
11127                        // Look for special character sets
11128                        if ((char0 === 0x11 || char0 === 0x19) &&
11129                            (char1 >= 0x30 && char1 <= 0x3F)) {
11130                            // Put in eigth note and space
11131                            char0 = 0x266A;
11132                            char1 = '';
11133                        }
11134
11135                        // ignore unsupported control codes
11136                        if ((char0 & 0xf0) === 0x10) {
11137                            return;
11138                        }
11139
11140                        // remove null chars
11141                        if (char0 === 0x00) {
11142                            char0 = null;
11143                        }
11144                        if (char1 === 0x00) {
11145                            char1 = null;
11146                        }
11147
11148                        // character handling is dependent on the current mode
11149                        this[this.mode_](packet.pts, char0, char1);
11150                        break;
11151                }
11152            };
11153        };
11154        Cea608Stream.prototype = new Stream();
11155// Trigger a cue point that captures the current state of the
11156// display buffer
11157        Cea608Stream.prototype.flushDisplayed = function(pts) {
11158            var content = this.displayed_
11159                // remove spaces from the start and end of the string
11160                .map(function(row) {
11161                    return row.trim();
11162                })
11163                // remove empty rows
11164                .filter(function(row) {
11165                    return row.length;
11166                })
11167                // combine all text rows to display in one cue
11168                .join('\n');
11169
11170            if (content.length) {
11171                this.trigger('data', {
11172                    startPts: this.startPts_,
11173                    endPts: pts,
11174                    text: content
11175                });
11176            }
11177        };
11178
11179// Mode Implementations
11180        Cea608Stream.prototype.popOn = function(pts, char0, char1) {
11181            var baseRow = this.nonDisplayed_[BOTTOM_ROW];
11182
11183            // buffer characters
11184            baseRow += getCharFromCode(char0);
11185            baseRow += getCharFromCode(char1);
11186            this.nonDisplayed_[BOTTOM_ROW] = baseRow;
11187        };
11188
11189        Cea608Stream.prototype.rollUp = function(pts, char0, char1) {
11190            var baseRow = this.displayed_[BOTTOM_ROW];
11191            if (baseRow === '') {
11192                // we're starting to buffer new display input, so flush out the
11193                // current display
11194                this.flushDisplayed(pts);
11195
11196                this.startPts_ = pts;
11197            }
11198
11199            baseRow += getCharFromCode(char0);
11200            baseRow += getCharFromCode(char1);
11201
11202            this.displayed_[BOTTOM_ROW] = baseRow;
11203        };
11204        Cea608Stream.prototype.shiftRowsUp_ = function() {
11205            var i;
11206            // clear out inactive rows
11207            for (i = 0; i < this.topRow_; i++) {
11208                this.displayed_[i] = '';
11209            }
11210            // shift displayed rows up
11211            for (i = this.topRow_; i < BOTTOM_ROW; i++) {
11212                this.displayed_[i] = this.displayed_[i + 1];
11213            }
11214            // clear out the bottom row
11215            this.displayed_[BOTTOM_ROW] = '';
11216        };
11217
11218// exports
11219        module.exports = {
11220            CaptionStream: CaptionStream,
11221            Cea608Stream: Cea608Stream
11222        };
11223
11224    },{"../utils/stream":60}],48:[function(require,module,exports){
11225        /**
11226         * mux.js
11227         *
11228         * Copyright (c) 2015 Brightcove
11229         * All rights reserved.
11230         *
11231         * A stream-based mp2t to mp4 converter. This utility can be used to
11232         * deliver mp4s to a SourceBuffer on platforms that support native
11233         * Media Source Extensions.
11234         */
11235        'use strict';
11236        var Stream = require('../utils/stream.js'),
11237            CaptionStream = require('./caption-stream'),
11238            StreamTypes = require('./stream-types'),
11239            TimestampRolloverStream = require('./timestamp-rollover-stream').TimestampRolloverStream;
11240
11241        var m2tsStreamTypes = require('./stream-types.js');
11242
11243// object types
vendor: 9,193 bytes, lines 11244-11448
11244        var TransportPacketStream, TransportParseStream, ElementaryStream;
11245
11246// constants
11247        var
11248            MP2T_PACKET_LENGTH = 188, // bytes
11249            SYNC_BYTE = 0x47;
11250
11251        /**
11252         * Splits an incoming stream of binary data into MPEG-2 Transport
11253         * Stream packets.
11254         */
11255        TransportPacketStream = function() {
11256            var
11257                buffer = new Uint8Array(MP2T_PACKET_LENGTH),
11258                bytesInBuffer = 0;
11259
11260            TransportPacketStream.prototype.init.call(this);
11261
11262            // Deliver new bytes to the stream.
11263
11264            this.push = function(bytes) {
11265                var
11266                    startIndex = 0,
11267                    endIndex = MP2T_PACKET_LENGTH,
11268                    everything;
11269
11270                // If there are bytes remaining from the last segment, prepend them to the
11271                // bytes that were pushed in
11272                if (bytesInBuffer) {
11273                    everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
11274                    everything.set(buffer.subarray(0, bytesInBuffer));
11275                    everything.set(bytes, bytesInBuffer);
11276                    bytesInBuffer = 0;
11277                } else {
11278                    everything = bytes;
11279                }
11280
11281                // While we have enough data for a packet
11282                while (endIndex < everything.byteLength) {
11283                    // Look for a pair of start and end sync bytes in the data..
11284                    if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
11285                        // We found a packet so emit it and jump one whole packet forward in
11286                        // the stream
11287                        this.trigger('data', everything.subarray(startIndex, endIndex));
11288                        startIndex += MP2T_PACKET_LENGTH;
11289                        endIndex += MP2T_PACKET_LENGTH;
11290                        continue;
11291                    }
11292                    // If we get here, we have somehow become de-synchronized and we need to step
11293                    // forward one byte at a time until we find a pair of sync bytes that denote
11294                    // a packet
11295                    startIndex++;
11296                    endIndex++;
11297                }
11298
11299                // If there was some data left over at the end of the segment that couldn't
11300                // possibly be a whole packet, keep it because it might be the start of a packet
11301                // that continues in the next segment
11302                if (startIndex < everything.byteLength) {
11303                    buffer.set(everything.subarray(startIndex), 0);
11304                    bytesInBuffer = everything.byteLength - startIndex;
11305                }
11306            };
11307
11308            this.flush = function() {
11309                // If the buffer contains a whole packet when we are being flushed, emit it
11310                // and empty the buffer. Otherwise hold onto the data because it may be
11311                // important for decoding the next segment
11312                if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
11313                    this.trigger('data', buffer);
11314                    bytesInBuffer = 0;
11315                }
11316                this.trigger('done');
11317            };
11318        };
11319        TransportPacketStream.prototype = new Stream();
11320
11321        /**
11322         * Accepts an MP2T TransportPacketStream and emits data events with parsed
11323         * forms of the individual transport stream packets.
11324         */
11325        TransportParseStream = function() {
11326            var parsePsi, parsePat, parsePmt, self;
11327            TransportParseStream.prototype.init.call(this);
11328            self = this;
11329
11330            this.packetsWaitingForPmt = [];
11331            this.programMapTable = undefined;
11332
11333            parsePsi = function(payload, psi) {
11334                var offset = 0;
11335
11336                // PSI packets may be split into multiple sections and those
11337                // sections may be split into multiple packets. If a PSI
11338                // section starts in this packet, the payload_unit_start_indicator
11339                // will be true and the first byte of the payload will indicate
11340                // the offset from the current position to the start of the
11341                // section.
11342                if (psi.payloadUnitStartIndicator) {
11343                    offset += payload[offset] + 1;
11344                }
11345
11346                if (psi.type === 'pat') {
11347                    parsePat(payload.subarray(offset), psi);
11348                } else {
11349                    parsePmt(payload.subarray(offset), psi);
11350                }
11351            };
11352
11353            parsePat = function(payload, pat) {
11354                pat.section_number = payload[7]; // eslint-disable-line camelcase
11355                pat.last_section_number = payload[8]; // eslint-disable-line camelcase
11356
11357                // skip the PSI header and parse the first PMT entry
11358                self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
11359                pat.pmtPid = self.pmtPid;
11360            };
11361
11362            /**
11363             * Parse out the relevant fields of a Program Map Table (PMT).
11364             * @param payload {Uint8Array} the PMT-specific portion of an MP2T
11365             * packet. The first byte in this array should be the table_id
11366             * field.
11367             * @param pmt {object} the object that should be decorated with
11368             * fields parsed from the PMT.
11369             */
11370            parsePmt = function(payload, pmt) {
11371                var sectionLength, tableEnd, programInfoLength, offset;
11372
11373                // PMTs can be sent ahead of the time when they should actually
11374                // take effect. We don't believe this should ever be the case
11375                // for HLS but we'll ignore "forward" PMT declarations if we see
11376                // them. Future PMT declarations have the current_next_indicator
11377                // set to zero.
11378                if (!(payload[5] & 0x01)) {
11379                    return;
11380                }
11381
11382                // overwrite any existing program map table
11383                self.programMapTable = {};
11384
11385                // the mapping table ends at the end of the current section
11386                sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
11387                tableEnd = 3 + sectionLength - 4;
11388
11389                // to determine where the table is, we have to figure out how
11390                // long the program info descriptors are
11391                programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];
11392
11393                // advance the offset to the first entry in the mapping table
11394                offset = 12 + programInfoLength;
11395                while (offset < tableEnd) {
11396                    // add an entry that maps the elementary_pid to the stream_type
11397                    self.programMapTable[(payload[offset + 1] & 0x1F) << 8 | payload[offset + 2]] = payload[offset];
11398
11399                    // move to the next table entry
11400                    // skip past the elementary stream descriptors, if present
11401                    offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
11402                }
11403
11404                // record the map on the packet as well
11405                pmt.programMapTable = self.programMapTable;
11406
11407                // if there are any packets waiting for a PMT to be found, process them now
11408                while (self.packetsWaitingForPmt.length) {
11409                    self.processPes_.apply(self, self.packetsWaitingForPmt.shift());
11410                }
11411            };
11412
11413            /**
11414             * Deliver a new MP2T packet to the stream.
11415             */
11416            this.push = function(packet) {
11417                var
11418                    result = {},
11419                    offset = 4;
11420
11421                result.payloadUnitStartIndicator = !!(packet[1] & 0x40);
11422
11423                // pid is a 13-bit field starting at the last bit of packet[1]
11424                result.pid = packet[1] & 0x1f;
11425                result.pid <<= 8;
11426                result.pid |= packet[2];
11427
11428                // if an adaption field is present, its length is specified by the
11429                // fifth byte of the TS packet header. The adaptation field is
11430                // used to add stuffing to PES packets that don't fill a complete
11431                // TS packet, and to specify some forms of timing and control data
11432                // that we do not currently use.
11433                if (((packet[3] & 0x30) >>> 4) > 0x01) {
11434                    offset += packet[offset] + 1;
11435                }
11436
11437                // parse the rest of the packet based on the type
11438                if (result.pid === 0) {
11439                    result.type = 'pat';
11440                    parsePsi(packet.subarray(offset), result);
11441                    this.trigger('data', result);
11442                } else if (result.pid === this.pmtPid) {
11443                    result.type = 'pmt';
11444                    parsePsi(packet.subarray(offset), result);
11445                    this.trigger('data', result);
11446                } else if (this.programMapTable === undefined) {
11447                    // When we have not seen a PMT yet, defer further processing of
11448                    // PES packets until one has been parsed
11449                    this.packetsWaitingForPmt.push([packet, offset, result]);
11450                } else {
11451                    this.processPes_(packet, offset, result);
11452                }
11453            };
11454
11455            this.processPes_ = function(packet, offset, result) {
11456                result.streamType = this.programMapTable[result.pid];
11457                result.type = 'pes';
11458                result.data = packet.subarray(offset);
11459
11460                this.trigger('data', result);
11461            };
11462
11463        };
11464        TransportParseStream.prototype = new Stream();
11465        TransportParseStream.STREAM_TYPES  = {
11466            h264: 0x1b,
11467            adts: 0x0f
11468        };
11469
11470        /**
11471         * Reconsistutes program elementary stream (PES) packets from parsed
11472         * transport stream packets. That is, if you pipe an
11473         * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
11474         * events will be events which capture the bytes for individual PES
11475         * packets plus relevant metadata that has been extracted from the
11476         * container.
11477         */
11478        ElementaryStream = function() {
11479            var
11480                self = this,
11481                // PES packet fragments
11482                video = {
11483                    data: [],
11484                    size: 0
11485                },
11486                audio = {
11487                    data: [],
11488                    size: 0
11489                },
11490                timedMetadata = {
11491                    data: [],
11492                    size: 0
11493                },
11494                parsePes = function(payload, pes) {
11495                    var ptsDtsFlags;
11496
11497                    // find out if this packets starts a new keyframe
11498                    pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
11499                    // PES packets may be annotated with a PTS value, or a PTS value
11500                    // and a DTS value. Determine what combination of values is
11501                    // available to work with.
11502                    ptsDtsFlags = payload[7];
11503
11504                    // PTS and DTS are normally stored as a 33-bit number.  Javascript
11505                    // performs all bitwise operations on 32-bit integers but javascript
11506                    // supports a much greater range (52-bits) of integer using standard
11507                    // mathematical operations.
11508                    // We construct a 31-bit value using bitwise operators over the 31
11509                    // most significant bits and then multiply by 4 (equal to a left-shift
11510                    // of 2) before we add the final 2 least significant bits of the
11511                    // timestamp (equal to an OR.)
11512                    if (ptsDtsFlags & 0xC0) {
11513                        // the PTS and DTS are not written out directly. For information
11514                        // on how they are encoded, see
11515                        // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
11516                        pes.pts = (payload[9] & 0x0E) << 27 |
11517                            (payload[10] & 0xFF) << 20 |
11518                            (payload[11] & 0xFE) << 12 |
11519                            (payload[12] & 0xFF) <<  5 |
11520                            (payload[13] & 0xFE) >>>  3;
11521                        pes.pts *= 4; // Left shift by 2
11522                        pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
11523                        pes.dts = pes.pts;
11524                        if (ptsDtsFlags & 0x40) {
11525                            pes.dts = (payload[14] & 0x0E) << 27 |
11526                                (payload[15] & 0xFF) << 20 |
11527                                (payload[16] & 0xFE) << 12 |
11528                                (payload[17] & 0xFF) << 5 |
11529                                (payload[18] & 0xFE) >>> 3;
11530                            pes.dts *= 4; // Left shift by 2
11531                            pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
11532                        }
11533                    }
11534                    // the data section starts immediately after the PES header.
11535                    // pes_header_data_length specifies the number of header bytes
11536                    // that follow the last byte of the field.
11537                    pes.data = payload.subarray(9 + payload[8]);
11538                },
11539                flushStream = function(stream, type) {
11540                    var
11541                        packetData = new Uint8Array(stream.size),
11542                        event = {
11543                            type: type
11544                        },
11545                        i = 0,
11546                        fragment;
11547
11548                    // do nothing if there is no buffered data
11549                    if (!stream.data.length) {
11550                        return;
11551                    }
11552                    event.trackId = stream.data[0].pid;
11553
11554                    // reassemble the packet
11555                    while (stream.data.length) {
11556                        fragment = stream.data.shift();
11557
11558                        packetData.set(fragment.data, i);
11559                        i += fragment.data.byteLength;
11560                    }
11561
11562                    // parse assembled packet's PES header
11563                    parsePes(packetData, event);
11564
11565                    stream.size = 0;
11566
11567                    self.trigger('data', event);
11568                };
11569
11570            ElementaryStream.prototype.init.call(this);
11571
11572            this.push = function(data) {
11573                ({
11574                    pat: function() {
11575                        // we have to wait for the PMT to arrive as well before we
11576                        // have any meaningful metadata
11577                    },
11578                    pes: function() {
11579                        var stream, streamType;
11580
11581                        switch (data.streamType) {
11582                            case StreamTypes.H264_STREAM_TYPE:
11583                            case m2tsStreamTypes.H264_STREAM_TYPE:
11584                                stream = video;
11585                                streamType = 'video';
11586                                break;
11587                            case StreamTypes.ADTS_STREAM_TYPE:
11588                                stream = audio;
11589                                streamType = 'audio';
11590                                break;
11591                            case StreamTypes.METADATA_STREAM_TYPE:
11592                                stream = timedMetadata;
11593                                streamType = 'timed-metadata';
11594                                break;
11595                            default:
11596                                // ignore unknown stream types
11597                                return;
11598                        }
11599
11600                        // if a new packet is starting, we can flush the completed
11601                        // packet
11602                        if (data.payloadUnitStartIndicator) {
11603                            flushStream(stream, streamType);
11604                        }
11605
11606                        // buffer this fragment until we are sure we've received the
11607                        // complete payload
11608                        stream.data.push(data);
11609                        stream.size += data.data.byteLength;
11610                    },
11611                    pmt: function() {
11612                        var
11613                            event = {
11614                                type: 'metadata',
11615                                tracks: []
11616                            },
11617                            programMapTable = data.programMapTable,
11618                            k,
11619                            track;
11620
11621                        // translate streams to tracks
11622                        for (k in programMapTable) {
11623                            if (programMapTable.hasOwnProperty(k)) {
11624                                track = {
11625                                    timelineStartInfo: {
11626                                        baseMediaDecodeTime: 0
11627                                    }
11628                                };
11629                                track.id = +k;
11630                                if (programMapTable[k] === m2tsStreamTypes.H264_STREAM_TYPE) {
11631                                    track.codec = 'avc';
11632                                    track.type = 'video';
11633                                } else if (programMapTable[k] === m2tsStreamTypes.ADTS_STREAM_TYPE) {
11634                                    track.codec = 'adts';
11635                                    track.type = 'audio';
11636                                }
11637                                event.tracks.push(track);
11638                            }
11639                        }
11640                        self.trigger('data', event);
11641                    }
11642                })[data.type]();
11643            };
11644
11645            /**
11646             * Flush any remaining input. Video PES packets may be of variable
11647             * length. Normally, the start of a new video packet can trigger the
11648             * finalization of the previous packet. That is not possible if no
11649             * more video is forthcoming, however. In that case, some other
11650             * mechanism (like the end of the file) has to be employed. When it is
11651             * clear that no additional data is forthcoming, calling this method
11652             * will flush the buffered packets.
11653             */
11654            this.flush = function() {
11655                // !!THIS ORDER IS IMPORTANT!!
11656                // video first then audio
11657                flushStream(video, 'video');
11658                flushStream(audio, 'audio');
11659                flushStream(timedMetadata, 'timed-metadata');
11660                this.trigger('done');
11661            };
11662        };
11663        ElementaryStream.prototype = new Stream();
11664
11665        var m2ts = {
11666            PAT_PID: 0x0000,
11667            MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
11668            TransportPacketStream: TransportPacketStream,
vendor: 54,702 bytes, lines 11669-13030
11669            TransportParseStream: TransportParseStream,
11670            ElementaryStream: ElementaryStream,
11671            TimestampRolloverStream: TimestampRolloverStream,
11672            CaptionStream: CaptionStream.CaptionStream,
11673            Cea608Stream: CaptionStream.Cea608Stream,
11674            MetadataStream: require('./metadata-stream')
11675        };
11676
11677        for (var type in StreamTypes) {
11678            if (StreamTypes.hasOwnProperty(type)) {
11679                m2ts[type] = StreamTypes[type];
11680            }
11681        }
11682
11683        module.exports = m2ts;
11684
11685    },{"../utils/stream.js":60,"./caption-stream":47,"./metadata-stream":49,"./stream-types":51,"./stream-types.js":51,"./timestamp-rollover-stream":52}],49:[function(require,module,exports){
11686        /**
11687         * Accepts program elementary stream (PES) data events and parses out
11688         * ID3 metadata from them, if present.
11689         * @see http://id3.org/id3v2.3.0
11690         */
11691        'use strict';
11692        var
11693            Stream = require('../utils/stream'),
11694            StreamTypes = require('./stream-types'),
11695            // return a percent-encoded representation of the specified byte range
11696            // @see http://en.wikipedia.org/wiki/Percent-encoding
11697            percentEncode = function(bytes, start, end) {
11698                var i, result = '';
11699                for (i = start; i < end; i++) {
11700                    result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
11701                }
11702                return result;
11703            },
11704            // return the string representation of the specified byte range,
11705            // interpreted as UTf-8.
11706            parseUtf8 = function(bytes, start, end) {
11707                return decodeURIComponent(percentEncode(bytes, start, end));
11708            },
11709            // return the string representation of the specified byte range,
11710            // interpreted as ISO-8859-1.
11711            parseIso88591 = function(bytes, start, end) {
11712                return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
11713            },
11714            parseSyncSafeInteger = function(data) {
11715                return (data[0] << 21) |
11716                    (data[1] << 14) |
11717                    (data[2] << 7) |
11718                    (data[3]);
11719            },
11720            tagParsers = {
11721                TXXX: function(tag) {
11722                    var i;
11723                    if (tag.data[0] !== 3) {
11724                        // ignore frames with unrecognized character encodings
11725                        return;
11726                    }
11727
11728                    for (i = 1; i < tag.data.length; i++) {
11729                        if (tag.data[i] === 0) {
11730                            // parse the text fields
11731                            tag.description = parseUtf8(tag.data, 1, i);
11732                            // do not include the null terminator in the tag value
11733                            tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
11734                            break;
11735                        }
11736                    }
11737                    tag.data = tag.value;
11738                },
11739                WXXX: function(tag) {
11740                    var i;
11741                    if (tag.data[0] !== 3) {
11742                        // ignore frames with unrecognized character encodings
11743                        return;
11744                    }
11745
11746                    for (i = 1; i < tag.data.length; i++) {
11747                        if (tag.data[i] === 0) {
11748                            // parse the description and URL fields
11749                            tag.description = parseUtf8(tag.data, 1, i);
11750                            tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
11751                            break;
11752                        }
11753                    }
11754                },
11755                PRIV: function(tag) {
11756                    var i;
11757
11758                    for (i = 0; i < tag.data.length; i++) {
11759                        if (tag.data[i] === 0) {
11760                            // parse the description and URL fields
11761                            tag.owner = parseIso88591(tag.data, 0, i);
11762                            break;
11763                        }
11764                    }
11765                    tag.privateData = tag.data.subarray(i + 1);
11766                    tag.data = tag.privateData;
11767                }
11768            },
11769            MetadataStream;
11770
11771        MetadataStream = function(options) {
11772            var
11773                settings = {
11774                    debug: !!(options && options.debug),
11775
11776                    // the bytes of the program-level descriptor field in MP2T
11777                    // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
11778                    // program element descriptors"
11779                    descriptor: options && options.descriptor
11780                },
11781                // the total size in bytes of the ID3 tag being parsed
11782                tagSize = 0,
11783                // tag data that is not complete enough to be parsed
11784                buffer = [],
11785                // the total number of bytes currently in the buffer
11786                bufferSize = 0,
11787                i;
11788
11789            MetadataStream.prototype.init.call(this);
11790
11791            // calculate the text track in-band metadata track dispatch type
11792            // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
11793            this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
11794            if (settings.descriptor) {
11795                for (i = 0; i < settings.descriptor.length; i++) {
11796                    this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
11797                }
11798            }
11799
11800            this.push = function(chunk) {
11801                var tag, frameStart, frameSize, frame, i, frameHeader;
11802                if (chunk.type !== 'timed-metadata') {
11803                    return;
11804                }
11805
11806                // if data_alignment_indicator is set in the PES header,
11807                // we must have the start of a new ID3 tag. Assume anything
11808                // remaining in the buffer was malformed and throw it out
11809                if (chunk.dataAlignmentIndicator) {
11810                    bufferSize = 0;
11811                    buffer.length = 0;
11812                }
11813
11814                // ignore events that don't look like ID3 data
11815                if (buffer.length === 0 &&
11816                    (chunk.data.length < 10 ||
11817                        chunk.data[0] !== 'I'.charCodeAt(0) ||
11818                        chunk.data[1] !== 'D'.charCodeAt(0) ||
11819                        chunk.data[2] !== '3'.charCodeAt(0))) {
11820                    if (settings.debug) {
11821                        // eslint-disable-next-line no-console
11822                        console.log('Skipping unrecognized metadata packet');
11823                    }
11824                    return;
11825                }
11826
11827                // add this chunk to the data we've collected so far
11828
11829                buffer.push(chunk);
11830                bufferSize += chunk.data.byteLength;
11831
11832                // grab the size of the entire frame from the ID3 header
11833                if (buffer.length === 1) {
11834                    // the frame size is transmitted as a 28-bit integer in the
11835                    // last four bytes of the ID3 header.
11836                    // The most significant bit of each byte is dropped and the
11837                    // results concatenated to recover the actual value.
11838                    tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
11839
11840                    // ID3 reports the tag size excluding the header but it's more
11841                    // convenient for our comparisons to include it
11842                    tagSize += 10;
11843                }
11844
11845                // if the entire frame has not arrived, wait for more data
11846                if (bufferSize < tagSize) {
11847                    return;
11848                }
11849
11850                // collect the entire frame so it can be parsed
11851                tag = {
11852                    data: new Uint8Array(tagSize),
11853                    frames: [],
11854                    pts: buffer[0].pts,
11855                    dts: buffer[0].dts
11856                };
11857                for (i = 0; i < tagSize;) {
11858                    tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
11859                    i += buffer[0].data.byteLength;
11860                    bufferSize -= buffer[0].data.byteLength;
11861                    buffer.shift();
11862                }
11863
11864                // find the start of the first frame and the end of the tag
11865                frameStart = 10;
11866                if (tag.data[5] & 0x40) {
11867                    // advance the frame start past the extended header
11868                    frameStart += 4; // header size field
11869                    frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
11870
11871                    // clip any padding off the end
11872                    tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
11873                }
11874
11875                // parse one or more ID3 frames
11876                // http://id3.org/id3v2.3.0#ID3v2_frame_overview
11877                do {
11878                    // determine the number of bytes in this frame
11879                    frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
11880                    if (frameSize < 1) {
11881                        // eslint-disable-next-line no-console
11882                        return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
11883                    }
11884                    frameHeader = String.fromCharCode(tag.data[frameStart],
11885                        tag.data[frameStart + 1],
11886                        tag.data[frameStart + 2],
11887                        tag.data[frameStart + 3]);
11888
11889
11890                    frame = {
11891                        id: frameHeader,
11892                        data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
11893                    };
11894                    frame.key = frame.id;
11895                    if (tagParsers[frame.id]) {
11896                        tagParsers[frame.id](frame);
11897
11898                        // handle the special PRIV frame used to indicate the start
11899                        // time for raw AAC data
11900                        if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
11901                            var
11902                                d = frame.data,
11903                                size = ((d[3] & 0x01)  << 30) |
11904                                    (d[4]  << 22) |
11905                                    (d[5] << 14) |
11906                                    (d[6] << 6) |
11907                                    (d[7] >>> 2);
11908
11909                            size *= 4;
11910                            size += d[7] & 0x03;
11911                            frame.timeStamp = size;
11912                            // in raw AAC, all subsequent data will be timestamped based
11913                            // on the value of this frame
11914                            // we couldn't have known the appropriate pts and dts before
11915                            // parsing this ID3 tag so set those values now
11916                            if (tag.pts === undefined && tag.dts === undefined) {
11917                                tag.pts = frame.timeStamp;
11918                                tag.dts = frame.timeStamp;
11919                            }
11920                            this.trigger('timestamp', frame);
11921                        }
11922                    }
11923                    tag.frames.push(frame);
11924
11925                    frameStart += 10; // advance past the frame header
11926                    frameStart += frameSize; // advance past the frame body
11927                } while (frameStart < tagSize);
11928                this.trigger('data', tag);
11929            };
11930        };
11931        MetadataStream.prototype = new Stream();
11932
11933        module.exports = MetadataStream;
11934
11935    },{"../utils/stream":60,"./stream-types":51}],50:[function(require,module,exports){
11936        /**
11937         * mux.js
11938         *
11939         * Copyright (c) 2016 Brightcove
11940         * All rights reserved.
11941         *
11942         * Utilities to detect basic properties and metadata about TS Segments.
11943         */
11944        'use strict';
11945
11946        var StreamTypes = require('./stream-types.js');
11947
11948        var parsePid = function(packet) {
11949            var pid = packet[1] & 0x1f;
11950            pid <<= 8;
11951            pid |= packet[2];
11952            return pid;
11953        };
11954
11955        var parsePayloadUnitStartIndicator = function(packet) {
11956            return !!(packet[1] & 0x40);
11957        };
11958
11959        var parseAdaptionField = function(packet) {
11960            var offset = 0;
11961            // if an adaption field is present, its length is specified by the
11962            // fifth byte of the TS packet header. The adaptation field is
11963            // used to add stuffing to PES packets that don't fill a complete
11964            // TS packet, and to specify some forms of timing and control data
11965            // that we do not currently use.
11966            if (((packet[3] & 0x30) >>> 4) > 0x01) {
11967                offset += packet[4] + 1;
11968            }
11969            return offset;
11970        };
11971
11972        var parseType = function(packet, pmtPid) {
11973            var pid = parsePid(packet);
11974            if (pid === 0) {
11975                return 'pat';
11976            } else if (pid === pmtPid) {
11977                return 'pmt';
11978            } else if (pmtPid) {
11979                return 'pes';
11980            }
11981            return null;
11982        };
11983
11984        var parsePat = function(packet) {
11985            var pusi = parsePayloadUnitStartIndicator(packet);
11986            var offset = 4 + parseAdaptionField(packet);
11987
11988            if (pusi) {
11989                offset += packet[offset] + 1;
11990            }
11991
11992            return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
11993        };
11994
11995        var parsePmt = function(packet) {
11996            var programMapTable = {};
11997            var pusi = parsePayloadUnitStartIndicator(packet);
11998            var payloadOffset = 4 + parseAdaptionField(packet);
11999
12000            if (pusi) {
12001                payloadOffset += packet[payloadOffset] + 1;
12002            }
12003
12004            // PMTs can be sent ahead of the time when they should actually
12005            // take effect. We don't believe this should ever be the case
12006            // for HLS but we'll ignore "forward" PMT declarations if we see
12007            // them. Future PMT declarations have the current_next_indicator
12008            // set to zero.
12009            if (!(packet[payloadOffset + 5] & 0x01)) {
12010                return;
12011            }
12012
12013            var sectionLength, tableEnd, programInfoLength;
12014            // the mapping table ends at the end of the current section
12015            sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
12016            tableEnd = 3 + sectionLength - 4;
12017
12018            // to determine where the table is, we have to figure out how
12019            // long the program info descriptors are
12020            programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11];
12021
12022            // advance the offset to the first entry in the mapping table
12023            var offset = 12 + programInfoLength;
12024            while (offset < tableEnd) {
12025                var i = payloadOffset + offset;
12026                // add an entry that maps the elementary_pid to the stream_type
12027                programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i];
12028
12029                // move to the next table entry
12030                // skip past the elementary stream descriptors, if present
12031                offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
12032            }
12033            return programMapTable;
12034        };
12035
12036        var parsePesType = function(packet, programMapTable) {
12037            var pid = parsePid(packet);
12038            var type = programMapTable[pid];
12039            switch (type) {
12040                case StreamTypes.H264_STREAM_TYPE:
12041                    return 'video';
12042                case StreamTypes.ADTS_STREAM_TYPE:
12043                    return 'audio';
12044                case StreamTypes.METADATA_STREAM_TYPE:
12045                    return 'timed-metadata';
12046                default:
12047                    return null;
12048            }
12049        };
12050
12051        var parsePesTime = function(packet) {
12052            var pusi = parsePayloadUnitStartIndicator(packet);
12053            if (!pusi) {
12054                return null;
12055            }
12056
12057            var offset = 4 + parseAdaptionField(packet);
12058
12059            if (offset >= packet.byteLength) {
12060                // From the H 222.0 MPEG-TS spec
12061                // "For transport stream packets carrying PES packets, stuffing is needed when there
12062                //  is insufficient PES packet data to completely fill the transport stream packet
12063                //  payload bytes. Stuffing is accomplished by defining an adaptation field longer than
12064                //  the sum of the lengths of the data elements in it, so that the payload bytes
12065                //  remaining after the adaptation field exactly accommodates the available PES packet
12066                //  data."
12067                //
12068                // If the offset is >= the length of the packet, then the packet contains no data
12069                // and instead is just adaption field stuffing bytes
12070                return null;
12071            }
12072
12073            var pes = null;
12074            var ptsDtsFlags;
12075
12076            // PES packets may be annotated with a PTS value, or a PTS value
12077            // and a DTS value. Determine what combination of values is
12078            // available to work with.
12079            ptsDtsFlags = packet[offset + 7];
12080
12081            // PTS and DTS are normally stored as a 33-bit number.  Javascript
12082            // performs all bitwise operations on 32-bit integers but javascript
12083            // supports a much greater range (52-bits) of integer using standard
12084            // mathematical operations.
12085            // We construct a 31-bit value using bitwise operators over the 31
12086            // most significant bits and then multiply by 4 (equal to a left-shift
12087            // of 2) before we add the final 2 least significant bits of the
12088            // timestamp (equal to an OR.)
12089            if (ptsDtsFlags & 0xC0) {
12090                pes = {};
12091                // the PTS and DTS are not written out directly. For information
12092                // on how they are encoded, see
12093                // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
12094                pes.pts = (packet[offset + 9] & 0x0E) << 27 |
12095                    (packet[offset + 10] & 0xFF) << 20 |
12096                    (packet[offset + 11] & 0xFE) << 12 |
12097                    (packet[offset + 12] & 0xFF) <<  5 |
12098                    (packet[offset + 13] & 0xFE) >>>  3;
12099                pes.pts *= 4; // Left shift by 2
12100                pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
12101                pes.dts = pes.pts;
12102                if (ptsDtsFlags & 0x40) {
12103                    pes.dts = (packet[offset + 14] & 0x0E) << 27 |
12104                        (packet[offset + 15] & 0xFF) << 20 |
12105                        (packet[offset + 16] & 0xFE) << 12 |
12106                        (packet[offset + 17] & 0xFF) << 5 |
12107                        (packet[offset + 18] & 0xFE) >>> 3;
12108                    pes.dts *= 4; // Left shift by 2
12109                    pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
12110                }
12111            }
12112            return pes;
12113        };
12114
12115        var parseNalUnitType = function(type) {
12116            switch (type) {
12117                case 0x05:
12118                    return 'slice_layer_without_partitioning_rbsp_idr';
12119                case 0x06:
12120                    return 'sei_rbsp';
12121                case 0x07:
12122                    return 'seq_parameter_set_rbsp';
12123                case 0x08:
12124                    return 'pic_parameter_set_rbsp';
12125                case 0x09:
12126                    return 'access_unit_delimiter_rbsp';
12127                default:
12128                    return null;
12129            }
12130        };
12131
12132        var videoPacketContainsKeyFrame = function(packet) {
12133            var offset = 4 + parseAdaptionField(packet);
12134            var frameBuffer = packet.subarray(offset);
12135            var frameI = 0;
12136            var frameSyncPoint = 0;
12137            var foundKeyFrame = false;
12138            var nalType;
12139
12140            // advance the sync point to a NAL start, if necessary
12141            for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
12142                if (frameBuffer[frameSyncPoint + 2] === 1) {
12143                    // the sync point is properly aligned
12144                    frameI = frameSyncPoint + 5;
12145                    break;
12146                }
12147            }
12148
12149            while (frameI < frameBuffer.byteLength) {
12150                // look at the current byte to determine if we've hit the end of
12151                // a NAL unit boundary
12152                switch (frameBuffer[frameI]) {
12153                    case 0:
12154                        // skip past non-sync sequences
12155                        if (frameBuffer[frameI - 1] !== 0) {
12156                            frameI += 2;
12157                            break;
12158                        } else if (frameBuffer[frameI - 2] !== 0) {
12159                            frameI++;
12160                            break;
12161                        }
12162
12163                        if (frameSyncPoint + 3 !== frameI - 2) {
12164                            nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
12165                            if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
12166                                foundKeyFrame = true;
12167                            }
12168                        }
12169
12170                        // drop trailing zeroes
12171                        do {
12172                            frameI++;
12173                        } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
12174                        frameSyncPoint = frameI - 2;
12175                        frameI += 3;
12176                        break;
12177                    case 1:
12178                        // skip past non-sync sequences
12179                        if (frameBuffer[frameI - 1] !== 0 ||
12180                            frameBuffer[frameI - 2] !== 0) {
12181                            frameI += 3;
12182                            break;
12183                        }
12184
12185                        nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
12186                        if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
12187                            foundKeyFrame = true;
12188                        }
12189                        frameSyncPoint = frameI - 2;
12190                        frameI += 3;
12191                        break;
12192                    default:
12193                        // the current byte isn't a one or zero, so it cannot be part
12194                        // of a sync sequence
12195                        frameI += 3;
12196                        break;
12197                }
12198            }
12199            frameBuffer = frameBuffer.subarray(frameSyncPoint);
12200            frameI -= frameSyncPoint;
12201            frameSyncPoint = 0;
12202            // parse the final nal
12203            if (frameBuffer && frameBuffer.byteLength > 3) {
12204                nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
12205                if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
12206                    foundKeyFrame = true;
12207                }
12208            }
12209
12210            return foundKeyFrame;
12211        };
12212
12213
12214        module.exports = {
12215            parseType: parseType,
12216            parsePat: parsePat,
12217            parsePmt: parsePmt,
12218            parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
12219            parsePesType: parsePesType,
12220            parsePesTime: parsePesTime,
12221            videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
12222        };
12223
12224    },{"./stream-types.js":51}],51:[function(require,module,exports){
12225        'use strict';
12226
12227        module.exports = {
12228            H264_STREAM_TYPE: 0x1B,
12229            ADTS_STREAM_TYPE: 0x0F,
12230            METADATA_STREAM_TYPE: 0x15
12231        };
12232
12233    },{}],52:[function(require,module,exports){
12234        /**
12235         * mux.js
12236         *
12237         * Copyright (c) 2016 Brightcove
12238         * All rights reserved.
12239         *
12240         * Accepts program elementary stream (PES) data events and corrects
12241         * decode and presentation time stamps to account for a rollover
12242         * of the 33 bit value.
12243         */
12244
12245        'use strict';
12246
12247        var Stream = require('../utils/stream');
12248
12249        var MAX_TS = 8589934592;
12250
12251        var RO_THRESH = 4294967296;
12252
12253        var handleRollover = function(value, reference) {
12254            var direction = 1;
12255
12256            if (value > reference) {
12257                // If the current timestamp value is greater than our reference timestamp and we detect a
12258                // timestamp rollover, this means the roll over is happening in the opposite direction.
12259                // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
12260                // point will be set to a small number, e.g. 1. The user then seeks backwards over the
12261                // rollover point. In loading this segment, the timestamp values will be very large,
12262                // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
12263                // the time stamp to be `value - 2^33`.
12264                direction = -1;
12265            }
12266
12267            // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
12268            // cause an incorrect adjustment.
12269            while (Math.abs(reference - value) > RO_THRESH) {
12270                value += (direction * MAX_TS);
12271            }
12272
12273            return value;
12274        };
12275
12276        var TimestampRolloverStream = function(type) {
12277            var lastDTS, referenceDTS;
12278
12279            TimestampRolloverStream.prototype.init.call(this);
12280
12281            this.type_ = type;
12282
12283            this.push = function(data) {
12284                if (data.type !== this.type_) {
12285                    return;
12286                }
12287
12288                if (referenceDTS === undefined) {
12289                    referenceDTS = data.dts;
12290                }
12291
12292                data.dts = handleRollover(data.dts, referenceDTS);
12293                data.pts = handleRollover(data.pts, referenceDTS);
12294
12295                lastDTS = data.dts;
12296
12297                this.trigger('data', data);
12298            };
12299
12300            this.flush = function() {
12301                referenceDTS = lastDTS;
12302                this.trigger('done');
12303            };
12304
12305            this.discontinuity = function() {
12306                referenceDTS = void 0;
12307                lastDTS = void 0;
12308            };
12309
12310        };
12311
12312        TimestampRolloverStream.prototype = new Stream();
12313
12314        module.exports = {
12315            TimestampRolloverStream: TimestampRolloverStream,
12316            handleRollover: handleRollover
12317        };
12318
12319    },{"../utils/stream":60}],53:[function(require,module,exports){
12320        module.exports = {
12321            generator: require('./mp4-generator'),
12322            Transmuxer: require('./transmuxer').Transmuxer,
12323            AudioSegmentStream: require('./transmuxer').AudioSegmentStream,
12324            VideoSegmentStream: require('./transmuxer').VideoSegmentStream
12325        };
12326
12327    },{"./mp4-generator":54,"./transmuxer":56}],54:[function(require,module,exports){
12328        /**
12329         * mux.js
12330         *
12331         * Copyright (c) 2015 Brightcove
12332         * All rights reserved.
12333         *
12334         * Functions that generate fragmented MP4s suitable for use with Media
12335         * Source Extensions.
12336         */
12337        'use strict';
12338
12339        var UINT32_MAX = Math.pow(2, 32) - 1;
12340
12341        var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd,
12342            trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex,
12343            trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR,
12344            AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS;
12345
12346// pre-calculate constants
12347        (function() {
12348            var i;
12349            types = {
12350                avc1: [], // codingname
12351                avcC: [],
12352                btrt: [],
12353                dinf: [],
12354                dref: [],
12355                esds: [],
12356                ftyp: [],
12357                hdlr: [],
12358                mdat: [],
12359                mdhd: [],
12360                mdia: [],
12361                mfhd: [],
12362                minf: [],
12363                moof: [],
12364                moov: [],
12365                mp4a: [], // codingname
12366                mvex: [],
12367                mvhd: [],
12368                sdtp: [],
12369                smhd: [],
12370                stbl: [],
12371                stco: [],
12372                stsc: [],
12373                stsd: [],
12374                stsz: [],
12375                stts: [],
12376                styp: [],
12377                tfdt: [],
12378                tfhd: [],
12379                traf: [],
12380                trak: [],
12381                trun: [],
12382                trex: [],
12383                tkhd: [],
12384                vmhd: []
12385            };
12386
12387            // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
12388            // don't throw an error
12389            if (typeof Uint8Array === 'undefined') {
12390                return;
12391            }
12392
12393            for (i in types) {
12394                if (types.hasOwnProperty(i)) {
12395                    types[i] = [
12396                        i.charCodeAt(0),
12397                        i.charCodeAt(1),
12398                        i.charCodeAt(2),
12399                        i.charCodeAt(3)
12400                    ];
12401                }
12402            }
12403
12404            MAJOR_BRAND = new Uint8Array([
12405                'i'.charCodeAt(0),
12406                's'.charCodeAt(0),
12407                'o'.charCodeAt(0),
12408                'm'.charCodeAt(0)
12409            ]);
12410            AVC1_BRAND = new Uint8Array([
12411                'a'.charCodeAt(0),
12412                'v'.charCodeAt(0),
12413                'c'.charCodeAt(0),
12414                '1'.charCodeAt(0)
12415            ]);
12416            MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
12417            VIDEO_HDLR = new Uint8Array([
12418                0x00, // version 0
12419                0x00, 0x00, 0x00, // flags
12420                0x00, 0x00, 0x00, 0x00, // pre_defined
12421                0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
12422                0x00, 0x00, 0x00, 0x00, // reserved
12423                0x00, 0x00, 0x00, 0x00, // reserved
12424                0x00, 0x00, 0x00, 0x00, // reserved
12425                0x56, 0x69, 0x64, 0x65,
12426                0x6f, 0x48, 0x61, 0x6e,
12427                0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
12428            ]);
12429            AUDIO_HDLR = new Uint8Array([
12430                0x00, // version 0
12431                0x00, 0x00, 0x00, // flags
12432                0x00, 0x00, 0x00, 0x00, // pre_defined
12433                0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
12434                0x00, 0x00, 0x00, 0x00, // reserved
12435                0x00, 0x00, 0x00, 0x00, // reserved
12436                0x00, 0x00, 0x00, 0x00, // reserved
12437                0x53, 0x6f, 0x75, 0x6e,
12438                0x64, 0x48, 0x61, 0x6e,
12439                0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
12440            ]);
12441            HDLR_TYPES = {
12442                video: VIDEO_HDLR,
12443                audio: AUDIO_HDLR
12444            };
12445            DREF = new Uint8Array([
12446                0x00, // version 0
12447                0x00, 0x00, 0x00, // flags
12448                0x00, 0x00, 0x00, 0x01, // entry_count
12449                0x00, 0x00, 0x00, 0x0c, // entry_size
12450                0x75, 0x72, 0x6c, 0x20, // 'url' type
12451                0x00, // version 0
12452                0x00, 0x00, 0x01 // entry_flags
12453            ]);
12454            SMHD = new Uint8Array([
12455                0x00,             // version
12456                0x00, 0x00, 0x00, // flags
12457                0x00, 0x00,       // balance, 0 means centered
12458                0x00, 0x00        // reserved
12459            ]);
12460            STCO = new Uint8Array([
12461                0x00, // version
12462                0x00, 0x00, 0x00, // flags
12463                0x00, 0x00, 0x00, 0x00 // entry_count
12464            ]);
12465            STSC = STCO;
12466            STSZ = new Uint8Array([
12467                0x00, // version
12468                0x00, 0x00, 0x00, // flags
12469                0x00, 0x00, 0x00, 0x00, // sample_size
12470                0x00, 0x00, 0x00, 0x00 // sample_count
12471            ]);
12472            STTS = STCO;
12473            VMHD = new Uint8Array([
12474                0x00, // version
12475                0x00, 0x00, 0x01, // flags
12476                0x00, 0x00, // graphicsmode
12477                0x00, 0x00,
12478                0x00, 0x00,
12479                0x00, 0x00 // opcolor
12480            ]);
12481        }());
12482
12483        box = function(type) {
12484            var
12485                payload = [],
12486                size = 0,
12487                i,
12488                result,
12489                view;
12490
12491            for (i = 1; i < arguments.length; i++) {
12492                payload.push(arguments[i]);
12493            }
12494
12495            i = payload.length;
12496
12497            // calculate the total size we need to allocate
12498            while (i--) {
12499                size += payload[i].byteLength;
12500            }
12501            result = new Uint8Array(size + 8);
12502            view = new DataView(result.buffer, result.byteOffset, result.byteLength);
12503            view.setUint32(0, result.byteLength);
12504            result.set(type, 4);
12505
12506            // copy the payload into the result
12507            for (i = 0, size = 8; i < payload.length; i++) {
12508                result.set(payload[i], size);
12509                size += payload[i].byteLength;
12510            }
12511            return result;
12512        };
12513
12514        dinf = function() {
12515            return box(types.dinf, box(types.dref, DREF));
12516        };
12517
12518        esds = function(track) {
12519            return box(types.esds, new Uint8Array([
12520                0x00, // version
12521                0x00, 0x00, 0x00, // flags
12522
12523                // ES_Descriptor
12524                0x03, // tag, ES_DescrTag
12525                0x19, // length
12526                0x00, 0x00, // ES_ID
12527                0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
12528
12529                // DecoderConfigDescriptor
12530                0x04, // tag, DecoderConfigDescrTag
12531                0x11, // length
12532                0x40, // object type
12533                0x15,  // streamType
12534                0x00, 0x06, 0x00, // bufferSizeDB
12535                0x00, 0x00, 0xda, 0xc0, // maxBitrate
12536                0x00, 0x00, 0xda, 0xc0, // avgBitrate
12537
12538                // DecoderSpecificInfo
12539                0x05, // tag, DecoderSpecificInfoTag
12540                0x02, // length
12541                // ISO/IEC 14496-3, AudioSpecificConfig
12542                // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
12543                (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1),
12544                (track.samplingfrequencyindex << 7) | (track.channelcount << 3),
12545                0x06, 0x01, 0x02 // GASpecificConfig
12546            ]));
12547        };
12548
12549        ftyp = function() {
12550            return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
12551        };
12552
12553        hdlr = function(type) {
12554            return box(types.hdlr, HDLR_TYPES[type]);
12555        };
12556        mdat = function(data) {
12557            return box(types.mdat, data);
12558        };
12559        mdhd = function(track) {
12560            var result = new Uint8Array([
12561                0x00,                   // version 0
12562                0x00, 0x00, 0x00,       // flags
12563                0x00, 0x00, 0x00, 0x02, // creation_time
12564                0x00, 0x00, 0x00, 0x03, // modification_time
12565                0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
12566
12567                (track.duration >>> 24) & 0xFF,
12568                (track.duration >>> 16) & 0xFF,
12569                (track.duration >>>  8) & 0xFF,
12570                track.duration & 0xFF,  // duration
12571                0x55, 0xc4,             // 'und' language (undetermined)
12572                0x00, 0x00
12573            ]);
12574
12575            // Use the sample rate from the track metadata, when it is
12576            // defined. The sample rate can be parsed out of an ADTS header, for
12577            // instance.
12578            if (track.samplerate) {
12579                result[12] = (track.samplerate >>> 24) & 0xFF;
12580                result[13] = (track.samplerate >>> 16) & 0xFF;
12581                result[14] = (track.samplerate >>>  8) & 0xFF;
12582                result[15] = (track.samplerate)        & 0xFF;
12583            }
12584
12585            return box(types.mdhd, result);
12586        };
12587        mdia = function(track) {
12588            return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
12589        };
12590        mfhd = function(sequenceNumber) {
12591            return box(types.mfhd, new Uint8Array([
12592                0x00,
12593                0x00, 0x00, 0x00, // flags
12594                (sequenceNumber & 0xFF000000) >> 24,
12595                (sequenceNumber & 0xFF0000) >> 16,
12596                (sequenceNumber & 0xFF00) >> 8,
12597                sequenceNumber & 0xFF // sequence_number
12598            ]));
12599        };
12600        minf = function(track) {
12601            return box(types.minf,
12602                track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD),
12603                dinf(),
12604                stbl(track));
12605        };
12606        moof = function(sequenceNumber, tracks) {
12607            var
12608                trackFragments = [],
12609                i = tracks.length;
12610            // build traf boxes for each track fragment
12611            while (i--) {
12612                trackFragments[i] = traf(tracks[i]);
12613            }
12614            return box.apply(null, [
12615                types.moof,
12616                mfhd(sequenceNumber)
12617            ].concat(trackFragments));
12618        };
12619        /**
12620         * Returns a movie box.
12621         * @param tracks {array} the tracks associated with this movie
12622         * @see ISO/IEC 14496-12:2012(E), section 8.2.1
12623         */
12624        moov = function(tracks) {
12625            var
12626                i = tracks.length,
12627                boxes = [];
12628
12629            while (i--) {
12630                boxes[i] = trak(tracks[i]);
12631            }
12632
12633            return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
12634        };
12635        mvex = function(tracks) {
12636            var
12637                i = tracks.length,
12638                boxes = [];
12639
12640            while (i--) {
12641                boxes[i] = trex(tracks[i]);
12642            }
12643            return box.apply(null, [types.mvex].concat(boxes));
12644        };
12645        mvhd = function(duration) {
12646            var
12647                bytes = new Uint8Array([
12648                    0x00, // version 0
12649                    0x00, 0x00, 0x00, // flags
12650                    0x00, 0x00, 0x00, 0x01, // creation_time
12651                    0x00, 0x00, 0x00, 0x02, // modification_time
12652                    0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
12653                    (duration & 0xFF000000) >> 24,
12654                    (duration & 0xFF0000) >> 16,
12655                    (duration & 0xFF00) >> 8,
12656                    duration & 0xFF, // duration
12657                    0x00, 0x01, 0x00, 0x00, // 1.0 rate
12658                    0x01, 0x00, // 1.0 volume
12659                    0x00, 0x00, // reserved
12660                    0x00, 0x00, 0x00, 0x00, // reserved
12661                    0x00, 0x00, 0x00, 0x00, // reserved
12662                    0x00, 0x01, 0x00, 0x00,
12663                    0x00, 0x00, 0x00, 0x00,
12664                    0x00, 0x00, 0x00, 0x00,
12665                    0x00, 0x00, 0x00, 0x00,
12666                    0x00, 0x01, 0x00, 0x00,
12667                    0x00, 0x00, 0x00, 0x00,
12668                    0x00, 0x00, 0x00, 0x00,
12669                    0x00, 0x00, 0x00, 0x00,
12670                    0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
12671                    0x00, 0x00, 0x00, 0x00,
12672                    0x00, 0x00, 0x00, 0x00,
12673                    0x00, 0x00, 0x00, 0x00,
12674                    0x00, 0x00, 0x00, 0x00,
12675                    0x00, 0x00, 0x00, 0x00,
12676                    0x00, 0x00, 0x00, 0x00, // pre_defined
12677                    0xff, 0xff, 0xff, 0xff // next_track_ID
12678                ]);
12679            return box(types.mvhd, bytes);
12680        };
12681
12682        sdtp = function(track) {
12683            var
12684                samples = track.samples || [],
12685                bytes = new Uint8Array(4 + samples.length),
12686                flags,
12687                i;
12688
12689            // leave the full box header (4 bytes) all zero
12690
12691            // write the sample table
12692            for (i = 0; i < samples.length; i++) {
12693                flags = samples[i].flags;
12694
12695                bytes[i + 4] = (flags.dependsOn << 4) |
12696                    (flags.isDependedOn << 2) |
12697                    (flags.hasRedundancy);
12698            }
12699
12700            return box(types.sdtp,
12701                bytes);
12702        };
12703
12704        stbl = function(track) {
12705            return box(types.stbl,
12706                stsd(track),
12707                box(types.stts, STTS),
12708                box(types.stsc, STSC),
12709                box(types.stsz, STSZ),
12710                box(types.stco, STCO));
12711        };
12712
12713        (function() {
12714            var videoSample, audioSample;
12715
12716            stsd = function(track) {
12717
12718                return box(types.stsd, new Uint8Array([
12719                    0x00, // version 0
12720                    0x00, 0x00, 0x00, // flags
12721                    0x00, 0x00, 0x00, 0x01
12722                ]), track.type === 'video' ? videoSample(track) : audioSample(track));
12723            };
12724
12725            videoSample = function(track) {
12726                var
12727                    sps = track.sps || [],
12728                    pps = track.pps || [],
12729                    sequenceParameterSets = [],
12730                    pictureParameterSets = [],
12731                    i;
12732
12733                // assemble the SPSs
12734                for (i = 0; i < sps.length; i++) {
12735                    sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
12736                    sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength
12737                    sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
12738                }
12739
12740                // assemble the PPSs
12741                for (i = 0; i < pps.length; i++) {
12742                    pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
12743                    pictureParameterSets.push((pps[i].byteLength & 0xFF));
12744                    pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
12745                }
12746
12747                return box(types.avc1, new Uint8Array([
12748                        0x00, 0x00, 0x00,
12749                        0x00, 0x00, 0x00, // reserved
12750                        0x00, 0x01, // data_reference_index
12751                        0x00, 0x00, // pre_defined
12752                        0x00, 0x00, // reserved
12753                        0x00, 0x00, 0x00, 0x00,
12754                        0x00, 0x00, 0x00, 0x00,
12755                        0x00, 0x00, 0x00, 0x00, // pre_defined
12756                        (track.width & 0xff00) >> 8,
12757                        track.width & 0xff, // width
12758                        (track.height & 0xff00) >> 8,
12759                        track.height & 0xff, // height
12760                        0x00, 0x48, 0x00, 0x00, // horizresolution
12761                        0x00, 0x48, 0x00, 0x00, // vertresolution
12762                        0x00, 0x00, 0x00, 0x00, // reserved
12763                        0x00, 0x01, // frame_count
12764                        0x13,
12765                        0x76, 0x69, 0x64, 0x65,
12766                        0x6f, 0x6a, 0x73, 0x2d,
12767                        0x63, 0x6f, 0x6e, 0x74,
12768                        0x72, 0x69, 0x62, 0x2d,
12769                        0x68, 0x6c, 0x73, 0x00,
12770                        0x00, 0x00, 0x00, 0x00,
12771                        0x00, 0x00, 0x00, 0x00,
12772                        0x00, 0x00, 0x00, // compressorname
12773                        0x00, 0x18, // depth = 24
12774                        0x11, 0x11 // pre_defined = -1
12775                    ]), box(types.avcC, new Uint8Array([
12776                        0x01, // configurationVersion
12777                        track.profileIdc, // AVCProfileIndication
12778                        track.profileCompatibility, // profile_compatibility
12779                        track.levelIdc, // AVCLevelIndication
12780                        0xff // lengthSizeMinusOne, hard-coded to 4 bytes
12781                    ].concat([
12782                        sps.length // numOfSequenceParameterSets
12783                    ]).concat(sequenceParameterSets).concat([
12784                        pps.length // numOfPictureParameterSets
12785                    ]).concat(pictureParameterSets))), // "PPS"
12786                    box(types.btrt, new Uint8Array([
12787                        0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
12788                        0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
12789                        0x00, 0x2d, 0xc6, 0xc0
12790                    ])) // avgBitrate
12791                );
12792            };
12793
12794            audioSample = function(track) {
12795                return box(types.mp4a, new Uint8Array([
12796
12797                    // SampleEntry, ISO/IEC 14496-12
12798                    0x00, 0x00, 0x00,
12799                    0x00, 0x00, 0x00, // reserved
12800                    0x00, 0x01, // data_reference_index
12801
12802                    // AudioSampleEntry, ISO/IEC 14496-12
12803                    0x00, 0x00, 0x00, 0x00, // reserved
12804                    0x00, 0x00, 0x00, 0x00, // reserved
12805                    (track.channelcount & 0xff00) >> 8,
12806                    (track.channelcount & 0xff), // channelcount
12807
12808                    (track.samplesize & 0xff00) >> 8,
12809                    (track.samplesize & 0xff), // samplesize
12810                    0x00, 0x00, // pre_defined
12811                    0x00, 0x00, // reserved
12812
12813                    (track.samplerate & 0xff00) >> 8,
12814                    (track.samplerate & 0xff),
12815                    0x00, 0x00 // samplerate, 16.16
12816
12817                    // MP4AudioSampleEntry, ISO/IEC 14496-14
12818                ]), esds(track));
12819            };
12820        }());
12821
12822        tkhd = function(track) {
12823            var result = new Uint8Array([
12824                0x00, // version 0
12825                0x00, 0x00, 0x07, // flags
12826                0x00, 0x00, 0x00, 0x00, // creation_time
12827                0x00, 0x00, 0x00, 0x00, // modification_time
12828                (track.id & 0xFF000000) >> 24,
12829                (track.id & 0xFF0000) >> 16,
12830                (track.id & 0xFF00) >> 8,
12831                track.id & 0xFF, // track_ID
12832                0x00, 0x00, 0x00, 0x00, // reserved
12833                (track.duration & 0xFF000000) >> 24,
12834                (track.duration & 0xFF0000) >> 16,
12835                (track.duration & 0xFF00) >> 8,
12836                track.duration & 0xFF, // duration
12837                0x00, 0x00, 0x00, 0x00,
12838                0x00, 0x00, 0x00, 0x00, // reserved
12839                0x00, 0x00, // layer
12840                0x00, 0x00, // alternate_group
12841                0x01, 0x00, // non-audio track volume
12842                0x00, 0x00, // reserved
12843                0x00, 0x01, 0x00, 0x00,
12844                0x00, 0x00, 0x00, 0x00,
12845                0x00, 0x00, 0x00, 0x00,
12846                0x00, 0x00, 0x00, 0x00,
12847                0x00, 0x01, 0x00, 0x00,
12848                0x00, 0x00, 0x00, 0x00,
12849                0x00, 0x00, 0x00, 0x00,
12850                0x00, 0x00, 0x00, 0x00,
12851                0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
12852                (track.width & 0xFF00) >> 8,
12853                track.width & 0xFF,
12854                0x00, 0x00, // width
12855                (track.height & 0xFF00) >> 8,
12856                track.height & 0xFF,
12857                0x00, 0x00 // height
12858            ]);
12859
12860            return box(types.tkhd, result);
12861        };
12862
12863        /**
12864         * Generate a track fragment (traf) box. A traf box collects metadata
12865         * about tracks in a movie fragment (moof) box.
12866         */
12867        traf = function(track) {
12868            var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun,
12869                sampleDependencyTable, dataOffset,
12870                upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
12871
12872            trackFragmentHeader = box(types.tfhd, new Uint8Array([
12873                0x00, // version 0
12874                0x00, 0x00, 0x3a, // flags
12875                (track.id & 0xFF000000) >> 24,
12876                (track.id & 0xFF0000) >> 16,
12877                (track.id & 0xFF00) >> 8,
12878                (track.id & 0xFF), // track_ID
12879                0x00, 0x00, 0x00, 0x01, // sample_description_index
12880                0x00, 0x00, 0x00, 0x00, // default_sample_duration
12881                0x00, 0x00, 0x00, 0x00, // default_sample_size
12882                0x00, 0x00, 0x00, 0x00  // default_sample_flags
12883            ]));
12884
12885            upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
12886            lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
12887
12888            trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([
12889                0x01, // version 1
12890                0x00, 0x00, 0x00, // flags
12891                // baseMediaDecodeTime
12892                (upperWordBaseMediaDecodeTime >>> 24) & 0xFF,
12893                (upperWordBaseMediaDecodeTime >>> 16) & 0xFF,
12894                (upperWordBaseMediaDecodeTime >>>  8) & 0xFF,
12895                upperWordBaseMediaDecodeTime & 0xFF,
12896                (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF,
12897                (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF,
12898                (lowerWordBaseMediaDecodeTime >>>  8) & 0xFF,
12899                lowerWordBaseMediaDecodeTime & 0xFF
12900            ]));
12901
12902            // the data offset specifies the number of bytes from the start of
12903            // the containing moof to the first payload byte of the associated
12904            // mdat
12905            dataOffset = (32 + // tfhd
12906                20 + // tfdt
12907                8 +  // traf header
12908                16 + // mfhd
12909                8 +  // moof header
12910                8);  // mdat header
12911
12912            // audio tracks require less metadata
12913            if (track.type === 'audio') {
12914                trackFragmentRun = trun(track, dataOffset);
12915                return box(types.traf,
12916                    trackFragmentHeader,
12917                    trackFragmentDecodeTime,
12918                    trackFragmentRun);
12919            }
12920
12921            // video tracks should contain an independent and disposable samples
12922            // box (sdtp)
12923            // generate one and adjust offsets to match
12924            sampleDependencyTable = sdtp(track);
12925            trackFragmentRun = trun(track,
12926                sampleDependencyTable.length + dataOffset);
12927            return box(types.traf,
12928                trackFragmentHeader,
12929                trackFragmentDecodeTime,
12930                trackFragmentRun,
12931                sampleDependencyTable);
12932        };
12933
12934        /**
12935         * Generate a track box.
12936         * @param track {object} a track definition
12937         * @return {Uint8Array} the track box
12938         */
12939        trak = function(track) {
12940            track.duration = track.duration || 0xffffffff;
12941            return box(types.trak,
12942                tkhd(track),
12943                mdia(track));
12944        };
12945
12946        trex = function(track) {
12947            var result = new Uint8Array([
12948                0x00, // version 0
12949                0x00, 0x00, 0x00, // flags
12950                (track.id & 0xFF000000) >> 24,
12951                (track.id & 0xFF0000) >> 16,
12952                (track.id & 0xFF00) >> 8,
12953                (track.id & 0xFF), // track_ID
12954                0x00, 0x00, 0x00, 0x01, // default_sample_description_index
12955                0x00, 0x00, 0x00, 0x00, // default_sample_duration
12956                0x00, 0x00, 0x00, 0x00, // default_sample_size
12957                0x00, 0x01, 0x00, 0x01 // default_sample_flags
12958            ]);
12959            // the last two bytes of default_sample_flags is the sample
12960            // degradation priority, a hint about the importance of this sample
12961            // relative to others. Lower the degradation priority for all sample
12962            // types other than video.
12963            if (track.type !== 'video') {
12964                result[result.length - 1] = 0x00;
12965            }
12966
12967            return box(types.trex, result);
12968        };
12969
12970        (function() {
12971            var audioTrun, videoTrun, trunHeader;
12972
12973            // This method assumes all samples are uniform. That is, if a
12974            // duration is present for the first sample, it will be present for
12975            // all subsequent samples.
12976            // see ISO/IEC 14496-12:2012, Section 8.8.8.1
12977            trunHeader = function(samples, offset) {
12978                var durationPresent = 0, sizePresent = 0,
12979                    flagsPresent = 0, compositionTimeOffset = 0;
12980
12981                // trun flag constants
12982                if (samples.length) {
12983                    if (samples[0].duration !== undefined) {
12984                        durationPresent = 0x1;
12985                    }
12986                    if (samples[0].size !== undefined) {
12987                        sizePresent = 0x2;
12988                    }
12989                    if (samples[0].flags !== undefined) {
12990                        flagsPresent = 0x4;
12991                    }
12992                    if (samples[0].compositionTimeOffset !== undefined) {
12993                        compositionTimeOffset = 0x8;
12994                    }
12995                }
12996
12997                return [
12998                    0x00, // version 0
12999                    0x00,
13000                    durationPresent | sizePresent | flagsPresent | compositionTimeOffset,
13001                    0x01, // flags
13002                    (samples.length & 0xFF000000) >>> 24,
13003                    (samples.length & 0xFF0000) >>> 16,
13004                    (samples.length & 0xFF00) >>> 8,
13005                    samples.length & 0xFF, // sample_count
13006                    (offset & 0xFF000000) >>> 24,
13007                    (offset & 0xFF0000) >>> 16,
13008                    (offset & 0xFF00) >>> 8,
13009                    offset & 0xFF // data_offset
13010                ];
13011            };
13012
13013            videoTrun = function(track, offset) {
13014                var bytes, samples, sample, i;
13015
13016                samples = track.samples || [];
13017                offset += 8 + 12 + (16 * samples.length);
13018
13019                bytes = trunHeader(samples, offset);
13020
13021                for (i = 0; i < samples.length; i++) {
13022                    sample = samples[i];
13023                    bytes = bytes.concat([
13024                        (sample.duration & 0xFF000000) >>> 24,
13025                        (sample.duration & 0xFF0000) >>> 16,
13026                        (sample.duration & 0xFF00) >>> 8,
13027                        sample.duration & 0xFF, // sample_duration
13028                        (sample.size & 0xFF000000) >>> 24,
13029                        (sample.size & 0xFF0000) >>> 16,
13030                        (sample.size & 0xFF00) >>> 8,
13031                        sample.size & 0xFF, // sample_size
13032                        (sample.flags.isLeading << 2) | sample.flags.dependsOn,
13033                        (sample.flags.isDependedOn << 6) |
13034                        (sample.flags.hasRedundancy << 4) |
13035                        (sample.flags.paddingValue << 1) |
13036                        sample.flags.isNonSyncSample,
13037                        sample.flags.degradationPriority & 0xF0 << 8,
13038                        sample.flags.degradationPriority & 0x0F, // sample_flags
13039                        (sample.compositionTimeOffset & 0xFF000000) >>> 24,
13040                        (sample.compositionTimeOffset & 0xFF0000) >>> 16,
13041                        (sample.compositionTimeOffset & 0xFF00) >>> 8,
13042                        sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
13043                    ]);
13044                }
13045                return box(types.trun, new Uint8Array(bytes));
13046            };
13047
13048            audioTrun = function(track, offset) {
13049                var bytes, samples, sample, i;
13050
13051                samples = track.samples || [];
13052                offset += 8 + 12 + (8 * samples.length);
13053
13054                bytes = trunHeader(samples, offset);
13055
13056                for (i = 0; i < samples.length; i++) {
13057                    sample = samples[i];
13058                    bytes = bytes.concat([
13059                        (sample.duration & 0xFF000000) >>> 24,
13060                        (sample.duration & 0xFF0000) >>> 16,
13061                        (sample.duration & 0xFF00) >>> 8,
13062                        sample.duration & 0xFF, // sample_duration
13063                        (sample.size & 0xFF000000) >>> 24,
13064                        (sample.size & 0xFF0000) >>> 16,
13065                        (sample.size & 0xFF00) >>> 8,
13066                        sample.size & 0xFF]); // sample_size
13067                }
13068
13069                return box(types.trun, new Uint8Array(bytes));
13070            };
13071
13072            trun = function(track, offset) {
13073                if (track.type === 'audio') {
13074                    return audioTrun(track, offset);
13075                }
13076
13077                return videoTrun(track, offset);
13078            };
13079        }());
13080
13081        module.exports = {
13082            ftyp: ftyp,
13083            mdat: mdat,
13084            moof: moof,
13085            moov: moov,
13086            initSegment: function(tracks) {
13087                var
13088                    fileType = ftyp(),
13089                    movie = moov(tracks),
13090                    result;
13091
13092                result = new Uint8Array(fileType.byteLength + movie.byteLength);
13093                result.set(fileType);
13094                result.set(movie, fileType.byteLength);
13095                return result;
13096            }
13097        };
13098
13099    },{}],55:[function(require,module,exports){
13100        /**
13101         * mux.js
13102         *
13103         * Copyright (c) 2015 Brightcove
13104         * All rights reserved.
13105         *
13106         * Utilities to detect basic properties and metadata about MP4s.
13107         */
13108        'use strict';
13109
13110        var findBox, parseType, timescale, startTime;
13111
13112// Find the data for a box specified by its path
13113        findBox = function(data, path) {
13114            var results = [],
13115                i, size, type, end, subresults;
13116
13117            if (!path.length) {
13118                // short-circuit the search for empty paths
13119                return null;
13120            }
13121
13122            for (i = 0; i < data.byteLength;) {
13123                size  = data[i]     << 24;
13124                size |= data[i + 1] << 16;
13125                size |= data[i + 2] << 8;
13126                size |= data[i + 3];
13127
13128                type = parseType(data.subarray(i + 4, i + 8));
13129
13130                end = size > 1 ? i + size : data.byteLength;
13131
13132                if (type === path[0]) {
13133                    if (path.length === 1) {
13134                        // this is the end of the path and we've found the box we were
13135                        // looking for
13136                        results.push(data.subarray(i + 8, end));
13137                    } else {
13138                        // recursively search for the next box along the path
13139                        subresults = findBox(data.subarray(i + 8, end), path.slice(1));
13140                        if (subresults.length) {
13141                            results = results.concat(subresults);
13142                        }
13143                    }
13144                }
13145                i = end;
13146            }
13147
13148            // we've finished searching all of data
13149            return results;
13150        };
13151
13152        /**
13153         * Returns the string representation of an ASCII encoded four byte buffer.
13154         * @param buffer {Uint8Array} a four-byte buffer to translate
13155         * @return {string} the corresponding string
13156         */
13157        parseType = function(buffer) {
13158            var result = '';
13159            result += String.fromCharCode(buffer[0]);
13160            result += String.fromCharCode(buffer[1]);
13161            result += String.fromCharCode(buffer[2]);
13162            result += String.fromCharCode(buffer[3]);
13163            return result;
13164        };
13165
13166        /**
13167         * Parses an MP4 initialization segment and extracts the timescale
13168         * values for any declared tracks. Timescale values indicate the
13169         * number of clock ticks per second to assume for time-based values
13170         * elsewhere in the MP4.
13171         *
13172         * To determine the start time of an MP4, you need two pieces of
13173         * information: the timescale unit and the earliest base media decode
13174         * time. Multiple timescales can be specified within an MP4 but the
13175         * base media decode time is always expressed in the timescale from
13176         * the media header box for the track:
13177         * ```
13178         * moov > trak > mdia > mdhd.timescale
13179         * ```
13180         * @param init {Uint8Array} the bytes of the init segment
13181         * @return {object} a hash of track ids to timescale values or null if
13182         * the init segment is malformed.
13183         */
13184        timescale = function(init) {
13185            var
13186                result = {},
13187                traks = findBox(init, ['moov', 'trak']);
13188
13189            // mdhd timescale
13190            return traks.reduce(function(result, trak) {
13191                var tkhd, version, index, id, mdhd;
13192
13193                tkhd = findBox(trak, ['tkhd'])[0];
13194                if (!tkhd) {
13195                    return null;
13196                }
13197                version = tkhd[0];
13198                index = version === 0 ? 12 : 20;
13199                id = tkhd[index]     << 24 |
13200                    tkhd[index + 1] << 16 |
13201                    tkhd[index + 2] <<  8 |
13202                    tkhd[index + 3];
13203
13204                mdhd = findBox(trak, ['mdia', 'mdhd'])[0];
13205                if (!mdhd) {
13206                    return null;
13207                }
13208                version = mdhd[0];
13209                index = version === 0 ? 12 : 20;
13210                result[id] = mdhd[index]     << 24 |
13211                    mdhd[index + 1] << 16 |
13212                    mdhd[index + 2] <<  8 |
13213                    mdhd[index + 3];
13214                return result;
13215            }, result);
13216        };
13217
13218        /**
13219         * Determine the base media decode start time, in seconds, for an MP4
13220         * fragment. If multiple fragments are specified, the earliest time is
13221         * returned.
13222         *
13223         * The base media decode time can be parsed from track fragment
13224         * metadata:
13225         * ```
13226         * moof > traf > tfdt.baseMediaDecodeTime
13227         * ```
13228         * It requires the timescale value from the mdhd to interpret.
13229         *
13230         * @param timescale {object} a hash of track ids to timescale values.
13231         * @return {number} the earliest base media decode start time for the
13232         * fragment, in seconds
13233         */
13234        startTime = function(timescale, fragment) {
13235            var trafs, baseTimes, result;
13236
13237            // we need info from two childrend of each track fragment box
13238            trafs = findBox(fragment, ['moof', 'traf']);
13239
13240            // determine the start times for each track
13241            baseTimes = [].concat.apply([], trafs.map(function(traf) {
13242                return findBox(traf, ['tfhd']).map(function(tfhd) {
13243                    var id, scale, baseTime;
13244
13245                    // get the track id from the tfhd
13246                    id = tfhd[4] << 24 |
13247                        tfhd[5] << 16 |
13248                        tfhd[6] << 8 |
13249                        tfhd[7];
13250                    // assume a 90kHz clock if no timescale was specified
13251                    scale = timescale[id] || 90e3;
13252
13253                    // get the base media decode time from the tfdt
13254                    baseTime = findBox(traf, ['tfdt']).map(function(tfdt) {
13255                        var version, result;
13256
13257                        version = tfdt[0];
13258                        result = tfdt[4] << 24 |
13259                            tfdt[5] << 16 |
13260                            tfdt[6] <<  8 |
13261                            tfdt[7];
13262                        if (version ===  1) {
13263                            result *= Math.pow(2, 32);
13264                            result += tfdt[8]  << 24 |
13265                                tfdt[9]  << 16 |
13266                                tfdt[10] <<  8 |
13267                                tfdt[11];
13268                        }
13269                        return result;
13270                    })[0];
13271                    baseTime = baseTime || Infinity;
13272
13273                    // convert base time to seconds
13274                    return baseTime / scale;
13275                });
13276            }));
13277
13278            // return the minimum
13279            result = Math.min.apply(null, baseTimes);
13280            return isFinite(result) ? result : 0;
13281        };
13282
13283        module.exports = {
13284            parseType: parseType,
13285            timescale: timescale,
13286            startTime: startTime
13287        };
13288
13289    },{}],56:[function(require,module,exports){
13290        /**
13291         * mux.js
13292         *
13293         * Copyright (c) 2015 Brightcove
13294         * All rights reserved.
13295         *
13296         * A stream-based mp2t to mp4 converter. This utility can be used to
13297         * deliver mp4s to a SourceBuffer on platforms that support native
13298         * Media Source Extensions.
13299         */
13300        'use strict';
13301
13302        var Stream = require('../utils/stream.js');
13303        var mp4 = require('./mp4-generator.js');
13304        var m2ts = require('../m2ts/m2ts.js');
13305        var AdtsStream = require('../codecs/adts.js');
13306        var H264Stream = require('../codecs/h264').H264Stream;
13307        var AacStream = require('../aac');
13308        var coneOfSilence = require('../data/silence');
13309        var clock = require('../utils/clock');
13310
13311// constants
13312        var AUDIO_PROPERTIES = [
13313            'audioobjecttype',
13314            'channelcount',
vendor: 8,746 bytes, lines 13315-13563
13315            'samplerate',
13316            'samplingfrequencyindex',
13317            'samplesize'
13318        ];
13319
13320        var VIDEO_PROPERTIES = [
13321            'width',
13322            'height',
13323            'profileIdc',
13324            'levelIdc',
13325            'profileCompatibility'
13326        ];
13327
13328        var ONE_SECOND_IN_TS = 90000; // 90kHz clock
13329
13330// object types
13331        var VideoSegmentStream, AudioSegmentStream, Transmuxer, CoalesceStream;
13332
13333// Helper functions
13334        var
13335            createDefaultSample,
13336            isLikelyAacData,
13337            collectDtsInfo,
13338            clearDtsInfo,
13339            calculateTrackBaseMediaDecodeTime,
13340            arrayEquals,
13341            sumFrameByteLengths;
13342
13343        /**
13344         * Default sample object
13345         * see ISO/IEC 14496-12:2012, section 8.6.4.3
13346         */
13347        createDefaultSample = function() {
13348            return {
13349                size: 0,
13350                flags: {
13351                    isLeading: 0,
13352                    dependsOn: 1,
13353                    isDependedOn: 0,
13354                    hasRedundancy: 0,
13355                    degradationPriority: 0
13356                }
13357            };
13358        };
13359
13360        isLikelyAacData = function(data) {
13361            if ((data[0] === 'I'.charCodeAt(0)) &&
13362                (data[1] === 'D'.charCodeAt(0)) &&
13363                (data[2] === '3'.charCodeAt(0))) {
13364                return true;
13365            }
13366            return false;
13367        };
13368
13369        /**
13370         * Compare two arrays (even typed) for same-ness
13371         */
13372        arrayEquals = function(a, b) {
13373            var
13374                i;
13375
13376            if (a.length !== b.length) {
13377                return false;
13378            }
13379
13380            // compare the value of each element in the array
13381            for (i = 0; i < a.length; i++) {
13382                if (a[i] !== b[i]) {
13383                    return false;
13384                }
13385            }
13386
13387            return true;
13388        };
13389
13390        /**
13391         * Sum the `byteLength` properties of the data in each AAC frame
13392         */
13393        sumFrameByteLengths = function(array) {
13394            var
13395                i,
13396                currentObj,
13397                sum = 0;
13398
13399            // sum the byteLength's all each nal unit in the frame
13400            for (i = 0; i < array.length; i++) {
13401                currentObj = array[i];
13402                sum += currentObj.data.byteLength;
13403            }
13404
13405            return sum;
13406        };
13407
13408        /**
13409         * Constructs a single-track, ISO BMFF media segment from AAC data
13410         * events. The output of this stream can be fed to a SourceBuffer
13411         * configured with a suitable initialization segment.
13412         */
13413        AudioSegmentStream = function(track) {
13414            var
13415                adtsFrames = [],
13416                sequenceNumber = 0,
13417                earliestAllowedDts = 0,
13418                audioAppendStartTs = 0,
13419                videoBaseMediaDecodeTime = Infinity;
13420
13421            AudioSegmentStream.prototype.init.call(this);
13422
13423            this.push = function(data) {
13424                collectDtsInfo(track, data);
13425
13426                if (track) {
13427                    AUDIO_PROPERTIES.forEach(function(prop) {
13428                        track[prop] = data[prop];
13429                    });
13430                }
13431
13432                // buffer audio data until end() is called
13433                adtsFrames.push(data);
13434            };
13435
13436            this.setEarliestDts = function(earliestDts) {
13437                earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
13438            };
13439
13440            this.setVideoBaseMediaDecodeTime = function(baseMediaDecodeTime) {
13441                videoBaseMediaDecodeTime = baseMediaDecodeTime;
13442            };
13443
13444            this.setAudioAppendStart = function(timestamp) {
13445                audioAppendStartTs = timestamp;
13446            };
13447
13448            this.flush = function() {
13449                var
13450                    frames,
13451                    moof,
13452                    mdat,
13453                    boxes;
13454
13455                // return early if no audio data has been observed
13456                if (adtsFrames.length === 0) {
13457                    this.trigger('done', 'AudioSegmentStream');
13458                    return;
13459                }
13460
13461                frames = this.trimAdtsFramesByEarliestDts_(adtsFrames);
13462                track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
13463
13464                this.prefixWithSilence_(track, frames);
13465
13466                // we have to build the index from byte locations to
13467                // samples (that is, adts frames) in the audio data
13468                track.samples = this.generateSampleTable_(frames);
13469
13470                // concatenate the audio data to constuct the mdat
13471                mdat = mp4.mdat(this.concatenateFrameData_(frames));
13472
13473                adtsFrames = [];
13474
13475                moof = mp4.moof(sequenceNumber, [track]);
13476                boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
13477
13478                // bump the sequence number for next time
13479                sequenceNumber++;
13480
13481                boxes.set(moof);
13482                boxes.set(mdat, moof.byteLength);
13483
13484                clearDtsInfo(track);
13485
13486                this.trigger('data', {track: track, boxes: boxes});
13487                this.trigger('done', 'AudioSegmentStream');
13488            };
13489
13490            // Possibly pad (prefix) the audio track with silence if appending this track
13491            // would lead to the introduction of a gap in the audio buffer
13492            this.prefixWithSilence_ = function(track, frames) {
13493                var
13494                    baseMediaDecodeTimeTs,
13495                    frameDuration = 0,
13496                    audioGapDuration = 0,
13497                    audioFillFrameCount = 0,
13498                    audioFillDuration = 0,
13499                    silentFrame,
13500                    i;
13501
13502                if (!frames.length) {
13503                    return;
13504                }
13505
13506                baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate);
13507                // determine frame clock duration based on sample rate, round up to avoid overfills
13508                frameDuration = Math.ceil(ONE_SECOND_IN_TS / (track.samplerate / 1024));
13509
13510                if (audioAppendStartTs && videoBaseMediaDecodeTime) {
13511                    // insert the shortest possible amount (audio gap or audio to video gap)
13512                    audioGapDuration =
13513                        baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime);
13514                    // number of full frames in the audio gap
13515                    audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
13516                    audioFillDuration = audioFillFrameCount * frameDuration;
13517                }
13518
13519                // don't attempt to fill gaps smaller than a single frame or larger
13520                // than a half second
13521                if (audioFillFrameCount < 1 || audioFillDuration > ONE_SECOND_IN_TS / 2) {
13522                    return;
13523                }
13524
13525                silentFrame = coneOfSilence[track.samplerate];
13526
13527                if (!silentFrame) {
13528                    // we don't have a silent frame pregenerated for the sample rate, so use a frame
13529                    // from the content instead
13530                    silentFrame = frames[0].data;
13531                }
13532
13533                for (i = 0; i < audioFillFrameCount; i++) {
13534                    frames.splice(i, 0, {
13535                        data: silentFrame
13536                    });
13537                }
13538
13539                track.baseMediaDecodeTime -=
13540                    Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
13541            };
13542
13543            // If the audio segment extends before the earliest allowed dts
13544            // value, remove AAC frames until starts at or after the earliest
13545            // allowed DTS so that we don't end up with a negative baseMedia-
13546            // DecodeTime for the audio track
13547            this.trimAdtsFramesByEarliestDts_ = function(adtsFrames) {
13548                if (track.minSegmentDts >= earliestAllowedDts) {
13549                    return adtsFrames;
13550                }
13551
13552                // We will need to recalculate the earliest segment Dts
13553                track.minSegmentDts = Infinity;
13554
13555                return adtsFrames.filter(function(currentFrame) {
13556                    // If this is an allowed frame, keep it and record it's Dts
13557                    if (currentFrame.dts >= earliestAllowedDts) {
13558                        track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
13559                        track.minSegmentPts = track.minSegmentDts;
13560                        return true;
13561                    }
13562                    // Otherwise, discard it
13563                    return false;
13564                });
13565            };
13566
13567            // generate the track's raw mdat data from an array of frames
13568            this.generateSampleTable_ = function(frames) {
13569                var
13570                    i,
13571                    currentFrame,
13572                    samples = [];
13573
13574                for (i = 0; i < frames.length; i++) {
13575                    currentFrame = frames[i];
13576                    samples.push({
13577                        size: currentFrame.data.byteLength,
13578                        duration: 1024 // For AAC audio, all samples contain 1024 samples
13579                    });
13580                }
13581                return samples;
13582            };
13583
13584            // generate the track's sample table from an array of frames
13585            this.concatenateFrameData_ = function(frames) {
13586                var
13587                    i,
13588                    currentFrame,
13589                    dataOffset = 0,
13590                    data = new Uint8Array(sumFrameByteLengths(frames));
13591
13592                for (i = 0; i < frames.length; i++) {
13593                    currentFrame = frames[i];
13594
13595                    data.set(currentFrame.data, dataOffset);
13596                    dataOffset += currentFrame.data.byteLength;
13597                }
13598                return data;
13599            };
13600        };
13601
13602        AudioSegmentStream.prototype = new Stream();
13603
13604        /**
13605         * Constructs a single-track, ISO BMFF media segment from H264 data
13606         * events. The output of this stream can be fed to a SourceBuffer
13607         * configured with a suitable initialization segment.
13608         * @param track {object} track metadata configuration
13609         */
13610        VideoSegmentStream = function(track) {
13611            var
13612                sequenceNumber = 0,
13613                nalUnits = [],
13614                config,
13615                pps;
13616
13617            VideoSegmentStream.prototype.init.call(this);
13618
13619            delete track.minPTS;
13620
13621            this.gopCache_ = [];
13622
13623            this.push = function(nalUnit) {
13624                collectDtsInfo(track, nalUnit);
13625
13626                // record the track config
13627                if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
13628                    config = nalUnit.config;
13629                    track.sps = [nalUnit.data];
13630
13631                    VIDEO_PROPERTIES.forEach(function(prop) {
13632                        track[prop] = config[prop];
13633                    }, this);
13634                }
13635
13636                if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' &&
13637                    !pps) {
13638                    pps = nalUnit.data;
13639                    track.pps = [nalUnit.data];
13640                }
13641
13642                // buffer video until flush() is called
13643                nalUnits.push(nalUnit);
13644            };
13645
13646            this.flush = function() {
13647                var
13648                    frames,
13649                    gopForFusion,
13650                    gops,
13651                    moof,
13652                    mdat,
13653                    boxes;
13654
13655                // Throw away nalUnits at the start of the byte stream until
13656                // we find the first AUD
13657                while (nalUnits.length) {
13658                    if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
13659                        break;
13660                    }
13661                    nalUnits.shift();
13662                }
13663
13664                // Return early if no video data has been observed
13665                if (nalUnits.length === 0) {
13666                    this.resetStream_();
13667                    this.trigger('done', 'VideoSegmentStream');
13668                    return;
13669                }
13670
13671                // Organize the raw nal-units into arrays that represent
13672                // higher-level constructs such as frames and gops
13673                // (group-of-pictures)
13674                frames = this.groupNalsIntoFrames_(nalUnits);
13675                gops = this.groupFramesIntoGops_(frames);
13676
13677                // If the first frame of this fragment is not a keyframe we have
13678                // a problem since MSE (on Chrome) requires a leading keyframe.
13679                //
13680                // We have two approaches to repairing this situation:
13681                // 1) GOP-FUSION:
13682                //    This is where we keep track of the GOPS (group-of-pictures)
13683                //    from previous fragments and attempt to find one that we can
13684                //    prepend to the current fragment in order to create a valid
13685                //    fragment.
13686                // 2) KEYFRAME-PULLING:
13687                //    Here we search for the first keyframe in the fragment and
13688                //    throw away all the frames between the start of the fragment
13689                //    and that keyframe. We then extend the duration and pull the
13690                //    PTS of the keyframe forward so that it covers the time range
13691                //    of the frames that were disposed of.
13692                //
13693                // #1 is far prefereable over #2 which can cause "stuttering" but
13694                // requires more things to be just right.
13695                if (!gops[0][0].keyFrame) {
13696                    // Search for a gop for fusion from our gopCache
13697                    gopForFusion = this.getGopForFusion_(nalUnits[0], track);
13698
13699                    if (gopForFusion) {
13700                        gops.unshift(gopForFusion);
13701                        // Adjust Gops' metadata to account for the inclusion of the
13702                        // new gop at the beginning
13703                        gops.byteLength += gopForFusion.byteLength;
13704                        gops.nalCount += gopForFusion.nalCount;
13705                        gops.pts = gopForFusion.pts;
13706                        gops.dts = gopForFusion.dts;
13707                        gops.duration += gopForFusion.duration;
13708                    } else {
13709                        // If we didn't find a candidate gop fall back to keyrame-pulling
13710                        gops = this.extendFirstKeyFrame_(gops);
13711                    }
13712                }
13713                collectDtsInfo(track, gops);
13714
13715                // First, we have to build the index from byte locations to
13716                // samples (that is, frames) in the video data
13717                track.samples = this.generateSampleTable_(gops);
13718
13719                // Concatenate the video data and construct the mdat
13720                mdat = mp4.mdat(this.concatenateNalData_(gops));
13721
13722                // save all the nals in the last GOP into the gop cache
13723                this.gopCache_.unshift({
13724                    gop: gops.pop(),
13725                    pps: track.pps,
13726                    sps: track.sps
13727                });
13728
13729                // Keep a maximum of 6 GOPs in the cache
13730                this.gopCache_.length = Math.min(6, this.gopCache_.length);
13731
13732                // Clear nalUnits
13733                nalUnits = [];
13734
13735                track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
13736
13737                this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
13738                this.trigger('timelineStartInfo', track.timelineStartInfo);
13739
13740                moof = mp4.moof(sequenceNumber, [track]);
13741
13742                // it would be great to allocate this array up front instead of
13743                // throwing away hundreds of media segment fragments
13744                boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
13745
13746                // Bump the sequence number for next time
13747                sequenceNumber++;
13748
13749                boxes.set(moof);
13750                boxes.set(mdat, moof.byteLength);
13751
13752                this.trigger('data', {track: track, boxes: boxes});
13753
vendor: 34,235 bytes, lines 13754-14527
13754                this.resetStream_();
13755
13756                // Continue with the flush process now
13757                this.trigger('done', 'VideoSegmentStream');
13758            };
13759
13760            this.resetStream_ = function() {
13761                clearDtsInfo(track);
13762
13763                // reset config and pps because they may differ across segments
13764                // for instance, when we are rendition switching
13765                config = undefined;
13766                pps = undefined;
13767            };
13768
13769            // Search for a candidate Gop for gop-fusion from the gop cache and
13770            // return it or return null if no good candidate was found
13771            this.getGopForFusion_ = function(nalUnit) {
13772                var
13773                    halfSecond = 45000, // Half-a-second in a 90khz clock
13774                    allowableOverlap = 10000, // About 3 frames @ 30fps
13775                    nearestDistance = Infinity,
13776                    dtsDistance,
13777                    nearestGopObj,
13778                    currentGop,
13779                    currentGopObj,
13780                    i;
13781
13782                // Search for the GOP nearest to the beginning of this nal unit
13783                for (i = 0; i < this.gopCache_.length; i++) {
13784                    currentGopObj = this.gopCache_[i];
13785                    currentGop = currentGopObj.gop;
13786
13787                    // Reject Gops with different SPS or PPS
13788                    if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) ||
13789                        !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
13790                        continue;
13791                    }
13792
13793                    // Reject Gops that would require a negative baseMediaDecodeTime
13794                    if (currentGop.dts < track.timelineStartInfo.dts) {
13795                        continue;
13796                    }
13797
13798                    // The distance between the end of the gop and the start of the nalUnit
13799                    dtsDistance = (nalUnit.dts - currentGop.dts) - currentGop.duration;
13800
13801                    // Only consider GOPS that start before the nal unit and end within
13802                    // a half-second of the nal unit
13803                    if (dtsDistance >= -allowableOverlap &&
13804                        dtsDistance <= halfSecond) {
13805
13806                        // Always use the closest GOP we found if there is more than
13807                        // one candidate
13808                        if (!nearestGopObj ||
13809                            nearestDistance > dtsDistance) {
13810                            nearestGopObj = currentGopObj;
13811                            nearestDistance = dtsDistance;
13812                        }
13813                    }
13814                }
13815
13816                if (nearestGopObj) {
13817                    return nearestGopObj.gop;
13818                }
13819                return null;
13820            };
13821
13822            this.extendFirstKeyFrame_ = function(gops) {
13823                var currentGop;
13824
13825                if (!gops[0][0].keyFrame && gops.length > 1) {
13826                    // Remove the first GOP
13827                    currentGop = gops.shift();
13828
13829                    gops.byteLength -=  currentGop.byteLength;
13830                    gops.nalCount -= currentGop.nalCount;
13831
13832                    // Extend the first frame of what is now the
13833                    // first gop to cover the time period of the
13834                    // frames we just removed
13835                    gops[0][0].dts = currentGop.dts;
13836                    gops[0][0].pts = currentGop.pts;
13837                    gops[0][0].duration += currentGop.duration;
13838                }
13839
13840                return gops;
13841            };
13842
13843            // Convert an array of nal units into an array of frames with each frame being
13844            // composed of the nal units that make up that frame
13845            // Also keep track of cummulative data about the frame from the nal units such
13846            // as the frame duration, starting pts, etc.
13847            this.groupNalsIntoFrames_ = function(nalUnits) {
13848                var
13849                    i,
13850                    currentNal,
13851                    currentFrame = [],
13852                    frames = [];
13853
13854                currentFrame.byteLength = 0;
13855
13856                for (i = 0; i < nalUnits.length; i++) {
13857                    currentNal = nalUnits[i];
13858
13859                    // Split on 'aud'-type nal units
13860                    if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
13861                        // Since the very first nal unit is expected to be an AUD
13862                        // only push to the frames array when currentFrame is not empty
13863                        if (currentFrame.length) {
13864                            currentFrame.duration = currentNal.dts - currentFrame.dts;
13865                            frames.push(currentFrame);
13866                        }
13867                        currentFrame = [currentNal];
13868                        currentFrame.byteLength = currentNal.data.byteLength;
13869                        currentFrame.pts = currentNal.pts;
13870                        currentFrame.dts = currentNal.dts;
13871                    } else {
13872                        // Specifically flag key frames for ease of use later
13873                        if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
13874                            currentFrame.keyFrame = true;
13875                        }
13876                        currentFrame.duration = currentNal.dts - currentFrame.dts;
13877                        currentFrame.byteLength += currentNal.data.byteLength;
13878                        currentFrame.push(currentNal);
13879                    }
13880                }
13881
13882                // For the last frame, use the duration of the previous frame if we
13883                // have nothing better to go on
13884                if (frames.length &&
13885                    (!currentFrame.duration ||
13886                        currentFrame.duration <= 0)) {
13887                    currentFrame.duration = frames[frames.length - 1].duration;
13888                }
13889
13890                // Push the final frame
13891                frames.push(currentFrame);
13892                return frames;
13893            };
13894
13895            // Convert an array of frames into an array of Gop with each Gop being composed
13896            // of the frames that make up that Gop
13897            // Also keep track of cummulative data about the Gop from the frames such as the
13898            // Gop duration, starting pts, etc.
13899            this.groupFramesIntoGops_ = function(frames) {
13900                var
13901                    i,
13902                    currentFrame,
13903                    currentGop = [],
13904                    gops = [];
13905
13906                // We must pre-set some of the values on the Gop since we
13907                // keep running totals of these values
13908                currentGop.byteLength = 0;
13909                currentGop.nalCount = 0;
13910                currentGop.duration = 0;
13911                currentGop.pts = frames[0].pts;
13912                currentGop.dts = frames[0].dts;
13913
13914                // store some metadata about all the Gops
13915                gops.byteLength = 0;
13916                gops.nalCount = 0;
13917                gops.duration = 0;
13918                gops.pts = frames[0].pts;
13919                gops.dts = frames[0].dts;
13920
13921                for (i = 0; i < frames.length; i++) {
13922                    currentFrame = frames[i];
13923
13924                    if (currentFrame.keyFrame) {
13925                        // Since the very first frame is expected to be an keyframe
13926                        // only push to the gops array when currentGop is not empty
13927                        if (currentGop.length) {
13928                            gops.push(currentGop);
13929                            gops.byteLength += currentGop.byteLength;
13930                            gops.nalCount += currentGop.nalCount;
13931                            gops.duration += currentGop.duration;
13932                        }
13933
13934                        currentGop = [currentFrame];
13935                        currentGop.nalCount = currentFrame.length;
13936                        currentGop.byteLength = currentFrame.byteLength;
13937                        currentGop.pts = currentFrame.pts;
13938                        currentGop.dts = currentFrame.dts;
13939                        currentGop.duration = currentFrame.duration;
13940                    } else {
13941                        currentGop.duration += currentFrame.duration;
13942                        currentGop.nalCount += currentFrame.length;
13943                        currentGop.byteLength += currentFrame.byteLength;
13944                        currentGop.push(currentFrame);
13945                    }
13946                }
13947
13948                if (gops.length && currentGop.duration <= 0) {
13949                    currentGop.duration = gops[gops.length - 1].duration;
13950                }
13951                gops.byteLength += currentGop.byteLength;
13952                gops.nalCount += currentGop.nalCount;
13953                gops.duration += currentGop.duration;
13954
13955                // push the final Gop
13956                gops.push(currentGop);
13957                return gops;
13958            };
13959
13960            // generate the track's sample table from an array of gops
13961            this.generateSampleTable_ = function(gops, baseDataOffset) {
13962                var
13963                    h, i,
13964                    sample,
13965                    currentGop,
13966                    currentFrame,
13967                    dataOffset = baseDataOffset || 0,
13968                    samples = [];
13969
13970                for (h = 0; h < gops.length; h++) {
13971                    currentGop = gops[h];
13972
13973                    for (i = 0; i < currentGop.length; i++) {
13974                        currentFrame = currentGop[i];
13975
13976                        sample = createDefaultSample();
13977
13978                        sample.dataOffset = dataOffset;
13979                        sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts;
13980                        sample.duration = currentFrame.duration;
13981                        sample.size = 4 * currentFrame.length; // Space for nal unit size
13982                        sample.size += currentFrame.byteLength;
13983
13984                        if (currentFrame.keyFrame) {
13985                            sample.flags.dependsOn = 2;
13986                        }
13987
13988                        dataOffset += sample.size;
13989
13990                        samples.push(sample);
13991                    }
13992                }
13993                return samples;
13994            };
13995
13996            // generate the track's raw mdat data from an array of gops
13997            this.concatenateNalData_ = function(gops) {
13998                var
13999                    h, i, j,
14000                    currentGop,
14001                    currentFrame,
14002                    currentNal,
14003                    dataOffset = 0,
14004                    nalsByteLength = gops.byteLength,
14005                    numberOfNals = gops.nalCount,
14006                    totalByteLength = nalsByteLength + 4 * numberOfNals,
14007                    data = new Uint8Array(totalByteLength),
14008                    view = new DataView(data.buffer);
14009
14010                // For each Gop..
14011                for (h = 0; h < gops.length; h++) {
14012                    currentGop = gops[h];
14013
14014                    // For each Frame..
14015                    for (i = 0; i < currentGop.length; i++) {
14016                        currentFrame = currentGop[i];
14017
14018                        // For each NAL..
14019                        for (j = 0; j < currentFrame.length; j++) {
14020                            currentNal = currentFrame[j];
14021
14022                            view.setUint32(dataOffset, currentNal.data.byteLength);
14023                            dataOffset += 4;
14024                            data.set(currentNal.data, dataOffset);
14025                            dataOffset += currentNal.data.byteLength;
14026                        }
14027                    }
14028                }
14029                return data;
14030            };
14031        };
14032
14033        VideoSegmentStream.prototype = new Stream();
14034
14035        /**
14036         * Store information about the start and end of the track and the
14037         * duration for each frame/sample we process in order to calculate
14038         * the baseMediaDecodeTime
14039         */
14040        collectDtsInfo = function(track, data) {
14041            if (typeof data.pts === 'number') {
14042                if (track.timelineStartInfo.pts === undefined) {
14043                    track.timelineStartInfo.pts = data.pts;
14044                }
14045
14046                if (track.minSegmentPts === undefined) {
14047                    track.minSegmentPts = data.pts;
14048                } else {
14049                    track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
14050                }
14051
14052                if (track.maxSegmentPts === undefined) {
14053                    track.maxSegmentPts = data.pts;
14054                } else {
14055                    track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
14056                }
14057            }
14058
14059            if (typeof data.dts === 'number') {
14060                if (track.timelineStartInfo.dts === undefined) {
14061                    track.timelineStartInfo.dts = data.dts;
14062                }
14063
14064                if (track.minSegmentDts === undefined) {
14065                    track.minSegmentDts = data.dts;
14066                } else {
14067                    track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
14068                }
14069
14070                if (track.maxSegmentDts === undefined) {
14071                    track.maxSegmentDts = data.dts;
14072                } else {
14073                    track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
14074                }
14075            }
14076        };
14077
14078        /**
14079         * Clear values used to calculate the baseMediaDecodeTime between
14080         * tracks
14081         */
14082        clearDtsInfo = function(track) {
14083            delete track.minSegmentDts;
14084            delete track.maxSegmentDts;
14085            delete track.minSegmentPts;
14086            delete track.maxSegmentPts;
14087        };
14088
14089        /**
14090         * Calculate the track's baseMediaDecodeTime based on the earliest
14091         * DTS the transmuxer has ever seen and the minimum DTS for the
14092         * current track
14093         */
14094        calculateTrackBaseMediaDecodeTime = function(track) {
14095            var
14096                baseMediaDecodeTime,
14097                scale,
14098                // Calculate the distance, in time, that this segment starts from the start
14099                // of the timeline (earliest time seen since the transmuxer initialized)
14100                timeSinceStartOfTimeline = track.minSegmentDts - track.timelineStartInfo.dts;
14101
14102            // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
14103            // we want the start of the first segment to be placed
14104            baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime;
14105
14106            // Add to that the distance this segment is from the very first
14107            baseMediaDecodeTime += timeSinceStartOfTimeline;
14108
14109            // baseMediaDecodeTime must not become negative
14110            baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
14111
14112            if (track.type === 'audio') {
14113                // Audio has a different clock equal to the sampling_rate so we need to
14114                // scale the PTS values into the clock rate of the track
14115                scale = track.samplerate / ONE_SECOND_IN_TS;
14116                baseMediaDecodeTime *= scale;
14117                baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
14118            }
14119
14120            return baseMediaDecodeTime;
14121        };
14122
14123        /**
14124         * A Stream that can combine multiple streams (ie. audio & video)
14125         * into a single output segment for MSE. Also supports audio-only
14126         * and video-only streams.
14127         */
14128        CoalesceStream = function(options, metadataStream) {
14129            // Number of Tracks per output segment
14130            // If greater than 1, we combine multiple
14131            // tracks into a single segment
14132            this.numberOfTracks = 0;
14133            this.metadataStream = metadataStream;
14134
14135            if (typeof options.remux !== 'undefined') {
14136                this.remuxTracks = !!options.remux;
14137            } else {
14138                this.remuxTracks = true;
14139            }
14140
14141            this.pendingTracks = [];
14142            this.videoTrack = null;
14143            this.pendingBoxes = [];
14144            this.pendingCaptions = [];
14145            this.pendingMetadata = [];
14146            this.pendingBytes = 0;
14147            this.emittedTracks = 0;
14148
14149            CoalesceStream.prototype.init.call(this);
14150
14151            // Take output from multiple
14152            this.push = function(output) {
14153                // buffer incoming captions until the associated video segment
14154                // finishes
14155                if (output.text) {
14156                    return this.pendingCaptions.push(output);
14157                }
14158                // buffer incoming id3 tags until the final flush
14159                if (output.frames) {
14160                    return this.pendingMetadata.push(output);
14161                }
14162
14163                // Add this track to the list of pending tracks and store
14164                // important information required for the construction of
14165                // the final segment
14166                this.pendingTracks.push(output.track);
14167                this.pendingBoxes.push(output.boxes);
14168                this.pendingBytes += output.boxes.byteLength;
14169
14170                if (output.track.type === 'video') {
14171                    this.videoTrack = output.track;
14172                }
14173                if (output.track.type === 'audio') {
14174                    this.audioTrack = output.track;
14175                }
14176            };
14177        };
14178
14179        CoalesceStream.prototype = new Stream();
14180        CoalesceStream.prototype.flush = function(flushSource) {
14181            var
14182                offset = 0,
14183                event = {
14184                    captions: [],
14185                    metadata: [],
14186                    info: {}
14187                },
14188                caption,
14189                id3,
14190                initSegment,
14191                timelineStartPts = 0,
14192                i;
14193
14194            if (this.pendingTracks.length < this.numberOfTracks) {
14195                if (flushSource !== 'VideoSegmentStream' &&
14196                    flushSource !== 'AudioSegmentStream') {
14197                    // Return because we haven't received a flush from a data-generating
14198                    // portion of the segment (meaning that we have only recieved meta-data
14199                    // or captions.)
14200                    return;
14201                } else if (this.remuxTracks) {
14202                    // Return until we have enough tracks from the pipeline to remux (if we
14203                    // are remuxing audio and video into a single MP4)
14204                    return;
14205                } else if (this.pendingTracks.length === 0) {
14206                    // In the case where we receive a flush without any data having been
14207                    // received we consider it an emitted track for the purposes of coalescing
14208                    // `done` events.
14209                    // We do this for the case where there is an audio and video track in the
14210                    // segment but no audio data. (seen in several playlists with alternate
14211                    // audio tracks and no audio present in the main TS segments.)
14212                    this.emittedTracks++;
14213
14214                    if (this.emittedTracks >= this.numberOfTracks) {
14215                        this.trigger('done');
14216                        this.emittedTracks = 0;
14217                    }
14218                    return;
14219                }
14220            }
14221
14222            if (this.videoTrack) {
14223                timelineStartPts = this.videoTrack.timelineStartInfo.pts;
14224                VIDEO_PROPERTIES.forEach(function(prop) {
14225                    event.info[prop] = this.videoTrack[prop];
14226                }, this);
14227            } else if (this.audioTrack) {
14228                timelineStartPts = this.audioTrack.timelineStartInfo.pts;
14229                AUDIO_PROPERTIES.forEach(function(prop) {
14230                    event.info[prop] = this.audioTrack[prop];
14231                }, this);
14232            }
14233
14234            if (this.pendingTracks.length === 1) {
14235                event.type = this.pendingTracks[0].type;
14236            } else {
14237                event.type = 'combined';
14238            }
14239
14240            this.emittedTracks += this.pendingTracks.length;
14241
14242            initSegment = mp4.initSegment(this.pendingTracks);
14243
14244            // Create a new typed array to hold the init segment
14245            event.initSegment = new Uint8Array(initSegment.byteLength);
14246
14247            // Create an init segment containing a moov
14248            // and track definitions
14249            event.initSegment.set(initSegment);
14250
14251            // Create a new typed array to hold the moof+mdats
14252            event.data = new Uint8Array(this.pendingBytes);
14253
14254            // Append each moof+mdat (one per track) together
14255            for (i = 0; i < this.pendingBoxes.length; i++) {
14256                event.data.set(this.pendingBoxes[i], offset);
14257                offset += this.pendingBoxes[i].byteLength;
14258            }
14259
14260            // Translate caption PTS times into second offsets into the
14261            // video timeline for the segment
14262            for (i = 0; i < this.pendingCaptions.length; i++) {
14263                caption = this.pendingCaptions[i];
14264                caption.startTime = (caption.startPts - timelineStartPts);
14265                caption.startTime /= 90e3;
14266                caption.endTime = (caption.endPts - timelineStartPts);
14267                caption.endTime /= 90e3;
14268                event.captions.push(caption);
14269            }
14270
14271            // Translate ID3 frame PTS times into second offsets into the
14272            // video timeline for the segment
14273            for (i = 0; i < this.pendingMetadata.length; i++) {
14274                id3 = this.pendingMetadata[i];
14275                id3.cueTime = (id3.pts - timelineStartPts);
14276                id3.cueTime /= 90e3;
14277                event.metadata.push(id3);
14278            }
14279            // We add this to every single emitted segment even though we only need
14280            // it for the first
14281            event.metadata.dispatchType = this.metadataStream.dispatchType;
14282
14283            // Reset stream state
14284            this.pendingTracks.length = 0;
14285            this.videoTrack = null;
14286            this.pendingBoxes.length = 0;
14287            this.pendingCaptions.length = 0;
14288            this.pendingBytes = 0;
14289            this.pendingMetadata.length = 0;
14290
14291            // Emit the built segment
14292            this.trigger('data', event);
14293
14294            // Only emit `done` if all tracks have been flushed and emitted
14295            if (this.emittedTracks >= this.numberOfTracks) {
14296                this.trigger('done');
14297                this.emittedTracks = 0;
14298            }
14299        };
14300        /**
14301         * A Stream that expects MP2T binary data as input and produces
14302         * corresponding media segments, suitable for use with Media Source
14303         * Extension (MSE) implementations that support the ISO BMFF byte
14304         * stream format, like Chrome.
14305         */
14306        Transmuxer = function(options) {
14307            var
14308                self = this,
14309                hasFlushed = true,
14310                videoTrack,
14311                audioTrack;
14312
14313            Transmuxer.prototype.init.call(this);
14314
14315            options = options || {};
14316            this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
14317            this.transmuxPipeline_ = {};
14318
14319            this.setupAacPipeline = function() {
14320                var pipeline = {};
14321                this.transmuxPipeline_ = pipeline;
14322
14323                pipeline.type = 'aac';
14324                pipeline.metadataStream = new m2ts.MetadataStream();
14325
14326                // set up the parsing pipeline
14327                pipeline.aacStream = new AacStream();
14328                pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
14329                pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
14330                pipeline.adtsStream = new AdtsStream();
14331                pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
14332                pipeline.headOfPipeline = pipeline.aacStream;
14333
14334                pipeline.aacStream
14335                    .pipe(pipeline.audioTimestampRolloverStream)
14336                    .pipe(pipeline.adtsStream);
14337                pipeline.aacStream
14338                    .pipe(pipeline.timedMetadataTimestampRolloverStream)
14339                    .pipe(pipeline.metadataStream)
14340                    .pipe(pipeline.coalesceStream);
14341
14342                pipeline.metadataStream.on('timestamp', function(frame) {
14343                    pipeline.aacStream.setTimestamp(frame.timeStamp);
14344                });
14345
14346                pipeline.aacStream.on('data', function(data) {
14347                    if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
14348                        audioTrack = audioTrack || {
14349                            timelineStartInfo: {
14350                                baseMediaDecodeTime: self.baseMediaDecodeTime
14351                            },
14352                            codec: 'adts',
14353                            type: 'audio'
14354                        };
14355                        // hook up the audio segment stream to the first track with aac data
14356                        pipeline.coalesceStream.numberOfTracks++;
14357                        pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
14358                        // Set up the final part of the audio pipeline
14359                        pipeline.adtsStream
14360                            .pipe(pipeline.audioSegmentStream)
14361                            .pipe(pipeline.coalesceStream);
14362                    }
14363                });
14364
14365                // Re-emit any data coming from the coalesce stream to the outside world
14366                pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
14367                // Let the consumer know we have finished flushing the entire pipeline
14368                pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
14369            };
14370
14371            this.setupTsPipeline = function() {
14372                var pipeline = {};
14373                this.transmuxPipeline_ = pipeline;
14374
14375                pipeline.type = 'ts';
14376                pipeline.metadataStream = new m2ts.MetadataStream();
14377
14378                // set up the parsing pipeline
14379                pipeline.packetStream = new m2ts.TransportPacketStream();
14380                pipeline.parseStream = new m2ts.TransportParseStream();
14381                pipeline.elementaryStream = new m2ts.ElementaryStream();
14382                pipeline.videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
14383                pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
14384                pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
14385                pipeline.adtsStream = new AdtsStream();
14386                pipeline.h264Stream = new H264Stream();
14387                pipeline.captionStream = new m2ts.CaptionStream();
14388                pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
14389                pipeline.headOfPipeline = pipeline.packetStream;
14390
14391                // disassemble MPEG2-TS packets into elementary streams
14392                pipeline.packetStream
14393                    .pipe(pipeline.parseStream)
14394                    .pipe(pipeline.elementaryStream);
14395
14396                // !!THIS ORDER IS IMPORTANT!!
14397                // demux the streams
14398                pipeline.elementaryStream
14399                    .pipe(pipeline.videoTimestampRolloverStream)
14400                    .pipe(pipeline.h264Stream);
14401                pipeline.elementaryStream
14402                    .pipe(pipeline.audioTimestampRolloverStream)
14403                    .pipe(pipeline.adtsStream);
14404
14405                pipeline.elementaryStream
14406                    .pipe(pipeline.timedMetadataTimestampRolloverStream)
14407                    .pipe(pipeline.metadataStream)
14408                    .pipe(pipeline.coalesceStream);
14409
14410                // Hook up CEA-608/708 caption stream
14411                pipeline.h264Stream.pipe(pipeline.captionStream)
14412                    .pipe(pipeline.coalesceStream);
14413
14414                pipeline.elementaryStream.on('data', function(data) {
14415                    var i;
14416
14417                    if (data.type === 'metadata') {
14418                        i = data.tracks.length;
14419
14420                        // scan the tracks listed in the metadata
14421                        while (i--) {
14422                            if (!videoTrack && data.tracks[i].type === 'video') {
14423                                videoTrack = data.tracks[i];
14424                                videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
14425                            } else if (!audioTrack && data.tracks[i].type === 'audio') {
14426                                audioTrack = data.tracks[i];
14427                                audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
14428                            }
14429                        }
14430
14431                        // hook up the video segment stream to the first track with h264 data
14432                        if (videoTrack && !pipeline.videoSegmentStream) {
14433                            pipeline.coalesceStream.numberOfTracks++;
14434                            pipeline.videoSegmentStream = new VideoSegmentStream(videoTrack);
14435
14436                            pipeline.videoSegmentStream.on('timelineStartInfo', function(timelineStartInfo) {
14437                                // When video emits timelineStartInfo data after a flush, we forward that
14438                                // info to the AudioSegmentStream, if it exists, because video timeline
14439                                // data takes precedence.
14440                                if (audioTrack) {
14441                                    audioTrack.timelineStartInfo = timelineStartInfo;
14442                                    // On the first segment we trim AAC frames that exist before the
14443                                    // very earliest DTS we have seen in video because Chrome will
14444                                    // interpret any video track with a baseMediaDecodeTime that is
14445                                    // non-zero as a gap.
14446                                    pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
14447                                }
14448                            });
14449
14450                            pipeline.videoSegmentStream.on('baseMediaDecodeTime', function(baseMediaDecodeTime) {
14451                                if (audioTrack) {
14452                                    pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
14453                                }
14454                            });
14455
14456                            // Set up the final part of the video pipeline
14457                            pipeline.h264Stream
14458                                .pipe(pipeline.videoSegmentStream)
14459                                .pipe(pipeline.coalesceStream);
14460                        }
14461
14462                        if (audioTrack && !pipeline.audioSegmentStream) {
14463                            // hook up the audio segment stream to the first track with aac data
14464                            pipeline.coalesceStream.numberOfTracks++;
14465                            pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
14466
14467                            // Set up the final part of the audio pipeline
14468                            pipeline.adtsStream
14469                                .pipe(pipeline.audioSegmentStream)
14470                                .pipe(pipeline.coalesceStream);
14471                        }
14472                    }
14473                });
14474
14475                // Re-emit any data coming from the coalesce stream to the outside world
14476                pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
14477                // Let the consumer know we have finished flushing the entire pipeline
14478                pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
14479            };
14480
14481            // hook up the segment streams once track metadata is delivered
14482            this.setBaseMediaDecodeTime = function(baseMediaDecodeTime) {
14483                var pipeline = this.transmuxPipeline_;
14484
14485                this.baseMediaDecodeTime = baseMediaDecodeTime;
14486                if (audioTrack) {
14487                    audioTrack.timelineStartInfo.dts = undefined;
14488                    audioTrack.timelineStartInfo.pts = undefined;
14489                    clearDtsInfo(audioTrack);
14490                    audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
14491                    if (pipeline.audioTimestampRolloverStream) {
14492                        pipeline.audioTimestampRolloverStream.discontinuity();
14493                    }
14494                }
14495                if (videoTrack) {
14496                    if (pipeline.videoSegmentStream) {
14497                        pipeline.videoSegmentStream.gopCache_ = [];
14498                        pipeline.videoTimestampRolloverStream.discontinuity();
14499                    }
14500                    videoTrack.timelineStartInfo.dts = undefined;
14501                    videoTrack.timelineStartInfo.pts = undefined;
14502                    clearDtsInfo(videoTrack);
14503                    videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
14504                }
14505
14506                if (pipeline.timedMetadataTimestampRolloverStream) {
14507                    pipeline.timedMetadataTimestampRolloverStream.discontinuity();
14508                }
14509            };
14510
14511            this.setAudioAppendStart = function(timestamp) {
14512                if (audioTrack) {
14513                    this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
14514                }
14515            };
14516
14517            // feed incoming data to the front of the parsing pipeline
14518            this.push = function(data) {
14519                if (hasFlushed) {
14520                    var isAac = isLikelyAacData(data);
14521
14522                    if (isAac && this.transmuxPipeline_.type !== 'aac') {
14523                        this.setupAacPipeline();
14524                    } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
14525                        this.setupTsPipeline();
14526                    }
14527                    hasFlushed = false;
14528                }
14529                this.transmuxPipeline_.headOfPipeline.push(data);
14530            };
14531
14532            // flush any buffered data
14533            this.flush = function() {
14534                hasFlushed = true;
14535                // Start at the top of the pipeline and flush all pending work
14536                this.transmuxPipeline_.headOfPipeline.flush();
14537            };
14538        };
14539        Transmuxer.prototype = new Stream();
14540
14541        module.exports = {
14542            Transmuxer: Transmuxer,
14543            VideoSegmentStream: VideoSegmentStream,
14544            AudioSegmentStream: AudioSegmentStream,
14545            AUDIO_PROPERTIES: AUDIO_PROPERTIES,
14546            VIDEO_PROPERTIES: VIDEO_PROPERTIES
14547        };
14548
14549    },{"../aac":36,"../codecs/adts.js":38,"../codecs/h264":39,"../data/silence":40,"../m2ts/m2ts.js":48,"../utils/clock":58,"../utils/stream.js":60,"./mp4-generator.js":54}],57:[function(require,module,exports){
14550        /**
14551         * mux.js
14552         *
14553         * Copyright (c) 2016 Brightcove
14554         * All rights reserved.
14555         *
14556         * Parse mpeg2 transport stream packets to extract basic timing information
14557         */
14558        'use strict';
14559
14560        var StreamTypes = require('../m2ts/stream-types.js');
14561        var handleRollover = require('../m2ts/timestamp-rollover-stream.js').handleRollover;
14562        var probe = {};
14563        probe.ts = require('../m2ts/probe.js');
14564        probe.aac = require('../aac/probe.js');
14565
14566
14567        var
14568            PES_TIMESCALE = 90000,
14569            MP2T_PACKET_LENGTH = 188, // bytes
14570            SYNC_BYTE = 0x47;
14571
14572        var isLikelyAacData = function(data) {
14573            if ((data[0] === 'I'.charCodeAt(0)) &&
14574                (data[1] === 'D'.charCodeAt(0)) &&
14575                (data[2] === '3'.charCodeAt(0))) {
14576                return true;
14577            }
14578            return false;
14579        };
14580
14581        /**
14582         * walks through segment data looking for pat and pmt packets to parse out
14583         * program map table information
14584         */
14585        var parsePsi_ = function(bytes, pmt) {
14586            var
14587                startIndex = 0,
14588                endIndex = MP2T_PACKET_LENGTH,
14589                packet, type;
14590
14591            while (endIndex < bytes.byteLength) {
14592                // Look for a pair of start and end sync bytes in the data..
14593                if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
14594                    // We found a packet
14595                    packet = bytes.subarray(startIndex, endIndex);
14596                    type = probe.ts.parseType(packet, pmt.pid);
14597
14598                    switch (type) {
14599                        case 'pat':
14600                            if (!pmt.pid) {
14601                                pmt.pid = probe.ts.parsePat(packet);
14602                            }
14603                            break;
14604                        case 'pmt':
14605                            if (!pmt.table) {
14606                                pmt.table = probe.ts.parsePmt(packet);
14607                            }
14608                            break;
14609                        default:
14610                            break;
14611                    }
14612
14613                    // Found the pat and pmt, we can stop walking the segment
14614                    if (pmt.pid && pmt.table) {
14615                        return;
14616                    }
14617
14618                    startIndex += MP2T_PACKET_LENGTH;
14619                    endIndex += MP2T_PACKET_LENGTH;
14620                    continue;
14621                }
14622
14623                // If we get here, we have somehow become de-synchronized and we need to step
14624                // forward one byte at a time until we find a pair of sync bytes that denote
14625                // a packet
14626                startIndex++;
14627                endIndex++;
14628            }
14629        };
14630
14631        /**
14632         * walks through the segment data from the start and end to get timing information
14633         * for the first and last audio pes packets
14634         */
14635        var parseAudioPes_ = function(bytes, pmt, result) {
14636            var
14637                startIndex = 0,
14638                endIndex = MP2T_PACKET_LENGTH,
14639                packet, type, pesType, pusi, parsed;
14640
14641            var endLoop = false;
vendor: 25,655 bytes, lines 14642-15291
14642
14643            // Start walking from start of segment to get first audio packet
14644            while (endIndex < bytes.byteLength) {
14645                // Look for a pair of start and end sync bytes in the data..
14646                if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
14647                    // We found a packet
14648                    packet = bytes.subarray(startIndex, endIndex);
14649                    type = probe.ts.parseType(packet, pmt.pid);
14650
14651                    switch (type) {
14652                        case 'pes':
14653                            pesType = probe.ts.parsePesType(packet, pmt.table);
14654                            pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
14655                            if (pesType === 'audio' && pusi) {
14656                                parsed = probe.ts.parsePesTime(packet);
14657                                if (parsed) {
14658                                    parsed.type = 'audio';
14659                                    result.audio.push(parsed);
14660                                    endLoop = true;
14661                                }
14662                            }
14663                            break;
14664                        default:
14665                            break;
14666                    }
14667
14668                    if (endLoop) {
14669                        break;
14670                    }
14671
14672                    startIndex += MP2T_PACKET_LENGTH;
14673                    endIndex += MP2T_PACKET_LENGTH;
14674                    continue;
14675                }
14676
14677                // If we get here, we have somehow become de-synchronized and we need to step
14678                // forward one byte at a time until we find a pair of sync bytes that denote
14679                // a packet
14680                startIndex++;
14681                endIndex++;
14682            }
14683
14684            // Start walking from end of segment to get last audio packet
14685            endIndex = bytes.byteLength;
14686            startIndex = endIndex - MP2T_PACKET_LENGTH;
14687            endLoop = false;
14688            while (startIndex >= 0) {
14689                // Look for a pair of start and end sync bytes in the data..
14690                if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
14691                    // We found a packet
14692                    packet = bytes.subarray(startIndex, endIndex);
14693                    type = probe.ts.parseType(packet, pmt.pid);
14694
14695                    switch (type) {
14696                        case 'pes':
14697                            pesType = probe.ts.parsePesType(packet, pmt.table);
14698                            pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
14699                            if (pesType === 'audio' && pusi) {
14700                                parsed = probe.ts.parsePesTime(packet);
14701                                if (parsed) {
14702                                    parsed.type = 'audio';
14703                                    result.audio.push(parsed);
14704                                    endLoop = true;
14705                                }
14706                            }
14707                            break;
14708                        default:
14709                            break;
14710                    }
14711
14712                    if (endLoop) {
14713                        break;
14714                    }
14715
14716                    startIndex -= MP2T_PACKET_LENGTH;
14717                    endIndex -= MP2T_PACKET_LENGTH;
14718                    continue;
14719                }
14720
14721                // If we get here, we have somehow become de-synchronized and we need to step
14722                // forward one byte at a time until we find a pair of sync bytes that denote
14723                // a packet
14724                startIndex--;
14725                endIndex--;
14726            }
14727        };
14728
14729        /**
14730         * walks through the segment data from the start and end to get timing information
14731         * for the first and last video pes packets as well as timing information for the first
14732         * key frame.
14733         */
14734        var parseVideoPes_ = function(bytes, pmt, result) {
14735            var
14736                startIndex = 0,
14737                endIndex = MP2T_PACKET_LENGTH,
14738                packet, type, pesType, pusi, parsed, frame, i, pes;
14739
14740            var endLoop = false;
14741
14742            var currentFrame = {
14743                data: [],
14744                size: 0
14745            };
14746
14747            // Start walking from start of segment to get first video packet
14748            while (endIndex < bytes.byteLength) {
14749                // Look for a pair of start and end sync bytes in the data..
14750                if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
14751                    // We found a packet
14752                    packet = bytes.subarray(startIndex, endIndex);
14753                    type = probe.ts.parseType(packet, pmt.pid);
14754
14755                    switch (type) {
14756                        case 'pes':
14757                            pesType = probe.ts.parsePesType(packet, pmt.table);
14758                            pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
14759                            if (pesType === 'video') {
14760                                if (pusi && !endLoop) {
14761                                    parsed = probe.ts.parsePesTime(packet);
14762                                    if (parsed) {
14763                                        parsed.type = 'video';
14764                                        result.video.push(parsed);
14765                                        endLoop = true;
14766                                    }
14767                                }
14768                                if (!result.firstKeyFrame) {
14769                                    if (pusi) {
14770                                        if (currentFrame.size !== 0) {
14771                                            frame = new Uint8Array(currentFrame.size);
14772                                            i = 0;
14773                                            while (currentFrame.data.length) {
14774                                                pes = currentFrame.data.shift();
14775                                                frame.set(pes, i);
14776                                                i += pes.byteLength;
14777                                            }
14778                                            if (probe.ts.videoPacketContainsKeyFrame(frame)) {
14779                                                result.firstKeyFrame = probe.ts.parsePesTime(frame);
14780                                                result.firstKeyFrame.type = 'video';
14781                                            }
14782                                            currentFrame.size = 0;
14783                                        }
14784                                    }
14785                                    currentFrame.data.push(packet);
14786                                    currentFrame.size += packet.byteLength;
14787                                }
14788                            }
14789                            break;
14790                        default:
14791                            break;
14792                    }
14793
14794                    if (endLoop && result.firstKeyFrame) {
14795                        break;
14796                    }
14797
14798                    startIndex += MP2T_PACKET_LENGTH;
14799                    endIndex += MP2T_PACKET_LENGTH;
14800                    continue;
14801                }
14802
14803                // If we get here, we have somehow become de-synchronized and we need to step
14804                // forward one byte at a time until we find a pair of sync bytes that denote
14805                // a packet
14806                startIndex++;
14807                endIndex++;
14808            }
14809
14810            // Start walking from end of segment to get last video packet
14811            endIndex = bytes.byteLength;
14812            startIndex = endIndex - MP2T_PACKET_LENGTH;
14813            endLoop = false;
14814            while (startIndex >= 0) {
14815                // Look for a pair of start and end sync bytes in the data..
14816                if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
14817                    // We found a packet
14818                    packet = bytes.subarray(startIndex, endIndex);
14819                    type = probe.ts.parseType(packet, pmt.pid);
14820
14821                    switch (type) {
14822                        case 'pes':
14823                            pesType = probe.ts.parsePesType(packet, pmt.table);
14824                            pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
14825                            if (pesType === 'video' && pusi) {
14826                                parsed = probe.ts.parsePesTime(packet);
14827                                if (parsed) {
14828                                    parsed.type = 'video';
14829                                    result.video.push(parsed);
14830                                    endLoop = true;
14831                                }
14832                            }
14833                            break;
14834                        default:
14835                            break;
14836                    }
14837
14838                    if (endLoop) {
14839                        break;
14840                    }
14841
14842                    startIndex -= MP2T_PACKET_LENGTH;
14843                    endIndex -= MP2T_PACKET_LENGTH;
14844                    continue;
14845                }
14846
14847                // If we get here, we have somehow become de-synchronized and we need to step
14848                // forward one byte at a time until we find a pair of sync bytes that denote
14849                // a packet
14850                startIndex--;
14851                endIndex--;
14852            }
14853        };
14854
14855        /**
14856         * Adjusts the timestamp information for the segment to account for
14857         * rollover and convert to seconds based on pes packet timescale (90khz clock)
14858         */
14859        var adjustTimestamp_ = function(segmentInfo, baseTimestamp) {
14860            if (segmentInfo.audio && segmentInfo.audio.length) {
14861                var audioBaseTimestamp = baseTimestamp;
14862                if (typeof audioBaseTimestamp === 'undefined') {
14863                    audioBaseTimestamp = segmentInfo.audio[0].dts;
14864                }
14865                segmentInfo.audio.forEach(function(info) {
14866                    info.dts = handleRollover(info.dts, audioBaseTimestamp);
14867                    info.pts = handleRollover(info.pts, audioBaseTimestamp);
14868                    // time in seconds
14869                    info.dtsTime = info.dts / PES_TIMESCALE;
14870                    info.ptsTime = info.pts / PES_TIMESCALE;
14871                });
14872            }
14873
14874            if (segmentInfo.video && segmentInfo.video.length) {
14875                var videoBaseTimestamp = baseTimestamp;
14876                if (typeof videoBaseTimestamp === 'undefined') {
14877                    videoBaseTimestamp = segmentInfo.video[0].dts;
14878                }
14879                segmentInfo.video.forEach(function(info) {
14880                    info.dts = handleRollover(info.dts, videoBaseTimestamp);
14881                    info.pts = handleRollover(info.pts, videoBaseTimestamp);
14882                    // time in seconds
14883                    info.dtsTime = info.dts / PES_TIMESCALE;
14884                    info.ptsTime = info.pts / PES_TIMESCALE;
14885                });
14886                if (segmentInfo.firstKeyFrame) {
14887                    var frame = segmentInfo.firstKeyFrame;
14888                    frame.dts = handleRollover(frame.dts, videoBaseTimestamp);
14889                    frame.pts = handleRollover(frame.pts, videoBaseTimestamp);
14890                    // time in seconds
14891                    frame.dtsTime = frame.dts / PES_TIMESCALE;
14892                    frame.ptsTime = frame.dts / PES_TIMESCALE;
14893                }
14894            }
14895        };
14896
14897        /**
14898         * inspects the aac data stream for start and end time information
14899         */
14900        var inspectAac_ = function(bytes) {
14901            var
14902                endLoop = false,
14903                audioCount = 0,
14904                sampleRate = null,
14905                timestamp = null,
14906                frameSize = 0,
14907                byteIndex = 0,
14908                packet;
14909
14910            while (bytes.length - byteIndex >= 3) {
14911                var type = probe.aac.parseType(bytes, byteIndex);
14912                switch (type) {
14913                    case 'timed-metadata':
14914                        // Exit early because we don't have enough to parse
14915                        // the ID3 tag header
14916                        if (bytes.length - byteIndex < 10) {
14917                            endLoop = true;
14918                            break;
14919                        }
14920
14921                        frameSize = probe.aac.parseId3TagSize(bytes, byteIndex);
14922
14923                        // Exit early if we don't have enough in the buffer
14924                        // to emit a full packet
14925                        if (frameSize > bytes.length) {
14926                            endLoop = true;
14927                            break;
14928                        }
14929                        if (timestamp === null) {
14930                            packet = bytes.subarray(byteIndex, byteIndex + frameSize);
14931                            timestamp = probe.aac.parseAacTimestamp(packet);
14932                        }
14933                        byteIndex += frameSize;
14934                        break;
14935                    case 'audio':
14936                        // Exit early because we don't have enough to parse
14937                        // the ADTS frame header
14938                        if (bytes.length - byteIndex < 7) {
14939                            endLoop = true;
14940                            break;
14941                        }
14942
14943                        frameSize = probe.aac.parseAdtsSize(bytes, byteIndex);
14944
14945                        // Exit early if we don't have enough in the buffer
14946                        // to emit a full packet
14947                        if (frameSize > bytes.length) {
14948                            endLoop = true;
14949                            break;
14950                        }
14951                        if (sampleRate === null) {
14952                            packet = bytes.subarray(byteIndex, byteIndex + frameSize);
14953                            sampleRate = probe.aac.parseSampleRate(packet);
14954                        }
14955                        audioCount++;
14956                        byteIndex += frameSize;
14957                        break;
14958                    default:
14959                        byteIndex++;
14960                        break;
14961                }
14962                if (endLoop) {
14963                    return null;
14964                }
14965            }
14966            if (sampleRate === null || timestamp === null) {
14967                return null;
14968            }
14969
14970            var audioTimescale = PES_TIMESCALE / sampleRate;
14971
14972            var result = {
14973                audio: [
14974                    {
14975                        type: 'audio',
14976                        dts: timestamp,
14977                        pts: timestamp
14978                    },
14979                    {
14980                        type: 'audio',
14981                        dts: timestamp + (audioCount * 1024 * audioTimescale),
14982                        pts: timestamp + (audioCount * 1024 * audioTimescale)
14983                    }
14984                ]
14985            };
14986
14987            return result;
14988        };
14989
14990        /**
14991         * inspects the transport stream segment data for start and end time information
14992         * of the audio and video tracks (when present) as well as the first key frame's
14993         * start time.
14994         */
14995        var inspectTs_ = function(bytes) {
14996            var pmt = {
14997                pid: null,
14998                table: null
14999            };
15000
15001            var result = {};
15002
15003            parsePsi_(bytes, pmt);
15004
15005            for (var pid in pmt.table) {
15006                if (pmt.table.hasOwnProperty(pid)) {
15007                    var type = pmt.table[pid];
15008                    switch (type) {
15009                        case StreamTypes.H264_STREAM_TYPE:
15010                            result.video = [];
15011                            parseVideoPes_(bytes, pmt, result);
15012                            if (result.video.length === 0) {
15013                                delete result.video;
15014                            }
15015                            break;
15016                        case StreamTypes.ADTS_STREAM_TYPE:
15017                            result.audio = [];
15018                            parseAudioPes_(bytes, pmt, result);
15019                            if (result.audio.length === 0) {
15020                                delete result.audio;
15021                            }
15022                            break;
15023                        default:
15024                            break;
15025                    }
15026                }
15027            }
15028            return result;
15029        };
15030
15031        /**
15032         * Inspects segment byte data and returns an object with start and end timing information
15033         *
15034         * @param {Uint8Array} bytes The segment byte data
15035         * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame
15036         *  timestamps for rollover. This value must be in 90khz clock.
15037         * @return {Object} Object containing start and end frame timing info of segment.
15038         */
15039        var inspect = function(bytes, baseTimestamp) {
15040            var isAacData = isLikelyAacData(bytes);
15041
15042            var result;
15043
15044            if (isAacData) {
15045                result = inspectAac_(bytes);
15046            } else {
15047                result = inspectTs_(bytes);
15048            }
15049
15050            if (!result || (!result.audio && !result.video)) {
15051                return null;
15052            }
15053
15054            adjustTimestamp_(result, baseTimestamp);
15055
15056            return result;
15057        };
15058
15059        module.exports = {
15060            inspect: inspect
15061        };
15062
15063    },{"../aac/probe.js":37,"../m2ts/probe.js":50,"../m2ts/stream-types.js":51,"../m2ts/timestamp-rollover-stream.js":52}],58:[function(require,module,exports){
15064        var
15065            ONE_SECOND_IN_TS = 90000, // 90kHz clock
15066            secondsToVideoTs,
15067            secondsToAudioTs,
15068            videoTsToSeconds,
15069            audioTsToSeconds,
15070            audioTsToVideoTs,
15071            videoTsToAudioTs;
15072
15073        secondsToVideoTs = function(seconds) {
15074            return seconds * ONE_SECOND_IN_TS;
15075        };
15076
15077        secondsToAudioTs = function(seconds, sampleRate) {
15078            return seconds * sampleRate;
15079        };
15080
15081        videoTsToSeconds = function(timestamp) {
15082            return timestamp / ONE_SECOND_IN_TS;
15083        };
15084
15085        audioTsToSeconds = function(timestamp, sampleRate) {
15086            return timestamp / sampleRate;
15087        };
15088
15089        audioTsToVideoTs = function(timestamp, sampleRate) {
15090            return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
15091        };
15092
15093        videoTsToAudioTs = function(timestamp, sampleRate) {
15094            return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
15095        };
15096
15097        module.exports = {
15098            secondsToVideoTs: secondsToVideoTs,
15099            secondsToAudioTs: secondsToAudioTs,
15100            videoTsToSeconds: videoTsToSeconds,
15101            audioTsToSeconds: audioTsToSeconds,
15102            audioTsToVideoTs: audioTsToVideoTs,
15103            videoTsToAudioTs: videoTsToAudioTs
15104        };
15105
15106    },{}],59:[function(require,module,exports){
15107        'use strict';
15108
15109        var ExpGolomb;
15110
15111        /**
15112         * Parser for exponential Golomb codes, a variable-bitwidth number encoding
15113         * scheme used by h264.
15114         */
15115        ExpGolomb = function(workingData) {
15116            var
15117                // the number of bytes left to examine in workingData
15118                workingBytesAvailable = workingData.byteLength,
15119
15120                // the current word being examined
15121                workingWord = 0, // :uint
15122
15123                // the number of bits left to examine in the current word
15124                workingBitsAvailable = 0; // :uint;
15125
15126            // ():uint
15127            this.length = function() {
15128                return (8 * workingBytesAvailable);
15129            };
15130
15131            // ():uint
15132            this.bitsAvailable = function() {
15133                return (8 * workingBytesAvailable) + workingBitsAvailable;
15134            };
15135
15136            // ():void
15137            this.loadWord = function() {
15138                var
15139                    position = workingData.byteLength - workingBytesAvailable,
15140                    workingBytes = new Uint8Array(4),
15141                    availableBytes = Math.min(4, workingBytesAvailable);
15142
15143                if (availableBytes === 0) {
15144                    throw new Error('no bytes available');
15145                }
15146
15147                workingBytes.set(workingData.subarray(position,
15148                    position + availableBytes));
15149                workingWord = new DataView(workingBytes.buffer).getUint32(0);
15150
15151                // track the amount of workingData that has been processed
15152                workingBitsAvailable = availableBytes * 8;
15153                workingBytesAvailable -= availableBytes;
15154            };
15155
15156            // (count:int):void
15157            this.skipBits = function(count) {
15158                var skipBytes; // :int
15159                if (workingBitsAvailable > count) {
15160                    workingWord          <<= count;
15161                    workingBitsAvailable -= count;
15162                } else {
15163                    count -= workingBitsAvailable;
15164                    skipBytes = Math.floor(count / 8);
15165
15166                    count -= (skipBytes * 8);
15167                    workingBytesAvailable -= skipBytes;
15168
15169                    this.loadWord();
15170
15171                    workingWord <<= count;
15172                    workingBitsAvailable -= count;
15173                }
15174            };
15175
15176            // (size:int):uint
15177            this.readBits = function(size) {
15178                var
15179                    bits = Math.min(workingBitsAvailable, size), // :uint
15180                    valu = workingWord >>> (32 - bits); // :uint
15181                // if size > 31, handle error
15182                workingBitsAvailable -= bits;
15183                if (workingBitsAvailable > 0) {
15184                    workingWord <<= bits;
15185                } else if (workingBytesAvailable > 0) {
15186                    this.loadWord();
15187                }
15188
15189                bits = size - bits;
15190                if (bits > 0) {
15191                    return valu << bits | this.readBits(bits);
15192                }
15193                return valu;
15194            };
15195
15196            // ():uint
15197            this.skipLeadingZeros = function() {
15198                var leadingZeroCount; // :uint
15199                for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
15200                    if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) {
15201                        // the first bit of working word is 1
15202                        workingWord <<= leadingZeroCount;
15203                        workingBitsAvailable -= leadingZeroCount;
15204                        return leadingZeroCount;
15205                    }
15206                }
15207
15208                // we exhausted workingWord and still have not found a 1
15209                this.loadWord();
15210                return leadingZeroCount + this.skipLeadingZeros();
15211            };
15212
15213            // ():void
15214            this.skipUnsignedExpGolomb = function() {
15215                this.skipBits(1 + this.skipLeadingZeros());
15216            };
15217
15218            // ():void
15219            this.skipExpGolomb = function() {
15220                this.skipBits(1 + this.skipLeadingZeros());
15221            };
15222
15223            // ():uint
15224            this.readUnsignedExpGolomb = function() {
15225                var clz = this.skipLeadingZeros(); // :uint
15226                return this.readBits(clz + 1) - 1;
15227            };
15228
15229            // ():int
15230            this.readExpGolomb = function() {
15231                var valu = this.readUnsignedExpGolomb(); // :int
15232                if (0x01 & valu) {
15233                    // the number is odd if the low order bit is set
15234                    return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
15235                }
15236                return -1 * (valu >>> 1); // divide by two then make it negative
15237            };
15238
15239            // Some convenience functions
15240            // :Boolean
15241            this.readBoolean = function() {
15242                return this.readBits(1) === 1;
15243            };
15244
15245            // ():int
15246            this.readUnsignedByte = function() {
15247                return this.readBits(8);
15248            };
15249
15250            this.loadWord();
15251        };
15252
15253        module.exports = ExpGolomb;
15254
15255    },{}],60:[function(require,module,exports){
15256        /**
15257         * mux.js
15258         *
15259         * Copyright (c) 2014 Brightcove
15260         * All rights reserved.
15261         *
15262         * A lightweight readable stream implemention that handles event dispatching.
15263         * Objects that inherit from streams should call init in their constructors.
15264         */
15265        'use strict';
15266
15267        var Stream = function() {
15268            this.init = function() {
15269                var listeners = {};
15270                /**
15271                 * Add a listener for a specified event type.
15272                 * @param type {string} the event name
15273                 * @param listener {function} the callback to be invoked when an event of
15274                 * the specified type occurs
15275                 */
15276                this.on = function(type, listener) {
15277                    if (!listeners[type]) {
15278                        listeners[type] = [];
15279                    }
15280                    listeners[type] = listeners[type].concat(listener);
15281                };
15282                /**
15283                 * Remove a listener for a specified event type.
15284                 * @param type {string} the event name
15285                 * @param listener {function} a function previously registered for this
15286                 * type of event through `on`
15287                 */
15288                this.off = function(type, listener) {
15289                    var index;
15290                    if (!listeners[type]) {
15291                        return false;
15292                    }
15293                    index = listeners[type].indexOf(listener);
15294                    listeners[type] = listeners[type].slice();
15295                    listeners[type].splice(index, 1);
15296                    return index > -1;
15297                };
15298                /**
15299                 * Trigger an event of the specified type on this stream. Any additional
15300                 * arguments to this function are passed as parameters to event listeners.
15301                 * @param type {string} the event name
15302                 */
15303                this.trigger = function(type) {
15304                    var callbacks, i, length, args;
15305                    callbacks = listeners[type];
15306                    if (!callbacks) {
15307                        return;
15308                    }
15309                    // Slicing the arguments on every invocation of this method
15310                    // can add a significant amount of overhead. Avoid the
15311                    // intermediate object creation for the common case of a
15312                    // single callback argument
15313                    if (arguments.length === 2) {
15314                        length = callbacks.length;
15315                        for (i = 0; i < length; ++i) {
15316                            callbacks[i].call(this, arguments[1]);
15317                        }
15318                    } else {
15319                        args = [];
15320                        i = arguments.length;
15321                        for (i = 1; i < arguments.length; ++i) {
15322                            args.push(arguments[i]);
15323                        }
15324                        length = callbacks.length;
15325                        for (i = 0; i < length; ++i) {
15326                            callbacks[i].apply(this, args);
15327                        }
15328                    }
15329                };
15330                /**
15331                 * Destroys the stream and cleans up.
15332                 */
15333                this.dispose = function() {
15334                    listeners = {};
15335                };
15336            };
15337        };
15338
15339        /**
15340         * Forwards all `data` events on this stream to the destination stream. The
15341         * destination stream should provide a method `push` to receive the data
15342         * events as they arrive.
15343         * @param destination {stream} the stream that will receive all `data` events
15344         * @param autoFlush {boolean} if false, we will not call `flush` on the destination
15345         *                            when the current stream emits a 'done' event
15346         * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
15347         */
15348        Stream.prototype.pipe = function(destination) {
15349            this.on('data', function(data) {
15350                destination.push(data);
15351            });
15352
15353            this.on('done', function(flushSource) {
15354                destination.flush(flushSource);
15355            });
15356
15357            return destination;
15358        };
15359
15360// Default stream functions that are expected to be overridden to perform
15361// actual work. These are provided by the prototype as a sort of no-op
15362// implementation so that we don't have to check for their existence in the
15363// `pipe` function above.
15364        Stream.prototype.push = function(data) {
15365            this.trigger('data', data);
15366        };
15367
15368        Stream.prototype.flush = function(flushSource) {
15369            this.trigger('done', flushSource);
15370        };
15371
15372        module.exports = Stream;
15373
15374    },{}],61:[function(require,module,exports){
15375        /* jshint ignore:start */
15376        (function(root) {
15377            /* jshint ignore:end */
15378            var URLToolkit = {
15379                // build an absolute URL from a relative one using the provided baseURL
15380                // if relativeURL is an absolute URL it will be returned as is.
15381                buildAbsoluteURL: function(baseURL, relativeURL) {
15382                    // remove any remaining space and CRLF
15383                    relativeURL = relativeURL.trim();
15384                    if (/^[a-z]+:/i.test(relativeURL)) {
15385                        // complete url, not relative
15386                        return relativeURL;
15387                    }
15388
15389                    var relativeURLQuery = null;
15390                    var relativeURLHash = null;
15391
15392                    var relativeURLHashSplit = /^([^#]*)(.*)$/.exec(relativeURL);
15393                    if (relativeURLHashSplit) {
15394                        relativeURLHash = relativeURLHashSplit[2];
15395                        relativeURL = relativeURLHashSplit[1];
15396                    }
15397                    var relativeURLQuerySplit = /^([^\?]*)(.*)$/.exec(relativeURL);
15398                    if (relativeURLQuerySplit) {
15399                        relativeURLQuery = relativeURLQuerySplit[2];
15400                        relativeURL = relativeURLQuerySplit[1];
15401                    }
15402
15403                    var baseURLHashSplit = /^([^#]*)(.*)$/.exec(baseURL);
15404                    if (baseURLHashSplit) {
15405                        baseURL = baseURLHashSplit[1];
15406                    }
15407                    var baseURLQuerySplit = /^([^\?]*)(.*)$/.exec(baseURL);
15408                    if (baseURLQuerySplit) {
15409                        baseURL = baseURLQuerySplit[1];
15410                    }
15411
15412                    var baseURLDomainSplit = /^(([a-z]+:)?\/\/[^:\/]+(:[0-9]+)?)?(\/?.*)$/i.exec(baseURL);
15413                    if (!baseURLDomainSplit) {
15414                        throw new Error('Error trying to parse base URL.');
15415                    }
15416
15417                    // e.g. 'http:', 'https:', ''
15418                    var baseURLProtocol = baseURLDomainSplit[2] || '';
15419                    // e.g. 'http://example.com', '//example.com', ''
15420                    var baseURLProtocolDomain = baseURLDomainSplit[1] || '';
15421                    // e.g. '/a/b/c/playlist.m3u8', 'a/b/c/playlist.m3u8'
15422                    var baseURLPath = baseURLDomainSplit[4];
15423                    if (baseURLPath.indexOf('/') !== 0 && baseURLProtocolDomain !== '') {
15424                        // this handles a base url of http://example.com (missing last slash)
15425                        baseURLPath = '/'+baseURLPath;
15426                    }
15427
15428                    var builtURL = null;
15429                    if (/^\/\//.test(relativeURL)) {
15430                        // relative url starts wth '//' so copy protocol (which may be '' if baseUrl didn't provide one)
15431                        builtURL = baseURLProtocol+'//'+URLToolkit.buildAbsolutePath('', relativeURL.substring(2));
15432                    }
15433                    else if (/^\//.test(relativeURL)) {
15434                        // relative url starts with '/' so start from root of domain
15435                        builtURL = baseURLProtocolDomain+'/'+URLToolkit.buildAbsolutePath('', relativeURL.substring(1));
15436                    }
15437                    else {
15438                        builtURL = URLToolkit.buildAbsolutePath(baseURLProtocolDomain+baseURLPath, relativeURL);
15439                    }
15440
15441                    // put the query and hash parts back
15442                    if (relativeURLQuery) {
15443                        builtURL += relativeURLQuery;
15444                    }
15445                    if (relativeURLHash) {
15446                        builtURL += relativeURLHash;
15447                    }
15448                    return builtURL;
15449                },
15450
15451                // build an absolute path using the provided basePath
15452                // adapted from https://developer.mozilla.org/en-US/docs/Web/API/document/cookie#Using_relative_URLs_in_the_path_parameter
15453                // this does not handle the case where relativePath is "/" or "//". These 
15453cases should be handled outside this.
15454                buildAbsolutePath: function(basePath, relativePath) {
15455                    var sRelPath = relativePath;
15456                    var nUpLn, sDir = '', sPath = basePath.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, '$1'));
15457                    for (var nEnd, nStart = 0; nEnd = sPath.indexOf('/../', nStart), nEnd > -1; nStart = nEnd + nUpLn) {
15458                        nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length;
15459                        sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp('(?:\\\/+[^\\\/]*){0,' + ((nUpLn - 1) / 3) + '}$'), '/');
15460                    }
15461                    return sDir + sPath.substr(nStart);
15462                }
15463            };
15464
15465            /* jshint ignore:start */
15466            if(typeof exports === 'object' && typeof module === 'object')
15467                module.exports = URLToolkit;
15468            else if(typeof define === 'function' && define.amd)
15469                define([], function() { return URLToolkit; });
15470            else if(typeof exports === 'object')
15471                exports["URLToolkit"] = URLToolkit;
15472            else
15473                root["URLToolkit"] = URLToolkit;
15474        })(this);
15475        /* jshint ignore:end */
15476
15477    },{}],62:[function(require,module,exports){
15478        (function (global){
15479            /**
15480             * @file add-text-track-data.js
15481             */
15482            'use strict';
15483
15484            Object.defineProperty(exports, '__esModule', {
15485                value: true
15486            });
15487
15488            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
15489
15490            var _globalWindow = require('global/window');
15491
15492            var _globalWindow2 = _interopRequireDefault(_globalWindow);
15493
15494            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
15495
15496            var _videoJs2 = _interopRequireDefault(_videoJs);
15497
15498            /**
15499             * Define properties on a cue for backwards compatability,
15500             * but warn the user that the way that they are using it
15501             * is depricated and will be removed at a later date.
15502             *
15503             * @param {Cue} cue the cue to add the properties on
15504             * @private
15505             */
15506            var deprecateOldCue = function deprecateOldCue(cue) {
15507                Object.defineProperties(cue.frame, {
15508                    id: {
15509                        get: function get() {
15510                            _videoJs2['default'].log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
15511                            return cue.value.key;
15512                        }
15513                    },
15514                    value: {
15515                        get: function get() {
15516                            _videoJs2['default'].log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
15517                            return cue.value.data;
15518                        }
15519                    },
15520                    privateData: {
15521                        get: function get() {
15522                            _videoJs2['default'].log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
15523                            return cue.value.data;
15524                        }
15525                    }
15526                });
15527            };
15528
15529            var durationOfVideo = function durationOfVideo(duration) {
15530                var dur = undefined;
15531
15532                if (isNaN(duration) || Math.abs(duration) === Infinity) {
15533                    dur = Number.MAX_VALUE;
15534                } else {
15535                    dur = duration;
15536                }
15537                return dur;
15538            };
15539            /**
15540             * Add text track data to a source handler given the captions and
15541             * metadata from the buffer.
15542             *
15543             * @param {Object} sourceHandler the flash or virtual source buffer
15544             * @param {Array} captionArray an array of caption data
15545             * @param {Array} metadataArray an array of meta data
15546             * @private
15547             */
15548            var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
15549                var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue;
15550
15551                if (captionArray) {
15552                    captionArray.forEach(function (caption) {
15553                        this.inbandTextTrack_.addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
15554                    }, sourceHandler);
15555                }
15556
15557                if (metadataArray) {
15558                    (function () {
15559                        var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
15560
15561                        metadataArray.forEach(function (metadata) {
15562                            var time = metadata.cueTime + this.timestampOffset;
15563
15564                            metadata.frames.forEach(function (frame) {
15565                                var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
15566
15567                                cue.frame = frame;
15568                                cue.value = frame;
15569                                deprecateOldCue(cue);
15570
15571                                this.metadataTrack_.addCue(cue);
15572                            }, this);
15573                        }, sourceHandler);
15574
15575                        // Updating the metadeta cues so that
15576                        // the endTime of each cue is the startTime of the next cue
15577                        // the endTime of last cue is the duration of the video
15578                        if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
15579                            (function () {
15580                                var cues = sourceHandler.metadataTrack_.cues;
15581                                var cuesArray = [];
15582
15583                                // Create a copy of the TextTrackCueList...
15584                                // ...disregarding cues with a falsey value
15585                                for (var i = 0; i < cues.length; i++) {
15586                                    if (cues[i]) {
15587                                        cuesArray.push(cues[i]);
15588                                    }
15589                                }
15590
15591                                // Group cues by their startTime value
15592                                var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
15593                                    var timeSlot = obj[cue.startTime] || [];
15594
15595                                    timeSlot.push(cue);
15596                                    obj[cue.startTime] = timeSlot;
15597
15598                                    return obj;
15599                                }, {});
15600
15601                                // Sort startTimes by ascending order
15602                                var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
15603                                    return Number(a) - Number(b);
15604                                });
15605
15606                                // Map each cue group's endTime to the next group's startTime
15607                                sortedStartTimes.forEach(function (startTime, idx) {
15608                                    var cueGroup = cuesGroupedByStartTime[startTime];
15609                                    var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration;
15610
15611                                    // Map each cue's endTime the next group's startTime
15612                                    cueGroup.forEach(function (cue) {
15613                                        cue.endTime = nextTime;
15614                                    });
15615                                });
15616                            })();
15617                        }
15618                    })();
15619                }
15620            };
15621
15622            exports['default'] = {
15623                addTextTrackData: addTextTrackData,
15624                durationOfVideo: durationOfVideo
15625            };
15626            module.exports = exports['default'];
15627        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
15628    },{"global/window":30}],63:[function(require,module,exports){
15629        /**
15630         * Remove the text track from the player if one with matching kind and
15631         * label properties already exists on the player
15632         *
15633         * @param {Object} player the video.js player object
15634         * @param {String} kind to be considered the text track's `kind` must match
15635         * @param {String} label to be considered the text track's `label` must match
15636         * @private
15637         */
15638        'use strict';
15639
15640        Object.defineProperty(exports, '__esModule', {
15641            value: true
15642        });
15643        var removeExistingTrack = function removeExistingTrack(player, kind, label) {
15644            var tracks = player.remoteTextTracks() || [];
15645
15646            for (var i = 0; i < tracks.length; i++) {
15647                var track = tracks[i];
15648
15649                if (track.kind === kind && track.label === label) {
15650                    player.removeRemoteTextTrack(track);
15651                }
15652            }
15653        };
15654
15655        exports.removeExistingTrack = removeExistingTrack;
15656        /**
15657         * Cleaup text tracks on video.js if they exist
15658         *
15659         * @param {Object} player the video.js player object
15660         * @private
15661         */
15662        var cleanupTextTracks = function cleanupTextTracks(player) {
15663            removeExistingTrack(player, 'captions', 'cc1');
15664            removeExistingTrack(player, 'metadata', 'Timed Metadata');
15665        };
15666        exports.cleanupTextTracks = cleanupTextTracks;
15667    },{}],64:[function(require,module,exports){
15668        /**
15669         * @file codec-utils.js
15670         */
15671
15672        /**
15673         * Check if a codec string refers to an audio codec.
15674         *
15675         * @param {String} codec codec string to check
15676         * @return {Boolean} if this is an audio codec
15677         * @private
15678         */
15679        'use strict';
15680
15681        Object.defineProperty(exports, '__esModule', {
15682            value: true
15683        });
15684        var isAudioCodec = function isAudioCodec(codec) {
15685            return (/mp4a\.\d+.\d+/i.test(codec)
15686            );
15687        };
15688
15689        /**
15690         * Check if a codec string refers to a video codec.
15691         *
15692         * @param {String} codec codec string to check
15693         * @return {Boolean} if this is a video codec
15694         * @private
15695         */
15696        var isVideoCodec = function isVideoCodec(codec) {
15697            return (/avc1\.[\da-f]+/i.test(codec)
15698            );
15699        };
15700
15701        /**
15702         * Parse a content type header into a type and parameters
15703         * object
15704         *
15705         * @param {String} type the content type header
15706         * @return {Object} the parsed content-type
15707         * @private
15708         */
15709        var parseContentType = function parseContentType(type) {
15710            var object = { type: '', parameters: {} };
15711            var parameters = type.trim().split(';');
15712
15713            // first parameter should always be content-type
15714            object.type = parameters.shift().trim();
15715            parameters.forEach(function (parameter) {
15716                var pair = parameter.trim().split('=');
15717
15718                if (pair.length > 1) {
15719                    var _name = pair[0].replace(/"/g, '').trim();
15720                    var value = pair[1].replace(/"/g, '').trim();
15721
15722                    object.parameters[_name] = value;
15723                }
15724            });
15725
15726            return object;
15727        };
15728
15729        /**
15730         * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
15731         * `avc1.<hhhhhh>`
15732         *
15733         * @param {Array} codecs an array of codec strings to fix
15734         * @return {Array} the translated codec array
15735         * @private
15736         */
15737        var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
15738            return codecs.map(function (codec) {
15739                return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
15740                    var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
15741                    var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
15742
15743                    return 'avc1.' + profileHex + '00' + avcLevelHex;
15744                });
15745            });
15746        };
15747
15748        exports['default'] = {
15749            isAudioCodec: isAudioCodec,
15750            parseContentType: parseContentType,
15751            isVideoCodec: isVideoCodec,
15752            translateLegacyCodecs: translateLegacyCodecs
15753        };
15754        module.exports = exports['default'];
15755    },{}],65:[function(require,module,exports){
15756        /**
15757         * @file create-text-tracks-if-necessary.js
15758         */
15759        'use strict';
15760
15761        Object.defineProperty(exports, '__esModule', {
15762            value: true
15763        });
15764
15765        var _cleanupTextTracks = require('./cleanup-text-tracks');
15766
15767        /**
15768         * Create text tracks on video.js if they exist on a segment.
15769         *
15770         * @param {Object} sourceBuffer the VSB or FSB
15771         * @param {Object} mediaSource the HTML or Flash media source
15772         * @param {Object} segment the segment that may contain the text track
15773         * @private
15774         */
15775        var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
15776            var player = mediaSource.player_;
15777
15778            // create an in-band caption track if one is present in the segment
15779            if (segment.captions && segment.captions.length && !sourceBuffer.inbandTextTrack_) {
15780                (0, _cleanupTextTracks.removeExistingTrack)(player, 'captions', 'cc1');
15781                sourceBuffer.inbandTextTrack_ = player.addRemoteTextTrack({
15782                    kind: 'captions',
15783                    label: 'cc1'
15784                }, false).track;
15785            }
15786
15787            if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
15788                (0, _cleanupTextTracks.removeExistingTrack)(player, 'metadata', 'Timed Metadata', true);
15789                sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
15790                    kind: 'metadata',
15791                    label: 'Timed Metadata'
15792                }, false).track;
15793                sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
15794            }
15795        };
15796
15797        exports['default'] = createTextTracksIfNecessary;
15798        module.exports = exports['default'];
15799    },{"./cleanup-text-tracks":63}],66:[function(require,module,exports){
15800        /**
15801         * @file flash-constants.js
15802         */
15803        /**
15804         * The maximum size in bytes for append operations to the video.js
15805         * SWF. Calling through to Flash blocks and can be expensive so
15806         * we chunk data and pass through 4KB at a time, yielding to the
15807         * browser between chunks. This gives a theoretical maximum rate of
15808         * 1MB/s into Flash. Any higher and we begin to drop frames and UI
15809         * responsiveness suffers.
15810         *
15811         * @private
15812         */
15813        "use strict";
15814
15815        Object.defineProperty(exports, "__esModule", {
15816            value: true
15817        });
15818        var flashConstants = {
15819            // times in milliseconds
15820            TIME_BETWEEN_CHUNKS: 1,
15821            BYTES_PER_CHUNK: 1024 * 32
15822        };
15823
15824        exports["default"] = flashConstants;
15825        module.exports = exports["default"];
15826    },{}],67:[function(require,module,exports){
15827        (function (global){
15828            /**
15829             * @file flash-media-source.js
15830             */
15831            'use strict';
15832
15833            Object.defineProperty(exports, '__esModule', {
15834                value: true
15835            });
15836
15837            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
15838
15839            var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
15840
15841            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
15842
15843            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
15844
15845            function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
15845onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
15846
15847            var _globalDocument = require('global/document');
15848
15849            var _globalDocument2 = _interopRequireDefault(_globalDocument);
15850
15851            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
15852
15853            var _videoJs2 = _interopRequireDefault(_videoJs);
15854
15855            var _flashSourceBuffer = require('./flash-source-buffer');
15856
15857            var _flashSourceBuffer2 = _interopRequireDefault(_flashSourceBuffer);
15858
15859            var _flashConstants = require('./flash-constants');
15860
15861            var _flashConstants2 = _interopRequireDefault(_flashConstants);
15862
15863            var _codecUtils = require('./codec-utils');
15864
15865            var _cleanupTextTracks = require('./cleanup-text-tracks');
15866
15867            /**
15868             * A flash implmentation of HTML MediaSources and a polyfill
15869             * for browsers that don't support native or HTML MediaSources..
15870             *
15871             * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
15872             * @class FlashMediaSource
15873             * @extends videojs.EventTarget
15874             */
15875
15876            var FlashMediaSource = (function (_videojs$EventTarget) {
15877                _inherits(FlashMediaSource, _videojs$EventTarget);
15878
15879                function FlashMediaSource() {
15880                    var _this = this;
15881
15882                    _classCallCheck(this, FlashMediaSource);
15883
15884                    _get(Object.getPrototypeOf(FlashMediaSource.prototype), 'constructor', this).call(this);
15885                    this.sourceBuffers = [];
15886                    this.readyState = 'closed';
15887
15888                    this.on(['sourceopen', 'webkitsourceopen'], function (event) {
15889                        // find the swf where we will push media data
15890                        _this.swfObj = _globalDocument2['default'].getElementById(event.swfId);
15891                        _this.player_ = (0, _videoJs2['default'])(_this.swfObj.parentNode);
15892                        _this.tech_ = _this.swfObj.tech;
15893                        _this.readyState = 'open';
15894
15895                        _this.tech_.on('seeking', function () {
15896                            var i = _this.sourceBuffers.length;
15897
15898                            while (i--) {
15899                                _this.sourceBuffers[i].abort();
15900                            }
15901                        });
15902
15903                        if (_this.tech_.hls) {
15904                            _this.tech_.hls.on('dispose', function () {
15905                                (0, _cleanupTextTracks.cleanupTextTracks)(_this.player_);
15906                            });
15907                        }
15908
15909                        // trigger load events
15910                        if (_this.swfObj) {
15911                            _this.swfObj.vjs_load();
15912                        }
15913                    });
15914                }
15915
15916                /**
15917                 * Set or return the presentation duration.
15918                 *
15919                 * @param {Double} value the duration of the media in seconds
15920                 * @param {Double} the current presentation duration
15921                 * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
15922                 */
15923
15924                /**
15925                 * We have this function so that the html and flash interfaces
15926                 * are the same.
15927                 *
15928                 * @private
15929                 */
15930
15931                _createClass(FlashMediaSource, [{
15932                    key: 'addSeekableRange_',
15933                    value: function addSeekableRange_() {}
15934                    // intentional no-op
15935
15936                    /**
15937                     * Create a new flash source buffer and add it to our flash media source.
15938                     *
15939                     * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
15940                     * @param {String} type the content-type of the source
15941                     * @return {Object} the flash source buffer
15942                     */
15943
15944                }, {
15945                    key: 'addSourceBuffer',
15946                    value: function addSourceBuffer(type) {
15947                        var parsedType = (0, _codecUtils.parseContentType)(type);
15948                        var sourceBuffer = undefined;
15949
15950                        // if this is an FLV type, we'll push data to flash
15951                        if (parsedType.type === 'video/mp2t') {
15952                            // Flash source buffers
15953                            sourceBuffer = new _flashSourceBuffer2['default'](this);
15954                        } else {
15955                            throw new Error('NotSupportedError (Video.js)');
15956                        }
15957
15958                        this.sourceBuffers.push(sourceBuffer);
15959                        return sourceBuffer;
15960                    }
15961
15962                    /**
15963                     * Signals the end of the stream.
15964                     *
15965                     * @link https://w3c.github.io/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
15966                     * @param {String=} error Signals that a playback error
15967                     * has occurred. If specified, it must be either "network" or
15968                     * "decode".
15969                     */
15970                }, {
15971                    key: 'endOfStream',
15972                    value: function endOfStream(error) {
15973                        if (error === 'network') {
15974                            // MEDIA_ERR_NETWORK
15975                            this.tech_.error(2);
15976                        } else if (error === 'decode') {
15977                            // MEDIA_ERR_DECODE
15978                            this.tech_.error(3);
15979                        }
15980                        if (this.readyState !== 'ended') {
15981                            this.readyState = 'ended';
15982                            this.swfObj.vjs_endOfStream();
15983                        }
15984                    }
15985                }]);
15986
15987                return FlashMediaSource;
15988            })(_videoJs2['default'].EventTarget);
15989
15990            exports['default'] = FlashMediaSource;
15991            try {
15992                Object.defineProperty(FlashMediaSource.prototype, 'duration', {
15993                    /**
15994                     * Return the presentation duration.
15995                     *
15996                     * @return {Double} the duration of the media in seconds
15997                     * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
15998                     */
15999                    get: function get() {
16000                        if (!this.swfObj) {
16001                            return NaN;
16002                        }
16003                        // get the current duration from the SWF
16004                        return this.swfObj.vjs_getProperty('duration');
16005                    },
16006                    /**
16007                     * Set the presentation duration.
16008                     *
16009                     * @param {Double} value the duration of the media in seconds
16010                     * @return {Double} the duration of the media in seconds
16011                     * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
16012                     */
16013                    set: function set(value) {
16014                        var i = undefined;
16015                        var oldDuration = this.swfObj.vjs_getProperty('duration');
16016
16017                        this.swfObj.vjs_setProperty('duration', value);
16018
16019                        if (value < oldDuration) {
16020                            // In MSE, this triggers the range removal algorithm which causes
16021                            // an update to occur
16022                            for (i = 0; i < this.sourceBuffers.length; i++) {
16023                                this.sourceBuffers[i].remove(value, oldDuration);
16024                            }
16025                        }
16026
16027                        return value;
16028                    }
16029                });
16030            } catch (e) {
16031                // IE8 throws if defineProperty is called on a non-DOM node. We
16032                // don't support IE8 but we shouldn't throw an error if loaded
16033                // there.
16034                FlashMediaSource.prototype.duration = NaN;
16035            }
16036
16037            for (var property in _flashConstants2['default']) {
16038                FlashMediaSource[property] = _flashConstants2['default'][property];
16039            }
16040            module.exports = exports['default'];
16041        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
16042    },{"./cleanup-text-tracks":63,"./codec-utils":64,"./flash-constants":66,"./flash-source-buffer":68,"global/document":29}],68:[function(require,module,exports){
16043        (function (global){
16044            /**
16045             * @file flash-source-buffer.js
16046             */
16047            'use strict';
16048
16049            Object.defineProperty(exports, '__esModule', {
16050                value: true
16051            });
16052
16053            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
16054
16055            var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
16056
16057            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
16058
16059            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
16060
16061            function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
16061onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16062
16063            var _globalWindow = require('global/window');
16064
16065            var _globalWindow2 = _interopRequireDefault(_globalWindow);
16066
16067            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
16068
16069            var _videoJs2 = _interopRequireDefault(_videoJs);
16070
16071            var _muxJsLibFlv = require('mux.js/lib/flv');
16072
16073            var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
16074
16075            var _removeCuesFromTrack = require('./remove-cues-from-track');
16076
16077            var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
16078
16079            var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
16080
16081            var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
16082
16083            var _addTextTrackData = require('./add-text-track-data');
16084
16085            var _flashTransmuxerWorker = require('./flash-transmuxer-worker');
16086
16087            var _flashTransmuxerWorker2 = _interopRequireDefault(_flashTransmuxerWorker);
16088
16089            var _webworkify = require('webworkify');
16090
16091            var _webworkify2 = _interopRequireDefault(_webworkify);
16092
16093            var _flashConstants = require('./flash-constants');
16094
16095            var _flashConstants2 = _interopRequireDefault(_flashConstants);
16096
16097            /**
16098             * A wrapper around the setTimeout function that uses
16099             * the flash constant time between ticks value.
16100             *
16101             * @param {Function} func the function callback to run
16102             * @private
16103             */
16104            var scheduleTick = function scheduleTick(func) {
16105                // Chrome doesn't invoke requestAnimationFrame callbacks
16106                // in background tabs, so use setTimeout.
16107                _globalWindow2['default'].setTimeout(func, _flashConstants2['default'].TIME_BETWEEN_CHUNKS);
16108            };
16109
16110            /**
16111             * Generates a random string of max length 6
16112             *
16113             * @return {String} the randomly generated string
16114             * @function generateRandomString
16115             * @private
16116             */
16117            var generateRandomString = function generateRandomString() {
16118                return Math.random().toString(36).slice(2, 8);
16119            };
16120
16121            /**
16122             * Round a number to a specified number of places much like
16123             * toFixed but return a number instead of a string representation.
16124             *
16125             * @param {Number} num A number
16126             * @param {Number} places The number of decimal places which to
16127             * round
16128             * @private
16129             */
16130            var toDecimalPlaces = function toDecimalPlaces(num, places) {
16131                if (typeof places !== 'number' || places < 0) {
16132                    places = 0;
16133                }
16134
16135                var scale = Math.pow(10, places);
16136
16137                return Math.round(num * scale) / scale;
16138            };
16139
16140            /**
16141             * A SourceBuffer implementation for Flash rather than HTML.
16142             *
16143             * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
16144             * @param {Object} mediaSource the flash media source
16145             * @class FlashSourceBuffer
16146             * @extends videojs.EventTarget
16147             */
16148
16149            var FlashSourceBuffer = (function (_videojs$EventTarget) {
16150                _inherits(FlashSourceBuffer, _videojs$EventTarget);
16151
16152                function FlashSourceBuffer(mediaSource) {
16153                    var _this = this;
16154
16155                    _classCallCheck(this, FlashSourceBuffer);
16156
16157                    _get(Object.getPrototypeOf(FlashSourceBuffer.prototype), 'constructor', this).call(this);
16158                    var encodedHeader = undefined;
16159
16160                    // Start off using the globally defined value but refine
16161                    // as we append data into flash
16162                    this.chunkSize_ = _flashConstants2['default'].BYTES_PER_CHUNK;
16163
16164                    // byte arrays queued to be appended
16165                    this.buffer_ = [];
16166
16167                    // the total number of queued bytes
16168                    this.bufferSize_ = 0;
16169
16170                    // to be able to determine the correct position to seek to, we
16171                    // need to retain information about the mapping between the
16172                    // media timeline and PTS values
16173                    this.basePtsOffset_ = NaN;
16174
16175                    this.mediaSource_ = mediaSource;
16176
16177                    this.audioBufferEnd_ = NaN;
16178                    this.videoBufferEnd_ = NaN;
16179
16180                    // indicates whether the asynchronous continuation of an operation
16181                    // is still being processed
16182                    // see https://w3c.github.io/media-source/#widl-SourceBuffer-updating
16183                    this.updating = false;
16184                    this.timestampOffset_ = 0;
16185
16186                    encodedHeader = _globalWindow2['default'].btoa(String.fromCharCode.apply(null, Array.prototype.slice.call(_muxJsLibFlv2['default'].getFlvHeader())));
16187
16188                    // create function names with added randomness for the global callbacks flash will use
16189                    // to get data from javascript into the swf. Random strings are added as a safety
16190                    // measure for pages with multiple players since these functions will be global
16191                    // instead of per instance. When making a call to the swf, the browser generates a
16192                    // try catch code snippet, but just takes the function name and writes out an unquoted
16193                    // call to that function. If the player id has any special characters, this will result
16194                    // in an error, so safePlayerId replaces all special characters to '_'
16195                    var safePlayerId = this.mediaSource_.player_.id().replace(/[^a-zA-Z0-9]/g, '_');
16196
16197                    this.flashEncodedHeaderName_ = 'vjs_flashEncodedHeader_' + safePlayerId + generateRandomString();
16198                    this.flashEncodedDataName_ = 'vjs_flashEncodedData_' + safePlayerId + generateRandomString();
16199
16200                    _globalWindow2['default'][this.flashEncodedHeaderName_] = function () {
16201                        delete _globalWindow2['default'][_this.flashEncodedHeaderName_];
16202                        return encodedHeader;
16203                    };
16204
16205                    this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedHeaderName_);
16206
16207                    this.transmuxer_ = (0, _webworkify2['default'])(_flashTransmuxerWorker2['default']);
16208                    this.transmuxer_.postMessage({ action: 'init', options: {} });
16209                    this.transmuxer_.onmessage = function (event) {
16210                        if (event.data.action === 'data') {
16211                            _this.receiveBuffer_(event.data.segment);
16212                        }
16213                    };
16214
16215                    this.one('updateend', function () {
16216                        _this.mediaSource_.tech_.trigger('loadedmetadata');
16217                    });
16218
16219                    Object.defineProperty(this, 'timestampOffset', {
16220                        get: function get() {
16221                            return this.timestampOffset_;
16222                        },
16223                        set: function set(val) {
16224                            if (typeof val === 'number' && val >= 0) {
16225                                this.timestampOffset_ = val;
16226                                // We have to tell flash to expect a discontinuity
16227                                this.mediaSource_.swfObj.vjs_discontinuity();
16228                                // the media <-> PTS mapping must be re-established after
16229                                // the discontinuity
16230                                this.basePtsOffset_ = NaN;
16231                                this.audioBufferEnd_ = NaN;
16232                                this.videoBufferEnd_ = NaN;
16233
16234                                this.transmuxer_.postMessage({ action: 'reset' });
16235                            }
16236                        }
16237                    });
16238
16239                    Object.defineProperty(this, 'buffered', {
16240                        get: function get() {
16241                            if (!this.mediaSource_ || !this.mediaSource_.swfObj || !('vjs_getProperty' in this.mediaSource_.swfObj)) {
16242                                return _videoJs2['default'].createTimeRange();
16243                            }
16244
16245                            var buffered = this.mediaSource_.swfObj.vjs_getProperty('buffered');
16246
16247                            if (buffered && buffered.length) {
16248                                buffered[0][0] = toDecimalPlaces(buffered[0][0], 3);
16249                                buffered[0][1] = toDecimalPlaces(buffered[0][1], 3);
16250                            }
16251                            return _videoJs2['default'].createTimeRanges(buffered);
16252                        }
16253                    });
16254
16255                    // On a seek we remove all text track data since flash has no concept
16256                    // of a buffered-range and everything else is reset on seek
16257                    this.mediaSource_.player_.on('seeked', function () {
16258                        (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.metadataTrack_);
16259                        (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.inbandTextTrack_);
16260                    });
16261
16262                    this.mediaSource_.player_.tech_.hls.on('dispose', function () {
16263                        _this.transmuxer_.terminate();
16264                    });
16265                }
16266
16267                /**
16268                 * Append bytes to the sourcebuffers buffer, in this case we
16269                 * have to append it to swf object.
16270                 *
16271                 * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
16272                 * @param {Array} bytes
16273                 */
16274
16275                _createClass(FlashSourceBuffer, [{
16276                    key: 'appendBuffer',
16277                    value: function appendBuffer(bytes) {
16278                        var error = undefined;
16279
16280                        if (this.updating) {
16281                            error = new Error('SourceBuffer.append() cannot be called ' + 'while an update is in progress');
16282                            error.name = 'InvalidStateError';
16283                            error.code = 11;
16284                            throw error;
16285                        }
16286                        this.updating = true;
16287                        this.mediaSource_.readyState = 'open';
16288                        this.trigger({ type: 'update' });
16289
16290                        this.transmuxer_.postMessage({
16291                            action: 'push',
16292                            data: bytes.buffer,
16293                            byteOffset: bytes.byteOffset,
16294                            byteLength: bytes.byteLength
16295                        }, [bytes.buffer]);
16296                        this.transmuxer_.postMessage({ action: 'flush' });
16297                    }
16298
16299                    /**
16300                     * Reset the parser and remove any data queued to be sent to the SWF.
16301                     *
16302                     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
16303                     */
16304                }, {
16305                    key: 'abort',
16306                    value: function abort() {
16307                        this.buffer_ = [];
16308                        this.bufferSize_ = 0;
16309                        this.mediaSource_.swfObj.vjs_abort();
16310
16311                        // report any outstanding updates have ended
16312                        if (this.updating) {
16313                            this.updating = false;
16314                            this.trigger({ type: 'updateend' });
16315                        }
16316                    }
16317
16318                    /**
16319                     * Flash cannot remove ranges already buffered in the NetStream
16320                     * but seeking clears the buffer entirely. For most purposes,
16321                     * having this operation act as a no-op is acceptable.
16322                     *
16323                     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
16324                     * @param {Double} start start of the section to remove
16325                     * @param {Double} end end of the section to remove
16326                     */
16327                }, {
16328                    key: 'remove',
16329                    value: function remove(start, end) {
16330                        (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
16331                        (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTrack_);
16332                        this.trigger({ type: 'update' });
16333                        this.trigger({ type: 'updateend' });
16334                    }
16335
16336                    /**
16337                     * Receive a buffer from the flv.
16338                     *
16339                     * @param {Object} segment
16340                     * @private
16341                     */
16342                }, {
16343                    key: 'receiveBuffer_',
16344                    value: function receiveBuffer_(segment) {
16345                        var _this2 = this;
16346
16347                        // create an in-band caption track if one is present in the segment
16348                        (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
16349                        (0, _addTextTrackData.addTextTrackData)(this, segment.captions, segment.metadata);
16350
16351                        // Do this asynchronously since convertTagsToData_ can be time consuming
16352                        scheduleTick(function () {
16353                            var flvBytes = _this2.convertTagsToData_(segment);
16354
16355                            if (_this2.buffer_.length === 0) {
16356                                scheduleTick(_this2.processBuffer_.bind(_this2));
16357                            }
16358
16359                            if (flvBytes) {
16360                                _this2.buffer_.push(flvBytes);
16361                                _this2.bufferSize_ += flvBytes.byteLength;
16362                            }
16363                        });
16364                    }
16365
16366                    /**
16367                     * Append a portion of the current buffer to the SWF.
16368                     *
16369                     * @private
16370                     */
16371                }, {
16372                    key: 'processBuffer_',
16373                    value: function processBuffer_() {
16374                        var _this3 = this;
16375
16376                        var chunkSize = _flashConstants2['default'].BYTES_PER_CHUNK;
16377
16378                        if (!this.buffer_.length) {
16379                            if (this.updating !== false) {
16380                                this.updating = false;
16381                                this.trigger({ type: 'updateend' });
16382                            }
16383                            // do nothing if the buffer is empty
16384                            return;
16385                        }
16386
16387                        // concatenate appends up to the max append size
16388                        var chunk = this.buffer_[0].subarray(0, chunkSize);
16389
16390                        // requeue any bytes that won't make it this round
16391                        if (chunk.byteLength < chunkSize || this.buffer_[0].byteLength === chunkSize) {
16392                            this.buffer_.shift();
16393                        } else {
16394                            this.buffer_[0] = this.buffer_[0].subarray(chunkSize);
16395                        }
16396
16397                        this.bufferSize_ -= chunk.byteLength;
16398
16399                        // base64 encode the bytes
16400                        var binary = [];
16401                        var length = chunk.byteLength;
16402
16403                        for (var i = 0; i < length; i++) {
16404                            binary.push(String.fromCharCode(chunk[i]));
16405                        }
16406                        var b64str = _globalWindow2['default'].btoa(binary.join(''));
16407
16408                        _globalWindow2['default'][this.flashEncodedDataName_] = function () {
16409                            // schedule another processBuffer to process any left over data or to
16410                            // trigger updateend
16411                            scheduleTick(_this3.processBuffer_.bind(_this3));
16412                            delete _globalWindow2['default'][_this3.flashEncodedDataName_];
16413                            return b64str;
16414                        };
16415
16416                        // Notify the swf that segment data is ready to be appended
16417                        this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedDataName_);
16418                    }
16419
16420                    /**
16421                     * Turns an array of flv tags into a Uint8Array representing the
16422                     * flv data. Also removes any tags that are before the current
16423                     * time so that playback begins at or slightly after the right
16424                     * place on a seek
16425                     *
16426                     * @private
16427                     * @param {Object} segmentData object of segment data
16428                     */
16429                }, {
16430                    key: 'convertTagsToData_',
16431                    value: function convertTagsToData_(segmentData) {
16432                        var segmentByteLength = 0;
16433                        var tech = this.mediaSource_.tech_;
16434                        var videoTargetPts = 0;
16435                        var segment = undefined;
16436                        var videoTags = segmentData.tags.videoTags;
16437                        var audioTags = segmentData.tags.audioTags;
16438
16439                        // Establish the media timeline to PTS translation if we don't
16440                        // have one already
16441                        if (isNaN(this.basePtsOffset_) && (videoTags.length || audioTags.length)) {
16442                            // We know there is at least one video or audio tag, but since we may not have both,
16443                            // we use pts: Infinity for the missing tag. The will force the following Math.min
16444                            // call will to use the proper pts value since it will always be less than Infinity
16445                            var firstVideoTag = videoTags[0] || { pts: Infinity };
16446                            var firstAudioTag = audioTags[0] || { pts: Infinity };
16447
16448                            this.basePtsOffset_ = Math.min(firstAudioTag.pts, firstVideoTag.pts);
16449                        }
16450
16451                        if (tech.seeking()) {
16452                            // Do not use previously saved buffer end values while seeking since buffer
16453                            // is cleared on all seeks
16454                            this.videoBufferEnd_ = NaN;
16455                            this.audioBufferEnd_ = NaN;
16456                        }
16457
16458                        if (isNaN(this.videoBufferEnd_)) {
16459                            if (tech.buffered().length) {
16460                                videoTargetPts = tech.buffered().end(0) - this.timestampOffset;
16461                            }
16462
16463                            // Trim to currentTime if seeking
16464                            if (tech.seeking()) {
16465                                videoTargetPts = Math.max(videoTargetPts, tech.currentTime() - this.timestampOffset);
16466                            }
16467
16468                            // PTS values are represented in milliseconds
16469                            videoTargetPts *= 1e3;
16470                            videoTargetPts += this.basePtsOffset_;
16471                        } else {
16472                            // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
16473                            // could append an overlapping segment, in which case there is a high likelyhood
16474                            // a tag could have a matching pts to videoBufferEnd_, which would cause
16475                            // that tag to get appended by the tag.pts >= targetPts check below even though it
16476                            // is a duplicate of what was previously appended
16477                            videoTargetPts = this.videoBufferEnd_ + 0.1;
16478                        }
16479
16480                        // filter complete GOPs with a presentation time less than the seek target/end of buffer
16481                        var currentIndex = videoTags.length;
16482
16483                        // if the last tag is beyond videoTargetPts, then do not search the list for a GOP
16484                        // since our videoTargetPts lies in a future segment
16485                        if (currentIndex && videoTags[currentIndex - 1].pts >= videoTargetPts) {
16486                            // Start by walking backwards from the end of the list until we reach a tag that
16487                            // is equal to or less than videoTargetPts
16488                            while (--currentIndex) {
16489                                var currentTag = videoTags[currentIndex];
16490
16491                                if (currentTag.pts > videoTargetPts) {
16492                                    continue;
16493                                }
16494
16495                                // if we see a keyFrame or metadata tag once we've gone below videoTargetPts,
16496                                // exit the loop as this is the start of the GOP that we want to append
16497                                if (currentTag.keyFrame || currentTag.metaDataTag) {
16498                                    break;
16499                                }
16500                            }
16501
16502                            // We need to check if there are any metadata tags that come before currentIndex
16503                            // as those will be metadata tags associated with the GOP we are appending
16504                            // There could be 0 to 2 metadata tags that come before the currentIndex depending
16505                            // on what videoTargetPts is and whether the transmuxer prepended metadata tags to this
16506                            // key frame
16507                            while (currentIndex) {
16508                                var nextTag = videoTags[currentIndex - 1];
16509
16510                                if (!nextTag.metaDataTag) {
16511                                    break;
16512                                }
16513
16514                                currentIndex--;
16515                            }
16516                        }
16517
16518                        var filteredVideoTags = videoTags.slice(currentIndex);
16519
16520                        var audioTargetPts = undefined;
16521
16522                        if (isNaN(this.audioBufferEnd_)) {
16523                            audioTargetPts = videoTargetPts;
16524                        } else {
16525                            // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
16526                            // could append an overlapping segment, in which case there is a high likelyhood
16527                            // a tag could have a matching pts to videoBufferEnd_, which would cause
16528                            // that tag to get appended by the tag.pts >= targetPts check below even though it
16529                            // is a duplicate of what was previously appended
16530                            audioTargetPts = this.audioBufferEnd_ + 0.1;
16531                        }
16532
16533                        if (filteredVideoTags.length) {
16534                            // If targetPts intersects a GOP and we appended the tags for the GOP that came
16535                            // before targetPts, we want to make sure to trim audio tags at the pts
16536                            // of the first video tag to avoid brief moments of silence
16537                            audioTargetPts = Math.min(audioTargetPts, filteredVideoTags[0].pts);
16538                        }
16539
16540                        // skip tags with a presentation time less than the seek target/end of buffer
16541                        currentIndex = 0;
16542
16543                        while (currentIndex < audioTags.length) {
16544                            if (audioTags[currentIndex].pts >= audioTargetPts) {
16545                                break;
16546                            }
16547
16548                            currentIndex++;
16549                        }
16550
16551                        var filteredAudioTags = audioTags.slice(currentIndex);
16552
16553                        // update the audio and video buffer ends
16554                        if (filteredAudioTags.length) {
16555                            this.audioBufferEnd_ = filteredAudioTags[filteredAudioTags.length - 1].pts;
16556                        }
16557                        if (filteredVideoTags.length) {
16558                            this.videoBufferEnd_ = filteredVideoTags[filteredVideoTags.length - 1].pts;
16559                        }
16560
16561                        var tags = this.getOrderedTags_(filteredVideoTags, filteredAudioTags);
16562
16563                        if (tags.length === 0) {
16564                            return;
16565                        }
16566
16567                        // If we are appending data that comes before our target pts, we want to tell
16568                        // the swf to adjust its notion of current time to account for the extra tags
16569                        // we are appending to complete the GOP that intersects with targetPts
16570                        if (tags[0].pts < videoTargetPts && tech.seeking()) {
16571                            var fudgeFactor = 1 / 30;
16572                            var currentTime = tech.currentTime();
16573                            var diff = (videoTargetPts - tags[0].pts) / 1e3;
16574                            var adjustedTime = currentTime - diff;
16575
16576                            if (adjustedTime < fudgeFactor) {
16577                                adjustedTime = 0;
16578                            }
16579
16580                            try {
16581                                this.mediaSource_.swfObj.vjs_adjustCurrentTime(adjustedTime);
16582                            } catch (e) {
16583                                // no-op for backwards compatability of swf. If adjustCurrentTime fails,
16584                                // the swf may incorrectly report currentTime and buffered ranges
16585                                // but should not affect playback over than the time displayed on the
16586                                // progress bar is inaccurate
16587                            }
16588                        }
16589
16590                        // concatenate the bytes into a single segment
16591                        for (var i = 0; i < tags.length; i++) {
16592                            segmentByteLength += tags[i].bytes.byteLength;
16593                        }
16594                        segment = new Uint8Array(segmentByteLength);
16595                        for (var i = 0, j = 0; i < tags.length; i++) {
16596                            segment.set(tags[i].bytes, j);
16597                            j += tags[i].bytes.byteLength;
16598                        }
16599
16600                        return segment;
16601                    }
16602
16603                    /**
16604                     * Assemble the FLV tags in decoder order.
16605                     *
16606                     * @private
16607                     * @param {Array} videoTags list of video tags
16608                     * @param {Array} audioTags list of audio tags
16609                     */
16610                }, {
16611                    key: 'getOrderedTags_',
16612                    value: function getOrderedTags_(videoTags, audioTags) {
16613                        var tag = undefined;
16614                        var tags = [];
16615
16616                        while (videoTags.length || audioTags.length) {
16617                            if (!videoTags.length) {
16618                                // only audio tags remain
16619                                tag = audioTags.shift();
16620                            } else if (!audioTags.length) {
16621                                // only video tags remain
16622                                tag = videoTags.shift();
16623                            } else if (audioTags[0].dts < videoTags[0].dts) {
16624                                // audio should be decoded next
16625                                tag = audioTags.shift();
16626                            } else {
16627                                // video should be decoded next
16628                                tag = videoTags.shift();
16629                            }
16630
16631                            tags.push(tag);
16632                        }
16633
16634                        return tags;
16635                    }
16636                }]);
16637
16638                return FlashSourceBuffer;
16639            })(_videoJs2['default'].EventTarget);
16640
16641            exports['default'] = FlashSourceBuffer;
16642            module.exports = exports['default'];
16643        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
16644    },{"./add-text-track-data":62,"./create-text-tracks-if-necessary":65,"./flash-constants":66,"./flash-transmuxer-worker":69,"./remove-cues-from-track":71,"global/window":30,"mux.js/lib/flv":44,"webworkify":75}],69:[function(require,module,exports){
16645        /**
16646         * @file flash-transmuxer-worker.js
16647         */
16648        'use strict';
16649
16650        Object.defineProperty(exports, '__esModule', {
16651            value: true
16652        });
16653
16654        var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
16655
16656        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
16657
16658        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
16659
16660        var _globalWindow = require('global/window');
16661
16662        var _globalWindow2 = _interopRequireDefault(_globalWindow);
16663
16664        var _muxJsLibFlv = require('mux.js/lib/flv');
16665
16666        var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
16667
16668        /**
16669         * Re-emits transmuxer events by converting them into messages to the
16670         * world outside the worker.
16671         *
16672         * @param {Object} transmuxer the transmuxer to wire events on
16673         * @private
16674         */
16675        var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
16676            transmuxer.on('data', function (segment) {
16677                _globalWindow2['default'].postMessage({
16678                    action: 'data',
16679                    segment: segment
16680                });
16681            });
16682
16683            transmuxer.on('done', function (data) {
16684                _globalWindow2['default'].postMessage({ action: 'done' });
16685            });
16686        };
16687
16688        /**
16689         * All incoming messages route through this hash. If no function exists
16690         * to handle an incoming message, then we ignore the message.
16691         *
16692         * @class MessageHandlers
16693         * @param {Object} options the options to initialize with
16694         */
16695
16696        var MessageHandlers = (function () {
16697            function MessageHandlers(options) {
16698                _classCallCheck(this, MessageHandlers);
16699
16700                this.options = options || {};
16701                this.init();
16702            }
16703
16704            /**
16705             * Our web wroker interface so that things can talk to mux.js
16706             * that will be running in a web worker. The scope is passed to this by
16707             * webworkify.
16708             *
16709             * @param {Object} self the scope for the web worker
16710             */
16711
16712            /**
16713             * initialize our web worker and wire all the events.
16714             */
16715
16716            _createClass(MessageHandlers, [{
16717                key: 'init',
16718                value: function init() {
16719                    if (this.transmuxer) {
16720                        this.transmuxer.dispose();
16721                    }
16722                    this.transmuxer = new _muxJsLibFlv2['default'].Transmuxer(this.options);
16723                    wireTransmuxerEvents(this.transmuxer);
16724                }
16725
16726                /**
16727                 * Adds data (a ts segment) to the start of the transmuxer pipeline for
16728                 * processing.
16729                 *
16730                 * @param {ArrayBuffer} data data to push into the muxer
16731                 */
16732            }, {
16733                key: 'push',
16734                value: function push(data) {
16735                    // Cast array buffer to correct type for transmuxer
16736                    var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
16737
16738                    this.transmuxer.push(segment);
16739                }
16740
16741                /**
16742                 * Recreate the transmuxer so that the next segment added via `push`
16743                 * start with a fresh transmuxer.
16744                 */
16745            }, {
16746                key: 'reset',
16747                value: function reset() {
16748                    this.init();
16749                }
16750
16751                /**
16752                 * Forces the pipeline to finish processing the last segment and emit its
16753                 * results.
16754                 */
16755            }, {
16756                key: 'flush',
16757                value: function flush() {
16758                    this.transmuxer.flush();
16759                }
16760            }]);
16761
16762            return MessageHandlers;
16763        })();
16764
16765        var FlashTransmuxerWorker = function FlashTransmuxerWorker(self) {
16766            self.onmessage = function (event) {
16767                if (event.data.action === 'init' && event.data.options) {
16768                    this.messageHandlers = new MessageHandlers(event.data.options);
16769                    return;
16770                }
16771
16772                if (!this.messageHandlers) {
16773                    this.messageHandlers = new MessageHandlers();
16774                }
16775
16776                if (event.data && event.data.action && event.data.action !== 'init') {
16777                    if (this.messageHandlers[event.data.action]) {
16778                        this.messageHandlers[event.data.action](event.data);
16779                    }
16780                }
16781            };
16782        };
16783
16784        exports['default'] = function (self) {
16785            return new FlashTransmuxerWorker(self);
16786        };
16787
16788        module.exports = exports['default'];
16789    },{"global/window":30,"mux.js/lib/flv":44}],70:[function(require,module,exports){
16790        (function (global){
16791            /**
16792             * @file html-media-source.js
16793             */
16794            'use strict';
16795
16796            Object.defineProperty(exports, '__esModule', {
16797                value: true
16798            });
16799
16800            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
16801
16802            var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
16803
16804            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
16805
16806            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
16807
16808            function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
16808onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16809
16810            var _globalWindow = require('global/window');
16811
16812            var _globalWindow2 = _interopRequireDefault(_globalWindow);
16813
16814            var _globalDocument = require('global/document');
16815
16816            var _globalDocument2 = _interopRequireDefault(_globalDocument);
16817
16818            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
16819
16820            var _videoJs2 = _interopRequireDefault(_videoJs);
16821
16822            var _virtualSourceBuffer = require('./virtual-source-buffer');
16823
16824            var _virtualSourceBuffer2 = _interopRequireDefault(_virtualSourceBuffer);
16825
16826            var _addTextTrackData = require('./add-text-track-data');
16827
16828            var _codecUtils = require('./codec-utils');
16829
16830            var _cleanupTextTracks = require('./cleanup-text-tracks');
16831
16832            /**
16833             * Our MediaSource implementation in HTML, mimics native
16834             * MediaSource where/if possible.
16835             *
16836             * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
16837             * @class HtmlMediaSource
16838             * @extends videojs.EventTarget
16839             */
16840
16841            var HtmlMediaSource = (function (_videojs$EventTarget) {
16842                _inherits(HtmlMediaSource, _videojs$EventTarget);
16843
16844                function HtmlMediaSource() {
16845                    var _this = this;
16846
16847                    _classCallCheck(this, HtmlMediaSource);
16848
16849                    _get(Object.getPrototypeOf(HtmlMediaSource.prototype), 'constructor', this).call(this);
16850                    var property = undefined;
16851
16852                    this.nativeMediaSource_ = new _globalWindow2['default'].MediaSource();
16853                    // delegate to the native MediaSource's methods by default
16854                    for (property in this.nativeMediaSource_) {
16855                        if (!(property in HtmlMediaSource.prototype) && typeof this.nativeMediaSource_[property] === 'function') {
16856                            this[property] = this.nativeMediaSource_[property].bind(this.nativeMediaSource_);
16857                        }
16858                    }
16859
16860                    // emulate `duration` and `seekable` until seeking can be
16861                    // handled uniformly for live streams
16862                    // see https://github.com/w3c/media-source/issues/5
16863                    this.duration_ = NaN;
16864                    Object.defineProperty(this, 'duration', {
16865                        get: function get() {
16866                            if (this.duration_ === Infinity) {
16867                                return this.duration_;
16868                            }
16869                            return this.nativeMediaSource_.duration;
16870                        },
16871                        set: function set(duration) {
16872                            this.duration_ = duration;
16873                            if (duration !== Infinity) {
16874                                this.nativeMediaSource_.duration = duration;
16875                                return;
16876                            }
16877                        }
16878                    });
16879                    Object.defineProperty(this, 'seekable', {
16880                        get: function get() {
16881                            if (this.duration_ === Infinity) {
16882                                return _videoJs2['default'].createTimeRanges([[0, this.nativeMediaSource_.duration]]);
16883                            }
16884                            return this.nativeMediaSource_.seekable;
16885                        }
16886                    });
16887
16888                    Object.defineProperty(this, 'readyState', {
16889                        get: function get() {
16890                            return this.nativeMediaSource_.readyState;
16891                        }
16892                    });
16893
16894                    Object.defineProperty(this, 'activeSourceBuffers', {
16895                        get: function get() {
16896                            return this.activeSourceBuffers_;
16897                        }
16898                    });
16899
16900                    // the list of virtual and native SourceBuffers created by this
16901                    // MediaSource
16902                    this.sourceBuffers = [];
16903
16904                    this.activeSourceBuffers_ = [];
16905
16906                    /**
16907                     * update the list of active source buffers based upon various
16908                     * imformation from HLS and video.js
16909                     *
16910                     * @private
16911                     */
16912                    this.updateActiveSourceBuffers_ = function () {
16913                        // Retain the reference but empty the array
16914                        _this.activeSourceBuffers_.length = 0;
16915
16916                        // By default, the audio in the combined virtual source buffer is enabled
16917                        // and the audio-only source buffer (if it exists) is disabled.
16918                        var combined = false;
16919                        var audioOnly = true;
16920
16921                        // TODO: maybe we can store the sourcebuffers on the track objects?
16922                        // safari may do something like this
16923                        for (var i = 0; i < _this.player_.audioTracks().length; i++) {
16924                            var track = _this.player_.audioTracks()[i];
16925
16926                            if (track.enabled && track.kind !== 'main') {
16927                                // The enabled track is an alternate audio track so disable the audio in
16928                                // the combined source buffer and enable the audio-only source buffer.
16929                                combined = true;
16930                                audioOnly = false;
16931                                break;
16932                            }
16933                        }
16934
16935                        // Since we currently support a max of two source buffers, add all of the source
16936                        // buffers (in order).
16937                        _this.sourceBuffers.forEach(function (sourceBuffer) {
16938                            /* eslinst-disable */
16939                            // TODO once codecs are required, we can switch to using the codecs to determine
16940                            //      what stream is the video stream, rather than relying on videoTracks
16941                            /* eslinst-enable */
16942
16943                            sourceBuffer.appendAudioInitSegment_ = true;
16944
16945                            if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
16946                                // combined
16947                                sourceBuffer.audioDisabled_ = combined;
16948                            } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
16949                                // If the "combined" source buffer is video only, then we do not want
16950                                // disable the audio-only source buffer (this is mostly for demuxed
16951                                // audio and video hls)
16952                                sourceBuffer.audioDisabled_ = true;
16953                                audioOnly = false;
16954                            } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
16955                                // audio only
16956                                sourceBuffer.audioDisabled_ = audioOnly;
16957                                if (audioOnly) {
16958                                    return;
16959                                }
16960                            }
16961
16962                            _this.activeSourceBuffers_.push(sourceBuffer);
16963                        });
16964                    };
16965
16966                    this.onPlayerMediachange_ = function () {
16967                        _this.sourceBuffers.forEach(function (sourceBuffer) {
16968                            sourceBuffer.appendAudioInitSegment_ = true;
16969                        });
16970                    };
16971
16972                    // Re-emit MediaSource events on the polyfill
16973                    ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
16974                        this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
16975                    }, this);
16976
16977                    // capture the associated player when the MediaSource is
16978                    // successfully attached
16979                    this.on('sourceopen', function (event) {
16980                        // Get the player this MediaSource is attached to
16981                        var video = _globalDocument2['default'].querySelector('[src="' + _this.url_ + '"]');
16982
16983                        if (!video) {
16984                            return;
16985                        }
16986
16987                        _this.player_ = (0, _videoJs2['default'])(video.parentNode);
16988
16989                        if (_this.player_.audioTracks && _this.player_.audioTracks()) {
16990                            _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
16991                            _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
16992                            _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
16993                        }
16994
16995                        _this.player_.on('mediachange', _this.onPlayerMediachange_);
16996                    });
16997
16998                    this.on('sourceended', function (event) {
16999                        var duration = (0, _addTextTrackData.durationOfVideo)(_this.duration);
17000
17001                        for (var i = 0; i < _this.sourceBuffers.length; i++) {
17002                            var sourcebuffer = _this.sourceBuffers[i];
17003                            var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
17004
17005                            if (cues && cues.length) {
17006                                cues[cues.length - 1].endTime = duration;
17007                            }
17008                        }
17009                    });
17010
17011                    // explicitly terminate any WebWorkers that were created
17012                    // by SourceHandlers
17013                    this.on('sourceclose', function (event) {
17014                        this.sourceBuffers.forEach(function (sourceBuffer) {
17015                            if (sourceBuffer.transmuxer_) {
17016                                sourceBuffer.transmuxer_.terminate();
17017                            }
17018                        });
17019
17020                        this.sourceBuffers.length = 0;
17021                        if (!this.player_) {
17022                            return;
17023                        }
17024
17025                        (0, _cleanupTextTracks.cleanupTextTracks)(this.player_);
17026
17027                        if (this.player_.audioTracks && this.player_.audioTracks()) {
17028                            this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
17029                            this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
17030                            this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
17031                        }
17032
17033                        // We can only change this if the player hasn't been disposed of yet
17034                        // because `off` eventually tries to use the el_ property. If it has
17035                        // been disposed of, then don't worry about it because there are no
17036                        // event handlers left to unbind anyway
17037                        if (this.player_.el_) {
17038                            this.player_.off('mediachange', this.onPlayerMediachange_);
17039                        }
17040                    });
17041                }
17042
17043                /**
17044                 * Add a range that that can now be seeked to.
17045                 *
17046                 * @param {Double} start where to start the addition
17047                 * @param {Double} end where to end the addition
17048                 * @private
17049                 */
17050
17051                _createClass(HtmlMediaSource, [{
17052                    key: 'addSeekableRange_',
17053                    value: function addSeekableRange_(start, end) {
17054                        var error = undefined;
17055
17056                        if (this.duration !== Infinity) {
17057                            error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
17058                            error.name = 'InvalidStateError';
17059                            error.code = 11;
17060                            throw error;
17061                        }
17062
17063                        if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
17064                            this.nativeMediaSource_.duration = end;
17065                        }
17066                    }
17067
17068                    /**
17069                     * Add a source buffer to the media source.
17070                     *
17071                     * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
17072                     * @param {String} type the content-type of the content
17073                     * @return {Object} the created source buffer
17074                     */
17075                }, {
17076                    key: 'addSourceBuffer',
17077                    value: function addSourceBuffer(type) {
17078                        var buffer = undefined;
17079                        var parsedType = (0, _codecUtils.parseContentType)(type);
17080
17081                        // Create a VirtualSourceBuffer to transmux MPEG-2 transport
17082                        // stream segments into fragmented MP4s
17083                        if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
17084                            var codecs = [];
17085
17086                            if (parsedType.parameters && parsedType.parameters.codecs) {
17087                                codecs = parsedType.parameters.codecs.split(',');
17088                                codecs = (0, _codecUtils.translateLegacyCodecs)(codecs);
17089                                codecs = codecs.filter(function (codec) {
17090                                    return (0, _codecUtils.isAudioCodec)(codec) || (0, _codecUtils.isVideoCodec)(codec);
17091                                });
17092                            }
17093
17094                            if (codecs.length === 0) {
17095                                codecs = ['avc1.4d400d', 'mp4a.40.2'];
17096                            }
17097
17098                            buffer = new _virtualSourceBuffer2['default'](this, codecs);
17099
17100                            if (this.sourceBuffers.length !== 0) {
17101                                // If another VirtualSourceBuffer already exists, then we are creating a
17102                                // SourceBuffer for an alternate audio track and therefore we know that
17103                                // the source has both an audio and video track.
17104                                // That means we should trigger the manual creation of the real
17105                                // SourceBuffers instead of waiting for the transmuxer to return data
17106                                this.sourceBuffers[0].createRealSourceBuffers_();
17107                                buffer.createRealSourceBuffers_();
17108
17109                                // Automatically disable the audio on the first source buffer if
17110                                // a second source buffer is ever created
17111                                this.sourceBuffers[0].audioDisabled_ = true;
17112                            }
17113                        } else {
17114                            // delegate to the native implementation
17115                            buffer = this.nativeMediaSource_.addSourceBuffer(type);
17116                        }
17117
17118                        this.sourceBuffers.push(buffer);
17119                        return buffer;
17120                    }
17121                }]);
17122
17123                return HtmlMediaSource;
17124            })(_videoJs2['default'].EventTarget);
17125
17126            exports['default'] = HtmlMediaSource;
17127            module.exports = exports['default'];
17128        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
17129    },{"./add-text-track-data":62,"./cleanup-text-tracks":63,"./codec-utils":64,"./virtual-source-buffer":74,"global/document":29,"global/window":30}],71:[function(require,module,exports){
17130        /**
17131         * @file remove-cues-from-track.js
17132         */
17133
17134        /**
17135         * Remove cues from a track on video.js.
17136         *
17137         * @param {Double} start start of where we should remove the cue
17138         * @param {Double} end end of where the we should remove the cue
17139         * @param {Object} track the text track to remove the cues from
17140         * @private
17141         */
17142        "use strict";
17143
17144        Object.defineProperty(exports, "__esModule", {
17145            value: true
17146        });
17147        var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
17148            var i = undefined;
17149            var cue = undefined;
17150
17151            if (!track) {
17152                return;
17153            }
17154
17155            if (!track.cues) {
17156                return;
17157            }
17158
17159            i = track.cues.length;
17160
17161            while (i--) {
17162                cue = track.cues[i];
17163
17164                // Remove any overlapping cue
17165                if (cue.startTime <= end && cue.endTime >= start) {
17166                    track.removeCue(cue);
17167                }
17168            }
17169        };
17170
17171        exports["default"] = removeCuesFromTrack;
17172        module.exports = exports["default"];
17173    },{}],72:[function(require,module,exports){
17174        /**
17175         * @file transmuxer-worker.js
17176         */
17177
17178        /**
17179         * videojs-contrib-media-sources
17180         *
17181         * Copyright (c) 2015 Brightcove
17182         * All rights reserved.
17183         *
17184         * Handles communication between the browser-world and the mux.js
17185         * transmuxer running inside of a WebWorker by exposing a simple
17186         * message-based interface to a Transmuxer object.
17187         */
17188        'use strict';
17189
17190        Object.defineProperty(exports, '__esModule', {
17191            value: true
17192        });
17193
17194        var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
17195
17196        function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
17197
17198        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
17199
17200        var _globalWindow = require('global/window');
17201
17202        var _globalWindow2 = _interopRequireDefault(_globalWindow);
17203
17204        var _muxJsLibMp4 = require('mux.js/lib/mp4');
17205
17206        var _muxJsLibMp42 = _interopRequireDefault(_muxJsLibMp4);
17207
17208        /**
17209         * Re-emits transmuxer events by converting them into messages to the
17210         * world outside the worker.
17211         *
17212         * @param {Object} transmuxer the transmuxer to wire events on
17213         * @private
17214         */
17215        var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
17216            transmuxer.on('data', function (segment) {
17217                // transfer ownership of the underlying ArrayBuffer
17218                // instead of doing a copy to save memory
17219                // ArrayBuffers are transferable but generic TypedArrays are not
17220                // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
17221                var initArray = segment.initSegment;
17222
17223                segment.initSegment = {
17224                    data: initArray.buffer,
17225                    byteOffset: initArray.byteOffset,
17226                    byteLength: initArray.byteLength
17227                };
17228
17229                var typedArray = segment.data;
17230
17231                segment.data = typedArray.buffer;
17232                _globalWindow2['default'].postMessage({
17233                    action: 'data',
17234                    segment: segment,
17235                    byteOffset: typedArray.byteOffset,
17236                    byteLength: typedArray.byteLength
17237                }, [segment.data]);
17238            });
17239
17240            if (transmuxer.captionStream) {
17241                transmuxer.captionStream.on('data', function (caption) {
17242                    _globalWindow2['default'].postMessage({
17243                        action: 'caption',
17244                        data: caption
17245                    });
17246                });
17247            }
17248
17249            transmuxer.on('done', function (data) {
17250                _globalWindow2['default'].postMessage({ action: 'done' });
17251            });
17252        };
17253
17254        /**
17255         * All incoming messages route through this hash. If no function exists
17256         * to handle an incoming message, then we ignore the message.
17257         *
17258         * @class MessageHandlers
17259         * @param {Object} options the options to initialize with
17260         */
17261
17262        var MessageHandlers = (function () {
17263            function MessageHandlers(options) {
17264                _classCallCheck(this, MessageHandlers);
17265
17266                this.options = options || {};
17267                this.init();
17268            }
17269
17270            /**
17271             * Our web wroker interface so that things can talk to mux.js
17272             * that will be running in a web worker. the scope is passed to this by
17273             * webworkify.
17274             *
17275             * @param {Object} self the scope for the web worker
17276             */
17277
17278            /**
17279             * initialize our web worker and wire all the events.
17280             */
17281
17282            _createClass(MessageHandlers, [{
17283                key: 'init',
17284                value: function init() {
17285                    if (this.transmuxer) {
17286                        this.transmuxer.dispose();
17287                    }
17288                    this.transmuxer = new _muxJsLibMp42['default'].Transmuxer(this.options);
17289                    wireTransmuxerEvents(this.transmuxer);
17290                }
17291
17292                /**
17293                 * Adds data (a ts segment) to the start of the transmuxer pipeline for
17294                 * processing.
17295                 *
17296                 * @param {ArrayBuffer} data data to push into the muxer
17297                 */
17298            }, {
17299                key: 'push',
17300                value: function push(data) {
17301                    // Cast array buffer to correct type for transmuxer
17302                    var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
17303
17304                    this.transmuxer.push(segment);
17305                }
17306
17307                /**
17308                 * Recreate the transmuxer so that the next segment added via `push`
17309                 * start with a fresh transmuxer.
17310                 */
17311            }, {
17312                key: 'reset',
17313                value: function reset() {
17314                    this.init();
17315                }
17316
17317                /**
17318                 * Set the value that will be used as the `baseMediaDecodeTime` time for the
17319                 * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
17320                 * set relative to the first based on the PTS values.
17321                 *
17322                 * @param {Object} data used to set the timestamp offset in the muxer
17323                 */
17324            }, {
17325                key: 'setTimestampOffset',
17326                value: function setTimestampOffset(data) {
17327                    var timestampOffset = data.timestampOffset || 0;
17328
17329                    this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
17330                }
17331            }, {
17332                key: 'setAudioAppendStart',
17333                value: function setAudioAppendStart(data) {
17334                    this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
17335                }
17336
17337                /**
17338                 * Forces the pipeline to finish processing the last segment and emit it's
17339                 * results.
17340                 *
17341                 * @param {Object} data event data, not really used
17342                 */
17343            }, {
17344                key: 'flush',
17345                value: function flush(data) {
17346                    this.transmuxer.flush();
17347                }
17348            }]);
17349
17350            return MessageHandlers;
17351        })();
17352
17353        var TransmuxerWorker = function TransmuxerWorker(self) {
17354            self.onmessage = function (event) {
17355                if (event.data.action === 'init' && event.data.options) {
17356                    this.messageHandlers = new MessageHandlers(event.data.options);
17357                    return;
17358                }
17359
17360                if (!this.messageHandlers) {
17361                    this.messageHandlers = new MessageHandlers();
17362                }
17363
17364                if (event.data && event.data.action && event.data.action !== 'init') {
17365                    if (this.messageHandlers[event.data.action]) {
17366                        this.messageHandlers[event.data.action](event.data);
17367                    }
17368                }
17369            };
17370        };
17371
17372        exports['default'] = function (self) {
17373            return new TransmuxerWorker(self);
17374        };
17375
17376        module.exports = exports['default'];
17377    },{"global/window":30,"mux.js/lib/mp4":53}],73:[function(require,module,exports){
17378        (function (global){
17379            /**
17380             * @file videojs-contrib-media-sources.js
17381             */
17382            'use strict';
17383
17384            Object.defineProperty(exports, '__esModule', {
17385                value: true
17386            });
17387
17388            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
17389
17390            var _globalWindow = require('global/window');
17391
17392            var _globalWindow2 = _interopRequireDefault(_globalWindow);
17393
17394            var _flashMediaSource = require('./flash-media-source');
17395
17396            var _flashMediaSource2 = _interopRequireDefault(_flashMediaSource);
17397
17398            var _htmlMediaSource = require('./html-media-source');
17399
17400            var _htmlMediaSource2 = _interopRequireDefault(_htmlMediaSource);
17401
17402            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
17403
17404            var _videoJs2 = _interopRequireDefault(_videoJs);
17405
17406            var urlCount = 0;
17407
17408// ------------
17409// Media Source
17410// ------------
17411
17412            var defaults = {
17413                // how to determine the MediaSource implementation to use. There
17414                // are three available modes:
17415                // - auto: use native MediaSources where available and Flash
17416                //   everywhere else
17417                // - html5: always use native MediaSources
17418                // - flash: always use the Flash MediaSource polyfill
17419                mode: 'auto'
17420            };
17421
17422// store references to the media sources so they can be connected
17423// to a video element (a swf object)
17424// TODO: can we store this somewhere local to this module?
17425            _videoJs2['default'].mediaSources = {};
17426
17427            /**
17428             * Provide a method for a swf object to notify JS that a
17429             * media source is now open.
17430             *
17431             * @param {String} msObjectURL string referencing the MSE Object URL
17432             * @param {String} swfId the swf id
17433             */
17434            var open = function open(msObjectURL, swfId) {
17435                var mediaSource = _videoJs2['default'].mediaSources[msObjectURL];
17436
17437                if (mediaSource) {
17438                    mediaSource.trigger({ type: 'sourceopen', swfId: swfId });
17439                } else {
17440                    throw new Error('Media Source not found (Video.js)');
17441                }
17442            };
17443
17444            /**
17445             * Check to see if the native MediaSource object exists and supports
17446             * an MP4 container with both H.264 video and AAC-LC audio.
17447             *
17448             * @return {Boolean} if  native media sources are supported
17449             */
17450            var supportsNativeMediaSources = function supportsNativeMediaSources() {
17451                return !!_globalWindow2['default'].MediaSource && !!_globalWindow2['default'].MediaSource.isTypeSupported && _globalWindow2['default'].MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
17452            };
17453
17454            /**
17455             * An emulation of the MediaSource API so that we can support
17456             * native and non-native functionality such as flash and
17457             * video/mp2t videos. returns an instance of HtmlMediaSource or
17458             * FlashMediaSource depending on what is supported and what options
17459             * are passed in.
17460             *
17461             * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
17462             * @param {Object} options options to use during setup.
17463             */
17464            var MediaSource = function MediaSource(options) {
17465                var settings = _videoJs2['default'].mergeOptions(defaults, options);
17466
17467                this.MediaSource = {
17468                    open: open,
17469                    supportsNativeMediaSources: supportsNativeMediaSources
17470                };
17471
17472                // determine whether HTML MediaSources should be used
17473                if (settings.mode === 'html5' || settings.mode === 'auto' && supportsNativeMediaSources()) {
17474                    return new _htmlMediaSource2['default']();
17475                } else if (_videoJs2['default'].getTech('Flash')) {
17476                    return new _flashMediaSource2['default']();
17477                }
17478
17479                throw new Error('Cannot use Flash or Html5 to create a MediaSource for this video');
17480            };
17481
17482            exports.MediaSource = MediaSource;
17483            MediaSource.open = open;
17484            MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
17485
17486            /**
17487             * A wrapper around the native URL for our MSE object
17488             * implementation, this object is exposed under videojs.URL
17489             *
17490             * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
17491             */
17492            var URL = {
17493                /**
17494                 * A wrapper around the native createObjectURL for our objects.
17495                 * This function maps a native or emulated mediaSource to a blob
17496                 * url so that it can be loaded into video.js
17497                 *
17498                 * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
17499                 * @param {MediaSource} object the object to create a blob url to
17500                 */
17501                createObjectURL: function createObjectURL(object) {
17502                    var objectUrlPrefix = 'blob:vjs-media-source/';
17503                    var url = undefined;
17504
17505                    // use the native MediaSource to generate an object URL
17506                    if (object instanceof _htmlMediaSource2['default']) {
17507                        url = _globalWindow2['default'].URL.createObjectURL(object.nativeMediaSource_);
17508                        object.url_ = url;
17509                        return url;
17510                    }
17511                    // if the object isn't an emulated MediaSource, delegate to the
17512                    // native implementation
17513                    if (!(object instanceof _flashMediaSource2['default'])) {
17514                        url = _globalWindow2['default'].URL.createObjectURL(object);
17515                        object.url_ = url;
17516                        return url;
17517                    }
17518
17519                    // build a URL that can be used to map back to the emulated
17520                    // MediaSource
17521                    url = objectUrlPrefix + urlCount;
17522
17523                    urlCount++;
17524
17525                    // setup the mapping back to object
17526                    _videoJs2['default'].mediaSources[url] = object;
17527
17528                    return url;
17529                }
17530            };
17531
17532            exports.URL = URL;
17533            _videoJs2['default'].MediaSource = MediaSource;
17534            _videoJs2['default'].URL = URL;
17535        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
17536    }
17536,{"./flash-media-source":67,"./html-media-source":70,"global/window":30}],74:[function(require,module,exports){
17537        (function (global){
17538            /**
17539             * @file virtual-source-buffer.js
17540             */
17541            'use strict';
17542
17543            Object.defineProperty(exports, '__esModule', {
17544                value: true
17545            });
17546
17547            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
17548
17549            var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
17550
17551            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
17552
17553            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
17554
17555            function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
17556
17557            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
17558
17559            var _videoJs2 = _interopRequireDefault(_videoJs);
17560
17561            var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
17562
17563            var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
17564
17565            var _removeCuesFromTrack = require('./remove-cues-from-track');
17566
17567            var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
17568
17569            var _addTextTrackData = require('./add-text-track-data');
17570
17571            var _webworkify = require('webworkify');
17572
17573            var _webworkify2 = _interopRequireDefault(_webworkify);
17574
17575            var _transmuxerWorker = require('./transmuxer-worker');
17576
17577            var _transmuxerWorker2 = _interopRequireDefault(_transmuxerWorker);
17578
17579            var _codecUtils = require('./codec-utils');
17580
17581// We create a wrapper around the SourceBuffer so that we can manage the
17582// state of the `updating` property manually. We have to do this because
17583// Firefox changes `updating` to false long before triggering `updateend`
17584// events and that was causing strange problems in videojs-contrib-hls
17585            var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) {
17586                var sourceBuffer = mediaSource.addSourceBuffer(mimeType);
17587                var wrapper = Object.create(null);
17588
17589                wrapper.updating = false;
17590                wrapper.realBuffer_ = sourceBuffer;
17591
17592                var _loop = function (key) {
17593                    if (typeof sourceBuffer[key] === 'function') {
17594                        wrapper[key] = function () {
17595                            return sourceBuffer[key].apply(sourceBuffer, arguments);
17596                        };
17597                    } else if (typeof wrapper[key] === 'undefined') {
17598                        Object.defineProperty(wrapper, key, {
17599                            get: function get() {
17600                                return sourceBuffer[key];
17601                            },
17602                            set: function set(v) {
17603                                return sourceBuffer[key] = v;
17604                            }
17605                        });
17606                    }
17607                };
17608
17609                for (var key in sourceBuffer) {
17610                    _loop(key);
17611                }
17612
17613                return wrapper;
17614            };
17615
17616            /**
17617             * VirtualSourceBuffers exist so that we can transmux non native formats
17618             * into a native format, but keep the same api as a native source buffer.
17619             * It creates a transmuxer, that works in its own thread (a web worker) and
17620             * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
17621             * then send all of that data to the naive sourcebuffer so that it is
17622             * indestinguishable from a natively supported format.
17623             *
17624             * @param {HtmlMediaSource} mediaSource the parent mediaSource
17625             * @param {Array} codecs array of codecs that we will be dealing with
17626             * @class VirtualSourceBuffer
17627             * @extends video.js.EventTarget
17628             */
17629
17630            var VirtualSourceBuffer = (function (_videojs$EventTarget) {
17631                _inherits(VirtualSourceBuffer, _videojs$EventTarget);
17632
17633                function VirtualSourceBuffer(mediaSource, codecs) {
17634                    var _this = this;
17635
17636                    _classCallCheck(this, VirtualSourceBuffer);
17637
17638                    _get(Object.getPrototypeOf(VirtualSourceBuffer.prototype), 'constructor', this).call(this, _videoJs2['default'].EventTarget);
17639                    this.timestampOffset_ = 0;
17640                    this.pendingBuffers_ = [];
17641                    this.bufferUpdating_ = false;
17642
17643                    this.mediaSource_ = mediaSource;
17644                    this.codecs_ = codecs;
17645                    this.audioCodec_ = null;
17646                    this.videoCodec_ = null;
17647                    this.audioDisabled_ = false;
17648                    this.appendAudioInitSegment_ = true;
17649
17650                    var options = {
17651                        remux: false
17652                    };
17653
17654                    this.codecs_.forEach(function (codec) {
17655                        if ((0, _codecUtils.isAudioCodec)(codec)) {
17656                            _this.audioCodec_ = codec;
17657                        } else if ((0, _codecUtils.isVideoCodec)(codec)) {
17658                            _this.videoCodec_ = codec;
17659                        }
17660                    });
17661
17662                    // append muxed segments to their respective native buffers as
17663                    // soon as they are available
17664                    this.transmuxer_ = (0, _webworkify2['default'])(_transmuxerWorker2['default']);
17665                    this.transmuxer_.postMessage({ action: 'init', options: options });
17666
17667                    this.transmuxer_.onmessage = function (event) {
17668                        if (event.data.action === 'data') {
17669                            return _this.data_(event);
17670                        }
17671
17672                        if (event.data.action === 'done') {
17673                            return _this.done_(event);
17674                        }
17675                    };
17676
17677                    // this timestampOffset is a property with the side-effect of resetting
17678                    // baseMediaDecodeTime in the transmuxer on the setter
17679                    Object.defineProperty(this, 'timestampOffset', {
17680                        get: function get() {
17681                            return this.timestampOffset_;
17682                        },
17683                        set: function set(val) {
17684                            if (typeof val === 'number' && val >= 0) {
17685                                this.timestampOffset_ = val;
17686                                this.appendAudioInitSegment_ = true;
17687
17688                                // We have to tell the transmuxer to set the baseMediaDecodeTime to
17689                                // the desired timestampOffset for the next segment
17690                                this.transmuxer_.postMessage({
17691                                    action: 'setTimestampOffset',
17692                                    timestampOffset: val
17693                                });
17694                            }
17695                        }
17696                    });
17697
17698                    // setting the append window affects both source buffers
17699                    Object.defineProperty(this, 'appendWindowStart', {
17700                        get: function get() {
17701                            return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
17702                        },
17703                        set: function set(start) {
17704                            if (this.videoBuffer_) {
17705                                this.videoBuffer_.appendWindowStart = start;
17706                            }
17707                            if (this.audioBuffer_) {
17708                                this.audioBuffer_.appendWindowStart = start;
17709                            }
17710                        }
17711                    });
17712
17713                    // this buffer is "updating" if either of its native buffers are
17714                    Object.defineProperty(this, 'updating', {
17715                        get: function get() {
17716                            return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
17717                        }
17718                    });
17719
17720                    // the buffered property is the intersection of the buffered
17721                    // ranges of the native source buffers
17722                    Object.defineProperty(this, 'buffered', {
17723                        get: function get() {
17724                            var start = null;
17725                            var end = null;
17726                            var arity = 0;
17727                            var extents = [];
17728                            var ranges = [];
17729
17730                            // neither buffer has been created yet
17731                            if (!this.videoBuffer_ && !this.audioBuffer_) {
17732                                return _videoJs2['default'].createTimeRange();
17733                            }
17734
17735                            // only one buffer is configured
17736                            if (!this.videoBuffer_) {
17737                                return this.audioBuffer_.buffered;
17738                            }
17739                            if (!this.audioBuffer_) {
17740                                return this.videoBuffer_.buffered;
17741                            }
17742
17743                            // both buffers are configured
17744                            if (this.audioDisabled_) {
17745                                return this.videoBuffer_.buffered;
17746                            }
17747
17748                            // both buffers are empty
17749                            if (this.videoBuffer_.buffered.length === 0 && this.audioBuffer_.buffered.length === 0) {
17750                                return _videoJs2['default'].createTimeRange();
17751                            }
17752
17753                            // Handle the case where we have both buffers and create an
17754                            // intersection of the two
17755                            var videoBuffered = this.videoBuffer_.buffered;
17756                            var audioBuffered = this.audioBuffer_.buffered;
17757                            var count = videoBuffered.length;
17758
17759                            // A) Gather up all start and end times
17760                            while (count--) {
17761                                extents.push({ time: videoBuffered.start(count), type: 'start' });
17762                                extents.push({ time: videoBuffered.end(count), type: 'end' });
17763                            }
17764                            count = audioBuffered.length;
17765                            while (count--) {
17766                                extents.push({ time: audioBuffered.start(count), type: 'start' });
17767                                extents.push({ time: audioBuffered.end(count), type: 'end' });
17768                            }
17769                            // B) Sort them by time
17770                            extents.sort(function (a, b) {
17771                                return a.time - b.time;
17772                            });
17773
17774                            // C) Go along one by one incrementing arity for start and decrementing
17775                            //    arity for ends
17776                            for (count = 0; count < extents.length; count++) {
17777                                if (extents[count].type === 'start') {
17778                                    arity++;
17779
17780                                    // D) If arity is ever incremented to 2 we are entering an
17781                                    //    overlapping range
17782                                    if (arity === 2) {
17783                                        start = extents[count].time;
17784                                    }
17785                                } else if (extents[count].type === 'end') {
17786                                    arity--;
17787
17788                                    // E) If arity is ever decremented to 1 we leav
17788ing an
17789                                    //    overlapping range
17790                                    if (arity === 1) {
17791                                        end = extents[count].time;
17792                                    }
17793                                }
17794
17795                                // F) Record overlapping ranges
17796                                if (start !== null && end !== null) {
17797                                    ranges.push([start, end]);
17798                                    start = null;
17799                                    end = null;
17800                                }
17801                            }
17802
17803                            return _videoJs2['default'].createTimeRanges(ranges);
17804                        }
17805                    });
17806                }
17807
17808                /**
17809                 * When we get a data event from the transmuxer
17810                 * we call this function and handle the data that
17811                 * was sent to us
17812                 *
17813                 * @private
17814                 * @param {Event} event the data event from the transmuxer
17815                 */
17816
17817                _createClass(VirtualSourceBuffer, [{
17818                    key: 'data_',
17819                    value: function data_(event) {
17820                        var segment = event.data.segment;
17821
17822                        // Cast ArrayBuffer to TypedArray
17823                        segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
17824
17825                        segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
17826
17827                        (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
17828
17829                        // Add the segments to the pendingBuffers array
17830                        this.pendingBuffers_.push(segment);
17831                        return;
17832                    }
17833
17834                    /**
17835                     * When we get a done event from the transmuxer
17836                     * we call this function and we process all
17837                     * of the pending data that we have been saving in the
17838                     * data_ function
17839                     *
17840                     * @private
17841                     * @param {Event} event the done event from the transmuxer
17842                     */
17843                }, {
17844                    key: 'done_',
17845                    value: function done_(event) {
17846                        // All buffers should have been flushed from the muxer
17847                        // start processing anything we have received
17848                        this.processPendingSegments_();
17849                        return;
17850                    }
17851
17852                    /**
17853                     * Create our internal native audio/video source buffers and add
17854                     * event handlers to them with the following conditions:
17855                     * 1. they do not already exist on the mediaSource
17856                     * 2. this VSB has a codec for them
17857                     *
17858                     * @private
17859                     */
17860                }, {
17861                    key: 'createRealSourceBuffers_',
17862                    value: function createRealSourceBuffers_() {
17863                        var _this2 = this;
17864
17865                        var types = ['audio', 'video'];
17866
17867                        types.forEach(function (type) {
17868                            // Don't create a SourceBuffer of this type if we don't have a
17869                            // codec for it
17870                            if (!_this2[type + 'Codec_']) {
17871                                return;
17872                            }
17873
17874                            // Do nothing if a SourceBuffer of this type already exists
17875                            if (_this2[type + 'Buffer_']) {
17876                                return;
17877                            }
17878
17879                            var buffer = null;
17880
17881                            // If the mediasource already has a SourceBuffer for the codec
17882                            // use that
17883                            if (_this2.mediaSource_[type + 'Buffer_']) {
17884                                buffer = _this2.mediaSource_[type + 'Buffer_'];
17885                                // In multiple audio track cases, the audio source buffer is disabled
17886                                // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier
17887                                // than createRealSourceBuffers_ is called to create the second
17888                                // VirtualSourceBuffer because that happens as a side-effect of
17889                                // videojs-contrib-hls starting the audioSegmentLoader. As a result,
17890                                // the audioBuffer is essentially "ownerless" and no one will toggle
17891                                // the `updating` state back to fal
vendor: 9,901 bytes, lines 17891-18085
17891se once the `updateend` event is received
17892                                //
17893                                // Setting `updating` to false manually will work around this
17894                                // situation and allow work to continue
17895                                buffer.updating = false;
17896                            } else {
17897                                var codecProperty = type + 'Codec_';
17898                                var mimeType = type + '/mp4;codecs="' + _this2[codecProperty] + '"';
17899
17900                                buffer = makeWrappedSourceBuffer(_this2.mediaSource_.nativeMediaSource_, mimeType);
17901
17902                                _this2.mediaSource_[type + 'Buffer_'] = buffer;
17903                            }
17904
17905                            _this2[type + 'Buffer_'] = buffer;
17906
17907                            // Wire up the events to the SourceBuffer
17908                            ['update', 'updatestart', 'updateend'].forEach(function (event) {
17909                                buffer.addEventListener(event, function () {
17910                                    // if audio is disabled
17911                                    if (type === 'audio' && _this2.audioDisabled_) {
17912                                        return;
17913                                    }
17914
17915                                    if (event === 'updateend') {
17916                                        _this2[type + 'Buffer_'].updating = false;
17917                                    }
17918
17919                                    var shouldTrigger = types.every(function (t) {
17920                                        // skip checking audio's updating status if audio
17921                                        // is not enabled
17922                                        if (t === 'audio' && _this2.audioDisabled_) {
17923                                            return true;
17924                                        }
17925                                        // if the other type if updating we don't trigger
17926                                        if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
17927                                            return false;
17928                                        }
17929                                        return true;
17930                                    });
17931
17932                                    if (shouldTrigger) {
17933                                        return _this2.trigger(event);
17934                                    }
17935                                });
17936                            });
17937                        });
17938                    }
17939
17940                    /**
17941                     * Emulate the native mediasource function, but our function will
17942                     * send all of the proposed segments to the transmuxer so that we
17943                     * can transmux them before we append them to our internal
17944                     * native source buffers in the correct format.
17945                     *
17946                     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
17947                     * @param {Uint8Array} segment the segment to append to the buffer
17948                     */
17949                }, {
17950                    key: 'appendBuffer',
17951                    value: function appendBuffer(segment) {
17952                        // Start the internal "updating" state
17953                        this.bufferUpdating_ = true;
17954
17955                        if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
17956                            var audioBuffered = this.audioBuffer_.buffered;
17957
17958                            this.transmuxer_.postMessage({
17959                                action: 'setAudioAppendStart',
17960                                appendStart: audioBuffered.end(audioBuffered.length - 1)
17961                            });
17962                        }
17963
17964                        this.transmuxer_.postMessage({
17965                            action: 'push',
17966                            // Send the typed-array of data as an ArrayBuffer so that
17967                            // it can be sent as a "Transferable" and avoid the costly
17968                            // memory copy
17969                            data: segment.buffer,
17970
17971                            // To recreate the original typed-array, we need information
17972                            // about what portion of the ArrayBuffer it was a view into
17973                            byteOffset: segment.byteOffset,
17974                            byteLength: segment.byteLength
17975                        }, [segment.buffer]);
17976                        this.transmuxer_.postMessage({ action: 'flush' });
17977                    }
17978
17979                    /**
17980                     * Emulate the native mediasource function and remove parts
17981                     * of the buffer from any of our internal buffers that exist
17982                     *
17983                     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
17984                     * @param {Double} start position to start the remove at
17985                     * @param {Double} end position to end the remove at
17986                     */
17987                }, {
17988                    key: 'remove',
17989                    value: function remove(start, end) {
17990                        if (this.videoBuffer_) {
17991                            this.videoBuffer_.updating = true;
17992                            this.videoBuffer_.remove(start, end);
17993                        }
17994                        if (!this.audioDisabled_ && this.audioBuffer_) {
17995                            this.audioBuffer_.updating = true;
17996                            this.audioBuffer_.remove(start, end);
17997                        }
17998
17999                        // Remove Metadata Cues (id3)
18000                        (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
18001
18002                        // Remove Any Captions
18003                        (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTrack_);
18004                    }
18005
18006                    /**
18007                     * Process any segments that the muxer has output
18008                     * Concatenate segments together based on type and append them into
18009                     * their respective sourceBuffers
18010                     *
18011                     * @private
18012                     */
18013                }, {
18014                    key: 'processPendingSegments_',
18015                    value: function processPendingSegments_() {
18016                        var sortedSegments = {
18017                            video: {
18018                                segments: [],
18019                                bytes: 0
18020                            },
18021                            audio: {
18022                                segments: [],
18023                                bytes: 0
18024                            },
18025                            captions: [],
18026                            metadata: []
18027                        };
18028
18029                        // Sort segments into separate video/audio arrays and
18030                        // keep track of their total byte lengths
18031                        sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
18032                            var type = segment.type;
18033                            var data = segment.data;
18034                            var initSegment = segment.initSegment;
18035
18036                            segmentObj[type].segments.push(data);
18037                            segmentObj[type].bytes += data.byteLength;
18038
18039                            segmentObj[type].initSegment = initSegment;
18040
18041                            // Gather any captions into a single array
18042                            if (segment.captions) {
18043                                segmentObj.captions = segmentObj.captions.concat(segment.captions);
18044                            }
18045
18046                            if (segment.info) {
18047                                segmentObj[type].info = segment.info;
18048                            }
18049
18050                            // Gather any metadata into a single array
18051                            if (segment.metadata) {
18052                                segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
18053                            }
18054
18055                            return segmentObj;
18056                        }, sortedSegments);
18057
18058                        // Create the real source buffers if they don't exist by now since we
18059                        // finally are sure what tracks are contained in the source
18060                        if (!this.videoBuffer_ && !this.audioBuffer_) {
18061                            // Remove any codecs that may have been specified by default but
18062                            // are no longer applicable now
18063                            if (sortedSegments.video.bytes === 0) {
18064                                this.videoCodec_ = null;
18065                            }
18066                            if (sortedSegments.audio.bytes === 0) {
18067                                this.audioCodec_ = null;
18068                            }
18069
18070                            this.createRealSourceBuffers_();
18071                        }
18072
18073                        if (sortedSegments.audio.info) {
18074                            this.mediaSource_.trigger({ type: 'audioinfo', info: sortedSegments.audio.info });
18075                        }
18076                        if (sortedSegments.video.info) {
18077                            this.mediaSource_.trigger({ type: 'videoinfo', info: sortedSegments.video.info });
18078                        }
18079
18080                        if (this.appendAudioInitSegment_) {
18081                            if (!this.audioDisabled_ && this.audioBuffer_) {
18082                                sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
18083                                sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
18084                            }
18085                            this.appendAudioInitSegment_ = false;
18086                        }
18087
18088                        // Merge multiple video and audio segments into one and append
18089                        if (this.videoBuffer_) {
18090                            sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
18091                            sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
18092                            this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
18093                            // TODO: are video tracks the only ones with text tracks?
18094                            (0, _addTextTrackData.addTextTrackData)(this, sortedSegments.captions, sortedSegments.metadata);
18095                        }
18096
18097                        if (!this.audioDisabled_ && this.audioBuffer_) {
18098                            this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
18099                        }
18100
18101                        this.pendingBuffers_.length = 0;
18102
18103                        // We are no longer in the internal "updating" state
18104                        this.bufferUpdating_ = false;
18105                    }
18106
18107                    /**
18108                     * Combine all segments into a single Uint8Array and then append them
18109                     * to the destination buffer
18110                     *
18111                     * @param {Object} segmentObj
18112                     * @param {SourceBuffer} destinationBuffer native source buffer to append data to
18113                     * @private
18114                     */
18115                }, {
18116                    key: 'concatAndAppendSegments_',
18117                    value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
18118                        var offset = 0;
18119                        var tempBuffer = undefined;
18120
18121                        if (segmentObj.bytes) {
18122                            tempBuffer = new Uint8Array(segmentObj.bytes);
18123
18124                            // Combine the individual segments into one large typed-array
18125                            segmentObj.segments.forEach(function (segment) {
18126                                tempBuffer.set(segment, offset);
18127                                offset += segment.byteLength;
18128                            });
18129
18130                            try {
18131                                destinationBuffer.updating = true;
18132                                destinationBuffer.appendBuffer(tempBuffer);
18133                            } catch (error) {
18134                                if (this.mediaSource_.player_) {
18135                                    this.mediaSource_.player_.error({
18136                                        code: -3,
18137                                        type: 'APPEND_BUFFER_ERR',
18138                                        message: error.message,
18139                                        originalError: error
18140                                    });
18141                                }
18142                            }
18143                        }
18144                    }
18145
18146                    /**
18147                     * Emulate the native mediasource function. abort any soureBuffer
18148                     * actions and throw out any un-appended data.
18149                     *
18150                     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
18151                     */
18152                }, {
18153                    key: 'abort',
18154                    value: function abort() {
18155                        if (this.videoBuffer_) {
18156                            this.videoBuffer_.abort();
18157                        }
18158                        if (!this.audioDisabled_ && this.audioBuffer_) {
18159                            this.audioBuffer_.abort();
18160                        }
18161                        if (this.transmuxer_) {
18162                            this.transmuxer_.postMessage({ action: 'reset' });
18163                        }
18164                        this.pendingBuffers_.length = 0;
18165                        this.bufferUpdating_ = false;
18166                    }
18167                }]);
18168
18169                return VirtualSourceBuffer;
18170            })(_videoJs2['default'].EventTarget);
18171
18172            exports['default'] = VirtualSourceBuffer;
18173            module.exports = exports['default'];
18174        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
18175    },{"./add-text-track-data":62,"./codec-utils":64,"./create-text-tracks-if-necessary":65,"./remove-cues-from-track":71,"./transmuxer-worker":72,"webworkify":75}],75:[function(require,module,exports){
18176        var bundleFn = arguments[3];
18177        var sources = arguments[4];
18178        var cache = arguments[5];
18179
18180        var stringify = JSON.stringify;
18181
18182        module.exports = function (fn) {
18183            var keys = [];
18184            var wkey;
18185            var cacheKeys = Object.keys(cache);
18186
18187            for (var i = 0, l = cacheKeys.length; i < l; i++) {
18188                var key = cacheKeys[i];
18189                if (cache[key].exports === fn) {
18190                    wkey = key;
18191                    break;
18192                }
18193            }
18194
18195            if (!wkey) {
18196                wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
18197                var wcache = {};
18198                for (var i = 0, l = cacheKeys.length; i < l; i++) {
18199                    var key = cacheKeys[i];
18200                    wcache[key] = key;
18201                }
18202                sources[wkey] = [
18203                    Function(['require','module','exports'], '(' + fn + ')(self)'),
18204                    wcache
18205                ];
18206            }
18207            var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
18208
18209            var scache = {}; scache[wkey] = wkey;
18210            sources[skey] = [
18211                Function(['require'],'require(' + stringify(wkey) + ')(self)'),
18212                scache
18213            ];
18214
18215            var src = '(' + bundleFn + ')({'
18216                + Object.keys(sources).map(function (key) {
18217                    return stringify(key) + ':['
18218                        + sources[key][0]
18219                        + ',' + stringify(sources[key][1]) + ']'
18220                        ;
18221                }).join(',')
18222                + '},{},[' + stringify(skey) + '])'
18223            ;
18224
18225            var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
18226
18227            return new Worker(URL.createObjectURL(
18228                new Blob([src], { type: 'text/javascript' })
18229            ));
18230        };
18231
18232    },{}],76:[function(require,module,exports){
18233        (function (global){
18234            /**
18235             * @file videojs-contrib-hls.js
18236             *
18237             * The main file for the HLS project.
18238             * License: https://github.com/videojs/videojs-contrib-hls/blob/master/LICENSE
18239             */
18240            'use strict';
18241
18242            var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
18243
18244            var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
18245
18246            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
18247
18248            function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
18249
18250            function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
18250onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
18251
18252            var _globalDocument = require('global/document');
18253
18254            var _globalDocument2 = _interopRequireDefault(_globalDocument);
18255
18256            var _playlistLoader = require('./playlist-loader');
18257
18258            var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
18259
18260            var _playlist = require('./playlist');
18261
18262            var _playlist2 = _interopRequireDefault(_playlist);
18263
18264            var _xhr = require('./xhr');
18265
18266            var _xhr2 = _interopRequireDefault(_xhr);
18267
18268            var _aesDecrypter = require('aes-decrypter');
18269
18270            var _binUtils = require('./bin-utils');
18271
18272            var _binUtils2 = _interopRequireDefault(_binUtils);
18273
18274            var _videojsContribMediaSources = require('videojs-contrib-media-sources');
18275
18276            var _m3u8Parser = require('m3u8-parser');
18277
18278            var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
18279
18280            var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
18281
18282            var _videoJs2 = _interopRequireDefault(_videoJs);
18283
18284            var _masterPlaylistController = require('./master-playlist-controller');
18285
18286            var _config = require('./config');
18287
18288            var _config2 = _interopRequireDefault(_config);
18289
18290            var _renditionMixin = require('./rendition-mixin');
18291
18292            var _renditionMixin2 = _interopRequireDefault(_renditionMixin);
18293
18294            var _globalWindow = require('global/window');
18295
18296            var _globalWindow2 = _interopRequireDefault(_globalWindow);
18297
18298            var _playbackWatcher = require('./playback-watcher');
18299
18300            var _playbackWatcher2 = _interopRequireDefault(_playbackWatcher);
18301
18302            var _reloadSourceOnError = require('./reload-source-on-error');
18303
18304            var _reloadSourceOnError2 = _interopRequireDefault(_reloadSourceOnError);
18305
18306            var Hls = {
18307                PlaylistLoader: _playlistLoader2['default'],
18308                Playlist: _playlist2['default'],
18309                Decrypter: _aesDecrypter.Decrypter,
18310                AsyncStream: _aesDecrypter.AsyncStream,
18311                decrypt: _aesDecrypter.decrypt,
18312                utils: _binUtils2['default'],
18313                xhr: (0, _xhr2['default'])()
18314            };
18315
18316            Object.defineProperty(Hls, 'GOAL_BUFFER_LENGTH', {
18317                get: function get() {
18318                    _videoJs2['default'].log.warn('using Hls.GOAL_BUFFER_LENGTH is UNSAFE be sure ' + 'you know what you are doing');
18319                    return _config2['default'].GOAL_BUFFER_LENGTH;
18320                },
18321                set: function set(v) {
18322                    _videoJs2['default'].log.warn('using Hls.GOAL_BUFFER_LENGTH is UNSAFE be sure ' + 'you know what you are doing');
18323                    if (typeof v !== 'number' || v <= 0) {
18324                        _videoJs2['default'].log.warn('value passed to Hls.GOAL_BUFFER_LENGTH ' + 'must be a number and greater than 0');
18325                        return;
18326                    }
18327                    _config2['default'].GOAL_BUFFER_LENGTH = v;
18328                }
18329            });
18330
18331            Object.defineProperty(Hls, 'BANDWIDTH_VARIANCE', {
18332                get: function get() {
18333                    _videoJs2['default'].log.warn('using Hls.BANDWIDTH_VARIANCE is UNSAFE be sure ' + 'you know what you are doing');
18334                    return _config2['default'].BANDWIDTH_VARIANCE;
18335                },
18336                set: function set(v) {
18337                    _videoJs2['default'].log.warn('using Hls.BANDWIDTH_VARIANCE is UNSAFE be sure ' + 'you know what you are doing');
18338                    if (typeof v !== 'number' || v <= 0) {
18339                        _videoJs2['default'].log.warn('value passed to Hls.BANDWIDTH_VARIANCE ' + 'must be a number and greater than 0');
18340                        return;
18341                    }
18342                    _config2['default'].BANDWIDTH_VARIANCE = v;
18343                }
18344            });
18345
18346            /**
18347             * Returns the CSS value for the specified property on an element
18348             * using `getComputedStyle`. Firefox has a long-standing issue where
18349             * getComputedStyle() may return null when running in an iframe with
18350             * `display: none`.
18351             *
18352             * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
18353             * @param {HTMLElement} el the htmlelement to work on
18354             * @param {string} the proprety to get the style for
18355             */
18356            var safeGetComputedStyle = function safeGetComputedStyle(el, property) {
18357                var result = undefined;
18358
18359                if (!el) {
18360                    return '';
18361                }
18362
18363                result = _globalWindow2['default'].getComputedStyle(el);
18364                if (!result) {
18365                    return '';
18366                }
18367
18368                return result[property];
18369            };
18370
18371            /**
18372             * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
18373             *
18374             * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
18375             * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
18376             * @function handleHlsMediaChange
18377             */
18378            var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
18379                var newPlaylist = playlistLoader.media();
18380                var selectedIndex = -1;
18381
18382                for (var i = 0; i < qualityLevels.length; i++) {
18383                    if (qualityLevels[i].id === newPlaylist.uri) {
18384                        selectedIndex = i;
18385                        break;
18386                    }
18387                }
18388
18389                qualityLevels.selectedIndex_ = selectedIndex;
18390                qualityLevels.trigger({
18391                    selectedIndex: selectedIndex,
18392                    type: 'change'
18393                });
18394            };
18395
18396            /**
18397             * Adds quality levels to list once playlist metadata is available
18398             *
18399             * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
18400             * @param {Object} hls Hls object to listen to for media events.
18401             * @function handleHlsLoadedMetadata
18402             */
18403            var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
18404                hls.representations().forEach(function (rep) {
18405                    qualityLevels.addQualityLevel(rep);
18406                });
18407                handleHlsMediaChange(qualityLevels, hls.playlists);
18408            };
18409
18410            /**
18411             * Resuable stable sort function
18412             *
18413             * @param {Playlists} array
18414             * @param {Function} sortFn Different comparators
18415             * @function stableSort
18416             */
18417            var stableSort = function stableSort(array, sortFn) {
18418                var newArray = array.slice();
18419
18420                array.sort(function (left, right) {
18421                    var cmp = sortFn(left, right);
18422
18423                    if (cmp === 0) {
18424                        return newArray.indexOf(left) - newArray.indexOf(right);
18425                    }
18426                    return cmp;
18427                });
18428            };
18429
18430            /**
18431             * Chooses the appropriate media playlist based on the current
18432             * bandwidth estimate and the player size.
18433             *
18434             * @return {Playlist} the highest bitrate playlist less than the currently detected
18435             * bandwidth, accounting for some amount of bandwidth variance
18436             */
18437            Hls.STANDARD_PLAYLIST_SELECTOR = function () {
18438                var sortedPlaylists = this.playlists.master.playlists.slice();
18439                var bandwidthPlaylists = [];
18440                var bandwidthBestVariant = undefined;
18441                var resolutionPlusOne = undefined;
18442                var resolutionBestVariant = undefined;
18443                var width = undefined;
18444                var height = undefined;
18445                var systemBandwidth = undefined;
18446                var haveResolution = undefined;
18447                var resolutionPlusOneList = [];
18448                var resolutionPlusOneSmallest = [];
18449                var resolutionBestVariantList = [];
18450
18451                stableSort(sortedPlaylists, Hls.comparePlaylistBandwidth);
18452
18453                // filter out any playlists that have been excluded due to
18454                // incompatible configurations or playback errors
18455                sortedPlaylists = sortedPlaylists.filter(_playlist2['default'].isEnabled);
18456                // filter out any variant that has greater effective bitrate
18457                // than the current estimated bandwidth
18458                systemBandwidth = this.systemBandwidth;
18459                bandwidthPlaylists = sortedPlaylists.filter(function (elem) {
18460                    return elem.attributes && elem.attributes.BANDWIDTH && elem.attributes.BANDWIDTH * _config2['default'].BANDWIDTH_VARIANCE < systemBandwidth;
18461                });
18462
18463                // get all of the renditions with the same (highest) bandwidth
18464                // and then taking the very first element
18465                bandwidthBestVariant = bandwidthPlaylists.filter(function (elem) {
18466                    return elem.attributes.BANDWIDTH === bandwidthPlaylists[bandwidthPlaylists.length - 1].attributes.BANDWIDTH;
18467                })[0];
18468
18469                // sort variants by resolution
18470                stableSort(bandwidthPlaylists, Hls.comparePlaylistResolution);
18471
18472                width = parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10);
18473                height = parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10);
18474
18475                // filter out playlists without resolution information
18476                haveResolution = bandwidthPlaylists.filter(function (elem) {
18477                    return elem.attributes && elem.attributes.RESOLUTION && elem.attributes.RESOLUTION.width && elem.attributes.RESOLUTION.height;
18478                });
18479
18480                // if we have the exact resolution as the player use it
18481                resolutionBestVariantList = haveResolution.filter(function (elem) {
18482                    return elem.attributes.RESOLUTION.width === width && elem.attributes.RESOLUTION.height === height;
18483                });
18484                // ensure that we pick the highest bandwidth variant that have exact resolution
18485                resolutionBestVariant = resolutionBestVariantList.filter(function (elem) {
18486                    return elem.attributes.BANDWIDTH === resolutionBestVariantList[resolutionBestVariantList.length - 1].attributes.BANDWIDTH;
18487                })[0];
18488
18489                // find the smallest variant that is larger than the player
18490                // if there is no match of exact resolution
18491                if (!resolutionBestVariant) {
18492                    resolutionPlusOneList = haveResolution.filter(function (elem) {
18493                        return elem.attributes.RESOLUTION.width > width || elem.attributes.RESOLUTION.height > height;
18494                    });
18495                    // find all the variants have the same smallest resolution
18496                    resolutionPlusOneSmallest = resolutionPlusOneList.filter(function (elem) {
18497                        return elem.attributes.RESOLUTION.width === resolutionPlusOneList[0].attributes.RESOLUTION.width && elem.attributes.RESOLUTION.height === resolutionPlusOneList[0].attributes.RESOLUTION.height;
18498                    });
18499                    // ensure that we also pick the highest bandwidth variant that
18500                    // is just-larger-than the video player
18501                    resolutionPlusOne = resolutionPlusOneSmallest.filter(function (elem) {
18502                        return elem.attributes.BANDWIDTH === resolutionPlusOneSmallest[resolutionPlusOneSmallest.length - 1].attributes.BANDWIDTH;
18503                    })[0];
18504                }
18505
18506                // fallback chain of variants
18507                return resolutionPlusOne || resolutionBestVariant || bandwidthBestVariant || sortedPlaylists[0];
18508            };
18509
18510// HLS is a source handler, not a tech. Make sure attempts to use it
18511// as one do not cause exceptions.
18512            Hls.canPlaySource = function () {
18513                return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
18514            };
18515
18516            /**
18517             * Whether the browser has built-in HLS support.
18518             */
18519            Hls.supportsNativeHls = (function () {
18520                var video = _globalDocument2['default'].createElement('video');
18521
18522                // native HLS is definitely not supported if HTML5 video isn't
18523                if (!_videoJs2['default'].getTech('Html5').isSupported()) {
18524                    return false;
18525                }
18526
18527                // HLS manifests can go by many mime-types
18528                var canPlay = [
18529                    // Apple santioned
18530                    'application/vnd.apple.mpegurl',
18531                    // Apple sanctioned for backwards compatibility
18532                    'audio/mpegurl',
18533                    // Very common
18534                    'audio/x-mpegurl',
18535                    // Very common
18536                    'application/x-mpegurl',
18537                    // Included for completeness
18538                    'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
18539
18540                return canPlay.some(function (canItPlay) {
18541                    return (/maybe|probably/i.test(video.canPlayType(canItPlay))
18542                    );
18543                });
18544            })();
18545
18546            /**
18547             * HLS is a source handler, not a tech. Make sure attempts to use it
18548             * as one do not cause exceptions.
18549             */
18550            Hls.isSupported = function () {
18551                return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
18552            };
18553
18554            var USER_AGENT = _globalWindow2['default'].navigator && _globalWindow2['default'].navigator.userAgent || '';
18555
18556            /**
18557             * Determines whether the browser supports a change in the audio configuration
18558             * during playback. Currently only Firefox 48 and below do not support this.
18559             * window.isSecureContext is a propterty that was added to window in firefox 49,
18560             * so we can use it to detect Firefox 49+.
18561             *
18562             * @return {Boolean} Whether the browser supports audio config change during playback
18563             */
18564            Hls.supportsAudioInfoChange_ = function () {
18565                if (_videoJs2['default'].browser.IS_FIREFOX) {
18566                    var firefoxVersionMap = /Firefox\/([\d.]+)/i.exec(USER_AGENT);
18567                    var version = parseInt(firefoxVersionMap[1], 10);
18568
18569                    return version >= 49;
18570                }
18571                return true;
18572            };
18573
18574            var Component = _videoJs2['default'].getComponent('Component');
18575
18576            /**
18577             * The Hls Handler object, where we orchestrate all of the parts
18578             * of HLS to interact with video.js
18579             *
18580             * @class HlsHandler
18581             * @extends videojs.Component
18582             * @param {Object} source the soruce object
18583             * @param {Tech} tech the parent tech object
18584             * @param {Object} options optional and required options
18585             */
18586
18587            var HlsHandler = (function (_Component) {
18588                _inherits(HlsHandler, _Component);
18589
18590                function HlsHandler(source, tech, options) {
18591                    var _this = this;
18592
18593                    _classCallCheck(this, HlsHandler);
18594
18595                    _get(Object.getPrototypeOf(HlsHandler.prototype), 'constructor', this).call(this, tech);
18596
18597                    // tech.player() is deprecated but setup a reference to HLS for
18598                    // backwards-compatibility
18599                    if (tech.options_ && tech.options_.playerId) {
18600                        var _player = (0, _videoJs2['default'])(tech.options_.playerId);
18601
18602                        if (!_player.hasOwnProperty('hls')) {
18603                            Object.defineProperty(_player, 'hls', {
18604                                get: function get() {
18605                                    _videoJs2['default'].log.warn('player.hls is deprecated. Use player.tech_.hls instead.');
18606                                    return _this;
18607                                }
18608                            });
18609                        }
18610                    }
18611
18612                    // overriding native HLS only works if audio tracks have been emulated
18613                    // error early if we're misconfigured:
18614                    if (_videoJs2['default'].options.hls.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
18615                        throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
18616                    }
18617
18618                    this.tech_ = tech;
18619                    this.source_ = source;
18620                    this.stats = {};
18621                    this.ignoreNextSeekingEvent_ = false;
18622
18623                    // handle global & Source Handler level options
18624                    this.options_ = _videoJs2['default'].mergeOptions(_videoJs2['default'].options.hls || {}, options.hls);
18625                    this.setOptions_();
18626
18627                    // listen for fullscreenchange events for this player so that we
18628                    // can adjust our quality selection quickly
18629                    this.on(_globalDocument2['default'], ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
18630                        var fullscreenElement = _globalDocument2['default'].fullscreenElement || _globalDocument2['default'].webkitFullscreenElement || _globalDocument2['default'].mozFullScreenElement || _globalDocument2['default'].msFullscreenElement;
18631
18632                        if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) {
18633                            _this.masterPlaylistController_.fastQualityChange_();
18634                        }
18635                    });
18636
18637                    this.on(this.tech_, 'seeking', function () {
18638                        if (this.ignoreNextSeekingEvent_) {
18639                            this.ignoreNextSeekingEvent_ = false;
18640                            return;
18641                        }
18642
18643                        this.setCurrentTime(this.tech_.currentTime());
18644                    });
18645                    this.on(this.tech_, 'error', function () {
18646                        if (this.masterPlaylistController_) {
18647                            this.masterPlaylistController_.pauseLoading();
18648                        }
18649                    });
18650
18651                    this.audioTrackChange_ = function () {
18652                        _this.masterPlaylistController_.setupAudio();
18653                    };
18654
18655                    this.textTrackChange_ = function () {
18656                        _this.masterPlaylistController_.setupSubtitles();
18657                    };
18658
18659                    this.on(this.tech_, 'play', this.play);
18660                }
18661
18662                /**
18663                 * The Source Handler object, which informs video.js what additional
18664                 * MIME types are supported and sets up playback. It is registered
18665                 * automatically to the appropriate tech based on the capabilities of
18666                 * the browser it is running in. It is not necessary to use or modify
18667                 * this object in normal usage.
18668                 */
18669
18670                _createClass(HlsHandler, [{
18671                    key: 'setOptions_',
18672                    value: function setOptions_() {
18673                        var _this2 = this;
18674
18675                        // defaults
18676                        this.options_.withCredentials = this.options_.withCredentials || false;
18677
18678                        if (typeof this.options_.blacklistDuration !== 'number') {
18679                            this.options_.blacklistDuration = 5 * 60;
18680                        }
18681
18682                        // start playlist selection at a reasonable bandwidth for
18683                        // broadband internet
18684                        // 0.5 MB/s
18685                        if (typeof this.options_.bandwidth !== 'number') {
18686                            this.options_.bandwidth = 4194304;
18687                        }
18688
18689                        // grab options passed to player.src
18690                        ['withCredentials', 'bandwidth'].forEach(function (option) {
18691                            if (typeof _this2.source_[option] !== 'undefined') {
18692                                _this2.options_[option] = _this2.source_[option];
18693                            }
18694                        });
18695
18696                        this.bandwidth = this.options_.bandwidth;
18697                    }
18698
18699                    /**
18700                     * called when player.src gets called, handle a new source
18701                     *
18702                     * @param {Object} src the source object to handle
18703                     */
18704                }, {
18705                    key: 'src',
18706                    value: function src(_src) {
18707                        var _this3 = this;
18708
18709                        // do nothing if the src is falsey
18710                        if (!_src) {
18711                            return;
18712                        }
18713                        this.setOptions_();
18714                        // add master playlist controller options
18715                        this.options_.url = this.source_.src;
18716                        this.options_.tech = this.tech_;
18717                        this.options_.externHls = Hls;
18718
18719                        this.masterPlaylistController_ = new _masterPlaylistController.MasterPlaylistController(this.options_);
18720                        this.playbackWatcher_ = new _playbackWatcher2['default'](_videoJs2['default'].mergeOptions(this.options_, {
18721                            seekable: function seekable() {
18722                                return _this3.seekable();
18723                            }
18724                        }));
18725
18726                        this.masterPlaylistController_.on('error', function () {
18727                            var player = _videoJs2['default'].players[_this3.tech_.options_.playerId];
18728
18729                            player.error(_this3.masterPlaylistController_.error);
18730                        });
18731
18732                        // `this` in selectPlaylist should be the HlsHandler for backwards
18733                        // compatibility with < v2
18734                        this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls.STANDARD_PLAYLIST_SELECTOR.bind(this);
18735
18736                        // re-expose some internal objects for backwards compatibility with < v2
18737                        this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
18738                        this.mediaSource = this.masterPlaylistController_.mediaSource;
18739
18740                        // Proxy assignment of some properties to the master playlist
18741                        // controller. Using a custom property for backwards compatibility
18742                        // with < v2
18743                        Object.defineProperties(this, {
18744                            selectPlaylist: {
18745                                get: function get() {
18746                                    return this.masterPlaylistController_.selectPlaylist;
18747                                },
18748                                set: function set(selectPlaylist) {
18749                                    this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
18750                                }
18751                            },
18752                            throughput: {
18753                                get: function get() {
18754                                    return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
18755                                },
18756                                set: function set(throughput) {
18757                                    this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput;
18758                                    // By setting `count` to 1 the throughput value becomes the starting value
18759                                    // for the cumulative average
18760                                    this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
18761                                }
18762                            },
18763                            bandwidth: {
18764                                get: function get() {
18765                                    return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
18766                                },
18767                                set: function set(bandwidth) {
18768                                    this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth;
18769                                    // setting the bandwidth manually resets the throughput counter
18770                                    // `count` is set to zero that current value of `rate` isn't included
18771                                    // in the cumulative average
18772                                    this.masterPlaylistController_.mainSegmentLoader_.throughput = { rate: 0, count: 0 };
18773                                }
18774                            },
18775                            /**
18776                             * `systemBandwidth` is a combination of two serial processes bit-rates. The first
18777                             * is the network bitrate provided by `bandwidth` and the second is the bitrate of
18778                             * the entire process after that - decryption, transmuxing, and appending - provided
18779                             * by `throughput`.
18780                             *
18781                             * Since the two process are serial, the overall system bandwidth is given by:
18782                             *   sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
18783                             */
18784                            systemBandwidth: {
18785                                get: function get() {
18786                                    var invBandwidth = 1 / (this.bandwidth || 1);
18787                                    var invThroughput = undefined;
18788
18789                                    if (this.throughput > 0) {
18790                                        invThroughput = 1 / this.throughput;
18791                                    } else {
18792                                        invThroughput = 0;
18793                                    }
18794
18795                                    var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
18796
18797                                    return systemBitrate;
18798                                },
18799                                set: function set() {
18800                                    _videoJs2['default'].log.error('The "systemBandwidth" property is read-only');
18801                                }
18802                            }
18803                        });
18804
18805                        Object.defineProperties(this.stats, {
18806                            bandwidth: {
18807                                get: function get() {
18808                                    return _this3.bandwidth || 0;
18809                                },
18810                                enumerable: true
18811                            },
18812                            mediaRequests: {
18813                                get: function get() {
18814                                    return _this3.masterPlaylistController_.mediaRequests_() || 0;
18815                                },
18816                                enumerable: true
18817                            },
18818                            mediaRequestsAborted: {
18819                                get: function get() {
18820                                    return _this3.masterPlaylistController_.mediaRequestsAborted_() || 0;
18821                                },
18822                                enumerable: true
18823                            },
18824                            mediaRequestsTimedout: {
18825                                get: function get() {
18826                                    return _this3.masterPlaylistController_.mediaRequestsTimedout_() || 0;
18827                                },
18828                                enumerable: true
18829                            },
18830                            mediaRequestsErrored: {
18831                                get: function get() {
18832                                    return _this3.masterPlaylistController_.mediaRequestsErrored_() || 0;
18833                                },
18834                                enumerable: true
18835                            },
18836                            mediaTransferDuration: {
18837                                get: function get() {
18838                                    return _this3.masterPlaylistController_.mediaTransferDuration_() || 0;
18839                                },
18840                                enumerable: true
18841                            },
18842                            mediaBytesTransferred: {
18843                                get: function get() {
18844                                    return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0;
18845                                },
18846                                enumerable: true
18847                            },
18848                            mediaSecondsLoaded: {
18849                                get: function get() {
18850                                    return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0;
18851                                },
18852                                enumerable: true
18853                            }
18854                        });
18855
18856                        this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
18857
18858                        this.masterPlaylistController_.on('sourceopen', function () {
18859                            _this3.tech_.audioTracks().addEventListener('change', _this3.audioTrackChange_);
18860                            _this3.tech_.remoteTextTracks().addEventListener('change', _this3.textTrackChange_);
18861                        });
18862
18863                        this.masterPlaylistController_.on('selectedinitialmedia', function () {
18864                            // Add the manual rendition mix-in to HlsHandler
18865                            (0, _renditionMixin2['default'])(_this3);
18866                        });
18867
18868                        this.masterPlaylistController_.on('audioupdate', function () {
18869                            // clear current audioTracks
18870                            _this3.tech_.clearTracks('audio');
18871                            _this3.masterPlaylistController_.activeAudioGroup().forEac
18871h(function (audioTrack) {
18872                                _this3.tech_.audioTracks().addTrack(audioTrack);
18873                            });
18874                        });
18875
18876                        // the bandwidth of the primary segment loader is our best
18877                        // estimate of overall bandwidth
18878                        this.on(this.masterPlaylistController_, 'progress', function () {
18879                            this.tech_.trigger('progress');
18880                        });
18881
18882                        // In the live case, we need to ignore the very first `seeking` event since
18883                        // that will be the result of the seek-to-live behavior
18884                        this.on(this.masterPlaylistController_, 'firstplay', function () {
18885                            this.ignoreNextSeekingEvent_ = true;
18886                        });
18887
18888                        this.tech_.ready(function () {
18889                            return _this3.setupQualityLevels_();
18890                        });
18891
18892                        // do nothing if the tech has been disposed already
18893                        // this can occur if someone sets the src in player.ready(), for instance
18894                        if (!this.tech_.el()) {
18895                            return;
18896                        }
18897
18898                        this.tech_.src(_videoJs2['default'].URL.createObjectURL(this.masterPlaylistController_.mediaSource));
18899                    }
18900
18901                    /**
18902                     * Initializes the quality levels and sets listeners to update them.
18903                     *
18904                     * @method setupQualityLevels_
18905                     * @private
18906                     */
18907                }, {
18908                    key: 'setupQualityLevels_',
18909                    value: function setupQualityLevels_() {
18910                        var _this4 = this;
18911
18912                        var player = _videoJs2['default'].players[this.tech_.options_.playerId];
18913
18914                        if (player && player.qualityLevels) {
18915                            this.qualityLevels_ = player.qualityLevels();
18916
18917                            this.masterPlaylistController_.on('selectedinitialmedia', function () {
18918                                handleHlsLoadedMetadata(_this4.qualityLevels_, _this4);
18919                            });
18920
18921                            this.playlists.on('mediachange', function () {
18922                                handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists);
18923                            });
18924                        }
18925                    }
18926
18927                    /**
18928                     * a helper for grabbing the active audio group from MasterPlaylistController
18929                     *
18930                     * @private
18931                     */
18932                }, {
18933                    key: 'activeAudioGroup_',
18934                    value: function activeAudioGroup_() {
18935                        return this.masterPlaylistController_.activeAudioGroup();
18936                    }
18937
18938                    /**
18939                     * Begin playing the video.
18940                     */
18941                }, {
18942                    key: 'play',
18943                    value: function play() {
18944                        this.masterPlaylistController_.play();
18945                    }
18946
18947                    /**
18948                     * a wrapper around the function in MasterPlaylistController
18949                     */
18950                }, {
18951                    key: 'setCurrentTime',
18952                    value: function setCurrentTime(currentTime) {
18953                        this.masterPlaylistController_.setCurrentTime(currentTime);
18954                    }
18955
18956                    /**
18957                     * a wrapper around the function in MasterPlaylistController
18958                     */
18959                }, {
18960                    key: 'duration',
18961                    value: function duration() {
18962                        return this.masterPlaylistController_.duration();
18963                    }
18964
18965                    /**
18966                     * a wrapper around the function in MasterPlaylistController
18967                     */
18968                }, {
18969                    key: 'seekable',
18970                    value: function seekable() {
18971                        return this.masterPlaylistController_.seekable();
18972                    }
18973
18974                    /**
18975                     * Abort all outstanding work and cleanup.
18976                     */
18977                }, {
18978                    key: 'dispose',
18979                    value: function dispose() {
18980                        if (this.playbackWatcher_) {
18981                            this.playbackWatcher_.dispose();
18982                        }
18983                        if (this.masterPlaylistController_) {
18984                            this.masterPlaylistController_.dispose();
18985                        }
18986                        if (this.qualityLevels_) {
18987                            this.qualityLevels_.dispose();
18988                        }
18989                        this.tech_.audioTracks().removeEventListener('change', this.audioTrackChange_);
18990                        this.tech_.remoteTextTracks().removeEventListener('change', this.textTrackChange_);
18991                        _get(Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this);
18992                    }
18993                }]);
18994
18995                return HlsHandler;
18996            })(Component);
18997
18998            var HlsSourceHandler = function HlsSourceHandler(mode) {
18999                return {
19000                    canHandleSource: function canHandleSource(srcObj) {
19001                        // this forces video.js to skip this tech/mode if its not the one we have been
19002                        // overriden to use, by returing that we cannot handle the source.
19003                        if (_videoJs2['default'].options.hls && _videoJs2['default'].options.hls.mode && _videoJs2['default'].options.hls.mode !== mode) {
19004                            return false;
19005                        }
19006                        return HlsSourceHandler.canPlayType(srcObj.type);
19007                    },
19008                    handleSource: function handleSource(source, tech, options) {
19009                        if (mode === 'flash') {
19010                            // We need to trigger this asynchronously to give others the chance
19011                            // to bind to the event when a source is set at player creation
19012                            tech.setTimeout(function () {
19013                                tech.trigger('loadstart');
19014                            }, 1);
19015                        }
19016
19017                        var settings = _videoJs2['default'].mergeOptions(options, { hls: { mode: mode } });
19018
19019                        tech.hls = new HlsHandler(source, tech, settings);
19020                        tech.hls.xhr = (0, _xhr2['default'])();
19021
19022                        tech.hls.src(source.src);
19023                        return tech.hls;
19024                    },
19025                    canPlayType: function canPlayType(type) {
19026                        if (HlsSourceHandler.canPlayType(type)) {
19027                            return 'maybe';
19028                        }
19029                        return '';
19030                    }
19031                };
19032            };
19033
19034            /**
19035             * A comparator function to sort two playlist object by bandwidth.
19036             *
19037             * @param {Object} left a media playlist object
19038             * @param {Object} right a media playlist object
19039             * @return {Number} Greater than zero if the bandwidth attribute of
19040             * left is greater than the corresponding attribute of right. Less
19041             * than zero if the bandwidth of right is greater than left and
19042             * exactly zero if the two are equal.
19043             */
19044            Hls.comparePlaylistBandwidth = function (left, right) {
19045                var leftBandwidth = undefined;
19046                var rightBandwidth = undefined;
19047
19048                if (left.attributes && left.attributes.BANDWIDTH) {
19049                    leftBandwidth = left.attributes.BANDWIDTH;
19050                }
19051                leftBandwidth = leftBandwidth || _globalWindow2['default'].Number.MAX_VALUE;
19052                if (right.attributes && right.attributes.BANDWIDTH) {
19053                    rightBandwidth = right.attributes.BANDWIDTH;
19054                }
19055                rightBandwidth = rightBandwidth || _globalWindow2['default'].Number.MAX_VALUE;
19056
19057                return leftBandwidth - rightBandwidth;
19058            };
19059
19060            /**
19061             * A comparator function to sort two playlist object by resolution (width).
19062             * @param {Object} left a media playlist object
19063             * @param {Object} right a media playlist object
19064             * @return {Number} Greater than zero if the resolution.width attribute of
19065             * left is greater than the corresponding attribute of right. Less
19066             * than zero if the resolution.width of right is greater than left and
19067             * exactly zero if the two are equal.
19068             */
19069            Hls.comparePlaylistResolution = function (left, right) {
19070                var leftWidth = undefined;
19071                var rightWidth = undefined;
19072
19073                if (left.attributes && left.attributes.RESOLUTION && left.attributes.RESOLUTION.width) {
19074                    leftWidth = left.attributes.RESOLUTION.width;
19075                }
19076
19077                leftWidth = leftWidth || _globalWindow2['default'].Number.MAX_VALUE;
19078
19079                if (right.attributes && right.attributes.RESOLUTION && right.attributes.RESOLUTION.width) {
19080                    rightWidth = right.attributes.RESOLUTION.width;
19081                }
19082
19083                rightWidth = rightWidth || _globalWindow2['default'].Number.MAX_VALUE;
19084
19085                // NOTE - Fallback to bandwidth sort as appropriate in cases where multiple renditions
19086                // have the same media dimensions/ resolution
19087                if (leftWidth === rightWidth && left.attributes.BANDWIDTH && right.attributes.BANDWIDTH) {
19088                    return left.attributes.BANDWIDTH - right.attributes.BANDWIDTH;
19089                }
19090                return leftWidth - rightWidth;
19091            };
19092
19093            HlsSourceHandler.canPlayType = function (type) {
19094                // No support for IE 10 or below
19095                if (_videoJs2['default'].browser.IE_VERSION && _videoJs2['default'].browser.IE_VERSION <= 10) {
19096                    return false;
19097                }
19098
19099                var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
19100
19101                // favor native HLS support if it's available
19102                if (!_videoJs2['default'].options.hls.overrideNative && Hls.supportsNativeHls) {
19103                    return false;
19104                }
19105                return mpegurlRE.test(type);
19106            };
19107
19108            if (typeof _videoJs2['default'].MediaSource === 'undefined' || typeof _videoJs2['default'].URL === 'undefined') {
19109                _videoJs2['default'].MediaSource = _videojsContribMediaSources.MediaSource;
19110                _videoJs2['default'].URL = _videojsContribMediaSources.URL;
19111            }
19112
19113            var flashTech = _videoJs2['default'].getTech('Flash');
19114
19115// register source handlers with the appropriate techs
19116            if (_videojsContribMediaSources.MediaSource.supportsNativeMediaSources()) {
19117                _videoJs2['default'].getTech('Html5').registerSourceHandler(HlsSourceHandler('html5'), 0);
19118            }
19119            if (_globalWindow2['default'].Uint8Array && flashTech) {
19120                flashTech.registerSourceHandler(HlsSourceHandler('flash'));
19121            }
19122
19123            _videoJs2['default'].HlsHandler = HlsHandler;
19124            _videoJs2['default'].HlsSourceHandler = HlsSourceHandler;
19125            _videoJs2['default'].Hls = Hls;
19126            if (!_videoJs2['default'].use) {
19127                _videoJs2['default'].registerComponent('Hls', Hls);
19128            }
19129            _videoJs2['default'].m3u8 = _m3u8Parser2['default'];
19130            _videoJs2['default'].options.hls = _videoJs2['default'].options.hls || {};
19131
19132            if (_videoJs2['default'].registerPlugin) {
19133                _videoJs2['default'].registerPlugin('reloadSourceOnError', _reloadSourceOnError2['default']);
19134            } else {
19135                _videoJs2['default'].plugin('reloadSourceOnError', _reloadSourceOnError2['default']);
19136            }
19137
19138            module.exports = {
19139                Hls: Hls,
19140                HlsHandler: HlsHandler,
19141                HlsSourceHandler: HlsSourceHandler
19142            };
19143        }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
19144    },{"./bin-utils":2,"./config":3,"./master-playlist-controller":5,"./playback-watcher":7,"./playlist":9,"./playlist-loader":8,"./reload-source-on-error":11,"./rendition-mixin":12,"./xhr":19,"aes-decrypter":23,"global/document":29,"global/window":30,"m3u8-parser":31,"videojs-contrib-media-sources":73}]},{},[76])(76)
19145});

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.