import { OVar } from "../jshelper/mod.ts"; import { EncodingProfile, JhlsMetadata } from "./jhls.d.ts"; import { profile_to_partial_track, test_media_capability } from "./mediacaps.ts"; import { Player } from "./player.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_video: EncodingProfileExt[] = [] profiles_audio: EncodingProfileExt[] = [] profiles_subtitles: EncodingProfileExt[] = [] remux_bandwidth = new Map() constructor(private player: Player, private bandwidth: OVar, private metadata: JhlsMetadata) { } async init() { for (let id = 0; id < this.metadata.extra_profiles.length; id++) { const p = this.metadata.extra_profiles[id]; if (!await test_media_capability(profile_to_partial_track(p))) continue if (p.audio) this.profiles_audio.push({ id, order: 0, ...p }) if (p.video) this.profiles_video.push({ id, order: 0, ...p }) if (p.subtitles) this.profiles_subtitles.push({ id, order: 0, ...p }) } this.profiles_audio.sort((a, b) => profile_byterate(b) - profile_byterate(a)) this.profiles_video.sort((a, b) => profile_byterate(b) - profile_byterate(a)) this.profiles_subtitles.sort((a, b) => profile_byterate(b) - profile_byterate(a)) for (let i = 0; i < this.profiles_audio.length; i++) this.profiles_audio[i].order = i for (let i = 0; i < this.profiles_video.length; i++) this.profiles_video[i].order = i for (let i = 0; i < this.profiles_subtitles.length; i++) this.profiles_subtitles[i].order = i } profile_list_for_track(track: number): EncodingProfileExt[] { const i = this.metadata.tracks[track].info.kind if (i.audio) return this.profiles_audio if (i.video) return this.profiles_video if (i.subtitles) return this.profiles_subtitles return [] } async remux_supported(track: number): Promise { return await test_media_capability(this.metadata.tracks[track].info) } async select_optimal_profile(track: number, profile: OVar) { const profs = this.profile_list_for_track(track) const sup_remux = await this.remux_supported(track); const min_prof = sup_remux ? -1 : 0 const co = profile.value?.order ?? min_prof const current_bitrate = profile_byterate(profs[co], 5000 * 1000) const next_bitrate = profile_byterate(profs[co - 1], 5000 * 1000) // console.log({ current_bitrate, next_bitrate, co, bandwidth: this.bandwidth.value * 8 }); if (!sup_remux && !profile.value) profile.value = profs[co]; if (current_bitrate > this.bandwidth.value * PROFILE_DOWN_FAC && co + 1 < profs.length) { console.log("profile up"); profile.value = profs[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 = profs[co - 1] this.log_change(track, profile.value) } // profile.value = profs[0] } 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 }