g2g_python/lib.rs
1//! Host gst-python-ml elements as first-class glass2glass elements (M198).
2//!
3//! Experimental (Tier 3 in `STABILITY.md`): no stability promise.
4//!
5//! gst-python-ml factored its ML logic away from GStreamer: the inference
6//! `tasks/`, the engine `MLEngineMixin`, and the per-frame work all run with no
7//! framework types, behind three seams selected by the `PYML_BACKEND` env var:
8//! `FrameIO` (read/write/append-blob a buffer), `AnalyticsBackend` (attach
9//! detection metadata), and the element base classes. Today only a `gst`
10//! backend exists. This crate is the g2g host those seams target: it embeds
11//! CPython in the g2g process, exposes a native `g2g` module that backs
12//! `FrameIO` / `AnalyticsBackend` against the live Rust [`Frame`], and wraps a
13//! hosted element instance in a g2g [`AsyncElement`] so it negotiates caps and
14//! flows frames like any other node.
15//!
16//! Build layers:
17//! - **default (`std`)**: the [`PyTransform`] shell and the pixel-format
18//! mapping ([`format`]) compile. Caps negotiation works; the per-frame Python
19//! call returns [`G2gError::UnsupportedDomain`] because there is no
20//! interpreter. This keeps the crate in `cargo check --workspace` without
21//! libpython.
22//! - **`python`**: pulls pyo3 + numpy, embeds CPython, and runs the hosted
23//! element for real. OS-coupled, off the no_std / RTOS baseline.
24//!
25//! Two zero-copy frame paths reach the hosted element: System memory over the
26//! Python buffer protocol (`g2g_process`), and a GPU-resident CUDA frame over
27//! `__cuda_array_interface__` / `__dlpack__`, one `g2g.CudaPlane` per NV12 plane
28//! handed to `g2g_process_cuda` (or its batch and produce siblings). The `host`
29//! and `cuda_plane` modules carry the contracts, the plane layout, and the
30//! CUDA-context caveat.
31//!
32//! This crate links `std` unconditionally (embedding CPython is the most
33//! OS-coupled thing in the tree). The `std` feature only forwards to
34//! `g2g-core/std`; it is not a no_std gate on this crate.
35
36pub mod format;
37
38mod aggregator;
39mod element;
40mod props;
41mod source;
42pub use aggregator::PyAggregator;
43pub use element::PyTransform;
44pub use source::PySource;
45
46/// Register the hosted Python elements as `gst-launch` / autoplug factories on
47/// `registry`, so they are instantiable by name like any built-in:
48/// - `pyelement module=... class=... draw-label=...` — a transform;
49/// - `pysrc module=... class=... format=... width=... height=... framerate=...
50/// num-buffers=...` — a source;
51/// - `pyaggregator module=... class=... draw-label=...` — an N-in/1-out batching
52/// muxer (its input count comes from link degree).
53///
54/// The parser default-constructs each and applies its `key=value` properties via
55/// the property system, then negotiation + `configure_pipeline` spawn the worker.
56/// Call after [`g2g_plugins::default_registry`] (or any `Registry`). Building the
57/// per-frame path still needs the `python` feature.
58#[cfg(feature = "std")]
59pub fn register(registry: &mut g2g_core::runtime::Registry) {
60 use g2g_core::runtime::{LaunchFactory, MuxerFactory, SourceFactory};
61 use g2g_core::{Caps, Dim, Rate, RawVideoFormat};
62
63 registry.register_launch(LaunchFactory::of::<PyTransform>("pyelement", || {
64 Box::new(PyTransform::new("", ""))
65 }));
66
67 registry.register_muxer(MuxerFactory::new("pyaggregator", |inputs| {
68 Box::new(PyAggregator::new("", "", inputs))
69 }));
70
71 // The declared caps are the default; `format=`/`width=`/... properties
72 // refine them, and `intercept_caps` returns the refined set at negotiation.
73 let default_caps = Caps::RawVideo {
74 format: RawVideoFormat::Rgba8,
75 width: Dim::Fixed(320),
76 height: Dim::Fixed(240),
77 framerate: Rate::Fixed(30),
78 interlace: g2g_core::Interlace::Any,
79 };
80 registry.register_source(SourceFactory::new("pysrc", default_caps, || {
81 Box::new(PySource::new("", ""))
82 }));
83}
84
85#[cfg(feature = "python")]
86mod cuda_plane;
87#[cfg(feature = "python")]
88mod host;
89#[cfg(feature = "python")]
90pub use host::init_host;