I have a folder of exactly 300 images in png format (labelled 1.png, 2.png, ..., 300.png), which I'm trying to convert to a video. I would like the video to be in the webm format, but there seems to be an issue:
using the following command:
ffmpeg -start_number 1 -i ./frames/%d.png -frames:v 300 -r 30 out.webm
does generate an out.webm file, and, according to ffprobe -select_streams v -count_frames -show_entries stream=nb_read_frames,r_frame_rate out.webm (which is presumably quite an inefficient way to get that information, but that's besides the point), it does contain 300 frames and has a framerate of exactly 30/1, however, instead of the expected exactly 10 seconds (from 300 frames being played at 30 fps), the video lasts slightly longer (about 12 seconds).
This discrepancy does seem to scale up with video length; 900 frames being converted to a video the same way and with the same frame rate yield a 36 (instead of 30) second video.
For testing, I also tried generating an mp4 file instead of a webm one, with the following command (exact same as above, but out.mp4 instead of out.webm), and that worked exactly as expected, out.mp4 was a 10-second long video.
ffmpeg -start_number 1 -i ./frames/%d.png -frames:v 100 -r 30 out.mp4
How do I fix this? is my ffmpeg command off or is this a bug within the tool?
The documentation ( https://www.ffmpeg.org/ffmpeg.html ) has an example:
For creating a video from many images: ffmpeg -f image2 -framerate 12
-i foo-%03d.jpeg -s WxH foo.avi
and
To force the frame rate of the input file (valid for raw formats only)
to 1 fps and the frame rate of the output file to 24 fps: ffmpeg -r 1
-i input.m2v -r 24 output.avi
and also
As an input option, ignore any timestamps stored in the file and
instead generate timestamps assuming constant frame rate fps. This is
not the same as the -framerate option used for some input formats like
image2 or v4l2 (it used to be the same in older versions of FFmpeg).
If in doubt use -framerate instead of the input option -r.
For your case result:
ffmpeg -framerate 30 -i ./frames/%d.png output.webm
Related
I have a directory that contains 2001 PNG files. I can convert all of the frames to an mp4 video using ffmpeg and the following command:
ffmpeg -framerate 60 -start_number 0 \
-i pic.comp2.%07d.png -c:v libx264 -r 30 \
-pix_fmt yuv420p input1ia.mp4
This works fine. However, I am creating a more complicated application that needs to read only the first 1020 files in the directory (specifically 0 thru 1019). Some googling around led me to the -vframes option. My problem is -- it seems to get ignored or at least interpreted differently than I expect.
My modified command looks like:
ffmpeg -framerate 60 -start_number 0 \
-i pic.comp2.%07d.png -vframes 1020 -c:v libx264
-r 30 -pix_fmt yuv420p input1.mp4
It seems like many other people doing the same thing as me do not encounter this issue. So I did some more digging. I tried changing vframes from 1020 to -vframes 20, and this seemed to work properly. So now I am thinking it might be some kind of mismatch between -framerate and -r?
The full resultant video is 33 sec long... which makes sense mathematically.
1 sec
--------- x 2001 frames = 33.35 seconds
60 frames
That's why I thought that specifying ~1/2 of the PNGs as the 'end point' would result in a video of the first ~16-17 seconds. But I always get the full length video from using the -vframes option.
I assume my input to -vframes must be incorrect mathematically, since a small number of frames seems to work. However, I do not understand why.
The most educated guess I can seem to make is that it is reading the PNGs as 60fps (-framerate), but the -r makes the output video 30fps or something? However, then I would assume that the full output video would not be 33 seconds long.
When the input and output rates don't match, ffmpeg drops or duplicate frames as per a regular scheme to achieve the output rate. So, for an input rate of 60 and an output rate of 30, half the frames are dropped. With the vframes option, 1020 frames at a output rate of 30 should produce a video of duration 1020/30 = 34 seconds.
To achieve what you want, use the t option
ffmpeg -framerate 60 -start_number 0 -t 17 \
-i pic.comp2.%07d.png -c:v libx264 -r 30 \
-pix_fmt yuv420p input1ia.mp4
where 17 is number of frames to be used / input rate
trying to get my head around ffmpeg to create a slideshow where each image is displayed for ~5 seconds with some audio. created a bat file to run the following so far:
ffmpeg -f image2 -i image-%%03d.jpg -i music.mp3 output.mpg
It gets the images and displayes them all very fast in the first second of the video, it then plays out the rest of the audio while showing the last image.
I want to make the images stay up longer (about 5 seconds), and stop the video after the last frame (not playing the rest of the song), are either of these things possible? i could hack the frame rate thing i guess by having hundreds of the same image in order to keep it up longer, but this is far from ideal!
Thanks
The default encoder for mpg output, mpeg1video, is strict about the allowed frame rates, so an input and an output -r are required:
ffmpeg -r 1/5 -i image-%03d.jpg -i music.mp3 -r 25 -qscale:v 2 -shortest -codec:a copy output.mpg
The input images will have a frame rate of 1 frame every 5 seconds and the output will duplicate frames to reach 25 frames per second.
-f image2 is generally not required.
-qscale:v can control output quality. A sane range is 2-5.
-shortest will make the output duration the same as the shortest input duration.
-codec:a copy copy your MP3 audio instead of re-encoding.
MPEG-1 video has more modern alternatives. See the FFmpeg and x264 Encoding Guide for more info.
Also see:
* FFmpeg FAQ: How do I encode single pictures into movies?
* FFmpeg Wiki: Create a video slideshow from images
You could use the filter fps instead of output framerate
ffmpeg -r 1/5 -i img%03d.png -i musicfile -c:v libx264 -vf fps=25 -pix_fmt yuv420p out.mp4
This however skips the last image for me strangely.
I have a GIF image. I am trying to convert it to MP4.
ffmpeg -f image2 -r {delay_time_of_gif_between_each_frame}/1 -i temp/%05d.png -vcodec libx264 video.mp4
This MP4 is not running at the same speed when compared to the original GIF. How do I make it to run with the same speed?
It seems I am making mistakes with the -r property. I played with it but don't get anything useful. I even removed it. Still it isn't working.
If you already know the time of delay between subsequent frames, then you need to take the inverse of it to convert it to a frame rate. For example, if the time between each frame is 40ms (or 0.04s), then the inverse would be 1 divided by 0.04, thus 25 fps.
You can not simply divide the time between frames by 1, since division by 1 will give you the same result as before.
So, try either of these again:
ffmpeg -f image2 -r 1/0.04 -i temp/%05d.png -c:v libx264 out.mp4
ffmpeg -f image2 -r 25 -i temp/%05d.png -c:v libx264 out.mp4
Note that the default input frame rate for image2 is 25 anyway, but this was just for illustration.
Also, you can change the frame rate of the output video as well, by putting -r after the input file, which should make a difference.
ffmpeg -f images -i temp/%05d.png -c:v libx264 -r 25 out.mp4
Although this question is somewhat older:
Current versions of ffmpeg automatically determine the delays between the frames according to the information in the gif images, so no need to set the frame rate in the command.
I'd like to programmatically create a video file that is composed of a series of images. However, I'd also like to be able to specify a duration for each image. I often see ffmpeg examples suggested for similar tasks, but they always assume the same duration for each image. Is there an efficient way to accomplish this? (An inefficient solution might be setting the frame rate to something high and repeatedly copying each image until it matches the intended duration)
I will be dynamically generating each of the images as well, so if there is way to encode the image data into video frames without writing each image to disk, that's even better. This, however, is not a requirement.
Edit: To be clear, I don't necessarily need to use ffmpeg. Other free command-line tools are fine, as are video-processing libraries. I'm just looking for a good solution.
I was able to solve the exact same problem with the following commands.
vframes is set to the number of seconds * fps
In the example the first video has 100 frames (100 frame / 25 fps = 4 seconds) and second one has 200 frames (8 seconds)
ffmpeg -f image2 -loop 1 -vframes 100 -r 25 -i a.jpg -vcodec mpeg4 a.avi
ffmpeg -f image2 -loop 1 -vframes 200 -r 25 -i b.jpg -vcodec mpeg4 b.avi
mencoder -ovc copy -o out.mp4 a.mp4 b.mp4
The mencoder part is just like the one of d33pika
You can use the concat demuxer to manually order images and to provide a specific duration for each image.
ffmpeg -f concat -i input.txt -vsync vfr -pix_fmt yuv420p output.mp4
Your input.txt should look like this.
file '/path/to/dog.png'
duration 5
file '/path/to/cat.png'
duration 1
file '/path/to/rat.png'
duration 3
file '/path/to/tapeworm.png'
duration 2
file '/path/to/tapeworm.png'
You can write this txt file dynamically according to your needs and excute the command.
For more info refer to https://trac.ffmpeg.org/wiki/Slideshow
It seems like there is no way to have different durations for different images using ffmpeg. I would create separate videos for each of the images and then concat them using mencoder like this:
ffmpeg -f image2 -vframes 30 -i a.jpg -vcodec libx264 -r 1 a.mp4
ffmpeg -f image2 -vframmes 10 -i bjpg -vcodec libx264 -r 1 b.mp4
mencoder -ovc copy -o out.mp4 a.mp4 b.mp4
mencoder for the concat operation needs all the output videos to have same resolution,framerate and codec.
Here a.mp4 has 30 frames of duration 30 seconds and b.mp4 has 10 frames of 10 seconds.
I have been trying to extract keyframes from video using ffmpeg 0.11.1 . So far all the commands I have tried do not extract keyframes but return all the frames ie 25fps*total time number of frames in the output.
I tried setting the keyint_min as 25 to make sure there is a amximum of 1 keyframe per second.
ffmpeg -vf select="eq(pict_type\,PICT_TYPE_I)" -g 250 -keyint_min 25 -i C:\test.mp4 -vsync 2 -f image2 C:\testTemp\thumbnails-%02d.jpeg
But still all the frames are returned.
Then i tried, to separate the keyframes by 20 seconds.
ffmpeg -i C:\test.mp4 -vf select='eq(pict_type\,I)*(isnan(prev_selected_t)+gte(t-prev_selected_t\,20))' -vsync 0 -f image2 C:\testTemp\%09d.jpg
Again same result, all the frames are returned.
What should I do?
In your first command you are using the filter as an input option. I don't know how ffmpeg will interpret that.
Try this:
ffmpeg -i C:\test.mp4 -vf select='eq(pict_type\,I)',setpts='N/(25*TB)' C:\testTemp\%09d.jpg
Change 25 to the frame rate of your source: 30000/1001 for NTSC video, 24000/1001 for NTSC film, 25 for PAL, etc.
Control output quality with the -q:v or -qscale:v option (just called -qscale in old ffmpeg). Range for mpeg* is 1-31 where 31 is the worst quality.
Next time remember that ffmpeg usage questions are to be asked at superuser.com since stackoverflow is specifically for programming.