Thank you @bartolo-otrit for the right track.
It took a long time but I've figured out what seems like a really efficient way to do this.
Alternatives
The naive solution would be a circular buffer. This might be possible, but it surely doesn't feel practical as I can't anything online about doing so.
You could also somehow remove data from the beginning of a file when it gets too long, but I don't think that's possible while the file is being written to.
@Sepero's answer is a great workaround, but I really wanted to be able to eventually include audio, so I explored a fully native ffmpeg solution.
This is where the segment
ffmpeg format comes in.
Concept
Let's say that we only want to keep the last 5 seconds of data.
The idea is that we write 5 seconds worth of data to File A, then start writing the next 5 seconds to File B. When we reach 10 seconds, we start overwriting the next 5 seconds onto File A (because we already have the last 5 seconds in File B), and so on. We'll always have the last 5 seconds across either of the two files, and we only have an overhead of 10 seconds at most. This prevents us from needing to store hours worth of screen recording, only to trim off the last N seconds/minutes.
After we have the two segments, we can combine them into one file <= 10 seconds long. Then we can trim the last 5 seconds from that file.
I'm using Windows 11 and ffmpeg version n4.3.1-29.
1. Record Screen Into Segments
This will constantly record your desktop using gdigrab and pass that input into segment. Segment will create two files named segment0.mp4 and segment1.mp4, and a catfile.ffcat to help combine them later.
You can stop this command with Ctrl + c or by typing q, then enter.
ffmpeg -y -f gdigrab -video_size 1920x1080 -show_region 1 -i desktop \
-f segment -segment_time 300 -segment_format mp4 -segment_list catfile.ffcat \
-segment_wrap 2 -reset_timestamps 1 segment%d.mp4
I'm not an ffmpeg expert, but I'll try to explain what each part of the command does:
-y
- Responds yes (Y) to any prompts ffmpeg asks. I use it to automatically overwrite any existing files from previous runs.
-f
- Stands for format. It's used to force the following input/output into the specified ffmpeg format. From what I can tell, this flag is often optional so some people leave it out. I think it really helps with readability, so I'd recommend including in the command.
gdigrab
- A Windows-only format used for recording your desktop. You can try switching this out for one of the formats for macOS or Linux by referring to this guide.
-video_size
- An option for gdigrab that specifies the screen size (gdigrab has a ton of helpful options you might like, see this link).
-show_region 1
- Option for gdigrab that shows an outline around the part of your screen currently being recorded.
-i desktop
- desktop is a keyword to capture your entire screen. If you don't specify the video_size, this command will record every monitor in one really wide video, which likely isn't preferable.
segment
- Goes over my head for the most part, but the segment format is for splitting input among segments of smaller video files of a predefined length (see the segment docs here).
-segment_time 300
- How long each video segment is in seconds. I set it to 300s, which comes out to 5 minutes. Note that this will likely be off by a few seconds (presumably due to encoding requirements). I tried setting it to 5 seconds, and my video segments ended up being ~7s long instead. Shouldn't be a big deal, but it's worth noting.
-segment_format mp4
- The file format you're going to output in. I used mp4 and I've seen others use mkv, but I'm not sure what others are supported with segment.
-segment_list catfile.ffcat
- This part of the command is super cool. It creates a special .ffcat file (I chose the name "catfile" but you can call it whatever). It keeps track of the order of your output segments. Otherwise, you have to figure out what order to recombine them in, which isn't fun.
-segment_wrap 2
- Only create up to two segments. After it fills up the second segment, it starts overwriting the first. This ensures that you'll never lose data newer than segment_time
. If you don't specify this value, it'll keep creating new segment files and never delete the old data, which defeats the purpose of this strategy.
-reset_timestamps 1
- I don't entirely understand what this does, but it seems to help when combining the segments back together.
segment%d.mp4
- This is your output file name. The %d
is important for the segment format to work correctly. It basically creates new segments using that file name, replacing the %d with the current segment index. You should end up with two files: "segment0.mp4" and "segment1.mp4".
2. Combine the Segments Back Together
From @Gyan's answer here
Once we've created two chunks, we can easily combine them into one file by leveraging our catfile created in the previous command.
I don't know the fine details of this one, but -c copy
is explained here.
ffmpeg -y -i catfile.ffcat -map 0 -c copy combined.mp4
3. Get the Last 5 Minutes
From @llogan's answer here.
Trim the last 5 minutes. Like I said, we have a video that's guaranteed to be between 5 - 10 minutes long, so we should have no problem trimming down the last 5 minutes.
ffmpeg -y -sseof -300 -i combined.mp4 output.mp4
4. Remove the Tempoary Files
You'll need to delete this extra files yourself after the operation. You can run:
rm catfile.ffcat segment0.mp4 segment1.mp4 combined.mp4
Very long-winded answer but I really wished I had something like it when I was looking, so I hope that this knowledge helps someone else!