/* 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) 2024 metamuffin */ /// import { EncodingProfile, SourceTrack, SourceTrackKind } from "./jhls.d.ts"; const cache = new Map() // 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 { 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 } export function track_to_content_type(track: SourceTrack): string | undefined { const codec = MASTROSKA_CODEC_MAP[track.codec] if (!codec) return return `${get_track_kind(track.kind)}/webm; codecs="${codec}"` } export function profile_to_partial_track(profile: EncodingProfile): SourceTrack { if (profile.audio) { return { codec: FFMPEG_ENCODER_CODEC_MAP[profile.audio.codec], kind: { audio: { bit_depth: 16, channels: 2, sample_rate: 48000 } }, name: "test audio", language: "en" } } else if (profile.video) { return { codec: FFMPEG_ENCODER_CODEC_MAP[profile.video.codec], kind: { video: { fps: 30, height: 1080, width: 1090 } }, language: "en", name: "test video" } } else if (profile.subtitles) { return { codec: FFMPEG_ENCODER_CODEC_MAP[profile.subtitles.codec], kind: { subtitles: true }, language: "en", name: "test subtitle" } } else throw new Error("unreachable"); } 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", } const FFMPEG_ENCODER_CODEC_MAP: { [key: string]: string } = { "libsvtav1": "V_AV1", "libvpx": "V_VP8", "libvpx-vp9": "V_VP9", "opus": "A_OPUS", "libopus": "A_OPUS", } export type TrackKind = "audio" | "video" | "subtitles" export function get_track_kind(track: SourceTrackKind): TrackKind { if (track.audio) return "audio" if (track.video) return "video" //@ts-ignore // TODO clean this mess up please // TODO why is the subtitle encoded diffenrently sometimes?! if (track == "subtitles" || track["subtitles"]) return "subtitles" throw new Error("invalid track"); }