Examples¶
Format convertion¶
The simplest example of usage is converting media from one format to another (in this case from MPEG transport stream to MP4) preserving all other attributes:
>>> from ffmpy3 import FFmpeg
... ff = FFmpeg(
... inputs={'input.ts': None},
... outputs={'output.mp4': None}
... )
>>> ff.cmd
'ffmpeg -i input.ts output.mp4'
>>> ff.run()
Transcoding¶
If at the same time we wanted to re-encode video and audio using different codecs we’d have to specify additional output options:
>>> ff = FFmpeg(
... inputs={'input.ts': None},
... outputs={'output.mp4': '-c:a mp2 -c:v mpeg2video'}
... )
>>> ff.cmd
'ffmpeg -i input.ts -c:a mp2 -c:v mpeg2video output.mp4'
>>> ff.run()
Demultiplexing¶
A more complex usage example would be demultiplexing an MPEG transport stream into separate elementary (audio and video) streams and save them in MP4 containers preserving the codecs (note how a list is used for options here):
>>> ff = FFmpeg(
... inputs={'input.ts': None},
... outputs={
... 'video.mp4': ['-map', '0:0', '-c:a', 'copy', '-f', 'mp4'],
... 'audio.mp4': ['-map', '0:1', '-c:a', 'copy', '-f', 'mp4']
... }
... )
>>> ff.cmd
'ffmpeg -i input.ts -map 0:1 -c:a copy -f mp4 audio.mp4 -map 0:0 -c:a copy -f mp4 video.mp4'
>>> ff.run()
Warning
Note that it is not possible to mix the expression formats for options, i.e. it is not possible to have a list that contains strings with spaces (an exception to this is Complex command lines). For example, this command line will not work with FFmpeg
:
>>> from subprocess import PIPE
>>> ff = FFmpeg(
... inputs={'input.ts': None},
... outputs={
... 'video.mp4': ['-map 0:0', '-c:a copy', '-f mp4'],
... 'audio.mp4': ['-map 0:1', '-c:a copy', '-f mp4']
... }
... )
>>> ff.cmd
'ffmpeg -hide_banner -i input.ts "-map 0:1" "-c:a copy" "-f mp4" audio.mp4 "-map 0:0" "-c:a copy" "-f mp4" video.mp4'
>>>
>>> ff.run(stderr=PIPE)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/ay/projects/personal/ffmpy3/ffmpy3.py", line 104, in run
raise FFRuntimeError(self.cmd, ff_command.returncode, out[0], out[1])
ffmpy3.FFRuntimeError: `ffmpeg -hide_banner -i input.ts "-map 0:1" "-c:a copy" "-f mp4" audio.mp4 "-map 0:0" "-c:a copy" "-f mp4" video.mp4` exited with status 1
STDOUT:
STDERR:
Unrecognized option 'map 0:1'.
Error splitting the argument list: Option not found
>>>
Notice how the actual FFmpeg
command line contains unnecessary quotes.
Multiplexing¶
To multiplex video and audio back into an MPEG transport stream with re-encoding:
>>> ff = FFmpeg(
... inputs={'video.mp4': None, 'audio.mp3': None},
... outputs={'output.ts': '-c:v h264 -c:a ac3'}
... )
>>> ff.cmd
'ffmpeg -i audio.mp4 -i video.mp4 -c:v h264 -c:a ac3 output.ts'
>>> ff.run()
There are cases where the order of inputs and outputs must be preserved (e.g. when using FFmpeg -map option). In these cases the use of regular Python dictionary will not work because it does not preserve order. Instead, use OrderedDict. For example we want to multiplex one video and two audio streams into an MPEG transport streams re-encoding both audio streams using different codecs. Here we use an OrderedDict to preserve the order of inputs so they match the order of streams in output options:
>>> from collections import OrderedDict
>>> inputs = OrderedDict([('video.mp4', None), ('audio_1.mp3', None), ('audio_2.mp3', None)])
>>> outputs = {'output.ts', '-map 0 -c:v h264 -map 1 -c:a:0 ac3 -map 2 -c:a:1 mp2'}
>>> ff = FFmpeg(inputs=inputs, outputs=outputs)
>>> ff.cmd
'ffmpeg -i video.mp4 -i audio_1.mp3 -i audio_2.mp3 -map 0 -c:v h264 -map 1 -c:a:0 ac3 -map 2 -c:a:1 mp2 output.ts'
>>> ff.run()
Using pipe
protocol¶
ffmpy3
can read input from STDIN
and write output to STDOUT
. This can be achieved by using FFmpeg pipe protocol. The following example reads data from a file containing raw video frames in RGB format and passes it to ffmpy3
on STDIN
; ffmpy3
in its turn will encode raw frame data with H.264 and pack it in an MP4 container passing the output to STDOUT
(note that you must redirect STDOUT
of the process to a pipe by using subprocess.PIPE
as stdout
value, otherwise the output will get lost):
>>> import subprocess
>>> ff = FFmpeg(
... inputs={'pipe:0': '-f rawvideo -pix_fmt rgb24 -s:v 640x480'},
... outputs={'pipe:1': '-c:v h264 -f mp4'}
... )
>>> ff.cmd
'ffmpeg -f rawvideo -pix_fmt rgb24 -s:v 640x480 -i pipe:0 -c:v h264 -f mp4 pipe:1'
>>> stdout, stderr = ff.run(input_data=open('rawvideo', 'rb').read(), stdout=subprocess.PIPE)
Asynchronous execution¶
In certain cases, one may not wish to run FFmpeg
and block on waiting for results or introduce multithreading into one’s application. In this case, asynchronous execution using asyncio
is possible.
>>> ff = ffmpy3.FFmpeg(
... inputs={'input.mp4': None},
... outputs={'output.avi': None}
... )
>>> ff.run_async()
>>> await ff.wait()
Processing FFmpeg
output without multithreading or blocking is also possible. The following code snippet replaces CR
with LF
from FFmpeg’s progress output and echoes it to STDERR
while FFmpeg
processes the input video.
>>> import asyncio
>>> import sys
>>> ff = ffmpy3.FFmpeg(
... inputs={'input.mp4': None},
... outputs={'output.avi': None},
... )
>>> _ffmpeg_process = await ff.run_async(stderr=asyncio.subprocess.PIPE)
>>> line_buf = bytearray()
>>> my_stderr =_ffmpeg_process.stderr
>>> while True:
>>> in_buf = (await my_stderr.read(128)).replace(b'\r', b'\n')
>>> if not in_buf:
>>> break
>>> line_buf.extend(in_buf)
>>> while b'\n' in line_buf:
>>> line, _, line_buf = line_buf.partition(b'\n')
>>> print(str(line), file=sys.stderr)
>>> await ff.wait()
Complex command lines¶
FFmpeg
command line options can get pretty complex, like when using filtering. Therefore, it is important to understand some of the rules for building command lines building with ffmpy3
. If an option contains quotes, it must be specified as a separate item in the options list without the quotes. However, if a single string is used for options, the quotes of the quoted option must be preserved in the string:
>>> ff = FFmpeg(
... inputs={'input.ts': None},
... outputs={'output.ts': ['-vf', 'adif=0:-1:0, scale=iw/2:-1']}
... )
>>> ff.cmd
'ffmpeg -i input.ts -vf "adif=0:-1:0, scale=iw/2:-1" output.ts'
>>>
>>> ff = FFmpeg(
... inputs={'input.ts': None},
... outputs={'output.ts': '-vf "adif=0:-1:0, scale=iw/2:-1"'}
... )
>>> ff.cmd
'ffmpeg -i input.ts -vf "adif=0:-1:0, scale=iw/2:-1" output.ts'
An even more complex example is a command line that burns the timecode into video:
ffmpeg -i input.ts -vf "drawtext=fontfile=/Library/Fonts/Verdana.ttf: timecode='09\:57\:00\:00': r=25: x=(w-tw)/2: y=h-(2*lh): fontcolor=white: box=1: boxcolor=0x00000000@1" -an output.ts
In ffmpy3
it can be expressed in the following way:
>>> ff = FFmpeg(
... inputs={'input.ts': None},
... outputs={'output.ts': ['-vf', "drawtext=fontfile=/Library/Fonts/Verdana.ttf: timecode='09\:57\:00\:00': r=25: x=(w-tw)/2: y=h-(2*lh): fontcolor=white: box=1: boxcolor=0x00000000@1", '-an']}
... )
>>> ff.cmd
'ffmpeg -i input.ts -vf "drawtext=fontfile=/Library/Fonts/Verdana.ttf: timecode=\'09\:57\:00\:00\': r=25: x=(w-tw)/2: y=h-(2*lh): fontcolor=white: box=1: boxcolor=0x00000000@1" -an output.ts'
The same command line can be compiled by passing output options as a single string, while keeping the quotes:
>>> ff = FFmpeg(
... inputs={'input.ts': None},
... outputs={'output.ts': ["-vf \"drawtext=fontfile=/Library/Fonts/Verdana.ttf: timecode='09\:57\:00\:00': r=25: x=(w-tw)/2: y=h-(2*lh): fontcolor=white: box=1: boxcolor=0x00000000@1\" -an"}
... )
>>> ff.cmd
'ffmpeg -i input.ts -vf "drawtext=fontfile=/Library/Fonts/Verdana.ttf: timecode=\'09\:57\:00\:00\': r=25: x=(w-tw)/2: y=h-(2*lh): fontcolor=white: box=1: boxcolor=0x00000000@1" -an output.ts'