# Assemble an ordered PNG sequence into a review clip

Build a review clip from existing still frames with an explicit two-FPS schedule. This is forward sequence assembly, with no generated in-between frames or reverse video frame extraction.

This complete kit uses Node standard-library modules only. Install/use your own FFmpeg; the binary is not redistributed. Set FFMPEG_BIN to its absolute path if it is not on PATH.

Run from this directory and select a NEW output directory. Existing output directories are refused. On a memory-managed workstation, put its local serialization wrapper before the Node command. No provider credentials or off-device fixture requests are used.

```sh
node run-recipe.mjs --out ./my-run
```

Recorded execution: 9 checks passed, including 4 expected rejections. Node v24.18.0. ffmpeg version 6.0 Copyright (c) 2000-2023 the FFmpeg developers

Preparation

- Prepare Node.js and FFmpeg, setting FFMPEG_BIN when needed. The inputs are three original 160×90 PNGs with red, green and blue fields plus a white edge marker.
- Read sequence.json: indices 1–3 are bound to frame-0001.png through frame-0003.png and their SHA-256 values, at a declared two FPS.
- Use a new output directory and require the exact input filename set. A visually plausible frame in the wrong sequence is still a manifest failure.

## 01. Freeze the image sequence

Generate the three practice PNGs and an explicit ordered manifest. Each row contains an index, exact basename and SHA-256. Do not rely on filesystem iteration order, which is not the editorial order of shots.

## 02. Reject gaps, extra names and wrong bytes

Before encoding, replay a swapped-row manifest, remove frame 2 temporarily, add frame 5 and supply a wrong hash. Each invalid case fails; finally blocks restore the original complete sequence before the actual encode.

## 03. Encode using the declared input rate

Use -framerate 2 -start_number 1 with frame-%04d.png and an explicit three-frame output cap. The encoder creates review.mp4 using yuv444p and no audio. Each source PNG contributes one frame; the intended duration is 1.5 seconds.

## 04. Verify frame count and colour order

Fully decode the MP4 and confirm exactly three 160×90 RGB frames. Sample the central field of every frame against its intended red/green/blue input within four channel values. The recorded worst difference is one; no interpolation was added.

Acceptance

- The three-row manifest exactly matches contiguous frame-0001.png through frame-0003.png.
- Swapped manifest rows are rejected before encoding.
- A missing frame and an extra noncontiguous frame are separately rejected.
- An incorrect expected PNG SHA-256 is rejected.
- The actual review file decodes to exactly three 160×90 frames at the declared two-FPS assembly schedule.
- The central colours appear red, green and blue in order within four channel values; all nine checks pass.

Troubleshooting

The clip contains the right images in the wrong order. Correct only the manifest order and its index-to-filename binding, then rerun preflight before encoding.

Encoding stops early because a numbered frame is missing. Restore the missing original PNG at the exact basename and hash. Do not silently duplicate its neighbour.

The review plays too quickly. Change only the declared input framerate and document the new expected duration; preserve the three input identities and rerun frame-order checks.

Public receipt copies normalize only local checkout paths and include publicPathNormalization with the original receipt SHA-256; raw originals are retained outside public assets. The actual receipts are under executed/. failure-receipt.json lists expected negative cases, not failed-suite assertions. Original tiny fixtures are generated again into your new output directory; shipped executed inputs and outputs let you inspect the recorded run. Subprocesses have a 15-second timeout, an 8-MB output cap, argument arrays and no shell. Temporary raw buffers and local HTTP servers are cleaned in finally.

checksums.json records every distributed file except itself and the archive, avoiding self-reference. The ZIP includes that same checksum list. Recompute hashes after download; comparison requires trusting the expected list. execution-receipt.json records executed script hashes before and after the run. The complete kit archive is also bound by the publishing catalog asset inventory.

Keep sequence.json beside the assembled review. This fixture proves the chosen order and frame count; it does not create motion, extend frame holds beyond the chosen rate or qualify a distribution codec.

Sources read on 2026-10-14

- FFmpeg image2 format: image sequence inputs: https://ffmpeg.org/ffmpeg-formats.html#image2-1
- FFmpeg command-line documentation: maps, codecs and error exit: https://ffmpeg.org/ffmpeg.html
- Node.js crypto API: SHA-256 hashing: https://nodejs.org/api/crypto.html
