mpegts.js
Version:
HTML5 MPEG2-TS Stream Player
151 lines (125 loc) • 4.24 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.
*/
import Log from '../utils/logger.js';
import {BaseLoader, LoaderStatus, LoaderErrors} from './loader.js';
import {RuntimeException} from '../utils/exception.js';
// For MPEG-TS/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;