nv_frame/lib.rs
1//! # nv-frame
2//!
3//! Frame abstraction for the NextVision video perception runtime.
4//!
5//! The central type is [`FrameEnvelope`] — an immutable, ref-counted frame
6//! that carries pixel data, timing information, and extensible metadata.
7//!
8//! ## Design goals
9//!
10//! - **Immutable after construction** — no mutable access to pixel data.
11//! - **Cheap to clone** — `Clone` is an `Arc` bump, not a pixel copy.
12//! - **Zero-copy from GStreamer** — via the `Mapped` pixel data variant.
13//! - **Thread-safe** — `Send + Sync` for cross-thread handoff.
14//! - **Self-describing** — carries format, dimensions, stride, and metadata.
15//!
16//! ## Pixel formats
17//!
18//! The library normalizes frames to a known set of [`PixelFormat`]s.
19//! Conversion utilities are in the [`convert`] module (opt-in, allocates).
20//!
21//! ## Data residency
22//!
23//! Frames are either **host-resident** (CPU-accessible bytes, the default)
24//! or **device-resident** (opaque accelerated buffer on a GPU/NPU).
25//!
26//! [`Residency`] describes where the data lives. [`DataAccess`] describes
27//! what host-access is available:
28//!
29//! | `Residency` | `DataAccess` | Meaning |
30//! |---|---|---|
31//! | `Host` | `HostReadable` | Zero-copy `&[u8]` via `host_data()`. |
32//! | `Device` | `MappableToHost` | Materializable via `require_host_data()`. |
33//! | `Device` | `Opaque` | No host path; use `accelerated_handle::<T>()`. |
34//!
35//! ### CPU-only stages
36//!
37//! Use [`FrameEnvelope::require_host_data()`] — all paths return
38//! `Cow::Borrowed`: host frames borrow directly from the frame,
39//! device frames borrow from a per-frame cache populated on first access.
40//! Opaque frames return `Err(`[`FrameAccessError`]`)`.
41//!
42//! ```ignore
43//! let pixels = frame.require_host_data()
44//! .map_err(|e| StageError::ProcessingFailed {
45//! stage_id: MY_STAGE,
46//! detail: e.to_string(),
47//! })?;
48//! process_cpu(&pixels);
49//! ```
50//!
51//! ### Memoization
52//!
53//! Materialized host bytes are cached in the frame's `Arc`-shared inner
54//! state. Repeated calls (including from clones) reuse the cache at zero
55//! cost. Failures are also cached — frame data is immutable, so a
56//! transfer that fails will not succeed on retry.
57//!
58//! ### Mixed CPU/GPU stages
59//!
60//! Branch on [`DataAccess`]:
61//!
62//! ```ignore
63//! match frame.data_access() {
64//! DataAccess::HostReadable => { /* host_data() */ }
65//! DataAccess::MappableToHost => { /* require_host_data() or accelerated_handle */ }
66//! DataAccess::Opaque => { /* accelerated_handle::<T>() only */ }
67//! _ => {}
68//! }
69//! ```
70//!
71//! ## Adapter crates and the opaque handle
72//!
73//! The accelerated handle is intended only for:
74//! - Backend adapter crates bridging accelerated decode buffers
75//! - GPU tensors destined for inference
76//! - Accelerator-native frame storage
77//!
78//! It must **not** be used for general stage metadata or cross-stage
79//! messaging. Use [`nv_core::TypedMetadata`] for those purposes.
80//!
81//! When constructing device frames, adapter crates may optionally provide
82//! a [`HostMaterializeFn`] that enables CPU fallback. The materializer
83//! returns [`HostBytes`] — either an owned `Vec<u8>` or a zero-copy
84//! mapped view. See [`FrameEnvelope::new_device()`] for details.
85
86pub mod convert;
87pub mod frame;
88
89pub use convert::ConvertError;
90pub use frame::{
91 DataAccess, FrameAccessError, FrameEnvelope, HostBytes, HostMaterializeFn, PixelFormat,
92 Residency,
93};