1 (function webpackUniversalModuleDefinition(root, factory) {
2 if(typeof exports === 'object' && typeof module === 'object')
3 module.exports = factory();
4 else if(typeof define === 'function' && define.amd)
6 else if(typeof exports === 'object')
7 exports["Hls"] = factory();
9 root["Hls"] = factory();
10 })(typeof self !== 'undefined' ? self : this, function() {
11 return /******/ (function(modules) { // webpackBootstrap
12 /******/ // The module cache
13 /******/ var installedModules = {};
15 /******/ // The require function
16 /******/ function __webpack_require__(moduleId) {
18 /******/ // Check if module is in cache
19 /******/ if(installedModules[moduleId]) {
20 /******/ return installedModules[moduleId].exports;
22 /******/ // Create a new module (and put it into the cache)
23 /******/ var module = installedModules[moduleId] = {
29 /******/ // Execute the module function
30 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
32 /******/ // Flag the module as loaded
33 /******/ module.l = true;
35 /******/ // Return the exports of the module
36 /******/ return module.exports;
40 /******/ // expose the modules object (__webpack_modules__)
41 /******/ __webpack_require__.m = modules;
43 /******/ // expose the module cache
44 /******/ __webpack_require__.c = installedModules;
46 /******/ // define getter function for harmony exports
47 /******/ __webpack_require__.d = function(exports, name, getter) {
48 /******/ if(!__webpack_require__.o(exports, name)) {
49 /******/ Object.defineProperty(exports, name, {
50 /******/ configurable: false,
51 /******/ enumerable: true,
57 /******/ // getDefaultExport function for compatibility with non-harmony modules
58 /******/ __webpack_require__.n = function(module) {
59 /******/ var getter = module && module.__esModule ?
60 /******/ function getDefault() { return module['default']; } :
61 /******/ function getModuleExports() { return module; };
62 /******/ __webpack_require__.d(getter, 'a', getter);
63 /******/ return getter;
66 /******/ // Object.prototype.hasOwnProperty.call
67 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
69 /******/ // __webpack_public_path__
70 /******/ __webpack_require__.p = "/dist/";
72 /******/ // Load entry module and return exports
73 /******/ return __webpack_require__(__webpack_require__.s = 9);
75 /************************************************************************/
78 /***/ (function(module, __webpack_exports__, __webpack_require__) {
81 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return enableLogs; });
82 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return logger; });
83 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
96 var exportedLogger = fakeLogger;
98 /* globals self: false */
101 // function formatMsgWithTimeInfo(type, msg) {
102 // const now = Date.now();
103 // const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';
104 // lastCallTime = now;
105 // msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )';
109 function formatMsg(type, msg) {
110 msg = '[' + type + '] > ' + msg;
114 function consolePrintFn(type) {
115 var func = self.console[type];
118 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
119 args[_key] = arguments[_key];
122 if (args[0]) args[0] = formatMsg(type, args[0]);
124 func.apply(self.console, args);
130 function exportLoggerFunctions(debugConfig) {
131 for (var _len2 = arguments.length, functions = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
132 functions[_key2 - 1] = arguments[_key2];
135 functions.forEach(function (type) {
136 exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type);
140 var enableLogs = function enableLogs(debugConfig) {
141 if (debugConfig === true || (typeof debugConfig === 'undefined' ? 'undefined' : _typeof(debugConfig)) === 'object') {
142 exportLoggerFunctions(debugConfig,
143 // Remove out from list here to hard-disable a log-level
145 'debug', 'log', 'info', 'warn', 'error');
146 // Some browsers don't allow to use bind on console object anyway
147 // fallback to default if needed
149 exportedLogger.log();
151 exportedLogger = fakeLogger;
154 exportedLogger = fakeLogger;
158 var logger = exportedLogger;
162 /***/ (function(module, __webpack_exports__, __webpack_require__) {
170 // fired before MediaSource is attaching to media element - data: { media }
171 MEDIA_ATTACHING: 'hlsMediaAttaching',
172 // fired when MediaSource has been succesfully attached to media element - data: { }
173 MEDIA_ATTACHED: 'hlsMediaAttached',
174 // fired before detaching MediaSource from media element - data: { }
175 MEDIA_DETACHING: 'hlsMediaDetaching',
176 // fired when MediaSource has been detached from media element - data: { }
177 MEDIA_DETACHED: 'hlsMediaDetached',
178 // fired when we buffer is going to be reset - data: { }
179 BUFFER_RESET: 'hlsBufferReset',
180 // fired when we know about the codecs that we need buffers for to push into - data: {tracks : { container, codec, levelCodec, initSegment, metadata }}
181 BUFFER_CODECS: 'hlsBufferCodecs',
182 // fired when sourcebuffers have been created - data: { tracks : tracks }
183 BUFFER_CREATED: 'hlsBufferCreated',
184 // fired when we append a segment to the buffer - data: { segment: segment object }
185 BUFFER_APPENDING: 'hlsBufferAppending',
186 // fired when we are done with appending a media segment to the buffer - data : { parent : segment parent that triggered BUFFER_APPENDING, pending : nb of segments waiting for appending for this segment parent}
187 BUFFER_APPENDED: 'hlsBufferAppended',
188 // fired when the stream is finished and we want to notify the media buffer that there will be no more data - data: { }
189 BUFFER_EOS: 'hlsBufferEos',
190 // fired when the media buffer should be flushed - data { startOffset, endOffset }
191 BUFFER_FLUSHING: 'hlsBufferFlushing',
192 // fired when the media buffer has been flushed - data: { }
193 BUFFER_FLUSHED: 'hlsBufferFlushed',
194 // fired to signal that a manifest loading starts - data: { url : manifestURL}
195 MANIFEST_LOADING: 'hlsManifestLoading',
196 // fired after manifest has been loaded - data: { levels : [available quality levels], audioTracks : [ available audio tracks], url : manifestURL, stats : { trequest, tfirst, tload, mtime}}
197 MANIFEST_LOADED: 'hlsManifestLoaded',
198 // fired after manifest has been parsed - data: { levels : [available quality levels], firstLevel : index of first quality level appearing in Manifest}
199 MANIFEST_PARSED: 'hlsManifestParsed',
200 // fired when a level switch is requested - data: { level : id of new level }
201 LEVEL_SWITCHING: 'hlsLevelSwitching',
202 // fired when a level switch is effective - data: { level : id of new level }
203 LEVEL_SWITCHED: 'hlsLevelSwitched',
204 // fired when a level playlist loading starts - data: { url : level URL, level : id of level being loaded}
205 LEVEL_LOADING: 'hlsLevelLoading',
206 // fired when a level playlist loading finishes - data: { details : levelDetails object, level : id of loaded level, stats : { trequest, tfirst, tload, mtime} }
207 LEVEL_LOADED: 'hlsLevelLoaded',
208 // fired when a level's details have been updated based on previous details, after it has been loaded - data: { details : levelDetails object, level : id of updated level }
209 LEVEL_UPDATED: 'hlsLevelUpdated',
210 // fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }
211 LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated',
212 // fired to notify that audio track lists has been updated - data: { audioTracks : audioTracks }
213 AUDIO_TRACKS_UPDATED: 'hlsAudioTracksUpdated',
214 // fired when an audio track switching is requested - data: { id : audio track id }
215 AUDIO_TRACK_SWITCHING: 'hlsAudioTrackSwitching',
216 // fired when an audio track switch actually occurs - data: { id : audio track id }
217 AUDIO_TRACK_SWITCHED: 'hlsAudioTrackSwitched',
218 // fired when an audio track loading starts - data: { url : audio track URL, id : audio track id }
219 AUDIO_TRACK_LOADING: 'hlsAudioTrackLoading',
220 // fired when an audio track loading finishes - data: { details : levelDetails object, id : audio track id, stats : { trequest, tfirst, tload, mtime } }
221 AUDIO_TRACK_LOADED: 'hlsAudioTrackLoaded',
222 // fired to notify that subtitle track lists has been updated - data: { subtitleTracks : subtitleTracks }
223 SUBTITLE_TRACKS_UPDATED: 'hlsSubtitleTracksUpdated',
224 // fired when an subtitle track switch occurs - data: { id : subtitle track id }
225 SUBTITLE_TRACK_SWITCH: 'hlsSubtitleTrackSwitch',
226 // fired when a subtitle track loading starts - data: { url : subtitle track URL, id : subtitle track id }
227 SUBTITLE_TRACK_LOADING: 'hlsSubtitleTrackLoading',
228 // fired when a subtitle track loading finishes - data: { details : levelDetails object, id : subtitle track id, stats : { trequest, tfirst, tload, mtime } }
229 SUBTITLE_TRACK_LOADED: 'hlsSubtitleTrackLoaded',
230 // fired when a subtitle fragment has been processed - data: { success : boolean, frag : the processed frag }
231 SUBTITLE_FRAG_PROCESSED: 'hlsSubtitleFragProcessed',
232 // fired when the first timestamp is found - data: { id : demuxer id, initPTS: initPTS, frag : fragment object }
233 INIT_PTS_FOUND: 'hlsInitPtsFound',
234 // fired when a fragment loading starts - data: { frag : fragment object }
235 FRAG_LOADING: 'hlsFragLoading',
236 // fired when a fragment loading is progressing - data: { frag : fragment object, { trequest, tfirst, loaded } }
237 FRAG_LOAD_PROGRESS: 'hlsFragLoadProgress',
238 // Identifier for fragment load aborting for emergency switch down - data: { frag : fragment object }
239 FRAG_LOAD_EMERGENCY_ABORTED: 'hlsFragLoadEmergencyAborted',
240 // fired when a fragment loading is completed - data: { frag : fragment object, payload : fragment payload, stats : { trequest, tfirst, tload, length } }
241 FRAG_LOADED: 'hlsFragLoaded',
242 // fired when a fragment has finished decrypting - data: { id : demuxer id, frag: fragment object, payload : fragment payload, stats : { tstart, tdecrypt } }
243 FRAG_DECRYPTED: 'hlsFragDecrypted',
244 // fired when Init Segment has been extracted from fragment - data: { id : demuxer id, frag: fragment object, moov : moov MP4 box, codecs : codecs found while parsing fragment }
245 FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
246 // fired when parsing sei text is completed - data: { id : demuxer id, frag: fragment object, samples : [ sei samples pes ] }
247 FRAG_PARSING_USERDATA: 'hlsFragParsingUserdata',
248 // fired when parsing id3 is completed - data: { id : demuxer id, frag: fragment object, samples : [ id3 samples pes ] }
249 FRAG_PARSING_METADATA: 'hlsFragParsingMetadata',
250 // fired when data have been extracted from fragment - data: { id : demuxer id, frag: fragment object, data1 : moof MP4 box or TS fragments, data2 : mdat MP4 box or null}
251 FRAG_PARSING_DATA: 'hlsFragParsingData',
252 // fired when fragment parsing is completed - data: { id : demuxer id, frag: fragment object }
253 FRAG_PARSED: 'hlsFragParsed',
254 // fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { id : demuxer id, frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length, bwEstimate } }
255 FRAG_BUFFERED: 'hlsFragBuffered',
256 // fired when fragment matching with current media position is changing - data : { id : demuxer id, frag : fragment object }
257 FRAG_CHANGED: 'hlsFragChanged',
258 // Identifier for a FPS drop event - data: { curentDropped, currentDecoded, totalDroppedFrames }
259 FPS_DROP: 'hlsFpsDrop',
260 // triggered when FPS drop triggers auto level capping - data: { level, droppedlevel }
261 FPS_DROP_LEVEL_CAPPING: 'hlsFpsDropLevelCapping',
262 // Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data }
264 // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example - data: { }
265 DESTROYING: 'hlsDestroying',
266 // fired when a decrypt key loading starts - data: { frag : fragment object }
267 KEY_LOADING: 'hlsKeyLoading',
268 // fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length } }
269 KEY_LOADED: 'hlsKeyLoaded',
270 // fired upon stream controller state transitions - data: { previousState, nextState }
271 STREAM_STATE_TRANSITION: 'hlsStreamStateTransition'
273 /* harmony default export */ __webpack_exports__["a"] = (HlsEvents);
277 /***/ (function(module, __webpack_exports__, __webpack_require__) {
280 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return ErrorTypes; });
281 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ErrorDetails; });
283 // Identifier for a network error (loading error / timeout ...)
284 NETWORK_ERROR: 'networkError',
285 // Identifier for a media Error (video/parsing/mediasource error)
286 MEDIA_ERROR: 'mediaError',
287 // EME (encrypted media extensions) errors
288 KEY_SYSTEM_ERROR: 'keySystemError',
289 // Identifier for a mux Error (demuxing/remuxing)
290 MUX_ERROR: 'muxError',
291 // Identifier for all other errors
292 OTHER_ERROR: 'otherError'
296 * @enum {ErrorDetails}
297 * @typedef {string} ErrorDetail
300 KEY_SYSTEM_NO_KEYS: 'keySystemNoKeys',
301 KEY_SYSTEM_NO_ACCESS: 'keySystemNoAccess',
302 KEY_SYSTEM_NO_SESSION: 'keySystemNoSession',
303 KEY_SYSTEM_LICENSE_REQUEST_FAILED: 'keySystemLicenseRequestFailed',
304 // Identifier for a manifest load error - data: { url : faulty URL, response : { code: error code, text: error text }}
305 MANIFEST_LOAD_ERROR: 'manifestLoadError',
306 // Identifier for a manifest load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
307 MANIFEST_LOAD_TIMEOUT: 'manifestLoadTimeOut',
308 // Identifier for a manifest parsing error - data: { url : faulty URL, reason : error reason}
309 MANIFEST_PARSING_ERROR: 'manifestParsingError',
310 // Identifier for a manifest with only incompatible codecs error - data: { url : faulty URL, reason : error reason}
311 MANIFEST_INCOMPATIBLE_CODECS_ERROR: 'manifestIncompatibleCodecsError',
312 // Identifier for a level load error - data: { url : faulty URL, response : { code: error code, text: error text }}
313 LEVEL_LOAD_ERROR: 'levelLoadError',
314 // Identifier for a level load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
315 LEVEL_LOAD_TIMEOUT: 'levelLoadTimeOut',
316 // Identifier for a level switch error - data: { level : faulty level Id, event : error description}
317 LEVEL_SWITCH_ERROR: 'levelSwitchError',
318 // Identifier for an audio track load error - data: { url : faulty URL, response : { code: error code, text: error text }}
319 AUDIO_TRACK_LOAD_ERROR: 'audioTrackLoadError',
320 // Identifier for an audio track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
321 AUDIO_TRACK_LOAD_TIMEOUT: 'audioTrackLoadTimeOut',
322 // Identifier for fragment load error - data: { frag : fragment object, response : { code: error code, text: error text }}
323 FRAG_LOAD_ERROR: 'fragLoadError',
324 // Identifier for fragment load timeout error - data: { frag : fragment object}
325 FRAG_LOAD_TIMEOUT: 'fragLoadTimeOut',
326 // Identifier for a fragment decryption error event - data: {id : demuxer Id,frag: fragment object, reason : parsing error description }
327 FRAG_DECRYPT_ERROR: 'fragDecryptError',
328 // Identifier for a fragment parsing error event - data: { id : demuxer Id, reason : parsing error description }
329 // will be renamed DEMUX_PARSING_ERROR and switched to MUX_ERROR in the next major release
330 FRAG_PARSING_ERROR: 'fragParsingError',
331 // Identifier for a remux alloc error event - data: { id : demuxer Id, frag : fragment object, bytes : nb of bytes on which allocation failed , reason : error text }
332 REMUX_ALLOC_ERROR: 'remuxAllocError',
333 // Identifier for decrypt key load error - data: { frag : fragment object, response : { code: error code, text: error text }}
334 KEY_LOAD_ERROR: 'keyLoadError',
335 // Identifier for decrypt key load timeout error - data: { frag : fragment object}
336 KEY_LOAD_TIMEOUT: 'keyLoadTimeOut',
337 // Triggered when an exception occurs while adding a sourceBuffer to MediaSource - data : { err : exception , mimeType : mimeType }
338 BUFFER_ADD_CODEC_ERROR: 'bufferAddCodecError',
339 // Identifier for a buffer append error - data: append error description
340 BUFFER_APPEND_ERROR: 'bufferAppendError',
341 // Identifier for a buffer appending error event - data: appending error description
342 BUFFER_APPENDING_ERROR: 'bufferAppendingError',
343 // Identifier for a buffer stalled error event
344 BUFFER_STALLED_ERROR: 'bufferStalledError',
345 // Identifier for a buffer full event
346 BUFFER_FULL_ERROR: 'bufferFullError',
347 // Identifier for a buffer seek over hole event
348 BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole',
349 // Identifier for a buffer nudge on stall (playback is stuck although currentTime is in a buffered area)
350 BUFFER_NUDGE_ON_STALL: 'bufferNudgeOnStall',
351 // Identifier for an internal exception happening inside hls.js while handling an event
352 INTERNAL_EXCEPTION: 'internalException'
357 /***/ (function(module, exports, __webpack_require__) {
359 // see https://tools.ietf.org/html/rfc1808
\r
361 /* jshint ignore:start */
\r
363 /* jshint ignore:end */
\r
365 var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/\;?#]*)?(.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
\r
366 var FIRST_SEGMENT_REGEX = /^([^\/;?#]*)(.*)$/;
\r
367 var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
\r
368 var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;
\r
370 var URLToolkit = { // jshint ignore:line
\r
371 // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
\r
373 // With opts.alwaysNormalize = false (default, spec compliant)
\r
374 // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
\r
375 // With opts.alwaysNormalize = true (not spec compliant)
\r
376 // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
\r
377 buildAbsoluteURL: function(baseURL, relativeURL, opts) {
\r
379 // remove any remaining space and CRLF
\r
380 baseURL = baseURL.trim();
\r
381 relativeURL = relativeURL.trim();
\r
382 if (!relativeURL) {
\r
383 // 2a) If the embedded URL is entirely empty, it inherits the
\r
384 // entire base URL (i.e., is set equal to the base URL)
\r
385 // and we are done.
\r
386 if (!opts.alwaysNormalize) {
\r
389 var basePartsForNormalise = URLToolkit.parseURL(baseURL);
\r
390 if (!basePartsForNormalise) {
\r
391 throw new Error('Error trying to parse base URL.');
\r
393 basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
\r
394 return URLToolkit.buildURLFromParts(basePartsForNormalise);
\r
396 var relativeParts = URLToolkit.parseURL(relativeURL);
\r
397 if (!relativeParts) {
\r
398 throw new Error('Error trying to parse relative URL.');
\r
400 if (relativeParts.scheme) {
\r
401 // 2b) If the embedded URL starts with a scheme name, it is
\r
402 // interpreted as an absolute URL and we are done.
\r
403 if (!opts.alwaysNormalize) {
\r
404 return relativeURL;
\r
406 relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
\r
407 return URLToolkit.buildURLFromParts(relativeParts);
\r
409 var baseParts = URLToolkit.parseURL(baseURL);
\r
411 throw new Error('Error trying to parse base URL.');
\r
413 if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
\r
414 // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
\r
415 // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
\r
416 var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
\r
417 baseParts.netLoc = pathParts[1];
\r
418 baseParts.path = pathParts[2];
\r
420 if (baseParts.netLoc && !baseParts.path) {
\r
421 baseParts.path = '/';
\r
424 // 2c) Otherwise, the embedded URL inherits the scheme of
\r
426 scheme: baseParts.scheme,
\r
427 netLoc: relativeParts.netLoc,
\r
429 params: relativeParts.params,
\r
430 query: relativeParts.query,
\r
431 fragment: relativeParts.fragment
\r
433 if (!relativeParts.netLoc) {
\r
434 // 3) If the embedded URL's <net_loc> is non-empty, we skip to
\r
435 // Step 7. Otherwise, the embedded URL inherits the <net_loc>
\r
436 // (if any) of the base URL.
\r
437 builtParts.netLoc = baseParts.netLoc;
\r
438 // 4) If the embedded URL path is preceded by a slash "/", the
\r
439 // path is not relative and we skip to Step 7.
\r
440 if (relativeParts.path[0] !== '/') {
\r
441 if (!relativeParts.path) {
\r
442 // 5) If the embedded URL path is empty (and not preceded by a
\r
443 // slash), then the embedded URL inherits the base URL path
\r
444 builtParts.path = baseParts.path;
\r
445 // 5a) if the embedded URL's <params> is non-empty, we skip to
\r
446 // step 7; otherwise, it inherits the <params> of the base
\r
447 // URL (if any) and
\r
448 if (!relativeParts.params) {
\r
449 builtParts.params = baseParts.params;
\r
450 // 5b) if the embedded URL's <query> is non-empty, we skip to
\r
451 // step 7; otherwise, it inherits the <query> of the base
\r
452 // URL (if any) and we skip to step 7.
\r
453 if (!relativeParts.query) {
\r
454 builtParts.query = baseParts.query;
\r
458 // 6) The last segment of the base URL's path (anything
\r
459 // following the rightmost slash "/", or the entire path if no
\r
460 // slash is present) is removed and the embedded URL's path is
\r
461 // appended in its place.
\r
462 var baseURLPath = baseParts.path;
\r
463 var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
\r
464 builtParts.path = URLToolkit.normalizePath(newPath);
\r
468 if (builtParts.path === null) {
\r
469 builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
\r
471 return URLToolkit.buildURLFromParts(builtParts);
\r
473 parseURL: function(url) {
\r
474 var parts = URL_REGEX.exec(url);
\r
479 scheme: parts[1] || '',
\r
480 netLoc: parts[2] || '',
\r
481 path: parts[3] || '',
\r
482 params: parts[4] || '',
\r
483 query: parts[5] || '',
\r
484 fragment: parts[6] || ''
\r
487 normalizePath: function(path) {
\r
488 // The following operations are
\r
489 // then applied, in order, to the new path:
\r
490 // 6a) All occurrences of "./", where "." is a complete path
\r
491 // segment, are removed.
\r
492 // 6b) If the path ends with "." as a complete path segment,
\r
493 // that "." is removed.
\r
494 path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, '');
\r
495 // 6c) All occurrences of "<segment>/../", where <segment> is a
\r
496 // complete path segment not equal to "..", are removed.
\r
497 // Removal of these path segments is performed iteratively,
\r
498 // removing the leftmost matching pattern on each iteration,
\r
499 // until no matching pattern remains.
\r
500 // 6d) If the path ends with "<segment>/..", where <segment> is a
\r
501 // complete path segment not equal to "..", that
\r
502 // "<segment>/.." is removed.
\r
503 while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line
\r
504 return path.split('').reverse().join('');
\r
506 buildURLFromParts: function(parts) {
\r
507 return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
\r
511 /* jshint ignore:start */
\r
513 module.exports = URLToolkit;
\r
514 else if(typeof define === 'function' && define.amd)
\r
515 define([], function() { return URLToolkit; });
\r
516 else if(typeof exports === 'object')
\r
517 exports["URLToolkit"] = URLToolkit;
\r
519 root["URLToolkit"] = URLToolkit;
\r
521 /* jshint ignore:end */
\r
526 /***/ (function(module, __webpack_exports__, __webpack_require__) {
529 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return utf8ArrayToStr; });
530 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
535 var ID3 = function () {
537 _classCallCheck(this, ID3);
541 * Returns true if an ID3 header can be found at offset in data
542 * @param {Uint8Array} data - The data to search in
543 * @param {number} offset - The offset at which to start searching
544 * @return {boolean} - True if an ID3 header is found
546 ID3.isHeader = function isHeader(data, offset) {
548 * http://id3.org/id3v2.3.0
556 * An ID3v2 tag can be detected with the following pattern:
557 * $49 44 33 yy yy xx zz zz zz zz
558 * Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80
560 if (offset + 10 <= data.length) {
561 // look for 'ID3' identifier
562 if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) {
563 // check version is within range
564 if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) {
565 // check size is within range
566 if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) return true;
575 * Returns true if an ID3 footer can be found at offset in data
576 * @param {Uint8Array} data - The data to search in
577 * @param {number} offset - The offset at which to start searching
578 * @return {boolean} - True if an ID3 footer is found
582 ID3.isFooter = function isFooter(data, offset) {
584 * The footer is a copy of the header, but with a different identifier
586 if (offset + 10 <= data.length) {
587 // look for '3DI' identifier
588 if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) {
589 // check version is within range
590 if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) {
591 // check size is within range
592 if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) return true;
601 * Returns any adjacent ID3 tags found in data starting at offset, as one block of data
602 * @param {Uint8Array} data - The data to search in
603 * @param {number} offset - The offset at which to start searching
604 * @return {Uint8Array} - The block of data containing any ID3 tags found
608 ID3.getID3Data = function getID3Data(data, offset) {
612 while (ID3.isHeader(data, offset)) {
613 // ID3 header is 10 bytes
616 var size = ID3._readSize(data, offset + 6);
619 if (ID3.isFooter(data, offset + 10)) {
620 // ID3 footer is 10 bytes
627 if (length > 0) return data.subarray(front, front + length);
632 ID3._readSize = function _readSize(data, offset) {
634 size = (data[offset] & 0x7f) << 21;
635 size |= (data[offset + 1] & 0x7f) << 14;
636 size |= (data[offset + 2] & 0x7f) << 7;
637 size |= data[offset + 3] & 0x7f;
642 * Searches for the Elementary Stream timestamp found in the ID3 data chunk
643 * @param {Uint8Array} data - Block of data containing one or more ID3 tags
644 * @return {number} - The timestamp
648 ID3.getTimeStamp = function getTimeStamp(data) {
649 var frames = ID3.getID3Frames(data);
650 for (var i = 0; i < frames.length; i++) {
651 var frame = frames[i];
652 if (ID3.isTimeStampFrame(frame)) return ID3._readTimeStamp(frame);
659 * Returns true if the ID3 frame is an Elementary Stream timestamp frame
660 * @param {ID3 frame} frame
664 ID3.isTimeStampFrame = function isTimeStampFrame(frame) {
665 return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp';
668 ID3._getFrameData = function _getFrameData(data) {
670 Frame ID $xx xx xx xx (four characters)
674 var type = String.fromCharCode(data[0], data[1], data[2], data[3]);
675 var size = ID3._readSize(data, 4);
677 // skip frame id, size, and flags
680 return { type: type, size: size, data: data.subarray(offset, offset + size) };
684 * Returns an array of ID3 frames found in all the ID3 tags in the id3Data
685 * @param {Uint8Array} id3Data - The ID3 data containing one or more ID3 tags
686 * @return {ID3 frame[]} - Array of ID3 frame objects
690 ID3.getID3Frames = function getID3Frames(id3Data) {
694 while (ID3.isHeader(id3Data, offset)) {
695 var size = ID3._readSize(id3Data, offset + 6);
696 // skip past ID3 header
698 var end = offset + size;
699 // loop through frames in the ID3 tag
700 while (offset + 8 < end) {
701 var frameData = ID3._getFrameData(id3Data.subarray(offset));
702 var frame = ID3._decodeFrame(frameData);
703 if (frame) frames.push(frame);
705 // skip frame header and frame data
706 offset += frameData.size + 10;
709 if (ID3.isFooter(id3Data, offset)) offset += 10;
715 ID3._decodeFrame = function _decodeFrame(frame) {
716 if (frame.type === 'PRIV') return ID3._decodePrivFrame(frame);else if (frame.type[0] === 'T') return ID3._decodeTextFrame(frame);else if (frame.type[0] === 'W') return ID3._decodeURLFrame(frame);
721 ID3._readTimeStamp = function _readTimeStamp(timeStampFrame) {
722 if (timeStampFrame.data.byteLength === 8) {
723 var data = new Uint8Array(timeStampFrame.data);
724 // timestamp is 33 bit expressed as a big-endian eight-octet number,
725 // with the upper 31 bits set to zero.
726 var pts33Bit = data[3] & 0x1;
727 var timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7];
730 if (pts33Bit) timestamp += 47721858.84; // 2^32 / 90
732 return Math.round(timestamp);
738 ID3._decodePrivFrame = function _decodePrivFrame(frame) {
740 Format: <text string>\0<binary data>
742 if (frame.size < 2) return undefined;
744 var owner = ID3._utf8ArrayToStr(frame.data, true);
745 var privateData = new Uint8Array(frame.data.subarray(owner.length + 1));
747 return { key: frame.type, info: owner, data: privateData.buffer };
750 ID3._decodeTextFrame = function _decodeTextFrame(frame) {
751 if (frame.size < 2) return undefined;
753 if (frame.type === 'TXXX') {
756 [0] = {Text Encoding}
757 [1-?] = {Description}\0{Value}
760 var description = ID3._utf8ArrayToStr(frame.data.subarray(index));
762 index += description.length + 1;
763 var value = ID3._utf8ArrayToStr(frame.data.subarray(index));
765 return { key: frame.type, info: description, data: value };
769 [0] = {Text Encoding}
772 var text = ID3._utf8ArrayToStr(frame.data.subarray(1));
773 return { key: frame.type, data: text };
777 ID3._decodeURLFrame = function _decodeURLFrame(frame) {
778 if (frame.type === 'WXXX') {
781 [0] = {Text Encoding}
782 [1-?] = {Description}\0{URL}
784 if (frame.size < 2) return undefined;
787 var description = ID3._utf8ArrayToStr(frame.data.subarray(index));
789 index += description.length + 1;
790 var value = ID3._utf8ArrayToStr(frame.data.subarray(index));
792 return { key: frame.type, info: description, data: value };
798 var url = ID3._utf8ArrayToStr(frame.data);
799 return { key: frame.type, data: url };
803 // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
804 // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
805 /* utf.js - UTF-8 <=> UTF-16 convertion
807 * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
809 * LastModified: Dec 25 1999
810 * This library is free. You can redistribute it and/or modify it.
814 ID3._utf8ArrayToStr = function _utf8ArrayToStr(array) {
815 var exitOnNull = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
817 var len = array.length;
825 if (c === 0x00 && exitOnNull) {
827 } else if (c === 0x00 || c === 0x03) {
828 // If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it
832 case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:
834 out += String.fromCharCode(c);
837 // 110x xxxx 10xx xxxx
839 out += String.fromCharCode((c & 0x1F) << 6 | char2 & 0x3F);
842 // 1110 xxxx 10xx xxxx 10xx xxxx
845 out += String.fromCharCode((c & 0x0F) << 12 | (char2 & 0x3F) << 6 | (char3 & 0x3F) << 0);
856 var utf8ArrayToStr = ID3._utf8ArrayToStr;
858 /* harmony default export */ __webpack_exports__["a"] = (ID3);
864 /***/ (function(module, __webpack_exports__, __webpack_require__) {
868 // CONCATENATED MODULE: ./src/crypt/aes-crypto.js
869 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
871 var AESCrypto = function () {
872 function AESCrypto(subtle, iv) {
873 _classCallCheck(this, AESCrypto);
875 this.subtle = subtle;
879 AESCrypto.prototype.decrypt = function decrypt(data, key) {
880 return this.subtle.decrypt({ name: 'AES-CBC', iv: this.aesIV }, key, data);
886 /* harmony default export */ var aes_crypto = (AESCrypto);
887 // CONCATENATED MODULE: ./src/crypt/fast-aes-key.js
888 function fast_aes_key__classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
890 var FastAESKey = function () {
891 function FastAESKey(subtle, key) {
892 fast_aes_key__classCallCheck(this, FastAESKey);
894 this.subtle = subtle;
898 FastAESKey.prototype.expandKey = function expandKey() {
899 return this.subtle.importKey('raw', this.key, { name: 'AES-CBC' }, false, ['encrypt', 'decrypt']);
905 /* harmony default export */ var fast_aes_key = (FastAESKey);
906 // CONCATENATED MODULE: ./src/crypt/aes-decryptor.js
907 function aes_decryptor__classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
910 function removePadding(buffer) {
911 var outputBytes = buffer.byteLength;
912 var paddingBytes = outputBytes && new DataView(buffer).getUint8(outputBytes - 1);
913 if (paddingBytes) return buffer.slice(0, outputBytes - paddingBytes);else return buffer;
916 var AESDecryptor = function () {
917 function AESDecryptor() {
918 aes_decryptor__classCallCheck(this, AESDecryptor);
920 // Static after running initTable
921 this.rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
922 this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
923 this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
924 this.sBox = new Uint32Array(256);
925 this.invSBox = new Uint32Array(256);
927 // Changes during runtime
928 this.key = new Uint32Array(0);
933 // Using view.getUint32() also swaps the byte order.
936 AESDecryptor.prototype.uint8ArrayToUint32Array_ = function uint8ArrayToUint32Array_(arrayBuffer) {
937 var view = new DataView(arrayBuffer);
938 var newArray = new Uint32Array(4);
939 for (var i = 0; i < 4; i++) {
940 newArray[i] = view.getUint32(i * 4);
944 AESDecryptor.prototype.initTable = function initTable() {
945 var sBox = this.sBox;
946 var invSBox = this.invSBox;
947 var subMix = this.subMix;
948 var subMix0 = subMix[0];
949 var subMix1 = subMix[1];
950 var subMix2 = subMix[2];
951 var subMix3 = subMix[3];
952 var invSubMix = this.invSubMix;
953 var invSubMix0 = invSubMix[0];
954 var invSubMix1 = invSubMix[1];
955 var invSubMix2 = invSubMix[2];
956 var invSubMix3 = invSubMix[3];
958 var d = new Uint32Array(256);
962 for (i = 0; i < 256; i++) {
963 if (i < 128) d[i] = i << 1;else d[i] = i << 1 ^ 0x11b;
966 for (i = 0; i < 256; i++) {
967 var sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4;
968 sx = sx >>> 8 ^ sx & 0xff ^ 0x63;
972 // Compute multiplication
977 // Compute sub/invSub bytes, mix columns tables
978 var t = d[sx] * 0x101 ^ sx * 0x1010100;
979 subMix0[x] = t << 24 | t >>> 8;
980 subMix1[x] = t << 16 | t >>> 16;
981 subMix2[x] = t << 8 | t >>> 24;
984 // Compute inv sub bytes, inv mix columns tables
985 t = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
986 invSubMix0[sx] = t << 24 | t >>> 8;
987 invSubMix1[sx] = t << 16 | t >>> 16;
988 invSubMix2[sx] = t << 8 | t >>> 24;
991 // Compute next counter
995 x = x2 ^ d[d[d[x8 ^ x2]]];
1001 AESDecryptor.prototype.expandKey = function expandKey(keyBuffer) {
1002 // convert keyBuffer to Uint32Array
1003 var key = this.uint8ArrayToUint32Array_(keyBuffer);
1007 while (offset < key.length && sameKey) {
1008 sameKey = key[offset] === this.key[offset];
1012 if (sameKey) return;
1015 var keySize = this.keySize = key.length;
1017 if (keySize !== 4 && keySize !== 6 && keySize !== 8) throw new Error('Invalid aes key size=' + keySize);
1019 var ksRows = this.ksRows = (keySize + 6 + 1) * 4;
1021 var invKsRow = void 0;
1023 var keySchedule = this.keySchedule = new Uint32Array(ksRows);
1024 var invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows);
1025 var sbox = this.sBox;
1026 var rcon = this.rcon;
1028 var invSubMix = this.invSubMix;
1029 var invSubMix0 = invSubMix[0];
1030 var invSubMix1 = invSubMix[1];
1031 var invSubMix2 = invSubMix[2];
1032 var invSubMix3 = invSubMix[3];
1037 for (ksRow = 0; ksRow < ksRows; ksRow++) {
1038 if (ksRow < keySize) {
1039 prev = keySchedule[ksRow] = key[ksRow];
1044 if (ksRow % keySize === 0) {
1046 t = t << 8 | t >>> 24;
1049 t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff];
1052 t ^= rcon[ksRow / keySize | 0] << 24;
1053 } else if (keySize > 6 && ksRow % keySize === 4) {
1055 t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff];
1058 keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0;
1061 for (invKsRow = 0; invKsRow < ksRows; invKsRow++) {
1062 ksRow = ksRows - invKsRow;
1063 if (invKsRow & 3) t = keySchedule[ksRow];else t = keySchedule[ksRow - 4];
1065 if (invKsRow < 4 || ksRow <= 4) invKeySchedule[invKsRow] = t;else invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[t >>> 16 & 0xff]] ^ invSubMix2[sbox[t >>> 8 & 0xff]] ^ invSubMix3[sbox[t & 0xff]];
1067 invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0;
1071 // Adding this as a method greatly improves performance.
1074 AESDecryptor.prototype.networkToHostOrderSwap = function networkToHostOrderSwap(word) {
1075 return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
1078 AESDecryptor.prototype.decrypt = function decrypt(inputArrayBuffer, offset, aesIV, removePKCS7Padding) {
1079 var nRounds = this.keySize + 6;
1080 var invKeySchedule = this.invKeySchedule;
1081 var invSBOX = this.invSBox;
1083 var invSubMix = this.invSubMix;
1084 var invSubMix0 = invSubMix[0];
1085 var invSubMix1 = invSubMix[1];
1086 var invSubMix2 = invSubMix[2];
1087 var invSubMix3 = invSubMix[3];
1089 var initVector = this.uint8ArrayToUint32Array_(aesIV);
1090 var initVector0 = initVector[0];
1091 var initVector1 = initVector[1];
1092 var initVector2 = initVector[2];
1093 var initVector3 = initVector[3];
1095 var inputInt32 = new Int32Array(inputArrayBuffer);
1096 var outputInt32 = new Int32Array(inputInt32.length);
1106 var inputWords0 = void 0,
1107 inputWords1 = void 0,
1108 inputWords2 = void 0,
1109 inputWords3 = void 0;
1113 var swapWord = this.networkToHostOrderSwap;
1115 while (offset < inputInt32.length) {
1116 inputWords0 = swapWord(inputInt32[offset]);
1117 inputWords1 = swapWord(inputInt32[offset + 1]);
1118 inputWords2 = swapWord(inputInt32[offset + 2]);
1119 inputWords3 = swapWord(inputInt32[offset + 3]);
1121 s0 = inputWords0 ^ invKeySchedule[0];
1122 s1 = inputWords3 ^ invKeySchedule[1];
1123 s2 = inputWords2 ^ invKeySchedule[2];
1124 s3 = inputWords1 ^ invKeySchedule[3];
1128 // Iterate through the rounds of decryption
1129 for (i = 1; i < nRounds; i++) {
1130 t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 0xff] ^ invSubMix2[s2 >> 8 & 0xff] ^ invSubMix3[s3 & 0xff] ^ invKeySchedule[ksRow];
1131 t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 0xff] ^ invSubMix2[s3 >> 8 & 0xff] ^ invSubMix3[s0 & 0xff] ^ invKeySchedule[ksRow + 1];
1132 t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 0xff] ^ invSubMix2[s0 >> 8 & 0xff] ^ invSubMix3[s1 & 0xff] ^ invKeySchedule[ksRow + 2];
1133 t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 0xff] ^ invSubMix2[s1 >> 8 & 0xff] ^ invSubMix3[s2 & 0xff] ^ invKeySchedule[ksRow + 3];
1143 // Shift rows, sub bytes, add round key
1144 t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 0xff] << 16 ^ invSBOX[s2 >> 8 & 0xff] << 8 ^ invSBOX[s3 & 0xff] ^ invKeySchedule[ksRow];
1145 t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 0xff] << 16 ^ invSBOX[s3 >> 8 & 0xff] << 8 ^ invSBOX[s0 & 0xff] ^ invKeySchedule[ksRow + 1];
1146 t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 0xff] << 16 ^ invSBOX[s0 >> 8 & 0xff] << 8 ^ invSBOX[s1 & 0xff] ^ invKeySchedule[ksRow + 2];
1147 t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 0xff] << 16 ^ invSBOX[s1 >> 8 & 0xff] << 8 ^ invSBOX[s2 & 0xff] ^ invKeySchedule[ksRow + 3];
1151 outputInt32[offset] = swapWord(t0 ^ initVector0);
1152 outputInt32[offset + 1] = swapWord(t3 ^ initVector1);
1153 outputInt32[offset + 2] = swapWord(t2 ^ initVector2);
1154 outputInt32[offset + 3] = swapWord(t1 ^ initVector3);
1156 // reset initVector to last 4 unsigned int
1157 initVector0 = inputWords0;
1158 initVector1 = inputWords1;
1159 initVector2 = inputWords2;
1160 initVector3 = inputWords3;
1162 offset = offset + 4;
1165 return removePKCS7Padding ? removePadding(outputInt32.buffer) : outputInt32.buffer;
1168 AESDecryptor.prototype.destroy = function destroy() {
1169 this.key = undefined;
1170 this.keySize = undefined;
1171 this.ksRows = undefined;
1173 this.sBox = undefined;
1174 this.invSBox = undefined;
1175 this.subMix = undefined;
1176 this.invSubMix = undefined;
1177 this.keySchedule = undefined;
1178 this.invKeySchedule = undefined;
1180 this.rcon = undefined;
1183 return AESDecryptor;
1186 /* harmony default export */ var aes_decryptor = (AESDecryptor);
1187 // EXTERNAL MODULE: ./src/errors.js
1188 var errors = __webpack_require__(2);
1190 // EXTERNAL MODULE: ./src/utils/logger.js
1191 var logger = __webpack_require__(0);
1193 // CONCATENATED MODULE: ./src/crypt/decrypter.js
1194 function decrypter__classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
1203 /* globals self: false */
1205 var decrypter_Decrypter = function () {
1206 function Decrypter(observer, config) {
1207 var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
1208 _ref$removePKCS7Paddi = _ref.removePKCS7Padding,
1209 removePKCS7Padding = _ref$removePKCS7Paddi === undefined ? true : _ref$removePKCS7Paddi;
1211 decrypter__classCallCheck(this, Decrypter);
1213 this.logEnabled = true;
1214 this.observer = observer;
1215 this.config = config;
1216 this.removePKCS7Padding = removePKCS7Padding;
1217 // built in decryptor expects PKCS7 padding
1218 if (removePKCS7Padding) {
1220 var browserCrypto = crypto || self.crypto;
1221 this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle;
1224 this.disableWebCrypto = !this.subtle;
1227 Decrypter.prototype.isSync = function isSync() {
1228 return this.disableWebCrypto && this.config.enableSoftwareAES;
1231 Decrypter.prototype.decrypt = function decrypt(data, key, iv, callback) {
1234 if (this.disableWebCrypto && this.config.enableSoftwareAES) {
1235 if (this.logEnabled) {
1236 logger["b" /* logger */].log('JS AES decrypt');
1237 this.logEnabled = false;
1239 var decryptor = this.decryptor;
1240 if (!decryptor) this.decryptor = decryptor = new aes_decryptor();
1242 decryptor.expandKey(key);
1243 callback(decryptor.decrypt(data, 0, iv, this.removePKCS7Padding));
1245 if (this.logEnabled) {
1246 logger["b" /* logger */].log('WebCrypto AES decrypt');
1247 this.logEnabled = false;
1249 var subtle = this.subtle;
1250 if (this.key !== key) {
1252 this.fastAesKey = new fast_aes_key(subtle, key);
1255 this.fastAesKey.expandKey().then(function (aesKey) {
1256 // decrypt using web crypto
1257 var crypto = new aes_crypto(subtle, iv);
1258 crypto.decrypt(data, aesKey).catch(function (err) {
1259 _this.onWebCryptoError(err, data, key, iv, callback);
1260 }).then(function (result) {
1263 }).catch(function (err) {
1264 _this.onWebCryptoError(err, data, key, iv, callback);
1269 Decrypter.prototype.onWebCryptoError = function onWebCryptoError(err, data, key, iv, callback) {
1270 if (this.config.enableSoftwareAES) {
1271 logger["b" /* logger */].log('WebCrypto Error, disable WebCrypto API');
1272 this.disableWebCrypto = true;
1273 this.logEnabled = true;
1274 this.decrypt(data, key, iv, callback);
1276 logger["b" /* logger */].error('decrypting error : ' + err.message);
1277 this.observer.trigger(Event.ERROR, { type: errors["b" /* ErrorTypes */].MEDIA_ERROR, details: errors["a" /* ErrorDetails */].FRAG_DECRYPT_ERROR, fatal: true, reason: err.message });
1281 Decrypter.prototype.destroy = function destroy() {
1282 var decryptor = this.decryptor;
1284 decryptor.destroy();
1285 this.decryptor = undefined;
1292 /* harmony default export */ var decrypter = __webpack_exports__["a"] = (decrypter_Decrypter);
1296 /***/ (function(module, exports) {
1298 // Copyright Joyent, Inc. and other Node contributors.
1300 // Permission is hereby granted, free of charge, to any person obtaining a
1301 // copy of this software and associated documentation files (the
1302 // "Software"), to deal in the Software without restriction, including
1303 // without limitation the rights to use, copy, modify, merge, publish,
1304 // distribute, sublicense, and/or sell copies of the Software, and to permit
1305 // persons to whom the Software is furnished to do so, subject to the
1306 // following conditions:
1308 // The above copyright notice and this permission notice shall be included
1309 // in all copies or substantial portions of the Software.
1311 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1312 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1313 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
1314 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
1315 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
1316 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
1317 // USE OR OTHER DEALINGS IN THE SOFTWARE.
1319 function EventEmitter() {
1320 this._events = this._events || {};
1321 this._maxListeners = this._maxListeners || undefined;
1323 module.exports = EventEmitter;
1325 // Backwards-compat with node 0.10.x
1326 EventEmitter.EventEmitter = EventEmitter;
1328 EventEmitter.prototype._events = undefined;
1329 EventEmitter.prototype._maxListeners = undefined;
1331 // By default EventEmitters will print a warning if more than 10 listeners are
1332 // added to it. This is a useful default which helps finding memory leaks.
1333 EventEmitter.defaultMaxListeners = 10;
1335 // Obviously not all Emitters should be limited to 10. This function allows
1336 // that to be increased. Set to zero for unlimited.
1337 EventEmitter.prototype.setMaxListeners = function(n) {
1338 if (!isNumber(n) || n < 0 || isNaN(n))
1339 throw TypeError('n must be a positive number');
1340 this._maxListeners = n;
1344 EventEmitter.prototype.emit = function(type) {
1345 var er, handler, len, args, i, listeners;
1350 // If there is no 'error' event listener then throw.
1351 if (type === 'error') {
1352 if (!this._events.error ||
1353 (isObject(this._events.error) && !this._events.error.length)) {
1355 if (er instanceof Error) {
1356 throw er; // Unhandled 'error' event
1358 // At least give some kind of context to the user
1359 var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
1366 handler = this._events[type];
1368 if (isUndefined(handler))
1371 if (isFunction(handler)) {
1372 switch (arguments.length) {
1378 handler.call(this, arguments[1]);
1381 handler.call(this, arguments[1], arguments[2]);
1385 args = Array.prototype.slice.call(arguments, 1);
1386 handler.apply(this, args);
1388 } else if (isObject(handler)) {
1389 args = Array.prototype.slice.call(arguments, 1);
1390 listeners = handler.slice();
1391 len = listeners.length;
1392 for (i = 0; i < len; i++)
1393 listeners[i].apply(this, args);
1399 EventEmitter.prototype.addListener = function(type, listener) {
1402 if (!isFunction(listener))
1403 throw TypeError('listener must be a function');
1408 // To avoid recursion in the case that type === "newListener"! Before
1409 // adding it to the listeners, first emit "newListener".
1410 if (this._events.newListener)
1411 this.emit('newListener', type,
1412 isFunction(listener.listener) ?
1413 listener.listener : listener);
1415 if (!this._events[type])
1416 // Optimize the case of one listener. Don't need the extra array object.
1417 this._events[type] = listener;
1418 else if (isObject(this._events[type]))
1419 // If we've already got an array, just append.
1420 this._events[type].push(listener);
1422 // Adding the second element, need to change to array.
1423 this._events[type] = [this._events[type], listener];
1425 // Check for listener leak
1426 if (isObject(this._events[type]) && !this._events[type].warned) {
1427 if (!isUndefined(this._maxListeners)) {
1428 m = this._maxListeners;
1430 m = EventEmitter.defaultMaxListeners;
1433 if (m && m > 0 && this._events[type].length > m) {
1434 this._events[type].warned = true;
1435 console.error('(node) warning: possible EventEmitter memory ' +
1436 'leak detected. %d listeners added. ' +
1437 'Use emitter.setMaxListeners() to increase limit.',
1438 this._events[type].length);
1439 if (typeof console.trace === 'function') {
1440 // not supported in IE 10
1449 EventEmitter.prototype.on = EventEmitter.prototype.addListener;
1451 EventEmitter.prototype.once = function(type, listener) {
1452 if (!isFunction(listener))
1453 throw TypeError('listener must be a function');
1458 this.removeListener(type, g);
1462 listener.apply(this, arguments);
1466 g.listener = listener;
1472 // emits a 'removeListener' event iff the listener was removed
1473 EventEmitter.prototype.removeListener = function(type, listener) {
1474 var list, position, length, i;
1476 if (!isFunction(listener))
1477 throw TypeError('listener must be a function');
1479 if (!this._events || !this._events[type])
1482 list = this._events[type];
1483 length = list.length;
1486 if (list === listener ||
1487 (isFunction(list.listener) && list.listener === listener)) {
1488 delete this._events[type];
1489 if (this._events.removeListener)
1490 this.emit('removeListener', type, listener);
1492 } else if (isObject(list)) {
1493 for (i = length; i-- > 0;) {
1494 if (list[i] === listener ||
1495 (list[i].listener && list[i].listener === listener)) {
1504 if (list.length === 1) {
1506 delete this._events[type];
1508 list.splice(position, 1);
1511 if (this._events.removeListener)
1512 this.emit('removeListener', type, listener);
1518 EventEmitter.prototype.removeAllListeners = function(type) {
1524 // not listening for removeListener, no need to emit
1525 if (!this._events.removeListener) {
1526 if (arguments.length === 0)
1528 else if (this._events[type])
1529 delete this._events[type];
1533 // emit removeListener for all listeners on all events
1534 if (arguments.length === 0) {
1535 for (key in this._events) {
1536 if (key === 'removeListener') continue;
1537 this.removeAllListeners(key);
1539 this.removeAllListeners('removeListener');
1544 listeners = this._events[type];
1546 if (isFunction(listeners)) {
1547 this.removeListener(type, listeners);
1548 } else if (listeners) {
1550 while (listeners.length)
1551 this.removeListener(type, listeners[listeners.length - 1]);
1553 delete this._events[type];
1558 EventEmitter.prototype.listeners = function(type) {
1560 if (!this._events || !this._events[type])
1562 else if (isFunction(this._events[type]))
1563 ret = [this._events[type]];
1565 ret = this._events[type].slice();
1569 EventEmitter.prototype.listenerCount = function(type) {
1571 var evlistener = this._events[type];
1573 if (isFunction(evlistener))
1575 else if (evlistener)
1576 return evlistener.length;
1581 EventEmitter.listenerCount = function(emitter, type) {
1582 return emitter.listenerCount(type);
1585 function isFunction(arg) {
1586 return typeof arg === 'function';
1589 function isNumber(arg) {
1590 return typeof arg === 'number';
1593 function isObject(arg) {
1594 return typeof arg === 'object' && arg !== null;
1597 function isUndefined(arg) {
1598 return arg === void 0;
1604 /***/ (function(module, __webpack_exports__, __webpack_require__) {
1607 /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_logger__ = __webpack_require__(0);
1608 /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__events__ = __webpack_require__(1);
1609 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
1617 var UINT32_MAX = Math.pow(2, 32) - 1;
1619 var MP4Demuxer = function () {
1620 function MP4Demuxer(observer, remuxer) {
1621 _classCallCheck(this, MP4Demuxer);
1623 this.observer = observer;
1624 this.remuxer = remuxer;
1627 MP4Demuxer.prototype.resetTimeStamp = function resetTimeStamp(initPTS) {
1628 this.initPTS = initPTS;
1631 MP4Demuxer.prototype.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
1632 // jshint unused:false
1633 if (initSegment && initSegment.byteLength) {
1634 var initData = this.initData = MP4Demuxer.parseInitSegment(initSegment);
1636 // default audio codec if nothing specified
1637 // TODO : extract that from initsegment
1638 if (audioCodec == null) audioCodec = 'mp4a.40.5';
1640 if (videoCodec == null) videoCodec = 'avc1.42e01e';
1643 if (initData.audio && initData.video) {
1644 tracks.audiovideo = { container: 'video/mp4', codec: audioCodec + ',' + videoCodec, initSegment: duration ? initSegment : null };
1646 if (initData.audio) tracks.audio = { container: 'audio/mp4', codec: audioCodec, initSegment: duration ? initSegment : null };
1648 if (initData.video) tracks.video = { container: 'video/mp4', codec: videoCodec, initSegment: duration ? initSegment : null };
1650 this.observer.trigger(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].FRAG_PARSING_INIT_SEGMENT, { tracks: tracks });
1652 if (audioCodec) this.audioCodec = audioCodec;
1654 if (videoCodec) this.videoCodec = videoCodec;
1658 MP4Demuxer.probe = function probe(data) {
1659 // ensure we find a moof box in the first 16 kB
1660 return MP4Demuxer.findBox({ data: data, start: 0, end: Math.min(data.length, 16384) }, ['moof']).length > 0;
1663 MP4Demuxer.bin2str = function bin2str(buffer) {
1664 return String.fromCharCode.apply(null, buffer);
1667 MP4Demuxer.readUint16 = function readUint16(buffer, offset) {
1669 offset += buffer.start;
1670 buffer = buffer.data;
1673 var val = buffer[offset] << 8 | buffer[offset + 1];
1675 return val < 0 ? 65536 + val : val;
1678 MP4Demuxer.readUint32 = function readUint32(buffer, offset) {
1680 offset += buffer.start;
1681 buffer = buffer.data;
1684 var val = buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3];
1685 return val < 0 ? 4294967296 + val : val;
1688 MP4Demuxer.writeUint32 = function writeUint32(buffer, offset, value) {
1690 offset += buffer.start;
1691 buffer = buffer.data;
1693 buffer[offset] = value >> 24;
1694 buffer[offset + 1] = value >> 16 & 0xff;
1695 buffer[offset + 2] = value >> 8 & 0xff;
1696 buffer[offset + 3] = value & 0xff;
1699 // Find the data for a box specified by its path
1702 MP4Demuxer.findBox = function findBox(data, path) {
1708 subresults = void 0,
1718 end = data.byteLength;
1722 // short-circuit the search for empty paths
1726 for (i = start; i < end;) {
1727 size = MP4Demuxer.readUint32(data, i);
1728 type = MP4Demuxer.bin2str(data.subarray(i + 4, i + 8));
1729 endbox = size > 1 ? i + size : end;
1731 if (type === path[0]) {
1732 if (path.length === 1) {
1733 // this is the end of the path and we've found the box we were
1735 results.push({ data: data, start: i + 8, end: endbox });
1737 // recursively search for the next box along the path
1738 subresults = MP4Demuxer.findBox({ data: data, start: i + 8, end: endbox }, path.slice(1));
1739 if (subresults.length) results = results.concat(subresults);
1745 // we've finished searching all of data
1749 MP4Demuxer.parseSegmentIndex = function parseSegmentIndex(initSegment) {
1750 var moov = MP4Demuxer.findBox(initSegment, ['moov'])[0];
1751 var moovEndOffset = moov ? moov.end : null; // we need this in case we need to chop of garbage of the end of current data
1754 var sidx = MP4Demuxer.findBox(initSegment, ['sidx']);
1755 var references = void 0;
1757 if (!sidx || !sidx[0]) return null;
1762 var version = sidx.data[0];
1764 // set initial offset, we skip the reference ID (not needed)
1765 index = version === 0 ? 8 : 16;
1767 var timescale = MP4Demuxer.readUint32(sidx, index);
1770 // TODO: parse earliestPresentationTime and firstOffset
1771 // usually zero in our case
1772 var earliestPresentationTime = 0;
1773 var firstOffset = 0;
1775 if (version === 0) index += 8;else index += 16;
1780 var startByte = sidx.end + firstOffset;
1782 var referencesCount = MP4Demuxer.readUint16(sidx, index);
1785 for (var i = 0; i < referencesCount; i++) {
1786 var referenceIndex = index;
1788 var referenceInfo = MP4Demuxer.readUint32(sidx, referenceIndex);
1789 referenceIndex += 4;
1791 var referenceSize = referenceInfo & 0x7FFFFFFF;
1792 var referenceType = (referenceInfo & 0x80000000) >>> 31;
1794 if (referenceType === 1) {
1795 console.warn('SIDX has hierarchical references (not supported)');
1799 var subsegmentDuration = MP4Demuxer.readUint32(sidx, referenceIndex);
1800 referenceIndex += 4;
1803 referenceSize: referenceSize,
1804 subsegmentDuration: subsegmentDuration, // unscaled
1806 duration: subsegmentDuration / timescale,
1808 end: startByte + referenceSize - 1
1812 startByte += referenceSize;
1814 // Skipping 1 bit for |startsWithSap|, 3 bits for |sapType|, and 28 bits
1816 referenceIndex += 4;
1819 index = referenceIndex;
1823 earliestPresentationTime: earliestPresentationTime,
1824 timescale: timescale,
1826 referencesCount: referencesCount,
1827 references: references,
1828 moovEndOffset: moovEndOffset
1833 * Parses an MP4 initialization segment and extracts stream type and
1834 * timescale values for any declared tracks. Timescale values indicate the
1835 * number of clock ticks per second to assume for time-based values
1836 * elsewhere in the MP4.
1838 * To determine the start time of an MP4, you need two pieces of
1839 * information: the timescale unit and the earliest base media decode
1840 * time. Multiple timescales can be specified within an MP4 but the
1841 * base media decode time is always expressed in the timescale from
1842 * the media header box for the track:
1844 * moov > trak > mdia > mdhd.timescale
1845 * moov > trak > mdia > hdlr
1847 * @param init {Uint8Array} the bytes of the init segment
1848 * @return {object} a hash of track type to timescale values or null if
1849 * the init segment is malformed.
1853 MP4Demuxer.parseInitSegment = function parseInitSegment(initSegment) {
1855 var traks = MP4Demuxer.findBox(initSegment, ['moov', 'trak']);
1857 traks.forEach(function (trak) {
1858 var tkhd = MP4Demuxer.findBox(trak, ['tkhd'])[0];
1860 var version = tkhd.data[tkhd.start];
1861 var index = version === 0 ? 12 : 20;
1862 var trackId = MP4Demuxer.readUint32(tkhd, index);
1864 var mdhd = MP4Demuxer.findBox(trak, ['mdia', 'mdhd'])[0];
1866 version = mdhd.data[mdhd.start];
1867 index = version === 0 ? 12 : 20;
1868 var timescale = MP4Demuxer.readUint32(mdhd, index);
1870 var hdlr = MP4Demuxer.findBox(trak, ['mdia', 'hdlr'])[0];
1872 var hdlrType = MP4Demuxer.bin2str(hdlr.data.subarray(hdlr.start + 8, hdlr.start + 12));
1873 var type = { 'soun': 'audio', 'vide': 'video' }[hdlrType];
1875 // extract codec info. TODO : parse codec details to be able to build MIME type
1876 var codecBox = MP4Demuxer.findBox(trak, ['mdia', 'minf', 'stbl', 'stsd']);
1877 if (codecBox.length) {
1878 codecBox = codecBox[0];
1879 var codecType = MP4Demuxer.bin2str(codecBox.data.subarray(codecBox.start + 12, codecBox.start + 16));
1880 __WEBPACK_IMPORTED_MODULE_0__utils_logger__["b" /* logger */].log('MP4Demuxer:' + type + ':' + codecType + ' found');
1882 result[trackId] = { timescale: timescale, type: type };
1883 result[type] = { timescale: timescale, id: trackId };
1893 * Determine the base media decode start time, in seconds, for an MP4
1894 * fragment. If multiple fragments are specified, the earliest time is
1897 * The base media decode time can be parsed from track fragment
1900 * moof > traf > tfdt.baseMediaDecodeTime
1902 * It requires the timescale value from the mdhd to interpret.
1904 * @param timescale {object} a hash of track ids to timescale values.
1905 * @return {number} the earliest base media decode start time for the
1906 * fragment, in seconds
1910 MP4Demuxer.getStartDTS = function getStartDTS(initData, fragment) {
1915 // we need info from two childrend of each track fragment box
1916 trafs = MP4Demuxer.findBox(fragment, ['moof', 'traf']);
1918 // determine the start times for each track
1919 baseTimes = [].concat.apply([], trafs.map(function (traf) {
1920 return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) {
1925 // get the track id from the tfhd
1926 id = MP4Demuxer.readUint32(tfhd, 4);
1927 // assume a 90kHz clock if no timescale was specified
1928 scale = initData[id].timescale || 90e3;
1930 // get the base media decode time from the tfdt
1931 baseTime = MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) {
1932 var version = void 0,
1935 version = tfdt.data[tfdt.start];
1936 result = MP4Demuxer.readUint32(tfdt, 4);
1937 if (version === 1) {
1938 result *= Math.pow(2, 32);
1940 result += MP4Demuxer.readUint32(tfdt, 8);
1944 // convert base time to seconds
1945 return baseTime / scale;
1949 // return the minimum
1950 result = Math.min.apply(null, baseTimes);
1951 return isFinite(result) ? result : 0;
1954 MP4Demuxer.offsetStartDTS = function offsetStartDTS(initData, fragment, timeOffset) {
1955 MP4Demuxer.findBox(fragment, ['moof', 'traf']).map(function (traf) {
1956 return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) {
1957 // get the track id from the tfhd
1958 var id = MP4Demuxer.readUint32(tfhd, 4);
1959 // assume a 90kHz clock if no timescale was specified
1960 var timescale = initData[id].timescale || 90e3;
1962 // get the base media decode time from the tfdt
1963 MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) {
1964 var version = tfdt.data[tfdt.start];
1965 var baseMediaDecodeTime = MP4Demuxer.readUint32(tfdt, 4);
1966 if (version === 0) {
1967 MP4Demuxer.writeUint32(tfdt, 4, baseMediaDecodeTime - timeOffset * timescale);
1969 baseMediaDecodeTime *= Math.pow(2, 32);
1970 baseMediaDecodeTime += MP4Demuxer.readUint32(tfdt, 8);
1971 baseMediaDecodeTime -= timeOffset * timescale;
1972 baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0);
1973 var upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1));
1974 var lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1));
1975 MP4Demuxer.writeUint32(tfdt, 4, upper);
1976 MP4Demuxer.writeUint32(tfdt, 8, lower);
1983 // feed incoming data to the front of the parsing pipeline
1986 MP4Demuxer.prototype.append = function append(data, timeOffset, contiguous, accurateTimeOffset) {
1987 var initData = this.initData;
1989 this.resetInitSegment(data, this.audioCodec, this.videoCodec, false);
1990 initData = this.initData;
1992 var startDTS = void 0,
1993 initPTS = this.initPTS;
1994 if (initPTS === undefined) {
1995 var _startDTS = MP4Demuxer.getStartDTS(initData, data);
1996 this.initPTS = initPTS = _startDTS - timeOffset;
1997 this.observer.trigger(__WEBPACK_IMPORTED_MODULE_1__events__["a" /* default */].INIT_PTS_FOUND, { initPTS: initPTS });
1999 MP4Demuxer.offsetStartDTS(initData, data, initPTS);
2000 startDTS = MP4Demuxer.getStartDTS(initData, data);
2001 this.remuxer.remux(initData.audio, initData.video, null, null, startDTS, contiguous, accurateTimeOffset, data);
2004 MP4Demuxer.prototype.destroy = function destroy() {};
2009 /* harmony default export */ __webpack_exports__["a"] = (MP4Demuxer);
2013 /***/ (function(module, __webpack_exports__, __webpack_require__) {
2017 // EXTERNAL MODULE: ./src/events.js
2018 var events = __webpack_require__(1);
2020 // EXTERNAL MODULE: ./src/errors.js
2021 var errors = __webpack_require__(2);
2023 // EXTERNAL MODULE: ./src/crypt/decrypter.js + 3 modules
2024 var crypt_decrypter = __webpack_require__(5);
2026 // EXTERNAL MODULE: ./src/utils/logger.js
2027 var logger = __webpack_require__(0);
2029 // CONCATENATED MODULE: ./src/demux/adts.js
2031 * ADTS parser helper
2036 function getAudioConfig(observer, data, offset, audioCodec) {
2037 var adtsObjectType = void 0,
2039 adtsSampleingIndex = void 0,
2041 adtsExtensionSampleingIndex = void 0,
2043 adtsChanelConfig = void 0,
2046 userAgent = navigator.userAgent.toLowerCase(),
2047 manifestCodec = audioCodec,
2048 adtsSampleingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
2050 adtsObjectType = ((data[offset + 2] & 0xC0) >>> 6) + 1;
2051 adtsSampleingIndex = (data[offset + 2] & 0x3C) >>> 2;
2052 if (adtsSampleingIndex > adtsSampleingRates.length - 1) {
2053 observer.trigger(Event.ERROR, { type: errors["b" /* ErrorTypes */].MEDIA_ERROR, details: errors["a" /* ErrorDetails */].FRAG_PARSING_ERROR, fatal: true, reason: 'invalid ADTS sampling index:' + adtsSampleingIndex });
2056 adtsChanelConfig = (data[offset + 2] & 0x01) << 2;
2058 adtsChanelConfig |= (data[offset + 3] & 0xC0) >>> 6;
2059 logger["b" /* logger */].log('manifest codec:' + audioCodec + ',ADTS data:type:' + adtsObjectType + ',sampleingIndex:' + adtsSampleingIndex + '[' + adtsSampleingRates[adtsSampleingIndex] + 'Hz],channelConfig:' + adtsChanelConfig);
2060 // firefox: freq less than 24kHz = AAC SBR (HE-AAC)
2061 if (/firefox/i.test(userAgent)) {
2062 if (adtsSampleingIndex >= 6) {
2064 config = new Array(4);
2065 // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies
2066 // there is a factor 2 between frame sample rate and output sample rate
2067 // multiply frequency by 2 (see table below, equivalent to substract 3)
2068 adtsExtensionSampleingIndex = adtsSampleingIndex - 3;
2071 config = new Array(2);
2072 adtsExtensionSampleingIndex = adtsSampleingIndex;
2074 // Android : always use AAC
2075 } else if (userAgent.indexOf('android') !== -1) {
2077 config = new Array(2);
2078 adtsExtensionSampleingIndex = adtsSampleingIndex;
2080 /* for other browsers (Chrome/Vivaldi/Opera ...)
2081 always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...)
2084 config = new Array(4);
2085 // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz)
2086 if (audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1) || !audioCodec && adtsSampleingIndex >= 6) {
2087 // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies
2088 // there is a factor 2 between frame sample rate and output sample rate
2089 // multiply frequency by 2 (see table below, equivalent to substract 3)
2090 adtsExtensionSampleingIndex = adtsSampleingIndex - 3;
2092 // if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio)
2093 // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo.
2094 if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && (adtsSampleingIndex >= 6 && adtsChanelConfig === 1 || /vivaldi/i.test(userAgent)) || !audioCodec && adtsChanelConfig === 1) {
2096 config = new Array(2);
2098 adtsExtensionSampleingIndex = adtsSampleingIndex;
2101 /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config
2102 ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig()
2103 Audio Profile / Audio Object Type
2106 2: AAC LC (Low Complexity)
2107 3: AAC SSR (Scalable Sample Rate)
2108 4: AAC LTP (Long Term Prediction)
2109 5: SBR (Spectral Band Replication)
2127 15: frequency is written explictly
2128 Channel Configurations
2129 These are the channel configurations:
2130 0: Defined in AOT Specifc Config
2131 1: 1 channel: front-center
2132 2: 2 channels: front-left, front-right
2134 // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1
2135 config[0] = adtsObjectType << 3;
2136 // samplingFrequencyIndex
2137 config[0] |= (adtsSampleingIndex & 0x0E) >> 1;
2138 config[1] |= (adtsSampleingIndex & 0x01) << 7;
2139 // channelConfiguration
2140 config[1] |= adtsChanelConfig << 3;
2141 if (adtsObjectType === 5) {
2142 // adtsExtensionSampleingIndex
2143 config[1] |= (adtsExtensionSampleingIndex & 0x0E) >> 1;
2144 config[2] = (adtsExtensionSampleingIndex & 0x01) << 7;
2145 // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ???
2146 // https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc
2147 config[2] |= 2 << 2;
2150 return { config: config, samplerate: adtsSampleingRates[adtsSampleingIndex], channelCount: adtsChanelConfig, codec: 'mp4a.40.' + adtsObjectType, manifestCodec: manifestCodec };
2153 function isHeaderPattern(data, offset) {
2154 return data[offset] === 0xff && (data[offset + 1] & 0xf6) === 0xf0;
2157 function getHeaderLength(data, offset) {
2158 return data[offset + 1] & 0x01 ? 7 : 9;
2161 function getFullFrameLength(data, offset) {
2162 return (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xE0) >>> 5;
2165 function isHeader(data, offset) {
2166 // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
2167 // Layer bits (position 14 and 15) in header should be always 0 for ADTS
2168 // More info https://wiki.multimedia.cx/index.php?title=ADTS
2169 if (offset + 1 < data.length && isHeaderPattern(data, offset)) return true;
2174 function adts_probe(data, offset) {
2175 // same as isHeader but we also check that ADTS frame follows last ADTS frame
2176 // or end of data is reached
2177 if (offset + 1 < data.length && isHeaderPattern(data, offset)) {
2178 // ADTS header Length
2179 var headerLength = getHeaderLength(data, offset);
2180 // ADTS frame Length
2181 var frameLength = headerLength;
2182 if (offset + 5 < data.length) frameLength = getFullFrameLength(data, offset);
2184 var newOffset = offset + frameLength;
2185 if (newOffset === data.length || newOffset + 1 < data.length && isHeaderPattern(data, newOffset)) return true;
2190 function initTrackConfig(track, observer, data, offset, audioCodec) {
2191 if (!track.samplerate) {
2192 var config = getAudioConfig(observer, data, offset, audioCodec);
2193 track.config = config.config;
2194 track.samplerate = config.samplerate;
2195 track.channelCount = config.channelCount;
2196 track.codec = config.codec;
2197 track.manifestCodec = config.manifestCodec;
2198 logger["b" /* logger */].log('parsed codec:' + track.codec + ',rate:' + config.samplerate + ',nb channel:' + config.channelCount);
2202 function getFrameDuration(samplerate) {
2203 return 1024 * 90000 / samplerate;
2206 function parseFrameHeader(data, offset, pts, frameIndex, frameDuration) {
2207 var headerLength = void 0,
2208 frameLength = void 0,
2210 var length = data.length;
2212 // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header
2213 headerLength = getHeaderLength(data, offset);
2214 // retrieve frame size
2215 frameLength = getFullFrameLength(data, offset);
2216 frameLength -= headerLength;
2218 if (frameLength > 0 && offset + headerLength + frameLength <= length) {
2219 stamp = pts + frameIndex * frameDuration;
2220 // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`);
2221 return { headerLength: headerLength, frameLength: frameLength, stamp: stamp };
2227 function appendFrame(track, data, offset, pts, frameIndex) {
2228 var frameDuration = getFrameDuration(track.samplerate);
2229 var header = parseFrameHeader(data, offset, pts, frameIndex, frameDuration);
2231 var stamp = header.stamp;
2232 var headerLength = header.headerLength;
2233 var frameLength = header.frameLength;
2235 // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`);
2237 unit: data.subarray(offset + headerLength, offset + headerLength + frameLength),
2242 track.samples.push(aacSample);
2243 track.len += frameLength;
2245 return { sample: aacSample, length: frameLength + headerLength };
2250 // EXTERNAL MODULE: ./src/demux/id3.js
2251 var id3 = __webpack_require__(4);
2253 // CONCATENATED MODULE: ./src/demux/aacdemuxer.js
2254 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2263 var aacdemuxer_AACDemuxer = function () {
2264 function AACDemuxer(observer, remuxer, config) {
2265 _classCallCheck(this, AACDemuxer);
2267 this.observer = observer;
2268 this.config = config;
2269 this.remuxer = remuxer;
2272 AACDemuxer.prototype.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
2273 this._audioTrack = { container: 'audio/adts', type: 'audio', id: 0, sequenceNumber: 0, isAAC: true, samples: [], len: 0, manifestCodec: audioCodec, duration: duration, inputTimeScale: 90000 };
2276 AACDemuxer.prototype.resetTimeStamp = function resetTimeStamp() {};
2278 AACDemuxer.probe = function probe(data) {
2279 if (!data) return false;
2281 // Check for the ADTS sync word
2282 // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
2283 // Layer bits (position 14 and 15) in header should be always 0 for ADTS
2284 // More info https://wiki.multimedia.cx/index.php?title=ADTS
2285 var id3Data = id3["a" /* default */].getID3Data(data, 0) || [];
2286 var offset = id3Data.length;
2288 for (var length = data.length; offset < length; offset++) {
2289 if (adts_probe(data, offset)) {
2290 logger["b" /* logger */].log('ADTS sync word found !');
2297 // feed incoming data to the front of the parsing pipeline
2300 AACDemuxer.prototype.append = function append(data, timeOffset, contiguous, accurateTimeOffset) {
2301 var track = this._audioTrack;
2302 var id3Data = id3["a" /* default */].getID3Data(data, 0) || [];
2303 var timestamp = id3["a" /* default */].getTimeStamp(id3Data);
2304 var pts = timestamp ? 90 * timestamp : timeOffset * 90000;
2307 var length = data.length;
2308 var offset = id3Data.length;
2310 var id3Samples = [{ pts: stamp, dts: stamp, data: id3Data }];
2312 while (offset < length - 1) {
2313 if (isHeader(data, offset) && offset + 5 < length) {
2314 initTrackConfig(track, this.observer, data, offset, track.manifestCodec);
2315 var frame = appendFrame(track, data, offset, pts, frameIndex);
2317 offset += frame.length;
2318 stamp = frame.sample.pts;
2321 logger["b" /* logger */].log('Unable to parse AAC frame');
2324 } else if (id3["a" /* default */].isHeader(data, offset)) {
2325 id3Data = id3["a" /* default */].getID3Data(data, offset);
2326 id3Samples.push({ pts: stamp, dts: stamp, data: id3Data });
2327 offset += id3Data.length;
2329 // nothing found, keep looking
2334 this.remuxer.remux(track, { samples: [] }, { samples: id3Samples, inputTimeScale: 90000 }, { samples: [] }, timeOffset, contiguous, accurateTimeOffset);
2337 AACDemuxer.prototype.destroy = function destroy() {};
2342 /* harmony default export */ var aacdemuxer = (aacdemuxer_AACDemuxer);
2343 // EXTERNAL MODULE: ./src/demux/mp4demuxer.js
2344 var mp4demuxer = __webpack_require__(7);
2346 // CONCATENATED MODULE: ./src/demux/mpegaudio.js
2348 * MPEG parser helper
2353 BitratesMap: [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],
2355 SamplingRateMap: [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000],
2357 SamplesCoefficients: [
2383 BytesInSlot: [0, // Reserved
2389 appendFrame: function appendFrame(track, data, offset, pts, frameIndex) {
2390 // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference
2391 if (offset + 24 > data.length) return undefined;
2393 var header = this.parseHeader(data, offset);
2394 if (header && offset + header.frameLength <= data.length) {
2395 var frameDuration = header.samplesPerFrame * 90000 / header.sampleRate;
2396 var stamp = pts + frameIndex * frameDuration;
2397 var sample = { unit: data.subarray(offset, offset + header.frameLength), pts: stamp, dts: stamp };
2400 track.channelCount = header.channelCount;
2401 track.samplerate = header.sampleRate;
2402 track.samples.push(sample);
2403 track.len += header.frameLength;
2405 return { sample: sample, length: header.frameLength };
2411 parseHeader: function parseHeader(data, offset) {
2412 var headerB = data[offset + 1] >> 3 & 3;
2413 var headerC = data[offset + 1] >> 1 & 3;
2414 var headerE = data[offset + 2] >> 4 & 15;
2415 var headerF = data[offset + 2] >> 2 & 3;
2416 var headerG = data[offset + 2] >> 1 & 1;
2417 if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) {
2418 var columnInBitrates = headerB === 3 ? 3 - headerC : headerC === 3 ? 3 : 4;
2419 var bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000;
2420 var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2;
2421 var sampleRate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF];
2422 var channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono)
2423 var sampleCoefficient = MpegAudio.SamplesCoefficients[headerB][headerC];
2424 var bytesInSlot = MpegAudio.BytesInSlot[headerC];
2425 var samplesPerFrame = sampleCoefficient * 8 * bytesInSlot;
2426 var frameLength = parseInt(sampleCoefficient * bitRate / sampleRate + headerG, 10) * bytesInSlot;
2428 return { sampleRate: sampleRate, channelCount: channelCount, frameLength: frameLength, samplesPerFrame: samplesPerFrame };
2434 isHeaderPattern: function isHeaderPattern(data, offset) {
2435 return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00;
2438 isHeader: function isHeader(data, offset) {
2439 // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
2440 // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
2441 // More info http://www.mp3-tech.org/programmer/frame_header.html
2442 if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) return true;
2447 probe: function probe(data, offset) {
2448 // same as isHeader but we also check that MPEG frame follows last MPEG frame
2449 // or end of data is reached
2450 if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) {
2451 // MPEG header Length
2452 var headerLength = 4;
2453 // MPEG frame Length
2454 var header = this.parseHeader(data, offset);
2455 var frameLength = headerLength;
2456 if (header && header.frameLength) frameLength = header.frameLength;
2458 var newOffset = offset + frameLength;
2459 if (newOffset === data.length || newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset)) return true;
2465 /* harmony default export */ var mpegaudio = (MpegAudio);
2466 // CONCATENATED MODULE: ./src/demux/exp-golomb.js
2467 function exp_golomb__classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2470 * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
2475 var exp_golomb_ExpGolomb = function () {
2476 function ExpGolomb(data) {
2477 exp_golomb__classCallCheck(this, ExpGolomb);
2480 // the number of bytes left to examine in this.data
2481 this.bytesAvailable = data.byteLength;
2482 // the current word being examined
2483 this.word = 0; // :uint
2484 // the number of bits left to examine in the current word
2485 this.bitsAvailable = 0; // :uint
2491 ExpGolomb.prototype.loadWord = function loadWord() {
2492 var data = this.data,
2493 bytesAvailable = this.bytesAvailable,
2494 position = data.byteLength - bytesAvailable,
2495 workingBytes = new Uint8Array(4),
2496 availableBytes = Math.min(4, bytesAvailable);
2497 if (availableBytes === 0) throw new Error('no bytes available');
2499 workingBytes.set(data.subarray(position, position + availableBytes));
2500 this.word = new DataView(workingBytes.buffer).getUint32(0);
2501 // track the amount of this.data that has been processed
2502 this.bitsAvailable = availableBytes * 8;
2503 this.bytesAvailable -= availableBytes;
2509 ExpGolomb.prototype.skipBits = function skipBits(count) {
2510 var skipBytes = void 0; // :int
2511 if (this.bitsAvailable > count) {
2512 this.word <<= count;
2513 this.bitsAvailable -= count;
2515 count -= this.bitsAvailable;
2516 skipBytes = count >> 3;
2517 count -= skipBytes >> 3;
2518 this.bytesAvailable -= skipBytes;
2520 this.word <<= count;
2521 this.bitsAvailable -= count;
2528 ExpGolomb.prototype.readBits = function readBits(size) {
2529 var bits = Math.min(this.bitsAvailable, size),
2531 valu = this.word >>> 32 - bits; // :uint
2532 if (size > 32) logger["b" /* logger */].error('Cannot read more than 32 bits at a time');
2534 this.bitsAvailable -= bits;
2535 if (this.bitsAvailable > 0) this.word <<= bits;else if (this.bytesAvailable > 0) this.loadWord();
2538 if (bits > 0 && this.bitsAvailable) return valu << bits | this.readBits(bits);else return valu;
2544 ExpGolomb.prototype.skipLZ = function skipLZ() {
2545 var leadingZeroCount = void 0; // :uint
2546 for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) {
2547 if ((this.word & 0x80000000 >>> leadingZeroCount) !== 0) {
2548 // the first bit of working word is 1
2549 this.word <<= leadingZeroCount;
2550 this.bitsAvailable -= leadingZeroCount;
2551 return leadingZeroCount;
2554 // we exhausted word and still have not found a 1
2556 return leadingZeroCount + this.skipLZ();
2562 ExpGolomb.prototype.skipUEG = function skipUEG() {
2563 this.skipBits(1 + this.skipLZ());
2569 ExpGolomb.prototype.skipEG = function skipEG() {
2570 this.skipBits(1 + this.skipLZ());
2576 ExpGolomb.prototype.readUEG = function readUEG() {
2577 var clz = this.skipLZ(); // :uint
2578 return this.readBits(clz + 1) - 1;
2584 ExpGolomb.prototype.readEG = function readEG() {
2585 var valu = this.readUEG(); // :int
2587 // the number is odd if the low order bit is set
2588 return 1 + valu >>> 1; // add 1 to make it even, and divide by 2
2590 return -1 * (valu >>> 1); // divide by two then make it negative
2594 // Some convenience functions
2598 ExpGolomb.prototype.readBoolean = function readBoolean() {
2599 return this.readBits(1) === 1;
2605 ExpGolomb.prototype.readUByte = function readUByte() {
2606 return this.readBits(8);
2612 ExpGolomb.prototype.readUShort = function readUShort() {
2613 return this.readBits(16);
2618 ExpGolomb.prototype.readUInt = function readUInt() {
2619 return this.readBits(32);
2623 * Advance the ExpGolomb decoder past a scaling list. The scaling
2624 * list is optionally transmitted as part of a sequence parameter
2625 * set and is not relevant to transmuxing.
2626 * @param count {number} the number of entries in this scaling list
2627 * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
2631 ExpGolomb.prototype.skipScalingList = function skipScalingList(count) {
2635 deltaScale = void 0;
2636 for (j = 0; j < count; j++) {
2637 if (nextScale !== 0) {
2638 deltaScale = this.readEG();
2639 nextScale = (lastScale + deltaScale + 256) % 256;
2641 lastScale = nextScale === 0 ? lastScale : nextScale;
2646 * Read a sequence parameter set and return some interesting video
2647 * properties. A sequence parameter set is the H264 metadata that
2648 * describes the properties of upcoming video frames.
2649 * @param data {Uint8Array} the bytes of a sequence parameter set
2650 * @return {object} an object with configuration parsed from the
2651 * sequence parameter set, including the dimensions of the
2652 * associated video frames.
2656 ExpGolomb.prototype.readSPS = function readSPS() {
2657 var frameCropLeftOffset = 0,
2658 frameCropRightOffset = 0,
2659 frameCropTopOffset = 0,
2660 frameCropBottomOffset = 0,
2661 profileIdc = void 0,
2662 profileCompat = void 0,
2664 numRefFramesInPicOrderCntCycle = void 0,
2665 picWidthInMbsMinus1 = void 0,
2666 picHeightInMapUnitsMinus1 = void 0,
2667 frameMbsOnlyFlag = void 0,
2668 scalingListCount = void 0,
2670 readUByte = this.readUByte.bind(this),
2671 readBits = this.readBits.bind(this),
2672 readUEG = this.readUEG.bind(this),
2673 readBoolean = this.readBoolean.bind(this),
2674 skipBits = this.skipBits.bind(this),
2675 skipEG = this.skipEG.bind(this),
2676 skipUEG = this.skipUEG.bind(this),
2677 skipScalingList = this.skipScalingList.bind(this);
2680 profileIdc = readUByte(); // profile_idc
2681 profileCompat = readBits(5); // constraint_set[0-4]_flag, u(5)
2682 skipBits(3); // reserved_zero_3bits u(3),
2683 levelIdc = readUByte(); // level_idc u(8)
2684 skipUEG(); // seq_parameter_set_id
2685 // some profiles have more optional data we don't need
2686 if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
2687 var chromaFormatIdc = readUEG();
2688 if (chromaFormatIdc === 3) skipBits(1); // separate_colour_plane_flag
2690 skipUEG(); // bit_depth_luma_minus8
2691 skipUEG(); // bit_depth_chroma_minus8
2692 skipBits(1); // qpprime_y_zero_transform_bypass_flag
2693 if (readBoolean()) {
2694 // seq_scaling_matrix_present_flag
2695 scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
2696 for (i = 0; i < scalingListCount; i++) {
2697 if (readBoolean()) {
2698 // seq_scaling_list_present_flag[ i ]
2699 if (i < 6) skipScalingList(16);else skipScalingList(64);
2704 skipUEG(); // log2_max_frame_num_minus4
2705 var picOrderCntType = readUEG();
2706 if (picOrderCntType === 0) {
2707 readUEG(); // log2_max_pic_order_cnt_lsb_minus4
2708 } else if (picOrderCntType === 1) {
2709 skipBits(1); // delta_pic_order_always_zero_flag
2710 skipEG(); // offset_for_non_ref_pic
2711 skipEG(); // offset_for_top_to_bottom_field
2712 numRefFramesInPicOrderCntCycle = readUEG();
2713 for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
2715 } // offset_for_ref_frame[ i ]
2717 skipUEG(); // max_num_ref_frames
2718 skipBits(1); // gaps_in_frame_num_value_allowed_flag
2719 picWidthInMbsMinus1 = readUEG();
2720 picHeightInMapUnitsMinus1 = readUEG();
2721 frameMbsOnlyFlag = readBits(1);
2722 if (frameMbsOnlyFlag === 0) skipBits(1); // mb_adaptive_frame_field_flag
2724 skipBits(1); // direct_8x8_inference_flag
2725 if (readBoolean()) {
2726 // frame_cropping_flag
2727 frameCropLeftOffset = readUEG();
2728 frameCropRightOffset = readUEG();
2729 frameCropTopOffset = readUEG();
2730 frameCropBottomOffset = readUEG();
2732 var pixelRatio = [1, 1];
2733 if (readBoolean()) {
2734 // vui_parameters_present_flag
2735 if (readBoolean()) {
2736 // aspect_ratio_info_present_flag
2737 var aspectRatioIdc = readUByte();
2738 switch (aspectRatioIdc) {
2740 pixelRatio = [1, 1];break;
2742 pixelRatio = [12, 11];break;
2744 pixelRatio = [10, 11];break;
2746 pixelRatio = [16, 11];break;
2748 pixelRatio = [40, 33];break;
2750 pixelRatio = [24, 11];break;
2752 pixelRatio = [20, 11];break;
2754 pixelRatio = [32, 11];break;
2756 pixelRatio = [80, 33];break;
2758 pixelRatio = [18, 11];break;
2760 pixelRatio = [15, 11];break;
2762 pixelRatio = [64, 33];break;
2764 pixelRatio = [160, 99];break;
2766 pixelRatio = [4, 3];break;
2768 pixelRatio = [3, 2];break;
2770 pixelRatio = [2, 1];break;
2773 pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
2780 width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
2781 height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
2782 pixelRatio: pixelRatio
2786 ExpGolomb.prototype.readSliceType = function readSliceType() {
2789 // discard first_mb_in_slice
2791 // return slice_type
2792 return this.readUEG();
2798 /* harmony default export */ var exp_golomb = (exp_golomb_ExpGolomb);
2799 // CONCATENATED MODULE: ./src/demux/sample-aes.js
2800 function sample_aes__classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2803 * SAMPLE-AES decrypter
2808 var sample_aes_SampleAesDecrypter = function () {
2809 function SampleAesDecrypter(observer, config, decryptdata, discardEPB) {
2810 sample_aes__classCallCheck(this, SampleAesDecrypter);
2812 this.decryptdata = decryptdata;
2813 this.discardEPB = discardEPB;
2814 this.decrypter = new crypt_decrypter["a" /* default */](observer, config, { removePKCS7Padding: false });
2817 SampleAesDecrypter.prototype.decryptBuffer = function decryptBuffer(encryptedData, callback) {
2818 this.decrypter.decrypt(encryptedData, this.decryptdata.key.buffer, this.decryptdata.iv.buffer, callback);
2821 // AAC - encrypt all full 16 bytes blocks starting from offset 16
2824 SampleAesDecrypter.prototype.decryptAacSample = function decryptAacSample(samples, sampleIndex, callback, sync) {
2825 var curUnit = samples[sampleIndex].unit;
2826 var encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16);
2827 var encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length);
2829 var localthis = this;
2830 this.decryptBuffer(encryptedBuffer, function (decryptedData) {
2831 decryptedData = new Uint8Array(decryptedData);
2832 curUnit.set(decryptedData, 16);
2834 if (!sync) localthis.decryptAacSamples(samples, sampleIndex + 1, callback);
2838 SampleAesDecrypter.prototype.decryptAacSamples = function decryptAacSamples(samples, sampleIndex, callback) {
2839 for (;; sampleIndex++) {
2840 if (sampleIndex >= samples.length) {
2845 if (samples[sampleIndex].unit.length < 32) continue;
2847 var sync = this.decrypter.isSync();
2849 this.decryptAacSample(samples, sampleIndex, callback, sync);
2855 // AVC - encrypt one 16 bytes block out of ten, starting from offset 32
2858 SampleAesDecrypter.prototype.getAvcEncryptedData = function getAvcEncryptedData(decodedData) {
2859 var encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16;
2860 var encryptedData = new Int8Array(encryptedDataLen);
2862 for (var inputPos = 32; inputPos <= decodedData.length - 16; inputPos += 160, outputPos += 16) {
2863 encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos);
2864 }return encryptedData;
2867 SampleAesDecrypter.prototype.getAvcDecryptedUnit = function getAvcDecryptedUnit(decodedData, decryptedData) {
2868 decryptedData = new Uint8Array(decryptedData);
2870 for (var outputPos = 32; outputPos <= decodedData.length - 16; outputPos += 160, inputPos += 16) {
2871 decodedData.set(decryptedData.subarray(inputPos, inputPos + 16), outputPos);
2872 }return decodedData;
2875 SampleAesDecrypter.prototype.decryptAvcSample = function decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync) {
2876 var decodedData = this.discardEPB(curUnit.data);
2877 var encryptedData = this.getAvcEncryptedData(decodedData);
2878 var localthis = this;
2880 this.decryptBuffer(encryptedData.buffer, function (decryptedData) {
2881 curUnit.data = localthis.getAvcDecryptedUnit(decodedData, decryptedData);
2883 if (!sync) localthis.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback);
2887 SampleAesDecrypter.prototype.decryptAvcSamples = function decryptAvcSamples(samples, sampleIndex, unitIndex, callback) {
2888 for (;; sampleIndex++, unitIndex = 0) {
2889 if (sampleIndex >= samples.length) {
2894 var curUnits = samples[sampleIndex].units;
2895 for (;; unitIndex++) {
2896 if (unitIndex >= curUnits.length) break;
2898 var curUnit = curUnits[unitIndex];
2899 if (curUnit.length <= 48 || curUnit.type !== 1 && curUnit.type !== 5) continue;
2901 var sync = this.decrypter.isSync();
2903 this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync);
2910 return SampleAesDecrypter;
2913 /* harmony default export */ var sample_aes = (sample_aes_SampleAesDecrypter);
2914 // CONCATENATED MODULE: ./src/demux/tsdemuxer.js
2915 function tsdemuxer__classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2918 * highly optimized TS demuxer:
2920 * extract PES packet from audio and video PIDs
2921 * extract AVC/H264 NAL units and AAC/ADTS samples from PES packet
2922 * trigger the remuxer upon parsing completion
2923 * it also tries to workaround as best as it can audio codec switch (HE-AAC to AAC and vice versa), without having to restart the MediaSource.
2924 * it also controls the remuxing process :
2925 * upon discontinuity or level switch detection, it will also notifies the remuxer so that it can reset its state.
2933 // import Hex from '../utils/hex';
2937 // We are using fixed track IDs for driving the MP4 remuxer
2938 // instead of following the TS PIDs.
2939 // There is no reason not to do this and some browsers/SourceBuffer-demuxers
2940 // may not like if there are TrackID "switches"
2941 // See https://github.com/video-dev/hls.js/issues/1331
2942 // Here we are mapping our internal track types to constant MP4 track IDs
2943 // With MSE currently one can only have one track of each, and we are muxing
2944 // whatever video/audio rendition in them.
2945 var RemuxerTrackIdConfig = {
2952 var tsdemuxer_TSDemuxer = function () {
2953 function TSDemuxer(observer, remuxer, config, typeSupported) {
2954 tsdemuxer__classCallCheck(this, TSDemuxer);
2956 this.observer = observer;
2957 this.config = config;
2958 this.typeSupported = typeSupported;
2959 this.remuxer = remuxer;
2960 this.sampleAes = null;
2963 TSDemuxer.prototype.setDecryptData = function setDecryptData(decryptdata) {
2964 if (decryptdata != null && decryptdata.key != null && decryptdata.method === 'SAMPLE-AES') this.sampleAes = new sample_aes(this.observer, this.config, decryptdata, this.discardEPB);else this.sampleAes = null;
2967 TSDemuxer.probe = function probe(data) {
2968 var syncOffset = TSDemuxer._syncOffset(data);
2969 if (syncOffset < 0) {
2972 if (syncOffset) logger["b" /* logger */].warn('MPEG2-TS detected but first sync word found @ offset ' + syncOffset + ', junk ahead ?');
2978 TSDemuxer._syncOffset = function _syncOffset(data) {
2979 // scan 1000 first bytes
2980 var scanwindow = Math.min(1000, data.length - 3 * 188);
2982 while (i < scanwindow) {
2983 // a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and one PID, each starting with 0x47
2984 if (data[i] === 0x47 && data[i + 188] === 0x47 && data[i + 2 * 188] === 0x47) return i;else i++;
2990 * Creates a track model internal to demuxer used to drive remuxing input
2992 * @param {string} type 'audio' | 'video' | 'id3' | 'text'
2993 * @param {number} duration
2994 * @return {object} TSDemuxer's internal track model
2998 TSDemuxer.createTrack = function createTrack(type, duration) {
3000 container: type === 'video' || type === 'audio' ? 'video/mp2t' : undefined,
3002 id: RemuxerTrackIdConfig[type],
3004 inputTimeScale: 90000,
3008 dropped: type === 'video' ? 0 : undefined,
3009 isAAC: type === 'audio' ? true : undefined,
3010 duration: type === 'audio' ? duration : undefined
3015 * Initializes a new init segment on the demuxer/remuxer interface. Needed for discontinuities/track-switches (or at stream start)
3016 * Resets all internal track instances of the demuxer.
3018 * @override Implements generic demuxing/remuxing interface (see DemuxerInline)
3019 * @param {object} initSegment
3020 * @param {string} audioCodec
3021 * @param {string} videoCodec
3022 * @param {number} duration (in TS timescale = 90kHz)
3026 TSDemuxer.prototype.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
3027 this.pmtParsed = false;
3030 this._avcTrack = TSDemuxer.createTrack('video', duration);
3031 this._audioTrack = TSDemuxer.createTrack('audio', duration);
3032 this._id3Track = TSDemuxer.createTrack('id3', duration);
3033 this._txtTrack = TSDemuxer.createTrack('text', duration);
3035 // flush any partial content
3036 this.aacOverFlow = null;
3037 this.aacLastPTS = null;
3038 this.avcSample = null;
3039 this.audioCodec = audioCodec;
3040 this.videoCodec = videoCodec;
3041 this._duration = duration;
3050 TSDemuxer.prototype.resetTimeStamp = function resetTimeStamp() {};
3052 // feed incoming data to the front of the parsing pipeline
3055 TSDemuxer.prototype.append = function append(data, timeOffset, contiguous, accurateTimeOffset) {
3063 unknownPIDs = false;
3064 this.contiguous = contiguous;
3065 var pmtParsed = this.pmtParsed,
3066 avcTrack = this._avcTrack,
3067 audioTrack = this._audioTrack,
3068 id3Track = this._id3Track,
3069 avcId = avcTrack.pid,
3070 audioId = audioTrack.pid,
3071 id3Id = id3Track.pid,
3072 pmtId = this._pmtId,
3073 avcData = avcTrack.pesData,
3074 audioData = audioTrack.pesData,
3075 id3Data = id3Track.pesData,
3076 parsePAT = this._parsePAT,
3077 parsePMT = this._parsePMT,
3078 parsePES = this._parsePES,
3079 parseAVCPES = this._parseAVCPES.bind(this),
3080 parseAACPES = this._parseAACPES.bind(this),
3081 parseMPEGPES = this._parseMPEGPES.bind(this),
3082 parseID3PES = this._parseID3PES.bind(this);
3084 var syncOffset = TSDemuxer._syncOffset(data);
3086 // don't parse last TS packet if incomplete
3087 len -= (len + syncOffset) % 188;
3089 // loop through TS packets
3090 for (start = syncOffset; start < len; start += 188) {
3091 if (data[start] === 0x47) {
3092 stt = !!(data[start + 1] & 0x40);
3093 // pid is a 13-bit field starting at the last bit of TS[1]
3094 pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2];
3095 atf = (data[start + 3] & 0x30) >> 4;
3096 // if an adaption field is present, its length is specified by the fifth byte of the TS packet header.
3098 offset = start + 5 + data[start + 4];
3099 // continue if there is only adaptation field
3100 if (offset === start + 188) continue;
3107 if (avcData && (pes = parsePES(avcData)) && pes.pts !== undefined) parseAVCPES(pes, false);
3109 avcData = { data: [], size: 0 };
3112 avcData.data.push(data.subarray(offset, start + 188));
3113 avcData.size += start + 188 - offset;
3118 if (audioData && (pes = parsePES(audioData)) && pes.pts !== undefined) {
3119 if (audioTrack.isAAC) parseAACPES(pes);else parseMPEGPES(pes);
3121 audioData = { data: [], size: 0 };
3124 audioData.data.push(data.subarray(offset, start + 188));
3125 audioData.size += start + 188 - offset;
3130 if (id3Data && (pes = parsePES(id3Data)) && pes.pts !== undefined) parseID3PES(pes);
3132 id3Data = { data: [], size: 0 };
3135 id3Data.data.push(data.subarray(offset, start + 188));
3136 id3Data.size += start + 188 - offset;
3140 if (stt) offset += data[offset] + 1;
3142 pmtId = this._pmtId = parsePAT(data, offset);
3145 if (stt) offset += data[offset] + 1;
3147 var parsedPIDs = parsePMT(data, offset, this.typeSupported.mpeg === true || this.typeSupported.mp3 === true, this.sampleAes != null);
3149 // only update track id if track PID found while parsing PMT
3150 // this is to avoid resetting the PID to -1 in case
3151 // track PID transiently disappears from the stream
3152 // this could happen in case of transient missing audio samples for example
3153 // NOTE this is only the PID of the track as found in TS,
3154 // but we are not using this for MP4 track IDs.
3155 avcId = parsedPIDs.avc;
3156 if (avcId > 0) avcTrack.pid = avcId;
3158 audioId = parsedPIDs.audio;
3160 audioTrack.pid = audioId;
3161 audioTrack.isAAC = parsedPIDs.isAAC;
3163 id3Id = parsedPIDs.id3;
3164 if (id3Id > 0) id3Track.pid = id3Id;
3166 if (unknownPIDs && !pmtParsed) {
3167 logger["b" /* logger */].log('reparse from beginning');
3168 unknownPIDs = false;
3169 // we set it to -188, the += 188 in the for loop will reset start to 0
3170 start = syncOffset - 188;
3172 pmtParsed = this.pmtParsed = true;
3182 this.observer.trigger(events["a" /* default */].ERROR, { type: errors["b" /* ErrorTypes */].MEDIA_ERROR, details: errors["a" /* ErrorDetails */].FRAG_PARSING_ERROR, fatal: false, reason: 'TS packet did not start with 0x47' });
3185 // try to parse last PES packets
3186 if (avcData && (pes = parsePES(avcData)) && pes.pts !== undefined) {
3187 parseAVCPES(pes, true);
3188 avcTrack.pesData = null;
3190 // either avcData null or PES truncated, keep it for next frag parsing
3191 avcTrack.pesData = avcData;
3194 if (audioData && (pes = parsePES(audioData)) && pes.pts !== undefined) {
3195 if (audioTrack.isAAC) parseAACPES(pes);else parseMPEGPES(pes);
3197 audioTrack.pesData = null;
3199 if (audioData && audioData.size) logger["b" /* logger */].log('last AAC PES packet truncated,might overlap between fragments');
3201 // either audioData null or PES truncated, keep it for next frag parsing
3202 audioTrack.pesData = audioData;
3205 if (id3Data && (pes = parsePES(id3Data)) && pes.pts !== undefined) {
3207 id3Track.pesData = null;
3209 // either id3Data null or PES truncated, keep it for next frag parsing
3210 id3Track.pesData = id3Data;
3213 if (this.sampleAes == null) this.remuxer.remux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset);else this.decryptAndRemux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset);
3216 TSDemuxer.prototype.decryptAndRemux = function decryptAndRemux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
3217 if (audioTrack.samples && audioTrack.isAAC) {
3218 var localthis = this;
3219 this.sampleAes.decryptAacSamples(audioTrack.samples, 0, function () {
3220 localthis.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
3223 this.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
3227 TSDemuxer.prototype.decryptAndRemuxAvc = function decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
3228 if (videoTrack.samples) {
3229 var localthis = this;
3230 this.sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, function () {
3231 localthis.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
3234 this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
3238 TSDemuxer.prototype.destroy = function destroy() {
3239 this._initPTS = this._initDTS = undefined;
3243 TSDemuxer.prototype._parsePAT = function _parsePAT(data, offset) {
3244 // skip the PSI header and parse the first PMT entry
3245 return (data[offset + 10] & 0x1F) << 8 | data[offset + 11];
3246 // logger.log('PMT PID:' + this._pmtId);
3249 TSDemuxer.prototype._parsePMT = function _parsePMT(data, offset, mpegSupported, isSampleAes) {
3250 var sectionLength = void 0,
3252 programInfoLength = void 0,
3254 result = { audio: -1, avc: -1, id3: -1, isAAC: true };
3255 sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2];
3256 tableEnd = offset + 3 + sectionLength - 4;
3257 // to determine where the table is, we have to figure out how
3258 // long the program info descriptors are
3259 programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11];
3260 // advance the offset to the first entry in the mapping table
3261 offset += 12 + programInfoLength;
3262 while (offset < tableEnd) {
3263 pid = (data[offset + 1] & 0x1F) << 8 | data[offset + 2];
3264 switch (data[offset]) {
3268 logger["b" /* logger */].log('unkown stream type:' + data[offset]);
3273 // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio)
3275 // logger.log('AAC PID:' + pid);
3276 if (result.audio === -1) result.audio = pid;
3280 // Packetized metadata (ID3)
3282 // logger.log('ID3 PID:' + pid);
3283 if (result.id3 === -1) result.id3 = pid;
3290 logger["b" /* logger */].log('unkown stream type:' + data[offset]);
3295 // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video)
3297 // logger.log('AVC PID:' + pid);
3298 if (result.avc === -1) result.avc = pid;
3302 // ISO/IEC 11172-3 (MPEG-1 audio)
3303 // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio)
3306 // logger.log('MPEG PID:' + pid);
3307 if (!mpegSupported) {
3308 logger["b" /* logger */].log('MPEG audio found, not supported in this browser for now');
3309 } else if (result.audio === -1) {
3311 result.isAAC = false;
3316 logger["b" /* logger */].warn('HEVC stream type found, not supported for now');
3320 logger["b" /* logger */].log('unkown stream type:' + data[offset]);
3323 // move to the next table entry
3324 // skip past the elementary stream descriptors, if present
3325 offset += ((data[offset + 3] & 0x0F) << 8 | data[offset + 4]) + 5;
3330 TSDemuxer.prototype._parsePES = function _parsePES(stream) {
3340 payloadStartOffset = void 0,