Buffering Detection

ffmpeg filter graph implementation doesn’t provide any flow control and there are situations where out-of-memory error can occur.

Reading multiple files

This situation describes a video processing pipeline where:

  • preroll and source are decoded only once

  • each video (result) starts with a common preroll

  • each uncompressed source is transcoded in a “lossless” way to a relatively small file (backup source)

To produce first frame for second output (backup source), ffmpeg requests first frame from source decoder. Decoded frame is pushed through filter graph to both outputs, but preroll needs to be transcoded first. So, while “backup source” receives frames one-by-one, decoded preroll is buffered in memory.

Instead, you may tell ffmpeg to read both input files for both outputs and use trim filter to cut preroll from “backup source”.

Non-linear editing

In this example ffmpeg cuts out Scene2 from source file and swaps Scene1 and Scene3, but Result2 follows Result1. Result2 is decoded before Result1, so it is buffered in memory. The only way to fix this is to decode source twice:

Buffering Prevention

Both described situations are detected via FFMPEG.check_buffering call.

  • All inputs must have streams defined with proper metadata

  • Filters that are used in filter graph must transform scenes in metadata properly (this is already done for Concat, SetPTS and Trim filters).

from pymediainfo import MediaInfo

from fffw.encoding import *
from fffw.graph import *

# detect information about input file
mi = MediaInfo.parse('source.mp4')
streams = [Stream(m.kind, m) for m in from_media_info(mi)]

# initialize input file with stream and metadata
source = input_file('source.mp4', *streams)

mi = MediaInfo.parse('preroll.mp4')
# initialize input file with stream and metadata
streams = [Stream(m.kind, m) for m in from_media_info(mi)]
preroll = input_file('preroll.mp4', *streams)

ff = FFMPEG()

ff < preroll
ff < source

result = output_file('result.mp4', VideoCodec('libx264'), AudioCodec('aac'))
backup = output_file('backup.mp4', VideoCodec('libx264'), AudioCodec('aac'))

split = source | Split(VIDEO)
split > backup

concat = preroll | Concat(VIDEO)
split | concat
concat > result

ff > result
ff > backup

ff.check_buffering()