fix(web): detect HDR via video-dynamic-range media query for Firefox (#259)
* fix(web): detect HDR via video-dynamic-range media query for Firefox Firefox's (dynamic-range: high) query reflects the browser canvas and reports standard even on HDR-enabled displays, so the player capability check always sent hdr=false. Firefox 116+ exposes the video plane via (video-dynamic-range: high); accept either query. Unknown media features parse as non-matching, so the extra query is safe in other browsers. Closes #206 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(web): keep Window receiver when passing matchMedia to HDR helper Invoking a detached matchMedia reference throws (Illegal invocation / TypeError) in some browsers, crashing capability detection instead of reporting HDR. Wrap it in an arrow so the Window receiver is preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
co-authored by
Claude Opus 4.8
parent
57fbc1e0b4
commit
18e9856227
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { detectMaxResolutionFromScreen } from "./useCodecDetection";
|
||||
import { detectHDRFromMatchMedia, detectMaxResolutionFromScreen } from "./useCodecDetection";
|
||||
|
||||
describe("detectMaxResolutionFromScreen", () => {
|
||||
it("treats a 2560x1440 display as above the 720p bucket", () => {
|
||||
@@ -10,3 +10,24 @@ describe("detectMaxResolutionFromScreen", () => {
|
||||
expect(detectMaxResolutionFromScreen(1280, 720)).toBe("720p");
|
||||
});
|
||||
});
|
||||
|
||||
describe("detectHDRFromMatchMedia", () => {
|
||||
const fakeMatchMedia = (matching: string[]) =>
|
||||
((query: string) => ({ matches: matching.includes(query) })) as unknown as typeof matchMedia;
|
||||
|
||||
it("returns false when matchMedia is unavailable", () => {
|
||||
expect(detectHDRFromMatchMedia(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true when dynamic-range reports high", () => {
|
||||
expect(detectHDRFromMatchMedia(fakeMatchMedia(["(dynamic-range: high)"]))).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true when only video-dynamic-range reports high (Firefox)", () => {
|
||||
expect(detectHDRFromMatchMedia(fakeMatchMedia(["(video-dynamic-range: high)"]))).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false when neither query matches", () => {
|
||||
expect(detectHDRFromMatchMedia(fakeMatchMedia([]))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,6 +82,21 @@ export function getPlaybackEnvironmentSnapshot(): PlaybackEnvironmentSnapshot |
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects HDR display support (best effort). Firefox's `dynamic-range` query
|
||||
* reflects the browser canvas and reports `standard` even on HDR displays;
|
||||
* the video plane is exposed via `video-dynamic-range` (Firefox 116+), so
|
||||
* accept either. Browsers treat unknown media features as non-matching, so
|
||||
* querying both is safe everywhere.
|
||||
*/
|
||||
export function detectHDRFromMatchMedia(matchMediaFn: typeof matchMedia | undefined): boolean {
|
||||
if (!matchMediaFn) return false;
|
||||
return (
|
||||
matchMediaFn("(dynamic-range: high)").matches ||
|
||||
matchMediaFn("(video-dynamic-range: high)").matches
|
||||
);
|
||||
}
|
||||
|
||||
function testCodec(mimeWithCodec: string): boolean {
|
||||
if (typeof MediaSource === "undefined") return false;
|
||||
try {
|
||||
@@ -170,8 +185,11 @@ export function useCodecDetection() {
|
||||
// Detect max resolution via reported screen dimensions.
|
||||
const max_resolution = detectMaxResolutionFromScreen(screen.width, screen.height);
|
||||
|
||||
// HDR detection (best effort).
|
||||
const hdr = typeof matchMedia !== "undefined" && matchMedia("(dynamic-range: high)").matches;
|
||||
// HDR detection (best effort). Wrap matchMedia so it keeps its Window
|
||||
// receiver — invoking a detached reference throws in some browsers.
|
||||
const hdr = detectHDRFromMatchMedia(
|
||||
typeof matchMedia !== "undefined" ? (query) => matchMedia(query) : undefined,
|
||||
);
|
||||
|
||||
return { codecs_video, codecs_audio, containers, max_resolution, hdr };
|
||||
}, []);
|
||||
|
||||
Reference in New Issue
Block a user