Originally published at ffmpeg-micro.com
Every video file lies to you a little. The container says MP4, but the codec might be MPEG-2. The filename says 1080p, but the actual resolution is 720x480. Running ffprobe before you process a file tells you exactly what you're working with. It saves you from wasting compute time and API credits on files that will fail, produce garbage output, or need completely different settings than you assumed.
What ffprobe Does
ffprobe is a companion tool that ships with every FFmpeg installation. It reads a media file's container and streams without decoding the actual video frames, which makes it fast. You get back structured metadata: codecs, resolution, duration, bitrate, audio channels, frame rate, and more.
The command you'll use 90% of the time looks like this:
ffprobe -v quiet -print_format json -show_streams -show_format input.mp4
Breaking that down:
-
-v quietsuppresses the banner and warnings, so you only get clean data -
-print_format jsongives you machine-readable JSON instead of the default key=value dump -
-show_streamsreturns details for each stream (video, audio, subtitle tracks) -
-show_formatreturns container-level info like duration, bitrate, and format name
You can also point ffprobe at a URL instead of a local file. It'll pull just enough bytes to read the headers.
Reading the JSON Output
The JSON output from ffprobe has two top-level keys: streams and format. The streams array contains one object per track in the file.
{
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_type": "video",
"width": 1920,
"height": 1080,
"r_frame_rate": "30/1",
"duration": "124.500000",
"bit_rate": "5000000"
},
{
"index": 1,
"codec_name": "aac",
"codec_type": "audio",
"channels": 2,
"sample_rate": "44100",
"bit_rate": "128000"
}
],
"format": {
"filename": "input.mp4",
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"duration": "124.500000",
"size": "78125000",
"bit_rate": "5017600"
}
}
The fields that matter most for pre-processing decisions:
| Field | Where | What it tells you |
|---|---|---|
codec_name |
streams | Video codec (h264, hevc, vp9, av1) |
width / height
|
streams | Actual pixel dimensions |
duration |
format | Total length in seconds |
bit_rate |
format | Overall bitrate in bits/sec |
channels |
streams (audio) | Mono (1), stereo (2), surround (6) |
r_frame_rate |
streams (video) | Frame rate as a fraction |
Pre-Processing Checks That Save You Money
I've seen developers skip inspection and send files straight to a transcoding API. Then they're surprised when jobs fail or produce weird results. A few checks with ffprobe catch the most common problems.
1. Codec validation
Not every codec is supported by every processing pipeline. If someone uploads a ProRes file but your workflow expects H.264, you want to know before you spend credits. For a full rundown of format handling, check out our guide on converting video formats with FFmpeg.
VIDEO_CODEC=$(ffprobe -v quiet -print_format json -show_streams input.mp4 | jq -r '.streams[] | select(.codec_type=="video") | .codec_name')
if [[ "$VIDEO_CODEC" != "h264" && "$VIDEO_CODEC" != "hevc" ]]; then
echo "Unsupported codec: $VIDEO_CODEC"
exit 1
fi
2. Resolution check
If you're targeting 1080p output and the input is already 640x360, upscaling won't add quality. It just wastes processing time and increases file size. You might want to skip the transcode entirely or warn the user. Our FFmpeg scale filter guide covers the details of resizing while preserving aspect ratio.
HEIGHT=$(ffprobe -v quiet -print_format json -show_streams input.mp4 | jq -r '.streams[] | select(.codec_type=="video") | .height')
if [[ "$HEIGHT" -lt 720 ]]; then
echo "Input is only ${HEIGHT}p. Skipping upscale."
fi
3. Duration sanity check
A 6-hour video will take a long time and cost real money to transcode. And a 0-second file means something is corrupt. Set boundaries.
DURATION=$(ffprobe -v quiet -print_format json -show_format input.mp4 | jq -r '.format.duration')
SECONDS=${DURATION%.*}
if [[ "$SECONDS" -eq 0 ]]; then
echo "File appears corrupt (0 duration)"
exit 1
elif [[ "$SECONDS" -gt 3600 ]]; then
echo "File is over 1 hour. Consider splitting first."
fi
If you do need to split long files, our guide to splitting video into segments covers three different methods.
4. Missing audio check
Some workflows assume audio exists. If you're adding subtitles or normalizing loudness, a video-only file will cause failures downstream.
AUDIO_STREAMS=$(ffprobe -v quiet -print_format json -show_streams input.mp4 | jq '[.streams[] | select(.codec_type=="audio")] | length')
if [[ "$AUDIO_STREAMS" -eq 0 ]]; then
echo "No audio track found"
fi
Using ffprobe Output to Pick API Settings
The real payoff comes when you use ffprobe data to dynamically set your transcoding parameters. Instead of hardcoding "1080p, high quality" for every file, you can make smart choices.
HEIGHT=$(ffprobe -v quiet -print_format json -show_streams input.mp4 | jq -r '.streams[] | select(.codec_type=="video") | .height')
if [[ "$HEIGHT" -ge 2160 ]]; then
RESOLUTION="1080p"
QUALITY="high"
elif [[ "$HEIGHT" -ge 1080 ]]; then
RESOLUTION="1080p"
QUALITY="medium"
else
RESOLUTION="${HEIGHT}p"
QUALITY="medium"
fi
Then pass those values to the FFmpeg Micro API:
curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"inputs\": [{\"url\": \"https://example.com/video.mp4\"}],
\"outputFormat\": \"mp4\",
\"preset\": {\"quality\": \"$QUALITY\", \"resolution\": \"$RESOLUTION\"}
}"
This pattern is especially useful for batch processing where you're handling hundreds of files with different properties. A 4K file gets downscaled to 1080p. A 720p file stays at 720p. A corrupt file gets skipped entirely. No wasted API calls.
Common ffprobe Pitfalls
A few things catch developers off guard.
Duration lives in two places. The stream-level duration and the format-level duration can differ. Some containers don't store duration in the stream metadata at all. Always prefer format.duration for the total file length.
Variable frame rate breaks assumptions. Screen recordings and phone videos often use variable frame rate (VFR). ffprobe reports the average or the container-declared rate, which might not match what you actually see. If frame-accurate processing matters, check the r_frame_rate vs avg_frame_rate fields.
codec_name isn't always what you expect. An MP4 file can contain nearly any codec. I've seen MP4 containers with MJPEG video streams (from older cameras) and even raw PCM audio. Don't trust the file extension. Trust the codec_name from ffprobe.
Remote URLs need accessible headers. When probing a URL, ffprobe sends a standard HTTP GET. If the server requires auth headers, you'll need to pass them with -headers. See our FFmpeg auth headers guide for the syntax.
Start Inspecting, Then Process
Running ffprobe before processing is a two-minute addition to any video pipeline that prevents the most frustrating failures. Once you know what's in the file, send it to FFmpeg Micro to process. One API call, no FFmpeg install, no server to maintain.
FAQ
How do I get ffprobe output in JSON format?
Use the -print_format json flag combined with -show_streams and -show_format. The full command is ffprobe -v quiet -print_format json -show_streams -show_format input.mp4. This returns a structured JSON object with all stream and container metadata.
Can ffprobe read a video from a URL without downloading the whole file?
Yes. ffprobe only reads the container headers and metadata, not the full file. You can pass a URL directly: ffprobe -v quiet -print_format json -show_streams https://example.com/video.mp4. It pulls just enough data to identify codecs, resolution, duration, and stream info.
What's the difference between ffprobe and ffmpeg for reading metadata?
ffmpeg can show metadata too (just run ffmpeg -i input.mp4 with no output), but ffprobe is purpose-built for inspection. It gives you structured output in JSON, XML, CSV, or flat formats. ffmpeg's metadata dump is unstructured text meant for humans, not scripts.
Does ffprobe work with audio files?
ffprobe works with any format FFmpeg supports, including MP3, WAV, FLAC, AAC, and OGG. The output structure is the same. You'll get stream-level info for codec, sample rate, channels, and bitrate.
How do I check if a video file is corrupt using ffprobe?
A quick corruption check is to look for a duration of 0 or a missing video stream. For deeper validation, run ffprobe -v error input.mp4, which prints only error-level messages. If it outputs anything, the file has problems. For a full frame-by-frame check, you'll need ffmpeg -v error -i input.mp4 -f null -, which actually decodes every frame.
Top comments (0)