linux_video/
lib.rs

1#![forbid(future_incompatible)]
2#![deny(bad_style/*, missing_docs*/)]
3#![doc = include_str!("../README.md")]
4
5pub use linux_video_core as types;
6use linux_video_core::private::*;
7use types::*;
8
9use std::{
10    fs::File,
11    os::unix::io::{AsRawFd, RawFd},
12    path::{Path, PathBuf},
13};
14
15/// Video device
16pub struct Device {
17    file: File,
18}
19
20impl AsRawFd for Device {
21    fn as_raw_fd(&self) -> RawFd {
22        self.file.as_raw_fd()
23    }
24}
25
26impl Device {
27    /// List video devices
28    pub fn list() -> Result<Devices> {
29        Devices::new()
30    }
31
32    /// Open video device
33    pub fn open(path: impl AsRef<Path>) -> Result<Self> {
34        let file = open(path, false)?;
35
36        Ok(Device { file })
37    }
38
39    /// Get capabilities
40    pub fn capabilities(&self) -> Result<Capability> {
41        Internal::<Capability>::query(self.as_raw_fd()).map(Internal::into_inner)
42    }
43
44    /// Get controls
45    pub fn controls(&self, class: Option<CtrlClass>) -> Controls<'_> {
46        let last_id = class.map(|c| c as _).unwrap_or_default();
47
48        Controls {
49            device: self,
50            class,
51            last_id,
52        }
53    }
54
55    /// Get control by identifier
56    pub fn control(&self, id: impl Into<u32>) -> Result<Control> {
57        let ctrl = Internal::<QueryExtCtrl>::query_fallback(self.as_raw_fd(), id.into())?;
58
59        Ok(Control { ctrl })
60    }
61
62    /// Get control menu items
63    pub fn control_items(&self, control: &Control) -> Option<MenuItems<'_>> {
64        if control.is_menu() {
65            Some(MenuItems {
66                device: self,
67                ctrl_type: control.type_(),
68                ctrl_id: control.id(),
69                index_iter: control.min() as _..=control.max() as _,
70            })
71        } else {
72            None
73        }
74    }
75
76    /// Get control value
77    pub fn get_control<T: GetValue>(&self, value: &mut T) -> Result<()> {
78        value.get(self.as_raw_fd())
79    }
80
81    /// Set control value
82    pub fn set_control<T: SetValue>(&self, value: &T) -> Result<()> {
83        value.set(self.as_raw_fd())
84    }
85
86    /// Get supported formats
87    pub fn formats(&self, type_: BufferType) -> FmtDescs {
88        FmtDescs {
89            device: self,
90            type_,
91            index: 0,
92        }
93    }
94
95    /// Get current format
96    pub fn format(&self, type_: BufferType) -> Result<Format> {
97        let mut fmt = Format::from(type_);
98        self.get_format(&mut fmt)?;
99        Ok(fmt)
100    }
101
102    /// Get current format
103    pub fn get_format(&self, fmt: &mut Format) -> Result<()> {
104        Internal::from(fmt).get(self.as_raw_fd())
105    }
106
107    /// Set current format
108    pub fn set_format(&self, fmt: &mut Format) -> Result<()> {
109        Internal::from(fmt).set(self.as_raw_fd())
110    }
111
112    /// Try format without set it
113    pub fn try_format(&self, fmt: &mut Format) -> Result<()> {
114        Internal::from(fmt).try_(self.as_raw_fd())
115    }
116
117    /// Get supported frame sizes
118    pub fn sizes(&self, pixel_format: FourCc) -> FrmSizes {
119        FrmSizes {
120            device: self,
121            pixel_format,
122            index: 0,
123        }
124    }
125
126    /// Get supported frame intervals
127    pub fn intervals(&self, pixel_format: FourCc, width: u32, height: u32) -> FrmIvals {
128        FrmIvals {
129            device: self,
130            pixel_format,
131            width,
132            height,
133            index: 0,
134        }
135    }
136
137    /// Get stream parameters
138    pub fn param(&self, type_: BufferType) -> Result<StreamParm> {
139        let mut param = StreamParm::from(type_);
140        self.get_param(&mut param)?;
141        Ok(param)
142    }
143
144    /// Get stream parameters
145    pub fn get_param(&self, param: &mut StreamParm) -> Result<()> {
146        Internal::from(param).get(self.as_raw_fd())
147    }
148
149    /// Set stream parameters
150    pub fn set_param(&self, param: &mut StreamParm) -> Result<()> {
151        Internal::from(param).set(self.as_raw_fd())
152    }
153
154    /// Create stream to input/output data
155    pub fn stream<Dir: Direction, Met: Method>(
156        &self,
157        type_: ContentType,
158        count: usize,
159    ) -> Result<Stream<Dir, Met>> {
160        Stream::new(self.file.try_clone()?, type_, count)
161    }
162}
163
164/// The interface to get available devices
165pub struct Devices {
166    reader: std::fs::ReadDir,
167}
168
169impl Devices {
170    fn new() -> Result<Self> {
171        std::fs::read_dir("/dev").map(|reader| Devices { reader })
172    }
173
174    /// Get path of the next device
175    pub fn fetch_next(&mut self) -> Result<Option<PathBuf>> {
176        use std::os::unix::fs::FileTypeExt;
177
178        for entry in self.reader.by_ref() {
179            let entry = entry?;
180            if let Some(file_name) = entry.file_name().to_str() {
181                if check_dev_name(file_name).is_some() {
182                    let file_type = entry.file_type()?;
183                    if file_type.is_char_device() {
184                        return Ok(Some(entry.path()));
185                    }
186                }
187            }
188        }
189
190        Ok(None)
191    }
192}
193
194/// The interface to get device controls
195pub struct Controls<'i> {
196    device: &'i Device,
197    class: Option<CtrlClass>,
198    last_id: u32,
199}
200
201impl<'i> Controls<'i> {
202    /// Get next control
203    pub fn fetch_next(&mut self) -> Result<Option<Control>> {
204        if self.last_id == u32::MAX {
205            return Ok(None);
206        }
207
208        if let Some(ctrl) =
209            Internal::<QueryExtCtrl>::query_next_fallback(self.device.as_raw_fd(), self.last_id)?
210        {
211            if self
212                .class
213                .map(|class| class.fast_match(ctrl.id()))
214                .unwrap_or(true)
215            {
216                self.last_id = ctrl.id();
217                Ok(Some(Control { ctrl }))
218            } else {
219                self.last_id = u32::MAX;
220                Ok(None)
221            }
222        } else {
223            self.last_id = u32::MAX;
224            Ok(None)
225        }
226    }
227}
228
229/// The control access interface
230pub struct Control {
231    ctrl: Internal<QueryExtCtrl>,
232}
233
234impl core::ops::Deref for Control {
235    type Target = QueryExtCtrl;
236
237    fn deref(&self) -> &Self::Target {
238        &self.ctrl
239    }
240}
241
242impl AsRef<QueryExtCtrl> for Control {
243    fn as_ref(&self) -> &QueryExtCtrl {
244        &self.ctrl
245    }
246}
247
248impl core::fmt::Display for Control {
249    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
250        self.ctrl.fmt(f)
251    }
252}
253
254/// The interface to get menu items
255pub struct MenuItems<'i> {
256    device: &'i Device,
257    ctrl_type: CtrlType,
258    ctrl_id: u32,
259    index_iter: core::ops::RangeInclusive<u32>,
260}
261
262impl<'i> MenuItems<'i> {
263    /// Get next menu control item
264    pub fn fetch_next(&mut self) -> Result<Option<MenuItem>> {
265        for index in &mut self.index_iter {
266            if let Some(item) = Internal::<MenuItem>::query(
267                self.device.as_raw_fd(),
268                self.ctrl_type,
269                self.ctrl_id,
270                index,
271            )? {
272                return Ok(Some(item.into_inner()));
273            }
274        }
275        Ok(None)
276    }
277}
278
279/// The interface to get format descriptions
280pub struct FmtDescs<'i> {
281    device: &'i Device,
282    type_: BufferType,
283    index: u32,
284}
285
286impl<'i> FmtDescs<'i> {
287    /// Fetch next format description
288    pub fn fetch_next(&mut self) -> Result<Option<FmtDesc>> {
289        if self.index == u32::MAX {
290            return Ok(None);
291        }
292
293        if let Some(desc) =
294            Internal::<FmtDesc>::query(self.device.as_raw_fd(), self.index, self.type_)?
295        {
296            self.index += 1;
297            Ok(Some(desc.into_inner()))
298        } else {
299            self.index = u32::MAX;
300            Ok(None)
301        }
302    }
303}
304
305/// The interface to get drame sizes
306pub struct FrmSizes<'i> {
307    device: &'i Device,
308    pixel_format: FourCc,
309    index: u32,
310}
311
312impl<'i> FrmSizes<'i> {
313    /// Get next frame size value
314    pub fn fetch_next(&mut self) -> Result<Option<FrmSizeEnum>> {
315        if self.index == u32::MAX {
316            return Ok(None);
317        }
318
319        if let Some(size) =
320            Internal::<FrmSizeEnum>::query(self.device.as_raw_fd(), self.index, self.pixel_format)?
321        {
322            self.index += 1;
323            Ok(Some(size.into_inner()))
324        } else {
325            self.index = u32::MAX;
326            Ok(None)
327        }
328    }
329}
330
331/// The interface to get frame intervals
332pub struct FrmIvals<'i> {
333    device: &'i Device,
334    pixel_format: FourCc,
335    width: u32,
336    height: u32,
337    index: u32,
338}
339
340impl<'i> FrmIvals<'i> {
341    /// Get next frame interval value
342    pub fn fetch_next(&mut self) -> Result<Option<FrmIvalEnum>> {
343        if self.index == u32::MAX {
344            return Ok(None);
345        }
346
347        if let Some(ival) = Internal::<FrmIvalEnum>::query(
348            self.device.as_raw_fd(),
349            self.index,
350            self.pixel_format,
351            self.width,
352            self.height,
353        )? {
354            self.index += 1;
355            Ok(Some(ival.into_inner()))
356        } else {
357            self.index = u32::MAX;
358            Ok(None)
359        }
360    }
361}
362
363/// Data I/O queue
364pub struct Stream<Dir, Met: Method> {
365    file: File,
366    queue: Internal<QueueData<Dir, Met>>,
367}
368
369impl<Dir, Met: Method> Drop for Stream<Dir, Met> {
370    fn drop(&mut self) {
371        let _ = self.queue.del(self.file.as_raw_fd());
372    }
373}
374
375impl<Dir: Direction, Met: Method> Stream<Dir, Met> {
376    fn new(file: File, type_: ContentType, count: usize) -> Result<Self> {
377        let queue = Internal::<QueueData<Dir, Met>>::new(file.as_raw_fd(), type_, count as _)?;
378
379        Ok(Self { file, queue })
380    }
381
382    /// Get next frame to write or read
383    pub fn next(&self) -> Result<BufferRef<Dir, Met>> {
384        self.queue.next(self.file.as_raw_fd())
385    }
386}
387
388macro_rules! iter_impls {
389    ($($type:ident $(<$($type_params:lifetime),*>)* => $item_type:ident,)*) => {
390        $(
391            impl $(<$($type_params),*>)* Iterator for $type $(<$($type_params),*>)* {
392                type Item = Result<$item_type>;
393
394                fn next(&mut self) -> Option<Self::Item> {
395                    self.fetch_next().transpose()
396                }
397            }
398
399            impl $(<$($type_params),*>)* core::iter::FusedIterator for $type $(<$($type_params),*>)* {}
400        )*
401    };
402}
403
404iter_impls! {
405    Devices => PathBuf,
406    Controls<'i> => Control,
407    MenuItems<'i> => MenuItem,
408    FmtDescs<'i> => FmtDesc,
409    FrmSizes<'i> => FrmSizeEnum,
410    FrmIvals<'i> => FrmIvalEnum,
411}