Extending fffw¶
The aim of fffw
library is to provide mechanics for preparing ffmpeg
command line arguments, not to cover all possible ffmpeg
parameters.
It provides all necessary base classes to extend input file handling, filtering
and output configuration.
Before writing code it’s useful to read section ffmpeg command line structure.
Extending FFMPEG¶
If you need to add more flags to common section of ffmpeg
, just extend
main wrapper:
from dataclasses import dataclass
from fffw import encoding
from fffw.wrapper import param
@dataclass
class FFMPEG(encoding.FFMPEG):
no_banner: bool = param(default=False, name='hide_banner')
Alternate inputs¶
Files are not the only sources for ffmpeg
. Audio/video streams could be read
from network or hardware devices. Below there is a sample implementation of
screen grabbing input.
from dataclasses import dataclass
from typing import List, Tuple, Optional
from fffw import encoding
from fffw.encoding import *
from fffw.wrapper import param
@dataclass
class X11Grab(encoding.Input):
"""
X-server grabbing input.
"""
# `skip=True` removes parameter from argument list
# (it is added manually in `as_pairs`).
# This field overwrites `default` from `encoding.Input`.
input_file: str = param(name='i', default=':0.0', skip=True)
# `init=False` excludes parameter from `__init__`.
# Field is initialized with value passed in `default`
# parameter. Exactly as in dataclasses.
format: str = param(name='f', default='x11grab', init=False)
size: str = param(name='video_size')
fps: float = param(name='framerate')
def as_pairs(self) -> List[Tuple[Optional[str], Optional[str]]]:
return super().as_pairs() + [('i', self.input_file)]
ff = FFMPEG()
ff < X11Grab(fps=25, size='cif', input_file=':0.0+10,20')
# As Output is not initialized with any video codec,
# force excluding `-vn` flag.
ff > output_file('out.mpg', no_video=False)
print(ff.get_cmd())
Implement filters¶
There are many useful filters in ffmpeg
. I.e. you may use
scale2ref to scale one video
to fit another one.
from dataclasses import dataclass
from fffw.encoding import *
from fffw.graph import VIDEO
from fffw.wrapper import param
@dataclass
class Scale2Ref(VideoFilter):
"""
Filter that scales one stream to fit another one.
"""
# define filter name
filter = 'scale2ref'
# configure input and output edges count
input_count = 2
output_count = 2
# add some parameters that compute dimensions
# based on input stream characteristics
width: str = param(name='w')
height: str = param(name='h')
ff = FFMPEG()
ff < input_file('preroll.mp4')
ff < input_file('input.mp4')
# don't know what that formulas mean, it's from ffmpeg docs
scale2ref = ff.video | Scale2Ref('oh*mdar', 'ih/10')
# pass second file to same filter as second input
ff.video | scale2ref
output = output_file('output.mp4')
# concatenate scaled preroll and main video
concat = scale2ref | Concat(VIDEO)
scale2ref | concat > output
ff > output
print(ff.get_cmd())
Configure muxer¶
There are lot’s of formats supported by ffmpeg
. Internet video streaming
often uses HLS protocol to provide better experience. HLS muxer has a bunch of
options to tune.
from dataclasses import dataclass
from fffw.encoding import *
from fffw.wrapper import param
@dataclass
class HLS(Output):
"""
m3u8 muxer
"""
format: str = param(name='f', init=False, default='hls')
# Add empty `param()` call to prevent
# "Non-default argument(s) follows default argument(s)"
# dataclass error.
hls_init_time: int = param()
hls_base_url: str = param()
hls_segment_filename: str = 'file%03d.ts'
ff = FFMPEG(input='input.mp4')
base_url = 'https://my.video.streaming.server.ru/my-playlist/'
ff > HLS(hls_base_url=base_url,
output_file='out.m3u8',
codecs=[VideoCodec('libx264'), AudioCodec('aac')])
print(ff.get_cmd())