Fix animated GIF timing

Have you found a priceless animated GIF but are disappointed with its timing? Maybe it’s too slow or too fast? I’ve got an easy solution for you. You don’t need any complex software like ffmpeg or mencoder or imagemagick or any of that crap (well, they’re not crap; they’re just ridiculously complicated and user hostile). All you need is a simple hex editor.

Here’s a simple before/after example of what I’m talking about:

Before:

Before

After:

After

As you can see, the top GIF is much slower (depending on your browser, here in Chrome it animates at the default of 10fps). The bottom GIF is much closer to the original movie’s framerate of approximately 25fps.

To do these kinds of simple corrections, all you need is a hex editor with a search and replace feature that lets you search for hex strings and replace them with other hex strings. The venerable HxD is a fantastic hex editor with exactly such a feature. Go download and install HxD now.

Now download your new favorite timing-challenged GIF to your local computer and open it with HxD.

Search (CTRL-F) from the beginning of the file for the hex string 21 F9 04 05 00 00 (you need to select Hex String from the drop down; the default is Text String which is not what you want). If you cannot find any matches using this 6 value string, search instead for 21 F9 04 05 (leave off the last two 00 00). If even that does not find any matches, then search for just the first three hex values 21 F9 04. Whatever you do find, copy 6 values out of the display starting at 21 F9 04 and put them back into the search/replace dialog.

The last two values of the 6 value sequence specify the delay time to wait after displaying each frame. You’ll encounter many copies of this sequence throughout the GIF file because it will exist for each frame of the animation.

Most of the problematic GIFs you’ll encounter will just specify 00 for the frame delay time, meaning “don’t care”, which is pretty dumb if you think about it. Browsers will just interpret that as defaulting to something obscenely slow and useless like 10fps, which explains why the GIF appears to be slow in playback.

Once you’ve identified the 6 value sequence to search for, go ahead and replace all occurrences of it with 21 F9 04 XX 07 00 (where XX is the same value as what you searched for, which may or may not be 05). The second-to-last value, 07, is the frame delay time measured in 1/100ths of a second. Feel free to modify that value to your liking. Choosing the best value here depends entirely on the source material’s frame rate, so I cannot tell you exactly what to fill in here.

I find that useful values are in the range from 04 to 07. Remember that the smaller the number, the faster the animation will run. You can do the math yourself based on the source material’s frame rate: 100 / n, where n is the frame rate.

  • 03 is pretty good for 30fps source content (actual rate will be 33.333fps, a bit too fast and sorta noticeable)
  • 04 is perfect for 25fps source content (most movies)
  • 07 is pretty good for 15fps source content (actual rate will be 14.286fps, a little slow but not very noticeable)

Unfortunately, we can’t specify fractional values in this animation delay time field, only integer values. This appears to be an oversight of the GIF animation specification. The 16 bits of space reserved for this animation rate value is horribly underutilized. No one should ever need an animation delay of 655.35 seconds, for instance. They should have instead stored a frequency value here, not a delay time value. Off the top of my head, I would make use of these 16 bits to store the animation rate in fps at a x100 scale. This would give much finer grained control over the frame rate, e.g. storing 2,997 as a 16-bit unsigned integer value will yield a playback rate of 29.97 fps, or 3,000 for 30.00fps, or 1,500 for 15.00fps, etc.

Use ffmpeg to record desktop video on Windows

Want to capture your desktop for screencasts on Windows? Use ffmpeg! It’s totally free and has no watermarks, no ads, etc. Unfortunately, there’s no GUI nor a simple installer for inexperienced users, but I’m here to try to alleviate some of that pain and give you the magical ffmpeg incantation to instantly get good quality results.

Download & Install

NOTE: When you go to download the installers for the tools you need, be sure to consistently choose either the x86 or the x64 version of each. Do not mix and match x86 and x64 installs as that will not work. If you have a 64-bit machine, I recommend the x64 version. If your machine is ancient and somehow only 32-bit, go with the x86 version.

First, download ffmpeg from here. Install it to somewhere obvious like C:\ffmpeg\. I don’t recommend installing into C:\Program Files\. File paths with whitespace in them are annoying to deal with on the command line, which is where we’ll be working.

ffmpeg is the main tool that handles video and audio encoding. It muxes those streams together and records them into a video file. Unfortunately, it does not come bundled with a desktop video capture input source for Windows which is what we need to capture your desktop screen with. So…

You’ll need a DirectShow filter to capture your desktop that ffmpeg can use. Go here and follow steps to download a binary version of that screen-capture-recorder project. (As of this writing you can find those binaries hosted on sourceforge‘s terrible, terrible site.)

Capture

Open a command prompt (Start -> Run -> cmd). Type in the following commands, assuming you installed ffmpeg to C:\ffmpeg:

C:\>SET PATH=%PATH%;C:\ffmpeg\bin

C:\>ffmpeg -f dshow -i video="screen-capture-recorder" -f dshow -ac 2 -i audio="virtual-audio-capturer" -ar 48000 -acodec libmp3lame -ab 192k -r 30 -vcodec libx264 -crf 18 -preset ultrafast -f mpegts desktop.mpg

That will start ffmpeg running and will capture both video from your desktop and audio from your sound card (what you’re hearing). It will encode video on-the-fly in h.264 at 30 frames per second with high quality and encode audio on-the-fly in MP3 at 192kbps (also high quality).

IMPORTANT: Press ‘q’ to stop. DO NOT PRESS CTRL-BREAK OR CTRL-C or you will prematurely abort the process and the file may not be finalized properly. Also, make sure the output file does not exist before you start recording.

Let’s break this command down a bit:

  • -f dshow specifies that the next input source is a DirectShow filter
  • -i video="screen-capture-recorder" specifies the screen-capture-recorder desktop video source you installed earlier
  • -f dshow specifies that the next input source is a DirectShow filter
  • -ac 2 specifies 2 audio channels to capture (i.e. stereo)
  • -i audio="virtual-audio-capturer" specifies the virtual-audio-capturer audio source that comes installed with the screen-capture-recorder (this records the audio you hear through your speakers)
  • -ar 48000 specifies to capture audio at 48000Hz (ideal for audio/video sync)
  • -acodec libmp3lame specifies to use libmp3lame as the audio encoder which implements the MP3 standard
  • -ab 192k specifies to encode MP3 audio at 192kbps (high quality)
  • -r 30 specifies to capture video at 30 frames per second (ideal for YouTube)
  • -vcodec libx264 specifies to encode video using the libx264 encoder which implements the h.264 standard
  • -crf 18 specifies the h.264 encoding quality of 18 which is good (0 = lossless, 30 = crap)
  • -preset ultrafast specifies an ultra-fast encoder setting so that we can reliably record without interruptions
  • -f mpegts to specify that we want to use MPEG-TS as our container format; this is beneficial for live streaming purposes and also for uploading to YouTube.
  • desktop.mpg is our output file

Feel free to tune the parameters to your liking. Enjoy!

Advanced Usage

To get a list of all the DirectShow filters (audio and video) available for you to record from, use this command:

ffmpeg -list_devices true -f dshow -i dummy

This will output something like this (example from my system):

[dshow @ 00000000028f83e0] DirectShow video devices
[dshow @ 00000000028f83e0]  "screen-capture-recorder"
[dshow @ 00000000028f83e0] DirectShow audio devices
[dshow @ 00000000028f83e0]  "1-2 (UA-1000)"
[dshow @ 00000000028f83e0]  "virtual-audio-capturer"
[dshow @ 00000000028f83e0]  "3-4 (UA-1000)"
[dshow @ 00000000028f83e0]  "5-6 (UA-1000)"
[dshow @ 00000000028f83e0]  "7-8 (UA-1000)"
[dshow @ 00000000028f83e0]  "9-10 (UA-1000)"
[dshow @ 00000000028f83e0]  "Mon (UA-1000)"

I use a Roland EDIROL UA-1000 multi-channel USB audio interface which has 8 input and output channels plus a monitor input source for recording what’s going out to the speakers.

You can add more than one audio track to your video if you want to narrate along with your video and also record the speaker output but not prematurely mix the two tracks. Here’s an example incantation (specific to my system) to do so:

ffmpeg -f dshow -i video="screen-capture-recorder" -ac 1 -f dshow -i audio="1-2 (UA-1000)" -ac 2 -f dshow -i audio="Mon (UA-1000)" -map 0 -r 30 -vcodec libx264 -crf 18 -preset ultrafast -map 1:0 -ar 48000 -acodec libmp3lame -ab 192k -map 2 -ar 48000 -acodec libmp3lame -ab 192k -f mpegts raw.mpg

I have my microphone on the “1-2 (UA-1000)” channel pair recorded to the primary audio track (in mono) and then the monitor output “Mon (UA-1000)” recorded to a second audio track (in stereo). Later, I extract the two audio streams from the recorded video file and process the microphone signal to clean it up; add compression, EQ to pull out low end and add high end, etc. Then I mix the two tracks back together and output a final video file including the mixed stereo audio track.

Note that parameter ordering is very important so don’t go rearranging things. The -map options specify how the input sources are to be mapped to output streams in the recorded file.