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.
CodePudding user response:
You need to manipulate the timestamp of the source images and use the ts_from_file
option of the image2
demuxer:
ts_from_file
If set to 1, will set frame timestamp to modification time of image file. Note that monotonity of timestamps is not provided: images go in the same order as without this option. Default value is 0. If set to 2, will set frame timestamp to the modification time of the image file in nanosecond precision.
You should be able to use os.utime
if ok to modify the original file or shutil.copy2 to copy and modify.
CodePudding user response:
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)
loop=1
- Makes the input image repeating in a loop (the repeated duration is set byt=2
andt=3
).framerate=24
- Images don't have framerate (opposed to video), so they are getting the default framerate (25fps) if framerate is not specified.
Assuming the desired output framerate is 24fps, we may set the input framerate to 24fps.
Selectingframerate=24
sets the input framerate to 24fps (and prevents framerate conversion).