Skip to content

Identify HDR10+ before playback, and what that opens for a cdm4 supplemental-codec tag #579

Description

@superuser404notfound

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions