Skip to main content

oximedia_virtual/
lib.rs

1//! Virtual Production and LED Wall Tools for `OxiMedia`
2//!
3//! This crate provides comprehensive virtual production capabilities including:
4//! - Camera tracking and calibration
5//! - LED wall rendering with perspective correction
6//! - In-camera VFX compositing
7//! - Color pipeline management
8//! - Genlock synchronization
9//! - Motion capture integration
10//! - Real-time keying and green screen alternatives
11//! - Unreal Engine integration
12//! - Multi-camera support
13//!
14//! # Examples
15//!
16//! ```
17//! use oximedia_virtual::{VirtualProduction, VirtualProductionConfig, WorkflowType};
18//!
19//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
20//! let config = VirtualProductionConfig::default()
21//!     .with_workflow(WorkflowType::LedWall)
22//!     .with_target_fps(60.0)
23//!     .with_sync_accuracy_ms(0.5);
24//!
25//! let mut vp = VirtualProduction::new(config)?;
26//! # Ok(())
27//! # }
28//! ```
29
30#![forbid(unsafe_code)]
31
32pub mod math;
33
34pub mod ar_overlay;
35pub mod background_plate;
36pub mod camera_frustum;
37pub mod camera_rig;
38pub mod camera_tracker;
39pub mod camera_tracking;
40pub mod color;
41pub mod color_temp_control;
42pub mod constants;
43pub mod core_interop;
44pub mod dmx_scene;
45pub mod examples;
46pub mod frustum;
47pub mod frustum_culling;
48pub mod genlock;
49pub mod greenscreen;
50pub mod hdri_capture;
51pub mod icvfx;
52pub mod keying;
53pub mod led;
54pub mod led_volume;
55pub mod led_wall;
56pub mod lens;
57pub mod lens_projection;
58pub mod light_rig;
59pub mod metrics;
60pub mod mocap;
61pub mod motion_path;
62pub mod multicam;
63pub mod ndi_bridge;
64pub mod panel_topology;
65pub mod pixel_mapping;
66pub mod pixel_mapping_lut;
67pub mod preview;
68pub mod previz;
69pub mod projection_map;
70pub mod remote_session;
71pub mod render_layer;
72pub mod render_output;
73pub mod rig_path;
74pub mod scene;
75pub mod scene_setup;
76pub mod set_extension;
77pub mod stage;
78pub mod stage_element_layout;
79pub mod stage_layout;
80pub mod stage_manager;
81pub mod stage_safety;
82pub mod stage_visualization;
83pub mod sync;
84pub mod talent_keying;
85pub mod talent_tracking;
86// `timecode` is the virtual-production SMPTE timecode module (distinct from the
87// oximedia-timecode crate).
88pub mod timecode;
89pub mod tracking;
90pub mod tracking_data;
91// `tracking_filter` provides crate-root 6-DOF filtering (PassThrough / LowPass /
92// Kalman / OneEuro). Not a collision: `crate::tracking::filter` lives at a
93// different path.
94pub mod tracking_filter;
95pub mod tracking_session;
96pub mod unreal;
97pub mod utils;
98pub mod virtual_set;
99pub mod virtual_studio;
100pub mod volume_calibration;
101pub mod workflows;
102
103use std::time::Duration;
104use thiserror::Error;
105
106/// Virtual production errors
107#[derive(Debug, Error)]
108pub enum VirtualProductionError {
109    #[error("Camera tracking error: {0}")]
110    CameraTracking(String),
111
112    #[error("LED wall error: {0}")]
113    LedWall(String),
114
115    #[error("Calibration error: {0}")]
116    Calibration(String),
117
118    #[error("Synchronization error: {0}")]
119    Sync(String),
120
121    #[error("Color pipeline error: {0}")]
122    Color(String),
123
124    #[error("Motion capture error: {0}")]
125    MotionCapture(String),
126
127    #[error("Compositing error: {0}")]
128    Compositing(String),
129
130    #[error("Unreal integration error: {0}")]
131    UnrealIntegration(String),
132
133    #[error("Multi-camera error: {0}")]
134    MultiCamera(String),
135
136    #[error("Frame timing error: expected {expected:?}, got {actual:?}")]
137    FrameTiming {
138        expected: Duration,
139        actual: Duration,
140    },
141
142    #[error("Invalid configuration: {0}")]
143    InvalidConfig(String),
144}
145
146pub type Result<T> = std::result::Result<T, VirtualProductionError>;
147
148/// Workflow types for virtual production
149#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
150pub enum WorkflowType {
151    /// Full LED volume with camera tracking
152    LedWall,
153    /// Mix LED wall and green screen
154    Hybrid,
155    /// Traditional green screen with real-time compositing
156    GreenScreen,
157    /// Augmented reality overlay
158    AugmentedReality,
159}
160
161/// Quality mode for real-time processing
162#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
163pub enum QualityMode {
164    /// Draft quality for setup and rehearsal
165    Draft,
166    /// Preview quality for monitoring
167    Preview,
168    /// Final quality for recording
169    Final,
170}
171
172/// Virtual production configuration
173#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
174pub struct VirtualProductionConfig {
175    /// Workflow type
176    pub workflow: WorkflowType,
177    /// Target frames per second (minimum 60fps recommended)
178    pub target_fps: f64,
179    /// Synchronization accuracy in milliseconds (target <1ms)
180    pub sync_accuracy_ms: f64,
181    /// Quality mode
182    pub quality: QualityMode,
183    /// Enable color calibration
184    pub color_calibration: bool,
185    /// Enable lens distortion correction
186    pub lens_correction: bool,
187    /// Number of cameras to track
188    pub num_cameras: usize,
189    /// Enable motion capture integration
190    pub motion_capture: bool,
191    /// Enable Unreal Engine integration
192    pub unreal_integration: bool,
193}
194
195impl Default for VirtualProductionConfig {
196    fn default() -> Self {
197        Self {
198            workflow: WorkflowType::LedWall,
199            target_fps: 60.0,
200            sync_accuracy_ms: 0.5,
201            quality: QualityMode::Preview,
202            color_calibration: true,
203            lens_correction: true,
204            num_cameras: 1,
205            motion_capture: false,
206            unreal_integration: false,
207        }
208    }
209}
210
211impl VirtualProductionConfig {
212    /// Set workflow type
213    #[must_use]
214    pub fn with_workflow(mut self, workflow: WorkflowType) -> Self {
215        self.workflow = workflow;
216        self
217    }
218
219    /// Set target FPS
220    #[must_use]
221    pub fn with_target_fps(mut self, fps: f64) -> Self {
222        self.target_fps = fps;
223        self
224    }
225
226    /// Set sync accuracy in milliseconds
227    #[must_use]
228    pub fn with_sync_accuracy_ms(mut self, accuracy: f64) -> Self {
229        self.sync_accuracy_ms = accuracy;
230        self
231    }
232
233    /// Set quality mode
234    #[must_use]
235    pub fn with_quality(mut self, quality: QualityMode) -> Self {
236        self.quality = quality;
237        self
238    }
239
240    /// Set number of cameras
241    #[must_use]
242    pub fn with_num_cameras(mut self, num: usize) -> Self {
243        self.num_cameras = num;
244        self
245    }
246}
247
248/// Main virtual production system
249pub struct VirtualProduction {
250    config: VirtualProductionConfig,
251    camera_tracker: tracking::camera::CameraTracker,
252    led_renderer: led::render::LedRenderer,
253    compositor: icvfx::composite::IcvfxCompositor,
254    color_pipeline: color::pipeline::ColorPipeline,
255    genlock: sync::genlock::GenlockSync,
256    multicam_manager: Option<multicam::manager::MultiCameraManager>,
257}
258
259impl VirtualProduction {
260    /// Create new virtual production system
261    pub fn new(config: VirtualProductionConfig) -> Result<Self> {
262        let camera_tracker =
263            tracking::camera::CameraTracker::new(tracking::camera::CameraTrackerConfig::default())?;
264
265        let led_renderer =
266            led::render::LedRenderer::new(led::render::LedRendererConfig::default())?;
267
268        let compositor =
269            icvfx::composite::IcvfxCompositor::new(icvfx::composite::CompositorConfig::default())?;
270
271        let color_pipeline =
272            color::pipeline::ColorPipeline::new(color::pipeline::ColorPipelineConfig::default())?;
273
274        let genlock = sync::genlock::GenlockSync::new(sync::genlock::GenlockConfig::default())?;
275
276        let multicam_manager = if config.num_cameras > 1 {
277            Some(multicam::manager::MultiCameraManager::new(
278                multicam::manager::MultiCameraConfig {
279                    num_cameras: config.num_cameras,
280                    auto_switch: false,
281                },
282            )?)
283        } else {
284            None
285        };
286
287        Ok(Self {
288            config,
289            camera_tracker,
290            led_renderer,
291            compositor,
292            color_pipeline,
293            genlock,
294            multicam_manager,
295        })
296    }
297
298    /// Get current configuration
299    #[must_use]
300    pub fn config(&self) -> &VirtualProductionConfig {
301        &self.config
302    }
303
304    /// Get camera tracker
305    #[must_use]
306    pub fn camera_tracker(&self) -> &tracking::camera::CameraTracker {
307        &self.camera_tracker
308    }
309
310    /// Get mutable camera tracker
311    pub fn camera_tracker_mut(&mut self) -> &mut tracking::camera::CameraTracker {
312        &mut self.camera_tracker
313    }
314
315    /// Get LED renderer
316    #[must_use]
317    pub fn led_renderer(&self) -> &led::render::LedRenderer {
318        &self.led_renderer
319    }
320
321    /// Get mutable LED renderer
322    pub fn led_renderer_mut(&mut self) -> &mut led::render::LedRenderer {
323        &mut self.led_renderer
324    }
325
326    /// Get compositor
327    #[must_use]
328    pub fn compositor(&self) -> &icvfx::composite::IcvfxCompositor {
329        &self.compositor
330    }
331
332    /// Get mutable compositor
333    pub fn compositor_mut(&mut self) -> &mut icvfx::composite::IcvfxCompositor {
334        &mut self.compositor
335    }
336
337    /// Reconfigure the compositor with a new resolution.
338    ///
339    /// Useful in tests to use a small resolution (e.g. 64×64) for speed.
340    pub fn set_compositor_resolution(&mut self, width: usize, height: usize) -> Result<()> {
341        let config = icvfx::composite::CompositorConfig {
342            resolution: (width, height),
343            ..self.compositor.config().clone()
344        };
345        self.compositor = icvfx::composite::IcvfxCompositor::new(config)?;
346        Ok(())
347    }
348
349    /// Get color pipeline
350    #[must_use]
351    pub fn color_pipeline(&self) -> &color::pipeline::ColorPipeline {
352        &self.color_pipeline
353    }
354
355    /// Get mutable reference to color pipeline
356    pub fn color_pipeline_mut(&mut self) -> &mut color::pipeline::ColorPipeline {
357        &mut self.color_pipeline
358    }
359
360    /// Get genlock sync
361    #[must_use]
362    pub fn genlock(&self) -> &sync::genlock::GenlockSync {
363        &self.genlock
364    }
365
366    /// Get multi-camera manager
367    #[must_use]
368    pub fn multicam_manager(&self) -> Option<&multicam::manager::MultiCameraManager> {
369        self.multicam_manager.as_ref()
370    }
371}
372
373#[cfg(test)]
374mod tests {
375    use super::*;
376
377    #[test]
378    fn test_config_default() {
379        let config = VirtualProductionConfig::default();
380        assert_eq!(config.workflow, WorkflowType::LedWall);
381        assert_eq!(config.target_fps, 60.0);
382        assert_eq!(config.sync_accuracy_ms, 0.5);
383        assert_eq!(config.quality, QualityMode::Preview);
384    }
385
386    #[test]
387    fn test_config_builder() {
388        let config = VirtualProductionConfig::default()
389            .with_workflow(WorkflowType::Hybrid)
390            .with_target_fps(120.0)
391            .with_quality(QualityMode::Final)
392            .with_num_cameras(4);
393
394        assert_eq!(config.workflow, WorkflowType::Hybrid);
395        assert_eq!(config.target_fps, 120.0);
396        assert_eq!(config.quality, QualityMode::Final);
397        assert_eq!(config.num_cameras, 4);
398    }
399
400    #[test]
401    fn test_virtual_production_creation() {
402        let config = VirtualProductionConfig::default();
403        let vp = VirtualProduction::new(config);
404        assert!(vp.is_ok());
405    }
406
407    #[test]
408    fn test_multicam_manager_creation() {
409        let config = VirtualProductionConfig::default().with_num_cameras(4);
410        let vp = VirtualProduction::new(config).expect("should succeed in test");
411        assert!(vp.multicam_manager().is_some());
412    }
413}