1extern crate alloc;
8
9use super::fmp4::{FragSample, Fmp4Demuxer, TrackKind};
10use alloc::string::String;
11use alloc::vec::Vec;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum MediaSourceReadyState {
16 Closed,
17 Open,
18 Ended,
19}
20
21impl MediaSourceReadyState {
22 pub fn as_str(&self) -> &'static str {
23 match self {
24 Self::Closed => "closed",
25 Self::Open => "open",
26 Self::Ended => "ended",
27 }
28 }
29}
30
31#[derive(Debug, Clone, PartialEq)]
33pub struct TimeRange {
34 pub start: f64,
35 pub end: f64,
36}
37
38#[derive(Debug, Clone, PartialEq, Default)]
39pub struct TimeRanges {
40 pub ranges: Vec<TimeRange>,
41}
42
43impl TimeRanges {
44 pub fn new() -> Self {
45 Self { ranges: Vec::new() }
46 }
47
48 pub fn len(&self) -> usize {
49 self.ranges.len()
50 }
51
52 pub fn is_empty(&self) -> bool {
53 self.ranges.is_empty()
54 }
55
56 pub fn start(&self, index: usize) -> Option<f64> {
57 self.ranges.get(index).map(|r| r.start)
58 }
59
60 pub fn end(&self, index: usize) -> Option<f64> {
61 self.ranges.get(index).map(|r| r.end)
62 }
63
64 pub fn add(&mut self, start: f64, end: f64) {
65 if start >= end {
66 return;
67 }
68 self.ranges.push(TimeRange { start, end });
69 }
70
71 pub fn intersect(&self, other: &TimeRanges) -> TimeRanges {
73 let mut out = TimeRanges::new();
74 for a in &self.ranges {
75 for b in &other.ranges {
76 out.add(a.start.max(b.start), a.end.min(b.end));
77 }
78 }
79 out
80 }
81
82 pub fn from_intervals(mut iv: Vec<(f64, f64)>, gap: f64) -> Self {
84 iv.retain(|(a, b)| a < b);
85 iv.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(core::cmp::Ordering::Equal));
86 let mut out = Self::new();
87 for (a, b) in iv {
88 match out.ranges.last_mut() {
89 Some(last) if a <= last.end + gap => last.end = last.end.max(b),
90 _ => out.ranges.push(TimeRange { start: a, end: b }),
91 }
92 }
93 out
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum MseError {
100 NotOpen,
101 UnsupportedType,
102 AlreadyUpdating,
103 BufferNotFound,
104 ParseError,
106 InvalidAccess,
108}
109
110pub struct SourceBufferCore {
114 pub id: usize,
115 pub mime_type: String,
116 pub updating: bool,
117 pub buffered: TimeRanges,
118 pub mode: String,
120 pub timestamp_offset: f64,
122 pub append_window_start: f64,
124 pub append_window_end: f64,
126 pub samples: Vec<FragSample>,
128 pub demux: Fmp4Demuxer,
129 pub owner: usize,
131}
132
133impl SourceBufferCore {
134 pub fn new(id: usize, mime_type: &str) -> Self {
135 Self {
136 id,
137 mime_type: mime_type.into(),
138 updating: false,
139 buffered: TimeRanges::new(),
140 mode: String::from("segments"),
141 timestamp_offset: 0.0,
142 append_window_start: 0.0,
143 append_window_end: f64::INFINITY,
144 samples: Vec::new(),
145 demux: Fmp4Demuxer::new(),
146 owner: 0,
147 }
148 }
149
150 pub fn set_mode(&mut self, mode: &str) -> Result<(), MseError> {
151 if self.updating {
152 return Err(MseError::AlreadyUpdating);
153 }
154 if mode != "segments" && mode != "sequence" {
155 return Err(MseError::InvalidAccess);
156 }
157 self.mode = String::from(mode);
158 Ok(())
159 }
160
161 pub fn set_timestamp_offset(&mut self, offset: f64) -> Result<(), MseError> {
162 if self.updating {
163 return Err(MseError::AlreadyUpdating);
164 }
165 if !offset.is_finite() {
166 return Err(MseError::InvalidAccess);
167 }
168 self.timestamp_offset = offset;
169 Ok(())
170 }
171
172 pub fn set_append_window_start(&mut self, start: f64) -> Result<(), MseError> {
173 if self.updating {
174 return Err(MseError::AlreadyUpdating);
175 }
176 if !start.is_finite() || start < 0.0 || start >= self.append_window_end {
177 return Err(MseError::InvalidAccess);
178 }
179 self.append_window_start = start;
180 Ok(())
181 }
182
183 pub fn set_append_window_end(&mut self, end: f64) -> Result<(), MseError> {
184 if self.updating {
185 return Err(MseError::AlreadyUpdating);
186 }
187 if end.is_nan() || end <= self.append_window_start {
188 return Err(MseError::InvalidAccess);
189 }
190 self.append_window_end = end;
191 Ok(())
192 }
193
194 pub fn change_type(&mut self, mime_type: &str) -> Result<(), MseError> {
196 if self.updating {
197 return Err(MseError::AlreadyUpdating);
198 }
199 if mime_type.trim().is_empty() || !MediaSourceCore::is_type_supported(mime_type) {
200 return Err(MseError::UnsupportedType);
201 }
202 self.mime_type = String::from(mime_type);
203 Ok(())
204 }
205
206 pub fn video_dimensions(&self) -> Option<(u32, u32)> {
208 self.demux
209 .tracks
210 .iter()
211 .find(|t| t.kind == TrackKind::Video && t.width > 0 && t.height > 0)
212 .map(|t| (t.width, t.height))
213 }
214
215 pub fn append_buffer(&mut self, chunk: &[u8]) -> Result<(), MseError> {
217 if self.updating {
218 return Err(MseError::AlreadyUpdating);
219 }
220 self.updating = true;
221 let r = self.demux.push(chunk);
222 self.updating = false;
223 let new = r.map_err(|_| MseError::ParseError)?;
224 if !new.is_empty() {
225 let scales: Vec<(u32, f64)> = self
226 .demux
227 .tracks
228 .iter()
229 .map(|t| (t.track_id, t.timescale.max(1) as f64))
230 .collect();
231 let scale_of = |tid: u32| -> f64 {
232 scales.iter().find(|(id, _)| *id == tid).map_or(1000.0, |x| x.1)
233 };
234
235 let primary_tid = self
236 .demux
237 .tracks
238 .iter()
239 .find(|t| t.kind == TrackKind::Video)
240 .or_else(|| self.demux.tracks.first())
241 .map(|t| t.track_id);
242
243 let effective_offset = if self.mode == "sequence" {
245 let min_pts_sec = new
246 .iter()
247 .filter(|s| primary_tid.is_none_or(|pid| s.track_id == pid))
248 .map(|s| s.pts as f64 / scale_of(s.track_id))
249 .reduce(f64::min)
250 .or_else(|| {
251 new.iter()
252 .map(|s| s.pts as f64 / scale_of(s.track_id))
253 .reduce(f64::min)
254 })
255 .unwrap_or(0.0);
256 self.timestamp_offset - min_pts_sec
257 } else {
258 self.timestamp_offset
259 };
260
261 let mut max_end_sec = self.timestamp_offset;
262 let mut filtered = Vec::with_capacity(new.len());
263 for mut s in new {
264 let ts = scale_of(s.track_id);
265 let delta_ticks = libm::round(effective_offset * ts) as i64;
266 s.pts += delta_ticks;
267 s.dts = (s.dts as i64 + delta_ticks).max(0) as u64;
268 let pts_sec = s.pts as f64 / ts;
269 let end_sec = (s.pts + s.duration as i64) as f64 / ts;
270 if primary_tid.is_none_or(|pid| s.track_id == pid) && end_sec > max_end_sec {
271 max_end_sec = end_sec;
272 }
273 if pts_sec + 1e-6 >= self.append_window_start && end_sec - 1e-6 <= self.append_window_end {
275 self.samples.retain(|old| {
277 !(old.track_id == s.track_id && (old.pts - s.pts).abs() * 2 < s.duration.max(1) as i64)
278 });
279 filtered.push(s);
280 }
281 }
282 if self.mode == "sequence" {
283 self.timestamp_offset = max_end_sec;
284 }
285 self.samples.extend(filtered);
286 self.update_buffered();
287 }
288 Ok(())
289 }
290
291 fn update_buffered(&mut self) {
293 let track = self
294 .demux
295 .tracks
296 .iter()
297 .find(|t| t.kind == TrackKind::Video)
298 .or_else(|| self.demux.tracks.first());
299 let Some(t) = track else { return };
300 let (id, ts) = (t.track_id, t.timescale.max(1) as f64);
301 let iv = self
302 .samples
303 .iter()
304 .filter(|s| s.track_id == id)
305 .map(|s| (s.pts as f64 / ts, (s.pts as f64 + s.duration as f64) / ts))
306 .collect();
307 self.buffered = TimeRanges::from_intervals(iv, 0.05);
309 }
310
311 pub fn remove_range(&mut self, start: f64, end: f64) {
313 let ts: Vec<(u32, f64)> = self.demux.tracks.iter().map(|t| (t.track_id, t.timescale.max(1) as f64)).collect();
314 self.samples.retain(|s| {
315 let scale = ts.iter().find(|(id, _)| *id == s.track_id).map_or(1000.0, |x| x.1);
316 let t = s.pts as f64 / scale;
317 !(t >= start && t < end)
318 });
319 self.update_buffered();
320 }
321
322 pub fn abort(&mut self) {
324 self.updating = false;
325 }
326
327 pub fn clear(&mut self) {
328 self.samples.clear();
329 self.buffered.ranges.clear();
330 self.updating = false;
331 }
332}
333
334pub struct MediaSourceCore {
336 pub ready_state: MediaSourceReadyState,
337 pub duration: f64,
338 pub source_buffers: Vec<SourceBufferCore>,
339 pub live_seekable_range: Option<(f64, f64)>,
340 next_buffer_id: usize,
341}
342
343impl MediaSourceCore {
344 pub fn new() -> Self {
345 Self {
346 ready_state: MediaSourceReadyState::Closed,
347 duration: 0.0,
348 source_buffers: Vec::new(),
349 live_seekable_range: None,
350 next_buffer_id: 1,
351 }
352 }
353
354 pub fn is_type_supported(mime_type: &str) -> bool {
361 let lower = mime_type.to_ascii_lowercase();
362 let mut parts = lower.split(';');
363 let container = parts.next().unwrap_or("").trim();
364 if container != "video/mp4" && container != "audio/mp4" {
365 return false;
366 }
367 let mut codecs: Option<&str> = None;
368 for param in parts.map(str::trim).filter(|s| !s.is_empty()) {
369 let Some((k, v)) = param.split_once('=') else {
370 return false;
371 };
372 let k = k.trim();
373 let v = v.trim().trim_matches(|ch| ch == '"' || ch == '\'');
374 match k {
375 "codecs" => {
376 if v.is_empty() {
377 return false;
378 }
379 codecs = Some(v);
380 }
381 "width" => {
382 let Ok(n) = v.parse::<u32>() else { return false };
383 if !(1..=3840).contains(&n) {
384 return false;
385 }
386 }
387 "height" => {
388 let Ok(n) = v.parse::<u32>() else { return false };
389 if !(1..=2160).contains(&n) {
390 return false;
391 }
392 }
393 "framerate" => {
394 let Ok(n) = v.parse::<f64>() else { return false };
395 if !(n > 0.0 && n <= 120.0) {
396 return false;
397 }
398 }
399 "bitrate" => {
400 let Ok(n) = v.parse::<u64>() else { return false };
401 if !(1..=80_000_000).contains(&n) {
402 return false;
403 }
404 }
405 "channels" => {
406 let Ok(n) = v.parse::<u32>() else { return false };
407 if !(1..=8).contains(&n) {
408 return false;
409 }
410 }
411 "samplerate" => {
412 let Ok(n) = v.parse::<u32>() else { return false };
413 if !(8000..=96000).contains(&n) {
414 return false;
415 }
416 }
417 "cryptoblockformat" | "eotf" => {}
418 _ => return false,
419 }
420 }
421 let Some(codecs) = codecs else { return true };
422 codecs.split(',').map(str::trim).all(|c| {
423 !c.is_empty()
424 && (c.starts_with("avc1.")
425 || c.starts_with("avc3.")
426 || c == "avc1"
427 || c == "mp4a.40.2"
428 || c == "mp4a.67"
429 || c == "mp4a")
430 })
431 }
432
433 pub fn open(&mut self) {
435 self.ready_state = MediaSourceReadyState::Open;
436 }
437
438 pub fn add_source_buffer(&mut self, mime_type: &str) -> Result<usize, MseError> {
440 if self.ready_state != MediaSourceReadyState::Open {
441 return Err(MseError::NotOpen);
442 }
443 if !Self::is_type_supported(mime_type) {
444 return Err(MseError::UnsupportedType);
445 }
446 let id = self.next_buffer_id;
447 self.next_buffer_id += 1;
448 self.source_buffers.push(SourceBufferCore::new(id, mime_type));
449 Ok(id)
450 }
451
452 pub fn remove_source_buffer(&mut self, id: usize) -> Result<(), MseError> {
454 if let Some(pos) = self.source_buffers.iter().position(|b| b.id == id) {
455 self.source_buffers.remove(pos);
456 Ok(())
457 } else {
458 Err(MseError::BufferNotFound)
459 }
460 }
461
462 pub fn get_source_buffer_mut(&mut self, id: usize) -> Option<&mut SourceBufferCore> {
464 self.source_buffers.iter_mut().find(|b| b.id == id)
465 }
466
467 pub fn end_of_stream(&mut self) -> Result<(), MseError> {
469 if self.ready_state != MediaSourceReadyState::Open {
470 return Err(MseError::NotOpen);
471 }
472 self.ready_state = MediaSourceReadyState::Ended;
473 Ok(())
474 }
475
476 pub fn set_live_seekable_range(&mut self, start: f64, end: f64) -> Result<(), MseError> {
478 if self.ready_state != MediaSourceReadyState::Open {
479 return Err(MseError::NotOpen);
480 }
481 if !start.is_finite() || !end.is_finite() || start < 0.0 || start > end {
482 return Err(MseError::InvalidAccess);
483 }
484 self.live_seekable_range = Some((start, end));
485 Ok(())
486 }
487
488 pub fn clear_live_seekable_range(&mut self) -> Result<(), MseError> {
490 if self.ready_state != MediaSourceReadyState::Open {
491 return Err(MseError::NotOpen);
492 }
493 self.live_seekable_range = None;
494 Ok(())
495 }
496}
497
498impl Default for MediaSourceCore {
499 fn default() -> Self {
500 Self::new()
501 }
502}
503
504#[derive(Debug, Clone, PartialEq)]
507pub enum MediaRequest {
508 Play(String),
510 Pause,
511}
512
513pub static MEDIA_REQUEST: spin::Mutex<Option<MediaRequest>> = spin::Mutex::new(None);
515
516#[derive(Clone, Debug, PartialEq)]
520pub struct PlaybackStatus {
521 pub url: String,
523 pub current_time: f64,
525 pub playing: bool,
526 pub ended: bool,
527 pub ended_pending: bool,
529 pub waiting_pending: bool,
531 pub playing_pending: bool,
533 pub stalled_pending: bool,
535 pub total_video_frames: u32,
537 pub dropped_video_frames: u32,
539 pub volume: f64,
541 pub muted: bool,
543 pub playback_rate: f64,
545}
546
547impl PlaybackStatus {
548 pub const fn new() -> Self {
549 Self {
550 url: String::new(),
551 current_time: 0.0,
552 playing: false,
553 ended: false,
554 ended_pending: false,
555 waiting_pending: false,
556 playing_pending: false,
557 stalled_pending: false,
558 total_video_frames: 0,
559 dropped_video_frames: 0,
560 volume: 1.0,
561 muted: false,
562 playback_rate: 1.0,
563 }
564 }
565
566 pub fn should_resume(&self, url: &str) -> bool {
568 !url.is_empty() && self.url == url && !self.ended && self.current_time > 0.0
569 }
570
571 pub fn set_volume(&mut self, v: f64) -> bool {
573 if v.is_finite() && (0.0..=1.0).contains(&v) {
574 self.volume = v;
575 true
576 } else {
577 false
578 }
579 }
580
581 pub fn set_playback_rate(&mut self, r: f64) -> bool {
583 if r.is_finite() && r > 0.0 {
584 self.playback_rate = r.clamp(0.25, 4.0);
585 true
586 } else {
587 false
588 }
589 }
590}
591
592pub static PLAYBACK: spin::Mutex<PlaybackStatus> = spin::Mutex::new(PlaybackStatus::new());
593
594pub static SEEK_REQUEST: spin::Mutex<Option<f64>> = spin::Mutex::new(None);
596
597pub fn select_audio_samples_in_window(
601 samples: &[FragSample],
602 track_id: u32,
603 timescale: u32,
604 after_dts: Option<u64>,
605 max_pts_sec: f64,
606 max_frames: usize,
607) -> (Vec<Vec<u8>>, Option<u64>) {
608 if max_frames == 0 || timescale == 0 {
609 return (Vec::new(), after_dts);
610 }
611 let ts = timescale as f64;
612 let mut out = Vec::new();
613 let mut last_dts = after_dts;
614 for s in samples {
615 if s.track_id != track_id {
616 continue;
617 }
618 if let Some(prev) = last_dts {
619 if s.dts <= prev {
620 continue;
621 }
622 }
623 let pts_sec = (s.pts.max(0) as f64) / ts;
624 if pts_sec > max_pts_sec {
625 break;
626 }
627 out.push(s.data.clone());
628 last_dts = Some(s.dts);
629 if out.len() >= max_frames {
630 break;
631 }
632 }
633 (out, last_dts)
634}
635
636pub fn scale_pcm_samples(pcm: &mut [i16], volume: f64, muted: bool) {
638 if muted || !volume.is_finite() || volume <= 0.0 {
639 pcm.fill(0);
640 return;
641 }
642 let vol = volume.clamp(0.0, 1.0);
643 if (vol - 1.0).abs() < 1e-6 {
644 return;
645 }
646 let scale = (vol * 256.0) as i32;
647 for sample in pcm.iter_mut() {
648 let v = (*sample as i32 * scale) >> 8;
649 *sample = v.clamp(i16::MIN as i32, i16::MAX as i32) as i16;
650 }
651}
652