This commit is contained in:
王创世 2024-03-15 16:53:30 +08:00
parent 59a4abcfeb
commit 1c2c165430
257 changed files with 218840 additions and 82 deletions

29
node_modules/.package-lock.json generated vendored
View File

@ -359,6 +359,11 @@
"node": ">= 4"
}
},
"node_modules/es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmmirror.com/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
},
"node_modules/esbuild": {
"version": "0.12.25",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.25.tgz",
@ -380,6 +385,15 @@
"integrity": "sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==",
"dev": true
},
"node_modules/flv.js": {
"version": "1.6.2",
"resolved": "https://registry.npmmirror.com/flv.js/-/flv.js-1.6.2.tgz",
"integrity": "sha512-xre4gUbX1MPtgQRKj2pxJENp/RnaHaxYvy3YToVVCrSmAWUu85b9mug6pTXF6zakUjNP2lFWZ1rkSX7gxhB/2A==",
"dependencies": {
"es6-promise": "^4.2.8",
"webworkify-webpack": "^2.1.5"
}
},
"node_modules/follow-redirects": {
"version": "1.15.5",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.5.tgz",
@ -448,6 +462,11 @@
"node": ">=12.0.0"
}
},
"node_modules/hls.js": {
"version": "1.5.7",
"resolved": "https://registry.npmmirror.com/hls.js/-/hls.js-1.5.7.tgz",
"integrity": "sha512-Hnyf7ojTBtXHeOW1/t6wCBJSiK1WpoKF9yg7juxldDx8u3iswrkPt2wbOA/1NiwU4j27DSIVoIEJRAhcdMef/A=="
},
"node_modules/icss-replace-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
@ -579,6 +598,11 @@
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"node_modules/mui-player": {
"version": "1.8.1",
"resolved": "https://registry.npmmirror.com/mui-player/-/mui-player-1.8.1.tgz",
"integrity": "sha512-5o0SnSyVImxT9XUO6jCMmcJ+ZyAEJeFvdeZDHHPNS/LdwhzWX4yQPNgx8nzRbcUJ749xpqEQ6uVddiexLyvvqg=="
},
"node_modules/naive-ui": {
"version": "2.37.3",
"resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.37.3.tgz",
@ -962,6 +986,11 @@
"vue": "^3.0.11"
}
},
"node_modules/webworkify-webpack": {
"version": "2.1.5",
"resolved": "https://registry.npmmirror.com/webworkify-webpack/-/webworkify-webpack-2.1.5.tgz",
"integrity": "sha512-2akF8FIyUvbiBBdD+RoHpoTbHMQF2HwjcxfDvgztAX5YwbZNyrtfUMgvfgFVsgDhDPVTlkbb5vyasqDHfIDPQw=="
},
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",

29
node_modules/.vite/_metadata.json generated vendored
View File

@ -1,12 +1,7 @@
{
"hash": "fa4efea0",
"browserHash": "c6181f89",
"hash": "9f12c9b6",
"browserHash": "1bba5c36",
"optimized": {
"axios": {
"file": "E:/小程序/百姓问政PC/pc/node_modules/.vite/axios.js",
"src": "E:/小程序/百姓问政PC/pc/node_modules/axios/index.js",
"needsInterop": false
},
"vue": {
"file": "E:/小程序/百姓问政PC/pc/node_modules/.vite/vue.js",
"src": "E:/小程序/百姓问政PC/pc/node_modules/vue/dist/vue.runtime.esm-bundler.js",
@ -26,6 +21,26 @@
"file": "E:/小程序/百姓问政PC/pc/node_modules/.vite/vue-router.js",
"src": "E:/小程序/百姓问政PC/pc/node_modules/vue-router/dist/vue-router.mjs",
"needsInterop": false
},
"mui-player": {
"file": "E:/小程序/百姓问政PC/pc/node_modules/.vite/mui-player.js",
"src": "E:/小程序/百姓问政PC/pc/node_modules/mui-player/dist/mui-player.min.js",
"needsInterop": true
},
"hls.js": {
"file": "E:/小程序/百姓问政PC/pc/node_modules/.vite/hls_js.js",
"src": "E:/小程序/百姓问政PC/pc/node_modules/hls.js/dist/hls.mjs",
"needsInterop": false
},
"axios": {
"file": "E:/小程序/百姓问政PC/pc/node_modules/.vite/axios.js",
"src": "E:/小程序/百姓问政PC/pc/node_modules/axios/index.js",
"needsInterop": false
},
"flv.js": {
"file": "E:/小程序/百姓问政PC/pc/node_modules/.vite/flv_js.js",
"src": "E:/小程序/百姓问政PC/pc/node_modules/flv.js/dist/flv.js",
"needsInterop": true
}
}
}

24573
node_modules/.vite/hls_js.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

7
node_modules/.vite/hls_js.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

708
node_modules/.vite/mui-player.js generated vendored Normal file

File diff suppressed because one or more lines are too long

7
node_modules/.vite/mui-player.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

155
node_modules/es6-promise/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,155 @@
# Master
# 4.2.5
* remove old try/catch performance hacks, modern runtimes do not require these tricks
# 4.2.4
* [Fixes #305] Confuse webpack
# 4.2.3
* Cleanup testem related build configuration
* Use `prepublishOnly` instead of `prepublish` (thanks @rhysd)
* Add Node.js 9, 8 to testing matrix
* drop now unused s3 deployment files
* internal cleanup (thanks to @bekzod, @mariusschulz)
* Fixup Changelog
# 4.2.2
* Ensure PROMISE_ID works correctly
* internal cleanup (thanks yo @mariusschulz)
# 4.2.1
* drop bower support
# 4.2.0
* drop `dist` from git repo
* add `Promise.prototype.finally`
* update various build related dependencies
* add CDN links
# 4.1.0
* [BUGFIX] Fix memory leak [#269]
* [BUGFIX] Auto Bundles within an AMD Environment [#263]
# 4.0.5
* fix require('es6-promise/auto') for Node < 4
# 4.0.4
* fix asap when using https://github.com/Kinvey/titanium-sdk
# 4.0.3
* fix Readme links
# 4.0.2
* fix require('es6-promise/auto');
# 4.0.0
* no longer polyfill automatically, if needed one can still invoke
`require('es6-promise/auto')` directly.
# 3.3.1
* fix links in readme
# 3.3.0
* support polyfil on WebMAF (playstation env)
* fix tampering related bug global `constructor` was referenced by mistake.
* provide TS Typings
* increase compatibliity with sinon.useFakeTimers();
* update build tools (use rollup)
* directly export promise;
# 3.2.2
* IE8: use isArray
* update build dependencies
# 3.2.1
* fix race tampering issue
* use eslint
* fix Promise.all tampering
* remove unused code
* fix issues with NWJS/electron
# 3.2.0
* improve tamper resistence of Promise.all Promise.race and
Promise.prototype.then (note, this isn't complete, but addresses an exception
when used \w core-js, follow up work will address entirely)
* remove spec incompatible then chaining fast-path
* add eslint
* update build deps
# 3.1.2
* fix node detection issues with NWJS/electron
# 3.1.0
* improve performance of Promise.all when it encounters a non-promise input object input
* then/resolve tamper protection
* reduce AST size of promise constructor, to facilitate more inlining
* Update README.md with details about PhantomJS requirement for running tests
* Mangle and compress the minified version
# 3.0.2
* correctly bump both bower and package.json versions
# 3.0.1
* no longer include dist/test in npm releases
# 3.0.0
* use nextTick() instead of setImmediate() to schedule microtasks with node 0.10. Later versions of
nodes are not affected as they were already using nextTick(). Note that using nextTick() might
trigger a depreciation warning on 0.10 as described at https://github.com/cujojs/when/issues/410.
The reason why nextTick() is preferred is that is setImmediate() would schedule a macrotask
instead of a microtask and might result in a different scheduling.
If needed you can revert to the former behavior as follow:
var Promise = require('es6-promise').Promise;
Promise._setScheduler(setImmediate);
# 2.3.0
* #121: Ability to override the internal asap implementation
* #120: Use an ascii character for an apostrophe, for source maps
# 2.2.0
* #116: Expose asap() and a way to override the scheduling mechanism on Promise
* Lock to v0.2.3 of ember-cli
# 2.1.1
* Fix #100 via #105: tell browserify to ignore vertx require
* Fix #101 via #102: "follow thenable state, not own state"
# 2.1.0
* #59: Automatic polyfill. No need to invoke `ES6Promise.polyfill()` anymore.
* ... (see the commit log)
# 2.0.0
* re-sync with RSVP. Many large performance improvements and bugfixes.
# 1.0.0
* first subset of RSVP

19
node_modules/es6-promise/LICENSE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

97
node_modules/es6-promise/README.md generated vendored Normal file
View File

@ -0,0 +1,97 @@
# ES6-Promise (subset of [rsvp.js](https://github.com/tildeio/rsvp.js)) [![Build Status](https://travis-ci.org/stefanpenner/es6-promise.svg?branch=master)](https://travis-ci.org/stefanpenner/es6-promise)
This is a polyfill of the [ES6 Promise](http://www.ecma-international.org/ecma-262/6.0/#sec-promise-constructor). The implementation is a subset of [rsvp.js](https://github.com/tildeio/rsvp.js) extracted by @jakearchibald, if you're wanting extra features and more debugging options, check out the [full library](https://github.com/tildeio/rsvp.js).
For API details and how to use promises, see the <a href="http://www.html5rocks.com/en/tutorials/es6/promises/">JavaScript Promises HTML5Rocks article</a>.
## Downloads
* [es6-promise 27.86 KB (7.33 KB gzipped)](https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.js)
* [es6-promise-auto 27.78 KB (7.3 KB gzipped)](https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.auto.js) - Automatically provides/replaces `Promise` if missing or broken.
* [es6-promise-min 6.17 KB (2.4 KB gzipped)](https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.min.js)
* [es6-promise-auto-min 6.19 KB (2.4 KB gzipped)](https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.auto.min.js) - Minified version of `es6-promise-auto` above.
## CDN
To use via a CDN include this in your html:
```html
<!-- Automatically provides/replaces `Promise` if missing or broken. -->
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.js"></script>
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script>
<!-- Minified version of `es6-promise-auto` below. -->
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script>
```
## Node.js
To install:
```sh
yarn add es6-promise
```
or
```sh
npm install es6-promise
```
To use:
```js
var Promise = require('es6-promise').Promise;
```
## Usage in IE<9
`catch` and `finally` are reserved keywords in IE<9, meaning
`promise.catch(func)` or `promise.finally(func)` throw a syntax error. To work
around this, you can use a string to access the property as shown in the
following example.
However most minifiers will automatically fix this for you, making the
resulting code safe for old browsers and production:
```js
promise['catch'](function(err) {
// ...
});
```
```js
promise['finally'](function() {
// ...
});
```
## Auto-polyfill
To polyfill the global environment (either in Node or in the browser via CommonJS) use the following code snippet:
```js
require('es6-promise').polyfill();
```
Alternatively
```js
require('es6-promise/auto');
```
Notice that we don't assign the result of `polyfill()` to any variable. The `polyfill()` method will patch the global environment (in this case to the `Promise` name) when called.
## Building & Testing
You will need to have PhantomJS installed globally in order to run the tests.
`npm install -g phantomjs`
* `npm run build` to build
* `npm test` to run tests
* `npm start` to run a build watcher, and webserver to test
* `npm run test:server` for a testem test runner and watching builder

4
node_modules/es6-promise/auto.js generated vendored Normal file
View File

@ -0,0 +1,4 @@
// This file can be required in Browserify and Node.js for automatic polyfill
// To use it: require('es6-promise/auto');
'use strict';
module.exports = require('./').polyfill();

1176
node_modules/es6-promise/dist/es6-promise.auto.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/es6-promise/dist/es6-promise.auto.map generated vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1174
node_modules/es6-promise/dist/es6-promise.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/es6-promise/dist/es6-promise.map generated vendored Normal file

File diff suppressed because one or more lines are too long

1
node_modules/es6-promise/dist/es6-promise.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

1
node_modules/es6-promise/dist/es6-promise.min.map generated vendored Normal file

File diff suppressed because one or more lines are too long

85
node_modules/es6-promise/es6-promise.d.ts generated vendored Normal file
View File

@ -0,0 +1,85 @@
export interface Thenable <R> {
then <U> (onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Thenable<U>;
then <U> (onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Thenable<U>;
}
export class Promise <R> implements Thenable <R> {
/**
* If you call resolve in the body of the callback passed to the constructor,
* your promise is fulfilled with result object passed to resolve.
* If you call reject your promise is rejected with the object passed to resolve.
* For consistency and debugging (eg stack traces), obj should be an instanceof Error.
* Any errors thrown in the constructor callback will be implicitly passed to reject().
*/
constructor (callback: (resolve : (value?: R | Thenable<R>) => void, reject: (error?: any) => void) => void);
/**
* onFulfilled is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
* Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called.
* Both callbacks have a single parameter , the fulfillment value or rejection reason.
* "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve.
* If an error is thrown in the callback, the returned promise rejects with that error.
*
* @param onFulfilled called when/if "promise" resolves
* @param onRejected called when/if "promise" rejects
*/
then <U> (onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
then <U> (onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Promise<U>;
/**
* Sugar for promise.then(undefined, onRejected)
*
* @param onRejected called when/if "promise" rejects
*/
catch <U> (onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
/**
* onSettled is invoked when/if the "promise" settles (either rejects or fulfills).
* The returned promise is settled when the `Thenable` returned by `onFinally` settles;
* it is rejected if `onFinally` throws or rejects; otherwise it assumes the state of the
* original Promise.
*
* @param onFinally called when/if "promise" settles
*/
finally (onFinally?: () => any | Thenable<any>): Promise<R>;
/**
* Make a new promise from the thenable.
* A thenable is promise-like in as far as it has a "then" method.
*/
static resolve (): Promise<void>;
static resolve <R> (value: R | Thenable<R>): Promise<R>;
/**
* Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error
*/
static reject <R> (error: any): Promise<R>;
/**
* Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects.
* the array passed to all can be a mixture of promise-like objects and other objects.
* The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value.
*/
static all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: [T1 | Thenable<T1>, T2 | Thenable<T2>, T3 | Thenable<T3>, T4 | Thenable <T4>, T5 | Thenable<T5>, T6 | Thenable<T6>, T7 | Thenable<T7>, T8 | Thenable<T8>, T9 | Thenable<T9>, T10 | Thenable<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
static all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: [T1 | Thenable<T1>, T2 | Thenable<T2>, T3 | Thenable<T3>, T4 | Thenable <T4>, T5 | Thenable<T5>, T6 | Thenable<T6>, T7 | Thenable<T7>, T8 | Thenable<T8>, T9 | Thenable<T9>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
static all<T1, T2, T3, T4, T5, T6, T7, T8>(values: [T1 | Thenable<T1>, T2 | Thenable<T2>, T3 | Thenable<T3>, T4 | Thenable <T4>, T5 | Thenable<T5>, T6 | Thenable<T6>, T7 | Thenable<T7>, T8 | Thenable<T8>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
static all<T1, T2, T3, T4, T5, T6, T7>(values: [T1 | Thenable<T1>, T2 | Thenable<T2>, T3 | Thenable<T3>, T4 | Thenable <T4>, T5 | Thenable<T5>, T6 | Thenable<T6>, T7 | Thenable<T7>]): Promise<[T1, T2, T3, T4, T5, T6, T7]>;
static all<T1, T2, T3, T4, T5, T6>(values: [T1 | Thenable<T1>, T2 | Thenable<T2>, T3 | Thenable<T3>, T4 | Thenable <T4>, T5 | Thenable<T5>, T6 | Thenable<T6>]): Promise<[T1, T2, T3, T4, T5, T6]>;
static all<T1, T2, T3, T4, T5>(values: [T1 | Thenable<T1>, T2 | Thenable<T2>, T3 | Thenable<T3>, T4 | Thenable <T4>, T5 | Thenable<T5>]): Promise<[T1, T2, T3, T4, T5]>;
static all<T1, T2, T3, T4>(values: [T1 | Thenable<T1>, T2 | Thenable<T2>, T3 | Thenable<T3>, T4 | Thenable <T4>]): Promise<[T1, T2, T3, T4]>;
static all<T1, T2, T3>(values: [T1 | Thenable<T1>, T2 | Thenable<T2>, T3 | Thenable<T3>]): Promise<[T1, T2, T3]>;
static all<T1, T2>(values: [T1 | Thenable<T1>, T2 | Thenable<T2>]): Promise<[T1, T2]>;
static all<T1>(values: [T1 | Thenable<T1>]): Promise<[T1]>;
static all<TAll>(values: Array<TAll | Thenable<TAll>>): Promise<TAll[]>;
/**
* Make a Promise that fulfills when any item fulfills, and rejects if any item rejects.
*/
static race <R> (promises: (R | Thenable<R>)[]): Promise<R>;
}
/**
* The polyfill method will patch the global environment (in this case to the Promise name) when called.
*/
export function polyfill (): void;

3
node_modules/es6-promise/lib/es6-promise.auto.js generated vendored Normal file
View File

@ -0,0 +1,3 @@
import Promise from './es6-promise';
Promise.polyfill();
export default Promise;

7
node_modules/es6-promise/lib/es6-promise.js generated vendored Normal file
View File

@ -0,0 +1,7 @@
import Promise from './es6-promise/promise';
import polyfill from './es6-promise/polyfill';
// Strange compat..
Promise.polyfill = polyfill;
Promise.Promise = Promise;
export default Promise;

243
node_modules/es6-promise/lib/es6-promise/-internal.js generated vendored Normal file
View File

@ -0,0 +1,243 @@
import {
objectOrFunction,
isFunction
} from './utils';
import {
asap
} from './asap';
import originalThen from './then';
import originalResolve from './promise/resolve';
export const PROMISE_ID = Math.random().toString(36).substring(2);
function noop() {}
const PENDING = void 0;
const FULFILLED = 1;
const REJECTED = 2;
function selfFulfillment() {
return new TypeError("You cannot resolve a promise with itself");
}
function cannotReturnOwn() {
return new TypeError('A promises callback cannot return that same promise.');
}
function tryThen(then, value, fulfillmentHandler, rejectionHandler) {
try {
then.call(value, fulfillmentHandler, rejectionHandler);
} catch(e) {
return e;
}
}
function handleForeignThenable(promise, thenable, then) {
asap(promise => {
let sealed = false;
let error = tryThen(then, thenable, value => {
if (sealed) { return; }
sealed = true;
if (thenable !== value) {
resolve(promise, value);
} else {
fulfill(promise, value);
}
}, reason => {
if (sealed) { return; }
sealed = true;
reject(promise, reason);
}, 'Settle: ' + (promise._label || ' unknown promise'));
if (!sealed && error) {
sealed = true;
reject(promise, error);
}
}, promise);
}
function handleOwnThenable(promise, thenable) {
if (thenable._state === FULFILLED) {
fulfill(promise, thenable._result);
} else if (thenable._state === REJECTED) {
reject(promise, thenable._result);
} else {
subscribe(thenable, undefined, value => resolve(promise, value),
reason => reject(promise, reason))
}
}
function handleMaybeThenable(promise, maybeThenable, then) {
if (maybeThenable.constructor === promise.constructor &&
then === originalThen &&
maybeThenable.constructor.resolve === originalResolve) {
handleOwnThenable(promise, maybeThenable);
} else {
if (then === undefined) {
fulfill(promise, maybeThenable);
} else if (isFunction(then)) {
handleForeignThenable(promise, maybeThenable, then);
} else {
fulfill(promise, maybeThenable);
}
}
}
function resolve(promise, value) {
if (promise === value) {
reject(promise, selfFulfillment());
} else if (objectOrFunction(value)) {
let then;
try {
then = value.then;
} catch (error) {
reject(promise, error);
return;
}
handleMaybeThenable(promise, value, then);
} else {
fulfill(promise, value);
}
}
function publishRejection(promise) {
if (promise._onerror) {
promise._onerror(promise._result);
}
publish(promise);
}
function fulfill(promise, value) {
if (promise._state !== PENDING) { return; }
promise._result = value;
promise._state = FULFILLED;
if (promise._subscribers.length !== 0) {
asap(publish, promise);
}
}
function reject(promise, reason) {
if (promise._state !== PENDING) { return; }
promise._state = REJECTED;
promise._result = reason;
asap(publishRejection, promise);
}
function subscribe(parent, child, onFulfillment, onRejection) {
let { _subscribers } = parent;
let { length } = _subscribers;
parent._onerror = null;
_subscribers[length] = child;
_subscribers[length + FULFILLED] = onFulfillment;
_subscribers[length + REJECTED] = onRejection;
if (length === 0 && parent._state) {
asap(publish, parent);
}
}
function publish(promise) {
let subscribers = promise._subscribers;
let settled = promise._state;
if (subscribers.length === 0) { return; }
let child, callback, detail = promise._result;
for (let i = 0; i < subscribers.length; i += 3) {
child = subscribers[i];
callback = subscribers[i + settled];
if (child) {
invokeCallback(settled, child, callback, detail);
} else {
callback(detail);
}
}
promise._subscribers.length = 0;
}
function invokeCallback(settled, promise, callback, detail) {
let hasCallback = isFunction(callback),
value, error, succeeded = true;
if (hasCallback) {
try {
value = callback(detail);
} catch (e) {
succeeded = false;
error = e;
}
if (promise === value) {
reject(promise, cannotReturnOwn());
return;
}
} else {
value = detail;
}
if (promise._state !== PENDING) {
// noop
} else if (hasCallback && succeeded) {
resolve(promise, value);
} else if (succeeded === false) {
reject(promise, error);
} else if (settled === FULFILLED) {
fulfill(promise, value);
} else if (settled === REJECTED) {
reject(promise, value);
}
}
function initializePromise(promise, resolver) {
try {
resolver(function resolvePromise(value){
resolve(promise, value);
}, function rejectPromise(reason) {
reject(promise, reason);
});
} catch(e) {
reject(promise, e);
}
}
let id = 0;
function nextId() {
return id++;
}
function makePromise(promise) {
promise[PROMISE_ID] = id++;
promise._state = undefined;
promise._result = undefined;
promise._subscribers = [];
}
export {
nextId,
makePromise,
noop,
resolve,
reject,
fulfill,
subscribe,
publish,
publishRejection,
initializePromise,
invokeCallback,
FULFILLED,
REJECTED,
PENDING,
handleMaybeThenable
};

119
node_modules/es6-promise/lib/es6-promise/asap.js generated vendored Normal file
View File

@ -0,0 +1,119 @@
let len = 0;
let vertxNext;
let customSchedulerFn;
export var asap = function asap(callback, arg) {
queue[len] = callback;
queue[len + 1] = arg;
len += 2;
if (len === 2) {
// If len is 2, that means that we need to schedule an async flush.
// If additional callbacks are queued before the queue is flushed, they
// will be processed by this flush that we are scheduling.
if (customSchedulerFn) {
customSchedulerFn(flush);
} else {
scheduleFlush();
}
}
}
export function setScheduler(scheduleFn) {
customSchedulerFn = scheduleFn;
}
export function setAsap(asapFn) {
asap = asapFn;
}
const browserWindow = (typeof window !== 'undefined') ? window : undefined;
const browserGlobal = browserWindow || {};
const BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
const isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
// test for web worker but not in IE10
const isWorker = typeof Uint8ClampedArray !== 'undefined' &&
typeof importScripts !== 'undefined' &&
typeof MessageChannel !== 'undefined';
// node
function useNextTick() {
// node version 0.10.x displays a deprecation warning when nextTick is used recursively
// see https://github.com/cujojs/when/issues/410 for details
return () => process.nextTick(flush);
}
// vertx
function useVertxTimer() {
if (typeof vertxNext !== 'undefined') {
return function() {
vertxNext(flush);
};
}
return useSetTimeout();
}
function useMutationObserver() {
let iterations = 0;
const observer = new BrowserMutationObserver(flush);
const node = document.createTextNode('');
observer.observe(node, { characterData: true });
return () => {
node.data = (iterations = ++iterations % 2);
};
}
// web worker
function useMessageChannel() {
const channel = new MessageChannel();
channel.port1.onmessage = flush;
return () => channel.port2.postMessage(0);
}
function useSetTimeout() {
// Store setTimeout reference so es6-promise will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
const globalSetTimeout = setTimeout;
return () => globalSetTimeout(flush, 1);
}
const queue = new Array(1000);
function flush() {
for (let i = 0; i < len; i+=2) {
let callback = queue[i];
let arg = queue[i+1];
callback(arg);
queue[i] = undefined;
queue[i+1] = undefined;
}
len = 0;
}
function attemptVertx() {
try {
const vertx = Function('return this')().require('vertx');
vertxNext = vertx.runOnLoop || vertx.runOnContext;
return useVertxTimer();
} catch(e) {
return useSetTimeout();
}
}
let scheduleFlush;
// Decide what async method to use to triggering processing of queued callbacks:
if (isNode) {
scheduleFlush = useNextTick();
} else if (BrowserMutationObserver) {
scheduleFlush = useMutationObserver();
} else if (isWorker) {
scheduleFlush = useMessageChannel();
} else if (browserWindow === undefined && typeof require === 'function') {
scheduleFlush = attemptVertx();
} else {
scheduleFlush = useSetTimeout();
}

124
node_modules/es6-promise/lib/es6-promise/enumerator.js generated vendored Normal file
View File

@ -0,0 +1,124 @@
import {
isArray,
isMaybeThenable
} from './utils';
import {
noop,
reject,
fulfill,
subscribe,
FULFILLED,
REJECTED,
PENDING,
handleMaybeThenable
} from './-internal';
import then from './then';
import Promise from './promise';
import originalResolve from './promise/resolve';
import originalThen from './then';
import { makePromise, PROMISE_ID } from './-internal';
function validationError() {
return new Error('Array Methods must be provided an Array');
};
export default class Enumerator {
constructor(Constructor, input) {
this._instanceConstructor = Constructor;
this.promise = new Constructor(noop);
if (!this.promise[PROMISE_ID]) {
makePromise(this.promise);
}
if (isArray(input)) {
this.length = input.length;
this._remaining = input.length;
this._result = new Array(this.length);
if (this.length === 0) {
fulfill(this.promise, this._result);
} else {
this.length = this.length || 0;
this._enumerate(input);
if (this._remaining === 0) {
fulfill(this.promise, this._result);
}
}
} else {
reject(this.promise, validationError());
}
}
_enumerate(input) {
for (let i = 0; this._state === PENDING && i < input.length; i++) {
this._eachEntry(input[i], i);
}
}
_eachEntry(entry, i) {
let c = this._instanceConstructor;
let { resolve } = c;
if (resolve === originalResolve) {
let then;
let error;
let didError = false;
try {
then = entry.then;
} catch (e) {
didError = true;
error = e;
}
if (then === originalThen &&
entry._state !== PENDING) {
this._settledAt(entry._state, i, entry._result);
} else if (typeof then !== 'function') {
this._remaining--;
this._result[i] = entry;
} else if (c === Promise) {
let promise = new c(noop);
if (didError) {
reject(promise, error);
} else {
handleMaybeThenable(promise, entry, then);
}
this._willSettleAt(promise, i);
} else {
this._willSettleAt(new c(resolve => resolve(entry)), i);
}
} else {
this._willSettleAt(resolve(entry), i);
}
}
_settledAt(state, i, value) {
let { promise } = this;
if (promise._state === PENDING) {
this._remaining--;
if (state === REJECTED) {
reject(promise, value);
} else {
this._result[i] = value;
}
}
if (this._remaining === 0) {
fulfill(promise, this._result);
}
}
_willSettleAt(promise, i) {
let enumerator = this;
subscribe(
promise, undefined,
value => enumerator._settledAt(FULFILLED, i, value),
reason => enumerator._settledAt(REJECTED, i, reason)
);
}
};

35
node_modules/es6-promise/lib/es6-promise/polyfill.js generated vendored Normal file
View File

@ -0,0 +1,35 @@
/*global self*/
import Promise from './promise';
export default function polyfill() {
let local;
if (typeof global !== 'undefined') {
local = global;
} else if (typeof self !== 'undefined') {
local = self;
} else {
try {
local = Function('return this')();
} catch (e) {
throw new Error('polyfill failed because global object is unavailable in this environment');
}
}
let P = local.Promise;
if (P) {
var promiseToString = null;
try {
promiseToString = Object.prototype.toString.call(P.resolve());
} catch(e) {
// silently ignored
}
if (promiseToString === '[object Promise]' && !P.cast){
return;
}
}
local.Promise = Promise;
}

431
node_modules/es6-promise/lib/es6-promise/promise.js generated vendored Normal file
View File

@ -0,0 +1,431 @@
import {
isFunction
} from './utils';
import {
noop,
nextId,
PROMISE_ID,
initializePromise
} from './-internal';
import {
asap,
setAsap,
setScheduler
} from './asap';
import all from './promise/all';
import race from './promise/race';
import Resolve from './promise/resolve';
import Reject from './promise/reject';
import then from './then';
function needsResolver() {
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}
function needsNew() {
throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
}
/**
Promise objects represent the eventual result of an asynchronous operation. The
primary way of interacting with a promise is through its `then` method, which
registers callbacks to receive either a promise's eventual value or the reason
why the promise cannot be fulfilled.
Terminology
-----------
- `promise` is an object or function with a `then` method whose behavior conforms to this specification.
- `thenable` is an object or function that defines a `then` method.
- `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
- `exception` is a value that is thrown using the throw statement.
- `reason` is a value that indicates why a promise was rejected.
- `settled` the final resting state of a promise, fulfilled or rejected.
A promise can be in one of three states: pending, fulfilled, or rejected.
Promises that are fulfilled have a fulfillment value and are in the fulfilled
state. Promises that are rejected have a rejection reason and are in the
rejected state. A fulfillment value is never a thenable.
Promises can also be said to *resolve* a value. If this value is also a
promise, then the original promise's settled state will match the value's
settled state. So a promise that *resolves* a promise that rejects will
itself reject, and a promise that *resolves* a promise that fulfills will
itself fulfill.
Basic Usage:
------------
```js
let promise = new Promise(function(resolve, reject) {
// on success
resolve(value);
// on failure
reject(reason);
});
promise.then(function(value) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Advanced Usage:
---------------
Promises shine when abstracting away asynchronous interactions such as
`XMLHttpRequest`s.
```js
function getJSON(url) {
return new Promise(function(resolve, reject){
let xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
function handler() {
if (this.readyState === this.DONE) {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
}
}
};
});
}
getJSON('/posts.json').then(function(json) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Unlike callbacks, promises are great composable primitives.
```js
Promise.all([
getJSON('/posts'),
getJSON('/comments')
]).then(function(values){
values[0] // => postsJSON
values[1] // => commentsJSON
return values;
});
```
@class Promise
@param {Function} resolver
Useful for tooling.
@constructor
*/
class Promise {
constructor(resolver) {
this[PROMISE_ID] = nextId();
this._result = this._state = undefined;
this._subscribers = [];
if (noop !== resolver) {
typeof resolver !== 'function' && needsResolver();
this instanceof Promise ? initializePromise(this, resolver) : needsNew();
}
}
/**
The primary way of interacting with a promise is through its `then` method,
which registers callbacks to receive either a promise's eventual value or the
reason why the promise cannot be fulfilled.
```js
findUser().then(function(user){
// user is available
}, function(reason){
// user is unavailable, and you are given the reason why
});
```
Chaining
--------
The return value of `then` is itself a promise. This second, 'downstream'
promise is resolved with the return value of the first promise's fulfillment
or rejection handler, or rejected if the handler throws an exception.
```js
findUser().then(function (user) {
return user.name;
}, function (reason) {
return 'default name';
}).then(function (userName) {
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it
// will be `'default name'`
});
findUser().then(function (user) {
throw new Error('Found user, but still unhappy');
}, function (reason) {
throw new Error('`findUser` rejected and we're unhappy');
}).then(function (value) {
// never reached
}, function (reason) {
// if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
// If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
});
```
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
```js
findUser().then(function (user) {
throw new PedagogicalException('Upstream error');
}).then(function (value) {
// never reached
}).then(function (value) {
// never reached
}, function (reason) {
// The `PedgagocialException` is propagated all the way down to here
});
```
Assimilation
------------
Sometimes the value you want to propagate to a downstream promise can only be
retrieved asynchronously. This can be achieved by returning a promise in the
fulfillment or rejection handler. The downstream promise will then be pending
until the returned promise is settled. This is called *assimilation*.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// The user's comments are now available
});
```
If the assimliated promise rejects, then the downstream promise will also reject.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// If `findCommentsByAuthor` fulfills, we'll have the value here
}, function (reason) {
// If `findCommentsByAuthor` rejects, we'll have the reason here
});
```
Simple Example
--------------
Synchronous Example
```javascript
let result;
try {
result = findResult();
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
findResult(function(result, err){
if (err) {
// failure
} else {
// success
}
});
```
Promise Example;
```javascript
findResult().then(function(result){
// success
}, function(reason){
// failure
});
```
Advanced Example
--------------
Synchronous Example
```javascript
let author, books;
try {
author = findAuthor();
books = findBooksByAuthor(author);
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
function foundBooks(books) {
}
function failure(reason) {
}
findAuthor(function(author, err){
if (err) {
failure(err);
// failure
} else {
try {
findBoooksByAuthor(author, function(books, err) {
if (err) {
failure(err);
} else {
try {
foundBooks(books);
} catch(reason) {
failure(reason);
}
}
});
} catch(error) {
failure(err);
}
// success
}
});
```
Promise Example;
```javascript
findAuthor().
then(findBooksByAuthor).
then(function(books){
// found books
}).catch(function(reason){
// something went wrong
});
```
@method then
@param {Function} onFulfilled
@param {Function} onRejected
Useful for tooling.
@return {Promise}
*/
/**
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
as the catch block of a try/catch statement.
```js
function findAuthor(){
throw new Error('couldn't find that author');
}
// synchronous
try {
findAuthor();
} catch(reason) {
// something went wrong
}
// async with promises
findAuthor().catch(function(reason){
// something went wrong
});
```
@method catch
@param {Function} onRejection
Useful for tooling.
@return {Promise}
*/
catch(onRejection) {
return this.then(null, onRejection);
}
/**
`finally` will be invoked regardless of the promise's fate just as native
try/catch/finally behaves
Synchronous example:
```js
findAuthor() {
if (Math.random() > 0.5) {
throw new Error();
}
return new Author();
}
try {
return findAuthor(); // succeed or fail
} catch(error) {
return findOtherAuther();
} finally {
// always runs
// doesn't affect the return value
}
```
Asynchronous example:
```js
findAuthor().catch(function(reason){
return findOtherAuther();
}).finally(function(){
// author was either found, or not
});
```
@method finally
@param {Function} callback
@return {Promise}
*/
finally(callback) {
let promise = this;
let constructor = promise.constructor;
if ( isFunction(callback) ) {
return promise.then(value => constructor.resolve(callback()).then(() => value),
reason => constructor.resolve(callback()).then(() => { throw reason; }));
}
return promise.then(callback, callback);
}
}
Promise.prototype.then = then;
export default Promise;
Promise.all = all;
Promise.race = race;
Promise.resolve = Resolve;
Promise.reject = Reject;
Promise._setScheduler = setScheduler;
Promise._setAsap = setAsap;
Promise._asap = asap;

View File

@ -0,0 +1,52 @@
import Enumerator from '../enumerator';
/**
`Promise.all` accepts an array of promises, and returns a new promise which
is fulfilled with an array of fulfillment values for the passed promises, or
rejected with the reason of the first passed promise to be rejected. It casts all
elements of the passed iterable to promises as it runs this algorithm.
Example:
```javascript
let promise1 = resolve(1);
let promise2 = resolve(2);
let promise3 = resolve(3);
let promises = [ promise1, promise2, promise3 ];
Promise.all(promises).then(function(array){
// The array here would be [ 1, 2, 3 ];
});
```
If any of the `promises` given to `all` are rejected, the first promise
that is rejected will be given as an argument to the returned promises's
rejection handler. For example:
Example:
```javascript
let promise1 = resolve(1);
let promise2 = reject(new Error("2"));
let promise3 = reject(new Error("3"));
let promises = [ promise1, promise2, promise3 ];
Promise.all(promises).then(function(array){
// Code here never runs because there are rejected promises!
}, function(error) {
// error.message === "2"
});
```
@method all
@static
@param {Array} entries array of promises
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise} promise that is fulfilled when all `promises` have been
fulfilled, or rejected if any of them become rejected.
@static
*/
export default function all(entries) {
return new Enumerator(this, entries).promise;
}

View File

@ -0,0 +1,84 @@
import {
isArray
} from "../utils";
/**
`Promise.race` returns a new promise which is settled in the same way as the
first passed promise to settle.
Example:
```javascript
let promise1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('promise 1');
}, 200);
});
let promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('promise 2');
}, 100);
});
Promise.race([promise1, promise2]).then(function(result){
// result === 'promise 2' because it was resolved before promise1
// was resolved.
});
```
`Promise.race` is deterministic in that only the state of the first
settled promise matters. For example, even if other promises given to the
`promises` array argument are resolved, but the first settled promise has
become rejected before the other promises became fulfilled, the returned
promise will become rejected:
```javascript
let promise1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('promise 1');
}, 200);
});
let promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
reject(new Error('promise 2'));
}, 100);
});
Promise.race([promise1, promise2]).then(function(result){
// Code here never runs
}, function(reason){
// reason.message === 'promise 2' because promise 2 became rejected before
// promise 1 became fulfilled
});
```
An example real-world use case is implementing timeouts:
```javascript
Promise.race([ajax('foo.json'), timeout(5000)])
```
@method race
@static
@param {Array} promises array of promises to observe
Useful for tooling.
@return {Promise} a promise which settles in the same way as the first passed
promise to settle.
*/
export default function race(entries) {
/*jshint validthis:true */
let Constructor = this;
if (!isArray(entries)) {
return new Constructor((_, reject) => reject(new TypeError('You must pass an array to race.')));
} else {
return new Constructor((resolve, reject) => {
let length = entries.length;
for (let i = 0; i < length; i++) {
Constructor.resolve(entries[i]).then(resolve, reject);
}
});
}
}

View File

@ -0,0 +1,46 @@
import {
noop,
reject as _reject
} from '../-internal';
/**
`Promise.reject` returns a promise rejected with the passed `reason`.
It is shorthand for the following:
```javascript
let promise = new Promise(function(resolve, reject){
reject(new Error('WHOOPS'));
});
promise.then(function(value){
// Code here doesn't run because the promise is rejected!
}, function(reason){
// reason.message === 'WHOOPS'
});
```
Instead of writing the above, your code now simply becomes the following:
```javascript
let promise = Promise.reject(new Error('WHOOPS'));
promise.then(function(value){
// Code here doesn't run because the promise is rejected!
}, function(reason){
// reason.message === 'WHOOPS'
});
```
@method reject
@static
@param {Any} reason value that the returned promise will be rejected with.
Useful for tooling.
@return {Promise} a promise rejected with the given `reason`.
*/
export default function reject(reason) {
/*jshint validthis:true */
let Constructor = this;
let promise = new Constructor(noop);
_reject(promise, reason);
return promise;
}

View File

@ -0,0 +1,48 @@
import {
noop,
resolve as _resolve
} from '../-internal';
/**
`Promise.resolve` returns a promise that will become resolved with the
passed `value`. It is shorthand for the following:
```javascript
let promise = new Promise(function(resolve, reject){
resolve(1);
});
promise.then(function(value){
// value === 1
});
```
Instead of writing the above, your code now simply becomes the following:
```javascript
let promise = Promise.resolve(1);
promise.then(function(value){
// value === 1
});
```
@method resolve
@static
@param {Any} value value that the returned promise will be resolved with
Useful for tooling.
@return {Promise} a promise that will become fulfilled with the given
`value`
*/
export default function resolve(object) {
/*jshint validthis:true */
let Constructor = this;
if (object && typeof object === 'object' && object.constructor === Constructor) {
return object;
}
let promise = new Constructor(noop);
_resolve(promise, object);
return promise;
}

32
node_modules/es6-promise/lib/es6-promise/then.js generated vendored Normal file
View File

@ -0,0 +1,32 @@
import {
invokeCallback,
subscribe,
FULFILLED,
REJECTED,
noop,
makePromise,
PROMISE_ID
} from './-internal';
import { asap } from './asap';
export default function then(onFulfillment, onRejection) {
const parent = this;
const child = new this.constructor(noop);
if (child[PROMISE_ID] === undefined) {
makePromise(child);
}
const { _state } = parent;
if (_state) {
const callback = arguments[_state - 1];
asap(() => invokeCallback(_state, child, callback, parent._result));
} else {
subscribe(parent, child, onFulfillment, onRejection);
}
return child;
}

21
node_modules/es6-promise/lib/es6-promise/utils.js generated vendored Normal file
View File

@ -0,0 +1,21 @@
export function objectOrFunction(x) {
let type = typeof x;
return x !== null && (type === 'object' || type === 'function');
}
export function isFunction(x) {
return typeof x === 'function';
}
export function isMaybeThenable(x) {
return x !== null && typeof x === 'object';
}
let _isArray;
if (Array.isArray) {
_isArray = Array.isArray;
} else {
_isArray = x => Object.prototype.toString.call(x) === '[object Array]';
}
export const isArray = _isArray;

78
node_modules/es6-promise/package.json generated vendored Normal file
View File

@ -0,0 +1,78 @@
{
"name": "es6-promise",
"description": "A lightweight library that provides tools for organizing asynchronous code",
"version": "4.2.8",
"author": "Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)",
"browser": {
"vertx": false
},
"bugs": {
"url": "https://github.com/stefanpenner/es6-promise/issues"
},
"dependencies": {},
"devDependencies": {
"babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
"babel-plugin-transform-es2015-block-scoping": "^6.24.1",
"babel-plugin-transform-es2015-classes": "^6.24.1",
"babel-plugin-transform-es2015-computed-properties": "^6.24.1",
"babel-plugin-transform-es2015-constants": "^6.1.4",
"babel-plugin-transform-es2015-destructuring": "^6.23.0",
"babel-plugin-transform-es2015-parameters": "^6.24.1",
"babel-plugin-transform-es2015-shorthand-properties": "^6.24.1",
"babel-plugin-transform-es2015-spread": "^6.22.0",
"babel-plugin-transform-es2015-template-literals": "^6.22.0",
"babel6-plugin-strip-class-callcheck": "^6.0.0",
"broccoli-babel-transpiler": "^6.0.0",
"broccoli-concat": "^3.1.0",
"broccoli-merge-trees": "^2.0.0",
"broccoli-rollup": "^2.0.0",
"broccoli-stew": "^1.5.0",
"broccoli-uglify-js": "^0.2.0",
"broccoli-watchify": "^1.0.1",
"ember-cli": "2.18.0-beta.2",
"ember-cli-dependency-checker": "^2.1.0",
"git-repo-version": "1.0.1",
"json3": "^3.3.2",
"mocha": "^4.0.1",
"promises-aplus-tests-phantom": "^2.1.0-revise"
},
"directories": {
"lib": "lib"
},
"files": [
"dist",
"lib",
"es6-promise.d.ts",
"auto.js",
"!dist/test"
],
"homepage": "https://github.com/stefanpenner/es6-promise",
"jsdelivr": "dist/es6-promise.auto.min.js",
"keywords": [
"futures",
"polyfill",
"promise",
"promises"
],
"license": "MIT",
"main": "dist/es6-promise.js",
"namespace": "es6-promise",
"repository": {
"type": "git",
"url": "git://github.com/stefanpenner/es6-promise.git"
},
"scripts": {
"build": "ember build --environment production",
"prepublishOnly": "ember build --environment production",
"start": "ember s",
"test": "ember test",
"test:browser": "ember test --launch PhantomJS",
"test:node": "ember test --launch Mocha",
"test:server": "ember test --server"
},
"spm": {
"main": "dist/es6-promise.js"
},
"typings": "es6-promise.d.ts",
"unpkg": "dist/es6-promise.auto.min.js"
}

2
node_modules/flv.js/.eslintignore generated vendored Normal file
View File

@ -0,0 +1,2 @@
dist/
node_modules/

46
node_modules/flv.js/.eslintrc.json generated vendored Normal file
View File

@ -0,0 +1,46 @@
{
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"impliedStrict": true
}
},
"env": {
"es6": true,
"worker": true,
"node": true,
"browser": true
},
"rules": {
"keyword-spacing": [2, {
"overrides": {
"if": {"after": true},
"for": {"after": true},
"while": {"after": true},
"switch": {"after": true},
"catch": {"after": true}
}
}],
"key-spacing": [2, {"beforeColon": false, "afterColon": true, "mode": "strict"}],
"arrow-spacing": 2,
"comma-spacing": 2,
"comma-style": 2,
"indent": [2, 4, {"SwitchCase": 1}],
"no-var": 2,
"no-bitwise": 0,
"no-alert": 2,
"no-console": 0,
"no-debugger": 1,
"no-unused-vars": 0,
"no-mixed-spaces-and-tabs": 2,
"quotes": [2, "single", "avoid-escape"],
"semi": 2,
"semi-spacing": 2,
"space-before-blocks": 2,
"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
"space-in-parens": [2, "never"],
"space-infix-ops": 2
}
}

202
node_modules/flv.js/LICENSE generated vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

104
node_modules/flv.js/README.md generated vendored Normal file
View File

@ -0,0 +1,104 @@
flv.js [![npm](https://img.shields.io/npm/v/flv.js.svg?style=flat)](https://www.npmjs.com/package/flv.js)
======
An HTML5 Flash Video (FLV) Player written in pure JavaScript without Flash. LONG LIVE FLV!
This project relies on [Media Source Extensions][] to work.
**For FLV live stream playback, please consider [mpegts.js][] which is under active development.**
**This project will become rarely maintained.**
[mpegts.js]: https://github.com/xqq/mpegts.js
## Overview
flv.js works by transmuxing FLV file stream into ISO BMFF (Fragmented MP4) segments, followed by feeding mp4 segments into an HTML5 `<video>` element through [Media Source Extensions][] API.
[Media Source Extensions]: https://w3c.github.io/media-source/
## Demo
[http://bilibili.github.io/flv.js/demo/](http://bilibili.github.io/flv.js/demo/)
## Features
- FLV container with H.264 + AAC / MP3 codec playback
- Multipart segmented video playback
- HTTP FLV low latency live stream playback
- FLV over WebSocket live stream playback
- Compatible with Chrome, FireFox, Safari 10, IE11 and Edge
- Extremely low overhead, and hardware accelerated by your browser!
## Installation
```bash
npm install --save flv.js
```
## Build
```bash
npm ci # install dependencies / dev-dependences
npm run build:debug # debug version flv.js will be emitted to /dist
npm run build # minimized release version flv.min.js will be emitted to /dist
```
[cnpm](https://github.com/cnpm/cnpm) mirror is recommended if you are in Mainland China.
## CORS
If you use standalone video server for FLV stream, `Access-Control-Allow-Origin` header must be configured correctly on video server for cross-origin resource fetching.
See [cors.md](docs/cors.md) for more details.
## Getting Started
```html
<script src="flv.min.js"></script>
<video id="videoElement"></video>
<script>
if (flvjs.isSupported()) {
var videoElement = document.getElementById('videoElement');
var flvPlayer = flvjs.createPlayer({
type: 'flv',
url: 'http://example.com/flv/video.flv'
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
flvPlayer.play();
}
</script>
```
## Limitations
- MP3 audio codec is currently not working on IE11 / Edge
- HTTP FLV live stream is not currently working on all browsers, see [livestream.md](docs/livestream.md)
## Multipart playback
You only have to provide a playlist for `MediaDataSource`. See [multipart.md](docs/multipart.md)
## Livestream playback
See [livestream.md](docs/livestream.md)
## API and Configuration
See [api.md](docs/api.md)
## Debug
```bash
npm ci # install dependencies / dev-dependences
npm run dev # watch file changes and build debug version on the fly
```
## Design
See [design.md](docs/design.md)
## License
```
Copyright (C) 2016 Bilibili. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```

418
node_modules/flv.js/d.ts/flv.d.ts generated vendored Normal file
View File

@ -0,0 +1,418 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// flv.js TypeScript definition file
declare namespace FlvJs {
interface MediaSegment {
duration: number;
filesize?: number;
url: string;
}
interface MediaDataSource {
type: string;
isLive?: boolean;
cors?: boolean;
withCredentials?: boolean;
hasAudio?: boolean;
hasVideo?: boolean;
duration?: number;
filesize?: number;
url?: string;
segments?: MediaSegment[];
}
interface Config {
/**
* @desc Enable separated thread for transmuxing (unstable for now)
* @defaultvalue false
*/
enableWorker?: boolean;
/**
* @desc Enable IO stash buffer. Set to false if you need realtime (minimal latency) for live stream
* playback, but may stalled if there's network jittering.
* @defaultvalue true
*/
enableStashBuffer?: boolean;
/**
* @desc Indicates IO stash buffer initial size. Default is `384KB`. Indicate a suitable size can
* improve video load/seek time.
*/
stashInitialSize?: number;
/**
* @desc Same to `isLive` in **MediaDataSource**, ignored if has been set in MediaDataSource structure.
* @defaultvalue false
*/
isLive?: boolean;
/**
* @desc Abort the http connection if there's enough data for playback.
* @defaultvalue true
*/
lazyLoad?: boolean;
/**
* @desc Indicates how many seconds of data to be kept for `lazyLoad`.
* @defaultvalue 3 * 60
*/
lazyLoadMaxDuration?: number;
/**
* @desc Indicates the `lazyLoad` recover time boundary in seconds.
* @defaultvalue 30
*/
lazyLoadRecoverDuration?: number;
/**
* @desc Do load after MediaSource `sourceopen` event triggered. On Chrome, tabs which
* be opened in background may not trigger `sourceopen` event until switched to that tab.
* @defaultvalue true
*/
deferLoadAfterSourceOpen?: boolean;
/**
* @desc Do auto cleanup for SourceBuffer
* @defaultvalue false (from docs)
*/
autoCleanupSourceBuffer?: boolean;
/**
* @desc When backward buffer duration exceeded this value (in seconds), do auto cleanup for SourceBuffer
* @defaultvalue 3 * 60
*/
autoCleanupMaxBackwardDuration?: number;
/**
* @desc Indicates the duration in seconds to reserve for backward buffer when doing auto cleanup.
* @defaultvalue 2 * 60
*/
autoCleanupMinBackwardDuration?: number;
/**
* @defaultvalue 600
*/
statisticsInfoReportInterval?: number;
/**
* @desc Fill silent audio frames to avoid a/v unsync when detect large audio timestamp gap.
* @defaultvalue true
*/
fixAudioTimestampGap?: boolean;
/**
* @desc Accurate seek to any frame, not limited to video IDR frame, but may a bit slower.
* Available on Chrome > 50, FireFox and Safari.
* @defaultvalue false
*/
accurateSeek?: boolean;
/**
* @desc 'range' use range request to seek, or 'param' add params into url to indicate request range.
* @defaultvalue 'range'
*/
seekType?: 'range' | 'param' | 'custom';
/**
* @desc Indicates seek start parameter name for seekType = 'param'
* @defaultvalue 'bstart'
*/
seekParamStart?: string;
/**
* @desc Indicates seek end parameter name for seekType = 'param'
* @defaultvalue 'bend'
*/
seekParamEnd?: string;
/**
* @desc Send Range: bytes=0- for first time load if use Range seek
* @defaultvalue false
*/
rangeLoadZeroStart?: boolean;
/**
* @desc Indicates a custom seek handler
* @desc Should implement `SeekHandler` interface
*/
customSeekHandler?: CustomSeekHandlerConstructor;
/**
* @desc Reuse 301/302 redirected url for subsequence request like seek, reconnect, etc.
* @defaultvalue false
*/
reuseRedirectedURL?: boolean;
/**
* @desc Indicates the Referrer Policy when using FetchStreamLoader
* @defaultvalue 'no-referrer-when-downgrade' (from docs)
*/
referrerPolicy?: ReferrerPolicy;
/**
* @desc Indicates additional headers that will be added to request
*/
headers?: {
[k: string]: string
}
/**
* @desc Should implement `BaseLoader` interface
*/
customLoader?: CustomLoaderConstructor;
}
interface CustomSeekHandlerConstructor {
new(): SeekHandler;
}
interface SeekHandler {
getConfig(sourceURL: string, range: Range): SeekConfig;
removeURLParameters(url: string): string;
}
interface SeekConfig {
url: string;
headers: Headers | object;
}
interface BaseLoaderConstructor {
new(typeName: string): BaseLoader;
}
interface BaseLoader {
_status: number;
_needStash: boolean;
destroy(): void;
isWorking(): boolean;
readonly type: string;
readonly status: number;
readonly needStashBuffer: boolean;
onContentLengthKnown: (contentLength: number) => void;
onURLRedirect: (redirectedURL: string) => void;
onDataArrival: (chunk: ArrayBuffer, byteStart: number, receivedLength?: number) => void;
onError: (errorType: LoaderErrors, errorInfo: LoaderErrorMessage) => void;
onComplete: (rangeFrom: number, rangeTo: number) => void;
open(dataSource: MediaSegment, range: Range): void;
abort(): void;
}
interface CustomLoaderConstructor {
new(seekHandler: SeekHandler, config: Config): BaseLoader;
}
interface Range {
from: number;
to: number;
}
interface LoaderStatus {
readonly kIdle: 0;
readonly kConnecting: 1;
readonly kBuffering: 2;
readonly kError: 3;
readonly kComplete: 4;
}
interface LoaderErrors {
readonly OK: 'OK';
readonly EXCEPTION: 'Exception';
readonly HTTP_STATUS_CODE_INVALID: 'HttpStatusCodeInvalid';
readonly CONNECTING_TIMEOUT: 'ConnectingTimeout';
readonly EARLY_EOF: 'EarlyEof';
readonly UNRECOVERABLE_EARLY_EOF: 'UnrecoverableEarlyEof';
}
interface LoaderErrorMessage {
code: number;
msg: string;
}
interface FeatureList {
mseFlvPlayback: boolean;
mseLiveFlvPlayback: boolean;
networkStreamIO: boolean;
networkLoaderName: string;
nativeMP4H264Playback: boolean;
nativeWebmVP8Playback: boolean;
nativeWebmVP9Playback: boolean;
}
interface PlayerConstructor {
new (mediaDataSource: MediaDataSource, config?: Config): Player;
}
interface Player {
destroy(): void;
on(event: string, listener: (...args: any[]) => void): void;
off(event: string, listener: (...args: any[]) => void): void;
attachMediaElement(mediaElement: HTMLMediaElement): void;
detachMediaElement(): void;
load(): void;
unload(): void;
play(): Promise<void> | void;
pause(): void;
type: string;
buffered: TimeRanges;
duration: number;
volume: number;
muted: boolean;
currentTime: number;
/**
* @deprecated FlvPlayer/NativePlayer have its own `mediaInfo` field.
* @desc Keep it for backwards compatibility
* @since 1.4
*/
mediaInfo: NativePlayerMediaInfo | FlvPlayerMediaInfo;
/**
* @deprecated FlvPlayer/NativePlayer have its own `statisticsInfo` field.
* @desc Keep it for backwards compatibility
* @since 1.4
*/
statisticsInfo: NativePlayerStatisticsInfo | FlvPlayerStatisticsInfo;
}
interface NativePlayerStatisticsInfo {
playerType: 'NativePlayer';
url: string;
decodedFrames?: number;
droppedFrames?: number;
}
interface FlvPlayerReportStatisticsInfo {
url: string;
hasRedirect: boolean;
redirectedURL?: string;
speed: number; // KB/s
loaderType: string;
currentSegmentIndex: number;
totalSegmentCount: number;
}
interface FlvPlayerStatisticsInfo extends Partial<FlvPlayerReportStatisticsInfo> {
playerType: 'FlvPlayer';
decodedFrames?: number;
droppedFrames?: number;
}
interface NativePlayerMediaInfo {
mimeType: string;
duration?: number;
width?: number;
height?: number;
}
interface FlvPlayerMediaInfo extends NativePlayerMediaInfo {
audioCodec?: string;
videoCodec?: string;
audioDataRate?: number;
videoDataRate?: number;
hasAudio?: boolean;
hasVideo?: boolean;
chromaFormat?: string;
fps?: number;
[k: string]: any;
}
interface FlvPlayer extends Player {
mediaInfo: FlvPlayerMediaInfo;
statisticsInfo: FlvPlayerStatisticsInfo;
}
interface NativePlayer extends Player {
mediaInfo: NativePlayerMediaInfo;
statisticsInfo: NativePlayerStatisticsInfo;
}
interface LoggingControlConfig {
forceGlobalTag: boolean;
globalTag: string;
enableAll: boolean;
enableDebug: boolean;
enableVerbose: boolean;
enableInfo: boolean;
enableWarn: boolean;
enableError: boolean;
}
interface LoggingControl extends LoggingControlConfig {
getConfig(): LoggingControlConfig;
applyConfig(config: Partial<LoggingControlConfig>): void;
addLogListener(listener: (...args: any[]) => void): void;
removeLogListener(listener: (...args: any[]) => void): void;
}
interface Events {
ERROR: string;
LOADING_COMPLETE: string;
RECOVERED_EARLY_EOF: string;
MEDIA_INFO: string;
METADATA_ARRIVED: string;
SCRIPTDATA_ARRIVED: string;
STATISTICS_INFO: string;
}
interface ErrorTypes {
NETWORK_ERROR: string;
MEDIA_ERROR: string;
OTHER_ERROR: string;
}
interface ErrorDetails {
NETWORK_EXCEPTION: string;
NETWORK_STATUS_CODE_INVALID: string;
NETWORK_TIMEOUT: string;
NETWORK_UNRECOVERABLE_EARLY_EOF: string;
MEDIA_MSE_ERROR: string;
MEDIA_FORMAT_ERROR: string;
MEDIA_FORMAT_UNSUPPORTED: string;
MEDIA_CODEC_UNSUPPORTED: string;
}
}
declare var FlvJs: {
createPlayer(mediaDataSource: FlvJs.MediaDataSource, config?: FlvJs.Config): FlvJs.Player;
isSupported(): boolean;
getFeatureList(): FlvJs.FeatureList;
/**
* @deprecated Use `FlvJs.BaseLoaderConstructor` instead.
* Because it's not available on `flvjs` variable.
* @desc implement interface `BaseLoader`
* @since 1.4
*/
BaseLoader: FlvJs.BaseLoaderConstructor;
/**
* @deprecated Use `FlvJs.BaseLoaderConstructor` instead.
* Because it's not available on `flvjs` variable.
* @since 1.4
*/
LoaderStatus: FlvJs.LoaderStatus;
/**
* @deprecated Use `FlvJs.BaseLoaderConstructor` instead.
* Because it's not available on `flvjs` variable.
* @since 1.4
*/
LoaderErrors: FlvJs.LoaderErrors;
readonly version: string;
readonly Events: Readonly<FlvJs.Events>;
readonly ErrorTypes: Readonly<FlvJs.ErrorTypes>;
readonly ErrorDetails: Readonly<FlvJs.ErrorDetails>;
readonly FlvPlayer: FlvJs.PlayerConstructor;
readonly NativePlayer: FlvJs.PlayerConstructor;
readonly LoggingControl: FlvJs.LoggingControl;
};
export default FlvJs;

10585
node_modules/flv.js/dist/flv.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/flv.js/dist/flv.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

10
node_modules/flv.js/dist/flv.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

1
node_modules/flv.js/dist/flv.min.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

43
node_modules/flv.js/package.json generated vendored Normal file
View File

@ -0,0 +1,43 @@
{
"name": "flv.js",
"version": "1.6.2",
"description": "HTML5 FLV Player",
"main": "./dist/flv.js",
"types": "./d.ts/flv.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/Bilibili/flv.js"
},
"keywords": [
"html5",
"flv",
"mse",
"javascript"
],
"scripts": {
"build": "webpack --mode=production --progress",
"build:debug": "webpack --mode=development --progress",
"dev": "webpack --mode=development --progress --watch",
"dtslint": "dtslint types"
},
"dependencies": {
"es6-promise": "^4.2.8",
"webworkify-webpack": "^2.1.5"
},
"devDependencies": {
"@types/node": "^16.3.3",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.28.3",
"browser-sync": "^2.27.4",
"eslint": "^7.30.0",
"exports-loader": "^3.0.0",
"source-map-loader": "^3.0.0",
"terser-webpack-plugin": "^5.1.4",
"ts-loader": "^9.2.3",
"typescript": "^4.3.5",
"webpack": "^5.45.1",
"webpack-cli": "^4.7.2"
},
"author": "zheng qian <xqq@xqq.im>",
"license": "Apache-2.0"
}

54
node_modules/flv.js/src/config.js generated vendored Normal file
View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const defaultConfig = {
enableWorker: false,
enableStashBuffer: true,
stashInitialSize: undefined,
isLive: false,
lazyLoad: true,
lazyLoadMaxDuration: 3 * 60,
lazyLoadRecoverDuration: 30,
deferLoadAfterSourceOpen: true,
// autoCleanupSourceBuffer: default as false, leave unspecified
autoCleanupMaxBackwardDuration: 3 * 60,
autoCleanupMinBackwardDuration: 2 * 60,
statisticsInfoReportInterval: 600,
fixAudioTimestampGap: true,
accurateSeek: false,
seekType: 'range', // [range, param, custom]
seekParamStart: 'bstart',
seekParamEnd: 'bend',
rangeLoadZeroStart: false,
customSeekHandler: undefined,
reuseRedirectedURL: false,
// referrerPolicy: leave as unspecified
headers: undefined,
customLoader: undefined
};
export function createDefaultConfig() {
return Object.assign({}, defaultConfig);
}

75
node_modules/flv.js/src/core/features.js generated vendored Normal file
View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import IOController from '../io/io-controller.js';
import {createDefaultConfig} from '../config.js';
class Features {
static supportMSEH264Playback() {
return window.MediaSource &&
window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"');
}
static supportNetworkStreamIO() {
let ioctl = new IOController({}, createDefaultConfig());
let loaderType = ioctl.loaderType;
ioctl.destroy();
return loaderType == 'fetch-stream-loader' || loaderType == 'xhr-moz-chunked-loader';
}
static getNetworkLoaderTypeName() {
let ioctl = new IOController({}, createDefaultConfig());
let loaderType = ioctl.loaderType;
ioctl.destroy();
return loaderType;
}
static supportNativeMediaPlayback(mimeType) {
if (Features.videoElement == undefined) {
Features.videoElement = window.document.createElement('video');
}
let canPlay = Features.videoElement.canPlayType(mimeType);
return canPlay === 'probably' || canPlay == 'maybe';
}
static getFeatureList() {
let features = {
mseFlvPlayback: false,
mseLiveFlvPlayback: false,
networkStreamIO: false,
networkLoaderName: '',
nativeMP4H264Playback: false,
nativeWebmVP8Playback: false,
nativeWebmVP9Playback: false
};
features.mseFlvPlayback = Features.supportMSEH264Playback();
features.networkStreamIO = Features.supportNetworkStreamIO();
features.networkLoaderName = Features.getNetworkLoaderTypeName();
features.mseLiveFlvPlayback = features.mseFlvPlayback && features.networkStreamIO;
features.nativeMP4H264Playback = Features.supportNativeMediaPlayback('video/mp4; codecs="avc1.42001E, mp4a.40.2"');
features.nativeWebmVP8Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp8.0, vorbis"');
features.nativeWebmVP9Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp9"');
return features;
}
}
export default Features;

130
node_modules/flv.js/src/core/media-info.js generated vendored Normal file
View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class MediaInfo {
constructor() {
this.mimeType = null;
this.duration = null;
this.hasAudio = null;
this.hasVideo = null;
this.audioCodec = null;
this.videoCodec = null;
this.audioDataRate = null;
this.videoDataRate = null;
this.audioSampleRate = null;
this.audioChannelCount = null;
this.width = null;
this.height = null;
this.fps = null;
this.profile = null;
this.level = null;
this.refFrames = null;
this.chromaFormat = null;
this.sarNum = null;
this.sarDen = null;
this.metadata = null;
this.segments = null; // MediaInfo[]
this.segmentCount = null;
this.hasKeyframesIndex = null;
this.keyframesIndex = null;
}
isComplete() {
let audioInfoComplete = (this.hasAudio === false) ||
(this.hasAudio === true &&
this.audioCodec != null &&
this.audioSampleRate != null &&
this.audioChannelCount != null);
let videoInfoComplete = (this.hasVideo === false) ||
(this.hasVideo === true &&
this.videoCodec != null &&
this.width != null &&
this.height != null &&
this.fps != null &&
this.profile != null &&
this.level != null &&
this.refFrames != null &&
this.chromaFormat != null &&
this.sarNum != null &&
this.sarDen != null);
// keyframesIndex may not be present
return this.mimeType != null &&
this.duration != null &&
this.metadata != null &&
this.hasKeyframesIndex != null &&
audioInfoComplete &&
videoInfoComplete;
}
isSeekable() {
return this.hasKeyframesIndex === true;
}
getNearestKeyframe(milliseconds) {
if (this.keyframesIndex == null) {
return null;
}
let table = this.keyframesIndex;
let keyframeIdx = this._search(table.times, milliseconds);
return {
index: keyframeIdx,
milliseconds: table.times[keyframeIdx],
fileposition: table.filepositions[keyframeIdx]
};
}
_search(list, value) {
let idx = 0;
let last = list.length - 1;
let mid = 0;
let lbound = 0;
let ubound = last;
if (value < list[0]) {
idx = 0;
lbound = ubound + 1; // skip search
}
while (lbound <= ubound) {
mid = lbound + Math.floor((ubound - lbound) / 2);
if (mid === last || (value >= list[mid] && value < list[mid + 1])) {
idx = mid;
break;
} else if (list[mid] < value) {
lbound = mid + 1;
} else {
ubound = mid - 1;
}
}
return idx;
}
}
export default MediaInfo;

230
node_modules/flv.js/src/core/media-segment-info.js generated vendored Normal file
View File

@ -0,0 +1,230 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Represents an media sample (audio / video)
export class SampleInfo {
constructor(dts, pts, duration, originalDts, isSync) {
this.dts = dts;
this.pts = pts;
this.duration = duration;
this.originalDts = originalDts;
this.isSyncPoint = isSync;
this.fileposition = null;
}
}
// Media Segment concept is defined in Media Source Extensions spec.
// Particularly in ISO BMFF format, an Media Segment contains a moof box followed by a mdat box.
export class MediaSegmentInfo {
constructor() {
this.beginDts = 0;
this.endDts = 0;
this.beginPts = 0;
this.endPts = 0;
this.originalBeginDts = 0;
this.originalEndDts = 0;
this.syncPoints = []; // SampleInfo[n], for video IDR frames only
this.firstSample = null; // SampleInfo
this.lastSample = null; // SampleInfo
}
appendSyncPoint(sampleInfo) { // also called Random Access Point
sampleInfo.isSyncPoint = true;
this.syncPoints.push(sampleInfo);
}
}
// Ordered list for recording video IDR frames, sorted by originalDts
export class IDRSampleList {
constructor() {
this._list = [];
}
clear() {
this._list = [];
}
appendArray(syncPoints) {
let list = this._list;
if (syncPoints.length === 0) {
return;
}
if (list.length > 0 && syncPoints[0].originalDts < list[list.length - 1].originalDts) {
this.clear();
}
Array.prototype.push.apply(list, syncPoints);
}
getLastSyncPointBeforeDts(dts) {
if (this._list.length == 0) {
return null;
}
let list = this._list;
let idx = 0;
let last = list.length - 1;
let mid = 0;
let lbound = 0;
let ubound = last;
if (dts < list[0].dts) {
idx = 0;
lbound = ubound + 1;
}
while (lbound <= ubound) {
mid = lbound + Math.floor((ubound - lbound) / 2);
if (mid === last || (dts >= list[mid].dts && dts < list[mid + 1].dts)) {
idx = mid;
break;
} else if (list[mid].dts < dts) {
lbound = mid + 1;
} else {
ubound = mid - 1;
}
}
return this._list[idx];
}
}
// Data structure for recording information of media segments in single track.
export class MediaSegmentInfoList {
constructor(type) {
this._type = type;
this._list = [];
this._lastAppendLocation = -1; // cached last insert location
}
get type() {
return this._type;
}
get length() {
return this._list.length;
}
isEmpty() {
return this._list.length === 0;
}
clear() {
this._list = [];
this._lastAppendLocation = -1;
}
_searchNearestSegmentBefore(originalBeginDts) {
let list = this._list;
if (list.length === 0) {
return -2;
}
let last = list.length - 1;
let mid = 0;
let lbound = 0;
let ubound = last;
let idx = 0;
if (originalBeginDts < list[0].originalBeginDts) {
idx = -1;
return idx;
}
while (lbound <= ubound) {
mid = lbound + Math.floor((ubound - lbound) / 2);
if (mid === last || (originalBeginDts > list[mid].lastSample.originalDts &&
(originalBeginDts < list[mid + 1].originalBeginDts))) {
idx = mid;
break;
} else if (list[mid].originalBeginDts < originalBeginDts) {
lbound = mid + 1;
} else {
ubound = mid - 1;
}
}
return idx;
}
_searchNearestSegmentAfter(originalBeginDts) {
return this._searchNearestSegmentBefore(originalBeginDts) + 1;
}
append(mediaSegmentInfo) {
let list = this._list;
let msi = mediaSegmentInfo;
let lastAppendIdx = this._lastAppendLocation;
let insertIdx = 0;
if (lastAppendIdx !== -1 && lastAppendIdx < list.length &&
msi.originalBeginDts >= list[lastAppendIdx].lastSample.originalDts &&
((lastAppendIdx === list.length - 1) ||
(lastAppendIdx < list.length - 1 &&
msi.originalBeginDts < list[lastAppendIdx + 1].originalBeginDts))) {
insertIdx = lastAppendIdx + 1; // use cached location idx
} else {
if (list.length > 0) {
insertIdx = this._searchNearestSegmentBefore(msi.originalBeginDts) + 1;
}
}
this._lastAppendLocation = insertIdx;
this._list.splice(insertIdx, 0, msi);
}
getLastSegmentBefore(originalBeginDts) {
let idx = this._searchNearestSegmentBefore(originalBeginDts);
if (idx >= 0) {
return this._list[idx];
} else { // -1
return null;
}
}
getLastSampleBefore(originalBeginDts) {
let segment = this.getLastSegmentBefore(originalBeginDts);
if (segment != null) {
return segment.lastSample;
} else {
return null;
}
}
getLastSyncPointBefore(originalBeginDts) {
let segmentIdx = this._searchNearestSegmentBefore(originalBeginDts);
let syncPoints = this._list[segmentIdx].syncPoints;
while (syncPoints.length === 0 && segmentIdx > 0) {
segmentIdx--;
syncPoints = this._list[segmentIdx].syncPoints;
}
if (syncPoints.length > 0) {
return syncPoints[syncPoints.length - 1];
} else {
return null;
}
}
}

539
node_modules/flv.js/src/core/mse-controller.js generated vendored Normal file
View File

@ -0,0 +1,539 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import EventEmitter from 'events';
import Log from '../utils/logger.js';
import Browser from '../utils/browser.js';
import MSEEvents from './mse-events.js';
import {SampleInfo, IDRSampleList} from './media-segment-info.js';
import {IllegalStateException} from '../utils/exception.js';
// Media Source Extensions controller
class MSEController {
constructor(config) {
this.TAG = 'MSEController';
this._config = config;
this._emitter = new EventEmitter();
if (this._config.isLive && this._config.autoCleanupSourceBuffer == undefined) {
// For live stream, do auto cleanup by default
this._config.autoCleanupSourceBuffer = true;
}
this.e = {
onSourceOpen: this._onSourceOpen.bind(this),
onSourceEnded: this._onSourceEnded.bind(this),
onSourceClose: this._onSourceClose.bind(this),
onSourceBufferError: this._onSourceBufferError.bind(this),
onSourceBufferUpdateEnd: this._onSourceBufferUpdateEnd.bind(this)
};
this._mediaSource = null;
this._mediaSourceObjectURL = null;
this._mediaElement = null;
this._isBufferFull = false;
this._hasPendingEos = false;
this._requireSetMediaDuration = false;
this._pendingMediaDuration = 0;
this._pendingSourceBufferInit = [];
this._mimeTypes = {
video: null,
audio: null
};
this._sourceBuffers = {
video: null,
audio: null
};
this._lastInitSegments = {
video: null,
audio: null
};
this._pendingSegments = {
video: [],
audio: []
};
this._pendingRemoveRanges = {
video: [],
audio: []
};
this._idrList = new IDRSampleList();
}
destroy() {
if (this._mediaElement || this._mediaSource) {
this.detachMediaElement();
}
this.e = null;
this._emitter.removeAllListeners();
this._emitter = null;
}
on(event, listener) {
this._emitter.addListener(event, listener);
}
off(event, listener) {
this._emitter.removeListener(event, listener);
}
attachMediaElement(mediaElement) {
if (this._mediaSource) {
throw new IllegalStateException('MediaSource has been attached to an HTMLMediaElement!');
}
let ms = this._mediaSource = new window.MediaSource();
ms.addEventListener('sourceopen', this.e.onSourceOpen);
ms.addEventListener('sourceended', this.e.onSourceEnded);
ms.addEventListener('sourceclose', this.e.onSourceClose);
this._mediaElement = mediaElement;
this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource);
mediaElement.src = this._mediaSourceObjectURL;
}
detachMediaElement() {
if (this._mediaSource) {
let ms = this._mediaSource;
for (let type in this._sourceBuffers) {
// pending segments should be discard
let ps = this._pendingSegments[type];
ps.splice(0, ps.length);
this._pendingSegments[type] = null;
this._pendingRemoveRanges[type] = null;
this._lastInitSegments[type] = null;
// remove all sourcebuffers
let sb = this._sourceBuffers[type];
if (sb) {
if (ms.readyState !== 'closed') {
// ms edge can throw an error: Unexpected call to method or property access
try {
ms.removeSourceBuffer(sb);
} catch (error) {
Log.e(this.TAG, error.message);
}
sb.removeEventListener('error', this.e.onSourceBufferError);
sb.removeEventListener('updateend', this.e.onSourceBufferUpdateEnd);
}
this._mimeTypes[type] = null;
this._sourceBuffers[type] = null;
}
}
if (ms.readyState === 'open') {
try {
ms.endOfStream();
} catch (error) {
Log.e(this.TAG, error.message);
}
}
ms.removeEventListener('sourceopen', this.e.onSourceOpen);
ms.removeEventListener('sourceended', this.e.onSourceEnded);
ms.removeEventListener('sourceclose', this.e.onSourceClose);
this._pendingSourceBufferInit = [];
this._isBufferFull = false;
this._idrList.clear();
this._mediaSource = null;
}
if (this._mediaElement) {
this._mediaElement.src = '';
this._mediaElement.removeAttribute('src');
this._mediaElement = null;
}
if (this._mediaSourceObjectURL) {
window.URL.revokeObjectURL(this._mediaSourceObjectURL);
this._mediaSourceObjectURL = null;
}
}
appendInitSegment(initSegment, deferred) {
if (!this._mediaSource || this._mediaSource.readyState !== 'open') {
// sourcebuffer creation requires mediaSource.readyState === 'open'
// so we defer the sourcebuffer creation, until sourceopen event triggered
this._pendingSourceBufferInit.push(initSegment);
// make sure that this InitSegment is in the front of pending segments queue
this._pendingSegments[initSegment.type].push(initSegment);
return;
}
let is = initSegment;
let mimeType = `${is.container}`;
if (is.codec && is.codec.length > 0) {
mimeType += `;codecs=${is.codec}`;
}
let firstInitSegment = false;
Log.v(this.TAG, 'Received Initialization Segment, mimeType: ' + mimeType);
this._lastInitSegments[is.type] = is;
if (mimeType !== this._mimeTypes[is.type]) {
if (!this._mimeTypes[is.type]) { // empty, first chance create sourcebuffer
firstInitSegment = true;
try {
let sb = this._sourceBuffers[is.type] = this._mediaSource.addSourceBuffer(mimeType);
sb.addEventListener('error', this.e.onSourceBufferError);
sb.addEventListener('updateend', this.e.onSourceBufferUpdateEnd);
} catch (error) {
Log.e(this.TAG, error.message);
this._emitter.emit(MSEEvents.ERROR, {code: error.code, msg: error.message});
return;
}
} else {
Log.v(this.TAG, `Notice: ${is.type} mimeType changed, origin: ${this._mimeTypes[is.type]}, target: ${mimeType}`);
}
this._mimeTypes[is.type] = mimeType;
}
if (!deferred) {
// deferred means this InitSegment has been pushed to pendingSegments queue
this._pendingSegments[is.type].push(is);
}
if (!firstInitSegment) { // append immediately only if init segment in subsequence
if (this._sourceBuffers[is.type] && !this._sourceBuffers[is.type].updating) {
this._doAppendSegments();
}
}
if (Browser.safari && is.container === 'audio/mpeg' && is.mediaDuration > 0) {
// 'audio/mpeg' track under Safari may cause MediaElement's duration to be NaN
// Manually correct MediaSource.duration to make progress bar seekable, and report right duration
this._requireSetMediaDuration = true;
this._pendingMediaDuration = is.mediaDuration / 1000; // in seconds
this._updateMediaSourceDuration();
}
}
appendMediaSegment(mediaSegment) {
let ms = mediaSegment;
this._pendingSegments[ms.type].push(ms);
if (this._config.autoCleanupSourceBuffer && this._needCleanupSourceBuffer()) {
this._doCleanupSourceBuffer();
}
let sb = this._sourceBuffers[ms.type];
if (sb && !sb.updating && !this._hasPendingRemoveRanges()) {
this._doAppendSegments();
}
}
seek(seconds) {
// remove all appended buffers
for (let type in this._sourceBuffers) {
if (!this._sourceBuffers[type]) {
continue;
}
// abort current buffer append algorithm
let sb = this._sourceBuffers[type];
if (this._mediaSource.readyState === 'open') {
try {
// If range removal algorithm is running, InvalidStateError will be throwed
// Ignore it.
sb.abort();
} catch (error) {
Log.e(this.TAG, error.message);
}
}
// IDRList should be clear
this._idrList.clear();
// pending segments should be discard
let ps = this._pendingSegments[type];
ps.splice(0, ps.length);
if (this._mediaSource.readyState === 'closed') {
// Parent MediaSource object has been detached from HTMLMediaElement
continue;
}
// record ranges to be remove from SourceBuffer
for (let i = 0; i < sb.buffered.length; i++) {
let start = sb.buffered.start(i);
let end = sb.buffered.end(i);
this._pendingRemoveRanges[type].push({start, end});
}
// if sb is not updating, let's remove ranges now!
if (!sb.updating) {
this._doRemoveRanges();
}
// Safari 10 may get InvalidStateError in the later appendBuffer() after SourceBuffer.remove() call
// Internal parser's state may be invalid at this time. Re-append last InitSegment to workaround.
// Related issue: https://bugs.webkit.org/show_bug.cgi?id=159230
if (Browser.safari) {
let lastInitSegment = this._lastInitSegments[type];
if (lastInitSegment) {
this._pendingSegments[type].push(lastInitSegment);
if (!sb.updating) {
this._doAppendSegments();
}
}
}
}
}
endOfStream() {
let ms = this._mediaSource;
let sb = this._sourceBuffers;
if (!ms || ms.readyState !== 'open') {
if (ms && ms.readyState === 'closed' && this._hasPendingSegments()) {
// If MediaSource hasn't turned into open state, and there're pending segments
// Mark pending endOfStream, defer call until all pending segments appended complete
this._hasPendingEos = true;
}
return;
}
if (sb.video && sb.video.updating || sb.audio && sb.audio.updating) {
// If any sourcebuffer is updating, defer endOfStream operation
// See _onSourceBufferUpdateEnd()
this._hasPendingEos = true;
} else {
this._hasPendingEos = false;
// Notify media data loading complete
// This is helpful for correcting total duration to match last media segment
// Otherwise MediaElement's ended event may not be triggered
ms.endOfStream();
}
}
getNearestKeyframe(dts) {
return this._idrList.getLastSyncPointBeforeDts(dts);
}
_needCleanupSourceBuffer() {
if (!this._config.autoCleanupSourceBuffer) {
return false;
}
let currentTime = this._mediaElement.currentTime;
for (let type in this._sourceBuffers) {
let sb = this._sourceBuffers[type];
if (sb) {
let buffered = sb.buffered;
if (buffered.length >= 1) {
if (currentTime - buffered.start(0) >= this._config.autoCleanupMaxBackwardDuration) {
return true;
}
}
}
}
return false;
}
_doCleanupSourceBuffer() {
let currentTime = this._mediaElement.currentTime;
for (let type in this._sourceBuffers) {
let sb = this._sourceBuffers[type];
if (sb) {
let buffered = sb.buffered;
let doRemove = false;
for (let i = 0; i < buffered.length; i++) {
let start = buffered.start(i);
let end = buffered.end(i);
if (start <= currentTime && currentTime < end + 3) { // padding 3 seconds
if (currentTime - start >= this._config.autoCleanupMaxBackwardDuration) {
doRemove = true;
let removeEnd = currentTime - this._config.autoCleanupMinBackwardDuration;
this._pendingRemoveRanges[type].push({start: start, end: removeEnd});
}
} else if (end < currentTime) {
doRemove = true;
this._pendingRemoveRanges[type].push({start: start, end: end});
}
}
if (doRemove && !sb.updating) {
this._doRemoveRanges();
}
}
}
}
_updateMediaSourceDuration() {
let sb = this._sourceBuffers;
if (this._mediaElement.readyState === 0 || this._mediaSource.readyState !== 'open') {
return;
}
if ((sb.video && sb.video.updating) || (sb.audio && sb.audio.updating)) {
return;
}
let current = this._mediaSource.duration;
let target = this._pendingMediaDuration;
if (target > 0 && (isNaN(current) || target > current)) {
Log.v(this.TAG, `Update MediaSource duration from ${current} to ${target}`);
this._mediaSource.duration = target;
}
this._requireSetMediaDuration = false;
this._pendingMediaDuration = 0;
}
_doRemoveRanges() {
for (let type in this._pendingRemoveRanges) {
if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) {
continue;
}
let sb = this._sourceBuffers[type];
let ranges = this._pendingRemoveRanges[type];
while (ranges.length && !sb.updating) {
let range = ranges.shift();
sb.remove(range.start, range.end);
}
}
}
_doAppendSegments() {
let pendingSegments = this._pendingSegments;
for (let type in pendingSegments) {
if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) {
continue;
}
if (pendingSegments[type].length > 0) {
let segment = pendingSegments[type].shift();
if (segment.timestampOffset) {
// For MPEG audio stream in MSE, if unbuffered-seeking occurred
// We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer.
let currentOffset = this._sourceBuffers[type].timestampOffset;
let targetOffset = segment.timestampOffset / 1000; // in seconds
let delta = Math.abs(currentOffset - targetOffset);
if (delta > 0.1) { // If time delta > 100ms
Log.v(this.TAG, `Update MPEG audio timestampOffset from ${currentOffset} to ${targetOffset}`);
this._sourceBuffers[type].timestampOffset = targetOffset;
}
delete segment.timestampOffset;
}
if (!segment.data || segment.data.byteLength === 0) {
// Ignore empty buffer
continue;
}
try {
this._sourceBuffers[type].appendBuffer(segment.data);
this._isBufferFull = false;
if (type === 'video' && segment.hasOwnProperty('info')) {
this._idrList.appendArray(segment.info.syncPoints);
}
} catch (error) {
this._pendingSegments[type].unshift(segment);
if (error.code === 22) { // QuotaExceededError
/* Notice that FireFox may not throw QuotaExceededError if SourceBuffer is full
* Currently we can only do lazy-load to avoid SourceBuffer become scattered.
* SourceBuffer eviction policy may be changed in future version of FireFox.
*
* Related issues:
* https://bugzilla.mozilla.org/show_bug.cgi?id=1279885
* https://bugzilla.mozilla.org/show_bug.cgi?id=1280023
*/
// report buffer full, abort network IO
if (!this._isBufferFull) {
this._emitter.emit(MSEEvents.BUFFER_FULL);
}
this._isBufferFull = true;
} else {
Log.e(this.TAG, error.message);
this._emitter.emit(MSEEvents.ERROR, {code: error.code, msg: error.message});
}
}
}
}
}
_onSourceOpen() {
Log.v(this.TAG, 'MediaSource onSourceOpen');
this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen);
// deferred sourcebuffer creation / initialization
if (this._pendingSourceBufferInit.length > 0) {
let pendings = this._pendingSourceBufferInit;
while (pendings.length) {
let segment = pendings.shift();
this.appendInitSegment(segment, true);
}
}
// there may be some pending media segments, append them
if (this._hasPendingSegments()) {
this._doAppendSegments();
}
this._emitter.emit(MSEEvents.SOURCE_OPEN);
}
_onSourceEnded() {
// fired on endOfStream
Log.v(this.TAG, 'MediaSource onSourceEnded');
}
_onSourceClose() {
// fired on detaching from media element
Log.v(this.TAG, 'MediaSource onSourceClose');
if (this._mediaSource && this.e != null) {
this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen);
this._mediaSource.removeEventListener('sourceended', this.e.onSourceEnded);
this._mediaSource.removeEventListener('sourceclose', this.e.onSourceClose);
}
}
_hasPendingSegments() {
let ps = this._pendingSegments;
return ps.video.length > 0 || ps.audio.length > 0;
}
_hasPendingRemoveRanges() {
let prr = this._pendingRemoveRanges;
return prr.video.length > 0 || prr.audio.length > 0;
}
_onSourceBufferUpdateEnd() {
if (this._requireSetMediaDuration) {
this._updateMediaSourceDuration();
} else if (this._hasPendingRemoveRanges()) {
this._doRemoveRanges();
} else if (this._hasPendingSegments()) {
this._doAppendSegments();
} else if (this._hasPendingEos) {
this.endOfStream();
}
this._emitter.emit(MSEEvents.UPDATE_END);
}
_onSourceBufferError(e) {
Log.e(this.TAG, `SourceBuffer Error: ${e}`);
// this error might not always be fatal, just ignore it
}
}
export default MSEController;

26
node_modules/flv.js/src/core/mse-events.js generated vendored Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const MSEEvents = {
ERROR: 'error',
SOURCE_OPEN: 'source_open',
UPDATE_END: 'update_end',
BUFFER_FULL: 'buffer_full'
};
export default MSEEvents;

257
node_modules/flv.js/src/core/transmuxer.js generated vendored Normal file
View File

@ -0,0 +1,257 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import EventEmitter from 'events';
import work from 'webworkify-webpack';
import Log from '../utils/logger.js';
import LoggingControl from '../utils/logging-control.js';
import TransmuxingController from './transmuxing-controller.js';
import TransmuxingEvents from './transmuxing-events.js';
import TransmuxingWorker from './transmuxing-worker.js';
import MediaInfo from './media-info.js';
class Transmuxer {
constructor(mediaDataSource, config) {
this.TAG = 'Transmuxer';
this._emitter = new EventEmitter();
if (config.enableWorker && typeof (Worker) !== 'undefined') {
try {
this._worker = work(require.resolve('./transmuxing-worker'));
this._workerDestroying = false;
this._worker.addEventListener('message', this._onWorkerMessage.bind(this));
this._worker.postMessage({cmd: 'init', param: [mediaDataSource, config]});
this.e = {
onLoggingConfigChanged: this._onLoggingConfigChanged.bind(this)
};
LoggingControl.registerListener(this.e.onLoggingConfigChanged);
this._worker.postMessage({cmd: 'logging_config', param: LoggingControl.getConfig()});
} catch (error) {
Log.e(this.TAG, 'Error while initialize transmuxing worker, fallback to inline transmuxing');
this._worker = null;
this._controller = new TransmuxingController(mediaDataSource, config);
}
} else {
this._controller = new TransmuxingController(mediaDataSource, config);
}
if (this._controller) {
let ctl = this._controller;
ctl.on(TransmuxingEvents.IO_ERROR, this._onIOError.bind(this));
ctl.on(TransmuxingEvents.DEMUX_ERROR, this._onDemuxError.bind(this));
ctl.on(TransmuxingEvents.INIT_SEGMENT, this._onInitSegment.bind(this));
ctl.on(TransmuxingEvents.MEDIA_SEGMENT, this._onMediaSegment.bind(this));
ctl.on(TransmuxingEvents.LOADING_COMPLETE, this._onLoadingComplete.bind(this));
ctl.on(TransmuxingEvents.RECOVERED_EARLY_EOF, this._onRecoveredEarlyEof.bind(this));
ctl.on(TransmuxingEvents.MEDIA_INFO, this._onMediaInfo.bind(this));
ctl.on(TransmuxingEvents.METADATA_ARRIVED, this._onMetaDataArrived.bind(this));
ctl.on(TransmuxingEvents.SCRIPTDATA_ARRIVED, this._onScriptDataArrived.bind(this));
ctl.on(TransmuxingEvents.STATISTICS_INFO, this._onStatisticsInfo.bind(this));
ctl.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, this._onRecommendSeekpoint.bind(this));
}
}
destroy() {
if (this._worker) {
if (!this._workerDestroying) {
this._workerDestroying = true;
this._worker.postMessage({cmd: 'destroy'});
LoggingControl.removeListener(this.e.onLoggingConfigChanged);
this.e = null;
}
} else {
this._controller.destroy();
this._controller = null;
}
this._emitter.removeAllListeners();
this._emitter = null;
}
on(event, listener) {
this._emitter.addListener(event, listener);
}
off(event, listener) {
this._emitter.removeListener(event, listener);
}
hasWorker() {
return this._worker != null;
}
open() {
if (this._worker) {
this._worker.postMessage({cmd: 'start'});
} else {
this._controller.start();
}
}
close() {
if (this._worker) {
this._worker.postMessage({cmd: 'stop'});
} else {
this._controller.stop();
}
}
seek(milliseconds) {
if (this._worker) {
this._worker.postMessage({cmd: 'seek', param: milliseconds});
} else {
this._controller.seek(milliseconds);
}
}
pause() {
if (this._worker) {
this._worker.postMessage({cmd: 'pause'});
} else {
this._controller.pause();
}
}
resume() {
if (this._worker) {
this._worker.postMessage({cmd: 'resume'});
} else {
this._controller.resume();
}
}
_onInitSegment(type, initSegment) {
// do async invoke
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.INIT_SEGMENT, type, initSegment);
});
}
_onMediaSegment(type, mediaSegment) {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.MEDIA_SEGMENT, type, mediaSegment);
});
}
_onLoadingComplete() {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.LOADING_COMPLETE);
});
}
_onRecoveredEarlyEof() {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.RECOVERED_EARLY_EOF);
});
}
_onMediaInfo(mediaInfo) {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.MEDIA_INFO, mediaInfo);
});
}
_onMetaDataArrived(metadata) {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.METADATA_ARRIVED, metadata);
});
}
_onScriptDataArrived(data) {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.SCRIPTDATA_ARRIVED, data);
});
}
_onStatisticsInfo(statisticsInfo) {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.STATISTICS_INFO, statisticsInfo);
});
}
_onIOError(type, info) {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.IO_ERROR, type, info);
});
}
_onDemuxError(type, info) {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, type, info);
});
}
_onRecommendSeekpoint(milliseconds) {
Promise.resolve().then(() => {
this._emitter.emit(TransmuxingEvents.RECOMMEND_SEEKPOINT, milliseconds);
});
}
_onLoggingConfigChanged(config) {
if (this._worker) {
this._worker.postMessage({cmd: 'logging_config', param: config});
}
}
_onWorkerMessage(e) {
let message = e.data;
let data = message.data;
if (message.msg === 'destroyed' || this._workerDestroying) {
this._workerDestroying = false;
this._worker.terminate();
this._worker = null;
return;
}
switch (message.msg) {
case TransmuxingEvents.INIT_SEGMENT:
case TransmuxingEvents.MEDIA_SEGMENT:
this._emitter.emit(message.msg, data.type, data.data);
break;
case TransmuxingEvents.LOADING_COMPLETE:
case TransmuxingEvents.RECOVERED_EARLY_EOF:
this._emitter.emit(message.msg);
break;
case TransmuxingEvents.MEDIA_INFO:
Object.setPrototypeOf(data, MediaInfo.prototype);
this._emitter.emit(message.msg, data);
break;
case TransmuxingEvents.METADATA_ARRIVED:
case TransmuxingEvents.SCRIPTDATA_ARRIVED:
case TransmuxingEvents.STATISTICS_INFO:
this._emitter.emit(message.msg, data);
break;
case TransmuxingEvents.IO_ERROR:
case TransmuxingEvents.DEMUX_ERROR:
this._emitter.emit(message.msg, data.type, data.info);
break;
case TransmuxingEvents.RECOMMEND_SEEKPOINT:
this._emitter.emit(message.msg, data);
break;
case 'logcat_callback':
Log.emitter.emit('log', data.type, data.logcat);
break;
default:
break;
}
}
}
export default Transmuxer;

439
node_modules/flv.js/src/core/transmuxing-controller.js generated vendored Normal file
View File

@ -0,0 +1,439 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import EventEmitter from 'events';
import Log from '../utils/logger.js';
import Browser from '../utils/browser.js';
import MediaInfo from './media-info.js';
import FLVDemuxer from '../demux/flv-demuxer.js';
import MP4Remuxer from '../remux/mp4-remuxer.js';
import DemuxErrors from '../demux/demux-errors.js';
import IOController from '../io/io-controller.js';
import TransmuxingEvents from './transmuxing-events.js';
import {LoaderStatus, LoaderErrors} from '../io/loader.js';
// Transmuxing (IO, Demuxing, Remuxing) controller, with multipart support
class TransmuxingController {
constructor(mediaDataSource, config) {
this.TAG = 'TransmuxingController';
this._emitter = new EventEmitter();
this._config = config;
// treat single part media as multipart media, which has only one segment
if (!mediaDataSource.segments) {
mediaDataSource.segments = [{
duration: mediaDataSource.duration,
filesize: mediaDataSource.filesize,
url: mediaDataSource.url
}];
}
// fill in default IO params if not exists
if (typeof mediaDataSource.cors !== 'boolean') {
mediaDataSource.cors = true;
}
if (typeof mediaDataSource.withCredentials !== 'boolean') {
mediaDataSource.withCredentials = false;
}
this._mediaDataSource = mediaDataSource;
this._currentSegmentIndex = 0;
let totalDuration = 0;
this._mediaDataSource.segments.forEach((segment) => {
// timestampBase for each segment, and calculate total duration
segment.timestampBase = totalDuration;
totalDuration += segment.duration;
// params needed by IOController
segment.cors = mediaDataSource.cors;
segment.withCredentials = mediaDataSource.withCredentials;
// referrer policy control, if exist
if (config.referrerPolicy) {
segment.referrerPolicy = config.referrerPolicy;
}
});
if (!isNaN(totalDuration) && this._mediaDataSource.duration !== totalDuration) {
this._mediaDataSource.duration = totalDuration;
}
this._mediaInfo = null;
this._demuxer = null;
this._remuxer = null;
this._ioctl = null;
this._pendingSeekTime = null;
this._pendingResolveSeekPoint = null;
this._statisticsReporter = null;
}
destroy() {
this._mediaInfo = null;
this._mediaDataSource = null;
if (this._statisticsReporter) {
this._disableStatisticsReporter();
}
if (this._ioctl) {
this._ioctl.destroy();
this._ioctl = null;
}
if (this._demuxer) {
this._demuxer.destroy();
this._demuxer = null;
}
if (this._remuxer) {
this._remuxer.destroy();
this._remuxer = null;
}
this._emitter.removeAllListeners();
this._emitter = null;
}
on(event, listener) {
this._emitter.addListener(event, listener);
}
off(event, listener) {
this._emitter.removeListener(event, listener);
}
start() {
this._loadSegment(0);
this._enableStatisticsReporter();
}
_loadSegment(segmentIndex, optionalFrom) {
this._currentSegmentIndex = segmentIndex;
let dataSource = this._mediaDataSource.segments[segmentIndex];
let ioctl = this._ioctl = new IOController(dataSource, this._config, segmentIndex);
ioctl.onError = this._onIOException.bind(this);
ioctl.onSeeked = this._onIOSeeked.bind(this);
ioctl.onComplete = this._onIOComplete.bind(this);
ioctl.onRedirect = this._onIORedirect.bind(this);
ioctl.onRecoveredEarlyEof = this._onIORecoveredEarlyEof.bind(this);
if (optionalFrom) {
this._demuxer.bindDataSource(this._ioctl);
} else {
ioctl.onDataArrival = this._onInitChunkArrival.bind(this);
}
ioctl.open(optionalFrom);
}
stop() {
this._internalAbort();
this._disableStatisticsReporter();
}
_internalAbort() {
if (this._ioctl) {
this._ioctl.destroy();
this._ioctl = null;
}
}
pause() { // take a rest
if (this._ioctl && this._ioctl.isWorking()) {
this._ioctl.pause();
this._disableStatisticsReporter();
}
}
resume() {
if (this._ioctl && this._ioctl.isPaused()) {
this._ioctl.resume();
this._enableStatisticsReporter();
}
}
seek(milliseconds) {
if (this._mediaInfo == null || !this._mediaInfo.isSeekable()) {
return;
}
let targetSegmentIndex = this._searchSegmentIndexContains(milliseconds);
if (targetSegmentIndex === this._currentSegmentIndex) {
// intra-segment seeking
let segmentInfo = this._mediaInfo.segments[targetSegmentIndex];
if (segmentInfo == undefined) {
// current segment loading started, but mediainfo hasn't received yet
// wait for the metadata loaded, then seek to expected position
this._pendingSeekTime = milliseconds;
} else {
let keyframe = segmentInfo.getNearestKeyframe(milliseconds);
this._remuxer.seek(keyframe.milliseconds);
this._ioctl.seek(keyframe.fileposition);
// Will be resolved in _onRemuxerMediaSegmentArrival()
this._pendingResolveSeekPoint = keyframe.milliseconds;
}
} else {
// cross-segment seeking
let targetSegmentInfo = this._mediaInfo.segments[targetSegmentIndex];
if (targetSegmentInfo == undefined) {
// target segment hasn't been loaded. We need metadata then seek to expected time
this._pendingSeekTime = milliseconds;
this._internalAbort();
this._remuxer.seek();
this._remuxer.insertDiscontinuity();
this._loadSegment(targetSegmentIndex);
// Here we wait for the metadata loaded, then seek to expected position
} else {
// We have target segment's metadata, direct seek to target position
let keyframe = targetSegmentInfo.getNearestKeyframe(milliseconds);
this._internalAbort();
this._remuxer.seek(milliseconds);
this._remuxer.insertDiscontinuity();
this._demuxer.resetMediaInfo();
this._demuxer.timestampBase = this._mediaDataSource.segments[targetSegmentIndex].timestampBase;
this._loadSegment(targetSegmentIndex, keyframe.fileposition);
this._pendingResolveSeekPoint = keyframe.milliseconds;
this._reportSegmentMediaInfo(targetSegmentIndex);
}
}
this._enableStatisticsReporter();
}
_searchSegmentIndexContains(milliseconds) {
let segments = this._mediaDataSource.segments;
let idx = segments.length - 1;
for (let i = 0; i < segments.length; i++) {
if (milliseconds < segments[i].timestampBase) {
idx = i - 1;
break;
}
}
return idx;
}
_onInitChunkArrival(data, byteStart) {
let probeData = null;
let consumed = 0;
if (byteStart > 0) {
// IOController seeked immediately after opened, byteStart > 0 callback may received
this._demuxer.bindDataSource(this._ioctl);
this._demuxer.timestampBase = this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase;
consumed = this._demuxer.parseChunks(data, byteStart);
} else if ((probeData = FLVDemuxer.probe(data)).match) {
// Always create new FLVDemuxer
this._demuxer = new FLVDemuxer(probeData, this._config);
if (!this._remuxer) {
this._remuxer = new MP4Remuxer(this._config);
}
let mds = this._mediaDataSource;
if (mds.duration != undefined && !isNaN(mds.duration)) {
this._demuxer.overridedDuration = mds.duration;
}
if (typeof mds.hasAudio === 'boolean') {
this._demuxer.overridedHasAudio = mds.hasAudio;
}
if (typeof mds.hasVideo === 'boolean') {
this._demuxer.overridedHasVideo = mds.hasVideo;
}
this._demuxer.timestampBase = mds.segments[this._currentSegmentIndex].timestampBase;
this._demuxer.onError = this._onDemuxException.bind(this);
this._demuxer.onMediaInfo = this._onMediaInfo.bind(this);
this._demuxer.onMetaDataArrived = this._onMetaDataArrived.bind(this);
this._demuxer.onScriptDataArrived = this._onScriptDataArrived.bind(this);
this._remuxer.bindDataSource(this._demuxer
.bindDataSource(this._ioctl
));
this._remuxer.onInitSegment = this._onRemuxerInitSegmentArrival.bind(this);
this._remuxer.onMediaSegment = this._onRemuxerMediaSegmentArrival.bind(this);
consumed = this._demuxer.parseChunks(data, byteStart);
} else {
probeData = null;
Log.e(this.TAG, 'Non-FLV, Unsupported media type!');
Promise.resolve().then(() => {
this._internalAbort();
});
this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, DemuxErrors.FORMAT_UNSUPPORTED, 'Non-FLV, Unsupported media type');
consumed = 0;
}
return consumed;
}
_onMediaInfo(mediaInfo) {
if (this._mediaInfo == null) {
// Store first segment's mediainfo as global mediaInfo
this._mediaInfo = Object.assign({}, mediaInfo);
this._mediaInfo.keyframesIndex = null;
this._mediaInfo.segments = [];
this._mediaInfo.segmentCount = this._mediaDataSource.segments.length;
Object.setPrototypeOf(this._mediaInfo, MediaInfo.prototype);
}
let segmentInfo = Object.assign({}, mediaInfo);
Object.setPrototypeOf(segmentInfo, MediaInfo.prototype);
this._mediaInfo.segments[this._currentSegmentIndex] = segmentInfo;
// notify mediaInfo update
this._reportSegmentMediaInfo(this._currentSegmentIndex);
if (this._pendingSeekTime != null) {
Promise.resolve().then(() => {
let target = this._pendingSeekTime;
this._pendingSeekTime = null;
this.seek(target);
});
}
}
_onMetaDataArrived(metadata) {
this._emitter.emit(TransmuxingEvents.METADATA_ARRIVED, metadata);
}
_onScriptDataArrived(data) {
this._emitter.emit(TransmuxingEvents.SCRIPTDATA_ARRIVED, data);
}
_onIOSeeked() {
this._remuxer.insertDiscontinuity();
}
_onIOComplete(extraData) {
let segmentIndex = extraData;
let nextSegmentIndex = segmentIndex + 1;
if (nextSegmentIndex < this._mediaDataSource.segments.length) {
this._internalAbort();
this._remuxer.flushStashedSamples();
this._loadSegment(nextSegmentIndex);
} else {
this._remuxer.flushStashedSamples();
this._emitter.emit(TransmuxingEvents.LOADING_COMPLETE);
this._disableStatisticsReporter();
}
}
_onIORedirect(redirectedURL) {
let segmentIndex = this._ioctl.extraData;
this._mediaDataSource.segments[segmentIndex].redirectedURL = redirectedURL;
}
_onIORecoveredEarlyEof() {
this._emitter.emit(TransmuxingEvents.RECOVERED_EARLY_EOF);
}
_onIOException(type, info) {
Log.e(this.TAG, `IOException: type = ${type}, code = ${info.code}, msg = ${info.msg}`);
this._emitter.emit(TransmuxingEvents.IO_ERROR, type, info);
this._disableStatisticsReporter();
}
_onDemuxException(type, info) {
Log.e(this.TAG, `DemuxException: type = ${type}, info = ${info}`);
this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, type, info);
}
_onRemuxerInitSegmentArrival(type, initSegment) {
this._emitter.emit(TransmuxingEvents.INIT_SEGMENT, type, initSegment);
}
_onRemuxerMediaSegmentArrival(type, mediaSegment) {
if (this._pendingSeekTime != null) {
// Media segments after new-segment cross-seeking should be dropped.
return;
}
this._emitter.emit(TransmuxingEvents.MEDIA_SEGMENT, type, mediaSegment);
// Resolve pending seekPoint
if (this._pendingResolveSeekPoint != null && type === 'video') {
let syncPoints = mediaSegment.info.syncPoints;
let seekpoint = this._pendingResolveSeekPoint;
this._pendingResolveSeekPoint = null;
// Safari: Pass PTS for recommend_seekpoint
if (Browser.safari && syncPoints.length > 0 && syncPoints[0].originalDts === seekpoint) {
seekpoint = syncPoints[0].pts;
}
// else: use original DTS (keyframe.milliseconds)
this._emitter.emit(TransmuxingEvents.RECOMMEND_SEEKPOINT, seekpoint);
}
}
_enableStatisticsReporter() {
if (this._statisticsReporter == null) {
this._statisticsReporter = self.setInterval(
this._reportStatisticsInfo.bind(this),
this._config.statisticsInfoReportInterval);
}
}
_disableStatisticsReporter() {
if (this._statisticsReporter) {
self.clearInterval(this._statisticsReporter);
this._statisticsReporter = null;
}
}
_reportSegmentMediaInfo(segmentIndex) {
let segmentInfo = this._mediaInfo.segments[segmentIndex];
let exportInfo = Object.assign({}, segmentInfo);
exportInfo.duration = this._mediaInfo.duration;
exportInfo.segmentCount = this._mediaInfo.segmentCount;
delete exportInfo.segments;
delete exportInfo.keyframesIndex;
this._emitter.emit(TransmuxingEvents.MEDIA_INFO, exportInfo);
}
_reportStatisticsInfo() {
let info = {};
info.url = this._ioctl.currentURL;
info.hasRedirect = this._ioctl.hasRedirect;
if (info.hasRedirect) {
info.redirectedURL = this._ioctl.currentRedirectedURL;
}
info.speed = this._ioctl.currentSpeed;
info.loaderType = this._ioctl.loaderType;
info.currentSegmentIndex = this._currentSegmentIndex;
info.totalSegmentCount = this._mediaDataSource.segments.length;
this._emitter.emit(TransmuxingEvents.STATISTICS_INFO, info);
}
}
export default TransmuxingController;

33
node_modules/flv.js/src/core/transmuxing-events.js generated vendored Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const TransmuxingEvents = {
IO_ERROR: 'io_error',
DEMUX_ERROR: 'demux_error',
INIT_SEGMENT: 'init_segment',
MEDIA_SEGMENT: 'media_segment',
LOADING_COMPLETE: 'loading_complete',
RECOVERED_EARLY_EOF: 'recovered_early_eof',
MEDIA_INFO: 'media_info',
METADATA_ARRIVED: 'metadata_arrived',
SCRIPTDATA_ARRIVED: 'scriptdata_arrived',
STATISTICS_INFO: 'statistics_info',
RECOMMEND_SEEKPOINT: 'recommend_seekpoint'
};
export default TransmuxingEvents;

205
node_modules/flv.js/src/core/transmuxing-worker.js generated vendored Normal file
View File

@ -0,0 +1,205 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Log from '../utils/logger.js';
import LoggingControl from '../utils/logging-control.js';
import Polyfill from '../utils/polyfill.js';
import TransmuxingController from './transmuxing-controller.js';
import TransmuxingEvents from './transmuxing-events.js';
/* post message to worker:
data: {
cmd: string
param: any
}
receive message from worker:
data: {
msg: string,
data: any
}
*/
let TransmuxingWorker = function (self) {
let TAG = 'TransmuxingWorker';
let controller = null;
let logcatListener = onLogcatCallback.bind(this);
Polyfill.install();
self.addEventListener('message', function (e) {
switch (e.data.cmd) {
case 'init':
controller = new TransmuxingController(e.data.param[0], e.data.param[1]);
controller.on(TransmuxingEvents.IO_ERROR, onIOError.bind(this));
controller.on(TransmuxingEvents.DEMUX_ERROR, onDemuxError.bind(this));
controller.on(TransmuxingEvents.INIT_SEGMENT, onInitSegment.bind(this));
controller.on(TransmuxingEvents.MEDIA_SEGMENT, onMediaSegment.bind(this));
controller.on(TransmuxingEvents.LOADING_COMPLETE, onLoadingComplete.bind(this));
controller.on(TransmuxingEvents.RECOVERED_EARLY_EOF, onRecoveredEarlyEof.bind(this));
controller.on(TransmuxingEvents.MEDIA_INFO, onMediaInfo.bind(this));
controller.on(TransmuxingEvents.METADATA_ARRIVED, onMetaDataArrived.bind(this));
controller.on(TransmuxingEvents.SCRIPTDATA_ARRIVED, onScriptDataArrived.bind(this));
controller.on(TransmuxingEvents.STATISTICS_INFO, onStatisticsInfo.bind(this));
controller.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, onRecommendSeekpoint.bind(this));
break;
case 'destroy':
if (controller) {
controller.destroy();
controller = null;
}
self.postMessage({msg: 'destroyed'});
break;
case 'start':
controller.start();
break;
case 'stop':
controller.stop();
break;
case 'seek':
controller.seek(e.data.param);
break;
case 'pause':
controller.pause();
break;
case 'resume':
controller.resume();
break;
case 'logging_config': {
let config = e.data.param;
LoggingControl.applyConfig(config);
if (config.enableCallback === true) {
LoggingControl.addLogListener(logcatListener);
} else {
LoggingControl.removeLogListener(logcatListener);
}
break;
}
}
});
function onInitSegment(type, initSegment) {
let obj = {
msg: TransmuxingEvents.INIT_SEGMENT,
data: {
type: type,
data: initSegment
}
};
self.postMessage(obj, [initSegment.data]); // data: ArrayBuffer
}
function onMediaSegment(type, mediaSegment) {
let obj = {
msg: TransmuxingEvents.MEDIA_SEGMENT,
data: {
type: type,
data: mediaSegment
}
};
self.postMessage(obj, [mediaSegment.data]); // data: ArrayBuffer
}
function onLoadingComplete() {
let obj = {
msg: TransmuxingEvents.LOADING_COMPLETE
};
self.postMessage(obj);
}
function onRecoveredEarlyEof() {
let obj = {
msg: TransmuxingEvents.RECOVERED_EARLY_EOF
};
self.postMessage(obj);
}
function onMediaInfo(mediaInfo) {
let obj = {
msg: TransmuxingEvents.MEDIA_INFO,
data: mediaInfo
};
self.postMessage(obj);
}
function onMetaDataArrived(metadata) {
let obj = {
msg: TransmuxingEvents.METADATA_ARRIVED,
data: metadata
};
self.postMessage(obj);
}
function onScriptDataArrived(data) {
let obj = {
msg: TransmuxingEvents.SCRIPTDATA_ARRIVED,
data: data
};
self.postMessage(obj);
}
function onStatisticsInfo(statInfo) {
let obj = {
msg: TransmuxingEvents.STATISTICS_INFO,
data: statInfo
};
self.postMessage(obj);
}
function onIOError(type, info) {
self.postMessage({
msg: TransmuxingEvents.IO_ERROR,
data: {
type: type,
info: info
}
});
}
function onDemuxError(type, info) {
self.postMessage({
msg: TransmuxingEvents.DEMUX_ERROR,
data: {
type: type,
info: info
}
});
}
function onRecommendSeekpoint(milliseconds) {
self.postMessage({
msg: TransmuxingEvents.RECOMMEND_SEEKPOINT,
data: milliseconds
});
}
function onLogcatCallback(type, str) {
self.postMessage({
msg: 'logcat_callback',
data: {
type: type,
logcat: str
}
});
}
};
export default TransmuxingWorker;

243
node_modules/flv.js/src/demux/amf-parser.js generated vendored Normal file
View File

@ -0,0 +1,243 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Log from '../utils/logger.js';
import decodeUTF8 from '../utils/utf8-conv.js';
import {IllegalStateException} from '../utils/exception.js';
let le = (function () {
let buf = new ArrayBuffer(2);
(new DataView(buf)).setInt16(0, 256, true); // little-endian write
return (new Int16Array(buf))[0] === 256; // platform-spec read, if equal then LE
})();
class AMF {
static parseScriptData(arrayBuffer, dataOffset, dataSize) {
let data = {};
try {
let name = AMF.parseValue(arrayBuffer, dataOffset, dataSize);
let value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size);
data[name.data] = value.data;
} catch (e) {
Log.e('AMF', e.toString());
}
return data;
}
static parseObject(arrayBuffer, dataOffset, dataSize) {
if (dataSize < 3) {
throw new IllegalStateException('Data not enough when parse ScriptDataObject');
}
let name = AMF.parseString(arrayBuffer, dataOffset, dataSize);
let value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size);
let isObjectEnd = value.objectEnd;
return {
data: {
name: name.data,
value: value.data
},
size: name.size + value.size,
objectEnd: isObjectEnd
};
}
static parseVariable(arrayBuffer, dataOffset, dataSize) {
return AMF.parseObject(arrayBuffer, dataOffset, dataSize);
}
static parseString(arrayBuffer, dataOffset, dataSize) {
if (dataSize < 2) {
throw new IllegalStateException('Data not enough when parse String');
}
let v = new DataView(arrayBuffer, dataOffset, dataSize);
let length = v.getUint16(0, !le);
let str;
if (length > 0) {
str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 2, length));
} else {
str = '';
}
return {
data: str,
size: 2 + length
};
}
static parseLongString(arrayBuffer, dataOffset, dataSize) {
if (dataSize < 4) {
throw new IllegalStateException('Data not enough when parse LongString');
}
let v = new DataView(arrayBuffer, dataOffset, dataSize);
let length = v.getUint32(0, !le);
let str;
if (length > 0) {
str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 4, length));
} else {
str = '';
}
return {
data: str,
size: 4 + length
};
}
static parseDate(arrayBuffer, dataOffset, dataSize) {
if (dataSize < 10) {
throw new IllegalStateException('Data size invalid when parse Date');
}
let v = new DataView(arrayBuffer, dataOffset, dataSize);
let timestamp = v.getFloat64(0, !le);
let localTimeOffset = v.getInt16(8, !le);
timestamp += localTimeOffset * 60 * 1000; // get UTC time
return {
data: new Date(timestamp),
size: 8 + 2
};
}
static parseValue(arrayBuffer, dataOffset, dataSize) {
if (dataSize < 1) {
throw new IllegalStateException('Data not enough when parse Value');
}
let v = new DataView(arrayBuffer, dataOffset, dataSize);
let offset = 1;
let type = v.getUint8(0);
let value;
let objectEnd = false;
try {
switch (type) {
case 0: // Number(Double) type
value = v.getFloat64(1, !le);
offset += 8;
break;
case 1: { // Boolean type
let b = v.getUint8(1);
value = b ? true : false;
offset += 1;
break;
}
case 2: { // String type
let amfstr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1);
value = amfstr.data;
offset += amfstr.size;
break;
}
case 3: { // Object(s) type
value = {};
let terminal = 0; // workaround for malformed Objects which has missing ScriptDataObjectEnd
if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) {
terminal = 3;
}
while (offset < dataSize - 4) { // 4 === type(UI8) + ScriptDataObjectEnd(UI24)
let amfobj = AMF.parseObject(arrayBuffer, dataOffset + offset, dataSize - offset - terminal);
if (amfobj.objectEnd)
break;
value[amfobj.data.name] = amfobj.data.value;
offset += amfobj.size;
}
if (offset <= dataSize - 3) {
let marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF;
if (marker === 9) {
offset += 3;
}
}
break;
}
case 8: { // ECMA array type (Mixed array)
value = {};
offset += 4; // ECMAArrayLength(UI32)
let terminal = 0; // workaround for malformed MixedArrays which has missing ScriptDataObjectEnd
if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) {
terminal = 3;
}
while (offset < dataSize - 8) { // 8 === type(UI8) + ECMAArrayLength(UI32) + ScriptDataVariableEnd(UI24)
let amfvar = AMF.parseVariable(arrayBuffer, dataOffset + offset, dataSize - offset - terminal);
if (amfvar.objectEnd)
break;
value[amfvar.data.name] = amfvar.data.value;
offset += amfvar.size;
}
if (offset <= dataSize - 3) {
let marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF;
if (marker === 9) {
offset += 3;
}
}
break;
}
case 9: // ScriptDataObjectEnd
value = undefined;
offset = 1;
objectEnd = true;
break;
case 10: { // Strict array type
// ScriptDataValue[n]. NOTE: according to video_file_format_spec_v10_1.pdf
value = [];
let strictArrayLength = v.getUint32(1, !le);
offset += 4;
for (let i = 0; i < strictArrayLength; i++) {
let val = AMF.parseValue(arrayBuffer, dataOffset + offset, dataSize - offset);
value.push(val.data);
offset += val.size;
}
break;
}
case 11: { // Date type
let date = AMF.parseDate(arrayBuffer, dataOffset + 1, dataSize - 1);
value = date.data;
offset += date.size;
break;
}
case 12: { // Long string type
let amfLongStr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1);
value = amfLongStr.data;
offset += amfLongStr.size;
break;
}
default:
// ignore and skip
offset = dataSize;
Log.w('AMF', 'Unsupported AMF value type ' + type);
}
} catch (e) {
Log.e('AMF', e.toString());
}
return {
data: value,
size: offset,
objectEnd: objectEnd
};
}
}
export default AMF;

26
node_modules/flv.js/src/demux/demux-errors.js generated vendored Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const DemuxErrors = {
OK: 'OK',
FORMAT_ERROR: 'FormatError',
FORMAT_UNSUPPORTED: 'FormatUnsupported',
CODEC_UNSUPPORTED: 'CodecUnsupported'
};
export default DemuxErrors;

116
node_modules/flv.js/src/demux/exp-golomb.js generated vendored Normal file
View File

@ -0,0 +1,116 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {IllegalStateException, InvalidArgumentException} from '../utils/exception.js';
// Exponential-Golomb buffer decoder
class ExpGolomb {
constructor(uint8array) {
this.TAG = 'ExpGolomb';
this._buffer = uint8array;
this._buffer_index = 0;
this._total_bytes = uint8array.byteLength;
this._total_bits = uint8array.byteLength * 8;
this._current_word = 0;
this._current_word_bits_left = 0;
}
destroy() {
this._buffer = null;
}
_fillCurrentWord() {
let buffer_bytes_left = this._total_bytes - this._buffer_index;
if (buffer_bytes_left <= 0)
throw new IllegalStateException('ExpGolomb: _fillCurrentWord() but no bytes available');
let bytes_read = Math.min(4, buffer_bytes_left);
let word = new Uint8Array(4);
word.set(this._buffer.subarray(this._buffer_index, this._buffer_index + bytes_read));
this._current_word = new DataView(word.buffer).getUint32(0, false);
this._buffer_index += bytes_read;
this._current_word_bits_left = bytes_read * 8;
}
readBits(bits) {
if (bits > 32)
throw new InvalidArgumentException('ExpGolomb: readBits() bits exceeded max 32bits!');
if (bits <= this._current_word_bits_left) {
let result = this._current_word >>> (32 - bits);
this._current_word <<= bits;
this._current_word_bits_left -= bits;
return result;
}
let result = this._current_word_bits_left ? this._current_word : 0;
result = result >>> (32 - this._current_word_bits_left);
let bits_need_left = bits - this._current_word_bits_left;
this._fillCurrentWord();
let bits_read_next = Math.min(bits_need_left, this._current_word_bits_left);
let result2 = this._current_word >>> (32 - bits_read_next);
this._current_word <<= bits_read_next;
this._current_word_bits_left -= bits_read_next;
result = (result << bits_read_next) | result2;
return result;
}
readBool() {
return this.readBits(1) === 1;
}
readByte() {
return this.readBits(8);
}
_skipLeadingZero() {
let zero_count;
for (zero_count = 0; zero_count < this._current_word_bits_left; zero_count++) {
if (0 !== (this._current_word & (0x80000000 >>> zero_count))) {
this._current_word <<= zero_count;
this._current_word_bits_left -= zero_count;
return zero_count;
}
}
this._fillCurrentWord();
return zero_count + this._skipLeadingZero();
}
readUEG() { // unsigned exponential golomb
let leading_zeros = this._skipLeadingZero();
return this.readBits(leading_zeros + 1) - 1;
}
readSEG() { // signed exponential golomb
let value = this.readUEG();
if (value & 0x01) {
return (value + 1) >>> 1;
} else {
return -1 * (value >>> 1);
}
}
}
export default ExpGolomb;

1101
node_modules/flv.js/src/demux/flv-demuxer.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

281
node_modules/flv.js/src/demux/sps-parser.js generated vendored Normal file
View File

@ -0,0 +1,281 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ExpGolomb from './exp-golomb.js';
class SPSParser {
static _ebsp2rbsp(uint8array) {
let src = uint8array;
let src_length = src.byteLength;
let dst = new Uint8Array(src_length);
let dst_idx = 0;
for (let i = 0; i < src_length; i++) {
if (i >= 2) {
// Unescape: Skip 0x03 after 00 00
if (src[i] === 0x03 && src[i - 1] === 0x00 && src[i - 2] === 0x00) {
continue;
}
}
dst[dst_idx] = src[i];
dst_idx++;
}
return new Uint8Array(dst.buffer, 0, dst_idx);
}
static parseSPS(uint8array) {
let rbsp = SPSParser._ebsp2rbsp(uint8array);
let gb = new ExpGolomb(rbsp);
gb.readByte();
let profile_idc = gb.readByte(); // profile_idc
gb.readByte(); // constraint_set_flags[5] + reserved_zero[3]
let level_idc = gb.readByte(); // level_idc
gb.readUEG(); // seq_parameter_set_id
let profile_string = SPSParser.getProfileString(profile_idc);
let level_string = SPSParser.getLevelString(level_idc);
let chroma_format_idc = 1;
let chroma_format = 420;
let chroma_format_table = [0, 420, 422, 444];
let bit_depth = 8;
if (profile_idc === 100 || profile_idc === 110 || profile_idc === 122 ||
profile_idc === 244 || profile_idc === 44 || profile_idc === 83 ||
profile_idc === 86 || profile_idc === 118 || profile_idc === 128 ||
profile_idc === 138 || profile_idc === 144) {
chroma_format_idc = gb.readUEG();
if (chroma_format_idc === 3) {
gb.readBits(1); // separate_colour_plane_flag
}
if (chroma_format_idc <= 3) {
chroma_format = chroma_format_table[chroma_format_idc];
}
bit_depth = gb.readUEG() + 8; // bit_depth_luma_minus8
gb.readUEG(); // bit_depth_chroma_minus8
gb.readBits(1); // qpprime_y_zero_transform_bypass_flag
if (gb.readBool()) { // seq_scaling_matrix_present_flag
let scaling_list_count = (chroma_format_idc !== 3) ? 8 : 12;
for (let i = 0; i < scaling_list_count; i++) {
if (gb.readBool()) { // seq_scaling_list_present_flag
if (i < 6) {
SPSParser._skipScalingList(gb, 16);
} else {
SPSParser._skipScalingList(gb, 64);
}
}
}
}
}
gb.readUEG(); // log2_max_frame_num_minus4
let pic_order_cnt_type = gb.readUEG();
if (pic_order_cnt_type === 0) {
gb.readUEG(); // log2_max_pic_order_cnt_lsb_minus_4
} else if (pic_order_cnt_type === 1) {
gb.readBits(1); // delta_pic_order_always_zero_flag
gb.readSEG(); // offset_for_non_ref_pic
gb.readSEG(); // offset_for_top_to_bottom_field
let num_ref_frames_in_pic_order_cnt_cycle = gb.readUEG();
for (let i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
gb.readSEG(); // offset_for_ref_frame
}
}
let ref_frames = gb.readUEG(); // max_num_ref_frames
gb.readBits(1); // gaps_in_frame_num_value_allowed_flag
let pic_width_in_mbs_minus1 = gb.readUEG();
let pic_height_in_map_units_minus1 = gb.readUEG();
let frame_mbs_only_flag = gb.readBits(1);
if (frame_mbs_only_flag === 0) {
gb.readBits(1); // mb_adaptive_frame_field_flag
}
gb.readBits(1); // direct_8x8_inference_flag
let frame_crop_left_offset = 0;
let frame_crop_right_offset = 0;
let frame_crop_top_offset = 0;
let frame_crop_bottom_offset = 0;
let frame_cropping_flag = gb.readBool();
if (frame_cropping_flag) {
frame_crop_left_offset = gb.readUEG();
frame_crop_right_offset = gb.readUEG();
frame_crop_top_offset = gb.readUEG();
frame_crop_bottom_offset = gb.readUEG();
}
let sar_width = 1, sar_height = 1;
let fps = 0, fps_fixed = true, fps_num = 0, fps_den = 0;
let vui_parameters_present_flag = gb.readBool();
if (vui_parameters_present_flag) {
if (gb.readBool()) { // aspect_ratio_info_present_flag
let aspect_ratio_idc = gb.readByte();
let sar_w_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2];
let sar_h_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1];
if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) {
sar_width = sar_w_table[aspect_ratio_idc - 1];
sar_height = sar_h_table[aspect_ratio_idc - 1];
} else if (aspect_ratio_idc === 255) {
sar_width = gb.readByte() << 8 | gb.readByte();
sar_height = gb.readByte() << 8 | gb.readByte();
}
}
if (gb.readBool()) { // overscan_info_present_flag
gb.readBool(); // overscan_appropriate_flag
}
if (gb.readBool()) { // video_signal_type_present_flag
gb.readBits(4); // video_format & video_full_range_flag
if (gb.readBool()) { // colour_description_present_flag
gb.readBits(24); // colour_primaries & transfer_characteristics & matrix_coefficients
}
}
if (gb.readBool()) { // chroma_loc_info_present_flag
gb.readUEG(); // chroma_sample_loc_type_top_field
gb.readUEG(); // chroma_sample_loc_type_bottom_field
}
if (gb.readBool()) { // timing_info_present_flag
let num_units_in_tick = gb.readBits(32);
let time_scale = gb.readBits(32);
fps_fixed = gb.readBool(); // fixed_frame_rate_flag
fps_num = time_scale;
fps_den = num_units_in_tick * 2;
fps = fps_num / fps_den;
}
}
let sarScale = 1;
if (sar_width !== 1 || sar_height !== 1) {
sarScale = sar_width / sar_height;
}
let crop_unit_x = 0, crop_unit_y = 0;
if (chroma_format_idc === 0) {
crop_unit_x = 1;
crop_unit_y = 2 - frame_mbs_only_flag;
} else {
let sub_wc = (chroma_format_idc === 3) ? 1 : 2;
let sub_hc = (chroma_format_idc === 1) ? 2 : 1;
crop_unit_x = sub_wc;
crop_unit_y = sub_hc * (2 - frame_mbs_only_flag);
}
let codec_width = (pic_width_in_mbs_minus1 + 1) * 16;
let codec_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16);
codec_width -= (frame_crop_left_offset + frame_crop_right_offset) * crop_unit_x;
codec_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * crop_unit_y;
let present_width = Math.ceil(codec_width * sarScale);
gb.destroy();
gb = null;
return {
profile_string: profile_string, // baseline, high, high10, ...
level_string: level_string, // 3, 3.1, 4, 4.1, 5, 5.1, ...
bit_depth: bit_depth, // 8bit, 10bit, ...
ref_frames: ref_frames,
chroma_format: chroma_format, // 4:2:0, 4:2:2, ...
chroma_format_string: SPSParser.getChromaFormatString(chroma_format),
frame_rate: {
fixed: fps_fixed,
fps: fps,
fps_den: fps_den,
fps_num: fps_num
},
sar_ratio: {
width: sar_width,
height: sar_height
},
codec_size: {
width: codec_width,
height: codec_height
},
present_size: {
width: present_width,
height: codec_height
}
};
}
static _skipScalingList(gb, count) {
let last_scale = 8, next_scale = 8;
let delta_scale = 0;
for (let i = 0; i < count; i++) {
if (next_scale !== 0) {
delta_scale = gb.readSEG();
next_scale = (last_scale + delta_scale + 256) % 256;
}
last_scale = (next_scale === 0) ? last_scale : next_scale;
}
}
static getProfileString(profile_idc) {
switch (profile_idc) {
case 66:
return 'Baseline';
case 77:
return 'Main';
case 88:
return 'Extended';
case 100:
return 'High';
case 110:
return 'High10';
case 122:
return 'High422';
case 244:
return 'High444';
default:
return 'Unknown';
}
}
static getLevelString(level_idc) {
return (level_idc / 10).toFixed(1);
}
static getChromaFormatString(chroma) {
switch (chroma) {
case 420:
return '4:2:0';
case 422:
return '4:2:2';
case 444:
return '4:4:4';
default:
return 'Unknown';
}
}
}
export default SPSParser;

92
node_modules/flv.js/src/flv.js generated vendored Normal file
View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Polyfill from './utils/polyfill.js';
import Features from './core/features.js';
import {BaseLoader, LoaderStatus, LoaderErrors} from './io/loader.js';
import FlvPlayer from './player/flv-player.js';
import NativePlayer from './player/native-player.js';
import PlayerEvents from './player/player-events.js';
import {ErrorTypes, ErrorDetails} from './player/player-errors.js';
import LoggingControl from './utils/logging-control.js';
import {InvalidArgumentException} from './utils/exception.js';
// here are all the interfaces
// install polyfills
Polyfill.install();
// factory method
function createPlayer(mediaDataSource, optionalConfig) {
let mds = mediaDataSource;
if (mds == null || typeof mds !== 'object') {
throw new InvalidArgumentException('MediaDataSource must be an javascript object!');
}
if (!mds.hasOwnProperty('type')) {
throw new InvalidArgumentException('MediaDataSource must has type field to indicate video file type!');
}
switch (mds.type) {
case 'flv':
return new FlvPlayer(mds, optionalConfig);
default:
return new NativePlayer(mds, optionalConfig);
}
}
// feature detection
function isSupported() {
return Features.supportMSEH264Playback();
}
function getFeatureList() {
return Features.getFeatureList();
}
// interfaces
let flvjs = {};
flvjs.createPlayer = createPlayer;
flvjs.isSupported = isSupported;
flvjs.getFeatureList = getFeatureList;
flvjs.BaseLoader = BaseLoader;
flvjs.LoaderStatus = LoaderStatus;
flvjs.LoaderErrors = LoaderErrors;
flvjs.Events = PlayerEvents;
flvjs.ErrorTypes = ErrorTypes;
flvjs.ErrorDetails = ErrorDetails;
flvjs.FlvPlayer = FlvPlayer;
flvjs.NativePlayer = NativePlayer;
flvjs.LoggingControl = LoggingControl;
Object.defineProperty(flvjs, 'version', {
enumerable: true,
get: function () {
// replace by webpack.DefinePlugin
return __VERSION__;
}
});
export default flvjs;

4
node_modules/flv.js/src/index.js generated vendored Normal file
View File

@ -0,0 +1,4 @@
// entry/index file
// make it compatible with browserify's umd wrapper
module.exports = require('./flv.js').default;

266
node_modules/flv.js/src/io/fetch-stream-loader.js generated vendored Normal file
View File

@ -0,0 +1,266 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Log from '../utils/logger.js';
import Browser from '../utils/browser.js';
import {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';
import {RuntimeException} from '../utils/exception.js';
/* fetch + stream IO loader. Currently working on chrome 43+.
* fetch provides a better alternative http API to XMLHttpRequest
*
* fetch spec https://fetch.spec.whatwg.org/
* stream spec https://streams.spec.whatwg.org/
*/
class FetchStreamLoader extends BaseLoader {
static isSupported() {
try {
// fetch + stream is broken on Microsoft Edge. Disable before build 15048.
// see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8196907/
// Fixed in Jan 10, 2017. Build 15048+ removed from blacklist.
let isWorkWellEdge = Browser.msedge && Browser.version.minor >= 15048;
let browserNotBlacklisted = Browser.msedge ? isWorkWellEdge : true;
return (self.fetch && self.ReadableStream && browserNotBlacklisted);
} catch (e) {
return false;
}
}
constructor(seekHandler, config) {
super('fetch-stream-loader');
this.TAG = 'FetchStreamLoader';
this._seekHandler = seekHandler;
this._config = config;
this._needStash = true;
this._requestAbort = false;
this._contentLength = null;
this._receivedLength = 0;
}
destroy() {
if (this.isWorking()) {
this.abort();
}
super.destroy();
}
open(dataSource, range) {
this._dataSource = dataSource;
this._range = range;
let sourceURL = dataSource.url;
if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) {
sourceURL = dataSource.redirectedURL;
}
let seekConfig = this._seekHandler.getConfig(sourceURL, range);
let headers = new self.Headers();
if (typeof seekConfig.headers === 'object') {
let configHeaders = seekConfig.headers;
for (let key in configHeaders) {
if (configHeaders.hasOwnProperty(key)) {
headers.append(key, configHeaders[key]);
}
}
}
let params = {
method: 'GET',
headers: headers,
mode: 'cors',
cache: 'default',
// The default policy of Fetch API in the whatwg standard
// Safari incorrectly indicates 'no-referrer' as default policy, fuck it
referrerPolicy: 'no-referrer-when-downgrade'
};
// add additional headers
if (typeof this._config.headers === 'object') {
for (let key in this._config.headers) {
headers.append(key, this._config.headers[key]);
}
}
// cors is enabled by default
if (dataSource.cors === false) {
// no-cors means 'disregard cors policy', which can only be used in ServiceWorker
params.mode = 'same-origin';
}
// withCredentials is disabled by default
if (dataSource.withCredentials) {
params.credentials = 'include';
}
// referrerPolicy from config
if (dataSource.referrerPolicy) {
params.referrerPolicy = dataSource.referrerPolicy;
}
// add abort controller, by wmlgl 2019-5-10 12:21:27
if (self.AbortController) {
this._abortController = new self.AbortController();
params.signal = this._abortController.signal;
}
this._status = LoaderStatus.kConnecting;
self.fetch(seekConfig.url, params).then((res) => {
if (this._requestAbort) {
this._status = LoaderStatus.kIdle;
res.body.cancel();
return;
}
if (res.ok && (res.status >= 200 && res.status <= 299)) {
if (res.url !== seekConfig.url) {
if (this._onURLRedirect) {
let redirectedURL = this._seekHandler.removeURLParameters(res.url);
this._onURLRedirect(redirectedURL);
}
}
let lengthHeader = res.headers.get('Content-Length');
if (lengthHeader != null) {
this._contentLength = parseInt(lengthHeader);
if (this._contentLength !== 0) {
if (this._onContentLengthKnown) {
this._onContentLengthKnown(this._contentLength);
}
}
}
return this._pump.call(this, res.body.getReader());
} else {
this._status = LoaderStatus.kError;
if (this._onError) {
this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, {code: res.status, msg: res.statusText});
} else {
throw new RuntimeException('FetchStreamLoader: Http code invalid, ' + res.status + ' ' + res.statusText);
}
}
}).catch((e) => {
if (this._abortController && this._abortController.signal.aborted) {
return;
}
this._status = LoaderStatus.kError;
if (this._onError) {
this._onError(LoaderErrors.EXCEPTION, {code: -1, msg: e.message});
} else {
throw e;
}
});
}
abort() {
this._requestAbort = true;
if (this._status !== LoaderStatus.kBuffering || !Browser.chrome) {
// Chrome may throw Exception-like things here, avoid using if is buffering
if (this._abortController) {
try {
this._abortController.abort();
} catch (e) {}
}
}
}
_pump(reader) { // ReadableStreamReader
return reader.read().then((result) => {
if (result.done) {
// First check received length
if (this._contentLength !== null && this._receivedLength < this._contentLength) {
// Report Early-EOF
this._status = LoaderStatus.kError;
let type = LoaderErrors.EARLY_EOF;
let info = {code: -1, msg: 'Fetch stream meet Early-EOF'};
if (this._onError) {
this._onError(type, info);
} else {
throw new RuntimeException(info.msg);
}
} else {
// OK. Download complete
this._status = LoaderStatus.kComplete;
if (this._onComplete) {
this._onComplete(this._range.from, this._range.from + this._receivedLength - 1);
}
}
} else {
if (this._abortController && this._abortController.signal.aborted) {
this._status = LoaderStatus.kComplete;
return;
} else if (this._requestAbort === true) {
this._status = LoaderStatus.kComplete;
return reader.cancel();
}
this._status = LoaderStatus.kBuffering;
let chunk = result.value.buffer;
let byteStart = this._range.from + this._receivedLength;
this._receivedLength += chunk.byteLength;
if (this._onDataArrival) {
this._onDataArrival(chunk, byteStart, this._receivedLength);
}
this._pump(reader);
}
}).catch((e) => {
if (this._abortController && this._abortController.signal.aborted) {
this._status = LoaderStatus.kComplete;
return;
}
if (e.code === 11 && Browser.msedge) { // InvalidStateError on Microsoft Edge
// Workaround: Edge may throw InvalidStateError after ReadableStreamReader.cancel() call
// Ignore the unknown exception.
// Related issue: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11265202/
return;
}
this._status = LoaderStatus.kError;
let type = 0;
let info = null;
if ((e.code === 19 || e.message === 'network error') && // NETWORK_ERR
(this._contentLength === null ||
(this._contentLength !== null && this._receivedLength < this._contentLength))) {
type = LoaderErrors.EARLY_EOF;
info = {code: e.code, msg: 'Fetch stream meet Early-EOF'};
} else {
type = LoaderErrors.EXCEPTION;
info = {code: e.code, msg: e.message};
}
if (this._onError) {
this._onError(type, info);
} else {
throw new RuntimeException(info.msg);
}
});
}
}
export default FetchStreamLoader;

647
node_modules/flv.js/src/io/io-controller.js generated vendored Normal file
View File

@ -0,0 +1,647 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Log from '../utils/logger.js';
import SpeedSampler from './speed-sampler.js';
import {LoaderStatus, LoaderErrors} from './loader.js';
import FetchStreamLoader from './fetch-stream-loader.js';
import MozChunkedLoader from './xhr-moz-chunked-loader.js';
import MSStreamLoader from './xhr-msstream-loader.js';
import RangeLoader from './xhr-range-loader.js';
import WebSocketLoader from './websocket-loader.js';
import RangeSeekHandler from './range-seek-handler.js';
import ParamSeekHandler from './param-seek-handler.js';
import {RuntimeException, IllegalStateException, InvalidArgumentException} from '../utils/exception.js';
/**
* DataSource: {
* url: string,
* filesize: number,
* cors: boolean,
* withCredentials: boolean
* }
*
*/
// Manage IO Loaders
class IOController {
constructor(dataSource, config, extraData) {
this.TAG = 'IOController';
this._config = config;
this._extraData = extraData;
this._stashInitialSize = 1024 * 384; // default initial size: 384KB
if (config.stashInitialSize != undefined && config.stashInitialSize > 0) {
// apply from config
this._stashInitialSize = config.stashInitialSize;
}
this._stashUsed = 0;
this._stashSize = this._stashInitialSize;
this._bufferSize = 1024 * 1024 * 3; // initial size: 3MB
this._stashBuffer = new ArrayBuffer(this._bufferSize);
this._stashByteStart = 0;
this._enableStash = true;
if (config.enableStashBuffer === false) {
this._enableStash = false;
}
this._loader = null;
this._loaderClass = null;
this._seekHandler = null;
this._dataSource = dataSource;
this._isWebSocketURL = /wss?:\/\/(.+?)/.test(dataSource.url);
this._refTotalLength = dataSource.filesize ? dataSource.filesize : null;
this._totalLength = this._refTotalLength;
this._fullRequestFlag = false;
this._currentRange = null;
this._redirectedURL = null;
this._speedNormalized = 0;
this._speedSampler = new SpeedSampler();
this._speedNormalizeList = [64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096];
this._isEarlyEofReconnecting = false;
this._paused = false;
this._resumeFrom = 0;
this._onDataArrival = null;
this._onSeeked = null;
this._onError = null;
this._onComplete = null;
this._onRedirect = null;
this._onRecoveredEarlyEof = null;
this._selectSeekHandler();
this._selectLoader();
this._createLoader();
}
destroy() {
if (this._loader.isWorking()) {
this._loader.abort();
}
this._loader.destroy();
this._loader = null;
this._loaderClass = null;
this._dataSource = null;
this._stashBuffer = null;
this._stashUsed = this._stashSize = this._bufferSize = this._stashByteStart = 0;
this._currentRange = null;
this._speedSampler = null;
this._isEarlyEofReconnecting = false;
this._onDataArrival = null;
this._onSeeked = null;
this._onError = null;
this._onComplete = null;
this._onRedirect = null;
this._onRecoveredEarlyEof = null;
this._extraData = null;
}
isWorking() {
return this._loader && this._loader.isWorking() && !this._paused;
}
isPaused() {
return this._paused;
}
get status() {
return this._loader.status;
}
get extraData() {
return this._extraData;
}
set extraData(data) {
this._extraData = data;
}
// prototype: function onDataArrival(chunks: ArrayBuffer, byteStart: number): number
get onDataArrival() {
return this._onDataArrival;
}
set onDataArrival(callback) {
this._onDataArrival = callback;
}
get onSeeked() {
return this._onSeeked;
}
set onSeeked(callback) {
this._onSeeked = callback;
}
// prototype: function onError(type: number, info: {code: number, msg: string}): void
get onError() {
return this._onError;
}
set onError(callback) {
this._onError = callback;
}
get onComplete() {
return this._onComplete;
}
set onComplete(callback) {
this._onComplete = callback;
}
get onRedirect() {
return this._onRedirect;
}
set onRedirect(callback) {
this._onRedirect = callback;
}
get onRecoveredEarlyEof() {
return this._onRecoveredEarlyEof;
}
set onRecoveredEarlyEof(callback) {
this._onRecoveredEarlyEof = callback;
}
get currentURL() {
return this._dataSource.url;
}
get hasRedirect() {
return (this._redirectedURL != null || this._dataSource.redirectedURL != undefined);
}
get currentRedirectedURL() {
return this._redirectedURL || this._dataSource.redirectedURL;
}
// in KB/s
get currentSpeed() {
if (this._loaderClass === RangeLoader) {
// SpeedSampler is inaccuracy if loader is RangeLoader
return this._loader.currentSpeed;
}
return this._speedSampler.lastSecondKBps;
}
get loaderType() {
return this._loader.type;
}
_selectSeekHandler() {
let config = this._config;
if (config.seekType === 'range') {
this._seekHandler = new RangeSeekHandler(this._config.rangeLoadZeroStart);
} else if (config.seekType === 'param') {
let paramStart = config.seekParamStart || 'bstart';
let paramEnd = config.seekParamEnd || 'bend';
this._seekHandler = new ParamSeekHandler(paramStart, paramEnd);
} else if (config.seekType === 'custom') {
if (typeof config.customSeekHandler !== 'function') {
throw new InvalidArgumentException('Custom seekType specified in config but invalid customSeekHandler!');
}
this._seekHandler = new config.customSeekHandler();
} else {
throw new InvalidArgumentException(`Invalid seekType in config: ${config.seekType}`);
}
}
_selectLoader() {
if (this._config.customLoader != null) {
this._loaderClass = this._config.customLoader;
} else if (this._isWebSocketURL) {
this._loaderClass = WebSocketLoader;
} else if (FetchStreamLoader.isSupported()) {
this._loaderClass = FetchStreamLoader;
} else if (MozChunkedLoader.isSupported()) {
this._loaderClass = MozChunkedLoader;
} else if (RangeLoader.isSupported()) {
this._loaderClass = RangeLoader;
} else {
throw new RuntimeException('Your browser doesn\'t support xhr with arraybuffer responseType!');
}
}
_createLoader() {
this._loader = new this._loaderClass(this._seekHandler, this._config);
if (this._loader.needStashBuffer === false) {
this._enableStash = false;
}
this._loader.onContentLengthKnown = this._onContentLengthKnown.bind(this);
this._loader.onURLRedirect = this._onURLRedirect.bind(this);
this._loader.onDataArrival = this._onLoaderChunkArrival.bind(this);
this._loader.onComplete = this._onLoaderComplete.bind(this);
this._loader.onError = this._onLoaderError.bind(this);
}
open(optionalFrom) {
this._currentRange = {from: 0, to: -1};
if (optionalFrom) {
this._currentRange.from = optionalFrom;
}
this._speedSampler.reset();
if (!optionalFrom) {
this._fullRequestFlag = true;
}
this._loader.open(this._dataSource, Object.assign({}, this._currentRange));
}
abort() {
this._loader.abort();
if (this._paused) {
this._paused = false;
this._resumeFrom = 0;
}
}
pause() {
if (this.isWorking()) {
this._loader.abort();
if (this._stashUsed !== 0) {
this._resumeFrom = this._stashByteStart;
this._currentRange.to = this._stashByteStart - 1;
} else {
this._resumeFrom = this._currentRange.to + 1;
}
this._stashUsed = 0;
this._stashByteStart = 0;
this._paused = true;
}
}
resume() {
if (this._paused) {
this._paused = false;
let bytes = this._resumeFrom;
this._resumeFrom = 0;
this._internalSeek(bytes, true);
}
}
seek(bytes) {
this._paused = false;
this._stashUsed = 0;
this._stashByteStart = 0;
this._internalSeek(bytes, true);
}
/**
* When seeking request is from media seeking, unconsumed stash data should be dropped
* However, stash data shouldn't be dropped if seeking requested from http reconnection
*
* @dropUnconsumed: Ignore and discard all unconsumed data in stash buffer
*/
_internalSeek(bytes, dropUnconsumed) {
if (this._loader.isWorking()) {
this._loader.abort();
}
// dispatch & flush stash buffer before seek
this._flushStashBuffer(dropUnconsumed);
this._loader.destroy();
this._loader = null;
let requestRange = {from: bytes, to: -1};
this._currentRange = {from: requestRange.from, to: -1};
this._speedSampler.reset();
this._stashSize = this._stashInitialSize;
this._createLoader();
this._loader.open(this._dataSource, requestRange);
if (this._onSeeked) {
this._onSeeked();
}
}
updateUrl(url) {
if (!url || typeof url !== 'string' || url.length === 0) {
throw new InvalidArgumentException('Url must be a non-empty string!');
}
this._dataSource.url = url;
// TODO: replace with new url
}
_expandBuffer(expectedBytes) {
let bufferNewSize = this._stashSize;
while (bufferNewSize + 1024 * 1024 * 1 < expectedBytes) {
bufferNewSize *= 2;
}
bufferNewSize += 1024 * 1024 * 1; // bufferSize = stashSize + 1MB
if (bufferNewSize === this._bufferSize) {
return;
}
let newBuffer = new ArrayBuffer(bufferNewSize);
if (this._stashUsed > 0) { // copy existing data into new buffer
let stashOldArray = new Uint8Array(this._stashBuffer, 0, this._stashUsed);
let stashNewArray = new Uint8Array(newBuffer, 0, bufferNewSize);
stashNewArray.set(stashOldArray, 0);
}
this._stashBuffer = newBuffer;
this._bufferSize = bufferNewSize;
}
_normalizeSpeed(input) {
let list = this._speedNormalizeList;
let last = list.length - 1;
let mid = 0;
let lbound = 0;
let ubound = last;
if (input < list[0]) {
return list[0];
}
// binary search
while (lbound <= ubound) {
mid = lbound + Math.floor((ubound - lbound) / 2);
if (mid === last || (input >= list[mid] && input < list[mid + 1])) {
return list[mid];
} else if (list[mid] < input) {
lbound = mid + 1;
} else {
ubound = mid - 1;
}
}
}
_adjustStashSize(normalized) {
let stashSizeKB = 0;
if (this._config.isLive) {
// live stream: always use single normalized speed for size of stashSizeKB
stashSizeKB = normalized;
} else {
if (normalized < 512) {
stashSizeKB = normalized;
} else if (normalized >= 512 && normalized <= 1024) {
stashSizeKB = Math.floor(normalized * 1.5);
} else {
stashSizeKB = normalized * 2;
}
}
if (stashSizeKB > 8192) {
stashSizeKB = 8192;
}
let bufferSize = stashSizeKB * 1024 + 1024 * 1024 * 1; // stashSize + 1MB
if (this._bufferSize < bufferSize) {
this._expandBuffer(bufferSize);
}
this._stashSize = stashSizeKB * 1024;
}
_dispatchChunks(chunks, byteStart) {
this._currentRange.to = byteStart + chunks.byteLength - 1;
return this._onDataArrival(chunks, byteStart);
}
_onURLRedirect(redirectedURL) {
this._redirectedURL = redirectedURL;
if (this._onRedirect) {
this._onRedirect(redirectedURL);
}
}
_onContentLengthKnown(contentLength) {
if (contentLength && this._fullRequestFlag) {
this._totalLength = contentLength;
this._fullRequestFlag = false;
}
}
_onLoaderChunkArrival(chunk, byteStart, receivedLength) {
if (!this._onDataArrival) {
throw new IllegalStateException('IOController: No existing consumer (onDataArrival) callback!');
}
if (this._paused) {
return;
}
if (this._isEarlyEofReconnecting) {
// Auto-reconnect for EarlyEof succeed, notify to upper-layer by callback
this._isEarlyEofReconnecting = false;
if (this._onRecoveredEarlyEof) {
this._onRecoveredEarlyEof();
}
}
this._speedSampler.addBytes(chunk.byteLength);
// adjust stash buffer size according to network speed dynamically
let KBps = this._speedSampler.lastSecondKBps;
if (KBps !== 0) {
let normalized = this._normalizeSpeed(KBps);
if (this._speedNormalized !== normalized) {
this._speedNormalized = normalized;
this._adjustStashSize(normalized);
}
}
if (!this._enableStash) { // disable stash
if (this._stashUsed === 0) {
// dispatch chunk directly to consumer;
// check ret value (consumed bytes) and stash unconsumed to stashBuffer
let consumed = this._dispatchChunks(chunk, byteStart);
if (consumed < chunk.byteLength) { // unconsumed data remain.
let remain = chunk.byteLength - consumed;
if (remain > this._bufferSize) {
this._expandBuffer(remain);
}
let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
stashArray.set(new Uint8Array(chunk, consumed), 0);
this._stashUsed += remain;
this._stashByteStart = byteStart + consumed;
}
} else {
// else: Merge chunk into stashBuffer, and dispatch stashBuffer to consumer.
if (this._stashUsed + chunk.byteLength > this._bufferSize) {
this._expandBuffer(this._stashUsed + chunk.byteLength);
}
let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
stashArray.set(new Uint8Array(chunk), this._stashUsed);
this._stashUsed += chunk.byteLength;
let consumed = this._dispatchChunks(this._stashBuffer.slice(0, this._stashUsed), this._stashByteStart);
if (consumed < this._stashUsed && consumed > 0) { // unconsumed data remain
let remainArray = new Uint8Array(this._stashBuffer, consumed);
stashArray.set(remainArray, 0);
}
this._stashUsed -= consumed;
this._stashByteStart += consumed;
}
} else { // enable stash
if (this._stashUsed === 0 && this._stashByteStart === 0) { // seeked? or init chunk?
// This is the first chunk after seek action
this._stashByteStart = byteStart;
}
if (this._stashUsed + chunk.byteLength <= this._stashSize) {
// just stash
let stashArray = new Uint8Array(this._stashBuffer, 0, this._stashSize);
stashArray.set(new Uint8Array(chunk), this._stashUsed);
this._stashUsed += chunk.byteLength;
} else { // stashUsed + chunkSize > stashSize, size limit exceeded
let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
if (this._stashUsed > 0) { // There're stash datas in buffer
// dispatch the whole stashBuffer, and stash remain data
// then append chunk to stashBuffer (stash)
let buffer = this._stashBuffer.slice(0, this._stashUsed);
let consumed = this._dispatchChunks(buffer, this._stashByteStart);
if (consumed < buffer.byteLength) {
if (consumed > 0) {
let remainArray = new Uint8Array(buffer, consumed);
stashArray.set(remainArray, 0);
this._stashUsed = remainArray.byteLength;
this._stashByteStart += consumed;
}
} else {
this._stashUsed = 0;
this._stashByteStart += consumed;
}
if (this._stashUsed + chunk.byteLength > this._bufferSize) {
this._expandBuffer(this._stashUsed + chunk.byteLength);
stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
}
stashArray.set(new Uint8Array(chunk), this._stashUsed);
this._stashUsed += chunk.byteLength;
} else { // stash buffer empty, but chunkSize > stashSize (oh, holy shit)
// dispatch chunk directly and stash remain data
let consumed = this._dispatchChunks(chunk, byteStart);
if (consumed < chunk.byteLength) {
let remain = chunk.byteLength - consumed;
if (remain > this._bufferSize) {
this._expandBuffer(remain);
stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
}
stashArray.set(new Uint8Array(chunk, consumed), 0);
this._stashUsed += remain;
this._stashByteStart = byteStart + consumed;
}
}
}
}
}
_flushStashBuffer(dropUnconsumed) {
if (this._stashUsed > 0) {
let buffer = this._stashBuffer.slice(0, this._stashUsed);
let consumed = this._dispatchChunks(buffer, this._stashByteStart);
let remain = buffer.byteLength - consumed;
if (consumed < buffer.byteLength) {
if (dropUnconsumed) {
Log.w(this.TAG, `${remain} bytes unconsumed data remain when flush buffer, dropped`);
} else {
if (consumed > 0) {
let stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
let remainArray = new Uint8Array(buffer, consumed);
stashArray.set(remainArray, 0);
this._stashUsed = remainArray.byteLength;
this._stashByteStart += consumed;
}
return 0;
}
}
this._stashUsed = 0;
this._stashByteStart = 0;
return remain;
}
return 0;
}
_onLoaderComplete(from, to) {
// Force-flush stash buffer, and drop unconsumed data
this._flushStashBuffer(true);
if (this._onComplete) {
this._onComplete(this._extraData);
}
}
_onLoaderError(type, data) {
Log.e(this.TAG, `Loader error, code = ${data.code}, msg = ${data.msg}`);
this._flushStashBuffer(false);
if (this._isEarlyEofReconnecting) {
// Auto-reconnect for EarlyEof failed, throw UnrecoverableEarlyEof error to upper-layer
this._isEarlyEofReconnecting = false;
type = LoaderErrors.UNRECOVERABLE_EARLY_EOF;
}
switch (type) {
case LoaderErrors.EARLY_EOF: {
if (!this._config.isLive) {
// Do internal http reconnect if not live stream
if (this._totalLength) {
let nextFrom = this._currentRange.to + 1;
if (nextFrom < this._totalLength) {
Log.w(this.TAG, 'Connection lost, trying reconnect...');
this._isEarlyEofReconnecting = true;
this._internalSeek(nextFrom, false);
}
return;
}
// else: We don't know totalLength, throw UnrecoverableEarlyEof
}
// live stream: throw UnrecoverableEarlyEof error to upper-layer
type = LoaderErrors.UNRECOVERABLE_EARLY_EOF;
break;
}
case LoaderErrors.UNRECOVERABLE_EARLY_EOF:
case LoaderErrors.CONNECTING_TIMEOUT:
case LoaderErrors.HTTP_STATUS_CODE_INVALID:
case LoaderErrors.EXCEPTION:
break;
}
if (this._onError) {
this._onError(type, data);
} else {
throw new RuntimeException('IOException: ' + data.msg);
}
}
}
export default IOController;

134
node_modules/flv.js/src/io/loader.js generated vendored Normal file
View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {NotImplementedException} from '../utils/exception.js';
export const LoaderStatus = {
kIdle: 0,
kConnecting: 1,
kBuffering: 2,
kError: 3,
kComplete: 4
};
export const LoaderErrors = {
OK: 'OK',
EXCEPTION: 'Exception',
HTTP_STATUS_CODE_INVALID: 'HttpStatusCodeInvalid',
CONNECTING_TIMEOUT: 'ConnectingTimeout',
EARLY_EOF: 'EarlyEof',
UNRECOVERABLE_EARLY_EOF: 'UnrecoverableEarlyEof'
};
/* Loader has callbacks which have following prototypes:
* function onContentLengthKnown(contentLength: number): void
* function onURLRedirect(url: string): void
* function onDataArrival(chunk: ArrayBuffer, byteStart: number, receivedLength: number): void
* function onError(errorType: number, errorInfo: {code: number, msg: string}): void
* function onComplete(rangeFrom: number, rangeTo: number): void
*/
export class BaseLoader {
constructor(typeName) {
this._type = typeName || 'undefined';
this._status = LoaderStatus.kIdle;
this._needStash = false;
// callbacks
this._onContentLengthKnown = null;
this._onURLRedirect = null;
this._onDataArrival = null;
this._onError = null;
this._onComplete = null;
}
destroy() {
this._status = LoaderStatus.kIdle;
this._onContentLengthKnown = null;
this._onURLRedirect = null;
this._onDataArrival = null;
this._onError = null;
this._onComplete = null;
}
isWorking() {
return this._status === LoaderStatus.kConnecting || this._status === LoaderStatus.kBuffering;
}
get type() {
return this._type;
}
get status() {
return this._status;
}
get needStashBuffer() {
return this._needStash;
}
get onContentLengthKnown() {
return this._onContentLengthKnown;
}
set onContentLengthKnown(callback) {
this._onContentLengthKnown = callback;
}
get onURLRedirect() {
return this._onURLRedirect;
}
set onURLRedirect(callback) {
this._onURLRedirect = callback;
}
get onDataArrival() {
return this._onDataArrival;
}
set onDataArrival(callback) {
this._onDataArrival = callback;
}
get onError() {
return this._onError;
}
set onError(callback) {
this._onError = callback;
}
get onComplete() {
return this._onComplete;
}
set onComplete(callback) {
this._onComplete = callback;
}
// pure virtual
open(dataSource, range) {
throw new NotImplementedException('Unimplemented abstract function!');
}
abort() {
throw new NotImplementedException('Unimplemented abstract function!');
}
}

85
node_modules/flv.js/src/io/param-seek-handler.js generated vendored Normal file
View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ParamSeekHandler {
constructor(paramStart, paramEnd) {
this._startName = paramStart;
this._endName = paramEnd;
}
getConfig(baseUrl, range) {
let url = baseUrl;
if (range.from !== 0 || range.to !== -1) {
let needAnd = true;
if (url.indexOf('?') === -1) {
url += '?';
needAnd = false;
}
if (needAnd) {
url += '&';
}
url += `${this._startName}=${range.from.toString()}`;
if (range.to !== -1) {
url += `&${this._endName}=${range.to.toString()}`;
}
}
return {
url: url,
headers: {}
};
}
removeURLParameters(seekedURL) {
let baseURL = seekedURL.split('?')[0];
let params = undefined;
let queryIndex = seekedURL.indexOf('?');
if (queryIndex !== -1) {
params = seekedURL.substring(queryIndex + 1);
}
let resultParams = '';
if (params != undefined && params.length > 0) {
let pairs = params.split('&');
for (let i = 0; i < pairs.length; i++) {
let pair = pairs[i].split('=');
let requireAnd = (i > 0);
if (pair[0] !== this._startName && pair[0] !== this._endName) {
if (requireAnd) {
resultParams += '&';
}
resultParams += pairs[i];
}
}
}
return (resultParams.length === 0) ? baseURL : baseURL + '?' + resultParams;
}
}
export default ParamSeekHandler;

52
node_modules/flv.js/src/io/range-seek-handler.js generated vendored Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class RangeSeekHandler {
constructor(zeroStart) {
this._zeroStart = zeroStart || false;
}
getConfig(url, range) {
let headers = {};
if (range.from !== 0 || range.to !== -1) {
let param;
if (range.to !== -1) {
param = `bytes=${range.from.toString()}-${range.to.toString()}`;
} else {
param = `bytes=${range.from.toString()}-`;
}
headers['Range'] = param;
} else if (this._zeroStart) {
headers['Range'] = 'bytes=0-';
}
return {
url: url,
headers: headers
};
}
removeURLParameters(seekedURL) {
return seekedURL;
}
}
export default RangeSeekHandler;

93
node_modules/flv.js/src/io/speed-sampler.js generated vendored Normal file
View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Utility class to calculate realtime network I/O speed
class SpeedSampler {
constructor() {
// milliseconds
this._firstCheckpoint = 0;
this._lastCheckpoint = 0;
this._intervalBytes = 0;
this._totalBytes = 0;
this._lastSecondBytes = 0;
// compatibility detection
if (self.performance && self.performance.now) {
this._now = self.performance.now.bind(self.performance);
} else {
this._now = Date.now;
}
}
reset() {
this._firstCheckpoint = this._lastCheckpoint = 0;
this._totalBytes = this._intervalBytes = 0;
this._lastSecondBytes = 0;
}
addBytes(bytes) {
if (this._firstCheckpoint === 0) {
this._firstCheckpoint = this._now();
this._lastCheckpoint = this._firstCheckpoint;
this._intervalBytes += bytes;
this._totalBytes += bytes;
} else if (this._now() - this._lastCheckpoint < 1000) {
this._intervalBytes += bytes;
this._totalBytes += bytes;
} else { // duration >= 1000
this._lastSecondBytes = this._intervalBytes;
this._intervalBytes = bytes;
this._totalBytes += bytes;
this._lastCheckpoint = this._now();
}
}
get currentKBps() {
this.addBytes(0);
let durationSeconds = (this._now() - this._lastCheckpoint) / 1000;
if (durationSeconds == 0) durationSeconds = 1;
return (this._intervalBytes / durationSeconds) / 1024;
}
get lastSecondKBps() {
this.addBytes(0);
if (this._lastSecondBytes !== 0) {
return this._lastSecondBytes / 1024;
} else { // lastSecondBytes === 0
if (this._now() - this._lastCheckpoint >= 500) {
// if time interval since last checkpoint has exceeded 500ms
// the speed is nearly accurate
return this.currentKBps;
} else {
// We don't know
return 0;
}
}
}
get averageKBps() {
let durationSeconds = (this._now() - this._firstCheckpoint) / 1000;
return (this._totalBytes / durationSeconds) / 1024;
}
}
export default SpeedSampler;

151
node_modules/flv.js/src/io/websocket-loader.js generated vendored Normal file
View File

@ -0,0 +1,151 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Log from '../utils/logger.js';
import {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';
import {RuntimeException} from '../utils/exception.js';
// For FLV over WebSocket live stream
class WebSocketLoader extends BaseLoader {
static isSupported() {
try {
return (typeof self.WebSocket !== 'undefined');
} catch (e) {
return false;
}
}
constructor() {
super('websocket-loader');
this.TAG = 'WebSocketLoader';
this._needStash = true;
this._ws = null;
this._requestAbort = false;
this._receivedLength = 0;
}
destroy() {
if (this._ws) {
this.abort();
}
super.destroy();
}
open(dataSource) {
try {
let ws = this._ws = new self.WebSocket(dataSource.url);
ws.binaryType = 'arraybuffer';
ws.onopen = this._onWebSocketOpen.bind(this);
ws.onclose = this._onWebSocketClose.bind(this);
ws.onmessage = this._onWebSocketMessage.bind(this);
ws.onerror = this._onWebSocketError.bind(this);
this._status = LoaderStatus.kConnecting;
} catch (e) {
this._status = LoaderStatus.kError;
let info = {code: e.code, msg: e.message};
if (this._onError) {
this._onError(LoaderErrors.EXCEPTION, info);
} else {
throw new RuntimeException(info.msg);
}
}
}
abort() {
let ws = this._ws;
if (ws && (ws.readyState === 0 || ws.readyState === 1)) { // CONNECTING || OPEN
this._requestAbort = true;
ws.close();
}
this._ws = null;
this._status = LoaderStatus.kComplete;
}
_onWebSocketOpen(e) {
this._status = LoaderStatus.kBuffering;
}
_onWebSocketClose(e) {
if (this._requestAbort === true) {
this._requestAbort = false;
return;
}
this._status = LoaderStatus.kComplete;
if (this._onComplete) {
this._onComplete(0, this._receivedLength - 1);
}
}
_onWebSocketMessage(e) {
if (e.data instanceof ArrayBuffer) {
this._dispatchArrayBuffer(e.data);
} else if (e.data instanceof Blob) {
let reader = new FileReader();
reader.onload = () => {
this._dispatchArrayBuffer(reader.result);
};
reader.readAsArrayBuffer(e.data);
} else {
this._status = LoaderStatus.kError;
let info = {code: -1, msg: 'Unsupported WebSocket message type: ' + e.data.constructor.name};
if (this._onError) {
this._onError(LoaderErrors.EXCEPTION, info);
} else {
throw new RuntimeException(info.msg);
}
}
}
_dispatchArrayBuffer(arraybuffer) {
let chunk = arraybuffer;
let byteStart = this._receivedLength;
this._receivedLength += chunk.byteLength;
if (this._onDataArrival) {
this._onDataArrival(chunk, byteStart, this._receivedLength);
}
}
_onWebSocketError(e) {
this._status = LoaderStatus.kError;
let info = {
code: e.code,
msg: e.message
};
if (this._onError) {
this._onError(LoaderErrors.EXCEPTION, info);
} else {
throw new RuntimeException(info.msg);
}
}
}
export default WebSocketLoader;

211
node_modules/flv.js/src/io/xhr-moz-chunked-loader.js generated vendored Normal file
View File

@ -0,0 +1,211 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Log from '../utils/logger.js';
import {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';
import {RuntimeException} from '../utils/exception.js';
// For FireFox browser which supports `xhr.responseType = 'moz-chunked-arraybuffer'`
class MozChunkedLoader extends BaseLoader {
static isSupported() {
try {
let xhr = new XMLHttpRequest();
// Firefox 37- requires .open() to be called before setting responseType
xhr.open('GET', 'https://example.com', true);
xhr.responseType = 'moz-chunked-arraybuffer';
return (xhr.responseType === 'moz-chunked-arraybuffer');
} catch (e) {
Log.w('MozChunkedLoader', e.message);
return false;
}
}
constructor(seekHandler, config) {
super('xhr-moz-chunked-loader');
this.TAG = 'MozChunkedLoader';
this._seekHandler = seekHandler;
this._config = config;
this._needStash = true;
this._xhr = null;
this._requestAbort = false;
this._contentLength = null;
this._receivedLength = 0;
}
destroy() {
if (this.isWorking()) {
this.abort();
}
if (this._xhr) {
this._xhr.onreadystatechange = null;
this._xhr.onprogress = null;
this._xhr.onloadend = null;
this._xhr.onerror = null;
this._xhr = null;
}
super.destroy();
}
open(dataSource, range) {
this._dataSource = dataSource;
this._range = range;
let sourceURL = dataSource.url;
if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) {
sourceURL = dataSource.redirectedURL;
}
let seekConfig = this._seekHandler.getConfig(sourceURL, range);
this._requestURL = seekConfig.url;
let xhr = this._xhr = new XMLHttpRequest();
xhr.open('GET', seekConfig.url, true);
xhr.responseType = 'moz-chunked-arraybuffer';
xhr.onreadystatechange = this._onReadyStateChange.bind(this);
xhr.onprogress = this._onProgress.bind(this);
xhr.onloadend = this._onLoadEnd.bind(this);
xhr.onerror = this._onXhrError.bind(this);
// cors is auto detected and enabled by xhr
// withCredentials is disabled by default
if (dataSource.withCredentials) {
xhr.withCredentials = true;
}
if (typeof seekConfig.headers === 'object') {
let headers = seekConfig.headers;
for (let key in headers) {
if (headers.hasOwnProperty(key)) {
xhr.setRequestHeader(key, headers[key]);
}
}
}
// add additional headers
if (typeof this._config.headers === 'object') {
let headers = this._config.headers;
for (let key in headers) {
if (headers.hasOwnProperty(key)) {
xhr.setRequestHeader(key, headers[key]);
}
}
}
this._status = LoaderStatus.kConnecting;
xhr.send();
}
abort() {
this._requestAbort = true;
if (this._xhr) {
this._xhr.abort();
}
this._status = LoaderStatus.kComplete;
}
_onReadyStateChange(e) {
let xhr = e.target;
if (xhr.readyState === 2) { // HEADERS_RECEIVED
if (xhr.responseURL != undefined && xhr.responseURL !== this._requestURL) {
if (this._onURLRedirect) {
let redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL);
this._onURLRedirect(redirectedURL);
}
}
if (xhr.status !== 0 && (xhr.status < 200 || xhr.status > 299)) {
this._status = LoaderStatus.kError;
if (this._onError) {
this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, {code: xhr.status, msg: xhr.statusText});
} else {
throw new RuntimeException('MozChunkedLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText);
}
} else {
this._status = LoaderStatus.kBuffering;
}
}
}
_onProgress(e) {
if (this._status === LoaderStatus.kError) {
// Ignore error response
return;
}
if (this._contentLength === null) {
if (e.total !== null && e.total !== 0) {
this._contentLength = e.total;
if (this._onContentLengthKnown) {
this._onContentLengthKnown(this._contentLength);
}
}
}
let chunk = e.target.response;
let byteStart = this._range.from + this._receivedLength;
this._receivedLength += chunk.byteLength;
if (this._onDataArrival) {
this._onDataArrival(chunk, byteStart, this._receivedLength);
}
}
_onLoadEnd(e) {
if (this._requestAbort === true) {
this._requestAbort = false;
return;
} else if (this._status === LoaderStatus.kError) {
return;
}
this._status = LoaderStatus.kComplete;
if (this._onComplete) {
this._onComplete(this._range.from, this._range.from + this._receivedLength - 1);
}
}
_onXhrError(e) {
this._status = LoaderStatus.kError;
let type = 0;
let info = null;
if (this._contentLength && e.loaded < this._contentLength) {
type = LoaderErrors.EARLY_EOF;
info = {code: -1, msg: 'Moz-Chunked stream meet Early-Eof'};
} else {
type = LoaderErrors.EXCEPTION;
info = {code: -1, msg: e.constructor.name + ' ' + e.type};
}
if (this._onError) {
this._onError(type, info);
} else {
throw new RuntimeException(info.msg);
}
}
}
export default MozChunkedLoader;

307
node_modules/flv.js/src/io/xhr-msstream-loader.js generated vendored Normal file
View File

@ -0,0 +1,307 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Log from '../utils/logger.js';
import {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';
import {RuntimeException} from '../utils/exception.js';
/* Notice: ms-stream may cause IE/Edge browser crash if seek too frequently!!!
* The browser may crash in wininet.dll. Disable for now.
*
* For IE11/Edge browser by microsoft which supports `xhr.responseType = 'ms-stream'`
* Notice that ms-stream API sucks. The buffer is always expanding along with downloading.
*
* We need to abort the xhr if buffer size exceeded limit size (e.g. 16 MiB), then do reconnect.
* in order to release previous ArrayBuffer to avoid memory leak
*
* Otherwise, the ArrayBuffer will increase to a terrible size that equals final file size.
*/
class MSStreamLoader extends BaseLoader {
static isSupported() {
try {
if (typeof self.MSStream === 'undefined' || typeof self.MSStreamReader === 'undefined') {
return false;
}
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com', true);
xhr.responseType = 'ms-stream';
return (xhr.responseType === 'ms-stream');
} catch (e) {
Log.w('MSStreamLoader', e.message);
return false;
}
}
constructor(seekHandler, config) {
super('xhr-msstream-loader');
this.TAG = 'MSStreamLoader';
this._seekHandler = seekHandler;
this._config = config;
this._needStash = true;
this._xhr = null;
this._reader = null; // MSStreamReader
this._totalRange = null;
this._currentRange = null;
this._currentRequestURL = null;
this._currentRedirectedURL = null;
this._contentLength = null;
this._receivedLength = 0;
this._bufferLimit = 16 * 1024 * 1024; // 16MB
this._lastTimeBufferSize = 0;
this._isReconnecting = false;
}
destroy() {
if (this.isWorking()) {
this.abort();
}
if (this._reader) {
this._reader.onprogress = null;
this._reader.onload = null;
this._reader.onerror = null;
this._reader = null;
}
if (this._xhr) {
this._xhr.onreadystatechange = null;
this._xhr = null;
}
super.destroy();
}
open(dataSource, range) {
this._internalOpen(dataSource, range, false);
}
_internalOpen(dataSource, range, isSubrange) {
this._dataSource = dataSource;
if (!isSubrange) {
this._totalRange = range;
} else {
this._currentRange = range;
}
let sourceURL = dataSource.url;
if (this._config.reuseRedirectedURL) {
if (this._currentRedirectedURL != undefined) {
sourceURL = this._currentRedirectedURL;
} else if (dataSource.redirectedURL != undefined) {
sourceURL = dataSource.redirectedURL;
}
}
let seekConfig = this._seekHandler.getConfig(sourceURL, range);
this._currentRequestURL = seekConfig.url;
let reader = this._reader = new self.MSStreamReader();
reader.onprogress = this._msrOnProgress.bind(this);
reader.onload = this._msrOnLoad.bind(this);
reader.onerror = this._msrOnError.bind(this);
let xhr = this._xhr = new XMLHttpRequest();
xhr.open('GET', seekConfig.url, true);
xhr.responseType = 'ms-stream';
xhr.onreadystatechange = this._xhrOnReadyStateChange.bind(this);
xhr.onerror = this._xhrOnError.bind(this);
if (dataSource.withCredentials) {
xhr.withCredentials = true;
}
if (typeof seekConfig.headers === 'object') {
let headers = seekConfig.headers;
for (let key in headers) {
if (headers.hasOwnProperty(key)) {
xhr.setRequestHeader(key, headers[key]);
}
}
}
// add additional headers
if (typeof this._config.headers === 'object') {
let headers = this._config.headers;
for (let key in headers) {
if (headers.hasOwnProperty(key)) {
xhr.setRequestHeader(key, headers[key]);
}
}
}
if (this._isReconnecting) {
this._isReconnecting = false;
} else {
this._status = LoaderStatus.kConnecting;
}
xhr.send();
}
abort() {
this._internalAbort();
this._status = LoaderStatus.kComplete;
}
_internalAbort() {
if (this._reader) {
if (this._reader.readyState === 1) { // LOADING
this._reader.abort();
}
this._reader.onprogress = null;
this._reader.onload = null;
this._reader.onerror = null;
this._reader = null;
}
if (this._xhr) {
this._xhr.abort();
this._xhr.onreadystatechange = null;
this._xhr = null;
}
}
_xhrOnReadyStateChange(e) {
let xhr = e.target;
if (xhr.readyState === 2) { // HEADERS_RECEIVED
if (xhr.status >= 200 && xhr.status <= 299) {
this._status = LoaderStatus.kBuffering;
if (xhr.responseURL != undefined) {
let redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL);
if (xhr.responseURL !== this._currentRequestURL && redirectedURL !== this._currentRedirectedURL) {
this._currentRedirectedURL = redirectedURL;
if (this._onURLRedirect) {
this._onURLRedirect(redirectedURL);
}
}
}
let lengthHeader = xhr.getResponseHeader('Content-Length');
if (lengthHeader != null && this._contentLength == null) {
let length = parseInt(lengthHeader);
if (length > 0) {
this._contentLength = length;
if (this._onContentLengthKnown) {
this._onContentLengthKnown(this._contentLength);
}
}
}
} else {
this._status = LoaderStatus.kError;
if (this._onError) {
this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, {code: xhr.status, msg: xhr.statusText});
} else {
throw new RuntimeException('MSStreamLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText);
}
}
} else if (xhr.readyState === 3) { // LOADING
if (xhr.status >= 200 && xhr.status <= 299) {
this._status = LoaderStatus.kBuffering;
let msstream = xhr.response;
this._reader.readAsArrayBuffer(msstream);
}
}
}
_xhrOnError(e) {
this._status = LoaderStatus.kError;
let type = LoaderErrors.EXCEPTION;
let info = {code: -1, msg: e.constructor.name + ' ' + e.type};
if (this._onError) {
this._onError(type, info);
} else {
throw new RuntimeException(info.msg);
}
}
_msrOnProgress(e) {
let reader = e.target;
let bigbuffer = reader.result;
if (bigbuffer == null) { // result may be null, workaround for buggy M$
this._doReconnectIfNeeded();
return;
}
let slice = bigbuffer.slice(this._lastTimeBufferSize);
this._lastTimeBufferSize = bigbuffer.byteLength;
let byteStart = this._totalRange.from + this._receivedLength;
this._receivedLength += slice.byteLength;
if (this._onDataArrival) {
this._onDataArrival(slice, byteStart, this._receivedLength);
}
if (bigbuffer.byteLength >= this._bufferLimit) {
Log.v(this.TAG, `MSStream buffer exceeded max size near ${byteStart + slice.byteLength}, reconnecting...`);
this._doReconnectIfNeeded();
}
}
_doReconnectIfNeeded() {
if (this._contentLength == null || this._receivedLength < this._contentLength) {
this._isReconnecting = true;
this._lastTimeBufferSize = 0;
this._internalAbort();
let range = {
from: this._totalRange.from + this._receivedLength,
to: -1
};
this._internalOpen(this._dataSource, range, true);
}
}
_msrOnLoad(e) { // actually it is onComplete event
this._status = LoaderStatus.kComplete;
if (this._onComplete) {
this._onComplete(this._totalRange.from, this._totalRange.from + this._receivedLength - 1);
}
}
_msrOnError(e) {
this._status = LoaderStatus.kError;
let type = 0;
let info = null;
if (this._contentLength && this._receivedLength < this._contentLength) {
type = LoaderErrors.EARLY_EOF;
info = {code: -1, msg: 'MSStream meet Early-Eof'};
} else {
type = LoaderErrors.EARLY_EOF;
info = {code: -1, msg: e.constructor.name + ' ' + e.type};
}
if (this._onError) {
this._onError(type, info);
} else {
throw new RuntimeException(info.msg);
}
}
}
export default MSStreamLoader;

366
node_modules/flv.js/src/io/xhr-range-loader.js generated vendored Normal file
View File

@ -0,0 +1,366 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Log from '../utils/logger.js';
import SpeedSampler from './speed-sampler.js';
import {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';
import {RuntimeException} from '../utils/exception.js';
// Universal IO Loader, implemented by adding Range header in xhr's request header
class RangeLoader extends BaseLoader {
static isSupported() {
try {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com', true);
xhr.responseType = 'arraybuffer';
return (xhr.responseType === 'arraybuffer');
} catch (e) {
Log.w('RangeLoader', e.message);
return false;
}
}
constructor(seekHandler, config) {
super('xhr-range-loader');
this.TAG = 'RangeLoader';
this._seekHandler = seekHandler;
this._config = config;
this._needStash = false;
this._chunkSizeKBList = [
128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 5120, 6144, 7168, 8192
];
this._currentChunkSizeKB = 384;
this._currentSpeedNormalized = 0;
this._zeroSpeedChunkCount = 0;
this._xhr = null;
this._speedSampler = new SpeedSampler();
this._requestAbort = false;
this._waitForTotalLength = false;
this._totalLengthReceived = false;
this._currentRequestURL = null;
this._currentRedirectedURL = null;
this._currentRequestRange = null;
this._totalLength = null; // size of the entire file
this._contentLength = null; // Content-Length of entire request range
this._receivedLength = 0; // total received bytes
this._lastTimeLoaded = 0; // received bytes of current request sub-range
}
destroy() {
if (this.isWorking()) {
this.abort();
}
if (this._xhr) {
this._xhr.onreadystatechange = null;
this._xhr.onprogress = null;
this._xhr.onload = null;
this._xhr.onerror = null;
this._xhr = null;
}
super.destroy();
}
get currentSpeed() {
return this._speedSampler.lastSecondKBps;
}
open(dataSource, range) {
this._dataSource = dataSource;
this._range = range;
this._status = LoaderStatus.kConnecting;
let useRefTotalLength = false;
if (this._dataSource.filesize != undefined && this._dataSource.filesize !== 0) {
useRefTotalLength = true;
this._totalLength = this._dataSource.filesize;
}
if (!this._totalLengthReceived && !useRefTotalLength) {
// We need total filesize
this._waitForTotalLength = true;
this._internalOpen(this._dataSource, {from: 0, to: -1});
} else {
// We have filesize, start loading
this._openSubRange();
}
}
_openSubRange() {
let chunkSize = this._currentChunkSizeKB * 1024;
let from = this._range.from + this._receivedLength;
let to = from + chunkSize;
if (this._contentLength != null) {
if (to - this._range.from >= this._contentLength) {
to = this._range.from + this._contentLength - 1;
}
}
this._currentRequestRange = {from, to};
this._internalOpen(this._dataSource, this._currentRequestRange);
}
_internalOpen(dataSource, range) {
this._lastTimeLoaded = 0;
let sourceURL = dataSource.url;
if (this._config.reuseRedirectedURL) {
if (this._currentRedirectedURL != undefined) {
sourceURL = this._currentRedirectedURL;
} else if (dataSource.redirectedURL != undefined) {
sourceURL = dataSource.redirectedURL;
}
}
let seekConfig = this._seekHandler.getConfig(sourceURL, range);
this._currentRequestURL = seekConfig.url;
let xhr = this._xhr = new XMLHttpRequest();
xhr.open('GET', seekConfig.url, true);
xhr.responseType = 'arraybuffer';
xhr.onreadystatechange = this._onReadyStateChange.bind(this);
xhr.onprogress = this._onProgress.bind(this);
xhr.onload = this._onLoad.bind(this);
xhr.onerror = this._onXhrError.bind(this);
if (dataSource.withCredentials) {
xhr.withCredentials = true;
}
if (typeof seekConfig.headers === 'object') {
let headers = seekConfig.headers;
for (let key in headers) {
if (headers.hasOwnProperty(key)) {
xhr.setRequestHeader(key, headers[key]);
}
}
}
// add additional headers
if (typeof this._config.headers === 'object') {
let headers = this._config.headers;
for (let key in headers) {
if (headers.hasOwnProperty(key)) {
xhr.setRequestHeader(key, headers[key]);
}
}
}
xhr.send();
}
abort() {
this._requestAbort = true;
this._internalAbort();
this._status = LoaderStatus.kComplete;
}
_internalAbort() {
if (this._xhr) {
this._xhr.onreadystatechange = null;
this._xhr.onprogress = null;
this._xhr.onload = null;
this._xhr.onerror = null;
this._xhr.abort();
this._xhr = null;
}
}
_onReadyStateChange(e) {
let xhr = e.target;
if (xhr.readyState === 2) { // HEADERS_RECEIVED
if (xhr.responseURL != undefined) { // if the browser support this property
let redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL);
if (xhr.responseURL !== this._currentRequestURL && redirectedURL !== this._currentRedirectedURL) {
this._currentRedirectedURL = redirectedURL;
if (this._onURLRedirect) {
this._onURLRedirect(redirectedURL);
}
}
}
if ((xhr.status >= 200 && xhr.status <= 299)) {
if (this._waitForTotalLength) {
return;
}
this._status = LoaderStatus.kBuffering;
} else {
this._status = LoaderStatus.kError;
if (this._onError) {
this._onError(LoaderErrors.HTTP_STATUS_CODE_INVALID, {code: xhr.status, msg: xhr.statusText});
} else {
throw new RuntimeException('RangeLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText);
}
}
}
}
_onProgress(e) {
if (this._status === LoaderStatus.kError) {
// Ignore error response
return;
}
if (this._contentLength === null) {
let openNextRange = false;
if (this._waitForTotalLength) {
this._waitForTotalLength = false;
this._totalLengthReceived = true;
openNextRange = true;
let total = e.total;
this._internalAbort();
if (total != null & total !== 0) {
this._totalLength = total;
}
}
// calculate currrent request range's contentLength
if (this._range.to === -1) {
this._contentLength = this._totalLength - this._range.from;
} else { // to !== -1
this._contentLength = this._range.to - this._range.from + 1;
}
if (openNextRange) {
this._openSubRange();
return;
}
if (this._onContentLengthKnown) {
this._onContentLengthKnown(this._contentLength);
}
}
let delta = e.loaded - this._lastTimeLoaded;
this._lastTimeLoaded = e.loaded;
this._speedSampler.addBytes(delta);
}
_normalizeSpeed(input) {
let list = this._chunkSizeKBList;
let last = list.length - 1;
let mid = 0;
let lbound = 0;
let ubound = last;
if (input < list[0]) {
return list[0];
}
while (lbound <= ubound) {
mid = lbound + Math.floor((ubound - lbound) / 2);
if (mid === last || (input >= list[mid] && input < list[mid + 1])) {
return list[mid];
} else if (list[mid] < input) {
lbound = mid + 1;
} else {
ubound = mid - 1;
}
}
}
_onLoad(e) {
if (this._status === LoaderStatus.kError) {
// Ignore error response
return;
}
if (this._waitForTotalLength) {
this._waitForTotalLength = false;
return;
}
this._lastTimeLoaded = 0;
let KBps = this._speedSampler.lastSecondKBps;
if (KBps === 0) {
this._zeroSpeedChunkCount++;
if (this._zeroSpeedChunkCount >= 3) {
// Try get currentKBps after 3 chunks
KBps = this._speedSampler.currentKBps;
}
}
if (KBps !== 0) {
let normalized = this._normalizeSpeed(KBps);
if (this._currentSpeedNormalized !== normalized) {
this._currentSpeedNormalized = normalized;
this._currentChunkSizeKB = normalized;
}
}
let chunk = e.target.response;
let byteStart = this._range.from + this._receivedLength;
this._receivedLength += chunk.byteLength;
let reportComplete = false;
if (this._contentLength != null && this._receivedLength < this._contentLength) {
// continue load next chunk
this._openSubRange();
} else {
reportComplete = true;
}
// dispatch received chunk
if (this._onDataArrival) {
this._onDataArrival(chunk, byteStart, this._receivedLength);
}
if (reportComplete) {
this._status = LoaderStatus.kComplete;
if (this._onComplete) {
this._onComplete(this._range.from, this._range.from + this._receivedLength - 1);
}
}
}
_onXhrError(e) {
this._status = LoaderStatus.kError;
let type = 0;
let info = null;
if (this._contentLength && this._receivedLength > 0
&& this._receivedLength < this._contentLength) {
type = LoaderErrors.EARLY_EOF;
info = {code: -1, msg: 'RangeLoader meet Early-Eof'};
} else {
type = LoaderErrors.EXCEPTION;
info = {code: -1, msg: e.constructor.name + ' ' + e.type};
}
if (this._onError) {
this._onError(type, info);
} else {
throw new RuntimeException(info.msg);
}
}
}
export default RangeLoader;

610
node_modules/flv.js/src/player/flv-player.js generated vendored Normal file
View File

@ -0,0 +1,610 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import EventEmitter from 'events';
import Log from '../utils/logger.js';
import Browser from '../utils/browser.js';
import PlayerEvents from './player-events.js';
import Transmuxer from '../core/transmuxer.js';
import TransmuxingEvents from '../core/transmuxing-events.js';
import MSEController from '../core/mse-controller.js';
import MSEEvents from '../core/mse-events.js';
import {ErrorTypes, ErrorDetails} from './player-errors.js';
import {createDefaultConfig} from '../config.js';
import {InvalidArgumentException, IllegalStateException} from '../utils/exception.js';
class FlvPlayer {
constructor(mediaDataSource, config) {
this.TAG = 'FlvPlayer';
this._type = 'FlvPlayer';
this._emitter = new EventEmitter();
this._config = createDefaultConfig();
if (typeof config === 'object') {
Object.assign(this._config, config);
}
if (mediaDataSource.type.toLowerCase() !== 'flv') {
throw new InvalidArgumentException('FlvPlayer requires an flv MediaDataSource input!');
}
if (mediaDataSource.isLive === true) {
this._config.isLive = true;
}
this.e = {
onvLoadedMetadata: this._onvLoadedMetadata.bind(this),
onvSeeking: this._onvSeeking.bind(this),
onvCanPlay: this._onvCanPlay.bind(this),
onvStalled: this._onvStalled.bind(this),
onvProgress: this._onvProgress.bind(this)
};
if (self.performance && self.performance.now) {
this._now = self.performance.now.bind(self.performance);
} else {
this._now = Date.now;
}
this._pendingSeekTime = null; // in seconds
this._requestSetTime = false;
this._seekpointRecord = null;
this._progressChecker = null;
this._mediaDataSource = mediaDataSource;
this._mediaElement = null;
this._msectl = null;
this._transmuxer = null;
this._mseSourceOpened = false;
this._hasPendingLoad = false;
this._receivedCanPlay = false;
this._mediaInfo = null;
this._statisticsInfo = null;
let chromeNeedIDRFix = (Browser.chrome &&
(Browser.version.major < 50 ||
(Browser.version.major === 50 && Browser.version.build < 2661)));
this._alwaysSeekKeyframe = (chromeNeedIDRFix || Browser.msedge || Browser.msie) ? true : false;
if (this._alwaysSeekKeyframe) {
this._config.accurateSeek = false;
}
}
destroy() {
if (this._progressChecker != null) {
window.clearInterval(this._progressChecker);
this._progressChecker = null;
}
if (this._transmuxer) {
this.unload();
}
if (this._mediaElement) {
this.detachMediaElement();
}
this.e = null;
this._mediaDataSource = null;
this._emitter.removeAllListeners();
this._emitter = null;
}
on(event, listener) {
if (event === PlayerEvents.MEDIA_INFO) {
if (this._mediaInfo != null) {
Promise.resolve().then(() => {
this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo);
});
}
} else if (event === PlayerEvents.STATISTICS_INFO) {
if (this._statisticsInfo != null) {
Promise.resolve().then(() => {
this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo);
});
}
}
this._emitter.addListener(event, listener);
}
off(event, listener) {
this._emitter.removeListener(event, listener);
}
attachMediaElement(mediaElement) {
this._mediaElement = mediaElement;
mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata);
mediaElement.addEventListener('seeking', this.e.onvSeeking);
mediaElement.addEventListener('canplay', this.e.onvCanPlay);
mediaElement.addEventListener('stalled', this.e.onvStalled);
mediaElement.addEventListener('progress', this.e.onvProgress);
this._msectl = new MSEController(this._config);
this._msectl.on(MSEEvents.UPDATE_END, this._onmseUpdateEnd.bind(this));
this._msectl.on(MSEEvents.BUFFER_FULL, this._onmseBufferFull.bind(this));
this._msectl.on(MSEEvents.SOURCE_OPEN, () => {
this._mseSourceOpened = true;
if (this._hasPendingLoad) {
this._hasPendingLoad = false;
this.load();
}
});
this._msectl.on(MSEEvents.ERROR, (info) => {
this._emitter.emit(PlayerEvents.ERROR,
ErrorTypes.MEDIA_ERROR,
ErrorDetails.MEDIA_MSE_ERROR,
info
);
});
this._msectl.attachMediaElement(mediaElement);
if (this._pendingSeekTime != null) {
try {
mediaElement.currentTime = this._pendingSeekTime;
this._pendingSeekTime = null;
} catch (e) {
// IE11 may throw InvalidStateError if readyState === 0
// We can defer set currentTime operation after loadedmetadata
}
}
}
detachMediaElement() {
if (this._mediaElement) {
this._msectl.detachMediaElement();
this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata);
this._mediaElement.removeEventListener('seeking', this.e.onvSeeking);
this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay);
this._mediaElement.removeEventListener('stalled', this.e.onvStalled);
this._mediaElement.removeEventListener('progress', this.e.onvProgress);
this._mediaElement = null;
}
if (this._msectl) {
this._msectl.destroy();
this._msectl = null;
}
}
load() {
if (!this._mediaElement) {
throw new IllegalStateException('HTMLMediaElement must be attached before load()!');
}
if (this._transmuxer) {
throw new IllegalStateException('FlvPlayer.load() has been called, please call unload() first!');
}
if (this._hasPendingLoad) {
return;
}
if (this._config.deferLoadAfterSourceOpen && this._mseSourceOpened === false) {
this._hasPendingLoad = true;
return;
}
if (this._mediaElement.readyState > 0) {
this._requestSetTime = true;
// IE11 may throw InvalidStateError if readyState === 0
this._mediaElement.currentTime = 0;
}
this._transmuxer = new Transmuxer(this._mediaDataSource, this._config);
this._transmuxer.on(TransmuxingEvents.INIT_SEGMENT, (type, is) => {
this._msectl.appendInitSegment(is);
});
this._transmuxer.on(TransmuxingEvents.MEDIA_SEGMENT, (type, ms) => {
this._msectl.appendMediaSegment(ms);
// lazyLoad check
if (this._config.lazyLoad && !this._config.isLive) {
let currentTime = this._mediaElement.currentTime;
if (ms.info.endDts >= (currentTime + this._config.lazyLoadMaxDuration) * 1000) {
if (this._progressChecker == null) {
Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task');
this._suspendTransmuxer();
}
}
}
});
this._transmuxer.on(TransmuxingEvents.LOADING_COMPLETE, () => {
this._msectl.endOfStream();
this._emitter.emit(PlayerEvents.LOADING_COMPLETE);
});
this._transmuxer.on(TransmuxingEvents.RECOVERED_EARLY_EOF, () => {
this._emitter.emit(PlayerEvents.RECOVERED_EARLY_EOF);
});
this._transmuxer.on(TransmuxingEvents.IO_ERROR, (detail, info) => {
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, detail, info);
});
this._transmuxer.on(TransmuxingEvents.DEMUX_ERROR, (detail, info) => {
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, detail, {code: -1, msg: info});
});
this._transmuxer.on(TransmuxingEvents.MEDIA_INFO, (mediaInfo) => {
this._mediaInfo = mediaInfo;
this._emitter.emit(PlayerEvents.MEDIA_INFO, Object.assign({}, mediaInfo));
});
this._transmuxer.on(TransmuxingEvents.METADATA_ARRIVED, (metadata) => {
this._emitter.emit(PlayerEvents.METADATA_ARRIVED, metadata);
});
this._transmuxer.on(TransmuxingEvents.SCRIPTDATA_ARRIVED, (data) => {
this._emitter.emit(PlayerEvents.SCRIPTDATA_ARRIVED, data);
});
this._transmuxer.on(TransmuxingEvents.STATISTICS_INFO, (statInfo) => {
this._statisticsInfo = this._fillStatisticsInfo(statInfo);
this._emitter.emit(PlayerEvents.STATISTICS_INFO, Object.assign({}, this._statisticsInfo));
});
this._transmuxer.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, (milliseconds) => {
if (this._mediaElement && !this._config.accurateSeek) {
this._requestSetTime = true;
this._mediaElement.currentTime = milliseconds / 1000;
}
});
this._transmuxer.open();
}
unload() {
if (this._mediaElement) {
this._mediaElement.pause();
}
if (this._msectl) {
this._msectl.seek(0);
}
if (this._transmuxer) {
this._transmuxer.close();
this._transmuxer.destroy();
this._transmuxer = null;
}
}
play() {
return this._mediaElement.play();
}
pause() {
this._mediaElement.pause();
}
get type() {
return this._type;
}
get buffered() {
return this._mediaElement.buffered;
}
get duration() {
return this._mediaElement.duration;
}
get volume() {
return this._mediaElement.volume;
}
set volume(value) {
this._mediaElement.volume = value;
}
get muted() {
return this._mediaElement.muted;
}
set muted(muted) {
this._mediaElement.muted = muted;
}
get currentTime() {
if (this._mediaElement) {
return this._mediaElement.currentTime;
}
return 0;
}
set currentTime(seconds) {
if (this._mediaElement) {
this._internalSeek(seconds);
} else {
this._pendingSeekTime = seconds;
}
}
get mediaInfo() {
return Object.assign({}, this._mediaInfo);
}
get statisticsInfo() {
if (this._statisticsInfo == null) {
this._statisticsInfo = {};
}
this._statisticsInfo = this._fillStatisticsInfo(this._statisticsInfo);
return Object.assign({}, this._statisticsInfo);
}
_fillStatisticsInfo(statInfo) {
statInfo.playerType = this._type;
if (!(this._mediaElement instanceof HTMLVideoElement)) {
return statInfo;
}
let hasQualityInfo = true;
let decoded = 0;
let dropped = 0;
if (this._mediaElement.getVideoPlaybackQuality) {
let quality = this._mediaElement.getVideoPlaybackQuality();
decoded = quality.totalVideoFrames;
dropped = quality.droppedVideoFrames;
} else if (this._mediaElement.webkitDecodedFrameCount != undefined) {
decoded = this._mediaElement.webkitDecodedFrameCount;
dropped = this._mediaElement.webkitDroppedFrameCount;
} else {
hasQualityInfo = false;
}
if (hasQualityInfo) {
statInfo.decodedFrames = decoded;
statInfo.droppedFrames = dropped;
}
return statInfo;
}
_onmseUpdateEnd() {
if (!this._config.lazyLoad || this._config.isLive) {
return;
}
let buffered = this._mediaElement.buffered;
let currentTime = this._mediaElement.currentTime;
let currentRangeStart = 0;
let currentRangeEnd = 0;
for (let i = 0; i < buffered.length; i++) {
let start = buffered.start(i);
let end = buffered.end(i);
if (start <= currentTime && currentTime < end) {
currentRangeStart = start;
currentRangeEnd = end;
break;
}
}
if (currentRangeEnd >= currentTime + this._config.lazyLoadMaxDuration && this._progressChecker == null) {
Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task');
this._suspendTransmuxer();
}
}
_onmseBufferFull() {
Log.v(this.TAG, 'MSE SourceBuffer is full, suspend transmuxing task');
if (this._progressChecker == null) {
this._suspendTransmuxer();
}
}
_suspendTransmuxer() {
if (this._transmuxer) {
this._transmuxer.pause();
if (this._progressChecker == null) {
this._progressChecker = window.setInterval(this._checkProgressAndResume.bind(this), 1000);
}
}
}
_checkProgressAndResume() {
let currentTime = this._mediaElement.currentTime;
let buffered = this._mediaElement.buffered;
let needResume = false;
for (let i = 0; i < buffered.length; i++) {
let from = buffered.start(i);
let to = buffered.end(i);
if (currentTime >= from && currentTime < to) {
if (currentTime >= to - this._config.lazyLoadRecoverDuration) {
needResume = true;
}
break;
}
}
if (needResume) {
window.clearInterval(this._progressChecker);
this._progressChecker = null;
if (needResume) {
Log.v(this.TAG, 'Continue loading from paused position');
this._transmuxer.resume();
}
}
}
_isTimepointBuffered(seconds) {
let buffered = this._mediaElement.buffered;
for (let i = 0; i < buffered.length; i++) {
let from = buffered.start(i);
let to = buffered.end(i);
if (seconds >= from && seconds < to) {
return true;
}
}
return false;
}
_internalSeek(seconds) {
let directSeek = this._isTimepointBuffered(seconds);
let directSeekBegin = false;
let directSeekBeginTime = 0;
if (seconds < 1.0 && this._mediaElement.buffered.length > 0) {
let videoBeginTime = this._mediaElement.buffered.start(0);
if ((videoBeginTime < 1.0 && seconds < videoBeginTime) || Browser.safari) {
directSeekBegin = true;
// also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid
directSeekBeginTime = Browser.safari ? 0.1 : videoBeginTime;
}
}
if (directSeekBegin) { // seek to video begin, set currentTime directly if beginPTS buffered
this._requestSetTime = true;
this._mediaElement.currentTime = directSeekBeginTime;
} else if (directSeek) { // buffered position
if (!this._alwaysSeekKeyframe) {
this._requestSetTime = true;
this._mediaElement.currentTime = seconds;
} else {
let idr = this._msectl.getNearestKeyframe(Math.floor(seconds * 1000));
this._requestSetTime = true;
if (idr != null) {
this._mediaElement.currentTime = idr.dts / 1000;
} else {
this._mediaElement.currentTime = seconds;
}
}
if (this._progressChecker != null) {
this._checkProgressAndResume();
}
} else {
if (this._progressChecker != null) {
window.clearInterval(this._progressChecker);
this._progressChecker = null;
}
this._msectl.seek(seconds);
this._transmuxer.seek(Math.floor(seconds * 1000)); // in milliseconds
// no need to set mediaElement.currentTime if non-accurateSeek,
// just wait for the recommend_seekpoint callback
if (this._config.accurateSeek) {
this._requestSetTime = true;
this._mediaElement.currentTime = seconds;
}
}
}
_checkAndApplyUnbufferedSeekpoint() {
if (this._seekpointRecord) {
if (this._seekpointRecord.recordTime <= this._now() - 100) {
let target = this._mediaElement.currentTime;
this._seekpointRecord = null;
if (!this._isTimepointBuffered(target)) {
if (this._progressChecker != null) {
window.clearTimeout(this._progressChecker);
this._progressChecker = null;
}
// .currentTime is consists with .buffered timestamp
// Chrome/Edge use DTS, while FireFox/Safari use PTS
this._msectl.seek(target);
this._transmuxer.seek(Math.floor(target * 1000));
// set currentTime if accurateSeek, or wait for recommend_seekpoint callback
if (this._config.accurateSeek) {
this._requestSetTime = true;
this._mediaElement.currentTime = target;
}
}
} else {
window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50);
}
}
}
_checkAndResumeStuckPlayback(stalled) {
let media = this._mediaElement;
if (stalled || !this._receivedCanPlay || media.readyState < 2) { // HAVE_CURRENT_DATA
let buffered = media.buffered;
if (buffered.length > 0 && media.currentTime < buffered.start(0)) {
Log.w(this.TAG, `Playback seems stuck at ${media.currentTime}, seek to ${buffered.start(0)}`);
this._requestSetTime = true;
this._mediaElement.currentTime = buffered.start(0);
this._mediaElement.removeEventListener('progress', this.e.onvProgress);
}
} else {
// Playback didn't stuck, remove progress event listener
this._mediaElement.removeEventListener('progress', this.e.onvProgress);
}
}
_onvLoadedMetadata(e) {
if (this._pendingSeekTime != null) {
this._mediaElement.currentTime = this._pendingSeekTime;
this._pendingSeekTime = null;
}
}
_onvSeeking(e) { // handle seeking request from browser's progress bar
let target = this._mediaElement.currentTime;
let buffered = this._mediaElement.buffered;
if (this._requestSetTime) {
this._requestSetTime = false;
return;
}
if (target < 1.0 && buffered.length > 0) {
// seek to video begin, set currentTime directly if beginPTS buffered
let videoBeginTime = buffered.start(0);
if ((videoBeginTime < 1.0 && target < videoBeginTime) || Browser.safari) {
this._requestSetTime = true;
// also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid
this._mediaElement.currentTime = Browser.safari ? 0.1 : videoBeginTime;
return;
}
}
if (this._isTimepointBuffered(target)) {
if (this._alwaysSeekKeyframe) {
let idr = this._msectl.getNearestKeyframe(Math.floor(target * 1000));
if (idr != null) {
this._requestSetTime = true;
this._mediaElement.currentTime = idr.dts / 1000;
}
}
if (this._progressChecker != null) {
this._checkProgressAndResume();
}
return;
}
this._seekpointRecord = {
seekPoint: target,
recordTime: this._now()
};
window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50);
}
_onvCanPlay(e) {
this._receivedCanPlay = true;
this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay);
}
_onvStalled(e) {
this._checkAndResumeStuckPlayback(true);
}
_onvProgress(e) {
this._checkAndResumeStuckPlayback();
}
}
export default FlvPlayer;

256
node_modules/flv.js/src/player/native-player.js generated vendored Normal file
View File

@ -0,0 +1,256 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import EventEmitter from 'events';
import PlayerEvents from './player-events.js';
import {createDefaultConfig} from '../config.js';
import {InvalidArgumentException, IllegalStateException} from '../utils/exception.js';
// Player wrapper for browser's native player (HTMLVideoElement) without MediaSource src.
class NativePlayer {
constructor(mediaDataSource, config) {
this.TAG = 'NativePlayer';
this._type = 'NativePlayer';
this._emitter = new EventEmitter();
this._config = createDefaultConfig();
if (typeof config === 'object') {
Object.assign(this._config, config);
}
if (mediaDataSource.type.toLowerCase() === 'flv') {
throw new InvalidArgumentException('NativePlayer does\'t support flv MediaDataSource input!');
}
if (mediaDataSource.hasOwnProperty('segments')) {
throw new InvalidArgumentException(`NativePlayer(${mediaDataSource.type}) doesn't support multipart playback!`);
}
this.e = {
onvLoadedMetadata: this._onvLoadedMetadata.bind(this)
};
this._pendingSeekTime = null;
this._statisticsReporter = null;
this._mediaDataSource = mediaDataSource;
this._mediaElement = null;
}
destroy() {
if (this._mediaElement) {
this.unload();
this.detachMediaElement();
}
this.e = null;
this._mediaDataSource = null;
this._emitter.removeAllListeners();
this._emitter = null;
}
on(event, listener) {
if (event === PlayerEvents.MEDIA_INFO) {
if (this._mediaElement != null && this._mediaElement.readyState !== 0) { // HAVE_NOTHING
Promise.resolve().then(() => {
this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo);
});
}
} else if (event === PlayerEvents.STATISTICS_INFO) {
if (this._mediaElement != null && this._mediaElement.readyState !== 0) {
Promise.resolve().then(() => {
this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo);
});
}
}
this._emitter.addListener(event, listener);
}
off(event, listener) {
this._emitter.removeListener(event, listener);
}
attachMediaElement(mediaElement) {
this._mediaElement = mediaElement;
mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata);
if (this._pendingSeekTime != null) {
try {
mediaElement.currentTime = this._pendingSeekTime;
this._pendingSeekTime = null;
} catch (e) {
// IE11 may throw InvalidStateError if readyState === 0
// Defer set currentTime operation after loadedmetadata
}
}
}
detachMediaElement() {
if (this._mediaElement) {
this._mediaElement.src = '';
this._mediaElement.removeAttribute('src');
this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata);
this._mediaElement = null;
}
if (this._statisticsReporter != null) {
window.clearInterval(this._statisticsReporter);
this._statisticsReporter = null;
}
}
load() {
if (!this._mediaElement) {
throw new IllegalStateException('HTMLMediaElement must be attached before load()!');
}
this._mediaElement.src = this._mediaDataSource.url;
if (this._mediaElement.readyState > 0) {
this._mediaElement.currentTime = 0;
}
this._mediaElement.preload = 'auto';
this._mediaElement.load();
this._statisticsReporter = window.setInterval(
this._reportStatisticsInfo.bind(this),
this._config.statisticsInfoReportInterval);
}
unload() {
if (this._mediaElement) {
this._mediaElement.src = '';
this._mediaElement.removeAttribute('src');
}
if (this._statisticsReporter != null) {
window.clearInterval(this._statisticsReporter);
this._statisticsReporter = null;
}
}
play() {
return this._mediaElement.play();
}
pause() {
this._mediaElement.pause();
}
get type() {
return this._type;
}
get buffered() {
return this._mediaElement.buffered;
}
get duration() {
return this._mediaElement.duration;
}
get volume() {
return this._mediaElement.volume;
}
set volume(value) {
this._mediaElement.volume = value;
}
get muted() {
return this._mediaElement.muted;
}
set muted(muted) {
this._mediaElement.muted = muted;
}
get currentTime() {
if (this._mediaElement) {
return this._mediaElement.currentTime;
}
return 0;
}
set currentTime(seconds) {
if (this._mediaElement) {
this._mediaElement.currentTime = seconds;
} else {
this._pendingSeekTime = seconds;
}
}
get mediaInfo() {
let mediaPrefix = (this._mediaElement instanceof HTMLAudioElement) ? 'audio/' : 'video/';
let info = {
mimeType: mediaPrefix + this._mediaDataSource.type
};
if (this._mediaElement) {
info.duration = Math.floor(this._mediaElement.duration * 1000);
if (this._mediaElement instanceof HTMLVideoElement) {
info.width = this._mediaElement.videoWidth;
info.height = this._mediaElement.videoHeight;
}
}
return info;
}
get statisticsInfo() {
let info = {
playerType: this._type,
url: this._mediaDataSource.url
};
if (!(this._mediaElement instanceof HTMLVideoElement)) {
return info;
}
let hasQualityInfo = true;
let decoded = 0;
let dropped = 0;
if (this._mediaElement.getVideoPlaybackQuality) {
let quality = this._mediaElement.getVideoPlaybackQuality();
decoded = quality.totalVideoFrames;
dropped = quality.droppedVideoFrames;
} else if (this._mediaElement.webkitDecodedFrameCount != undefined) {
decoded = this._mediaElement.webkitDecodedFrameCount;
dropped = this._mediaElement.webkitDroppedFrameCount;
} else {
hasQualityInfo = false;
}
if (hasQualityInfo) {
info.decodedFrames = decoded;
info.droppedFrames = dropped;
}
return info;
}
_onvLoadedMetadata(e) {
if (this._pendingSeekTime != null) {
this._mediaElement.currentTime = this._pendingSeekTime;
this._pendingSeekTime = null;
}
this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo);
}
_reportStatisticsInfo() {
this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo);
}
}
export default NativePlayer;

39
node_modules/flv.js/src/player/player-errors.js generated vendored Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {LoaderErrors} from '../io/loader.js';
import DemuxErrors from '../demux/demux-errors.js';
export const ErrorTypes = {
NETWORK_ERROR: 'NetworkError',
MEDIA_ERROR: 'MediaError',
OTHER_ERROR: 'OtherError'
};
export const ErrorDetails = {
NETWORK_EXCEPTION: LoaderErrors.EXCEPTION,
NETWORK_STATUS_CODE_INVALID: LoaderErrors.HTTP_STATUS_CODE_INVALID,
NETWORK_TIMEOUT: LoaderErrors.CONNECTING_TIMEOUT,
NETWORK_UNRECOVERABLE_EARLY_EOF: LoaderErrors.UNRECOVERABLE_EARLY_EOF,
MEDIA_MSE_ERROR: 'MediaMSEError',
MEDIA_FORMAT_ERROR: DemuxErrors.FORMAT_ERROR,
MEDIA_FORMAT_UNSUPPORTED: DemuxErrors.FORMAT_UNSUPPORTED,
MEDIA_CODEC_UNSUPPORTED: DemuxErrors.CODEC_UNSUPPORTED
};

29
node_modules/flv.js/src/player/player-events.js generated vendored Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const PlayerEvents = {
ERROR: 'error',
LOADING_COMPLETE: 'loading_complete',
RECOVERED_EARLY_EOF: 'recovered_early_eof',
MEDIA_INFO: 'media_info',
METADATA_ARRIVED: 'metadata_arrived',
SCRIPTDATA_ARRIVED: 'scriptdata_arrived',
STATISTICS_INFO: 'statistics_info'
};
export default PlayerEvents;

56
node_modules/flv.js/src/remux/aac-silent.js generated vendored Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* This file is modified from dailymotion's hls.js library (hls.js/src/helper/aac.js)
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class AAC {
static getSilentFrame(codec, channelCount) {
if (codec === 'mp4a.40.2') {
// handle LC-AAC
if (channelCount === 1) {
return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]);
} else if (channelCount === 2) {
return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]);
} else if (channelCount === 3) {
return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]);
} else if (channelCount === 4) {
return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]);
} else if (channelCount === 5) {
return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]);
} else if (channelCount === 6) {
return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x00, 0xb2, 0x00, 0x20, 0x08, 0xe0]);
}
} else {
// handle HE-AAC (mp4a.40.5 / mp4a.40.29)
if (channelCount === 1) {
// ffmpeg -y -f lavfi -i "aevalsrc=0:d=0.05" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac
return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x4e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x1c, 0x6, 0xf1, 0xc1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);
} else if (channelCount === 2) {
// ffmpeg -y -f lavfi -i "aevalsrc=0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac
return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);
} else if (channelCount === 3) {
// ffmpeg -y -f lavfi -i "aevalsrc=0|0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac
return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);
}
}
return null;
}
}
export default AAC;

569
node_modules/flv.js/src/remux/mp4-generator.js generated vendored Normal file
View File

@ -0,0 +1,569 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* This file is derived from dailymotion's hls.js library (hls.js/src/remux/mp4-generator.js)
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// MP4 boxes generator for ISO BMFF (ISO Base Media File Format, defined in ISO/IEC 14496-12)
class MP4 {
static init() {
MP4.types = {
avc1: [], avcC: [], btrt: [], dinf: [],
dref: [], esds: [], ftyp: [], hdlr: [],
mdat: [], mdhd: [], mdia: [], mfhd: [],
minf: [], moof: [], moov: [], mp4a: [],
mvex: [], mvhd: [], sdtp: [], stbl: [],
stco: [], stsc: [], stsd: [], stsz: [],
stts: [], tfdt: [], tfhd: [], traf: [],
trak: [], trun: [], trex: [], tkhd: [],
vmhd: [], smhd: [], '.mp3': []
};
for (let name in MP4.types) {
if (MP4.types.hasOwnProperty(name)) {
MP4.types[name] = [
name.charCodeAt(0),
name.charCodeAt(1),
name.charCodeAt(2),
name.charCodeAt(3)
];
}
}
let constants = MP4.constants = {};
constants.FTYP = new Uint8Array([
0x69, 0x73, 0x6F, 0x6D, // major_brand: isom
0x0, 0x0, 0x0, 0x1, // minor_version: 0x01
0x69, 0x73, 0x6F, 0x6D, // isom
0x61, 0x76, 0x63, 0x31 // avc1
]);
constants.STSD_PREFIX = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
0x00, 0x00, 0x00, 0x01 // entry_count
]);
constants.STTS = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
0x00, 0x00, 0x00, 0x00 // entry_count
]);
constants.STSC = constants.STCO = constants.STTS;
constants.STSZ = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
0x00, 0x00, 0x00, 0x00, // sample_size
0x00, 0x00, 0x00, 0x00 // sample_count
]);
constants.HDLR_VIDEO = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
0x00, 0x00, 0x00, 0x00, // pre_defined
0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x56, 0x69, 0x64, 0x65,
0x6F, 0x48, 0x61, 0x6E,
0x64, 0x6C, 0x65, 0x72, 0x00 // name: VideoHandler
]);
constants.HDLR_AUDIO = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
0x00, 0x00, 0x00, 0x00, // pre_defined
0x73, 0x6F, 0x75, 0x6E, // handler_type: 'soun'
0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x53, 0x6F, 0x75, 0x6E,
0x64, 0x48, 0x61, 0x6E,
0x64, 0x6C, 0x65, 0x72, 0x00 // name: SoundHandler
]);
constants.DREF = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
0x00, 0x00, 0x00, 0x01, // entry_count
0x00, 0x00, 0x00, 0x0C, // entry_size
0x75, 0x72, 0x6C, 0x20, // type 'url '
0x00, 0x00, 0x00, 0x01 // version(0) + flags
]);
// Sound media header
constants.SMHD = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
0x00, 0x00, 0x00, 0x00 // balance(2) + reserved(2)
]);
// video media header
constants.VMHD = new Uint8Array([
0x00, 0x00, 0x00, 0x01, // version(0) + flags
0x00, 0x00, // graphicsmode: 2 bytes
0x00, 0x00, 0x00, 0x00, // opcolor: 3 * 2 bytes
0x00, 0x00
]);
}
// Generate a box
static box(type) {
let size = 8;
let result = null;
let datas = Array.prototype.slice.call(arguments, 1);
let arrayCount = datas.length;
for (let i = 0; i < arrayCount; i++) {
size += datas[i].byteLength;
}
result = new Uint8Array(size);
result[0] = (size >>> 24) & 0xFF; // size
result[1] = (size >>> 16) & 0xFF;
result[2] = (size >>> 8) & 0xFF;
result[3] = (size) & 0xFF;
result.set(type, 4); // type
let offset = 8;
for (let i = 0; i < arrayCount; i++) { // data body
result.set(datas[i], offset);
offset += datas[i].byteLength;
}
return result;
}
// emit ftyp & moov
static generateInitSegment(meta) {
let ftyp = MP4.box(MP4.types.ftyp, MP4.constants.FTYP);
let moov = MP4.moov(meta);
let result = new Uint8Array(ftyp.byteLength + moov.byteLength);
result.set(ftyp, 0);
result.set(moov, ftyp.byteLength);
return result;
}
// Movie metadata box
static moov(meta) {
let mvhd = MP4.mvhd(meta.timescale, meta.duration);
let trak = MP4.trak(meta);
let mvex = MP4.mvex(meta);
return MP4.box(MP4.types.moov, mvhd, trak, mvex);
}
// Movie header box
static mvhd(timescale, duration) {
return MP4.box(MP4.types.mvhd, new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
0x00, 0x00, 0x00, 0x00, // creation_time
0x00, 0x00, 0x00, 0x00, // modification_time
(timescale >>> 24) & 0xFF, // timescale: 4 bytes
(timescale >>> 16) & 0xFF,
(timescale >>> 8) & 0xFF,
(timescale) & 0xFF,
(duration >>> 24) & 0xFF, // duration: 4 bytes
(duration >>> 16) & 0xFF,
(duration >>> 8) & 0xFF,
(duration) & 0xFF,
0x00, 0x01, 0x00, 0x00, // Preferred rate: 1.0
0x01, 0x00, 0x00, 0x00, // PreferredVolume(1.0, 2bytes) + reserved(2bytes)
0x00, 0x00, 0x00, 0x00, // reserved: 4 + 4 bytes
0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, // ----begin composition matrix----
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, // ----end composition matrix----
0x00, 0x00, 0x00, 0x00, // ----begin pre_defined 6 * 4 bytes----
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // ----end pre_defined 6 * 4 bytes----
0xFF, 0xFF, 0xFF, 0xFF // next_track_ID
]));
}
// Track box
static trak(meta) {
return MP4.box(MP4.types.trak, MP4.tkhd(meta), MP4.mdia(meta));
}
// Track header box
static tkhd(meta) {
let trackId = meta.id, duration = meta.duration;
let width = meta.presentWidth, height = meta.presentHeight;
return MP4.box(MP4.types.tkhd, new Uint8Array([
0x00, 0x00, 0x00, 0x07, // version(0) + flags
0x00, 0x00, 0x00, 0x00, // creation_time
0x00, 0x00, 0x00, 0x00, // modification_time
(trackId >>> 24) & 0xFF, // track_ID: 4 bytes
(trackId >>> 16) & 0xFF,
(trackId >>> 8) & 0xFF,
(trackId) & 0xFF,
0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes
(duration >>> 24) & 0xFF, // duration: 4 bytes
(duration >>> 16) & 0xFF,
(duration >>> 8) & 0xFF,
(duration) & 0xFF,
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // layer(2bytes) + alternate_group(2bytes)
0x00, 0x00, 0x00, 0x00, // volume(2bytes) + reserved(2bytes)
0x00, 0x01, 0x00, 0x00, // ----begin composition matrix----
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, // ----end composition matrix----
(width >>> 8) & 0xFF, // width and height
(width) & 0xFF,
0x00, 0x00,
(height >>> 8) & 0xFF,
(height) & 0xFF,
0x00, 0x00
]));
}
// Media Box
static mdia(meta) {
return MP4.box(MP4.types.mdia, MP4.mdhd(meta), MP4.hdlr(meta), MP4.minf(meta));
}
// Media header box
static mdhd(meta) {
let timescale = meta.timescale;
let duration = meta.duration;
return MP4.box(MP4.types.mdhd, new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
0x00, 0x00, 0x00, 0x00, // creation_time
0x00, 0x00, 0x00, 0x00, // modification_time
(timescale >>> 24) & 0xFF, // timescale: 4 bytes
(timescale >>> 16) & 0xFF,
(timescale >>> 8) & 0xFF,
(timescale) & 0xFF,
(duration >>> 24) & 0xFF, // duration: 4 bytes
(duration >>> 16) & 0xFF,
(duration >>> 8) & 0xFF,
(duration) & 0xFF,
0x55, 0xC4, // language: und (undetermined)
0x00, 0x00 // pre_defined = 0
]));
}
// Media handler reference box
static hdlr(meta) {
let data = null;
if (meta.type === 'audio') {
data = MP4.constants.HDLR_AUDIO;
} else {
data = MP4.constants.HDLR_VIDEO;
}
return MP4.box(MP4.types.hdlr, data);
}
// Media infomation box
static minf(meta) {
let xmhd = null;
if (meta.type === 'audio') {
xmhd = MP4.box(MP4.types.smhd, MP4.constants.SMHD);
} else {
xmhd = MP4.box(MP4.types.vmhd, MP4.constants.VMHD);
}
return MP4.box(MP4.types.minf, xmhd, MP4.dinf(), MP4.stbl(meta));
}
// Data infomation box
static dinf() {
let result = MP4.box(MP4.types.dinf,
MP4.box(MP4.types.dref, MP4.constants.DREF)
);
return result;
}
// Sample table box
static stbl(meta) {
let result = MP4.box(MP4.types.stbl, // type: stbl
MP4.stsd(meta), // Sample Description Table
MP4.box(MP4.types.stts, MP4.constants.STTS), // Time-To-Sample
MP4.box(MP4.types.stsc, MP4.constants.STSC), // Sample-To-Chunk
MP4.box(MP4.types.stsz, MP4.constants.STSZ), // Sample size
MP4.box(MP4.types.stco, MP4.constants.STCO) // Chunk offset
);
return result;
}
// Sample description box
static stsd(meta) {
if (meta.type === 'audio') {
if (meta.codec === 'mp3') {
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp3(meta));
}
// else: aac -> mp4a
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp4a(meta));
} else {
return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.avc1(meta));
}
}
static mp3(meta) {
let channelCount = meta.channelCount;
let sampleRate = meta.audioSampleRate;
let data = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // reserved(4)
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
0x00, 0x00, 0x00, 0x00,
0x00, channelCount, // channelCount(2)
0x00, 0x10, // sampleSize(2)
0x00, 0x00, 0x00, 0x00, // reserved(4)
(sampleRate >>> 8) & 0xFF, // Audio sample rate
(sampleRate) & 0xFF,
0x00, 0x00
]);
return MP4.box(MP4.types['.mp3'], data);
}
static mp4a(meta) {
let channelCount = meta.channelCount;
let sampleRate = meta.audioSampleRate;
let data = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // reserved(4)
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
0x00, 0x00, 0x00, 0x00,
0x00, channelCount, // channelCount(2)
0x00, 0x10, // sampleSize(2)
0x00, 0x00, 0x00, 0x00, // reserved(4)
(sampleRate >>> 8) & 0xFF, // Audio sample rate
(sampleRate) & 0xFF,
0x00, 0x00
]);
return MP4.box(MP4.types.mp4a, data, MP4.esds(meta));
}
static esds(meta) {
let config = meta.config || [];
let configSize = config.length;
let data = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version 0 + flags
0x03, // descriptor_type
0x17 + configSize, // length3
0x00, 0x01, // es_id
0x00, // stream_priority
0x04, // descriptor_type
0x0F + configSize, // length
0x40, // codec: mpeg4_audio
0x15, // stream_type: Audio
0x00, 0x00, 0x00, // buffer_size
0x00, 0x00, 0x00, 0x00, // maxBitrate
0x00, 0x00, 0x00, 0x00, // avgBitrate
0x05 // descriptor_type
].concat([
configSize
]).concat(
config
).concat([
0x06, 0x01, 0x02 // GASpecificConfig
]));
return MP4.box(MP4.types.esds, data);
}
static avc1(meta) {
let avcc = meta.avcc;
let width = meta.codecWidth, height = meta.codecHeight;
let data = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // reserved(4)
0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
0x00, 0x00, 0x00, 0x00, // pre_defined(2) + reserved(2)
0x00, 0x00, 0x00, 0x00, // pre_defined: 3 * 4 bytes
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
(width >>> 8) & 0xFF, // width: 2 bytes
(width) & 0xFF,
(height >>> 8) & 0xFF, // height: 2 bytes
(height) & 0xFF,
0x00, 0x48, 0x00, 0x00, // horizresolution: 4 bytes
0x00, 0x48, 0x00, 0x00, // vertresolution: 4 bytes
0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes
0x00, 0x01, // frame_count
0x0A, // strlen
0x78, 0x71, 0x71, 0x2F, // compressorname: 32 bytes
0x66, 0x6C, 0x76, 0x2E,
0x6A, 0x73, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
0x00, 0x18, // depth
0xFF, 0xFF // pre_defined = -1
]);
return MP4.box(MP4.types.avc1, data, MP4.box(MP4.types.avcC, avcc));
}
// Movie Extends box
static mvex(meta) {
return MP4.box(MP4.types.mvex, MP4.trex(meta));
}
// Track Extends box
static trex(meta) {
let trackId = meta.id;
let data = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) + flags
(trackId >>> 24) & 0xFF, // track_ID
(trackId >>> 16) & 0xFF,
(trackId >>> 8) & 0xFF,
(trackId) & 0xFF,
0x00, 0x00, 0x00, 0x01, // default_sample_description_index
0x00, 0x00, 0x00, 0x00, // default_sample_duration
0x00, 0x00, 0x00, 0x00, // default_sample_size
0x00, 0x01, 0x00, 0x01 // default_sample_flags
]);
return MP4.box(MP4.types.trex, data);
}
// Movie fragment box
static moof(track, baseMediaDecodeTime) {
return MP4.box(MP4.types.moof, MP4.mfhd(track.sequenceNumber), MP4.traf(track, baseMediaDecodeTime));
}
static mfhd(sequenceNumber) {
let data = new Uint8Array([
0x00, 0x00, 0x00, 0x00,
(sequenceNumber >>> 24) & 0xFF, // sequence_number: int32
(sequenceNumber >>> 16) & 0xFF,
(sequenceNumber >>> 8) & 0xFF,
(sequenceNumber) & 0xFF
]);
return MP4.box(MP4.types.mfhd, data);
}
// Track fragment box
static traf(track, baseMediaDecodeTime) {
let trackId = track.id;
// Track fragment header box
let tfhd = MP4.box(MP4.types.tfhd, new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) & flags
(trackId >>> 24) & 0xFF, // track_ID
(trackId >>> 16) & 0xFF,
(trackId >>> 8) & 0xFF,
(trackId) & 0xFF
]));
// Track Fragment Decode Time
let tfdt = MP4.box(MP4.types.tfdt, new Uint8Array([
0x00, 0x00, 0x00, 0x00, // version(0) & flags
(baseMediaDecodeTime >>> 24) & 0xFF, // baseMediaDecodeTime: int32
(baseMediaDecodeTime >>> 16) & 0xFF,
(baseMediaDecodeTime >>> 8) & 0xFF,
(baseMediaDecodeTime) & 0xFF
]));
let sdtp = MP4.sdtp(track);
let trun = MP4.trun(track, sdtp.byteLength + 16 + 16 + 8 + 16 + 8 + 8);
return MP4.box(MP4.types.traf, tfhd, tfdt, trun, sdtp);
}
// Sample Dependency Type box
static sdtp(track) {
let samples = track.samples || [];
let sampleCount = samples.length;
let data = new Uint8Array(4 + sampleCount);
// 0~4 bytes: version(0) & flags
for (let i = 0; i < sampleCount; i++) {
let flags = samples[i].flags;
data[i + 4] = (flags.isLeading << 6) // is_leading: 2 (bit)
| (flags.dependsOn << 4) // sample_depends_on
| (flags.isDependedOn << 2) // sample_is_depended_on
| (flags.hasRedundancy); // sample_has_redundancy
}
return MP4.box(MP4.types.sdtp, data);
}
// Track fragment run box
static trun(track, offset) {
let samples = track.samples || [];
let sampleCount = samples.length;
let dataSize = 12 + 16 * sampleCount;
let data = new Uint8Array(dataSize);
offset += 8 + dataSize;
data.set([
0x00, 0x00, 0x0F, 0x01, // version(0) & flags
(sampleCount >>> 24) & 0xFF, // sample_count
(sampleCount >>> 16) & 0xFF,
(sampleCount >>> 8) & 0xFF,
(sampleCount) & 0xFF,
(offset >>> 24) & 0xFF, // data_offset
(offset >>> 16) & 0xFF,
(offset >>> 8) & 0xFF,
(offset) & 0xFF
], 0);
for (let i = 0; i < sampleCount; i++) {
let duration = samples[i].duration;
let size = samples[i].size;
let flags = samples[i].flags;
let cts = samples[i].cts;
data.set([
(duration >>> 24) & 0xFF, // sample_duration
(duration >>> 16) & 0xFF,
(duration >>> 8) & 0xFF,
(duration) & 0xFF,
(size >>> 24) & 0xFF, // sample_size
(size >>> 16) & 0xFF,
(size >>> 8) & 0xFF,
(size) & 0xFF,
(flags.isLeading << 2) | flags.dependsOn, // sample_flags
(flags.isDependedOn << 6) | (flags.hasRedundancy << 4) | flags.isNonSync,
0x00, 0x00, // sample_degradation_priority
(cts >>> 24) & 0xFF, // sample_composition_time_offset
(cts >>> 16) & 0xFF,
(cts >>> 8) & 0xFF,
(cts) & 0xFF
], 12 + 16 * i);
}
return MP4.box(MP4.types.trun, data);
}
static mdat(data) {
return MP4.box(MP4.types.mdat, data);
}
}
MP4.init();
export default MP4;

767
node_modules/flv.js/src/remux/mp4-remuxer.js generated vendored Normal file
View File

@ -0,0 +1,767 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Log from '../utils/logger.js';
import MP4 from './mp4-generator.js';
import AAC from './aac-silent.js';
import Browser from '../utils/browser.js';
import { SampleInfo, MediaSegmentInfo, MediaSegmentInfoList } from '../core/media-segment-info.js';
import { IllegalStateException } from '../utils/exception.js';
// Fragmented mp4 remuxer
class MP4Remuxer {
constructor(config) {
this.TAG = 'MP4Remuxer';
this._config = config;
this._isLive = (config.isLive === true) ? true : false;
this._dtsBase = -1;
this._dtsBaseInited = false;
this._audioDtsBase = Infinity;
this._videoDtsBase = Infinity;
this._audioNextDts = undefined;
this._videoNextDts = undefined;
this._audioStashedLastSample = null;
this._videoStashedLastSample = null;
this._audioMeta = null;
this._videoMeta = null;
this._audioSegmentInfoList = new MediaSegmentInfoList('audio');
this._videoSegmentInfoList = new MediaSegmentInfoList('video');
this._onInitSegment = null;
this._onMediaSegment = null;
// Workaround for chrome < 50: Always force first sample as a Random Access Point in media segment
// see https://bugs.chromium.org/p/chromium/issues/detail?id=229412
this._forceFirstIDR = (Browser.chrome &&
(Browser.version.major < 50 ||
(Browser.version.major === 50 && Browser.version.build < 2661))) ? true : false;
// Workaround for IE11/Edge: Fill silent aac frame after keyframe-seeking
// Make audio beginDts equals with video beginDts, in order to fix seek freeze
this._fillSilentAfterSeek = (Browser.msedge || Browser.msie);
// While only FireFox supports 'audio/mp4, codecs="mp3"', use 'audio/mpeg' for chrome, safari, ...
this._mp3UseMpegAudio = !Browser.firefox;
this._fillAudioTimestampGap = this._config.fixAudioTimestampGap;
}
destroy() {
this._dtsBase = -1;
this._dtsBaseInited = false;
this._audioMeta = null;
this._videoMeta = null;
this._audioSegmentInfoList.clear();
this._audioSegmentInfoList = null;
this._videoSegmentInfoList.clear();
this._videoSegmentInfoList = null;
this._onInitSegment = null;
this._onMediaSegment = null;
}
bindDataSource(producer) {
producer.onDataAvailable = this.remux.bind(this);
producer.onTrackMetadata = this._onTrackMetadataReceived.bind(this);
return this;
}
/* prototype: function onInitSegment(type: string, initSegment: ArrayBuffer): void
InitSegment: {
type: string,
data: ArrayBuffer,
codec: string,
container: string
}
*/
get onInitSegment() {
return this._onInitSegment;
}
set onInitSegment(callback) {
this._onInitSegment = callback;
}
/* prototype: function onMediaSegment(type: string, mediaSegment: MediaSegment): void
MediaSegment: {
type: string,
data: ArrayBuffer,
sampleCount: int32
info: MediaSegmentInfo
}
*/
get onMediaSegment() {
return this._onMediaSegment;
}
set onMediaSegment(callback) {
this._onMediaSegment = callback;
}
insertDiscontinuity() {
this._audioNextDts = this._videoNextDts = undefined;
}
seek(originalDts) {
this._audioStashedLastSample = null;
this._videoStashedLastSample = null;
this._videoSegmentInfoList.clear();
this._audioSegmentInfoList.clear();
}
remux(audioTrack, videoTrack) {
if (!this._onMediaSegment) {
throw new IllegalStateException('MP4Remuxer: onMediaSegment callback must be specificed!');
}
if (!this._dtsBaseInited) {
this._calculateDtsBase(audioTrack, videoTrack);
}
this._remuxVideo(videoTrack);
this._remuxAudio(audioTrack);
}
_onTrackMetadataReceived(type, metadata) {
let metabox = null;
let container = 'mp4';
let codec = metadata.codec;
if (type === 'audio') {
this._audioMeta = metadata;
if (metadata.codec === 'mp3' && this._mp3UseMpegAudio) {
// 'audio/mpeg' for MP3 audio track
container = 'mpeg';
codec = '';
metabox = new Uint8Array();
} else {
// 'audio/mp4, codecs="codec"'
metabox = MP4.generateInitSegment(metadata);
}
} else if (type === 'video') {
this._videoMeta = metadata;
metabox = MP4.generateInitSegment(metadata);
} else {
return;
}
// dispatch metabox (Initialization Segment)
if (!this._onInitSegment) {
throw new IllegalStateException('MP4Remuxer: onInitSegment callback must be specified!');
}
this._onInitSegment(type, {
type: type,
data: metabox.buffer,
codec: codec,
container: `${type}/${container}`,
mediaDuration: metadata.duration // in timescale 1000 (milliseconds)
});
}
_calculateDtsBase(audioTrack, videoTrack) {
if (this._dtsBaseInited) {
return;
}
if (audioTrack.samples && audioTrack.samples.length) {
this._audioDtsBase = audioTrack.samples[0].dts;
}
if (videoTrack.samples && videoTrack.samples.length) {
this._videoDtsBase = videoTrack.samples[0].dts;
}
this._dtsBase = Math.min(this._audioDtsBase, this._videoDtsBase);
this._dtsBaseInited = true;
}
flushStashedSamples() {
let videoSample = this._videoStashedLastSample;
let audioSample = this._audioStashedLastSample;
let videoTrack = {
type: 'video',
id: 1,
sequenceNumber: 0,
samples: [],
length: 0
};
if (videoSample != null) {
videoTrack.samples.push(videoSample);
videoTrack.length = videoSample.length;
}
let audioTrack = {
type: 'audio',
id: 2,
sequenceNumber: 0,
samples: [],
length: 0
};
if (audioSample != null) {
audioTrack.samples.push(audioSample);
audioTrack.length = audioSample.length;
}
this._videoStashedLastSample = null;
this._audioStashedLastSample = null;
this._remuxVideo(videoTrack, true);
this._remuxAudio(audioTrack, true);
}
_remuxAudio(audioTrack, force) {
if (this._audioMeta == null) {
return;
}
let track = audioTrack;
let samples = track.samples;
let dtsCorrection = undefined;
let firstDts = -1, lastDts = -1, lastPts = -1;
let refSampleDuration = this._audioMeta.refSampleDuration;
let mpegRawTrack = this._audioMeta.codec === 'mp3' && this._mp3UseMpegAudio;
let firstSegmentAfterSeek = this._dtsBaseInited && this._audioNextDts === undefined;
let insertPrefixSilentFrame = false;
if (!samples || samples.length === 0) {
return;
}
if (samples.length === 1 && !force) {
// If [sample count in current batch] === 1 && (force != true)
// Ignore and keep in demuxer's queue
return;
} // else if (force === true) do remux
let offset = 0;
let mdatbox = null;
let mdatBytes = 0;
// calculate initial mdat size
if (mpegRawTrack) {
// for raw mpeg buffer
offset = 0;
mdatBytes = track.length;
} else {
// for fmp4 mdat box
offset = 8; // size + type
mdatBytes = 8 + track.length;
}
let lastSample = null;
// Pop the lastSample and waiting for stash
if (samples.length > 1) {
lastSample = samples.pop();
mdatBytes -= lastSample.length;
}
// Insert [stashed lastSample in the previous batch] to the front
if (this._audioStashedLastSample != null) {
let sample = this._audioStashedLastSample;
this._audioStashedLastSample = null;
samples.unshift(sample);
mdatBytes += sample.length;
}
// Stash the lastSample of current batch, waiting for next batch
if (lastSample != null) {
this._audioStashedLastSample = lastSample;
}
let firstSampleOriginalDts = samples[0].dts - this._dtsBase;
// calculate dtsCorrection
if (this._audioNextDts) {
dtsCorrection = firstSampleOriginalDts - this._audioNextDts;
} else { // this._audioNextDts == undefined
if (this._audioSegmentInfoList.isEmpty()) {
dtsCorrection = 0;
if (this._fillSilentAfterSeek && !this._videoSegmentInfoList.isEmpty()) {
if (this._audioMeta.originalCodec !== 'mp3') {
insertPrefixSilentFrame = true;
}
}
} else {
let lastSample = this._audioSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts);
if (lastSample != null) {
let distance = (firstSampleOriginalDts - (lastSample.originalDts + lastSample.duration));
if (distance <= 3) {
distance = 0;
}
let expectedDts = lastSample.dts + lastSample.duration + distance;
dtsCorrection = firstSampleOriginalDts - expectedDts;
} else { // lastSample == null, cannot found
dtsCorrection = 0;
}
}
}
if (insertPrefixSilentFrame) {
// align audio segment beginDts to match with current video segment's beginDts
let firstSampleDts = firstSampleOriginalDts - dtsCorrection;
let videoSegment = this._videoSegmentInfoList.getLastSegmentBefore(firstSampleOriginalDts);
if (videoSegment != null && videoSegment.beginDts < firstSampleDts) {
let silentUnit = AAC.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount);
if (silentUnit) {
let dts = videoSegment.beginDts;
let silentFrameDuration = firstSampleDts - videoSegment.beginDts;
Log.v(this.TAG, `InsertPrefixSilentAudio: dts: ${dts}, duration: ${silentFrameDuration}`);
samples.unshift({ unit: silentUnit, dts: dts, pts: dts });
mdatBytes += silentUnit.byteLength;
} // silentUnit == null: Cannot generate, skip
} else {
insertPrefixSilentFrame = false;
}
}
let mp4Samples = [];
// Correct dts for each sample, and calculate sample duration. Then output to mp4Samples
for (let i = 0; i < samples.length; i++) {
let sample = samples[i];
let unit = sample.unit;
let originalDts = sample.dts - this._dtsBase;
let dts = originalDts;
let needFillSilentFrames = false;
let silentFrames = null;
let sampleDuration = 0;
if (originalDts < -0.001) {
continue; //pass the first sample with the invalid dts
}
if (this._audioMeta.codec !== 'mp3') {
// for AAC codec, we need to keep dts increase based on refSampleDuration
let curRefDts = originalDts;
const maxAudioFramesDrift = 3;
if (this._audioNextDts) {
curRefDts = this._audioNextDts;
}
dtsCorrection = originalDts - curRefDts;
if (dtsCorrection <= -maxAudioFramesDrift * refSampleDuration) {
// If we're overlapping by more than maxAudioFramesDrift number of frame, drop this sample
Log.w(this.TAG, `Dropping 1 audio frame (originalDts: ${originalDts} ms ,curRefDts: ${curRefDts} ms) due to dtsCorrection: ${dtsCorrection} ms overlap.`);
continue;
}
else if (dtsCorrection >= maxAudioFramesDrift * refSampleDuration && this._fillAudioTimestampGap && !Browser.safari) {
// Silent frame generation, if large timestamp gap detected && config.fixAudioTimestampGap
needFillSilentFrames = true;
// We need to insert silent frames to fill timestamp gap
let frameCount = Math.floor(dtsCorrection / refSampleDuration);
Log.w(this.TAG, 'Large audio timestamp gap detected, may cause AV sync to drift. ' +
'Silent frames will be generated to avoid unsync.\n' +
`originalDts: ${originalDts} ms, curRefDts: ${curRefDts} ms, ` +
`dtsCorrection: ${Math.round(dtsCorrection)} ms, generate: ${frameCount} frames`);
dts = Math.floor(curRefDts);
sampleDuration = Math.floor(curRefDts + refSampleDuration) - dts;
let silentUnit = AAC.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount);
if (silentUnit == null) {
Log.w(this.TAG, 'Unable to generate silent frame for ' +
`${this._audioMeta.originalCodec} with ${this._audioMeta.channelCount} channels, repeat last frame`);
// Repeat last frame
silentUnit = unit;
}
silentFrames = [];
for (let j = 0; j < frameCount; j++) {
curRefDts = curRefDts + refSampleDuration;
let intDts = Math.floor(curRefDts); // change to integer
let intDuration = Math.floor(curRefDts + refSampleDuration) - intDts;
let frame = {
dts: intDts,
pts: intDts,
cts: 0,
unit: silentUnit,
size: silentUnit.byteLength,
duration: intDuration, // wait for next sample
originalDts: originalDts,
flags: {
isLeading: 0,
dependsOn: 1,
isDependedOn: 0,
hasRedundancy: 0
}
};
silentFrames.push(frame);
mdatBytes += frame.size;;
}
this._audioNextDts = curRefDts + refSampleDuration;
} else {
dts = Math.floor(curRefDts);
sampleDuration = Math.floor(curRefDts + refSampleDuration) - dts;
this._audioNextDts = curRefDts + refSampleDuration;
}
} else {
// keep the original dts calculate algorithm for mp3
dts = originalDts - dtsCorrection;
if (i !== samples.length - 1) {
let nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection;
sampleDuration = nextDts - dts;
} else { // the last sample
if (lastSample != null) { // use stashed sample's dts to calculate sample duration
let nextDts = lastSample.dts - this._dtsBase - dtsCorrection;
sampleDuration = nextDts - dts;
} else if (mp4Samples.length >= 1) { // use second last sample duration
sampleDuration = mp4Samples[mp4Samples.length - 1].duration;
} else { // the only one sample, use reference sample duration
sampleDuration = Math.floor(refSampleDuration);
}
}
this._audioNextDts = dts + sampleDuration;
}
if (firstDts === -1) {
firstDts = dts;
}
mp4Samples.push({
dts: dts,
pts: dts,
cts: 0,
unit: sample.unit,
size: sample.unit.byteLength,
duration: sampleDuration,
originalDts: originalDts,
flags: {
isLeading: 0,
dependsOn: 1,
isDependedOn: 0,
hasRedundancy: 0
}
});
if (needFillSilentFrames) {
// Silent frames should be inserted after wrong-duration frame
mp4Samples.push.apply(mp4Samples, silentFrames);
}
}
if (mp4Samples.length === 0) {
//no samples need to remux
track.samples = [];
track.length = 0;
return;
}
// allocate mdatbox
if (mpegRawTrack) {
// allocate for raw mpeg buffer
mdatbox = new Uint8Array(mdatBytes);
} else {
// allocate for fmp4 mdat box
mdatbox = new Uint8Array(mdatBytes);
// size field
mdatbox[0] = (mdatBytes >>> 24) & 0xFF;
mdatbox[1] = (mdatBytes >>> 16) & 0xFF;
mdatbox[2] = (mdatBytes >>> 8) & 0xFF;
mdatbox[3] = (mdatBytes) & 0xFF;
// type field (fourCC)
mdatbox.set(MP4.types.mdat, 4);
}
// Write samples into mdatbox
for (let i = 0; i < mp4Samples.length; i++) {
let unit = mp4Samples[i].unit;
mdatbox.set(unit, offset);
offset += unit.byteLength;
}
let latest = mp4Samples[mp4Samples.length - 1];
lastDts = latest.dts + latest.duration;
//this._audioNextDts = lastDts;
// fill media segment info & add to info list
let info = new MediaSegmentInfo();
info.beginDts = firstDts;
info.endDts = lastDts;
info.beginPts = firstDts;
info.endPts = lastDts;
info.originalBeginDts = mp4Samples[0].originalDts;
info.originalEndDts = latest.originalDts + latest.duration;
info.firstSample = new SampleInfo(mp4Samples[0].dts,
mp4Samples[0].pts,
mp4Samples[0].duration,
mp4Samples[0].originalDts,
false);
info.lastSample = new SampleInfo(latest.dts,
latest.pts,
latest.duration,
latest.originalDts,
false);
if (!this._isLive) {
this._audioSegmentInfoList.append(info);
}
track.samples = mp4Samples;
track.sequenceNumber++;
let moofbox = null;
if (mpegRawTrack) {
// Generate empty buffer, because useless for raw mpeg
moofbox = new Uint8Array();
} else {
// Generate moof for fmp4 segment
moofbox = MP4.moof(track, firstDts);
}
track.samples = [];
track.length = 0;
let segment = {
type: 'audio',
data: this._mergeBoxes(moofbox, mdatbox).buffer,
sampleCount: mp4Samples.length,
info: info
};
if (mpegRawTrack && firstSegmentAfterSeek) {
// For MPEG audio stream in MSE, if seeking occurred, before appending new buffer
// We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer.
segment.timestampOffset = firstDts;
}
this._onMediaSegment('audio', segment);
}
_remuxVideo(videoTrack, force) {
if (this._videoMeta == null) {
return;
}
let track = videoTrack;
let samples = track.samples;
let dtsCorrection = undefined;
let firstDts = -1, lastDts = -1;
let firstPts = -1, lastPts = -1;
if (!samples || samples.length === 0) {
return;
}
if (samples.length === 1 && !force) {
// If [sample count in current batch] === 1 && (force != true)
// Ignore and keep in demuxer's queue
return;
} // else if (force === true) do remux
let offset = 8;
let mdatbox = null;
let mdatBytes = 8 + videoTrack.length;
let lastSample = null;
// Pop the lastSample and waiting for stash
if (samples.length > 1) {
lastSample = samples.pop();
mdatBytes -= lastSample.length;
}
// Insert [stashed lastSample in the previous batch] to the front
if (this._videoStashedLastSample != null) {
let sample = this._videoStashedLastSample;
this._videoStashedLastSample = null;
samples.unshift(sample);
mdatBytes += sample.length;
}
// Stash the lastSample of current batch, waiting for next batch
if (lastSample != null) {
this._videoStashedLastSample = lastSample;
}
let firstSampleOriginalDts = samples[0].dts - this._dtsBase;
// calculate dtsCorrection
if (this._videoNextDts) {
dtsCorrection = firstSampleOriginalDts - this._videoNextDts;
} else { // this._videoNextDts == undefined
if (this._videoSegmentInfoList.isEmpty()) {
dtsCorrection = 0;
} else {
let lastSample = this._videoSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts);
if (lastSample != null) {
let distance = (firstSampleOriginalDts - (lastSample.originalDts + lastSample.duration));
if (distance <= 3) {
distance = 0;
}
let expectedDts = lastSample.dts + lastSample.duration + distance;
dtsCorrection = firstSampleOriginalDts - expectedDts;
} else { // lastSample == null, cannot found
dtsCorrection = 0;
}
}
}
let info = new MediaSegmentInfo();
let mp4Samples = [];
// Correct dts for each sample, and calculate sample duration. Then output to mp4Samples
for (let i = 0; i < samples.length; i++) {
let sample = samples[i];
let originalDts = sample.dts - this._dtsBase;
let isKeyframe = sample.isKeyframe;
let dts = originalDts - dtsCorrection;
let cts = sample.cts;
let pts = dts + cts;
if (firstDts === -1) {
firstDts = dts;
firstPts = pts;
}
let sampleDuration = 0;
if (i !== samples.length - 1) {
let nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection;
sampleDuration = nextDts - dts;
} else { // the last sample
if (lastSample != null) { // use stashed sample's dts to calculate sample duration
let nextDts = lastSample.dts - this._dtsBase - dtsCorrection;
sampleDuration = nextDts - dts;
} else if (mp4Samples.length >= 1) { // use second last sample duration
sampleDuration = mp4Samples[mp4Samples.length - 1].duration;
} else { // the only one sample, use reference sample duration
sampleDuration = Math.floor(this._videoMeta.refSampleDuration);
}
}
if (isKeyframe) {
let syncPoint = new SampleInfo(dts, pts, sampleDuration, sample.dts, true);
syncPoint.fileposition = sample.fileposition;
info.appendSyncPoint(syncPoint);
}
mp4Samples.push({
dts: dts,
pts: pts,
cts: cts,
units: sample.units,
size: sample.length,
isKeyframe: isKeyframe,
duration: sampleDuration,
originalDts: originalDts,
flags: {
isLeading: 0,
dependsOn: isKeyframe ? 2 : 1,
isDependedOn: isKeyframe ? 1 : 0,
hasRedundancy: 0,
isNonSync: isKeyframe ? 0 : 1
}
});
}
// allocate mdatbox
mdatbox = new Uint8Array(mdatBytes);
mdatbox[0] = (mdatBytes >>> 24) & 0xFF;
mdatbox[1] = (mdatBytes >>> 16) & 0xFF;
mdatbox[2] = (mdatBytes >>> 8) & 0xFF;
mdatbox[3] = (mdatBytes) & 0xFF;
mdatbox.set(MP4.types.mdat, 4);
// Write samples into mdatbox
for (let i = 0; i < mp4Samples.length; i++) {
let units = mp4Samples[i].units;
while (units.length) {
let unit = units.shift();
let data = unit.data;
mdatbox.set(data, offset);
offset += data.byteLength;
}
}
let latest = mp4Samples[mp4Samples.length - 1];
lastDts = latest.dts + latest.duration;
lastPts = latest.pts + latest.duration;
this._videoNextDts = lastDts;
// fill media segment info & add to info list
info.beginDts = firstDts;
info.endDts = lastDts;
info.beginPts = firstPts;
info.endPts = lastPts;
info.originalBeginDts = mp4Samples[0].originalDts;
info.originalEndDts = latest.originalDts + latest.duration;
info.firstSample = new SampleInfo(mp4Samples[0].dts,
mp4Samples[0].pts,
mp4Samples[0].duration,
mp4Samples[0].originalDts,
mp4Samples[0].isKeyframe);
info.lastSample = new SampleInfo(latest.dts,
latest.pts,
latest.duration,
latest.originalDts,
latest.isKeyframe);
if (!this._isLive) {
this._videoSegmentInfoList.append(info);
}
track.samples = mp4Samples;
track.sequenceNumber++;
// workaround for chrome < 50: force first sample as a random access point
// see https://bugs.chromium.org/p/chromium/issues/detail?id=229412
if (this._forceFirstIDR) {
let flags = mp4Samples[0].flags;
flags.dependsOn = 2;
flags.isNonSync = 0;
}
let moofbox = MP4.moof(track, firstDts);
track.samples = [];
track.length = 0;
this._onMediaSegment('video', {
type: 'video',
data: this._mergeBoxes(moofbox, mdatbox).buffer,
sampleCount: mp4Samples.length,
info: info
});
}
_mergeBoxes(moof, mdat) {
let result = new Uint8Array(moof.byteLength + mdat.byteLength);
result.set(moof, 0);
result.set(mdat, moof.byteLength);
return result;
}
}
export default MP4Remuxer;

128
node_modules/flv.js/src/utils/browser.js generated vendored Normal file
View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let Browser = {};
function detect() {
// modified from jquery-browser-plugin
let ua = self.navigator.userAgent.toLowerCase();
let match = /(edge)\/([\w.]+)/.exec(ua) ||
/(opr)[\/]([\w.]+)/.exec(ua) ||
/(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(iemobile)[\/]([\w.]+)/.exec(ua) ||
/(version)(applewebkit)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+).*(version)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf('trident') >= 0 && /(rv)(?::| )([\w.]+)/.exec(ua) ||
ua.indexOf('compatible') < 0 && /(firefox)[ \/]([\w.]+)/.exec(ua) ||
[];
let platform_match = /(ipad)/.exec(ua) ||
/(ipod)/.exec(ua) ||
/(windows phone)/.exec(ua) ||
/(iphone)/.exec(ua) ||
/(kindle)/.exec(ua) ||
/(android)/.exec(ua) ||
/(windows)/.exec(ua) ||
/(mac)/.exec(ua) ||
/(linux)/.exec(ua) ||
/(cros)/.exec(ua) ||
[];
let matched = {
browser: match[5] || match[3] || match[1] || '',
version: match[2] || match[4] || '0',
majorVersion: match[4] || match[2] || '0',
platform: platform_match[0] || ''
};
let browser = {};
if (matched.browser) {
browser[matched.browser] = true;
let versionArray = matched.majorVersion.split('.');
browser.version = {
major: parseInt(matched.majorVersion, 10),
string: matched.version
};
if (versionArray.length > 1) {
browser.version.minor = parseInt(versionArray[1], 10);
}
if (versionArray.length > 2) {
browser.version.build = parseInt(versionArray[2], 10);
}
}
if (matched.platform) {
browser[matched.platform] = true;
}
if (browser.chrome || browser.opr || browser.safari) {
browser.webkit = true;
}
// MSIE. IE11 has 'rv' identifer
if (browser.rv || browser.iemobile) {
if (browser.rv) {
delete browser.rv;
}
let msie = 'msie';
matched.browser = msie;
browser[msie] = true;
}
// Microsoft Edge
if (browser.edge) {
delete browser.edge;
let msedge = 'msedge';
matched.browser = msedge;
browser[msedge] = true;
}
// Opera 15+
if (browser.opr) {
let opera = 'opera';
matched.browser = opera;
browser[opera] = true;
}
// Stock android browsers are marked as Safari
if (browser.safari && browser.android) {
let android = 'android';
matched.browser = android;
browser[android] = true;
}
browser.name = matched.browser;
browser.platform = matched.platform;
for (let key in Browser) {
if (Browser.hasOwnProperty(key)) {
delete Browser[key];
}
}
Object.assign(Browser, browser);
}
detect();
export default Browser;

73
node_modules/flv.js/src/utils/exception.js generated vendored Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class RuntimeException {
constructor(message) {
this._message = message;
}
get name() {
return 'RuntimeException';
}
get message() {
return this._message;
}
toString() {
return this.name + ': ' + this.message;
}
}
export class IllegalStateException extends RuntimeException {
constructor(message) {
super(message);
}
get name() {
return 'IllegalStateException';
}
}
export class InvalidArgumentException extends RuntimeException {
constructor(message) {
super(message);
}
get name() {
return 'InvalidArgumentException';
}
}
export class NotImplementedException extends RuntimeException {
constructor(message) {
super(message);
}
get name() {
return 'NotImplementedException';
}
}

140
node_modules/flv.js/src/utils/logger.js generated vendored Normal file
View File

@ -0,0 +1,140 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import EventEmitter from 'events';
class Log {
static e(tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (Log.ENABLE_CALLBACK) {
Log.emitter.emit('log', 'error', str);
}
if (!Log.ENABLE_ERROR) {
return;
}
if (console.error) {
console.error(str);
} else if (console.warn) {
console.warn(str);
} else {
console.log(str);
}
}
static i(tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (Log.ENABLE_CALLBACK) {
Log.emitter.emit('log', 'info', str);
}
if (!Log.ENABLE_INFO) {
return;
}
if (console.info) {
console.info(str);
} else {
console.log(str);
}
}
static w(tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (Log.ENABLE_CALLBACK) {
Log.emitter.emit('log', 'warn', str);
}
if (!Log.ENABLE_WARN) {
return;
}
if (console.warn) {
console.warn(str);
} else {
console.log(str);
}
}
static d(tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (Log.ENABLE_CALLBACK) {
Log.emitter.emit('log', 'debug', str);
}
if (!Log.ENABLE_DEBUG) {
return;
}
if (console.debug) {
console.debug(str);
} else {
console.log(str);
}
}
static v(tag, msg) {
if (!tag || Log.FORCE_GLOBAL_TAG)
tag = Log.GLOBAL_TAG;
let str = `[${tag}] > ${msg}`;
if (Log.ENABLE_CALLBACK) {
Log.emitter.emit('log', 'verbose', str);
}
if (!Log.ENABLE_VERBOSE) {
return;
}
console.log(str);
}
}
Log.GLOBAL_TAG = 'flv.js';
Log.FORCE_GLOBAL_TAG = false;
Log.ENABLE_ERROR = true;
Log.ENABLE_INFO = true;
Log.ENABLE_WARN = true;
Log.ENABLE_DEBUG = true;
Log.ENABLE_VERBOSE = true;
Log.ENABLE_CALLBACK = false;
Log.emitter = new EventEmitter();
export default Log;

165
node_modules/flv.js/src/utils/logging-control.js generated vendored Normal file
View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import EventEmitter from 'events';
import Log from './logger.js';
class LoggingControl {
static get forceGlobalTag() {
return Log.FORCE_GLOBAL_TAG;
}
static set forceGlobalTag(enable) {
Log.FORCE_GLOBAL_TAG = enable;
LoggingControl._notifyChange();
}
static get globalTag() {
return Log.GLOBAL_TAG;
}
static set globalTag(tag) {
Log.GLOBAL_TAG = tag;
LoggingControl._notifyChange();
}
static get enableAll() {
return Log.ENABLE_VERBOSE
&& Log.ENABLE_DEBUG
&& Log.ENABLE_INFO
&& Log.ENABLE_WARN
&& Log.ENABLE_ERROR;
}
static set enableAll(enable) {
Log.ENABLE_VERBOSE = enable;
Log.ENABLE_DEBUG = enable;
Log.ENABLE_INFO = enable;
Log.ENABLE_WARN = enable;
Log.ENABLE_ERROR = enable;
LoggingControl._notifyChange();
}
static get enableDebug() {
return Log.ENABLE_DEBUG;
}
static set enableDebug(enable) {
Log.ENABLE_DEBUG = enable;
LoggingControl._notifyChange();
}
static get enableVerbose() {
return Log.ENABLE_VERBOSE;
}
static set enableVerbose(enable) {
Log.ENABLE_VERBOSE = enable;
LoggingControl._notifyChange();
}
static get enableInfo() {
return Log.ENABLE_INFO;
}
static set enableInfo(enable) {
Log.ENABLE_INFO = enable;
LoggingControl._notifyChange();
}
static get enableWarn() {
return Log.ENABLE_WARN;
}
static set enableWarn(enable) {
Log.ENABLE_WARN = enable;
LoggingControl._notifyChange();
}
static get enableError() {
return Log.ENABLE_ERROR;
}
static set enableError(enable) {
Log.ENABLE_ERROR = enable;
LoggingControl._notifyChange();
}
static getConfig() {
return {
globalTag: Log.GLOBAL_TAG,
forceGlobalTag: Log.FORCE_GLOBAL_TAG,
enableVerbose: Log.ENABLE_VERBOSE,
enableDebug: Log.ENABLE_DEBUG,
enableInfo: Log.ENABLE_INFO,
enableWarn: Log.ENABLE_WARN,
enableError: Log.ENABLE_ERROR,
enableCallback: Log.ENABLE_CALLBACK
};
}
static applyConfig(config) {
Log.GLOBAL_TAG = config.globalTag;
Log.FORCE_GLOBAL_TAG = config.forceGlobalTag;
Log.ENABLE_VERBOSE = config.enableVerbose;
Log.ENABLE_DEBUG = config.enableDebug;
Log.ENABLE_INFO = config.enableInfo;
Log.ENABLE_WARN = config.enableWarn;
Log.ENABLE_ERROR = config.enableError;
Log.ENABLE_CALLBACK = config.enableCallback;
}
static _notifyChange() {
let emitter = LoggingControl.emitter;
if (emitter.listenerCount('change') > 0) {
let config = LoggingControl.getConfig();
emitter.emit('change', config);
}
}
static registerListener(listener) {
LoggingControl.emitter.addListener('change', listener);
}
static removeListener(listener) {
LoggingControl.emitter.removeListener('change', listener);
}
static addLogListener(listener) {
Log.emitter.addListener('log', listener);
if (Log.emitter.listenerCount('log') > 0) {
Log.ENABLE_CALLBACK = true;
LoggingControl._notifyChange();
}
}
static removeLogListener(listener) {
Log.emitter.removeListener('log', listener);
if (Log.emitter.listenerCount('log') === 0) {
Log.ENABLE_CALLBACK = false;
LoggingControl._notifyChange();
}
}
}
LoggingControl.emitter = new EventEmitter();
export default LoggingControl;

58
node_modules/flv.js/src/utils/polyfill.js generated vendored Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Polyfill {
static install() {
// ES6 Object.setPrototypeOf
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
};
// ES6 Object.assign
Object.assign = Object.assign || function (target) {
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
let output = Object(target);
for (let i = 1; i < arguments.length; i++) {
let source = arguments[i];
if (source !== undefined && source !== null) {
for (let key in source) {
if (source.hasOwnProperty(key)) {
output[key] = source[key];
}
}
}
}
return output;
};
// ES6 Promise (missing support in IE11)
if (typeof self.Promise !== 'function') {
require('es6-promise').polyfill();
}
}
}
Polyfill.install();
export default Polyfill;

84
node_modules/flv.js/src/utils/utf8-conv.js generated vendored Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2016 Bilibili. All Rights Reserved.
*
* This file is derived from C++ project libWinTF8 (https://github.com/m13253/libWinTF8)
* @author zheng qian <xqq@xqq.im>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function checkContinuation(uint8array, start, checkLength) {
let array = uint8array;
if (start + checkLength < array.length) {
while (checkLength--) {
if ((array[++start] & 0xC0) !== 0x80)
return false;
}
return true;
} else {
return false;
}
}
function decodeUTF8(uint8array) {
let out = [];
let input = uint8array;
let i = 0;
let length = uint8array.length;
while (i < length) {
if (input[i] < 0x80) {
out.push(String.fromCharCode(input[i]));
++i;
continue;
} else if (input[i] < 0xC0) {
// fallthrough
} else if (input[i] < 0xE0) {
if (checkContinuation(input, i, 1)) {
let ucs4 = (input[i] & 0x1F) << 6 | (input[i + 1] & 0x3F);
if (ucs4 >= 0x80) {
out.push(String.fromCharCode(ucs4 & 0xFFFF));
i += 2;
continue;
}
}
} else if (input[i] < 0xF0) {
if (checkContinuation(input, i, 2)) {
let ucs4 = (input[i] & 0xF) << 12 | (input[i + 1] & 0x3F) << 6 | input[i + 2] & 0x3F;
if (ucs4 >= 0x800 && (ucs4 & 0xF800) !== 0xD800) {
out.push(String.fromCharCode(ucs4 & 0xFFFF));
i += 3;
continue;
}
}
} else if (input[i] < 0xF8) {
if (checkContinuation(input, i, 3)) {
let ucs4 = (input[i] & 0x7) << 18 | (input[i + 1] & 0x3F) << 12
| (input[i + 2] & 0x3F) << 6 | (input[i + 3] & 0x3F);
if (ucs4 > 0x10000 && ucs4 < 0x110000) {
ucs4 -= 0x10000;
out.push(String.fromCharCode((ucs4 >>> 10) | 0xD800));
out.push(String.fromCharCode((ucs4 & 0x3FF) | 0xDC00));
i += 4;
continue;
}
}
}
out.push(String.fromCharCode(0xFFFD));
++i;
}
return out.join('');
}
export default decodeUTF8;

16
node_modules/flv.js/tsconfig.json generated vendored Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"outDir": "./dist/",
"allowJs": true,
"sourceMap": true,
"module": "es6",
"target": "es5",
"experimentalDecorators": true,
"declaration": true,
"declarationDir": "./d.ts/",
"lib": ["dom", "es5", "es2015.promise", "es2015.collection"]
},
"include": [
"./src/**/*",
]
}

1
node_modules/flv.js/tslint.json generated vendored Normal file
View File

@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }

3
node_modules/flv.js/types/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,3 @@
// TypeScript Version: 2.3
import '../d.ts/flv.d.ts';

8
node_modules/flv.js/types/test-flv.ts generated vendored Normal file
View File

@ -0,0 +1,8 @@
import flvjs from '../';
type LoaderStatusAlias = flvjs.LoaderStatus;
type LoaderErrorsAlias = flvjs.LoaderErrors;
interface MediaDataSourceExt extends flvjs.MediaDataSource {
example: string;
}

24
node_modules/flv.js/types/tsconfig.json generated vendored Normal file
View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "./",
"typeRoots": [
"./"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"../d.ts/flv.d.ts"
]
}

70
node_modules/flv.js/webpack.config.js generated vendored Normal file
View File

@ -0,0 +1,70 @@
const webpack = require('webpack');
const pkg = require('./package.json');
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
let config = {
entry: './src/index.js',
output: {
filename: 'flv.js',
path: path.resolve(__dirname, 'dist'),
library: 'flvjs',
libraryTarget: 'umd',
environment: {
arrowFunction: false,
bigIntLiteral: false,
const: false,
destructuring: false,
dynamicImport: false,
forOf: false,
module: false
}
},
devtool: 'source-map',
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
fallback: {
fs: false,
path: false
}
},
plugins: [
new webpack.DefinePlugin({
__VERSION__: JSON.stringify(pkg.version)
})
],
optimization: {
minimizer: [
new TerserPlugin({
extractComments: false,
})
]
},
module: {
rules: [
{
test: /\.(ts|js)$/,
use: 'ts-loader',
exclude: /node-modules/
},
{
enforce: 'pre',
test: /\.js$/,
use: 'source-map-loader'
}
]
}
};
module.exports = (env, argv) => {
if (argv.mode === 'production') {
config.output.filename = 'flv.min.js';
}
return config;
};

28
node_modules/hls.js/LICENSE generated vendored Normal file
View File

@ -0,0 +1,28 @@
Copyright (c) 2017 Dailymotion (http://www.dailymotion.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
src/remux/mp4-generator.js and src/demux/exp-golomb.ts implementation in this project
are derived from the HLS library for video.js (https://github.com/videojs/videojs-contrib-hls)
That work is also covered by the Apache 2 License, following copyright:
Copyright (c) 2013-2015 Brightcove
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

452
node_modules/hls.js/README.md generated vendored Normal file
View File

@ -0,0 +1,452 @@
[![npm](https://img.shields.io/npm/v/hls.js.svg?style=flat)](https://npmjs.org/package/hls.js)
[![npm](https://img.shields.io/npm/v/hls.js/canary.svg?style=flat)](https://www.npmjs.com/package/hls.js/v/canary)
[![](https://data.jsdelivr.com/v1/package/npm/hls.js/badge?style=rounded)](https://www.jsdelivr.com/package/npm/hls.js)
[![Sauce Test Status](https://saucelabs.com/buildstatus/robwalch)](https://app.saucelabs.com/u/robwalch)
[![jsDeliver](https://data.jsdelivr.com/v1/package/npm/hls.js/badge)](https://www.jsdelivr.com/package/npm/hls.js)
[comment]: <> ([![Sauce Test Status]&#40;https://saucelabs.com/browser-matrix/robwalch.svg&#41;]&#40;https://saucelabs.com/u/robwalch&#41;)
# ![HLS.js](https://raw.githubusercontent.com/video-dev/hls.js/master/docs/logo.svg)
HLS.js is a JavaScript library that implements an [HTTP Live Streaming] client.
It relies on [HTML5 video][] and [MediaSource Extensions][] for playback.
It works by transmuxing MPEG-2 Transport Stream and AAC/MP3 streams into ISO BMFF (MP4) fragments.
Transmuxing is performed asynchronously using a [Web Worker] when available in the browser.
HLS.js also supports HLS + fmp4, as announced during [WWDC2016](https://developer.apple.com/videos/play/wwdc2016/504/).
HLS.js works directly on top of a standard HTML`<video>` element.
HLS.js is written in [ECMAScript6] (`*.js`) and [TypeScript] (`*.ts`) (strongly typed superset of ES6), and transpiled in ECMAScript5 using [Babel](https://babeljs.io/) and the [TypeScript compiler].
[Rollup] is used to build the distro bundle and serve the local development environment.
[html5 video]: https://www.html5rocks.com/en/tutorials/video/basics/
[mediasource extensions]: https://w3c.github.io/media-source/
[http live streaming]: https://en.wikipedia.org/wiki/HTTP_Live_Streaming
[web worker]: https://caniuse.com/#search=worker
[ecmascript6]: https://github.com/ericdouglas/ES6-Learning#articles--tutorials
[typescript]: https://www.typescriptlang.org/
[typescript compiler]: https://www.typescriptlang.org/docs/handbook/compiler-options.html
[rollup]: https://rollupjs.org/
## Features
- VOD & Live playlists
- DVR support on Live playlists
- Fragmented MP4 container
- MPEG-2 TS container
- ITU-T Rec. H.264 and ISO/IEC 14496-10 Elementary Stream
- ISO/IEC 13818-7 ADTS AAC Elementary Stream
- ISO/IEC 11172-3 / ISO/IEC 13818-3 (MPEG-1/2 Audio Layer III) Elementary Stream
- ATSC A/52 / AC-3 / Dolby Digital Elementary Stream
- Packetized metadata (ID3v2.3.0) Elementary Stream
- AAC container (audio only streams)
- MPEG Audio container (MPEG-1/2 Audio Layer III audio only streams)
- Timed Metadata for HTTP Live Streaming (ID3 format carried in MPEG-2 TS, Emsg in CMAF/Fragmented MP4, and DATERANGE playlist tags)
- AES-128 decryption
- "identity" format SAMPLE-AES decryption of MPEG-2 TS segments only
- Encrypted media extensions (EME) support for DRM (digital rights management)
- FairPlay, PlayReady, Widevine CDMs with fmp4 segments
- Level capping based on HTMLMediaElement resolution, dropped-frames, and HDCP-Level
- CEA-608/708 captions
- WebVTT subtitles
- Alternate Audio Track Rendition (Master Playlist with Alternative Audio) for VoD and Live playlists
- Adaptive streaming
- Manual & Auto Quality Switching
- 3 Quality Switching modes are available (controllable through API means)
- Instant switching (immediate quality switch at current video position)
- Smooth switching (quality switch for next loaded fragment)
- Bandwidth conservative switching (quality switch change for next loaded fragment, without flushing the buffer)
- In Auto-Quality mode, emergency switch down in case bandwidth is suddenly dropping to minimize buffering.
- Accurate Seeking on VoD & Live (not limited to fragment or keyframe boundary)
- Ability to seek in buffer and back buffer without redownloading segments
- Built-in Analytics
- All internal events can be monitored (Network Events, Video Events)
- Playback session metrics are also exposed
- Resilience to errors
- Retry mechanism embedded in the library
- Recovery actions can be triggered fix fatal media or network errors
- [Redundant/Failover Playlists](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/UsingHTTPLiveStreaming/UsingHTTPLiveStreaming.html#//apple_ref/doc/uid/TP40008332-CH102-SW22)
- HLS Variable Substitution
### Supported HLS tags
For details on the HLS format and these tags' meanings, see https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis
#### Multivariant Playlist tags
- `#EXT-X-STREAM-INF:<attribute-list>`
`<URI>`
- `#EXT-X-MEDIA:<attribute-list>`
- `#EXT-X-SESSION-DATA:<attribute-list>`
- `#EXT-X-SESSION-KEY:<attribute-list>` EME Key-System selection and preloading
- `#EXT-X-START:TIME-OFFSET=<n>`
- `#EXT-X-CONTENT-STEERING:<attribute-list>` Content Steering
- `#EXT-X-DEFINE:<attribute-list>` Variable Substitution (`NAME,VALUE,QUERYPARAM` attributes)
The following properties are added to their respective variants' attribute list but are not implemented in their selection and playback.
- `VIDEO-RANGE` (See [#2489](https://github.com/video-dev/hls.js/issues/2489))
#### Media Playlist tags
- `#EXTM3U`
- `#EXT-X-VERSION=<n>`
- `#EXTINF:<duration>,[<title>]`
- `#EXT-X-ENDLIST`
- `#EXT-X-MEDIA-SEQUENCE=<n>`
- `#EXT-X-TARGETDURATION=<n>`
- `#EXT-X-DISCONTINUITY`
- `#EXT-X-DISCONTINUITY-SEQUENCE=<n>`
- `#EXT-X-BYTERANGE=<n>[@<o>]`
- `#EXT-X-MAP:<attribute-list>`
- `#EXT-X-KEY:<attribute-list>` (`KEYFORMAT="identity",METHOD=SAMPLE-AES` is only supports with MPEG-2 TS segments)
- `#EXT-X-PROGRAM-DATE-TIME:<attribute-list>`
- `#EXT-X-START:TIME-OFFSET=<n>`
- `#EXT-X-SERVER-CONTROL:<attribute-list>`
- `#EXT-X-PART-INF:PART-TARGET=<n>`
- `#EXT-X-PART:<attribute-list>`
- `#EXT-X-SKIP:<attribute-list>` Delta Playlists
- `#EXT-X-RENDITION-REPORT:<attribute-list>`
- `#EXT-X-DATERANGE:<attribute-list>` Metadata
- `#EXT-X-DEFINE:<attribute-list>` Variable Import and Substitution (`NAME,VALUE,IMPORT,QUERYPARAM` attributes)
- `#EXT-X-GAP` (Skips loading GAP segments and parts. Skips playback of unbuffered program containing only GAP content and no suitable alternates. See [#2940](https://github.com/video-dev/hls.js/issues/2940))
The following tags are added to their respective fragment's attribute list but are not implemented in streaming and playback.
- `#EXT-X-BITRATE` (Not used in ABR controller)
Parsed but missing feature support
- `#EXT-X-PRELOAD-HINT:<attribute-list>` (See [#5074](https://github.com/video-dev/hls.js/issues/3988))
- #5074
### Not Supported
For a complete list of issues, see ["Top priorities" in the Release Planning and Backlog project tab](https://github.com/video-dev/hls.js/projects/6). Codec support is dependent on the runtime environment (for example, not all browsers on the same OS support HEVC).
- HLS Interstitials
- `#EXT-X-I-FRAME-STREAM-INF` I-frame Media Playlist files
- "identity" format `SAMPLE-AES` method keys with fmp4, aac, mp3, vtt... segments (MPEG-2 TS only)
- MPEG-2 TS segments with FairPlay Streaming, PlayReady, or Widevine encryption
- FairPlay Streaming legacy keys (For com.apple.fps.1_0 use native Safari playback)
- MP3 elementary stream audio in IE and Edge (<=18) on Windows 10 (See [#1641](https://github.com/video-dev/hls.js/issues/1641) and [Microsoft answers forum](https://answers.microsoft.com/en-us/ie/forum/all/ie11-on-windows-10-cannot-play-hls-with-mp3/2da994b5-8dec-4ae9-9201-7d138ede49d9))
### Server-side-rendering (SSR) and `require` from a Node.js runtime
You can safely require this library in Node and **absolutely nothing will happen**. A dummy object is exported so that requiring the library does not throw an error. HLS.js is not instantiable in Node.js. See [#1841](https://github.com/video-dev/hls.js/pull/1841) for more details.
## Getting started with development
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/video-dev/hls.js/tree/master?title=HLS.JS)
First, checkout the repository and install the required dependencies
```sh
git clone https://github.com/video-dev/hls.js.git
cd hls.js
# After cloning or pulling from the repository, make sure all dependencies are up-to-date
npm install ci
# Run dev-server for demo page (recompiles on file-watch, but doesn't write to actual dist fs artifacts)
npm run dev
# After making changes run the sanity-check task to verify all checks before committing changes
npm run sanity-check
```
The dev server will host files on port 8000. Once started, the demo can be found running at http://localhost:8000/demo/.
Before submitting a PR, please see our [contribution guidelines](CONTRIBUTING.md).
Join the discussion on Slack via [video-dev.org](https://video-dev.org) in #hlsjs for updates and questions about development.
### Build tasks
Build all flavors (suitable for prod-mode/CI):
```
npm install ci
npm run build
```
Only debug-mode artifacts:
```
npm run build:debug
```
Build and watch (customized dev setups where you'll want to host through another server - for example in a sub-module/project)
```
npm run build:watch
```
Only specific flavor (known configs are: debug, dist, light, light-dist, demo):
```
npm run build -- --env dist # replace "dist" by other configuration name, see above ^
```
Note: The "demo" config is always built.
**NOTE:** `hls.light.*.js` dist files do not include alternate-audio, subtitles, CMCD, EME (DRM), or Variable Substitution support. In addition, the following types are not available in the light build:
- `AudioStreamController`
- `AudioTrackController`
- `CuesInterface`
- `EMEController`
- `SubtitleStreamController`
- `SubtitleTrackController`
- `TimelineController`
- `CmcdController`
### Linter (ESlint)
Run linter:
```
npm run lint
```
Run linter with auto-fix mode:
```
npm run lint:fix
```
Run linter with errors only (no warnings)
```
npm run lint:quiet
```
### Formatting Code
Run prettier to format code
```
npm run prettier
```
### Type Check
Run type-check to verify TypeScript types
```
npm run type-check
```
### Automated tests (Mocha/Karma)
Run all tests at once:
```
npm test
```
Run unit tests:
```
npm run test:unit
```
Run unit tests in watch mode:
```
npm run test:unit:watch
```
Run functional (integration) tests:
```
npm run test:func
```
## Design
An overview of this project's design, it's modules, events, and error handling can be found [here](/docs/design.md).
## API docs and usage guide
- [API and usage docs, with code examples](./docs/API.md)
- [Auto-Generated API Docs (Latest Release)](https://hlsjs.video-dev.org/api-docs)
- [Auto-Generated API Docs (Development Branch)](https://hlsjs-dev.video-dev.org/api-docs)
_Note you can access the docs for a particular version using "[https://github.com/video-dev/hls.js/tree/deployments](https://github.com/video-dev/hls.js/tree/deployments)"_
## Demo
### Latest Release
[https://hlsjs.video-dev.org/demo](https://hlsjs.video-dev.org/demo)
### Master
[https://hlsjs-dev.video-dev.org/demo](https://hlsjs-dev.video-dev.org/demo)
### Specific Version
Find the commit on [https://github.com/video-dev/hls.js/tree/deployments](https://github.com/video-dev/hls.js/tree/deployments).
[![](https://opensource.saucelabs.com/images/opensauce/powered-by-saucelabs-badge-gray.png?sanitize=true)](https://saucelabs.com)
## Compatibility
HLS.js is only compatible with browsers supporting MediaSource extensions (MSE) API with 'video/MP4' mime-type inputs.
HLS.js is supported on:
- Chrome 39+ for Android
- Chrome 39+ for Desktop
- Firefox 41+ for Android
- Firefox 42+ for Desktop
- Edge for Windows 10+
- Safari 9+ for macOS 10.11+
- Safari for iPadOS 13+
- Safari for iOS 17.1+
A [Promise polyfill](https://github.com/taylorhakes/promise-polyfill) is required in browsers missing native promise support.
**Please note:** iOS Safari on iPhone does not support the MediaSource API. This includes all browsers on iOS as well as apps using UIWebView and WKWebView.
Safari browsers (iOS, iPadOS, and macOS) have built-in HLS support through the plain video "tag" source URL. See the example below (Using HLS.js) to run appropriate feature detection and choose between using HLS.js or natively built-in HLS support.
When a platform has neither MediaSource nor native HLS support, the browser cannot play HLS.
_Keep in mind that if the intention is to support HLS on multiple platforms, beyond those compatible with HLS.js, the HLS streams need to strictly follow the specifications of RFC8216, especially if apps, smart TVs, and set-top boxes are to be supported._
Find a support matrix of the MediaSource API here: https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
## Using HLS.js
### Installation
Prepackaged builds are included [with each release](https://github.com/video-dev/hls.js/releases). Or install the hls.js as a dependency
of your project:
```sh
npm install --save hls.js
```
A canary channel is also available if you prefer to work off the development branch (master):
```
npm install hls.js@canary
```
### Embedding HLS.js
Directly include dist/hls.js or dist/hls.min.js in a script tag on the page. This setup prioritizes HLS.js MSE playback over
native browser support for HLS playback in HTMLMediaElements:
```html
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<!-- Or if you want the latest version from the main branch -->
<!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@canary"></script> -->
<video id="video"></video>
<script>
var video = document.getElementById('video');
var videoSrc = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
// 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 through 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')) {
video.src = videoSrc;
}
</script>
```
#### Alternative setup
To check for native browser support first and then fallback to HLS.js, swap these conditionals. See [this comment](https://github.com/video-dev/hls.js/pull/2954#issuecomment-670021358) to understand some of the tradeoffs.
```html
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<!-- Or if you want the latest version from the main branch -->
<!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@canary"></script> -->
<video id="video"></video>
<script>
var video = document.getElementById('video');
var videoSrc = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
//
// First check for native browser HLS support
//
if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
//
// If no native HLS support, check if HLS.js is supported
//
} else if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
</script>
```
For more embed and API examples see [docs/API.md](./docs/API.md).
## CORS
All HLS resources must be delivered with [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) permitting `GET` requests.
## Video Control
Video is controlled through HTML `<video>` element `HTMLVideoElement` methods, events and optional UI controls (`<video controls>`).
## Build a Custom UI
- [Media Chrome](https://github.com/muxinc/media-chrome)
## Player Integration
The following players integrate HLS.js for HLS playback:
- [JW Player](https://www.jwplayer.com)
- [Akamai Adaptive Media Player (AMP)](https://www.akamai.com/us/en/solutions/products/media-delivery/adaptive-media-player.jsp)
- [BridTV Player](https://www.brid.tv)
- [Clappr](https://github.com/clappr/clappr)
- [Flowplayer](https://www.flowplayer.org) through [flowplayer-hlsjs](https://github.com/flowplayer/flowplayer-hlsjs)
- [MediaElement.js](https://www.mediaelementjs.com)
- [KalturaPlayer](https://developer.kaltura.com) through [kaltura-player-js](https://github.com/kaltura/kaltura-player-js#readme)
- [Videojs](https://videojs.com) through [Videojs-hlsjs](https://github.com/benjipott/videojs-hlsjs)
- [Videojs](https://videojs.com) through [videojs-hls.js](https://github.com/streamroot/videojs-hls.js). hls.js is integrated as a SourceHandler -- new feature in Video.js 5.
- [Videojs](https://videojs.com) through [videojs-contrib-hls.js](https://github.com/Peer5/videojs-contrib-hls.js). Production ready plug-in with full fallback compatibility built-in.
- [Fluid Player](https://www.fluidplayer.com)
- [OpenPlayerJS](https://www.openplayerjs.com), as part of the [OpenPlayer project](https://github.com/openplayerjs)
- [CDNBye](https://github.com/cdnbye/hlsjs-p2p-engine), a p2p engine for hls.js powered by WebRTC Datachannel.
- [M3U IPTV](http://m3u-ip.tv/browser/)
### They use HLS.js in production!
| | | | |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [<img src="https://i.cdn.turner.com/adultswim/big/img/global/adultswim.jpg" width="120">](https://www.adultswim.com/streams) | [<img src="https://avatars3.githubusercontent.com/u/5497190?s=200&v=4" width="120">](https://www.akamai.com) | [<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Canal%2B.svg/2000px-Canal%2B.svg.png" width="120">](https://www.canalplus.fr) | [<img src="https://avatars2.githubusercontent.com/u/115313" width="120">](https://www.dailymotion.com) |
| [<img src="https://user-images.githubusercontent.com/4006693/44003595-baff193c-9e8f-11e8-9848-7bb91563499f.png" width="120">](https://freshlive.tv) | [<img src="https://user-images.githubusercontent.com/360826/231535440-7cf075f1-bf38-4640-a0a7-d9ff74a1e396.png" width="120">](https://www.mux.com/) | [<img src="https://avatars1.githubusercontent.com/u/12554082?s=240" width="120">](https://www.foxsports.com.au) | [<img src="https://cloud.githubusercontent.com/assets/244265/12556435/dfaceb48-c353-11e5-971b-2c4429725469.png" width="120">](https://www.globo.com) |
| [<img src="https://images.gunosy.com/logo/gunosy_icon_company_logo.png" width="120">](https://gunosy.com) | [<img src="https://user-images.githubusercontent.com/1480052/35802840-f8e85b8a-0a71-11e8-8eb2-eee323e3f159.png" width="120">](https://www.gl-systemhaus.de/) | [<img src="https://cloud.githubusercontent.com/assets/6525783/20801836/700490de-b7ea-11e6-82bd-e249f91c7bae.jpg" width="120">](https://nettrek.de) | [<img src="https://cloud.githubusercontent.com/assets/244265/12556385/999aa884-c353-11e5-9102-79df54384498.png" width="120">](https://www.nytimes.com/) |
| [<img src="https://cloud.githubusercontent.com/assets/1798553/20356424/ba158574-ac24-11e6-95e1-1ae591b11a0a.png" width="120">](https://www.peer5.com/) | [<img src="https://cloud.githubusercontent.com/assets/4909096/20925062/e26e6fc8-bbb4-11e6-99a5-d4762274a342.png" width="120">](https://www.qbrick.com) | [<img src="https://www.radiantmediaplayer.com/images/radiantmediaplayer-new-logo-640.jpg" width="120">](https://www.radiantmediaplayer.com/) | [<img src="https://www.rts.ch/hummingbird-static/images/logos/logo_marts.svg" width="120">](https://www.rts.ch) |
| [<img src="https://cloud.githubusercontent.com/assets/12702747/19316434/0a3601de-9067-11e6-85e2-936b1cb099a0.png" width="120">](https://www.snapstream.com/) | [<img src="https://pamediagroup.com/wp-content/uploads/2019/05/StreamAMG-Logo-RGB.png" width="120">](https://www.streamamg.com/) | [<img src="https://streamsharkio.sa.metacdn.com/wp-content/uploads/2015/10/streamshark-dark.svg" width="120">](https://streamshark.io/) | [<img src="https://camo.githubusercontent.com/9580f10e9bfa8aa7fba52c5cb447bee0757e33da/68747470733a2f2f7777772e7461626c6f74762e636f6d2f7374617469632f696d616765732f7461626c6f5f6c6f676f2e706e67" width="120">](https://my.tablotv.com/) |
| [<img src="https://user-images.githubusercontent.com/2803310/34083705-349c8fd0-e375-11e7-92a6-5c38509f4936.png" width="120">](https://www.streamroot.io/) | [<img src="https://user-images.githubusercontent.com/360826/231538721-156a865d-a505-45e7-a362-dafbaf2b182f.png" width="120">](https://www.ted.com/) | [<img src="https://www.seeklogo.net/wp-content/uploads/2014/12/twitter-logo-vector-download.jpg" width="120">](https://twitter.com/) | [<img src="https://player.clevercast.com/img/clevercast.png" width="120">](https://www.clevercast.com) |
| [<img src="https://player.mtvnservices.com/edge/hosted/Viacom_logo.svg" width="120">](https://www.viacom.com/) | [<img src="https://user-images.githubusercontent.com/1181974/29248959-efabc440-802d-11e7-8050-7c1f4ca6c607.png" width="120">](https://vk.com/) | [<img src="https://avatars0.githubusercontent.com/u/5090060?s=200&v=4" width="120">](https://www.jwplayer.com) | [<img src="https://raw.githubusercontent.com/kaltura/kaltura-player-js/master/docs/images/kaltura-logo.svg" width="120">](https://corp.kaltura.com/) |
| [<img src="https://showmax.akamaized.net/e/logo/showmax_black.png" width="120">](https://tech.showmax.com) | [<img src="https://static3.1tv.ru/assets/web/logo-ac67852f1625b338f9d1fb96be089d03557d50bfc5790d5f48dc56799f59dec6.svg" width="120" height="120">](https://www.1tv.ru/) | [<img src="https://user-images.githubusercontent.com/1480052/40482633-c013ebce-5f55-11e8-96d5-b776415de0ce.png" width="120">](https://www.zdf.de) | [<img src="https://cms-static.brid.tv/img/brid-logo-120x120.jpg" width="120">](https://www.brid.tv/) |
| [cdn77](https://streaming.cdn77.com/) | [<img src="https://avatars0.githubusercontent.com/u/7442371?s=200&v=4" width="120">](https://r7.com/) | [<img src="https://raw.githubusercontent.com/Novage/p2p-media-loader/gh-pages/images/p2pml-logo.png" width="120">](https://github.com/Novage/p2p-media-loader) | [<img src="https://avatars3.githubusercontent.com/u/45617200?s=400" width="120">](https://kayosports.com.au) |
| [<img src="https://avatars1.githubusercontent.com/u/5279615?s=400&u=9771a216836c613f1edf4afe71cfc69d4c5657ed&v=4" width="120">](https://flosports.tv) | [<img src="https://www.logolynx.com/images/logolynx/c6/c67a2cb3ad33a82b5518f8ad8f124703.png" width="120">](https://global.axon.com/) | | |
## Chrome/Firefox integration
made by [gramk](https://github.com/gramk/chrome-hls), plays hls from address bar and m3u8 links
- Chrome [native-hls](https://chrome.google.com/webstore/detail/native-hls-playback/emnphkkblegpebimobpbekeedfgemhof)
- Firefox [native-hls](https://addons.mozilla.org/en-US/firefox/addon/native_hls_playback/)
## License
HLS.js is released under [Apache 2.0 License](LICENSE)

25578
node_modules/hls.js/dist/hls-demo.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/hls.js/dist/hls-demo.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

29245
node_modules/hls.js/dist/hls.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

3105
node_modules/hls.js/dist/hls.js.d.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/hls.js/dist/hls.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

21096
node_modules/hls.js/dist/hls.light.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/hls.js/dist/hls.light.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

2
node_modules/hls.js/dist/hls.light.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

1
node_modules/hls.js/dist/hls.light.min.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More