UNPKG

@jxstjh/jhvideo

Version:

HTML5 jhvideo base on MPEG2-TS Stream Player

404 lines (367 loc) 11.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; const kAudioInfo = 13; 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.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; 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); 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(); 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, ']'); 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; // Send audio config to main thread var sampleRate = 8000; var channels = 1; var format = 1; // 1 = 16-bit signed int try { if (typeof Module._getAudioSampleRate === 'function') { sampleRate = Module._getAudioSampleRate(this.decoderID); } if (typeof Module._getAudioChannels === 'function') { channels = Module._getAudioChannels(this.decoderID); } if (typeof Module._getAudioSampleFormat === 'function') { format = Module._getAudioSampleFormat(this.decoderID); } } catch (e) { // WASM module doesn't expose audio query functions, use defaults } self.postMessage({ t: kAudioInfo, f: format, c: channels, r: sampleRate }); } var objData = { t: kOpenDecoderRsp, e: ret }; self.postMessage(objData); Module._free(paramByteBuffer); }; Decoder.prototype.closeDecoder = function () { this.tmpDataQue = []; if (this.decodeTimer) { clearInterval(this.decodeTimer); this.decodeTimer = null; } var ret = Module._closeDecoder(this.decoderID); this.decoderID = 0 var objData = { t: kCloseDecoderRsp, e: 0 }; self.postMessage(objData); }; Decoder.prototype.startDecoding = function (speed, bdel) { if (this.decodeTimer) { clearInterval(this.decodeTimer); } if (typeof speed === 'number') { this._speed = speed; } if (bdel === true) { var dq = this.tmpDataQue; dq.splice(0, dq.length); } this.decodeTimer = setInterval(this.decode.bind(this), this._frameDur / this._speed); }; Decoder.prototype.pauseDecoding = function () { if (this.decodeTimer) { clearInterval(this.decodeTimer); this.decodeTimer = null; } }; Decoder.prototype.SeekDecoder = function (pts) { var dq = this.tmpDataQue; var del = 0; if (pts == -1) { dq.splice(0, dq.length); } else { for (var i = 0, len = dq.length; i < len; i++) { if (dq[i].k == 1) { 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); } } }; Decoder.prototype.decode = function () { if (this.feedstatus == 1 && this.tmpDataQue.length < MINREQCOUNT) { this.feedstatus = 0; self.postMessage({ t: kStartFeedingEvt }); } 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; 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); 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) { 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: } }; 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; } } } 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); } } else { if (this.feedstatus == 0 && this.tmpDataQue.length > MAXREQCOUNT) { this.feedstatus = 1; self.postMessage({ t: kPauseFeedingEvt }); } else if (this.feedstatus == 1 && this.tmpDataQue.length < MINREQCOUNT) { this.feedstatus = 0; self.postMessage({ t: kStartFeedingEvt }); } } } 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.wasmLoaded = true; var _this = this; this.videoCallback = Module.addFunction(function (y, yline, u, uline, v, vline, 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!"); } }