python-3.ximageffmpegdurationffmpeg-python

How to set individual image display durations with ffmpeg-python


I am using ffmpeg-python 0.2.0 with Python 3.10.0. Displaying videos in VLC 3.0.17.4.

I am making an animation from a set of images. Each image is displayed for different amount of time.

I have the basics in place with inputting images and concatenating streams, but I can't figure out how to correctly set frame duration.

Consider the following example:

stream1 = ffmpeg.input(image1_file)
stream2 = ffmpeg.input(image2_file)
combined_streams = ffmpeg.concat(stream1, stream2)
output_stream = ffmpeg.output(combined_streams, output_file)
ffmpeg.run(output_stream)

With this I get a video with duration of a split second that barely shows an image before ending. Which is to be expected with two individual frames.

For this example, my goal is to have a video of 5 seconds total duration, showing the image in stream1 for 2 seconds and the image in stream2 for 3 seconds.

Attempt 1: Setting t for inputs

stream1 = ffmpeg.input(image1_file, t=2)
stream2 = ffmpeg.input(image2_file, t=3)
combined_streams = ffmpeg.concat(stream1, stream2)
output_stream = ffmpeg.output(combined_streams, output_file)
ffmpeg.run(output_stream)

With this, I get a video with the duration of a split second and no image displayed.

Attempt 2: Setting frames for inputs

stream1 = ffmpeg.input(image1_file, frames=48)
stream2 = ffmpeg.input(image2_file, frames=72)
combined_streams = ffmpeg.concat(stream1, stream2)
output_stream = ffmpeg.output(combined_streams, output_file, r=24)
ffmpeg.run(output_stream)

In this case, I get the following error from ffmpeg:

Option frames (set the number of frames to output) cannot be applied to input url ########## -- you are trying to apply an input option to an output file or vice versa. Move this option before the file it belongs to.

I can't tell if this is a bug in ffmpeg-python or if I did it wrong.

Attempt 3: Setting framerate for inputs

stream1 = ffmpeg.input(image1_file, framerate=1/2)
stream2 = ffmpeg.input(image2_file, framerate=1/3)
combined_streams = ffmpeg.concat(stream1, stream2)
output_stream = ffmpeg.output(combined_streams, output_file)
ffmpeg.run(output_stream)

With this, I get a video with the duration of a split second and no image displayed. However, when I set both framerate values to 1/2, I get an animation of 4 seconds duration that displays the first image for two seconds and the second image for two seconds. This is the closest I got to a functional solution, but it is not quite there.

I am aware that multiple images can be globbed by input, but that would apply the same duration setting to all images, and my images each have different durations, so I am looking for a different solution.

Any ideas for how to get ffmpeg-python to do the thing is much appreciated.


Solution

  • A simple solution is adding loop=1 and framerate=24 to the "first example":

    import ffmpeg
    
    image1_file = 'image1_file.png'
    image2_file = 'image2_file.png'
    output_file = 'output_file.mp4'
    
    stream1 = ffmpeg.input(image1_file, framerate=24, t=2, loop=1)
    stream2 = ffmpeg.input(image2_file, framerate=24, t=3, loop=1)
    combined_streams = ffmpeg.concat(stream1, stream2)
    output_stream = ffmpeg.output(combined_streams, output_file)
    ffmpeg.run(output_stream)