#![cfg(feature = "demux")]
use vtome::{Compositor, Output, OutputLayer};
use vtome::geometry::Rect;
use vtome::{Frame, ColorSpace, PixelFormat};
use std::time::Duration;
fn checkerboard_frame(width: u32, height: u32, frame_num: u32) -> Frame {
let block_size = 32;
let mut data = vec![0u8; (width * height * 4) as usize];
for y in 0..height {
for x in 0..width {
let block_x = x / block_size;
let block_y = y / block_size;
let is_white = (block_x + block_y) % 2 == 0;
let color = if is_white { 255u8 } else { 0u8 };
let idx = ((y * width + x) * 4) as usize;
data[idx] = color;
data[idx + 1] = color;
data[idx + 2] = color;
data[idx + 3] = 255;
}
}
Frame::packed(
width,
height,
PixelFormat::Rgba8,
ColorSpace::srgb(),
Duration::from_millis(frame_num as u64 * 41),
data,
)
.expect("valid frame")
}
fn gradient_frame(width: u32, height: u32, frame_num: u32) -> Frame {
let mut data = vec![0u8; (width * height * 4) as usize];
for y in 0..height {
for x in 0..width {
let r = ((x as f32 / width as f32) * 255.0) as u8;
let g = ((y as f32 / height as f32) * 255.0) as u8;
let b = 128u8;
let idx = ((y * width + x) * 4) as usize;
data[idx] = r;
data[idx + 1] = g;
data[idx + 2] = b;
data[idx + 3] = 255;
}
}
Frame::packed(
width,
height,
PixelFormat::Rgba8,
ColorSpace::srgb(),
Duration::from_millis(frame_num as u64 * 41),
data,
)
.expect("valid frame")
}
#[test]
fn compositor_layer_ordering() {
let mut comp = Compositor::new();
let mut output = Output::new(640, 480);
let layer_a = OutputLayer::new("video_a", Rect::new(0.0, 0.0, 200.0, 200.0))
.with_z_index(2);
let layer_b = OutputLayer::new("video_b", Rect::new(100.0, 100.0, 200.0, 200.0))
.with_z_index(0);
let layer_c = OutputLayer::new("video_c", Rect::new(200.0, 200.0, 200.0, 200.0))
.with_z_index(1);
output.add_layers(vec![layer_a, layer_b, layer_c]);
assert_eq!(output.layers[0].z_index, 0);
assert_eq!(output.layers[1].z_index, 1);
assert_eq!(output.layers[2].z_index, 2);
comp.add_output(output);
assert_eq!(comp.output_count(), 1);
}
#[test]
fn output_background_with_layers() {
let output = Output::new(1920, 1080)
.with_background_color([0.2, 0.2, 0.2, 1.0])
.with_background_image("background.png");
assert_eq!(output.background_color, [0.2, 0.2, 0.2, 1.0]);
assert_eq!(output.background_image, Some("background.png".to_string()));
assert_eq!(output.width, 1920);
assert_eq!(output.height, 1080);
}
#[test]
fn compositor_output_management() {
let mut comp = Compositor::new();
let output1 = Output::new(1920, 1080);
let output2 = Output::new(1280, 720);
let output3 = Output::new(640, 480);
comp.add_output(output1);
comp.add_output(output2);
comp.add_output(output3);
assert_eq!(comp.output_count(), 3);
assert_eq!(comp.get_output(0).unwrap().width, 1920);
assert_eq!(comp.get_output(1).unwrap().width, 1280);
assert_eq!(comp.get_output(2).unwrap().width, 640);
let removed = comp.remove_output(1);
assert!(removed.is_some());
assert_eq!(comp.output_count(), 2);
assert_eq!(comp.get_output(1).unwrap().width, 640);
}
#[test]
fn output_layer_visibility_filtering() {
let mut output = Output::new(640, 480);
let mut layer_visible = OutputLayer::new("visible", Rect::new(0.0, 0.0, 100.0, 100.0));
let mut layer_hidden = OutputLayer::new("hidden", Rect::new(100.0, 100.0, 100.0, 100.0));
layer_visible.visible = true;
layer_hidden.visible = false;
output.add_layers(vec![layer_visible, layer_hidden]);
let visible_count: usize = output.visible_layers().count();
assert_eq!(visible_count, 1);
let visible_names: Vec<&str> = output
.visible_layers()
.map(|l| l.video_source_id.as_str())
.collect();
assert_eq!(visible_names, vec!["visible"]);
}
#[test]
fn output_layer_opacity_clamping() {
let rect = Rect::new(0.0, 0.0, 100.0, 100.0);
let layer = OutputLayer::new("test", rect).with_opacity(1.5);
assert_eq!(layer.opacity, 1.0);
let layer = OutputLayer::new("test", rect).with_opacity(-0.5);
assert_eq!(layer.opacity, 0.0);
let layer = OutputLayer::new("test", rect).with_opacity(0.5);
assert_eq!(layer.opacity, 0.5);
}
#[test]
fn compositor_advance_without_sources() {
let mut comp = Compositor::new();
let output = Output::new(640, 480);
comp.add_output(output);
let result = comp.advance(std::time::Duration::from_millis(16));
assert!(result.is_ok());
}
#[test]
fn compositor_render_output_stub() {
let mut comp = Compositor::new();
let output = Output::new(640, 480);
comp.add_output(output);
let result = comp.render_output(0);
assert!(result.is_ok());
}
#[test]
fn video_source_persistent_id_consistency() {
let id1 = {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
"test_video.mp4".hash(&mut hasher);
format!("{:x}", hasher.finish())
};
let id2 = {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
"test_video.mp4".hash(&mut hasher);
format!("{:x}", hasher.finish())
};
assert_eq!(id1, id2);
let id3 = {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
"other_video.mp4".hash(&mut hasher);
format!("{:x}", hasher.finish())
};
assert_ne!(id1, id3);
}
#[cfg(feature = "render")]
#[test]
fn end_to_end_composition_to_texture() {
let mut comp = Compositor::new();
let mut output = Output::new(640, 480)
.with_background_color([0.1, 0.1, 0.1, 1.0]);
let layer = OutputLayer::new("test_video", Rect::new(50.0, 50.0, 320.0, 240.0));
output.add_layer(layer);
comp.add_output(output);
let result = comp.advance(std::time::Duration::from_millis(16));
assert!(result.is_ok());
let result = comp.render_output(0);
assert!(result.is_ok());
assert_eq!(comp.get_output(0).unwrap().layers.len(), 1);
}
#[test]
fn test_fixture_frame_generation() {
let cb = checkerboard_frame(320, 240, 0);
assert_eq!(cb.width(), 320);
assert_eq!(cb.height(), 240);
let grad = gradient_frame(640, 480, 1);
assert_eq!(grad.width(), 640);
assert_eq!(grad.height(), 480);
}
#[test]
fn full_pipeline_composition_test() {
let mut comp = Compositor::new();
let mut output1 = Output::new(1920, 1080)
.with_background_color([0.0, 0.0, 0.0, 1.0]);
let mut output2 = Output::new(1280, 720)
.with_background_color([0.1, 0.1, 0.1, 1.0])
.with_background_image("background.png");
let layer1 = OutputLayer::new("video_main", Rect::new(100.0, 100.0, 800.0, 600.0))
.with_z_index(0)
.with_opacity(1.0);
let layer2 = OutputLayer::new("video_overlay", Rect::new(1000.0, 800.0, 400.0, 200.0))
.with_z_index(1)
.with_opacity(0.8);
output1.add_layers(vec![layer1, layer2]);
let layer3 = OutputLayer::new("video_main", Rect::new(50.0, 50.0, 600.0, 450.0))
.with_z_index(0);
output2.add_layer(layer3);
comp.add_output(output1);
comp.add_output(output2);
assert_eq!(comp.output_count(), 2);
let result = comp.advance(Duration::from_millis(41));
assert!(result.is_ok());
let result1 = comp.render_output(0);
let result2 = comp.render_output(1);
assert!(result1.is_ok());
assert!(result2.is_ok());
let out1 = comp.get_output(0).unwrap();
assert_eq!(out1.width, 1920);
assert_eq!(out1.height, 1080);
assert_eq!(out1.layers.len(), 2);
let out2 = comp.get_output(1).unwrap();
assert_eq!(out2.width, 1280);
assert_eq!(out2.height, 720);
assert_eq!(out2.layers.len(), 1);
}
#[test]
fn multi_output_sync_test() {
let mut comp = Compositor::new();
let output_monitor = Output::new(3840, 2160); let output_stream = Output::new(1920, 1080); let output_preview = Output::new(640, 480);
comp.add_output(output_monitor);
comp.add_output(output_stream);
comp.add_output(output_preview);
assert_eq!(comp.output_count(), 3);
let advance_result = comp.advance(Duration::from_millis(16));
assert!(advance_result.is_ok());
for i in 0..3 {
let render_result = comp.render_output(i);
assert!(render_result.is_ok(), "Failed to render output {}", i);
}
for i in 0..3 {
assert!(comp.get_output(i).is_some());
}
}