aboutsummaryrefslogtreecommitdiff
path: root/web/script/player/track
diff options
context:
space:
mode:
Diffstat (limited to 'web/script/player/track')
-rw-r--r--web/script/player/track/create.ts10
-rw-r--r--web/script/player/track/mse.ts44
-rw-r--r--web/script/player/track/vtt.ts52
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 {