diff options
Diffstat (limited to 'web/script/player/profiles.ts_')
-rw-r--r-- | web/script/player/profiles.ts_ | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/web/script/player/profiles.ts_ b/web/script/player/profiles.ts_ new file mode 100644 index 0000000..943639c --- /dev/null +++ b/web/script/player/profiles.ts_ @@ -0,0 +1,80 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2025 metamuffin <metamuffin.org> +*/ +/// <reference lib="dom" /> +import { OVar } from "../jshelper/mod.ts"; +import { Player } from "./player.ts"; +import { MSEPlayerTrack } from "./track/mse.ts"; + +const PROFILE_UP_FAC = 0.6 +const PROFILE_DOWN_FAC = 0.8 + +export interface EncodingProfileExt extends EncodingProfile { id: number, order: number } +export class ProfileSelector { + profiles: EncodingProfileExt[] = [] + is_init = false + + constructor( + private player: Player, + private track: MSEPlayerTrack, + private bandwidth: OVar<number> + ) { + } + async init() { + for (let id = 0; id < this.track.index!.extra_profiles.length; id++) { + const p = this.track.index!.extra_profiles[id]; + // TODO hacky type casting solution + if (get_track_kind(this.track.trackinfo.kind) != get_track_kind(p as unknown as SourceTrackKind)) continue + if (!await test_media_capability(profile_to_partial_track(p))) continue + this.profiles.push({ id, order: 0, ...p }) + } + this.profiles.sort((a, b) => profile_byterate(b) - profile_byterate(a)) + for (let i = 0; i < this.profiles.length; i++) this.profiles[i].order = i + } + async remux_supported(track: number): Promise<boolean> { + return await test_media_capability(this.player.tracks![track]) + } + async select_optimal_profile(track: number, profile: OVar<EncodingProfileExt | undefined>) { + if (!this.is_init) await this.init(), this.is_init = true; + + const sup_remux = await this.remux_supported(track); + if (!sup_remux && !this.profiles.length) { + this.player.logger?.log("None of the available codecs are supported. This track can't be played back.") + return false + } + const min_prof = sup_remux ? -1 : 0 + const co = profile.value?.order ?? min_prof + // TODO use actual bitrate as a fallback. the server should supply it. + const current_bitrate = profile_byterate(this.profiles[co], 500 * 1000) + const next_bitrate = profile_byterate(this.profiles[co - 1], 500 * 1000) + // console.log({ current_bitrate, next_bitrate, co, bandwidth: this.bandwidth.value * 8 }); + if (!sup_remux && !profile.value) profile.value = this.profiles[co]; + if (current_bitrate > this.bandwidth.value * PROFILE_DOWN_FAC && co + 1 < this.profiles.length) { + console.log("profile up"); + profile.value = this.profiles[co + 1] + this.log_change(track, profile.value) + } + if (next_bitrate < this.bandwidth.value * PROFILE_UP_FAC && co > min_prof) { + console.log("profile down"); + profile.value = this.profiles[co - 1] + this.log_change(track, profile.value) + } + + // profile.value = profs[0] + return true + } + + log_change(track: number, p: EncodingProfileExt | undefined) { + const ps = p ? `transcoding profile ${p.id}` : `remuxed original` + this.player.logger?.log(`Track #${track} switched to ${ps}`) + } +} + +function profile_byterate(p?: EncodingProfile, fallback = 0): number { + if (p?.audio) return p.audio.bitrate / 8 + if (p?.video) return p.video.bitrate / 8 + if (p?.subtitles) return 100 + return fallback +} |