1 //! This library provides two levels of abstraction over V4L2:
2 //!
3 //! * The `ioctl` module provides direct, thin wrappers over the V4L2 ioctls
4 //!   with added safety. Note that "safety" here is in terms of memory safety:
5 //!   this layer won't guard against passing invalid data that the ioctls will
6 //!   reject - it just makes sure that data passed from and to the kernel can
7 //!   be accessed safely. Since this is a 1:1 mapping over the V4L2 ioctls,
8 //!   working at this level is a bit laborious, although more comfortable than
9 //!   doing the same in C.
10 //!
11 //! * The `device` module (still WIP) provides a higher-level abstraction over
12 //!   the V4L2 entities, like device and queue. Strong typing will ensure that
13 //!   most inconsistencies while using the V4L2 API can be caught at
14 //!   compile-time.
15 //!
16 //! These two layers should provide the foundations for higher-level libraries
17 //! to provide safe, specialized APIs that support various V4L2 usage scenarios
18 //! (camera, decoder/encoder, etc).
19 //!
20 #[doc(hidden)]
21 pub mod bindings;
22 pub mod controls;
23 pub mod decoder;
24 pub mod device;
25 pub mod encoder;
26 pub mod ioctl;
27 pub mod memory;
28 
29 // This can be needed to match nix errors that we expose.
30 pub use nix;
31 
32 use std::convert::TryFrom;
33 use std::fmt;
34 use std::fmt::{Debug, Display};
35 
36 use enumn::N;
37 use thiserror::Error;
38 
39 // The goal of this library is to provide two layers of abstraction:
40 // ioctl: direct, safe counterparts of the V4L2 ioctls.
41 // device/queue/buffer: higher abstraction, still mapping to core V4L2 mechanics.
42 
43 /// Possible directions for the queue
44 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
45 pub enum QueueDirection {
46     Output,
47     Capture,
48 }
49 
50 /// Possible classes for this queue.
51 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
52 pub enum QueueClass {
53     Video,
54     Vbi,
55     SlicedVbi,
56     VideoOverlay,
57     VideoMplane,
58     Sdr,
59     Meta,
60 }
61 
62 /// Types of queues currently supported by this library.
63 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, N)]
64 #[repr(u32)]
65 pub enum QueueType {
66     VideoCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE,
67     VideoOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT,
68     VideoOverlay = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OVERLAY,
69     VbiCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VBI_CAPTURE,
70     VbiOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VBI_OUTPUT,
71     SlicedVbiCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_SLICED_VBI_CAPTURE,
72     SlicedVbiOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_SLICED_VBI_OUTPUT,
73     VideoOutputOverlay = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY,
74     VideoCaptureMplane = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
75     VideoOutputMplane = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
76     SdrCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_SDR_CAPTURE,
77     SdrOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_SDR_OUTPUT,
78     MetaCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_META_CAPTURE,
79     MetaOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_META_OUTPUT,
80 }
81 
82 impl QueueType {
83     /// Returns the queue corresponding to the passed `direction` and `class`.
from_dir_and_class(direction: QueueDirection, class: QueueClass) -> Self84     pub fn from_dir_and_class(direction: QueueDirection, class: QueueClass) -> Self {
85         match (direction, class) {
86             (QueueDirection::Capture, QueueClass::Video) => Self::VideoCapture,
87             (QueueDirection::Output, QueueClass::Video) => Self::VideoOutput,
88             (QueueDirection::Capture, QueueClass::VideoOverlay) => Self::VideoOverlay,
89             (QueueDirection::Output, QueueClass::VideoOverlay) => Self::VideoOutputOverlay,
90             (QueueDirection::Capture, QueueClass::Vbi) => Self::VbiCapture,
91             (QueueDirection::Output, QueueClass::Vbi) => Self::VbiOutput,
92             (QueueDirection::Capture, QueueClass::SlicedVbi) => Self::SlicedVbiCapture,
93             (QueueDirection::Output, QueueClass::SlicedVbi) => Self::SlicedVbiOutput,
94             (QueueDirection::Capture, QueueClass::VideoMplane) => Self::VideoCaptureMplane,
95             (QueueDirection::Output, QueueClass::VideoMplane) => Self::VideoOutputMplane,
96             (QueueDirection::Capture, QueueClass::Sdr) => Self::SdrCapture,
97             (QueueDirection::Output, QueueClass::Sdr) => Self::SdrOutput,
98             (QueueDirection::Capture, QueueClass::Meta) => Self::MetaCapture,
99             (QueueDirection::Output, QueueClass::Meta) => Self::MetaOutput,
100         }
101     }
102 
103     /// Returns whether the queue type is multiplanar.
is_multiplanar(&self) -> bool104     pub fn is_multiplanar(&self) -> bool {
105         matches!(
106             self,
107             QueueType::VideoCaptureMplane | QueueType::VideoOutputMplane
108         )
109     }
110 
111     /// Returns the direction of the queue type (Output or Capture).
direction(&self) -> QueueDirection112     pub fn direction(&self) -> QueueDirection {
113         match self {
114             QueueType::VideoOutput
115             | QueueType::VideoOutputMplane
116             | QueueType::VideoOverlay
117             | QueueType::VideoOutputOverlay
118             | QueueType::VbiOutput
119             | QueueType::SlicedVbiOutput
120             | QueueType::SdrOutput
121             | QueueType::MetaOutput => QueueDirection::Output,
122 
123             QueueType::VideoCapture
124             | QueueType::VbiCapture
125             | QueueType::SlicedVbiCapture
126             | QueueType::VideoCaptureMplane
127             | QueueType::SdrCapture
128             | QueueType::MetaCapture => QueueDirection::Capture,
129         }
130     }
131 
class(&self) -> QueueClass132     pub fn class(&self) -> QueueClass {
133         match self {
134             QueueType::VideoCapture | QueueType::VideoOutput => QueueClass::Video,
135             QueueType::VideoOverlay | QueueType::VideoOutputOverlay => QueueClass::VideoOverlay,
136             QueueType::VbiCapture | QueueType::VbiOutput => QueueClass::Vbi,
137             QueueType::SlicedVbiCapture | QueueType::SlicedVbiOutput => QueueClass::SlicedVbi,
138             QueueType::VideoCaptureMplane | QueueType::VideoOutputMplane => QueueClass::VideoMplane,
139             QueueType::SdrCapture | QueueType::SdrOutput => QueueClass::Sdr,
140             QueueType::MetaCapture | QueueType::MetaOutput => QueueClass::Meta,
141         }
142     }
143 }
144 
145 impl Display for QueueType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result146     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147         Debug::fmt(self, f)
148     }
149 }
150 
151 /// A Fourcc pixel format, used to pass formats to V4L2. It can be converted
152 /// back and forth from a 32-bit integer, or a 4-bytes string.
153 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
154 pub struct PixelFormat(u32);
155 
156 impl PixelFormat {
from_u32(v: u32) -> Self157     pub const fn from_u32(v: u32) -> Self {
158         Self(v)
159     }
160 
to_u32(self) -> u32161     pub const fn to_u32(self) -> u32 {
162         self.0
163     }
164 
from_fourcc(n: &[u8; 4]) -> Self165     pub const fn from_fourcc(n: &[u8; 4]) -> Self {
166         Self(n[0] as u32 | (n[1] as u32) << 8 | (n[2] as u32) << 16 | (n[3] as u32) << 24)
167     }
168 
to_fourcc(self) -> [u8; 4]169     pub const fn to_fourcc(self) -> [u8; 4] {
170         self.0.to_le_bytes()
171     }
172 }
173 
174 /// Converts a Fourcc in 32-bit integer format (like the ones passed in V4L2
175 /// structures) into the matching pixel format.
176 ///
177 /// # Examples
178 ///
179 /// ```
180 /// # use v4l2r::PixelFormat;
181 /// // Fourcc representation of NV12.
182 /// let nv12 = u32::from_le(0x3231564e);
183 /// let f = PixelFormat::from(nv12);
184 /// assert_eq!(u32::from(f), nv12);
185 /// ```
186 impl From<u32> for PixelFormat {
from(i: u32) -> Self187     fn from(i: u32) -> Self {
188         Self::from_u32(i)
189     }
190 }
191 
192 /// Converts a pixel format back to its 32-bit representation.
193 ///
194 /// # Examples
195 ///
196 /// ```
197 /// # use v4l2r::PixelFormat;
198 /// // Fourcc representation of NV12.
199 /// let nv12 = u32::from_le(0x3231564e);
200 /// let f = PixelFormat::from(nv12);
201 /// assert_eq!(u32::from(f), nv12);
202 /// ```
203 impl From<PixelFormat> for u32 {
from(format: PixelFormat) -> Self204     fn from(format: PixelFormat) -> Self {
205         format.to_u32()
206     }
207 }
208 
209 /// Simple way to convert a string litteral (e.g. b"NV12") into a pixel
210 /// format that can be passed to V4L2.
211 ///
212 /// # Examples
213 ///
214 /// ```
215 /// # use v4l2r::PixelFormat;
216 /// let nv12 = b"NV12";
217 /// let f = PixelFormat::from(nv12);
218 /// assert_eq!(&<[u8; 4]>::from(f), nv12);
219 /// ```
220 impl From<&[u8; 4]> for PixelFormat {
from(n: &[u8; 4]) -> Self221     fn from(n: &[u8; 4]) -> Self {
222         Self::from_fourcc(n)
223     }
224 }
225 
226 /// Convert a pixel format back to its 4-character representation.
227 ///
228 /// # Examples
229 ///
230 /// ```
231 /// # use v4l2r::PixelFormat;
232 /// let nv12 = b"NV12";
233 /// let f = PixelFormat::from(nv12);
234 /// assert_eq!(&<[u8; 4]>::from(f), nv12);
235 /// ```
236 impl From<PixelFormat> for [u8; 4] {
from(format: PixelFormat) -> Self237     fn from(format: PixelFormat) -> Self {
238         format.to_fourcc()
239     }
240 }
241 
242 /// Produces a debug string for this PixelFormat, including its hexadecimal
243 /// and string representation.
244 ///
245 /// # Examples
246 ///
247 /// ```
248 /// # use v4l2r::PixelFormat;
249 /// // Fourcc representation of NV12.
250 /// let nv12 = u32::from_le(0x3231564e);
251 /// let f = PixelFormat::from(nv12);
252 /// assert_eq!(format!("{:?}", f), "0x3231564e (NV12)");
253 /// ```
254 impl fmt::Debug for PixelFormat {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result255     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256         f.write_fmt(format_args!("0x{:08x} ({})", self.0, self))
257     }
258 }
259 
260 /// Produces a displayable form of this PixelFormat.
261 ///
262 /// # Examples
263 ///
264 /// ```
265 /// # use v4l2r::PixelFormat;
266 /// // Fourcc representation of NV12.
267 /// let nv12 = u32::from_le(0x3231564e);
268 /// let f = PixelFormat::from(nv12);
269 /// assert_eq!(f.to_string(), "NV12");
270 /// ```
271 impl fmt::Display for PixelFormat {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result272     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
273         let fourcc = self
274             .0
275             .to_le_bytes()
276             .iter()
277             .map(|&x| x as char)
278             .collect::<String>();
279         f.write_str(fourcc.as_str())
280     }
281 }
282 
283 /// Description of a single plane in a format.
284 #[derive(Debug, PartialEq, Clone, Default)]
285 pub struct PlaneLayout {
286     /// Useful size of the plane ; the backing memory must be at least that large.
287     pub sizeimage: u32,
288     /// Bytes per line of data. Only meaningful for image formats.
289     pub bytesperline: u32,
290 }
291 
292 /// Unified representation of a V4L2 format capable of handling both single
293 /// and multi-planar formats. When the single-planar API is used, only
294 /// one plane shall be used - attempts to have more will be rejected by the
295 /// ioctl wrappers.
296 #[derive(Debug, PartialEq, Clone, Default)]
297 pub struct Format {
298     /// Width of the image in pixels.
299     pub width: u32,
300     /// Height of the image in pixels.
301     pub height: u32,
302     /// Format each pixel is encoded in.
303     pub pixelformat: PixelFormat,
304     /// Individual layout of each plane in this format. The exact number of planes
305     /// is defined by `pixelformat`.
306     pub plane_fmt: Vec<PlaneLayout>,
307 }
308 
309 #[derive(Debug, Error, PartialEq)]
310 pub enum FormatConversionError {
311     #[error("too many planes ({0}) specified,")]
312     TooManyPlanes(usize),
313     #[error("invalid buffer type requested")]
314     InvalidBufferType(u32),
315 }
316 
317 impl TryFrom<bindings::v4l2_format> for Format {
318     type Error = FormatConversionError;
319 
try_from(fmt: bindings::v4l2_format) -> std::result::Result<Self, Self::Error>320     fn try_from(fmt: bindings::v4l2_format) -> std::result::Result<Self, Self::Error> {
321         match fmt.type_ {
322             bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE
323             | bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT => {
324                 let pix = unsafe { &fmt.fmt.pix };
325                 Ok(Format {
326                     width: pix.width,
327                     height: pix.height,
328                     pixelformat: PixelFormat::from(pix.pixelformat),
329                     plane_fmt: vec![PlaneLayout {
330                         bytesperline: pix.bytesperline,
331                         sizeimage: pix.sizeimage,
332                     }],
333                 })
334             }
335             bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
336             | bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE => {
337                 let pix_mp = unsafe { &fmt.fmt.pix_mp };
338 
339                 // Can only happen if we passed a malformed v4l2_format.
340                 if pix_mp.num_planes as usize > pix_mp.plane_fmt.len() {
341                     return Err(Self::Error::TooManyPlanes(pix_mp.num_planes as usize));
342                 }
343 
344                 let mut plane_fmt = Vec::new();
345                 for i in 0..pix_mp.num_planes as usize {
346                     let plane = &pix_mp.plane_fmt[i];
347                     plane_fmt.push(PlaneLayout {
348                         sizeimage: plane.sizeimage,
349                         bytesperline: plane.bytesperline,
350                     });
351                 }
352 
353                 Ok(Format {
354                     width: pix_mp.width,
355                     height: pix_mp.height,
356                     pixelformat: PixelFormat::from(pix_mp.pixelformat),
357                     plane_fmt,
358                 })
359             }
360             t => Err(Self::Error::InvalidBufferType(t)),
361         }
362     }
363 }
364 
365 /// Quickly build a usable `Format` from a pixel format and resolution.
366 ///
367 /// # Examples
368 ///
369 /// ```
370 /// # use v4l2r::Format;
371 /// let f = Format::from((b"NV12", (640, 480)));
372 /// assert_eq!(f.width, 640);
373 /// assert_eq!(f.height, 480);
374 /// assert_eq!(f.pixelformat.to_string(), "NV12");
375 /// assert_eq!(f.plane_fmt.len(), 0);
376 /// ```
377 impl<T: Into<PixelFormat>> From<(T, (usize, usize))> for Format {
from((pixel_format, (width, height)): (T, (usize, usize))) -> Self378     fn from((pixel_format, (width, height)): (T, (usize, usize))) -> Self {
379         Format {
380             width: width as u32,
381             height: height as u32,
382             pixelformat: pixel_format.into(),
383             ..Default::default()
384         }
385     }
386 }
387 
388 /// A more elegant representation for `v4l2_rect`.
389 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
390 pub struct Rect {
391     pub left: i32,
392     pub top: i32,
393     pub width: u32,
394     pub height: u32,
395 }
396 
397 impl Rect {
new(left: i32, top: i32, width: u32, height: u32) -> Rect398     pub fn new(left: i32, top: i32, width: u32, height: u32) -> Rect {
399         Rect {
400             left,
401             top,
402             width,
403             height,
404         }
405     }
406 }
407 
408 impl From<bindings::v4l2_rect> for Rect {
from(rect: bindings::v4l2_rect) -> Self409     fn from(rect: bindings::v4l2_rect) -> Self {
410         Rect {
411             left: rect.left,
412             top: rect.top,
413             width: rect.width,
414             height: rect.height,
415         }
416     }
417 }
418 
419 impl From<bindings::v4l2_selection> for Rect {
from(selection: bindings::v4l2_selection) -> Self420     fn from(selection: bindings::v4l2_selection) -> Self {
421         Self::from(selection.r)
422     }
423 }
424 
425 impl From<Rect> for bindings::v4l2_rect {
from(rect: Rect) -> Self426     fn from(rect: Rect) -> Self {
427         bindings::v4l2_rect {
428             left: rect.left,
429             top: rect.top,
430             width: rect.width,
431             height: rect.height,
432         }
433     }
434 }
435 
436 impl Display for Rect {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result437     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438         write!(
439             f,
440             "({}, {}), {}x{}",
441             self.left, self.top, self.width, self.height
442         )
443     }
444 }
445 
446 /// Equivalent of `enum v4l2_colorspace`.
447 #[repr(u32)]
448 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, N)]
449 pub enum Colorspace {
450     #[default]
451     Default = bindings::v4l2_colorspace_V4L2_COLORSPACE_DEFAULT,
452     Smpte170M = bindings::v4l2_colorspace_V4L2_COLORSPACE_SMPTE170M,
453     Smpte240M = bindings::v4l2_colorspace_V4L2_COLORSPACE_SMPTE240M,
454     Rec709 = bindings::v4l2_colorspace_V4L2_COLORSPACE_REC709,
455     Bt878 = bindings::v4l2_colorspace_V4L2_COLORSPACE_BT878,
456     SystemM470 = bindings::v4l2_colorspace_V4L2_COLORSPACE_470_SYSTEM_M,
457     SystemBG470 = bindings::v4l2_colorspace_V4L2_COLORSPACE_470_SYSTEM_BG,
458     Jpeg = bindings::v4l2_colorspace_V4L2_COLORSPACE_JPEG,
459     Srgb = bindings::v4l2_colorspace_V4L2_COLORSPACE_SRGB,
460     OpRgb = bindings::v4l2_colorspace_V4L2_COLORSPACE_OPRGB,
461     Bt2020 = bindings::v4l2_colorspace_V4L2_COLORSPACE_BT2020,
462     Raw = bindings::v4l2_colorspace_V4L2_COLORSPACE_RAW,
463     DciP3 = bindings::v4l2_colorspace_V4L2_COLORSPACE_DCI_P3,
464 }
465 
466 /// Equivalent of `enum v4l2_xfer_func`.
467 #[repr(u32)]
468 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, N)]
469 pub enum XferFunc {
470     #[default]
471     Default = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_DEFAULT,
472     F709 = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_709,
473     Srgb = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_SRGB,
474     OpRgb = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_OPRGB,
475     Smpte240M = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_SMPTE240M,
476     None = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_NONE,
477     DciP3 = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_DCI_P3,
478     Smpte2084 = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_SMPTE2084,
479 }
480 
481 /// Equivalent of `enum v4l2_ycbcr_encoding`.
482 #[repr(u32)]
483 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, N)]
484 pub enum YCbCrEncoding {
485     #[default]
486     Default = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_DEFAULT,
487     E601 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_601,
488     E709 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_709,
489     Xv601 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_XV601,
490     Xv709 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_XV709,
491     Sycc = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_SYCC,
492     Bt2020 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_BT2020,
493     Bt2020ConstLum = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_BT2020_CONST_LUM,
494     Smpte240M = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_SMPTE240M,
495 }
496 
497 /// Equivalent of `enum v4l2_quantization`.
498 #[repr(u32)]
499 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, N)]
500 pub enum Quantization {
501     #[default]
502     Default = bindings::v4l2_quantization_V4L2_QUANTIZATION_DEFAULT,
503     FullRange = bindings::v4l2_quantization_V4L2_QUANTIZATION_FULL_RANGE,
504     LimRange = bindings::v4l2_quantization_V4L2_QUANTIZATION_LIM_RANGE,
505 }
506