aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--web/script/player/mediacaps.ts66
-rw-r--r--web/script/player/profiles.ts19
-rw-r--r--web/script/player/track.ts2
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 => {