Files
nokhwa/src/backends/capture/uvc_backend.rs
T

566 lines
21 KiB
Rust

/*
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#![allow(clippy::too_many_arguments)]
use crate::{
ApiBackend, CameraControl, CameraFormat, CameraInfo, CaptureBackendTrait, FrameFormat,
KnownCameraControl, KnownCameraControlFlag, NokhwaError, Resolution,
};
use flume::{Receiver, Sender};
use image::{ImageBuffer, Rgb};
use ouroboros::self_referencing;
use std::{
any::Any,
borrow::Cow,
cell::{Cell, RefCell},
collections::HashMap,
mem::MaybeUninit,
sync::{atomic::AtomicUsize, Arc},
};
use uvc::{
ActiveStream, Context, DescriptionSubtype, Device, DeviceHandle, StreamFormat, StreamHandle,
};
// ignore the IDE, this compiles
/// The backend struct that interfaces with `libuvc`.
/// To see what this does, please see [`CaptureBackendTrait`]
/// # Quirks
/// - You may need administrator/superuser privileges to access a UVC device.
/// - The indexing for this backend is based off of `libuvc`'s device ordering, not the OS.
/// - You must call [create()](UVCCaptureDevice::create()) instead `new()`, some methods are auto-generated by the self-referencer and are not meant to be used.
/// - The [create()](UVCCaptureDevice::create()) method will open the device twice.
/// - Calling [`set_resolution()`](CaptureBackendTrait::set_resolution()), [`set_frame_rate()`](crate::CaptureBackendTrait::set_frame_rate()), or [`set_frame_format()`](crate::CaptureBackendTrait::set_frame_format()) each internally calls [`set_camera_format()`](crate::CaptureBackendTrait::set_camera_format()).
/// - [`frame_raw()`](crate::CaptureBackendTrait::frame_raw()) returns the same raw data as [`get_frame()`](crate::CaptureBackendTrait::frame()), a.k.a. no custom decoding required, all data is automatically RGB
/// - The [`frame_raw()`](crate::CaptureBackendTrait::frame_raw()) and by extension [`frame()`](crate::CaptureBackendTrait::frame()) functions block.
/// - Setting controls is not supported.
/// - This backend, once stream is open, will constantly collect frames. When you call [`frame()`](crate::CaptureBackendTrait::frame()) or one of its variants, it will only give you the latest frame.
/// # Safety
/// This backend requires use of `unsafe` due to the self-referencing structs involved.
/// - If [`open_stream()`](crate::CaptureBackendTrait::open_stream()) and [`frame()`](crate::CaptureBackendTrait::frame()) are called in the wrong order this will cause undefined behaviour.
/// - If internal variables `stream_handle_init` and `active_stream_init` become de-synchronized with the true reality (weather streamhandle/activestream is init or not) this will cause undefined behaviour.
#[self_referencing]
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-uvc")))]
#[deprecated(
since = "0.10",
note = "Use one of the native backends instead(V4L, AVF, MSMF) or OpenCV"
)]
pub struct UVCCaptureDevice<'a> {
camera_format: CameraFormat,
camera_info: CameraInfo<'a>,
frame_receiver: Receiver<Vec<u8>>,
frame_sender: Sender<Vec<u8>>,
stream_handle_init: Cell<bool>,
active_stream_init: Cell<bool>,
context: Context<'a>,
#[not_covariant]
#[borrows(context)]
device: Device<'this>,
#[not_covariant]
#[borrows(device)]
device_handle: DeviceHandle<'this>,
stream_handle: RefCell<MaybeUninit<StreamHandle<'a>>>,
active_stream: RefCell<MaybeUninit<ActiveStream<'a, Arc<AtomicUsize>>>>,
}
impl<'a> UVCCaptureDevice<'a> {
/// Creates a UVC Camera device with optional [`CameraFormat`].
/// If `camera_format` is `None`, it will be spawned with with 640x480@15 FPS, MJPEG [`CameraFormat`] default.
/// # Panics
/// This operation may panic! If the UVC Context fails to retrieve the device from the gotten IDs, this operation will panic.
/// # Errors
/// This may error when the `libuvc` backend fails to retrieve the device or its data.
pub fn create(index: usize, cam_fmt: Option<CameraFormat>) -> Result<Self, NokhwaError> {
let context = match Context::new() {
Ok(ctx) => ctx,
Err(why) => {
return Err(NokhwaError::OpenDeviceError(
index.to_string(),
why.to_string(),
))
}
};
let (camera_info, frame_receiver, frame_sender) = {
let device_list = match context.devices() {
Ok(device_list) => device_list,
Err(why) => {
return Err(NokhwaError::OpenDeviceError(
index.to_string(),
why.to_string(),
))
}
};
let device = match device_list.into_iter().nth(index) {
Some(d) => d,
None => {
return Err(NokhwaError::OpenDeviceError(
index.to_string(),
"Not Found".to_string(),
))
}
};
let device_desc = match device.description() {
Ok(desc) => desc,
Err(why) => {
return Err(NokhwaError::OpenDeviceError(
index.to_string(),
why.to_string(),
))
}
};
let device_name = match (device_desc.manufacturer, device_desc.product) {
(Some(manu), Some(prod)) => {
format!("{} {}", manu, prod)
}
(_, Some(prod)) => prod,
(Some(manu), _) => {
format!(
"{}:{} {}",
device_desc.vendor_id, device_desc.product_id, manu
)
}
(_, _) => {
format!("{}:{}", device_desc.vendor_id, device_desc.product_id)
}
};
let camera_info = CameraInfo::new(
device_name,
"".to_string(),
format!("{}:{}", device_desc.vendor_id, device_desc.product_id),
index,
);
let (frame_sender, frame_receiver) = {
let (a, b) = flume::unbounded::<Vec<u8>>();
(a, b)
};
(camera_info, frame_receiver, frame_sender)
};
let camera_format = match cam_fmt {
Some(cfmt) => cfmt,
None => CameraFormat::default(),
};
Ok(UVCCaptureDeviceBuilder {
camera_format,
camera_info,
frame_receiver,
frame_sender,
context,
stream_handle_init: Cell::new(false),
active_stream_init: Cell::new(false),
device_builder: |context_builder| {
context_builder
.devices()
.unwrap()
.into_iter()
.nth(index)
.unwrap()
},
device_handle_builder: |device_builder| device_builder.open().unwrap(),
stream_handle: RefCell::new(MaybeUninit::uninit()),
active_stream: RefCell::new(MaybeUninit::uninit()),
}
.build())
}
/// Create a UVC Camera with desired settings.
/// # Panics
/// This operation may panic! If the UVC Context fails to retrieve the device from the gotten IDs, this operation will panic.
/// # Errors
/// This may error when the `libuvc` backend fails to retrieve the device or its data.
pub fn create_with(
index: usize,
width: u32,
height: u32,
fps: u32,
fourcc: FrameFormat,
) -> Result<Self, NokhwaError> {
let camera_format = Some(CameraFormat::new_from(width, height, fourcc, fps));
UVCCaptureDevice::create(index, camera_format)
}
}
// IDE Autocomplete ends here. Do not be afraid it your IDE does not show completion.
// Here are some docs to help you out: https://docs.rs/ouroboros/0.9.3/ouroboros/attr.self_referencing.html
impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> {
fn backend(&self) -> ApiBackend {
ApiBackend::UniversalVideoClass
}
fn camera_info(&self) -> &CameraInfo {
self.borrow_camera_info()
}
fn camera_format(&self) -> CameraFormat {
*self.borrow_camera_format()
}
fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
let prev_fmt = *self.borrow_camera_format();
self.with_camera_format_mut(|cfmt| {
*cfmt = new_fmt;
});
let is_streamh_some = self.borrow_stream_handle_init().get();
if is_streamh_some {
return match self.open_stream() {
Ok(_) => Ok(()),
Err(why) => {
// revert
self.with_camera_format_mut(|cfmt| {
*cfmt = prev_fmt;
});
Err(NokhwaError::SetPropertyError {
property: "CameraFormat".to_string(),
value: new_fmt.to_string(),
error: why.to_string(),
})
}
};
}
Ok(())
}
#[allow(clippy::cast_possible_truncation)]
fn compatible_list_by_resolution(
&mut self,
fourcc: FrameFormat,
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
let mut resolution_fps_map: HashMap<Resolution, Vec<u32>> = HashMap::new();
for fmt in self.with_device_handle(|devh| devh).supported_formats() {
for frame_desc in fmt.supported_formats() {
// FIXME: Verify that this is correct way to interpret DescriptionSubtype!
let format = match frame_desc.subtype() {
DescriptionSubtype::FormatMJPEG | DescriptionSubtype::FrameMJPEG => {
FrameFormat::MJPEG
}
DescriptionSubtype::FormatUncompressed
| DescriptionSubtype::FrameUncompressed => FrameFormat::YUYV,
_ => continue,
};
if format != fourcc {
continue;
}
let resolution =
Resolution::new(frame_desc.width().into(), frame_desc.height().into());
let fps: Vec<u32> = frame_desc
.intervals_duration()
.into_iter()
.map(|duration| (1000 / duration.as_millis()) as u32)
.collect();
resolution_fps_map.insert(resolution, fps);
}
}
Ok(resolution_fps_map)
}
fn compatible_fourcc(&mut self) -> Result<Vec<FrameFormat>, NokhwaError> {
let mut frameformats = vec![];
for fmt in self.with_device_handle(|devh| devh).supported_formats() {
for frame_desc in fmt.supported_formats() {
// FIXME: Verify that this is correct way to interpret DescriptionSubtype!
match frame_desc.subtype() {
DescriptionSubtype::FormatMJPEG | DescriptionSubtype::FrameMJPEG => {
frameformats.push(FrameFormat::MJPEG);
}
DescriptionSubtype::FormatUncompressed
| DescriptionSubtype::FrameUncompressed => frameformats.push(FrameFormat::YUYV),
_ => continue,
};
}
}
frameformats.sort();
frameformats.dedup();
Ok(frameformats)
}
fn resolution(&self) -> Resolution {
self.borrow_camera_format().resolution()
}
fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
let mut current_format = *self.borrow_camera_format();
current_format.set_resolution(new_res);
self.set_camera_format(current_format)
}
fn frame_rate(&self) -> u32 {
self.borrow_camera_format().frame_rate()
}
fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
let mut current_format = *self.borrow_camera_format();
current_format.set_frame_rate(new_fps);
self.set_camera_format(current_format)
}
fn frame_format(&self) -> FrameFormat {
self.borrow_camera_format().format()
}
fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
let mut current_format = *self.borrow_camera_format();
current_format.set_format(fourcc);
self.set_camera_format(current_format)
}
fn supported_camera_controls(&self) -> Result<Vec<KnownCameraControl>, NokhwaError> {
Ok(vec![
KnownCameraControl::Exposure,
KnownCameraControl::Focus,
])
}
fn camera_control(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
match control {
KnownCameraControl::Focus => match self.with_device_handle(|x| x).exposure_rel() {
Ok(v) => {
let v: i8 = v;
match CameraControl::new(
control,
i32::from(i8::MIN),
i32::from(i8::MAX),
i32::from(v),
1_i32,
i32::from(v),
KnownCameraControlFlag::Automatic,
true,
) {
Ok(cc) => Ok(cc),
Err(why) => Err(NokhwaError::GetPropertyError {
property: control.to_string(),
error: why.to_string(),
}),
}
}
Err(why) => Err(NokhwaError::GetPropertyError {
property: control.to_string(),
error: why.to_string(),
}),
},
_ => Err(NokhwaError::GetPropertyError {
property: control.to_string(),
error: "Not Supported".to_string(),
}),
}
}
fn set_camera_control(&mut self, _control: CameraControl) -> Result<(), NokhwaError> {
Err(NokhwaError::UnsupportedOperationError(
ApiBackend::UniversalVideoClass,
))
}
fn raw_supported_camera_controls(&self) -> Result<Vec<Box<dyn Any>>, NokhwaError> {
Err(NokhwaError::UnsupportedOperationError(
ApiBackend::UniversalVideoClass,
))
}
fn raw_camera_control(&self, _control: &dyn Any) -> Result<Box<dyn Any>, NokhwaError> {
Err(NokhwaError::UnsupportedOperationError(
ApiBackend::UniversalVideoClass,
))
}
fn set_raw_camera_control(
&mut self,
_control: &dyn Any,
_value: &dyn Any,
) -> Result<(), NokhwaError> {
Err(NokhwaError::UnsupportedOperationError(
ApiBackend::UniversalVideoClass,
))
}
fn open_stream(&mut self) -> Result<(), NokhwaError> {
let ret: Result<(), NokhwaError> = self.with_mut(|fields| {
let stream_format: StreamFormat = StreamFormat {
width: (*fields.camera_format).width(),
height: (*fields.camera_format).height(),
fps: (*fields.camera_format).frame_rate(),
format: (*fields.camera_format).format().into(),
};
// first, drop the existing stream by setting it to None
{
if fields.active_stream_init.get() {
let innard_value = fields.active_stream.replace(MaybeUninit::uninit());
unsafe {
std::mem::drop(innard_value.assume_init());
};
fields.active_stream_init.set(false);
}
if fields.stream_handle_init.get() {
let innard_value = fields.stream_handle.replace(MaybeUninit::uninit());
unsafe {
std::mem::drop(innard_value.assume_init());
};
fields.stream_handle_init.set(false);
}
}
// second, set the stream handle according to the streamformat
match fields
.device_handle
.get_stream_handle_with_format(stream_format)
{
Ok(streamh) => match fields.stream_handle.try_borrow_mut() {
Ok(mut streamh_raw) => {
*streamh_raw = MaybeUninit::new(streamh);
fields.stream_handle_init.set(true);
}
Err(why) => return Err(NokhwaError::OpenStreamError(why.to_string())),
},
Err(why) => return Err(NokhwaError::OpenStreamError(why.to_string())),
}
Ok(())
});
if ret.is_err() {
return ret;
}
let ret_2: Result<(), NokhwaError> = self.with(|fields| {
// finally, get the active stream
let counter = Arc::new(AtomicUsize::new(0));
let frame_sender: Sender<Vec<u8>> = self.with_frame_sender(Clone::clone);
let streamh = unsafe {
let raw_ptr =
(*fields.stream_handle.borrow_mut()).as_ptr() as *mut MaybeUninit<StreamHandle>;
let assume_inited: *mut MaybeUninit<StreamHandle<'static>> =
raw_ptr.cast::<MaybeUninit<uvc::StreamHandle>>();
&mut *assume_inited
};
let streamh_init = unsafe {
match streamh.as_mut_ptr().as_mut() {
Some(sth) => sth,
None => {
return Err(NokhwaError::OpenStreamError(
"Failed to get mutable raw pointer to stream handle!".to_string(),
))
}
}
};
let active_stream = match streamh_init.start_stream(
move |frame, _count| {
let vec_frame = frame.to_rgb().unwrap().to_bytes().to_vec();
if frame_sender.send(vec_frame).is_err() {
// do nothing
}
},
counter,
) {
Ok(active) => active,
Err(why) => return Err(NokhwaError::OpenStreamError(why.to_string())),
};
*fields.active_stream.borrow_mut() = MaybeUninit::new(active_stream);
Ok(())
});
if ret_2.is_err() {
return ret_2;
}
self.borrow_active_stream_init().set(true);
Ok(())
}
fn is_stream_open(&self) -> bool {
self.with_active_stream_init(Cell::get)
}
fn frame(&mut self) -> Result<ImageBuffer<Rgb<u8>, Vec<u8>>, NokhwaError> {
let resolution: Resolution = self.borrow_camera_format().resolution();
let data = match self.frame_raw() {
Ok(d) => d,
Err(why) => return Err(why),
};
let imagebuf: ImageBuffer<Rgb<u8>, Vec<u8>> =
match ImageBuffer::from_vec(resolution.width(), resolution.height(), data.to_vec()) {
Some(img) => img,
None => {
return Err(NokhwaError::ReadFrameError(
"ImageBuffer too small! This is probably a bug, please report it!"
.to_string(),
))
}
};
Ok(imagebuf)
}
fn frame_raw(&mut self) -> Result<Cow<[u8]>, NokhwaError> {
// assertions
if !self.borrow_active_stream_init().get() {
return Err(NokhwaError::ReadFrameError(
"Please call `open_stream()` first!".to_string(),
));
}
let f_recv = self.borrow_frame_receiver();
let messages_iter = f_recv.drain();
match messages_iter.last() {
Some(msg) => Ok(Cow::from(msg)),
None => match f_recv.recv() {
Ok(msg) => Ok(Cow::from(msg)),
Err(why) => {
return Err(NokhwaError::ReadFrameError(format!(
"All sender dropped: {}",
why.to_string()
)))
}
},
}
}
fn stop_stream(&mut self) -> Result<(), NokhwaError> {
self.with(|fields| {
if fields.active_stream_init.get() {
let innard_value = fields.active_stream.replace(MaybeUninit::uninit());
unsafe {
std::mem::drop(innard_value.assume_init());
};
fields.active_stream_init.set(false);
}
if fields.stream_handle_init.get() {
let innard_value = fields.stream_handle.replace(MaybeUninit::uninit());
unsafe {
std::mem::drop(innard_value.assume_init());
};
fields.stream_handle_init.set(false);
}
});
Ok(())
}
}