Demo Video Assembly Skill
This skill assembles a narrated demo video from ordered visual segments and matching narration audio. It is designed for first-pass walkthrough videos that combine captured prototype frames or clips with per-segment voiceover WAV files.
Overview
The workflow takes a manifest that describes each segment, resolves the visual
source, and uses FFmpeg to render each segment into a normalized video clip
before assembling the final MP4. It supports hard cuts by default or optional
synchronized video and audio fades. The narration track is muxed from WAV files
so the output can be reviewed without a separate video-editing tool.
Manifest Schema
Use a segments.yml manifest with optional top-level output settings and an ordered list of segments. Each entry describes a visual source and the narration audio to combine for that portion of the video. All paths resolve relative to the manifest file.
output: ./output/demo.mp4 # optional; destination path for the assembled MP4
resolution: 1280x720 # optional; default 1280x720
fps: 24 # optional; default 24
transition: # optional; omit for hard cuts
type: crossfade
duration: 0.5
fade_in: true
fade_out: true
segments:
- type: frame
visual: ./frames/intro.png
narration: ./audio/intro.wav
duration: 4.5
- type: clip
clip: ./clips/interaction.mp4
narration: ./audio/interaction.wav
Top-level fields
output sets the destination path for the assembled MP4, resolved relative to the manifest; the --output or -OutputPath argument overrides it when supplied
resolution controls the output width and height in WIDTHxHEIGHT form (default 1280x720); the --resolution or -Resolution argument overrides it
fps sets the frame rate applied when rendering each segment (default 24); the --fps or -Fps argument overrides it
transition enables synchronized scene and narration fades; omit it or set it
to none to retain hard cuts
Transition fields
type accepts crossfade or fade, which both select FFmpeg's fade
cross-transition
duration sets every opening, scene, audio, and closing fade in seconds
(default 0.5)
fade_in controls the opening video and audio fade (default true)
fade_out controls the closing video and audio fade (default true)
The assembler adds silent handles around speech and holds the matching endpoint
pictures. Crossfades overlap those handles, not adjacent spoken instructions;
do not pre-pad narration to compensate. The normalized segments must exceed
twice the transition duration. Output duration is the sum of unpadded narration
durations plus one transition duration per scene boundary and per enabled
opening/closing fade. With transitions disabled, no handles are added.
Segment fields
type identifies whether the segment is a still image (frame) or a motion clip (clip)
visual points to an image file for a frame segment
clip points to a motion clip file for a clip segment
narration points to the WAV file generated from narration text (the script also accepts narration_wav as an alias)
duration is optional and overrides the inferred duration when you want a fixed segment length
Quick Start
Use the bash or PowerShell wrappers to invoke the assembler from the skill directory.
scripts/assemble-video.sh --manifest examples/segments.yml --output ./output/demo.mp4
scripts/Invoke-AssembleVideo.ps1 -ManifestPath examples/segments.yml -OutputPath ./output/demo.mp4
Parameters Reference
The assembly step accepts the following high-level controls:
--manifest or -ManifestPath selects the YAML manifest to process
--output or -OutputPath sets the destination MP4 path
--fps or -Fps controls the output frame rate for rendered segments
--resolution or -Resolution controls the output width and height in the form WIDTHxHEIGHT
--timeout or -TimeoutSeconds sets the maximum seconds for each ffprobe or ffmpeg invocation (1-86400, default 600); a timed-out step stops the run with an error that names the step
duration per segment lets you override the inferred length when narration timing is known in advance
Failure Behavior
The assembled MP4 is written to a temporary file next to the destination and
moved into place only after FFmpeg succeeds. A failed, timed-out, interrupted,
or invalid transition run leaves no partial MP4 at the output path and keeps any
existing file there unchanged. A successful run replaces an existing file at
the output path. Temporary segment files are always removed.
Narration Quality
Narration quality is the single biggest driver of how polished the final video feels. Generate narration with neural voices from Azure AI Speech (part of Azure AI Foundry) through the tts-voiceover skill, for example en-US-Andrew:DragonHDLatestNeural or en-US-Jenny:DragonHDLatestNeural. These produce natural, presentation-grade narration.
This skill assembles video locally with FFmpeg and does not generate video with an AI model. When the narration is synthetic, tell viewers that the voice is AI-generated. HVE demo-material CI publishes silent videos; internal silent timing tracks are removed during finalization.
See the tts-voiceover skill for the neural voice catalog, --voice and --rate controls, and Azure authentication (Microsoft Entra ID recommended, or a key).
Reuse Bridge
This skill is intentionally designed to fit into the existing media workflow:
tts-voiceover provides the narration WAV files that this skill muxes into the final output; prefer its Azure AI Speech neural voices for production-quality narration
vscode-playwright provides the frame-capture source for prototype walkthroughs and screen-based demos
Prerequisites
FFmpeg and ffprobe must be available on your PATH.
Linux
sudo apt update && sudo apt install ffmpeg
macOS
brew install ffmpeg
Windows
winget install FFmpeg.FFmpeg