FFmpeg Scene Detection: Auto-Split a Long Video at Scene Changes

You have a 40-minute recording and you want the clips inside it, the natural cuts where the camera or scene changes, without scrubbing a timeline and typing timestamps by hand. Time-based splitting (every 60 seconds) chops mid-sentence and mid-shot. What you actually want is content-aware splitting: let FFmpeg find where the picture changes and cut there.
Quick answer: FFmpeg scene detection uses the select='gt(scene,0.4)',showinfo filter to score how much each frame differs from the last and print the timestamp whenever that score crosses your threshold. Run it once to collect the cut points, then feed those timestamps into a segment split to auto-chop a long video at its scene changes.Conventional wisdom says splitting a long video means marking timestamps yourself or accepting dumb fixed-length segments. That's true for most setups. But the tool already ships with a detector. The catch isn't finding the feature, it's picking the right sensitivity, because one threshold number decides whether you get 8 clips or 800.
How FFmpeg scene detection works
FFmpeg's scene value is a per-frame score between 0 and 1 that measures how different a frame is from the previous one. A hard cut scores near 1. A slow pan scores near 0. The select filter passes frames whose score beats your threshold, and showinfo prints the details of each passed frame, including its pts_time (the timestamp in seconds).
Run detection like this and send the log to a file:
ffmpeg -i long-video.mp4 -filter:v "select='gt(scene,0.4)',showinfo" -f null - 2> scenes.txt
The -f null - tells FFmpeg to decode and analyze without writing an output file, so this pass is read-only and fast. Every detected cut shows up in scenes.txt on a line like pts_time:12.345.
Pull just the timestamps out:
grep showinfo scenes.txt | grep -oP 'pts_time:\K[0-9.]+'
You now have a list: 12.345, 47.8, 88.2, and so on. Those are your cut points.
Choosing a sensitivity threshold (0.2 to 0.5)
The threshold in gt(scene, 0.4) is the whole game. Lower catches more, higher catches less.
- 0.2 to 0.3 is aggressive. It fires on lighting shifts, fast motion, and camera shake. Use it for talking-head footage where cuts are subtle, and expect false positives.
- 0.4 is a solid default for most edited content with clean hard cuts.
- 0.5 and up is conservative. It only trips on dramatic changes, so you'll miss softer transitions but get almost no noise.
Start at 0.4, count how many cuts you get, then adjust. If a 20-minute video gives you 3 clips, drop to 0.3. If it gives you 200, raise to 0.5. Newer FFmpeg builds also expose the scdet filter (scdet=threshold=10), which reports scene scores on a 0 to 100 scale, but select plus showinfo is the technique documented across the FFmpeg cookbook, Bogotobogo, and dozens of gists because it works on every version people actually have installed.
Split the video at the detected scenes
Once you have the timestamps, feed them straight into a segment split. The segment muxer cuts a single input into multiple files at the times you name:
ffmpeg -i long-video.mp4 -c copy -map 0 \
-f segment -segment_times 12.345,47.8,88.2 \
clip%03d.mp4
This writes clip000.mp4, clip001.mp4, clip002.mp4, and so on. -c copy means no re-encode, so it's near-instant, and -map 0 keeps every stream (see the FFmpeg -map flag guide for what that selects). One detection pass, one split pass, and a long-form video becomes a folder of scene-accurate clips.
Manual FFmpeg vs an API for batch clipping
The CLI approach works for one video on your laptop. It stops scaling the moment you have a queue.
| Local FFmpeg | FFmpeg Micro API | |
|---|---|---|
| Setup | Install and update FFmpeg yourself | No servers to run, no binary |
| One video | Two commands | One API call |
| 200 videos | A bash loop that ties up your machine | Submit jobs, get webhooks |
| Runs inside n8n/Make | Needs a self-hosted execute node | Native HTTP node |
| Long jobs | Your process babysits them | Poll or webhook, download output |
If you're repurposing long-form into shorts for a faceless YouTube channel, you don't want a bash loop pinning your laptop for an hour. FFmpeg Micro exposes the same FFmpeg operations as a REST job: submit the video, the job runs on managed infrastructure, and you poll or get a webhook when the clips are ready to download. The job semantics and exact payload live in the docs, and it works with n8n, Make, and Zapier through a plain HTTP request node, so a whole repurposing pipeline runs without you touching a server.
Common pitfalls
- Keyframe snapping with
-c copy. Stream-copy segments can only cut on keyframes, so a clip may start a fraction of a second off from the exact scene time. If you need frame-accurate cuts, re-encode by dropping-c copyand adding-c:v libx264, which is slower but exact. - Threshold set once and forgotten. A value tuned for a screen recording will flood a music video with false cuts. Re-check the cut count per content type.
- Audio glitches at the seam. Copied audio can pop at cut boundaries. Re-encoding audio with
-c:a aacsmooths it. - Running the detection pass in production without limits. Analyzing a 4K two-hour file is CPU-heavy. Downscale first with
-vf scale=640:-1in the detection pass to find cuts faster, then split the original.
FAQ
How do I detect scene changes in FFmpeg?
Use the select='gt(scene,0.4)',showinfo filter with -f null - and read the pts_time values from stderr. Each pts_time is the timestamp, in seconds, where FFmpeg detected a scene change above your threshold.
What is a good scene detection threshold?
0.4 is a reliable default for edited content with hard cuts. Lower it toward 0.2 to 0.3 for subtle changes like lighting shifts, and raise it toward 0.5 to catch only dramatic scene changes without false positives.
How do I split a video by scene in FFmpeg?
Run scene detection to get the cut timestamps, then pass them to the segment muxer with -f segment -segment_times 12.3,47.8,88.2 -c copy. FFmpeg writes one numbered clip per interval between the detected cuts.
Why are my clips not cutting exactly at the scene change?
Because -c copy snaps every cut to the nearest keyframe. For frame-accurate splits, re-encode instead of stream-copying, which trades speed for precision.
Can I batch scene-split hundreds of videos automatically?
Yes. Run the detect-then-split logic as an API job per video instead of a local loop, so 200 files process in parallel without babysitting a single machine. FFmpeg Micro runs this as one API call from n8n, Make, Zapier, or your code.
If you're auto-chopping long-form into clips at any real volume, start on the learn hub to see the batch pipeline end to end, then sign up free and run your first scene-split job on the free tier.
About Javid Jamae
Founder & CEO at FFmpeg Micro
Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.
You might also like

Turn long-form video into Shorts/Reels/TikToks automatically
Turn long form video into shorts automatically by splitting moment-picking from clip-making, then calling a video API to cut, crop to 9:16, and caption.

Compress video for the web: bitrate, codecs, and one-call presets
Compress video for the web with the right bitrate, codec, and CRF preset. See the exact FFmpeg command plus a one-call video compression API you can try free.

Concatenate clips programmatically: a composition API primer
Concatenate clips programmatically with a composition API: why FFmpeg's concat demuxer breaks on mixed inputs, the real CLI commands, and the one-call fix.
Ready to process videos at scale?
Start using FFmpeg Micro's simple API today. No infrastructure required.
Get Started Free