2

I have a set of images that I extracted from a GIF animation for the purpose of enhancement (which I have done), but now that it's time to convert the still images into a video (I'm going to use webm for higher quality output) I've run into a problem. The GIF format allows individual frames to be displayed for an artificial amount of time and this specific GIF animation makes use that feature, with most frames being displayed for 125 milliseconds but some remaining on the screen for as long as 3000 milliseconds. So just generating a video with a set frame rate isn't going to work as that would break the flow of the animation; I need a way import the frame duration data from the original GIF file and encode the extracted, modified frames into a video using that data.

Is there a way to do this with FFmpeg or maybe a combination of FFprobe and FFmpeg? I ran the GIF through ffprobe -show_frames and it would seem that the information I need is pkt_duration_time which shows the duration the frame is displayed.

2
  • 1
    See superuser.com/a/1320358/114058
    – Gyan
    Commented Oct 11, 2018 at 13:24
  • @Gyan Thanks for pointing me in the right direction. I posted an answer to my question below.
    – veikk0
    Commented Oct 15, 2018 at 13:12

1 Answer 1

1

It took me quite a while of Googling and trial-and-error but I finally figured out how to actually do this. Thanks to Gyan for pointing me in the right direction.

I tried to do more of the process as one-liners but it always turned into a non-functional, undebuggable mess. If you think you could script this, please do so, test it, and then post it as a new answer.


Print pkt_duration_time line and save it into a file:

ffprobe -show_frames input.gif | grep pkt_duration_time= >frames.txt

Modify the line to the format that FFmpeg's concat filter requires:

sed 's/pkt_duration_time=/duration /g' frametimes.txt >frametimes.txt

Print a list of your input files (frames) located in folder/ and modify the list to be the format FFmpeg's concat filter requires:

ls --quoting-style=shell folder/ >filenames.txt && sed -i -e 's/^/file /' filenames.txt

Combine your frame-time file and filenames file to a concat file that FFmpeg accepts:

paste -d \\n filenames.txt frametimes.txt > folder/concatfile.txt

We saved the concat file to where the input files are located because otherwise FFmpeg won't accept it. One more thing before we can encode the video: the data for the last frame needs to be present in the concat file twice for some reason. I don't have a CLI for that so just copy-paste it manually.

Now we can use FFmpeg to turn the image files to an animated video with correct frame times:

cd folder/

ffmpeg -f concat -safe 0 -i concatfile.txt -pix_fmt yuv420p -c:v libvpx-vp9 -b:v 0 -cpu-used 0 -tile-columns 2 -row-mt 1 -lag-in-frames 25 -threads 16 -crf 35 ../output.webm

  • The -safe 0 switch is required because otherwise FFmpeg complains about unsafe file names. At least it did in my case, possibly because the file names contained square brackets.

  • The -pix_fmt yuv420p ensures the correct color space is used. If you omit this, a color space that isn't standard for web video will be used and in the case of VP9 the resulting file will use Profile 1 which doesn't have as wide hardware decoding support as Profile 0. You could use -profile:v 0 to make sure that this doesn't happen without you noticing.

The rest of the parameters are quality and rate control features for libvpx-vp9. Remember to at least use -b:v 0, otherwise CRF won't behave as you'd expect. Also note that setting -tile-columns and -threads manually is required, otherwise encoding is limited to a single thread.

Your Answer

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

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