1use 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 pub paused: bool,
18}
19
20pub(super) static VIDEO_STATE: Mutex<Option<VideoPlayState>> = Mutex::new(None);
21
22#[derive(Clone)]
23pub(super) struct SharedVideoFrame {
24 pub width: u32,
25 pub height: u32,
26 pub rgb_data: Vec<u8>,
27}
28
29pub(super) static CURRENT_VIDEO_FRAME: Mutex<Option<SharedVideoFrame>> = Mutex::new(None);
30
31pub(super) struct VideoLayer {
39 pub target: crate::kernel::window_mgr::DrawTarget,
40 pub dst: (i32, i32, u32, u32),
42 pub clip: (u32, u32, u32, u32),
44 pub last_app_draw_ms: u64,
46}
47
48pub(super) static VIDEO_LAYER: Mutex<Option<VideoLayer>> = Mutex::new(None);
49
50const STALL_MS: u64 = 150;
52
53pub(super) fn note_app_video_draw(dst: (i32, i32, u32, u32), clip: (u32, u32, u32, u32)) {
55 let Some(target) = crate::kernel::window_mgr::current_draw_target() else { return };
56 let now = crate::kernel::timer::get_system_time_ms();
57 *VIDEO_LAYER.lock() = Some(VideoLayer { target, dst, clip, last_app_draw_ms: now });
58}
59
60fn blit_if_app_stalled(frame: &SharedVideoFrame) {
62 let now = crate::kernel::timer::get_system_time_ms();
63 let layer = VIDEO_LAYER.lock();
64 let Some(l) = layer.as_ref() else { return };
65 if now.wrapping_sub(l.last_app_draw_ms) < STALL_MS || frame.width == 0 || frame.height == 0 {
66 return;
67 }
68 let (dx, dy, dw, dh) = l.dst;
69 let (bw, bh) = (l.target.width, l.target.height);
70 let x0 = (dx.max(0) as u32).max(l.clip.0);
71 let y0 = (dy.max(0) as u32).max(l.clip.1);
72 let x1 = ((dx + dw as i32).max(0) as u32).min(l.clip.2).min(bw);
73 let y1 = ((dy + dh as i32).max(0) as u32).min(l.clip.3).min(bh);
74 if x0 >= x1 || y0 >= y1 || dw == 0 || dh == 0 {
75 return;
76 }
77 let mut buf = l.target.buffer.lock();
78 if buf.len() < (bw * bh) as usize {
79 return;
80 }
81 for y in y0..y1 {
82 let sy = ((y as i32 - dy) as u32 * frame.height / dh).min(frame.height - 1);
83 let row = (y * bw) as usize;
84 for x in x0..x1 {
85 let sx = ((x as i32 - dx) as u32 * frame.width / dw).min(frame.width - 1);
86 let i = ((sy * frame.width + sx) * 3) as usize;
87 if let (Some(&r), Some(&g), Some(&b)) = (frame.rgb_data.get(i), frame.rgb_data.get(i + 1), frame.rgb_data.get(i + 2)) {
88 buf[row + x as usize] = 0xFF00_0000 | ((b as u32) << 16) | ((g as u32) << 8) | r as u32;
90 }
91 }
92 }
93 l.target.updated.store(true, core::sync::atomic::Ordering::Release);
94}
95
96pub(super) fn stop_video_playback() {
97 *VIDEO_LAYER.lock() = None;
98 crate::kernel::audio::AudioStream::stop();
99 let mut state = VIDEO_STATE.lock();
100 if let Some(ref mut st) = *state {
101 st.stop_request = true;
102 }
103 crate::kernel::audio::Audio::mute();
104 crate::kernel::audio::Audio::stop_mml();
105}
106
107pub(super) fn video_playback_thread() {
108 let state_opt = {
109 let state = VIDEO_STATE.lock();
110 state.clone()
111 };
112
113 let state = match state_opt {
114 Some(s) => s,
115 None => return,
116 };
117
118 let url = state.src_url.clone();
119 {
120 let mut pb = crate::os_lib::mse::PLAYBACK.lock();
121 let resume = pb.should_resume(&url);
122 pb.url = url.clone();
123 pb.playing = true;
124 pb.ended = false;
125 pb.ended_pending = false;
126 if resume {
127 let mut seek = crate::os_lib::mse::SEEK_REQUEST.lock();
128 if seek.is_none() {
129 *seek = Some(pb.current_time);
130 }
131 } else {
132 pb.current_time = 0.0;
133 pb.total_video_frames = 0;
134 pb.dropped_video_frames = 0;
135 *CURRENT_VIDEO_FRAME.lock() = None;
136 }
137 }
138 if url.starts_with("blob:mediasource-") {
139 play_media_source(&url);
140 finish_playback();
141 }
142 let (is_https, host, path) = match super::parse_url_scheme(&url) {
143 Some(res) => res,
144 None => {
145 crate::println!("Video Error: Invalid URL (requires http:// or https://): {}", url);
146 return;
147 }
148 };
149
150 crate::println!(
151 "Video Loader: Downloading video via {} from {}{} ...",
152 if is_https { "HTTPS" } else { "HTTP" },
153 host,
154 path
155 );
156 let video_data = if is_https {
157 match crate::kernel::tls::https_get_binary(&host, &path) {
158 Ok(data) => data,
159 Err(e) => {
160 crate::println!("Video Error: Failed to download HTTPS video: {}", e);
161 return;
162 }
163 }
164 } else {
165 match crate::kernel::net::http_get_binary(&host, &path) {
166 Ok(data) => data,
167 Err(e) => {
168 crate::println!("Video Error: Failed to download HTTP video: {}", e);
169 return;
170 }
171 }
172 };
173 crate::println!("Video Loader: Downloaded {} bytes.", video_data.len());
174
175 let demuxer = match crate::os_lib::mp4::Mp4Demuxer::parse(&video_data) {
176 Ok(d) => d,
177 Err(e) => {
178 crate::println!("Video Error: MP4 parse failed: {}", e);
179 return;
180 }
181 };
182
183 crate::println!(
184 "Video Loader: Video track samples: {}, Audio track samples: {}",
185 demuxer.video_samples.len(),
186 demuxer.audio_samples.len()
187 );
188
189 let mut h264_decoder = crate::os_lib::h264_decode::VideoDecoder::new();
191 if let Some(ref avcc) = demuxer.video_avcc {
192 h264_decoder.init_avcc(avcc);
193 crate::println!("Video Loader: avcC initialized into H.264 decoder");
194 } else {
195 crate::println!("Video Loader: no avcC (will rely on in-band SPS/PPS)");
196 }
197 let mut full_pcm = alloc::vec::Vec::new();
200 let aac = demuxer
201 .audio_asc
202 .as_deref()
203 .ok_or("no AudioSpecificConfig")
204 .and_then(crate::os_lib::aac::parse_audio_specific_config)
205 .and_then(crate::os_lib::aac::AacDecoder::new);
206 let mut audio_fmt: Option<(u32, u32)> = None;
207 match aac {
208 Ok(mut dec) => {
209 let cfg = dec.config();
210 audio_fmt = Some((cfg.sample_rate, cfg.channels));
211 crate::println!(
212 "Video Loader: AAC {} Hz, {} ch, {} frames",
213 cfg.sample_rate,
214 cfg.channels,
215 demuxer.audio_samples.len()
216 );
217 for sample in &demuxer.audio_samples {
218 let Some(sample_data) = video_data.get(sample.offset..sample.offset + sample.size) else {
219 continue;
220 };
221 match dec.decode_frame(sample_data) {
222 Ok(pcm) => full_pcm.extend(pcm),
223 Err(e) => crate::println!("Video Loader: AAC frame error: {}", e),
224 }
225 }
226 }
227 Err(e) => {
228 if !demuxer.audio_samples.is_empty() {
229 crate::println!("Video Loader: audio not decoded: {}", e);
230 }
231 }
232 }
233
234 if !full_pcm.is_empty() {
235 let (vol, muted) = {
236 let pb = crate::os_lib::mse::PLAYBACK.lock();
237 (pb.volume, pb.muted)
238 };
239 crate::os_lib::mse::scale_pcm_samples(&mut full_pcm, vol, muted);
240 if !muted && vol > 0.0 {
241 crate::println!(
242 "Video Player: Starting audio playback ({} samples) ...",
243 full_pcm.len()
244 );
245 if let Some((rate, ch)) = audio_fmt {
247 crate::kernel::audio::AudioStream::start(rate, ch);
248 crate::kernel::audio::AudioStream::push(&full_pcm);
249 }
250 }
251 }
252
253 let start_time = crate::kernel::timer::get_system_time_ms();
254 let mut video_sample_idx = 0;
255
256 while video_sample_idx < demuxer.video_samples.len() {
257 {
258 let s = VIDEO_STATE.lock();
259 if s.as_ref().is_none_or(|state| state.stop_request) {
260 break;
261 }
262 }
263
264 let sample = &demuxer.video_samples[video_sample_idx];
265 let elapsed = crate::kernel::timer::get_system_time_ms().wrapping_sub(start_time);
266
267 if elapsed >= sample.timestamp_ms {
268 let sample_data = &video_data[sample.offset..sample.offset + sample.size];
269 let decoded = h264_decoder.decode_sample_rgb(sample_data);
270 {
271 let mut pb = crate::os_lib::mse::PLAYBACK.lock();
272 pb.current_time = sample.timestamp_ms as f64 / 1000.0;
273 if decoded.is_some() {
274 pb.total_video_frames = pb.total_video_frames.saturating_add(1);
275 } else {
276 pb.dropped_video_frames = pb.dropped_video_frames.saturating_add(1);
277 }
278 }
279 if let Some((fw, fh, rgb)) = decoded {
280 let f = SharedVideoFrame { width: fw, height: fh, rgb_data: rgb };
281 blit_if_app_stalled(&f);
282 *CURRENT_VIDEO_FRAME.lock() = Some(f);
283 }
284 video_sample_idx += 1;
285 } else {
286 let sleep_ms = (sample.timestamp_ms - elapsed).min(10);
287 if sleep_ms > 0 {
288 crate::kernel::scheduler::sleep((sleep_ms / 10).max(1) as usize);
289 } else {
290 crate::kernel::scheduler::yield_now();
291 }
292 }
293 }
294
295 finish_playback();
296}
297
298fn finish_playback() -> ! {
300 let stopped = VIDEO_STATE.lock().as_ref().is_none_or(|st| st.stop_request);
302 {
303 let mut pb = crate::os_lib::mse::PLAYBACK.lock();
304 pb.playing = false;
305 if !stopped {
306 pb.ended = true;
307 pb.ended_pending = true;
308 }
309 }
310 {
311 let mut s = VIDEO_STATE.lock();
312 if let Some(ref mut st) = *s {
313 st.playing = false;
314 }
315 }
316 if !stopped {
318 let mut current_frame = CURRENT_VIDEO_FRAME.lock();
319 *current_frame = None;
320 }
321 crate::println!("Video Player: Playback finished.");
322 crate::kernel::scheduler::exit();
329}
330
331fn stop_requested() -> bool {
332 VIDEO_STATE.lock().as_ref().is_none_or(|st| st.stop_request)
333}
334
335fn play_media_source(url: &str) {
340 use crate::os_lib::fmp4::TrackKind;
341 use crate::os_lib::js::builtins::{media_source_for_url, SOURCE_BUFFERS};
342 let Some((ms_id, _)) = media_source_for_url(url) else {
343 crate::warn!("[VIDEO] unknown MediaSource URL: {}", url);
344 return;
345 };
346 let now = crate::kernel::timer::get_system_time_ms;
347 let started_at = now();
348 crate::info!("[VIDEO] MSE 再生開始 t={}ms", started_at);
349 let mut dec = crate::os_lib::h264_decode::VideoDecoder::new();
350 let mut inited = false;
351 let mut last_dts: Option<u64> = None;
352 let mut clock: Option<(u64, u64)> = None; let mut seek_target: Option<f64> = None; let mut last_progress = now();
355 let mut aac_dec: Option<crate::os_lib::aac::AacDecoder> = None;
356 let mut last_audio_dts: Option<u64> = None;
357 let mut frames = 0usize;
358 let mut was_waiting = false;
359 while !stop_requested() {
360 if VIDEO_STATE.lock().as_ref().is_some_and(|st| st.paused) {
362 crate::os_lib::mse::PLAYBACK.lock().playing = false;
363 crate::kernel::audio::AudioStream::set_paused(true);
364 clock = None;
365 crate::kernel::scheduler::sleep(5);
366 last_progress = now();
367 continue;
368 }
369 crate::os_lib::mse::PLAYBACK.lock().playing = true;
370 crate::kernel::audio::AudioStream::set_paused(false);
371 if let Some(target) = crate::os_lib::mse::SEEK_REQUEST.lock().take() {
373 let (key, audio_prev_dts) = {
374 let list = SOURCE_BUFFERS.lock();
375 let v_key = list.iter().filter(|b| b.owner == ms_id).find_map(|b| {
376 let t = b.demux.tracks.iter().find(|t| t.kind == TrackKind::Video)?;
377 let ts = t.timescale.max(1) as f64;
378 b.samples
379 .iter()
380 .filter(|s| s.track_id == t.track_id && s.keyframe && s.pts as f64 / ts <= target + 0.001)
381 .max_by_key(|s| s.dts)
382 .map(|s| s.dts)
383 });
384 let a_prev = list.iter().filter(|b| b.owner == ms_id).find_map(|b| {
385 let t = b.demux.tracks.iter().find(|t| t.kind == TrackKind::Audio)?;
386 let ts = t.timescale.max(1) as f64;
387 b.samples
388 .iter()
389 .filter(|s| s.track_id == t.track_id && (s.pts.max(0) as f64) / ts < target)
390 .max_by_key(|s| s.dts)
391 .map(|s| s.dts)
392 });
393 (v_key, a_prev)
394 };
395 last_dts = key.and_then(|d| d.checked_sub(1));
396 last_audio_dts = audio_prev_dts;
397 crate::kernel::audio::AudioStream::flush();
399 clock = None;
400 seek_target = Some(target);
401 dec = crate::os_lib::h264_decode::VideoDecoder::new();
402 inited = false;
403 crate::os_lib::mse::PLAYBACK.lock().current_time = target;
404 }
405 let next = {
407 let list = SOURCE_BUFFERS.lock();
408 list.iter().filter(|b| b.owner == ms_id).find_map(|b| {
409 let t = b.demux.tracks.iter().find(|t| t.kind == TrackKind::Video)?;
410 let s = b
411 .samples
412 .iter()
413 .filter(|s| s.track_id == t.track_id && last_dts.is_none_or(|d| s.dts > d))
414 .min_by_key(|s| s.dts)?;
415 Some((t.avcc.clone(), t.timescale.max(1) as u64, s.clone()))
416 })
417 };
418 let Some((avcc, ts, sample)) = next else {
419 let ended = media_source_for_url(url).is_some_and(|x| x.1);
420 let elapsed = now().wrapping_sub(last_progress);
421 if ended || elapsed > 15_000 {
422 break;
423 }
424 if !was_waiting {
425 crate::os_lib::mse::PLAYBACK.lock().waiting_pending = true;
426 was_waiting = true;
427 } else if elapsed > 5_000 {
428 crate::os_lib::mse::PLAYBACK.lock().stalled_pending = true;
429 }
430 crate::kernel::scheduler::sleep(2);
431 continue;
432 };
433 if was_waiting {
434 crate::os_lib::mse::PLAYBACK.lock().playing_pending = true;
435 was_waiting = false;
436 }
437 if !inited {
438 if let Some(a) = avcc {
439 dec.init_avcc(&a);
440 }
441 inited = true;
442 }
443 let pts_sec = sample.pts.max(0) as f64 / ts as f64;
444 if let Some(target) = seek_target {
446 if pts_sec + 0.001 < target {
447 let _ = dec.decode_sample_rgb(&sample.data);
448 last_dts = Some(sample.dts);
449 last_progress = now();
450 continue;
451 }
452 seek_target = None;
453 }
454 feed_mse_audio_window(ms_id, &mut aac_dec, &mut last_audio_dts, pts_sec + 1.0);
458
459 let rate = crate::os_lib::mse::PLAYBACK.lock().playback_rate.clamp(0.25, 4.0);
460 let t_ms = sample.dts * 1000 / ts;
461 let (c0, t0) = *clock.get_or_insert((now(), t_ms));
462 let scaled_delta = ((t_ms.saturating_sub(t0) as f64) / rate) as u64;
463 let due = c0 + scaled_delta;
464 let cur = now();
465 if cur < due {
466 crate::kernel::scheduler::sleep(((due - cur) / 10).clamp(1, 5) as usize);
467 continue;
468 }
469 let decoded = dec.decode_sample_rgb(&sample.data);
470 if let Some((fw, fh, rgb)) = decoded {
471 let f = SharedVideoFrame { width: fw, height: fh, rgb_data: rgb };
472 blit_if_app_stalled(&f);
473 *CURRENT_VIDEO_FRAME.lock() = Some(f);
474 frames += 1;
475 let mut pb = crate::os_lib::mse::PLAYBACK.lock();
476 pb.current_time = pts_sec;
477 pb.total_video_frames = pb.total_video_frames.saturating_add(1);
478 } else {
479 let mut pb = crate::os_lib::mse::PLAYBACK.lock();
480 pb.current_time = pts_sec;
481 pb.dropped_video_frames = pb.dropped_video_frames.saturating_add(1);
482 }
483 last_dts = Some(sample.dts);
484 last_progress = now();
485 }
486 crate::kernel::audio::AudioStream::stop();
487 crate::info!("[VIDEO] MSE 再生終了 frames={} elapsed={}ms", frames, now().wrapping_sub(started_at));
488}
489
490fn feed_mse_audio_window(
493 ms_id: usize,
494 aac_dec: &mut Option<crate::os_lib::aac::AacDecoder>,
495 last_audio_dts: &mut Option<u64>,
496 max_pts_sec: f64,
497) {
498 use crate::os_lib::fmp4::TrackKind;
499 use crate::os_lib::js::builtins::SOURCE_BUFFERS;
500 let (asc, frames, next_dts) = {
501 let list = SOURCE_BUFFERS.lock();
502 let Some((b, t)) = list.iter().filter(|b| b.owner == ms_id).find_map(|b| {
503 let t = b.demux.tracks.iter().find(|t| t.kind == TrackKind::Audio)?;
504 Some((b, t))
505 }) else {
506 return;
507 };
508 let (frames, next_dts) = crate::os_lib::mse::select_audio_samples_in_window(
509 &b.samples,
510 t.track_id,
511 t.timescale,
512 *last_audio_dts,
513 max_pts_sec,
514 16,
515 );
516 (t.asc.clone(), frames, next_dts)
517 };
518 if frames.is_empty() {
519 return;
520 }
521 *last_audio_dts = next_dts;
522 if aac_dec.is_none() {
523 if let Some(asc_bytes) = asc.as_deref() {
524 if let Ok(cfg) = crate::os_lib::aac::parse_audio_specific_config(asc_bytes) {
525 if let Ok(dec) = crate::os_lib::aac::AacDecoder::new(cfg) {
526 crate::kernel::audio::AudioStream::start(cfg.sample_rate, cfg.channels);
528 *aac_dec = Some(dec);
529 }
530 }
531 }
532 }
533 let Some(dec) = aac_dec.as_mut() else {
534 return;
535 };
536 let (vol, muted) = {
537 let pb = crate::os_lib::mse::PLAYBACK.lock();
538 (pb.volume, pb.muted)
539 };
540 if muted || vol <= 0.0 {
541 for f in &frames {
543 let _ = dec.decode_frame(f);
544 }
545 return;
546 }
547 let mut pcm = Vec::new();
548 for f in &frames {
549 if let Ok(p) = dec.decode_frame(f) {
550 pcm.extend(p);
551 }
552 }
553 if !pcm.is_empty() {
554 crate::os_lib::mse::scale_pcm_samples(&mut pcm, vol, muted);
555 crate::kernel::audio::AudioStream::push(&pcm);
556 }
557}