Browse Source

use mjpeg instead, and use only one tcp server

master
Martin Dørum 3 years ago
parent
commit
82513cab14
2 changed files with 177 additions and 62 deletions
  1. BIN
      placeholder.jpg
  2. 177
    62
      server.js

BIN
placeholder.jpg View File


+ 177
- 62
server.js View File

@@ -1,5 +1,5 @@
let http = require("http");
let net = require("net");
let fs = require("fs");

let tcpport = process.env["TCPPORT"] ? parseInt(process.env["TCPPORT"]) : 8099;
let httpport = process.env["HTTPPORT"] ? parseInt(process.env["HTTPPORT"]) : 8098;
@@ -11,84 +11,199 @@ let html = `
<meta charset="utf-8">
<title>XRemoteView</title>
</head>
<body>
<img id="image" src="/image" style="object-fit: contain; height: 100vh; width: 100%;">
<body style="margin: 0px">
<img
src="/image"
style="display: block; object-fit: contain; height: 100vh; width: 100%"
ondblclick="fullscreen(event.target)">
<script>
let img = document.getElementById("image");
let url = img.src;

function refresh() {
img.onload = wait;
img.onerror = () => {
console.error("Load error");
setTimeout(refresh, 4000);
};

let rand = Math.floor(Math.random() * 1000000);
img.src = url + "?" + rand;
}

function wait() {
fetch("/wait").then(refresh).catch(err => {
console.error("Wait error:", err);
wait();
});
function fullscreen(el) {
if (document.fullscreenElement) {
document.exitFullscreen();
} else if (el.requestFullscreen) {
el.requestFullscreen();
}
}

wait();
</script>
</body>
</html>
`;

let imgdata = Buffer.alloc(0);
let boundary = "mjpeg-boundary";

let imgdata = fs.readFileSync("placeholder.jpg");
let waiters = [];

net.createServer(conn => {
console.log("Connection", conn.remoteAddress);
class Reader {
constructor(stream) {
this.stream = stream;
this.buf = Buffer.alloc(0);
this.resolve = null;
this.resolveMax = null;
this.good = true;

received = Buffer.alloc(0);
let hasLength = false;
let expectedLen = 0;
conn.on("data", data => {
received = Buffer.concat([received, data]);

while (true) {
if (!hasLength && received.length >= 4) {
expectedLen = received.readUInt32BE(0);
received = received.slice(4);
hasLength = true;
this.stream.on("close", () => {
this.good = false;
if (this.resolve) {
let resolve = this.resolve;
this.resolve = null;
resolve(Buffer.alloc(0));
}
});

if (hasLength && received.length >= expectedLen) {
imgdata = received.slice(0, expectedLen);
received = received.slice(expectedLen);
hasLength = false;

for (let waiter of waiters) {
waiter.end();
}
this.stream.on("error", err => {
console.log(this.stream.remoteAddress + ": " + err.code);
});

waiters = [];
this.stream.on("data", data => {
let resolve = this.resolve;
let max = this.resolveMax;
if (resolve && data.length <= this.resolveMax) {
this.resolve = null;
resolve(data);
} else if (this.resolve) {
this.resolve = null;
this.buf = data.slice(max);
resolve(data.slice(0, max));
} else {
break;
this.buf = Buffer.concat([this.buf, data]);
}
});
}

read(max) {
if (this.buf.length > max) {
let buf = this.buf.slice(0, max);
this.buf = this.buf.slice(max);
return Promise.resolve(buf);
} else if (this.buf.length != 0) {
let buf = this.buf;
this.buf = Buffer.alloc(0);
return buf;
} else if (this.good) {
this.resolveMax = max;
return new Promise((resolve, reject) => {
this.resolve = resolve;
});
} else {
return Buffer.alloc(0);
}
});
}).on("error", err => console.error(err)).listen(tcpport, "0.0.0.0");
console.log(`TCP listening on 0.0.0.0:${tcpport}`);
}

async readAll(length) {
if (!this.good) {
return Buffer.alloc(0);
}

let buf = Buffer.alloc(0);
while (buf.length < length && this.good) {
let b = await this.read(length - buf.length);
buf = Buffer.concat([buf, b]);
}

return buf;
}
};

async function respondToHttp(reader, conn) {
let path = "";
while (true) {
let ch = (await reader.readAll(1)).toString();
if (ch == " ") {
break;
}

path += ch;
}

// We frankly don't care about anything else the client has to say

http.createServer((req, res) => {
if (req.url.split("?")[0] == "/image") {
res.writeHead(200, {
"Content-Type": "image/jpeg",
if (path == "/image") {
conn.write(
"HTTP/1.1 200 OK\r\n" +
"Content-Type: multipart/x-mixed-replace;boundary=" + boundary + "\r\n" +
"\r\n");
conn.write(
"--" + boundary + "\r\n" +
"Content-Type: image/jpeg\r\n" +
"\r\n");
conn.write(imgdata);
conn.write(
"\r\n" +
"--" + boundary + "\r\n");

let idx = waiters.length;
waiters.push({ w: conn, fresh: true });
conn.on("close", () => {
delete waiters[idx];
});
res.end(imgdata);
} else if (req.url.split("?")[0] == "/wait") {
waiters.push(res);
} else {
res.writeHead(200);
res.end(html);
conn.write(
"HTTP/1.1 200 OK\r\n" +
"Connection: close\r\n" +
"Content-Type: text/html\r\n" +
"\r\n");
conn.write(html);
conn.end();
}
}

net.createServer(async conn => {
console.log("Connection", conn.remoteAddress);

let reader = new Reader(conn);

let magic = await reader.readAll(4);
if (magic.equals(Buffer.from("GET "))) {
return respondToHttp(reader, conn);
}

let length = magic.readUInt32BE(0);
let received = Buffer.alloc(0);
while (reader.good) {
for (let waiter of waiters) {
if (waiter == null) continue;
waiter.fresh = false;
waiter.w.write(
"Content-Type: image/jpeg\r\n" +
"\r\n");
}

while (length > 0) {
let buf = await reader.read(length);
if (buf.length == 0) {
console.log(conn.remoteAddress, "disconnected.");
return;
}

for (let waiter of waiters) {
if (waiter && !waiter.fresh) {
waiter.w.write(buf);
}
}

received = Buffer.concat([received, buf]);
length -= buf.length;
}

for (let waiter of waiters) {
if (waiter && !waiter.fresh) {
waiter.w.write(
"\r\n" +
"--" + boundary + "\r\n");
}
}

imgdata = received;

received = await reader.readAll(4);
if (received.length < 4) {
console.log(conn.remoteAddress, "disconnected.");
return;
}

length = received.readUInt32BE(0);
received = Buffer.alloc(0);
}
}).listen(httpport, "0.0.0.0");
console.log(`HTTP listening on 0.0.0.0:${httpport}`);
}).on("error", err => console.error(err)).listen(tcpport, "0.0.0.0");
console.log(`TCP listening on 0.0.0.0:${tcpport}`);

Loading…
Cancel
Save