To loop several videos into a YouTube live stream with FFmpeg and Nginx RTMP, put their paths in a concat playlist, loop that input, pace it in real time and send the output to an RTMP application configured in Nginx. The playlist controls order; FFmpeg reads and encodes or copies the media; Nginx accepts and forwards the RTMP publication.
This is a hands-on workflow, not a guarantee of an uninterrupted broadcast. You still need compatible source files, a working YouTube ingest setup, a reachable Nginx application and a process that remains running. File looping alone does not ensure that publishing continues if the encoder, network or server fails.
From playlist to YouTube
The path has three separate stages. First, FFmpeg’s concat demuxer reads a text file and presents its listed media files sequentially as one input. Next, FFmpeg either copies compatible streams or transcodes them into streams appropriate for the output. Finally, it publishes an FLV-formatted RTMP output to the application name configured in Nginx RTMP. YouTube is a further destination only if that application forwards the feed there, or if you publish to YouTube’s ingest directly instead of to a local Nginx instance.
That distinction matters when diagnosing a blank or offline broadcast. A successful FFmpeg process means only that it is sending data to its output address; it does not prove that the Nginx application is configured correctly or that YouTube is receiving a usable stream. If you are new to YouTube’s publishing credentials, first review how a YouTube stream key fits into a continuous broadcast. Keep the key private and enter it only in the destination configuration you intend to use.
Nginx RTMP is not the encoder in this workflow. It accepts an RTMP publish and can provide the live application endpoint; FFmpeg performs the media work. The nginx-rtmp-module project documentation shows the application-based RTMP URL pattern and sample configuration. Its examples are starting points, not a replacement for checking the configuration and access rules on your own host.
Before assembling a long playlist, test with two short, trusted files. This makes it easier to separate a playlist path error from a codec mismatch or a problem with the RTMP application. Keep the eventual file order stable: if you change the order in the playlist, the sequence changes, but neither FFmpeg nor Nginx supplies editorial scheduling rules for you.
Create the concat playlist
Create a plain text file named playlist.txt. Put the following header on its first line, then add one file directive per video in the order you want them to play:
ffconcat version 1.0
file 'video1.mp4'
file 'video2.mp4'
file 'video3.mp4'
The header must be exactly ffconcat version 1.0, at the very start of the first line. Do not add a blank line, a comment, or a byte-order mark before it. That exact first-line marker lets FFmpeg recognise the file as a concat script automatically. The remaining lines identify media inputs; they are not shell commands, and you should not add shell quoting rules such as a leading $ or command prompt.
Relative paths are usually easiest to maintain when the videos sit beside playlist.txt. In that case, file 'video1.mp4' refers to the file relative to the playlist’s location, not necessarily the directory from which you happen to launch FFmpeg. A playlist in /home/channel/playlist.txt can therefore refer to /home/channel/video1.mp4 with the simple relative path shown above. Keep the playlist and its media together, or document the directory relationship so a later restart does not depend on an assumed working directory.
The concat demuxer joins streams, not arbitrary movie experiences. FFmpeg’s documentation says, “All files must have the same streams (same codecs, same time base, etc.).” See the concat demuxer documentation before relying on files with differing audio tracks, resolutions, frame rates or stream layouts. When the clips are not compatible, prepare normalized copies or choose a deliberate transcoding workflow rather than hoping a playlist will reconcile their differences.
A duration directive can be useful if a file’s stored or inferred duration is inaccurate, because duration estimates affect timestamp adjustment between concatenated inputs. Use it only when you know the intended duration and have checked how the resulting transition behaves. It is not a substitute for verifying the source media or correcting an incompatible stream layout.
Paths, quoting and safe mode
The single quotes in the example are part of the concat file syntax. They protect a path that contains spaces, for example:
file 'morning prayers/part one.mp4'
Do not confuse this with shell quoting around the FFmpeg command. The shell parses the command line; the concat demuxer separately parses the text file. A path that is safely quoted in the playlist can still fail if the playlist points to the wrong directory, and a path correct for the shell is not automatically valid in the playlist format.
The demuxer’s default safe setting is restrictive. It accepts portable relative paths in the expected form, and can reject absolute paths or names containing characters outside its safe-path rules. Prefer simple relative paths and uncomplicated filenames where practical. If you need absolute paths or other forms, -safe 0 disables that restriction, but use it only with a playlist you trust. It permits arbitrary file names to be read, so do not accept a playlist supplied by someone else and run it in this mode without reviewing its entries.
A command that uses -safe 0 should make that choice visible rather than hiding it in a script. If the media directory is under your control, an alternative is to move or copy files into a known working directory and rewrite the playlist with relative names. This may take a little preparation, but it makes the playlist easier to inspect and reduces confusion over which paths are allowed.
On a Unix-like shell, a path containing an apostrophe needs special attention because the playlist uses quotes. Do not assume that adding a backslash in front of every punctuation mark will work: concat-file escaping has its own rules. The most reliable practical approach for a non-technical workflow is to rename the media to simple names without apostrophes, then confirm each entry points to a real file. If preserving unusual names is necessary, test one entry on its own and consult the current FFmpeg concat syntax rather than guessing at layered escaping.
Loop the playlist indefinitely
Use FFmpeg’s -stream_loop -1 to repeat the concat input indefinitely. The -1 value means no fixed repetition count. Place it before -i playlist.txt, because FFmpeg options are positional and input options apply to the next input. The same ordering principle applies to -f concat, -safe 0 when needed, and -re:
ffmpeg -re -stream_loop -1 -f concat -safe 0 -i playlist.txt \
-c:v libx264 -c:a aac -f flv \
rtmp://127.0.0.1:1935/live/playlist
This is a representative command pattern, not a universal tested recipe. In particular, the codec choices are examples. Your installed FFmpeg build must include the selected encoders, and the result must match what the receiving application and downstream player can accept. Check ffmpeg -encoders on the host to see available encoders. If you use a shell that does not treat the backslash as a line continuation, put the command on one line or use that shell’s own continuation syntax.
The command’s address assumes FFmpeg and Nginx are on the same host, with Nginx listening on port 1935 and an application called live. The final path component, playlist, is the stream name. If your application uses a different name, update the URL to match it; if the processes are on separate hosts, replace the loopback address with an address the FFmpeg host can reach and adjust publishing access deliberately.
A loop is not a recovery policy. If FFmpeg exits because a file disappears, an encoder fails or a destination refuses the publish, -stream_loop -1 does not launch a new process. It only repeats its input while that invocation is running. For a channel that must run overnight, decide how you will notice a stopped process and restart it, and test that operational path separately from the media loop.
Pace file reading at real-time speed
A file can be read faster than its original playback rate unless you pace it. The -re option makes FFmpeg read the input at native frame rate; FFmpeg documents it as equivalent to -readrate 1. For live-style publishing from stored media, that prevents the command from racing through the playlist and sending the content as fast as the host can read it.
Keep -re before the input it applies to. It is an input reading option, so placing it after -i playlist.txt can apply it differently or not to the intended input. The FFmpeg command-line documentation explains option ordering and input/output scope; check it when extending a command with additional inputs, filters or outputs. Do not add options from a different FFmpeg example without checking which input or output they govern.
Real-time reading also means the host has to keep pace with playback. If transcoding takes more processing than the machine can provide, frames may be delayed or dropped, and a long playlist will not solve that. A stream-copy path can reduce encoding work, but only where the input streams and output format are suitable. Watch the FFmpeg progress output during a test and compare its reported speed with real time; sustained lag is a reason to simplify the output or transcode the files ahead of the live run.
For a channel assembled from recorded lessons, devotional clips or shop demonstrations, a playlist makes ordering straightforward but does not remove the work of checking each item. A guide to running recorded coaching classes as a continuous stream from the cloud discusses the operational distinction between preparing media and keeping a channel available. If the ongoing task you want to remove is leaving a computer on simply to replay an uploaded file, StreamNeo addresses that specific burden by turning an uploaded video into a YouTube live stream without keeping your own computer running.
Publish to the Nginx RTMP application
The application part of the RTMP URL must match an application {} block in the Nginx RTMP configuration. A minimal shape, adapted from the module project’s example, looks like this:
rtmp {
server {
listen 1935;
application live {
live on;
allow publish 127.0.0.1;
deny publish all;
}
}
}
Here live is the application, so an FFmpeg URL on the same host can be rtmp://127.0.0.1:1935/live/playlist. The access directives shown allow publication from the local host and deny other publishers; they are a sample policy, not a suitable setting for every deployment. If FFmpeg runs on another machine, local-only permission will prevent it publishing until you deliberately adapt the policy and network exposure. Do not open publishing access broadly merely to make an error disappear.
The module’s example configuration and URL conventions are documented in the nginx-rtmp-module README. If you have not installed or enabled the module, an ordinary Nginx configuration alone does not imply that an RTMP application exists. Check how the module was built and loaded, validate the configuration with the host’s Nginx test command, and reload only after the test passes. Those actions are specific to your installation and permissions.
If the goal is YouTube, Nginx must also be connected to the YouTube ingest destination through the arrangement you have configured. The example above publishes to a local application; it does not, by itself, send anything to YouTube. Confirm the destination URL and stream key through YouTube’s current official live-streaming instructions, and keep credentials out of public scripts or screenshots. For a direct FFmpeg-to-YouTube setup, the output URL instead needs to target the YouTube ingest endpoint and key, following the current official guidance.
Choose copy or transcode deliberately
You have two broad processing choices. With -c copy, FFmpeg passes the input streams through without re-encoding. That reduces encoding work, but it does not convert an incompatible codec, repair mismatched stream layouts or ensure that the receiving RTMP/FLV path accepts the media. The concat inputs still need matching streams, and the output still needs to be acceptable to the server and intended player.
Transcoding selects encoders and gives you control over output codecs and parameters, at the cost of encoding work. The sample uses libx264 for video and aac for audio, but your FFmpeg build may not include them and your source characteristics may call for a different preparation. Check the installed encoder list and test the resulting stream with the actual destination before relying on it. If files vary substantially, normalising them into a consistent set before creating the playlist is often easier to debug than trying to resolve every difference during a live run.
| Choice | Useful when | Trade-off to check |
|---|---|---|
-c copy |
Inputs already have compatible streams and the destination accepts them | Low encoding work, but no format conversion; transitions and output compatibility still need testing |
| Transcode, such as the sample encoders | You need a consistent output format or the input needs conversion | More encoding work; available encoders and host capacity determine whether it can keep pace |
These are practical trade-offs, not benchmark results. There is no universal setting that fits every mix of MP4 files, audio tracks and receiving application. If YouTube is the final destination, compare the output with its current published recommendations rather than treating an example command as a platform guarantee. If the underlying question is whether FFmpeg is the right long-running arrangement for your channel, cloud service versus VPS for a 24/7 stream lays out the different kinds of responsibility involved.
Test transitions and stream health
First test the playlist without a long unattended session. Run it with two short clips and observe the end of the first and start of the second. Check that both picture and sound continue, that no unexpected pause or black frame appears, and that the resulting stream reaches the intended RTMP application. If you transcode, also check that the host can keep pace while encoding; if you copy, ensure the output accepts the source streams as they are.
When a transition produces a timestamp jump or a brief artifact, investigate the source durations and stream compatibility. The concat demuxer adjusts timestamps between files, and inaccurate duration information can affect that adjustment. Try a known-good pair of files first, then add the problematic one back. Where the files differ in codec, time base, audio layout or other stream properties, normalize them before adding them to the permanent playlist.
If FFmpeg reports that it cannot open a file, inspect the exact path as read from the playlist, including case, spaces and the playlist’s directory. If it rejects a path under the default safety mode, prefer a clean relative path; use -safe 0 only for trusted entries. If FFmpeg starts but Nginx refuses the publish, check that the port, application name, access rules and host address agree. A URL such as /live/playlist cannot publish to an application named something else.
Keep separate checks for each stage: FFmpeg’s process and progress, Nginx’s application and logs, and YouTube’s live control room or stream-health indications. One stage reporting success does not establish the status of the others. Once a short test works, run a longer observation across a playlist boundary and confirm you know how to detect and restart a failed process. For a recurring YouTube live workflow, the guide to scheduling live streams in advance can help with the platform-side planning, while the loop itself remains an FFmpeg and host operations concern.
Before committing, compare the operating options on the pricing page. When the file and channel are ready, start free — 24-hour trial, no card.
FAQ
Does Nginx loop the videos?
No. FFmpeg reads the concat playlist and repeats it with -stream_loop -1; Nginx RTMP receives a published stream at the configured application. If FFmpeg stops, the loop stops as well.
Why does the playlist need the exact header?
The first line ffconcat version 1.0 enables automatic recognition of the concat script format. Keep it at the very beginning of the file, before all file entries, with no preceding blank line or text.
Can I use -c copy for any MP4 playlist?
No. The files need compatible streams, and the output format and receiving application must support them. If they differ or need conversion, prepare consistent files or transcode, then test transitions before leaving the stream unattended.
Does the sample URL publish directly to YouTube?
No. rtmp://127.0.0.1:1935/live/playlist targets a local Nginx application named live. You need a separate configured path from Nginx to YouTube, or use YouTube’s current official ingest instructions for a direct publication.