use std::fs::File;
use std::io;
use std::io::Read;
use std::os::fd::AsRawFd;
use std::path::{Path, PathBuf};
use media_subsystem::{MediaEntF, MediaDeviceInfo, media_ioc_device_info, Zeroed, MediaV2Entity, MediaV2Interface, media_v2_interface, MediaV2Link, MediaIntfT, media_ioc_g_topology, media_v2_topology};
use media_subsystem::sys::{media_v2_entity, media_v2_link};
use super::flock::{Lock, Locked, FileLike};
pub struct Device(pub File);
impl FileLike for Device {
fn get_metadata(&self) -> io::Result<std::fs::Metadata> {
self.0.get_metadata()
}
fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
AsRawFd::as_raw_fd(&self.0)
}
}
impl Device {
pub fn new(p: impl AsRef<Path>) -> Result<Self, io::Error> {
Ok(Self(File::open(p)?))
}
pub fn get_device_info(&self) -> Result<MediaDeviceInfo, nix::errno::Errno>{
let device_pointer = AsRawFd::as_raw_fd(&self.0);
let mut media_device_info = MediaDeviceInfo::zeroed();
unsafe {
media_ioc_device_info(
device_pointer,
&mut media_device_info,
)
}?;
Ok(media_device_info)
}
pub fn try_lock(self) -> Result<Locked<Self>, Self> {
Locked::new(self)
}
pub fn get_video_capture_path(&self) -> Result<PathBuf, io::Error> {
let device_pointer = AsRawFd::as_raw_fd(&self.0);
let mut topology = media_v2_topology::zeroed();
unsafe {
media_ioc_g_topology(device_pointer, &mut topology)
}?;
let mut links: Vec<_> = (0..topology.num_links)
.map(|_| media_v2_link::zeroed())
.collect();
let mut entities: Vec<_> = (0..topology.num_entities)
.map(|_| media_v2_entity::zeroed())
.collect();
let mut interfaces: Vec<_> = (0..topology.num_interfaces)
.map(|_| media_v2_interface::zeroed())
.collect();
let mut topology = media_v2_topology::zeroed();
topology.set_entities(&mut entities);
topology.set_interfaces(&mut interfaces);
topology.num_links = links.len() as u32;
topology.ptr_links = links.as_mut_ptr() as u64;
unsafe {
media_ioc_g_topology(device_pointer, &mut topology)
}.unwrap();
let links: Vec<_> = links.into_iter()
.map(MediaV2Link::from)
.collect();
let interfaces: Vec<_> = interfaces.iter()
.map(MediaV2Interface::from)
.collect();
let video_interface = interfaces.iter()
.filter(|i| i.intf_type == MediaIntfT::V4LVideo)
.find(|i| find_path_by_devnode(i.devnode.major, i.devnode.minor)
.and_then(v4l::Device::with_path)
.and_then(|dev| dev.query_caps())
.map(|caps| !(
caps.capabilities
& (v4l::capability::Flags::VIDEO_CAPTURE | v4l::capability::Flags::STREAMING)
).is_empty())
.unwrap_or(false) )
.ok_or(io::Error::other("No V4lVideo interface"))?;
find_path_by_devnode(video_interface.devnode.major, video_interface.devnode.minor)
}
}
fn find_path_by_devnode(major:u32, minor: u32) -> Result<PathBuf, io::Error> {
let mut data = String::new();
File::open(format!("/sys/dev/char/{}:{}/uevent", major, minor))?
.read_to_string(&mut data)?;
let devline = data.lines()
.find(|line| line.starts_with("DEVNAME="))
.ok_or(io::Error::other("File does not contain a \"DEVNAME=\" line"))?;
Ok(format!("/dev/{}", devline.split_at("DEVNAME=".len()).1).into())
}