encode.FormatNV12 already ends in hwupload, so appending it again produced
"format=nv12,hwupload,hwupload", which fails because the frames are already on
the GPU. Build the filter from FormatNV12 directly to upload exactly once, and
add a gated Vulkan case to the transcode command tests.
The transcode tests gated their real hardware run on PHOTOPRISM_FFMPEG_ENCODER,
but the package init() unconditionally unset that variable, so the hardware path
never executed and an FFmpeg 8 regression like #5630 could pass unnoticed.
Switch the opt-in to a dedicated PHOTOPRISM_FFMPEG_TEST_ENCODER variable so real
VAAPI/QSV/NVENC transcodes run when enabled on a matching host, while a runtime
PHOTOPRISM_FFMPEG_ENCODER value still cannot trigger them by accident.
FFmpeg 8 no longer derives a filter hardware device from "-hwaccel
vaapi" alone, so the "format=nv12,hwupload" step aborts with "A hardware
device reference is required to upload frames to." and PhotoPrism falls
back to software encoding.
Initialize a named VAAPI device and reference it for both decoding
(-hwaccel_device) and filtering (-filter_hw_device). When no device path
is configured, FFmpeg auto-detects the default render node.