// Copyright 2024 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. 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 { device: V4l2Device, timestamp: u64, handle: ioctl::Request, output_buffer: V4l2OutputBuffer, picture: Weak>>, frame: V, } impl InitRequestHandle { fn new( device: V4l2Device, 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(&mut self, ctrl: C) -> StatelessBackendResult<&mut Self> where C: Into>, T: ExtControlTrait, { let mut ctrl: SafeExtControl = 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> { 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>>) { self.picture = picture; } } struct PendingRequestHandle { device: V4l2Device, timestamp: u64, picture: Weak>>, } impl PendingRequestHandle { fn sync(self) -> DoneRequestHandle { DoneRequestHandle { capture_buffer: Rc::new(RefCell::new(self.device.sync(self.timestamp))), } } fn associate_dequeued_buffer( &mut self, capture_buffer: V4l2CaptureBuffer, ) -> DoneRequestHandle { self.picture.upgrade().unwrap().as_ref().try_borrow_mut().unwrap().drop_references(); DoneRequestHandle { capture_buffer: Rc::new(RefCell::new(capture_buffer)) } } } struct DoneRequestHandle { capture_buffer: Rc>>, } impl DoneRequestHandle { fn result(&self) -> V4l2Result { V4l2Result { capture_buffer: self.capture_buffer.clone() } } } #[derive(Default)] enum RequestHandle { Init(InitRequestHandle), Pending(PendingRequestHandle), Done(DoneRequestHandle), #[default] Unknown, } impl RequestHandle { fn new( device: V4l2Device, 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(&mut self, ctrl: C) -> StatelessBackendResult<&mut Self> where C: Into>, 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 } // This method can modify in-place instead of returning a new value. This removes the need for // a RefCell in V4l2Request. 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 { match self { Self::Done(handle) => handle.result(), _ => panic!("ERROR"), } } fn set_picture_ref(&mut self, picture: Weak>>) { match self { Self::Init(handle) => handle.set_picture_ref(picture), _ => panic!("ERROR"), } } fn associate_dequeued_buffer(&mut self, capture_buffer: V4l2CaptureBuffer) { match self { Self::Pending(handle) => { *self = Self::Done(handle.associate_dequeued_buffer(capture_buffer)) } _ => panic!("ERROR"), } } } pub struct V4l2Request(RequestHandle); impl V4l2Request { pub fn new( device: V4l2Device, 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(&mut self, ctrl: C) -> StatelessBackendResult<&mut Self> where C: Into>, 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 { self.0.result() } pub fn set_picture_ref(&mut self, picture: Weak>>) { self.0.set_picture_ref(picture); } pub fn associate_dequeued_buffer(&mut self, capture_buffer: V4l2CaptureBuffer) { self.0.associate_dequeued_buffer(capture_buffer) } } pub struct V4l2Result { pub capture_buffer: Rc>>, }