blob: 9b4ec5fa7be644429f0145132d613748db6449bb [file] [log] [blame] [edit]
//! Safe definitions around V4L2 extended controls.
//!
//! Extended controls are represented using the `[SafeExtControl]` type, which is a transparent
//! wrapper for `v4l2_ext_control`. It takes a generic parameter defining the actual control to
//! use, which limits its API to the methods safe to use for that control type.
//!
//! A mutable reference to a single `[SafeExtControl]` can be passed to ioctl methods such as
//! [`crate::ioctl::g_ext_ctrls`] to get or set the value of that control only. Setting more than
//! one control at the same time requires to pass a type implementing [`AsV4l2ControlSlice`], that
//! returns a slice if the `v4l2_ext_control`s to manipulate.
//!
//! Since [`SafeExtControl`] is a transparent wrapper around `v4l2_ext_control`, an array of it can
//! safely implement `AsV4l2ControlSlice`. Or, more conveniently, a `#[repr(C)]` type containing
//! only [`SafeExtControl`]s:
//!
//! ```no_run
//! # use std::os::fd::OwnedFd;
//! # use std::path::Path;
//! #
//! # use v4l2r::bindings::v4l2_ext_control;
//! # use v4l2r::controls::AsV4l2ControlSlice;
//! # use v4l2r::controls::SafeExtControl;
//! # use v4l2r::controls::user::Brightness;
//! # use v4l2r::controls::user::Contrast;
//! # use v4l2r::device::Device;
//! # use v4l2r::ioctl::s_ext_ctrls;
//! # use v4l2r::ioctl::CtrlWhich;
//! #
//! # let device = Device::open(Path::new("/dev/video0"), Default::default()).unwrap();
//! #
//! #[repr(C)]
//! struct Controls {
//! brightness: SafeExtControl<Brightness>,
//! contrast: SafeExtControl<Contrast>,
//! }
//!
//! impl AsV4l2ControlSlice for &mut Controls {
//! fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control] {
//! let ptr = (*self) as *mut Controls as *mut v4l2_ext_control;
//! unsafe { std::slice::from_raw_parts_mut(ptr, 2) }
//! }
//! }
//!
//! let mut controls = Controls {
//! brightness: SafeExtControl::<Brightness>::from_value(128),
//! contrast: SafeExtControl::<Contrast>::from_value(128),
//! };
//!
//! s_ext_ctrls(&device, CtrlWhich::Current, &mut controls).unwrap();
//! assert_eq!(controls.brightness.value(), 128);
//! assert_eq!(controls.contrast.value(), 128);
//! ```
//!
//! Due to the use of `repr(C)`, the `Controls` type has the same layout as an array of
//! `v4l2_ext_control`s and thus can be passed to `s_ext_ctrls` safely.
//!
//! Sub-modules contain the type definitions for each control, organized by control class. Due to
//! the large number of controls they are not all defined, so please add those you need if they are
//! missing.
pub mod codec;
pub mod user;
use std::marker::PhantomData;
use crate::bindings;
use crate::bindings::v4l2_ctrl_fwht_params;
use crate::bindings::v4l2_ctrl_h264_decode_params;
use crate::bindings::v4l2_ctrl_h264_pps;
use crate::bindings::v4l2_ctrl_h264_pred_weights;
use crate::bindings::v4l2_ctrl_h264_scaling_matrix;
use crate::bindings::v4l2_ctrl_h264_slice_params;
use crate::bindings::v4l2_ctrl_h264_sps;
use crate::bindings::v4l2_ctrl_vp8_frame;
use crate::bindings::v4l2_ext_control;
use crate::bindings::v4l2_ext_control__bindgen_ty_1;
use crate::controls::codec::FwhtFlags;
/// Trait implemented by types that can be passed to the
/// [`g/s/try_ext_ctrls`](crate::ioctl::g_ext_ctrls) family of functions.
pub trait AsV4l2ControlSlice {
fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control];
}
impl AsV4l2ControlSlice for &mut [v4l2_ext_control] {
fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control] {
self
}
}
/// Trait implemented by types representing a given control in order to define its properties and
/// set of available methods.
pub trait ExtControlTrait {
/// One of `V4L2_CID_*`
const ID: u32;
/// Type of the value of this control.
type PAYLOAD;
}
/// Memory-safe `v4l2_ext_control`.
///
/// This type is a `v4l2_ext_control` with the following invariants:
///
/// * `id` is always a valid control ID,
/// * `size` is always correct (0 for non-pointer controls or size of payload for pointer
/// controls),
/// * For pointer types, the payload is always allocated to match `size` bytes.
///
/// In addition, the value of the control can only be accessed through methods that return the
/// correct type.
#[repr(transparent)]
pub struct SafeExtControl<T: ExtControlTrait>(v4l2_ext_control, PhantomData<T>);
impl<T: ExtControlTrait> SafeExtControl<T> {
pub fn id(&self) -> u32 {
self.0.id
}
}
/// Allows us to pass a `&mut` of a single `SafeExtControl` to `g/s/try_ext_ctrls`.
impl<T: ExtControlTrait> AsV4l2ControlSlice for &mut SafeExtControl<T> {
fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control] {
unsafe { std::slice::from_raw_parts_mut(&mut self.0, 1) }
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = i32>,
{
/// Create a new control from its value.
pub fn from_value(value: i32) -> Self {
Self(
v4l2_ext_control {
id: T::ID,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { value },
..Default::default()
},
PhantomData,
)
}
/// Returns the value of the control.
pub fn value(&self) -> i32 {
unsafe { self.0.__bindgen_anon_1.value }
}
/// Updates the value of the control.
pub fn set_value(&mut self, value: i32) {
self.0.__bindgen_anon_1.value = value;
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = i64>,
{
/// Create a new control from its value.
pub fn from_value64(value64: i64) -> Self {
Self(
v4l2_ext_control {
id: T::ID,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { value64 },
..Default::default()
},
PhantomData,
)
}
/// Returns the value of the control.
pub fn value64(&self) -> i64 {
unsafe { self.0.__bindgen_anon_1.value64 }
}
/// Updates the value of the control.
pub fn set_value64(&mut self, value: i64) {
self.0.__bindgen_anon_1.value64 = value;
}
}
impl<T> From<v4l2_ctrl_fwht_params> for SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_fwht_params>,
{
fn from(params: v4l2_ctrl_fwht_params) -> Self {
let payload = Box::new(params);
Self(
v4l2_ext_control {
id: T::ID,
size: std::mem::size_of::<T::PAYLOAD>() as u32,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
p_fwht_params: Box::into_raw(payload),
},
..Default::default()
},
PhantomData,
)
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_fwht_params>,
{
pub fn fwht_params(&self) -> &v4l2_ctrl_fwht_params {
unsafe { self.0.__bindgen_anon_1.p_fwht_params.as_ref().unwrap() }
}
pub fn fwht_params_mut(&mut self) -> &mut v4l2_ctrl_fwht_params {
unsafe { self.0.__bindgen_anon_1.p_fwht_params.as_mut().unwrap() }
}
pub fn flags(&self) -> Option<FwhtFlags> {
FwhtFlags::from_bits(self.fwht_params().flags)
}
}
impl<T> From<v4l2_ctrl_h264_sps> for SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_sps>,
{
fn from(params: v4l2_ctrl_h264_sps) -> Self {
let payload = Box::new(params);
Self(
v4l2_ext_control {
id: T::ID,
size: std::mem::size_of::<T::PAYLOAD>() as u32,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
p_h264_sps: Box::into_raw(payload),
},
..unsafe { std::mem::zeroed() }
},
PhantomData,
)
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_sps>,
{
pub fn h264_sps(&self) -> &v4l2_ctrl_h264_sps {
unsafe { self.0.__bindgen_anon_1.p_h264_sps.as_ref().unwrap() }
}
pub fn h264_sps_mut(&mut self) -> &mut v4l2_ctrl_h264_sps {
unsafe { self.0.__bindgen_anon_1.p_h264_sps.as_mut().unwrap() }
}
}
impl<T> From<v4l2_ctrl_h264_pps> for SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pps>,
{
fn from(params: v4l2_ctrl_h264_pps) -> Self {
let payload = Box::new(params);
Self(
v4l2_ext_control {
id: T::ID,
size: std::mem::size_of::<T::PAYLOAD>() as u32,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
p_h264_pps: Box::into_raw(payload),
},
..unsafe { std::mem::zeroed() }
},
PhantomData,
)
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pps>,
{
pub fn h264_pps(&self) -> &v4l2_ctrl_h264_pps {
unsafe { self.0.__bindgen_anon_1.p_h264_pps.as_ref().unwrap() }
}
pub fn h264_pps_mut(&mut self) -> &mut v4l2_ctrl_h264_pps {
unsafe { self.0.__bindgen_anon_1.p_h264_pps.as_mut().unwrap() }
}
}
impl<T> From<v4l2_ctrl_h264_scaling_matrix> for SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_scaling_matrix>,
{
fn from(params: v4l2_ctrl_h264_scaling_matrix) -> Self {
let payload = Box::new(params);
Self(
v4l2_ext_control {
id: T::ID,
size: std::mem::size_of::<T::PAYLOAD>() as u32,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
p_h264_scaling_matrix: Box::into_raw(payload),
},
..unsafe { std::mem::zeroed() }
},
PhantomData,
)
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_scaling_matrix>,
{
pub fn h264_scaling_matrix(&self) -> &v4l2_ctrl_h264_scaling_matrix {
unsafe {
self.0
.__bindgen_anon_1
.p_h264_scaling_matrix
.as_ref()
.unwrap()
}
}
pub fn h264_scaling_matrix_mut(&mut self) -> &mut v4l2_ctrl_h264_scaling_matrix {
unsafe {
self.0
.__bindgen_anon_1
.p_h264_scaling_matrix
.as_mut()
.unwrap()
}
}
}
impl<T> From<v4l2_ctrl_h264_pred_weights> for SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pred_weights>,
{
fn from(params: v4l2_ctrl_h264_pred_weights) -> Self {
let payload = Box::new(params);
Self(
v4l2_ext_control {
id: T::ID,
size: std::mem::size_of::<T::PAYLOAD>() as u32,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
p_h264_pred_weights: Box::into_raw(payload),
},
..unsafe { std::mem::zeroed() }
},
PhantomData,
)
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pred_weights>,
{
pub fn h264_pred_weights(&self) -> &v4l2_ctrl_h264_pred_weights {
unsafe {
self.0
.__bindgen_anon_1
.p_h264_pred_weights
.as_ref()
.unwrap()
}
}
pub fn h264_pred_weights_mut(&mut self) -> &mut v4l2_ctrl_h264_pred_weights {
unsafe {
self.0
.__bindgen_anon_1
.p_h264_pred_weights
.as_mut()
.unwrap()
}
}
}
impl<T> From<v4l2_ctrl_h264_slice_params> for SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_slice_params>,
{
fn from(params: v4l2_ctrl_h264_slice_params) -> Self {
let payload = Box::new(params);
Self(
v4l2_ext_control {
id: T::ID,
size: std::mem::size_of::<T::PAYLOAD>() as u32,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
p_h264_slice_params: Box::into_raw(payload),
},
..unsafe { std::mem::zeroed() }
},
PhantomData,
)
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_slice_params>,
{
pub fn h264_slice_params(&self) -> &v4l2_ctrl_h264_slice_params {
unsafe {
self.0
.__bindgen_anon_1
.p_h264_slice_params
.as_ref()
.unwrap()
}
}
pub fn h264_slice_params_mut(&mut self) -> &mut v4l2_ctrl_h264_slice_params {
unsafe {
self.0
.__bindgen_anon_1
.p_h264_slice_params
.as_mut()
.unwrap()
}
}
}
impl<T> From<v4l2_ctrl_h264_decode_params> for SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_decode_params>,
{
fn from(params: v4l2_ctrl_h264_decode_params) -> Self {
let payload = Box::new(params);
Self(
v4l2_ext_control {
id: T::ID,
size: std::mem::size_of::<T::PAYLOAD>() as u32,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
p_h264_decode_params: Box::into_raw(payload),
},
..unsafe { std::mem::zeroed() }
},
PhantomData,
)
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_decode_params>,
{
pub fn h264_decode_params(&self) -> &v4l2_ctrl_h264_decode_params {
unsafe {
self.0
.__bindgen_anon_1
.p_h264_decode_params
.as_ref()
.unwrap()
}
}
pub fn h264_decode_params_mut(&mut self) -> &mut v4l2_ctrl_h264_decode_params {
unsafe {
self.0
.__bindgen_anon_1
.p_h264_decode_params
.as_mut()
.unwrap()
}
}
}
impl<T> From<v4l2_ctrl_vp8_frame> for SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_vp8_frame>,
{
fn from(params: v4l2_ctrl_vp8_frame) -> Self {
let payload = Box::new(params);
Self(
v4l2_ext_control {
id: T::ID,
size: std::mem::size_of::<T::PAYLOAD>() as u32,
__bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
p_vp8_frame: Box::into_raw(payload),
},
..Default::default()
},
PhantomData,
)
}
}
impl<T> SafeExtControl<T>
where
T: ExtControlTrait<PAYLOAD = v4l2_ctrl_vp8_frame>,
{
pub fn vp8_frame(&self) -> &v4l2_ctrl_vp8_frame {
unsafe { self.0.__bindgen_anon_1.p_vp8_frame.as_ref().unwrap() }
}
pub fn vp8_frame_mut(&mut self) -> &mut v4l2_ctrl_vp8_frame {
unsafe { self.0.__bindgen_anon_1.p_vp8_frame.as_mut().unwrap() }
}
}
// Due to a limitation of the type system we cannot conditionally implement the `Drop` trait on
// e.g. `where T: ControlTrait<PAYLOAD = v4l2_ctrl_fwht_params>`, so we need this global implementation.
impl<T: ExtControlTrait> Drop for SafeExtControl<T> {
fn drop(&mut self) {
// If we have allocated some payload for this control, re-wrap it into its original
// container that we immediately drop to free it.
if self.0.size > 0 {
unsafe {
match self.0.id {
bindings::V4L2_CID_STATELESS_FWHT_PARAMS => {
let _ = Box::from_raw(self.0.__bindgen_anon_1.p_fwht_params);
}
bindings::V4L2_CID_STATELESS_VP8_FRAME => {
let _ = Box::from_raw(self.0.__bindgen_anon_1.p_vp8_frame);
}
_ => (),
}
}
}
}
}