aboutsummaryrefslogtreecommitdiff
path: root/web/script/player
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-10-02 21:35:31 +0200
committermetamuffin <metamuffin@disroot.org>2023-10-02 21:35:31 +0200
commit7450134996a94c4629f1e112e90ad458a84c0c04 (patch)
tree0d5e80cc66ccb7da53bf936e2cc063b90a219a1d /web/script/player
parentf783143b4adf22be662db1af2ca00b34a868cf72 (diff)
downloadjellything-7450134996a94c4629f1e112e90ad458a84c0c04.tar
jellything-7450134996a94c4629f1e112e90ad458a84c0c04.tar.bz2
jellything-7450134996a94c4629f1e112e90ad458a84c0c04.tar.zst
estimate bandwidth
Diffstat (limited to 'web/script/player')
-rw-r--r--web/script/player/download.ts42
-rw-r--r--web/script/player/mod.ts19
-rw-r--r--web/script/player/player.ts8
-rw-r--r--web/script/player/track.ts18
4 files changed, 65 insertions, 22 deletions
diff --git a/web/script/player/download.ts b/web/script/player/download.ts
new file mode 100644
index 0000000..63085b8
--- /dev/null
+++ b/web/script/player/download.ts
@@ -0,0 +1,42 @@
+import { OVar } from "../jshelper/mod.ts";
+
+interface Measurement { time: number, duration: number, size: number }
+export class SegmentDownloader {
+ private measurements: Measurement[] = []
+
+ public bandwidth = new OVar(Infinity)
+
+ constructor() { }
+
+ async download(url: string): Promise<ArrayBuffer> {
+ const dl_start = performance.now();
+ const res = await fetch(url)
+ const dl_header = performance.now();
+ if (!res.ok) throw new Error("aaaaa");
+ const buf = await res.arrayBuffer()
+ const dl_body = performance.now();
+
+ if (buf.byteLength > 500 * 1000) {
+ const m = {
+ time: dl_start,
+ duration: (dl_body - dl_header) / 1000,
+ size: buf.byteLength
+ }
+ console.log(m);
+ this.measurements.push(m)
+ this.update_bandwidth()
+ }
+ return buf;
+ }
+
+ update_bandwidth() {
+ while (this.measurements.length > 32)
+ this.measurements.splice(0, 1)
+ const total_size = this.measurements.reduce((a, v) => v.size + a, 0)
+ const total_duration = this.measurements.reduce((a, v) => v.duration + a, 0)
+ const average = total_size / total_duration
+ console.log(total_size, average, this.measurements);
+
+ this.bandwidth.value = average
+ }
+}
diff --git a/web/script/player/mod.ts b/web/script/player/mod.ts
index 02b8a12..bf294aa 100644
--- a/web/script/player/mod.ts
+++ b/web/script/player/mod.ts
@@ -3,6 +3,7 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2023 metamuffin <metamuffin.org>
*/
+import { OVar, show } from "../jshelper/mod.ts";
import { e } from "../jshelper/mod.ts";
import { Player } from "./player.ts";
@@ -20,6 +21,7 @@ function initialize_player(el: HTMLElement, node_id: string) {
el.innerHTML = "" // clear the body
const player = new Player(node_id)
+ const show_stats = new OVar(false);
const toggle_playing = () => player.playing.value ? player.pause() : player.play()
const pri_map = (v: number) => (v / player.duration.value * 100) + "%"
@@ -29,8 +31,8 @@ function initialize_player(el: HTMLElement, node_id: string) {
const controls = e("div", { class: "jsp-controls" },
player.playing.map(playing => e("button", playing ? "||" : "|>", { onclick: toggle_playing })),
e("p", { class: "jsp-status" },
- player.position.map(v => e("span", display_time(v))), e("br"),
- player.position.map(v => e("span", display_time(v - player.duration.value)))
+ player.position.map(v => e("span", show.duration(v))), e("br"),
+ player.position.map(v => e("span", show.duration(v - player.duration.value)))
),
pri = e("div", { class: "jsp-pri" },
pri_current = e("div", { class: "jsp-pri-current" }),
@@ -64,6 +66,9 @@ function initialize_player(el: HTMLElement, node_id: string) {
player.buffering_status.map(b => e("div", { class: "jsp-overlay" },
b ? e("p", { class: "jsp-buffering" }, b) : undefined
)),
+ show_stats.map(do_show => !do_show ? e("div") : e("div", {class: "jsp-stats"},
+ player.downloader.bandwidth.map(b => e("pre", `estimated bandwidth: ${show.byte_size(b)}`))
+ )),
controls,
)
el.append(pel)
@@ -82,6 +87,7 @@ function initialize_player(el: HTMLElement, node_id: string) {
document.body.addEventListener("keydown", k => {
if (k.code == "Period") player.pause(), player.frame_forward()
if (k.code == "Space") toggle_playing()
+ else if (k.code == "KeyV") show_stats.value = !show_stats.value
else if (k.code == "ArrowLeft") player.seek(player.position.value - 5)
else if (k.code == "ArrowRight") player.seek(player.position.value + 5)
else if (k.code == "ArrowUp") player.seek(player.position.value - 60)
@@ -108,12 +114,3 @@ function mouse_idle(e: HTMLElement, timeout: number, cb: (b: boolean) => unknown
}, timeout)
}
}
-function display_time(t: number): string {
- if (t < 0) return "-" + display_time(-t)
- let h = 0, m = 0, s = 0;
- while (t > 3600) t -= 3600, h++;
- while (t > 60) t -= 60, m++;
- while (t > 1) t -= 1, s++;
- return (h ? h + "h" : "") + (m ? m + "m" : "") + (s ? s + "s" : "")
-}
-
diff --git a/web/script/player/player.ts b/web/script/player/player.ts
index 5c38dc8..08975b2 100644
--- a/web/script/player/player.ts
+++ b/web/script/player/player.ts
@@ -1,12 +1,14 @@
import { OVar, e } from "../jshelper/mod.ts";
import { JhlsMetadata, TimeRange } from "./jhls.d.ts";
+import { SegmentDownloader } from "./download.ts";
import { PlayerTrack } from "./track.ts";
export interface BufferRange extends TimeRange { status: "buffered" | "loading" | "queued" }
export class Player {
public video = e("video")
- private media_source = new MediaSource();
+ public media_source = new MediaSource();
public tracks = new OVar<PlayerTrack[]>([]);
+ public downloader: SegmentDownloader = new SegmentDownloader();
public position = new OVar(0)
public duration = new OVar(1)
@@ -66,8 +68,8 @@ export class Player {
this.duration.value = metadata.duration
this.video.src = URL.createObjectURL(this.media_source)
this.media_source.addEventListener("sourceopen", async () => {
- this.tracks.value.push(new PlayerTrack(this.media_source, this.node_id, 0, metadata.tracks[0]))
- this.tracks.value.push(new PlayerTrack(this.media_source, this.node_id, 1, metadata.tracks[1]))
+ this.tracks.value.push(new PlayerTrack(this, this.node_id, 0, metadata.tracks[0]))
+ this.tracks.value.push(new PlayerTrack(this, this.node_id, 1, metadata.tracks[1]))
this.tracks.change()
this.buffering_status.value = "Fetching initial segments..."
this.update()
diff --git a/web/script/player/track.ts b/web/script/player/track.ts
index b089932..e64cdf3 100644
--- a/web/script/player/track.ts
+++ b/web/script/player/track.ts
@@ -1,6 +1,6 @@
import { OVar } from "../jshelper/mod.ts";
import { JhlsTrack, TimeRange } from "./jhls.d.ts";
-import { BufferRange } from "./player.ts";
+import { BufferRange, Player } from "./player.ts";
const TARGET_BUFFER_DURATION = 15
const MIN_BUFFER_DURATION = 1
@@ -12,8 +12,13 @@ export class PlayerTrack {
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")
+ 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")
this.source_buffer.mode = "segments"
this.source_buffer.addEventListener("updateend", () => {
if (this.current_load) {
@@ -70,10 +75,7 @@ export class PlayerTrack {
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()
-
+ const buf = await this.player.downloader.download(`/n/${encodeURIComponent(this.node_id)}/stream?format=hlsseg&tracks=${this.track_index}&index=${index}`)
await new Promise<void>(cb => {
this.append_queue.push({ buf, ...this.metadata.segments[index], index, cb })
this.tick_append()
@@ -89,4 +91,4 @@ export class PlayerTrack {
this.current_load = seg
}
}
-} \ No newline at end of file
+}