decktalk verify checks the final mp4 in three ways: section starts, cuts, and cues. A section that sets
carries_previous adds a fourth check, cut continuity. This page defines each measurement, limit, and result,
in the order that verify computes them.
[verify] in Configuration,
unless the page names another table. The defaults assume 25 fps, so one frame lasts 40 ms.
Which checks run
The verify stage insidedecktalk build checks section starts, cuts, and cut continuity, and no cue.
decktalk verify checks all four.
- With no cue named,
verifychecks every cue inbeats.json, in section order and then cue time. - With no cue named,
verifyskips a cue thatcues.jsonmarks"verify": false. - Named cues run in the order given, positional cues first, then each
--cue. - A named cue runs even when
cues.jsonmarks it"verify": false. --only Nkeeps only the cues of sectionN, named cues included. A named cue outside--onlygets no row.- Section starts, cuts, and cut continuity are always checked, whatever
--onlynames.
SECTION:CUE, such as 8:7.1checked. SECTION is the section number, not the scene
number. Section 8 of the scaffold plays scene 7. A name that does not start with a section number is an error (malformed check), and the command
exits 1.
Section starts
The start check confirms that each section opens on a visible frame.build/out/NN-section.mp4 of every earlier section in decktalk.toml.
The check reads one frame after each start.
verify reads only the sections in decktalk.toml. A file that matches NN-section.mp4 but belongs to no section,
such as one left after you renumber sections, is ignored, and verify warns about it.
YMAXis the brightest luma in the frame, andYAVGis the average. A mid-gray slide with no pixel above 60 readsBLACK.- The probe sits 0.2 s after the start, whatever the dip length. The default
[transition] dip_secondsof 0.15 s rounds to 4 frames, 0.16 s. If you set a longer dip, raiseafter_dip_secondstoo. - A clip section has a row too. In the sample, sections
05and07are the scaffold’s clip sections, which play slates.
Cuts
The cut check confirms that the narration is quiet just before each cut.cut at is where the section’s narration ends in the final mp4. The check measures the narration track in the
window before it.
- The check reads
narration.mp3alone. An underscore or a sound effect at a cut does not count. - A level of exactly -40 dBFS passes.
- A clip section has no row, because it has no narration. In the sample, sections
05and07have none. - A page section’s
hold_secondscomes after itscut at. In the sample, section09holds its last slide from 255.22 s to the end of the video.
Cut continuity
The continuity check confirms that the cut into a section that setscarries_previous = true in
decktalk.toml does not show. It runs only for those sections.
cut at is the section start in the final mp4. chg % is the changed share of two frames at
diff_level: the previous section’s last frame before any dip, and this section’s first frame after any dip.
- The frames sit outside the dip, so a fade through black is never a pop. A dip still shows on screen, so leave
the pair out of
[transition] dipsfor a cut that should not show. - The default of 0.1 matches
min_changed_percent, so any change that would pass as a reveal is a pop. - A section with no
NN-section.mp4, or whose previous section has none, gets no row. POP AT CUTis a certain finding, sodecktalk verifyanddecktalk buildexit 1 on it.
Cues
The cue check confirms that the picture changes on each cue, at the cue time.- Silent build
- Voiced build
verify computes them.
Note: The table prints the
a/v column only when at least one row has an a/v value. The JSON silent key
says whether the build was silent.
Cue time
The cue time is the start of the cued word plus the offset key. It is rounded to hundredths, in seconds after the section start.$starthas a word start of 0.0 s.$endhas the end of the section’s last spoken word.build/audio/beats.anchors.jsonkeeps each word start without the offset key, also rounded to hundredths.- A
$startcue with a negative offset key keeps its negative cue time.
8:7.1checked has a cue time of 10.03 s. Its at is 238.31 s.
Changed share
The changed share of two frames is the percentage of pixels whose luma differs by more than a level.verify scales both frames to probe_width by probe_height first, which is 480 by 270.
- Every share is on a 0 to 100 scale. At 480 by 270, a share of 0.1 is about 130 pixels.
- The share compares luma only. A reveal that changes only color at the same brightness reads
NO CHANGE. Mark such a cue"verify": falseincues.json. - Probes and control shares use
diff_level(40). The onset scan usesonset_diff_level(12). It also reads a block share atblock_widthbyblock_height(240 by 135). Onset and offset says why. - A pixel counts only when its difference is strictly greater than the level.
chg % of 0.83 for 8:7.1checked is about 1,070 of 129,600 pixels.
Reference frame
The reference frame is the picture before the cue. Every probe is compared with it.[verify] lead_secondshas an effect only above (max_offset_frames+ 1.5) /fps, which is 0.14 s at the defaults.[verify] lead_secondsis not the sidecar’slead_seconds. The sidecar value is the recorder’s wall-clock wait before narration t=0.- The lead keeps every frame inside the offset limit after the reference, so an early reveal is not already in
it. A reveal earlier than the reference frame usually reads
NO CHANGE. - With
[transition] page_fades_in = true, the default, the cut adds no fade-in to a page section. The floor of a page section is then its first frame. - A clip section gets the dip when a dip comes before it. So does a page section when
page_fades_inis false. - A row is
REFERENCE_CLAMPEDwhen its cue time is under 0.04 s with no fade-in, or under 0.20 s with the default fade-in. A named cue is still skipped. - The reference time is at least one frame before the cue. The reference frame can be closer when the cue time is off the frame grid.
8:7.1checked, the reference time is 238.31 - 0.14 = 238.17 s. The reference frame is the next frame on
the 40 ms grid, at 238.20 s. That frame is 110 ms before the cue, as the onset figure shows.
Probes
A probe is the frame a fixed delay after the cue.verify takes one probe for each delay in probe_delays.
- The section end is the next section’s start in the final mp4, or the video length for the last section.
- The later probe catches a slow reveal.
- The row is
TOO_CLOSE_TO_ENDonly when every probe is dropped. At the defaults, that is a cue less than 0.75 s before the section end.
Control share
The control share measures motion that has nothing to do with the cue. Each probe gets its own control share.Probes run forward from the reference, and two back-to-back control spans run back from it.
- Each span compares only its first and its last frame.
- A span that starts before the floor is dropped. If both spans are dropped, the control share is 0.
- Steady motion, such as a camera push, shows in both spans. An earlier reveal that is still finishing shows in only one, so the smaller share leaves it out.
Close cues
A cue close to another cue gets probes that fit the gap between them. Otherwise the other cue’s reveal would count as this cue’s change, or as motion in every control span.- A probe that is not spoiled is used as it is. A cue whose probes are all unspoiled is measured exactly as the sections above describe.
- A fitted probe has shorter control spans, because each span still lasts as long as the probe’s span.
- Two probes fitted to the same delay count once. The probes are then taken in order of delay.
- If no probe can be fitted,
verifyusesprobe_delaysas they are. - A cue within the reference lead of another is part of the same reveal, so it is not a neighbor.
- Every cue of the section in
beats.jsoncan be a neighbor, including a cue that the run does not check. verifylogsanother cue is close, so the probes are fitted to <delays> s after the cuefor each such cue.
Change test
The change test decides whether the picture changed because of the cue.- If the reported probe fails the change test, the row is
NO CHANGE. - A row that passes by a thin change reads
THIN CHANGE?instead ofchanged. The change is thin when the changed share is belowthin_change_factortimesmin_changed_percent, or the margin is belowthin_change_factortimesmin_margin_percent. At the default factor of 3, both limits are 0.3. THIN CHANGE?is an uncertain finding. The cue passed, but a slightly smaller reveal or a little more motion would fail it.verifyexits 1 on it only with--strict. Setthin_change_factorto 1 to turn it off.- The
chg %andctl %columns show the reported probe, which is not always the probe that changed most. min_changed_percentapplies to the raw changed share. A camera push raises that share too.
8:7.1checked has a changed share of 0.83 and a control share of 0.00. Its margin is 0.83, so it
passes, and the change is not thin. The next cue, 7.1back, fires 1.44 s later, so verify fits the 1.5 s probe
to 1.3 s, as Close cues describes. Both probes have a margin of 0.83, so verify reports the
earlier 0.7 s probe.
Onset and offset
After a cue passes the change test,verify finds its onset, the first frame of the reveal.
verifymeasures the changed share of each frame from the reference frame up to the reported probe. It compares each frame with the reference frame, atonset_diff_level. It also measures a block share of each frame, the same comparison with both frames scaled toblock_widthbyblock_height(240 by 135).- The onset is the first frame after the reference whose share rises by at least
onset_percentover the frame before it, and whose block share is above 0. - If no frame rises that much, the onset is the first frame after the reference whose share is above a threshold, and whose block share is above 0.
- If no frame is above the threshold, the row has no onset.
The outlined frame is the onset, 10 ms after the cue and inside the 80 ms offset limit.
onset_diff_level (12), so the onset frame reads 1.24%. The chg % column reads 0.83%,
because the change test uses diff_level (40), at the probe 0.7 s after the cue.
- A rise is in percentage points of share. At 480 by 270, 0.01 points is about 13 pixels.
- At 240 by 135, each pixel averages an 8 by 8 block of a 1080p frame, the size of an H.264 transform block. The x264 encoder leaves a little ringing on a still picture in the one or two frames before a change: a few pixels, up to about 15 luma levels apart at 480 by 270. Alone, that ringing read as a reveal 40 to 100 ms before it happened, while the recording showed the reveal on time. Ringing is as much lighter as darker, so it cancels over a block and leaves the block share at 0. A reveal changes whole blocks, so its first frame has a block share above 0.
- A reveal changes the picture in a step. Steady motion, such as a camera push or a line that was already drawing before the reference, grows a little every frame. That motion does not count as an onset, but it can change frames before the onset.
- The onset threshold is far below
min_changed_percenton purpose. A fade starts on its cue but takes many frames to change 0.1 percent of the pixels. - The fallback catches a reveal that grows slowly, such as text that types in. At the defaults, its window ends 100 ms before the cue.
- The onset can come before the cue time.
- A cue with no onset reads
changed. Its offset column shows-, itsa/vis blank, and neither limit applies. NoNO_CLICKreason is set.
8:7.1checked has an offset column of +10 ms, so its onset frame is at 238.32 s.
The a/v value
After a silent build,verify also compares the picture with the sound in the final mp4. The click track of a
silent build has a click at every word start.
- The word start comes from
beats.anchors.json, so the offset key does not add toa/v. Ana/vof 0 means the picture changes one offset key after the click. - The offset column compares the picture with the cue time in
beats.json. Thea/vvalue compares the picture with the audio in the final file. - An
a/vnear 0 shows that these steps kept sound and picture together: the recording, the trim at narration t=0, the cut, the join, and the mix. - The search window never reaches past the cue’s own section, so the audio of a clip next to it is never the click.
- A sound effect or an underscore that is louder than the click inside the window becomes the click. A
decktalk build --silent --nomixleaves them out of the mix. - The clicks stay at -24 dBFS, because a silent build skips the loudness pass.
- If no sample reaches the level, the row gets the reason
NO_CLICK, and itsa/vis blank. The row keeps its offset result. NO_CLICKis usual for a$endcue whose last word is longer thanclick_search_seconds.$endanchors on the end of the last word, and clicks sit on word starts. A shorter last word gives ana/vthat is off by the word’s length.- A voiced build has no
a/v. Its JSONsilentisfalse, and everyav_msisnull.
8:7.1checked has an offset column of +10 ms and an a/v of +3 ms, so its click distance is +7 ms.
Limits
Two limits turn a measured row intoOFF CUE. Both apply to early and late values.
- The a/v limit is one frame wider, because the click carries encoding jitter too.
- The table prints only the result. Compare the offset column with the offset limit and
a/vwith the a/v limit to see which one failed. - A slower machine can put a reveal two or three frames late. Build in CI and offline shows how to widen the limits on one runner.
OFF CUE row.
OFF CUE because its offset column, -90 ms, is past the 80 ms offset limit. Its a/v of -118 ms
is inside the a/v limit.
Results
Each cue row gets its result from the first rule that matches.- If the run names no cue and
cues.jsonmarks the cue"verify": false, the row isskippedwith the reasonOPTED_OUT. - If a named cue is not in
beats.json, the row isUNRESOLVED. - If the section has no
NN-section.mp4, the row isskippedwith the reasonSECTION_NOT_ASSEMBLED. - If no reference frame fits, the row is
skippedwith the reasonREFERENCE_CLAMPED. - If every probe is dropped, the row is
skippedwith the reasonTOO_CLOSE_TO_END. A cue close to another cue gets fitted probes first. - If the reported probe fails the change test, the row is
NO CHANGE. - If the scan finds no onset, the row is
changed, with-in the offset column and noa/v. A thin change readsTHIN CHANGE?instead. - If the offset column is past the offset limit, the row is
OFF CUE. - After a silent build, if no click is found, the row gets the reason
NO_CLICKand keeps the result of rule 8. - After a silent build, if the
a/vis past the a/v limit, the row isOFF CUE. - Otherwise, the row is
changed, orTHIN CHANGE?when the change is thin.
OFF CUE, NO CHANGE, UNRESOLVED, BLACK, SPEECH AT CUT, and POP AT CUT are certain findings, so decktalk verify
exits 1. THIN CHANGE? is an uncertain finding, so verify exits 1 on it only with --strict. A skipped row
never fails. Exit codes lists the codes for every
command.
Skipped rows
A skipped row measured nothing. The table printsskipped, the reason, and a short note.
Note:
NO_CLICK is a reason on a measured row, not a skipped row. The a/v value explains it.
A reveal that is too small or too slow for a frame difference, such as a thin arrow, can fail the change test.
Mark such a cue "verify": false, and check it by eye with decktalk shots --step ID --cue CUE.
JSON fields
decktalk verify --json prints one JSON object on stdout. The measurements are under the verify key.
Note: Seconds are rounded to three decimals, and shares to two. A skipped row has null measurements.
Related
- Run the command and read its options: CLI
- Fix a failed check: Troubleshooting
- Change a limit: Configuration