From 737f0248b26f0025492ed9153b2c9e54c894ad4f Mon Sep 17 00:00:00 2001 From: DavidVentura Date: Fri, 2 Feb 2024 12:06:37 +0100 Subject: [PATCH] add support for audio streaming --- README.md | 2 ++ asd.html | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ http_server.ts | 60 ++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 asd.html diff --git a/README.md b/README.md index 995de48..14c0c5b 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ There's a basic UI which can display multiple cameras: The server will send a broadcast packet every few seconds to discover all the cameras available; this means that it *must* run in the same broadcast domain (VLAN) as your cameras. +Clicking on the image will take you to a page that has audio streaming enabled. + ## Pairing a new camera Connect to the device's access point and run `SSID= PSK= make pair`. diff --git a/asd.html b/asd.html new file mode 100644 index 0000000..f523ff4 --- /dev/null +++ b/asd.html @@ -0,0 +1,62 @@ + +

${id}


+ + diff --git a/http_server.ts b/http_server.ts index e484656..befce11 100644 --- a/http_server.ts +++ b/http_server.ts @@ -1,5 +1,5 @@ import { RemoteInfo } from "dgram"; -import { createWriteStream } from "node:fs"; +import { readFileSync } from "node:fs"; import http from "node:http"; import { discoverDevices } from "./discovery.js"; @@ -11,15 +11,52 @@ const opts = { debug: false, ansi: false, // discovery_ip: "192.168.40.255", //, "192.168.1.255" - discovery_ip: "192.168.40.101", + discovery_ip: "192.168.40.104", attempt_to_fix_packet_loss: false, + //attempt_to_fix_packet_loss: true, }; let BOUNDARY = "a very good boundary line"; let responses: Record = {}; +let audioResponses: Record = {}; let sessions: Record = {}; const server = http.createServer((req, res) => { + if (req.url.startsWith("/ui/")) { + let devId = req.url.split("/")[2]; + let s = sessions[devId]; + if (s === undefined) { + res.writeHead(400); + res.end("invalid ID"); + return; + } + if (!s.connected) { + res.writeHead(400); + res.end("Nothing online"); + return; + } + const ui = readFileSync("asd.html").toString(); + res.end(ui.replace(/\${id}/g, devId)); + return; + } + if (req.url.startsWith("/audio/")) { + let devId = req.url.split("/")[2]; + let s = sessions[devId]; + if (s === undefined) { + res.writeHead(400); + res.end("invalid ID"); + return; + } + if (!s.connected) { + res.writeHead(400); + res.end("Nothing online"); + return; + } + res.setHeader("Content-Type", `text/event-stream`); + audioResponses[devId].push(res); + return; + } + if (req.url.startsWith("/camera/")) { let devId = req.url.split("/")[2]; console.log("requested for", devId); @@ -35,6 +72,7 @@ const server = http.createServer((req, res) => { res.end("Nothing online"); return; } + res.setHeader("Content-Type", `multipart/x-mixed-replace; boundary="${BOUNDARY}"`); responses[devId].push(res); res.on("close", () => { @@ -43,7 +81,9 @@ const server = http.createServer((req, res) => { }); } else { res.write(``); - Object.keys(sessions).forEach((id) => res.write(`
`)); + Object.keys(sessions).forEach((id) => + res.write(`

${id}


`), + ); res.write(``); res.end(); } @@ -58,9 +98,10 @@ devEv.on("discover", (rinfo: RemoteInfo, dev: DevSerial) => { console.log(`discovered ${dev.devId} - ${rinfo.address}`); responses[dev.devId] = []; + audioResponses[dev.devId] = []; const s = makeSession(Handlers, dev, rinfo, startVideoStream, opts); - const withAudio = false; + const withAudio = true; const header = Buffer.from(`--${BOUNDARY}\r\nContent-Type: image/jpeg\r\n\r\n`); s.eventEmitter.on("frame", () => { @@ -77,9 +118,14 @@ devEv.on("discover", (rinfo: RemoteInfo, dev: DevSerial) => { delete sessions[dev.devId]; }); if (withAudio) { - const audioFd = createWriteStream(`audio.pcm`); - s.eventEmitter.on("audio", (frame: Buffer) => { - audioFd.write(frame); + s.eventEmitter.on("audio", ({ gap, data }) => { + // ew, maybe WS? + var b64encoded = Buffer.from(data).toString("base64"); + audioResponses[dev.devId].forEach((res) => { + res.write("data: "); + res.write(b64encoded); + res.write("\n\n"); + }); }); } sessions[dev.devId] = s;