Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions examples/window-rgb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ fn main() {
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();

let devices = Device::list_all_devices();
println!("Available devices");
for (i, device) in devices.iter().enumerate() {
println!("#{}: {}", i, device.name());
}

let mut camera = Camera::new_default_device();
camera.start();

Expand Down
30 changes: 29 additions & 1 deletion src/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,24 @@ use super::win_mf as backend;
#[cfg(target_os = "linux")]
use super::linux_v4l2 as backend;

#[derive(Debug)]
pub struct Device {
inner: backend::Device,
}

impl Device {
pub fn list_all_devices() -> Vec<Self> {
backend::Device::list_all_devices()
.into_iter()
.map(|device| Device { inner: device })
.collect()
}

pub fn name(&self) -> String {
self.inner.name()
}
}

#[derive(Debug, Clone)]
pub struct Camera {
inner: backend::Camera,
}
Expand All @@ -26,6 +43,10 @@ impl Camera {
Self { inner: backend::Camera::new_default_device() }
}

pub fn new_from_device(device: Device) -> Self {
Self { inner: backend::Camera::new_from_device(device.inner) }
}

pub fn start(&self) {
self.inner.start();
}
Expand Down Expand Up @@ -64,11 +85,18 @@ impl<'a> FrameData<'a> {
}

pub(crate) trait InnerCamera: std::fmt::Debug {
type Device;
type Frame;

fn new_from_device(device: Self::Device) -> Self;
fn new_default_device() -> Self;
fn start(&self);
fn stop(&self);
fn wait_for_frame(&self) -> Option<Self::Frame>;
fn change_device(&mut self);
}

pub(crate) trait InnerDevice: Sized {
fn list_all_devices() -> Vec<Self>;
fn name(&self) -> String;
}
51 changes: 34 additions & 17 deletions src/linux_v4l2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,28 @@ use std::marker::PhantomData;
use std::sync::RwLock;

use crate::InnerCamera;
use crate::InnerDevice;

pub struct Device(v4l::context::Node);

impl InnerDevice for Device {
fn list_all_devices() -> Vec<Self> {
v4l::context::enum_devices()
.into_iter()
.filter_map(|node| {
v4l::Device::with_path(node.path()).ok().map(|device| (node, device))
})
.filter(|(_, device)| device.format().is_ok())
.map(|(node, _)| Device(node))
.collect()
}

fn name(&self) -> String {
name_or_path(&self.0)
}
}

#[derive(Debug, Clone)]
pub struct Camera {
device: RwLock<v4l::Device>,
device_name: String,
Expand All @@ -22,7 +43,7 @@ fn name_or_path(device_node: &v4l::context::Node) -> String {
device_node.name().unwrap_or_else(|| device_node.path().to_string_lossy().to_string())
}

fn get_next_best_format(device: &Device) -> Format {
fn get_next_best_format(device: &v4l::Device) -> Format {
let _rgb = FourCC::new(b"RGB3");
let mut fmt = device.format().expect("device.format()");
let size = device
Expand Down Expand Up @@ -52,7 +73,7 @@ fn display_node(node: &Node) {
}

#[allow(unused)]
fn display_device_formats(device: &Device) {
fn display_device_formats(device: &v4l::Device) {
println!("Device formats:");
for fmt in device.enum_formats().unwrap() {
println!(" {:?}", fmt);
Expand All @@ -63,15 +84,6 @@ fn display_device_formats(device: &Device) {
}
}

fn enum_devices() -> Vec<Node> {
v4l::context::enum_devices()
.into_iter()
.filter_map(|node| Device::with_path(node.path()).ok().map(|device| (node, device)))
.filter(|(_, device)| device.format().is_ok())
.map(|(node, _)| node)
.collect()
}

impl Camera {
fn from_node(node: &v4l::context::Node) -> Self {
let device = v4l::Device::with_path(node.path()).unwrap();
Expand All @@ -86,10 +98,15 @@ impl Camera {

impl InnerCamera for Camera {
type Frame = Frame;
type Device = Device;

fn new_from_device(device: Self::Device) -> Self {
Self::from_node(&device.0)
}

fn new_default_device() -> Self {
let node = enum_devices().into_iter().next().unwrap();
Self::from_node(&node)
let device = Device::list_all_devices().into_iter().next().unwrap();
Self::from_node(&device.0)
}

fn start(&self) {
Expand Down Expand Up @@ -124,15 +141,15 @@ impl InnerCamera for Camera {
}

fn change_device(&mut self) {
let devices = enum_devices();
if let Some(pos) = devices.iter().position(|n| name_or_path(n) == self.device_name) {
let devices = Device::list_all_devices();
if let Some(pos) = devices.iter().position(|d| name_or_path(&d.0) == self.device_name) {
let new_pos = (pos + 1) % devices.len();
if new_pos != pos {
*self = Self::from_node(&devices[new_pos]);
*self = Self::from_node(&devices[new_pos].0);
self.start();
}
} else if !devices.is_empty() {
*self = Self::from_node(&devices[0]);
*self = Self::from_node(&devices[0].0);
self.start();
} else {
self.stop();
Expand Down
38 changes: 30 additions & 8 deletions src/mac_avf/camera.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
use crate::{InnerCamera, InnerDevice};

use super::*;
use objc2::rc::Id;
use std::sync::Arc;

#[derive(Debug)]
pub struct Device(Id<AVCaptureDevice>);

impl InnerDevice for Device {
fn list_all_devices() -> Vec<Device> {
AVCaptureDevice::all_video_devices().into_iter().map(Device).collect()
}

fn name(&self) -> String {
self.0.localized_name().to_string()
}
}

#[derive(Debug, Clone)]
pub struct Camera {
device: Id<AVCaptureDevice>,
input: Id<AVCaptureDeviceInput>,
Expand All @@ -21,9 +35,12 @@ pub struct FrameData<'a> {
pixels: Pixels<'a>,
}

impl Camera {
pub fn new_default_device() -> Self {
let device = AVCaptureDevice::default_video_device();
impl InnerCamera for Camera {
type Frame = Frame;
type Device = Device;

fn new_from_device(device: Device) -> Self {
let device = device.0;
let input = AVCaptureDeviceInput::from_device(&device).unwrap();
let output = AVCaptureVideoDataOutput::new();
output.set_video_settings(&video_settings_from_pixel_format("ARGB"));
Expand All @@ -37,19 +54,24 @@ impl Camera {
Camera { device, input, output, session, slot }
}

pub fn start(&self) {
fn new_default_device() -> Self {
let device = AVCaptureDevice::default_video_device();
Self::new_from_device(Device(device))
}

fn start(&self) {
self.session.start_running();
}

pub fn stop(&self) {
fn stop(&self) {
self.session.stop_running();
}

pub fn wait_for_frame(&self) -> Option<Frame> {
fn wait_for_frame(&self) -> Option<Frame> {
self.slot.wait_for_sample().map(|sample| Frame { sample })
}

pub fn change_device(&mut self) {
fn change_device(&mut self) {
let devices = AVCaptureDevice::all_video_devices();
let Some(index) = devices.iter().position(|d| d.unique_id() == self.device.unique_id())
else {
Expand Down
45 changes: 20 additions & 25 deletions src/win_mf/camera.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::{InnerCamera, InnerDevice};

use super::mf::*;

use std::{sync::mpsc::*, time::Duration};

use windows::Win32::Media::MediaFoundation::*;

#[allow(unused)]
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Camera {
engine: IMFCaptureEngine,
device: Device,
Expand All @@ -24,8 +26,11 @@ pub struct FrameData<'a> {
data: &'a [u8],
}

impl Camera {
pub fn new_default_device() -> Self {
impl InnerCamera for Camera {
type Device = Device;
type Frame = Frame;

fn new_from_device(device: Self::Device) -> Self {
co_initialize_multithreaded();
media_foundation_startup().expect("media_foundation_startup");

Expand All @@ -35,9 +40,6 @@ impl Camera {
let event_cb = CaptureEventCallback { event_tx }.into();
let sample_cb = CaptureSampleCallback { sample_tx }.into();

let devices = Device::enum_devices();
let Some(device) = devices.first().cloned() else { todo!() };

init_capture_engine(&engine, Some(&device.source), &event_cb).unwrap();

let camera = Camera { engine, device, event_rx, sample_rx, event_cb, sample_cb };
Expand All @@ -46,32 +48,36 @@ impl Camera {
camera
}

pub fn start(&self) {
fn new_default_device() -> Self {
let devices = Device::list_all_devices();
let Some(device) = devices.into_iter().next() else { todo!() };
Self::new_from_device(device)
}

fn start(&self) {
unsafe { self.engine.StartPreview().unwrap() }
}

pub fn stop(&self) {
fn stop(&self) {
capture_engine_stop_preview(&self.engine).unwrap();
}

pub fn wait_for_frame(&self) -> Option<Frame> {
fn wait_for_frame(&self) -> Option<Self::Frame> {
self.sample_rx
// TODO sometimes running two engines on the same camera breaks frame delivery, so wait not too long
.recv_timeout(Duration::from_secs(3))
.ok()
.flatten()
.and_then(|sample| {
let Some(mt) = capture_engine_sink_get_media_type(&self.engine).ok() else {
return None;
};
let mt = capture_engine_sink_get_media_type(&self.engine).ok()?;
let width = mt.frame_width();
let height = mt.frame_height();
sample_to_locked_buffer(&sample, width, height).ok()
})
.map(|buffer: LockedBuffer| Frame { buffer })
}

pub fn change_device(&mut self) {
fn change_device(&mut self) {
let devices: Vec<Device> = enum_device_sources().into_iter().map(Device::new).collect();
let Some(index) = devices.iter().position(|d| d.id() == self.device.id()) else { return };
let new_index = (index + 1) % devices.len();
Expand All @@ -81,18 +87,7 @@ impl Camera {
}
let new_device = devices[new_index].clone();

let engine = new_capture_engine().unwrap();
let (event_tx, event_rx) = channel::<CaptureEngineEvent>();
let (sample_tx, sample_rx) = channel::<Option<IMFSample>>();
let event_cb = CaptureEventCallback { event_tx }.into();
let sample_cb = CaptureSampleCallback { sample_tx }.into();

init_capture_engine(&engine, Some(&new_device.source), &event_cb).unwrap();

*self = Camera { engine, device: new_device, event_rx, sample_rx, event_cb, sample_cb };
self.wait_for_event(CaptureEngineEvent::Initialized);
self.prepare_source_sink();
self.start(); // TODO watch out about playing state
*self = Self::new_from_device(new_device);
}
}

Expand Down
Loading