3

I want to mimic the following ffmpeg command with ffmpeg-python

ffmpeg -y -i in.mp4 -t 30 -filter_complex "fps=10,scale=-1:-1:flags=lanczos[x];[0:v]palettegen[y];[x][y]paletteuse" out.gif

So far, this is what I've got:

in_stream = ffmpeg.input(src, ss=start_time, t=(stop_time-start_time))
scale_input = in_stream
if fps >= 1:
    stream = ffmpeg.filter(in_stream['v'], 'fps', fps)
    scale_input = stream

stream = ffmpeg.filter(scale_input, 'scale', output_width, output_height, 'lanczos')

palette = ffmpeg.filter(in_stream['v'], 'palettegen')
#stream = ffmpeg.filter(stream, palette, 'paletteuse') ???
stream.output(dst).run()

I checked, the palette generates well if I output it as a png. However, I can't find how to use it through the multi-input command paletteuse, as filters only take one stream as an input in ffmpeg-python. I tried concatenating them with ffmpeg.concat() which is the only method I found to make one stream from two but I think it is non-sense (and it doesn't work anyway).

Any idea?

Nick ODell
  • 15,465
  • 3
  • 32
  • 66
Benjamin Barrois
  • 2,566
  • 13
  • 30
  • I don't know anything about ffmpeg-python, but your scale isn't actually doing anything (don't use `-1` in both dimensions), and palettegen input should ideally be the same as the paletteuse input, so add split: `"fps=10,scale=320:-1:flags=lanczos,split[x][y];[x]palettegen[z];[y][z]paletteuse"` – llogan Mar 29 '19 at 18:13
  • Yes sorry the -1:-1 is useless (that was a debug line I was using). Thanks for the `split` tip btw! – Benjamin Barrois Mar 30 '19 at 16:36
  • Is split compulsory? can't I use [x] for both inputs? – Benjamin Barrois Mar 30 '19 at 16:47
  • split is needed in this case because filter labels can't be reused once consumed. – llogan Apr 01 '19 at 16:54

1 Answers1

6

I encountered the same problem, and this question was one of the top search results. I eventually figured out how to do it.

The following function takes a stream (for example stream = ffmpeg.input('input.mp4')) and rescales it. It then splits the stream. The first split is used to generate a palette. Finally, the second split is used with the palette to output a .gif.

# stream: input stream
# output: name of output file
def output_lowres_gif(stream, output):
  split = (
    stream
    .filter('scale', 512, -1, flags='lanczos') # Scale width to 512px, retain aspect ratio
    .filter('fps', fps=12)
    .split()
  )

  palette = (
    split[0]
    .filter('palettegen')
  )

  (
    ffmpeg
    .filter([split[1], palette], 'paletteuse')
    .output(f'{output}.gif')
    .overwrite_output()
    .run()
  )

Tweak resolution, fps or add other filters as needed.