Nodes/comfyui-vae-float32/ANDRO Video QC
ComfyUI Node

ANDRO Video QC

Before a generated clip is accepted, someone has to actually check it

By AndreiOrehov·Created 28 days ago·Updated about 18 hours ago· 4
ANDRO Video QC
  • images
  • report
  • json
  • sheet
  • pass
source_path
expected_fps0.000
expected_frames0
expected_width0
expected_height0
held_ratio0.20
flash_jump0.25
black_level0.030
write_jsonfalse
json_prefixqc/report

Everything else in the comfyui-vae-float32 pack asks what did our decode do. ANDRO Video QC is the odd one out and the most generally useful of the ten: it asks what did the file someone sent us actually contain - the ingest check a pipeline runs before a plate is accepted. Point it at a clip out of Runway, Seedance or Kling and it reports effective bit depth and which quantisation grid the values sit on, the share outside [0,1] and any NaN, black frames, flashes, flicker, duplicate and held frames, resolution / frame count / fps against what was ordered - and, with source_path set, codec, pixel format and the container's colour tags via ffprobe.

Because half of what it flags is a judgement call, it doesn't just hand you a verdict. It gives you a contact sheet of the flagged frames, each labelled with its number and reason, plus one of PASS / WARN / FAIL and a pass:BOOLEAN you can gate a downstream branch on without reading text.

What it catches that you'd otherwise miss

Two findings from the real clips it was built against, both 24 fps h264, are worth internalising:

WARN: untagged colour (space, transfer, primaries): readers will assume BT.601 / unknown transfer
WARN: 81 held frame(s) = 22.4% of the clip

Untagged colour is the normal state of AI-generated video, and it's the most expensive item on the list - the grade starts from the wrong primaries and nothing errors. And that period-4 held pattern means roughly one frame in four barely moves, so a 24 fps file is carrying about 6 fps of motion: it conforms, it plays, and it judders on the first pan. Both are invisible to a spot-check and obvious once measured.

Inputs that matter

  • images - the clip, straight off the loader. Read-only; wire the source on past this node.
  • source_path - the file the batch came from. Only with it can ffprobe report what the container claims, which is the half of ingest QC no pixel statistic reaches. Leave empty and everything else still runs.
  • expected_fps / expected_frames / expected_width / expected_height - what the shot list says; 0 means don't check. A generator that returns 96 frames for a 4 s 24 fps order has silently changed the edit.
  • held_ratio (0.2), flash_jump (0.25), black_level (0.03) - the sensitivity dials. Held is deliberately relative: a frame counts as held when it moves less than a fifth as much as the clip's own median - an absolute threshold would call a locked-off shot a stall and a whip pan clean.
  • write_json - also write the report to disk as JSON so batches of clips leave a diffable trail.

Outputs: report (Markdown, readable on canvas and pasteable into a delivery note), json (same data, complete frame lists), sheet (the contact sheet), and pass (true only on PASS - WARN and FAIL are both false, so you can branch on it without parsing text).

One caveat the node prints for itself: a yuv420p file reaches an IMAGE batch through a YUV→RGB matrix, which lands 8-bit values on a much finer grid - both test clips measured ~50–60k levels per channel. When the container states a depth, the container wins; the level count is describing the decode path.

Install

ANDRO Video QC ships in the comfyui-vae-float32 pack. ComfyUI-Manager → search comfyui-vae-float32 → install → restart, or:

cd ComfyUI/custom_nodes
git clone https://github.com/AndreiOrehov/comfyui-vae-float32

Restart, find it under ANDRO. Requires ffprobe on your PATH for the container half. If you generate clips for fun and never deliver them to anyone, most of this is overkill - but if you've ever accepted 96 frames of a 4-second order, or graded footage that turned out to be untagged colour, you're the target audience. It's the one node in this VAE-obsessed pack you'll reach for on clips that never touched a VAE at all.

CategoryANDRO

Inputs (11)

NameTypeDefaultDescription
imagesIMAGEThe clip to check, straight off the loader. Read only - nothing comes back out, so wire the source on to the graph directly.
source_pathoptSTRINGThe video file the batch was loaded from. With it, ffprobe adds what the CONTAINER claims - codec, pix_fmt, fps, and the colour tags - which is the half of ingest QC no pixel statistic can reach. Leave empty and everything else still runs.
expected_fpsoptFLOAT0.0000–240What the shot list says. 0 = do not check. Only checkable from ffprobe: an IMAGE batch has no timebase.
expected_framesoptINT00–1000000Expected frame count. 0 = do not check. A generator that returns 96 frames for a 4 s 24 fps order has silently changed the edit.
expected_widthoptINT00–16384Expected width in pixels. 0 = do not check.
expected_heightoptINT00–16384Expected height in pixels. 0 = do not check.
held_ratiooptFLOAT0.200.01–1A frame counts as HELD when its mean absolute delta to the previous frame falls below this fraction of the clip's median delta. 0.2 = 'moved less than a fifth as much as this clip normally moves'. Relative on purpose - an absolute threshold would call a locked-off shot a stall and a whip pan clean. The check switches itself off when the median delta is under 1/255, because there is then no motion to be held against.
flash_jumpoptFLOAT0.250.01–1Mean luma jump between neighbouring frames, in 0..1 units, that reads as a flash or a hard cut. Those frames are also excluded from the flicker figure, so one legitimate cut does not make an otherwise steady clip look unstable.
black_leveloptFLOAT0.0300–1Mean luma below which a frame is called black. Always a FAIL: a black frame in a generated clip is a dropped frame, not a creative choice.
write_jsonoptBOOLEANfalseAlso write the JSON report to disk, so a batch of clips leaves a trail that can be diffed later.
json_prefixoptSTRINGqc/reportPath under the ComfyUI output folder, without the extension.

Outputs (4)

NameTypeDescription
reportSTRINGThe verdict and one line per check, as Markdown - readable on the canvas and pasteable into a delivery note.
jsonSTRINGThe same measurements as JSON, with every flagged-frame list complete rather than truncated. For logging a batch of clips.
sheetIMAGEContact sheet of the flagged frames, each labelled with its number and why it was flagged - or eight evenly spaced frames when nothing was.
passBOOLEANTrue only on PASS. WARN and FAIL are both False, so this can gate a downstream branch without reading the text.