@jxstjh/jhvideo
Version:
HTML5 jhvideo base on MPEG2-TS Stream Player
263 lines • 11.2 kB
JavaScript
/*
* 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.
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
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/
*/
var FetchStreamLoader = /** @class */ (function (_super) {
__extends(FetchStreamLoader, _super);
function FetchStreamLoader(seekHandler, config) {
var _this = _super.call(this, 'fetch-stream-loader') || this;
_this.TAG = 'FetchStreamLoader';
_this._seekHandler = seekHandler;
_this._config = config;
_this._needStash = true;
_this._requestAbort = false;
_this._abortController = null;
_this._contentLength = null;
_this._receivedLength = 0;
return _this;
}
FetchStreamLoader.isSupported = function () {
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.
var isWorkWellEdge = Browser.msedge && Browser.version.minor >= 15048;
var browserNotBlacklisted = Browser.msedge ? isWorkWellEdge : true;
return (self.fetch && self.ReadableStream && browserNotBlacklisted);
}
catch (e) {
return false;
}
};
FetchStreamLoader.prototype.destroy = function () {
if (this.isWorking()) {
this.abort();
}
_super.prototype.destroy.call(this);
};
FetchStreamLoader.prototype.open = function (dataSource, range) {
var _this = this;
this._dataSource = dataSource;
this._range = range;
var sourceURL = dataSource.url;
if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) {
sourceURL = dataSource.redirectedURL;
}
var seekConfig = this._seekHandler.getConfig(sourceURL, range);
var headers = new self.Headers();
if (typeof seekConfig.headers === 'object') {
var configHeaders = seekConfig.headers;
for (var key in configHeaders) {
if (configHeaders.hasOwnProperty(key)) {
headers.append(key, configHeaders[key]);
}
}
}
var 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 (var 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;
}
if (self.AbortController) {
this._abortController = new self.AbortController();
params.signal = this._abortController.signal;
}
this._status = LoaderStatus.kConnecting;
self.fetch(seekConfig.url, params).then(function (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) {
var redirectedURL = _this._seekHandler.removeURLParameters(res.url);
_this._onURLRedirect(redirectedURL);
}
}
var 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(function (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;
}
});
};
FetchStreamLoader.prototype.abort = function () {
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) { }
}
}
};
FetchStreamLoader.prototype._pump = function (reader) {
var _this = this;
return reader.read().then(function (result) {
if (result.done) {
// First check received length
if (_this._contentLength !== null && _this._receivedLength < _this._contentLength) {
// Report Early-EOF
_this._status = LoaderStatus.kError;
var type = LoaderErrors.EARLY_EOF;
var 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;
var chunk = result.value.buffer;
var byteStart = _this._range.from + _this._receivedLength;
_this._receivedLength += chunk.byteLength;
if (_this._onDataArrival) {
_this._onDataArrival(chunk, byteStart, _this._receivedLength);
}
_this._pump(reader);
}
}).catch(function (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;
var type = 0;
var 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);
}
});
};
return FetchStreamLoader;
}(BaseLoader));
export default FetchStreamLoader;
//# sourceMappingURL=fetch-stream-loader.js.map