/* AUTHOR Trek Hopton LICENSE This file is Copyright (C) 2020 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License in gpl.txt. If not, see http://www.gnu.org/licenses. */ let frameRate = 25; //Keeps track of the frame rate, default is 25fps. self.importScripts('./lex-mjpeg.js'); self.importScripts('./hlsjs/mts-demuxer.js'); const codecs = { MJPEG: 1, MTS_MJPEG: 2, } // onmessage is called whenever the main thread sends this worker a message. onmessage = e => { switch (e.data.msg) { case "setFrameRate": frameRate = e.data.data; break; case "loadMjpeg": player = new PlayerWorker(); player.init(codecs.MJPEG); player.append(e.data.data); player.setFrameRate(frameRate); player.start(); break; case "loadMtsMjpeg": player = new PlayerWorker(); player.init(codecs.MTS_MJPEG); player.append(e.data.data); player.start(); break; case "appendMtsMjpeg": player.append(e.data.data); break; default: console.error("unknown message from main thread"); break; } }; // PlayerWorker has a FrameBuffer to hold frames and once started, passes them one at a time to the main thread. class PlayerWorker { init(codec) { this.frameRate = frameRate; this.codec = codec; switch (codec) { case codecs.MJPEG: this.frameSrc = new MJPEGLexer(); break; case codecs.MTS_MJPEG: this.frameSrc = new FrameBuffer(); break; } } setFrameRate(rate) { this.frameRate = rate; } start() { let frame = this.frameSrc.read(); if (frame != null) { postMessage({ msg: "frame", data: frame.buffer }, [frame.buffer]); setTimeout(() => { this.start(); }, 1000 / this.frameRate); } else { postMessage({ msg: "stop" }); } } append(data) { this.frameSrc.append(data); } } // FrameBuffer allows an array of subarrays (MJPEG frames) to be read one at a time. class FrameBuffer { constructor() { this.segments = []; this.off = { segment: 0, frame: 0 }; this.demuxer = new MTSDemuxer(); } // read returns the next single frame. read() { let off = this.off; let prevOff = off; if (this.incrementOff()) { return this.segments[prevOff.segment][prevOff.frame]; } else { return null; } } append(data) { let demuxed = this.demuxer.demux(new Uint8Array(data)); this.segments.push(demuxed.data); } incrementOff() { if (!this.segments || !this.segments[this.off.segment]) { return false; } if (this.off.frame + 1 >= this.segments[this.off.segment].length) { if (this.off.segment + 1 >= this.segments.length) { return false; } else { this.off.segment++; this.off.frame = 0; return true; } } else { this.off.frame++; return true; } } }