@tarojs/components
Version:
Taro 组件库
1,502 lines (1,450 loc) • 885 kB
JavaScript
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var urlToolkit = {exports: {}};
(function (module, exports) {
// see https://tools.ietf.org/html/rfc1808
(function (root) {
var URL_REGEX =
/^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/;
var FIRST_SEGMENT_REGEX = /^(?=([^\/?#]*))\1([^]*)$/;
var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g;
var URLToolkit = {
// 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
) {}
return path.split('').reverse().join('');
},
buildURLFromParts: function (parts) {
return (
parts.scheme +
parts.netLoc +
parts.path +
parts.params +
parts.query +
parts.fragment
);
},
};
module.exports = URLToolkit;
})();
} (urlToolkit));
var urlToolkitExports = urlToolkit.exports;
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
enumerableOnly && (symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})), keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = null != arguments[i] ? arguments[i] : {};
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
_defineProperty(target, key, source[key]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
return target;
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _toPrimitive(input, hint) {
if (typeof input !== "object" || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || "default");
if (typeof res !== "object") return res;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return (hint === "string" ? String : Number)(input);
}
function _toPropertyKey(arg) {
var key = _toPrimitive(arg, "string");
return typeof key === "symbol" ? key : String(key);
}
const isFiniteNumber = Number.isFinite || function (value) {
return typeof value === 'number' && isFinite(value);
};
let Events = /*#__PURE__*/function (Events) {
Events["MEDIA_ATTACHING"] = "hlsMediaAttaching";
Events["MEDIA_ATTACHED"] = "hlsMediaAttached";
Events["MEDIA_DETACHING"] = "hlsMediaDetaching";
Events["MEDIA_DETACHED"] = "hlsMediaDetached";
Events["BUFFER_RESET"] = "hlsBufferReset";
Events["BUFFER_CODECS"] = "hlsBufferCodecs";
Events["BUFFER_CREATED"] = "hlsBufferCreated";
Events["BUFFER_APPENDING"] = "hlsBufferAppending";
Events["BUFFER_APPENDED"] = "hlsBufferAppended";
Events["BUFFER_EOS"] = "hlsBufferEos";
Events["BUFFER_FLUSHING"] = "hlsBufferFlushing";
Events["BUFFER_FLUSHED"] = "hlsBufferFlushed";
Events["MANIFEST_LOADING"] = "hlsManifestLoading";
Events["MANIFEST_LOADED"] = "hlsManifestLoaded";
Events["MANIFEST_PARSED"] = "hlsManifestParsed";
Events["LEVEL_SWITCHING"] = "hlsLevelSwitching";
Events["LEVEL_SWITCHED"] = "hlsLevelSwitched";
Events["LEVEL_LOADING"] = "hlsLevelLoading";
Events["LEVEL_LOADED"] = "hlsLevelLoaded";
Events["LEVEL_UPDATED"] = "hlsLevelUpdated";
Events["LEVEL_PTS_UPDATED"] = "hlsLevelPtsUpdated";
Events["LEVELS_UPDATED"] = "hlsLevelsUpdated";
Events["AUDIO_TRACKS_UPDATED"] = "hlsAudioTracksUpdated";
Events["AUDIO_TRACK_SWITCHING"] = "hlsAudioTrackSwitching";
Events["AUDIO_TRACK_SWITCHED"] = "hlsAudioTrackSwitched";
Events["AUDIO_TRACK_LOADING"] = "hlsAudioTrackLoading";
Events["AUDIO_TRACK_LOADED"] = "hlsAudioTrackLoaded";
Events["SUBTITLE_TRACKS_UPDATED"] = "hlsSubtitleTracksUpdated";
Events["SUBTITLE_TRACKS_CLEARED"] = "hlsSubtitleTracksCleared";
Events["SUBTITLE_TRACK_SWITCH"] = "hlsSubtitleTrackSwitch";
Events["SUBTITLE_TRACK_LOADING"] = "hlsSubtitleTrackLoading";
Events["SUBTITLE_TRACK_LOADED"] = "hlsSubtitleTrackLoaded";
Events["SUBTITLE_FRAG_PROCESSED"] = "hlsSubtitleFragProcessed";
Events["CUES_PARSED"] = "hlsCuesParsed";
Events["NON_NATIVE_TEXT_TRACKS_FOUND"] = "hlsNonNativeTextTracksFound";
Events["INIT_PTS_FOUND"] = "hlsInitPtsFound";
Events["FRAG_LOADING"] = "hlsFragLoading";
Events["FRAG_LOAD_EMERGENCY_ABORTED"] = "hlsFragLoadEmergencyAborted";
Events["FRAG_LOADED"] = "hlsFragLoaded";
Events["FRAG_DECRYPTED"] = "hlsFragDecrypted";
Events["FRAG_PARSING_INIT_SEGMENT"] = "hlsFragParsingInitSegment";
Events["FRAG_PARSING_USERDATA"] = "hlsFragParsingUserdata";
Events["FRAG_PARSING_METADATA"] = "hlsFragParsingMetadata";
Events["FRAG_PARSED"] = "hlsFragParsed";
Events["FRAG_BUFFERED"] = "hlsFragBuffered";
Events["FRAG_CHANGED"] = "hlsFragChanged";
Events["FPS_DROP"] = "hlsFpsDrop";
Events["FPS_DROP_LEVEL_CAPPING"] = "hlsFpsDropLevelCapping";
Events["ERROR"] = "hlsError";
Events["DESTROYING"] = "hlsDestroying";
Events["KEY_LOADING"] = "hlsKeyLoading";
Events["KEY_LOADED"] = "hlsKeyLoaded";
Events["LIVE_BACK_BUFFER_REACHED"] = "hlsLiveBackBufferReached";
Events["BACK_BUFFER_REACHED"] = "hlsBackBufferReached";
return Events;
}({});
/**
* Defines each Event type and payload by Event name. Used in {@link hls.js#HlsEventEmitter} to strongly type the event listener API.
*/
let ErrorTypes = /*#__PURE__*/function (ErrorTypes) {
ErrorTypes["NETWORK_ERROR"] = "networkError";
ErrorTypes["MEDIA_ERROR"] = "mediaError";
ErrorTypes["KEY_SYSTEM_ERROR"] = "keySystemError";
ErrorTypes["MUX_ERROR"] = "muxError";
ErrorTypes["OTHER_ERROR"] = "otherError";
return ErrorTypes;
}({});
let ErrorDetails = /*#__PURE__*/function (ErrorDetails) {
ErrorDetails["KEY_SYSTEM_NO_KEYS"] = "keySystemNoKeys";
ErrorDetails["KEY_SYSTEM_NO_ACCESS"] = "keySystemNoAccess";
ErrorDetails["KEY_SYSTEM_NO_SESSION"] = "keySystemNoSession";
ErrorDetails["KEY_SYSTEM_NO_CONFIGURED_LICENSE"] = "keySystemNoConfiguredLicense";
ErrorDetails["KEY_SYSTEM_LICENSE_REQUEST_FAILED"] = "keySystemLicenseRequestFailed";
ErrorDetails["KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED"] = "keySystemServerCertificateRequestFailed";
ErrorDetails["KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED"] = "keySystemServerCertificateUpdateFailed";
ErrorDetails["KEY_SYSTEM_SESSION_UPDATE_FAILED"] = "keySystemSessionUpdateFailed";
ErrorDetails["KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED"] = "keySystemStatusOutputRestricted";
ErrorDetails["KEY_SYSTEM_STATUS_INTERNAL_ERROR"] = "keySystemStatusInternalError";
ErrorDetails["MANIFEST_LOAD_ERROR"] = "manifestLoadError";
ErrorDetails["MANIFEST_LOAD_TIMEOUT"] = "manifestLoadTimeOut";
ErrorDetails["MANIFEST_PARSING_ERROR"] = "manifestParsingError";
ErrorDetails["MANIFEST_INCOMPATIBLE_CODECS_ERROR"] = "manifestIncompatibleCodecsError";
ErrorDetails["LEVEL_EMPTY_ERROR"] = "levelEmptyError";
ErrorDetails["LEVEL_LOAD_ERROR"] = "levelLoadError";
ErrorDetails["LEVEL_LOAD_TIMEOUT"] = "levelLoadTimeOut";
ErrorDetails["LEVEL_PARSING_ERROR"] = "levelParsingError";
ErrorDetails["LEVEL_SWITCH_ERROR"] = "levelSwitchError";
ErrorDetails["AUDIO_TRACK_LOAD_ERROR"] = "audioTrackLoadError";
ErrorDetails["AUDIO_TRACK_LOAD_TIMEOUT"] = "audioTrackLoadTimeOut";
ErrorDetails["SUBTITLE_LOAD_ERROR"] = "subtitleTrackLoadError";
ErrorDetails["SUBTITLE_TRACK_LOAD_TIMEOUT"] = "subtitleTrackLoadTimeOut";
ErrorDetails["FRAG_LOAD_ERROR"] = "fragLoadError";
ErrorDetails["FRAG_LOAD_TIMEOUT"] = "fragLoadTimeOut";
ErrorDetails["FRAG_DECRYPT_ERROR"] = "fragDecryptError";
ErrorDetails["FRAG_PARSING_ERROR"] = "fragParsingError";
ErrorDetails["FRAG_GAP"] = "fragGap";
ErrorDetails["REMUX_ALLOC_ERROR"] = "remuxAllocError";
ErrorDetails["KEY_LOAD_ERROR"] = "keyLoadError";
ErrorDetails["KEY_LOAD_TIMEOUT"] = "keyLoadTimeOut";
ErrorDetails["BUFFER_ADD_CODEC_ERROR"] = "bufferAddCodecError";
ErrorDetails["BUFFER_INCOMPATIBLE_CODECS_ERROR"] = "bufferIncompatibleCodecsError";
ErrorDetails["BUFFER_APPEND_ERROR"] = "bufferAppendError";
ErrorDetails["BUFFER_APPENDING_ERROR"] = "bufferAppendingError";
ErrorDetails["BUFFER_STALLED_ERROR"] = "bufferStalledError";
ErrorDetails["BUFFER_FULL_ERROR"] = "bufferFullError";
ErrorDetails["BUFFER_SEEK_OVER_HOLE"] = "bufferSeekOverHole";
ErrorDetails["BUFFER_NUDGE_ON_STALL"] = "bufferNudgeOnStall";
ErrorDetails["INTERNAL_EXCEPTION"] = "internalException";
ErrorDetails["INTERNAL_ABORTED"] = "aborted";
ErrorDetails["UNKNOWN"] = "unknown";
return ErrorDetails;
}({});
const noop = function noop() {};
const fakeLogger = {
trace: noop,
debug: noop,
log: noop,
warn: noop,
info: noop,
error: noop
};
let exportedLogger = fakeLogger;
// let lastCallTime;
// function formatMsgWithTimeInfo(type, msg) {
// const now = Date.now();
// const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';
// lastCallTime = now;
// msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )';
// return msg;
// }
function consolePrintFn(type) {
const func = self.console[type];
if (func) {
return func.bind(self.console, `[${type}] >`);
}
return noop;
}
function exportLoggerFunctions(debugConfig, ...functions) {
functions.forEach(function (type) {
exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type);
});
}
function enableLogs(debugConfig, id) {
// check that console is available
if (self.console && debugConfig === true || typeof debugConfig === 'object') {
exportLoggerFunctions(debugConfig,
// Remove out from list here to hard-disable a log-level
// 'trace',
'debug', 'log', 'info', 'warn', 'error');
// Some browsers don't allow to use bind on console object anyway
// fallback to default if needed
try {
exportedLogger.log(`Debug logs enabled for "${id}" in hls.js version ${"1.4.1"}`);
} catch (e) {
exportedLogger = fakeLogger;
}
} else {
exportedLogger = fakeLogger;
}
}
const logger = exportedLogger;
const DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/;
const ATTR_LIST_REGEX = /(.+?)=(".*?"|.*?)(?:,|$)/g;
// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js
class AttrList {
constructor(attrs) {
if (typeof attrs === 'string') {
attrs = AttrList.parseAttrList(attrs);
}
for (const attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
if (attr.substring(0, 2) === 'X-') {
this.clientAttrs = this.clientAttrs || [];
this.clientAttrs.push(attr);
}
this[attr] = attrs[attr];
}
}
}
decimalInteger(attrName) {
const intValue = parseInt(this[attrName], 10);
if (intValue > Number.MAX_SAFE_INTEGER) {
return Infinity;
}
return intValue;
}
hexadecimalInteger(attrName) {
if (this[attrName]) {
let stringValue = (this[attrName] || '0x').slice(2);
stringValue = (stringValue.length & 1 ? '0' : '') + stringValue;
const value = new Uint8Array(stringValue.length / 2);
for (let i = 0; i < stringValue.length / 2; i++) {
value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16);
}
return value;
} else {
return null;
}
}
hexadecimalIntegerAsNumber(attrName) {
const intValue = parseInt(this[attrName], 16);
if (intValue > Number.MAX_SAFE_INTEGER) {
return Infinity;
}
return intValue;
}
decimalFloatingPoint(attrName) {
return parseFloat(this[attrName]);
}
optionalFloat(attrName, defaultValue) {
const value = this[attrName];
return value ? parseFloat(value) : defaultValue;
}
enumeratedString(attrName) {
return this[attrName];
}
bool(attrName) {
return this[attrName] === 'YES';
}
decimalResolution(attrName) {
const res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]);
if (res === null) {
return undefined;
}
return {
width: parseInt(res[1], 10),
height: parseInt(res[2], 10)
};
}
static parseAttrList(input) {
let match;
const attrs = {};
const quote = '"';
ATTR_LIST_REGEX.lastIndex = 0;
while ((match = ATTR_LIST_REGEX.exec(input)) !== null) {
let value = match[2];
if (value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1) {
value = value.slice(1, -1);
}
const name = match[1].trim();
attrs[name] = value;
}
return attrs;
}
}
// Avoid exporting const enum so that these values can be inlined
function isDateRangeCueAttribute(attrName) {
return attrName !== "ID" && attrName !== "CLASS" && attrName !== "START-DATE" && attrName !== "DURATION" && attrName !== "END-DATE" && attrName !== "END-ON-NEXT";
}
function isSCTE35Attribute(attrName) {
return attrName === "SCTE35-OUT" || attrName === "SCTE35-IN";
}
class DateRange {
constructor(dateRangeAttr, dateRangeWithSameId) {
this.attr = void 0;
this._startDate = void 0;
this._endDate = void 0;
this._badValueForSameId = void 0;
if (dateRangeWithSameId) {
const previousAttr = dateRangeWithSameId.attr;
for (const key in previousAttr) {
if (Object.prototype.hasOwnProperty.call(dateRangeAttr, key) && dateRangeAttr[key] !== previousAttr[key]) {
logger.warn(`DATERANGE tag attribute: "${key}" does not match for tags with ID: "${dateRangeAttr.ID}"`);
this._badValueForSameId = key;
break;
}
}
// Merge DateRange tags with the same ID
dateRangeAttr = _extends(new AttrList({}), previousAttr, dateRangeAttr);
}
this.attr = dateRangeAttr;
this._startDate = new Date(dateRangeAttr["START-DATE"]);
if ("END-DATE" in this.attr) {
const endDate = new Date(this.attr["END-DATE"]);
if (isFiniteNumber(endDate.getTime())) {
this._endDate = endDate;
}
}
}
get id() {
return this.attr.ID;
}
get class() {
return this.attr.CLASS;
}
get startDate() {
return this._startDate;
}
get endDate() {
if (this._endDate) {
return this._endDate;
}
const duration = this.duration;
if (duration !== null) {
return new Date(this._startDate.getTime() + duration * 1000);
}
return null;
}
get duration() {
if ("DURATION" in this.attr) {
const duration = this.attr.decimalFloatingPoint("DURATION");
if (isFiniteNumber(duration)) {
return duration;
}
} else if (this._endDate) {
return (this._endDate.getTime() - this._startDate.getTime()) / 1000;
}
return null;
}
get plannedDuration() {
if ("PLANNED-DURATION" in this.attr) {
return this.attr.decimalFloatingPoint("PLANNED-DURATION");
}
return null;
}
get endOnNext() {
return this.attr.bool("END-ON-NEXT");
}
get isValid() {
return !!this.id && !this._badValueForSameId && isFiniteNumber(this.startDate.getTime()) && (this.duration === null || this.duration >= 0) && (!this.endOnNext || !!this.class);
}
}
class LoadStats {
constructor() {
this.aborted = false;
this.loaded = 0;
this.retry = 0;
this.total = 0;
this.chunkCount = 0;
this.bwEstimate = 0;
this.loading = {
start: 0,
first: 0,
end: 0
};
this.parsing = {
start: 0,
end: 0
};
this.buffering = {
start: 0,
first: 0,
end: 0
};
}
}
var ElementaryStreamTypes = {
AUDIO: "audio",
VIDEO: "video",
AUDIOVIDEO: "audiovideo"
};
class BaseSegment {
// baseurl is the URL to the playlist
// relurl is the portion of the URL that comes from inside the playlist.
// Holds the types of data this fragment supports
constructor(baseurl) {
this._byteRange = null;
this._url = null;
this.baseurl = void 0;
this.relurl = void 0;
this.elementaryStreams = {
[ElementaryStreamTypes.AUDIO]: null,
[ElementaryStreamTypes.VIDEO]: null,
[ElementaryStreamTypes.AUDIOVIDEO]: null
};
this.baseurl = baseurl;
}
// setByteRange converts a EXT-X-BYTERANGE attribute into a two element array
setByteRange(value, previous) {
const params = value.split('@', 2);
const byteRange = [];
if (params.length === 1) {
byteRange[0] = previous ? previous.byteRangeEndOffset : 0;
} else {
byteRange[0] = parseInt(params[1]);
}
byteRange[1] = parseInt(params[0]) + byteRange[0];
this._byteRange = byteRange;
}
get byteRange() {
if (!this._byteRange) {
return [];
}
return this._byteRange;
}
get byteRangeStartOffset() {
return this.byteRange[0];
}
get byteRangeEndOffset() {
return this.byteRange[1];
}
get url() {
if (!this._url && this.baseurl && this.relurl) {
this._url = urlToolkitExports.buildAbsoluteURL(this.baseurl, this.relurl, {
alwaysNormalize: true
});
}
return this._url || '';
}
set url(value) {
this._url = value;
}
}
/**
* Object representing parsed data from an HLS Segment. Found in {@link hls.js#LevelDetails.fragments}.
*/
class Fragment extends BaseSegment {
// EXTINF has to be present for a m3u8 to be considered valid
// sn notates the sequence number for a segment, and if set to a string can be 'initSegment'
// levelkeys are the EXT-X-KEY tags that apply to this segment for decryption
// core difference from the private field _decryptdata is the lack of the initialized IV
// _decryptdata will set the IV for this segment based on the segment number in the fragment
// A string representing the fragment type
// A reference to the loader. Set while the fragment is loading, and removed afterwards. Used to abort fragment loading
// A reference to the key loader. Set while the key is loading, and removed afterwards. Used to abort key loading
// The level/track index to which the fragment belongs
// The continuity counter of the fragment
// The starting Presentation Time Stamp (PTS) of the fragment. Set after transmux complete.
// The ending Presentation Time Stamp (PTS) of the fragment. Set after transmux complete.
// The starting Decode Time Stamp (DTS) of the fragment. Set after transmux complete.
// The ending Decode Time Stamp (DTS) of the fragment. Set after transmux complete.
// The start time of the fragment, as listed in the manifest. Updated after transmux complete.
// Set by `updateFragPTSDTS` in level-helper
// The maximum starting Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete.
// The minimum ending Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete.
// Load/parse timing information
// A flag indicating whether the segment was downloaded in order to test bitrate, and was not buffered
// #EXTINF segment title
// The Media Initialization Section for this segment
// Fragment is the last fragment in the media playlist
// Fragment is marked by an EXT-X-GAP tag indicating that it does not contain media data and should not be loaded
constructor(type, baseurl) {
super(baseurl);
this._decryptdata = null;
this.rawProgramDateTime = null;
this.programDateTime = null;
this.tagList = [];
this.duration = 0;
this.sn = 0;
this.levelkeys = void 0;
this.type = void 0;
this.loader = null;
this.keyLoader = null;
this.level = -1;
this.cc = 0;
this.startPTS = void 0;
this.endPTS = void 0;
this.startDTS = void 0;
this.endDTS = void 0;
this.start = 0;
this.deltaPTS = void 0;
this.maxStartPTS = void 0;
this.minEndPTS = void 0;
this.stats = new LoadStats();
this.urlId = 0;
this.data = void 0;
this.bitrateTest = false;
this.title = null;
this.initSegment = null;
this.endList = void 0;
this.gap = void 0;
this.type = type;
}
get decryptdata() {
const {
levelkeys
} = this;
if (!levelkeys && !this._decryptdata) {
return null;
}
if (!this._decryptdata && this.levelkeys && !this.levelkeys.NONE) {
const key = this.levelkeys.identity;
if (key) {
this._decryptdata = key.getDecryptData(this.sn);
} else {
const keyFormats = Object.keys(this.levelkeys);
if (keyFormats.length === 1) {
return this._decryptdata = this.levelkeys[keyFormats[0]].getDecryptData(this.sn);
}
}
}
return this._decryptdata;
}
get end() {
return this.start + this.duration;
}
get endProgramDateTime() {
if (this.programDateTime === null) {
return null;
}
if (!isFiniteNumber(this.programDateTime)) {
return null;
}
const duration = !isFiniteNumber(this.duration) ? 0 : this.duration;
return this.programDateTime + duration * 1000;
}
get encrypted() {
var _this$_decryptdata;
// At the m3u8-parser level we need to add support for manifest signalled keyformats
// when we want the fragment to start reporting that it is encrypted.
// Currently, keyFormat will only be set for identity keys
if ((_this$_decryptdata = this._decryptdata) != null && _this$_decryptdata.encrypted) {
return true;
} else if (this.levelkeys) {
const keyFormats = Object.keys(this.levelkeys);
const len = keyFormats.length;
if (len > 1 || len === 1 && this.levelkeys[keyFormats[0]].encrypted) {
return true;
}
}
return false;
}
setKeyFormat(keyFormat) {
if (this.levelkeys) {
const key = this.levelkeys[keyFormat];
if (key && !this._decryptdata) {
this._decryptdata = key.getDecryptData(this.sn);
}
}
}
abortRequests() {
var _this$loader, _this$keyLoader;
(_this$loader = this.loader) == null ? void 0 : _this$loader.abort();
(_this$keyLoader = this.keyLoader) == null ? void 0 : _this$keyLoader.abort();
}
setElementaryStreamInfo(type, startPTS, endPTS, startDTS, endDTS, partial = false) {
const {
elementaryStreams
} = this;
const info = elementaryStreams[type];
if (!info) {
elementaryStreams[type] = {
startPTS,
endPTS,
startDTS,
endDTS,
partial
};
return;
}
info.startPTS = Math.min(info.startPTS, startPTS);
info.endPTS = Math.max(info.endPTS, endPTS);
info.startDTS = Math.min(info.startDTS, startDTS);
info.endDTS = Math.max(info.endDTS, endDTS);
}
clearElementaryStreamInfo() {
const {
elementaryStreams
} = this;
elementaryStreams[ElementaryStreamTypes.AUDIO] = null;
elementaryStreams[ElementaryStreamTypes.VIDEO] = null;
elementaryStreams[ElementaryStreamTypes.AUDIOVIDEO] = null;
}
}
/**
* Object representing parsed data from an HLS Partial Segment. Found in {@link hls.js#LevelDetails.partList}.
*/
class Part extends BaseSegment {
constructor(partAttrs, frag, baseurl, index, previous) {
super(baseurl);
this.fragOffset = 0;
this.duration = 0;
this.gap = false;
this.independent = false;
this.relurl = void 0;
this.fragment = void 0;
this.index = void 0;
this.stats = new LoadStats();
this.duration = partAttrs.decimalFloatingPoint('DURATION');
this.gap = partAttrs.bool('GAP');
this.independent = partAttrs.bool('INDEPENDENT');
this.relurl = partAttrs.enumeratedString('URI');
this.fragment = frag;
this.index = index;
const byteRange = partAttrs.enumeratedString('BYTERANGE');
if (byteRange) {
this.setByteRange(byteRange, previous);
}
if (previous) {
this.fragOffset = previous.fragOffset + previous.duration;
}
}
get start() {
return this.fragment.start + this.fragOffset;
}
get end() {
return this.start + this.duration;
}
get loaded() {
const {
elementaryStreams
} = this;
return !!(elementaryStreams.audio || elementaryStreams.video || elementaryStreams.audiovideo);
}
}
const DEFAULT_TARGET_DURATION = 10;
/**
* Object representing parsed data from an HLS Media Playlist. Found in {@link hls.js#Level.details}.
*/
class LevelDetails {
// Manifest reload synchronization
constructor(baseUrl) {
this.PTSKnown = false;
this.alignedSliding = false;
this.averagetargetduration = void 0;
this.endCC = 0;
this.endSN = 0;
this.fragments = void 0;
this.fragmentHint = void 0;
this.partList = null;
this.dateRanges = void 0;
this.live = true;
this.ageHeader = 0;
this.advancedDateTime = void 0;
this.updated = true;
this.advanced = true;
this.availabilityDelay = void 0;
this.misses = 0;
this.startCC = 0;
this.startSN = 0;
this.startTimeOffset = null;
this.targetduration = 0;
this.totalduration = 0;
this.type = null;
this.url = void 0;
this.m3u8 = '';
this.version = null;
this.canBlockReload = false;
this.canSkipUntil = 0;
this.canSkipDateRanges = false;
this.skippedSegments = 0;
this.recentlyRemovedDateranges = void 0;
this.partHoldBack = 0;
this.holdBack = 0;
this.partTarget = 0;
this.preloadHint = void 0;
this.renditionReports = void 0;
this.tuneInGoal = 0;
this.deltaUpdateFailed = void 0;
this.driftStartTime = 0;
this.driftEndTime = 0;
this.driftStart = 0;
this.driftEnd = 0;
this.encryptedFragments = void 0;
this.playlistParsingError = null;
this.variableList = null;
this.hasVariableRefs = false;
this.fragments = [];
this.encryptedFragments = [];
this.dateRanges = {};
this.url = baseUrl;
}
reloaded(previous) {
if (!previous) {
this.advanced = true;
this.updated = true;
return;
}
const partSnDiff = this.lastPartSn - previous.lastPartSn;
const partIndexDiff = this.lastPartIndex - previous.lastPartIndex;
this.updated = this.endSN !== previous.endSN || !!partIndexDiff || !!partSnDiff;
this.advanced = this.endSN > previous.endSN || partSnDiff > 0 || partSnDiff === 0 && partIndexDiff > 0;
if (this.updated || this.advanced) {
this.misses = Math.floor(previous.misses * 0.6);
} else {
this.misses = previous.misses + 1;
}
this.availabilityDelay = previous.availabilityDelay;
}
get hasProgramDateTime() {
if (this.fragments.length) {
return isFiniteNumber(this.fragments[this.fragments.length - 1].programDateTime);
}
return false;
}
get levelTargetDuration() {
return this.averagetargetduration || this.targetduration || DEFAULT_TARGET_DURATION;
}
get drift() {
const runTime = this.driftEndTime - this.driftStartTime;
if (runTime > 0) {
const runDuration = this.driftEnd - this.driftStart;
return runDuration * 1000 / runTime;
}
return 1;
}
get edge() {
return this.partEnd || this.fragmentEnd;
}
get partEnd() {
var _this$partList;
if ((_this$partList = this.partList) != null && _this$partList.length) {
return this.partList[this.partList.length - 1].end;
}
return this.fragmentEnd;
}
get fragmentEnd() {
var _this$fragments;
if ((_this$fragments = this.fragments) != null && _this$fragments.length) {
return this.fragments[this.fragments.length - 1].end;
}
return 0;
}
get age() {
if (this.advancedDateTime) {
return Math.max(Date.now() - this.advancedDateTime, 0) / 1000;
}
return 0;
}
get lastPartIndex() {
var _this$partList2;
if ((_this$partList2 = this.partList) != null && _this$partList2.length) {
return this.partList[this.partList.length - 1].index;
}
return -1;
}
get lastPartSn() {
var _this$partList3;
if ((_this$partList3 = this.partList) != null && _this$partList3.length) {
return this.partList[this.partList.length - 1].fragment.sn;
}
return this.endSN;
}
}
function base64Decode(base64encodedStr) {
return Uint8Array.from(atob(base64encodedStr), c => c.charCodeAt(0));
}
function getKeyIdBytes(str) {
const keyIdbytes = strToUtf8array(str).subarray(0, 16);
const paddedkeyIdbytes = new Uint8Array(16);
paddedkeyIdbytes.set(keyIdbytes, 16 - keyIdbytes.length);
return paddedkeyIdbytes;
}
function changeEndianness(keyId) {
const swap = function swap(array, from, to) {
const cur = array[from];
array[from] = array[to];
array[to] = cur;
};
swap(keyId, 0, 3);
swap(keyId, 1, 2);
swap(keyId, 4, 5);
swap(keyId, 6, 7);
}
function convertDataUriToArrayBytes(uri) {
// data:[<media type][;attribute=value][;base64],<data>
const colonsplit = uri.split(':');
let keydata = null;
if (colonsplit[0] === 'data' && colonsplit.length === 2) {
const semicolonsplit = colonsplit[1].split(';');
const commasplit = semicolonsplit[semicolonsplit.length - 1].split(',');
if (commasplit.length === 2) {
const isbase64 = commasplit[0] === 'base64';
const data = commasplit[1];
if (isbase64) {
semicolonsplit.splice(-1, 1); // remove from processing
keydata = base64Decode(data);
} else {
keydata = getKeyIdBytes(data);
}
}
}
return keydata;
}
function strToUtf8array(str) {
return Uint8Array.from(unescape(encodeURIComponent(str)), c => c.charCodeAt(0));
}
/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMediaKeySystemAccess
*/
var KeySystems = {
CLEARKEY: "org.w3.clearkey",
FAIRPLAY: "com.apple.fps",
PLAYREADY: "com.microsoft.playready",
WIDEVINE: "com.widevine.alpha"
};
// Playlist #EXT-X-KEY KEYFORMAT values
var KeySystemFormats = {
CLEARKEY: "org.w3.clearkey",
FAIRPLAY: "com.apple.streamingkeydelivery",
PLAYREADY: "com.microsoft.playready",
WIDEVINE: "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
};
function keySystemFormatToKeySystemDomain(format) {
switch (format) {
case KeySystemFormats.FAIRPLAY:
return KeySystems.FAIRPLAY;
case KeySystemFormats.PLAYREADY:
return KeySystems.PLAYREADY;
case KeySystemFormats.WIDEVINE:
return KeySystems.WIDEVINE;
case KeySystemFormats.CLEARKEY:
return KeySystems.CLEARKEY;
}
}
// System IDs for which we can extract a key ID from "encrypted" event PSSH
var KeySystemIds = {
WIDEVINE: "edef8ba979d64acea3c827dcd51d21ed"
};
function keySystemIdToKeySystemDomain(systemId) {
if (systemId === KeySystemIds.WIDEVINE) {
return KeySystems.WIDEVINE;
// } else if (systemId === KeySystemIds.PLAYREADY) {
// return KeySystems.PLAYREADY;
// } else if (systemId === KeySystemIds.CENC || systemId === KeySystemIds.CLEARKEY) {
// return KeySystems.CLEARKEY;
}
}
function keySystemDomainToKeySystemFormat(keySystem) {
switch (keySystem) {
case KeySystems.FAIRPLAY:
return KeySystemFormats.FAIRPLAY;
case KeySystems.PLAYREADY:
return KeySystemFormats.PLAYREADY;
case KeySystems.WIDEVINE:
return KeySystemFormats.WIDEVINE;
case KeySystems.CLEARKEY:
return KeySystemFormats.CLEARKEY;
}
}
function getKeySystemsForConfig(config) {
const {
drmSystems,
widevineLicenseUrl
} = config;
const keySystemsToAttempt = drmSystems ? [KeySystems.FAIRPLAY, KeySystems.WIDEVINE, KeySystems.PLAYREADY, KeySystems.CLEARKEY].filter(keySystem => !!drmSystems[keySystem]) : [];
if (!keySystemsToAttempt[KeySystems.WIDEVINE] && widevineLicenseUrl) {
keySystemsToAttempt.push(KeySystems.WIDEVINE);
}
return keySystemsToAttempt;
}
const requestMediaKeySystemAccess = function () {
if (typeof self !== 'undefined' && self.navigator && self.navigator.requestMediaKeySystemAccess) {
return self.navigator.requestMediaKeySystemAccess.bind(self.navigator);
} else {
return null;
}
}();
/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemConfiguration
*/
function getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs, drmSystemOptions) {
let initDataTypes;
switch (keySystem) {
case KeySystems.FAIRPLAY:
initDataTypes = ['cenc', 'sinf'];
break;
case KeySystems.WIDEVINE:
case KeySystems.PLAYREADY:
initDataTypes = ['cenc'];
break;
case KeySystems.CLEARKEY:
initDataTypes = ['cenc', 'keyids'];
break;
default:
throw new Error(`Unknown key-system: ${keySystem}`);
}
return createMediaKeySystemConfigurations(initDataTypes, audioCodecs, videoCodecs, drmSystemOptions);
}
function createMediaKeySystemConfigurations(initDataTypes, audioCodecs, videoCodecs, drmSystemOptions) {
const baseConfig = {
initDataTypes: initDataTypes,
persistentState: drmSystemOptions.persistentState || 'not-allowed',
distinctiveIdentifier: drmSystemOptions.distinctiveIdentifier || 'not-allowed',
sessionTypes: drmSystemOptions.sessionTypes || [drmSystemOptions.sessionType || 'temporary'],
audioCapabilities: audioCodecs.map(codec => ({
contentType: `audio/mp4; codecs="${codec}"`,
robustness: drmSystemOptions.audioRobustness || '',
encryptionScheme: drmSystemOptions.audioEncryptionScheme || null
})),
videoCapabilities: videoCodecs.map(codec => ({
contentType: `video/mp4; codecs="${codec}"`,
robustness: drmSystemOptions.videoRobustness || '',
encryptionScheme: drmSystemOptions.videoEncryptionScheme || null
}))
};
return [baseConfig];
}
function sliceUint8(array, start, end) {
// @ts-expect-error This polyfills IE11 usage of Uint8Array slice.
// It always exists in the TypeScript definition so fails, but it fails at runtime on IE11.
return Uint8Array.prototype.slice ? array.slice(start, end) : new Uint8Array(Array.prototype.slice.call(array, start, end));
}
// breaking up those two types in order to clarify what is happening in the decoding path.
/**
* Returns true if an ID3 header can be found at offset in data
* @param data - The data to search
* @param offset - The offset at which to start searching
*/
const isHeader$2 = (data, offset) => {
/*
* http://id3.org/id3v2.3.0
* [0] = 'I'
* [1] = 'D'
* [2] = '3'
* [3,4] = {Version}
* [5] = {Flags}
* [6-9] = {ID3 Size}
*
* An ID3v2 tag can be detected with the following pattern:
* $49 44 33 yy yy xx zz zz zz zz
* Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80
*/
if (offset + 10 <= data.length) {
// look for 'ID3' identifier
if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) {
// check version is within range
if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {
// check size is within range
if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
return true;
}
}
}
}
return false;
};
/**
* Returns true if an ID3 footer can be found at offset in data
* @param data - The data to search
* @param offset - The offset at which to start searching
*/
const isFooter = (data, offset) => {
/*
* The footer is a copy of the header, but with a different identifier
*/
if (offset + 10 <= data.length) {
// look for '3DI' identifier
if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) {
// check version is within range
if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) {
// check size is within range
if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
return true;
}
}
}
}
return false;
};
/**
* Returns any adjacent ID3 tags found in data starting at offset, as one block of data
* @param data - The data to search in
* @param offset - The offset at which to start searching
* @returns the block of data containing any ID3 tags found
* or *undefined* if no header is found at the starting offset
*/
const getID3Data = (data, offset) => {
const front = offset;
let length = 0;
while (isHeader$2(data, offset)) {
// ID3 header is 10 bytes
length += 10;
const size = readSize(data, offset + 6);
length += size;
if (isFooter(data, offset + 10)) {
// ID3 footer is 10 bytes
length += 10;
}
offset += length;
}
if (length > 0) {
return data.subarray(front, front + length);
}
return undefined;
};
const readSize = (data, offset) => {
let size = 0;
size = (data[offset] & 0x7f) << 21;
size |= (data[offset + 1] & 0x7f) << 14;
size |= (data[offset + 2] & 0x7f) << 7;
size |= data[offset + 3] & 0x7f;
return size;
};
const canParse$2 = (data, offset) => {
return isHeader$2(data, offset) && readSize(data, offset + 6) + 10 <= data.length - offset;
};
/**
* Searches for the Elementary Stream timestamp found in the ID3 data chunk
* @param data - Block of data containing one or more ID3 tags
*/
const getTimeStamp = data => {
const frames = getID3Frames(data);
for (let i = 0; i < frames.length; i++) {
const frame = frames[i];
if (isTimeStampFrame(frame)) {
return readTimeStamp(frame);
}
}
return undefined;
};
/**
* Returns true if the ID3 frame is an Elementary Stream timestamp frame
*/
const isTimeStampFrame = frame => {
return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp';
};
const getFrameData = data => {
/*
Frame ID $xx xx xx xx (four characters)
Size $xx xx xx xx
Flags $xx xx
*/
const type = String.fromCharCode(data[0], data[1], data[2], data[3]);
const size = readSize(data, 4);
// skip frame id, size, and flags
const offset = 10;
return {
type,
size,
data: data.subarray(offset, offset + size)
};
};
/**
* Returns an array of ID3 frames found in all the ID3 tags in the id3Data
* @param id3Data - The ID3 data containing one or more ID3 tags
*/
const getID3Frames = id3Data => {
let offset = 0;
const frames = [];
while (isHeader$2(id3Data, offset)) {
const size = readSize(id3Data, offset + 6);
// skip past ID3 header
offset += 10;
const end = offset + size;
// loop through frames in the ID3 tag
while (offset + 8 < end) {
const frameData = getFrameData(id3Data.subarray(offset));
const frame = decodeFrame(frameData);
if (frame) {
frames.push(frame);
}
// skip frame header and frame data
offset += frameData.size + 10;
}
if (isFooter(id3Data, offset)) {
offset += 10;
}
}
return frames;
};
const decodeFrame = frame => {
if (frame.type === 'PRIV') {
return decodePrivFrame(frame);
} else if (frame.type[0] === 'W') {
return decodeURLFrame(frame);
}
return decodeTextFrame(frame);
};
const decodePrivFrame = frame => {
/*
Format: <text string>\0<binary data>
*/
if (frame.size < 2) {
return undefined;
}
const owner = utf8ArrayToStr(frame.data, true);
const privateData = new Uint8Array(frame.data.subarray(owner.length + 1));
return {
key: frame.type,
info: owner,
data: privateData.buffer
};
};
const decodeTextFrame = frame => {
if (frame.size < 2) {
return undefined;
}
if (frame.type === 'TXXX') {
/*
Format:
[0] = {Text Encoding}
[1-?] = {Description}\0{Value}
*/
let index = 1;
const description = utf8ArrayToStr(frame.data.subarray(index), true);
index += description.length + 1;
const value = utf8ArrayToStr(frame.data.subarray(index));
return {
key: frame.type,
info: description,
data: value
};
}
/*
Format:
[0] = {Text Encoding}
[1-?] = {Value}
*/
const text = utf8ArrayToStr(frame.data.subarray(1));
return {
key: frame.type,
data: text
};
};
const decodeURLFrame = frame => {
if (frame.type === 'WXXX') {
/*
Format:
[0] = {Text Encoding}
[1-?] = {Description}\0{URL}
*/
if (frame.size < 2) {
return undefined;
}
let index = 1;
const description = utf8ArrayToStr(frame.data.subarray(index), true);
index += description.length + 1;
const value = utf8ArrayToStr(frame.data.subarray(index));
return {
key: frame.type,
info: description,
data: value
};
}
/*
Format:
[0-?] = {URL}
*/
const url = utf8ArrayToStr(frame.data);
return {
key: frame.type,
data: url
};
};
const readTimeStamp = timeStampFrame => {
if (timeStampFrame.data.byteLength === 8) {
const data = new Uint8Array(timeStampFrame.data);
// timestamp is 33 bit expressed as a big-endian eight-octet number,
// with the upper 31 bits set to zero.
const pts33Bit = data[3] & 0x1;
let timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7];
timestamp /= 45;
if (pts33Bit) {
timestamp += 47721858.84;
} // 2^32 / 90
return Math.round(timestamp);
}
return undefined;
};
// http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
/* utf.js - UTF-8 <=> UTF-16 convertion
*
* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
* Version: 1.0
* LastModified: Dec 25 1999
* This library is free. You can redistribute it and/or modify it.
*/
const utf8ArrayToStr = (array, exitOnNull = false) => {
const decoder = getTextDecoder();
if (decoder) {
const decoded = decoder.decode(array);
if (exitOnNull) {
// grab up to the first null
const idx = decoded.indexOf('\0');
return idx !== -1 ? decoded.substring(0, idx) : decoded;
}
// remove any null characters
return decoded.replace(/\0/g, '');
}
const len = array.length;
let c;
let char2;
let char3;
let out = '';
let i = 0;
while (i < len) {
c = array[i++];
if (c === 0x00 && exitOnNull) {
return out;
} else if (c === 0x00 || c === 0x03) {
// If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it
continue;
}
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12:
case 13:
// 110x xxxx 10xx xxxx
char2 = array[i++];
out += String.fromCharCode((c & 0x1f) << 6 | char2 & 0x3f);
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = array[i++];
char3 = array[i++];
out += String.fromCharCode((c & 0x0f) << 12 | (char2 & 0x3f) << 6 | (char3 & 0x3f) << 0);
break;
}
}
return out;
};
let decoder;
function getTextDecoder() {
if (!decoder && typeof self.TextDecoder !== 'undefined') {
decoder = new self.TextDecoder('utf-8');
}
return decoder;
}
/**
* hex du