Skip to main content

atmos/os_lib/web_engine/
video.rs

1// web_engine/video.rs - Video playback state & background thread
2
3use 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    // 本物の H.264 baseline デコーダ(h264_decode)。avcC から SPS/PPS を初期化。
83    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    // 【2026-08-05】スレッドの入口関数は**関数末尾で終わらない**。
155    //
156    // カーネルスレッドは入口から再実行され続ける。
157    // フォント/画像のワーカーで同じ形の欠陥があり、実測で
158    // **3 億 4 千万回**空回りしていた(`spec/resource_loading.md` R-17)。
159    // 再生が終わったら明示的に終了する。
160    crate::kernel::scheduler::exit();
161}