Give video posters the colours the video shows (#411)
feuerwerk / gate (push) Successful in 4m30s
feuerwerk / suite (push) Successful in 18m12s

The worker made the poster with a plain scale=320:-2. ffmpeg then
read every picture as BT.601 in the limited range, so a red of 255 in
a BT.709 video came out as 229 in the JPEG. The picker showed paler
posters than the video in the editor.

- VideoFormat::colour reads matrix and range of the converted MP4.
  When the file says nothing, it follows Chrome: BT.709 from 720 lines
  up, BT.601 below. That guess was measured in Chromium with a red
  H.264 video without colour tags.
- posterArgs turns the picture from that matrix into BT.601 in the
  full range, the way a JPEG is read. accurate_rnd and
  full_chroma_int keep dark greys from landing two steps too low.
- The search for the first moving video stream is now one helper,
  used by plan and colour.
- Posters made before this change stay as they are.

Tests:
- videos.test.php in the worker image: ALL PASSED, 90 checks. Three
  red videos go through the real worker: BT.709 tagged, 720p without
  tags and 360p in BT.601. Red comes out as 254.
- Counter-check with the old filter: 3 checks red, red is 229 again.
- php -l clean on all three files. The gate was not run, the change
  has no TypeScript.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-13 16:45:20 +02:00
1 parent ba68167704
commit 31ce6480b2
3 files changed
+90 -16

No files matched your search

+3 -2
View File
@@ -49,8 +49,9 @@ final class VideoWorker
if (!$finished) {
return;
}
$details = VideoFormat::details(self::probe($tmpVideo));
$this->run(VideoFormat::posterArgs($tmpVideo, $tmpPoster, $details['duration']), 0, null);
$converted = self::probe($tmpVideo);
$details = VideoFormat::details($converted);
$this->run(VideoFormat::posterArgs($tmpVideo, $tmpPoster, $details['duration'], VideoFormat::colour($converted)), 0, null);
if ($queue->job($folder, $hash) === null) {
return;
}