Ahosting Logo
Knowledge Base

Using ffprobe to Inspect Media Files

Three checks that stop a batch job dying halfwayRun these before processing anythingDoes it open at alla truncated or corrupt filefails here, cheaplyWhat codecs are insidewhich decides whether you needto convert at allDuration, resolution, streamsso the command you buildactually fits the fileSingle values for scriptsask for one field rather thanparsing the whole outputJSON outputwhen a script needs structurerather than textThe payoffa batch that skips bad filesinstead of stopping on oneValidating first turns a batch job that dies halfway into one that logs the bad file and carries on.

ffprobe comes with FFmpeg and answers questions about a media file: what codecs it uses, how long it is, what resolution, how many streams. It reads the file and reports; it never modifies anything.

It is the tool to reach for before writing an FFmpeg command, and the one most people skip, which is why so much time gets spent debugging a conversion whose input was not what was assumed.

The one command worth memorising

ffprobe -hide_banner input.mp4

-hide_banner removes the build information, leaving the part you wanted. You get every stream with its codec, resolution, frame rate, and the file's duration and bitrate.

That is enough for most questions, and it takes a second on a file of any size because ffprobe reads the header rather than the whole file.

Getting one value for a script

The default output is for reading. For a script, ask for exactly one thing:

ffprobe -v error -show_entries format=duration \
-of default=noprint_wrappers=1:nokey=1 input.mp4

That prints the duration in seconds and nothing else, which can go straight into a variable.

-v error suppresses everything except real errors, so a malformed file still tells you rather than failing silently.

The same shape works for resolution:

ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height \
-of csv=s=x:p=0 input.mp4

Which prints something like 1920x1080.

JSON output

For anything more than one value, ask for JSON and parse it properly:

ffprobe -v error -print_format json -show_format -show_streams input.mp4

This is the right approach in a PHP or Python pipeline. Parsing ffprobe's human-readable output with string matching works until it meets a file with an unusual stream layout, and then it silently produces wrong values.

Checking a file before processing it

The habit that prevents most pipeline failures.

Before converting, confirm the file is what you think. Three checks cover nearly everything.

Does it have a video stream? An audio file with cover art has a video stream that is a single image, and a conversion treating it as video produces something strange.

Does it have audio? A file with no audio stream fails any command that maps one.

Is the duration sane? Zero or absurdly large means a truncated or corrupt file, and processing it wastes the time before it fails.

Running those three checks first is what turns a batch job that dies halfway into one that skips a bad file and continues. Building a batch pipeline explains the wider structure.

Multiple streams

A file can hold several video, audio and subtitle streams: multiple languages, commentary tracks, different subtitle formats.

ffprobe lists them with their indexes, and those indexes are what you use with FFmpeg's -map to choose. Without checking first, a conversion takes the default stream, which may not be the one anyone wanted.

This is the usual explanation for a converted film that came out with the wrong language. Working with subtitles walks through selecting them deliberately.

Frame rate: two numbers

ffprobe reports r_frame_rate and avg_frame_rate, and they can differ.

The first is the base rate the container declares. The second is what the frames actually work out to. A variable frame rate file, which is what phones and screen recorders produce: shows a difference between them.

That difference matters, because variable frame rate is the usual cause of audio drifting out of sync after a conversion. Normalising to a constant rate during the conversion fixes it, and knowing to do so requires having looked.

Rotation metadata

A phone video is often recorded sideways with a rotation flag telling the player to turn it. ffprobe shows that flag in the stream's metadata.

If a converted video comes out rotated when the original played correctly, that flag was dropped. Checking for it before converting tells you whether you need to handle it, and it is invisible in any player, because players honour it.

Counting frames

Frame count is not always in the header. To get an exact number:

ffprobe -v error -count_frames -select_streams v:0 \
-show_entries stream=nb_read_frames \
-of default=nokey=1:noprint_wrappers=1 input.mp4

This reads the whole file, so it is slow on a large one. Use it when you need certainty, and use the duration for everything else.

In a web application

ffprobe is the right tool for validating uploads: check the duration against your limit, the resolution against what you support, and that the streams are what you expect, before accepting the file.

Never pass a user-supplied filename into a shell command without escaping it. A filename is untrusted input, and this is the classic route to command injection in a media pipeline.

Set a timeout too. A deliberately malformed file can make a media tool work far longer than any real file would. Getting started with FFmpeg goes over running it on a server.

Read a file you did not create

Files arriving from customers, phones and other tools are frequently not what their extension claims, and the inspection answers that before anything else touches them.

ffprobe -v error -show_entries format=format_name,duration,size,bit_rate -of default=nw=1 input.mp4
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,profile,width,height,pix_fmt -of default=nw=1 input.mp4

Three things are worth reading immediately. The container format, which may not match the extension. The pixel format, since anything other than the common one fails to play in browsers even when the codec is right. And the profile, because a high profile plays on a computer and not on older devices.

A file that reports no duration or a duration of zero is truncated, and processing it wastes the whole job. That check alone belongs at the front of any pipeline.

Spot the file that will cost you

Before queueing an encode, a few numbers predict whether it will take minutes or hours.

ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate,nb_frames,bit_rate -of csv=p=0 input.mp4
ffprobe -v error -show_entries format=duration -of csv=p=0 input.mp4

A very high frame rate, an unusually large resolution, or a bitrate far above what the content needs all mean a long job. So does a long duration, obviously, and the combination is what matters rather than any single value.

Knowing this in advance lets you reject or reroute the expensive files rather than discovering them when the queue stops moving. Running FFmpeg jobs in parallel deals with the queue.

Verify the output, not only the input

An encode that finishes without an error can still have produced something wrong, and comparing the two files takes one command each.

for f in input.mp4 output.mp4; do
  printf '%-14s ' "$f"
  ffprobe -v error -show_entries format=duration:stream=codec_name,width,height -of csv=p=0 "$f" | tr '\n' ' '
  echo
done

Compare duration first. A result meaningfully shorter than the source means the encode stopped early, which happens on a damaged input and produces no error worth noticing.

Then compare dimensions and codec against what you asked for. A stream copy that silently became a re-encode, or a scale filter that produced an odd number of pixels, both show here and neither shows in the exit status.