atmos/os_lib/web_engine/
video.rs1use alloc::string::String;
4use alloc::vec::Vec;
5use spin::Mutex;
6
7#[derive(Clone)]
8pub(super) struct VideoPlayState {
9 pub src_url: String,
10 pub x: u32,
11 pub y: u32,
12 pub width: u32,
13 pub height: u32,
14 pub playing: bool,
15 pub stop_request: bool,
16}
17
18pub(super) static VIDEO_STATE: Mutex<Option<VideoPlayState>> = Mutex::new(None);
19
20#[derive(Clone)]
21pub(super) struct SharedVideoFrame {
22 pub width: u32,
23 pub height: u32,
24 pub rgb_data: Vec<u8>,
25}
26
27pub(super) static CURRENT_VIDEO_FRAME: Mutex<Option<SharedVideoFrame>> = Mutex::new(None);
28
29pub(super) fn stop_video_playback() {
30 let mut state = VIDEO_STATE.lock();
31 if let Some(ref mut st) = *state {
32 st.stop_request = true;
33 }
34 crate::kernel::audio::Audio::mute();
35 crate::kernel::audio::Audio::stop_mml();
36}
37
38pub(super) fn video_playback_thread() {
39 let state_opt = {
40 let state = VIDEO_STATE.lock();
41 state.clone()
42 };
43
44 let state = match state_opt {
45 Some(s) => s,
46 None => return,
47 };
48
49 let url = state.src_url.clone();
50 let (host, path) = match super::parse_url(&url) {
51 Some(res) => res,
52 None => {
53 crate::println!("Video Error: Invalid URL: {}", url);
54 return;
55 }
56 };
57
58 crate::println!("Video Loader: Downloading video from {}{} ...", host, path);
59 let video_data = match crate::kernel::net::http_get_binary(&host, &path) {
60 Ok(data) => data,
61 Err(e) => {
62 crate::println!("Video Error: Failed to download video: {}", e);
63 return;
64 }
65 };
66 crate::println!("Video Loader: Downloaded {} bytes.", video_data.len());
67
68 let demuxer = match crate::os_lib::mp4::Mp4Demuxer::parse(&video_data) {
69 Ok(d) => d,
70 Err(e) => {
71 crate::println!("Video Error: MP4 parse failed: {}", e);
72 return;
73 }
74 };
75
76 crate::println!(
77 "Video Loader: Video track samples: {}, Audio track samples: {}",
78 demuxer.video_samples.len(),
79 demuxer.audio_samples.len()
80 );
81
82 let mut h264_decoder = crate::os_lib::h264_decode::VideoDecoder::new();
84 if let Some(ref avcc) = demuxer.video_avcc {
85 h264_decoder.init_avcc(avcc);
86 crate::println!("Video Loader: avcC initialized into H.264 decoder");
87 } else {
88 crate::println!("Video Loader: no avcC (will rely on in-band SPS/PPS)");
89 }
90 let mut mp3_decoder = crate::os_lib::mp3::Mp3Decoder::new();
91
92 let mut full_pcm = alloc::vec::Vec::new();
93 for sample in &demuxer.audio_samples {
94 let sample_data = &video_data[sample.offset..sample.offset + sample.size];
95 if let Ok(pcm) = mp3_decoder.decode_frame(sample_data) {
96 full_pcm.extend(pcm);
97 }
98 }
99
100 if !full_pcm.is_empty() {
101 crate::println!(
102 "Video Player: Starting audio playback ({} samples) ...",
103 full_pcm.len()
104 );
105 crate::kernel::audio::Audio::play_pcm_stream(&full_pcm);
106 }
107
108 let start_time = crate::kernel::timer::get_system_time_ms();
109 let mut video_sample_idx = 0;
110
111 while video_sample_idx < demuxer.video_samples.len() {
112 {
113 let s = VIDEO_STATE.lock();
114 if s.as_ref().is_none_or(|state| state.stop_request) {
115 break;
116 }
117 }
118
119 let sample = &demuxer.video_samples[video_sample_idx];
120 let elapsed = crate::kernel::timer::get_system_time_ms().wrapping_sub(start_time);
121
122 if elapsed >= sample.timestamp_ms {
123 let sample_data = &video_data[sample.offset..sample.offset + sample.size];
124 if let Some((fw, fh, rgb)) = h264_decoder.decode_sample_rgb(sample_data) {
125 let mut current_frame = CURRENT_VIDEO_FRAME.lock();
126 *current_frame = Some(SharedVideoFrame {
127 width: fw,
128 height: fh,
129 rgb_data: rgb,
130 });
131 }
132 video_sample_idx += 1;
133 } else {
134 let sleep_ms = (sample.timestamp_ms - elapsed).min(10);
135 if sleep_ms > 0 {
136 crate::kernel::scheduler::sleep((sleep_ms / 10).max(1) as usize);
137 } else {
138 crate::kernel::scheduler::yield_now();
139 }
140 }
141 }
142
143 {
144 let mut s = VIDEO_STATE.lock();
145 if let Some(ref mut st) = *s {
146 st.playing = false;
147 }
148 }
149 {
150 let mut current_frame = CURRENT_VIDEO_FRAME.lock();
151 *current_frame = None;
152 }
153 crate::println!("Video Player: Playback finished.");
154 crate::kernel::scheduler::exit();
161}