Skip to main content

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}