UNPKG

@jxstjh/jhvideo

Version:

HTML5 jhvideo base on MPEG2-TS Stream Player

263 lines 11.2 kB
/* * 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