### Capture and Decode Webcam Frame in Rust Source: https://github.com/l1npengtul/nokhwa/blob/senpai/README.md Initialize a camera, request a specific format, capture a frame, and decode it into an image buffer. This example demonstrates basic camera usage and frame processing. ```rust // first camera in system let index = CameraIndex::Index(0); // request the absolute highest resolution CameraFormat that can be decoded to RGB. let requested = RequestedFormat::new::(RequestedFormatType::AbsoluteHighestFrameRate); // make the camera let mut camera = Camera::new(index, requested).unwrap(); // get a frame let frame = camera.frame().unwrap(); println!("Captured Single Frame of {}", frame.buffer().len()); // decode into an ImageBuffer let decoded = frame.decode_image::().unwrap(); println!("Decoded Frame of {}", decoded.len()); ``` -------------------------------- ### Interact with Camera Controls and Streams Source: https://context7.com/l1npengtul/nokhwa/llms.txt Provides an example of using the `CameraTrait` to enumerate formats, inspect and modify camera controls, and open a streaming session. This is the primary interface for camera operations. ```rust use nokhwa_core::camera::CameraTrait; use nokhwa_core::control::{ControlId, ControlValue}; use nokhwa_core::types::{CameraFormat, FrameRate, Resolution}; use nokhwa_core::frame_format::FrameFormat; // Example usage with a concrete backend camera (pseudo-code, requires feature flag): fn use_camera(mut cam: impl CameraTrait) { // List all supported CameraFormats let formats = cam.enumerate_formats().expect("failed to list formats"); for fmt in &formats { println!("{fmt}"); } // Inspect available controls let controls = cam.controls().expect("failed to list controls"); for ctrl in &controls { println!("Control: {:?}", ctrl.id); } // Read a control value let exposure = cam.control_value(ControlId::ExposureAbsolute) .expect("failed to get exposure"); println!("Exposure: {exposure}"); // Set a control cam.set_control(ControlId::WhiteBalanceMode, ControlValue::Boolean(false)) .expect("failed to set white balance"); // Open a streaming session — callbacks run on a background thread let fmt = CameraFormat::new( Resolution::new(1280, 720), FrameFormat::MJPEG, FrameRate::from_fps(30), ); let stream = cam.open_stream( fmt, |frame_buffer| { println!("Got frame: {} bytes", frame_buffer.len()); }, |err| { eprintln!("Stream error: {err}"); }, ).expect("failed to open stream"); // Stop the stream stream.stop_stream().expect("failed to stop"); } ``` -------------------------------- ### Threaded Camera Frame Capture with CallbackCamera Source: https://context7.com/l1npengtul/nokhwa/llms.txt This example shows how to use CallbackCamera for high-level, threaded frame delivery. It requires 'input-native' and 'output-threaded' features. Note that macOS requires explicit permission requests before camera access. ```rust // Cargo.toml: // [dependencies.nokhwa] // version = "0.10" // features = ["input-native", "output-threaded"] use nokhwa:: nokhwa_initialize, pixel_format::RgbAFormat, query, utils::{ApiBackend, RequestedFormat, RequestedFormatType}, CallbackCamera, ; fn main() { // macOS requires an explicit permission request before any camera access nokhwa_initialize(|granted| { if !granted { panic!("Camera permission denied"); } run_camera(); }); // Keep the process alive while the callback thread runs std::thread::sleep(std::time::Duration::from_secs(10)); } fn run_camera() { let cameras = query(ApiBackend::Auto).expect("failed to query cameras"); let first = cameras.first().expect("no cameras found"); println!("Using camera: {:?}", first.human_name()); let format = RequestedFormat::new::(RequestedFormatType::AbsoluteHighestFrameRate); let mut cam = CallbackCamera::new( first.index().clone(), format, |buffer| { // Decode each frame to RGBA on the callback thread match buffer.decode_image::() { Ok(img) => println!("Frame {}x{} ({} bytes)", img.width(), img.height(), img.len()), Err(e) => eprintln!("Decode error: {e}"), } }, ).expect("failed to create CallbackCamera"); cam.open_stream().expect("failed to open stream"); // Poll for frames from the main thread as well loop { let frame = cam.poll_frame().expect("poll failed"); let img = frame.decode_image::().unwrap(); println!("Poll: {}x{}", img.width(), img.height()); } } ``` -------------------------------- ### Encode Video to AV1 with SvtAv1EncApp Source: https://github.com/l1npengtul/nokhwa/blob/senpai/nokhwa-decoders/test_images/notes.txt Pipe video from FFmpeg to the SvtAv1EncApp encoder for AV1 encoding. This example uses 10-bit color depth and a CRF of 30. ```bash ffmpeg -i input.mkv -pix_fmt yuv420p10le -f yuv4mpegpipe -strict -1 - | SvtAv1EncApp -i stdin --preset 2 --keyint 240 --input-depth 10 --crf 30 --film-grain 20 --output test.ivf ``` -------------------------------- ### Enumerate and Open Cameras with PlatformTrait Source: https://context7.com/l1npengtul/nokhwa/llms.txt Handles OS camera permission requests, checks if permission is granted, enumerates attached cameras, and opens the first available camera. Requires an implementation of the PlatformTrait. ```rust use nokhwa_core::platform::PlatformTrait; use nokhwa_core::types::CameraIndex; fn enumerate_and_open(mut platform: impl PlatformTrait) { // Request OS camera permission (macOS / browser; no-op on Linux/Windows) platform.block_on_permission().expect("permission denied"); if !platform.check_permission_given() { eprintln!("Camera permission not granted!"); return; } // List all cameras attached to the system let cameras = platform.query().expect("failed to query cameras"); println!("Found {} camera(s):", cameras.len()); for cam in &cameras { println!(" [{:?}] {}", cam.index, cam.information.human_name()); } // Open the first camera if let Some(first) = cameras.first() { let mut camera = platform.open(first.index.clone()) .expect("failed to open camera"); // camera now implements CameraTrait let formats = camera.enumerate_formats().unwrap(); println!("First camera supports {} formats", formats.len()); } } ``` -------------------------------- ### Linux V4L2 Camera Backend Implementation Source: https://context7.com/l1npengtul/nokhwa/llms.txt This snippet demonstrates how to use the V4L2Platform for Linux to query, open, and stream from cameras. Ensure the 'input-v4l' feature is enabled. It shows how to list supported formats and open an MJPEG stream. ```rust // Cargo.toml: // [dependencies.nokhwa] // version = "0.11" // features = ["input-v4l", "decoding-mjpeg"] // src/main.rs: use nokhwa_bindings_linux::v4l2::V4L2Platform; use nokhwa_core::platform::PlatformTrait; use nokhwa_core::types::CameraIndex; use nokhwa_core::camera::CameraTrait; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::types::{CameraFormat, FrameRate, Resolution}; fn main() { let mut platform = V4L2Platform {}; // V4L2 never needs permission prompts platform.block_on_permission().unwrap(); let cameras = platform.query().expect("failed to query V4L2 devices"); println!("{} V4L2 camera(s) found", cameras.len()); for cam in &cameras { println!(" {} -> {}", cam.index, cam.information.human_name()); } if cameras.is_empty() { return; } let mut cam = platform.open(CameraIndex::Index(0)).expect("cannot open /dev/video0"); // List supported formats let formats = cam.enumerate_formats().expect("cannot enumerate formats"); println!("Supported formats:"); for fmt in &formats { println!(" {fmt}"); } // Open MJPEG stream at 1280x720@30fps let stream_fmt = CameraFormat::new( Resolution::new(1280, 720), FrameFormat::MJPEG, FrameRate::from_fps(30), ); let _stream = cam.open_stream( stream_fmt, |buf| println!("Frame: {} bytes", buf.len()), |err| eprintln!("Error: {err}"), ).expect("failed to open stream"); } ``` -------------------------------- ### Create and Access CameraInformation Source: https://context7.com/l1npengtul/nokhwa/llms.txt Demonstrates how to create a `CameraInformation` struct and access its fields. This is useful for retrieving human-readable and technical details about a camera device. ```rust use nokhwa_core::types::CameraInformation; let info = CameraInformation::new( "Logitech C920".to_string(), "uvcvideo".to_string(), "USB 3.0, bus 1 port 2".to_string(), Some("usb-0000:00:14.0-2".to_string()), ); println!("Camera: {}", info.human_name()); // "Logitech C920" println!("Driver: {}", info.description()); // "uvcvideo" println!("Misc: {}", info.misc()); // "USB 3.0, bus 1 port 2" println!("Stable: {:?}", info.stable_id()); // Some("usb-...") // Display impl println!("{info}"); // "Name: Logitech C920, Description: uvcvideo, Extra: USB 3.0, ..." ``` -------------------------------- ### Build and Validate Camera Control Descriptions Source: https://context7.com/l1npengtul/nokhwa/llms.txt Demonstrates building a ControlDescription for an integer-range control and validating candidate values against it. Ensure the default value is within the specified range and of the correct type. ```rust use nokhwa_core::control::{ ControlId, ControlValue, ControlDescription, ControlFlags, ControlValueDescriptor, }; use nokhwa_core::ranges::Range; use std::collections::HashSet; // Build a description for an integer-range control (e.g. absolute exposure) let descriptor = ControlValueDescriptor::Integer(Range::new(1_i64, 10_000, Some(1))); let flags = HashSet::from([ControlFlags::Slider]); let description = ControlDescription::new(flags, descriptor, Some(ControlValue::Integer(166))) .expect("invalid default"); // Validate a candidate value assert!(description.validate(&ControlValue::Integer(500))); assert!(!description.validate(&ControlValue::Integer(0))); // below min assert!(!description.validate(&ControlValue::Boolean(true))); // wrong type // Construct a control value to pass to set_control() let focus_auto_on = ControlValue::Boolean(true); let zoom_level = ControlValue::Integer(200); let wb_temperature = ControlValue::Integer(5600); println!("{}", ControlId::ExposureAbsolute); // "Control ID: ExposureAbsolute" println!("{}", focus_auto_on); // "Control Value: Boolean(true)" // Custom control by raw u32 CID (V4L2 extension controls, etc.) use nokhwa_core::control::CustomControlId; let custom_id = ControlId::Custom(CustomControlId::U32(0x00980900)); ``` -------------------------------- ### YUVConfig::try_from(CameraFormat) Source: https://context7.com/l1npengtul/nokhwa/llms.txt Constructs a YUVConfig (and thus YUVDecoder) from a CameraFormat with default settings. Returns an error if the format is not a YCbCr variant. ```APIDOC ## `YUVConfig::try_from(CameraFormat)` — YUV decoder from format Constructs a `YUVConfig` (and thus `YUVDecoder`) from a `CameraFormat` with default BT.601 Full-range Balanced settings. Returns an error if the format is not a YCbCr variant. ```rust use nokhwa_decoders::yuv::{YUVConfig, YUVDecoder}; use nokhwa_core::types::{CameraFormat, FrameRate, Resolution}; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::decoder::Decoder; use nokhwa_core::frame_buffer::FrameBuffer; use image::Rgb; use std::borrow::Cow; let fmt = CameraFormat::new( Resolution::new(1280, 720), FrameFormat::Yuyv_4_2_2, FrameRate::from_fps(30), ); let config = YUVConfig::try_from(fmt).expect("must be a YCbCr format"); let mut decoder = YUVDecoder::new(config); // Decode a YUYV frame to RGB8 let yuyv_data = vec![0u8; 1280 * 720 * 2]; // placeholder let frame_buf = FrameBuffer::new(Cow::Owned(yuyv_data), None); let decoded = decoder.decode::>(frame_buf).expect("decode failed"); println!("YUYV->RGB: {}x{}", decoded.buffer.width(), decoded.buffer.height()); // Non-YCbCr format returns an error let bad_fmt = CameraFormat::new( Resolution::new(1280, 720), FrameFormat::MJPEG, FrameRate::from_fps(30), ); assert!(YUVConfig::try_from(bad_fmt).is_err()); ``` ``` -------------------------------- ### Create and Use FrameBuffer Source: https://context7.com/l1npengtul/nokhwa/llms.txt Illustrates creating `FrameBuffer` instances from existing data slices or vectors, and accessing the raw byte buffer. `FrameBuffer` is used to pass raw camera frames to callbacks. ```rust use nokhwa_core::frame_buffer::FrameBuffer; use std::borrow::Cow; // Borrow from an existing slice (zero-copy) let raw_data: Vec = vec![0xFF, 0xD8, 0xFF, 0xE0]; // JPEG SOI let buf = FrameBuffer::from_buffer(&raw_data, None); assert_eq!(buf.len(), 4); assert!(!buf.is_empty()); // Owned (from a Vec) let buf_owned = FrameBuffer::from_vec(raw_data.clone(), None); // Deep copy (always produces an owned copy) let buf_copy = buf.deep_copy(); // Access raw bytes let bytes: &[u8] = buf.buffer(); println!("First byte: 0x{:02X}", bytes[0]); // "First byte: 0xFF" // Consume to get inner Cow + metadata let (cow, meta) = buf_owned.consume(); assert!(meta.is_none()); ``` -------------------------------- ### Convert PNG to Raw NV12 Video with FFmpeg Source: https://github.com/l1npengtul/nokhwa/blob/senpai/nokhwa-decoders/test_images/notes.txt Use this command to convert a PNG image to a raw NV12 video stream. Ensure the input image is compatible. ```bash ffmpeg -i crimeandsekai.png -pix_fmt nv12 -f rawvideo crimeandsekai.yuv ``` -------------------------------- ### Identify Camera Device with CameraIndex Source: https://context7.com/l1npengtul/nokhwa/llms.txt Use CameraIndex to select which camera to open. It supports OS index, string path, device ID, or a stable string ID for consistent device identification across reboots. ```rust use nokhwa_core::types::CameraIndex; // Most common: open the first camera by OS index let index_zero = CameraIndex::Index(0); // Open a specific V4L2 device node by path let index_path = CameraIndex::String("/dev/video2".to_string()); // Stable ID (e.g. USB serial number) – survives USB re-plug let index_stable = CameraIndex::Stable("usb-0000:00:14.0-1".to_string()); // Convert to u32 for backends that require numeric indices assert_eq!(index_zero.as_index().unwrap(), 0u32); assert_eq!(index_zero.as_string(), "0"); assert!(index_zero.is_index()); assert!(index_path.is_string()); // Default is Index(0) let default: CameraIndex = CameraIndex::default(); assert_eq!(default.as_index().unwrap(), 0); ``` -------------------------------- ### Map Image Pixel Types to PixelDestination Source: https://context7.com/l1npengtul/nokhwa/llms.txt Use `get_by_pixel` to map common `image::Pixel` types to their corresponding `PixelDestination` variants at compile time. Check decoder support for specific pixel formats using `supports_destination`. ```rust use nokhwa_core::pixel_destination::PixelDestination; use image::{Luma, Rgb, Rgba}; // Mapping image::Pixel types to PixelDestination let rgb8 = PixelDestination::get_by_pixel::>(); let rgba8 = PixelDestination::get_by_pixel::>(); let rgb16 = PixelDestination::get_by_pixel::>(); let luma8 = PixelDestination::get_by_pixel::>(); assert_eq!(rgb8, Some(PixelDestination::Rgb8)); assert_eq!(rgba8, Some(PixelDestination::Rgba8)); assert_eq!(rgb16, Some(PixelDestination::Rgb16)); assert_eq!(luma8, Some(PixelDestination::Luma8)); // Check decoder support use nokhwa_decoders::mjpeg::MJpegDecoder; use nokhwa_core::decoder::Decoder; assert!(MJpegDecoder::supports_destination(PixelDestination::Rgb8)); assert!(MJpegDecoder::supports_destination(PixelDestination::Luma8)); assert!(!MJpegDecoder::supports_destination(PixelDestination::Rgb16)); // not supported by MJPEG decoder println!("All destinations: {:?}", PixelDestination::ALL); ``` -------------------------------- ### Handling NokhwaError Variants Source: https://context7.com/l1npengtul/nokhwa/llms.txt Demonstrates pattern matching on NokhwaError variants for specific error handling. Also shows the usage of the NokhwaResult type alias. ```rust use nokhwa_core::error::{NokhwaError, StreamError}; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::types::{Backends, CameraIndex}; // Pattern-match on specific variants fn handle_error(e: NokhwaError) { match e { NokhwaError::OpenDeviceError(idx, msg) => eprintln!("Cannot open camera {idx}: {msg}"), NokhwaError::InitializeError { backend, error } => eprintln!("Backend {backend} init failed: {error}"), NokhwaError::StreamError(StreamError::NoLongerExists) => eprintln!("Camera was unplugged"), NokhwaError::DecoderUnsupportedFrameFormat(fmt) => eprintln!("Cannot decode {fmt}"), NokhwaError::DecoderNeedsMoreData(msg) => { // Not a fatal error – send more data to the decoder assert!(e.is_needs_more()); eprintln!("Send more: {msg}"); } other => eprintln!("Other error: {other}"), } } // Using the result alias use nokhwa_core::error::NokhwaResult; fn open_camera_index(raw: u32) -> NokhwaResult<()> { let idx = CameraIndex::Index(raw); if raw > 9 { return Err(NokhwaError::OpenDeviceError( idx, "index too large (demo)".to_string(), )); } Ok(()) } assert!(open_camera_index(0).is_ok()); assert!(open_camera_index(10).is_err()); ``` -------------------------------- ### Convert Raw NV12 Video to PNG with FFmpeg Source: https://github.com/l1npengtul/nokhwa/blob/senpai/nokhwa-decoders/test_images/notes.txt This command converts a raw NV12 video file back into a PNG image. Specify the input dimensions and pixel format. ```bash ffmpeg -s 1024x1520 -pix_fmt nv12 -i crimeandsekai.yuv -f image2 -pix_fmt rgb24 crimeandsekai.nv12.png ``` -------------------------------- ### YUVConfig from CameraFormat Source: https://context7.com/l1npengtul/nokhwa/llms.txt Constructs a YUVConfig and YUVDecoder from a CameraFormat. Requires a YCbCr variant; other formats will result in an error. Decodes YUYV frames to RGB8. ```rust use nokhwa_decoders::yuv::{YUVConfig, YUVDecoder}; use nokhwa_core::types::{CameraFormat, FrameRate, Resolution}; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::decoder::Decoder; use nokhwa_core::frame_buffer::FrameBuffer; use image::Rgb; use std::borrow::Cow; let fmt = CameraFormat::new( Resolution::new(1280, 720), FrameFormat::Yuyv_4_2_2, FrameRate::from_fps(30), ); let config = YUVConfig::try_from(fmt).expect("must be a YCbCr format"); let mut decoder = YUVDecoder::new(config); // Decode a YUYV frame to RGB8 let yuyv_data = vec![0u8; 1280 * 720 * 2]; // placeholder let frame_buf = FrameBuffer::new(Cow::Owned(yuyv_data), None); let decoded = decoder.decode::>(frame_buf).expect("decode failed"); println!("YUYV->RGB: {}x{}", decoded.buffer.width(), decoded.buffer.height()); // Non-YCbCr format returns an error let bad_fmt = CameraFormat::new( Resolution::new(1280, 720), FrameFormat::MJPEG, FrameRate::from_fps(30), ); assert!(YUVConfig::try_from(bad_fmt).is_err()); ``` -------------------------------- ### Select Camera Format using FormatRequest Source: https://context7.com/l1npengtul/nokhwa/llms.txt Filters and sorts camera formats based on specified criteria like highest frame rate, closest match to a target resolution/frame rate, or exact match. Supports filtering by frame format. ```rust use nokhwa_core::format_request::{FormatRequest, FormatRequestType}; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::ranges::Range; use nokhwa_core::types::{CameraFormat, FrameRate, Resolution}; // Build a list of candidate formats (normally from camera.enumerate_formats()) let formats = vec![ CameraFormat::new(Resolution::new(640, 480), FrameFormat::MJPEG, FrameRate::from_fps(30)), CameraFormat::new(Resolution::new(1280, 720), FrameFormat::MJPEG, FrameRate::from_fps(30)), CameraFormat::new(Resolution::new(1920,1080), FrameFormat::MJPEG, FrameRate::from_fps(15)), CameraFormat::new(Resolution::new(1920,1080), FrameFormat::NV12, FrameRate::from_fps(30)), ]; // Pick the highest frame rate among MJPEG formats let req = FormatRequest::new( FormatRequestType::HighestFrameRate { frame_rate: Range::new(FrameRate::from_fps(1), FrameRate::from_fps(120), None), }, vec![FrameFormat::MJPEG], ); let sorted = req.sort_foramts(formats.clone()); println!("Best MJPEG: {}", sorted[0]); // sorted by fps, filtered to MJPEG // Closest match to 720p@30fps (any YCbCr or MJPEG) let req_closest = FormatRequest::new( FormatRequestType::Closest { resolution: None, preferred_resolution: Some(Resolution::new(1280, 720)), frame_rate: None, preferred_frame_rate: Some(FrameRate::from_fps(30)), }, vec![FrameFormat::MJPEG, FrameFormat::NV12], ); let closest_sorted = req_closest.sort_foramts(formats.clone()); if let Some(best) = closest_sorted.first() { println!("Closest format: {best}"); } // Exact match let req_exact = FormatRequest::new( FormatRequestType::Exact { resolution: Resolution::new(1920, 1080), frame_rate: FrameRate::from_fps(30), }, vec![FrameFormat::NV12], ); let exact = req_exact.sort_foramts(formats); assert_eq!(exact.len(), 1); println!("Exact: {}", exact[0]); // "1920x1080@30/1 FPS, NV12 Format" ``` -------------------------------- ### MJpegDecoder from CameraFormat Source: https://context7.com/l1npengtul/nokhwa/llms.txt Convenience constructor for MJpegDecoder using CameraFormat. Ensure the format is MJPEG, otherwise an error is returned. Reads JPEG data from a file and decodes it. ```rust use nokhwa_decoders::mjpeg::MJpegDecoder; use nokhwa_core::types::{CameraFormat, FrameRate, Resolution}; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::decoder::Decoder; use nokhwa_core::frame_buffer::FrameBuffer; use image::Rgba; let camera_fmt = CameraFormat::new( Resolution::new(640, 480), FrameFormat::MJPEG, FrameRate::from_fps(30), ); let mut decoder = MJpegDecoder::from_camera_format(camera_fmt) .expect("must be MJPEG format"); let jpeg_data = std::fs::read("640x480.mjpeg").unwrap_or_default(); let buf = FrameBuffer::from_vec(jpeg_data, None); let result = decoder.decode::>(buf); match result { Ok(img) => println!("Got {}x{} RGBA frame", img.buffer.width(), img.buffer.height()), Err(e) => eprintln!("Error: {e}"), } // Wrong format returns an error let wrong_fmt = CameraFormat::new( Resolution::new(640, 480), FrameFormat::NV12, FrameRate::from_fps(30), ); assert!(MJpegDecoder::from_camera_format(wrong_fmt).is_err()); ``` -------------------------------- ### `FrameRate` — Camera frame rate as a rational number Source: https://context7.com/l1npengtul/nokhwa/llms.txt `FrameRate` represents the camera's frame rate using a rational number (`num_rational::Ratio`), allowing for precise fractional rates like 29.97 FPS. It provides methods to create rates from integers or fractions and to approximate the rate as a float. ```APIDOC ## `FrameRate` — Camera frame rate as a rational number `FrameRate` wraps a `num_rational::Ratio` to represent fractional frame rates (e.g. 30000/1001 for 29.97 fps). Panics if denominator is 0. ```rust use nokhwa_core::types::FrameRate; // Integer fps let fps30 = FrameRate::from_fps(30); let fps60 = FrameRate::from_fps(60); // Fractional (29.97 NTSC) let ntsc = FrameRate::new(30_000, 1_001); assert_eq!(fps30.numerator(), 30); assert_eq!(fps30.denominator(), 1); let approx = ntsc.approximate_float().unwrap(); assert!((approx - 29.97).abs() < 0.01, "got {approx}"); println!("{fps30}"); // "30/1 FPS" println!("{ntsc}"); // "30000/1001 FPS" // Default is 30/1 let default = FrameRate::default(); assert_eq!(default.numerator(), 30); ``` ``` -------------------------------- ### Convert MKV to H.265 Annex B Stream with FFmpeg Source: https://github.com/l1npengtul/nokhwa/blob/senpai/nokhwa-decoders/test_images/notes.txt Use this command to copy the video stream from an MKV container and convert it to the H.265 Annex B format. This is useful for preparing streams for certain encoders. ```bash ffmpeg -i in.mkv -c:v copy -bsf hevc_mp4toannexb out.h265 ``` -------------------------------- ### Define and Validate Integer Ranges with Steps Source: https://context7.com/l1npengtul/nokhwa/llms.txt Create inclusive/exclusive ranges with optional steps for validating control values or filtering formats. Ensure values fall within the defined bounds and adhere to the step. ```rust use nokhwa_core::ranges::{Range, ValidatableRange}; // Integer range with step (e.g. exposure 1–10000, step 1) let exposure_range: Range = Range::new(1, 10_000, Some(1)); assert!(exposure_range.validate(&500)); assert!(!exposure_range.validate(&0)); // below min assert!(!exposure_range.validate(&10_001)); // above max // Range with step (step must evenly divide offset from min) let stepped: Range = Range::new(0, 100, Some(5)); assert!(stepped.validate(&25)); assert!(!stepped.validate(&23)); // not a multiple of 5 from 0 // Exclusive upper bound let excl: Range = Range::with_inclusive(0, true, 10, false, None); assert!(excl.validate(&9)); assert!(!excl.validate(&10)); // upper is exclusive println!("{exposure_range}"); // "Range: [1, 10000]" ``` -------------------------------- ### Convert MKV to H.264 Annex B Stream with FFmpeg Source: https://github.com/l1npengtul/nokhwa/blob/senpai/nokhwa-decoders/test_images/notes.txt This command copies the video stream from an MKV container and converts it to the H.264 Annex B format. Ensure the input stream is H.264. ```bash ffmpeg -i in.mkv -c:v copy -bsf h264_mp4toannexb out.h264 ``` -------------------------------- ### Add Nokhwa to Cargo.toml Source: https://github.com/l1npengtul/nokhwa/blob/senpai/README.md Include Nokhwa in your project's dependencies. Enable specific features like native input backends and WGPU integration. Add the 'image' crate for JPEG/PNG export. ```toml [dependencies.nokhwa] version = "0.10" # Use the native input backends, enable WGPU integration features = ["input-native", "output-wgpu"] # add this to enable exporting to a JPEG/PNG [dependencies.image] version = "0.25" features = ["default-formats"] ``` -------------------------------- ### MJpegDecoder::from_camera_format Source: https://context7.com/l1npengtul/nokhwa/llms.txt Creates an MJpegDecoder directly from a CameraFormat. It requires the format to be FrameFormat::MJPEG, returning an error otherwise. ```APIDOC ## `MJpegDecoder::from_camera_format` — Convenience constructor Creates an `MJpegDecoder` directly from a `CameraFormat` that must have `FrameFormat::MJPEG`. Returns `NokhwaError::DecoderInvalidFrameData` if the format is not MJPEG. ```rust use nokhwa_decoders::mjpeg::MJpegDecoder; use nokhwa_core::types::{CameraFormat, FrameRate, Resolution}; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::decoder::Decoder; use nokhwa_core::frame_buffer::FrameBuffer; use image::Rgba; let camera_fmt = CameraFormat::new( Resolution::new(640, 480), FrameFormat::MJPEG, FrameRate::from_fps(30), ); let mut decoder = MJpegDecoder::from_camera_format(camera_fmt) .expect("must be MJPEG format"); let jpeg_data = std::fs::read("640x480.mjpeg").unwrap_or_default(); let buf = FrameBuffer::from_vec(jpeg_data, None); let result = decoder.decode::>(buf); match result { Ok(img) => println!("Got {}x{} RGBA frame", img.buffer.width(), img.buffer.height()), Err(e) => eprintln!("Error: {e}"), } // Wrong format returns an error let wrong_fmt = CameraFormat::new( Resolution::new(640, 480), FrameFormat::NV12, FrameRate::from_fps(30), ); assert!(MJpegDecoder::from_camera_format(wrong_fmt).is_err()); ``` ``` -------------------------------- ### Decode NV12 YUV Frames Source: https://context7.com/l1npengtul/nokhwa/llms.txt Demonstrates decoding NV12 formatted YUV frame data into an RGB image buffer using YUVDecoder. Configuration includes resolution, YUV format specifics, and conversion settings. ```rust use nokhwa_decoders::yuv::{YUVConfig, YUVDecoder}; use nokhwa_core::decoder::Decoder; use nokhwa_core::frame_buffer::FrameBuffer; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::types::Resolution; use image::Rgb; use yuv::{YuvConversionMode, YuvRange, YuvStandardMatrix}; use std::borrow::Cow; // --- NV12 decode --- let nv12_bytes: Vec = std::fs::read("frame.yuv").unwrap_or_default(); let mut yuv_decoder = YUVDecoder::new(YUVConfig { resolution: Resolution::new(1920, 1080), yuv_type: FrameFormat::NV12, range: YuvRange::Full, matrix: YuvStandardMatrix::Bt601, mode: YuvConversionMode::Balanced, premultiply_alpha: false, custom_frame_format_map: None, }); let frame_buf = FrameBuffer::new(Cow::Owned(nv12_bytes), None); let decoded = yuv_decoder.decode::>(frame_buf).expect("YUV decode failed"); println!("NV12 decoded: {}x{}", decoded.buffer.width(), decoded.buffer.height()); ``` -------------------------------- ### `CameraIndex` — Identify a camera device Source: https://context7.com/l1npengtul/nokhwa/llms.txt `CameraIndex` is used to select which camera device to open. It supports various identification methods including OS index, string path, device ID, and a stable string ID for persistent device recognition across reboots. ```APIDOC ## `CameraIndex` — Identify a camera device `CameraIndex` selects which camera to open. It can be a numeric OS index, a string path or device ID (used for IP cameras and browser `deviceId`s), or a stable string ID for reopening the same physical device across reboots. ```rust use nokhwa_core::types::CameraIndex; // Most common: open the first camera by OS index let index_zero = CameraIndex::Index(0); // Open a specific V4L2 device node by path let index_path = CameraIndex::String("/dev/video2".to_string()); // Stable ID (e.g. USB serial number) – survives USB re-plug let index_stable = CameraIndex::Stable("usb-0000:00:14.0-1".to_string()); // Convert to u32 for backends that require numeric indices assert_eq!(index_zero.as_index().unwrap(), 0u32); assert_eq!(index_zero.as_string(), "0"); assert!(index_zero.is_index()); assert!(index_path.is_string()); // Default is Index(0) let default: CameraIndex = CameraIndex::default(); assert_eq!(default.as_index().unwrap(), 0); ``` ``` -------------------------------- ### CameraTrait Source: https://context7.com/l1npengtul/nokhwa/llms.txt The core trait implemented by camera backends, providing methods for enumerating formats, accessing camera controls, and opening streaming sessions. It's the primary interface for interacting with camera devices. ```APIDOC ## `CameraTrait` — Core camera backend interface `CameraTrait` is the trait every backend camera type implements. It exposes format enumeration, camera controls, and stream opening. Backends (V4L2, MSMF, AVFoundation) return types implementing this trait from `PlatformTrait::open`. ```rust use nokhwa_core::camera::CameraTrait; use nokhwa_core::control::{ControlId, ControlValue}; use nokhwa_core::types::{CameraFormat, FrameRate, Resolution}; use nokhwa_core::frame_format::FrameFormat; // Example usage with a concrete backend camera (pseudo-code, requires feature flag): fn use_camera(mut cam: impl CameraTrait) { // List all supported CameraFormats let formats = cam.enumerate_formats().expect("failed to list formats"); for fmt in &formats { println!("{fmt}"); } // Inspect available controls let controls = cam.controls().expect("failed to list controls"); for ctrl in &controls { println!("Control: {:?}", ctrl.id); } // Read a control value let exposure = cam.control_value(ControlId::ExposureAbsolute) .expect("failed to get exposure"); println!("Exposure: {exposure}"); // Set a control cam.set_control(ControlId::WhiteBalanceMode, ControlValue::Boolean(false)) .expect("failed to set white balance"); // Open a streaming session — callbacks run on a background thread let fmt = CameraFormat::new( Resolution::new(1280, 720), FrameFormat::MJPEG, FrameRate::from_fps(30), ); let stream = cam.open_stream( fmt, |frame_buffer| { println!("Got frame: {} bytes", frame_buffer.len()); }, |err| { eprintln!("Stream error: {err}"); }, ).expect("failed to open stream"); // Stop the stream stream.stop_stream().expect("failed to stop"); } ``` ``` -------------------------------- ### Combined Stream Format with CameraFormat Source: https://context7.com/l1npengtul/nokhwa/llms.txt CameraFormat bundles Resolution, FrameFormat, and FrameRate into a single descriptor. It is used when opening a stream or querying device capabilities. Convenience constructors are available for raw values. ```rust use nokhwa_core::types::{CameraFormat, FrameRate, Resolution}; use nokhwa_core::frame_format::FrameFormat; // Construct explicitly let fmt = CameraFormat::new( Resolution::new(1920, 1080), FrameFormat::MJPEG, FrameRate::from_fps(30), ); assert_eq!(fmt.width(), 1920); assert_eq!(fmt.height(), 1080); assert_eq!(fmt.format(), FrameFormat::MJPEG); assert_eq!(fmt.frame_rate().numerator(), 30); // Convenience raw constructor let fmt2 = CameraFormat::new_from(640, 480, FrameFormat::NV12, FrameRate::from_fps(60)); println!("{fmt}"); // "1920x1080@30/1 FPS, MJPEG Format" // Default: 640x480 MJPEG @ 30fps let default = CameraFormat::default(); assert_eq!(default.width(), 640); ``` -------------------------------- ### Camera Frame Rate Representation with FrameRate Source: https://context7.com/l1npengtul/nokhwa/llms.txt FrameRate wraps a num_rational::Ratio to represent fractional frame rates. It panics if the denominator is 0. The approximate_float method provides a floating-point approximation. ```rust use nokhwa_core::types::FrameRate; // Integer fps let fps30 = FrameRate::from_fps(30); let fps60 = FrameRate::from_fps(60); // Fractional (29.97 NTSC) let ntsc = FrameRate::new(30_000, 1_001); assert_eq!(fps30.numerator(), 30); assert_eq!(fps30.denominator(), 1); let approx = ntsc.approximate_float().unwrap(); assert!((approx - 29.97).abs() < 0.01, "got {approx}"); println!("{fps30}"); // "30/1 FPS" println!("{ntsc}"); // "30000/1001 FPS" // Default is 30/1 let default = FrameRate::default(); assert_eq!(default.numerator(), 30); ``` -------------------------------- ### Decode MJPEG Frames Source: https://context7.com/l1npengtul/nokhwa/llms.txt Shows how to decode MJPEG encoded frame data into an RGB image buffer using MJpegDecoder. Requires frame data, resolution, and decoder options. ```rust use nokhwa_decoders::mjpeg::{MJpegConfig, MJpegDecoder}; use nokhwa_core::decoder::Decoder; use nokhwa_core::frame_buffer::FrameBuffer; use nokhwa_core::types::Resolution; use zune_core::options::DecoderOptions; use image::Rgb; // --- MJPEG decode --- let jpeg_bytes: Vec = std::fs::read("frame.mjpeg").unwrap_or_default(); let config = MJpegConfig { resolution: Resolution::new(1280, 720), decoder_options: DecoderOptions::new_safe(), }; let mut decoder = MJpegDecoder::new(config); let frame_buf = FrameBuffer::from_vec(jpeg_bytes, None); match decoder.decode::>(frame_buf) { Ok(decoded) => { // decoded.buffer is an ImageBuffer, Vec> println!("Decoded {}x{} pixels", decoded.buffer.width(), decoded.buffer.height()); // Save with the `image` crate decoded.buffer.save("output.png").unwrap(); } Err(e) => eprintln!("Decode error: {e}"), } ``` -------------------------------- ### Validate Resolution Ranges Source: https://context7.com/l1npengtul/nokhwa/llms.txt Use `Range` to define acceptable video resolutions for format selection. Ensure that provided resolutions fall within the specified width and height bounds. ```rust use nokhwa_core::ranges::Range; use nokhwa_core::types::Resolution; let res_range: Range = Range::new( Resolution::new(640, 480), Resolution::new(1920, 1080), None, ); assert!(res_range.validate(&Resolution::new(1280, 720))); assert!(!res_range.validate(&Resolution::new(3840, 2160))); ``` -------------------------------- ### Camera Image Dimensions with Resolution Source: https://context7.com/l1npengtul/nokhwa/llms.txt Resolution stores width and height as u32. It supports arithmetic operations for format calculations and implements Ord based on total pixel count. The aspect_ratio method returns the width/height ratio as f64. ```rust use nokhwa_core::types::Resolution; let hd = Resolution::new(1280, 720); let fhd = Resolution::new(1920, 1080); let qhd = Resolution::new(2560, 1440); assert_eq!(hd.width(), 1280); assert_eq!(hd.height(), 720); assert!((hd.aspect_ratio() - 16.0 / 9.0).abs() < 1e-6); // Ordering is by pixel count (width first, then height) assert!(hd < fhd); assert!(fhd < qhd); // Arithmetic let diff = fhd - hd; // Resolution { width: 640, height: 360 } let ratio = fhd / hd; // Resolution { width: 1, height: 1 } println!("{fhd}"); // "1920x1080" ``` -------------------------------- ### Inspect and Stop Camera Stream Source: https://context7.com/l1npengtul/nokhwa/llms.txt The `StreamTrait` provides access to the current camera format and allows stopping the stream to release hardware resources. Handle potential errors during stream termination. ```rust use nokhwa_core::stream::StreamTrait; fn inspect_and_stop(stream: impl StreamTrait) { let fmt = stream.current_format(); println!( "Streaming at {}x{} @ {} fps ({})", fmt.width(), fmt.height(), fmt.frame_rate(), fmt.format(), ); // Stop the stream, releasing the camera hardware match stream.stop_stream() { Ok(()) => println!("Stream stopped cleanly"), Err(e) => eprintln!("Error stopping stream: {e}"), } } ``` -------------------------------- ### Encode Video to H.265 (HEVC) with FFmpeg Source: https://github.com/l1npengtul/nokhwa/blob/senpai/nokhwa-decoders/test_images/notes.txt Encode an input video file to the H.265 (HEVC) format using the libx265 encoder. The -vtag hvc1 is used for compatibility. ```bash ffmpeg -i input.mp4 -c:v libx265 -vtag hvc1 output.mp4 ``` -------------------------------- ### CameraInformation Source: https://context7.com/l1npengtul/nokhwa/llms.txt Represents metadata for a camera device, including its human-readable name, driver description, and optional stable identifier. This struct is populated from OS-level capabilities queries. ```APIDOC ## `CameraInformation` — Device metadata `CameraInformation` carries the human-readable name, driver description, miscellaneous info string, and an optional stable identifier for the device. Backends populate these fields from OS-level capabilities queries. ```rust use nokhwa_core::types::CameraInformation; let info = CameraInformation::new( "Logitech C920".to_string(), "uvcvideo".to_string(), "USB 3.0, bus 1 port 2".to_string(), Some("usb-0000:00:14.0-2".to_string()), ); println!("Camera: {}", info.human_name()); // "Logitech C920" println!("Driver: {}", info.description()); // "uvcvideo" println!("Misc: {}", info.misc()); // "USB 3.0, bus 1 port 2" println!("Stable: {:?}", info.stable_id()); // Some("usb-...") // Display impl println!("{info}"); // "Name: Logitech C920, Description: uvcvideo, Extra: USB 3.0, ..." ``` ``` -------------------------------- ### FrameBuffer Source: https://context7.com/l1npengtul/nokhwa/llms.txt A wrapper for raw frame bytes, containing a `Cow<'a, [u8]>` and optional metadata. It implements `Deref` and `AsRef<[u8]>` for easy access to frame data. ```APIDOC ## `FrameBuffer` — Raw frame bytes from a camera `FrameBuffer<'a>` wraps a `Cow<'a, [u8]>` and optional `Metadata`. It is the type handed to frame callbacks from `CameraTrait::open_stream`. Implements `Deref` and `AsRef<[u8]>`. ```rust use nokhwa_core::frame_buffer::FrameBuffer; use std::borrow::Cow; // Borrow from an existing slice (zero-copy) let raw_data: Vec = vec![0xFF, 0xD8, 0xFF, 0xE0]; // JPEG SOI let buf = FrameBuffer::from_buffer(&raw_data, None); assert_eq!(buf.len(), 4); assert!(!buf.is_empty()); // Owned (from a Vec) let buf_owned = FrameBuffer::from_vec(raw_data.clone(), None); // Deep copy (always produces an owned copy) let buf_copy = buf.deep_copy(); // Access raw bytes let bytes: &[u8] = buf.buffer(); println!("First byte: 0x{:02X}", bytes[0]); // "First byte: 0xFF" // Consume to get inner Cow + metadata let (cow, meta) = buf_owned.consume(); assert!(meta.is_none()); ``` ```