1#![forbid(future_incompatible)]
2#![deny(bad_style)]
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
21pub 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 pub async fn list() -> Result<Devices> {
35 Devices::new().await
36 }
37
38 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 Ok(Device { file })
45 }
46
47 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 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 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 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 pub async fn get_control<T: GetValue>(&self, value: &mut T) -> Result<()> {
89 value.get(self.as_raw_fd())
92 }
93
94 pub async fn set_control<T: SetValue>(&self, value: &T) -> Result<()> {
96 value.set(self.as_raw_fd())
97 }
98
99 pub fn formats(&self, type_: BufferType) -> FmtDescs {
101 FmtDescs {
102 device: self,
103 type_,
104 index: 0,
105 }
106 }
107
108 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 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 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 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 pub fn sizes(&self, pixel_format: FourCc) -> FrmSizes {
152 FrmSizes {
153 device: self,
154 pixel_format,
155 index: 0,
156 }
157 }
158
159 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 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 pub async fn get_param(&self, param: &mut StreamParm) -> Result<()> {
183 *param = self.param(param.type_()).await?;
184 Ok(())
185 }
186
187 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 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
209pub 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 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
241pub struct Controls<'i> {
243 device: &'i Device,
244 class: Option<CtrlClass>,
245 last_id: u32,
246}
247
248impl<'i> Controls<'i> {
249 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
279pub 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
304pub 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 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
329pub struct FmtDescs<'i> {
331 device: &'i Device,
332 type_: BufferType,
333 index: u32,
334}
335
336impl<'i> FmtDescs<'i> {
337 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
357pub struct FrmSizes<'i> {
359 device: &'i Device,
360 pixel_format: FourCc,
361 index: u32,
362}
363
364impl<'i> FrmSizes<'i> {
365 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
387pub 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 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
422pub 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 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}