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 ## 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/. 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). 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. 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 ```yml
http_server: 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. 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 ## 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 { hideBin } from "yargs/helpers";
import yargs from "yargs/yargs"; import yargs from "yargs/yargs";
import { captureSingle } from "../capture_single.js";
import { serveHttp } from "../http_server.js"; import { serveHttp } from "../http_server.js";
import { pair } from "../pair.js"; import { pair } from "../pair.js";
import { loadConfig, config } from "../settings.js"; import { loadConfig, config } from "../settings.js";
@@ -70,5 +71,24 @@ yargs(hideBin(process.argv))
pair({ ssid: argv.ssid, password: argv.password }); 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() .demandCommand()
.parseSync(); .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 // this should always be enabled but currently it seems to cause more
// visual distortion than just missing some frames // visual distortion than just missing some frames
if (!config.cameras[session.devName].fix_packet_loss) { if (!config.cameras[session.devName]?.fix_packet_loss) {
return; return;
} }