DEV Community

Javid Jamae
Javid Jamae

Posted on • Originally published at ffmpeg-micro.com

How to Loop Video with FFmpeg (-stream_loop and the loop Filter)

Originally published at ffmpeg-micro.com

Every video editor eventually needs to loop a clip. Background videos on landing pages, smooth animated loops for social, boomerang effects, extended B-roll from a 3-second shot. FFmpeg has two ways to do this, and picking the wrong one wastes time or breaks audio sync.

Quick answer: use -stream_loop N before -i for simple repeats with no re-encoding. Use the loop video filter when you need frame-level control or want to loop just the video track independently from audio.

This guide covers both methods, the audio sync problems you'll hit, and how to loop video through the FFmpeg Micro API without installing anything.

Last verified 2026-07-23: all commands tested on FFmpeg 7.1. API examples match the live FFmpeg Micro endpoint.

Skip the FFmpeg install. Run any of these loops through one API call, no codecs to compile, no server to manage. Get a free API key

Method 1: -stream_loop (fast, no re-encoding)

The -stream_loop flag tells FFmpeg to repeat the entire input file N times before processing starts. It goes before -i, not after.

ffmpeg -stream_loop 2 -i input.mp4 -c copy output.mp4
Enter fullscreen mode Exit fullscreen mode

-stream_loop 2 means "loop the input 2 additional times," so the output is 3x the original length. A 10-second clip becomes 30 seconds.

The -c copy flag matters here. Because -stream_loop works at the demuxer level, FFmpeg can concatenate the repeated streams without decoding or re-encoding. That makes it fast and lossless.

Loop infinitely

ffmpeg -stream_loop -1 -i input.mp4 -t 300 -c copy output.mp4
Enter fullscreen mode Exit fullscreen mode

Setting -stream_loop to -1 loops forever. You almost always want to pair this with -t to set a maximum duration in seconds. Without it, FFmpeg will keep writing until your disk fills up.

When -stream_loop breaks

There are a few cases where -stream_loop won't give you clean output:

  • Containers without proper timestamps. Some MKV files or raw streams have timing gaps at the loop point. You'll see a brief freeze or stutter where the loop resets.
  • Audio drift. If the audio and video tracks have slightly different durations (common with variable frame rate footage), the drift compounds with each loop. After 5 or 6 repeats, audio can be noticeably out of sync.
  • No per-track control. You can't loop video 3 times but keep audio at 1x. The whole input gets looped together.

If you hit any of these, use method 2.

Method 2: The loop video filter

The loop filter operates on decoded frames, which gives you finer control but requires re-encoding.

ffmpeg -i input.mp4 -vf "loop=loop=3:size=250:start=0" -c:v libx264 -crf 23 output.mp4
Enter fullscreen mode Exit fullscreen mode

The filter takes three parameters:

  • loop = number of times to repeat the selected frames (not including the first play)
  • size = number of frames in the loop segment
  • start = frame number where the loop begins (0-indexed)

So loop=3:size=250:start=0 takes the first 250 frames and plays them 4 times total (1 original + 3 loops).

Loop the entire video

To loop the whole video, set size to the total frame count. You can get that with ffprobe:

# Get total frame count
ffprobe -v error -count_frames -select_streams v:0 \
  -show_entries stream=nb_read_frames -of csv=p=0 input.mp4

# Then use that number as size
ffmpeg -i input.mp4 -vf "loop=loop=2:size=750:start=0" -c:v libx264 -crf 23 output.mp4
Enter fullscreen mode Exit fullscreen mode

Create a boomerang effect

Play forward, then reverse, then forward again. This uses the reverse filter with concat:

ffmpeg -i input.mp4 \
  -filter_complex "[0:v]split[fwd][rev];[rev]reverse[rvd];[fwd][rvd]concat=n=2:v=1:a=0" \
  -c:v libx264 -crf 23 boomerang.mp4
Enter fullscreen mode Exit fullscreen mode

This splits the video into two copies, reverses one, and concatenates them. The result is one forward-reverse cycle. Stack more concat inputs to repeat the boomerang.

Audio sync: the gotcha nobody warns you about

Both methods can produce audio sync issues, but for different reasons.

With -stream_loop, the problem is accumulated drift. If your video track is 10.033 seconds and your audio is 10.028 seconds, each loop adds 5ms of drift. After 20 loops, you're 100ms off. That's noticeable.

With the loop filter, the problem is that -vf loop only loops the video stream. Audio plays once and stops. If you want looped audio too, you need to handle it separately:

ffmpeg -i input.mp4 \
  -vf "loop=loop=2:size=250:start=0" \
  -af "aloop=loop=2:size=250*44100:start=0" \
  -c:v libx264 -crf 23 output.mp4
Enter fullscreen mode Exit fullscreen mode

The aloop filter works on audio samples, not frames. Multiply your frame count by the sample rate (usually 44100 or 48000) to match the video loop length.

Simpler alternative: drop the audio, loop the video, then re-add audio in a second pass:

# Loop video only
ffmpeg -stream_loop 3 -i input.mp4 -an -c:v copy video_only.mp4

# Add background music that matches the new duration
ffmpeg -i video_only.mp4 -i background.mp3 -c:v copy -c:a aac -shortest output.mp4
Enter fullscreen mode Exit fullscreen mode

Which method to use

Scenario Method Why
Repeat a clip 2-5 times, audio doesn't matter -stream_loop Fast, no re-encoding
Background video for a website (no audio) -stream_loop -1 with -t Infinite loop capped to duration
Precise frame-level loop control loop filter Pick exact start frame and segment length
Boomerang / ping-pong effect reverse + concat filter Needs filter graph
Loop with new background audio -stream_loop + second pass Loop video, add audio separately

Loop video with the FFmpeg Micro API

If you don't want to install FFmpeg, you can run the same operations through the FFmpeg Micro REST API. One HTTP call, and the output lands in cloud storage.

Simple repeat with -stream_loop

Input-level options like -stream_loop go inside the input object:

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{
      "url": "https://example.com/clip.mp4",
      "options": [
        {"option": "-stream_loop", "argument": "2"}
      ]
    }],
    "outputFormat": "mp4"
  }'
Enter fullscreen mode Exit fullscreen mode

Loop with re-encoding (filter-based)

Output-level options like -vf go in the top-level options array:

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{"url": "https://example.com/clip.mp4"}],
    "outputFormat": "mp4",
    "options": [
      {"option": "-vf", "argument": "loop=loop=3:size=250:start=0"},
      {"option": "-c:v", "argument": "libx264"},
      {"option": "-crf", "argument": "23"}
    ]
  }'
Enter fullscreen mode Exit fullscreen mode

The API queues the job and returns a transcode ID. Poll GET /v1/transcodes/{id} to check status and grab the output URL when it's done. No binary to install, no server to maintain, and it scales to thousands of jobs without touching your infrastructure.

Sign up for a free API key and try it.

Common pitfalls

  • Forgetting -stream_loop goes before -i. Put it after -i and FFmpeg silently ignores it. No error, just no looping.
  • Using -c copy with the loop filter. The loop filter decodes frames, so you must re-encode. -c copy with -vf loop will either error or produce garbage.
  • Not capping infinite loops. Always pair -stream_loop -1 with -t <seconds>. Otherwise your output file grows until the disk is full.
  • Assuming audio loops with video. The -vf loop filter only touches the video stream. Audio stops after one play unless you add aloop or handle it separately.
  • Variable frame rate inputs. VFR footage and -stream_loop don't mix well. Convert to constant frame rate first with -vsync cfr or -vf fps=30.

FAQ

Does -stream_loop work with all container formats?

It works reliably with MP4, MOV, and TS containers. MKV and WebM can have timestamp discontinuities at loop boundaries. If you're looping MKV, test the output for stutters at the transition point.

Can I loop just a section of a video?

Yes. Use the loop filter with start and size to pick the exact frame range. Or trim the section first with -ss and -t, then loop the trimmed clip with -stream_loop.

How do I create a perfect loop with no visible cut?

The cleanest approach is to crossfade the last few frames into the first few frames using xfade:

ffmpeg -stream_loop 1 -i input.mp4 \
  -filter_complex "xfade=transition=fade:duration=0.5:offset=9.5" \
  -c:v libx264 -crf 23 looped.mp4
Enter fullscreen mode Exit fullscreen mode

Set offset to (clip_duration - fade_duration) for the fade to start at the right moment.

What's the maximum number of loops?

There's no hard limit with -stream_loop, but practical limits kick in. A 10-second 1080p clip looped 100 times at 5 Mbps is about 6 GB. The API handles large jobs fine, but check your storage and bandwidth budget.

Top comments (0)