use std::cell::RefCell;
use std::os::fd::AsRawFd;
use std::rc::{Rc, Weak};
use v4l2r::controls::ExtControlTrait;
use v4l2r::controls::SafeExtControl;
use v4l2r::ioctl;
use crate::backend::v4l2::decoder::stateless::V4l2Picture;
use crate::decoder::stateless::StatelessBackendError;
use crate::decoder::stateless::StatelessBackendResult;
use crate::device::v4l2::stateless::device::V4l2Device;
use crate::device::v4l2::stateless::queue::V4l2CaptureBuffer;
use crate::device::v4l2::stateless::queue::V4l2OutputBuffer;
use crate::video_frame::VideoFrame;
struct InitRequestHandle<V: VideoFrame> {
device: V4l2Device<V>,
timestamp: u64,
handle: ioctl::Request,
output_buffer: V4l2OutputBuffer,
picture: Weak<RefCell<V4l2Picture<V>>>,
frame: V,
}
impl<V: VideoFrame> InitRequestHandle<V> {
fn new(
device: V4l2Device<V>,
timestamp: u64,
handle: ioctl::Request,
output_buffer: V4l2OutputBuffer,
frame: V,
) -> Self {
Self { device, timestamp, handle, output_buffer, picture: Weak::new(), frame }
}
fn which(&self) -> ioctl::CtrlWhich {
ioctl::CtrlWhich::Request(self.handle.as_raw_fd())
}
fn ioctl<C, T>(&mut self, ctrl: C) -> StatelessBackendResult<&mut Self>
where
C: Into<SafeExtControl<T>>,
T: ExtControlTrait,
{
let mut ctrl: SafeExtControl<T> = ctrl.into();
ioctl::s_ext_ctrls(&self.device, self.which(), &mut ctrl)
.map_err(|e| StatelessBackendError::Other(anyhow::anyhow!(e)))?;
Ok(self)
}
fn write(&mut self, data: &[u8]) -> &mut Self {
self.output_buffer.write(data);
self
}
fn submit(self) -> StatelessBackendResult<PendingRequestHandle<V>> {
self.device
.queue_capture_buffer(self.frame)
.map_err(|e| StatelessBackendError::Other(anyhow::anyhow!(e)))?;
self.output_buffer
.submit(self.timestamp, self.handle.as_raw_fd())
.map_err(|e| StatelessBackendError::Other(anyhow::anyhow!(e)))?;
self.handle.queue().map_err(|e| StatelessBackendError::Other(anyhow::anyhow!(e)))?;
Ok(PendingRequestHandle {
device: self.device.clone(),
timestamp: self.timestamp,
picture: self.picture,
})
}
fn set_picture_ref(&mut self, picture: Weak<RefCell<V4l2Picture<V>>>) {
self.picture = picture;
}
}
struct PendingRequestHandle<V: VideoFrame> {
device: V4l2Device<V>,
timestamp: u64,
picture: Weak<RefCell<V4l2Picture<V>>>,
}
impl<V: VideoFrame> PendingRequestHandle<V> {
fn sync(self) -> DoneRequestHandle<V> {
DoneRequestHandle {
capture_buffer: Rc::new(RefCell::new(self.device.sync(self.timestamp))),
}
}
fn associate_dequeued_buffer(
&mut self,
capture_buffer: V4l2CaptureBuffer<V>,
) -> DoneRequestHandle<V> {
self.picture.upgrade().unwrap().as_ref().try_borrow_mut().unwrap().drop_references();
DoneRequestHandle { capture_buffer: Rc::new(RefCell::new(capture_buffer)) }
}
}
struct DoneRequestHandle<V: VideoFrame> {
capture_buffer: Rc<RefCell<V4l2CaptureBuffer<V>>>,
}
impl<V: VideoFrame> DoneRequestHandle<V> {
fn result(&self) -> V4l2Result<V> {
V4l2Result { capture_buffer: self.capture_buffer.clone() }
}
}
#[derive(Default)]
enum RequestHandle<V: VideoFrame> {
Init(InitRequestHandle<V>),
Pending(PendingRequestHandle<V>),
Done(DoneRequestHandle<V>),
#[default]
Unknown,
}
impl<V: VideoFrame> RequestHandle<V> {
fn new(
device: V4l2Device<V>,
timestamp: u64,
handle: ioctl::Request,
output_buffer: V4l2OutputBuffer,
frame: V,
) -> Self {
Self::Init(InitRequestHandle::new(device, timestamp, handle, output_buffer, frame))
}
fn timestamp(&self) -> u64 {
match self {
Self::Init(handle) => handle.timestamp,
Self::Pending(handle) => handle.timestamp,
Self::Done(handle) => handle.capture_buffer.borrow().timestamp(),
_ => panic!("ERROR"),
}
}
fn which(&self) -> ioctl::CtrlWhich {
match self {
Self::Init(handle) => handle.which(),
_ => panic!("ERROR"),
}
}
fn ioctl<C, T>(&mut self, ctrl: C) -> StatelessBackendResult<&mut Self>
where
C: Into<SafeExtControl<T>>,
T: ExtControlTrait,
{
match self {
Self::Init(handle) => handle.ioctl(ctrl)?,
_ => {
return Err(StatelessBackendError::Other(anyhow::anyhow!(
"incorrect request state"
)))
}
};
Ok(self)
}
fn write(&mut self, data: &[u8]) -> &mut Self {
match self {
Self::Init(handle) => handle.write(data),
_ => panic!("ERROR"),
};
self
}
fn submit(&mut self) -> StatelessBackendResult<()> {
match std::mem::take(self) {
Self::Init(handle) => Ok(*self = Self::Pending(handle.submit()?)),
_ => Err(StatelessBackendError::Other(anyhow::anyhow!("incorrect request state"))),
}
}
fn sync(&mut self) {
match std::mem::take(self) {
Self::Pending(handle) => *self = Self::Done(handle.sync()),
s @ Self::Done(_) => *self = s,
_ => panic!("ERROR"),
}
}
fn result(&self) -> V4l2Result<V> {
match self {
Self::Done(handle) => handle.result(),
_ => panic!("ERROR"),
}
}
fn set_picture_ref(&mut self, picture: Weak<RefCell<V4l2Picture<V>>>) {
match self {
Self::Init(handle) => handle.set_picture_ref(picture),
_ => panic!("ERROR"),
}
}
fn associate_dequeued_buffer(&mut self, capture_buffer: V4l2CaptureBuffer<V>) {
match self {
Self::Pending(handle) => {
*self = Self::Done(handle.associate_dequeued_buffer(capture_buffer))
}
_ => panic!("ERROR"),
}
}
}
pub struct V4l2Request<V: VideoFrame>(RequestHandle<V>);
impl<V: VideoFrame> V4l2Request<V> {
pub fn new(
device: V4l2Device<V>,
timestamp: u64,
handle: ioctl::Request,
output_buffer: V4l2OutputBuffer,
frame: V,
) -> Self {
Self(RequestHandle::new(device, timestamp, handle, output_buffer, frame))
}
pub fn timestamp(&self) -> u64 {
self.0.timestamp()
}
pub fn which(&self) -> ioctl::CtrlWhich {
self.0.which()
}
pub fn ioctl<C, T>(&mut self, ctrl: C) -> StatelessBackendResult<&mut Self>
where
C: Into<SafeExtControl<T>>,
T: ExtControlTrait,
{
self.0.ioctl(ctrl)?;
Ok(self)
}
pub fn write(&mut self, data: &[u8]) -> &mut Self {
self.0.write(data);
self
}
pub fn submit(&mut self) -> StatelessBackendResult<()> {
self.0.submit()
}
pub fn sync(&mut self) {
self.0.sync();
}
pub fn result(&self) -> V4l2Result<V> {
self.0.result()
}
pub fn set_picture_ref(&mut self, picture: Weak<RefCell<V4l2Picture<V>>>) {
self.0.set_picture_ref(picture);
}
pub fn associate_dequeued_buffer(&mut self, capture_buffer: V4l2CaptureBuffer<V>) {
self.0.associate_dequeued_buffer(capture_buffer)
}
}
pub struct V4l2Result<V: VideoFrame> {
pub capture_buffer: Rc<RefCell<V4l2CaptureBuffer<V>>>,
}