1#![forbid(future_incompatible)]
2#![deny(bad_style)]
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
15pub 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 pub fn list() -> Result<Devices> {
29 Devices::new()
30 }
31
32 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
34 let file = open(path, false)?;
35
36 Ok(Device { file })
37 }
38
39 pub fn capabilities(&self) -> Result<Capability> {
41 Internal::<Capability>::query(self.as_raw_fd()).map(Internal::into_inner)
42 }
43
44 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 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 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 pub fn get_control<T: GetValue>(&self, value: &mut T) -> Result<()> {
78 value.get(self.as_raw_fd())
79 }
80
81 pub fn set_control<T: SetValue>(&self, value: &T) -> Result<()> {
83 value.set(self.as_raw_fd())
84 }
85
86 pub fn formats(&self, type_: BufferType) -> FmtDescs {
88 FmtDescs {
89 device: self,
90 type_,
91 index: 0,
92 }
93 }
94
95 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 pub fn get_format(&self, fmt: &mut Format) -> Result<()> {
104 Internal::from(fmt).get(self.as_raw_fd())
105 }
106
107 pub fn set_format(&self, fmt: &mut Format) -> Result<()> {
109 Internal::from(fmt).set(self.as_raw_fd())
110 }
111
112 pub fn try_format(&self, fmt: &mut Format) -> Result<()> {
114 Internal::from(fmt).try_(self.as_raw_fd())
115 }
116
117 pub fn sizes(&self, pixel_format: FourCc) -> FrmSizes {
119 FrmSizes {
120 device: self,
121 pixel_format,
122 index: 0,
123 }
124 }
125
126 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 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 pub fn get_param(&self, param: &mut StreamParm) -> Result<()> {
146 Internal::from(param).get(self.as_raw_fd())
147 }
148
149 pub fn set_param(&self, param: &mut StreamParm) -> Result<()> {
151 Internal::from(param).set(self.as_raw_fd())
152 }
153
154 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
164pub 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 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
194pub struct Controls<'i> {
196 device: &'i Device,
197 class: Option<CtrlClass>,
198 last_id: u32,
199}
200
201impl<'i> Controls<'i> {
202 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
229pub 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
254pub 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 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
279pub struct FmtDescs<'i> {
281 device: &'i Device,
282 type_: BufferType,
283 index: u32,
284}
285
286impl<'i> FmtDescs<'i> {
287 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
305pub struct FrmSizes<'i> {
307 device: &'i Device,
308 pixel_format: FourCc,
309 index: u32,
310}
311
312impl<'i> FrmSizes<'i> {
313 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
331pub 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 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
363pub 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 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}