UNPKG

@jxstjh/jhvideo

Version:

HTML5 jhvideo base on MPEG2-TS Stream Player

426 lines (384 loc) 13.3 kB
const MAXREQCOUNT = 100 const MINREQCOUNT = 10 const MAXREQCOUNT_LIVE = 50 //Decoder request. const kInitDecoderReq = 0; const kUninitDecoderReq = 1; const kOpenDecoderReq = 2; const kCloseDecoderReq = 3; const kFeedDataReq = 4; const kStartDecodingReq = 5; const kPauseDecodingReq = 6; const kSeekDecoderReq = 7; const kClearDecoderReq = 8; //Decoder response. const kInitDecoderRsp = 0; const kUninitDecoderRsp = 1; const kOpenDecoderRsp = 2; const kCloseDecoderRsp = 3; const kVideoFrame = 4; const kAudioFrame = 5; const kStartDecodingRsp = 6; const kPauseDecodingRsp = 7; const kDecodeFinishedEvt = 8; const kStartFeedingEvt = 9; const kPauseFeedingEvt = 10; const kDecodeErrorEvt = 11; const kWamsLoadErrorEvt = 12; //import "./libffmpeg.js"; self.Module = { onRuntimeInitialized: function () { onWasmLoaded(); } }; var g_loadjs = true; self.onmessage = function (evt) { if (!g_loadjs) { var objData = { t: kWamsLoadErrorEvt, }; self.postMessage(objData); console.log("[ER] importScripts(libffmpeg.js)"); return false; } if (!self.decoder) { console.log("[ER] Decoder not initialized!"); return; } var req = evt.data; self.decoder.cacheAndProReq(req); }; try { self.importScripts('libffmpeg.js'); } catch (e) { console.log(e) g_loadjs = false; } function Decoder() { //this.logger = new Logger("Decoder"); this.coreLogLevel = 1; this.wasmLoaded = false; this.tmpReqQue = []; this.tmpDataQue = []; this.cacheBuffer = null; this.cacheSize = 0; this.decodeTimer = null; this.videoCallback = null; this.audioCallback = null; this.decoderID = 0; this.feedstatus = 0; this._frameDur = 40;//MS this._startDec = 0; this._lastPts = 0; this._totalDur = 0; this._count = 0; this._speed = 1; this._inputCount = 0; } Decoder.prototype.initDecoder = function (chunkSize) { var ret = Module._initDecoder(this.coreLogLevel); //this.logger.logInfo("initDecoder return " + ret + "."); if (0 == ret) { chunkSize = chunkSize || 65536; this.cacheBuffer = Module._malloc(chunkSize); this.cacheSize = chunkSize } var objData = { t: kInitDecoderRsp, e: ret }; self.postMessage(objData); }; Decoder.prototype.uninitDecoder = function () { var ret = Module._uninitDecoder(); // this.logger.logInfo("Uninit ffmpeg decoder return " + ret + "."); if (this.cacheBuffer != null) { Module._free(this.cacheBuffer); this.cacheBuffer = null; this.cacheSize = 0; } }; Decoder.prototype.openDecoder = function (cd, islive) { var paramCount = 1, paramSize = 4; var paramByteBuffer = Module._malloc(paramCount * paramSize); var codeId = 3; if (cd == 9) { //mpeg4 codeId = 2; } else if (cd == 12) { //265 codeId = 3; } else if (cd == 7) { //264 codeId = 1; } else if (cd == 13) { //svac codeId = 4; } else if (cd == 14) { //mjpeg codeId = 5; } else if (cd == 15) { //mjpegb codeId = 6; } console.log('Decoder.openDecoder: code[', cd, '], codeId[', codeId, ']'); //codeId: 1-h264; 2-mpeg4; 3-h265; 4-svac 5-mjpeg 6-mjpegb //copydata: 1 拷贝YUV数据 0 不拷贝,仅svac有效 var ret = Module._openDecoder(paramByteBuffer, codeId, 1, this.videoCallback, this.audioCallback); if (ret == 0) { var paramIntBuff = paramByteBuffer >> 2; var paramArray = Module.HEAP32.subarray(paramIntBuff, paramIntBuff + paramCount); this.decoderID = paramArray[0]; this.islive = islive; } var objData = { t: kOpenDecoderRsp, e: ret }; self.postMessage(objData); Module._free(paramByteBuffer); }; Decoder.prototype.closeDecoder = function () { //this.logger.logInfo("closeDecoder."); this.tmpDataQue = []; if (this.decodeTimer) { clearInterval(this.decodeTimer); this.decodeTimer = null; // this.logger.logInfo("Decode timer stopped."); } var ret = Module._closeDecoder(this.decoderID); // this.logger.logInfo("Close ffmpeg decoder return " + ret + "."); this.decoderID = 0 var objData = { t: kCloseDecoderRsp, e: 0 }; self.postMessage(objData); }; Decoder.prototype.startDecoding = function (speed, bdel) { //this.logger.logInfo("Start decoding."); if (this.decodeTimer) { clearInterval(this.decodeTimer); } //this._startDec = new Date().getTime(); if (typeof speed === 'number') { this._speed = speed; } if (bdel === true) { //清空数据 var dq = this.tmpDataQue; dq.splice(0, dq.length); //console.log("decode delete data by startDecoding: ", dq.length) } //console.log("Decoder tmpDataQue: ",this.tmpDataQue); this.decodeTimer = setInterval(this.decode.bind(this), this._frameDur / this._speed); }; Decoder.prototype.pauseDecoding = function () { //this.logger.logInfo("Pause decoding."); if (this.decodeTimer) { clearInterval(this.decodeTimer); this.decodeTimer = null; } }; Decoder.prototype.SeekDecoder = function (pts) { //this.logger.logInfo("Pause decoding."); var dq = this.tmpDataQue; var del = 0; if (pts == -1) { dq.splice(0, dq.length); //console.log("decoder delete data by SeekDecoder:", dq.length); } else { for (var i = 0, len = dq.length; i < len; i++) { if (dq[i].k == 1) { //I帧 if (dq[i].s < pts) { //时间戳小 del = i; } else if (dq[i].s = pts) { // del = i; break; } else { break; } } } if (del > 0) { dq.splice(0, del); //console.log("decoder delete data by SeekDecoder:", del); //console.log("delete [", del,"] :", dq.length); } } }; Decoder.prototype.decode = function () { if (this.feedstatus == 1 && this.tmpDataQue.length < MINREQCOUNT) { this.feedstatus = 0; self.postMessage({ t: kStartFeedingEvt }); //console.log("startfeeding (", this.tmpDataQue.length,")"); } if (this.tmpDataQue.length > 0) { var dataReq = this.tmpDataQue.shift(); this.decodeFrameData(dataReq.d, dataReq.s, dataReq.k); } }; Decoder.prototype.decodeFrameData = function (data, pts, key) { var typedArray = data;//new Uint8Array(data); if (typedArray.length > this.cacheSize) { Module._free(this.cacheBuffer); this.cacheSize = typedArray.length + 1000; this.cacheBuffer = Module._malloc(this.cacheSize); } Module.HEAPU8.set(typedArray, this.cacheBuffer); this._getFrame = false; var error = Module._decodeOnePacket(this.decoderID, this.cacheBuffer, typedArray.length, pts, pts); //console.log(this._index, "decoder _decodeOnePacket(",this.decoderID, typedArray.length, pts, key,")=", error); this._inputCount += 1; if (this._inputCount > 200) { self.postMessage({ t: kDecodeErrorEvt }); this._inputCount = 0; console.log("wasm deocdeError 200 times"); } else { if (!this._getFrame && this.tmpDataQue.length > 0) { //没有回调,追加一帧 this.decode(); } } }; Decoder.prototype.processReq = function (req) { //this.logger.logInfo("processReq " + req.t + "."); switch (req.t) { case kInitDecoderReq: this.initDecoder(req.s, req.c); break; case kUninitDecoderReq: this.uninitDecoder(); break; case kOpenDecoderReq: this.openDecoder(req.cd, req.l); break; case kCloseDecoderReq: this.closeDecoder(); break; case kStartDecodingReq: this.startDecoding(req.i, req.d); break; case kPauseDecodingReq: this.pauseDecoding(); break; case kSeekDecoderReq: this.SeekDecoder(req.s); break; default: // this.logger.logError("Unsupport messsage " + req.t); } }; Decoder.prototype.cacheAndProReq = function (req) { if (req) { if (req.t === kFeedDataReq) { //保存数据 this.tmpDataQue.push(req); if (this._count >= 0) { var dif = req.s - this._lastPts, frame = this._frameDur; this._totalDur += (dif <= 0 || dif >= 2000) ? frame : dif; if (this._count > 20) { var val = this._totalDur / this._count; if (frame - val > 3) { this._frameDur = val; this.startDecoding(); this._count = -1; //console.log("adjust display speed") } } } this._lastPts = req.s; this._count++ var dq = this.tmpDataQue; if (this.islive) { //丢帧 if (req.k == 1 && dq.length > MAXREQCOUNT_LIVE) { dq.splice(0, dq.length - 1); //console.log("decoder delete data by cacheAndProReq: ", dq.length-1); } } else { //暂停流 if (this.feedstatus == 0 && this.tmpDataQue.length > MAXREQCOUNT) { this.feedstatus = 1; self.postMessage({ t: kPauseFeedingEvt }); //console.log("pausefeeding (", this.tmpDataQue.length, req.s,")"); } else if (this.feedstatus == 1 && this.tmpDataQue.length < MINREQCOUNT) { this.feedstatus = 0; self.postMessage({ t: kStartFeedingEvt }); //console.log("startfeeding (", this.tmpDataQue.length, req.s,")"); } } } else { //处理命令 if (!this.wasmLoaded || this.tmpReqQue.length > 0) { //保存命令 this.tmpReqQue.push(req); } else { //直接处理 this.processReq(req); } } } if (this.wasmLoaded && this.tmpReqQue.length > 0) { //处理命令 var proReq = this.tmpReqQue.shift(); this.processReq(proReq); if (this.tmpReqQue.length > 0) { setTimeout(this.cacheAndProReq, 0); } } }; Decoder.prototype.onWasmLoaded = function () { // this.logger.logInfo("Wasm loaded."); //console.log("Wasm loaded!!!!"); this.wasmLoaded = true; // this.videoCallback = Module.addFunction(function (buff, size, width, height, timestamp) { // var outArray = Module.HEAPU8.subarray(buff, buff + size); // var data = new Uint8Array(outArray); // var objData = { // t: kVideoFrame, // w: width, // h: height, // s: timestamp, // d: data // }; // self.postMessage(objData, [objData.d.buffer]); // }); var _this = this; this.videoCallback = Module.addFunction(function (y, yline, u, uline, v, vline, width, height, timestamp) { //console.log("videoCallback ",width, height, timestamp) _this._inputCount = 0; _this._getFrame = true; var outArray = Module.HEAPU8.subarray(y, y + yline * height); var ydata = new Uint8Array(outArray); outArray = Module.HEAPU8.subarray(u, u + uline * height / 2); var udata = new Uint8Array(outArray); outArray = Module.HEAPU8.subarray(v, v + vline * height / 2); var vdata = new Uint8Array(outArray); var objData = { t: kVideoFrame, w: width, h: height, ly: yline, lu: uline, lv: vline, s: timestamp, y: ydata, u: udata, v: vdata }; self.postMessage(objData, [objData.y.buffer], [objData.u.buffer], [objData.v.buffer]); }); this.audioCallback = Module.addFunction(function (buff, size) { var outArray = Module.HEAPU8.subarray(buff, buff + size); var data = new Uint8Array(outArray); var objData = { t: kAudioFrame, d: data }; self.postMessage(objData, [objData.d.buffer]); }); this.cacheAndProReq(); }; self.decoder = new Decoder; function onWasmLoaded() { if (self.decoder) { self.decoder.onWasmLoaded(); } else { console.log("[ER] No decoder!"); } }