nokhwa/query.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 nokhwa_core::{
18 error::NokhwaError,
19 types::{ApiBackend, CameraInfo},
20};
21
22/// Gets the native [`ApiBackend`]
23#[must_use]
24pub fn native_api_backend() -> Option<ApiBackend> {
25 match std::env::consts::OS {
26 "linux" => Some(ApiBackend::Video4Linux),
27 "macos" | "ios" => Some(ApiBackend::AVFoundation),
28 "windows" => Some(ApiBackend::MediaFoundation),
29 _ => None,
30 }
31}
32
33// TODO: Update as this goes
34/// Query the system for a list of available devices. Please refer to the API Backends that support `Query`) <br>
35/// Usually the order goes Native -> UVC -> Gstreamer.
36/// # Quirks
37/// - `Media Foundation`: The symbolic link for the device is listed in the `misc` attribute of the [`CameraInfo`].
38/// - `Media Foundation`: The names may contain invalid characters since they were converted from UTF16.
39/// - `AVFoundation`: The ID of the device is stored in the `misc` attribute of the [`CameraInfo`].
40/// - `AVFoundation`: There is lots of miscellaneous info in the `desc` attribute.
41/// - `WASM`: The `misc` field contains the device ID and group ID are seperated by a space (' ')
42/// # Errors
43/// If you use an unsupported API (check the README or crate root for more info), incompatible backend for current platform, incompatible platform, or insufficient permissions, etc
44/// this will error.
45pub fn query(api: ApiBackend) -> Result<Vec<CameraInfo>, NokhwaError> {
46 match api {
47 ApiBackend::Auto => {
48 // determine platform
49 match std::env::consts::OS {
50 "linux" => {
51 if cfg!(feature = "input-v4l") && cfg!(target_os = "linux") {
52 query(ApiBackend::Video4Linux)
53 } else if cfg!(feature = "input-opencv") {
54 query(ApiBackend::OpenCv)
55 } else {
56 dbg!("Error: No suitable Backends available. Perhaps you meant to enable one of the backends such as `input-v4l`? (Please read the docs.)");
57 Err(NokhwaError::UnsupportedOperationError(ApiBackend::Auto))
58 }
59 }
60 "windows" => {
61 if cfg!(feature = "input-msmf") && cfg!(target_os = "windows") {
62 query(ApiBackend::MediaFoundation)
63 } else if cfg!(feature = "input-opencv") {
64 query(ApiBackend::OpenCv)
65 } else {
66 dbg!("Error: No suitable Backends available. Perhaps you meant to enable one of the backends such as `input-msmf`? (Please read the docs.)");
67 Err(NokhwaError::UnsupportedOperationError(ApiBackend::Auto))
68 }
69 }
70 "macos" => {
71 if cfg!(feature = "input-avfoundation") {
72 query(ApiBackend::AVFoundation)
73 } else if cfg!(feature = "input-opencv") {
74 query(ApiBackend::OpenCv)
75 } else {
76 dbg!("Error: No suitable Backends available. Perhaps you meant to enable one of the backends such as `input-avfoundation`? (Please read the docs.)");
77 Err(NokhwaError::UnsupportedOperationError(ApiBackend::Auto))
78 }
79 }
80 "ios" => {
81 if cfg!(feature = "input-avfoundation") {
82 query(ApiBackend::AVFoundation)
83 } else {
84 dbg!("Error: No suitable Backends available. Perhaps you meant to enable one of the backends such as `input-avfoundation`? (Please read the docs.)");
85 Err(NokhwaError::UnsupportedOperationError(ApiBackend::Auto))
86 }
87 }
88 _ => {
89 dbg!("Error: No suitable Backends available. You are on an unsupported platform.");
90 Err(NokhwaError::NotImplementedError("Bad Platform".to_string()))
91 }
92 }
93 }
94 ApiBackend::AVFoundation => query_avfoundation(),
95 ApiBackend::Video4Linux => query_v4l(),
96 ApiBackend::MediaFoundation => query_msmf(),
97 ApiBackend::OpenCv | ApiBackend::Network => {
98 Err(NokhwaError::UnsupportedOperationError(api))
99 }
100 ApiBackend::Browser => query_wasm(),
101 _ => Err(NokhwaError::UnsupportedOperationError(api)),
102 }
103}
104
105// TODO: More
106
107#[cfg(all(feature = "input-v4l", target_os = "linux"))]
108fn query_v4l() -> Result<Vec<CameraInfo>, NokhwaError> {
109 nokhwa_bindings_linux::query()
110}
111
112#[cfg(any(not(feature = "input-v4l"), not(target_os = "linux")))]
113fn query_v4l() -> Result<Vec<CameraInfo>, NokhwaError> {
114 Err(NokhwaError::UnsupportedOperationError(
115 ApiBackend::Video4Linux,
116 ))
117}
118
119// #[cfg(feature = "input-uvc")]
120// fn query_uvc() -> Result<Vec<CameraInfo>, NokhwaError> {
121// use crate::CameraIndex;
122// use uvc::Device;
123//
124// let context = match uvc::Context::new() {
125// Ok(ctx) => ctx,
126// Err(why) => {
127// return Err(NokhwaError::GeneralError(format!(
128// "UVC Context failure: {}",
129// why
130// )))
131// }
132// };
133//
134// let usb_devices = usb_enumeration::enumerate(None, None);
135// let uvc_devices = match context.devices() {
136// Ok(devs) => {
137// let device_vec: Vec<Device> = devs.collect();
138// device_vec
139// }
140// Err(why) => {
141// return Err(NokhwaError::GeneralError(format!(
142// "UVC Context Devicelist failure: {}",
143// why
144// )))
145// }
146// };
147//
148// let mut camera_info_vec = vec![];
149// let mut counter = 0_usize;
150//
151// // Optimize this O(n*m) algorithm
152// for usb_dev in &usb_devices {
153// for uvc_dev in &uvc_devices {
154// if let Ok(desc) = uvc_dev.description() {
155// if desc.product_id == usb_dev.product_id && desc.vendor_id == usb_dev.vendor_id {
156// let name = usb_dev
157// .description
158// .as_ref()
159// .unwrap_or(&format!(
160// "{}:{} {} {}",
161// desc.vendor_id,
162// desc.product_id,
163// desc.manufacturer.unwrap_or_else(|| "Generic".to_string()),
164// desc.product.unwrap_or_else(|| "Camera".to_string())
165// ))
166// .clone();
167//
168// camera_info_vec.push(CameraInfo::new(
169// name.clone(),
170// usb_dev
171// .description
172// .as_ref()
173// .unwrap_or(&"".to_string())
174// .clone(),
175// format!(
176// "{}:{} {}",
177// desc.vendor_id,
178// desc.product_id,
179// desc.serial_number.unwrap_or_else(|| "".to_string())
180// ),
181// CameraIndex::Index(counter as u32),
182// ));
183// counter += 1;
184// }
185// }
186// }
187// }
188// Ok(camera_info_vec)
189// }
190//
191// #[cfg(not(feature = "input-uvc"))]
192// #[allow(deprecated)]
193// fn query_uvc() -> Result<Vec<CameraInfo>, NokhwaError> {
194// Err(NokhwaError::UnsupportedOperationError(
195// ApiBackend::UniversalVideoClass,
196// ))
197// }
198//
199// #[cfg(feature = "input-gst")]
200// fn query_gstreamer() -> Result<Vec<CameraInfo>, NokhwaError> {
201// use gstreamer::{
202// prelude::{DeviceExt, DeviceMonitorExt, DeviceMonitorExtManual},
203// Caps, DeviceMonitor,
204// };
205// use nokhwa_core::types::CameraIndex;
206// use std::str::FromStr;
207//
208// if let Err(why) = gstreamer::init() {
209// return Err(NokhwaError::GeneralError(format!(
210// "Failed to init gstreamer: {}",
211// why
212// )));
213// }
214// let device_monitor = DeviceMonitor::new();
215// let video_caps = match Caps::from_str("video/x-raw") {
216// Ok(cap) => cap,
217// Err(why) => {
218// return Err(NokhwaError::GeneralError(format!(
219// "Failed to generate caps: {}",
220// why
221// )))
222// }
223// };
224// let _video_filter_id = match device_monitor.add_filter(Some("Video/Source"), Some(&video_caps))
225// {
226// Some(id) => id,
227// None => {
228// return Err(NokhwaError::StructureError {
229// structure: "Video Filter ID Video/Source".to_string(),
230// error: "Null".to_string(),
231// })
232// }
233// };
234// if let Err(why) = device_monitor.start() {
235// return Err(NokhwaError::GeneralError(format!(
236// "Failed to start device monitor: {}",
237// why
238// )));
239// }
240// let mut counter = 0;
241// let devices: Vec<CameraInfo> = device_monitor
242// .devices()
243// .iter_mut()
244// .map(|gst_dev| {
245// let name = DeviceExt::display_name(gst_dev);
246// let class = DeviceExt::device_class(gst_dev);
247// counter += 1;
248// CameraInfo::new(&name, &class, "", CameraIndex::Index(counter - 1))
249// })
250// .collect();
251// device_monitor.stop();
252// Ok(devices)
253// }
254//
255// #[cfg(not(feature = "input-gst"))]
256// #[allow(deprecated)]
257// fn query_gstreamer() -> Result<Vec<CameraInfo>, NokhwaError> {
258// Err(NokhwaError::UnsupportedOperationError(
259// ApiBackend::GStreamer,
260// ))
261// }
262
263// please refer to https://docs.microsoft.com/en-us/windows/win32/medfound/enumerating-video-capture-devices
264#[cfg(all(feature = "input-msmf", target_os = "windows"))]
265fn query_msmf() -> Result<Vec<CameraInfo>, NokhwaError> {
266 nokhwa_bindings_windows::wmf::query_media_foundation_descriptors()
267}
268
269#[cfg(any(not(feature = "input-msmf"), not(target_os = "windows")))]
270fn query_msmf() -> Result<Vec<CameraInfo>, NokhwaError> {
271 Err(NokhwaError::UnsupportedOperationError(
272 ApiBackend::MediaFoundation,
273 ))
274}
275
276#[cfg(all(
277 feature = "input-avfoundation",
278 any(target_os = "macos", target_os = "ios")
279))]
280fn query_avfoundation() -> Result<Vec<CameraInfo>, NokhwaError> {
281 use nokhwa_bindings_macos::query_avfoundation;
282
283 Ok(query_avfoundation()?
284 .into_iter()
285 .collect::<Vec<CameraInfo>>())
286}
287
288#[cfg(not(all(
289 feature = "input-avfoundation",
290 any(target_os = "macos", target_os = "ios")
291)))]
292fn query_avfoundation() -> Result<Vec<CameraInfo>, NokhwaError> {
293 Err(NokhwaError::UnsupportedOperationError(
294 ApiBackend::AVFoundation,
295 ))
296}
297
298// #[cfg(feature = "input-jscam")]
299// fn query_wasm() -> Result<Vec<CameraInfo>, NokhwaError> {
300// use crate::js_camera::query_js_cameras;
301// use wasm_rs_async_executor::single_threaded::block_on;
302
303// block_on(query_js_cameras())
304// }
305
306fn query_wasm() -> Result<Vec<CameraInfo>, NokhwaError> {
307 Err(NokhwaError::UnsupportedOperationError(ApiBackend::Browser))
308}