async_std_linux_video/
lib.rs

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