diff options
Diffstat (limited to 'web/script/player/track')
-rw-r--r-- | web/script/player/track/create.ts | 10 | ||||
-rw-r--r-- | web/script/player/track/mse.ts | 44 | ||||
-rw-r--r-- | web/script/player/track/vtt.ts | 52 |
3 files changed, 57 insertions, 49 deletions
diff --git a/web/script/player/track/create.ts b/web/script/player/track/create.ts index f674c3a..d63a9ce 100644 --- a/web/script/player/track/create.ts +++ b/web/script/player/track/create.ts @@ -1,12 +1,12 @@ import { get_track_kind } from "../mediacaps.ts"; -import { create_vtt_track } from "./vtt.ts"; -import { create_mse_track } from "./mse.ts"; +import { VttPlayerTrack } from "./vtt.ts"; +import { MSEPlayerTrack } from "./mse.ts"; import { Player } from "../player.ts"; import { SourceTrack } from "../jhls.d.ts"; import { PlayerTrack } from "./mod.ts"; -export async function create_track(player: Player, node_id: string, track_index: number, metadata: SourceTrack): Promise<PlayerTrack | undefined> { +export function create_track(player: Player, node_id: string, track_index: number, metadata: SourceTrack): PlayerTrack | undefined { const kind = get_track_kind(metadata.kind) - if (kind == "subtitles") return await create_vtt_track(player, node_id, track_index, metadata) - else return await create_mse_track(player, node_id, track_index, metadata) + if (kind == "subtitles") return new VttPlayerTrack(player, node_id, track_index, metadata) + else return new MSEPlayerTrack(player, node_id, track_index, metadata) } diff --git a/web/script/player/track/mse.ts b/web/script/player/track/mse.ts index 2d17b87..a4320b5 100644 --- a/web/script/player/track/mse.ts +++ b/web/script/player/track/mse.ts @@ -5,25 +5,6 @@ import { BufferRange, Player } from "../player.ts"; import { EncodingProfileExt, ProfileSelector } from "../profiles.ts"; import { PlayerTrack, AppendRange, TARGET_BUFFER_DURATION, MIN_BUFFER_DURATION } from "./mod.ts"; -export async function create_mse_track(player: Player, node_id: string, track_index: number, metadata: SourceTrack): Promise<MSEPlayerTrack | undefined> { - try { - const res = await fetch(`/n/${encodeURIComponent(player.node_id)}/stream?format=jhlsi&tracks=${track_index}`, { headers: { "Accept": "application/json" } }); - if (!res.ok) return player.error.value = "Cannot download index.", undefined; - let index!: JhlsTrackIndex & { error: string; }; - try { index = await res.json(); } - catch (_) { player.set_pers("Error: Failed to fetch node"); } - if (index.error) return player.set_pers("server error: " + index.error), undefined; - - const t = new MSEPlayerTrack(player, node_id, track_index, metadata, index); - await t.init(); - return t; - } catch (e) { - if (e instanceof TypeError) { - player.set_pers("Cannot download index: Network Error"); - } else throw e; - } -} - export class MSEPlayerTrack extends PlayerTrack { public source_buffer!: SourceBuffer; private current_load?: AppendRange; @@ -31,18 +12,36 @@ export class MSEPlayerTrack extends PlayerTrack { private append_queue: AppendRange[] = []; public profile_selector: ProfileSelector; public profile = new OVar<EncodingProfileExt | undefined>(undefined); + public index?: JhlsTrackIndex constructor( private player: Player, private node_id: string, track_index: number, private metadata: SourceTrack, - public index: JhlsTrackIndex ) { super(track_index); this.profile_selector = new ProfileSelector(player, this, player.downloader.bandwidth); + this.init() } + async init() { + this.buffered.value = [{ start: 0, end: this.player.duration.value, status: "loading" }] + try { + const res = await fetch(`/n/${encodeURIComponent(this.node_id)}/stream?format=jhlsi&tracks=${this.track_index}`, { headers: { "Accept": "application/json" } }); + if (!res.ok) return this.player.error.value = "Cannot download index.", undefined; + let index!: JhlsTrackIndex & { error: string; }; + try { index = await res.json(); } + catch (_) { this.player.set_pers("Error: Failed to fetch node"); } + if (index.error) return this.player.set_pers("server error: " + index.error), undefined; + this.index = index + } catch (e) { + if (e instanceof TypeError) { + this.player.set_pers("Cannot download index: Network Error"); + } else throw e; + } + this.buffered.value = [] + await this.profile_selector.select_optimal_profile(this.track_index, this.profile); const ct = track_to_content_type(this.track_from_profile())!; console.log(`track ${this.track_index} source buffer content-type: ${ct}`); @@ -71,6 +70,8 @@ export class MSEPlayerTrack extends PlayerTrack { this.source_buffer.addEventListener("abort", e => { console.error("sourcebuffer abort", e); }); + + this.update(this.player.video.currentTime) } track_from_profile(): SourceTrack { if (this.profile.value) return profile_to_partial_track(this.profile.value); @@ -78,6 +79,7 @@ export class MSEPlayerTrack extends PlayerTrack { } update_buf_ranges() { + if (!this.index) return; const ranges: BufferRange[] = []; for (let i = 0; i < this.source_buffer.buffered.length; i++) { ranges.push({ @@ -93,6 +95,7 @@ export class MSEPlayerTrack extends PlayerTrack { } async update(target: number) { + if (!this.index) return; this.update_buf_ranges(); // TODO required? const blocking = []; @@ -122,6 +125,7 @@ export class MSEPlayerTrack extends PlayerTrack { const url = `/n/${encodeURIComponent(this.node_id)}/stream?format=snippet&webm=true&tracks=${this.track_index}&index=${index}${this.profile.value ? `&profile=${this.profile.value.id}` : ""}`; const buf = await this.player.downloader.download(url); await new Promise<void>(cb => { + if (!this.index) return; if (this.abort.signal.aborted) return; this.append_queue.push({ buf, ...this.index.segments[index], index, cb }); this.tick_append(); diff --git a/web/script/player/track/vtt.ts b/web/script/player/track/vtt.ts index efdafe0..4dddbc7 100644 --- a/web/script/player/track/vtt.ts +++ b/web/script/player/track/vtt.ts @@ -2,40 +2,26 @@ import { SourceTrack, JvttCue } from "../jhls.d.ts"; import { Player } from "../player.ts"; import { PlayerTrack } from "./mod.ts"; -export async function create_vtt_track(player: Player, node_id: string, track_index: number, metadata: SourceTrack): Promise<VttPlayerTrack | undefined> { - let index: JvttCue[]; - try { - const res = await fetch(`/n/${encodeURIComponent(player.node_id)}/stream?format=jvtt&tracks=${track_index}`, { headers: { "Accept": "application/json" } }); - if (!res.ok) return player.error.value = "Cannot download index.", undefined; - let ai!: JvttCue[] & { error: string; }; - try { ai = await res.json(); } - catch (_) { player.set_pers("Error: Failed to fetch node"); } - if (ai.error) return player.set_pers("server error: " + ai.error), undefined; - index = ai; - } catch (e) { - if (e instanceof TypeError) { - player.set_pers("Cannot download subtitles: Network Error"); - return undefined - } else throw e; - } - const t = new VttPlayerTrack(player, node_id, track_index, metadata, index); - return t; -} - export class VttPlayerTrack extends PlayerTrack { private track: TextTrack; + public cues?: JvttCue[] constructor( private player: Player, private node_id: string, track_index: number, private metadata: SourceTrack, - public cues: JvttCue[] ) { super(track_index); - this.buffered.value = [{ start: 0, end: player.duration.value, status: "buffered" }] - this.track = this.player.video.addTextTrack("subtitles", metadata.name, metadata.language); - for (const cue of cues) { + this.track = this.player.video.addTextTrack("subtitles", this.metadata.name, this.metadata.language); + this.buffered.value = [{ start: 0, end: this.player.duration.value, status: "loading" }] + this.init() + } + + private on_ready() { + if (!this.cues) return + this.buffered.value = [{ start: 0, end: this.player.duration.value, status: "buffered" }] + for (const cue of this.cues) { this.track.addCue(create_cue(cue)); } this.track.mode = "showing"; @@ -44,6 +30,24 @@ export class VttPlayerTrack extends PlayerTrack { this.track.mode = "hidden"; }); } + + async init() { + try { + const res = await fetch(`/n/${encodeURIComponent(this.node_id)}/stream?format=jvtt&tracks=${this.track_index}`, { headers: { "Accept": "application/json" } }); + if (!res.ok) return this.player.error.value = "Cannot download index.", undefined; + let ai!: JvttCue[] & { error: string; }; + try { ai = await res.json(); } + catch (_) { this.player.set_pers("Error: Failed to fetch node"); } + if (ai.error) return this.player.set_pers("server error: " + ai.error), undefined; + this.cues = ai; + } catch (e) { + if (e instanceof TypeError) { + this.player.set_pers("Cannot download subtitles: Network Error"); + return undefined + } else throw e; + } + this.on_ready() + } } function create_cue(cue: JvttCue): VTTCue { |