From ec2228f6ee9349c0866483abc21124dae31f2b52 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Wed, 4 Mar 2026 20:11:48 +0100 Subject: use codec param in js player --- remuxer/src/codec_param/mod.rs | 4 ++- stream/src/stream_info.rs | 4 +-- ui/client-scripts/src/player/mediacaps.ts | 38 ++++++++++------------------ ui/client-scripts/src/player/types_stream.ts | 1 + 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/remuxer/src/codec_param/mod.rs b/remuxer/src/codec_param/mod.rs index 8c0b6b7..147fa0d 100644 --- a/remuxer/src/codec_param/mod.rs +++ b/remuxer/src/codec_param/mod.rs @@ -11,7 +11,8 @@ mod av1; mod hevc; pub fn codec_param(te: &TrackEntry) -> String { - let cp = te.codec_private.as_ref().unwrap(); + let empty_cp = vec![]; + let cp = te.codec_private.as_ref().unwrap_or(&empty_cp); match te.codec_id.as_str() { "A_AAC" => format!("mp4a.40.2"), // TODO "A_FLAC" => "flac".to_string(), @@ -21,6 +22,7 @@ pub fn codec_param(te: &TrackEntry) -> String { "V_AV1" => av1_codec_param(cp), "V_MPEG4/ISO/AVC" => format!("avc1.{:02x}{:02x}{:02x}", cp[1], cp[2], cp[3]), "V_MPEGH/ISO/HEVC" => hevc_codec_param(cp), + "V_VP9" => "vp09.00.50.08".to_string(), // TODO x => todo!("{x:?}"), } diff --git a/stream/src/stream_info.rs b/stream/src/stream_info.rs index 1cc1663..adebda8 100644 --- a/stream/src/stream_info.rs +++ b/stream/src/stream_info.rs @@ -149,7 +149,7 @@ fn stream_formats( } } TrackType::Audio => { - for br in [256e3, 128e3, 64e3] { + for br in [128e3, 64e3, 32e3] { formats.push(StreamFormatInfo { codec: "A_OPUS".to_string(), codec_param: "opus".to_string(), @@ -184,7 +184,7 @@ fn containers_by_codec(codec: &str) -> Vec { use StreamContainer::*; match codec { "V_VP8" | "V_VP9" | "V_AV1" | "A_OPUS" | "A_VORBIS" => vec![Matroska, WebM], - "V_MPEG4/ISO/AVC" | "A_AAC" => vec![Matroska, MPEG4], + "V_MPEG4/ISO/AVC" | "V_MPEGH/ISO/HEVC" | "A_AAC" => vec![Matroska, MPEG4], "S_TEXT/UTF8" | "S_TEXT/WEBVTT" => vec![Matroska, WebVTT, WebM, JVTT], _ => vec![Matroska], } diff --git a/ui/client-scripts/src/player/mediacaps.ts b/ui/client-scripts/src/player/mediacaps.ts index 9b0e934..4393fe9 100644 --- a/ui/client-scripts/src/player/mediacaps.ts +++ b/ui/client-scripts/src/player/mediacaps.ts @@ -7,24 +7,22 @@ import { FormatInfo, StreamContainer } from "./types_stream.ts"; -const cache = new Map() +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 type CodecSupport = "unsupported" | "supported" | "supported_smooth" -export async function test_media_capability(format: FormatInfo, container: StreamContainer): Promise { +export async function test_media_capability(format: FormatInfo, container: StreamContainer): Promise { const cache_key = JSON.stringify(format) + container const cached = cache.get(cache_key); if (cached !== undefined) return cached const r = await test_media_capability_inner(format, container) - console.log(`${r ? "positive" : "negative"} media capability test finished for codec=${format.codec}`); + console.log(`media caps: codec=${format.codec} sup=${r}`); cache.set(cache_key, r) return r } -async function test_media_capability_inner(format: FormatInfo, container: StreamContainer) { - if (format.codec.startsWith("S_") || format.codec.startsWith("D_")) { - // TODO do we need to check this? - return format.codec == "S_TEXT/WEBVTT" || format.codec == "S_TEXT/UTF8" || format.codec == "D_WEBVTT/SUBTITLES" +async function test_media_capability_inner(format: FormatInfo, container: StreamContainer): Promise { + if (format.codec == "S_TEXT/WEBVTT" || format.codec == "S_TEXT/UTF8" || format.codec == "D_WEBVTT/SUBTITLES") { + return "supported" } let res; if (format.codec.startsWith("A_")) { @@ -37,39 +35,29 @@ async function test_media_capability_inner(format: FormatInfo, container: Stream bitrate: format.bitrate, } }) - } - if (format.codec.startsWith("V_")) { + } else if (format.codec.startsWith("V_")) { res = await navigator.mediaCapabilities.decodingInfo({ type: "media-source", video: { contentType: track_to_content_type(format, container), - framerate: 30, // TODO get average framerate from server + framerate: format.samplerate ?? 60, width: format.width ?? 1920, height: format.height ?? 1080, bitrate: format.bitrate } }) + } else { + res = { supported: false, smooth: false } } - return res?.supported ?? false + return res.supported ? (res.smooth ? "supported_smooth" : "supported") : "unsupported" } export function track_to_content_type(format: FormatInfo, container: StreamContainer): string { let c = CONTAINER_TO_MIME_TYPE[container]; if (format.codec.startsWith("A_")) c = c.replace("video/", "audio/") - return `${c}; codecs="${MASTROSKA_CODEC_MAP[format.codec]}"` + return `${c}; codecs="${format.codec_param}"` } -const MASTROSKA_CODEC_MAP: { [key: string]: string } = { - "V_VP9": "vp9", - "V_VP8": "vp8", - "V_AV1": "av1", - "V_MPEG4/ISO/AVC": "avc1.42C01F", - "V_MPEGH/ISO/HEVC": "hev1.1.6.L93.90", - "A_OPUS": "opus", - "A_VORBIS": "vorbis", - "S_TEXT/WEBVTT": "webvtt", - "D_WEBVTT/SUBTITLES": "webvtt", -} const CONTAINER_TO_MIME_TYPE: { [key in StreamContainer]: string } = { webvtt: "text/webvtt", webm: "video/webm", diff --git a/ui/client-scripts/src/player/types_stream.ts b/ui/client-scripts/src/player/types_stream.ts index 272f98b..4719335 100644 --- a/ui/client-scripts/src/player/types_stream.ts +++ b/ui/client-scripts/src/player/types_stream.ts @@ -23,6 +23,7 @@ export interface TrackInfo { export type StreamContainer = "webm" | "matroska" | "mpeg4" | "jvtt" | "webvtt" export interface FormatInfo { codec: string, + codec_param: string, bitrate: number, remux: boolean, containers: StreamContainer[] -- cgit v1.3