Preserve the original and choose one of three paths

Treat the phone original as a read-only source and write every inspection or conversion to a new candidate path. Record the capture device and mode, original transfer route, any messaging or editing step, available camera/export setting captures, and a trustworthy reference playback. ffprobe reports declarations carried by the file and bitstream. Those fields can expose absence or contradiction, but they do not by themselves prove the transfer actually used by the pixels. A gray, dark, or blown-out appearance also cannot establish the source type.

Evidence conclusionAction for this pass
Provenance and declarations jointly confirm HDR pixelsPerform external HDR-to-SDR tone mapping, retain the original, and create a new SDR candidate.
Independent provenance or a reliable reference proves correct SDR pixels with wrong declarations onlyCorrect properties only, then probe and play the result; do not tone-map the pixels.
Provenance is absent, or provenance, declarations, and reference conflictMark unknown; stop guessing transfer, gamut, or range and recover the original or a trustworthy record.

The second path requires independent evidence, such as a controlled SDR export record for that file or a traceable comparison with a trusted SDR reference. “It looks better after retagging” is insufficient. The third path protects the original from another unsupported transformation.

If controlled SDR export records prove the second branch contains BT.709 limited-range pixels with wrong properties only, this filter-only value may be used for a new re-encoded candidate:

setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709:range=limited

It marks properties without transforming pixels. However, when used through -vf, the picture is decoded, filtered, and re-encoded with a separately selected video encoder. That may introduce a lossy generation and must not be described as stream copy or lossless container-header repair. Retain the original, record encoder settings, then probe and actually view the candidate.

Record declarations and tool availability

Write the input path into the record, then run a read-only probe:

ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,pix_fmt,color_range,color_space,color_transfer,color_primaries -of json INPUT.mov

Preserve the raw JSON as a probe observation; do not rewrite it as pixel truth. Record the build and filters:

ffmpeg -hide_banner -version

ffmpeg -hide_banner -filters | grep -E '(^|[[:space:]])(zscale|tonemap|setparams)[[:space:]]'

In the FFmpeg documentation, zscale depends on a build enabled with libzimg, tonemap expects floating-point linear-light input, and setparams marks frame color properties without changing pixels. The processing order is therefore: linearize with the confirmed input transfer and color information, tone-map in floating-point linear light, then convert to an explicit BT.709 SDR output and encode. Every input property must come from confirmed evidence for the current source; a phone brand or an “HDR” label does not supply a transfer curve, gamut, or range.

This article avoids a universal command that forces unknown material through fixed parameters. The local writing environment used FFmpeg 9.0.1 without zscale / libzimg, so no HDR conversion or converted output was tested here. Check filter availability before execution. If required capabilities are absent, use a color-managed application that explicitly supports the confirmed input and SDR destination, or an approved build.

For confirmed HDR, map evidence to this exact filter plan

The following is a docs-only, build-dependent, unexecuted hypothetical plan. It applies only when independent records have confirmed every input fact: BT.2020 primaries, BT.2020 non-constant-luminance matrix, PQ transfer, limited range, and an actual signal peak of 1000 cd/m². This example selects npl=100 for zscale’s nominal peak luminance parameter. Separately, the pinned FFmpeg signal-peak helper defines REFERENCE_WHITE=100 and divides MaxCLL or mastering-display maximum luminance by that constant. Record the npl parameter and normalization constant separately: this example uses the same numeric value and derives peak=10 from 1000/100. Selecting this candidate premise remains a bounded article design, not a universal input inferred from a phone model.

Confirmed fact or author choiceCorresponding filter item and meaning
BT.2020 primariescolor_primaries=bt2020 in setparams marks the known input; primariesin=2020 tells zscale how to read it.
BT.2020-NCL matrixcolorspace=bt2020nc and matrixin=2020_ncl express the same confirmed matrix.
PQ transfersetparams marks known PQ with color_trc=smpte2084. The following zscale does not guess a transferin literal; its documented default reads the input transfer property before transfer=linear requests linearization.
Limited rangerange=limited and rangein=limited come from independent source records, not appearance.
Actual signal peak 1000 cd/m²; example npl=100; implementation constant REFERENCE_WHITE=100npl=100 is the example’s zscale nominal peak luminance parameter. REFERENCE_WHITE=100 is a separate constant in the pinned implementation. tonemap peak=10 is this example’s normalized 1000/100 signal ratio. Record these as a processing setting, an implementation constant, and a signal peak respectively.
Mobius selected by the author for the first visual candidatetonemap=mobius is a creative choice awaiting actual viewing, not a metadata-derived result or claimed universal default.
BT.709 limited-range yuv420p output targetThe final zscale requests 709 primaries/transfer/matrix and limited range, format selects yuv420p, and the final setparams marks the output properties.

The next single line is the exact filter-only value passed as the argument after -vf; it is not a complete ffmpeg command:

setparams=color_primaries=bt2020:color_trc=smpte2084:colorspace=bt2020nc:range=limited,zscale=primariesin=2020:matrixin=2020_ncl:rangein=limited:transfer=linear:npl=100,format=pix_fmts=gbrpf32le,tonemap=tonemap=mobius:peak=10,zscale=primaries=709:transfer=709:matrix=709:range=limited,format=pix_fmts=yuv420p,setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709:range=limited

The real command must separately choose a real video encoder and a fresh candidate path. Using -vf decodes, filters, and re-encodes the picture; it cannot use -c:v copy. This article does not invent audio, container, or complete encoding options because those depend on the real handoff. Confirm zscale on the target machine, then pass the complete line above as one -vf argument for a new candidate. Save output ffprobe observations and perform the SDR playback review. No result exists here; local FFmpeg 9.0.1 lacks zscale/libzimg. If source color facts are absent or contradictory, return to the source-type unknown branch. If HDR color facts are confirmed but peak or another conversion parameter remains unverified, retain “confirmed HDR” and separately mark “numeric conversion plan pending.” Obtain trustworthy peak evidence or an explicitly reviewed grading decision before preparing that source’s plan; do not copy this graph meanwhile.

Original paper case: a rainy night-market clip

Thirty Seconds Under the Awning is a fictional teaching case written for this article. There is no real media, conversion, playback test, or successful result; filenames and observations are hypothetical.

A creator wants to place a phone-shot night-market passage into an ordinary SDR recap. The camera moves from lantern reflections on wet pavement to a vendor handing over a hot drink; a white price board, red awning, and steam sit behind. The creator says the first export “looks gray and the lanterns seem flattened,” but only a messaging-app copy named market-share.mov remains. Record that appearance as a symptom without declaring the clip HDR.

Hypothetical evidence stateDecision and next step
The phone original and capture record are recovered, and mode plus declarations consistently confirm HDRPreserve the original and create an SDR candidate from the confirmed input. During real viewing, inspect face and steam midtones, lantern and price-board highlights, wet-pavement blacks, and brightness continuity through the pan.
A controlled SDR export log proves the transferred file contains correct SDR pixels but was mislabeledCopy to a new candidate and correct only the proven-wrong properties. Probe again and play the entire candidate through an SDR path. This branch is unavailable without independent evidence.
Only the transferred copy remains, fields are absent or contradictory, and neither original nor record can be recoveredRecord “unknown.” Do not guess the input curve, try random tag repair, or perform HDR tone mapping. Request the camera original or a trustworthy export record.

The before decision changes tags from appearance alone, conflating pixel conversion with declaration repair. The after decision preserves evidence and enters exactly one branch. Even if the first branch produces a candidate, its status remains “awaiting SDR playback review.” It becomes a handoff file only after actual checks pass.

Real SDR playback decides the handoff

Record mechanical and viewing checks separately. Probing confirms that the candidate is readable, declarations are complete, and canvas and clock match the plan. Playback confirms whether content is usable. Play the complete program at normal speed at least once.

Review itemWhat to inspect in the actual candidate
MidtonesFaces, walls, steam, and shadow subjects are not globally crushed or lifted; adjacent SDR shots remain coherent.
HighlightsLamps, windows, white signs, and reflections avoid abrupt edges, broad featureless areas, and shot-to-shot jumps; retaining every HDR peak is not promised.
Skin and neutralsDo not approve from “more colorful.” Check stable skin and unintended casts on neutral objects.
Black and white levelsBlacks are neither crushed nor raised and whites do not ride the ceiling continuously, judged through the actual SDR range and playback path.
Shot sequenceCross every cut and exposure change so a pleasant isolated shot does not create brightness pulsing.
Sound and clockListen throughout and confirm duration, sync, and adopted content. Color conversion does not authorize incidental editorial or mix changes.

When review finds a problem, return to the current candidate's conversion settings and save another candidate. Keep failed candidates and records so the original is never overwritten and the newest unviewed file is not mistaken for an approved version.

Copy this verified-SDR handoff record

Provenance and original: Device/mode/transfer chain; read-only original path or hash; trustworthy reference.

Probe observations: Input ffprobe raw JSON path; missing or contradictory declarations.

Decision and evidence: Confirmed HDR pixels / confirmed SDR pixels with wrong tags / unknown; independent evidence.

Build and filters: Tool version; actual availability of zscale, tonemap, setparams, or equivalent capabilities.

Conversion record: Confirmed input; linearization, tone mapping, BT.709 SDR output stages and parameters; or evidence for property-only correction.

Candidate path: New file path or hash; original retained unchanged.

Output probe: Codec, pixel format, range, color space, transfer, and primaries declarations.

Actual viewing: SDR playback path; midtones, highlights, skin, neutrals, black/white levels, shot sequence, sound, and sync.

Downstream handoff: Only after actual checks pass, record “verified SDR candidate” and hand it to ordinary video-recap editing; otherwise mark revision pending or unknown.

At the pinned revision, ordinary Video Recap assembly may copy picture or encode H.264 yuv420p for subtitles, masking, overlays, or sizing. It does not implement this HDR tone-mapping chain. The transparent-foreground path specifically requires an already H.264 yuv420p master with explicit BT.709 / TV limited-range declarations. Complete and verify the external SDR candidate first, then use that new file as ordinary editing input. Originals, unknown files, and unviewed candidates do not enter this step.

FAQ

Do BT.2020 or PQ fields in ffprobe prove the pixels are HDR?

Not by themselves. They are file or bitstream declarations and must be evaluated with original capture, export, or trustworthy reference evidence. Declarations can be missing, wrong, or altered during transfer.

Can setparams fix every washed-out video by marking BT.709?

No. setparams marks properties without changing pixels. Property correction is appropriate only when independent evidence proves correct SDR pixels with wrong declarations; confirmed HDR pixels require a real conversion.

Do it with the skill

Use $video-recap to continue ordinary recap editing from this verified SDR file. The input path is the new file marked “verified SDR candidate” in my handoff record. Retain the phone original and conversion record, and do not treat HDR conversion as a native capability of this skill. Inspect the actual media first, plan the edit, narration, captions, and final video, then create a new output. Report the SDR candidate version used and the viewing or listening checks that remain.

Read the method: Ordinary Video Recap assembly and encoding boundary · Transparent composition requires an already prepared BT.709 SDR master · Official ffprobe documentation · Official FFmpeg zscale filter documentation · Official FFmpeg tonemap filter documentation · Official FFmpeg setparams filter documentation · Pinned FFmpeg reference-white constant · Pinned FFmpeg normalized signal-peak calculation

About Video Recap SkillsThe video-recap skill on GitHub

All guides