use crate::{
icvfx::composite::CompositeFrame, led::LedWall, tracking::CameraPose, QualityMode, Result,
VirtualProduction, VirtualProductionConfig, WorkflowType,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WorkflowState {
Idle,
Calibrating,
Ready,
Recording,
Playback,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowSession {
pub id: String,
pub workflow_type: WorkflowType,
pub state: WorkflowState,
pub start_time: Option<u64>,
pub frame_count: u64,
}
impl WorkflowSession {
#[must_use]
pub fn new(id: String, workflow_type: WorkflowType) -> Self {
Self {
id,
workflow_type,
state: WorkflowState::Idle,
start_time: None,
frame_count: 0,
}
}
pub fn start(&mut self, timestamp_ns: u64) {
self.start_time = Some(timestamp_ns);
self.state = WorkflowState::Recording;
self.frame_count = 0;
}
pub fn stop(&mut self) {
self.state = WorkflowState::Idle;
}
pub fn next_frame(&mut self) {
self.frame_count += 1;
}
}
pub struct LedWallWorkflow {
vp: VirtualProduction,
session: Option<WorkflowSession>,
#[allow(dead_code)]
led_wall: LedWall,
}
impl LedWallWorkflow {
pub fn new() -> Result<Self> {
Self::with_panel_resolution((1920, 1080))
}
pub fn with_panel_resolution(panel_res: (usize, usize)) -> Result<Self> {
let config = VirtualProductionConfig::default()
.with_workflow(WorkflowType::LedWall)
.with_quality(QualityMode::Final);
let mut vp = VirtualProduction::new(config)?;
let mut led_wall = LedWall::new("Main LED Wall".to_string());
use crate::led::LedPanel;
use crate::math::Point3;
led_wall.add_panel(LedPanel::new(
Point3::new(0.0, 0.0, 0.0),
5.0,
3.0,
panel_res,
2.5,
));
vp.led_renderer_mut().set_led_wall(led_wall.clone());
Ok(Self {
vp,
session: None,
led_wall,
})
}
pub fn start_recording(&mut self, session_id: String, timestamp_ns: u64) -> Result<()> {
let mut session = WorkflowSession::new(session_id, WorkflowType::LedWall);
session.start(timestamp_ns);
self.session = Some(session);
Ok(())
}
pub fn process_frame(
&mut self,
camera_pose: &CameraPose,
source_frame: &[u8],
source_width: usize,
source_height: usize,
timestamp_ns: u64,
) -> Result<Vec<u8>> {
if let Some(session) = &mut self.session {
if session.state == WorkflowState::Recording {
session.next_frame();
}
}
let led_output = self.vp.led_renderer_mut().render(
camera_pose,
source_frame,
source_width,
source_height,
timestamp_ns,
)?;
Ok(led_output.pixels)
}
pub fn stop_recording(&mut self) {
if let Some(session) = &mut self.session {
session.stop();
}
}
#[must_use]
pub fn session(&self) -> Option<&WorkflowSession> {
self.session.as_ref()
}
}
pub struct HybridWorkflow {
vp: VirtualProduction,
session: Option<WorkflowSession>,
#[allow(dead_code)]
led_wall: LedWall,
}
impl HybridWorkflow {
pub fn new() -> Result<Self> {
Self::with_resolution(1920, 1080)
}
pub fn with_resolution(width: usize, height: usize) -> Result<Self> {
let config = VirtualProductionConfig::default()
.with_workflow(WorkflowType::Hybrid)
.with_quality(QualityMode::Final);
let mut vp = VirtualProduction::new(config)?;
vp.set_compositor_resolution(width, height)?;
let led_wall = LedWall::new("Hybrid Wall".to_string());
Ok(Self {
vp,
session: None,
led_wall,
})
}
pub fn start_session(&mut self, session_id: String, timestamp_ns: u64) -> Result<()> {
let mut session = WorkflowSession::new(session_id, WorkflowType::Hybrid);
session.start(timestamp_ns);
self.session = Some(session);
Ok(())
}
pub fn composite_frame(
&mut self,
foreground: &[u8],
background: &[u8],
timestamp_ns: u64,
) -> Result<CompositeFrame> {
self.vp
.compositor_mut()
.composite(foreground, background, None, timestamp_ns)
}
}
pub struct ArWorkflow {
vp: VirtualProduction,
session: Option<WorkflowSession>,
}
impl ArWorkflow {
pub fn new() -> Result<Self> {
Self::with_resolution(1920, 1080)
}
pub fn with_resolution(width: usize, height: usize) -> Result<Self> {
let config = VirtualProductionConfig::default()
.with_workflow(WorkflowType::AugmentedReality)
.with_quality(QualityMode::Preview);
let mut vp = VirtualProduction::new(config)?;
vp.set_compositor_resolution(width, height)?;
Ok(Self { vp, session: None })
}
pub fn start(&mut self, session_id: String, timestamp_ns: u64) -> Result<()> {
let mut session = WorkflowSession::new(session_id, WorkflowType::AugmentedReality);
session.start(timestamp_ns);
self.session = Some(session);
Ok(())
}
pub fn overlay(
&mut self,
camera_feed: &[u8],
virtual_content: &[u8],
timestamp_ns: u64,
) -> Result<CompositeFrame> {
self.vp
.compositor_mut()
.composite(camera_feed, virtual_content, None, timestamp_ns)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_workflow_session() {
let mut session = WorkflowSession::new("test-1".to_string(), WorkflowType::LedWall);
assert_eq!(session.state, WorkflowState::Idle);
session.start(0);
assert_eq!(session.state, WorkflowState::Recording);
session.next_frame();
assert_eq!(session.frame_count, 1);
session.stop();
assert_eq!(session.state, WorkflowState::Idle);
}
#[test]
fn test_led_wall_workflow() {
let workflow = LedWallWorkflow::new();
assert!(workflow.is_ok());
}
#[test]
fn test_led_wall_workflow_session() {
let mut workflow = LedWallWorkflow::new().expect("should succeed in test");
workflow
.start_recording("session-1".to_string(), 0)
.expect("should succeed in test");
assert!(workflow.session().is_some());
workflow.stop_recording();
assert_eq!(
workflow.session().expect("should succeed in test").state,
WorkflowState::Idle
);
}
#[test]
fn test_hybrid_workflow() {
let workflow = HybridWorkflow::new();
assert!(workflow.is_ok());
}
#[test]
fn test_ar_workflow() {
let workflow = ArWorkflow::new();
assert!(workflow.is_ok());
}
#[test]
fn test_ar_workflow_start() {
let mut workflow = ArWorkflow::new().expect("should succeed in test");
workflow
.start("ar-session-1".to_string(), 0)
.expect("should succeed in test");
assert!(workflow.session.is_some());
}
}