From d1ef54af27d737e88c95921351dd8340114e185b Mon Sep 17 00:00:00 2001 From: core Date: Mon, 1 Jun 2026 16:03:30 +0500 Subject: [PATCH] player: fix HEVC color (BT.709 limited range) The built-in WASM player (frontend/static/vendor/hevcjs/hevc-core.js) rendered washed-out, green/red-shifted video. Two causes, both fixed: - VideoFrame path (Chrome): the I420 VideoFrame was created without a colorSpace, so the browser defaulted to BT.601 limited -> hue shift, and guessed the range -> washout. Set explicit BT.709, fullRange:false. - WebGL fallback shader: used BT.709 FULL-range coefficients on limited-range camera output -> washout/desaturation. Switched to BT.709 limited-range conversion ((Y-16)*255/219, chroma centered 128). VERSION -> 0.0.116. Co-Authored-By: Claude Opus 4.8 (1M context) --- VERSION | 2 +- frontend/static/vendor/hevcjs/hevc-core.js | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/VERSION b/VERSION index f64402b..30f8347 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.115 +0.0.116 diff --git a/frontend/static/vendor/hevcjs/hevc-core.js b/frontend/static/vendor/hevcjs/hevc-core.js index 0101a2b..1508f0f 100644 --- a/frontend/static/vendor/hevcjs/hevc-core.js +++ b/frontend/static/vendor/hevcjs/hevc-core.js @@ -286,7 +286,11 @@ var FrameRenderer = class _FrameRenderer { format: "I420", codedWidth: w, codedHeight: h, - timestamp + timestamp, + // Hikvision H.265 — BT.709, TV/limited range. Без явного colorSpace браузер для + // I420 по умолчанию берёт BT.601 (зелёно-красный сдвиг) и неверный диапазон + // (белёсость). Задаём явно, чтобы YUV→RGB шёл по правильной матрице и диапазону. + colorSpace: { primaries: "bt709", transfer: "bt709", matrix: "bt709", fullRange: false } }); await this._writer.write(videoFrame); videoFrame.close(); @@ -394,12 +398,13 @@ var FRAGMENT_SRC = ` uniform sampler2D u_texCb; uniform sampler2D u_texCr; void main() { - float y = texture2D(u_texY, v_tex).r; - float cb = texture2D(u_texCb, v_tex).r - 0.5; - float cr = texture2D(u_texCr, v_tex).r - 0.5; - float r = y + 1.5748 * cr; - float g = y - 0.1873 * cb - 0.4681 * cr; - float b = y + 1.8556 * cb; + // BT.709 limited range (TV): Y∈[16,235]/255 → (Y-16)*255/219; Cb/Cr∈[16,240]/255 центр 128/255. + float y = (texture2D(u_texY, v_tex).r - 0.0627451) * 1.1643836; + float cb = texture2D(u_texCb, v_tex).r - 0.5019608; + float cr = texture2D(u_texCr, v_tex).r - 0.5019608; + float r = y + 1.7927411 * cr; + float g = y - 0.2132486 * cb - 0.5329093 * cr; + float b = y + 2.1124018 * cb; gl_FragColor = vec4(clamp(r, 0.0, 1.0), clamp(g, 0.0, 1.0), clamp(b, 0.0, 1.0), 1.0); } `;