use crate::backends::capture::OpenCvCaptureDevice;
use image::{buffer::ConvertBuffer, ImageBuffer, Rgb, RgbaImage};
use nokhwa_core::{error::NokhwaError, traits::CaptureBackendTrait};
use std::{borrow::Cow, cell::RefCell, collections::HashMap};
#[cfg(feature = "output-wgpu")]
use wgpu::{
Device as WgpuDevice, Extent3d, ImageCopyTexture, ImageDataLayout, Queue as WgpuQueue,
Texture as WgpuTexture, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat,
TextureUsages,
};
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-ipcam")))]
#[deprecated(
since = "0.10.0",
note = "please use `Camera` with `CameraIndex::String` and `input-opencv` enabled."
)]
pub struct NetworkCamera {
ip: String,
opencv_backend: RefCell<OpenCvCaptureDevice>,
}
impl NetworkCamera {
pub fn new(ip: String) -> Result<Self, NokhwaError> {
let opencv_camera = OpenCvCaptureDevice::new_ip_camera(ip.clone())?;
Ok(NetworkCamera {
ip,
opencv_backend: RefCell::new(opencv_camera),
})
}
pub fn ip(&self) -> String {
self.ip.clone()
}
pub fn set_ip(&mut self, ip: String) -> Result<(), NokhwaError> {
*self.opencv_backend.borrow_mut() = OpenCvCaptureDevice::new_ip_camera(ip.clone())?;
self.ip = ip;
Ok(())
}
fn open_stream(&self) -> Result<(), NokhwaError> {
self.opencv_backend.borrow_mut().open_stream()
}
fn frame(&self) -> Result<ImageBuffer<Rgb<u8>, Vec<u8>>, NokhwaError> {
self.opencv_backend.borrow_mut().frame()
}
fn min_buffer_size(&self, rgba: bool) -> usize {
let resolution = self.opencv_backend.borrow().resolution();
if rgba {
return (resolution.width() * resolution.height() * 4) as usize;
}
(resolution.width() * resolution.height() * 3) as usize
}
fn frame_to_buffer(&self, buffer: &mut [u8], convert_rgba: bool) -> Result<usize, NokhwaError> {
let frame = self.frame()?;
let mut frame_data = frame.to_vec();
if convert_rgba {
let rgba_image: RgbaImage = frame.convert();
frame_data = rgba_image.to_vec();
}
let bytes = frame_data.len();
buffer.copy_from_slice(&frame_data);
Ok(bytes)
}
#[cfg(feature = "output-wgpu")]
fn frame_texture<'a>(
&mut self,
device: &WgpuDevice,
queue: &WgpuQueue,
label: Option<&'a str>,
) -> Result<WgpuTexture, NokhwaError> {
use std::num::NonZeroU32;
let frame = self.frame()?;
let rgba_frame: RgbaImage = frame.convert();
let texture_size = Extent3d {
width: frame.width(),
height: frame.height(),
depth_or_array_layers: 1,
};
let texture = device.create_texture(&TextureDescriptor {
label,
size: texture_size,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8UnormSrgb,
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
});
let width_nonzero = match NonZeroU32::try_from(4 * rgba_frame.width()) {
Ok(w) => Some(w),
Err(why) => return Err(NokhwaError::ReadFrameError(why.to_string())),
};
let height_nonzero = match NonZeroU32::try_from(rgba_frame.height()) {
Ok(h) => Some(h),
Err(why) => return Err(NokhwaError::ReadFrameError(why.to_string())),
};
queue.write_texture(
ImageCopyTexture {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: TextureAspect::All,
},
&rgba_frame.to_vec(),
ImageDataLayout {
offset: 0,
bytes_per_row: width_nonzero,
rows_per_image: height_nonzero,
},
texture_size,
);
Ok(texture)
}
fn stop_stream(&mut self) -> Result<(), NokhwaError> {
self.opencv_backend.borrow_mut().stop_stream()
}
}
impl Drop for NetworkCamera {
fn drop(&mut self) {
let _stop_stream_err = self.stop_stream();
}
}