diff --git a/compose.intel.yaml b/compose.intel.yaml index 598e46d81..6a507f85f 100644 --- a/compose.intel.yaml +++ b/compose.intel.yaml @@ -125,7 +125,7 @@ services: PHOTOPRISM_THUMB_LIBRARY: "auto" # image processing library to be used for generating thumbnails (auto, vips) PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage) ## Intel Quick Sync Video (QSV) (https://docs.photoprism.app/getting-started/advanced/transcoding/#intel-quick-sync): - PHOTOPRISM_FFMPEG_ENCODER: "intel" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + PHOTOPRISM_FFMPEG_ENCODER: "intel" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "64" # video bitrate limit in Mbps (default: 60) ## Run/install on first startup (options: update tensorflow https intel gpu davfs yt-dlp): diff --git a/compose.nvidia.yaml b/compose.nvidia.yaml index 4874fb929..e278e505a 100644 --- a/compose.nvidia.yaml +++ b/compose.nvidia.yaml @@ -136,7 +136,7 @@ services: ## NVIDIA GPU Hardware Acceleration (see https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html): NVIDIA_VISIBLE_DEVICES: "all" NVIDIA_DRIVER_CAPABILITIES: "all" - PHOTOPRISM_FFMPEG_ENCODER: "nvidia" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + PHOTOPRISM_FFMPEG_ENCODER: "nvidia" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) PHOTOPRISM_FFMPEG_BITRATE: "64" # video bitrate limit in Mbps (default: 60) ## Additional dependencies and tools: diff --git a/compose.yaml b/compose.yaml index 78bbf900f..782745212 100644 --- a/compose.yaml +++ b/compose.yaml @@ -132,7 +132,7 @@ services: PHOTOPRISM_THUMB_LIBRARY: "auto" # image processing library to be used for generating thumbnails (auto, vips) PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage) ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # LIBVA_DRIVER_NAME: "i965" # For Intel architectures Haswell and older which do not support QSV yet but use VAAPI instead PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "64" # video bitrate limit in Mbps (default: 60) diff --git a/internal/config/config_ffmpeg_test.go b/internal/config/config_ffmpeg_test.go index 80b195018..31017e8a9 100644 --- a/internal/config/config_ffmpeg_test.go +++ b/internal/config/config_ffmpeg_test.go @@ -18,6 +18,8 @@ func TestConfig_FFmpegEncoder(t *testing.T) { assert.Equal(t, encode.SoftwareAvc, c.FFmpegEncoder()) c.options.FFmpegEncoder = "intel" assert.Equal(t, encode.IntelAvc, c.FFmpegEncoder()) + c.options.FFmpegEncoder = "vulkan" + assert.Equal(t, encode.VulkanAvc, c.FFmpegEncoder()) c.options.FFmpegEncoder = "xxx" assert.Equal(t, encode.SoftwareAvc, c.FFmpegEncoder()) c.options.FFmpegEncoder = "" diff --git a/internal/ffmpeg/encode/encoder.go b/internal/ffmpeg/encode/encoder.go index fd8aeefaa..a5b1ca0ce 100644 --- a/internal/ffmpeg/encode/encoder.go +++ b/internal/ffmpeg/encode/encoder.go @@ -20,6 +20,7 @@ const ( VaapiAvc Encoder = "h264_vaapi" // VaapiAvc is the Video Acceleration API H.264 encoder. NvidiaAvc Encoder = "h264_nvenc" // NvidiaAvc is the NVIDIA H.264 encoder. V4LAvc Encoder = "h264_v4l2m2m" // V4LAvc is the Video4Linux H.264 encoder. + VulkanAvc Encoder = "h264_vulkan" // VulkanAvc is the Vulkan video extensions H.264 encoder (requires FFmpeg 8+). ) // AvcEncoders is the list of supported H.264 encoders with aliases. @@ -51,6 +52,9 @@ var AvcEncoders = map[string]Encoder{ "raspberry": V4LAvc, "raspberrypi": V4LAvc, string(V4LAvc): V4LAvc, + "vulkan": VulkanAvc, + "vk": VulkanAvc, + string(VulkanAvc): VulkanAvc, } // FindEncoder finds an FFmpeg encoder by name. diff --git a/internal/ffmpeg/encode/encoder_test.go b/internal/ffmpeg/encode/encoder_test.go index e1458b462..67353c31b 100644 --- a/internal/ffmpeg/encode/encoder_test.go +++ b/internal/ffmpeg/encode/encoder_test.go @@ -13,6 +13,11 @@ func TestFindEncoder(t *testing.T) { t.Run("Apple", func(t *testing.T) { assert.Equal(t, "h264_videotoolbox", FindEncoder("apple").String()) }) + t.Run("Vulkan", func(t *testing.T) { + assert.Equal(t, "h264_vulkan", FindEncoder("vulkan").String()) + assert.Equal(t, "h264_vulkan", FindEncoder("vk").String()) + assert.Equal(t, "h264_vulkan", FindEncoder("h264_vulkan").String()) + }) t.Run("Unsupported", func(t *testing.T) { assert.Equal(t, "libx264", FindEncoder("xxx").String()) }) diff --git a/internal/ffmpeg/transcode_cmd.go b/internal/ffmpeg/transcode_cmd.go index c85407bad..350bc8428 100644 --- a/internal/ffmpeg/transcode_cmd.go +++ b/internal/ffmpeg/transcode_cmd.go @@ -10,6 +10,7 @@ import ( "github.com/photoprism/photoprism/internal/ffmpeg/nvidia" "github.com/photoprism/photoprism/internal/ffmpeg/v4l" "github.com/photoprism/photoprism/internal/ffmpeg/vaapi" + "github.com/photoprism/photoprism/internal/ffmpeg/vulkan" "github.com/photoprism/photoprism/pkg/fs" ) @@ -66,6 +67,8 @@ func TranscodeCmd(srcName, destName string, opt encode.Options) (cmd *exec.Cmd, cmd = nvidia.TranscodeToAvcCmd(srcName, destName, opt) case encode.V4LAvc: cmd = v4l.TranscodeToAvcCmd(srcName, destName, opt) + case encode.VulkanAvc: + cmd = vulkan.TranscodeToAvcCmd(srcName, destName, opt) default: cmd = encode.TranscodeToAvcCmd(srcName, destName, opt) } diff --git a/internal/ffmpeg/vulkan/avc.go b/internal/ffmpeg/vulkan/avc.go new file mode 100644 index 000000000..bc93da3ab --- /dev/null +++ b/internal/ffmpeg/vulkan/avc.go @@ -0,0 +1,42 @@ +package vulkan + +import ( + "os/exec" + + "github.com/photoprism/photoprism/internal/ffmpeg/encode" +) + +// TranscodeToAvcCmd returns the FFmpeg command for hardware-accelerated transcoding to MPEG-4 AVC via the Vulkan video extensions. +func TranscodeToAvcCmd(srcName, destName string, opt encode.Options) *exec.Cmd { + // Vulkan needs a named hardware device that filters and the encoder can attach to; + // "vk" is the local alias, and an optional physical device selector is appended when set. + hwDevice := "vulkan=vk" + if opt.Device != "" { + hwDevice = "vulkan=vk:" + opt.Device + } + + // Scale + format conversion happens in system memory, then hwupload moves frames into the Vulkan device. + videoFilter := opt.VideoFilter(encode.FormatNV12) + ",hwupload" + + // #nosec G204 -- command arguments are built from validated options and paths. + return exec.Command( + opt.Bin, + "-hide_banner", + "-y", + "-strict", "-2", + "-init_hw_device", hwDevice, + "-filter_hw_device", "vk", + "-i", srcName, + "-c:a", "aac", + "-vf", videoFilter, + "-c:v", opt.Encoder.String(), + "-map", opt.MapVideo, + "-map", opt.MapAudio, + "-ignore_unknown", + "-qp", opt.QpQuality(), + "-f", "mp4", + "-movflags", opt.MovFlags, + "-map_metadata", opt.MapMetadata, + destName, + ) +} diff --git a/internal/ffmpeg/vulkan/avc_test.go b/internal/ffmpeg/vulkan/avc_test.go new file mode 100644 index 000000000..d14190b84 --- /dev/null +++ b/internal/ffmpeg/vulkan/avc_test.go @@ -0,0 +1,33 @@ +package vulkan + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/photoprism/photoprism/internal/ffmpeg/encode" +) + +func TestVulkan_TranscodeToAvcCmd_WithDevice(t *testing.T) { + opt := encode.NewVideoOptions("/usr/bin/ffmpeg", encode.VulkanAvc, 1500, encode.DefaultQuality, encode.PresetFast, "0", "0:v:0", "0:a:0?") + cmd := TranscodeToAvcCmd("SRC.mov", "DEST.mp4", opt) + s := cmd.String() + assert.True(t, strings.Contains(s, "-init_hw_device vulkan=vk:0")) + assert.True(t, strings.Contains(s, "-filter_hw_device vk")) + assert.True(t, strings.Contains(s, "format=nv12")) + assert.True(t, strings.Contains(s, "hwupload")) + assert.True(t, strings.Contains(s, "-c:v h264_vulkan")) + assert.True(t, strings.Contains(s, "-qp 25")) + assert.True(t, strings.Contains(s, "-f mp4")) +} + +func TestVulkan_TranscodeToAvcCmd_NoDevice(t *testing.T) { + opt := encode.NewVideoOptions("/usr/bin/ffmpeg", encode.VulkanAvc, 1500, encode.DefaultQuality, encode.PresetFast, "", "0:v:0", "0:a:0?") + cmd := TranscodeToAvcCmd("SRC.mov", "DEST.mp4", opt) + s := cmd.String() + assert.True(t, strings.Contains(s, "-init_hw_device vulkan=vk ")) + assert.False(t, strings.Contains(s, "vulkan=vk:")) + assert.True(t, strings.Contains(s, "-filter_hw_device vk")) + assert.True(t, strings.Contains(s, "-c:v h264_vulkan")) +} diff --git a/internal/ffmpeg/vulkan/vulkan.go b/internal/ffmpeg/vulkan/vulkan.go new file mode 100644 index 000000000..b984389c8 --- /dev/null +++ b/internal/ffmpeg/vulkan/vulkan.go @@ -0,0 +1,28 @@ +/* +Package vulkan contains ffmpeg helpers for configuring Vulkan video +hardware acceleration on compatible GPUs when building PhotoPrism encode +pipelines. Requires FFmpeg 8 or later, which ships the h264_vulkan +encoder via the Vulkan video extensions. + +Copyright (c) 2018 - 2025 PhotoPrism UG. All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under Version 3 of the GNU Affero General Public License (the "AGPL"): + + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + The AGPL is supplemented by our Trademark and Brand Guidelines, + which describe how our Brand Assets may be used: + + +Feel free to send an email to hello@photoprism.app if you have questions, +want to support our work, or just want to say hello. + +Additional information can be found in our Developer Guide: + +*/ +package vulkan diff --git a/setup/cloud/digitalocean/compose.yaml b/setup/cloud/digitalocean/compose.yaml index df76bffca..b618d7951 100644 --- a/setup/cloud/digitalocean/compose.yaml +++ b/setup/cloud/digitalocean/compose.yaml @@ -159,7 +159,7 @@ services: ## Run/install on first startup, see https://github.com/photoprism/photoprism/blob/develop/scripts/dist/Makefile: PHOTOPRISM_INIT: "https tensorflow" # common options: update https tensorflow tensorflow-gpu intel gpu davfs yt-dlp ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "32" # video bitrate limit in Mbps (default: 60) working_dir: "/photoprism" # do not change or remove diff --git a/setup/docker/arm64/compose.yaml b/setup/docker/arm64/compose.yaml index f38dbd43f..cf6129ead 100644 --- a/setup/docker/arm64/compose.yaml +++ b/setup/docker/arm64/compose.yaml @@ -109,7 +109,7 @@ services: OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud OLLAMA_API_KEY: "" # API key required to access Ollama (optional) ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "64" # video bitrate limit in Mbps (default: 60) ## Run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, and 900-1200): diff --git a/setup/docker/compose.yaml b/setup/docker/compose.yaml index 80ec185a1..31f9cd3e0 100644 --- a/setup/docker/compose.yaml +++ b/setup/docker/compose.yaml @@ -99,7 +99,7 @@ services: OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud OLLAMA_API_KEY: "" # API key required to access Ollama (optional) ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "64" # video bitrate limit in Mbps (default: 60) ## NVIDIA GPU Hardware Acceleration (see https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html): diff --git a/setup/docker/macos/compose.yaml b/setup/docker/macos/compose.yaml index e4eb7b839..d73106c06 100644 --- a/setup/docker/macos/compose.yaml +++ b/setup/docker/macos/compose.yaml @@ -92,7 +92,7 @@ services: OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud OLLAMA_API_KEY: "" # API key required to access Ollama (optional) ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "32" # video bitrate limit in Mbps (default: 60) ## Storage Folders: "~" is a shortcut for your home directory, "." for the current directory diff --git a/setup/docker/nvidia/compose.yaml b/setup/docker/nvidia/compose.yaml index a41a961c8..f8f725848 100644 --- a/setup/docker/nvidia/compose.yaml +++ b/setup/docker/nvidia/compose.yaml @@ -103,7 +103,7 @@ services: OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud OLLAMA_API_KEY: "" # API key required to access Ollama (optional) ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - PHOTOPRISM_FFMPEG_ENCODER: "nvidia" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + PHOTOPRISM_FFMPEG_ENCODER: "nvidia" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) PHOTOPRISM_FFMPEG_BITRATE: "64" # video bitrate limit in Mbps (default: 60) ## NVIDIA GPU Hardware Acceleration (see https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html): diff --git a/setup/docker/portainer/stack.env b/setup/docker/portainer/stack.env index 91bb1a5ff..20fbf3c5f 100644 --- a/setup/docker/portainer/stack.env +++ b/setup/docker/portainer/stack.env @@ -68,7 +68,7 @@ PHOTOPRISM_SITE_CAPTION=AI-Powered Photos App PHOTOPRISM_SITE_DESCRIPTION= PHOTOPRISM_SITE_AUTHOR= ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): -# PHOTOPRISM_FFMPEG_ENCODER=software # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) +# PHOTOPRISM_FFMPEG_ENCODER=software # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # PHOTOPRISM_FFMPEG_SIZE=1920 # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE=32 # video bitrate limit in Mbps (default: 60) ## Run/install on first startup, see https://github.com/photoprism/photoprism/blob/develop/scripts/dist/Makefile: diff --git a/setup/docker/scheduler/compose.yaml b/setup/docker/scheduler/compose.yaml index 9928f741a..f14c52c9c 100644 --- a/setup/docker/scheduler/compose.yaml +++ b/setup/docker/scheduler/compose.yaml @@ -96,7 +96,7 @@ services: OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud OLLAMA_API_KEY: "" # API key required to access Ollama (optional) ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "32" # video bitrate limit in Mbps (default: 60) ## Run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, and 900-1200): diff --git a/setup/docker/sqlite/compose.yaml b/setup/docker/sqlite/compose.yaml index 0ebad5867..14f0dd6f2 100644 --- a/setup/docker/sqlite/compose.yaml +++ b/setup/docker/sqlite/compose.yaml @@ -89,7 +89,7 @@ services: OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud OLLAMA_API_KEY: "" # API key required to access Ollama (optional) ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "32" # video bitrate limit in Mbps (default: 60) ## Run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, and 900-1200): diff --git a/setup/docker/windows/compose.yaml b/setup/docker/windows/compose.yaml index 6686be970..0f63537b8 100644 --- a/setup/docker/windows/compose.yaml +++ b/setup/docker/windows/compose.yaml @@ -97,7 +97,7 @@ services: OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud OLLAMA_API_KEY: "" # API key required to access Ollama (optional) ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "32" # video bitrate limit in Mbps (default: 60) working_dir: "/photoprism" # do not change or remove diff --git a/setup/podman/docker-compose.yml b/setup/podman/docker-compose.yml index 190bd31ee..95f169fda 100644 --- a/setup/podman/docker-compose.yml +++ b/setup/podman/docker-compose.yml @@ -100,7 +100,7 @@ services: ## Run/install on first startup, see https://github.com/photoprism/photoprism/blob/develop/scripts/dist/Makefile: PHOTOPRISM_INIT: "https tensorflow" # common options: update https tensorflow tensorflow-gpu intel gpu davfs yt-dlp ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): - # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan) # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) # PHOTOPRISM_FFMPEG_BITRATE: "32" # video bitrate limit in Mbps (default: 60) ## Run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, and 900-1200):