ffmpegvideo-automationapi

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

·Javid Jamae·6 min read
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 FFmpegFFmpeg Micro API
SetupInstall and update FFmpeg yourselfNo servers to run, no binary
One videoTwo commandsOne API call
200 videosA bash loop that ties up your machineSubmit jobs, get webhooks
Runs inside n8n/MakeNeeds a self-hosted execute nodeNative HTTP node
Long jobsYour process babysits themPoll 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 copy and 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 aac smooths it.
  • Running the detection pass in production without limits. Analyzing a 4K two-hour file is CPU-heavy. Downscale first with -vf scale=640:-1 in 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.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Ready to process videos at scale?

Start using FFmpeg Micro's simple API today. No infrastructure required.

Get Started Free