linuxvideo/
format.rs

1//! Image and pixel formats.
2
3use std::{fmt, io, mem};
4
5use crate::shared::{FrmIvalType, FrmSizeType};
6use crate::{byte_array_to_str, raw, BufType, Device, Fract};
7
8pub use crate::pixel_format::PixelFormat;
9pub use crate::shared::FormatFlags;
10
11/// Formats of all possible buffer types.
12#[derive(Debug)]
13#[non_exhaustive]
14pub enum Format {
15    VideoCapture(PixFormat),
16    VideoOutput(PixFormat),
17    VideoCaptureMplane(PixFormatMplane),
18    VideoOutputMplane(PixFormatMplane),
19    VideoOverlay(Window),
20    MetaCapture(MetaFormat),
21    MetaOutput(MetaFormat),
22    // TODO...
23}
24
25/// Pixel format of a [`VIDEO_OUTPUT`][BufType::VIDEO_OUTPUT] or
26/// [`VIDEO_CAPTURE`][BufType::VIDEO_CAPTURE] buffer.
27pub struct PixFormat(raw::PixFormat);
28
29pub struct PixFormatMplane(raw::PixFormatMplane);
30
31pub struct Window(raw::Window);
32
33pub struct PlanePixFormat(raw::PlanePixFormat);
34
35/// Metadata format of a [`META_CAPTURE`][BufType::META_CAPTURE] or
36/// [`META_OUTPUT`][BufType::META_OUTPUT] buffer.
37pub struct MetaFormat(raw::MetaFormat);
38
39impl Format {
40    pub(crate) unsafe fn from_raw(raw: raw::Format) -> Option<Self> {
41        Some(match raw.type_ {
42            BufType::VIDEO_CAPTURE => Self::VideoCapture(PixFormat(raw.fmt.pix)),
43            BufType::VIDEO_OUTPUT => Self::VideoOutput(PixFormat(raw.fmt.pix)),
44            BufType::VIDEO_CAPTURE_MPLANE => {
45                Self::VideoCaptureMplane(PixFormatMplane(raw.fmt.pix_mp))
46            }
47            BufType::VIDEO_OUTPUT_MPLANE => {
48                Self::VideoOutputMplane(PixFormatMplane(raw.fmt.pix_mp))
49            }
50            BufType::VIDEO_OVERLAY => Self::VideoOverlay(Window(raw.fmt.win)),
51            BufType::META_CAPTURE => Self::MetaCapture(MetaFormat(raw.fmt.meta)),
52            _ => return None,
53        })
54    }
55}
56
57impl PixFormat {
58    pub fn new(width: u32, height: u32, pixel_format: PixelFormat) -> Self {
59        Self(raw::PixFormat {
60            width,
61            height,
62            pixel_format,
63            ..unsafe { mem::zeroed() }
64        })
65    }
66
67    pub(crate) fn to_raw(self) -> raw::PixFormat {
68        self.0
69    }
70
71    pub fn width(&self) -> u32 {
72        self.0.width
73    }
74
75    pub fn height(&self) -> u32 {
76        self.0.height
77    }
78
79    pub fn pixel_format(&self) -> PixelFormat {
80        self.0.pixel_format
81    }
82
83    pub fn bytes_per_line(&self) -> u32 {
84        self.0.bytesperline
85    }
86
87    pub fn size_image(&self) -> u32 {
88        self.0.sizeimage
89    }
90}
91
92impl PixFormatMplane {
93    pub(crate) fn to_raw(self) -> raw::PixFormatMplane {
94        self.0
95    }
96
97    pub fn num_planes(&self) -> usize {
98        self.0.num_planes.into()
99    }
100
101    pub fn plane_formats(&self) -> impl Iterator<Item = PlanePixFormat> + '_ {
102        // NB: this cannot return `&[PlanePixFormat]` because the underlying data is unaligned
103        (0..self.num_planes()).map(move |i| PlanePixFormat(self.0.plane_fmt[i]))
104    }
105}
106
107impl PlanePixFormat {
108    pub fn size_image(&self) -> u32 {
109        self.0.sizeimage
110    }
111
112    pub fn bytes_per_line(&self) -> u32 {
113        self.0.bytesperline
114    }
115}
116
117impl Window {
118    pub(crate) fn to_raw(self) -> raw::Window {
119        self.0
120    }
121}
122
123impl MetaFormat {
124    pub fn new(format: PixelFormat) -> Self {
125        Self(raw::MetaFormat {
126            dataformat: format,
127            buffersize: 0, // set by driver during `S_FMT`
128        })
129    }
130
131    pub fn buffer_size(&self) -> u32 {
132        self.0.buffersize
133    }
134
135    pub(crate) fn to_raw(self) -> raw::MetaFormat {
136        self.0
137    }
138}
139
140impl fmt::Debug for PixFormat {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        f.debug_struct("PixFormat")
143            .field("width", &self.0.width)
144            .field("height", &self.0.height)
145            .field("pixel_format", &self.0.pixel_format)
146            .field("field", &self.0.field)
147            .field("bytesperline", &self.0.bytesperline)
148            .field("sizeimage", &self.0.sizeimage)
149            .field("colorspace", &self.0.colorspace)
150            .field("flags", &self.0.flags)
151            .field("enc", &self.0.enc)
152            .field("quantization", &self.0.quantization)
153            .field("xfer_func", &self.0.xfer_func)
154            .finish()
155    }
156}
157
158impl fmt::Debug for PixFormatMplane {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        f.debug_struct("PixFormatMplane")
161            .field("width", &{ self.0.width })
162            .field("height", &{ self.0.height })
163            .field("pixel_format", &{ self.0.pixel_format })
164            .field("field", &{ self.0.field })
165            .field("colorspace", &{ self.0.colorspace })
166            .field("plane_fmt", &self.plane_formats().collect::<Vec<_>>())
167            .field("num_planes", &self.0.num_planes)
168            .finish()
169    }
170}
171
172impl fmt::Debug for PlanePixFormat {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        f.debug_struct("PlanePixFormat")
175            .field("sizeimage", &{ self.0.sizeimage })
176            .field("bytesperline", &{ self.0.bytesperline })
177            .finish()
178    }
179}
180
181impl fmt::Debug for Window {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        f.debug_struct("Window")
184            .field("rect", &self.0.w)
185            .field("field", &self.0.field)
186            .field("chromakey", &self.0.chromakey)
187            .field("clips", &self.0.clips)
188            .field("clipcount", &self.0.clipcount)
189            .field("bitmap", &self.0.bitmap)
190            .field("global_alpha", &self.0.global_alpha)
191            .finish()
192    }
193}
194
195impl fmt::Debug for MetaFormat {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        f.debug_struct("MetaFormat")
198            .field("dataformat", &{ self.0.dataformat })
199            .field("buffersize", &{ self.0.buffersize })
200            .finish()
201    }
202}
203
204/// Iterator over a device's supported [`FormatDesc`]s.
205pub struct FormatDescIter<'a> {
206    device: &'a Device,
207    buf_type: BufType,
208    next_index: u32,
209    finished: bool,
210}
211
212impl<'a> FormatDescIter<'a> {
213    pub(crate) fn new(device: &'a Device, buf_type: BufType) -> Self {
214        Self {
215            device,
216            buf_type,
217            next_index: 0,
218            finished: false,
219        }
220    }
221}
222
223impl Iterator for FormatDescIter<'_> {
224    type Item = io::Result<FormatDesc>;
225
226    fn next(&mut self) -> Option<Self::Item> {
227        if self.finished {
228            return None;
229        }
230
231        unsafe {
232            let mut desc = raw::FmtDesc {
233                index: self.next_index,
234                type_: self.buf_type,
235                mbus_code: 0,
236                ..mem::zeroed()
237            };
238            match raw::VIDIOC_ENUM_FMT.ioctl(self.device, &mut desc) {
239                Ok(_) => {}
240                Err(e) => {
241                    self.finished = true;
242                    if e.raw_os_error() == Some(libc::EINVAL as _) {
243                        // `EINVAL` indicates the end of the list.
244                        return None;
245                    } else {
246                        return Some(Err(e));
247                    }
248                }
249            }
250
251            self.next_index += 1;
252
253            Some(Ok(FormatDesc(desc)))
254        }
255    }
256}
257
258pub struct FormatDesc(raw::FmtDesc);
259
260impl FormatDesc {
261    pub fn flags(&self) -> FormatFlags {
262        self.0.flags
263    }
264
265    pub fn description(&self) -> &str {
266        byte_array_to_str(&self.0.description)
267    }
268
269    pub fn pixel_format(&self) -> PixelFormat {
270        self.0.pixel_format
271    }
272}
273
274impl fmt::Debug for FormatDesc {
275    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276        f.debug_struct("Format")
277            .field("index", &self.0.index)
278            .field("type", &self.0.type_)
279            .field("flags", &self.0.flags)
280            .field("description", &self.description())
281            .field("pixel_format", &self.0.pixel_format)
282            .finish()
283    }
284}
285
286pub enum FrameSizes {
287    Discrete(Vec<DiscreteFrameSize>),
288    Stepwise(StepwiseFrameSizes),
289    Continuous(StepwiseFrameSizes),
290}
291
292impl FrameSizes {
293    pub(crate) fn new(device: &Device, pixel_format: PixelFormat) -> io::Result<Self> {
294        unsafe {
295            let mut desc = raw::FrmSizeEnum {
296                index: 0,
297                pixel_format,
298                ..mem::zeroed()
299            };
300            raw::VIDIOC_ENUM_FRAMESIZES.ioctl(device, &mut desc)?;
301
302            match desc.type_ {
303                FrmSizeType::DISCRETE => {
304                    let mut sizes = vec![DiscreteFrameSize {
305                        raw: desc.union.discrete,
306                        index: 0,
307                    }];
308                    for index in 1.. {
309                        let mut desc = raw::FrmSizeEnum {
310                            index,
311                            pixel_format,
312                            ..mem::zeroed()
313                        };
314                        match raw::VIDIOC_ENUM_FRAMESIZES.ioctl(device, &mut desc) {
315                            Ok(_) => {
316                                assert_eq!(desc.type_, FrmSizeType::DISCRETE);
317                                sizes.push(DiscreteFrameSize {
318                                    raw: desc.union.discrete,
319                                    index,
320                                });
321                            }
322                            Err(e) if e.raw_os_error() == Some(libc::EINVAL as _) => break,
323                            Err(e) => return Err(e.into()),
324                        }
325                    }
326
327                    Ok(FrameSizes::Discrete(sizes))
328                }
329                FrmSizeType::CONTINUOUS => Ok(FrameSizes::Continuous(StepwiseFrameSizes(
330                    desc.union.stepwise,
331                ))),
332                FrmSizeType::STEPWISE => Ok(FrameSizes::Stepwise(StepwiseFrameSizes(
333                    desc.union.stepwise,
334                ))),
335                _ => unreachable!("unknown frame size type {:?}", desc.type_),
336            }
337        }
338    }
339
340    pub fn min_width(&self) -> u32 {
341        match self {
342            FrameSizes::Discrete(sizes) => sizes.iter().map(|size| size.width()).min().unwrap(),
343            FrameSizes::Stepwise(sizes) | FrameSizes::Continuous(sizes) => sizes.min_width(),
344        }
345    }
346
347    pub fn min_height(&self) -> u32 {
348        match self {
349            FrameSizes::Discrete(sizes) => sizes.iter().map(|size| size.height()).min().unwrap(),
350            FrameSizes::Stepwise(sizes) | FrameSizes::Continuous(sizes) => sizes.min_height(),
351        }
352    }
353
354    pub fn max_width(&self) -> u32 {
355        match self {
356            FrameSizes::Discrete(sizes) => sizes.iter().map(|size| size.width()).max().unwrap(),
357            FrameSizes::Stepwise(sizes) | FrameSizes::Continuous(sizes) => sizes.max_width(),
358        }
359    }
360
361    pub fn max_height(&self) -> u32 {
362        match self {
363            FrameSizes::Discrete(sizes) => sizes.iter().map(|size| size.height()).max().unwrap(),
364            FrameSizes::Stepwise(sizes) | FrameSizes::Continuous(sizes) => sizes.max_height(),
365        }
366    }
367}
368
369pub struct StepwiseFrameSizes(raw::FrmSizeStepwise);
370
371pub struct DiscreteFrameSize {
372    raw: raw::FrmSizeDiscrete,
373    index: u32,
374}
375
376impl StepwiseFrameSizes {
377    pub fn min_width(&self) -> u32 {
378        self.0.min_width
379    }
380
381    pub fn min_height(&self) -> u32 {
382        self.0.min_height
383    }
384
385    pub fn max_width(&self) -> u32 {
386        self.0.max_width
387    }
388
389    pub fn max_height(&self) -> u32 {
390        self.0.max_height
391    }
392
393    pub fn step_width(&self) -> u32 {
394        self.0.step_width
395    }
396
397    pub fn step_height(&self) -> u32 {
398        self.0.step_height
399    }
400}
401
402impl DiscreteFrameSize {
403    pub fn width(&self) -> u32 {
404        self.raw.width
405    }
406
407    pub fn height(&self) -> u32 {
408        self.raw.height
409    }
410
411    pub fn index(&self) -> u32 {
412        self.index
413    }
414}
415
416pub enum FrameIntervals {
417    Discrete(Vec<DiscreteFrameInterval>),
418    Stepwise(StepwiseFrameIntervals),
419    Continuous(StepwiseFrameIntervals),
420}
421
422impl FrameIntervals {
423    pub(crate) fn new(
424        device: &Device,
425        pixel_format: PixelFormat,
426        width: u32,
427        height: u32,
428    ) -> io::Result<Self> {
429        unsafe {
430            let mut desc = raw::FrmIvalEnum {
431                index: 0,
432                pixel_format,
433                width,
434                height,
435                ..mem::zeroed()
436            };
437            raw::VIDIOC_ENUM_FRAMEINTERVALS.ioctl(device, &mut desc)?;
438
439            match desc.type_ {
440                FrmIvalType::DISCRETE => {
441                    let mut ivals = vec![DiscreteFrameInterval {
442                        raw: desc.union.discrete,
443                        index: 0,
444                    }];
445                    for index in 1.. {
446                        let mut desc = raw::FrmIvalEnum {
447                            index,
448                            pixel_format,
449                            width,
450                            height,
451                            ..mem::zeroed()
452                        };
453                        match raw::VIDIOC_ENUM_FRAMEINTERVALS.ioctl(device, &mut desc) {
454                            Ok(_) => {
455                                assert_eq!(desc.type_, FrmIvalType::DISCRETE);
456                                ivals.push(DiscreteFrameInterval {
457                                    raw: desc.union.discrete,
458                                    index,
459                                });
460                            }
461                            Err(e) if e.raw_os_error() == Some(libc::EINVAL as _) => break,
462                            Err(e) => return Err(e.into()),
463                        }
464                    }
465
466                    Ok(FrameIntervals::Discrete(ivals))
467                }
468                FrmIvalType::CONTINUOUS => Ok(FrameIntervals::Continuous(StepwiseFrameIntervals(
469                    desc.union.stepwise,
470                ))),
471                FrmIvalType::STEPWISE => Ok(FrameIntervals::Stepwise(StepwiseFrameIntervals(
472                    desc.union.stepwise,
473                ))),
474                _ => {
475                    // sadly, this can happen with a v4l2loopback device that no application is outputting data to
476                    log::error!("driver bug: unknown frame interval type {:?}", desc.type_);
477                    Ok(FrameIntervals::Continuous(StepwiseFrameIntervals(
478                        raw::FrmIvalStepwise {
479                            min: Fract::new(1, 600),
480                            max: Fract::new(1, 1),
481                            // FIXME: docs say that `step` is "1", but surely "1 second" is not the right step size?
482                            // https://kernel.org/doc/html/v5.17-rc3/userspace-api/media/v4l/vidioc-enum-frameintervals.html
483                            step: Fract::new(1, 1),
484                        },
485                    )))
486                }
487            }
488        }
489    }
490
491    pub fn min(&self) -> Fract {
492        match self {
493            FrameIntervals::Discrete(list) => list.iter().map(|ival| ival.raw).min().unwrap(),
494            FrameIntervals::Stepwise(ivals) | FrameIntervals::Continuous(ivals) => *ivals.min(),
495        }
496    }
497
498    pub fn max(&self) -> Fract {
499        match self {
500            FrameIntervals::Discrete(list) => list.iter().map(|ival| ival.raw).max().unwrap(),
501            FrameIntervals::Stepwise(ivals) | FrameIntervals::Continuous(ivals) => *ivals.max(),
502        }
503    }
504}
505
506impl fmt::Display for FrameIntervals {
507    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508        match self {
509            FrameIntervals::Discrete(list) => {
510                for (i, ival) in list.iter().enumerate() {
511                    if i != 0 {
512                        f.write_str(", ")?;
513                    }
514
515                    write!(f, "{}", ival.fract())?;
516                }
517
518                Ok(())
519            }
520            FrameIntervals::Stepwise(ival) => {
521                write!(f, "{}-{} (step {})", ival.min(), ival.max(), ival.step())
522            }
523            FrameIntervals::Continuous(ival) => {
524                write!(f, "{}-{}", ival.min(), ival.max())
525            }
526        }
527    }
528}
529
530pub struct DiscreteFrameInterval {
531    index: u32,
532    raw: Fract,
533}
534
535impl DiscreteFrameInterval {
536    pub fn index(&self) -> u32 {
537        self.index
538    }
539
540    pub fn fract(&self) -> &Fract {
541        &self.raw
542    }
543}
544
545pub struct StepwiseFrameIntervals(raw::FrmIvalStepwise);
546
547impl StepwiseFrameIntervals {
548    pub fn min(&self) -> &Fract {
549        &self.0.min
550    }
551
552    pub fn max(&self) -> &Fract {
553        &self.0.max
554    }
555
556    pub fn step(&self) -> &Fract {
557        &self.0.step
558    }
559}