Skip to main content

lager/nets/
webcam.rs

1//! Webcam nets: MJPEG streaming from USB cameras attached to the box, for
2//! visually observing a DUT during tests.
3
4use super::net_handle;
5pub use crate::wire::{WebcamStatus, WebcamStream};
6
7pub(crate) mod ops {
8    use serde_json::json;
9
10    use crate::error::Result;
11    use crate::wire::{
12        net_command, value_as, CommandResponse, Op, Timeout, WebcamStatus, WebcamStream,
13    };
14
15    const ROLE: &str = "webcam";
16
17    fn parse_stopped(resp: CommandResponse) -> Result<bool> {
18        let status: WebcamStopped = value_as(resp)?;
19        Ok(status.stopped)
20    }
21
22    #[derive(serde::Deserialize)]
23    struct WebcamStopped {
24        #[serde(default)]
25        stopped: bool,
26    }
27
28    fn parse_url(resp: CommandResponse) -> Result<Option<String>> {
29        let status: WebcamStatus = value_as(resp)?;
30        Ok(status.url)
31    }
32
33    pub(crate) fn start(name: &str) -> Op<WebcamStream> {
34        Op {
35            // Starting ffmpeg/the stream subprocess can take a few seconds.
36            req: net_command(
37                name,
38                ROLE,
39                "start",
40                json!({}),
41                Timeout::After(std::time::Duration::from_secs(30)),
42            ),
43            parse: value_as::<WebcamStream>,
44        }
45    }
46
47    pub(crate) fn stop(name: &str) -> Op<bool> {
48        Op {
49            req: net_command(name, ROLE, "stop", json!({}), Timeout::Default),
50            parse: parse_stopped,
51        }
52    }
53
54    pub(crate) fn status(name: &str) -> Op<WebcamStatus> {
55        Op {
56            req: net_command(name, ROLE, "status", json!({}), Timeout::Default),
57            parse: value_as::<WebcamStatus>,
58        }
59    }
60
61    pub(crate) fn url(name: &str) -> Op<Option<String>> {
62        Op {
63            req: net_command(name, ROLE, "url", json!({}), Timeout::Default),
64            parse: parse_url,
65        }
66    }
67}
68
69net_handle! {
70    /// Handle for a webcam net.
71    ///
72    /// Stream URLs point at the box host as the client reached it; pass the
73    /// URL to any MJPEG viewer (browser, VLC, OpenCV).
74    sync: Webcam,
75    async: AsyncWebcam,
76    methods: {
77        /// Start the MJPEG stream (idempotent: reports `already_running`
78        /// when a stream is already up).
79        fn start() -> WebcamStream = ops::start;
80        /// Stop the stream; returns `false` if none was running.
81        fn stop() -> bool = ops::stop;
82        /// Current stream state (running, URL, port, device).
83        fn status() -> WebcamStatus = ops::status;
84        /// Stream URL, or `None` when no stream is running.
85        fn url() -> Option<String> = ops::url;
86    }
87}