diff options
author | metamuffin <metamuffin@disroot.org> | 2023-10-23 21:41:57 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-10-23 21:41:57 +0200 |
commit | e7096a499a6e2a8a05f0991898f75da9c928dd70 (patch) | |
tree | cb2428b9e2619961273b19e66b8f2c3e1bdfbc8d /web/script | |
parent | 13a6887415547f5e3bafec80d5088ff90ae94aa6 (diff) | |
download | jellything-e7096a499a6e2a8a05f0991898f75da9c928dd70.tar jellything-e7096a499a6e2a8a05f0991898f75da9c928dd70.tar.bz2 jellything-e7096a499a6e2a8a05f0991898f75da9c928dd70.tar.zst |
player: do actual cap check to see if transcode is required
Diffstat (limited to 'web/script')
-rw-r--r-- | web/script/player/mediacaps.ts | 66 | ||||
-rw-r--r-- | web/script/player/profiles.ts | 19 | ||||
-rw-r--r-- | web/script/player/track.ts | 2 |
3 files changed, 72 insertions, 15 deletions
diff --git a/web/script/player/mediacaps.ts b/web/script/player/mediacaps.ts new file mode 100644 index 0000000..ff143c0 --- /dev/null +++ b/web/script/player/mediacaps.ts @@ -0,0 +1,66 @@ +import { SourceTrack, SourceTrackKind } from "./jhls.d.ts"; + +const cache = new Map<string, boolean>() + +// TODO this testing method makes the assumption, that if the codec is supported on its own, it can be +// TODO arbitrarly combined with others that are supported. in reality this is true but the spec does not gurantee it. + +export async function test_media_capability(track: SourceTrack): Promise<boolean> { + const cache_key = `${get_track_kind(track.kind)};${track.codec}` + const cached = cache.get(cache_key); + if (cached !== undefined) return cached + const r = await test_media_capability_inner(track) + console.log(`${r ? "positive" : "negative"} media capability test finished for codec=${track.codec}`); + cache.set(cache_key, r) + return r +} +async function test_media_capability_inner(track: SourceTrack) { + if (track.kind.subtitles) { + return track.codec == "V_TEXT/WEBVTT" // TODO: actually implement it + } + let res; + const codec = MASTROSKA_CODEC_MAP[track.codec] + if (!codec) return console.warn(`unknown codec: ${track.codec}`), false + if (track.kind.audio) { + res = await navigator.mediaCapabilities.decodingInfo({ + type: "media-source", + audio: { + contentType: `audio/webm^; codecs=${codec}`, + samplerate: track.kind.audio.sample_rate, + channels: "" + track.kind.audio.channels, + bitrate: 128 * 1000, + } + }) + } + if (track.kind.video) { + res = await navigator.mediaCapabilities.decodingInfo({ + type: "media-source", + video: { + contentType: `video/webm; codecs=${codec}`, + framerate: track.kind.video.fps || 30, + width: track.kind.video.width, + height: track.kind.video.height, + bitrate: 5 * 1000 * 1000 // TODO we dont know this but we should in the future + } + }) + } + return res?.supported ?? false +} + +const MASTROSKA_CODEC_MAP: { [key: string]: string } = { + "V_VP9": "vp9", + "V_VP8": "vp8", + "V_AV1": "av1", + "V_MPEG4/ISO/AVC": "h264", + "V_MPEGH/ISO/HEVC": "h265", + "A_OPUS": "opus", + "A_VORBIS": "vorbis", + "S_TEXT/WEBVTT": "webvtt", +} + +export function get_track_kind(track: SourceTrackKind): "audio" | "video" | "subtitles" { + if (track.audio) return "audio" + if (track.video) return "video" + if (track.subtitles) return "subtitles" + throw new Error("invalid track"); +} diff --git a/web/script/player/profiles.ts b/web/script/player/profiles.ts index adaa717..caa46bd 100644 --- a/web/script/player/profiles.ts +++ b/web/script/player/profiles.ts @@ -1,5 +1,6 @@ import { OVar } from "../jshelper/mod.ts"; import { EncodingProfile, JhlsMetadata } from "./jhls.d.ts"; +import { test_media_capability } from "./mediacaps.ts"; import { Player } from "./player.ts"; const PROFILE_UP_FAC = 0.6 @@ -33,23 +34,13 @@ export class ProfileSelector { if (i.subtitles) return this.profiles_subtitles return [] } - remux_supported(track: number): boolean { - // TODO use media capability API - // like so: await navigator.mediaCapabilities.decodingInfo({type:"file",video:{contentType:"video/webm; codecs=av1",bitrate:5*1000*1000/8,framerate:60,height:1080,width:1920}}) - const codec = this.metadata.tracks[track].info.codec - return [ - "V_AV1", - "V_VP8", - "V_VP9", - "A_OPUS", - "A_VORBIS", - "S_TEXT/WEBVTT" - ].includes(codec) + async remux_supported(track: number): Promise<boolean> { + return await test_media_capability(this.metadata.tracks[track].info) } - select_optimal_profile(track: number, profile: OVar<EncodingProfileExt | undefined>) { + async select_optimal_profile(track: number, profile: OVar<EncodingProfileExt | undefined>) { const profs = this.profile_list_for_track(track) - const sup_remux = this.remux_supported(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) diff --git a/web/script/player/track.ts b/web/script/player/track.ts index e2d9d85..4173b12 100644 --- a/web/script/player/track.ts +++ b/web/script/player/track.ts @@ -81,7 +81,7 @@ export class PlayerTrack { async load(index: number) { this.loading.add(index) - this.player.profile_selector.select_optimal_profile(this.track_index, this.profile) + await this.player.profile_selector.select_optimal_profile(this.track_index, this.profile) const url = `/n/${encodeURIComponent(this.node_id)}/stream?format=hlsseg&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 => { |