FFmpeg Slideshow issues - ffmpeg

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.

Related

Using ffmpeg, jpg to mp4 to mpegts, play with HLS M3U8, only first TS file plays - why?

Before posting I have searched and found similar questions on stackoverflow (I list some below) - none have helped me towards a solution, hence this post. The duration that each image is shown within the movie file differs from many posts that I have seen thus far.
A camera captures 1 image every 30 seconds. I need stream them, preferably via HLS, thus I wrap 2 images in an MP4. I then convert MP4 to mpegts. Each MP4 and TS file play fine individually (each contain two images, each image transitions after 30seconds, each movie file is 1minute long).
When I reference the two TS files in an M3U8 playlist, only the first TS file gets played. Can anyone advise why it stops and how I can get it to play all the TS files that I expect to create, not just the first TS file? Besides my ffmpeg commands, I also include my VLC log file (though I expect to stream to Firefox/Chrome clients). I am using ffmpeg 4.2.2-static installed on an AWS EC2 with AMI2 Linux.
I have four jpgs named image11.jpg, image12.jpg, image21.jpg, image22.jpg - The images look near identical as only the timestamp in top left changes.
The following command creates 1.mp4, using image11.jpg and image12.jpg, each image displayed for 30 seconds, total duration of the mp4 is 1 minute. It plays like expected.
ffmpeg -y -framerate 1/30 -f image2 -i image1%1d.jpg -c:v libx264 -vf "fps=1,format=yuvj420p" 1.mp4
I then convert 1.mp4 to an mpegts file, creating 1.ts. It plays like expected.
ffmpeg -y -i 1.mp4 -c:v libx264 -vbsf h264_mp4toannexb -flags -global_header -f mpegts 1.ts
I repeat the above steps except specific to image21.jpg and image22.jpg, creating 2.mp4 and 2.ts
ffmpeg -y -framerate 1/30 -f image2 -i image1%1d.jpg -c:v libx264 -vf "fps=1,format=yuvj420p" 2.mp4
ffmpeg -y -i 1.mp4 -c:v libx264 -vbsf h264_mp4toannexb -flags -global_header -f mpegts 2.ts
Thus now I have 1.mp4, 1.ts, 2.mp4, 2.ts and all four play individually just fine.
Using ffprobe I can confirm their duration is 60seconds, for example:
ffprobe -i 1.ts -v quiet -show_entries format=duration -hide_banner -print_format json
My m3u8 playlist follows:
#EXTM3U
#EXT-X-VERSION:4
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-TARGETDURATION:60.000
#EXTINF:60.0000,
1.ts
#EXTINF:60.000,
2.ts
#EXT-X-ENDLIST
Can anyone advise where I am going wrong?
VLC Error Log (though I expect to play via web browser)
I have researched the process using these (and other pages) as a guide:
How to create a video from images with ffmpeg
convert from jpg to mp4 by ffmpeg
ffmpeg examples page
FFMPEG An Intermediate Guide/image sequence
How to use FFmpeg to convert images to video
Take a look at the start_pts/start_time in the ffprobe -show_streams output, my guess is that they all start at zero/near-zero which will cause playback to fail after your first segment.
You can still produce them independently but you will want to use something like -output_ts_offset to correctly set the timestamps for subsequent segments.
The following solution works well for me. I have tested it uninterrupted for more than two hours and believe it ticks all my boxes. (Edited because I forgot the all important -re tag)
ffmpeg will loop continuously, reading test.jpg and stream it to my RTMP server. When my camera posts an image every 30seconds, I copy the new image on top of the existing test.jpg which in effect changes what is streamed out.
Note the command below is all one line, I have put new lines in to assist reading and The order of the parameters are important - the loop and fflags genpts for example must appear before the -i parameter
ffmpeg
-re
-loop 1
-fflags +genpts
-framerate 1/30
-i test.jpg
-c:v libx264
-vf fps=25
-pix_fmt yuvj420p
-crf 30
-f fifo -attempt_recovery 1 -recovery_wait_time 1
-f flv rtmp://localhost:5555/video/test
Some arguments explained:
-re implies play in real time
loop 1 (1 turns the loop on, 0 off)
-fflags +genpts is something I only half understand. PTS I believe is the start/end time of the segment and without this flag, the PTS is reset to zero with every new image. Using this arguement means I avoid EXT-X-DISCONTINUITY when a new image is served.
-framerate 1/30 means one frame for 30seconds
-i test.jpg is my image 'placeholder'. As new images are received via a separate script, it overwrites this image. When combined with loop it means the ffmpeg output will reference the new image.
-c:v libx264 is for H264 video output formating
-vf fps=25 Removing this, or using a different value resulted in my output stream not being 30seconds.
-pix_fmt yuvj420p (sometimes I have seen yuv420p referenced but this did not work on my environment). I believe there are different jpg colour palettes and this switch ensures I can process a wider choice.
-crf 30 implies highest quality image, lowest compression (important for my client)
-f fifo -attempt_recovery 1 -recovery_wait_time 1 -f flv rtmp://localhost:5555/video/test is part of the magic to go with loop. I believe it keeps the connection open with my stream server, reduces the risk of DISCONTINUITY in the play list.
I hope this helps someone going forward.
The following links helped nudge me forward and I share as it might help others to improve upon my solution
Creating a video from a single image for a specific duration in ffmpeg
How can I loop one frame with ffmpeg? All the other frames should point to the first with no changes, maybe like a recusion
Display images on video at specific framerate with loop using FFmpeg
Loop image ffmpeg HLS
https://trac.ffmpeg.org/wiki/Slideshow
https://superuser.com/questions/1699893/generate-ts-stream-from-image-file
https://ffmpeg.org/ffmpeg-formats.html#Examples-3
https://trac.ffmpeg.org/wiki/StreamingGuide

ffmpeg: Is it possible to replace frames in a variable frame-rate video?

Machine learning algorithms for video processing typically work on frames (images) rather than video.
In my work, I use ffmpeg to dump a specific scene as a sequence of .png files, process them in some way (denoise, deblur, colorize, annotate, inpainting, etc), output the results into an equal number of .png files, and then update the original video with the new frames.
This works well with constant frame-rate (CFR) video. I dump the images as so (eg, 50-frame sequence starting at 1:47):
ffmpeg -i input.mp4 -vf "select='gte(t,107)*lt(selected_n,50)'" -vsync passthrough '107+%06d.png'
And then after editing the images, I replace the originals as so (for a 12.5fps CFR video):
ffmpeg -i input.mp4 -itsoffset 107 -framerate 25/2 -i '107+%06d.png' -filter_complex "[0]overlay=eof_action=pass" -vsync passthrough -c:a copy output.mp4
However, many of the videos I work with are variable frame-rate (VFR), and this has created some challenges.
A simple solution is to convert VFR video to CFR, which ffmpeg wants to do anyway, but I'm wondering if it's possible to avoid this. The reason is that CFR requires either dropping frames - since the purpose of ML video processing is usually to improve the output, I'd like to avoid this - or duplicating frames - but an upscaling algorithm that I'm working with right now uses the previous and next frame for data - if the previous or next frame is a duplicate, then ... no data for upscaling.
With -vsync passthrough, I had hoped that I could simply remove the -framerate option, and preserve the original frames as-is, but the resulting command:
ffmpeg -i input.mp4 -itsoffset 107 -i '107+%06d.png' -filter_complex "[0]overlay=eof_action=pass" -vsync passthrough -c:a copy output.mp4
uses ffmpeg's default of 25fps, and drops a lot of frames. Is there a reliable way to replace frames in VFR video?
Yes, it can be done, but it's complicated. It is crucial that the overlay video have exactly the same frame timestamps as the underlay video for this process to work reliably. Generating such a VFR video segment overlay requires capturing the frame timestamps from the source video to generate a precisely timed replacement segment.
The short version of the process is to replace the above commands with the following to extract the images:
ffmpeg -i input.mp4 -vf "select='gte(t,107)*lt(selected_n,50)',showinfo" -vsync passthrough '107+%06d.png' 2>&1 | 'sed s/\r/\n/g' | showinfo2concat.py --prefix="107+" >concat.txt
This requires a script that can be downloaded here. After editing the images, update the source video with:
ffmpeg -i input.mp4 -f concat -safe 0 -i concat.txt -filter_complex"[1]settb=1/90000,setpts=9644455+PTS*25/90000[o];[0:v:0][o]overlay=eof_action=pass" -vsync passthrough -r 90000 output.mp4
Where 90000 is the timescale (inverse of timebase), and 9644455 is the PTS of the first frame to replace.
See the source for more details about what these commands actually do.

ffmpeg command to combine audio and images

I'm trying to achieve something which I earlier thought should be a simple task.
Is there a ffmpeg command that can do the following:
convert an audio.wav file to a video,
Add some 100 pics (img%d.png) to the video so they "automatically" stretch to fill the entire length of the video.
I don't want to set the frame rate manually because it's either making the audio go ahead or lack behind.
I also don't want the following to happen, which happenned when I used "loop_input":
A short video of images got created, which played fast and then repeated itself for the entire duration of the audio.
Please let me know the command.
I've currently tried the following, but these are not giving me the desired results:
This one makes, but video goes fast and audio is not full:
ffmpeg -i img%d.png -i audio.wav -acodec copy output.mpg
This one makes short video which repeats for full audio duration:
ffmpeg -loop_input -shortest -i img%d.png -i audio.wav -acodec copy output.mpg
This one works nearly, but "-r 4" makes video go slow and audio goes ahead. If I use "-r 5" then audio goes slow, and video goes ahead:
ffmpeg -r 4 -i img%d.png -i audio.wav -acodec copy -r 30 output.mpg
measure the time of the audio track and then use -t $audio_duration.
this arg, along with "-loop 1" will stop the mp4 at a time that matches the audio.
you might also try 2 pass technique , including -vcodec libx264 as it works well producing mp4.
and think about the following adjusted for your inputs and record rates:
-b:v 200k -bt 50k

FFmpeg frame rate when converting from GIF to MP4

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.

How to create a video from a series of images with varying image durations?

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.

Resources