Inspect media
Give it a video, an audio file or a still. You get its streams, codecs, size, duration, frame rate and, when you ask for it, the exact frame count. Run it on the source when a job starts, and on every render before you trim, measure or splice it. Frame counts and trims are only right at the frame rate you checked.
Run it
Everything at once (JSON):
exec ffprobe -v error -show_entries "stream=index,codec_type,codec_name,width,height,pix_fmt,r_frame_rate,avg_frame_rate,nb_frames,duration,sample_rate,channels:format=duration,size,bit_rate" -of json <file>
Exact frame count (decodes the stream; slower, but nb_frames from the
header can be missing or wrong):
exec ffprobe -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames -of csv=p=0 <file>
Is there an audio stream? It prints one index per audio stream, and nothing when there is none:
exec ffprobe -v error -select_streams a -show_entries stream=index -of csv=p=0 <file>
A still's size:
exec ffprobe -v error -show_entries stream=width,height -of csv=p=0 <image>
What you get
r_frame_rateis a fraction (24/1,30000/1001). Divide it out. When it differs fromavg_frame_rate, the file has a variable frame rate. Conform it before cutting by frame.- Stream
durationand formatdurationcan differ: the audio can run longer than the picture. Cut by video frames, not by the format's duration. widthandheightare the stored size. A 2K 16:9 still from an image model can come back 2752×1536 (1.792, not 1.778), so fit it to 16:9 before pinning it as a frame.
Before a trim: the frame rate
- Check every render's frame rate against the shot's before trimming or counting frames. Providers change it. One lip-sync model returns 25 fps from a 24 fps input; convert back to 24 without blind resampling. Another provider returned 24 fps motion padded to 30 fps. H3 Max returns 24 fps, a little longer than the length asked (ask for 1 s, get 1.2 to 1.4 s), so count the frames before trimming.
- The shot's frame count is
round(seconds × fps)at the source's rate. Cut every chunk of a long track on that one grid. Trimming each piece with a rounded-tdrifted the picture 313 ms behind the audio over 17 chunks.
Before a mux: the audio stream
- Check for an audio stream before you map one. Muxing
-map 1:afrom a file with no audio fails. Use-map 1:a?when it is optional. - A video with no audio stream gets an empty transcript. Skip speech-to-text for it.
- Check a reference audio's duration before sending it. A window under 2 s is padded to 3 s for fal's reference-audio minimums. H3 Max lip sync needs at least 5 s, so pad it with silence rather than taking a longer window.
Check it
After any cut or splice, count the frames again with the exact frame count
command. They must equal the planned count. On mp4 output, ffmpeg 7 can drop the
last frame of a trimmed segment when no output rate is set. If a render was
measured against pinned stills, python-check-pinned-frames reports the same
frame count.


