Video-Poster: Farben falsch umgerechnet (BT.709 als BT.601) #411

Closed
opened 2026-10-11 21:24:18 +02:00 by meleki · 2 comments
Owner

Aus #156, Durchgang 6. Fund in #405, Abschluss 4371 („Betrifft nur das Vorschaubild, nicht angefasst“).

Befund

  • Das Posterbild, das der Worker zu jedem hochgeladenen Video schneidet, ist farblich daneben. Gemessen beim Bau von #405: Ein Rot mit 255 im Video kommt im Poster-JPEG als 229 heraus. Das Video selbst ist richtig, in Chrome dekodiert Rot ≥ 245.
  • Sichtbar ist das Poster nur im Video-Picker (src/editor/video/VideoPicker.ts:57). Die Szene, die PREVIEW und das Spiel zeigen das Video, nicht das Poster.

Vermutete Ursache (gerechnet, nicht gegengeprüft)

  • VideoFormat::posterArgs (api/lib/VideoFormat.php:136) schneidet mit -vf scale=320:-2 und nennt keine Farbmatrix.
  • Die MP4 ist BT.709 (die Aufnahme aus #405 schreibt das in die Datei, ffprobe zeigt es nach der Umwandlung). ffmpeg übernimmt die Y'CbCr-Werte unverändert ins JPEG, ein JPEG wird aber immer mit BT.601 gelesen.
  • Gerechnet: reines Rot in BT.709, mit BT.601 zurückgerechnet, ergibt rund 233. Mit der JPEG-Kompression passt das zu den gemessenen 229.

Vorschlag

  • Beim Poster die Matrix ausdrücklich umrechnen: scale=320:-2:in_color_matrix=bt709:out_color_matrix=bt601:out_range=full. Die Eingangsmatrix kommt aus ffprobe (color_space). Was ohne Angabe gelten soll, ist Frage 2.
  • Bestehende Poster bleiben, wie sie sind. Neu geschnitten werden sie nur, wenn ein Video neu hochgeladen wird.

Offen (mit Vorschlag)

  1. Alte Poster nachziehen? Vorschlag: nein. Es geht nur um das kleine Bild im Picker.
  2. Video ohne Farbangabe: Vorschlag: BT.709 ab 720 px Höhe, darunter BT.601 (übliche Faustregel). Was Chrome in diesem Fall annimmt, ist nicht gemessen. Das gehört beim Bau mit einem Testvideo ohne Angabe geprüft.

Fertig, wenn

  • PHP-Test videos.test.php (Worker-Image, echtes ffmpeg): Ein Testvideo mit reinem Rot ergibt ein Poster mit Rot ≥ 245. Gegenprobe mit den alten Argumenten: unter 235.
Aus #156, Durchgang 6. Fund in #405, Abschluss 4371 („Betrifft nur das Vorschaubild, nicht angefasst“). ## Befund - Das Posterbild, das der Worker zu jedem hochgeladenen Video schneidet, ist farblich daneben. Gemessen beim Bau von #405: Ein Rot mit 255 im Video kommt im Poster-JPEG als **229** heraus. Das Video selbst ist richtig, in Chrome dekodiert Rot ≥ 245. - Sichtbar ist das Poster nur im Video-Picker (`src/editor/video/VideoPicker.ts:57`). Die Szene, die PREVIEW und das Spiel zeigen das Video, nicht das Poster. ## Vermutete Ursache (gerechnet, nicht gegengeprüft) - `VideoFormat::posterArgs` (`api/lib/VideoFormat.php:136`) schneidet mit `-vf scale=320:-2` und nennt keine Farbmatrix. - Die MP4 ist BT.709 (die Aufnahme aus #405 schreibt das in die Datei, ffprobe zeigt es nach der Umwandlung). ffmpeg übernimmt die Y'CbCr-Werte unverändert ins JPEG, ein JPEG wird aber immer mit BT.601 gelesen. - Gerechnet: reines Rot in BT.709, mit BT.601 zurückgerechnet, ergibt rund 233. Mit der JPEG-Kompression passt das zu den gemessenen 229. ## Vorschlag - Beim Poster die Matrix ausdrücklich umrechnen: `scale=320:-2:in_color_matrix=bt709:out_color_matrix=bt601:out_range=full`. Die Eingangsmatrix kommt aus ffprobe (`color_space`). Was ohne Angabe gelten soll, ist Frage 2. - Bestehende Poster bleiben, wie sie sind. Neu geschnitten werden sie nur, wenn ein Video neu hochgeladen wird. ## Offen (mit Vorschlag) 1. **Alte Poster nachziehen?** Vorschlag: **nein.** Es geht nur um das kleine Bild im Picker. 2. **Video ohne Farbangabe:** Vorschlag: BT.709 ab 720 px Höhe, darunter BT.601 (übliche Faustregel). Was Chrome in diesem Fall annimmt, ist nicht gemessen. Das gehört beim Bau mit einem Testvideo ohne Angabe geprüft. ## Fertig, wenn - PHP-Test `videos.test.php` (Worker-Image, echtes ffmpeg): Ein Testvideo mit reinem Rot ergibt ein Poster mit Rot ≥ 245. Gegenprobe mit den alten Argumenten: unter 235.
meleki added the new label 2026-10-11 21:24:18 +02:00
admin added this to the Development project 2026-10-11 21:25:32 +02:00
admin moved this to Next in Development on 2026-10-11 21:25:50 +02:00

vorschläge passen

vorschläge passen
admin removed the new label 2026-10-11 21:27:31 +02:00
Author
Owner

Erledigt

Commits: Repo 31ce648 „Give video posters the colours the video shows (#411)“, Wiki 7b29263 „Videos: posters keep the colours of the video (#411)“.

Was gebaut ist

  • VideoFormat::colour($probe) (neu) liest Matrix und Range aus der fertigen MP4. Der Worker probt dafür die umgewandelte Datei, nicht das Original.
  • VideoFormat::posterArgs rechnet das Poster ausdrücklich von dieser Matrix nach BT.601 Full Range um: scale=320:-2:in_color_matrix=…:in_range=…:out_color_matrix=bt601:out_range=pc:flags=accurate_rnd+full_chroma_int. Ohne accurate_rnd+full_chroma_int landen dunkle Grautöne rund zwei Stufen zu tief.
  • Die Suche nach dem ersten bewegten Video-Strom ist ein gemeinsamer Helfer für plan und colour.
  • Wiki Videos: neuer Absatz zu den Poster-Farben.

Frage 2: Video ohne Farbangabe (gemessen)

In Chromium mit einem roten H.264-Video ohne Farb-Tags gemessen (VideoFrame.colorSpace.matrix): ab 720 Zeilen BT.709, darunter BT.601 (smpte170m bei 640x360). Das ist genau die Faustregel aus dem Vorschlag, colour() folgt ihr.

Frage 1: alte Poster

Bleiben wie besprochen, wie sie sind.

Tests

  • videos.test.php im Worker-Image (echtes ffmpeg): ALL PASSED, 90 Checks. Drei rote Videos laufen durch den echten Worker: BT.709 mit Angabe, 720p ohne Angabe, 360p in BT.601. Rot kommt als 254 heraus (Soll ≥ 245, vorher 229).
  • Gegenprobe mit dem alten Filter scale=320:-2: 3 Checks rot, Rot wieder 229. Datei zurückgesetzt, cmp bestätigt.
  • php -l sauber. Das JS-Gate lief nicht, die Änderung enthält kein TypeScript.
  • feuerwerk-video ist neu gestartet (Warteschlange war leer).

Nicht geprüft

  • Ein Upload über die Live-API mit Blick auf das Poster im Picker.

Nebenbei (nicht Teil dieses Issues)

Beim Bau kam die Frage auf, warum Aufnahmen in VLC dunkler wirken. Gefunden: Chromes Encoder markiert die Aufnahmen mit der Transferkurve sRGB (iec61966-2-1), und bei Game-View-Aufnahmen steht „Full Range“ nur im Matroska-Kopf, nicht im H.264-Strom. Chrome ignoriert beides beim Abspielen, die Bilddaten selbst stimmen. Der VLC-Vergleich mit umgetaggten Testdateien läuft beim User, das Ergebnis gehört in ein eigenes Issue.

Co-Authored-By: Claude Opus 5.5

## Erledigt Commits: Repo `31ce648` „Give video posters the colours the video shows (#411)“, Wiki `7b29263` „Videos: posters keep the colours of the video (#411)“. ## Was gebaut ist - **`VideoFormat::colour($probe)`** (neu) liest Matrix und Range aus der fertigen MP4. Der Worker probt dafür die umgewandelte Datei, nicht das Original. - **`VideoFormat::posterArgs`** rechnet das Poster ausdrücklich von dieser Matrix nach BT.601 Full Range um: `scale=320:-2:in_color_matrix=…:in_range=…:out_color_matrix=bt601:out_range=pc:flags=accurate_rnd+full_chroma_int`. Ohne `accurate_rnd+full_chroma_int` landen dunkle Grautöne rund zwei Stufen zu tief. - Die Suche nach dem ersten bewegten Video-Strom ist ein gemeinsamer Helfer für `plan` und `colour`. - Wiki *Videos*: neuer Absatz zu den Poster-Farben. ## Frage 2: Video ohne Farbangabe (gemessen) In Chromium mit einem roten H.264-Video ohne Farb-Tags gemessen (`VideoFrame.colorSpace.matrix`): **ab 720 Zeilen BT.709, darunter BT.601** (smpte170m bei 640x360). Das ist genau die Faustregel aus dem Vorschlag, `colour()` folgt ihr. ## Frage 1: alte Poster Bleiben wie besprochen, wie sie sind. ## Tests - `videos.test.php` im Worker-Image (echtes ffmpeg): **ALL PASSED, 90 Checks.** Drei rote Videos laufen durch den echten Worker: BT.709 mit Angabe, 720p ohne Angabe, 360p in BT.601. **Rot kommt als 254** heraus (Soll ≥ 245, vorher 229). - **Gegenprobe** mit dem alten Filter `scale=320:-2`: 3 Checks rot, Rot wieder **229**. Datei zurückgesetzt, `cmp` bestätigt. - `php -l` sauber. Das JS-Gate lief nicht, die Änderung enthält kein TypeScript. - `feuerwerk-video` ist neu gestartet (Warteschlange war leer). ## Nicht geprüft - Ein Upload über die Live-API mit Blick auf das Poster im Picker. ## Nebenbei (nicht Teil dieses Issues) Beim Bau kam die Frage auf, warum Aufnahmen in VLC dunkler wirken. Gefunden: Chromes Encoder markiert die Aufnahmen mit der Transferkurve sRGB (`iec61966-2-1`), und bei Game-View-Aufnahmen steht „Full Range“ nur im Matroska-Kopf, nicht im H.264-Strom. Chrome ignoriert beides beim Abspielen, die Bilddaten selbst stimmen. Der VLC-Vergleich mit umgetaggten Testdateien läuft beim User, das Ergebnis gehört in ein eigenes Issue. Co-Authored-By: Claude Opus 5.5
admin moved this to Done in Development on 2026-10-13 20:12:43 +02:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#411