How would I get the height and width of a video from ffmpeg
's informa开发者_开发技巧tion 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
?
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 thewidth
andheight
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 manywidth
andheight
outputs as there are video streams.-select_streams v
can be used to show info from all video streams and avoid empty audiostream
info in JSON and XML output.See the FFprobe Documentation and FFmpeg Wiki: FFprobe Tips for more info.
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 :-)
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
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'
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.
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
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]
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 :(
精彩评论