1use super::*;
7use crate::os_lib::mse::{MediaSourceCore, SourceBufferCore, TimeRanges};
8
9pub(crate) static SOURCE_BUFFERS: spin::Mutex<Vec<SourceBufferCore>> = spin::Mutex::new(Vec::new());
13static NEXT_SB_ID: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(1);
14static NEXT_MS_ID: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(1);
15
16pub(crate) static MEDIA_SOURCE_URLS: spin::Mutex<Vec<(String, usize, bool)>> = spin::Mutex::new(Vec::new());
19
20fn ms_id_of(v: &Value) -> usize {
21 match v {
22 Value::Object(o) => o.borrow().props.get("_ms_id").map_or(0, |x| x.to_number() as usize),
23 _ => 0,
24 }
25}
26
27pub(crate) fn register_media_source_url(url: &str, ms: &Value) {
29 let id = ms_id_of(ms);
30 if id != 0 {
31 MEDIA_SOURCE_URLS.lock().push((String::from(url), id, false));
32 }
33}
34
35pub(crate) fn media_source_ready_state(url: &str) -> Option<u8> {
37 let (id, _) = media_source_for_url(url)?;
38 let list = SOURCE_BUFFERS.lock();
39 let mine: Vec<&SourceBufferCore> = list.iter().filter(|b| b.owner == id).collect();
40 if mine.iter().any(|b| !b.samples.is_empty()) {
41 Some(4)
42 } else if mine.iter().any(|b| !b.demux.tracks.is_empty()) {
43 Some(1)
44 } else {
45 Some(0)
46 }
47}
48
49pub(crate) fn media_source_buffered_end(url: &str) -> Option<f64> {
51 let (id, _) = media_source_for_url(url)?;
52 let list = SOURCE_BUFFERS.lock();
53 list.iter()
54 .filter(|b| b.owner == id)
55 .filter_map(|b| b.buffered.ranges.last().map(|r| r.end))
56 .reduce(f64::max)
57}
58
59pub(crate) fn media_element_buffered(url: &str) -> Option<Value> {
61 let (id, _) = media_source_for_url(url)?;
62 let list = SOURCE_BUFFERS.lock();
63 let tr = list
64 .iter()
65 .filter(|b| b.owner == id && !b.buffered.is_empty())
66 .map(|b| b.buffered.clone())
67 .reduce(|acc, b| acc.intersect(&b))
68 .unwrap_or_default();
69 Some(make_time_ranges_object(&tr))
70}
71
72pub(crate) fn media_element_seekable(duration: Option<f64>) -> Value {
74 let mut tr = TimeRanges::new();
75 if let Some(d) = duration {
76 tr.add(0.0, d);
77 }
78 make_time_ranges_object(&tr)
79}
80
81pub(crate) fn media_source_video_dimensions(url: &str) -> Option<(u32, u32)> {
83 let (id, _) = media_source_for_url(url)?;
84 let list = SOURCE_BUFFERS.lock();
85 list.iter()
86 .filter(|b| b.owner == id)
87 .find_map(SourceBufferCore::video_dimensions)
88}
89
90pub(crate) fn media_source_for_url(url: &str) -> Option<(usize, bool)> {
92 MEDIA_SOURCE_URLS.lock().iter().find(|(u, _, _)| u == url).map(|(_, id, ended)| (*id, *ended))
93}
94
95pub(crate) fn media_source_ctor(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
97 let obj = match this {
98 Value::Object(o) => o,
99 _ => Obj::plain(),
100 };
101
102 let source_buffers = Obj::array(alloc::vec![]);
103 let active_buffers = Obj::array(alloc::vec![]);
104
105 {
106 let mut m = obj.borrow_mut();
107 m.props.insert("readyState".into(), Value::str("closed"));
108 m.props.insert("duration".into(), Value::Number(0.0));
109 m.props.insert("sourceBuffers".into(), Value::Object(source_buffers.clone()));
110 m.props.insert("activeSourceBuffers".into(), Value::Object(active_buffers));
111 m.props.insert("addSourceBuffer".into(), nv("MediaSource.addSourceBuffer", media_source_add_source_buffer));
112 m.props.insert("removeSourceBuffer".into(), nv("MediaSource.removeSourceBuffer", media_source_remove_source_buffer));
113 m.props.insert("endOfStream".into(), nv("MediaSource.endOfStream", media_source_end_of_stream));
114 m.props.insert("setLiveSeekableRange".into(), nv("MediaSource.setLiveSeekableRange", media_source_set_live_seekable_range));
115 m.props.insert("clearLiveSeekableRange".into(), nv("MediaSource.clearLiveSeekableRange", media_source_clear_live_seekable_range));
116 m.props.insert("_listeners".into(), Value::Object(Obj::plain()));
117 m.props.insert("_is_media_source".into(), Value::Bool(true));
118 m.props.insert("addEventListener".into(), nv("addEventListener", event_target_add_event_listener));
119 m.props.insert("removeEventListener".into(), nv("removeEventListener", event_target_remove_event_listener));
120 m.props.insert("dispatchEvent".into(), nv("dispatchEvent", event_target_dispatch_event));
121 m.props.insert("onsourceopen".into(), Value::Null);
122 m.props.insert("onsourceended".into(), Value::Null);
123 m.props.insert("onsourceclose".into(), Value::Null);
124 m.props.insert("_core".into(), Value::Object(Obj::plain()));
125 m.props.insert(
126 "_ms_id".into(),
127 Value::Number(NEXT_MS_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed) as f64),
128 );
129 }
130
131 Ok(Value::Object(obj))
132}
133
134pub(crate) fn media_source_open_internal(it: &mut Interp, ms: &Value) {
136 if let Value::Object(o) = ms {
137 let is_already_open = {
138 let b = o.borrow();
139 b.props.get("readyState").map(|v| v.to_js_string()).as_deref() == Some("open")
140 };
141 if !is_already_open {
142 o.borrow_mut().props.insert("readyState".into(), Value::str("open"));
143 dispatch_mse_event(it, ms, "sourceopen");
144 }
145 }
146}
147
148pub(crate) fn dispatch_mse_event(it: &mut Interp, target: &Value, event_type: &str) {
150 let ev = Obj::plain();
151 {
152 let mut e = ev.borrow_mut();
153 e.props.insert("type".into(), Value::str(event_type));
154 e.props.insert("target".into(), target.clone());
155 e.props.insert("currentTarget".into(), target.clone());
156 }
157 let ev_val = Value::Object(ev);
158
159 let handler_key = alloc::format!("on{}", event_type);
161 let handler = if let Value::Object(t) = target {
162 t.borrow().props.get(&handler_key).cloned()
163 } else {
164 None
165 };
166 if let Some(h) = handler.filter(|h| matches!(h, Value::Object(o) if o.borrow().is_callable())) {
168 let ctx = alloc::format!("{} on-handler", event_type);
169 it.call_listener(&h, target.clone(), core::slice::from_ref(&ev_val), &ctx);
170 }
171
172 let _ = event_target_dispatch_event(it, target.clone(), &[ev_val]);
174}
175
176pub(crate) fn media_source_is_type_supported(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
178 let mime = arg(a, 0).to_js_string();
179 let supported = MediaSourceCore::is_type_supported(&mime);
180 Ok(Value::Bool(supported))
181}
182
183pub(crate) fn media_source_add_source_buffer(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
185 let mime = arg(a, 0).to_js_string();
186 if !MediaSourceCore::is_type_supported(&mime) {
187 return Err(it.type_error(alloc::format!("Unsupported MIME type: {}", mime)));
188 }
189
190 if let Value::Object(ms_obj) = &this {
191 let mut m = ms_obj.borrow_mut();
192 let ready_state = m.props.get("readyState").map(|v| v.to_js_string()).unwrap_or_else(|| "closed".into());
194 if ready_state != "open" {
195 m.props.insert("readyState".into(), Value::str("open"));
197 }
198
199 let owner = m.props.get("_ms_id").map_or(0, |x| x.to_number() as usize);
200 let sb_obj = make_source_buffer_object(&mime, owner);
201
202 if let Some(Value::Object(sbl)) = m.props.get("sourceBuffers") {
203 if let ObjKind::Array(ref mut vec) = sbl.borrow_mut().kind {
204 vec.push(sb_obj.clone());
205 }
206 }
207
208 return Ok(sb_obj);
209 }
210
211 Err(it.type_error("addSourceBuffer called on non-MediaSource"))
212}
213
214pub(crate) fn media_source_remove_source_buffer(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
216 let target = arg(a, 0);
217 if let Value::Object(ms_obj) = &this {
218 let m = ms_obj.borrow();
219 if let Some(Value::Object(sbl)) = m.props.get("sourceBuffers") {
220 if let ObjKind::Array(ref mut vec) = sbl.borrow_mut().kind {
221 if let Some(pos) = vec.iter().position(|v| match (v, &target) {
222 (Value::Object(o1), Value::Object(o2)) => Rc::ptr_eq(o1, o2),
223 _ => false,
224 }) {
225 vec.remove(pos);
226 }
227 }
228 }
229 }
230 Ok(Value::Undefined)
231}
232
233pub(crate) fn media_source_end_of_stream(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
235 if let Value::Object(ms_obj) = &this {
236 ms_obj.borrow_mut().props.insert("readyState".into(), Value::str("ended"));
237 }
238 let id = ms_id_of(&this);
239 for e in MEDIA_SOURCE_URLS.lock().iter_mut().filter(|e| e.1 == id) {
240 e.2 = true;
241 }
242 Ok(Value::Undefined)
243}
244
245pub(crate) fn media_source_set_live_seekable_range(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
247 let start = arg(a, 0).to_number();
248 let end = arg(a, 1).to_number();
249 if !start.is_finite() || !end.is_finite() || start < 0.0 || start > end {
250 return Err(it.type_error("Invalid live seekable range"));
251 }
252 if let Value::Object(ms_obj) = &this {
253 let mut m = ms_obj.borrow_mut();
254 let rs = m.props.get("readyState").map(|v| v.to_js_string()).unwrap_or_default();
255 if rs != "open" {
256 return Err(it.type_error("MediaSource is not open"));
257 }
258 m.props.insert("_live_seekable_start".into(), Value::Number(start));
259 m.props.insert("_live_seekable_end".into(), Value::Number(end));
260 return Ok(Value::Undefined);
261 }
262 Err(it.type_error("setLiveSeekableRange called on non-MediaSource"))
263}
264
265pub(crate) fn media_source_clear_live_seekable_range(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
267 if let Value::Object(ms_obj) = &this {
268 let mut m = ms_obj.borrow_mut();
269 let rs = m.props.get("readyState").map(|v| v.to_js_string()).unwrap_or_default();
270 if rs != "open" {
271 return Err(it.type_error("MediaSource is not open"));
272 }
273 m.props.shift_remove("_live_seekable_start");
274 m.props.shift_remove("_live_seekable_end");
275 return Ok(Value::Undefined);
276 }
277 Err(it.type_error("clearLiveSeekableRange called on non-MediaSource"))
278}
279
280fn make_source_buffer_object(mime_type: &str, owner: usize) -> Value {
282 let id = NEXT_SB_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
283 let mut core = SourceBufferCore::new(id, mime_type);
284 core.owner = owner;
285 SOURCE_BUFFERS.lock().push(core);
286 let sb = Obj::plain();
287 {
288 let mut s = sb.borrow_mut();
289 s.props.insert("_sb_id".into(), Value::Number(id as f64));
290 s.props.insert("mode".into(), Value::str("segments"));
291 s.props.insert("timestampOffset".into(), Value::Number(0.0));
292 s.props.insert("appendWindowStart".into(), Value::Number(0.0));
293 s.props.insert("appendWindowEnd".into(), Value::Number(f64::INFINITY));
294 s.props.insert("mimeType".into(), Value::str(mime_type));
295 s.props.insert("updating".into(), Value::Bool(false));
296 s.props.insert("buffered".into(), make_time_ranges_object(&TimeRanges::new()));
297 s.props.insert("appendBuffer".into(), nv("SourceBuffer.appendBuffer", source_buffer_append_buffer));
298 s.props.insert("changeType".into(), nv("SourceBuffer.changeType", source_buffer_change_type));
299 s.props.insert("abort".into(), nv("SourceBuffer.abort", source_buffer_abort));
300 s.props.insert("remove".into(), nv("SourceBuffer.remove", source_buffer_remove));
301 s.props.insert("_listeners".into(), Value::Object(Obj::plain()));
302 s.props.insert("addEventListener".into(), nv("addEventListener", event_target_add_event_listener));
303 s.props.insert("removeEventListener".into(), nv("removeEventListener", event_target_remove_event_listener));
304 s.props.insert("dispatchEvent".into(), nv("dispatchEvent", event_target_dispatch_event));
305 s.props.insert("onupdatestart".into(), Value::Null);
306 s.props.insert("onupdate".into(), Value::Null);
307 s.props.insert("onupdateend".into(), Value::Null);
308 s.props.insert("onerror".into(), Value::Null);
309 s.props.insert("onabort".into(), Value::Null);
310 s.props.insert("_data".into(), Value::Object(Obj::array(alloc::vec![])));
311 }
312 Value::Object(sb)
313}
314
315pub(crate) fn source_buffer_append_buffer(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
317 let data_val = arg(a, 0);
318 if let Value::Object(sb_obj) = &this {
319 let (updating, id, mode_str, ts_offset, win_start, win_end) = {
320 let s = sb_obj.borrow();
321 let u = s.props.get("updating").map(|v| v.truthy()).unwrap_or(false);
322 let id = s.props.get("_sb_id").map_or(0, |v| v.to_number() as usize);
323 let mode = s.props.get("mode").map(|v| v.to_js_string()).unwrap_or_else(|| "segments".into());
324 let tso = s.props.get("timestampOffset").map_or(0.0, |v| v.to_number());
325 let ws = s.props.get("appendWindowStart").map_or(0.0, |v| v.to_number());
326 let we = s.props.get("appendWindowEnd").map_or(f64::INFINITY, |v| v.to_number());
327 (u, id, mode, tso, ws, we)
328 };
329 if updating {
330 return Err(it.type_error("Cannot appendBuffer while SourceBuffer is updating"));
331 }
332
333 let mut raw: Vec<f64> = Vec::new();
335 super::typed_array::blob_part_bytes(&data_val, &mut raw);
336 let bytes: Vec<u8> = raw.iter().map(|&v| v as i64 as u8).collect();
337 dispatch_mse_event(it, &this, "updatestart");
338 let result = {
339 let mut list = SOURCE_BUFFERS.lock();
340 match list.iter_mut().find(|b| b.id == id) {
341 Some(core) => {
342 let _ = core.set_mode(&mode_str);
343 let _ = core.set_timestamp_offset(ts_offset);
344 if win_end > core.append_window_start {
345 let _ = core.set_append_window_end(win_end);
346 let _ = core.set_append_window_start(win_start);
347 } else {
348 let _ = core.set_append_window_start(win_start);
349 let _ = core.set_append_window_end(win_end);
350 }
351 core.append_buffer(&bytes)
352 .map(|_| (core.buffered.clone(), core.timestamp_offset))
353 }
354 None => Err(crate::os_lib::mse::MseError::BufferNotFound),
355 }
356 };
357 let ok = match result {
362 Ok((tr, new_ts_offset)) => {
363 let tr_obj = make_time_ranges_object(&tr);
364 let mut s = sb_obj.borrow_mut();
365 s.props.insert("buffered".into(), tr_obj);
366 s.props.insert("timestampOffset".into(), Value::Number(new_ts_offset));
367 true
368 }
369 Err(e) => {
370 crate::warn!("[MSE] appendBuffer failed ({} bytes): {:?}", bytes.len(), e);
371 false
372 }
373 };
374 sb_obj.borrow_mut().props.insert("updating".into(), Value::Bool(true));
375 schedule_sb_completion(it, &this, ok);
376 return Ok(Value::Undefined);
377 }
378 Err(it.type_error("appendBuffer called on non-SourceBuffer"))
379}
380
381fn schedule_sb_completion(it: &mut Interp, sb: &Value, ok: bool) {
383 let cb = nv("SourceBuffer update completion", sb_completion_task);
384 let _ = super::timers_gen::set_timeout(it, Value::Undefined, &[cb, Value::Number(0.0), sb.clone(), Value::Bool(ok)]);
385}
386
387fn sb_completion_task(it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
388 let sb = arg(a, 0);
389 let ok = arg(a, 1).truthy();
390 if let Value::Object(o) = &sb {
391 o.borrow_mut().props.insert("updating".into(), Value::Bool(false));
392 }
393 if ok {
394 dispatch_mse_event(it, &sb, "update");
395 } else {
396 dispatch_mse_event(it, &sb, "error");
397 }
398 dispatch_mse_event(it, &sb, "updateend");
399 fire_media_ready_events(it);
400 Ok(Value::Undefined)
401}
402
403fn fire_media_ready_events(it: &mut Interp) {
408 let targets: Vec<(usize, String, u8)> = {
409 let dom = it.dom.borrow();
410 dom.nodes
411 .iter()
412 .enumerate()
413 .filter(|(_, n)| n.tag == "video" || n.tag == "audio")
414 .filter_map(|(i, n)| {
415 let src = n.attrs.get("src")?;
416 if !src.starts_with("blob:mediasource") {
417 return None;
418 }
419 let prev = n.attrs.get("_rs_fired").and_then(|v| v.parse::<u8>().ok()).unwrap_or(0);
420 Some((i, src.clone(), prev))
421 })
422 .collect()
423 };
424 for (idx, src, prev) in targets {
425 let rs = media_source_ready_state(&src).unwrap_or(0);
426 if rs >= 1 && prev < 1 {
427 it.dom.borrow_mut().set_attr(idx, "_rs_fired", "1");
428 let _ = it.dispatch_event_in_interp(idx, "loadedmetadata", &[]);
429 let _ = it.dispatch_event_in_interp(idx, "durationchange", &[]);
430 }
431 if rs >= 4 && prev < 4 {
432 it.dom.borrow_mut().set_attr(idx, "_rs_fired", "4");
433 let _ = it.dispatch_event_in_interp(idx, "loadeddata", &[]);
434 let _ = it.dispatch_event_in_interp(idx, "canplay", &[]);
435 let _ = it.dispatch_event_in_interp(idx, "canplaythrough", &[]);
436 }
437 }
438}
439
440pub(crate) fn source_buffer_change_type(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
442 let mime = arg(a, 0).to_js_string();
443 if mime.trim().is_empty() {
444 return Err(it.type_error("changeType requires a non-empty MIME type"));
445 }
446 if let Value::Object(sb_obj) = &this {
447 let (updating, id) = {
448 let s = sb_obj.borrow();
449 let u = s.props.get("updating").map(|v| v.truthy()).unwrap_or(false);
450 let id = s.props.get("_sb_id").map_or(0, |v| v.to_number() as usize);
451 (u, id)
452 };
453 if updating {
454 return Err(it.type_error("Cannot changeType while SourceBuffer is updating"));
455 }
456 let res = {
457 let mut list = SOURCE_BUFFERS.lock();
458 match list.iter_mut().find(|b| b.id == id) {
459 Some(core) => core.change_type(&mime),
460 None => {
461 if MediaSourceCore::is_type_supported(&mime) {
462 Ok(())
463 } else {
464 Err(crate::os_lib::mse::MseError::UnsupportedType)
465 }
466 }
467 }
468 };
469 match res {
470 Ok(()) => {
471 sb_obj.borrow_mut().props.insert("mimeType".into(), Value::str(&mime));
472 return Ok(Value::Undefined);
473 }
474 Err(_) => {
475 return Err(it.type_error(alloc::format!("Unsupported MIME type for changeType: {}", mime)));
476 }
477 }
478 }
479 Err(it.type_error("changeType called on non-SourceBuffer"))
480}
481
482pub(crate) fn source_buffer_abort(it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
483 if let Value::Object(sb_obj) = &this {
484 let (was_updating, id) = {
485 let mut s = sb_obj.borrow_mut();
486 let u = s.props.get("updating").map(|v| v.truthy()).unwrap_or(false);
487 let id = s.props.get("_sb_id").map_or(0, |v| v.to_number() as usize);
488 s.props.insert("updating".into(), Value::Bool(false));
489 (u, id)
490 };
491 if let Some(core) = SOURCE_BUFFERS.lock().iter_mut().find(|b| b.id == id) {
492 core.abort();
493 }
494 if was_updating {
495 dispatch_mse_event(it, &this, "abort");
496 dispatch_mse_event(it, &this, "updateend");
497 }
498 }
499 Ok(Value::Undefined)
500}
501
502pub(crate) fn source_buffer_remove(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
504 let (start, end) = (arg(a, 0).to_number(), arg(a, 1).to_number());
505 if let Value::Object(sb_obj) = &this {
506 let id = sb_obj.borrow().props.get("_sb_id").map_or(0, |v| v.to_number() as usize);
507 let tr = {
508 let mut list = SOURCE_BUFFERS.lock();
509 list.iter_mut().find(|b| b.id == id).map(|core| {
510 core.remove_range(start, end);
511 core.buffered.clone()
512 })
513 };
514 sb_obj
515 .borrow_mut()
516 .props
517 .insert("buffered".into(), make_time_ranges_object(&tr.unwrap_or_default()));
518 sb_obj.borrow_mut().props.insert("updating".into(), Value::Bool(true));
520 schedule_sb_completion(it, &this, true);
521 }
522 Ok(Value::Undefined)
523}
524
525fn make_time_ranges_object(tr: &TimeRanges) -> Value {
527 let obj = Obj::plain();
528 let starts = Obj::array(tr.ranges.iter().map(|r| Value::Number(r.start)).collect());
529 let ends = Obj::array(tr.ranges.iter().map(|r| Value::Number(r.end)).collect());
530 {
531 let mut o = obj.borrow_mut();
532 o.props.insert("length".into(), Value::Number(tr.len() as f64));
533 o.props.insert("_starts".into(), Value::Object(starts));
534 o.props.insert("_ends".into(), Value::Object(ends));
535 o.props.insert("start".into(), nv("TimeRanges.start", time_ranges_start));
536 o.props.insert("end".into(), nv("TimeRanges.end", time_ranges_end));
537 }
538 Value::Object(obj)
539}
540
541pub(crate) fn time_ranges_start(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
542 let idx = arg(a, 0).to_number() as usize;
543 if let Value::Object(o) = &this {
544 if let Some(Value::Object(arr)) = o.borrow().props.get("_starts") {
545 if let ObjKind::Array(ref vec) = arr.borrow().kind {
546 if let Some(v) = vec.get(idx) {
547 return Ok(v.clone());
548 }
549 }
550 }
551 }
552 Ok(Value::Undefined)
553}
554
555pub(crate) fn time_ranges_end(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
556 let idx = arg(a, 0).to_number() as usize;
557 if let Value::Object(o) = &this {
558 if let Some(Value::Object(arr)) = o.borrow().props.get("_ends") {
559 if let ObjKind::Array(ref vec) = arr.borrow().kind {
560 if let Some(v) = vec.get(idx) {
561 return Ok(v.clone());
562 }
563 }
564 }
565 }
566 Ok(Value::Undefined)
567}