Raised by Geordie in #engine-dev: probe the source for SMPTE ST 2094-40 so HDR10+ is known before playback, which gives a correct label on first play and opens the route to a cdm4 supplemental-codec tag. The same message asked for a probe API that works without playback, for sources with no data provider (SMB, WebDAV).
Where the engine stood
AetherEngine.probe(url:) / probe(source:) already are that playback-free API, including probe(source: .custom(reader:formatHint:)) for anything behind an IOReader, which is the SMB / WebDAV shape. What they could not report was HDR10+, because nothing in the container declares it: the engine only learned it during playback, from HLSSegmentProducer's per-packet scan, which fires onFirstHDR10PlusDetected and upgrades videoFormat mid-session.
What the mechanism actually is
Checked against the pinned FFmpeg tree. AV_PKT_DATA_DYNAMIC_HDR10_PLUS is attached by exactly one demuxer, matroskadec.c, out of a BlockAdditional whose ITU-T T.35 header it strips (country US, provider Samsung, app id 4). That is the VP9 / AV1-in-Matroska carriage. For HEVC, which is where essentially every HDR10+ title lives, the metadata is in-band T.35 SEI: mov, mpegts and matroska all hand it through inside the video packet, and only hevc/hevcdec.c surfaces it, post-decode, as AV_FRAME_DATA_DYNAMIC_HDR_PLUS (decode.c maps packet side data to frame side data, not the other way round). So ffprobe -show_packets shows HDR10+ only for the Matroska case; everywhere else it takes -show_frames, i.e. a decode.
A packet scan is therefore the general answer, and it is cheap: the signature is six bytes and no decoder has to be opened.
Shipped on feat/hdr10plus-probe
probe(url:options:detecting:atmosDetection:hdr10PlusDetection:) with a ProbeDetail option set, running the opt-in passes over ONE open handle. .hdr10Plus is the packet scan, .atmos is the bounded JOC decode probeDetectingAtmos already ran (kept, now a spelling of detecting: .atmos). Asking for both costs one connection rather than two.
SourceProbe.carriesHDR10PlusMetadata, plus the .hdr10 to .hdr10Plus upgrade through the same rule the running session applies, in one place so probe and session cannot drift. A Dolby Vision source keeps .dolbyVision and carries the flag alongside, because Profile 7 and the 8.1 remuxes of it carry an HDR10+ base layer.
- The scan runs before any seek, so the common case is answered out of the packets
avformat_find_stream_info already queued, with no further I/O. Bounded by HDR10PlusDetectionOptions (32 packets, 16 MiB, 2 s).
- Additive only: a cap leaves the base probe's answer untouched, and a negative means "not seen inside the budget", never "proven absent".
- The signature scan moved out of
HLSSegmentProducer into HDR10PlusMetadataScan, which also gains the Matroska side-data carriage the producer could not see before.
aetherctl probe --detect-hdr10plus / --detect-atmos make both passes observable without a host.
Tested against two ~1 KB HEVC / PQ fixtures that differ only in the injected SEI. The positive one carries a payload built to libavutil/hdr_dynamic_metadata.c's bit layout, and the generator (Scripts/make-hdr10plus-fixture.py) only emits it once ffprobe -show_frames reports HDR Dynamic Metadata SMPTE2094-40 on it, so FFmpeg's own parser accepting it is what makes it a fixture of the case.
Open: the cdm4 half
Whether a SUPPLEMENTAL-CODECS entry with the cdm4 brand makes AVFoundation engage HDR10+ on the native path is unmeasured, and that is the load-bearing question before any route is built for it. Today the software path forwards per-frame metadata through kCMSampleAttachmentKey_HDR10PlusPerFrameData, while the native path leaves the SEI in-band in the HEVC bitstream. Claims of the form "this manifest key makes the platform do X" have twice been expensive here (see the -11868 / -12927 ladder in the Dolby Vision work), so this wants a measurement on a real HDR10+ panel via aetherctl serve first, not a route.
The probe work above is what that measurement needs anyway: it is how a session knows it is looking at HDR10+ content before it decides what to serve.
Raised by Geordie in #engine-dev: probe the source for SMPTE ST 2094-40 so HDR10+ is known before playback, which gives a correct label on first play and opens the route to a
cdm4supplemental-codec tag. The same message asked for a probe API that works without playback, for sources with no data provider (SMB, WebDAV).Where the engine stood
AetherEngine.probe(url:)/probe(source:)already are that playback-free API, includingprobe(source: .custom(reader:formatHint:))for anything behind anIOReader, which is the SMB / WebDAV shape. What they could not report was HDR10+, because nothing in the container declares it: the engine only learned it during playback, fromHLSSegmentProducer's per-packet scan, which firesonFirstHDR10PlusDetectedand upgradesvideoFormatmid-session.What the mechanism actually is
Checked against the pinned FFmpeg tree.
AV_PKT_DATA_DYNAMIC_HDR10_PLUSis attached by exactly one demuxer,matroskadec.c, out of a BlockAdditional whose ITU-T T.35 header it strips (country US, provider Samsung, app id 4). That is the VP9 / AV1-in-Matroska carriage. For HEVC, which is where essentially every HDR10+ title lives, the metadata is in-band T.35 SEI:mov,mpegtsandmatroskaall hand it through inside the video packet, and onlyhevc/hevcdec.csurfaces it, post-decode, asAV_FRAME_DATA_DYNAMIC_HDR_PLUS(decode.cmaps packet side data to frame side data, not the other way round). Soffprobe -show_packetsshows HDR10+ only for the Matroska case; everywhere else it takes-show_frames, i.e. a decode.A packet scan is therefore the general answer, and it is cheap: the signature is six bytes and no decoder has to be opened.
Shipped on
feat/hdr10plus-probeprobe(url:options:detecting:atmosDetection:hdr10PlusDetection:)with aProbeDetailoption set, running the opt-in passes over ONE open handle..hdr10Plusis the packet scan,.atmosis the bounded JOC decodeprobeDetectingAtmosalready ran (kept, now a spelling ofdetecting: .atmos). Asking for both costs one connection rather than two.SourceProbe.carriesHDR10PlusMetadata, plus the.hdr10to.hdr10Plusupgrade through the same rule the running session applies, in one place so probe and session cannot drift. A Dolby Vision source keeps.dolbyVisionand carries the flag alongside, because Profile 7 and the 8.1 remuxes of it carry an HDR10+ base layer.avformat_find_stream_infoalready queued, with no further I/O. Bounded byHDR10PlusDetectionOptions(32 packets, 16 MiB, 2 s).HLSSegmentProducerintoHDR10PlusMetadataScan, which also gains the Matroska side-data carriage the producer could not see before.aetherctl probe --detect-hdr10plus/--detect-atmosmake both passes observable without a host.Tested against two ~1 KB HEVC / PQ fixtures that differ only in the injected SEI. The positive one carries a payload built to
libavutil/hdr_dynamic_metadata.c's bit layout, and the generator (Scripts/make-hdr10plus-fixture.py) only emits it onceffprobe -show_framesreportsHDR Dynamic Metadata SMPTE2094-40on it, so FFmpeg's own parser accepting it is what makes it a fixture of the case.Open: the cdm4 half
Whether a
SUPPLEMENTAL-CODECSentry with thecdm4brand makes AVFoundation engage HDR10+ on the native path is unmeasured, and that is the load-bearing question before any route is built for it. Today the software path forwards per-frame metadata throughkCMSampleAttachmentKey_HDR10PlusPerFrameData, while the native path leaves the SEI in-band in the HEVC bitstream. Claims of the form "this manifest key makes the platform do X" have twice been expensive here (see the -11868 / -12927 ladder in the Dolby Vision work), so this wants a measurement on a real HDR10+ panel viaaetherctl servefirst, not a route.The probe work above is what that measurement needs anyway: it is how a session knows it is looking at HDR10+ content before it decides what to serve.