aboutsummaryrefslogtreecommitdiff
path: root/web/script/player/mediacaps.ts
blob: b7229046b61f95ece4cee6f9ec9c07bf2cca34f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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");
}