Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ffmpeg drops time delay on last frame of animated GIF

I am trying to convert animated GIFs to MP4 files using ffmpeg and x264. However, I seem be suffering from the effects of this bug in ffmpeg that causes the delay time of the last frame of the GIF to be ignored. For very short GIFs, this is quite a problem.

As a work around, I was thinking that I should be able to manually tell ffmpeg to freeze on the last frame for a certain amount of time, specifically the proper duration of that frame (which I can extract from the GIF). However, I can't seem to find a good way to do this. Any suggestions? I would really like to be able to do this without having to split to the GIF into frames before putting it into ffmpeg since that will mess up GIFs with a non-constant framerate (in addition to being much slower).

I am using ffmpeg version 2.3, though I have also tried this with the latest git code without any improvement. The full ffmpeg commands I'm using look like this:

ffmpeg -i animation.gif -vf "scale=trunc(in_w/2)*2:trunc(in_h/2)*2" -c:v libx264 -b:v 2000k -y -pix_fmt yuv420p -f mp4 animation.mp4

Here is some console output:

ffmpeg version 2.3 Copyright (c) 2000-2014 the FFmpeg developers
  built on Aug 11 2014 21:19:46 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1)
  configuration: --enable-gpl --enable-libass --enable-libfreetype --enable-libtheora --enable-libvorbis --enable-libx264
  libavutil      52. 92.100 / 52. 92.100
  libavcodec     55. 69.100 / 55. 69.100
  libavformat    55. 48.100 / 55. 48.100
  libavdevice    55. 13.102 / 55. 13.102
  libavfilter     4. 11.100 /  4. 11.100
  libswscale      2.  6.100 /  2.  6.100
  libswresample   0. 19.100 /  0. 19.100
  libpostproc    52.  3.100 / 52.  3.100
Input #0, gif, from 'animation.gif':
  Duration: N/A, bitrate: N/A
    Stream #0:0: Video: gif, bgra, 500x375, 100 tbr, 100 tbn, 100 tbc
[libx264 @ 0x239ea00] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX
[libx264 @ 0x239ea00] profile High, level 3.1
[libx264 @ 0x239ea00] 264 - core 142 - H.264/MPEG-4 AVC codec - Copyleft 2003-2014 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=abr mbtree=1 bitrate=2000 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to 'animation.mp4':
  Metadata:
    encoder         : Lavf55.48.100
    Stream #0:0: Video: h264 (libx264) ([33][0][0][0] / 0x0021), yuv420p, 500x374, q=-1--1, 2000 kb/s, 100 fps, 12800 tbn, 100 tbc
    Metadata:
      encoder         : Lavc55.69.100 libx264
Stream mapping:
  Stream #0:0 -> #0:0 (gif (native) -> h264 (libx264))
Press [q] to stop, [?] for help
frame=    7 fps=0.0 q=-1.0 Lsize=       7kB time=00:00:00.05 bitrate=1222.1kbits/s dup=5 drop=0    
video:7kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 13.542441%
[libx264 @ 0x239ea00] frame I:1     Avg QP:34.86  size:  3657
[libx264 @ 0x239ea00] frame P:3     Avg QP:30.86  size:   744
[libx264 @ 0x239ea00] frame B:3     Avg QP:33.33  size:    49
[libx264 @ 0x239ea00] consecutive B-frames: 42.9%  0.0%  0.0% 57.1%
[libx264 @ 0x239ea00] mb I  I16..4: 10.2% 78.3% 11.6%
[libx264 @ 0x239ea00] mb P  I16..4:  1.2%  5.0%  0.8%  P16..4: 11.7%  3.3%  1.2%  0.0%  0.0%    skip:76.8%
[libx264 @ 0x239ea00] mb B  I16..4:  0.0%  0.1%  0.0%  B16..8:  3.7%  0.0%  0.0%  direct: 0.0%  skip:96.2%  L0:23.5% L1:76.5% BI: 0.0%
[libx264 @ 0x239ea00] final ratefactor: 20.31
[libx264 @ 0x239ea00] 8x8 transform intra:77.0% inter:79.4%
[libx264 @ 0x239ea00] coded y,uvDC,uvAC intra: 39.5% 0.0% 0.0% inter: 2.7% 0.0% 0.0%
[libx264 @ 0x239ea00] i16 v,h,dc,p: 38% 27%  7% 28%
[libx264 @ 0x239ea00] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 35% 12% 13%  7%  6% 10%  4%  8%  5%
[libx264 @ 0x239ea00] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 38% 11% 14%  5%  8% 10%  5%  7%  2%
[libx264 @ 0x239ea00] i8c dc,h,v,p: 100%  0%  0%  0%
[libx264 @ 0x239ea00] Weighted P-Frames: Y:0.0% UV:0.0%
[libx264 @ 0x239ea00] ref P L0: 99.1%  0.7%  0.3%
[libx264 @ 0x239ea00] ref B L0: 85.0% 15.0%
[libx264 @ 0x239ea00] ref B L1: 95.4%  4.6%
[libx264 @ 0x239ea00] kb/s:689.83
like image 413
Austin Avatar asked Oct 20 '22 03:10

Austin


1 Answers

So I managed to modify this answer, which suggests using nullsrc with an overlay filter. nullsrc causes problems for transparent GIFs, so I used color instead:

ffmpeg -i animation.gif -filter_complex "color=c=white:s=340x240:d=0.300 [base]; [base][0:v] overlay" -c:v libx264 -b:v 2000k -y -pix_fmt yuv420p -f mp4 animation.mp4

Since the default action for the overlay filter is to hold the last frame of the overlay if the overlay source runs out before the base source, this effectively tricks ffmpeg into getting the duration of the last frame correct. However, the downside to this is that it requires knowing the dimensions and correct duration of the GIF beforehand (this can be done with image magick).

Additionally, the original command us using a scale filter to make sure the GIF has an even number of pixels on each edge (since yuv420p requires this). The new command uses the color dimensions to take care of this automatically, so you will have to round the GIF dimensions down to the nearest even numbers when setting the dimensions.

like image 184
Austin Avatar answered Oct 24 '22 00:10

Austin