media/lib.rs
1#![allow(
2 clippy::doc_markdown,
3 reason = "scaffolded module docs use bare GStreamer / MediaTime / MediaClock in prose; M-MEDIA.1..22 chunks rewrite each module's docs as the real types land"
4)]
5
6//! `media` — GStreamer-backed audio + video capture, playback orchestration,
7//! and the data models that `wisp` (renderer) and `app` (Tauri/Leptos
8//! shell) consume.
9//!
10//! # Three-way split
11//!
12//! Boundaries are load-bearing. Crossing them once would bloat every wisp
13//! consumer (storybook, headless export, future plugins) with GStreamer's
14//! build + license footprint.
15//!
16//! - **`media` (this crate)** owns: GStreamer capture (audio + video),
17//! GStreamer playback, audio/video data models ([`audio::AudioChunk`],
18//! `decode::VideoFrame` re-exported under [`video`]), the
19//! [`clock::MediaClock`] + [`clock::MediaTime`] timing model,
20//! [`histogram::AudioHistogram`] quantization, the
21//! `manifest::RecordingManifest` session descriptor (planned —
22//! see [`manifest`] for the scaffolded module), and the device
23//! enumeration model.
24//! - **`wisp`** owns visual composition only. It **must not** depend on
25//! this crate's GStreamer integration. It receives `VideoFrame` /
26//! `Texture` handles, waveform-bar geometry, cursor state, and timeline
27//! timestamps via typed structs — and renders them through its sprite +
28//! graphics pipelines.
29//! - **`app` (Tauri + Leptos)** owns UI orchestration. It calls this
30//! crate's [Leptos/Tauri integration seam](#leptostauri-seam) and the
31//! wisp renderer; it does not poke GStreamer or `wgpu` directly.
32//!
33//! # Layering
34//!
35//! ```text
36//! ┌──────────────────────────────────────────────┐
37//! │ app (Tauri shell + Leptos UI) │
38//! │ - calls `media::commands::*` │
39//! │ - feeds `wisp` typed data │
40//! └────────────┬──────────────────┬──────────────┘
41//! │ │
42//! ┌─────────▼────────┐ ┌─────▼────────────┐
43//! │ media (this) │ │ wisp │
44//! │ - GStreamer │ │ - render │
45//! │ - timing model │ │ - sprite/graphics│
46//! │ - histogram │ │ - text + mask │
47//! │ - manifest │ │ - filter + blend│
48//! └────────┬─────────┘ └──────────────────┘
49//! │ typed data (VideoFrame,
50//! │ WaveformBarRect, MediaTime, …)
51//! ▼
52//! (no direct dep on wisp)
53//! ```
54//!
55//! # Build-on-decode
56//!
57//! [`decode::VideoFrame`] + [`decode::VideoStream`] already carry the
58//! BGRA-frame contract used throughout the project. This crate re-exports
59//! them under [`video`] rather than reinventing the wheel. M-MEDIA.6
60//! (`gstreamer::video_test_source`) builds on `decode`'s
61//! `GstreamerPipeStream`.
62//!
63//! # GStreamer integration choice
64//!
65//! CLI-pipe pattern — spawn `gst-launch-1.0` as a child process and pipe
66//! raw bytes through `fdsrc` / `fdsink`. Documented in
67//! [`gstreamer`]. No compile-time `libgstreamer` dependency.
68//!
69//! # Leptos/Tauri seam
70//!
71//! M-MEDIA.21 (AUT-117) lands the command/service surface at
72//! `media::commands::*` — `list_devices`, `start_capture`,
73//! `stop_capture`, `current_clock`, `latest_histogram_window`,
74//! `latest_video_handle`. Until then, this module is a placeholder.
75//!
76//! # Track status
77//!
78//! Each M-MEDIA chunk lands as its own commit on `m-media` branch.
79//! Sub-modules fill in chunk-by-chunk:
80//!
81//! | Module | Chunk | Status |
82//! |---|---|---|
83//! | [`gstreamer`] | M-MEDIA.1 (AUT-97) | scaffolded |
84//! | [`clock`] | M-MEDIA.2 (AUT-98) | scaffolded |
85//! | [`audio`] | M-MEDIA.3 (AUT-99) | scaffolded |
86//! | [`video`] | (re-export) | scaffolded |
87//! | [`histogram`] | M-MEDIA.8 (AUT-104) | scaffolded |
88//! | [`manifest`] | M-MEDIA.20 (AUT-116) | scaffolded |
89
90pub mod audio;
91pub mod audio_mix;
92pub mod camera;
93pub mod clock;
94pub mod encode;
95pub mod gstreamer;
96pub mod gstreamer_audio;
97pub mod gstreamer_video;
98pub mod histogram;
99pub mod manifest;
100pub mod microphone;
101pub mod mock_audio;
102#[cfg(target_os = "macos")]
103pub mod sck_audio;
104#[cfg(target_os = "macos")]
105pub mod sck_video;
106#[cfg(target_os = "macos")]
107pub mod screen;
108pub mod sync;
109pub mod video;
110pub mod waveform;
111
112pub use audio::{AudioChunk, AudioChunkError, AudioFormat, SampleFormat};
113pub use camera::{CameraDevice, list_cameras};
114pub use clock::{MediaClock, MediaDuration, MediaTime, Timestamped};
115pub use histogram::{AudioBar, AudioHistogram};
116pub use microphone::{MicrophoneDevice, list_microphones};
117pub use mock_audio::{SilenceSource, SineWaveSource, StepPulseSource};
118pub use video::{VideoFrame, VideoStream};
119pub use waveform::{
120 BarMetric, WaveformBarRect, WaveformDisplayMode, WaveformLayout, mono_bars, stereo_bars,
121};
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn crate_re_exports_decode_video_frame() {
129 // Smoke: the crate boundary surfaces VideoFrame so downstream
130 // callers don't have to depend on `decode` directly.
131 let _: fn() -> Option<VideoFrame> = || None;
132 }
133
134 #[test]
135 fn crate_re_exports_decode_video_stream_trait() {
136 // Smoke: VideoStream is the contract that gstreamer + mock
137 // implementations both satisfy.
138 fn assert_object_safe<T: ?Sized>(_: &T) {}
139 // Trait existence check — compile-time only.
140 let _ = std::marker::PhantomData::<dyn VideoStream>;
141 assert_object_safe(&std::marker::PhantomData::<dyn VideoStream>);
142 }
143}