support single-frame capture

This commit is contained in:
David Ventura
2024-07-01 16:03:21 +02:00
parent 80b1e4643b
commit f85248bc6a
4 changed files with 69 additions and 3 deletions
+9 -2
View File
@@ -27,6 +27,7 @@ Connect to the device's access point (e.g., FTYC811847AGFDZ) and run `node dist/
## Running
### HTTP Server
To execute the HTTP server, run `node dist/bin.cjs http_server`; you can access the JPEG stream at http://localhost:5000/.
The roundtrip delay when using MJPEG is [~350ms](pics/delay.jpg?raw=true).
@@ -38,9 +39,9 @@ There's a basic UI which can display multiple cameras:
Clicking on the image will take you to a page that has audio streaming. Click the button below the image to mute/unmute the audio.
## Settings
#### Settings
You can provide a config file in `yml` format, check `config.example.yml` for an updated example:
You can provide a config file in `yml` format, then pass it as an argument: `node bin.cjs http_server --config_file <your_config.yml>`
```yml
http_server:
@@ -82,6 +83,12 @@ All keys are optional
You must restart the HTTP server for changes to the settings file to take effect.
### Single capture mode
```bash
node bin.cjs frame --discovery_ip 192.168.40.104 --out out.jpg
```
----
## Protocol
+39
View File
@@ -0,0 +1,39 @@
import fs from "node:fs";
import { DevSerial } from "./impl.js";
import { RemoteInfo } from "dgram";
import { logger } from "./logger.js";
import { startVideoStream } from "./session.js";
import { discoverDevices } from "./discovery.js";
import { Session } from "./session.js";
import { Handlers, makeSession } from "./session.js";
import { config } from "./settings.js";
const sessions: Record<string, Session> = {};
export const captureSingle = ({ discovery_ip, out_file }: { discovery_ip: string; out_file: string }) => {
let devEv = discoverDevices([discovery_ip]);
const startSession = (s: Session) => {
startVideoStream(s);
logger.info(`Camera ${s.devName} is now ready to stream`);
};
devEv.on("discover", (rinfo: RemoteInfo, dev: DevSerial) => {
if (dev.devId in sessions) {
logger.info(`Camera ${dev.devId} at ${rinfo.address} already discovered, ignoring`);
return;
}
logger.info(`Discovered camera ${dev.devId} at ${rinfo.address}`);
const s = makeSession(Handlers, dev, rinfo, startSession, 5000);
sessions[dev.devId] = s;
config.cameras[dev.devId] = { fix_packet_loss: false };
s.eventEmitter.on("frame", () => {
const assembled = Buffer.concat(s.curImage);
fs.writeFileSync(out_file, assembled);
logger.info(`Got frame. Exiting`);
devEv.emit("close");
s.close();
});
});
};
+20
View File
@@ -2,6 +2,7 @@ import process from "node:process";
import { hideBin } from "yargs/helpers";
import yargs from "yargs/yargs";
import { captureSingle } from "../capture_single.js";
import { serveHttp } from "../http_server.js";
import { pair } from "../pair.js";
import { loadConfig, config } from "../settings.js";
@@ -70,5 +71,24 @@ yargs(hideBin(process.argv))
pair({ ssid: argv.ssid, password: argv.password });
},
)
.command(
"frame",
"capture a single frame from the first discovered camera",
(yargs) => {
return yargs
.option("log_level", { describe: "Set log level", default: "info" })
.option("discovery_ip", { describe: "Camera discovery IP address", default: "192.168.1.255" })
.option("out", { describe: "Path for output file" })
.demandOption(["out"])
.string(["out", "discovery_ip"]);
},
(argv) => {
buildLogger(argv.log_level, undefined);
if (majorVersion < 16) {
logger.error(`Node version ${majorVersion} is not supported, may malfunction`);
}
captureSingle({ discovery_ip: argv.discovery_ip, out_file: argv.out });
},
)
.demandCommand()
.parseSync();
+1 -1
View File
@@ -249,7 +249,7 @@ const deal_with_data = (session: Session, dv: DataView) => {
}
// this should always be enabled but currently it seems to cause more
// visual distortion than just missing some frames
if (!config.cameras[session.devName].fix_packet_loss) {
if (!config.cameras[session.devName]?.fix_packet_loss) {
return;
}