use std::convert::TryFrom;
use std::path::Path;
use std::sync::Arc;
use std::{io, mem};
use libc;
use crate::control;
use crate::sys::*;
use crate::v4l2;
use crate::v4l2::videodev::v4l2_ext_controls;
use crate::{capability::Capabilities, control::Control};
pub struct Device {
handle: Arc<Handle>,
}
impl Device {
pub fn new(index: usize) -> io::Result<Self> {
let path = format!("{}{}", "/dev/video", index);
let fd = v4l2::open(path, libc::O_RDWR | libc::O_NONBLOCK)?;
if fd == -1 {
return Err(io::Error::last_os_error());
}
Ok(Device {
handle: Arc::new(Handle::new(fd)),
})
}
pub fn with_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
let fd = v4l2::open(&path, libc::O_RDWR | libc::O_NONBLOCK)?;
if fd == -1 {
return Err(io::Error::last_os_error());
}
Ok(Device {
handle: Arc::new(Handle::new(fd)),
})
}
pub fn handle(&self) -> Arc<Handle> {
self.handle.clone()
}
pub fn query_caps(&self) -> io::Result<Capabilities> {
unsafe {
let mut v4l2_caps: v4l2_capability = mem::zeroed();
v4l2::ioctl(
self.handle().fd(),
v4l2::vidioc::VIDIOC_QUERYCAP,
&mut v4l2_caps as *mut _ as *mut std::os::raw::c_void,
)?;
Ok(Capabilities::from(v4l2_caps))
}
}
pub fn query_controls(&self) -> io::Result<Vec<control::Description>> {
let mut controls = Vec::new();
unsafe {
let mut v4l2_ctrl: v4l2_query_ext_ctrl = mem::zeroed();
loop {
v4l2_ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
v4l2_ctrl.id |= V4L2_CTRL_FLAG_NEXT_COMPOUND;
match v4l2::ioctl(
self.handle().fd(),
v4l2::vidioc::VIDIOC_QUERY_EXT_CTRL,
&mut v4l2_ctrl as *mut _ as *mut std::os::raw::c_void,
) {
Ok(_) => {
let mut control = control::Description::from(v4l2_ctrl);
if control.typ == control::Type::Menu || control.typ == control::Type::IntegerMenu {
let mut items = Vec::new();
for i in (v4l2_ctrl.minimum..=v4l2_ctrl.maximum).step_by(v4l2_ctrl.step as usize) {
let mut v4l2_menu = v4l2_querymenu {
id: v4l2_ctrl.id,
index: i as u32,
..mem::zeroed()
};
let res = v4l2::ioctl(
self.handle().fd(),
v4l2::vidioc::VIDIOC_QUERYMENU,
&mut v4l2_menu as *mut _ as *mut std::os::raw::c_void,
);
if res.is_err() {
continue;
}
let item = control::MenuItem::try_from((control.typ, v4l2_menu)).unwrap();
items.push((v4l2_menu.index, item));
}
control.items = Some(items);
}
controls.push(control);
}
Err(e) => {
if controls.is_empty() || e.kind() != io::ErrorKind::InvalidInput {
return Err(e);
} else {
break;
}
}
}
}
}
Ok(controls)
}
pub fn control(&self, id: u32) -> io::Result<Control> {
unsafe {
let mut queryctrl = v4l2_query_ext_ctrl { id, ..mem::zeroed() };
v4l2::ioctl(
self.handle().fd(),
v4l2::vidioc::VIDIOC_QUERY_EXT_CTRL,
&mut queryctrl as *mut _ as *mut std::os::raw::c_void,
)?;
let description = control::Description::from(queryctrl);
let mut v4l2_ctrl = v4l2_ext_control { id, ..mem::zeroed() };
let mut v4l2_ctrls = v4l2_ext_controls {
count: 1,
controls: &mut v4l2_ctrl,
..mem::zeroed()
};
v4l2::ioctl(
self.handle().fd(),
v4l2::vidioc::VIDIOC_G_EXT_CTRLS,
&mut v4l2_ctrls as *mut _ as *mut std::os::raw::c_void,
)?;
let value = match description.typ {
control::Type::Integer64 => control::Value::Integer(v4l2_ctrl.__bindgen_anon_1.value64),
control::Type::Integer | control::Type::Menu => {
control::Value::Integer(v4l2_ctrl.__bindgen_anon_1.value as i64)
}
control::Type::Boolean => control::Value::Boolean(v4l2_ctrl.__bindgen_anon_1.value == 1),
_ => return Err(io::Error::other("cannot handle control type")),
};
Ok(Control { id, value })
}
}
pub fn set_control(&self, ctrl: Control) -> io::Result<()> {
self.set_controls(vec![ctrl])
}
pub fn set_controls(&self, ctrls: Vec<Control>) -> io::Result<()> {
unsafe {
let mut control_list: Vec<v4l2_ext_control> = vec![];
let mut class: Option<u32> = None;
if ctrls.is_empty() {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "ctrls cannot be empty"));
}
for ref ctrl in ctrls {
let mut control = v4l2_ext_control {
id: ctrl.id,
..mem::zeroed()
};
class = match class {
Some(c) => {
if c != (control.id & 0xFFFF0000) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"All controls must be in the same class",
));
} else {
Some(c)
}
}
None => Some(control.id & 0xFFFF0000),
};
match ctrl.value {
control::Value::None => {}
control::Value::Integer(val) => {
control.__bindgen_anon_1.value64 = val;
control.size = std::mem::size_of::<i64>() as u32;
}
control::Value::Boolean(val) => {
control.__bindgen_anon_1.value64 = val as i64;
control.size = std::mem::size_of::<i64>() as u32;
}
control::Value::String(ref val) => {
control.__bindgen_anon_1.string = val.as_ptr() as *mut std::os::raw::c_char;
control.size = val.len() as u32;
}
control::Value::CompoundU8(ref val) => {
control.__bindgen_anon_1.p_u8 = val.as_ptr() as *mut u8;
control.size = (val.len() * std::mem::size_of::<u8>()) as u32;
}
control::Value::CompoundU16(ref val) => {
control.__bindgen_anon_1.p_u16 = val.as_ptr() as *mut u16;
control.size = (val.len() * std::mem::size_of::<u16>()) as u32;
}
control::Value::CompoundU32(ref val) => {
control.__bindgen_anon_1.p_u32 = val.as_ptr() as *mut u32;
control.size = (val.len() * std::mem::size_of::<u32>()) as u32;
}
control::Value::CompoundPtr(ref val) => {
control.__bindgen_anon_1.ptr = val.as_ptr() as *mut std::os::raw::c_void;
control.size = (val.len() * std::mem::size_of::<u8>()) as u32;
}
};
control_list.push(control);
}
let class = class
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "failed to determine control class"))?;
let mut controls = v4l2_ext_controls {
count: control_list.len() as u32,
controls: control_list.as_mut_ptr(),
which: class,
..mem::zeroed()
};
v4l2::ioctl(
self.handle().fd(),
v4l2::vidioc::VIDIOC_S_EXT_CTRLS,
&mut controls as *mut _ as *mut std::os::raw::c_void,
)
}
}
}
impl io::Read for Device {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
let ret = libc::read(
self.handle().fd(),
buf.as_mut_ptr() as *mut std::os::raw::c_void,
buf.len(),
);
match ret {
-1 => Err(io::Error::last_os_error()),
ret => Ok(ret as usize),
}
}
}
}
impl io::Write for Device {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe {
let ret = libc::write(
self.handle().fd(),
buf.as_ptr() as *const std::os::raw::c_void,
buf.len(),
);
match ret {
-1 => Err(io::Error::last_os_error()),
ret => Ok(ret as usize),
}
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub struct Handle {
fd: std::os::raw::c_int,
}
impl Handle {
fn new(fd: std::os::raw::c_int) -> Self {
Self { fd }
}
pub fn fd(&self) -> std::os::raw::c_int {
self.fd
}
pub fn poll(&self, events: i16, timeout: i32) -> io::Result<i32> {
match unsafe {
libc::poll(
[libc::pollfd {
fd: self.fd,
events,
revents: 0,
}]
.as_mut_ptr(),
1,
timeout,
)
} {
-1 => Err(io::Error::last_os_error()),
ret => {
assert!(ret == 0 || ret == 1);
Ok(ret)
}
}
}
}
impl Drop for Handle {
fn drop(&mut self) {
v4l2::close(self.fd).unwrap();
}
}