DEV Community

Kling4 AI
Kling4 AI

Posted on

Pre-flight checks for lip-sync inputs with ffmpeg (500 MB / 10 MB, run on test files)

Wan-family lip-sync tools ask for a person video and a target audio track. Public tool pages list practical caps such as video up to about 500 MB (MP4/MOV) and audio up to about 10 MB with short duration limits. Uploading first and learning from a red banner wastes time. This post builds a tiny local checker—and shows how to pair it with ffprobe—using synthetic files only. No lip-sync job is submitted.

For the product door after your files pass, see AI Video Lip Sync.

Inputs: video + audio
Diagram made for this article.

Limits we encode

From the public tool copy (verify on the live page before you rely on a number):

  • Video: clear face/mouth preferred; MP4/MOV; size on the order of 500 MB max
  • Audio: speech track; common compressed formats; size on the order of 10 MB max; duration caps apply

The script below treats those size/extension rules as constants you can edit when the page changes.

Limit table
Diagram made for this article from public tool copy.

The checker

VIDEO_MAX = 500 * 1024 * 1024
AUDIO_MAX = 10 * 1024 * 1024
VIDEO_EXT = {".mp4", ".mov"}
AUDIO_EXT = {".mp3", ".wav", ".aac", ".m4a", ".ogg"}
Enter fullscreen mode Exit fullscreen mode

Run python3 preflight_lipsync.py --video take.mp4 --audio line.wav. Exit code is non-zero on failure so it can sit in a folder action or CI step. --self-test writes tiny synthetic files and should print OK lines.

Self-test output
Real self-test output on 2026-10-26. Not a model run.

Optional ffprobe companion

Size checks do not prove the mouth is visible or that the audio is mono speech. After the script passes, a human (or a separate probe) should confirm duration, resolution and track layout:

ffprobe -hide_banner -show_streams -of json take.mp4
ffprobe -hide_banner -show_streams -of json line.wav
Enter fullscreen mode Exit fullscreen mode

Reject clips where the mouth leaves frame, or where music beds drown the speech you want synced.

Probe then upload
Diagram made for this article.

Failure playbook

  • Oversize video: re-encode with a milder bitrate; do not upscale.
  • Oversize audio: export speech-only WAV/MP3 at talk-radio bitrates.
  • Wrong extension: remux; do not rename blindly.
  • Empty file: fix the export path before debugging the model.

Playbook
Diagram made for this article.

Lip sync tool page
Screenshot: lip-sync tool page capture 2026-10-26 Asia/Shanghai.

Document the exact limits from the page on the day you ship; constants drift.

Why not call the API here

Lip-sync quality depends on the face crop and the speech take. Publishing fake "before/after" mouths would invent results. A pre-flight script is the honest technical artefact: it fails closed on files the UI would reject anyway.

Wiring the script into a folder action

On macOS or Linux you can wrap the checker in a one-liner that refuses to open the browser until exit code 0:

python3 preflight_lipsync.py --video "$1" --audio "$2" && open "https://wan30.io/ai-lip-sync"
Enter fullscreen mode Exit fullscreen mode

Replace open with xdg-open on Linux. The point is behavioural: people stop treating the upload page as the validator.

What ffprobe should make you reject

  • Video duration far longer than the speech you care about (the mouth will idle or invent).
  • Audio with a huge leading silence—trim before upload.
  • Stereo beds where the vocal is hard-panned; bounce a centre speech track.
  • Variable frame-rate webcam files that some UIs accept then process oddly—re-wrap to constant frame rate when your editor supports it.

None of these are in the size script on purpose. Size is machine-checkable; taste still needs a human.

Synthetic self-test vs real takes

--self-test only proves the script runs. Before a team adopts it, run it once on a real rejected file (oversize) and a real accepted pair, and paste both transcripts into the repo README. That anchors the constants to lived UI behaviour on a dated page capture.

Security note

The script reads local files only. Do not point it at production customer media inside a shared CI cache without retention rules. Lip-sync inputs are often recognizable faces and voices.

Pairing with ffmpeg loudnorm (optional)

After size checks pass, some teams run a loudness pass so speech sits at a predictable level:

ffmpeg -i line.wav -af loudnorm=I=-16:TP=-1.5:LRA=11 line_norm.wav
Enter fullscreen mode Exit fullscreen mode

This is optional and not required by the lip-sync UI. It simply reduces "why is this take quieter than yesterday" noise when producers A/B voices.

Changelog discipline

When the public page changes the 500 MB / 10 MB figures, bump a LIMITS_DATE constant in the script and re-run self-test. Leave the old numbers in a comment so archaeology is easy.

Operators who only remember one rule from this article should remember the checklist near the end: it is short on purpose so it fits on a sticky note next to the monitor. Long essays do not survive busy publish days; a five-line gate does. Revisit the sticky note whenever a shot fails for a reason you have already written down once.

Acceptance tests you can automate tomorrow

Beyond size, a future version of the script can shell out to ffprobe -v error -show_entries format=duration -of csv=p=0 and reject pairs where audio is more than a second longer than video without a documented hold. Start with printed warnings before you turn them into hard failures so editors learn the new rule.

Keep fixtures under code/fixtures/ with a manifest JSON listing expected OK/FAIL. New contributors run one command and see green/red without inventing files.

Finally, schedule a ten-minute review after the first accepted shot: what limit surprised you, which checklist line caught a real error, and which line you can delete because it never fires. Living checklists stay short; abandoned ones grow until nobody reads them.

Wrap-up

Validate extensions and sizes locally, probe streams, then open the lip-sync workspace. The model should see a face and a clean voice—not your first experiment with file too large.

Top comments (0)