diff options
author | metamuffin <metamuffin@disroot.org> | 2023-10-02 20:07:09 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-10-02 20:07:09 +0200 |
commit | f783143b4adf22be662db1af2ca00b34a868cf72 (patch) | |
tree | 180ea44b0f7e7052231643da6e929b1be5f8def1 /web/script/player/track.ts | |
parent | e25beb3e0c2531b09d8efd70e858396dcc631dd2 (diff) | |
download | jellything-f783143b4adf22be662db1af2ca00b34a868cf72.tar jellything-f783143b4adf22be662db1af2ca00b34a868cf72.tar.bz2 jellything-f783143b4adf22be662db1af2ca00b34a868cf72.tar.zst |
player: split files
Diffstat (limited to 'web/script/player/track.ts')
-rw-r--r-- | web/script/player/track.ts | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/web/script/player/track.ts b/web/script/player/track.ts new file mode 100644 index 0000000..b089932 --- /dev/null +++ b/web/script/player/track.ts @@ -0,0 +1,92 @@ +import { OVar } from "../jshelper/mod.ts"; +import { JhlsTrack, TimeRange } from "./jhls.d.ts"; +import { BufferRange } from "./player.ts"; + +const TARGET_BUFFER_DURATION = 15 +const MIN_BUFFER_DURATION = 1 + +export interface AppendRange extends TimeRange { buf: ArrayBuffer, index: number, cb: () => void } +export class PlayerTrack { + private source_buffer: SourceBuffer + private current_load?: AppendRange + private loading = new Set<number>() + public buffered = new OVar<BufferRange[]>([]) + private append_queue: AppendRange[] = [] + constructor(media_source: MediaSource, private node_id: string, private track_index: number, private metadata: JhlsTrack) { + this.source_buffer = media_source.addSourceBuffer("video/webm") + this.source_buffer.mode = "segments" + this.source_buffer.addEventListener("updateend", () => { + if (this.current_load) { + this.current_load.cb() + this.loading.delete(this.current_load.index) + this.current_load = undefined + } + this.update_buf_ranges() + this.tick_append() + }) + this.source_buffer.addEventListener("error", e => { + console.error("sourcebuffer error", e); + }) + } + + update_buf_ranges() { + const ranges: BufferRange[] = [] + for (let i = 0; i < this.source_buffer.buffered.length; i++) { + ranges.push({ + start: this.source_buffer.buffered.start(i), + end: this.source_buffer.buffered.end(i), + status: "buffered" + }) + } + for (const r of this.loading) { + ranges.push({ ...this.metadata.segments[r], status: "loading" }) + } + this.buffered.value = ranges + } + + async update(target: number) { + this.update_buf_ranges() // TODO required? + + const blocking = [] + for (let i = 0; i < this.metadata.segments.length; i++) { + const seg = this.metadata.segments[i]; + if (seg.end < target) continue + if (seg.start >= target + TARGET_BUFFER_DURATION) break + if (!this.check_buf_collision(seg.start, seg.end)) continue + if (seg.start <= target + MIN_BUFFER_DURATION) + blocking.push(this.load(i)) + else + this.load(i) + } + await Promise.all(blocking) + } + check_buf_collision(start: number, end: number) { + const EPSILON = 0.01 + for (const r of this.buffered.value) + if (r.end - EPSILON > start && r.start < end - EPSILON) + return false + return true + } + + async load(index: number) { + this.loading.add(index) + const res = await fetch(`/n/${encodeURIComponent(this.node_id)}/stream?format=hlsseg&tracks=${this.track_index}&index=${index}`) + if (!res.ok) throw new Error(`segment fail i=${index} t=${this.track_index}`); + const buf = await res.arrayBuffer() + + await new Promise<void>(cb => { + this.append_queue.push({ buf, ...this.metadata.segments[index], index, cb }) + this.tick_append() + }) + } + tick_append() { + if (this.source_buffer.updating) return + if (this.append_queue.length) { + const seg = this.append_queue[0]; + this.source_buffer.timestampOffset = seg.start + this.source_buffer.appendBuffer(seg.buf); + this.append_queue.splice(0, 1) + this.current_load = seg + } + } +}
\ No newline at end of file |