Use ffprobe to obtain information from video files

As we have seen in another article recently (Extract information from a video file using ffmpeg) we can extract basic information from a media file using ffmpeg.

The problem that we raised in this article is that we should need more information about the file than the one offered by ffmpeg, or maybe, that we want to access that information from another program or script for later use. For example we can have a server program written in PHP that wants to access that information. If we want to access that information using ffmpeg it can be possible using pattern matching on the output (however it is a bit difficult). That is a valid and viable option, but allow us to introduce you an even more easy alternative using ffprobe.

Ffprobe is a tool that can be compiled with ffmpeg, and also comes in many distributions with ffmpeg. Therefore it works in the same environments that ffmpeg (Linux, Windows, etc.)

We will extract information from a file named video.webm using ffprobe with the following command:

ffprobe -i video.webm -hide_banner

We use the parameter -hide_banner as we do with ffmpeg to hide details on the compilation and program version.

As you can see the information we get here is the same as we can get with ffmpeg. It is only necessary  to introduce an input file and not use any output file or any other parameter (with the difference that ffmpeg warns us that we must use an output file for the process and in this case we don't have such warning)

Well, the interesting thing starts here with using some ffprobe parameters such as:

  • -show_format: It is a parameter that will show us information about file format. This information refers to information of the video file container. We could say that in a mp4 file for example, it offers information about that format (MP4, number of streams that the file contains, the entire file bitrate, etc ..)
  • -show_streams: This parameter causes ffprobe to show us information of each of the streams (each of the tracks). A video file can contain multiple tracks (it is common that at least a video file contains a video stream and one audio stream, but this can vary) and this parameter will show us the specific information of each track (such as the codec used, the duration of each stream, its own bitrate, etc.)

Ffprobe can use more parameters but these two are the ones that I find most interesting. When we use them directly we can access information in a more friendly format to access to it. Here is an example for the same example.mp4 file.

ffprobe -i example.mp4 -show_format -hide_banner

As we can see in the example, the information appears here between the labels [FORMAT] and [/ FORMAT]. This format will be fairly easy to capture from another program because it is a simple succession of pairs "key=value".

If you run the same example with the parameter -show_streams you have the command:

ffprobe -i example.mp4 -show_streams -hide_banner

We obtain information regarding the streams on this example. The program output can be something like this:

bcm@bcm:~/vids$ ffprobe -i example.mp4 -show_streams -hide_banner
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'example.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf54.36.100
  Duration: 00:01:45.71, start: 0.000000, bitrate: 440 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 640x360 [SAR 1:1 DAR 16:9], 308 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 126 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
[STREAM]
index=0
codec_name=h264
codec_long_name=H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
profile=High
codec_type=video
codec_time_base=1/50
codec_tag_string=avc1
codec_tag=0x31637661
width=640
height=360
coded_width=640
coded_height=368
has_b_frames=2
sample_aspect_ratio=1:1
display_aspect_ratio=16:9
pix_fmt=yuv420p
level=30
color_range=N/A
color_space=unknown
color_transfer=unknown
color_primaries=unknown
chroma_location=left
timecode=N/A
refs=4
is_avc=true
nal_length_size=4
id=N/A
r_frame_rate=25/1
avg_frame_rate=25/1
time_base=1/12800
start_pts=0
start_time=0.000000
duration_ts=1352704
duration=105.680000
bit_rate=308236
max_bit_rate=N/A
bits_per_raw_sample=8
nb_frames=2642
nb_read_frames=N/A
nb_read_packets=N/A
DISPOSITION:default=1
DISPOSITION:dub=0
DISPOSITION:original=0
DISPOSITION:comment=0
DISPOSITION:lyrics=0
DISPOSITION:karaoke=0
DISPOSITION:forced=0
DISPOSITION:hearing_impaired=0
DISPOSITION:visual_impaired=0
DISPOSITION:clean_effects=0
DISPOSITION:attached_pic=0
TAG:language=und
TAG:handler_name=VideoHandler
[/STREAM]
[STREAM]
index=1
codec_name=aac
codec_long_name=AAC (Advanced Audio Coding)
profile=LC
codec_type=audio
codec_time_base=1/48000
codec_tag_string=mp4a
codec_tag=0x6134706d
sample_fmt=fltp
sample_rate=48000
channels=2
channel_layout=stereo
bits_per_sample=0
id=N/A
r_frame_rate=0/0
avg_frame_rate=0/0
time_base=1/48000
start_pts=0
start_time=0.000000
duration_ts=5073920
duration=105.706667
bit_rate=126438
max_bit_rate=128000
bits_per_raw_sample=N/A
nb_frames=4955
nb_read_frames=N/A
nb_read_packets=N/A
DISPOSITION:default=1
DISPOSITION:dub=0
DISPOSITION:original=0
DISPOSITION:comment=0
DISPOSITION:lyrics=0
DISPOSITION:karaoke=0
DISPOSITION:forced=0
DISPOSITION:hearing_impaired=0
DISPOSITION:visual_impaired=0
DISPOSITION:clean_effects=0
DISPOSITION:attached_pic=0
TAG:language=und
TAG:handler_name=SoundHandler
[/STREAM]

As you can see this is a far more complete information than the one offered by ffprobe as standard, and it will be easier to interpret for other programs also.

Extracting information in JSON format

To extract the same information in JSON format it is only necessary to run ffprobe using parameter -print_format json. In order to hide the information that ffprobe shows initially we use the -v quiet parameter in the following example. The command to extract information about file format and file streams in JSON format would be:

ffprobe -i example.mp4 -v quiet -print_format json -show_format -show_streams -hide_banner

In this way we will get something like this:

{
    "streams": [
        {
            "index": 0,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "High",
            "codec_type": "video",
            "codec_time_base": "1/50",
            "codec_tag_string": "avc1",
            "codec_tag": "0x31637661",
            "width": 640,
            "height": 360,
            "coded_width": 640,
            "coded_height": 368,
            "has_b_frames": 2,
            "sample_aspect_ratio": "1:1",
            "display_aspect_ratio": "16:9",
            "pix_fmt": "yuv420p",
            "level": 30,
            "chroma_location": "left",
            "refs": 4,
            "is_avc": "true",
            "nal_length_size": "4",
            "r_frame_rate": "25/1",
            "avg_frame_rate": "25/1",
            "time_base": "1/12800",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 1352704,
            "duration": "105.680000",
            "bit_rate": "308236",
            "bits_per_raw_sample": "8",
            "nb_frames": "2642",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0
            },
            "tags": {
                "language": "und",
                "handler_name": "VideoHandler"
            }
        },
        {
            "index": 1,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_time_base": "1/48000",
            "codec_tag_string": "mp4a",
            "codec_tag": "0x6134706d",
            "sample_fmt": "fltp",
            "sample_rate": "48000",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/48000",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 5073920,
            "duration": "105.706667",
            "bit_rate": "126438",
            "max_bit_rate": "128000",
            "nb_frames": "4955",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0
            },
            "tags": {
                "language": "und",
                "handler_name": "SoundHandler"
            }
        }
    ],
    "format": {
        "filename": "example.mp4",
        "nb_streams": 2,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "105.707000",
        "size": "5821755",
        "bit_rate": "440595",
        "probe_score": 100,
        "tags": {
            "major_brand": "isom",
            "minor_version": "512",
            "compatible_brands": "isomiso2avc1mp41",
            "encoder": "Lavf54.36.100"
        }
    }
}

The good thing is that when using JSON format, we can access that information very quickly from another programs.

For example, say you want to find some data from a video file from a PHP script. It would be a very simple script like this:

<?php
exec("ffprobe -i example.mp4 -v quiet -print_format json -show_format -show_streams -hide_banner > temp_file", $output, $res);
$info = json_decode(file_get_contents("temp_file"));
echo "Video duration: " . $info->format->duration . "\n";
echo "Video size: " . $info->format->size . "\n";
echo "Video resolution width: " . $info->streams[0]->width . "\n";
echo "Video resolution height: " . $info->streams[0]->height . "\n";
echo "Video aspect ratio: " . $info->streams[0]->display_aspect_ratio . "\n";
echo "Video codec: " . $info->streams[0]->codec_name . "\n";
echo "Audio codec: " . $info->streams[1]->codec_name . "\n";
echo "Audio sample rate: " . $info->streams[1]->sample_rate . "\n";
?>

In this code we use the temporary file "temp_file" to save the output of the ffprobe command and then pass that file through the function json_decode, leaving the data in the $info variable. The variable $info will contain all file data as an object (you can imagine the structure with the previous example). So now it is easy to find the data you are looking for, only having to look in the right key. In the code we have included some examples. (Duration in $info->format->duration, size in $info->format->size, resolution in $info->streams[0]->width and $info->streams[0]->height, aspect ratio in $info->streams[0]->display_aspect_ratio, etc ...)

Extract video information in XML format

As we have extracted information in JSON format, we can also do it in XML format if you prefer so. Change the parameter -print_format json to -print_format xml so that ffprobe command would look like this:

ffprobe -i example.mp4 -v quiet -print_format xml -show_format -show_streams -hide_banner

In this case we get a valid XML string output like this (obtained from the above example with the example.mp4 file):

bcm@bcm:~/vids$ ffprobe -i example.mp4 -v quiet -print_format xml -show_format -show_streams -hide_banner
<?xml version="1.0" encoding="UTF-8"?>
<ffprobe>
    <streams>
        <stream index="0" codec_name="h264" codec_long_name="H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10" profile="High" codec_type="video" codec_time_base="1/50" codec_tag_string="avc1" codec_tag="0x31637661" width="640" height="360" coded_width="640" coded_height="368" has_b_frames="2" sample_aspect_ratio="1:1" display_aspect_ratio="16:9" pix_fmt="yuv420p" level="30" chroma_location="left" refs="4" is_avc="true" nal_length_size="4" r_frame_rate="25/1" avg_frame_rate="25/1" time_base="1/12800" start_pts="0" start_time="0.000000" duration_ts="1352704" duration="105.680000" bit_rate="308236" bits_per_raw_sample="8" nb_frames="2642">
            <disposition default="1" dub="0" original="0" comment="0" lyrics="0" karaoke="0" forced="0" hearing_impaired="0" visual_impaired="0" clean_effects="0" attached_pic="0"/>
            <tag key="language" value="und"/>
            <tag key="handler_name" value="VideoHandler"/>
        </stream>
        <stream index="1" codec_name="aac" codec_long_name="AAC (Advanced Audio Coding)" profile="LC" codec_type="audio" codec_time_base="1/48000" codec_tag_string="mp4a" codec_tag="0x6134706d" sample_fmt="fltp" sample_rate="48000" channels="2" channel_layout="stereo" bits_per_sample="0" r_frame_rate="0/0" avg_frame_rate="0/0" time_base="1/48000" start_pts="0" start_time="0.000000" duration_ts="5073920" duration="105.706667" bit_rate="126438" max_bit_rate="128000" nb_frames="4955">
            <disposition default="1" dub="0" original="0" comment="0" lyrics="0" karaoke="0" forced="0" hearing_impaired="0" visual_impaired="0" clean_effects="0" attached_pic="0"/>
            <tag key="language" value="und"/>
            <tag key="handler_name" value="SoundHandler"/>
        </stream>
    </streams>

    <format filename="example.mp4" nb_streams="2" nb_programs="0" format_name="mov,mp4,m4a,3gp,3g2,mj2" format_long_name="QuickTime / MOV" start_time="0.000000" duration="105.707000" size="5821755" bit_rate="440595" probe_score="100">
        <tag key="major_brand" value="isom"/>
        <tag key="minor_version" value="512"/>
        <tag key="compatible_brands" value="isomiso2avc1mp41"/>
        <tag key="encoder" value="Lavf54.36.100"/>
    </format>
</ffprobe>

As you can imagine, from this data, it will be very easy to access the exact data that we want. Following our previous example to access them from a PHP program, we could prepare a script that would do the same from the XML output:

<?php
exec("ffprobe -i example.mp4 -v quiet -print_format xml -show_format -show_streams -hide_banner > temp_file", $output, $res);
$info = new SimpleXMLElement(file_get_contents("temp_file"));
echo "Video duration: " . $info->format['duration'] . "\n";
echo "Video size: " . $info->format['size'] . "\n";
echo "Video resolution width: " . $info->streams->stream[0]['width'] . "\n";
echo "Video resolution height: " . $info->streams->stream[0]['height'] . "\n";
echo "Video aspect ratio: " . $info->streams->stream[0]['display_aspect_ratio'] . "\n";
echo "Video codec: " . $info->streams->stream[0]['codec_name'] . "\n";
echo "Audio codec: " . $info->streams->stream[1]['codec_name'] . "\n";
echo "Audio sample rate: " . $info->streams->stream[1]['sample_rate'] . "\n";
?>

We also use a temporary file to store the output of ffprobe. Also in this case the $info object structure varies slightly as the structure ffprobe builds in XML is a little different, but you can see that in the end, we can also get the data in a fairly simple manner.

In the end, you can use the method that you prefer, but if you allow me the recommendation, I prefer to use JSON.

    Did you find this article helpful?