eye_hal/platform/
mod.rs

1//! Hardware Abstraction Layer (HAL)
2//!
3//! Multiple backends can be implemented for a given platform.
4
5use std::array;
6
7use crate::control;
8use crate::device;
9use crate::error::Result;
10use crate::stream::Descriptor as StreamDescriptor;
11use crate::traits::{Context as ContextTrait, Device as DeviceTrait, Stream as StreamTrait};
12
13#[cfg(target_os = "linux")]
14pub(crate) mod v4l2;
15
16#[cfg(any(target_os = "windows", feature = "plat-uvc"))]
17pub(crate) mod uvc;
18
19#[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
20pub(crate) mod openpnp;
21
22/// Platform context
23///
24/// Leaky abstraction: if you require access to platform specific features, match the enum instance
25/// to get the underlying HAL implementation.
26///
27/// A context is used to query platform properties, available devices and more.
28pub enum Context<'a> {
29    /// Can be used to wrap your own struct
30    Custom(Box<dyn 'a + ContextTrait<'a, Device = Device<'a>> + Send>),
31    #[cfg(target_os = "linux")]
32    /// Video4Linux2 context
33    V4l2(v4l2::context::Context),
34    #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
35    /// Universal Video Class context
36    Uvc(uvc::context::Context),
37    #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
38    /// Open Pick and Place
39    OpenPnP(openpnp::context::Context),
40}
41
42impl<'a> Context<'a> {
43    pub fn all() -> impl Iterator<Item = Context<'a>> {
44        array::IntoIter::new([
45            #[cfg(target_os = "linux")]
46            Context::V4l2(v4l2::context::Context {}),
47            #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
48            Context::Uvc(uvc::context::Context {}),
49            #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
50            Context::OpenPnP(openpnp::context::Context {}),
51        ])
52    }
53}
54
55impl<'a> Default for Context<'a> {
56    fn default() -> Self {
57        Self::all()
58            .next()
59            .expect("no contexts available for this platform")
60    }
61}
62
63impl<'a> ContextTrait<'a> for Context<'a> {
64    type Device = Device<'a>;
65
66    fn devices(&self) -> Result<Vec<device::Description>> {
67        match self {
68            Self::Custom(ctx) => ctx.devices(),
69            #[cfg(target_os = "linux")]
70            Self::V4l2(ctx) => ctx.devices(),
71            #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
72            Self::Uvc(ctx) => ctx.devices(),
73            #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
74            Self::OpenPnP(ctx) => ctx.devices(),
75        }
76    }
77
78    fn open_device(&self, uri: &str) -> Result<Self::Device> {
79        match self {
80            Self::Custom(ctx) => ctx.open_device(uri),
81            #[cfg(target_os = "linux")]
82            Self::V4l2(ctx) => Ok(Device::V4l2(ctx.open_device(uri)?)),
83            #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
84            Self::Uvc(ctx) => Ok(Device::Uvc(ctx.open_device(uri)?)),
85            #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
86            Self::OpenPnP(ctx) => Ok(Device::OpenPnP(ctx.open_device(uri)?)),
87        }
88    }
89}
90
91/// Platform device
92///
93/// Leaky abstraction: if you require access to platform specific features, match the enum instance
94/// to get the underlying HAL implementation.
95///
96/// A device is used to read/write control properties, start video streams and more.
97pub enum Device<'a> {
98    /// Can be used to wrap your own struct
99    Custom(Box<dyn 'a + DeviceTrait<'a, Stream = Stream<'a>> + Send>),
100    #[cfg(target_os = "linux")]
101    /// Video4Linux2 device handle
102    V4l2(v4l2::device::Handle),
103    #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
104    /// Universal Video Class device handle
105    Uvc(uvc::device::Handle<'a>),
106    #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
107    /// Open Pick and Place
108    OpenPnP(openpnp::device::Handle),
109}
110
111impl<'a> DeviceTrait<'a> for Device<'a> {
112    type Stream = Stream<'a>;
113
114    fn streams(&self) -> Result<Vec<StreamDescriptor>> {
115        match self {
116            Self::Custom(dev) => dev.streams(),
117            #[cfg(target_os = "linux")]
118            Self::V4l2(dev) => dev.streams(),
119            #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
120            Self::Uvc(dev) => dev.streams(),
121            #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
122            Self::OpenPnP(dev) => dev.streams(),
123        }
124    }
125
126    fn controls(&self) -> Result<Vec<control::Descriptor>> {
127        match self {
128            Self::Custom(dev) => dev.controls(),
129            #[cfg(target_os = "linux")]
130            Self::V4l2(dev) => dev.controls(),
131            #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
132            Self::Uvc(dev) => dev.controls(),
133            #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
134            Self::OpenPnP(dev) => dev.controls(),
135        }
136    }
137
138    fn control(&self, id: u32) -> Result<control::State> {
139        match self {
140            Self::Custom(dev) => dev.control(id),
141            #[cfg(target_os = "linux")]
142            Self::V4l2(dev) => dev.control(id),
143            #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
144            Self::Uvc(dev) => dev.control(id),
145            #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
146            Self::OpenPnP(dev) => dev.control(id),
147        }
148    }
149
150    fn set_control(&mut self, id: u32, val: &control::State) -> Result<()> {
151        match self {
152            Self::Custom(dev) => dev.set_control(id, val),
153            #[cfg(target_os = "linux")]
154            Self::V4l2(dev) => dev.set_control(id, val),
155            #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
156            Self::Uvc(dev) => dev.set_control(id, val),
157            #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
158            Self::OpenPnP(dev) => dev.set_control(id, val),
159        }
160    }
161
162    fn start_stream(&self, desc: &StreamDescriptor) -> Result<Self::Stream> {
163        match self {
164            Self::Custom(dev) => dev.start_stream(desc),
165            #[cfg(target_os = "linux")]
166            Self::V4l2(dev) => Ok(Stream::V4l2(dev.start_stream(desc)?)),
167            #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
168            Self::Uvc(dev) => Ok(Stream::Uvc(dev.start_stream(desc)?)),
169            #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
170            Self::OpenPnP(dev) => Ok(Stream::OpenPnP(dev.start_stream(desc)?)),
171        }
172    }
173}
174
175/// Platform stream
176///
177/// Leaky abstraction: if you require access to platform specific features, match the enum instance
178/// to get the underlying HAL implementation.
179///
180/// A stream is used read frames from a camera device. Many HAL implementations feature advanced
181/// I/O method such as memory mapped streaming, DMA and more. We attempt to automatically select
182/// the best method available.
183pub enum Stream<'a> {
184    /// Can be used to wrap your own struct
185    Custom(Box<dyn 'a + for<'b> StreamTrait<'b, Item = Result<&'b [u8]>> + Send>),
186    #[cfg(target_os = "linux")]
187    /// Video4Linux2 stream handle
188    V4l2(v4l2::stream::Handle<'a>),
189    #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
190    /// Universal Video Class stream handle
191    Uvc(uvc::stream::Handle<'a>),
192    #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
193    /// Open Pick and Place
194    OpenPnP(openpnp::stream::Handle),
195}
196
197impl<'a, 'b> StreamTrait<'b> for Stream<'a> {
198    type Item = Result<&'b [u8]>;
199
200    fn next(&'b mut self) -> Option<Self::Item> {
201        match self {
202            Self::Custom(stream) => stream.next(),
203            #[cfg(target_os = "linux")]
204            Self::V4l2(stream) => stream.next(),
205            #[cfg(any(target_os = "windows", feature = "plat-uvc"))]
206            Self::Uvc(stream) => stream.next(),
207            #[cfg(any(target_os = "macos", feature = "plat-openpnp"))]
208            Self::OpenPnP(stream) => stream.next(),
209        }
210    }
211}