Skip to main content

capture/
capture.rs

1use std::sync::Arc;
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::time::Duration;
4
5use camera_stream::device::{CameraDevice, CameraManager};
6use camera_stream::frame::{Frame, Timestamp};
7use camera_stream::stream::CameraStream;
8
9fn main() {
10    #[cfg(target_os = "macos")]
11    {
12        use camera_stream::platform::macos::device::MacosCameraManager;
13
14        let manager = MacosCameraManager::default();
15
16        // Discover devices
17        let devices = manager
18            .discover_devices()
19            .expect("failed to discover devices");
20        println!("Found {} camera(s):", devices.len());
21        for (i, dev) in devices.iter().enumerate() {
22            println!("  [{}] {} (id: {})", i, dev.name(), dev.id());
23        }
24
25        if devices.is_empty() {
26            println!("No cameras found.");
27            return;
28        }
29
30        // Use default device
31        let device = manager
32            .default_device()
33            .expect("failed to get default device")
34            .expect("no default camera");
35
36        println!("\nUsing: {} ({})", device.name(), device.id());
37
38        // Print supported formats
39        let formats = device.supported_formats().expect("failed to get formats");
40        println!("\nSupported formats ({} total):", formats.len());
41        for (i, f) in formats.iter().take(10).enumerate() {
42            println!(
43                "  [{}] {:?} {}x{} ({} frame rate range(s))",
44                i,
45                f.pixel_format,
46                f.size.width,
47                f.size.height,
48                f.frame_rate_ranges.len(),
49            );
50            for rr in &f.frame_rate_ranges {
51                println!("       {:.1}-{:.1} fps", rr.min.as_f64(), rr.max.as_f64(),);
52            }
53        }
54        if formats.len() > 10 {
55            println!("  ... and {} more", formats.len() - 10);
56        }
57
58        // Pick first format or a reasonable default
59        let config =
60            if let Some(f) = formats.first() {
61                let rate = f.frame_rate_ranges.first().map(|r| r.max).unwrap_or(
62                    camera_stream::FrameRate {
63                        numerator: 30000,
64                        denominator: 1000,
65                    },
66                );
67                camera_stream::StreamConfig {
68                    pixel_format: f.pixel_format,
69                    size: f.size,
70                    frame_rate: rate,
71                }
72            } else {
73                println!("No supported formats found.");
74                return;
75            };
76
77        println!(
78            "\nOpening with {:?} {}x{} @ {:.1} fps",
79            config.pixel_format,
80            config.size.width,
81            config.size.height,
82            config.frame_rate.as_f64(),
83        );
84
85        let mut stream = device.open(&config).expect("failed to open stream");
86
87        let frame_count = Arc::new(AtomicU64::new(0));
88        let count_clone = frame_count.clone();
89        let target_frames: u64 = 60;
90
91        stream
92            .start(move |frame| {
93                let n = count_clone.fetch_add(1, Ordering::Relaxed) + 1;
94                let planes = frame.planes();
95                let total_bytes: usize = planes.iter().map(|p| p.data.len()).sum();
96                println!(
97                    "Frame {}: {:?} {}x{} ts={:.3}s planes={} bytes={}",
98                    n,
99                    frame.pixel_format(),
100                    frame.size().width,
101                    frame.size().height,
102                    frame.timestamp().as_secs_f64(),
103                    planes.len(),
104                    total_bytes,
105                );
106            })
107            .expect("failed to start stream");
108
109        // Wait until we've captured enough frames
110        loop {
111            std::thread::sleep(Duration::from_millis(100));
112            if frame_count.load(Ordering::Relaxed) >= target_frames {
113                break;
114            }
115        }
116
117        stream.stop().expect("failed to stop stream");
118        println!(
119            "\nDone. Captured {} frames.",
120            frame_count.load(Ordering::Relaxed)
121        );
122    }
123
124    #[cfg(not(target_os = "macos"))]
125    {
126        println!("This example only works on macOS.");
127    }
128}