Hack FFmpeg With Python, Part Two

Hack FFmpeg With Python, Part Two

In the first installment of this tutorial we took a look at some simple examples that make use of the python programming language for interacting with the FFmpeg leading multimedia open source framework.

Now it is time to advance the skills to the next level!

Before getting some nice output from the ffprobe utility by probing a video and turning it into some python object we highly recommend you take a look at python dictionaries which are being used to store information following the key, value format.

{'website':'unixmen', 'language':'python'}

The above object is called a dictionary in python. It is of type dict. Better fire up the python shell we installed in the previous tutorial so it is easier for you to understand the process.

In case you don’t know how to fire it up, just launch the terminal and type the following command:

python2.7

Once the python interpreter is being launched assign a variable to the above dictionary as shown below:

d = {'website':'unixmen', 'language':'python'}

One can easily get information on the type of python object by using the following syntax:

type(d)

The following is going to be displayed on the console from running the above command:

<type 'dict'>

To access a value of the dictionary the following syntax can be used:

d['website']

And the value is going to be displayed on the console.

'unixmen'

The same approach for the second value. For any value which is part of the dictionary object being created.

d['language']

The following will come up:

'python'

The main reason for choosing such a python object is the format of the output being returned by the ffprobe utility. A python dictionary makes it really easy to store such a long string.

Let us take a closer look at a real world example. Suppose we want to get all the information stored inside the [FORMAT], [/FORMAT] tags and save it into a python dictionary to make it easier for us to pull out data. The subprocess module is going to help us spawn a process, as shown in the following block of code:

import subprocess
cmds = ['/usr/local/bin/ffprobe', '-show_format', 'test.mp4']
p = subprocess.Popen(cmds, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)

The communicate method helps to get the output:

output, _ = p.communicate()

Then print the format information using the print statement as shown below:

print(output)

Something similar to the string shown below is going to be displayed on the python console.

[FORMAT]
filename=test.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=263.012422
size=36004015
bit_rate=1095127
probe_score=100
TAG:major_brand=mp42
TAG:minor_version=0
TAG:compatible_brands=isommp42
TAG:creation_time=2016-08-11T03:35:58.000000Z
[/FORMAT]

As you can see from the above example the long string is being stored in the variable output. As it is, it’s very hard to work on it. This is the perfect case where parsing becomes really useful.

So basically we need to build a simple function which is going to receive a long string as an input like the one shown above and return a python dictionary with all the information. The following example makes it easy to understand the kind of object we plan to return inside the function which is going to deal with parsing the string:

{'filename':'test.mp4',  'nb_streams':2, 'duration':263.012422}

A function is needed to handle the parsing. Python makes is really easy to define a function by making use of the def syntax.

def test(a):
    return a

The function test is really simple and self explanatory. It takes an argument and returns it back as it is.

So doing a = test(5) on the python interactive shell after defining the above function is going to assign the variable a the value of five.

Testing can be easily performed using the print statement:

print(a)

The following value gets displayed on my console.

5

For the purpose of this tutorial the function which is going to deal with parsing the format info is going to be called parse_format. The following is a primary thought on how the function should be designed:

def parse_format(format_string):
    format_info = {}
    return format_info

So the idea behind the above function is that it takes a string as an input, creates an empty dictionary which is going to be used for storing the data and then it just returns the dictionary.

Giving a quick look at the long string returned by probing the testing video there are two lines that do not matter to the data which is going to be stored inside the format_info dictionary. These two lines are the tags [FORMAT] and [/FORMAT].

An if statement can help to check if any of this line is present in the string, so if this condition is met we go skip them and go to the next line.

The following is the final code for our function.

def parse_format(format_string):
    format_info = {}
    for line in format_string.split('\n'):
        if '=' in line:
            k, v = line.split('=')
            k = k.strip()
            v = v.strip()
            format_info[k] = v
    return format_info

So make sure you write it in the python interactive shell as we are going to make use of it. So far we have written some code in our interactive shell. Making use of the function which deals with parsing the information returned from ffprobe is really easy.

Before making use of the above function it is needed to spawn a new process as shown below:

cmds = ['/usr/local/bin/ffprobe', '-show_format', 'test.mp4']
format_p = subprocess.Popen(cmds, stdout=subprocess.PIPE, 
stdin=subprocess.PIPE, stderr=subprocess.PIPE)

Then use the communicate method as shown here:

output, _ = format_p.communicate()

Let’s see if our parsing function does the job it is supposed to do.

format_information = parse_format(output)

If the above python statement does not produce any errors it means we can easily print the information using a print statement.

print(format_information)

The output displayed on the console should look similar to the one shown below. Depending on the video probed by ffprobe your keys are going to store different values.

{'nb_streams': '2', 'start_time': '0.000000', 
'format_long_name': 'QuickTime / MOV', 
'TAG:creation_time': '2016-08-11T03:35:58.000000Z', 
'format_name': 'mov,mp4,m4a,3gp,3g2,mj2', 
'filename': 'test.mp4', 
'TAG:compatible_brands': 'isommp42', 
'bit_rate': '1095127', 'nb_programs': '0', 
'TAG:major_brand': 'mp42', 
'duration': '263.012422', 
'probe_score': '100', 
'TAG:minor_version': '0', 
'size': '36004015'}

Having all the information on a python dictionary makes it real easy for us to pull out data such as the title of the video being probed, the number of streams, the duration in seconds and also the size of the file.

For example, to get the title of the video we can make use of the following python code.

format_information['filename']

The duration of the video can be pulled out of the dictionary using the following python code:

format_information['duration']

Same way for pulling out the size:

format_information['size']

The following shows on my console when running the command shown above.

'36004015'

Conclusion

Through this tutorial we automated the probing process by making use of the subprocess python module. In the next installment we are going to build a very useful script from scratch which is going to automate some of the ffmpeg features we have covered so far.