37

How would I get the height and width of a video from ffmpeg's information output. For example, with the following output:

$ ffmpeg -i video.mp4
...
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'video.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 1
    compatible_brands: isomavc1
    creation_time   : 2010-01-24 00:55:16
  Duration: 00:00:35.08, start: 0.000000, bitrate: 354 kb/s
    Stream #0.0(und): Video: h264 (High), yuv420p, 640x360 [PAR 1:1 DAR 16:9], 597 kb/s, 25 fps, 25 tbr, 25k tbn, 50 tbc
    Metadata:
      creation_time   : 2010-01-24 00:55:16
    Stream #0.1(und): Audio: aac, 44100 Hz, stereo, s16, 109 kb/s
    Metadata:
      creation_time   : 2010-01-24 00:55:17
At least one output file must be specified

How would I get height = 640, width= 360?

llogan
  • 121,796
  • 28
  • 232
  • 243
David542
  • 104,438
  • 178
  • 489
  • 842

8 Answers8

113

Use ffprobe

Example 1: With keys / variable names

ffprobe -v error -show_entries stream=width,height -of default=noprint_wrappers=1 input.mp4
width=1280
height=720

Example 2: Just width x height

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

Example 3: JSON

ffprobe -v error -select_streams v -show_entries stream=width,height -of json input.mkv 
{
    "programs": [

    ],
    "streams": [
        {
            "width": 1280,
            "height": 720
        }
    ]
}

Example 4: JSON Compact

ffprobe -v error -select_streams v -show_entries stream=width,height -of json=compact=1 input.mkv 
{
    "programs": [

    ],
    "streams": [
        { "width": 1280, "height": 720 }
    ]
}

Example 5: XML

ffprobe -v error -select_streams v -show_entries stream=width,height -of xml input.mkv 
<?xml version="1.0" encoding="UTF-8"?>
<ffprobe>
    <programs>
    </programs>

    <streams>
        <stream width="1280" height="720"/>
    </streams>
</ffprobe>

What the options do:

  • -v error Make a quiet output, but allow errors to be displayed. Excludes the usual generic FFmpeg output info including version, config, and input details.

  • -show_entries stream=width,height Just show the width and height stream information.

  • -of option chooses the output format (default, compact, csv, flat, ini, json, xml). See FFprobe Documentation: Writers for a description of each format and to view additional formatting options.

  • -select_streams v:0 This can be added in case your input contains multiple video streams. v:0 will select only the first video stream. Otherwise you'll get as many width and height outputs as there are video streams. -select_streams v can be used to show info from all video streams and avoid empty audio stream info in JSON and XML output.

  • See the FFprobe Documentation and FFmpeg Wiki: FFprobe Tips for more info.

piojo
  • 6,351
  • 1
  • 26
  • 36
llogan
  • 121,796
  • 28
  • 232
  • 243
  • This is helpful, but I think OP wanted to capture the values in python. – Geoff Jun 08 '15 at 16:21
  • @Geoff This will provide the needed values, and will be more reliable than the other method shown here. How it's used with Python is up to the user. – llogan Jun 08 '15 at 16:33
  • Indeed. It helped me a lot. Thanks. Very easy to parse with `re.search` using the `subprocess` package to capture the output. Sorry for sounding negative. – Geoff Jun 09 '15 at 14:54
  • 5
    `-of json` returns the data in JSON format, which is easier accessible in Python (avoiding regex). – Simon Steinberger Jul 28 '15 at 22:33
  • 1
    @SimonSteinberger Edited to mention json, along with all the other available formats. – llogan Jul 28 '15 at 23:09
  • The advantage of this solution is that it returns the correct dimensions not only for mp4 but also m4v. The method by @oldo.nicho proposed wouldn't work as m4v stores the dimensions not in the first but second json array – Agile Bean Jul 29 '18 at 04:34
  • This is an excellent answer. If you pass the JSON output to Python, you can parse it with `json.loads(cmd_output)`. You saved me quite a bit of trouble! – nicbou Oct 23 '20 at 09:56
  • Sometime ,the width and height value are wrong, It may be related to display_aspect_ratio, I encountered this situation. please help me – Stephen.lin Mar 10 '21 at 02:45
  • @zui-coding You should ask a new question. Show your command and the full log, and describe how you determine the values are wrong. – llogan Mar 10 '21 at 02:56
  • possible to get video resolution? 4k? 2k? – Choy Jul 28 '21 at 09:59
  • @Choy ffprobe can't tell you if something is 4k or 2k or whatever. It will tell you the width and height of the video which you can use to determine if something is 4k. For example, if ffprobe gives you 4096x3072 then that is 4k. – llogan Jul 28 '21 at 17:12
  • In the second example, what does the `v` after `select_streams` do? – Hashim Aziz Oct 18 '21 at 02:36
  • 1
    @HashimAziz It is mentioned in the answer: "`-select_streams v:0` This can be added in case your input contains multiple video streams. `v:0` will select only the first video stream. Otherwise you'll get as many width and height outputs as there are video streams. `-select_streams v` can be used to show info from all video streams and avoid empty audio stream info in JSON and XML output." So it has been added to avoid the extra, empty lines for that blank audio info that appear for some output formats. – llogan Oct 18 '21 at 17:39
  • @llogan Apologies, it was late and I was skimming – Hashim Aziz Oct 18 '21 at 17:44
  • 1
    @HashimAziz I didn't even remember it was there until I was about to reply. I'm guessing 98% of readers skip the notes anyway. – llogan Oct 18 '21 at 17:46
  • I think the answer is cool, but its usually easier to get access to ffmpeg than ffprobe because many libraries carry a copy of one but not the other. – Mark Rogers Mar 09 '22 at 17:34
  • @zui-coding Try replacing `stream=width,height` with `stream=width,height:stream_tags=rotate`. The `rotate` tag will only be present if the video is actually rotated. It should have a value of 90, 180, or 270. If it's 90 or 270 then you should swap the width and height. I think this is important enough that it should be part of this answer. Usually when you want the dimensions of something, you want the dimensions that are used for displaying it, not the raw dimensions. – Adam Taylor Oct 20 '22 at 21:29
15

Have a look at mediainfo Handles most of the formats out there.

If you looking for a way to parse the output from ffmpeg, use the regexp \d+x\d+

Example using perl:

$ ./ffmpeg -i test020.3gp 2>&1 | perl -lane 'print $1 if /(\d+x\d+)/'
176x120

Example using python (not perfect):

$ ./ffmpeg -i /nfshome/enilfre/pub/test020.3gp 2>&1 | python -c "import sys,re;[sys.stdout.write(str(re.findall(r'(\d+x\d+)', line))) for line in sys.stdin]"

[][][][][][][][][][][][][][][][][][][]['176x120'][][][]

Python one-liners aren't as catchy as perl ones :-)

Fredrik Pihl
  • 44,604
  • 7
  • 83
  • 130
  • 1
    This one fails in when I tested, because the stream info is `Stream #0:0: Video: mjpeg (MJPG / 0x47504A4D), yuvj420p(pc, bt470bg/unknown/unknown), 733x446 [SAR 1:1 DAR 733:446], 7516 kb/s, 60 fps, 60 tbr, 60 tbn, 60 tbc`, so the result is `[][][][][][][][][][][][][]['0x47504', '733x446'][]` – P.R. Jun 08 '15 at 16:53
5

As mentioned here, ffprobe provides a way of retrieving data about a video file. I found the following command useful ffprobe -v quiet -print_format json -show_streams input-video.xxx to see what sort of data you can checkout.

I then wrote a function that runs the above command and returns the height and width of the video file:

import subprocess
import shlex
import json

# function to find the resolution of the input video file
def findVideoResolution(pathToInputVideo):
    cmd = "ffprobe -v quiet -print_format json -show_streams"
    args = shlex.split(cmd)
    args.append(pathToInputVideo)
    # run the ffprobe process, decode stdout into utf-8 & convert to JSON
    ffprobeOutput = subprocess.check_output(args).decode('utf-8')
    ffprobeOutput = json.loads(ffprobeOutput)

    # find height and width
    height = ffprobeOutput['streams'][0]['height']
    width = ffprobeOutput['streams'][0]['width']

    return height, width
oldo.nicho
  • 2,149
  • 2
  • 25
  • 39
  • This is great but it doesn't work for m4v which stores the dimensions in the second array. so only then, the dimensions must be retrieved by `height = ffprobeOutput['streams'][1]['height']` and `width = ffprobeOutput['streams'][0]['width']` – Agile Bean Jul 29 '18 at 04:36
3

In this blog post theres a rough solution in python:

import subprocess, re
pattern = re.compile(r'Stream.*Video.*([0-9]{3,})x([0-9]{3,})')

def get_size(pathtovideo):
    p = subprocess.Popen(['ffmpeg', '-i', pathtovideo],
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    stdout, stderr = p.communicate()
    match = pattern.search(stderr)
    if match:
        x, y = map(int, match.groups()[0:2])
    else:
        x = y = 0
    return x, y

This however assumes it's 3 digits x 3 digits (i.e. 854x480), you'll need to loop through the possible dimension lengths, such as (1280x720):

possible_patterns = [re.compile(r'Stream.*Video.*([0-9]{4,})x([0-9]{4,})'), \
            re.compile(r'Stream.*Video.*([0-9]{4,})x([0-9]{3,})'), \
re.compile(r'Stream.*Video.*([0-9]{3,})x([0-9]{3,})')]

and check if match returns None on each step:

for pattern in possible_patterns:
    match = pattern.search(stderr)
    if match!=None:
        x, y = map(int, match.groups()[0:2])
        break

if match == None:
    print "COULD NOT GET VIDEO DIMENSIONS"
    x = y = 0

return '%sx%s' % (x, y)

Could be prettier, but works.

HdN8
  • 2,953
  • 3
  • 24
  • 26
3

BAD (\d+x\d+)

$ echo 'Stream #0:0(eng): Video: mjpeg (jpeg / 0x6765706A), yuvj420p, 1280x720, 19939 kb/s, 30 fps, 30 tbr, 30 tbn, 30 tbc' | perl -lane 'print $1 if /(\d+x\d+)/'
> 0x6765706

GOOD ([0-9]{2,}x[0-9]+)

$ echo 'Stream #0:0(eng): Video: mjpeg (jpeg / 0x6765706A), yuvj420p, 1280x720, 19939 kb/s, 30 fps, 30 tbr, 30 tbn, 30 tbc' | perl -lane 'print $1 if /([0-9]{2,}x[0-9]+)/'
> 1280x720
sonique
  • 4,539
  • 2
  • 30
  • 39
3

From Fredrik's tip above, here is how I did it using MediaInfo ( http://mediainfo.sourceforge.net/en ):

>>> p1 = subprocess.Popen(['mediainfo', '--Inform=Video;%Width%x%Height%',         
    '/Users/david/Desktop/10stest720p.mov'],stdout=PIPE)
>>> dimensions=p1.communicate()[0].strip('\n')
>>> dimensions
'1280x688'
David542
  • 104,438
  • 178
  • 489
  • 842
2

The best way for to answer this question would be for an ffmpeg developer to explain exactly what the format of the ffmpeg output is expected to be and whether we can consistently assume the size to be located in a specified context within it. Until then we can only guess from example what the format usually is.

Here's my attempt. It's verbose compared to these "one-liners", but that's because I'd like to know why it fails when it eventually does.

import subprocess

def get_video_size(video_filename):
    """Returns width, height of video using ffprobe"""
    # Video duration and hence start time
    proc = subprocess.Popen(['ffprobe', video_filename],
        stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    res = proc.communicate()[0]

    # Check if ffprobe failed, probably on a bad file
    if 'Invalid data found when processing input' in res:
        raise ValueError("Invalid data found by ffprobe in %s" % video_filename)

    # Find the video stream
    width_height_l = []
    for line in res.split("\n"):
        # Skip lines that aren't stream info
        if not line.strip().startswith("Stream #"):
            continue

        # Check that this is a video stream
        comma_split = line.split(',')
        if " Video: " not in comma_split[0]:
            continue

        # The third group should contain the size and aspect ratio
        if len(comma_split) < 3:
            raise ValueError("malform video stream string:", line)

        # The third group should contain the size and aspect, separated
        # by spaces
        size_and_aspect = comma_split[2].split()        
        if len(size_and_aspect) == 0:
            raise ValueError("malformed size/aspect:", comma_split[2])
        size_string = size_and_aspect[0]

        # The size should be two numbers separated by x
        width_height = size_string.split('x')
        if len(width_height) != 2:
            raise ValueError("malformed size string:", size_string)

        # Cast to int
        width_height_l.append(map(int, width_height))

    if len(width_height_l) > 1:
        print "warning: multiple video streams found, returning first"
    return width_height_l[0]
cxrodgers
  • 4,317
  • 2
  • 23
  • 29
0

without re module

out = error_message.split()               # make a list from resulting error string
out.reverse()
for index, item in enumerate(out):        # extract the item before item= "[PAR"
    if item == "[PAR":                      #
        dimension_string = out[i+1]          #
        video_width, video_height = dimension_string.split("x")

Edit: not a good answer because not all videos have that "PAR" information :(