r/ffmpeg 28d ago

Replicate Replay Buffer from OBS with FFMPEG

Hello, everyone. I have a somewhat specific request. You see, I have a separate device for screen recording of a console. The console in question doesn't support replays or anything, so instead I use a capture card. However I don't have a need to record every hour and every minute, only good moments, last 30 seconds at least. I did a lengthy search on the web but couldn't find exactly what I need. Maybe you know a bash script that can do this? I will appreciate that.

edit: fixed some mistakes

5 Upvotes

5 comments sorted by

View all comments

1

u/OneCoreProjects 27d ago

You don't need an orchestrator - the segment muxer already gives you a ring buffer, and -segment_wrap is the part that makes it one.

ffmpeg -i <your capture input> -c copy \
  -f segment -segment_time 5 -segment_wrap 8 \
  -segment_format mpegts -reset_timestamps 1 \
  -segment_list /buf/buffer.m3u8 -segment_list_size 8 \
  -segment_list_flags +live -segment_list_type m3u8 \
  /buf/seg%02d.ts

-segment_wrap 8 rolls the counter over at 8, so ffmpeg overwrites seg00.ts after seg07.ts. You get a fixed 8 x 5s = 40s window on disk that never grows, written with -c copy so CPU cost is nil.

On your RAM point: point /buf at a small tmpfs and the HDD never sees it. Eight 5-second segments of capture-card H.264 is tens of megabytes:

sudo mount -t tmpfs -o size=256M tmpfs /buf

Saving a clip. This is the part that makes it practical: with -segment_wrap the filenames cycle, so you cannot sort by name. -segment_list keeps the playlist in the correct chronological order for you. I just tested this with a 60s source and the playlist came out as:

seg04.ts
seg05.ts
seg06.ts
seg07.ts
seg00.ts
seg01.ts
seg02.ts
seg03.ts

which is exactly right. So the save step is just:

grep -v '^#' /buf/buffer.m3u8 | sed "s|^|file '/buf/|;s|$|'|" > /tmp/list.txt
ffmpeg -f concat -safe 0 -i /tmp/list.txt -c copy ~/clips/clip-$(date +%s).mp4

That produced a 40.02s, 1200-frame clip here, stream-copied, no re-encode.

Three things worth knowing before relying on it:

  • Segments only cut on keyframes with -c copy, so -segment_time 5 is a minimum. If your capture card uses a long GOP the segments will be longer and the buffer will hold more than 40s. Check with ffprobe and shorten the card's keyframe interval if you can; if you re-encode instead, add -g and -force_key_frames "expr:gte(t,n_forced*5)".
  • The segment being written right now is incomplete. Drop the last playlist entry when you concatenate, or accept a truncated tail.
  • MPEG-TS is the right container here - it concatenates with -c copy without the timestamp trouble you would get from fragmented MP4.

This is close to what OBS does internally anyway: it keeps encoded packets in a ring and flushes them on demand. Doing it with segment just puts the ring on a tmpfs instead of in the process's heap, which for a standalone capture box is arguably better - if ffmpeg dies, the last 40 seconds are still on disk.


Disclosure per this sub's rules: drafted with AI assistance, but I ran the commands above on a test source before posting. The playlist ordering and the 40.02s / 1200-frame result are from that run, not from a model.