nokhwa_core/traits.rs
1/*
2 * Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::{
18 buffer::Buffer,
19 error::NokhwaError,
20 types::{
21 ApiBackend, CameraControl, CameraFormat, CameraInfo, ControlValueSetter, FrameFormat,
22 KnownCameraControl, Resolution,
23 },
24};
25use std::{borrow::Cow, collections::HashMap};
26#[cfg(feature = "wgpu-types")]
27use wgpu::{
28 Device as WgpuDevice, Extent3d, Queue as WgpuQueue, TexelCopyBufferLayout,
29 Texture as WgpuTexture, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat,
30 TextureUsages,
31};
32
33/// This trait is for any backend that allows you to grab and take frames from a camera.
34/// Many of the backends are **blocking**, if the camera is occupied the library will block while it waits for it to become available.
35///
36/// **Note**:
37/// - Backends, if not provided with a camera format, will be spawned with 640x480@15 FPS, MJPEG [`CameraFormat`].
38/// - Behaviour can differ from backend to backend. While the Camera struct abstracts most of this away, if you plan to use the raw backend structs please read the `Quirks` section of each backend.
39/// - If you call [`stop_stream()`](CaptureBackendTrait::stop_stream()), you will usually need to call [`open_stream()`](CaptureBackendTrait::open_stream()) to get more frames from the camera.
40pub trait CaptureBackendTrait {
41 /// Returns the current backend used.
42 fn backend(&self) -> ApiBackend;
43
44 /// Gets the camera information such as Name and Index as a [`CameraInfo`].
45 fn camera_info(&self) -> &CameraInfo;
46
47 /// Forcefully refreshes the stored camera format, bringing it into sync with "reality" (current camera state)
48 /// # Errors
49 /// If the camera can not get its most recent [`CameraFormat`]. this will error.
50 fn refresh_camera_format(&mut self) -> Result<(), NokhwaError>;
51
52 /// Gets the current [`CameraFormat`]. This will force refresh to the current latest if it has changed.
53 fn camera_format(&self) -> CameraFormat;
54
55 /// Will set the current [`CameraFormat`]
56 /// This will reset the current stream if used while stream is opened.
57 ///
58 /// This will also update the cache.
59 /// # Errors
60 /// If you started the stream and the camera rejects the new camera format, this will return an error.
61 fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError>;
62
63 /// A hashmap of [`Resolution`]s mapped to framerates. Not sorted!
64 /// # Errors
65 /// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a Unsupported Operation ([`UnsupportedOperationError`](crate::error::NokhwaError::UnsupportedOperationError)).
66 fn compatible_list_by_resolution(
67 &mut self,
68 fourcc: FrameFormat,
69 ) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError>;
70
71 /// Gets the compatible [`CameraFormat`] of the camera
72 /// # Errors
73 /// If it fails to get, this will error.
74 fn compatible_camera_formats(&mut self) -> Result<Vec<CameraFormat>, NokhwaError> {
75 let mut compatible_formats = vec![];
76 for fourcc in self.compatible_fourcc()? {
77 for (resolution, fps_list) in self.compatible_list_by_resolution(fourcc)? {
78 for fps in fps_list {
79 compatible_formats.push(CameraFormat::new(resolution, fourcc, fps));
80 }
81 }
82 }
83
84 Ok(compatible_formats)
85 }
86
87 /// A Vector of compatible [`FrameFormat`]s. Will only return 2 elements at most.
88 /// # Errors
89 /// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a Unsupported Operation ([`UnsupportedOperationError`](crate::error::NokhwaError::UnsupportedOperationError)).
90 fn compatible_fourcc(&mut self) -> Result<Vec<FrameFormat>, NokhwaError>;
91
92 /// Gets the current camera resolution (See: [`Resolution`], [`CameraFormat`]). This will force refresh to the current latest if it has changed.
93 fn resolution(&self) -> Resolution;
94
95 /// Will set the current [`Resolution`]
96 /// This will reset the current stream if used while stream is opened.
97 ///
98 /// This will also update the cache.
99 /// # Errors
100 /// If you started the stream and the camera rejects the new resolution, this will return an error.
101 fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError>;
102
103 /// Gets the current camera framerate (See: [`CameraFormat`]). This will force refresh to the current latest if it has changed.
104 fn frame_rate(&self) -> u32;
105
106 /// Will set the current framerate
107 /// This will reset the current stream if used while stream is opened.
108 ///
109 /// This will also update the cache.
110 /// # Errors
111 /// If you started the stream and the camera rejects the new framerate, this will return an error.
112 fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError>;
113
114 /// Gets the current camera's frame format (See: [`FrameFormat`], [`CameraFormat`]). This will force refresh to the current latest if it has changed.
115 fn frame_format(&self) -> FrameFormat;
116
117 /// Will set the current [`FrameFormat`]
118 /// This will reset the current stream if used while stream is opened.
119 ///
120 /// This will also update the cache.
121 /// # Errors
122 /// If you started the stream and the camera rejects the new frame format, this will return an error.
123 fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError>;
124
125 /// Gets the value of [`KnownCameraControl`].
126 /// # Errors
127 /// If the `control` is not supported or there is an error while getting the camera control values (e.g. unexpected value, too high, etc)
128 /// this will error.
129 fn camera_control(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError>;
130
131 /// Gets the current supported list of [`KnownCameraControl`]
132 /// # Errors
133 /// If the list cannot be collected, this will error. This can be treated as a "nothing supported".
134 fn camera_controls(&self) -> Result<Vec<CameraControl>, NokhwaError>;
135
136 /// Sets the control to `control` in the camera.
137 /// Usually, the pipeline is calling [`camera_control()`](CaptureBackendTrait::camera_control), getting a camera control that way
138 /// then calling [`value()`](CameraControl::value()) to get a [`ControlValueSetter`] and setting the value that way.
139 /// # Errors
140 /// If the `control` is not supported, the value is invalid (less than min, greater than max, not in step), or there was an error setting the control,
141 /// this will error.
142 fn set_camera_control(
143 &mut self,
144 id: KnownCameraControl,
145 value: ControlValueSetter,
146 ) -> Result<(), NokhwaError>;
147
148 /// Will open the camera stream with set parameters. This will be called internally if you try and call [`frame()`](CaptureBackendTrait::frame()) before you call [`open_stream()`](CaptureBackendTrait::open_stream()).
149 /// # Errors
150 /// If the specific backend fails to open the camera (e.g. already taken, busy, doesn't exist anymore) this will error.
151 fn open_stream(&mut self) -> Result<(), NokhwaError>;
152
153 /// Checks if stream if open. If it is, it will return true.
154 fn is_stream_open(&self) -> bool;
155
156 /// Will get a frame from the camera as a [`Buffer`]. Depending on the backend, if you have not called [`open_stream()`](CaptureBackendTrait::open_stream()) before you called this,
157 /// it will either return an error.
158 /// # Errors
159 /// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), the decoding fails (e.g. MJPEG -> u8), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet,
160 /// this will error.
161 fn frame(&mut self) -> Result<Buffer, NokhwaError>;
162
163 /// Will get a frame from the camera **without** any processing applied, meaning you will usually get a frame you need to decode yourself.
164 /// # Errors
165 /// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet, this will error.
166 fn frame_raw(&mut self) -> Result<Cow<'_, [u8]>, NokhwaError>;
167
168 /// The minimum buffer size needed to write the current frame. If `alpha` is true, it will instead return the minimum size of the buffer with an alpha channel as well.
169 /// This assumes that you are decoding to RGB/RGBA for [`FrameFormat::MJPEG`] or [`FrameFormat::YUYV`] and Luma8/LumaA8 for [`FrameFormat::GRAY`]
170 #[must_use]
171 fn decoded_buffer_size(&self, alpha: bool) -> usize {
172 let cfmt = self.camera_format();
173 let resolution = cfmt.resolution();
174 let pxwidth = match cfmt.format() {
175 FrameFormat::MJPEG
176 | FrameFormat::YUYV
177 | FrameFormat::RAWRGB
178 | FrameFormat::RAWBGR
179 | FrameFormat::NV12 => 3,
180 FrameFormat::GRAY => 1,
181 };
182 if alpha {
183 return (resolution.width() * resolution.height() * (pxwidth + 1)) as usize;
184 }
185 (resolution.width() * resolution.height() * pxwidth) as usize
186 }
187
188 #[cfg(feature = "wgpu-types")]
189 #[cfg_attr(feature = "docs-features", doc(cfg(feature = "wgpu-types")))]
190 /// Directly copies a frame to a Wgpu texture. This will automatically convert the frame into a RGBA frame.
191 /// # Errors
192 /// If the frame cannot be captured or the resolution is 0 on any axis, this will error.
193 fn frame_texture(
194 &mut self,
195 device: &WgpuDevice,
196 queue: &WgpuQueue,
197 label: Option<&str>,
198 ) -> Result<WgpuTexture, NokhwaError> {
199 use wgpu::{Origin3d, TexelCopyTextureInfoBase};
200
201 use crate::pixel_format::RgbAFormat;
202 let frame = self.frame()?.decode_image::<RgbAFormat>()?;
203
204 let texture_size = Extent3d {
205 width: frame.width(),
206 height: frame.height(),
207 depth_or_array_layers: 1,
208 };
209
210 let texture = device.create_texture(&TextureDescriptor {
211 label,
212 size: texture_size,
213 mip_level_count: 1,
214 sample_count: 1,
215 dimension: TextureDimension::D2,
216 format: TextureFormat::Rgba8UnormSrgb,
217 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
218 view_formats: &[],
219 });
220
221 let width_nonzero = 4 * frame.width();
222
223 let height_nonzero = frame.height();
224
225 queue.write_texture(
226 TexelCopyTextureInfoBase {
227 texture: &texture,
228 mip_level: 1,
229 origin: Origin3d { x: 0, y: 0, z: 0 },
230 aspect: TextureAspect::All,
231 },
232 &frame,
233 TexelCopyBufferLayout {
234 offset: 0,
235 bytes_per_row: Some(width_nonzero),
236 rows_per_image: Some(height_nonzero),
237 },
238 texture_size,
239 );
240
241 Ok(texture)
242 }
243
244 /// Will drop the stream.
245 /// # Errors
246 /// Please check the `Quirks` section of each backend.
247 fn stop_stream(&mut self) -> Result<(), NokhwaError>;
248}
249
250impl<T> From<T> for Box<dyn CaptureBackendTrait>
251where
252 T: CaptureBackendTrait + 'static,
253{
254 fn from(capbackend: T) -> Self {
255 Box::new(capbackend)
256 }
257}
258
259pub trait VirtualBackendTrait {}