When I figured out the problem of the beginning being cut off when converting a MOV trimmed in QuickTime to a GIF using FFmpeg, an unexpected specification came to light

When I figured out the problem of the beginning being cut off when converting a MOV trimmed in QuickTime to a GIF using FFmpeg, an unexpected specification came to light

When converting a MOV file recorded and trimmed with QuickTime to a GIF using FFmpeg, I encountered an issue where the beginning of the video was missing. After investigating the cause and solution, I discovered a surprising difference in specifications between QuickTime and FFmpeg.
2026.07.27

This page has been translated by machine translation. View original

First, I've summarized the solution for those in a hurry. If you're wondering what this is all about, skip ahead to the background section!

Solution Summary for Those Having Issues

Adding -ignore_editlist 1 to the input options will fix it. Adjust the trimming with -ss/-to as needed.

Note that -ignore_editlist 1 must be written before -i (input side), and -ss/-to must be written after -i (output side), or they won't work.

ffmpeg -ignore_editlist 1 -i input.mov -vf "fps=<frame rate>" -ss <start position in seconds> -to <end position in seconds> output.gif

Values to Specify for -ss/-to

You need to specify the timestamp visible in QuickTime Player plus an offset.

The offset can be calculated as "edit list time ÷ timescale". Each can be checked with the following commands.

# Check the edit list time
ffprobe -v trace input.mov 2>&1 | grep -A 3 elst

# Check the timescale
ffprobe -v trace input.mov 2>&1 | grep time_scale

If you still have the file before trimming, or if you can re-record, using an unedited MOV file means the following command will work. You can also specify the seconds using the timestamps visible in QuickTime Player.

ffmpeg -i input.mov -vf "fps=<frame rate>" -ss <start position in seconds> -to <end position in seconds> output.gif

Note that this article uses QuickTime Player 10.5 and FFmpeg 8.1.2.

Background

At DevelopersIO, given its nature as a technical blog, there are frequent occasions where we want to embed videos for demos and similar purposes. However, as of July 24, 2026, Zenn markup does not support embedding video files. While embedding YouTube videos is possible, I find it cumbersome to play back, so I almost exclusively convert videos to GIF for embedding.

Since I use QuickTime Player for screen recording, the file format is MOV. To convert MOV to GIF, I use the convenient command-line tool FFmpeg.

Now, this happened when I was writing the following blog post the other day.

https://dev.classmethod.jp/articles/summer-project-2026-call-and-support-ai-assistant/

Since it was an article introducing an app I built, I needed to include a demo video at the beginning. I started by screen recording with QuickTime Player.

Due to the nature of GIFs looping automatically, if you don't have a few seconds of still image at the beginning and end, the transition looks abrupt and hard to watch. This time, since showing the AI chat streaming all the way to the end would make it too long, I recorded it so that only the beginning had a longer still section. Afterward, I trimmed the beginning and end to the optimal length and saved it. I cut about 1 second from the beginning and about 3 seconds from the end.

I then converted that MOV to a GIF using FFmpeg with the following command.

ffmpeg -i input.mov -vf "fps=10" output.gif

For some reason, the still section at the beginning of the output GIF was completely cut off. The end, on the other hand, was fine.

Comparison of MOV and GIF

Click here for an actual GIF comparison

Comparison of expected vs. actual
Part of the beginning is missing.

This had never happened before. I thought for a while about whether I had done anything differently than usual, and come to think of it, this time I had trimmed more than usual from the beginning and end...

As a test, I converted a MOV with no trimming at all using the same command, and the problem did not occur. In other words, trimming is the cause.

Investigating the Edit List

QuickTime's trimming uses a mechanism called an edit list (elst). An edit list is metadata that exists in the MOV/MP4 container. It is designed to change only the playback range without touching the video data itself.

The edit list can be checked in ffprobe's trace log.

ffprobe -v trace input.mov 2>&1 | grep -A 3 elst

For the trimmed MOV file, the result was as follows.

[mov,mp4,m4a,3gp,3g2,mj2 @ 0xae0c0c000] track[0].edit_count = 1
[mov,mp4,m4a,3gp,3g2,mj2 @ 0xae0c0c000] duration=8709 time=648 rate=1.000000

time=648 means "start playback from a position 648 units from the beginning of the media data," and duration=8709 means "stop playback at a position 8709 units in."

Conceptual diagram of MOV container

This unit is the track's timescale. The default timescale in QuickTime is 600, and this file was the same. So time=648 means skipping 648 ÷ 600 = 1.08 seconds from the beginning, and duration=8709 means cutting off after 8709 ÷ 600 ≈ 14.5 seconds of playback. This matches the trimming I applied to the MOV file.

In other words, while the file itself was already trimmed, the edit list still contained the trimming instructions. Since FFmpeg follows the edit list by default, this skip was applied on top of the existing trimming, and my initial theory was that the GIF was being generated with the beginning trimmed twice. Though that doesn't explain the end trimming...

Initial theory

For now, I tried converting to a GIF using the following command. The -ignore_editlist 1 option ignores the edit list instructions and decodes all media data from the beginning[1].

ffmpeg -ignore_editlist 1 -i input.mov -vf "fps=10" output.gif

Checking the GIF, the still section at the beginning was properly preserved. However, something seemed off...

On closer inspection, the portion at the beginning that I had trimmed in the MOV was completely restored. The end wasn't fully restored, but it was partially back.

Timeline comparison

Strange behavior. This behavior cannot be explained by the double-trimming theory alone.

For the GIF conversion itself, I just needed to remove the extra parts from this point, and as introduced at the beginning, using -ignore_editlist 1 together with -ss/-to (options to cut input/output) worked fine. But having come this far, I felt the urge to unravel this mysterious behavior!

To start, further consideration of the specifications of QuickTime and FFmpeg seemed necessary.

QuickTime's Trimming Specifications

First of all, what is the point of leaving an edit list in a trimmed file? Once you reopen a trimmed video in QuickTime Player, you can't revert it, and it seems pointless to deliberately keep a history of trimming.

After researching this point in detail, it appears this is related to a special specification in QuickTime's trimming. I'll explain in detail below.

Prerequisite Terminology

QuickTime's screen recording uses a video compression codec called H.264. Video is a sequence of tens of frames per second, but saving every frame as an image would be enormous in size, so H.264 records most frames as "differences from other frames" and inserts keyframes that hold complete image information at regular intervals.

A group from one keyframe up to just before the next is called a GOP (Group of Pictures). Since keyframes have large data sizes, encoders increasingly widen the intervals for footage with little change. As a result, screen recordings with minimal movement can have GOPs spanning several seconds.

Schematic of frame sequence

For those who want a detailed explanation, the following AWS blog post is easy to understand.

https://aws.amazon.com/jp/blogs/news/jpmne-back-to-basic-what-mechanisms-are-used-behind-the-scenes-in-video-compression/

Handling Edit Lists and Preroll

Difference frames cannot be decoded without their referenced frames. Meanwhile, QuickTime's trimming allows cutting at any frame regardless of keyframe positions, and it's lossless trimming without re-encoding[2].

To achieve both of these, at minimum the data from the trimming point back to the preceding keyframe (the preroll) must remain in the file. Without the preroll, the frames immediately after the trimming point cannot be decoded. On top of that, the edit list instructs "playback starts from the trimming point" to hide the unnecessary footage.

In short, the edit list (and preroll) is information that must always be preserved to achieve lossless trimming.

I verified the detailed specifications of QuickTime's trimming by creating several files with different trimming positions. I'll omit the details of the verification, but the results were as follows.

  • Beginning side: Deletes data before the keyframe immediately preceding the trimming point. The remaining preroll is the minimum necessary for decoding, and the edit list's time becomes the distance from the trimming point to the preceding keyframe.
  • End side: Retains the frame containing the trimming point + up to 2 more frames, and deletes data beyond that. In H.264, the storage order and display order can be offset by several frames because future frames are also referenced, so this is probably a buffer for that reordering.

Mechanism of trimming

By the way, QuickTime's screen recording is basically Variable Frame Rate (VFR). Since frames are only recorded when there is a change, the same frame continues to be displayed for several seconds in still sections like the beginning of this recording.

With this in mind, when I checked the list of keyframes in this file, the 1.08-second trimming point was between the first keyframe and the second keyframe.

Media timeline

Actual process of checking keyframes

Keyframe positions can be checked with the following command.

ffprobe -v error -skip_frame nokey -select_streams v \
  -show_frames -show_entries frame=pts_time -of csv=p=0 input.mov

Here are the results when run on the trimmed recording file.

0.773333
1.760000
2.746667
4.908333
6.841667
9.015000

The first keyframe is at the 0.773-second position, and there is no keyframe at time 0. Since ffprobe basically returns timestamps reflecting the edit list, it follows the time instruction and hides the keyframe at the beginning. That's why it appears as if there is no keyframe at the start.

So next, I ran the same list with -ignore_editlist 1.

0.000000
3.135000
4.121667
5.108333
7.270000
9.203333
11.376667

New keyframes that weren't in the previous list appeared, showing up at the 0-second point. This is the true first keyframe, the starting point of the original video.

This confirmed that the earlier schematic was correct, and I now had a general understanding of what QuickTime had done.

In other words, the 1.08 seconds at the beginning that should have been trimmed had not been deleted from the data and remained entirely as preroll. The end also likely had a little over 2 extra frames remaining.

Differences Between FFmpeg and QuickTime Specifications

Now, the main topic. Why does FFmpeg remove the still section at the beginning entirely?

Comparing the timestamps in the keyframe list, all entries were offset by about 2.36 seconds between the case with the edit list applied and without it. This is the amount of skip that FFmpeg applied to the MOV file.

On the other hand, the edit list instruction was time=648 (1.08 seconds), meaning it was cutting about 1.28 seconds more than instructed.

I hypothesized that this was due to differences in trimming specifications between FFmpeg and QuickTime. After investigating FFmpeg's source code and other materials, the following specifications became clear.

  • QuickTime Player: Displays the frame spanning the trimming point (not a keyframe, but a frame) for the remaining duration starting from the trimming point, so the still beginning is visible. Similarly, it cuts off the frame spanning the trimming point at the end, so it ends at the intended position.
  • FFmpeg: Discards frames that start before the trimming point and plays from the first frame that starts after the trimming point, so the still beginning disappears. Conversely at the end, it includes frames that span the end point without discarding them, so the still ending doesn't disappear and can actually end up longer than intended.[3]

Furthermore, after actually checking the timestamps of all frames including non-keyframes, there was a long frame lasting 1.8 seconds from 0.53 seconds to 2.36 seconds.

About the difference in specifications

In other words, the cause of this incident was that FFmpeg discarded the entire long frame that spanned the trimming point.

Incidentally, I also checked the end side in the actual file, and as described in the deletion rules above, the frame spanning the trimming point + 2 frames remained hidden by the edit list. This is why part of the end trimming is restored when using -ignore_editlist 1.

Actual verification process

I checked the list of all frames including non-keyframes. If the hypothesis was correct, the entry just before 1.08 seconds should be immediately followed by a jump to 2.361667 seconds.

ffprobe -v error -ignore_editlist 1 -select_streams v \
  -show_frames -show_entries frame=pts_time -of csv=p=0 input.mov | head -10
0.000000
0.026667
0.066667
0.093333
0.466667
0.506667
0.533333
0.560000 ← Frame immediately before the 1.08-second mark
2.361667
2.388333

Just as expected, 0.560000 is immediately followed by 2.361667. The frame starting at 0.56 seconds continues to be displayed for about 1.8 seconds, spanning the trimming point of 1.08 seconds. The measured excess skip of 2.361667 − instruction of 1.08 = 1.281667 seconds closely matches the excess amount measured from the keyframe list!

The reason the still section at the beginning disappeared entirely has finally become clear.

I also checked the end side in the actual file.

12.525000
12.631667
13.045000
13.538333
14.098333
14.538333
15.031667 ← Start of frame spanning the trimming point
15.525000 ← End
16.060000
17.166667

As described in the deletion rules above, the frame spanning the trimming point + 2 frames remain hidden by the edit list.

Why Are the Specifications Different?

I couldn't find a clear source explaining why FFmpeg discards frames, so I'd like to consider it here.

QuickTime Player is an application for playing back video, and as long as it can render the image to display at any given moment, it doesn't matter if that timestamp falls in the middle of a frame's display duration.

On the other hand, FFmpeg reads files and cuts them into frame or packet unit data, passing frames one by one to the next process. In terms of implementation, there is no mechanism to convey "include this frame but start displaying it midway through"[3:1].

Therefore, my current hypothesis is that FFmpeg simply doesn't account for cases in VFR where a single frame is extremely long. If anyone knows the real reason, please let me know!

Summary

The specifications uncovered this time can be summarized as follows.

QuickTime Player's Trimming

  • It is lossless trimming using an edit list, and the video data itself is not completely deleted
  • On the beginning side, only data before the keyframe immediately preceding the trimming point is deleted, and the data in between remains as preroll. On the end side, the frame spanning the trimming point + up to 2 frames are retained.

FFmpeg's Interpretation of the Edit List

  • By default it follows the edit list, but on the beginning side it discards entire frames that start before time. On the end side, conversely, it includes frames that span the end point without discarding them.
  • As a result, the beginning can be cut more than instructed, and the end can remain longer than intended.

Screen Recording Specifications

  • QuickTime's screen recording is VFR, so in still sections a single frame's display duration can span several seconds.
  • When that long frame spans the trimming point, FFmpeg will discard the entire still section at the beginning.

As an aside, the specifications uncovered this time mean that, conversely, some footage dropped by QuickTime trimming can remain without being deleted. As shown in this article, it can be recovered by adding -ignore_editlist, so it may be risky to handle accidentally captured confidential information by trimming it out.

I hope this is helpful to anyone experiencing the same issue!

脚注
  1. FFmpeg Formats Documentation ↩︎

  2. Apple Support Community thread on lossless trimming verification ↩︎

  3. FFmpeg/libavformat/mov.c at 705890061467ad550ecc1dad5eea07f28ccfb43e · FFmpeg/FFmpeg · GitHub ↩︎ ↩︎

Share this article

Related articles