From e3008d08be22059c43086290474f3a164eff10f1 Mon Sep 17 00:00:00 2001 From: invario <67800603+invario@users.noreply.github.com> Date: Sun, 9 Mar 2025 11:48:36 -0400 Subject: [PATCH 1/3] feat(previews): Use proper colorspace and apply tonemap for HDR videos Signed-off-by: KT --- lib/private/Preview/Movie.php | 55 ++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/lib/private/Preview/Movie.php b/lib/private/Preview/Movie.php index ed6a277053ba4..5cb22cdbff80a 100644 --- a/lib/private/Preview/Movie.php +++ b/lib/private/Preview/Movie.php @@ -9,11 +9,15 @@ use OCP\Files\File; use OCP\Files\FileInfo; +use OCP\IConfig; use OCP\IImage; use OCP\Server; use Psr\Log\LoggerInterface; class Movie extends ProviderV2 { + /** @var IConfig */ + private $config; + /** * @deprecated 23.0.0 pass option to \OCP\Preview\ProviderV2 * @var string @@ -32,6 +36,12 @@ class Movie extends ProviderV2 { /** * {@inheritDoc} */ + + public function __construct(array $config) { + parent::__construct($config); + $this->config = \OC::$server->get(IConfig::class); + } + public function getMimeType(): string { return '/video\/.*/'; } @@ -105,6 +115,34 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { return $result; } + private function useHdr(string $absPath): bool { + // load ffprobe path from configuration, otherwise generate binary path using ffmpeg binary path + $ffprobe_binary = $this->config->getSystemValue('preview_ffprobe_path', null) ?? pathinfo($this->binary, PATHINFO_DIRNAME) . '/ffprobe'; + // run ffprobe on the video file to get value of "color_transfer" + $test_hdr_cmd = [$ffprobe_binary,'-select_streams', 'v:0', + '-show_entries', 'stream=color_transfer', + '-of', 'default=noprint_wrappers=1:nokey=1', + $absPath]; + $test_hdr_proc = proc_open($test_hdr_cmd, [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $test_hdr_pipes); + if (is_resource($test_hdr_proc)) { + $test_hdr_stdout = trim(stream_get_contents($test_hdr_pipes[1])); + $test_hdr_stderr = trim(stream_get_contents($test_hdr_pipes[2])); + proc_close($test_hdr_proc); + // search build options for libzimg (provides zscale filter) + $ffmpeg_libzimg_installed = strpos($test_hdr_stderr, '--enable-libzimg'); + } else { + $ffmpeg_libzimg_installed = false; + } + // Only values of "smpte2084" and "arib-std-b67" indicate an HDR video. Force colorspace to '2020_ncl' + // because some videos are tagged incorrectly as 'reserved' resulting in fail. + // Only return true if video is detected as HDR and libzimg is installed. + if (($test_hdr_stdout === 'smpte2084' || $test_hdr_stdout === 'arib-std-b67') && $ffmpeg_libzimg_installed !== false) { + return true; + } else { + return false; + } + } + private function generateThumbNail(int $maxX, int $maxY, string $absPath, int $second): ?IImage { $tmpPath = \OC::$server->getTempManager()->getTemporaryFile(); @@ -116,10 +154,19 @@ private function generateThumbNail(int $maxX, int $maxY, string $absPath, int $s '-an', '-f', 'mjpeg', '-vframes', '1', '-vsync', '1', $tmpPath]; } elseif ($binaryType === 'ffmpeg') { - $cmd = [$this->binary, '-y', '-ss', (string)$second, - '-i', $absPath, - '-f', 'mjpeg', '-vframes', '1', - $tmpPath]; + if ($this->useHdr($absPath)) { + $cmd = [$this->binary, '-y', '-ss', (string)$second, + '-i', $absPath, + '-f', 'mjpeg', '-vframes', '1', + '-vf', 'zscale=min=2020_ncl:t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p', + $tmpPath]; + } else { + // alway default to generating preview using non-HDR command + $cmd = [$this->binary, '-y', '-ss', (string)$second, + '-i', $absPath, + '-f', 'mjpeg', '-vframes', '1', + $tmpPath]; + } } else { // Not supported unlink($tmpPath); From 91e2f05ee6aeaa978fec953455d7fcab20a07900 Mon Sep 17 00:00:00 2001 From: invario <67800603+invario@users.noreply.github.com> Date: Thu, 3 Apr 2025 11:05:30 -0400 Subject: [PATCH 2/3] feat(previews): Clean up code for HDR video previews Signed-off-by: invario <67800603+invario@users.noreply.github.com> --- lib/private/Preview/Movie.php | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/lib/private/Preview/Movie.php b/lib/private/Preview/Movie.php index 5cb22cdbff80a..1011eebe85016 100644 --- a/lib/private/Preview/Movie.php +++ b/lib/private/Preview/Movie.php @@ -15,8 +15,7 @@ use Psr\Log\LoggerInterface; class Movie extends ProviderV2 { - /** @var IConfig */ - private $config; + private Iconfig $config; /** * @deprecated 23.0.0 pass option to \OCP\Preview\ProviderV2 @@ -33,13 +32,9 @@ class Movie extends ProviderV2 { /** @var string */ private $binary; - /** - * {@inheritDoc} - */ - public function __construct(array $config) { parent::__construct($config); - $this->config = \OC::$server->get(IConfig::class); + $this->config = Server::get(IConfig::class); } public function getMimeType(): string { @@ -117,22 +112,21 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { private function useHdr(string $absPath): bool { // load ffprobe path from configuration, otherwise generate binary path using ffmpeg binary path - $ffprobe_binary = $this->config->getSystemValue('preview_ffprobe_path', null) ?? pathinfo($this->binary, PATHINFO_DIRNAME) . '/ffprobe'; + $ffprobe_binary = $this->config->getSystemValue('preview_ffprobe_path', null) ?? (pathinfo($this->binary, PATHINFO_DIRNAME) . '/ffprobe'); // run ffprobe on the video file to get value of "color_transfer" $test_hdr_cmd = [$ffprobe_binary,'-select_streams', 'v:0', '-show_entries', 'stream=color_transfer', '-of', 'default=noprint_wrappers=1:nokey=1', $absPath]; $test_hdr_proc = proc_open($test_hdr_cmd, [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $test_hdr_pipes); - if (is_resource($test_hdr_proc)) { - $test_hdr_stdout = trim(stream_get_contents($test_hdr_pipes[1])); - $test_hdr_stderr = trim(stream_get_contents($test_hdr_pipes[2])); - proc_close($test_hdr_proc); - // search build options for libzimg (provides zscale filter) - $ffmpeg_libzimg_installed = strpos($test_hdr_stderr, '--enable-libzimg'); - } else { - $ffmpeg_libzimg_installed = false; + if ($test_hdr_proc === false) { + return false; } + $test_hdr_stdout = trim(stream_get_contents($test_hdr_pipes[1])); + $test_hdr_stderr = trim(stream_get_contents($test_hdr_pipes[2])); + proc_close($test_hdr_proc); + // search build options for libzimg (provides zscale filter) + $ffmpeg_libzimg_installed = strpos($test_hdr_stderr, '--enable-libzimg'); // Only values of "smpte2084" and "arib-std-b67" indicate an HDR video. Force colorspace to '2020_ncl' // because some videos are tagged incorrectly as 'reserved' resulting in fail. // Only return true if video is detected as HDR and libzimg is installed. From eb3ca406f89a98e96a7542f3caca6e6bec0879e3 Mon Sep 17 00:00:00 2001 From: invario <67800603+invario@users.noreply.github.com> Date: Thu, 3 Apr 2025 12:52:19 -0400 Subject: [PATCH 3/3] feat(previews): fix casing and comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Côme Chilliet <91878298+come-nc@users.noreply.github.com> Signed-off-by: invario <67800603+invario@users.noreply.github.com> --- lib/private/Preview/Movie.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/private/Preview/Movie.php b/lib/private/Preview/Movie.php index 1011eebe85016..9ffda38b136cc 100644 --- a/lib/private/Preview/Movie.php +++ b/lib/private/Preview/Movie.php @@ -15,7 +15,7 @@ use Psr\Log\LoggerInterface; class Movie extends ProviderV2 { - private Iconfig $config; + private IConfig $config; /** * @deprecated 23.0.0 pass option to \OCP\Preview\ProviderV2 @@ -127,8 +127,7 @@ private function useHdr(string $absPath): bool { proc_close($test_hdr_proc); // search build options for libzimg (provides zscale filter) $ffmpeg_libzimg_installed = strpos($test_hdr_stderr, '--enable-libzimg'); - // Only values of "smpte2084" and "arib-std-b67" indicate an HDR video. Force colorspace to '2020_ncl' - // because some videos are tagged incorrectly as 'reserved' resulting in fail. + // Only values of "smpte2084" and "arib-std-b67" indicate an HDR video. // Only return true if video is detected as HDR and libzimg is installed. if (($test_hdr_stdout === 'smpte2084' || $test_hdr_stdout === 'arib-std-b67') && $ffmpeg_libzimg_installed !== false) { return true; @@ -149,13 +148,15 @@ private function generateThumbNail(int $maxX, int $maxY, string $absPath, int $s $tmpPath]; } elseif ($binaryType === 'ffmpeg') { if ($this->useHdr($absPath)) { + // Force colorspace to '2020_ncl' because some videos are + // tagged incorrectly as 'reserved' resulting in fail if not forced. $cmd = [$this->binary, '-y', '-ss', (string)$second, '-i', $absPath, '-f', 'mjpeg', '-vframes', '1', '-vf', 'zscale=min=2020_ncl:t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p', $tmpPath]; } else { - // alway default to generating preview using non-HDR command + // always default to generating preview using non-HDR command $cmd = [$this->binary, '-y', '-ss', (string)$second, '-i', $absPath, '-f', 'mjpeg', '-vframes', '1',