加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
player.js 33.14 KB
一键复制 编辑 原始数据 按行查看 历史
冯宸宇 提交于 2020-07-14 10:51 . readme
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211
//Decoder states.
const decoderStateIdle = 0;
const decoderStateInitializing = 1;
const decoderStateReady = 2;
const decoderStateFinished = 3;
//Player states.
const playerStateIdle = 0;
const playerStatePlaying = 1;
const playerStatePausing = 2;
//Constant.
const maxBufferTimeLength = 1.0;
const downloadSpeedByteRateCoef = 2.0;
String.prototype.startWith = function(str) {
var reg = new RegExp("^" + str);
return reg.test(this);
};
function FileInfo(url) {
this.url = url;
this.size = 0;
this.offset = 0;
this.chunkSize = 65536;
}
function Player() {
this.fileInfo = null;
this.pcmPlayer = null;
this.canvas = null;
this.webglPlayer = null;
this.callback = null;
this.waitHeaderLength = 524288;
this.duration = 0;
this.pixFmt = 0;
this.videoWidth = 0;
this.videoHeight = 0;
this.yLength = 0;
this.uvLength = 0;
this.beginTimeOffset = 0;
this.decoderState = decoderStateIdle;
this.playerState = playerStateIdle;
this.decoding = false;
this.decodeInterval = 5;
this.videoRendererTimer = null;
this.downloadTimer = null;
this.chunkInterval = 200;
this.downloadSeqNo = 0;
this.downloading = false;
this.downloadProto = kProtoHttp;
this.timeLabel = null;
this.timeTrack = null;
this.trackTimer = null;
this.trackTimerInterval = 500;
this.displayDuration = "00:00:00";
this.audioEncoding = "";
this.audioChannels = 0;
this.audioSampleRate = 0;
this.seeking = false; // Flag to preventing multi seek from track.
this.justSeeked = false; // Flag to preventing multi seek from ffmpeg.
this.urgent = false;
this.seekWaitLen = 524288; // Default wait for 512K, will be updated in onVideoParam.
this.seekReceivedLen = 0;
this.loadingDiv = null;
this.buffering = false;
this.frameBuffer = [];
this.isStream = false;
this.streamReceivedLen = 0;
this.firstAudioFrame = true;
this.fetchController = null;
this.streamPauseParam = null;
this.logger = new Logger("Player");
this.initDownloadWorker();
this.initDecodeWorker();
}
Player.prototype.initDownloadWorker = function () {
var self = this;
this.downloadWorker = new Worker("downloader.js");
this.downloadWorker.onmessage = function (evt) {
var objData = evt.data;
switch (objData.t) {
case kGetFileInfoRsp:
self.onGetFileInfo(objData.i);
break;
case kFileData:
self.onFileData(objData.d, objData.s, objData.e, objData.q);
break;
}
}
};
Player.prototype.initDecodeWorker = function () {
var self = this;
this.decodeWorker = new Worker("decoder.js");
this.decodeWorker.onmessage = function (evt) {
var objData = evt.data;
switch (objData.t) {
case kInitDecoderRsp:
self.onInitDecoder(objData);
break;
case kOpenDecoderRsp:
self.onOpenDecoder(objData);
break;
case kVideoFrame:
self.onVideoFrame(objData);
break;
case kAudioFrame:
self.onAudioFrame(objData);
break;
case kDecodeFinishedEvt:
self.onDecodeFinished(objData);
break;
case kRequestDataEvt:
self.onRequestData(objData.o, objData.a);
break;
case kSeekToRsp:
self.onSeekToRsp(objData.r);
break;
}
}
};
Player.prototype.play = function (url, canvas, callback, waitHeaderLength, isStream) {
this.logger.logInfo("Play " + url + ".");
var ret = {
e: 0,
m: "Success"
};
var success = true;
do {
if (this.playerState == playerStatePausing) {
ret = this.resume();
break;
}
if (this.playerState == playerStatePlaying) {
break;
}
if (!url) {
ret = {
e: -1,
m: "Invalid url"
};
success = false;
this.logger.logError("[ER] playVideo error, url empty.");
break;
}
if (!canvas) {
ret = {
e: -2,
m: "Canvas not set"
};
success = false;
this.logger.logError("[ER] playVideo error, canvas empty.");
break;
}
if (!this.downloadWorker) {
ret = {
e: -3,
m: "Downloader not initialized"
};
success = false;
this.logger.logError("[ER] Downloader not initialized.");
break
}
if (!this.decodeWorker) {
ret = {
e: -4,
m: "Decoder not initialized"
};
success = false;
this.logger.logError("[ER] Decoder not initialized.");
break
}
if (url.startWith("ws://") || url.startWith("wss://")) {
this.downloadProto = kProtoWebsocket;
} else {
this.downloadProto = kProtoHttp;
}
this.fileInfo = new FileInfo(url);
this.canvas = canvas;
this.callback = callback;
this.waitHeaderLength = waitHeaderLength || this.waitHeaderLength;
this.playerState = playerStatePlaying;
this.isStream = isStream;
this.startTrackTimer();
this.displayLoop();
//var playCanvasContext = playCanvas.getContext("2d"); //If get 2d, webgl will be disabled.
this.webglPlayer = new WebGLPlayer(this.canvas, {
preserveDrawingBuffer: false
});
if (!this.isStream) {
var req = {
t: kGetFileInfoReq,
u: url,
p: this.downloadProto
};
this.downloadWorker.postMessage(req);
} else {
this.requestStream(url);
this.onGetFileInfo({
sz: -1,
st: 200
});
}
var self = this;
this.registerVisibilityEvent(function(visible) {
if (visible) {
self.resume();
} else {
self.pause();
}
});
this.buffering = true;
this.showLoading();
} while (false);
return ret;
};
Player.prototype.pauseStream = function () {
if (this.playerState != playerStatePlaying) {
var ret = {
e: -1,
m: "Not playing"
};
return ret;
}
this.streamPauseParam = {
url: this.fileInfo.url,
canvas: this.canvas,
callback: this.callback,
waitHeaderLength: this.waitHeaderLength
}
this.logger.logInfo("Stop in stream pause.");
this.stop();
var ret = {
e: 0,
m: "Success"
};
return ret;
}
Player.prototype.pause = function () {
if (this.isStream) {
return this.pauseStream();
}
this.logger.logInfo("Pause.");
if (this.playerState != playerStatePlaying) {
var ret = {
e: -1,
m: "Not playing"
};
return ret;
}
//Pause video rendering and audio flushing.
this.playerState = playerStatePausing;
//Pause audio context.
if (this.pcmPlayer) {
this.pcmPlayer.pause();
}
//Pause decoding.
this.pauseDecoding();
//Stop track timer.
this.stopTrackTimer();
//Do not stop downloader for background buffering.
var ret = {
e: 0,
m: "Success"
};
return ret;
};
Player.prototype.resumeStream = function () {
if (this.playerState != playerStateIdle || !this.streamPauseParam) {
var ret = {
e: -1,
m: "Not pausing"
};
return ret;
}
this.logger.logInfo("Play in stream resume.");
this.play(this.streamPauseParam.url,
this.streamPauseParam.canvas,
this.streamPauseParam.callback,
this.streamPauseParam.waitHeaderLength,
true);
this.streamPauseParam = null;
var ret = {
e: 0,
m: "Success"
};
return ret;
}
Player.prototype.resume = function (fromSeek) {
if (this.isStream) {
return this.resumeStream();
}
this.logger.logInfo("Resume.");
if (this.playerState != playerStatePausing) {
var ret = {
e: -1,
m: "Not pausing"
};
return ret;
}
if (!fromSeek) {
//Resume audio context.
this.pcmPlayer.resume();
}
//If there's a flying video renderer op, interrupt it.
if (this.videoRendererTimer != null) {
clearTimeout(this.videoRendererTimer);
this.videoRendererTimer = null;
}
//Restart video rendering and audio flushing.
this.playerState = playerStatePlaying;
//Restart decoding.
this.startDecoding();
//Restart track timer.
if (!this.seeking) {
this.startTrackTimer();
}
var ret = {
e: 0,
m: "Success"
};
return ret;
};
Player.prototype.stop = function () {
this.logger.logInfo("Stop.");
if (this.playerState == playerStateIdle) {
var ret = {
e: -1,
m: "Not playing"
};
return ret;
}
if (this.videoRendererTimer != null) {
clearTimeout(this.videoRendererTimer);
this.videoRendererTimer = null;
this.logger.logInfo("Video renderer timer stopped.");
}
this.stopDownloadTimer();
this.stopTrackTimer();
this.hideLoading();
this.fileInfo = null;
this.canvas = null;
this.webglPlayer = null;
this.callback = null;
this.duration = 0;
this.pixFmt = 0;
this.videoWidth = 0;
this.videoHeight = 0;
this.yLength = 0;
this.uvLength = 0;
this.beginTimeOffset = 0;
this.decoderState = decoderStateIdle;
this.playerState = playerStateIdle;
this.decoding = false;
this.frameBuffer = [];
this.buffering = false;
this.streamReceivedLen = 0;
this.firstAudioFrame = true;
this.urgent = false;
this.seekReceivedLen = 0;
if (this.pcmPlayer) {
this.pcmPlayer.destroy();
this.pcmPlayer = null;
this.logger.logInfo("Pcm player released.");
}
if (this.timeTrack) {
this.timeTrack.value = 0;
}
this.logger.logInfo("Closing decoder.");
this.decodeWorker.postMessage({
t: kCloseDecoderReq
});
this.logger.logInfo("Uniniting decoder.");
this.decodeWorker.postMessage({
t: kUninitDecoderReq
});
if (this.fetchController) {
this.fetchController.abort();
this.fetchController = null;
}
return ret;
};
Player.prototype.seekTo = function(ms) {
if (this.isStream) {
return;
}
// Pause playing.
this.pause();
// Stop download.
this.stopDownloadTimer();
// Clear frame buffer.
this.frameBuffer.length = 0;
// Request decoder to seek.
this.decodeWorker.postMessage({
t: kSeekToReq,
ms: ms
});
// Reset begin time offset.
this.beginTimeOffset = ms / 1000;
this.logger.logInfo("seekTo beginTimeOffset " + this.beginTimeOffset);
this.seeking = true;
this.justSeeked = true;
this.urgent = true;
this.seekReceivedLen = 0;
this.startBuffering();
};
Player.prototype.fullscreen = function () {
if (this.webglPlayer) {
this.webglPlayer.fullscreen();
}
};
Player.prototype.getState = function () {
return this.playerState;
};
Player.prototype.setTrack = function (timeTrack, timeLabel) {
this.timeTrack = timeTrack;
this.timeLabel = timeLabel;
if (this.timeTrack) {
var self = this;
this.timeTrack.oninput = function () {
if (!self.seeking) {
self.seekTo(self.timeTrack.value);
}
}
this.timeTrack.onchange = function () {
if (!self.seeking) {
self.seekTo(self.timeTrack.value);
}
}
}
};
Player.prototype.onGetFileInfo = function (info) {
if (this.playerState == playerStateIdle) {
return;
}
this.logger.logInfo("Got file size rsp:" + info.st + " size:" + info.sz + " byte.");
if (info.st == 200) {
this.fileInfo.size = info.sz;
this.logger.logInfo("Initializing decoder.");
var req = {
t: kInitDecoderReq,
s: this.fileInfo.size,
c: this.fileInfo.chunkSize
};
this.decodeWorker.postMessage(req);
} else {
this.reportPlayError(-1, info.st);
}
};
Player.prototype.onFileData = function (data, start, end, seq) {
//this.logger.logInfo("Got data bytes=" + start + "-" + end + ".");
this.downloading = false;
if (this.playerState == playerStateIdle) {
return;
}
if (seq != this.downloadSeqNo) {
return; // Old data.
}
if (this.playerState == playerStatePausing) {
if (this.seeking) {
this.seekReceivedLen += data.byteLength;
let left = this.fileInfo.size - this.fileInfo.offset;
let seekWaitLen = Math.min(left, this.seekWaitLen);
if (this.seekReceivedLen >= seekWaitLen) {
this.logger.logInfo("Resume in seek now");
setTimeout(() => {
this.resume(true);
}, 0);
}
} else {
return;
}
}
var len = end - start + 1;
this.fileInfo.offset += len;
var objData = {
t: kFeedDataReq,
d: data
};
this.decodeWorker.postMessage(objData, [objData.d]);
switch (this.decoderState) {
case decoderStateIdle:
this.onFileDataUnderDecoderIdle();
break;
case decoderStateInitializing:
this.onFileDataUnderDecoderInitializing();
break;
case decoderStateReady:
this.onFileDataUnderDecoderReady();
break;
}
if (this.urgent) {
setTimeout(() => {
this.downloadOneChunk();
}, 0);
}
};
Player.prototype.onFileDataUnderDecoderIdle = function () {
if (this.fileInfo.offset >= this.waitHeaderLength) {
this.logger.logInfo("Opening decoder.");
this.decoderState = decoderStateInitializing;
var req = {
t: kOpenDecoderReq
};
this.decodeWorker.postMessage(req);
}
this.downloadOneChunk();
};
Player.prototype.onFileDataUnderDecoderInitializing = function () {
this.downloadOneChunk();
};
Player.prototype.onFileDataUnderDecoderReady = function () {
//this.downloadOneChunk();
};
Player.prototype.onInitDecoder = function (objData) {
if (this.playerState == playerStateIdle) {
return;
}
this.logger.logInfo("Init decoder response " + objData.e + ".");
if (objData.e == 0) {
if (!this.isStream) {
this.downloadOneChunk();
}
} else {
this.reportPlayError(objData.e);
}
};
Player.prototype.onOpenDecoder = function (objData) {
if (this.playerState == playerStateIdle) {
return;
}
this.logger.logInfo("Open decoder response " + objData.e + ".");
if (objData.e == 0) {
this.onVideoParam(objData.v);
this.onAudioParam(objData.a);
this.decoderState = decoderStateReady;
this.logger.logInfo("Decoder ready now.");
this.startDecoding();
} else {
this.reportPlayError(objData.e);
}
};
Player.prototype.onVideoParam = function (v) {
if (this.playerState == playerStateIdle) {
return;
}
this.logger.logInfo("Video param duation:" + v.d + " pixFmt:" + v.p + " width:" + v.w + " height:" + v.h + ".");
this.duration = v.d;
this.pixFmt = v.p;
//this.canvas.width = v.w;
//this.canvas.height = v.h;
this.videoWidth = v.w;
this.videoHeight = v.h;
this.yLength = this.videoWidth * this.videoHeight;
this.uvLength = (this.videoWidth / 2) * (this.videoHeight / 2);
/*
//var playCanvasContext = playCanvas.getContext("2d"); //If get 2d, webgl will be disabled.
this.webglPlayer = new WebGLPlayer(this.canvas, {
preserveDrawingBuffer: false
});
*/
if (this.timeTrack) {
this.timeTrack.min = 0;
this.timeTrack.max = this.duration;
this.timeTrack.value = 0;
this.displayDuration = this.formatTime(this.duration / 1000);
}
// $('timeTrack').RangeSlider({ min: 0, max: this.duration, step: 0.1});
var byteRate = 1000 * this.fileInfo.size / this.duration;
var targetSpeed = downloadSpeedByteRateCoef * byteRate;
var chunkPerSecond = targetSpeed / this.fileInfo.chunkSize;
this.chunkInterval = 1000 / chunkPerSecond;
this.seekWaitLen = byteRate * maxBufferTimeLength * 2;
this.logger.logInfo("Seek wait len " + this.seekWaitLen);
if (!this.isStream) {
this.startDownloadTimer();
}
this.logger.logInfo("Byte rate:" + byteRate + " target speed:" + targetSpeed + " chunk interval:" + this.chunkInterval + ".");
};
Player.prototype.onAudioParam = function (a) {
if (this.playerState == playerStateIdle) {
return;
}
this.logger.logInfo("Audio param sampleFmt:" + a.f + " channels:" + a.c + " sampleRate:" + a.r + ".");
var sampleFmt = a.f;
var channels = a.c;
var sampleRate = a.r;
var encoding = "16bitInt";
switch (sampleFmt) {
case 0:
encoding = "8bitInt";
break;
case 1:
encoding = "16bitInt";
break;
case 2:
encoding = "32bitInt";
break;
case 3:
encoding = "32bitFloat";
break;
default:
this.logger.logError("Unsupported audio sampleFmt " + sampleFmt + "!");
}
this.logger.logInfo("Audio encoding " + encoding + ".");
this.pcmPlayer = new PCMPlayer({
encoding: encoding,
channels: channels,
sampleRate: sampleRate,
flushingTime: 5000
});
this.audioEncoding = encoding;
this.audioChannels = channels;
this.audioSampleRate = sampleRate;
};
Player.prototype.restartAudio = function () {
if (this.pcmPlayer) {
this.pcmPlayer.destroy();
this.pcmPlayer = null;
}
this.pcmPlayer = new PCMPlayer({
encoding: this.audioEncoding,
channels: this.audioChannels,
sampleRate: this.audioSampleRate,
flushingTime: 5000
});
};
Player.prototype.bufferFrame = function (frame) {
// If not decoding, it may be frame before seeking, should be discarded.
if (!this.decoding) {
return;
}
this.frameBuffer.push(frame);
//this.logger.logInfo("bufferFrame " + frame.s + ", seq " + frame.q);
if (this.getBufferTimerLength() >= maxBufferTimeLength || this.decoderState == decoderStateFinished) {
if (this.decoding) {
//this.logger.logInfo("Frame buffer time length >= " + maxBufferTimeLength + ", pause decoding.");
this.pauseDecoding();
}
if (this.buffering) {
this.stopBuffering();
}
}
}
Player.prototype.displayAudioFrame = function (frame) {
if (this.playerState != playerStatePlaying) {
return false;
}
if (this.seeking) {
this.restartAudio();
this.startTrackTimer();
this.hideLoading();
this.seeking = false;
this.urgent = false;
}
if (this.isStream && this.firstAudioFrame) {
this.firstAudioFrame = false;
this.beginTimeOffset = frame.s;
}
this.pcmPlayer.play(new Uint8Array(frame.d));
return true;
};
Player.prototype.onAudioFrame = function (frame) {
this.bufferFrame(frame);
};
Player.prototype.onDecodeFinished = function (objData) {
this.pauseDecoding();
this.decoderState = decoderStateFinished;
};
Player.prototype.getBufferTimerLength = function() {
if (!this.frameBuffer || this.frameBuffer.length == 0) {
return 0;
}
let oldest = this.frameBuffer[0];
let newest = this.frameBuffer[this.frameBuffer.length - 1];
return newest.s - oldest.s;
};
Player.prototype.onVideoFrame = function (frame) {
this.bufferFrame(frame);
};
Player.prototype.displayVideoFrame = function (frame) {
if (this.playerState != playerStatePlaying) {
return false;
}
if (this.seeking) {
this.restartAudio();
this.startTrackTimer();
this.hideLoading();
this.seeking = false;
this.urgent = false;
}
var audioCurTs = this.pcmPlayer.getTimestamp();
var audioTimestamp = audioCurTs + this.beginTimeOffset;
var delay = frame.s - audioTimestamp;
//this.logger.logInfo("displayVideoFrame delay=" + delay + "=" + " " + frame.s + " - (" + audioCurTs + " + " + this.beginTimeOffset + ")" + "->" + audioTimestamp);
if (audioTimestamp <= 0 || delay <= 0) {
var data = new Uint8Array(frame.d);
this.renderVideoFrame(data);
return true;
}
return false;
};
Player.prototype.onSeekToRsp = function (ret) {
if (ret != 0) {
this.justSeeked = false;
this.seeking = false;
}
};
Player.prototype.onRequestData = function (offset, available) {
if (this.justSeeked) {
this.logger.logInfo("Request data " + offset + ", available " + available);
if (offset == -1) {
// Hit in buffer.
let left = this.fileInfo.size - this.fileInfo.offset;
if (available >= left) {
this.logger.logInfo("No need to wait");
this.resume();
} else {
this.startDownloadTimer();
}
} else {
if (offset >= 0 && offset < this.fileInfo.size) {
this.fileInfo.offset = offset;
}
this.startDownloadTimer();
}
//this.restartAudio();
this.justSeeked = false;
}
};
Player.prototype.displayLoop = function() {
requestAnimationFrame(this.displayLoop.bind(this));
if (this.playerState != playerStatePlaying) {
return;
}
if (this.frameBuffer.length == 0) {
return;
}
if (this.buffering) {
return;
}
// requestAnimationFrame may be 60fps, if stream fps too large,
// we need to render more frames in one loop, otherwise display
// fps won't catch up with source fps, leads to memory increasing,
// set to 2 now.
for (i = 0; i < 2; ++i) {
var frame = this.frameBuffer[0];
switch (frame.t) {
case kAudioFrame:
if (this.displayAudioFrame(frame)) {
this.frameBuffer.shift();
}
break;
case kVideoFrame:
if (this.displayVideoFrame(frame)) {
this.frameBuffer.shift();
}
break;
default:
return;
}
if (this.frameBuffer.length == 0) {
break;
}
}
if (this.getBufferTimerLength() < maxBufferTimeLength / 2) {
if (!this.decoding) {
//this.logger.logInfo("Buffer time length < " + maxBufferTimeLength / 2 + ", restart decoding.");
this.startDecoding();
}
}
if (this.bufferFrame.length == 0) {
if (this.decoderState == decoderStateFinished) {
this.reportPlayError(1, 0, "Finished");
this.stop();
} else {
this.startBuffering();
}
}
};
Player.prototype.startBuffering = function () {
this.buffering = true;
this.showLoading();
this.pause();
}
Player.prototype.stopBuffering = function () {
this.buffering = false;
this.hideLoading();
this.resume();
}
Player.prototype.renderVideoFrame = function (data) {
this.webglPlayer.renderFrame(data, this.videoWidth, this.videoHeight, this.yLength, this.uvLength);
};
Player.prototype.downloadOneChunk = function () {
if (this.downloading || this.isStream) {
return;
}
var start = this.fileInfo.offset;
if (start >= this.fileInfo.size) {
this.logger.logError("Reach file end.");
this.stopDownloadTimer();
return;
}
var end = this.fileInfo.offset + this.fileInfo.chunkSize - 1;
if (end >= this.fileInfo.size) {
end = this.fileInfo.size - 1;
}
var len = end - start + 1;
if (len > this.fileInfo.chunkSize) {
console.log("Error: request len:" + len + " > chunkSize:" + this.fileInfo.chunkSize);
return;
}
var req = {
t: kDownloadFileReq,
u: this.fileInfo.url,
s: start,
e: end,
q: this.downloadSeqNo,
p: this.downloadProto
};
this.downloadWorker.postMessage(req);
this.downloading = true;
};
Player.prototype.startDownloadTimer = function () {
var self = this;
this.downloadSeqNo++;
this.downloadTimer = setInterval(function () {
self.downloadOneChunk();
}, this.chunkInterval);
};
Player.prototype.stopDownloadTimer = function () {
if (this.downloadTimer != null) {
clearInterval(this.downloadTimer);
this.downloadTimer = null;
}
this.downloading = false;
};
Player.prototype.startTrackTimer = function () {
var self = this;
this.trackTimer = setInterval(function () {
self.updateTrackTime();
}, this.trackTimerInterval);
};
Player.prototype.stopTrackTimer = function () {
if (this.trackTimer != null) {
clearInterval(this.trackTimer);
this.trackTimer = null;
}
};
Player.prototype.updateTrackTime = function () {
if (this.playerState == playerStatePlaying && this.pcmPlayer) {
var currentPlayTime = this.pcmPlayer.getTimestamp() + this.beginTimeOffset;
if (this.timeTrack) {
this.timeTrack.value = 1000 * currentPlayTime;
// $('#timeTrack').RangeSlider({ min: 0,max: 100,step: 0.1})
// console.log(this.timeTrack.value)
}
if (this.timeLabel) {
this.timeLabel.innerHTML = this.formatTime(currentPlayTime) + "/" + this.displayDuration;
}
}
};
Player.prototype.startDecoding = function () {
var req = {
t: kStartDecodingReq,
i: this.urgent ? 0 : this.decodeInterval,
};
this.decodeWorker.postMessage(req);
this.decoding = true;
};
Player.prototype.pauseDecoding = function () {
var req = {
t: kPauseDecodingReq
};
this.decodeWorker.postMessage(req);
this.decoding = false;
};
Player.prototype.formatTime = function (s) {
var h = Math.floor(s / 3600) < 10 ? '0' + Math.floor(s / 3600) : Math.floor(s / 3600);
var m = Math.floor((s / 60 % 60)) < 10 ? '0' + Math.floor((s / 60 % 60)) : Math.floor((s / 60 % 60));
var s = Math.floor((s % 60)) < 10 ? '0' + Math.floor((s % 60)) : Math.floor((s % 60));
return result = h + ":" + m + ":" + s;
};
Player.prototype.reportPlayError = function (error, status, message) {
var e = {
error: error || 0,
status: status || 0,
message: message
};
if (this.callback) {
this.callback(e);
}
};
Player.prototype.setLoadingDiv = function (loadingDiv) {
this.loadingDiv = loadingDiv;
}
Player.prototype.hideLoading = function () {
if (this.loadingDiv != null) {
loading.style.display = "none";
}
};
Player.prototype.showLoading = function () {
if (this.loadingDiv != null) {
loading.style.display = "block";
}
};
Player.prototype.registerVisibilityEvent = function (cb) {
var hidden = "hidden";
// Standards:
if (hidden in document) {
document.addEventListener("visibilitychange", onchange);
} else if ((hidden = "mozHidden") in document) {
document.addEventListener("mozvisibilitychange", onchange);
} else if ((hidden = "webkitHidden") in document) {
document.addEventListener("webkitvisibilitychange", onchange);
} else if ((hidden = "msHidden") in document) {
document.addEventListener("msvisibilitychange", onchange);
} else if ("onfocusin" in document) {
// IE 9 and lower.
document.onfocusin = document.onfocusout = onchange;
} else {
// All others.
window.onpageshow = window.onpagehide = window.onfocus = window.onblur = onchange;
}
function onchange (evt) {
var v = true;
var h = false;
var evtMap = {
focus:v,
focusin:v,
pageshow:v,
blur:h,
focusout:h,
pagehide:h
};
evt = evt || window.event;
var visible = v;
if (evt.type in evtMap) {
visible = evtMap[evt.type];
} else {
visible = this[hidden] ? h : v;
}
cb(visible);
}
// set the initial state (but only if browser supports the Page Visibility API)
if( document[hidden] !== undefined ) {
onchange({type: document[hidden] ? "blur" : "focus"});
}
}
Player.prototype.onStreamDataUnderDecoderIdle = function (length) {
if (this.streamReceivedLen >= this.waitHeaderLength) {
this.logger.logInfo("Opening decoder.");
this.decoderState = decoderStateInitializing;
var req = {
t: kOpenDecoderReq
};
this.decodeWorker.postMessage(req);
} else {
this.streamReceivedLen += length;
}
};
Player.prototype.requestStream = function (url) {
var self = this;
// ws,wss协议。 webscoket拉取流
var isWs = url.slice(0,2);
if(isWs=='ws'){
const ws = new WebSocket(url);
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("send WebSockets!");
};
ws.onmessage = function(evt) {
if(typeof(evt.data)=="string"){
textHandler(JSON.parse(evt.data));
}else{
var reader = new FileReader();
reader.onload = function(evt){
if(evt.target.readyState == FileReader.DONE){
var value = new Uint8Array(evt.target.result);
var dataLength = value.byteLength;
var offset = 0;
if (dataLength >self.fileInfo.chunkSize) {
do {
let len = Math.min(self.fileInfo.chunkSize, dataLength);
var data = value.buffer.slice(offset, offset + len);
dataLength -= len;
offset += len;
var objData = {
t: kFeedDataReq,
d: data
};
self.decodeWorker.postMessage(objData, [objData.d]);
} while (dataLength > 0)
}
else {
var objData = {
t: kFeedDataReq,
d: value.buffer
};
self.decodeWorker.postMessage(objData, [objData.d]);
}
if (self.decoderState == decoderStateIdle) {
self.onStreamDataUnderDecoderIdle(dataLength);
}
// ws.close();
}
}
reader.readAsArrayBuffer(evt.data);
}
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
}
else{
//http https协议, fetch拉取流
this.fetchController = new AbortController();
const signal = this.fetchController.signal;
fetch(url, {signal}).then(async function respond(response) {
const reader = response.body.getReader();
reader.read().then(function processData({done, value}) {
if (done) {
self.logger.logInfo("Stream done.");
return;
}
if (self.playerState != playerStatePlaying) {
return;
}
var dataLength = value.byteLength;
var offset = 0;
if (dataLength > self.fileInfo.chunkSize) {
do {
let len = Math.min(self.fileInfo.chunkSize, dataLength);
var data = value.buffer.slice(offset, offset + len);
dataLength -= len;
offset += len;
var objData = {
t: kFeedDataReq,
d: data
};
self.decodeWorker.postMessage(objData, [objData.d]);
} while (dataLength > 0)
} else {
var objData = {
t: kFeedDataReq,
d: value.buffer
};
self.decodeWorker.postMessage(objData, [objData.d]);
}
if (self.decoderState == decoderStateIdle) {
self.onStreamDataUnderDecoderIdle(dataLength);
}
return reader.read().then(processData);
});
}).catch(err => {
});
}
};
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化