114

I just want to loop a mp4 video with ffmpeg and keep the current settings and codec.

For example if input.mp4 is 0:10 long, and I would want to loop it 4 times so output.mp4 is 0:40 long, how could I change the following command line to do that?

ffmpeg -i input.mp4 -c copy output.mp4

I tried...

ffmpeg -loop 4 -i input.mp4 -c copy output.mp4

...but I get the error "Option loop not found."

1
  • Use quicktime 7 pro and mov container. So you won't get a bigger file size, than with opensource one. Commented Jul 26, 2018 at 9:05

6 Answers 6

154

-stream_loop option

ffmpeg -stream_loop 3 -i input.mp4 -c copy output.mp4
  • This can avoid re-encoding because it can use stream copy.
  • 0 means no loop, -1 means infinite loop.
  • This may not work with anything older than FFmpeg 4.0.

concat demuxer

The concat demuxer allows you to loop an input without needing to re-encode because it can use stream copy.

  1. Make a text file. Contents of an example text file to repeat 4 times.

     file 'input.mp4'
     file 'input.mp4'
     file 'input.mp4'
     file 'input.mp4'
    
  2. Then run ffmpeg:

     ffmpeg -f concat -i list.txt -c copy output.mp4
    

If you want to add additional inputs make sure they all have the same attributes.

Automatically make list.txt in Linux/macOS

This example is the same as above but you don't have to manually make list.txt:

for i in {1..4}; do printf "file '%s'\n" input.mp4 >> list.txt; done
ffmpeg -f concat -i list.txt -c copy output.mp4

With most commonly-used modern shells, you can even avoid the creation of the list.txt file entirely. For example, with bash:

ffmpeg -f concat -i <(for i in {1..4}; do printf "file '%s'\n" input.mp4; done) -c copy output.mp4

Also see:


loop filter

Example using the loop filter to loop 4 times, each loop is 75 frames, each loop skips the first 25 frames of the input:

ffmpeg -i input.mp4 -filter_complex "loop=loop=3:size=75:start=25" output.mp4
  • Or use the shorthand: loop=3:75:25
  • Filtering requires re-encoding.
  • This filter places all frames into memory.
  • Using loop=3 will loop 4 times.
  • To loop infinitely use -1.
  • You must list the number of frames to loop (shown as 75 in the example above). Max value is 32767.
  • Also see ffmpeg -h filter=loop.

movie filter

The movie and amovie filters have a loop option:

ffmpeg -f lavfi -i "movie=filename=input.mp4:loop=4,setpts=N/FRAME_RATE/TB" -f lavfi -i "amovie=filename=input.mp4:loop=4,asetpts=N/SR/TB" output.mp4
  • Filtering requires re-encoding.
  • 1 means no loop, 0 means infinite loop.

-loop option

The -loop option is specific to the image file demuxer and gif muxer, so it can't be used for typical video files. But it can be used to infinitely loop a single image or a series of images.

single image

This example will loop a single image over and over but the -t 30 will limit the output duration to 30 seconds:

ffmpeg -loop 1 -i input.png -t 30 -vf format=yuv420p output.mp4

-vf format=yuv420p is for compatibility reasons.

series of images

ffmpeg -loop 1 -i %03d.jpg -t 30 -vf format=yuv420p output.mp4

GIF output

Or to loop a GIF:

ffmpeg -i input -loop 3 output.gif

For GIF output also see How do I convert a video to GIF using ffmpeg, with reasonable quality?

9
  • Does this work fine with mp4 files? Commented Oct 28, 2014 at 19:08
  • 3
    I get this error > <(for i in {1..4}; do printf "file '%s'\n" input.mp4; done) -bash: /dev/fd/63: Permission denied
    – JZ11
    Commented Nov 9, 2015 at 18:46
  • 1
    @jamesfzhang - I think that's because you need an absolute path to the file (as explained here: trac.ffmpeg.org/wiki/Concatenate). To fulfill that, you could write: ffmpeg -safe 0 -f concat -i <(for i in {1..4}; do printf "file '$PWD'/'%s'\n" input.mp4; done) -c copy output.mp4
    – Gnaural
    Commented May 18, 2017 at 15:57
  • 1
    As for me this works to repeat full time video without skipping fames for 4 times ffmpeg -i video.mp4 -filter_complex "loop=4:32767" output.mp4
    – Vlad
    Commented Jul 15, 2018 at 7:28
  • This issue is 4 years old and noone noticed that the link to the second issue in the -stream_loop section is pointing to the same ticket as the first one... Commented Aug 31, 2018 at 22:38
32

With ffmpeg 2.8.4, the following command creates output.mp4 that is a repeating copy of input.mp4 until the ffmpeg process is stopped:

ffmpeg -stream_loop -1 -i input.mp4 -c copy output.mp4

This command won't terminate on its own, and the output file will grow infinitely.

6
  • 2
    Seems to be the fastest method
    – josef
    Commented Jan 23, 2017 at 17:43
  • 4
    Fastest method is an understatement, this method ramps up to multi-hour videos in a matter of seconds.
    – SleighBoy
    Commented Feb 13, 2018 at 23:28
  • This is a great convenient method, because there is no need to create a file with repeating lines. Commented Jun 10, 2019 at 10:08
  • 1
    People need to change the value for -stream_loop parameter from -1 some positive integer according to theirs requirement. Otherwise, the file grows and the system throws No space left on device error. Commented May 8, 2020 at 11:07
  • 5
    If you want to specify a time, add -t 30 this will generate a looped video with 30 sec Commented Apr 6, 2021 at 23:21
16

At least on FFmpeg 2.8.x (but oldie should works too) you can use lavfi as input format and complex filter graph using movie and setpts filters as a argument for -i option.

Next command doing this work for you:

ffmpeg -re -f lavfi -i "movie=filename=input.mp4:loop=0, setpts=N/(FRAME_RATE*TB)" output.mp4

Zero loop= arguments means infinity loop. Values greater zero sets repeat counts. setpts filters required for PTS adjusting for second and later repeats, otherwise most output muxers will fails with non-monotonic PTS increasing: loop does not recalc PTS.

Note, that using filters assuming that bypass frames without decoding/encoding is impossible: by design filters deals with decoded frames only.

At the FFmpeg 2.8.2 new input option -stream_loop inroduced. I first look it works more simple and allows copy content without transcoding:

ffmpeg -re -stream_loop -1 -i input.mp4 -c copy -y output.mp4

But it does not recalculate PTS and output file is wrong. If you add filter to fix PTS (see setpts) you must remove -c copy too. Only bitstream filters can deals with encoded packets, but there is no any bitstream filters to fix PTS (see: https://ffmpeg.org/ffmpeg-bitstream-filters.html)

In any case ffmpeg on second pass fails with error:

input.mp4: Resource temporarily unavailable

Known work around for me: use container for input file without PTS limits (streaming container). One of them, known me, is MPEG-TS. So, you can simple convert your MP4 file to the MPEG-TS:

ffmpeg -i input.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts input.ts

And use next command to compose infinity file:

ffmpeg -re -stream_loop -1 -i input.ts -c copy -strict -2 -bsf:a aac_adtstoasc -y output.mp4

(bitstream filters should be used only if needed, on my samples it is required)

FFmpeg >= 2.8.2 is required in this case.

9
  • 1
    Running that command gives me error : "[mp4 @ 00000000053fa1a0] Could not find tag for codec rawvideo in stream #0, codec not currently supported in container". If I add -vcodec h264 before the input, that error disappears, but the encoder doesn't exit. Supplying a non-zero loop count produces a lot of errors but does exit. The file is not a valid output.
    – Gyan
    Commented Nov 18, 2015 at 8:35
  • 1
    The documentation for stream_loop says "Loop 0 means no loop, loop -1 means infinite loop."
    – Gyan
    Commented Nov 18, 2015 at 8:36
  • Seems incorrect input files. Could you pleas share it? "Loop 0 means no loop, loop -1 means infinite loop." - seems I see old docs :-)
    – Monah Tuk
    Commented Nov 18, 2015 at 13:43
  • 1
    I forgot: filters assuming transcoding (decode/encode). By design. So you must remove -c copy. Answer fixed.
    – Monah Tuk
    Commented Nov 18, 2015 at 16:48
  • 1
    Nice to know, but that's a significant limitation if you have a large loop to generate.
    – Gyan
    Commented Nov 18, 2015 at 17:02
0
@echo off

setlocal ENABLEDELAYEDEXPANSION

REM When not there gives file not found message
for /f "tokens=*" %%I in ('dir /b "*.mkv"') do (
    if exist "%%~nI.nl.srt" (
        call :BURN_FILE "%%~nI" "%%I"
    )
    if exist "%%~nI.en.srt" (
        call :BURN_FILE "%%~nI" "%%I"
    ) 
)
for /f "tokens=*" %%I in ('dir /b "*.mp4"') do (
    if exist "%%~nI.nl.srt" (
        call :BURN_FILE "%%~nI" "%%I"
    )
    if exist "%%~nI.en.srt" (
        call :BURN_FILE "%%~nI" "%%I"
    ) 
)
for /f "tokens=*" %%I in ('dir /b "*.webm"') do (
    if exist "%%~nI.nl.srt" (
        call :BURN_FILE "%%~nI" "%%I"
    )
    if exist "%%~nI.en.srt" (
        call :BURN_FILE "%%~nI" "%%I"
    ) 
)

pause

if not exist ORIGINAL mkdir ORIGINAL
move "%%I" ORIGINAL >NUL
move "%%~nI.*" ORIGINAL >NUL

exit /b

:BURN_FILE

    set hoppa=%1
    set input=%2
    echo hoppa=!hoppa!
    echo input=!input!

    ffprobe.exe -v error -select_streams v:0 -show_entries format=bit_rate -of default=nw=1 !input! > bit_rate2.txt
    ffprobe.exe -v error -show_entries format=duration -of default=nw=1 !input! > duration2.txt
    ffprobe.exe -v error -show_entries format=duration -of default=nw=1 intro.wav > duration_intro.txt

    for /F "tokens=*" %%R in (bit_rate2.txt) do (
        for /f "tokens=1,2 delims==" %%a in ("%%R") do set NAME=%%a & set bit_rate=%%b
    )
    for /F "tokens=*" %%R in (duration2.txt) do (
        for /f "tokens=1,2 delims==" %%a in ("%%R") do set NAME=%%a & set duration=%%b
    )
    for /F "tokens=*" %%R in (duration_intro.txt) do (
        for /f "tokens=1,2 delims==" %%a in ("%%R") do set NAME=%%a & set duration_intro=%%b
    )
    for /f "tokens=1 delims=." %%a in ('echo !duration!') do set /a number=%%a
    for /f "tokens=1 delims=." %%a in ('echo !duration_intro!') do set /a number_intro=%%a

    set /A number_offset = !number! - !number_intro! - 1
    set /A number_offset = !number_offset! * 1000

    sof.exe "!hoppa!.nl.srt"

    if not exist DUTCH mkdir DUTCH

    for %%x in ("!hoppa!.nl.srt.fixed") do (
        if not %%~zx == 0 (
            if not  exist "DUTCH/!hoppa!.nl.mp4" (

            ffmpeg -y -hide_banner -i !input! -i intro.wav -i intro.wav -i pacman2.mp4 -loop 20 -filter_complex "[1]adelay=500|500[s1];[1]adelay=!number_offset!|!number_offset![s2];[0][s1][s2]amix=inputs=3;[0:v]scale=1920:1080[j];[3:v]split[m][a];[a]geq='lum(X,Y)',hue=s=0[al];[m][al]alphamerge[ovr];[ovr]scale=150:-1[sml];[j][sml]overlay=main_w-overlay_w-2:main_h-overlay_h-2[l];[l]subtitles='!hoppa!.nl.srt.fixed':force_style='FontName=Mada Black,FontSize=32,BackColour=&HA0000000,BorderStyle=4'[k]" -map "[k]" -map 0:a -strict -2 -c:v h264_nvenc -pix_fmt yuv420p -rc-lookahead 32 -b:v !bit_rate! -ac 2 -ar 44100 -acodec aac -shortest "DUTCH/!hoppa!.nl.PART.mp4"

rem             ffmpeg -y -hide_banner -i !input! -filter_complex "[0:v]scale=1920:1080[j];[j]subtitles='!hoppa!.nl.srt.fixed':force_style='FontName=Mada Black,FontSize=32'[k]" -map "[k]" -map 0:a -strict -2 -c:v h264_nvenc -pix_fmt yuv420p -rc-lookahead 32 -b:v !bit_rate! -ac 2 -ar 44100 -acodec aac "DUTCH/!hoppa!.nl.PART.mp4"
rem             ffmpeg -y -hide_banner -i !input! -filter_complex "[0:v]scale=1920:1080[j];[j]subtitles='!hoppa!.nl.srt.fixed'[k]" -map "[k]" -map 0:a -strict -2 -c:v h264_nvenc -pix_fmt yuv420p -rc-lookahead 32 -b:v !bit_rate! -ac 2 -ar 44100 -acodec aac "DUTCH/!hoppa!.nl.PART.mp4"
rem             ffmpeg -y -hide_banner -i !input! -filter_complex "[0:v]scale=1920:1080[j];[j]subtitles='!hoppa!.nl.srt.fixed'[k]" -map "[k]" -map 0:a -strict -2 -c:v libx264 -preset slow -crf 23 -pix_fmt yuv420p -b:v !bit_rate! -ac 2 -ar 44100 -acodec aac "DUTCH/!hoppa!.nl.PART.mp4"

            ) else (
                echo Already exists: "DUTCH/!hoppa!.nl.mp4" 
            )
        ) else (
            echo Subtitle file empty: !hoppa!.nl.srt.fixed
        )
    )

    if exist "DUTCH/!hoppa!.nl.PART.mp4" rename "DUTCH\!hoppa!.nl.PART.mp4" "!hoppa!.nl.mp4"

rem if not exist SUBTITLES mkdir SUBTITLES
rem if exist "!hoppa!.nl.srt.fixed" move "!hoppa!.nl.srt.fixed" SUBTITLES
rem if exist "!hoppa!.nl.srt" move "!hoppa!.nl.srt" SUBTITLES

rem remark (rem) goto for making second language
goto :ENDING

sof.exe "!hoppa!.en.srt"

if not exist ENGLISH mkdir ENGLISH

for %%x in ("!hoppa!.en.srt.fixed") do (
    if not %%~zx == 0 (
        if not  exist "ENGLISH/!hoppa!.en.mp4" (
            ffmpeg -y -hide_banner -i !input! -filter_complex "[0:v]scale=1920:1080[j];[j]subtitles='!hoppa!.en.srt.fixed'[k]" -map "[k]" -map 0:a -strict -2 -c:v h264_nvenc -pix_fmt yuv420p -rc-lookahead 32 -b:v !bit_rate! -ac 2 -ar 44100 -acodec aac "ENGLISH/!hoppa!.en.PART.mp4"
rem                 ffmpeg -y -hide_banner -i !input! -filter_complex "[0:v]scale=1920:1080[j];[j]subtitles='!hoppa!.en.srt.fixed'[k]" -map "[k]" -map 0:a -strict -2 -c:v libx264 -preset slow -crf 23 -pix_fmt yuv420p -b:v !bit_rate! -ac 2 -ar 44100 -acodec aac "ENGLISH/!hoppa!.en.PART.mp4"
        ) else (
            echo Already exists: "DUTCH/!hoppa!.en.mp4" 
        )
    ) else (
        echo Subtitle file empty: !hoppa!.en.srt.fixed
    )
)

if exist "ENGLISH/!hoppa!.en.PART.mp4" rename "ENGLISH\!hoppa!.en.PART.mp4" "!hoppa!.en.mp4"

rem     if exist "!hoppa!.en.srt.fixed" move "!hoppa!.en.srt.fixed" SUBTITLES >NUL
rem     if exist "!hoppa!.en.srt" move "!hoppa!.en.srt" SUBTITLES >NUL

:ENDING

if not exist ORIGINAL mkdir ORIGINAL
move !input! ORIGINAL >NUL
move "!hoppa!.*" ORIGINAL >NUL

exit /b
0

If you have MP4 files, these could be losslessly concatenated by first re-wrapping them to MPEG-2 transport streams. With H.264 video and AAC audio, the following can be used:

ffmpeg -i input.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate.ts
ffmpeg -i "concat:intermediate.ts|intermediate.ts|intermediate.ts|intermediate.ts" -c copy -bsf:a aac_adtstoasc output.mp4
-1

The simple method that avoids problems of which Commands work with which version is the 'trivial solution':

ffmpeg -i input.mp4 -i input.mp4 -i input.mp4 -i input.mp4 -c copy output.mp4

See also: https://trac.ffmpeg.org/wiki/Concatenate for many examples.

2
  • 4
    This does not do any concatenation. At least one of the concat methods has to be used: protocol, filter or demuxer. In this command, ffmpeg's auto mapping will select one video, one audio and one subtitle stream from among the inputs. Since they're identical, it's equivalent to the OP's first command.
    – Gyan
    Commented Mar 11, 2017 at 6:36
  • Thanks for testing that Mulvya, as it differs from the behavior described in the Docs: ffmpeg.org/ffmpeg.html#Description - If there's similarity in the File names using "-i -i /tmp/test%d.mp4" (et. al) is also possible.
    – Rob
    Commented Mar 11, 2017 at 17:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.