Parsing ffprobe output is harder than it looks

Read a media file's metadata with ffprobe, then watch the traps: rotation, HDR, fake image durations, missing channel layouts, and cover art as video.

Share

ffprobe is the first thing I reach for when a video does something weird. One command and you get the codec, the resolution, the duration, and every stream, all as JSON. It’s the best tool of its kind.

Then you start trusting that JSON literally, and it bites you.

I spent a while building a normalizer for ffprobe output at Rendobar. These are the fields I stopped trusting, and why.

Rotation isn’t where you’d look

A video shot in portrait on a phone usually stores its frames in landscape and carries a rotation. Older files put that in tags.rotate. Newer files, and ffmpeg 8 in particular, put it in the stream’s side_data_list as a Display Matrix and drop the tag entirely. So if you read tags.rotate, a phone clip that plays perfectly upright reports 1080x1920 and you compute the wrong aspect ratio. You have to read the display matrix and normalize it to 0, 90, 180, or 270.

Images have a fake duration

Run ffprobe on a JPEG and the image2 demuxer hands you "duration": "0.040000" and a bitrate in the millions. That is not the image. It’s the demuxer pretending the still is one frame of a 25 fps video. A PNG comes through png_pipe with no duration at all. If you surface that 0.04 anywhere, it’s wrong. Null it out.

Channel layout goes missing

You’d expect a stereo file to say "channel_layout": "stereo". A WAV often doesn’t. It reports "channels": 2 and no layout string at all. So “stereo” is something you infer from the channel count, not something you can read off the field.

Cover art is a “video stream”

An MP3 with embedded album art has two streams: the audio, and a tiny video stream for the picture. If you count video streams to decide “is this a video,” every MP3 with a cover looks like one. The tell is disposition.attached_pic = 1. Exclude those before you decide what kind of file you’re holding.

HDR isn’t a flag

There is no "hdr": true. You work it out from the color metadata: a color_transfer of smpte2084 (HDR10) or arib-std-b67 (HLG), or color_primaries of bt2020. Miss it and you hand an HDR clip to a pipeline that treats it as SDR.

The short version

None of this is ffprobe being wrong. It’s ffprobe being literal, reporting exactly what the container claims even when the container is quietly lying. The real work is turning that into something you can act on.

That’s the work we did once so you don’t have to. Rendobar’s ffprobe job runs the real ffprobe command, either a full command or just a URL, and hands back a normalized summary with rotation, HDR, variable frame rate, and the rest already sorted out, next to the raw report when you want it.

The rest of the ffprobe cluster

Common ffprobe commands compared is the command reference, measured across 213 real probe jobs. How to get video duration with ffprobe covers the one field that disagrees with itself. Validate video uploads with ffprobe covers using a probe as a gate before you spend anything on an encode.

Frequently asked questions

Why does ffprobe report my portrait video as landscape?

Because the rotation usually lives in the stream's display-matrix side data rather than in the pixels. A phone video that plays upright can report 1920x1080, and you have to read the side data to know it should be displayed rotated.

How do I read rotation from ffprobe?

Look at the video stream's side_data_list for a Display Matrix entry and its rotation value. The older top-level rotate tag is deprecated and is not present on many modern files.

Why does my MP3 look like it has video?

Cover art is stored as a video stream with disposition.attached_pic set to 1. Checking for the presence of a video stream is not enough; you have to check the disposition.

How do I detect HDR with ffprobe?

There is no HDR boolean. Read color_transfer for smpte2084 or arib-std-b67, or color_primaries for bt2020. Any of those indicates HDR signalling.

Sources

Tags #ffprobe#ffmpeg#video-metadata#media
All posts
Share
  1. Custom fonts in a video API Engineering blog
  2. Opus vs AAC vs MP3 vs FLAC Engineering blog
  3. AV1 vs H.264 VMAF compared Engineering blog