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