aboutsummaryrefslogtreecommitdiff
path: root/web/script/player/track.ts
diff options
context:
space:
mode:
Diffstat (limited to 'web/script/player/track.ts')
-rw-r--r--web/script/player/track.ts109
1 files changed, 63 insertions, 46 deletions
diff --git a/web/script/player/track.ts b/web/script/player/track.ts
index 3f1e073..cc21763 100644
--- a/web/script/player/track.ts
+++ b/web/script/player/track.ts
@@ -1,103 +1,120 @@
+import { SourceTrack, TimeRange } from "./jhls.d.ts";
import { OVar } from "../jshelper/mod.ts";
-import { JhlsTrack, TimeRange } from "./jhls.d.ts";
+import { JhlsTrack } from "./jhls.d.ts";
+import { profile_to_partial_track, track_to_content_type } from "./mediacaps.ts";
import { BufferRange, Player } from "./player.ts";
import { EncodingProfileExt } from "./profiles.ts";
-const TARGET_BUFFER_DURATION = 10
-const MIN_BUFFER_DURATION = 1
+export const TARGET_BUFFER_DURATION = 10
+export 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[] = []
- public profile = new OVar<EncodingProfileExt | undefined>(undefined)
+ private source_buffer!: SourceBuffer;
+ private current_load?: AppendRange;
+ private loading = new Set<number>();
+ public buffered = new OVar<BufferRange[]>([]);
+ private append_queue: AppendRange[] = [];
+ public profile = new OVar<EncodingProfileExt | undefined>(undefined);
+ public static async new(player: Player, node_id: string, track_index: number, metadata: JhlsTrack) {
+ const t = new PlayerTrack(player, node_id, track_index, metadata)
+ await t.init()
+ return t
+ }
constructor(
private player: Player,
private node_id: string,
private track_index: number,
private metadata: JhlsTrack
- ) {
- this.source_buffer = this.player.media_source.addSourceBuffer("video/webm; codecs=\"opus,vorbis,vp8,vp9,av1\"")
- this.source_buffer.mode = "segments"
+ ) { }
+ async init() {
+ await this.player.profile_selector.select_optimal_profile(this.track_index, this.profile);
+ const ct = track_to_content_type(this.track_from_profile())!
+ console.log("source buffer content-type: " + ct);
+ this.source_buffer = this.player.media_source.addSourceBuffer(ct);
+ 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.current_load.cb();
+ this.loading.delete(this.current_load.index);
+ this.current_load = undefined;
}
- this.update_buf_ranges()
- this.tick_append()
- })
+ this.update_buf_ranges();
+ this.tick_append();
+ });
this.source_buffer.addEventListener("error", e => {
console.error("sourcebuffer error", e);
- })
+ });
this.source_buffer.addEventListener("abort", e => {
console.error("sourcebuffer abort", e);
- })
+ });
+ }
+
+ track_from_profile(): SourceTrack {
+ if (this.profile.value) return profile_to_partial_track(this.profile.value)
+ else return this.metadata.info
}
update_buf_ranges() {
- const ranges: BufferRange[] = []
+ 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" })
+ ranges.push({ ...this.metadata.segments[r], status: "loading" });
}
- this.buffered.value = ranges
+ this.buffered.value = ranges;
}
async update(target: number) {
- this.update_buf_ranges() // TODO required?
+ this.update_buf_ranges(); // TODO required?
- const blocking = []
+ 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.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))
+ blocking.push(this.load(i));
else
- this.load(i)
+ this.load(i);
}
- await Promise.all(blocking)
+ await Promise.all(blocking);
}
check_buf_collision(start: number, end: number) {
- const EPSILON = 0.01
+ const EPSILON = 0.01;
for (const r of this.buffered.value)
if (r.end - EPSILON > start && r.start < end - EPSILON)
- return false
- return true
+ return false;
+ return true;
}
async load(index: number) {
- this.loading.add(index)
- await this.player.profile_selector.select_optimal_profile(this.track_index, this.profile)
+ this.loading.add(index);
+ await this.player.profile_selector.select_optimal_profile(this.track_index, this.profile);
const url = `/n/${encodeURIComponent(this.node_id)}/stream?format=hlsseg&webm=true&tracks=${this.track_index}&index=${index}${this.profile.value ? `&profile=${this.profile.value.id}` : ""}`;
- const buf = await this.player.downloader.download(url)
+ const buf = await this.player.downloader.download(url);
await new Promise<void>(cb => {
- this.append_queue.push({ buf, ...this.metadata.segments[index], index, cb })
- this.tick_append()
- })
+ this.append_queue.push({ buf, ...this.metadata.segments[index], index, cb });
+ this.tick_append();
+ });
}
tick_append() {
- if (this.source_buffer.updating) return
+ if (this.source_buffer.updating) return;
if (this.append_queue.length) {
const seg = this.append_queue[0];
- this.append_queue.splice(0, 1)
- this.current_load = seg
+ this.append_queue.splice(0, 1);
+ this.current_load = seg;
// TODO why is appending so unreliable?! sometimes it does not add it
- this.source_buffer.changeType("video/webm; codecs=\"opus,vorbis,vp8,vp9,av1\"")
- this.source_buffer.timestampOffset = seg.start
+ this.source_buffer.changeType(track_to_content_type(this.track_from_profile())!);
+ this.source_buffer.timestampOffset = seg.start;
this.source_buffer.appendBuffer(seg.buf);
}