wisp_storybook/stories/
s_video_frame_handoff.rs1use glam::Vec2;
10use media::VideoFrame;
11use wisp::application::Application;
12use wisp::texture::video_texture::VideoTexture;
13use wisp::{Sprite, Stage};
14
15use crate::story::Story;
16
17pub fn story() -> Story {
18 Story {
19 id: "video-frame-handoff",
20 category: "Media",
21 title: "VideoFrame → VideoTexture",
22 milestone: "M-MEDIA.12",
23 writeup: include_str!("writeups/video_frame_handoff.md"),
24 build,
25 tick: None,
26 }
27}
28
29fn build(app: &Application, stage: &mut Stage) {
30 let frame = synthetic_frame(128, 72);
33
34 let video_tex = VideoTexture::new(app, frame.width, frame.height);
35 video_tex.upload_bgra(app, &frame.bgra);
36
37 let mut sprite =
38 Sprite::from_texture(video_tex.texture().clone()).with_anchor(Vec2::splat(0.5));
39 sprite.container.transform.scale = Vec2::new(1.5, 1.5 * 72.0 / 128.0);
41 let _ = stage.add_child(stage.root(), sprite);
42}
43
44fn synthetic_frame(width: u32, height: u32) -> VideoFrame {
45 let pixels_w = width as usize;
46 let pixels_h = height as usize;
47 let mut bgra = vec![0u8; pixels_w * pixels_h * 4];
48 for row in 0..pixels_h {
49 for col in 0..pixels_w {
50 let idx = (row * pixels_w + col) * 4;
51 let red = u8::try_from((col * 255) / pixels_w).unwrap_or(255);
54 let green = u8::try_from((row * 255) / pixels_h).unwrap_or(255);
55 let blue = u8::try_from(((col + row) * 255) / (pixels_w + pixels_h)).unwrap_or(255);
56 let stripe = if row % 12 < 3 { 60 } else { 0 };
59 bgra[idx] = blue.saturating_add(stripe); bgra[idx + 1] = green; bgra[idx + 2] = red.saturating_add(stripe); bgra[idx + 3] = 255; }
64 }
65 VideoFrame {
66 width,
67 height,
68 bgra,
69 pts_seconds: 0.0,
70 frame_index: 0,
71 }
72}