github derekshreds/Snacks v2.19.1
Snacks v2.19.1

6 hours ago

Snacks v2.19.1

Automated Media Library Transcoder

A point release that fixes HEVC playback on Apple devices. ffmpeg's MP4 muxer writes HEVC with the hev1 sample entry by default, which AVFoundation / VideoToolbox players (Safari, iOS, tvOS, QuickTime, Infuse, Plex on Apple hardware) refuse to render — the file plays audio over a black screen. Every HEVC stream Snacks writes into an MP4 container is now tagged hvc1, whether it was encoded by libx265 / NVENC / QSV / VAAPI / AMF / VideoToolbox or stream-copied from an HEVC source. Dolby Vision copies are handled by profile so they stay Dolby Vision instead of silently dropping to the base layer, and advanced profiles that take ownership of the tag are left alone (with a validator warning when a Dolby Vision profile forgets to).

It also fixes hardware decode of 10-bit H.264 sources: High 10 / 4:2:2 / 4:4:4 H.264 has no fixed-function decoder on any GPU, so those files now software-decode into the hardware encoder instead of failing every packet under QSV / VAAPI / NVDEC. The TesseractOCR package moves to 5.5.2.


HEVC in MP4 is tagged hvc1

TranscodingService.GetVideoTagArguments (new) returns the literal output tokens that fix the MP4 sample entry, and the ffmpeg command builder places them right after the video codec flags — before the audio, subtitle, and muxer options, and before the output path.

Case Emitted Why
HEVC encode into MP4 (any h265 encoder in VideoEncoderRegistry) -tag:v hvc1 ffmpeg defaults to hev1; Apple wants hvc1
HEVC stream copy into MP4, no Dolby Vision -tag:v hvc1 Untagged MKV sources ([0][0][0][0]) and hev1 sources are retagged; hvc1 is reaffirmed
HEVC copy of a Dolby Vision profile 5 source -strict unofficial -tag:v dvh1 Profile 5 has no compatible base layer and must be dvh1
HEVC copy of a Dolby Vision profile 8 source -strict unofficial -tag:v hvc1 Profile 8 is HDR10 / HLG-compatible; Apple wants hvc1
HEVC copy of another Dolby Vision profile (4, 7, …) nothing Dual-layer profile 7's enhancement layer is not carried into MP4; legacy profiles are left to the muxer
HEVC copy whose only Dolby Vision signal is a dvh1 / dvhe tag nothing The source tag stands
HEVC copy from a dvh1 / dvhe MP4 into MKV -tag:v hvc1 Avoids an inherited tag Matroska rejects; the output keeps its normal HEVC mapping and Dolby Vision metadata
Non-HEVC video, WebM, or other MKV output nothing No sample-entry adjustment is needed

Dolby Vision copies keep their configuration record

ffmpeg's MP4 muxer writes the Dolby Vision dvcC / dvvC box under -strict unofficial or a more permissive level; without it a stream copy of a Dolby Vision source quietly loses the record and becomes plain HDR10 (or, for profile 5, unwatchable). Snacks now reads the DOVI configuration record from ffprobe's per-stream side_data_list — the only place an MKV source carries Dolby Vision, since its codec tag is always [0][0][0][0] — and raises the compliance level together with the profile's correct tag. The job log records "Dolby Vision source: keeping its configuration record in the MP4 copy." when it does. Ordinary HEVC encodes receive hvc1; Dolby Vision re-encodes need an advanced profile configured for Dolby Vision, including the sample entry and muxer compliance level.

Advanced profiles own their tag

An advanced video profile that emits a codec tag option (-tag:v, -tag:v:0, -vtag) or drives a Dolby Vision encode (x265 dolby-vision-* params or ffmpeg's -dolbyvision) is responsible for the sample entry, and Snacks adds nothing (AdvancedProfileOwnsVideoTag, new; logged as "Advanced profile owns the MP4 sample entry; not adding a video tag."). -tag:a and other non-video tags do not count. Disabling Dolby Vision with -dolbyvision 0 / false or dolby-vision-profile=0 keeps automatic hvc1 tagging enabled; unrelated filenames containing "dolby" do not affect it. Repeated options use the last applicable value.

Mp4SampleEntryOptions (new) holds the shared rules for video-tag options, effective Dolby Vision settings, muxer compliance, and the side-data label used by the transcoder and validator.

Validator warning: dolby_vision_sample_entry

AdvancedVideoValidator now warns when a profile's custom options enable Dolby Vision but do not also set both -tag:v and a muxer compliance level that writes the configuration record. -strict normal, an empty value, or a later restrictive override still warns. unofficial / -1 and experimental / -2 are accepted; stream-specific encoder compliance does not set muxer compliance:

Dolby Vision options make this profile responsible for the MP4 sample entry: add -tag:v (dvh1 for profile 5, hvc1 for profile 8) and -strict unofficial so the Dolby Vision configuration box is written.

It is a warning, not an error — the profile still runs — because the author may be targeting MKV, where none of this applies.

Probe model

ProbeResult.Stream gains SideDataList, and the new StreamSideData models just enough of an ffprobe side-data entry to recognise a Dolby Vision record: side_data_type, dv_profile, and dv_bl_signal_compatibility_id. Other side-data entries (content light level, mastering display metadata, …) deserialize with only their type and are ignored.


Hardware decode skips H.264 High 10 / 4:2:2 / 4:4:4

The hardware-decode gate (CanVaapiDecode) only looked at the source codec name, so a 10-bit H.264 file (profile High 10, yuv420p10le) was sent through the full -hwaccel qsv / -hwaccel vaapi / -c:v h264_cuvid pipeline. No consumer Intel, AMD, or NVIDIA GPU decodes H.264 outside 8-bit 4:2:0, so every packet failed (QSV: "Error querying IO surface: unsupported" / "Error submitting packet to decoder: Function not implemented") and the job never produced a frame.

TranscodingService.IsHwDecodableVideoProfile (new) rejects H.264 whose ffprobe profile or pix_fmt indicates High 10, High 4:2:2, or High 4:4:4 (either signal is enough; unknown values are treated as decodable). It is applied in three places:

  • Both CanVaapiDecode overloads — the vainfo-detected codec set can't override it, since vainfo reports codec-level profiles (VAProfileH264High) and never High 10. This covers Linux Intel QSV, VAAPI, and the VAAPI quality calibration pass.
  • The NVIDIA path — -c:v h264_cuvid is not forced on such sources, leaving -hwaccel cuda's auto-attach to fall back to the software h264 decoder.
  • The job log — "Using software decode + QSV encode (source profile has no hardware decoder)" explains the choice up front instead of only on the retry path.

The result is the same command the software-decode retry already used (-init_hw_device … -filter_hw_device hw with no -hwaccel): ffmpeg's native decoder runs on the CPU, the hardware encoder (hevc_qsv, hevc_vaapi, hevc_nvenc, …) still runs on the GPU, and ffmpeg converts 10-bit frames to p010 on the way in. HEVC Main 10, VP9 Profile 2, and AV1 10-bit are unaffected.

Retry tiers recognise decoder failures

HandleConversionFailure treats "Error querying IO surface", "Error submitting packet to decoder", "Error while decoding", and [dec: as decoder-side failures. Previously the trailing "Function not implemented" matched the encoder-feature tier first, so the conservative-encoder-flags retry replayed the identical decode before the software-decode retry got a turn. Decoder failures now skip straight to the software-decode + hardware-encode retry.


TesseractOCR 5.5.2

The OCR package moves from 5.3.5 to 5.5.2. Its native libraries are now tesseract55 / leptonica-1.85.0, so the Dockerfile and bundle-ocr-mac.sh symlink the system libtesseract / libleptonica under those names for the package's Linux / macOS loader. Windows uses the package's bundled DLLs directly.


Tests

  • VideoTagArgumentsTests (new) — every h265 encoder is tagged hvc1 into MP4 and no other encoder is; Dolby Vision MP4 tags are normalized when copying into MKV, while other MKV / WebM outputs are unchanged; HEVC copies retag null / [0][0][0][0] / hev1 / hvc1 sources, keep dvh1 / dvhe source tags, and follow the Dolby Vision profile matrix (5 → dvh1, 8 → hvc1, both with -strict unofficial; 4 and 7 untouched); a real ffprobe side_data_list payload round-trips through ProbeResult; encodes of Dolby Vision sources drop the record; non-HEVC copies ignore the encoder name; advanced-profile ownership handles disabled, repeated, and stream-specific Dolby Vision options and explicit tags; and the tag and compliance tokens land in the right position of the assembled ffmpeg argument vector.
  • AdvancedVideoArgumentsTests — the dolby_vision_sample_entry warning checks enabled Dolby Vision settings and the effective muxer compliance value, including numeric aliases and repeated options.
  • ProbeBuilder — Video(...) accepts codecTag and dolbyVisionProfile so fixtures can describe MKV Dolby Vision sources.
  • RateControlAndScaleTests — IsHwDecodableVideoProfile matrix (H.264 High / Main / Constrained Baseline stay decodable; High 10, High 10 Intra, High 4:2:2, High 4:4:4 Predictive are rejected by profile or by pix_fmt alone; HEVC Main 10, VP9 Profile 2, and AV1 10-bit are untouched; null stream is optimistic); CanVaapiDecode rejects H.264 High 10 on the Elkhart Lake baseline and when the vainfo-detected set lists h264, and keeps HEVC Main 10.

Files Changed

HEVC sample entry

  • Snacks/Services/TranscodingService.cs — GetVideoTagArguments, AdvancedProfileOwnsVideoTag, RaisesMuxerComplianceLevel, GetHevcCopyArguments, GetDolbyVisionProfile (all new); the tag tokens ride in the literal video arguments passed to BuildFfmpegArguments
  • Snacks/Services/Mp4SampleEntryOptions.cs (new) — shared token rules for -tag:v / -vtag, -strict, Dolby Vision mentions, and the ffprobe side-data label
  • Snacks/Services/AdvancedVideoValidator.cs — WarnWhenDolbyVisionLeavesSampleEntryUnset (new) and the dolby_vision_sample_entry diagnostic
  • Snacks/Models/ProbeResult.cs — Stream.SideDataList and StreamSideData (new)

Hardware decode profile gate

  • Snacks/Services/TranscodingService.cs — IsHwDecodableVideoProfile (new); CanVaapiDecode applies it before the codec check; the NVIDIA cuvid selection and the software-decode log line in ConvertVideoAsync use it; HandleConversionFailure classifies decoder-side errors ahead of encoder-feature errors and routes them to the software-decode retry

TesseractOCR 5.5.2

  • Snacks/Snacks.csproj — TesseractOCR 5.3.5 → 5.5.2
  • Snacks/Dockerfile — /app/x64/libtesseract55.dll.so and /app/x64/libleptonica-1.85.0.dll.so symlinks
  • electron-app/scripts/bundle-ocr-mac.sh — matching .dylib symlink names

Tests

  • Snacks.Tests/Video/VideoTagArgumentsTests.cs — sample-entry, Dolby Vision, ownership, and argument-position suite (new)
  • Snacks.Tests/Video/AdvancedVideoArgumentsTests.cs — validator warning cases
  • Snacks.Tests/Fixtures/ProbeBuilder.cs — codecTag and dolbyVisionProfile parameters
  • Snacks.Tests/Video/RateControlAndScaleTests.cs — hardware-decode profile gate cases

Version bumps

  • Snacks/Snacks.csproj — <Version>2.19.1</Version> (single source of truth)
  • electron-app/package.json / package-lock.json, build-and-export.bat, Snacks.Tests/Settings/AppVersionTests.cs, Snacks/wwwroot/docs/index.html — synchronized via sync-version.mjs (the README carries a live latest release badge and no version string)

Full documentation: README.md · /docs/index.html on a running instance

Don't miss a new Snacks release

NewReleases is sending notifications on new releases.