1 //! Safe definitions around V4L2 extended controls. 2 //! 3 //! Extended controls are represented using the `[SafeExtControl]` type, which is a transparent 4 //! wrapper for `v4l2_ext_control`. It takes a generic parameter defining the actual control to 5 //! use, which limits its API to the methods safe to use for that control type. 6 //! 7 //! A mutable reference to a single `[SafeExtControl]` can be passed to ioctl methods such as 8 //! [`crate::ioctl::g_ext_ctrls`] to get or set the value of that control only. Setting more than 9 //! one control at the same time requires to pass a type implementing [`AsV4l2ControlSlice`], that 10 //! returns a slice if the `v4l2_ext_control`s to manipulate. 11 //! 12 //! Since [`SafeExtControl`] is a transparent wrapper around `v4l2_ext_control`, an array of it can 13 //! safely implement `AsV4l2ControlSlice`. Or, more conveniently, a `#[repr(C)]` type containing 14 //! only [`SafeExtControl`]s: 15 //! 16 //! ```no_run 17 //! # use std::os::fd::OwnedFd; 18 //! # use std::path::Path; 19 //! # 20 //! # use v4l2r::bindings::v4l2_ext_control; 21 //! # use v4l2r::controls::AsV4l2ControlSlice; 22 //! # use v4l2r::controls::SafeExtControl; 23 //! # use v4l2r::controls::user::Brightness; 24 //! # use v4l2r::controls::user::Contrast; 25 //! # use v4l2r::device::Device; 26 //! # use v4l2r::ioctl::s_ext_ctrls; 27 //! # use v4l2r::ioctl::CtrlWhich; 28 //! # 29 //! # let device = Device::open(Path::new("/dev/video0"), Default::default()).unwrap(); 30 //! # 31 //! #[repr(C)] 32 //! struct Controls { 33 //! brightness: SafeExtControl<Brightness>, 34 //! contrast: SafeExtControl<Contrast>, 35 //! } 36 //! 37 //! impl AsV4l2ControlSlice for &mut Controls { 38 //! fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control] { 39 //! let ptr = (*self) as *mut Controls as *mut v4l2_ext_control; 40 //! unsafe { std::slice::from_raw_parts_mut(ptr, 2) } 41 //! } 42 //! } 43 //! 44 //! let mut controls = Controls { 45 //! brightness: SafeExtControl::<Brightness>::from_value(128), 46 //! contrast: SafeExtControl::<Contrast>::from_value(128), 47 //! }; 48 //! 49 //! s_ext_ctrls(&device, CtrlWhich::Current, &mut controls).unwrap(); 50 //! assert_eq!(controls.brightness.value(), 128); 51 //! assert_eq!(controls.contrast.value(), 128); 52 //! ``` 53 //! 54 //! Due to the use of `repr(C)`, the `Controls` type has the same layout as an array of 55 //! `v4l2_ext_control`s and thus can be passed to `s_ext_ctrls` safely. 56 //! 57 //! Sub-modules contain the type definitions for each control, organized by control class. Due to 58 //! the large number of controls they are not all defined, so please add those you need if they are 59 //! missing. 60 61 pub mod codec; 62 pub mod user; 63 64 use std::marker::PhantomData; 65 66 use crate::bindings; 67 use crate::bindings::v4l2_ctrl_fwht_params; 68 use crate::bindings::v4l2_ctrl_h264_decode_params; 69 use crate::bindings::v4l2_ctrl_h264_pps; 70 use crate::bindings::v4l2_ctrl_h264_pred_weights; 71 use crate::bindings::v4l2_ctrl_h264_scaling_matrix; 72 use crate::bindings::v4l2_ctrl_h264_slice_params; 73 use crate::bindings::v4l2_ctrl_h264_sps; 74 use crate::bindings::v4l2_ctrl_vp8_frame; 75 use crate::bindings::v4l2_ext_control; 76 use crate::bindings::v4l2_ext_control__bindgen_ty_1; 77 use crate::controls::codec::FwhtFlags; 78 79 /// Trait implemented by types that can be passed to the 80 /// [`g/s/try_ext_ctrls`](crate::ioctl::g_ext_ctrls) family of functions. 81 pub trait AsV4l2ControlSlice { as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control]82 fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control]; 83 } 84 85 impl AsV4l2ControlSlice for &mut [v4l2_ext_control] { as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control]86 fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control] { 87 self 88 } 89 } 90 91 /// Trait implemented by types representing a given control in order to define its properties and 92 /// set of available methods. 93 pub trait ExtControlTrait { 94 /// One of `V4L2_CID_*` 95 const ID: u32; 96 /// Type of the value of this control. 97 type PAYLOAD; 98 } 99 100 /// Memory-safe `v4l2_ext_control`. 101 /// 102 /// This type is a `v4l2_ext_control` with the following invariants: 103 /// 104 /// * `id` is always a valid control ID, 105 /// * `size` is always correct (0 for non-pointer controls or size of payload for pointer 106 /// controls), 107 /// * For pointer types, the payload is always allocated to match `size` bytes. 108 /// 109 /// In addition, the value of the control can only be accessed through methods that return the 110 /// correct type. 111 #[repr(transparent)] 112 pub struct SafeExtControl<T: ExtControlTrait>(v4l2_ext_control, PhantomData<T>); 113 114 impl<T: ExtControlTrait> SafeExtControl<T> { id(&self) -> u32115 pub fn id(&self) -> u32 { 116 self.0.id 117 } 118 } 119 120 /// Allows us to pass a `&mut` of a single `SafeExtControl` to `g/s/try_ext_ctrls`. 121 impl<T: ExtControlTrait> AsV4l2ControlSlice for &mut SafeExtControl<T> { as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control]122 fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control] { 123 unsafe { std::slice::from_raw_parts_mut(&mut self.0, 1) } 124 } 125 } 126 127 impl<T> SafeExtControl<T> 128 where 129 T: ExtControlTrait<PAYLOAD = i32>, 130 { 131 /// Create a new control from its value. from_value(value: i32) -> Self132 pub fn from_value(value: i32) -> Self { 133 Self( 134 v4l2_ext_control { 135 id: T::ID, 136 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { value }, 137 ..Default::default() 138 }, 139 PhantomData, 140 ) 141 } 142 143 /// Returns the value of the control. value(&self) -> i32144 pub fn value(&self) -> i32 { 145 unsafe { self.0.__bindgen_anon_1.value } 146 } 147 148 /// Updates the value of the control. set_value(&mut self, value: i32)149 pub fn set_value(&mut self, value: i32) { 150 self.0.__bindgen_anon_1.value = value; 151 } 152 } 153 154 impl<T> SafeExtControl<T> 155 where 156 T: ExtControlTrait<PAYLOAD = i64>, 157 { 158 /// Create a new control from its value. from_value64(value64: i64) -> Self159 pub fn from_value64(value64: i64) -> Self { 160 Self( 161 v4l2_ext_control { 162 id: T::ID, 163 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { value64 }, 164 ..Default::default() 165 }, 166 PhantomData, 167 ) 168 } 169 170 /// Returns the value of the control. value64(&self) -> i64171 pub fn value64(&self) -> i64 { 172 unsafe { self.0.__bindgen_anon_1.value64 } 173 } 174 175 /// Updates the value of the control. set_value64(&mut self, value: i64)176 pub fn set_value64(&mut self, value: i64) { 177 self.0.__bindgen_anon_1.value64 = value; 178 } 179 } 180 181 impl<T> From<v4l2_ctrl_fwht_params> for SafeExtControl<T> 182 where 183 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_fwht_params>, 184 { from(params: v4l2_ctrl_fwht_params) -> Self185 fn from(params: v4l2_ctrl_fwht_params) -> Self { 186 let payload = Box::new(params); 187 188 Self( 189 v4l2_ext_control { 190 id: T::ID, 191 size: std::mem::size_of::<T::PAYLOAD>() as u32, 192 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { 193 p_fwht_params: Box::into_raw(payload), 194 }, 195 ..Default::default() 196 }, 197 PhantomData, 198 ) 199 } 200 } 201 202 impl<T> SafeExtControl<T> 203 where 204 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_fwht_params>, 205 { fwht_params(&self) -> &v4l2_ctrl_fwht_params206 pub fn fwht_params(&self) -> &v4l2_ctrl_fwht_params { 207 unsafe { self.0.__bindgen_anon_1.p_fwht_params.as_ref().unwrap() } 208 } 209 fwht_params_mut(&mut self) -> &mut v4l2_ctrl_fwht_params210 pub fn fwht_params_mut(&mut self) -> &mut v4l2_ctrl_fwht_params { 211 unsafe { self.0.__bindgen_anon_1.p_fwht_params.as_mut().unwrap() } 212 } 213 flags(&self) -> Option<FwhtFlags>214 pub fn flags(&self) -> Option<FwhtFlags> { 215 FwhtFlags::from_bits(self.fwht_params().flags) 216 } 217 } 218 219 impl<T> From<v4l2_ctrl_h264_sps> for SafeExtControl<T> 220 where 221 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_sps>, 222 { from(params: v4l2_ctrl_h264_sps) -> Self223 fn from(params: v4l2_ctrl_h264_sps) -> Self { 224 let payload = Box::new(params); 225 226 Self( 227 v4l2_ext_control { 228 id: T::ID, 229 size: std::mem::size_of::<T::PAYLOAD>() as u32, 230 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { 231 p_h264_sps: Box::into_raw(payload), 232 }, 233 ..unsafe { std::mem::zeroed() } 234 }, 235 PhantomData, 236 ) 237 } 238 } 239 240 impl<T> SafeExtControl<T> 241 where 242 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_sps>, 243 { h264_sps(&self) -> &v4l2_ctrl_h264_sps244 pub fn h264_sps(&self) -> &v4l2_ctrl_h264_sps { 245 unsafe { self.0.__bindgen_anon_1.p_h264_sps.as_ref().unwrap() } 246 } 247 h264_sps_mut(&mut self) -> &mut v4l2_ctrl_h264_sps248 pub fn h264_sps_mut(&mut self) -> &mut v4l2_ctrl_h264_sps { 249 unsafe { self.0.__bindgen_anon_1.p_h264_sps.as_mut().unwrap() } 250 } 251 } 252 253 impl<T> From<v4l2_ctrl_h264_pps> for SafeExtControl<T> 254 where 255 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pps>, 256 { from(params: v4l2_ctrl_h264_pps) -> Self257 fn from(params: v4l2_ctrl_h264_pps) -> Self { 258 let payload = Box::new(params); 259 260 Self( 261 v4l2_ext_control { 262 id: T::ID, 263 size: std::mem::size_of::<T::PAYLOAD>() as u32, 264 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { 265 p_h264_pps: Box::into_raw(payload), 266 }, 267 ..unsafe { std::mem::zeroed() } 268 }, 269 PhantomData, 270 ) 271 } 272 } 273 274 impl<T> SafeExtControl<T> 275 where 276 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pps>, 277 { h264_pps(&self) -> &v4l2_ctrl_h264_pps278 pub fn h264_pps(&self) -> &v4l2_ctrl_h264_pps { 279 unsafe { self.0.__bindgen_anon_1.p_h264_pps.as_ref().unwrap() } 280 } 281 h264_pps_mut(&mut self) -> &mut v4l2_ctrl_h264_pps282 pub fn h264_pps_mut(&mut self) -> &mut v4l2_ctrl_h264_pps { 283 unsafe { self.0.__bindgen_anon_1.p_h264_pps.as_mut().unwrap() } 284 } 285 } 286 287 impl<T> From<v4l2_ctrl_h264_scaling_matrix> for SafeExtControl<T> 288 where 289 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_scaling_matrix>, 290 { from(params: v4l2_ctrl_h264_scaling_matrix) -> Self291 fn from(params: v4l2_ctrl_h264_scaling_matrix) -> Self { 292 let payload = Box::new(params); 293 294 Self( 295 v4l2_ext_control { 296 id: T::ID, 297 size: std::mem::size_of::<T::PAYLOAD>() as u32, 298 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { 299 p_h264_scaling_matrix: Box::into_raw(payload), 300 }, 301 ..unsafe { std::mem::zeroed() } 302 }, 303 PhantomData, 304 ) 305 } 306 } 307 308 impl<T> SafeExtControl<T> 309 where 310 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_scaling_matrix>, 311 { h264_scaling_matrix(&self) -> &v4l2_ctrl_h264_scaling_matrix312 pub fn h264_scaling_matrix(&self) -> &v4l2_ctrl_h264_scaling_matrix { 313 unsafe { 314 self.0 315 .__bindgen_anon_1 316 .p_h264_scaling_matrix 317 .as_ref() 318 .unwrap() 319 } 320 } 321 h264_scaling_matrix_mut(&mut self) -> &mut v4l2_ctrl_h264_scaling_matrix322 pub fn h264_scaling_matrix_mut(&mut self) -> &mut v4l2_ctrl_h264_scaling_matrix { 323 unsafe { 324 self.0 325 .__bindgen_anon_1 326 .p_h264_scaling_matrix 327 .as_mut() 328 .unwrap() 329 } 330 } 331 } 332 333 impl<T> From<v4l2_ctrl_h264_pred_weights> for SafeExtControl<T> 334 where 335 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pred_weights>, 336 { from(params: v4l2_ctrl_h264_pred_weights) -> Self337 fn from(params: v4l2_ctrl_h264_pred_weights) -> Self { 338 let payload = Box::new(params); 339 340 Self( 341 v4l2_ext_control { 342 id: T::ID, 343 size: std::mem::size_of::<T::PAYLOAD>() as u32, 344 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { 345 p_h264_pred_weights: Box::into_raw(payload), 346 }, 347 ..unsafe { std::mem::zeroed() } 348 }, 349 PhantomData, 350 ) 351 } 352 } 353 354 impl<T> SafeExtControl<T> 355 where 356 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pred_weights>, 357 { h264_pred_weights(&self) -> &v4l2_ctrl_h264_pred_weights358 pub fn h264_pred_weights(&self) -> &v4l2_ctrl_h264_pred_weights { 359 unsafe { 360 self.0 361 .__bindgen_anon_1 362 .p_h264_pred_weights 363 .as_ref() 364 .unwrap() 365 } 366 } 367 h264_pred_weights_mut(&mut self) -> &mut v4l2_ctrl_h264_pred_weights368 pub fn h264_pred_weights_mut(&mut self) -> &mut v4l2_ctrl_h264_pred_weights { 369 unsafe { 370 self.0 371 .__bindgen_anon_1 372 .p_h264_pred_weights 373 .as_mut() 374 .unwrap() 375 } 376 } 377 } 378 379 impl<T> From<v4l2_ctrl_h264_slice_params> for SafeExtControl<T> 380 where 381 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_slice_params>, 382 { from(params: v4l2_ctrl_h264_slice_params) -> Self383 fn from(params: v4l2_ctrl_h264_slice_params) -> Self { 384 let payload = Box::new(params); 385 386 Self( 387 v4l2_ext_control { 388 id: T::ID, 389 size: std::mem::size_of::<T::PAYLOAD>() as u32, 390 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { 391 p_h264_slice_params: Box::into_raw(payload), 392 }, 393 ..unsafe { std::mem::zeroed() } 394 }, 395 PhantomData, 396 ) 397 } 398 } 399 400 impl<T> SafeExtControl<T> 401 where 402 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_slice_params>, 403 { h264_slice_params(&self) -> &v4l2_ctrl_h264_slice_params404 pub fn h264_slice_params(&self) -> &v4l2_ctrl_h264_slice_params { 405 unsafe { 406 self.0 407 .__bindgen_anon_1 408 .p_h264_slice_params 409 .as_ref() 410 .unwrap() 411 } 412 } 413 h264_slice_params_mut(&mut self) -> &mut v4l2_ctrl_h264_slice_params414 pub fn h264_slice_params_mut(&mut self) -> &mut v4l2_ctrl_h264_slice_params { 415 unsafe { 416 self.0 417 .__bindgen_anon_1 418 .p_h264_slice_params 419 .as_mut() 420 .unwrap() 421 } 422 } 423 } 424 425 impl<T> From<v4l2_ctrl_h264_decode_params> for SafeExtControl<T> 426 where 427 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_decode_params>, 428 { from(params: v4l2_ctrl_h264_decode_params) -> Self429 fn from(params: v4l2_ctrl_h264_decode_params) -> Self { 430 let payload = Box::new(params); 431 432 Self( 433 v4l2_ext_control { 434 id: T::ID, 435 size: std::mem::size_of::<T::PAYLOAD>() as u32, 436 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { 437 p_h264_decode_params: Box::into_raw(payload), 438 }, 439 ..unsafe { std::mem::zeroed() } 440 }, 441 PhantomData, 442 ) 443 } 444 } 445 446 impl<T> SafeExtControl<T> 447 where 448 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_decode_params>, 449 { h264_decode_params(&self) -> &v4l2_ctrl_h264_decode_params450 pub fn h264_decode_params(&self) -> &v4l2_ctrl_h264_decode_params { 451 unsafe { 452 self.0 453 .__bindgen_anon_1 454 .p_h264_decode_params 455 .as_ref() 456 .unwrap() 457 } 458 } 459 h264_decode_params_mut(&mut self) -> &mut v4l2_ctrl_h264_decode_params460 pub fn h264_decode_params_mut(&mut self) -> &mut v4l2_ctrl_h264_decode_params { 461 unsafe { 462 self.0 463 .__bindgen_anon_1 464 .p_h264_decode_params 465 .as_mut() 466 .unwrap() 467 } 468 } 469 } 470 471 impl<T> From<v4l2_ctrl_vp8_frame> for SafeExtControl<T> 472 where 473 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_vp8_frame>, 474 { from(params: v4l2_ctrl_vp8_frame) -> Self475 fn from(params: v4l2_ctrl_vp8_frame) -> Self { 476 let payload = Box::new(params); 477 478 Self( 479 v4l2_ext_control { 480 id: T::ID, 481 size: std::mem::size_of::<T::PAYLOAD>() as u32, 482 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { 483 p_vp8_frame: Box::into_raw(payload), 484 }, 485 ..Default::default() 486 }, 487 PhantomData, 488 ) 489 } 490 } 491 492 impl<T> SafeExtControl<T> 493 where 494 T: ExtControlTrait<PAYLOAD = v4l2_ctrl_vp8_frame>, 495 { vp8_frame(&self) -> &v4l2_ctrl_vp8_frame496 pub fn vp8_frame(&self) -> &v4l2_ctrl_vp8_frame { 497 unsafe { self.0.__bindgen_anon_1.p_vp8_frame.as_ref().unwrap() } 498 } 499 vp8_frame_mut(&mut self) -> &mut v4l2_ctrl_vp8_frame500 pub fn vp8_frame_mut(&mut self) -> &mut v4l2_ctrl_vp8_frame { 501 unsafe { self.0.__bindgen_anon_1.p_vp8_frame.as_mut().unwrap() } 502 } 503 } 504 505 // Due to a limitation of the type system we cannot conditionally implement the `Drop` trait on 506 // e.g. `where T: ControlTrait<PAYLOAD = v4l2_ctrl_fwht_params>`, so we need this global implementation. 507 impl<T: ExtControlTrait> Drop for SafeExtControl<T> { drop(&mut self)508 fn drop(&mut self) { 509 // If we have allocated some payload for this control, re-wrap it into its original 510 // container that we immediately drop to free it. 511 if self.0.size > 0 { 512 unsafe { 513 match self.0.id { 514 bindings::V4L2_CID_STATELESS_FWHT_PARAMS => { 515 let _ = Box::from_raw(self.0.__bindgen_anon_1.p_fwht_params); 516 } 517 bindings::V4L2_CID_STATELESS_VP8_FRAME => { 518 let _ = Box::from_raw(self.0.__bindgen_anon_1.p_vp8_frame); 519 } 520 _ => (), 521 } 522 } 523 } 524 } 525 } 526