-
-
Save Xe/fadf1100a8152a3f328fc07522cf8176 to your computer and use it in GitHub Desktop.
stream.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
main { | |
font-family: monospace, monospace; | |
max-width: 600px; | |
margin: auto; | |
} | |
@media only screen and (max-device-width: 736px) { | |
main { | |
padding: 0rem; | |
} | |
video { | |
max-width: 100%; | |
max-height: 240px; | |
} | |
} | |
::selection { | |
background: #d3869b; | |
} | |
body { | |
background: #282828; | |
color: #ebdbb2; | |
} | |
pre { | |
background-color: #3c3836; | |
padding: 1em; | |
border: 0; | |
} | |
a, | |
a:active, | |
a:visited { | |
color: #b16286; | |
background-color: #1d2021; | |
} | |
h1, | |
h2, | |
h3, | |
h4, | |
h5 { | |
margin-bottom: .1rem; | |
} | |
blockquote { | |
border-left: 1px solid #bdae93; | |
margin: 0.5em 10px; | |
padding: 0.5em 10px; | |
} | |
footer { | |
align: center; | |
} | |
@media (prefers-color-scheme: light) { | |
body { | |
background: #fbf1c7; | |
color: #3c3836; | |
} | |
pre { | |
background-color: #ebdbb2; | |
padding: 1em; | |
border: 0; | |
} | |
a, | |
a:active, | |
a:visited { | |
color: #b16286; | |
background-color: #f9f5d7; | |
} | |
h1, | |
h2, | |
h3, | |
h4, | |
h5 { | |
margin-bottom: .1rem; | |
} | |
blockquote { | |
border-left: 1px solid #655c54; | |
margin: 0.5em 10px; | |
padding: 0.5em 10px; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>Streams from Within</title> | |
<link rel="stylesheet" href="/gruvbox.css"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
</head> | |
<body> | |
<main> | |
<script src="stream_hls.js"></script> | |
<h1>Streams from Within</h1> | |
<p><a href="https://discord.gg/xqz2Eb8">For chat click here</a></p> | |
</main> | |
<center> | |
<video height="600" id="video" controls></video> | |
</center> | |
<script> | |
var video = document.getElementById('video'); | |
if (screen.width <= 736) { | |
video.src = "/live/cadey.m3u8"; | |
} | |
if (Hls.isSupported()) { | |
var hls = new Hls(); | |
hls.loadSource('/hls/cadey.m3u8'); | |
hls.attachMedia(video); | |
hls.on(Hls.Events.MANIFEST_PARSED, function() { | |
video.play(); | |
console.log("hls"); | |
}); | |
} | |
// hls.js is not supported on platforms that do not have Media Source Extensions (MSE) enabled. | |
// When the browser has built-in HLS support (check using `canPlayType`), we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video element throught the `src` property. | |
// This is using the built-in support of the plain video element, without using hls.js. | |
else if (video.canPlayType('application/vnd.apple.mpegurl')) { | |
console.log("live"); | |
video.src = '/live/cadey.m3u8'; | |
video.addEventListener('canplay', function() { | |
video.play(); | |
}); | |
} | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
typeof window !== "undefined" && | |
(function webpackUniversalModuleDefinition(root, factory) { | |
if(typeof exports === 'object' && typeof module === 'object') | |
module.exports = factory(); | |
else if(typeof define === 'function' && define.amd) | |
define([], factory); | |
else if(typeof exports === 'object') | |
exports["Hls"] = factory(); | |
else | |
root["Hls"] = factory(); | |
})(this, function() { | |
return /******/ (function(modules) { // webpackBootstrap | |
/******/ // The module cache | |
/******/ var installedModules = {}; | |
/******/ | |
/******/ // The require function | |
/******/ function __webpack_require__(moduleId) { | |
/******/ | |
/******/ // Check if module is in cache | |
/******/ if(installedModules[moduleId]) { | |
/******/ return installedModules[moduleId].exports; | |
/******/ } | |
/******/ // Create a new module (and put it into the cache) | |
/******/ var module = installedModules[moduleId] = { | |
/******/ i: moduleId, | |
/******/ l: false, | |
/******/ exports: {} | |
/******/ }; | |
/******/ | |
/******/ // Execute the module function | |
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | |
/******/ | |
/******/ // Flag the module as loaded | |
/******/ module.l = true; | |
/******/ | |
/******/ // Return the exports of the module | |
/******/ return module.exports; | |
/******/ } | |
/******/ | |
/******/ | |
/******/ // expose the modules object (__webpack_modules__) | |
/******/ __webpack_require__.m = modules; | |
/******/ | |
/******/ // expose the module cache | |
/******/ __webpack_require__.c = installedModules; | |
/******/ | |
/******/ // define getter function for harmony exports | |
/******/ __webpack_require__.d = function(exports, name, getter) { | |
/******/ if(!__webpack_require__.o(exports, name)) { | |
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); | |
/******/ } | |
/******/ }; | |
/******/ | |
/******/ // define __esModule on exports | |
/******/ __webpack_require__.r = function(exports) { | |
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { | |
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | |
/******/ } | |
/******/ Object.defineProperty(exports, '__esModule', { value: true }); | |
/******/ }; | |
/******/ | |
/******/ // create a fake namespace object | |
/******/ // mode & 1: value is a module id, require it | |
/******/ // mode & 2: merge all properties of value into the ns | |
/******/ // mode & 4: return value when already ns object | |
/******/ // mode & 8|1: behave like require | |
/******/ __webpack_require__.t = function(value, mode) { | |
/******/ if(mode & 1) value = __webpack_require__(value); | |
/******/ if(mode & 8) return value; | |
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; | |
/******/ var ns = Object.create(null); | |
/******/ __webpack_require__.r(ns); | |
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); | |
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); | |
/******/ return ns; | |
/******/ }; | |
/******/ | |
/******/ // getDefaultExport function for compatibility with non-harmony modules | |
/******/ __webpack_require__.n = function(module) { | |
/******/ var getter = module && module.__esModule ? | |
/******/ function getDefault() { return module['default']; } : | |
/******/ function getModuleExports() { return module; }; | |
/******/ __webpack_require__.d(getter, 'a', getter); | |
/******/ return getter; | |
/******/ }; | |
/******/ | |
/******/ // Object.prototype.hasOwnProperty.call | |
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; | |
/******/ | |
/******/ // __webpack_public_path__ | |
/******/ __webpack_require__.p = "/dist/"; | |
/******/ | |
/******/ | |
/******/ // Load entry module and return exports | |
/******/ return __webpack_require__(__webpack_require__.s = "./src/hls.ts"); | |
/******/ }) | |
/************************************************************************/ | |
/******/ ({ | |
/***/ "./node_modules/eventemitter3/index.js": | |
/*!*********************************************!*\ | |
!*** ./node_modules/eventemitter3/index.js ***! | |
\*********************************************/ | |
/*! no static exports found */ | |
/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ | |
/***/ (function(module, exports, __webpack_require__) { | |
"use strict"; | |
var has = Object.prototype.hasOwnProperty | |
, prefix = '~'; | |
/** | |
* Constructor to create a storage for our `EE` objects. | |
* An `Events` instance is a plain object whose properties are event names. | |
* | |
* @constructor | |
* @private | |
*/ | |
function Events() {} | |
// | |
// We try to not inherit from `Object.prototype`. In some engines creating an | |
// instance in this way is faster than calling `Object.create(null)` directly. | |
// If `Object.create(null)` is not supported we prefix the event names with a | |
// character to make sure that the built-in object properties are not | |
// overridden or used as an attack vector. | |
// | |
if (Object.create) { | |
Events.prototype = Object.create(null); | |
// | |
// This hack is needed because the `__proto__` property is still inherited in | |
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. | |
// | |
if (!new Events().__proto__) prefix = false; | |
} | |
/** | |
* Representation of a single event listener. | |
* | |
* @param {Function} fn The listener function. | |
* @param {*} context The context to invoke the listener with. | |
* @param {Boolean} [once=false] Specify if the listener is a one-time listener. | |
* @constructor | |
* @private | |
*/ | |
function EE(fn, context, once) { | |
this.fn = fn; | |
this.context = context; | |
this.once = once || false; | |
} | |
/** | |
* Add a listener for a given event. | |
* | |
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance. | |
* @param {(String|Symbol)} event The event name. | |
* @param {Function} fn The listener function. | |
* @param {*} context The context to invoke the listener with. | |
* @param {Boolean} once Specify if the listener is a one-time listener. | |
* @returns {EventEmitter} | |
* @private | |
*/ | |
function addListener(emitter, event, fn, context, once) { | |
if (typeof fn !== 'function') { | |
throw new TypeError('The listener must be a function'); | |
} | |
var listener = new EE(fn, context || emitter, once) | |
, evt = prefix ? prefix + event : event; | |
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; | |
else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); | |
else emitter._events[evt] = [emitter._events[evt], listener]; | |
return emitter; | |
} | |
/** | |
* Clear event by name. | |
* | |
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance. | |
* @param {(String|Symbol)} evt The Event name. | |
* @private | |
*/ | |
function clearEvent(emitter, evt) { | |
if (--emitter._eventsCount === 0) emitter._events = new Events(); | |
else delete emitter._events[evt]; | |
} | |
/** | |
* Minimal `EventEmitter` interface that is molded against the Node.js | |
* `EventEmitter` interface. | |
* | |
* @constructor | |
* @public | |
*/ | |
function EventEmitter() { | |
this._events = new Events(); | |
this._eventsCount = 0; | |
} | |
/** | |
* Return an array listing the events for which the emitter has registered | |
* listeners. | |
* | |
* @returns {Array} | |
* @public | |
*/ | |
EventEmitter.prototype.eventNames = function eventNames() { | |
var names = [] | |
, events | |
, name; | |
if (this._eventsCount === 0) return names; | |
for (name in (events = this._events)) { | |
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); | |
} | |
if (Object.getOwnPropertySymbols) { | |
return names.concat(Object.getOwnPropertySymbols(events)); | |
} | |
return names; | |
}; | |
/** | |
* Return the listeners registered for a given event. | |
* | |
* @param {(String|Symbol)} event The event name. | |
* @returns {Array} The registered listeners. | |
* @public | |
*/ | |
EventEmitter.prototype.listeners = function listeners(event) { | |
var evt = prefix ? prefix + event : event | |
, handlers = this._events[evt]; | |
if (!handlers) return []; | |
if (handlers.fn) return [handlers.fn]; | |
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { | |
ee[i] = handlers[i].fn; | |
} | |
return ee; | |
}; | |
/** | |
* Return the number of listeners listening to a given event. | |
* | |
* @param {(String|Symbol)} event The event name. | |
* @returns {Number} The number of listeners. | |
* @public | |
*/ | |
EventEmitter.prototype.listenerCount = function listenerCount(event) { | |
var evt = prefix ? prefix + event : event | |
, listeners = this._events[evt]; | |
if (!listeners) return 0; | |
if (listeners.fn) return 1; | |
return listeners.length; | |
}; | |
/** | |
* Calls each of the listeners registered for a given event. | |
* | |
* @param {(String|Symbol)} event The event name. | |
* @returns {Boolean} `true` if the event had listeners, else `false`. | |
* @public | |
*/ | |
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { | |
var evt = prefix ? prefix + event : event; | |
if (!this._events[evt]) return false; | |
var listeners = this._events[evt] | |
, len = arguments.length | |
, args | |
, i; | |
if (listeners.fn) { | |
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); | |
switch (len) { | |
case 1: return listeners.fn.call(listeners.context), true; | |
case 2: return listeners.fn.call(listeners.context, a1), true; | |
case 3: return listeners.fn.call(listeners.context, a1, a2), true; | |
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; | |
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; | |
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; | |
} | |
for (i = 1, args = new Array(len -1); i < len; i++) { | |
args[i - 1] = arguments[i]; | |
} | |
listeners.fn.apply(listeners.context, args); | |
} else { | |
var length = listeners.length | |
, j; | |
for (i = 0; i < length; i++) { | |
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); | |
switch (len) { | |
case 1: listeners[i].fn.call(listeners[i].context); break; | |
case 2: listeners[i].fn.call(listeners[i].context, a1); break; | |
case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; | |
case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; | |
default: | |
if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { | |
args[j - 1] = arguments[j]; | |
} | |
listeners[i].fn.apply(listeners[i].context, args); | |
} | |
} | |
} | |
return true; | |
}; | |
/** | |
* Add a listener for a given event. | |
* | |
* @param {(String|Symbol)} event The event name. | |
* @param {Function} fn The listener function. | |
* @param {*} [context=this] The context to invoke the listener with. | |
* @returns {EventEmitter} `this`. | |
* @public | |
*/ | |
EventEmitter.prototype.on = function on(event, fn, context) { | |
return addListener(this, event, fn, context, false); | |
}; | |
/** | |
* Add a one-time listener for a given event. | |
* | |
* @param {(String|Symbol)} event The event name. | |
* @param {Function} fn The listener function. | |
* @param {*} [context=this] The context to invoke the listener with. | |
* @returns {EventEmitter} `this`. | |
* @public | |
*/ | |
EventEmitter.prototype.once = function once(event, fn, context) { | |
return addListener(this, event, fn, context, true); | |
}; | |
/** | |
* Remove the listeners of a given event. | |
* | |
* @param {(String|Symbol)} event The event name. | |
* @param {Function} fn Only remove the listeners that match this function. | |
* @param {*} context Only remove the listeners that have this context. | |
* @param {Boolean} once Only remove one-time listeners. | |
* @returns {EventEmitter} `this`. | |
* @public | |
*/ | |
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { | |
var evt = prefix ? prefix + event : event; | |
if (!this._events[evt]) return this; | |
if (!fn) { | |
clearEvent(this, evt); | |
return this; | |
} | |
var listeners = this._events[evt]; | |
if (listeners.fn) { | |
if ( | |
listeners.fn === fn && | |
(!once || listeners.once) && | |
(!context || listeners.context === context) | |
) { | |
clearEvent(this, evt); | |
} | |
} else { | |
for (var i = 0, events = [], length = listeners.length; i < length; i++) { | |
if ( | |
listeners[i].fn !== fn || | |
(once && !listeners[i].once) || | |
(context && listeners[i].context !== context) | |
) { | |
events.push(listeners[i]); | |
} | |
} | |
// | |
// Reset the array, or remove it completely if we have no more listeners. | |
// | |
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; | |
else clearEvent(this, evt); | |
} | |
return this; | |
}; | |
/** | |
* Remove all listeners, or those of the specified event. | |
* | |
* @param {(String|Symbol)} [event] The event name. | |
* @returns {EventEmitter} `this`. | |
* @public | |
*/ | |
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { | |
var evt; | |
if (event) { | |
evt = prefix ? prefix + event : event; | |
if (this._events[evt]) clearEvent(this, evt); | |
} else { | |
this._events = new Events(); | |
this._eventsCount = 0; | |
} | |
return this; | |
}; | |
// | |
// Alias methods names because people roll like that. | |
// | |
EventEmitter.prototype.off = EventEmitter.prototype.removeListener; | |
EventEmitter.prototype.addListener = EventEmitter.prototype.on; | |
// | |
// Expose the prefix. | |
// | |
EventEmitter.prefixed = prefix; | |
// | |
// Allow `EventEmitter` to be imported as module namespace. | |
// | |
EventEmitter.EventEmitter = EventEmitter; | |
// | |
// Expose the module. | |
// | |
if (true) { | |
module.exports = EventEmitter; | |
} | |
/***/ }), | |
/***/ "./node_modules/url-toolkit/src/url-toolkit.js": | |
/*!*****************************************************!*\ | |
!*** ./node_modules/url-toolkit/src/url-toolkit.js ***! | |
\*****************************************************/ | |
/*! no static exports found */ | |
/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ | |
/***/ (function(module, exports, __webpack_require__) { | |
// see https://tools.ietf.org/html/rfc1808 | |
/* jshint ignore:start */ | |
(function(root) { | |
/* jshint ignore:end */ | |
var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/\?#]*\/)*.*?)??(;.*?)?(\?.*?)?(#.*?)?$/; | |
var FIRST_SEGMENT_REGEX = /^([^\/?#]*)(.*)$/; | |
var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g; | |
var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g; | |
var URLToolkit = { // jshint ignore:line | |
// If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or // | |
// E.g | |
// With opts.alwaysNormalize = false (default, spec compliant) | |
// http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g | |
// With opts.alwaysNormalize = true (not spec compliant) | |
// http://a.com/b/cd + /e/f/../g => http://a.com/e/g | |
buildAbsoluteURL: function(baseURL, relativeURL, opts) { | |
opts = opts || {}; | |
// remove any remaining space and CRLF | |
baseURL = baseURL.trim(); | |
relativeURL = relativeURL.trim(); | |
if (!relativeURL) { | |
// 2a) If the embedded URL is entirely empty, it inherits the | |
// entire base URL (i.e., is set equal to the base URL) | |
// and we are done. | |
if (!opts.alwaysNormalize) { | |
return baseURL; | |
} | |
var basePartsForNormalise = URLToolkit.parseURL(baseURL); | |
if (!basePartsForNormalise) { | |
throw new Error('Error trying to parse base URL.'); | |
} | |
basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path); | |
return URLToolkit.buildURLFromParts(basePartsForNormalise); | |
} | |
var relativeParts = URLToolkit.parseURL(relativeURL); | |
if (!relativeParts) { | |
throw new Error('Error trying to parse relative URL.'); | |
} | |
if (relativeParts.scheme) { | |
// 2b) If the embedded URL starts with a scheme name, it is | |
// interpreted as an absolute URL and we are done. | |
if (!opts.alwaysNormalize) { | |
return relativeURL; | |
} | |
relativeParts.path = URLToolkit.normalizePath(relativeParts.path); | |
return URLToolkit.buildURLFromParts(relativeParts); | |
} | |
var baseParts = URLToolkit.parseURL(baseURL); | |
if (!baseParts) { | |
throw new Error('Error trying to parse base URL.'); | |
} | |
if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') { | |
// If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc | |
// This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a' | |
var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path); | |
baseParts.netLoc = pathParts[1]; | |
baseParts.path = pathParts[2]; | |
} | |
if (baseParts.netLoc && !baseParts.path) { | |
baseParts.path = '/'; | |
} | |
var builtParts = { | |
// 2c) Otherwise, the embedded URL inherits the scheme of | |
// the base URL. | |
scheme: baseParts.scheme, | |
netLoc: relativeParts.netLoc, | |
path: null, | |
params: relativeParts.params, | |
query: relativeParts.query, | |
fragment: relativeParts.fragment | |
}; | |
if (!relativeParts.netLoc) { | |
// 3) If the embedded URL's <net_loc> is non-empty, we skip to | |
// Step 7. Otherwise, the embedded URL inherits the <net_loc> | |
// (if any) of the base URL. | |
builtParts.netLoc = baseParts.netLoc; | |
// 4) If the embedded URL path is preceded by a slash "/", the | |
// path is not relative and we skip to Step 7. | |
if (relativeParts.path[0] !== '/') { | |
if (!relativeParts.path) { | |
// 5) If the embedded URL path is empty (and not preceded by a | |
// slash), then the embedded URL inherits the base URL path | |
builtParts.path = baseParts.path; | |
// 5a) if the embedded URL's <params> is non-empty, we skip to | |
// step 7; otherwise, it inherits the <params> of the base | |
// URL (if any) and | |
if (!relativeParts.params) { | |
builtParts.params = baseParts.params; | |
// 5b) if the embedded URL's <query> is non-empty, we skip to | |
// step 7; otherwise, it inherits the <query> of the base | |
// URL (if any) and we skip to step 7. | |
if (!relativeParts.query) { | |
builtParts.query = baseParts.query; | |
} | |
} | |
} else { | |
// 6) The last segment of the base URL's path (anything | |
// following the rightmost slash "/", or the entire path if no | |
// slash is present) is removed and the embedded URL's path is | |
// appended in its place. | |
var baseURLPath = baseParts.path; | |
var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path; | |
builtParts.path = URLToolkit.normalizePath(newPath); | |
} | |
} | |
} | |
if (builtParts.path === null) { | |
builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path; | |
} | |
return URLToolkit.buildURLFromParts(builtParts); | |
}, | |
parseURL: function(url) { | |
var parts = URL_REGEX.exec(url); | |
if (!parts) { | |
return null; | |
} | |
return { | |
scheme: parts[1] || '', | |
netLoc: parts[2] || '', | |
path: parts[3] || '', | |
params: parts[4] || '', | |
query: parts[5] || '', | |
fragment: parts[6] || '' | |
}; | |
}, | |
normalizePath: function(path) { | |
// The following operations are | |
// then applied, in order, to the new path: | |
// 6a) All occurrences of "./", where "." is a complete path | |
// segment, are removed. | |
// 6b) If the path ends with "." as a complete path segment, | |
// that "." is removed. | |
path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); | |
// 6c) All occurrences of "<segment>/../", where <segment> is a | |
// complete path segment not equal to "..", are removed. | |
// Removal of these path segments is performed iteratively, | |
// removing the leftmost matching pattern on each iteration, | |
// until no matching pattern remains. | |
// 6d) If the path ends with "<segment>/..", where <segment> is a | |
// complete path segment not equal to "..", that | |
// "<segment>/.." is removed. | |
while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line | |
return path.split('').reverse().join(''); | |
}, | |
buildURLFromParts: function(parts) { | |
return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment; | |
} | |
}; | |
/* jshint ignore:start */ | |
if(true) | |
module.exports = URLToolkit; | |
else {} | |
})(this); | |
/* jshint ignore:end */ | |
/***/ }), | |
/***/ "./node_modules/webworkify-webpack/index.js": | |
/*!**************************************************!*\ | |
!*** ./node_modules/webworkify-webpack/index.js ***! | |
\**************************************************/ | |
/*! no static exports found */ | |
/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ | |
/***/ (function(module, exports, __webpack_require__) { | |
function webpackBootstrapFunc (modules) { | |
/******/ // The module cache | |
/******/ var installedModules = {}; | |
/******/ // The require function | |
/******/ function __webpack_require__(moduleId) { | |
/******/ // Check if module is in cache | |
/******/ if(installedModules[moduleId]) | |
/******/ return installedModules[moduleId].exports; | |
/******/ // Create a new module (and put it into the cache) | |
/******/ var module = installedModules[moduleId] = { | |
/******/ i: moduleId, | |
/******/ l: false, | |
/******/ exports: {} | |
/******/ }; | |
/******/ // Execute the module function | |
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | |
/******/ // Flag the module as loaded | |
/******/ module.l = true; | |
/******/ // Return the exports of the module | |
/******/ return module.exports; | |
/******/ } | |
/******/ // expose the modules object (__webpack_modules__) | |
/******/ __webpack_require__.m = modules; | |
/******/ // expose the module cache | |
/******/ __webpack_require__.c = installedModules; | |
/******/ // identity function for calling harmony imports with the correct context | |
/******/ __webpack_require__.i = function(value) { return value; }; | |
/******/ // define getter function for harmony exports | |
/******/ __webpack_require__.d = function(exports, name, getter) { | |
/******/ if(!__webpack_require__.o(exports, name)) { | |
/******/ Object.defineProperty(exports, name, { | |
/******/ configurable: false, | |
/******/ enumerable: true, | |
/******/ get: getter | |
/******/ }); | |
/******/ } | |
/******/ }; | |
/******/ // define __esModule on exports | |
/******/ __webpack_require__.r = function(exports) { | |
/******/ Object.defineProperty(exports, '__esModule', { value: true }); | |
/******/ }; | |
/******/ // getDefaultExport function for compatibility with non-harmony modules | |
/******/ __webpack_require__.n = function(module) { | |
/******/ var getter = module && module.__esModule ? | |
/******/ function getDefault() { return module['default']; } : | |
/******/ function getModuleExports() { return module; }; | |
/******/ __webpack_require__.d(getter, 'a', getter); | |
/******/ return getter; | |
/******/ }; | |
/******/ // Object.prototype.hasOwnProperty.call | |
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; | |
/******/ // __webpack_public_path__ | |
/******/ __webpack_require__.p = "/"; | |
/******/ // on error function for async loading | |
/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; | |
var f = __webpack_require__(__webpack_require__.s = ENTRY_MODULE) | |
return f.default || f // try to call default if defined to also support babel esmodule exports | |
} | |
var moduleNameReqExp = '[\\.|\\-|\\+|\\w|\/|@]+' | |
var dependencyRegExp = '\\(\\s*(\/\\*.*?\\*\/)?\\s*.*?(' + moduleNameReqExp + ').*?\\)' // additional chars when output.pathinfo is true | |
// http://stackoverflow.com/a/2593661/130442 | |
function quoteRegExp (str) { | |
return (str + '').replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&') | |
} | |
function isNumeric(n) { | |
return !isNaN(1 * n); // 1 * n converts integers, integers as string ("123"), 1e3 and "1e3" to integers and strings to NaN | |
} | |
function getModuleDependencies (sources, module, queueName) { | |
var retval = {} | |
retval[queueName] = [] | |
var fnString = module.toString() | |
var wrapperSignature = fnString.match(/^function\s?\w*\(\w+,\s*\w+,\s*(\w+)\)/) | |
if (!wrapperSignature) return retval | |
var webpackRequireName = wrapperSignature[1] | |
// main bundle deps | |
var re = new RegExp('(\\\\n|\\W)' + quoteRegExp(webpackRequireName) + dependencyRegExp, 'g') | |
var match | |
while ((match = re.exec(fnString))) { | |
if (match[3] === 'dll-reference') continue | |
retval[queueName].push(match[3]) | |
} | |
// dll deps | |
re = new RegExp('\\(' + quoteRegExp(webpackRequireName) + '\\("(dll-reference\\s(' + moduleNameReqExp + '))"\\)\\)' + dependencyRegExp, 'g') | |
while ((match = re.exec(fnString))) { | |
if (!sources[match[2]]) { | |
retval[queueName].push(match[1]) | |
sources[match[2]] = __webpack_require__(match[1]).m | |
} | |
retval[match[2]] = retval[match[2]] || [] | |
retval[match[2]].push(match[4]) | |
} | |
// convert 1e3 back to 1000 - this can be important after uglify-js converted 1000 to 1e3 | |
var keys = Object.keys(retval); | |
for (var i = 0; i < keys.length; i++) { | |
for (var j = 0; j < retval[keys[i]].length; j++) { | |
if (isNumeric(retval[keys[i]][j])) { | |
retval[keys[i]][j] = 1 * retval[keys[i]][j]; | |
} | |
} | |
} | |
return retval | |
} | |
function hasValuesInQueues (queues) { | |
var keys = Object.keys(queues) | |
return keys.reduce(function (hasValues, key) { | |
return hasValues || queues[key].length > 0 | |
}, false) | |
} | |
function getRequiredModules (sources, moduleId) { | |
var modulesQueue = { | |
main: [moduleId] | |
} | |
var requiredModules = { | |
main: [] | |
} | |
var seenModules = { | |
main: {} | |
} | |
while (hasValuesInQueues(modulesQueue)) { | |
var queues = Object.keys(modulesQueue) | |
for (var i = 0; i < queues.length; i++) { | |
var queueName = queues[i] | |
var queue = modulesQueue[queueName] | |
var moduleToCheck = queue.pop() | |
seenModules[queueName] = seenModules[queueName] || {} | |
if (seenModules[queueName][moduleToCheck] || !sources[queueName][moduleToCheck]) continue | |
seenModules[queueName][moduleToCheck] = true | |
requiredModules[queueName] = requiredModules[queueName] || [] | |
requiredModules[queueName].push(moduleToCheck) | |
var newModules = getModuleDependencies(sources, sources[queueName][moduleToCheck], queueName) | |
var newModulesKeys = Object.keys(newModules) | |
for (var j = 0; j < newModulesKeys.length; j++) { | |
modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]] || [] | |
modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]].concat(newModules[newModulesKeys[j]]) | |
} | |
} | |
} | |
return requiredModules | |
} | |
module.exports = function (moduleId, options) { | |
options = options || {} | |
var sources = { | |
main: __webpack_require__.m | |
} | |
var requiredModules = options.all ? { main: Object.keys(sources.main) } : getRequiredModules(sources, moduleId) | |
var src = '' | |
Object.keys(requiredModules).filter(function (m) { return m !== 'main' }).forEach(function (module) { | |
var entryModule = 0 | |
while (requiredModules[module][entryModule]) { | |
entryModule++ | |
} | |
requiredModules[module].push(entryModule) | |
sources[module][entryModule] = '(function(module, exports, __webpack_require__) { module.exports = __webpack_require__; })' | |
src = src + 'var ' + module + ' = (' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(entryModule)) + ')({' + requiredModules[module].map(function (id) { return '' + JSON.stringify(id) + ': ' + sources[module][id].toString() }).join(',') + '});\n' | |
}) | |
src = src + 'new ((' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(moduleId)) + ')({' + requiredModules.main.map(function (id) { return '' + JSON.stringify(id) + ': ' + sources.main[id].toString() }).join(',') + '}))(self);' | |
var blob = new window.Blob([src], { type: 'text/javascript' }) | |
if (options.bare) { return blob } | |
var URL = window.URL || window.webkitURL || window.mozURL || window.msURL | |
var workerUrl = URL.createObjectURL(blob) | |
var worker = new window.Worker(workerUrl) | |
worker.objectURL = workerUrl | |
return worker | |
} | |
/***/ }), | |
/***/ "./src/crypt/decrypter.js": | |
/*!********************************************!*\ | |
!*** ./src/crypt/decrypter.js + 3 modules ***! | |
\********************************************/ | |
/*! exports provided: default */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/errors.ts because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/events.js because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/utils/get-self-scope.js because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/utils/logger.js because of ./src/hls.ts */ | |
/***/ (function(module, __webpack_exports__, __webpack_require__) { | |
"use strict"; | |
__webpack_require__.r(__webpack_exports__); | |
// CONCATENATED MODULE: ./src/crypt/aes-crypto.js | |
var AESCrypto = | |
/*#__PURE__*/ | |
function () { | |
function AESCrypto(subtle, iv) { | |
this.subtle = subtle; | |
this.aesIV = iv; | |
} | |
var _proto = AESCrypto.prototype; | |
_proto.decrypt = function decrypt(data, key) { | |
return this.subtle.decrypt({ | |
name: 'AES-CBC', | |
iv: this.aesIV | |
}, key, data); | |
}; | |
return AESCrypto; | |
}(); | |
// CONCATENATED MODULE: ./src/crypt/fast-aes-key.js | |
var FastAESKey = | |
/*#__PURE__*/ | |
function () { | |
function FastAESKey(subtle, key) { | |
this.subtle = subtle; | |
this.key = key; | |
} | |
var _proto = FastAESKey.prototype; | |
_proto.expandKey = function expandKey() { | |
return this.subtle.importKey('raw', this.key, { | |
name: 'AES-CBC' | |
}, false, ['encrypt', 'decrypt']); | |
}; | |
return FastAESKey; | |
}(); | |
/* harmony default export */ var fast_aes_key = (FastAESKey); | |
// CONCATENATED MODULE: ./src/crypt/aes-decryptor.js | |
// PKCS7 | |
function removePadding(buffer) { | |
var outputBytes = buffer.byteLength; | |
var paddingBytes = outputBytes && new DataView(buffer).getUint8(outputBytes - 1); | |
if (paddingBytes) { | |
return buffer.slice(0, outputBytes - paddingBytes); | |
} else { | |
return buffer; | |
} | |
} | |
var AESDecryptor = | |
/*#__PURE__*/ | |
function () { | |
function AESDecryptor() { | |
// Static after running initTable | |
this.rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; | |
this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; | |
this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; | |
this.sBox = new Uint32Array(256); | |
this.invSBox = new Uint32Array(256); // Changes during runtime | |
this.key = new Uint32Array(0); | |
this.initTable(); | |
} // Using view.getUint32() also swaps the byte order. | |
var _proto = AESDecryptor.prototype; | |
_proto.uint8ArrayToUint32Array_ = function uint8ArrayToUint32Array_(arrayBuffer) { | |
var view = new DataView(arrayBuffer); | |
var newArray = new Uint32Array(4); | |
for (var i = 0; i < 4; i++) { | |
newArray[i] = view.getUint32(i * 4); | |
} | |
return newArray; | |
}; | |
_proto.initTable = function initTable() { | |
var sBox = this.sBox; | |
var invSBox = this.invSBox; | |
var subMix = this.subMix; | |
var subMix0 = subMix[0]; | |
var subMix1 = subMix[1]; | |
var subMix2 = subMix[2]; | |
var subMix3 = subMix[3]; | |
var invSubMix = this.invSubMix; | |
var invSubMix0 = invSubMix[0]; | |
var invSubMix1 = invSubMix[1]; | |
var invSubMix2 = invSubMix[2]; | |
var invSubMix3 = invSubMix[3]; | |
var d = new Uint32Array(256); | |
var x = 0; | |
var xi = 0; | |
var i = 0; | |
for (i = 0; i < 256; i++) { | |
if (i < 128) { | |
d[i] = i << 1; | |
} else { | |
d[i] = i << 1 ^ 0x11b; | |
} | |
} | |
for (i = 0; i < 256; i++) { | |
var sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4; | |
sx = sx >>> 8 ^ sx & 0xff ^ 0x63; | |
sBox[x] = sx; | |
invSBox[sx] = x; // Compute multiplication | |
var x2 = d[x]; | |
var x4 = d[x2]; | |
var x8 = d[x4]; // Compute sub/invSub bytes, mix columns tables | |
var t = d[sx] * 0x101 ^ sx * 0x1010100; | |
subMix0[x] = t << 24 | t >>> 8; | |
subMix1[x] = t << 16 | t >>> 16; | |
subMix2[x] = t << 8 | t >>> 24; | |
subMix3[x] = t; // Compute inv sub bytes, inv mix columns tables | |
t = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100; | |
invSubMix0[sx] = t << 24 | t >>> 8; | |
invSubMix1[sx] = t << 16 | t >>> 16; | |
invSubMix2[sx] = t << 8 | t >>> 24; | |
invSubMix3[sx] = t; // Compute next counter | |
if (!x) { | |
x = xi = 1; | |
} else { | |
x = x2 ^ d[d[d[x8 ^ x2]]]; | |
xi ^= d[d[xi]]; | |
} | |
} | |
}; | |
_proto.expandKey = function expandKey(keyBuffer) { | |
// convert keyBuffer to Uint32Array | |
var key = this.uint8ArrayToUint32Array_(keyBuffer); | |
var sameKey = true; | |
var offset = 0; | |
while (offset < key.length && sameKey) { | |
sameKey = key[offset] === this.key[offset]; | |
offset++; | |
} | |
if (sameKey) { | |
return; | |
} | |
this.key = key; | |
var keySize = this.keySize = key.length; | |
if (keySize !== 4 && keySize !== 6 && keySize !== 8) { | |
throw new Error('Invalid aes key size=' + keySize); | |
} | |
var ksRows = this.ksRows = (keySize + 6 + 1) * 4; | |
var ksRow; | |
var invKsRow; | |
var keySchedule = this.keySchedule = new Uint32Array(ksRows); | |
var invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows); | |
var sbox = this.sBox; | |
var rcon = this.rcon; | |
var invSubMix = this.invSubMix; | |
var invSubMix0 = invSubMix[0]; | |
var invSubMix1 = invSubMix[1]; | |
var invSubMix2 = invSubMix[2]; | |
var invSubMix3 = invSubMix[3]; | |
var prev; | |
var t; | |
for (ksRow = 0; ksRow < ksRows; ksRow++) { | |
if (ksRow < keySize) { | |
prev = keySchedule[ksRow] = key[ksRow]; | |
continue; | |
} | |
t = prev; | |
if (ksRow % keySize === 0) { | |
// Rot word | |
t = t << 8 | t >>> 24; // Sub word | |
t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff]; // Mix Rcon | |
t ^= rcon[ksRow / keySize | 0] << 24; | |
} else if (keySize > 6 && ksRow % keySize === 4) { | |
// Sub word | |
t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff]; | |
} | |
keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0; | |
} | |
for (invKsRow = 0; invKsRow < ksRows; invKsRow++) { | |
ksRow = ksRows - invKsRow; | |
if (invKsRow & 3) { | |
t = keySchedule[ksRow]; | |
} else { | |
t = keySchedule[ksRow - 4]; | |
} | |
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]]; | |
} | |
invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0; | |
} | |
} // Adding this as a method greatly improves performance. | |
; | |
_proto.networkToHostOrderSwap = function networkToHostOrderSwap(word) { | |
return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24; | |
}; | |
_proto.decrypt = function decrypt(inputArrayBuffer, offset, aesIV, removePKCS7Padding) { | |
var nRounds = this.keySize + 6; | |
var invKeySchedule = this.invKeySchedule; | |
var invSBOX = this.invSBox; | |
var invSubMix = this.invSubMix; | |
var invSubMix0 = invSubMix[0]; | |
var invSubMix1 = invSubMix[1]; | |
var invSubMix2 = invSubMix[2]; | |
var invSubMix3 = invSubMix[3]; | |
var initVector = this.uint8ArrayToUint32Array_(aesIV); | |
var initVector0 = initVector[0]; | |
var initVector1 = initVector[1]; | |
var initVector2 = initVector[2]; | |
var initVector3 = initVector[3]; | |
var inputInt32 = new Int32Array(inputArrayBuffer); | |
var outputInt32 = new Int32Array(inputInt32.length); | |
var t0, t1, t2, t3; | |
var s0, s1, s2, s3; | |
var inputWords0, inputWords1, inputWords2, inputWords3; | |
var ksRow, i; | |
var swapWord = this.networkToHostOrderSwap; | |
while (offset < inputInt32.length) { | |
inputWords0 = swapWord(inputInt32[offset]); | |
inputWords1 = swapWord(inputInt32[offset + 1]); | |
inputWords2 = swapWord(inputInt32[offset + 2]); | |
inputWords3 = swapWord(inputInt32[offset + 3]); | |
s0 = inputWords0 ^ invKeySchedule[0]; | |
s1 = inputWords3 ^ invKeySchedule[1]; | |
s2 = inputWords2 ^ invKeySchedule[2]; | |
s3 = inputWords1 ^ invKeySchedule[3]; | |
ksRow = 4; // Iterate through the rounds of decryption | |
for (i = 1; i < nRounds; i++) { | |
t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 0xff] ^ invSubMix2[s2 >> 8 & 0xff] ^ invSubMix3[s3 & 0xff] ^ invKeySchedule[ksRow]; | |
t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 0xff] ^ invSubMix2[s3 >> 8 & 0xff] ^ invSubMix3[s0 & 0xff] ^ invKeySchedule[ksRow + 1]; | |
t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 0xff] ^ invSubMix2[s0 >> 8 & 0xff] ^ invSubMix3[s1 & 0xff] ^ invKeySchedule[ksRow + 2]; | |
t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 0xff] ^ invSubMix2[s1 >> 8 & 0xff] ^ invSubMix3[s2 & 0xff] ^ invKeySchedule[ksRow + 3]; // Update state | |
s0 = t0; | |
s1 = t1; | |
s2 = t2; | |
s3 = t3; | |
ksRow = ksRow + 4; | |
} // Shift rows, sub bytes, add round key | |
t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 0xff] << 16 ^ invSBOX[s2 >> 8 & 0xff] << 8 ^ invSBOX[s3 & 0xff] ^ invKeySchedule[ksRow]; | |
t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 0xff] << 16 ^ invSBOX[s3 >> 8 & 0xff] << 8 ^ invSBOX[s0 & 0xff] ^ invKeySchedule[ksRow + 1]; | |
t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 0xff] << 16 ^ invSBOX[s0 >> 8 & 0xff] << 8 ^ invSBOX[s1 & 0xff] ^ invKeySchedule[ksRow + 2]; | |
t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 0xff] << 16 ^ invSBOX[s1 >> 8 & 0xff] << 8 ^ invSBOX[s2 & 0xff] ^ invKeySchedule[ksRow + 3]; | |
ksRow = ksRow + 3; // Write | |
outputInt32[offset] = swapWord(t0 ^ initVector0); | |
outputInt32[offset + 1] = swapWord(t3 ^ initVector1); | |
outputInt32[offset + 2] = swapWord(t2 ^ initVector2); | |
outputInt32[offset + 3] = swapWord(t1 ^ initVector3); // reset initVector to last 4 unsigned int | |
initVector0 = inputWords0; | |
initVector1 = inputWords1; | |
initVector2 = inputWords2; | |
initVector3 = inputWords3; | |
offset = offset + 4; | |
} | |
return removePKCS7Padding ? removePadding(outputInt32.buffer) : outputInt32.buffer; | |
}; | |
_proto.destroy = function destroy() { | |
this.key = undefined; | |
this.keySize = undefined; | |
this.ksRows = undefined; | |
this.sBox = undefined; | |
this.invSBox = undefined; | |
this.subMix = undefined; | |
this.invSubMix = undefined; | |
this.keySchedule = undefined; | |
this.invKeySchedule = undefined; | |
this.rcon = undefined; | |
}; | |
return AESDecryptor; | |
}(); | |
/* harmony default export */ var aes_decryptor = (AESDecryptor); | |
// EXTERNAL MODULE: ./src/errors.ts | |
var errors = __webpack_require__("./src/errors.ts"); | |
// EXTERNAL MODULE: ./src/utils/logger.js | |
var logger = __webpack_require__("./src/utils/logger.js"); | |
// EXTERNAL MODULE: ./src/events.js | |
var events = __webpack_require__("./src/events.js"); | |
// EXTERNAL MODULE: ./src/utils/get-self-scope.js | |
var get_self_scope = __webpack_require__("./src/utils/get-self-scope.js"); | |
// CONCATENATED MODULE: ./src/crypt/decrypter.js | |
// see https://stackoverflow.com/a/11237259/589493 | |
var global = Object(get_self_scope["getSelfScope"])(); // safeguard for code that might run both on worker and main thread | |
var decrypter_Decrypter = | |
/*#__PURE__*/ | |
function () { | |
function Decrypter(observer, config, _temp) { | |
var _ref = _temp === void 0 ? {} : _temp, | |
_ref$removePKCS7Paddi = _ref.removePKCS7Padding, | |
removePKCS7Padding = _ref$removePKCS7Paddi === void 0 ? true : _ref$removePKCS7Paddi; | |
this.logEnabled = true; | |
this.observer = observer; | |
this.config = config; | |
this.removePKCS7Padding = removePKCS7Padding; // built in decryptor expects PKCS7 padding | |
if (removePKCS7Padding) { | |
try { | |
var browserCrypto = global.crypto; | |
if (browserCrypto) { | |
this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle; | |
} | |
} catch (e) {} | |
} | |
this.disableWebCrypto = !this.subtle; | |
} | |
var _proto = Decrypter.prototype; | |
_proto.isSync = function isSync() { | |
return this.disableWebCrypto && this.config.enableSoftwareAES; | |
}; | |
_proto.decrypt = function decrypt(data, key, iv, callback) { | |
var _this = this; | |
if (this.disableWebCrypto && this.config.enableSoftwareAES) { | |
if (this.logEnabled) { | |
logger["logger"].log('JS AES decrypt'); | |
this.logEnabled = false; | |
} | |
var decryptor = this.decryptor; | |
if (!decryptor) { | |
this.decryptor = decryptor = new aes_decryptor(); | |
} | |
decryptor.expandKey(key); | |
callback(decryptor.decrypt(data, 0, iv, this.removePKCS7Padding)); | |
} else { | |
if (this.logEnabled) { | |
logger["logger"].log('WebCrypto AES decrypt'); | |
this.logEnabled = false; | |
} | |
var subtle = this.subtle; | |
if (this.key !== key) { | |
this.key = key; | |
this.fastAesKey = new fast_aes_key(subtle, key); | |
} | |
this.fastAesKey.expandKey().then(function (aesKey) { | |
// decrypt using web crypto | |
var crypto = new AESCrypto(subtle, iv); | |
crypto.decrypt(data, aesKey).catch(function (err) { | |
_this.onWebCryptoError(err, data, key, iv, callback); | |
}).then(function (result) { | |
callback(result); | |
}); | |
}).catch(function (err) { | |
_this.onWebCryptoError(err, data, key, iv, callback); | |
}); | |
} | |
}; | |
_proto.onWebCryptoError = function onWebCryptoError(err, data, key, iv, callback) { | |
if (this.config.enableSoftwareAES) { | |
logger["logger"].log('WebCrypto Error, disable WebCrypto API'); | |
this.disableWebCrypto = true; | |
this.logEnabled = true; | |
this.decrypt(data, key, iv, callback); | |
} else { | |
logger["logger"].error("decrypting error : " + err.message); | |
this.observer.trigger(events["default"].ERROR, { | |
type: errors["ErrorTypes"].MEDIA_ERROR, | |
details: errors["ErrorDetails"].FRAG_DECRYPT_ERROR, | |
fatal: true, | |
reason: err.message | |
}); | |
} | |
}; | |
_proto.destroy = function destroy() { | |
var decryptor = this.decryptor; | |
if (decryptor) { | |
decryptor.destroy(); | |
this.decryptor = undefined; | |
} | |
}; | |
return Decrypter; | |
}(); | |
/* harmony default export */ var decrypter = __webpack_exports__["default"] = (decrypter_Decrypter); | |
/***/ }), | |
/***/ "./src/demux/demuxer-inline.js": | |
/*!**************************************************!*\ | |
!*** ./src/demux/demuxer-inline.js + 12 modules ***! | |
\**************************************************/ | |
/*! exports provided: default */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/crypt/decrypter.js because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/demux/id3.js because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/demux/mp4demuxer.js because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/errors.ts because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/events.js because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/polyfills/number-isFinite.js because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/utils/get-self-scope.js because of ./src/hls.ts */ | |
/*! ModuleConcatenation bailout: Cannot concat with ./src/utils/logger.js because of ./src/hls.ts */ | |
/***/ (function(module, __webpack_exports__, __webpack_require__) { | |
"use strict"; | |
__webpack_require__.r(__webpack_exports__); | |
// EXTERNAL MODULE: ./src/events.js | |
var events = __webpack_require__("./src/events.js"); | |
// EXTERNAL MODULE: ./src/errors.ts | |
var errors = __webpack_require__("./src/errors.ts"); | |
// EXTERNAL MODULE: ./src/crypt/decrypter.js + 3 modules | |
var crypt_decrypter = __webpack_require__("./src/crypt/decrypter.js"); | |
// EXTERNAL MODULE: ./src/polyfills/number-isFinite.js | |
var number_isFinite = __webpack_require__("./src/polyfills/number-isFinite.js"); | |
// EXTERNAL MODULE: ./src/utils/logger.js | |
var logger = __webpack_require__("./src/utils/logger.js"); | |
// EXTERNAL MODULE: ./src/utils/get-self-scope.js | |
var get_self_scope = __webpack_require__("./src/utils/get-self-scope.js"); | |
// CONCATENATED MODULE: ./src/demux/adts.js | |
/** | |
* ADTS parser helper | |
* @link https://wiki.multimedia.cx/index.php?title=ADTS | |
*/ | |
function getAudioConfig(observer, data, offset, audioCodec) { | |
var adtsObjectType, | |
// :int | |
adtsSampleingIndex, | |
// :int | |
adtsExtensionSampleingIndex, | |
// :int | |
adtsChanelConfig, | |
// :int | |
config, | |
userAgent = navigator.userAgent.toLowerCase(), | |
manifestCodec = audioCodec, | |
adtsSampleingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; // byte 2 | |
adtsObjectType = ((data[offset + 2] & 0xC0) >>> 6) + 1; | |
adtsSampleingIndex = (data[offset + 2] & 0x3C) >>> 2; | |
if (adtsSampleingIndex > adtsSampleingRates.length - 1) { | |
observer.trigger(events["default"].ERROR, { | |
type: errors["ErrorTypes"].MEDIA_ERROR, | |
details: errors["ErrorDetails"].FRAG_PARSING_ERROR, | |
fatal: true, | |
reason: "invalid ADTS sampling index:" + adtsSampleingIndex | |
}); | |
return; | |
} | |
adtsChanelConfig = (data[offset + 2] & 0x01) << 2; // byte 3 | |
adtsChanelConfig |= (data[offset + 3] & 0xC0) >>> 6; | |
logger["logger"].log("manifest codec:" + audioCodec + ",ADTS data:type:" + adtsObjectType + ",sampleingIndex:" + adtsSampleingIndex + "[" + adtsSampleingRates[adtsSampleingIndex] + "Hz],channelConfig:" + adtsChanelConfig); // firefox: freq less than 24kHz = AAC SBR (HE-AAC) | |
if (/firefox/i.test(userAgent)) { | |
if (adtsSampleingIndex >= 6) { | |
adtsObjectType = 5; | |
config = new Array(4); // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies | |
// there is a factor 2 between frame sample rate and output sample rate | |
// multiply frequency by 2 (see table below, equivalent to substract 3) | |
adtsExtensionSampleingIndex = adtsSampleingIndex - 3; | |
} else { | |
adtsObjectType = 2; | |
config = new Array(2); | |
adtsExtensionSampleingIndex = adtsSampleingIndex; | |
} // Android : always use AAC | |
} else if (userAgent.indexOf('android') !== -1) { | |
adtsObjectType = 2; | |
config = new Array(2); | |
adtsExtensionSampleingIndex = adtsSampleingIndex; | |
} else { | |
/* for other browsers (Chrome/Vivaldi/Opera ...) | |
always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...) | |
*/ | |
adtsObjectType = 5; | |
config = new Array(4); // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz) | |
if (audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1) || !audioCodec && adtsSampleingIndex >= 6) { | |
// HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies | |
// there is a factor 2 between frame sample rate and output sample rate | |
// multiply frequency by 2 (see table below, equivalent to substract 3) | |
adtsExtensionSampleingIndex = adtsSampleingIndex - 3; | |
} else { | |
// if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio) | |
// Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo. | |
if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && (adtsSampleingIndex >= 6 && adtsChanelConfig === 1 || /vivaldi/i.test(userAgent)) || !audioCodec && adtsChanelConfig === 1) { | |
adtsObjectType = 2; | |
config = new Array(2); | |
} | |
adtsExtensionSampleingIndex = adtsSampleingIndex; | |
} | |
} | |
/* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config | |
ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig() | |
Audio Profile / Audio Object Type | |
0: Null | |
1: AAC Main | |
2: AAC LC (Low Complexity) | |
3: AAC SSR (Scalable Sample Rate) | |
4: AAC LTP (Long Term Prediction) | |
5: SBR (Spectral Band Replication) | |
6: AAC Scalable | |
sampling freq | |
0: 96000 Hz | |
1: 88200 Hz | |
2: 64000 Hz | |
3: 48000 Hz | |
4: 44100 Hz | |
5: 32000 Hz | |
6: 24000 Hz | |
7: 22050 Hz | |
8: 16000 Hz | |
9: 12000 Hz | |
10: 11025 Hz | |
11: 8000 Hz | |
12: 7350 Hz | |
13: Reserved | |
14: Reserved | |
15: frequency is written explictly | |
Channel Configurations | |
These are the channel configurations: | |
0: Defined in AOT Specifc Config | |
1: 1 channel: front-center | |
2: 2 channels: front-left, front-right | |
*/ | |
// audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1 | |
config[0] = adtsObjectType << 3; // samplingFrequencyIndex | |
config[0] |= (adtsSampleingIndex & 0x0E) >> 1; | |
config[1] |= (adtsSampleingIndex & 0x01) << 7; // channelConfiguration | |
config[1] |= adtsChanelConfig << 3; | |
if (adtsObjectType === 5) { | |
// adtsExtensionSampleingIndex | |
config[1] |= (adtsExtensionSampleingIndex & 0x0E) >> 1; | |
config[2] = (adtsExtensionSampleingIndex & 0x01) << 7; // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ??? | |
// https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc | |
config[2] |= 2 << 2; | |
config[3] = 0; | |
} | |
return { | |
config: config, | |
samplerate: adtsSampleingRates[adtsSampleingIndex], | |
channelCount: adtsChanelConfig, | |
codec: 'mp4a.40.' + adtsObjectType, | |
manifestCodec: manifestCodec | |
}; | |
} | |
function isHeaderPattern(data, offset) { | |
return data[offset] === 0xff && (data[offset + 1] & 0xf6) === 0xf0; | |
} | |
function getHeaderLength(data, offset) { | |
return data[offset + 1] & 0x01 ? 7 : 9; | |
} | |
function getFullFrameLength(data, offset) { | |
return (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xE0) >>> 5; | |
} | |
function isHeader(data, offset) { | |
// Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 | |
// Layer bits (position 14 and 15) in header should be always 0 for ADTS | |
// More info https://wiki.multimedia.cx/index.php?title=ADTS | |
if (offset + 1 < data.length && isHeaderPattern(data, offset)) { | |
return true; | |
} | |
return false; | |
} | |
function adts_probe(data, offset) { | |
// same as isHeader but we also check that ADTS frame follows last ADTS frame | |
// or end of data is reached | |
if (isHeader(data, offset)) { | |
// ADTS header Length | |
var headerLength = getHeaderLength(data, offset); // ADTS frame Length | |
var frameLength = headerLength; | |
if (offset + 5 < data.length) { | |
frameLength = getFullFrameLength(data, offset); | |
} | |
var newOffset = offset + frameLength; | |
if (newOffset === data.length || newOffset + 1 < data.length && isHeaderPattern(data, newOffset)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
function initTrackConfig(track, observer, data, offset, audioCodec) { | |
if (!track.samplerate) { | |
var config = getAudioConfig(observer, data, offset, audioCodec); | |
track.config = config.config; | |
track.samplerate = config.samplerate; | |
track.channelCount = config.channelCount; | |
track.codec = config.codec; | |
track.manifestCodec = config.manifestCodec; | |
logger["logger"].log("parsed codec:" + track.codec + ",rate:" + config.samplerate + ",nb channel:" + config.channelCount); | |
} | |
} | |
function getFrameDuration(samplerate) { | |
return 1024 * 90000 / samplerate; | |
} | |
function parseFrameHeader(data, offset, pts, frameIndex, frameDuration) { | |
var headerLength, frameLength, stamp; | |
var length = data.length; // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header | |
headerLength = getHeaderLength(data, offset); // retrieve frame size | |
frameLength = getFullFrameLength(data, offset); | |
frameLength -= headerLength; | |
if (frameLength > 0 && offset + headerLength + frameLength <= length) { | |
stamp = pts + frameIndex * frameDuration; // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`); | |
return { | |
headerLength: headerLength, | |
frameLength: frameLength, | |
stamp: stamp | |
}; | |
} | |
return undefined; | |
} | |
function appendFrame(track, data, offset, pts, frameIndex) { | |
var frameDuration = getFrameDuration(track.samplerate); | |
var header = parseFrameHeader(data, offset, pts, frameIndex, frameDuration); | |
if (header) { | |
var stamp = header.stamp; | |
var headerLength = header.headerLength; | |
var frameLength = header.frameLength; // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`); | |
var aacSample = { | |
unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), | |
pts: stamp, | |
dts: stamp | |
}; | |
track.samples.push(aacSample); | |
return { | |
sample: aacSample, | |
length: frameLength + headerLength | |
}; | |
} | |
return undefined; | |
} | |
// EXTERNAL MODULE: ./src/demux/id3.js | |
var id3 = __webpack_require__("./src/demux/id3.js"); | |
// CONCATENATED MODULE: ./src/demux/aacdemuxer.js | |
/** | |
* AAC demuxer | |
*/ | |
var aacdemuxer_AACDemuxer = | |
/*#__PURE__*/ | |
function () { | |
function AACDemuxer(observer, remuxer, config) { | |
this.observer = observer; | |
this.config = config; | |
this.remuxer = remuxer; | |
} | |
var _proto = AACDemuxer.prototype; | |
_proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) { | |
this._audioTrack = { | |
container: 'audio/adts', | |
type: 'audio', | |
id: 0, | |
sequenceNumber: 0, | |
isAAC: true, | |
samples: [], | |
len: 0, | |
manifestCodec: audioCodec, | |
duration: duration, | |
inputTimeScale: 90000 | |
}; | |
}; | |
_proto.resetTimeStamp = function resetTimeStamp() {}; | |
AACDemuxer.probe = function probe(data) { | |
if (!data) { | |
return false; | |
} // Check for the ADTS sync word | |
// Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 | |
// Layer bits (position 14 and 15) in header should be always 0 for ADTS | |
// More info https://wiki.multimedia.cx/index.php?title=ADTS | |
var id3Data = id3["default"].getID3Data(data, 0) || []; | |
var offset = id3Data.length; | |
for (var length = data.length; offset < length; offset++) { | |
if (adts_probe(data, offset)) { | |
logger["logger"].log('ADTS sync word found !'); | |
return true; | |
} | |
} | |
return false; | |
} // feed incoming data to the front of the parsing pipeline | |
; | |
_proto.append = function append(data, timeOffset, contiguous, accurateTimeOffset) { | |
var track = this._audioTrack; | |
var id3Data = id3["default"].getID3Data(data, 0) || []; | |
var timestamp = id3["default"].getTimeStamp(id3Data); | |
var pts = Object(number_isFinite["isFiniteNumber"])(timestamp) ? timestamp * 90 : timeOffset * 90000; | |
var frameIndex = 0; | |
var stamp = pts; | |
var length = data.length; | |
var offset = id3Data.length; | |
var id3Samples = [{ | |
pts: stamp, | |
dts: stamp, | |
data: id3Data | |
}]; | |
while (offset < length - 1) { | |
if (isHeader(data, offset) && offset + 5 < length) { | |
initTrackConfig(track, this.observer, data, offset, track.manifestCodec); | |
var frame = appendFrame(track, data, offset, pts, frameIndex); | |
if (frame) { | |
offset += frame.length; | |
stamp = frame.sample.pts; | |
frameIndex++; | |
} else { | |
logger["logger"].log('Unable to parse AAC frame'); | |
break; | |
} | |
} else if (id3["default"].isHeader(data, offset)) { | |
id3Data = id3["default"].getID3Data(data, offset); | |
id3Samples.push({ | |
pts: stamp, | |
dts: stamp, | |
data: id3Data | |
}); | |
offset += id3Data.length; | |
} else { | |
// nothing found, keep looking | |
offset++; | |
} | |
} | |
this.remuxer.remux(track, { | |
samples: [] | |
}, { | |
samples: id3Samples, | |
inputTimeScale: 90000 | |
}, { | |
samples: [] | |
}, timeOffset, contiguous, accurateTimeOffset); | |
}; | |
_proto.destroy = function destroy() {}; | |
return AACDemuxer; | |
}(); | |
/* harmony default export */ var aacdemuxer = (aacdemuxer_AACDemuxer); | |
// EXTERNAL MODULE: ./src/demux/mp4demuxer.js | |
var mp4demuxer = __webpack_require__("./src/demux/mp4demuxer.js"); | |
// CONCATENATED MODULE: ./src/demux/mpegaudio.js | |
/** | |
* MPEG parser helper | |
*/ | |
var MpegAudio = { | |
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], | |
SamplingRateMap: [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000], | |
SamplesCoefficients: [// MPEG 2.5 | |
[0, // Reserved | |
72, // Layer3 | |
144, // Layer2 | |
12 // Layer1 | |
], // Reserved | |
[0, // Reserved | |
0, // Layer3 | |
0, // Layer2 | |
0 // Layer1 | |
], // MPEG 2 | |
[0, // Reserved | |
72, // Layer3 | |
144, // Layer2 | |
12 // Layer1 | |
], // MPEG 1 | |
[0, // Reserved | |
144, // Layer3 | |
144, // Layer2 | |
12 // Layer1 | |
]], | |
BytesInSlot: [0, // Reserved | |
1, // Layer3 | |
1, // Layer2 | |
4 // Layer1 | |
], | |
appendFrame: function appendFrame(track, data, offset, pts, frameIndex) { | |
// Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference | |
if (offset + 24 > data.length) { | |
return undefined; | |
} | |
var header = this.parseHeader(data, offset); | |
if (header && offset + header.frameLength <= data.length) { | |
var frameDuration = header.samplesPerFrame * 90000 / header.sampleRate; | |
var stamp = pts + frameIndex * frameDuration; | |
var sample = { | |
unit: data.subarray(offset, offset + header.frameLength), | |
pts: stamp, | |
dts: stamp | |
}; | |
track.config = []; | |
track.channelCount = header.channelCount; | |
track.samplerate = header.sampleRate; | |
track.samples.push(sample); | |
return { | |
sample: sample, | |
length: header.frameLength | |
}; | |
} | |
return undefined; | |
}, | |
parseHeader: function parseHeader(data, offset) { | |
var headerB = data[offset + 1] >> 3 & 3; | |
var headerC = data[offset + 1] >> 1 & 3; | |
var headerE = data[offset + 2] >> 4 & 15; | |
var headerF = data[offset + 2] >> 2 & 3; | |
var headerG = data[offset + 2] >> 1 & 1; | |
if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { | |
var columnInBitrates = headerB === 3 ? 3 - headerC : headerC === 3 ? 3 : 4; | |
var bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; | |
var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; | |
var sampleRate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF]; | |
var channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) | |
var sampleCoefficient = MpegAudio.SamplesCoefficients[headerB][headerC]; | |
var bytesInSlot = MpegAudio.BytesInSlot[headerC]; | |
var samplesPerFrame = sampleCoefficient * 8 * bytesInSlot; | |
var frameLength = parseInt(sampleCoefficient * bitRate / sampleRate + headerG, 10) * bytesInSlot; | |
return { | |
sampleRate: sampleRate, | |
channelCount: channelCount, | |
frameLength: frameLength, | |
samplesPerFrame: samplesPerFrame | |
}; | |
} | |
return undefined; | |
}, | |
isHeaderPattern: function isHeaderPattern(data, offset) { | |
return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00; | |
}, | |
isHeader: function isHeader(data, offset) { | |
// Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 | |
// Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) | |
// More info http://www.mp3-tech.org/programmer/frame_header.html | |
if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) { | |
return true; | |
} | |
return false; | |
}, | |
probe: function probe(data, offset) { | |
// same as isHeader but we also check that MPEG frame follows last MPEG frame | |
// or end of data is reached | |
if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) { | |
// MPEG header Length | |
var headerLength = 4; // MPEG frame Length | |
var header = this.parseHeader(data, offset); | |
var frameLength = headerLength; | |
if (header && header.frameLength) { | |
frameLength = header.frameLength; | |
} | |
var newOffset = offset + frameLength; | |
if (newOffset === data.length || newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
}; | |
/* harmony default export */ var mpegaudio = (MpegAudio); | |
// CONCATENATED MODULE: ./src/demux/exp-golomb.js | |
/** | |
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264. | |
*/ | |
var exp_golomb_ExpGolomb = | |
/*#__PURE__*/ | |
function () { | |
function ExpGolomb(data) { | |
this.data = data; // the number of bytes left to examine in this.data | |
this.bytesAvailable = data.byteLength; // the current word being examined | |
this.word = 0; // :uint | |
// the number of bits left to examine in the current word | |
this.bitsAvailable = 0; // :uint | |
} // ():void | |
var _proto = ExpGolomb.prototype; | |
_proto.loadWord = function loadWord() { | |
var data = this.data, | |
bytesAvailable = this.bytesAvailable, | |
position = data.byteLength - bytesAvailable, | |
workingBytes = new Uint8Array(4), | |
availableBytes = Math.min(4, bytesAvailable); | |
if (availableBytes === 0) { | |
throw new Error('no bytes available'); | |
} | |
workingBytes.set(data.subarray(position, position + availableBytes)); | |
this.word = new DataView(workingBytes.buffer).getUint32(0); // track the amount of this.data that has been processed | |
this.bitsAvailable = availableBytes * 8; | |
this.bytesAvailable -= availableBytes; | |
} // (count:int):void | |
; | |
_proto.skipBits = function skipBits(count) { | |
var skipBytes; // :int | |
if (this.bitsAvailable > count) { | |
this.word <<= count; | |
this.bitsAvailable -= count; | |
} else { | |
count -= this.bitsAvailable; | |
skipBytes = count >> 3; | |
count -= skipBytes >> 3; | |
this.bytesAvailable -= skipBytes; | |
this.loadWord(); | |
this.word <<= count; | |
this.bitsAvailable -= count; | |
} | |
} // (size:int):uint | |
; | |
_proto.readBits = function readBits(size) { | |
var bits = Math.min(this.bitsAvailable, size), | |
// :uint | |
valu = this.word >>> 32 - bits; // :uint | |
if (size > 32) { | |
logger["logger"].error('Cannot read more than 32 bits at a time'); | |
} | |
this.bitsAvailable -= bits; | |
if (this.bitsAvailable > 0) { | |
this.word <<= bits; | |
} else if (this.bytesAvailable > 0) { | |
this.loadWord(); | |
} | |
bits = size - bits; | |
if (bits > 0 && this.bitsAvailable) { | |
return valu << bits | this.readBits(bits); | |
} else { | |
return valu; | |
} | |
} // ():uint | |
; | |
_proto.skipLZ = function skipLZ() { | |
var leadingZeroCount; // :uint | |
for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) { | |
if ((this.word & 0x80000000 >>> leadingZeroCount) !== 0) { | |
// the first bit of working word is 1 | |
this.word <<= leadingZeroCount; | |
this.bitsAvailable -= leadingZeroCount; | |
return leadingZeroCount; | |
} | |
} // we exhausted word and still have not found a 1 | |
this.loadWord(); | |
return leadingZeroCount + this.skipLZ(); | |
} // ():void | |
; | |
_proto.skipUEG = function skipUEG() { | |
this.skipBits(1 + this.skipLZ()); | |
} // ():void | |
; | |
_proto.skipEG = function skipEG() { | |
this.skipBits(1 + this.skipLZ()); | |
} // ():uint | |
; | |
_proto.readUEG = function readUEG() { | |
var clz = this.skipLZ(); // :uint | |
return this.readBits(clz + 1) - 1; | |
} // ():int | |
; | |
_proto.readEG = function readEG() { | |
var valu = this.readUEG(); // :int | |
if (0x01 & valu) { | |
// the number is odd if the low order bit is set | |
return 1 + valu >>> 1; // add 1 to make it even, and divide by 2 | |
} else { | |
return -1 * (valu >>> 1); // divide by two then make it negative | |
} | |
} // Some convenience functions | |
// :Boolean | |
; | |
_proto.readBoolean = function readBoolean() { | |
return this.readBits(1) === 1; | |
} // ():int | |
; | |
_proto.readUByte = function readUByte() { | |
return this.readBits(8); | |
} // ():int | |
; | |
_proto.readUShort = function readUShort() { | |
return this.readBits(16); | |
} // ():int | |
; | |
_proto.readUInt = function readUInt() { | |
return this.readBits(32); | |
} | |
/** | |
* Advance the ExpGolomb decoder past a scaling list. The scaling | |
* list is optionally transmitted as part of a sequence parameter | |
* set and is not relevant to transmuxing. | |
* @param count {number} the number of entries in this scaling list | |
* @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1 | |
*/ | |
; | |
_proto.skipScalingList = function skipScalingList(count) { | |
var lastScale = 8, | |
nextScale = 8, | |
j, | |
deltaScale; | |
for (j = 0; j < count; j++) { | |
if (nextScale !== 0) { | |
deltaScale = this.readEG(); | |
nextScale = (lastScale + deltaScale + 256) % 256; | |
} | |
lastScale = nextScale === 0 ? lastScale : nextScale; | |
} | |
} | |
/** | |
* Read a sequence parameter set and return some interesting video | |
* properties. A sequence parameter set is the H264 metadata that | |
* describes the properties of upcoming video frames. | |
* @param data {Uint8Array} the bytes of a sequence parameter set | |
* @return {object} an object with configuration parsed from the | |
* sequence parameter set, including the dimensions of the | |
* associated video frames. | |
*/ | |
; | |
_proto.readSPS = function readSPS() { | |
var frameCropLeftOffset = 0, | |
frameCropRightOffset = 0, | |
frameCropTopOffset = 0, | |
frameCropBottomOffset = 0, | |
profileIdc, | |
profileCompat, | |
levelIdc, | |
numRefFramesInPicOrderCntCycle, | |
picWidthInMbsMinus1, | |
picHeightInMapUnitsMinus1, | |
frameMbsOnlyFlag, | |
scalingListCount, | |
i, | |
readUByte = this.readUByte.bind(this), | |
readBits = this.readBits.bind(this), | |
readUEG = this.readUEG.bind(this), | |
readBoolean = this.readBoolean.bind(this), | |
skipBits = this.skipBits.bind(this), | |
skipEG = this.skipEG.bind(this), | |
skipUEG = this.skipUEG.bind(this), | |
skipScalingList = this.skipScalingList.bind(this); | |
readUByte(); | |
profileIdc = readUByte(); // profile_idc | |
profileCompat = readBits(5); // constraint_set[0-4]_flag, u(5) | |
skipBits(3); // reserved_zero_3bits u(3), | |
levelIdc = readUByte(); // level_idc u(8) | |
skipUEG(); // seq_parameter_set_id | |
// some profiles have more optional data we don't need | |
if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) { | |
var chromaFormatIdc = readUEG(); | |
if (chromaFormatIdc === 3) { | |
skipBits(1); | |
} // separate_colour_plane_flag | |
skipUEG(); // bit_depth_luma_minus8 | |
skipUEG(); // bit_depth_chroma_minus8 | |
skipBits(1); // qpprime_y_zero_transform_bypass_flag | |
if (readBoolean()) { | |
// seq_scaling_matrix_present_flag | |
scalingListCount = chromaFormatIdc !== 3 ? 8 : 12; | |
for (i = 0; i < scalingListCount; i++) { | |
if (readBoolean()) { | |
// seq_scaling_list_present_flag[ i ] | |
if (i < 6) { | |
skipScalingList(16); | |
} else { | |
skipScalingList(64); | |
} | |
} | |
} | |
} | |
} | |
skipUEG(); // log2_max_frame_num_minus4 | |
var picOrderCntType = readUEG(); | |
if (picOrderCntType === 0) { | |
readUEG(); // log2_max_pic_order_cnt_lsb_minus4 | |
} else if (picOrderCntType === 1) { | |
skipBits(1); // delta_pic_order_always_zero_flag | |
skipEG(); // offset_for_non_ref_pic | |
skipEG(); // offset_for_top_to_bottom_field | |
numRefFramesInPicOrderCntCycle = readUEG(); | |
for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) { | |
skipEG(); | |
} // offset_for_ref_frame[ i ] | |
} | |
skipUEG(); // max_num_ref_frames | |
skipBits(1); // gaps_in_frame_num_value_allowed_flag | |
picWidthInMbsMinus1 = readUEG(); | |
picHeightInMapUnitsMinus1 = readUEG(); | |
frameMbsOnlyFlag = readBits(1); | |
if (frameMbsOnlyFlag === 0) { | |
skipBits(1); | |
} // mb_adaptive_frame_field_flag | |
skipBits(1); // direct_8x8_inference_flag | |
if (readBoolean()) { | |
// frame_cropping_flag | |
frameCropLeftOffset = readUEG(); | |
frameCropRightOffset = readUEG(); | |
frameCropTopOffset = readUEG(); | |
frameCropBottomOffset = readUEG(); | |
} | |
var pixelRatio = [1, 1]; | |
if (readBoolean()) { | |
// vui_parameters_present_flag | |
if (readBoolean()) { | |
// aspect_ratio_info_present_flag | |
var aspectRatioIdc = readUByte(); | |
switch (aspectRatioIdc) { | |
case 1: | |
pixelRatio = [1, 1]; | |
break; | |
case 2: | |
pixelRatio = [12, 11]; | |
break; | |
case 3: | |
pixelRatio = [10, 11]; | |
break; | |
case 4: | |
pixelRatio = [16, 11]; | |
break; | |
case 5: | |
pixelRatio = [40, 33]; | |
break; | |
case 6: | |
pixelRatio = [24, 11]; | |
break; | |
case 7: | |
pixelRatio = [20, 11]; | |
break; | |
case 8: | |
pixelRatio = [32, 11]; | |
break; | |
case 9: | |
pixelRatio = [80, 33]; | |
break; | |
case 10: | |
pixelRatio = [18, 11]; | |
break; | |
case 11: | |
pixelRatio = [15, 11]; | |
break; | |
case 12: | |
pixelRatio = [64, 33]; | |
break; | |
case 13: | |
pixelRatio = [160, 99]; | |
break; | |
case 14: | |
pixelRatio = [4, 3]; | |
break; | |
case 15: | |
pixelRatio = [3, 2]; | |
break; | |
case 16: | |
pixelRatio = [2, 1]; | |
break; | |
case 255: | |
{ | |
pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()]; | |
break; | |
} | |
} | |
} | |
} | |
return { | |
width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2), | |
height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset), | |
pixelRatio: pixelRatio | |
}; | |
}; | |
_proto.readSliceType = function readSliceType() { | |
// skip NALu type | |
this.readUByte(); // discard first_mb_in_slice | |
this.readUEG(); // return slice_type | |
return this.readUEG(); | |
}; | |
return ExpGolomb; | |
}(); | |
/* harmony default export */ var exp_golomb = (exp_golomb_ExpGolomb); | |
// CONCATENATED MODULE: ./src/demux/sample-aes.js | |
/** | |
* SAMPLE-AES decrypter | |
*/ | |
var sample_aes_SampleAesDecrypter = | |
/*#__PURE__*/ | |
function () { | |
function SampleAesDecrypter(observer, config, decryptdata, discardEPB) { | |
this.decryptdata = decryptdata; | |
this.discardEPB = discardEPB; | |
this.decrypter = new crypt_decrypter["default"](observer, config, { | |
removePKCS7Padding: false | |
}); | |
} | |
var _proto = SampleAesDecrypter.prototype; | |
_proto.decryptBuffer = function decryptBuffer(encryptedData, callback) { | |
this.decrypter.decrypt(encryptedData, this.decryptdata.key.buffer, this.decryptdata.iv.buffer, callback); | |
} // AAC - encrypt all full 16 bytes blocks starting from offset 16 | |
; | |
_proto.decryptAacSample = function decryptAacSample(samples, sampleIndex, callback, sync) { | |
var curUnit = samples[sampleIndex].unit; | |
var encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16); | |
var encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length); | |
var localthis = this; | |
this.decryptBuffer(encryptedBuffer, function (decryptedData) { | |
decryptedData = new Uint8Array(decryptedData); | |
curUnit.set(decryptedData, 16); | |
if (!sync) { | |
localthis.decryptAacSamples(samples, sampleIndex + 1, callback); | |
} | |
}); | |
}; | |
_proto.decryptAacSamples = function decryptAacSamples(samples, sampleIndex, callback) { | |
for (;; sampleIndex++) { | |
if (sampleIndex >= samples.length) { | |
callback(); | |
return; | |
} | |
if (samples[sampleIndex].unit.length < 32) { | |
continue; | |
} | |
var sync = this.decrypter.isSync(); | |
this.decryptAacSample(samples, sampleIndex, callback, sync); | |
if (!sync) { | |
return; | |
} | |
} | |
} // AVC - encrypt one 16 bytes block out of ten, starting from offset 32 | |
; | |
_proto.getAvcEncryptedData = function getAvcEncryptedData(decodedData) { | |
var encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16; | |
var encryptedData = new Int8Array(encryptedDataLen); | |
var outputPos = 0; | |
for (var inputPos = 32; inputPos <= decodedData.length - 16; inputPos += 160, outputPos += 16) { | |
encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos); | |
} | |
return encryptedData; | |
}; | |
_proto.getAvcDecryptedUnit = function getAvcDecryptedUnit(decodedData, decryptedData) { | |
decryptedData = new Uint8Array(decryptedData); | |
var inputPos = 0; | |
for (var outputPos = 32; outputPos <= decodedData.length - 16; outputPos += 160, inputPos += 16) { | |
decodedData.set(decryptedData.subarray(inputPos, inputPos + 16), outputPos); | |
} | |
return decodedData; | |
}; | |
_proto.decryptAvcSample = function decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync) { | |
var decodedData = this.discardEPB(curUnit.data); | |
var encryptedData = this.getAvcEncryptedData(decodedData); | |
var localthis = this; | |
this.decryptBuffer(encryptedData.buffer, function (decryptedData) { | |
curUnit.data = localthis.getAvcDecryptedUnit(decodedData, decryptedData); | |
if (!sync) { | |
localthis.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback); | |
} | |
}); | |
}; | |
_proto.decryptAvcSamples = function decryptAvcSamples(samples, sampleIndex, unitIndex, callback) { | |
for (;; sampleIndex++, unitIndex = 0) { | |
if (sampleIndex >= samples.length) { | |
callback(); | |
return; | |
} | |
var curUnits = samples[sampleIndex].units; | |
for (;; unitIndex++) { | |
if (unitIndex >= curUnits.length) { | |
break; | |
} | |
var curUnit = curUnits[unitIndex]; | |
if (curUnit.length <= 48 || curUnit.type !== 1 && curUnit.type !== 5) { | |
continue; | |
} | |
var sync = this.decrypter.isSync(); | |
this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync); | |
if (!sync) { | |
return; | |
} | |
} | |
} | |
}; | |
return SampleAesDecrypter; | |
}(); | |
/* harmony default export */ var sample_aes = (sample_aes_SampleAesDecrypter); | |
// CONCATENATED MODULE: ./src/demux/tsdemuxer.js | |
/** | |
* highly optimized TS demuxer: | |
* parse PAT, PMT | |
* extract PES packet from audio and video PIDs | |
* extract AVC/H264 NAL units and AAC/ADTS samples from PES packet | |
* trigger the remuxer upon parsing completion | |
* 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. | |
* it also controls the remuxing process : | |
* upon discontinuity or level switch detection, it will also notifies the remuxer so that it can reset its state. | |
*/ | |
// import Hex from '../utils/hex'; | |
// We are using fixed track IDs for driving the MP4 remuxer | |
// instead of following the TS PIDs. | |
// There is no reason not to do this and some browsers/SourceBuffer-demuxers | |
// may not like if there are TrackID "switches" | |
// See https://github.com/video-dev/hls.js/issues/1331 | |
// Here we are mapping our internal track types to constant MP4 track IDs | |
// With MSE currently one can only have one track of each, and we are muxing | |
// whatever video/audio rendition in them. | |
var RemuxerTrackIdConfig = { | |
video: 1, | |
audio: 2, | |
id3: 3, | |
text: 4 | |
}; | |
var tsdemuxer_TSDemuxer = | |
/*#__PURE__*/ | |
function () { | |
function TSDemuxer(observer, remuxer, config, typeSupported) { | |
this.observer = observer; | |
this.config = config; | |
this.typeSupported = typeSupported; | |
this.remuxer = remuxer; | |
this.sampleAes = null; | |
} | |
var _proto = TSDemuxer.prototype; | |
_proto.setDecryptData = function setDecryptData(decryptdata) { | |
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; | |
} | |
}; | |
TSDemuxer.probe = function probe(data) { | |
var syncOffset = TSDemuxer._syncOffset(data); | |
if (syncOffset < 0) { | |
return false; | |
} else { | |
if (syncOffset) { | |
logger["logger"].warn("MPEG2-TS detected but first sync word found @ offset " + syncOffset + ", junk ahead ?"); | |
} | |
return true; | |
} | |
}; | |
TSDemuxer._syncOffset = function _syncOffset(data) { | |
// scan 1000 first bytes | |
var scanwindow = Math.min(1000, data.length - 3 * 188); | |
var i = 0; | |
while (i < scanwindow) { | |
// a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and one PID, each starting with 0x47 | |
if (data[i] === 0x47 && data[i + 188] === 0x47 && data[i + 2 * 188] === 0x47) { | |
return i; | |
} else { | |
i++; | |
} | |
} | |
return -1; | |
} | |
/** | |
* Creates a track model internal to demuxer used to drive remuxing input | |
* | |
* @param {string} type 'audio' | 'video' | 'id3' | 'text' | |
* @param {number} duration | |
* @return {object} TSDemuxer's internal track model | |
*/ | |
; | |
TSDemuxer.createTrack = function createTrack(type, duration) { | |
return { | |
container: type === 'video' || type === 'audio' ? 'video/mp2t' : undefined, | |
type: type, | |
id: RemuxerTrackIdConfig[type], | |
pid: -1, | |
inputTimeScale: 90000, | |
sequenceNumber: 0, | |
samples: [], | |
dropped: type === 'video' ? 0 : undefined, | |
isAAC: type === 'audio' ? true : undefined, | |
duration: type === 'audio' ? duration : undefined | |
}; | |
} | |
/** | |
* Initializes a new init segment on the demuxer/remuxer interface. Needed for discontinuities/track-switches (or at stream start) | |
* Resets all internal track instances of the demuxer. | |
* | |
* @override Implements generic demuxing/remuxing interface (see DemuxerInline) | |
* @param {object} initSegment | |
* @param {string} audioCodec | |
* @param {string} videoCodec | |
* @param {number} duration (in TS timescale = 90kHz) | |
*/ | |
; | |
_proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) { | |
this.pmtParsed = false; | |
this._pmtId = -1; | |
this._avcTrack = TSDemuxer.createTrack('video', duration); | |
this._audioTrack = TSDemuxer.createTrack('audio', duration); | |
this._id3Track = TSDemuxer.createTrack('id3', duration); | |
this._txtTrack = TSDemuxer.createTrack('text', duration); // flush any partial content | |
this.aacOverFlow = null; | |
this.aacLastPTS = null; | |
this.avcSample = null; | |
this.audioCodec = audioCodec; | |
this.videoCodec = videoCodec; | |
this._duration = duration; | |
} | |
/** | |
* | |
* @override | |
*/ | |
; | |
_proto.resetTimeStamp = function resetTimeStamp() {} // feed incoming data to the front of the parsing pipeline | |
; | |
_proto.append = function append(data, timeOffset, contiguous, accurateTimeOffset) { | |
var start, | |
len = data.length, | |
stt, | |
pid, | |
atf, | |
offset, | |
pes, | |
unknownPIDs = false; | |
this.contiguous = contiguous; | |
var pmtParsed = this.pmtParsed, | |
avcTrack = this._avcTrack, | |
audioTrack = this._audioTrack, | |
id3Track = this._id3Track, | |
avcId = avcTrack.pid, | |
audioId = audioTrack.pid, | |
id3Id = id3Track.pid, | |
pmtId = this._pmtId, | |
avcData = avcTrack.pesData, | |
audioData = audioTrack.pesData, | |
id3Data = id3Track.pesData, | |
parsePAT = this._parsePAT, | |
parsePMT = this._parsePMT, | |
parsePES = this._parsePES, | |
parseAVCPES = this._parseAVCPES.bind(this), | |
parseAACPES = this._parseAACPES.bind(this), | |
parseMPEGPES = this._parseMPEGPES.bind(this), | |
parseID3PES = this._parseID3PES.bind(this); | |
var syncOffset = TSDemuxer._syncOffset(data); // don't parse last TS packet if incomplete | |
len -= (len + syncOffset) % 188; // loop through TS packets | |
for (start = syncOffset; start < len; start += 188) { | |
if (data[start] === 0x47) { | |
stt = !!(data[start + 1] & 0x40); // pid is a 13-bit field starting at the last bit of TS[1] | |
pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2]; | |
atf = (data[start + 3] & 0x30) >> 4; // if an adaption field is present, its length is specified by the fifth byte of the TS packet header. | |
if (atf > 1) { | |
offset = start + 5 + data[start + 4]; // continue if there is only adaptation field | |
if (offset === start + 188) { | |
continue; | |
} | |
} else { | |
offset = start + 4; | |
} | |
switch (pid) { | |
case avcId: | |
if (stt) { | |
if (avcData && (pes = parsePES(avcData)) && pes.pts !== undefined) { | |
parseAVCPES(pes, false); | |
} | |
avcData = { | |
data: [], | |
size: 0 | |
}; | |
} | |
if (avcData) { | |
avcData.data.push(data.subarray(offset, start + 188)); | |
avcData.size += start + 188 - offset; | |
} | |
break; | |
case audioId: | |
if (stt) { | |
if (audioData && (pes = parsePES(audioData)) && pes.pts !== undefined) { | |
if (audioTrack.isAAC) { | |
parseAACPES(pes); | |
} else { | |
parseMPEGPES(pes); | |
} | |
} | |
audioData = { | |
data: [], | |
size: 0 | |
}; | |
} | |
if (audioData) { | |
audioData.data.push(data.subarray(offset, start + 188)); | |
audioData.size += start + 188 - offset; | |
} | |
break; | |
case id3Id: | |
if (stt) { | |
if (id3Data && (pes = parsePES(id3Data)) && pes.pts !== undefined) { | |
parseID3PES(pes); | |
} | |
id3Data = { | |
data: [], | |
size: 0 | |
}; | |
} | |
if (id3Data) { | |
id3Data.data.push(data.subarray(offset, start + 188)); | |
id3Data.size += start + 188 - offset; | |
} | |
break; | |
case 0: | |
if (stt) { | |
offset += data[offset] + 1; | |
} | |
pmtId = this._pmtId = parsePAT(data, offset); | |
break; | |
case pmtId: | |
if (stt) { | |
offset += data[offset] + 1; | |
} | |
var parsedPIDs = parsePMT(data, offset, this.typeSupported.mpeg === true || this.typeSupported.mp3 === true, this.sampleAes != null); // only update track id if track PID found while parsing PMT | |
// this is to avoid resetting the PID to -1 in case | |
// track PID transiently disappears from the stream | |
// this could happen in case of transient missing audio samples for example | |
// NOTE this is only the PID of the track as found in TS, | |
// but we are not using this for MP4 track IDs. | |
avcId = parsedPIDs.avc; | |
if (avcId > 0) { | |
avcTrack.pid = avcId; | |
} | |
audioId = parsedPIDs.audio; | |
if (audioId > 0) { | |
audioTrack.pid = audioId; | |
audioTrack.isAAC = parsedPIDs.isAAC; | |
} | |
id3Id = parsedPIDs.id3; | |
if (id3Id > 0) { | |
id3Track.pid = id3Id; | |
} | |
if (unknownPIDs && !pmtParsed) { | |
logger["logger"].log('reparse from beginning'); | |
unknownPIDs = false; // we set it to -188, the += 188 in the for loop will reset start to 0 | |
start = syncOffset - 188; | |
} | |
pmtParsed = this.pmtParsed = true; | |
break; | |
case 17: | |
case 0x1fff: | |
break; | |
default: | |
unknownPIDs = true; | |
break; | |
} | |
} else { | |
this.observer.trigger(events["default"].ERROR, { | |
type: errors["ErrorTypes"].MEDIA_ERROR, | |
details: errors["ErrorDetails"].FRAG_PARSING_ERROR, | |
fatal: false, | |
reason: 'TS packet did not start with 0x47' | |
}); | |
} | |
} // try to parse last PES packets | |
if (avcData && (pes = parsePES(avcData)) && pes.pts !== undefined) { | |
parseAVCPES(pes, true); | |
avcTrack.pesData = null; | |
} else { | |
// either avcData null or PES truncated, keep it for next frag parsing | |
avcTrack.pesData = avcData; | |
} | |
if (audioData && (pes = parsePES(audioData)) && pes.pts !== undefined) { | |
if (audioTrack.isAAC) { | |
parseAACPES(pes); | |
} else { | |
parseMPEGPES(pes); | |
} | |
audioTrack.pesData = null; | |
} else { | |
if (audioData && audioData.size) { | |
logger["logger"].log('last AAC PES packet truncated,might overlap between fragments'); | |
} // either audioData null or PES truncated, keep it for next frag parsing | |
audioTrack.pesData = audioData; | |
} | |
if (id3Data && (pes = parsePES(id3Data)) && pes.pts !== undefined) { | |
parseID3PES(pes); | |
id3Track.pesData = null; | |
} else { | |
// either id3Data null or PES truncated, keep it for next frag parsing | |
id3Track.pesData = id3Data; | |
} | |
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); | |
} | |
}; | |
_proto.decryptAndRemux = function decryptAndRemux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) { | |
if (audioTrack.samples && audioTrack.isAAC) { | |
var localthis = this; | |
this.sampleAes.decryptAacSamples(audioTrack.samples, 0, function () { | |
localthis.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); | |
}); | |
} else { | |
this.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); | |
} | |
}; | |
_proto.decryptAndRemuxAvc = function decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) { | |
if (videoTrack.samples) { | |
var localthis = this; | |
this.sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, function () { | |
localthis.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); | |
}); | |
} else { | |
this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); | |
} | |
}; | |
_proto.destroy = function destroy() { | |
this._initPTS = this._initDTS = undefined; | |
this._duration = 0; | |
}; | |
_proto._parsePAT = function _parsePAT(data, offset) { | |
// skip the PSI header and parse the first PMT entry | |
return (data[offset + 10] & 0x1F) << 8 | data[offset + 11]; // logger.log('PMT PID:' + this._pmtId); | |
}; | |
_proto._parsePMT = function _parsePMT(data, offset, mpegSupported, isSampleAes) { | |
var sectionLength, | |
tableEnd, | |
programInfoLength, | |
pid, | |
result = { | |
audio: -1, | |
avc: -1, | |
id3: -1, | |
isAAC: true | |
}; | |
sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2]; | |
tableEnd = offset + 3 + sectionLength - 4; // to determine where the table is, we have to figure out how | |
// long the program info descriptors are | |
programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11]; // advance the offset to the first entry in the mapping table | |
offset += 12 + programInfoLength; | |
while (offset < tableEnd) { | |
pid = (data[offset + 1] & 0x1F) << 8 | data[offset + 2]; | |
switch (data[offset]) { | |
case 0xcf: | |
// SAMPLE-AES AAC | |
if (!isSampleAes) { | |
logger["logger"].log('unknown stream type:' + data[offset]); | |
break; | |
} | |
/* falls through */ | |
// ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio) | |
case 0x0f: | |
// logger.log('AAC PID:' + pid); | |
if (result.audio === -1) { | |
result.audio = pid; | |
} | |
break; | |
// Packetized metadata (ID3) | |
case 0x15: | |
// logger.log('ID3 PID:' + pid); | |
if (result.id3 === -1) { | |
result.id3 = pid; | |
} | |
break; | |
case 0xdb: | |
// SAMPLE-AES AVC | |
if (!isSampleAes) { | |
logger["logger"].log('unknown stream type:' + data[offset]); | |
break; | |
} | |
/* falls through */ | |
// ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video) | |
case 0x1b: | |
// logger.log('AVC PID:' + pid); | |
if (result.avc === -1) { | |
result.avc = pid; | |
} | |
break; | |
// ISO/IEC 11172-3 (MPEG-1 audio) | |
// or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio) | |
case 0x03: | |
case 0x04: | |
// logger.log('MPEG PID:' + pid); | |
if (!mpegSupported) { | |
logger["logger"].log('MPEG audio found, not supported in this browser for now'); | |
} else if (result.audio === -1) { | |
result.audio = pid; | |
result.isAAC = false; | |
} | |
break; | |
case 0x24: | |
logger["logger"].warn('HEVC stream type found, not supported for now'); | |
break; | |
default: | |
logger["logger"].log('unknown stream type:' + data[offset]); | |
break; | |
} // move to the next table entry | |
// skip past the elementary stream descriptors, if present | |
offset += ((data[offset + 3] & 0x0F) << 8 | data[offset + 4]) + 5; | |
} | |
return result; | |
}; | |
_proto._parsePES = function _parsePES(stream) { | |
var i = 0, | |
frag, | |
pesFlags, | |
pesPrefix, | |
pesLen, | |
pesHdrLen, | |
pesData, | |
pesPts, | |
pesDts, | |
payloadStartOffset, | |
data = stream.data; // safety check | |
if (!stream || stream.size === 0) { | |
return null; | |
} // we might need up to 19 bytes to read PES header | |
// if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes | |
// usually only one merge is needed (and this is rare ...) | |
while (data[0].length < 19 && data.length > 1) { | |
var newData = new Uint8Array(data[0].length + data[1].length); | |
newData.set(data[0]); | |
newData.set(data[1], data[0].length); | |
data[0] = newData; | |
data.splice(1, 1); | |
} // retrieve PTS/DTS from first fragment | |
frag = data[0]; | |
pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2]; | |
if (pesPrefix === 1) { | |
pesLen = (frag[4] << 8) + frag[5]; // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated | |
// minus 6 : PES header size | |
if (pesLen && pesLen > stream.size - 6) { | |
return null; | |
} | |
pesFlags = frag[7]; | |
if (pesFlags & 0xC0) { | |
/* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html | |
as PTS / DTS is 33 bit we cannot use bitwise operator in JS, | |
as Bitwise operators treat their operands as a sequence of 32 bits */ | |
pesPts = (frag[9] & 0x0E) * 536870912 + // 1 << 29 | |
(frag[10] & 0xFF) * 4194304 + // 1 << 22 | |
(frag[11] & 0xFE) * 16384 + // 1 << 14 | |
(frag[12] & 0xFF) * 128 + // 1 << 7 | |
(frag[13] & 0xFE) / 2; // check if greater than 2^32 -1 | |
if (pesPts > 4294967295) { | |
// decrement 2^33 | |
pesPts -= 8589934592; | |
} | |
if (pesFlags & 0x40) { | |
pesDts = (frag[14] & 0x0E) * 536870912 + // 1 << 29 | |
(frag[15] & 0xFF) * 4194304 + // 1 << 22 | |
(frag[16] & 0xFE) * 16384 + // 1 << 14 | |
(frag[17] & 0xFF) * 128 + // 1 << 7 | |
(frag[18] & 0xFE) / 2; // check if greater than 2^32 -1 | |
if (pesDts > 4294967295) { | |
// decrement 2^33 | |
pesDts -= 8589934592; | |
} | |
if (pesPts - pesDts > 60 * 90000) { | |
logger["logger"].warn(Math.round((pesPts - pesDts) / 90000) + "s delta between PTS and DTS, align them"); | |
pesPts = pesDts; | |
} | |
} else { | |
pesDts = pesPts; | |
} | |
} | |
pesHdrLen = frag[8]; // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension | |
payloadStartOffset = pesHdrLen + 9; | |
if (stream.size <= payloadStartOffset) { | |
return null; | |
} | |
stream.size -= payloadStartOffset; // reassemble PES packet | |
pesData = new Uint8Array(stream.size); | |
for (var j = 0, dataLen = data.length; j < dataLen; j++) { | |
frag = data[j]; | |
var len = frag.byteLength; | |
if (payloadStartOffset) { | |
if (payloadStartOffset > len) { | |
// trim full frag if PES header bigger than frag | |
payloadStartOffset -= len; | |
continue; | |
} else { | |
// trim partial frag if PES header smaller than frag | |
frag = frag.subarray(payloadStartOffset); | |
len -= payloadStartOffset; | |
payloadStartOffset = 0; | |
} | |
} | |
pesData.set(frag, i); | |
i += len; | |
} | |
if (pesLen) { | |
// payload size : remove PES header + PES extension | |
pesLen -= pesHdrLen + 3; | |
} | |
return { | |
data: pesData, | |
pts: pesPts, | |
dts: pesDts, | |
len: pesLen | |
}; | |
} else { | |
return null; | |
} | |
}; | |
_proto.pushAccesUnit = function pushAccesUnit(avcSample, avcTrack) { | |
if (avcSample.units.length && avcSample.frame) { | |
var samples = avcTrack.samples; | |
var nbSamples = samples.length; // only push AVC sample if starting with a keyframe is not mandatory OR | |
// if keyframe already found in this fragment OR | |
// keyframe found in last fragment (track.sps) AND | |
// samples already appended (we already found a keyframe in this fragment) OR fragment is contiguous | |
if (!this.config.forceKeyFrameOnDiscontinuity || avcSample.key === true || avcTrack.sps && (nbSamples || this.contiguous)) { | |
avcSample.id = nbSamples; | |
samples.push(avcSample); | |
} else { | |
// dropped samples, track it | |
avcTrack.dropped++; | |
} | |
} | |
if (avcSample.debug.length) { | |
logger["logger"].log(avcSample.pts + '/' + avcSample.dts + ':' + avcSample.debug); | |
} | |
}; | |
_proto._parseAVCPES = function _parseAVCPES(pes, last) { | |
var _this = this; | |
// logger.log('parse new PES'); | |
var track = this._avcTrack, | |
units = this._parseAVCNALu(pes.data), | |
debug = false, | |
expGolombDecoder, | |
avcSample = this.avcSample, | |
push, | |
spsfound = false, | |
i, | |
pushAccesUnit = this.pushAccesUnit.bind(this), | |
createAVCSample = function createAVCSample(key, pts, dts, debug) { | |
return { | |
key: key, | |
pts: pts, | |
dts: dts, | |
units: [], | |
debug: debug | |
}; | |
}; // free pes.data to save up some memory | |
pes.data = null; // if new NAL units found and last sample still there, let's push ... | |
// this helps parsing streams with missing AUD (only do this if AUD never found) | |
if (avcSample && units.length && !track.audFound) { | |
pushAccesUnit(avcSample, track); | |
avcSample = this.avcSample = createAVCSample(false, pes.pts, pes.dts, ''); | |
} | |
units.forEach(function (unit) { | |
switch (unit.type) { | |
// NDR | |
case 1: | |
push = true; | |
if (!avcSample) { | |
avcSample = _this.avcSample = createAVCSample(true, pes.pts, pes.dts, ''); | |
} | |
if (debug) { | |
avcSample.debug += 'NDR '; | |
} | |
avcSample.frame = true; | |
var data = unit.data; // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...) | |
if (spsfound && data.length > 4) { | |
// retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR | |
var sliceType = new exp_golomb(data).readSliceType(); // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice | |
// SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples. | |
// An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice. | |
// I slice: A slice that is not an SI slice that is decoded using intra prediction only. | |
// if (sliceType === 2 || sliceType === 7) { | |
if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) { | |
avcSample.key = true; | |
} | |
} | |
break; | |
// IDR | |
case 5: | |