aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorinvario <67800603+invario@users.noreply.github.com>2025-03-09 11:48:36 -0400
committerKT <invario@gmail.com>2025-04-03 00:36:33 -0400
commite3008d08be22059c43086290474f3a164eff10f1 (patch)
tree11cb7d0c2095b06a08a64c4fdfe6dc4f46a8a9f4
parent57c201b8ab1b958d2b7f082255cf6486c104c70c (diff)
downloadnextcloud-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.php55
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);