5

I trying to create a video using FFmpeg from a few images. I tried several ffmpeg commands but I can't repeat each image for several frames, instead, each image is one frame size and the video result is too short.

The idea, in general, is to obtain a video like: Firs_image during X seconds, Second_image during X seconds, Third_image during x seconds...

There is any command to accomplish this?

2 Answers 2

6

Use

ffmpeg -framerate 1/X -i img%d.png -r 25 -pix_fmt yuv420p out.mp4

e.g. for each image to show for 2.5 seconds,

ffmpeg -framerate 1/2.5 -i img%d.png -r 25 -pix_fmt yuv420p out.mp4
6

To get each image to have a different time shown, instead of all images having the same time, you'll have to use the concat demuxer and a text file.

Create a text file like the following, ordering the images as you'd want them.

file img_001.jpg
outpoint 5
file img_002.jpg
outpoint 3
file img_003.jpg
outpoint 9
.... [keep going until you've included all the images]

Then you run the following command:

ffmpeg -f concat -i [name you gave above text file] [your commands] output.mp4

Explanation

  • The command uses -f concat demuxer to add all the files listed in the text file to a single output.
  • The text file lists the file name, then lists the amount of time to show it in seconds.
  • If just the three files in the example are run, you will get a 17 second video that shows 3 images; the first for 5 seconds, the second for 3 seconds, and the third for 9 seconds.
  • The framerate will be whatever you specify under [your commands] (the correct flag here would be -r) or default to 30 fps (I think) if you do not specify anything.
  • You can give partial seconds with a decimal.
  • You can put full file paths with the file name, but will also have to include -safe 0 after concat for it to work. This is not necessary if they are relative paths.

Differences when working with video files instead of images:

In the text file, the format is usually like what is found in this similar question/answer, where you specify an "inpoint" as well as an "outpoint". The inpoint is where in the video input timeline you want to start, and the outpoint is for how many seconds you want to go. However, when the input file is an image (instead of a video) the inpoint creates an error, so you have to leave it out.

A Caveat

The concat demuxer requires all input files to have the same streams. This means basically everything like framerate and codec must be the same, but the container can be different. I've never tried this method with different image types, but I assume something like a mix of jpeg and png inputs would throw an error.

If you want to concatenate files of different types, you will have to use the concat filter. It's not as intuitive, but you have full control and won't need a text file. The above demuxer solution in the filter form looks like this (line breaks added for readability):

ffmpeg -loop 1 -t 5 -i img_001.jpg 
               -t 3 -i img_002.jpg 
               -t 9 -i img_003.jpg 
       filter_complex "[0:v]
                       [1:v]
                       [2:v]
                      concat=n=3:v=1:a=0
                      [outv]" 
                      -map "[outv]"
       [your commands] output.mp4

Basically, you name all the inputs at the beginning of the command. -loop 1 lets ffmpeg treat the images like looping video, so you don't have an inpoint. A video file can specify an inpoint with -ss before the -t, and will take 0 seconds as the default if not specified. filter_complex does all the work. The items like [0:v] are calling the inputs, specifying which input (the full syntax is 1 [0:v:0], where the first number is the input file and the second number is the stream, but you don't have to specific the stream if there is only 1 in the file). concat=n=3:v=1:a=0 tells how many files the filter should expect (n=3) and how many video and audio streams to output (v=1:a=0). [outv] Is kind of like a user created variable and this same principle is used on far more complex filter chains. You would either send this variable to additional filters, or map it to the output (-map "[outv]").

To add an audio stream to the output file, the command changes only slightly:

ffmpeg -loop 1 -t 5 -i img_001.jpg 
               -t 3 -i img_002.jpg 
               -t 9 -i img_003.jpg 
               -t 17 -i audio.wav
       filter_complex "[0:v]
                       [1:v]
                       [2:v]
                      concat=n=3:v=1:a=0
                      [outv]" 
                      -map "[outv]" -map "[3:a]" 
       [your commands] output.mp4

We added a fourth input, a wav audio file and specified with -t 17 that will only take the first 17 seconds of it. Again, you could write -ss 10 -t 17 which would get you 17 seconds starting at the 10 second mark. 17 was chosen here because that is the sum of the other input lengths. If you do not specify matching input lengths for your audio and video you will get unexpected results and possibly errors. ffmpeg doesn't automatically close off the output to the shortest input, but the flag -shortest literally does this for you, so you could have used that option instead. The filter doesn't change, because the filter is only working on the video output, but we do need to map the audio input using the same syntax -map "[3:a]".

Like I said, the filter is not very intuitive, and using it requires yet more unintuitive use of flags outside of the filter to get your desired result. If you need further help writing your filtergraph or even the whole command, ask another question or follow the links I've already given.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.