diff options
author | invario <67800603+invario@users.noreply.github.com> | 2025-03-09 11:48:36 -0400 |
---|---|---|
committer | KT <invario@gmail.com> | 2025-04-03 00:36:33 -0400 |
commit | e3008d08be22059c43086290474f3a164eff10f1 (patch) | |
tree | 11cb7d0c2095b06a08a64c4fdfe6dc4f46a8a9f4 | |
parent | 57c201b8ab1b958d2b7f082255cf6486c104c70c (diff) | |
download | nextcloud-server-e3008d08be22059c43086290474f3a164eff10f1.tar.gz nextcloud-server-e3008d08be22059c43086290474f3a164eff10f1.zip |
feat(previews): Use proper colorspace and apply tonemap for HDR videos
Signed-off-by: KT <invario@gmail.com>
-rw-r--r-- | lib/private/Preview/Movie.php | 55 |
1 files changed, 51 insertions, 4 deletions
diff --git a/lib/private/Preview/Movie.php b/lib/private/Preview/Movie.php index ed6a277053b..5cb22cdbff8 100644 --- a/lib/private/Preview/Movie.php +++ b/lib/private/Preview/Movie.php @@ -9,11 +9,15 @@ namespace OC\Preview; 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 @@ class Movie extends ProviderV2 { 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 @@ class Movie extends ProviderV2 { '-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); |