Files
hyperframes/packages/core/src/hdrToneMap.ts
T
Miguel Ángel ada9932d4e fix(cli): a long video copy reports progress, resumes in 15 s pieces and stops only when stalled (#6307)
* fix(cli): a long video copy reports progress, resumes in 15 s pieces and stops only when stalled

* perf(core): a video copy's size box follows the stage within a fifth, not a half

* docs(cli): shorten two comments to the line limit

* fix(cli): the copy heartbeat drops a finished copy; size budgets follow the stall and progress code

* chore: size budgets rise by this change's measured bytes
2026-10-25 13:53:03 +00:00

44 lines
1.8 KiB
TypeScript

export interface ToneMapSourceColour {
colorSpace?: string;
colorPrimaries?: string;
colorTransfer?: string;
}
function known(value: string | undefined): string | undefined {
return value && value !== "unknown" && value !== "reserved" ? value : undefined;
}
/**
* HDR to SDR BT.709 with zscale and hable. zscale reads each frame's own tags, so only those the
* first frame lacks are set: from ffprobe, which may see the container's, else BT.2020.
*/
export function hdrToSdrToneMapFilter(
probed: ToneMapSourceColour,
firstFrame: ToneMapSourceColour,
size?: { width: string; height: string },
): string {
const fill = (key: keyof ToneMapSourceColour, fallback?: string) =>
known(firstFrame[key]) ? undefined : (known(probed[key]) ?? fallback);
const tags = [
["colorspace", fill("colorSpace", "bt2020nc")],
["color_primaries", fill("colorPrimaries", "bt2020")],
["color_trc", fill("colorTransfer")],
].filter(([, value]) => value);
const setTags = tags.length ? `setparams=${tags.map((tag) => tag.join("=")).join(":")},` : "";
const resizeBeforeToneMap = size ? `w=${size.width}:h=${size.height}:` : "";
return `${setTags}zscale=${resizeBeforeToneMap}t=linear:npl=100,tonemap=hable:desat=0,zscale=p=bt709:t=bt709:m=bt709:r=tv`;
}
/** ffmpeg args printing the first shown frame's tags; ffprobe's packet-bounded reads miss edit-list pre-roll. */
export function firstFrameColourArgs(videoPath: string): string[] {
return [
...["-hide_banner", "-nostats", "-i", videoPath, "-map", "0:v:0", "-frames:v", "1"],
...["-vf", "showinfo", "-f", "null", "-"],
];
}
export function parseFirstFrameColour(stderr: string): ToneMapSourceColour {
const tags = / color_space:(\S+) color_primaries:(\S+) color_trc:(\S+)/.exec(stderr);
return tags ? { colorSpace: tags[1], colorPrimaries: tags[2], colorTransfer: tags[3] } : {};
}