123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494 |
- /*! @name @videojs/vhs-utils @version 4.0.0 @license MIT */
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.vhsUtils = factory());
- })(this, (function () { 'use strict';
- const regexs = {
- // to determine mime types
- mp4: /^(av0?1|avc0?[1234]|vp0?9|flac|opus|mp3|mp4a|mp4v|stpp.ttml.im1t)/,
- webm: /^(vp0?[89]|av0?1|opus|vorbis)/,
- ogg: /^(vp0?[89]|theora|flac|opus|vorbis)/,
- // to determine if a codec is audio or video
- video: /^(av0?1|avc0?[1234]|vp0?[89]|hvc1|hev1|theora|mp4v)/,
- audio: /^(mp4a|flac|vorbis|opus|ac-[34]|ec-3|alac|mp3|speex|aac)/,
- text: /^(stpp.ttml.im1t)/,
- // mux.js support regex
- muxerVideo: /^(avc0?1)/,
- muxerAudio: /^(mp4a)/,
- // match nothing as muxer does not support text right now.
- // there cannot never be a character before the start of a string
- // so this matches nothing.
- muxerText: /a^/
- };
- const mediaTypes = ['video', 'audio', 'text'];
- const upperMediaTypes = ['Video', 'Audio', 'Text'];
- /**
- * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
- * `avc1.<hhhhhh>`
- *
- * @param {string} codec
- * Codec string to translate
- * @return {string}
- * The translated codec string
- */
- const translateLegacyCodec = function (codec) {
- if (!codec) {
- return codec;
- }
- return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
- const profileHex = ('00' + Number(profile).toString(16)).slice(-2);
- const avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
- return 'avc1.' + profileHex + '00' + avcLevelHex;
- });
- };
- /**
- * Replace the old apple-style `avc1.<dd>.<dd>` codec strings with the standard
- * `avc1.<hhhhhh>`
- *
- * @param {string[]} codecs
- * An array of codec strings to translate
- * @return {string[]}
- * The translated array of codec strings
- */
- const translateLegacyCodecs = function (codecs) {
- return codecs.map(translateLegacyCodec);
- };
- /**
- * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
- * standard `avc1.<hhhhhh>`.
- *
- * @param {string} codecString
- * The codec string
- * @return {string}
- * The codec string with old apple-style codecs replaced
- *
- * @private
- */
- const mapLegacyAvcCodecs = function (codecString) {
- return codecString.replace(/avc1\.(\d+)\.(\d+)/i, match => {
- return translateLegacyCodecs([match])[0];
- });
- };
- /**
- * @typedef {Object} ParsedCodecInfo
- * @property {number} codecCount
- * Number of codecs parsed
- * @property {string} [videoCodec]
- * Parsed video codec (if found)
- * @property {string} [videoObjectTypeIndicator]
- * Video object type indicator (if found)
- * @property {string|null} audioProfile
- * Audio profile
- */
- /**
- * Parses a codec string to retrieve the number of codecs specified, the video codec and
- * object type indicator, and the audio profile.
- *
- * @param {string} [codecString]
- * The codec string to parse
- * @return {ParsedCodecInfo}
- * Parsed codec info
- */
- const parseCodecs = function (codecString = '') {
- const codecs = codecString.split(',');
- const result = [];
- codecs.forEach(function (codec) {
- codec = codec.trim();
- let codecType;
- mediaTypes.forEach(function (name) {
- const match = regexs[name].exec(codec.toLowerCase());
- if (!match || match.length <= 1) {
- return;
- }
- codecType = name; // maintain codec case
- const type = codec.substring(0, match[1].length);
- const details = codec.replace(type, '');
- result.push({
- type,
- details,
- mediaType: name
- });
- });
- if (!codecType) {
- result.push({
- type: codec,
- details: '',
- mediaType: 'unknown'
- });
- }
- });
- return result;
- };
- /**
- * Returns a ParsedCodecInfo object for the default alternate audio playlist if there is
- * a default alternate audio playlist for the provided audio group.
- *
- * @param {Object} master
- * The master playlist
- * @param {string} audioGroupId
- * ID of the audio group for which to find the default codec info
- * @return {ParsedCodecInfo}
- * Parsed codec info
- */
- const codecsFromDefault = (master, audioGroupId) => {
- if (!master.mediaGroups.AUDIO || !audioGroupId) {
- return null;
- }
- const audioGroup = master.mediaGroups.AUDIO[audioGroupId];
- if (!audioGroup) {
- return null;
- }
- for (const name in audioGroup) {
- const audioType = audioGroup[name];
- if (audioType.default && audioType.playlists) {
- // codec should be the same for all playlists within the audio type
- return parseCodecs(audioType.playlists[0].attributes.CODECS);
- }
- }
- return null;
- };
- const isVideoCodec = (codec = '') => regexs.video.test(codec.trim().toLowerCase());
- const isAudioCodec = (codec = '') => regexs.audio.test(codec.trim().toLowerCase());
- const isTextCodec = (codec = '') => regexs.text.test(codec.trim().toLowerCase());
- const getMimeForCodec = codecString => {
- if (!codecString || typeof codecString !== 'string') {
- return;
- }
- const codecs = codecString.toLowerCase().split(',').map(c => translateLegacyCodec(c.trim())); // default to video type
- let type = 'video'; // only change to audio type if the only codec we have is
- // audio
- if (codecs.length === 1 && isAudioCodec(codecs[0])) {
- type = 'audio';
- } else if (codecs.length === 1 && isTextCodec(codecs[0])) {
- // text uses application/<container> for now
- type = 'application';
- } // default the container to mp4
- let container = 'mp4'; // every codec must be able to go into the container
- // for that container to be the correct one
- if (codecs.every(c => regexs.mp4.test(c))) {
- container = 'mp4';
- } else if (codecs.every(c => regexs.webm.test(c))) {
- container = 'webm';
- } else if (codecs.every(c => regexs.ogg.test(c))) {
- container = 'ogg';
- }
- return `${type}/${container};codecs="${codecString}"`;
- };
- const browserSupportsCodec = (codecString = '') => window.MediaSource && window.MediaSource.isTypeSupported && window.MediaSource.isTypeSupported(getMimeForCodec(codecString)) || false;
- const muxerSupportsCodec = (codecString = '') => codecString.toLowerCase().split(',').every(codec => {
- codec = codec.trim(); // any match is supported.
- for (let i = 0; i < upperMediaTypes.length; i++) {
- const type = upperMediaTypes[i];
- if (regexs[`muxer${type}`].test(codec)) {
- return true;
- }
- }
- return false;
- });
- const DEFAULT_AUDIO_CODEC = 'mp4a.40.2';
- const DEFAULT_VIDEO_CODEC = 'avc1.4d400d';
- var codecs = /*#__PURE__*/Object.freeze({
- __proto__: null,
- translateLegacyCodec: translateLegacyCodec,
- translateLegacyCodecs: translateLegacyCodecs,
- mapLegacyAvcCodecs: mapLegacyAvcCodecs,
- parseCodecs: parseCodecs,
- codecsFromDefault: codecsFromDefault,
- isVideoCodec: isVideoCodec,
- isAudioCodec: isAudioCodec,
- isTextCodec: isTextCodec,
- getMimeForCodec: getMimeForCodec,
- browserSupportsCodec: browserSupportsCodec,
- muxerSupportsCodec: muxerSupportsCodec,
- DEFAULT_AUDIO_CODEC: DEFAULT_AUDIO_CODEC,
- DEFAULT_VIDEO_CODEC: DEFAULT_VIDEO_CODEC
- });
- // const log2 = Math.log2 ? Math.log2 : (x) => (Math.log(x) / Math.log(2));
- const repeat = function (str, len) {
- let acc = '';
- while (len--) {
- acc += str;
- }
- return acc;
- }; // count the number of bits it would take to represent a number
- // we used to do this with log2 but BigInt does not support builtin math
- // Math.ceil(log2(x));
- const countBits = x => x.toString(2).length; // count the number of whole bytes it would take to represent a number
- const countBytes = x => Math.ceil(countBits(x) / 8);
- const padStart = (b, len, str = ' ') => (repeat(str, len) + b.toString()).slice(-len);
- const isArrayBufferView = obj => {
- if (ArrayBuffer.isView === 'function') {
- return ArrayBuffer.isView(obj);
- }
- return obj && obj.buffer instanceof ArrayBuffer;
- };
- const isTypedArray = obj => isArrayBufferView(obj);
- const toUint8 = function (bytes) {
- if (bytes instanceof Uint8Array) {
- return bytes;
- }
- if (!Array.isArray(bytes) && !isTypedArray(bytes) && !(bytes instanceof ArrayBuffer)) {
- // any non-number or NaN leads to empty uint8array
- // eslint-disable-next-line
- if (typeof bytes !== 'number' || typeof bytes === 'number' && bytes !== bytes) {
- bytes = 0;
- } else {
- bytes = [bytes];
- }
- }
- return new Uint8Array(bytes && bytes.buffer || bytes, bytes && bytes.byteOffset || 0, bytes && bytes.byteLength || 0);
- };
- const toHexString = function (bytes) {
- bytes = toUint8(bytes);
- let str = '';
- for (let i = 0; i < bytes.length; i++) {
- str += padStart(bytes[i].toString(16), 2, '0');
- }
- return str;
- };
- const toBinaryString = function (bytes) {
- bytes = toUint8(bytes);
- let str = '';
- for (let i = 0; i < bytes.length; i++) {
- str += padStart(bytes[i].toString(2), 8, '0');
- }
- return str;
- };
- const BigInt = window.BigInt || Number;
- const BYTE_TABLE = [BigInt('0x1'), BigInt('0x100'), BigInt('0x10000'), BigInt('0x1000000'), BigInt('0x100000000'), BigInt('0x10000000000'), BigInt('0x1000000000000'), BigInt('0x100000000000000'), BigInt('0x10000000000000000')];
- const ENDIANNESS = function () {
- const a = new Uint16Array([0xFFCC]);
- const b = new Uint8Array(a.buffer, a.byteOffset, a.byteLength);
- if (b[0] === 0xFF) {
- return 'big';
- }
- if (b[0] === 0xCC) {
- return 'little';
- }
- return 'unknown';
- }();
- const IS_BIG_ENDIAN = ENDIANNESS === 'big';
- const IS_LITTLE_ENDIAN = ENDIANNESS === 'little';
- const bytesToNumber = function (bytes, {
- signed = false,
- le = false
- } = {}) {
- bytes = toUint8(bytes);
- const fn = le ? 'reduce' : 'reduceRight';
- const obj = bytes[fn] ? bytes[fn] : Array.prototype[fn];
- let number = obj.call(bytes, function (total, byte, i) {
- const exponent = le ? i : Math.abs(i + 1 - bytes.length);
- return total + BigInt(byte) * BYTE_TABLE[exponent];
- }, BigInt(0));
- if (signed) {
- const max = BYTE_TABLE[bytes.length] / BigInt(2) - BigInt(1);
- number = BigInt(number);
- if (number > max) {
- number -= max;
- number -= max;
- number -= BigInt(2);
- }
- }
- return Number(number);
- };
- const numberToBytes = function (number, {
- le = false
- } = {}) {
- // eslint-disable-next-line
- if (typeof number !== 'bigint' && typeof number !== 'number' || typeof number === 'number' && number !== number) {
- number = 0;
- }
- number = BigInt(number);
- const byteCount = countBytes(number);
- const bytes = new Uint8Array(new ArrayBuffer(byteCount));
- for (let i = 0; i < byteCount; i++) {
- const byteIndex = le ? i : Math.abs(i + 1 - bytes.length);
- bytes[byteIndex] = Number(number / BYTE_TABLE[i] & BigInt(0xFF));
- if (number < 0) {
- bytes[byteIndex] = Math.abs(~bytes[byteIndex]);
- bytes[byteIndex] -= i === 0 ? 1 : 2;
- }
- }
- return bytes;
- };
- const bytesToString = bytes => {
- if (!bytes) {
- return '';
- } // TODO: should toUint8 handle cases where we only have 8 bytes
- // but report more since this is a Uint16+ Array?
- bytes = Array.prototype.slice.call(bytes);
- const string = String.fromCharCode.apply(null, toUint8(bytes));
- try {
- return decodeURIComponent(escape(string));
- } catch (e) {// if decodeURIComponent/escape fails, we are dealing with partial
- // or full non string data. Just return the potentially garbled string.
- }
- return string;
- };
- const stringToBytes = (string, stringIsBytes) => {
- if (typeof string !== 'string' && string && typeof string.toString === 'function') {
- string = string.toString();
- }
- if (typeof string !== 'string') {
- return new Uint8Array();
- } // If the string already is bytes, we don't have to do this
- // otherwise we do this so that we split multi length characters
- // into individual bytes
- if (!stringIsBytes) {
- string = unescape(encodeURIComponent(string));
- }
- const view = new Uint8Array(string.length);
- for (let i = 0; i < string.length; i++) {
- view[i] = string.charCodeAt(i);
- }
- return view;
- };
- const concatTypedArrays = (...buffers) => {
- buffers = buffers.filter(b => b && (b.byteLength || b.length) && typeof b !== 'string');
- if (buffers.length <= 1) {
- // for 0 length we will return empty uint8
- // for 1 length we return the first uint8
- return toUint8(buffers[0]);
- }
- const totalLen = buffers.reduce((total, buf, i) => total + (buf.byteLength || buf.length), 0);
- const tempBuffer = new Uint8Array(totalLen);
- let offset = 0;
- buffers.forEach(function (buf) {
- buf = toUint8(buf);
- tempBuffer.set(buf, offset);
- offset += buf.byteLength;
- });
- return tempBuffer;
- };
- /**
- * Check if the bytes "b" are contained within bytes "a".
- *
- * @param {Uint8Array|Array} a
- * Bytes to check in
- *
- * @param {Uint8Array|Array} b
- * Bytes to check for
- *
- * @param {Object} options
- * options
- *
- * @param {Array|Uint8Array} [offset=0]
- * offset to use when looking at bytes in a
- *
- * @param {Array|Uint8Array} [mask=[]]
- * mask to use on bytes before comparison.
- *
- * @return {boolean}
- * If all bytes in b are inside of a, taking into account
- * bit masks.
- */
- const bytesMatch = (a, b, {
- offset = 0,
- mask = []
- } = {}) => {
- a = toUint8(a);
- b = toUint8(b); // ie 11 does not support uint8 every
- const fn = b.every ? b.every : Array.prototype.every;
- return b.length && a.length - offset >= b.length && // ie 11 doesn't support every on uin8
- fn.call(b, (bByte, i) => {
- const aByte = mask[i] ? mask[i] & a[offset + i] : a[offset + i];
- return bByte === aByte;
- });
- };
- const sliceBytes = function (src, start, end) {
- if (Uint8Array.prototype.slice) {
- return Uint8Array.prototype.slice.call(src, start, end);
- }
- return new Uint8Array(Array.prototype.slice.call(src, start, end));
- };
- const reverseBytes = function (src) {
- if (src.reverse) {
- return src.reverse();
- }
- return Array.prototype.reverse.call(src);
- };
- var byteHelpers = /*#__PURE__*/Object.freeze({
- __proto__: null,
- countBits: countBits,
- countBytes: countBytes,
- padStart: padStart,
- isArrayBufferView: isArrayBufferView,
- isTypedArray: isTypedArray,
- toUint8: toUint8,
- toHexString: toHexString,
- toBinaryString: toBinaryString,
- ENDIANNESS: ENDIANNESS,
- IS_BIG_ENDIAN: IS_BIG_ENDIAN,
- IS_LITTLE_ENDIAN: IS_LITTLE_ENDIAN,
- bytesToNumber: bytesToNumber,
- numberToBytes: numberToBytes,
- bytesToString: bytesToString,
- stringToBytes: stringToBytes,
- concatTypedArrays: concatTypedArrays,
- bytesMatch: bytesMatch,
- sliceBytes: sliceBytes,
- reverseBytes: reverseBytes
- });
- const normalizePath$1 = function (path) {
- if (typeof path === 'string') {
- return stringToBytes(path);
- }
- if (typeof path === 'number') {
- return path;
- }
- return path;
- };
- const normalizePaths$1 = function (paths) {
- if (!Array.isArray(paths)) {
- return [normalizePath$1(paths)];
- }
- return paths.map(p => normalizePath$1(p));
- };
- /**
- * find any number of boxes by name given a path to it in an iso bmff
- * such as mp4.
- *
- * @param {TypedArray} bytes
- * bytes for the iso bmff to search for boxes in
- *
- * @param {Uint8Array[]|string[]|string|Uint8Array} name
- * An array of paths or a single path representing the name
- * of boxes to search through in bytes. Paths may be
- * uint8 (character codes) or strings.
- *
- * @param {boolean} [complete=false]
- * Should we search only for complete boxes on the final path.
- * This is very useful when you do not want to get back partial boxes
- * in the case of streaming files.
- *
- * @return {Uint8Array[]}
- * An array of the end paths that we found.
- */
- const findBox = function (bytes, paths, complete = false) {
- paths = normalizePaths$1(paths);
- bytes = toUint8(bytes);
- const results = [];
- if (!paths.length) {
- // short-circuit the search for empty paths
- return results;
- }
- let i = 0;
- while (i < bytes.length) {
- const size = (bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]) >>> 0;
- const type = bytes.subarray(i + 4, i + 8); // invalid box format.
- if (size === 0) {
- break;
- }
- let end = i + size;
- if (end > bytes.length) {
- // this box is bigger than the number of bytes we have
- // and complete is set, we cannot find any more boxes.
- if (complete) {
- break;
- }
- end = bytes.length;
- }
- const data = bytes.subarray(i + 8, end);
- if (bytesMatch(type, paths[0])) {
- if (paths.length === 1) {
- // this is the end of the path and we've found the box we were
- // looking for
- results.push(data);
- } else {
- // recursively search for the next box along the path
- results.push.apply(results, findBox(data, paths.slice(1), complete));
- }
- }
- i = end;
- } // we've finished searching all of bytes
- return results;
- };
- // https://matroska-org.github.io/libebml/specs.html
- // https://www.matroska.org/technical/elements.html
- // https://www.webmproject.org/docs/container/
- const EBML_TAGS = {
- EBML: toUint8([0x1A, 0x45, 0xDF, 0xA3]),
- DocType: toUint8([0x42, 0x82]),
- Segment: toUint8([0x18, 0x53, 0x80, 0x67]),
- SegmentInfo: toUint8([0x15, 0x49, 0xA9, 0x66]),
- Tracks: toUint8([0x16, 0x54, 0xAE, 0x6B]),
- Track: toUint8([0xAE]),
- TrackNumber: toUint8([0xd7]),
- DefaultDuration: toUint8([0x23, 0xe3, 0x83]),
- TrackEntry: toUint8([0xAE]),
- TrackType: toUint8([0x83]),
- FlagDefault: toUint8([0x88]),
- CodecID: toUint8([0x86]),
- CodecPrivate: toUint8([0x63, 0xA2]),
- VideoTrack: toUint8([0xe0]),
- AudioTrack: toUint8([0xe1]),
- // Not used yet, but will be used for live webm/mkv
- // see https://www.matroska.org/technical/basics.html#block-structure
- // see https://www.matroska.org/technical/basics.html#simpleblock-structure
- Cluster: toUint8([0x1F, 0x43, 0xB6, 0x75]),
- Timestamp: toUint8([0xE7]),
- TimestampScale: toUint8([0x2A, 0xD7, 0xB1]),
- BlockGroup: toUint8([0xA0]),
- BlockDuration: toUint8([0x9B]),
- Block: toUint8([0xA1]),
- SimpleBlock: toUint8([0xA3])
- };
- /**
- * This is a simple table to determine the length
- * of things in ebml. The length is one based (starts at 1,
- * rather than zero) and for every zero bit before a one bit
- * we add one to length. We also need this table because in some
- * case we have to xor all the length bits from another value.
- */
- const LENGTH_TABLE = [0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100, 0b00000010, 0b00000001];
- const getLength = function (byte) {
- let len = 1;
- for (let i = 0; i < LENGTH_TABLE.length; i++) {
- if (byte & LENGTH_TABLE[i]) {
- break;
- }
- len++;
- }
- return len;
- }; // length in ebml is stored in the first 4 to 8 bits
- // of the first byte. 4 for the id length and 8 for the
- // data size length. Length is measured by converting the number to binary
- // then 1 + the number of zeros before a 1 is encountered starting
- // from the left.
- const getvint = function (bytes, offset, removeLength = true, signed = false) {
- const length = getLength(bytes[offset]);
- let valueBytes = bytes.subarray(offset, offset + length); // NOTE that we do **not** subarray here because we need to copy these bytes
- // as they will be modified below to remove the dataSizeLen bits and we do not
- // want to modify the original data. normally we could just call slice on
- // uint8array but ie 11 does not support that...
- if (removeLength) {
- valueBytes = Array.prototype.slice.call(bytes, offset, offset + length);
- valueBytes[0] ^= LENGTH_TABLE[length - 1];
- }
- return {
- length,
- value: bytesToNumber(valueBytes, {
- signed
- }),
- bytes: valueBytes
- };
- };
- const normalizePath = function (path) {
- if (typeof path === 'string') {
- return path.match(/.{1,2}/g).map(p => normalizePath(p));
- }
- if (typeof path === 'number') {
- return numberToBytes(path);
- }
- return path;
- };
- const normalizePaths = function (paths) {
- if (!Array.isArray(paths)) {
- return [normalizePath(paths)];
- }
- return paths.map(p => normalizePath(p));
- };
- const getInfinityDataSize = (id, bytes, offset) => {
- if (offset >= bytes.length) {
- return bytes.length;
- }
- const innerid = getvint(bytes, offset, false);
- if (bytesMatch(id.bytes, innerid.bytes)) {
- return offset;
- }
- const dataHeader = getvint(bytes, offset + innerid.length);
- return getInfinityDataSize(id, bytes, offset + dataHeader.length + dataHeader.value + innerid.length);
- };
- /**
- * Notes on the EBLM format.
- *
- * EBLM uses "vints" tags. Every vint tag contains
- * two parts
- *
- * 1. The length from the first byte. You get this by
- * converting the byte to binary and counting the zeros
- * before a 1. Then you add 1 to that. Examples
- * 00011111 = length 4 because there are 3 zeros before a 1.
- * 00100000 = length 3 because there are 2 zeros before a 1.
- * 00000011 = length 7 because there are 6 zeros before a 1.
- *
- * 2. The bits used for length are removed from the first byte
- * Then all the bytes are merged into a value. NOTE: this
- * is not the case for id ebml tags as there id includes
- * length bits.
- *
- */
- const findEbml = function (bytes, paths) {
- paths = normalizePaths(paths);
- bytes = toUint8(bytes);
- let results = [];
- if (!paths.length) {
- return results;
- }
- let i = 0;
- while (i < bytes.length) {
- const id = getvint(bytes, i, false);
- const dataHeader = getvint(bytes, i + id.length);
- const dataStart = i + id.length + dataHeader.length; // dataSize is unknown or this is a live stream
- if (dataHeader.value === 0x7f) {
- dataHeader.value = getInfinityDataSize(id, bytes, dataStart);
- if (dataHeader.value !== bytes.length) {
- dataHeader.value -= dataStart;
- }
- }
- const dataEnd = dataStart + dataHeader.value > bytes.length ? bytes.length : dataStart + dataHeader.value;
- const data = bytes.subarray(dataStart, dataEnd);
- if (bytesMatch(paths[0], id.bytes)) {
- if (paths.length === 1) {
- // this is the end of the paths and we've found the tag we were
- // looking for
- results.push(data);
- } else {
- // recursively search for the next tag inside of the data
- // of this one
- results = results.concat(findEbml(data, paths.slice(1)));
- }
- }
- const totalLength = id.length + dataHeader.length + data.length; // move past this tag entirely, we are not looking for it
- i += totalLength;
- }
- return results;
- }; // see https://www.matroska.org/technical/basics.html#block-structure
- const ID3 = toUint8([0x49, 0x44, 0x33]);
- const getId3Size = function (bytes, offset = 0) {
- bytes = toUint8(bytes);
- const flags = bytes[offset + 5];
- const returnSize = bytes[offset + 6] << 21 | bytes[offset + 7] << 14 | bytes[offset + 8] << 7 | bytes[offset + 9];
- const footerPresent = (flags & 16) >> 4;
- if (footerPresent) {
- return returnSize + 20;
- }
- return returnSize + 10;
- };
- const getId3Offset = function (bytes, offset = 0) {
- bytes = toUint8(bytes);
- if (bytes.length - offset < 10 || !bytesMatch(bytes, ID3, {
- offset
- })) {
- return offset;
- }
- offset += getId3Size(bytes, offset); // recursive check for id3 tags as some files
- // have multiple ID3 tag sections even though
- // they should not.
- return getId3Offset(bytes, offset);
- };
- const NAL_TYPE_ONE = toUint8([0x00, 0x00, 0x00, 0x01]);
- const NAL_TYPE_TWO = toUint8([0x00, 0x00, 0x01]);
- const EMULATION_PREVENTION = toUint8([0x00, 0x00, 0x03]);
- /**
- * Expunge any "Emulation Prevention" bytes from a "Raw Byte
- * Sequence Payload"
- *
- * @param data {Uint8Array} the bytes of a RBSP from a NAL
- * unit
- * @return {Uint8Array} the RBSP without any Emulation
- * Prevention Bytes
- */
- const discardEmulationPreventionBytes = function (bytes) {
- const positions = [];
- let i = 1; // Find all `Emulation Prevention Bytes`
- while (i < bytes.length - 2) {
- if (bytesMatch(bytes.subarray(i, i + 3), EMULATION_PREVENTION)) {
- positions.push(i + 2);
- i++;
- }
- i++;
- } // If no Emulation Prevention Bytes were found just return the original
- // array
- if (positions.length === 0) {
- return bytes;
- } // Create a new array to hold the NAL unit data
- const newLength = bytes.length - positions.length;
- const newData = new Uint8Array(newLength);
- let sourceIndex = 0;
- for (i = 0; i < newLength; sourceIndex++, i++) {
- if (sourceIndex === positions[0]) {
- // Skip this byte
- sourceIndex++; // Remove this position index
- positions.shift();
- }
- newData[i] = bytes[sourceIndex];
- }
- return newData;
- };
- const findNal = function (bytes, dataType, types, nalLimit = Infinity) {
- bytes = toUint8(bytes);
- types = [].concat(types);
- let i = 0;
- let nalStart;
- let nalsFound = 0; // keep searching until:
- // we reach the end of bytes
- // we reach the maximum number of nals they want to seach
- // NOTE: that we disregard nalLimit when we have found the start
- // of the nal we want so that we can find the end of the nal we want.
- while (i < bytes.length && (nalsFound < nalLimit || nalStart)) {
- let nalOffset;
- if (bytesMatch(bytes.subarray(i), NAL_TYPE_ONE)) {
- nalOffset = 4;
- } else if (bytesMatch(bytes.subarray(i), NAL_TYPE_TWO)) {
- nalOffset = 3;
- } // we are unsynced,
- // find the next nal unit
- if (!nalOffset) {
- i++;
- continue;
- }
- nalsFound++;
- if (nalStart) {
- return discardEmulationPreventionBytes(bytes.subarray(nalStart, i));
- }
- let nalType;
- if (dataType === 'h264') {
- nalType = bytes[i + nalOffset] & 0x1f;
- } else if (dataType === 'h265') {
- nalType = bytes[i + nalOffset] >> 1 & 0x3f;
- }
- if (types.indexOf(nalType) !== -1) {
- nalStart = i + nalOffset;
- } // nal header is 1 length for h264, and 2 for h265
- i += nalOffset + (dataType === 'h264' ? 1 : 2);
- }
- return bytes.subarray(0, 0);
- };
- const findH264Nal = (bytes, type, nalLimit) => findNal(bytes, 'h264', type, nalLimit);
- const findH265Nal = (bytes, type, nalLimit) => findNal(bytes, 'h265', type, nalLimit);
- const CONSTANTS = {
- // "webm" string literal in hex
- 'webm': toUint8([0x77, 0x65, 0x62, 0x6d]),
- // "matroska" string literal in hex
- 'matroska': toUint8([0x6d, 0x61, 0x74, 0x72, 0x6f, 0x73, 0x6b, 0x61]),
- // "fLaC" string literal in hex
- 'flac': toUint8([0x66, 0x4c, 0x61, 0x43]),
- // "OggS" string literal in hex
- 'ogg': toUint8([0x4f, 0x67, 0x67, 0x53]),
- // ac-3 sync byte, also works for ec-3 as that is simply a codec
- // of ac-3
- 'ac3': toUint8([0x0b, 0x77]),
- // "RIFF" string literal in hex used for wav and avi
- 'riff': toUint8([0x52, 0x49, 0x46, 0x46]),
- // "AVI" string literal in hex
- 'avi': toUint8([0x41, 0x56, 0x49]),
- // "WAVE" string literal in hex
- 'wav': toUint8([0x57, 0x41, 0x56, 0x45]),
- // "ftyp3g" string literal in hex
- '3gp': toUint8([0x66, 0x74, 0x79, 0x70, 0x33, 0x67]),
- // "ftyp" string literal in hex
- 'mp4': toUint8([0x66, 0x74, 0x79, 0x70]),
- // "styp" string literal in hex
- 'fmp4': toUint8([0x73, 0x74, 0x79, 0x70]),
- // "ftypqt" string literal in hex
- 'mov': toUint8([0x66, 0x74, 0x79, 0x70, 0x71, 0x74]),
- // moov string literal in hex
- 'moov': toUint8([0x6D, 0x6F, 0x6F, 0x76]),
- // moof string literal in hex
- 'moof': toUint8([0x6D, 0x6F, 0x6F, 0x66])
- };
- const _isLikely = {
- aac(bytes) {
- const offset = getId3Offset(bytes);
- return bytesMatch(bytes, [0xFF, 0x10], {
- offset,
- mask: [0xFF, 0x16]
- });
- },
- mp3(bytes) {
- const offset = getId3Offset(bytes);
- return bytesMatch(bytes, [0xFF, 0x02], {
- offset,
- mask: [0xFF, 0x06]
- });
- },
- webm(bytes) {
- const docType = findEbml(bytes, [EBML_TAGS.EBML, EBML_TAGS.DocType])[0]; // check if DocType EBML tag is webm
- return bytesMatch(docType, CONSTANTS.webm);
- },
- mkv(bytes) {
- const docType = findEbml(bytes, [EBML_TAGS.EBML, EBML_TAGS.DocType])[0]; // check if DocType EBML tag is matroska
- return bytesMatch(docType, CONSTANTS.matroska);
- },
- mp4(bytes) {
- // if this file is another base media file format, it is not mp4
- if (_isLikely['3gp'](bytes) || _isLikely.mov(bytes)) {
- return false;
- } // if this file starts with a ftyp or styp box its mp4
- if (bytesMatch(bytes, CONSTANTS.mp4, {
- offset: 4
- }) || bytesMatch(bytes, CONSTANTS.fmp4, {
- offset: 4
- })) {
- return true;
- } // if this file starts with a moof/moov box its mp4
- if (bytesMatch(bytes, CONSTANTS.moof, {
- offset: 4
- }) || bytesMatch(bytes, CONSTANTS.moov, {
- offset: 4
- })) {
- return true;
- }
- },
- mov(bytes) {
- return bytesMatch(bytes, CONSTANTS.mov, {
- offset: 4
- });
- },
- '3gp'(bytes) {
- return bytesMatch(bytes, CONSTANTS['3gp'], {
- offset: 4
- });
- },
- ac3(bytes) {
- const offset = getId3Offset(bytes);
- return bytesMatch(bytes, CONSTANTS.ac3, {
- offset
- });
- },
- ts(bytes) {
- if (bytes.length < 189 && bytes.length >= 1) {
- return bytes[0] === 0x47;
- }
- let i = 0; // check the first 376 bytes for two matching sync bytes
- while (i + 188 < bytes.length && i < 188) {
- if (bytes[i] === 0x47 && bytes[i + 188] === 0x47) {
- return true;
- }
- i += 1;
- }
- return false;
- },
- flac(bytes) {
- const offset = getId3Offset(bytes);
- return bytesMatch(bytes, CONSTANTS.flac, {
- offset
- });
- },
- ogg(bytes) {
- return bytesMatch(bytes, CONSTANTS.ogg);
- },
- avi(bytes) {
- return bytesMatch(bytes, CONSTANTS.riff) && bytesMatch(bytes, CONSTANTS.avi, {
- offset: 8
- });
- },
- wav(bytes) {
- return bytesMatch(bytes, CONSTANTS.riff) && bytesMatch(bytes, CONSTANTS.wav, {
- offset: 8
- });
- },
- 'h264'(bytes) {
- // find seq_parameter_set_rbsp
- return findH264Nal(bytes, 7, 3).length;
- },
- 'h265'(bytes) {
- // find video_parameter_set_rbsp or seq_parameter_set_rbsp
- return findH265Nal(bytes, [32, 33], 3).length;
- }
- }; // get all the isLikely functions
- // but make sure 'ts' is above h264 and h265
- // but below everything else as it is the least specific
- const isLikelyTypes = Object.keys(_isLikely) // remove ts, h264, h265
- .filter(t => t !== 'ts' && t !== 'h264' && t !== 'h265') // add it back to the bottom
- .concat(['ts', 'h264', 'h265']); // make sure we are dealing with uint8 data.
- isLikelyTypes.forEach(function (type) {
- const isLikelyFn = _isLikely[type];
- _isLikely[type] = bytes => isLikelyFn(toUint8(bytes));
- }); // export after wrapping
- const isLikely = _isLikely; // A useful list of file signatures can be found here
- // https://en.wikipedia.org/wiki/List_of_file_signatures
- const detectContainerForBytes = bytes => {
- bytes = toUint8(bytes);
- for (let i = 0; i < isLikelyTypes.length; i++) {
- const type = isLikelyTypes[i];
- if (isLikely[type](bytes)) {
- return type;
- }
- }
- return '';
- }; // fmp4 is not a container
- const isLikelyFmp4MediaSegment = bytes => {
- return findBox(bytes, ['moof']).length > 0;
- };
- var containers = /*#__PURE__*/Object.freeze({
- __proto__: null,
- isLikely: isLikely,
- detectContainerForBytes: detectContainerForBytes,
- isLikelyFmp4MediaSegment: isLikelyFmp4MediaSegment
- });
- const atob = s => window.atob ? window.atob(s) : Buffer.from(s, 'base64').toString('binary');
- function decodeB64ToUint8Array(b64Text) {
- const decodedString = atob(b64Text);
- const array = new Uint8Array(decodedString.length);
- for (let i = 0; i < decodedString.length; i++) {
- array[i] = decodedString.charCodeAt(i);
- }
- return array;
- }
- /**
- * Loops through all supported media groups in master and calls the provided
- * callback for each group
- *
- * @param {Object} master
- * The parsed master manifest object
- * @param {string[]} groups
- * The media groups to call the callback for
- * @param {Function} callback
- * Callback to call for each media group
- */
- const forEachMediaGroup = (master, groups, callback) => {
- groups.forEach(mediaType => {
- for (const groupKey in master.mediaGroups[mediaType]) {
- for (const labelKey in master.mediaGroups[mediaType][groupKey]) {
- const mediaProperties = master.mediaGroups[mediaType][groupKey][labelKey];
- callback(mediaProperties, mediaType, groupKey, labelKey);
- }
- }
- });
- };
- var mediaGroups = /*#__PURE__*/Object.freeze({
- __proto__: null,
- forEachMediaGroup: forEachMediaGroup
- });
- var urlToolkit = {exports: {}};
- (function (module, exports) {
- // see https://tools.ietf.org/html/rfc1808
- (function (root) {
- var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/?#]*\/)*[^;?#]*)?(;[^?#]*)?(\?[^#]*)?(#.*)?$/;
- var FIRST_SEGMENT_REGEX = /^([^\/?#]*)(.*)$/;
- 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 URLToolkit = urlToolkit.exports;
- const DEFAULT_LOCATION = 'http://example.com';
- const resolveUrl = (baseUrl, relativeUrl) => {
- // return early if we don't need to resolve
- if (/^[a-z]+:/i.test(relativeUrl)) {
- return relativeUrl;
- } // if baseUrl is a data URI, ignore it and resolve everything relative to window.location
- if (/^data:/.test(baseUrl)) {
- baseUrl = window.location && window.location.href || '';
- } // IE11 supports URL but not the URL constructor
- // feature detect the behavior we want
- const nativeURL = typeof window.URL === 'function';
- const protocolLess = /^\/\//.test(baseUrl); // remove location if window.location isn't available (i.e. we're in node)
- // and if baseUrl isn't an absolute url
- const removeLocation = !window.location && !/\/\//i.test(baseUrl); // if the base URL is relative then combine with the current location
- if (nativeURL) {
- baseUrl = new window.URL(baseUrl, window.location || DEFAULT_LOCATION);
- } else if (!/\/\//i.test(baseUrl)) {
- baseUrl = URLToolkit.buildAbsoluteURL(window.location && window.location.href || '', baseUrl);
- }
- if (nativeURL) {
- const newUrl = new URL(relativeUrl, baseUrl); // if we're a protocol-less url, remove the protocol
- // and if we're location-less, remove the location
- // otherwise, return the url unmodified
- if (removeLocation) {
- return newUrl.href.slice(DEFAULT_LOCATION.length);
- } else if (protocolLess) {
- return newUrl.href.slice(newUrl.protocol.length);
- }
- return newUrl.href;
- }
- return URLToolkit.buildAbsoluteURL(baseUrl, relativeUrl);
- };
- /**
- * @file stream.js
- */
- /**
- * A lightweight readable stream implemention that handles event dispatching.
- *
- * @class Stream
- */
- class Stream {
- constructor() {
- this.listeners = {};
- }
- /**
- * Add a listener for a specified event type.
- *
- * @param {string} type the event name
- * @param {Function} listener the callback to be invoked when an event of
- * the specified type occurs
- */
- on(type, listener) {
- if (!this.listeners[type]) {
- this.listeners[type] = [];
- }
- this.listeners[type].push(listener);
- }
- /**
- * Remove a listener for a specified event type.
- *
- * @param {string} type the event name
- * @param {Function} listener a function previously registered for this
- * type of event through `on`
- * @return {boolean} if we could turn it off or not
- */
- off(type, listener) {
- if (!this.listeners[type]) {
- return false;
- }
- const index = this.listeners[type].indexOf(listener); // TODO: which is better?
- // In Video.js we slice listener functions
- // on trigger so that it does not mess up the order
- // while we loop through.
- //
- // Here we slice on off so that the loop in trigger
- // can continue using it's old reference to loop without
- // messing up the order.
- this.listeners[type] = this.listeners[type].slice(0);
- this.listeners[type].splice(index, 1);
- return index > -1;
- }
- /**
- * Trigger an event of the specified type on this stream. Any additional
- * arguments to this function are passed as parameters to event listeners.
- *
- * @param {string} type the event name
- */
- trigger(type) {
- const callbacks = this.listeners[type];
- if (!callbacks) {
- return;
- } // Slicing the arguments on every invocation of this method
- // can add a significant amount of overhead. Avoid the
- // intermediate object creation for the common case of a
- // single callback argument
- if (arguments.length === 2) {
- const length = callbacks.length;
- for (let i = 0; i < length; ++i) {
- callbacks[i].call(this, arguments[1]);
- }
- } else {
- const args = Array.prototype.slice.call(arguments, 1);
- const length = callbacks.length;
- for (let i = 0; i < length; ++i) {
- callbacks[i].apply(this, args);
- }
- }
- }
- /**
- * Destroys the stream and cleans up.
- */
- dispose() {
- this.listeners = {};
- }
- /**
- * Forwards all `data` events on this stream to the destination stream. The
- * destination stream should provide a method `push` to receive the data
- * events as they arrive.
- *
- * @param {Stream} destination the stream that will receive all `data` events
- * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
- */
- pipe(destination) {
- this.on('data', function (data) {
- destination.push(data);
- });
- }
- }
- var index = {
- codecs,
- byteHelpers,
- containers,
- decodeB64ToUint8Array,
- mediaGroups,
- resolveUrl,
- Stream
- };
- return index;
- }));
|