Слияние кода завершено, страница обновится автоматически
export function supportWebGL() {
try {
if (!window.WebGLRenderingContext) {
return false;
}
var canvas = document.createElement('canvas');
return !!(
canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
);
} catch (err) {
return false;
}
}
export default class Player {
static logPrefix = '[jsmpeg-player] ';
static WORKER_PATH = './jsmpeg.worker.min.js';
/** @type {Worker[]} */
static workers = [];
/**
* 当前所有激活的Worker
*/
static allWorkers = [];
static MAX_WORKER_SIZE = 1;
static SUPPORT_WEBGL = supportWebGL();
static createWorker() {
const wk = new Worker(Player.WORKER_PATH, {});
wk.startTime = performance.now();
wk.waitingList = [];
wk.ready = false;
wk.addEventListener('message', evt => {
const data = evt.data;
console.log(Player.logPrefix + 'get message from worker', data);
switch (data.type) {
// 就绪
case 'ready':
console.log('worker 就绪,耗时', performance.now() - wk.startTime);
wk.ready = true;
if (wk.waitingList.length) {
const list = wk.waitingList;
wk.waitingList = [];
for (const i of list) {
i();
}
}
break;
case 'destroyed':
// 已销毁, 放回线程池
console.log('worker 已清理');
break;
case 'fatal':
// 严重错误
if (wk.player) {
wk.player.onFatal(data.data);
}
wk.terminate();
break;
case 'created':
if (wk.player) {
wk.player.onCreated();
}
break;
case 'initial':
break;
case 'restart':
// 需要重启
if (wk.player) {
wk.player.onRestart()
}
break
}
});
wk.addEventListener('error', err => {
console.log(Player.logPrefix + 'get error from worker()', err);
if (wk.player) {
wk.player.onError(err);
}
});
// 记录当前所有 worker
this.allWorkers.push(wk);
const _t = wk.terminate;
wk.terminate = () => {
const idx = this.allWorkers.indexOf(wk);
if (idx !== -1) {
this.allWorkers.splice(idx, 1);
}
_t.call(wk);
};
return wk;
}
/**
* 获取线程池 worker, 可以携带url,方便找到之前worker的复用
* @param {string} prefer
*/
static chooseWorker(prefer) {
if (!this.workers.length) {
return null;
}
if (this.workers.length === 1) {
return this.workers.pop();
}
const pick = this.workers.findIndex(i => i.id === prefer);
if (pick !== -1) {
const wk = this.workers[pick];
this.workers.splice(pick, 1);
return wk;
}
return this.workers.pop();
}
/**
* 判断是否存在 指定 id 的worker
* @param {string} id
*/
static hasWorker(id) {
if (this.allWorkers.length) {
return this.allWorkers.find(i => i.id === id);
}
return false;
}
/**
* 判断是否存在 指定 id 的空闲 worker
* @param {string} id
*/
static hasIdleWorker(id) {
if (this.workers.length) {
return this.workers.find(i => i.id === id);
}
return false;
}
/**
* 预热worker
*/
static warnup() {
Player.workers.push(this.createWorker());
}
/**
* @param {{url: string, attachCanvas: (c: HTMLCanvasElement) => void, onError?: (error) => void, onCreated?: () => void }} options
*/
constructor(options = {}) {
this.url = options.url;
this.attachCanvas = options.attachCanvas
this._onError = options.onError;
this._onCreated = options.onCreated;
this.initial();
}
resize() {
if (this.created && this.wk) {
this.wk.postMessage({
type: 'resize',
});
}
}
async initial() {
console.log(Player.logPrefix + '正在初始化播放器: ', this.url);
if (Player.hasWorker(this.url) && !Player.hasIdleWorker(this.url)) {
console.log(
Player.logPrefix + '已存在相同 id 的非空闲 Worker, 等待一会再初始化: ',
this.url,
);
await new Promise(r => setTimeout(r, 100));
}
let wk = Player.chooseWorker(this.url);
if (wk) {
console.log(Player.logPrefix + '从Worker池复用: ', this.url);
this.wk = wk;
} else {
console.log(Player.logPrefix + '正在创建新的 Worker: ', this.url);
this.wk = wk = Player.createWorker();
}
wk.player = this;
if (wk.ready) {
this.create();
} else {
wk.waitingList.push(this.create.bind(this));
}
}
onError(err) {
if (this._onError) {
this._onError(err);
}
}
onRestart() {
this.create()
}
onCreated() {
this.created = true;
if (this._onCreated) {
this._onCreated();
}
}
onFatal(err) {
if (!this.created) {
console.log(Player.logPrefix + '复用worker失败,重新初始化', err);
this.initial();
} else if (!this.destroyed) {
console.log(Player.logPrefix + 'fatal error, 准备重新创建播放器', err);
this.initial();
}
}
/**
* 创建播放器
*/
create() {
console.log(Player.logPrefix + '正在创建 player: ' + this.url);
// transferControlToOffscreen 只能创建一次
const canvas = document.createElement('canvas')
this.attachCanvas(canvas)
const oc = canvas.transferControlToOffscreen();
this.wk.id = this.url;
this.wk.postMessage(
{
type: 'create',
data: {
canvas: oc,
webgl: Player.SUPPORT_WEBGL,
url: this.url,
},
},
[oc],
);
}
/**
* 销毁播放器
*/
destroy() {
console.log(Player.logPrefix + '正在销毁 player: ' + this.url);
this.wk.postMessage({
type: 'destroy',
data: {},
});
// 立即放入worker 池
if (Player.workers.length < Player.MAX_WORKER_SIZE) {
console.log(Player.logPrefix + '正在回收 Worker: ' + this.url);
Player.workers.push(this.wk);
} else {
console.log(Player.logPrefix + '正在销毁 Worker: ' + this.url);
this.wk.terminate();
}
this.destroyed = true;
}
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )