From 640f50543d41eba30633f37e642394fc9d788961 Mon Sep 17 00:00:00 2001 From: Trek H Date: Fri, 24 Jan 2020 19:22:51 +1030 Subject: [PATCH 1/4] mjpeg-player: added back changes that were overwritten --- cmd/mjpeg-player/index.html | 69 ++++++++++----- cmd/mjpeg-player/main.js | 168 +++++++++++++++++++++++++----------- cmd/mjpeg-player/player.js | 153 ++++++++++++++++++++------------ 3 files changed, 264 insertions(+), 126 deletions(-) diff --git a/cmd/mjpeg-player/index.html b/cmd/mjpeg-player/index.html index 064f436c..60a40429 100644 --- a/cmd/mjpeg-player/index.html +++ b/cmd/mjpeg-player/index.html @@ -1,34 +1,59 @@ + + - - Mjpeg Player - - + + Mjpeg Player + -
-
-
-
- -
-
-
- Frame Rate: fps -
-
- -
+
+
+
+
+
+
+
+ + + +
+
+ Frame Rate: fps +
+
+ +
-
-
- ©2019 Australian Ocean Laboratory Limited (AusOcean) (License) -
-
+
+
+
+ ©2019 Australian Ocean Laboratory Limited (AusOcean) (License) +
+
\ No newline at end of file diff --git a/cmd/mjpeg-player/main.js b/cmd/mjpeg-player/main.js index 037b31ed..e4a0eba0 100644 --- a/cmd/mjpeg-player/main.js +++ b/cmd/mjpeg-player/main.js @@ -1,12 +1,9 @@ /* -NAME - main.js - AUTHOR Trek Hopton LICENSE - This file is Copyright (C) 2019 the Australian Ocean Lab (AusOcean) + 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 @@ -19,57 +16,128 @@ 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. + If not, see http://www.gnu.org/licenses.. */ -// play will process and play the chosen target file. -function play() { - const viewer = document.getElementById('viewer'); - const input = event.target.files[0]; - const reader = new FileReader(); +import Hls from "./hlsjs/hls.js"; - reader.onload = event => { - const player = new Worker("player.js"); +let started = false; +let player, viewer; - let rate = document.getElementById('rate'); - if (rate.value && rate.value > 0) { - player.postMessage({ msg: "setFrameRate", data: rate.value }); - } +// init gets DOM elements once the document has been loaded and adds listeners where necessary. +function init() { + document.addEventListener('DOMContentLoaded', load); + document.addEventListener('DOMContentLoaded', function () { + document.getElementById('urlBtn').addEventListener('click', load); + document.getElementById('fileInput').addEventListener('change', play); + viewer = document.getElementById('viewer'); + } + ); +} - player.onmessage = e => { - switch (e.data.msg) { - case "frame": - const blob = new Blob([new Uint8Array(e.data.data)], { - type: 'video/x-motion-jpeg' - }); - const url = URL.createObjectURL(blob); - viewer.src = url; - break; - case "log": - console.log(e.data.data); - break; - case "stop": - break; - default: - console.error("unknown message from player"); - break; - } - }; +init(); - switch (input.name.split('.')[1]) { - case "mjpeg": - case "mjpg": - player.postMessage({ msg: "loadMjpeg", data: event.target.result }, [event.target.result]); - break; - case "ts": - player.postMessage({ msg: "loadMtsMjpeg", data: event.target.result }, [event.target.result]); - break; - default: - console.error("unknown file format"); - break; - } - }; - reader.onerror = error => reject(error); - reader.readAsArrayBuffer(input); +// load gets the URL from the URL input element or the browser's URL bar +// and creates an Hls instance to load the content from the URL. +function load() { + let url = document.getElementById('url').value; + if (url == "") { + url = getQuery() + document.getElementById('url').value = url; + } + if (url[0] == '/') { + url = window.location.protocol + '//' + window.location.host + url; + } + if (url == "") { + return; + } + + let hls = new Hls(); + hls.loadSource(url, append); +} + +// getQuery gets everything after the question mark from the URL in the browser's URL bar. +function getQuery() { + let regex = new RegExp("\\?(.*)"); + let match = regex.exec(window.location.href); + if (match == null) { + return ''; + } else { + return decodeURIComponent(match[1].replace(/\+/g, " ")); + } +} + +// append, on the first call, starts a player worker and passes it a frame rate and the video data, +// on subsequent calls it passes the video data to the player worker. +function append(data) { + if (!started) { + player = new Worker("player.js"); + + let rate = document.getElementById('rate'); + if (rate.value && rate.value > 0) { + player.postMessage({ msg: "setFrameRate", data: rate.value }); + } + + player.onmessage = handleMessage; + + player.postMessage({ msg: "loadMtsMjpeg", data: data }, [data]); + started = true; + } else { + player.postMessage({ msg: "appendMtsMjpeg", data: data }, [data]); + } } + +// play will process and play the target file chosen with the file input element. +function play() { + const input = event.target.files[0]; + const reader = new FileReader(); + + reader.onload = event => { + const player = new Worker("player.js"); + + let rate = document.getElementById('rate'); + if (rate.value && rate.value > 0) { + player.postMessage({ msg: "setFrameRate", data: rate.value }); + } + + player.onmessage = handleMessage; + + switch (input.name.split('.')[1]) { + case "mjpeg": + case "mjpg": + player.postMessage({ msg: "loadMjpeg", data: event.target.result }, [event.target.result]); + break; + case "ts": + player.postMessage({ msg: "loadMtsMjpeg", data: event.target.result }, [event.target.result]); + break; + default: + console.error("unknown file format"); + break; + } + }; + reader.onerror = error => reject(error); + reader.readAsArrayBuffer(input); +} + +// handleMessage handles messgaes from the player workers, its main job is to update the display when a frame is received. +function handleMessage(e) { + switch (e.data.msg) { + case "frame": + const blob = new Blob([new Uint8Array(e.data.data)], { + type: 'video/x-motion-jpeg' + }); + const url = URL.createObjectURL(blob); + viewer.src = url; + break; + case "log": + console.log(e.data.data); + break; + case "stop": + console.log("stopped"); + break; + default: + console.error("unknown message from player"); + break; + } +} \ No newline at end of file diff --git a/cmd/mjpeg-player/player.js b/cmd/mjpeg-player/player.js index 34ff75ab..ea96e031 100644 --- a/cmd/mjpeg-player/player.js +++ b/cmd/mjpeg-player/player.js @@ -1,12 +1,9 @@ /* -NAME - player.js - AUTHOR Trek Hopton LICENSE - This file is Copyright (C) 2019 the Australian Ocean Lab (AusOcean) + 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 @@ -22,68 +19,116 @@ LICENSE If not, see http://www.gnu.org/licenses. */ -let frameRate = 30; +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": - self.importScripts('./lex-mjpeg.js'); - let mjpeg = new Uint8Array(e.data.data); - let lex = new MJPEGLexer(mjpeg); - player = new Player(lex); - player.setFrameRate(frameRate); - player.start(); - break; - case "loadMtsMjpeg": - self.importScripts('./hlsjs/mts-demuxer.js'); - let mtsMjpeg = new Uint8Array(e.data.data); - let demux = new MTSDemuxer(); - let tracks = demux._getTracks(); - demux.append(mtsMjpeg); - buf = new FrameBuffer(tracks.video.data); - player = new Player(buf); - player.setFrameRate(frameRate); //TODO: read frame rate from metadata. - player.start(); - break; - default: - console.error("unknown message from main thread"); - break; - } + 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; + } }; -class Player { - constructor(buffer) { - this.buffer = buffer; - this.frameRate = frameRate; +// 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; - } + setFrameRate(rate) { + this.frameRate = rate; + } - start() { - let frame = this.buffer.read(); - if (frame != null) { - postMessage({ msg: "frame", data: frame.buffer }, [frame.buffer]); - setTimeout(() => { this.start(); }, 1000 / this.frameRate); - } else { - postMessage({ msg: "stop" }); - } + 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(src) { - this.src = src; - this.off = 0; - } + constructor() { + this.segments = []; + this.off = { segment: 0, frame: 0 }; + this.demuxer = new MTSDemuxer(); + } - // read returns the next single frame. - read() { - return this.src[this.off++]; + // 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; + } + } } \ No newline at end of file From 09c254e1f0aee89cee34ba2d88d624625da5e865 Mon Sep 17 00:00:00 2001 From: Trek H Date: Tue, 28 Jan 2020 13:29:17 +1030 Subject: [PATCH 2/4] mjpeg-player: removed bootstrap classes, updated copyright year --- cmd/mjpeg-player/index.html | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd/mjpeg-player/index.html b/cmd/mjpeg-player/index.html index 60a40429..d42069fb 100644 --- a/cmd/mjpeg-player/index.html +++ b/cmd/mjpeg-player/index.html @@ -29,29 +29,29 @@ LICENSE -
-
-
-
- +
+
+
+
+
-
+
-
- Frame Rate: fps +
+ Frame Rate: fps
-
+
-