To loop a playlist of local videos into YouTube Live with FFmpeg, put the files in a concat playlist, choose a method that suits their media streams, then send the output to the server URL and stream key shown in YouTube Live Control Room. There is no single command that safely joins every collection of files: stream copy depends on compatible inputs, while mismatched media usually needs filtering and re-encoding.
The examples below are starting points, not guarantees for every FFmpeg build, shell, file or channel configuration. Test with the actual playlist and check YouTube’s preview and stream health before relying on it overnight.
Check whether the inputs can be concatenated
Start by inspecting every file, not just the first one. Note the video and audio codecs, dimensions, frame rates, pixel formats, audio sample rates and channel layouts. The files do not need identical filenames, but their streams and parameters must be suitable for the operation you choose.
For stream copy, FFmpeg passes encoded packets through without decoding and encoding them again. That avoids the CPU work and generational quality loss of re-encoding, but it leaves little room to reconcile differences. A change in resolution, codec, time base, audio layout or other stream parameters can make a simple join fail, produce a transition problem or yield output that the ingest does not accept. Check the FFmpeg concat FAQ and the installed version’s documentation rather than assuming a group of MP4 files is uniform.
A useful first pass is to open each file with ffprobe, which is distributed with FFmpeg, and compare the reported streams. For example, ffprobe -hide_banner clip-01.mp4 prints stream details to the terminal. If one item is portrait video and the others are landscape, or some files have no audio while others do, plan a consistent output format instead of expecting stream copy to repair the differences.
Also watch for practical playlist issues: a file that is incomplete, a path that is inaccessible to the account running FFmpeg, or a source that ends with unexpected silence. For a devotional channel, one clip with a different audio layout may be more disruptive than a modest difference in picture quality. For a local news loop, a resolution change can be obvious at every transition. Inspect representative joins, not only individual files.
If the media varies, decide whether to prepare normalised copies in advance or to decode, filter and re-encode as FFmpeg reads the playlist. The first approach takes storage and a preparation step, but can reduce the live process’s workload. The second keeps the workflow in one command, but encoding in real time requires a machine capable of sustaining the chosen output. Our guide to fixing videos with different resolutions in an OBS playlist explains the same mismatch problem from a playlist workflow perspective.
Prepare the FFmpeg concat file
The concat demuxer reads a text manifest listing files in order. Save a UTF-8 text file, for example playlist.ffconcat, with the explicit format header as its first line:
ffconcat version 1.0
file 'clip-01.mp4'
file 'clip-02.mp4'
file 'clip-03.mp4'
Keep the videos together with the playlist for a simple setup, then use paths relative to the working directory from which FFmpeg runs. Alternatively, write full paths. Paths with spaces or special characters need correct ffconcat escaping; the examples are deliberately simple. The FFmpeg manual documents the concat format and per-file directives such as duration and inpoint. Add those only if you need their specific behaviour and have checked how they interact with your media.
The header matters when you use the explicit ffconcat format name: it must be the first line, with no comment or blank line before it. You can use a different filename such as playlist.txt, but a .ffconcat extension makes the file’s purpose easier to recognise. Keep the playlist under version control or make a dated backup if you update a channel’s running order regularly; that makes it easier to restore a known-good sequence after an edit.
The -safe 0 option in many concat examples relaxes FFmpeg’s path restrictions. It may be needed for absolute paths or certain path forms, but it also means FFmpeg will accept paths beyond the restricted safe subset. Use it only for a playlist you control. Do not put untrusted filenames or manifests into a process that uses this setting.
Before starting a long run, test the manifest with a short, local output or a brief preview. Correct misspellings, confirm that every file opens and check that the final item reaches its end. If a playlist is assembled by a script, make sure it does not silently include a temporary download or an old version of a clip. A playlist is a sequence, not a media-quality check.
Choose the concat demuxer or filter
The concat demuxer presents the listed files as one input to FFmpeg. It is the usual candidate when you want to join compatible files without re-encoding, or when you will encode the combined input to a consistent output. It does not make incompatible streams identical by itself. The FFmpeg FAQ distinguishes this approach from the concat filter, which is used when inputs must be decoded and re-encoded as part of joining them.
With the demuxer and stream copy, the important idea is that both video and audio packets pass through as they are. A schematic command is:
ffmpeg -re -stream_loop -1 -f concat -safe 0 -i playlist.ffconcat \
-c copy -f flv "$YOUTUBE_RTMPS_URL/$YOUTUBE_STREAM_KEY"
Treat that as a pattern only after checking the playlist and output container requirements. The command does not scale, resample or repair variable stream parameters. If it reports errors at a file boundary, or the resulting preview is unhealthy, do not keep retrying the same copy command with different output flags as if the source differences had disappeared. Inspect the streams and choose a normalisation or filter workflow.
The concat filter is more appropriate when the inputs need transformations such as scaling, frame-rate conversion, audio resampling or adding a silent audio stream for files that lack one. Its filter graph must account for every input and the number and type of streams; it is therefore not a drop-in replacement for the demuxer command above. For varied source material, prepare each video and audio stream to compatible parameters, then concatenate the filtered streams and encode the result.
You can also normalise each source beforehand, using a consistent resolution, frame rate, video codec, audio codec, sample rate and channel layout. Then use the demuxer on those prepared files. This costs an extra conversion pass and disk space but can make the live command simpler and reduce the burden of encoding multiple different sources during transmission. Which route is better depends on whether you value a simple prepared playlist or need a single processing pipeline.
Build the looping playlist command
For a controlled playlist whose files are compatible with the chosen output, this encoding pattern is a reasonable place to begin:
ffmpeg -re -stream_loop -1 -f concat -safe 0 -i playlist.ffconcat \
-c:v libx264 -preset veryfast -pix_fmt yuv420p \
-c:a aac -b:a 128k -ar 44100 \
-f flv "$YOUTUBE_RTMPS_URL/$YOUTUBE_STREAM_KEY"
Here, -f concat selects the concat demuxer, -i points to the manifest, and -stream_loop -1 requests indefinite input looping. -re paces file input at its native rate rather than sending the stored file as fast as the computer can read it. The video and audio options request encoding to H.264 and AAC, while -f flv selects the output muxer commonly used for RTMP-style ingest. These choices are not universal: check your FFmpeg build, source streams and current YouTube guidance before using them.
An encoded command can still fail if the live process cannot sustain the workload. Real-time encoding at a high resolution may use substantially more processing than stream copy. Try the exact files and output settings while watching both the machine and YouTube’s preview. If the picture stalls, the audio is absent, or FFmpeg reports errors at a transition, return to the input inspection step rather than assuming the loop option is the cause.
A re-encoding command can also be adapted to a filter graph, but the graph depends on what differs. For example, scaling every video to one canvas does not solve different audio layouts, and resampling audio does not add an audio stream to a silent clip. If some sources have no audio, the graph needs a deliberate choice about whether to supply silence or handle audio separately. Do not copy a filter graph designed for one collection of files and treat it as a general playlist recipe.
For an unattended local setup, use a shell script with the playlist and FFmpeg arguments kept in one place, and avoid writing the stream key into a public repository. A restart policy can help recover from a process exit, but it cannot fix a malformed playlist or an unreachable connection. The systemd guide for running an FFmpeg YouTube stream covers process supervision; it is most useful after the media and ingest command are already tested.
Set the YouTube ingest URL and stream key
In YouTube Studio, open Live Control Room and create or select the live stream using YouTube’s encoder workflow. YouTube supplies the server URL and stream key for the encoder. Its official setup instructions explain where to find them and how to connect, preview and start the broadcast.
Put the server URL and key in the shell variables used by the examples, or substitute them carefully in a private command environment. The combined destination shown here is a common RTMP-style form, but use the exact values and protocol YouTube gives for the selected stream. Treat the key like a password: do not put it in a public script, a screenshot, a support post or logs you share. If it is exposed, replace it in YouTube Studio before continuing.
A preview is not the same as being live to viewers. Follow the status shown in Live Control Room and use its Go live control when the scheduled stream is awaiting confirmation. The title and schedule in Studio, privacy setting, and the encoder connection all need to agree with the event you intend to run. Keep a note of which stream the key belongs to, especially if you operate separate devotional, ambience or news channels.
Match the output to YouTube ingest
YouTube’s encoder guidance currently recommends RTMPS and describes supported codecs and settings. It recommends constant bitrate encoding and a two-second keyframe interval, with the interval not exceeding four seconds. Bitrate recommendations vary with codec, resolution and frame rate; consult the current YouTube encoder settings page for the profile you plan to send rather than borrowing a bitrate from an unrelated setup.
The example command does not explicitly set a target video bitrate, rate-control mode or keyframe interval, so it should not be mistaken for a complete ingest profile. To apply an encoder profile, add suitable options for your FFmpeg build and confirm the actual output properties. Select a resolution and frame rate your sources justify, and a bitrate your encoder and sustained upload connection can maintain. A higher setting is not automatically better if the connection or machine cannot hold it steadily.
| Choice | When it fits | Main trade-off |
|---|---|---|
| Demuxer with stream copy | Compatible streams, and preserving the encoded source matters | Low encoding load, but no reconciliation of stream differences |
| Demuxer with re-encoding | Inputs can be read as a sequence and you want consistent output encoding | More control over output, but the machine must encode in real time |
| Filter-based join | Inputs need transformations or different streams must be brought to common parameters | Flexible, but the graph is specific to the media and requires careful testing |
| Pre-normalised files | Playlist changes are manageable and you prefer a simpler live process | Preparation and storage work happen before the broadcast |
For a small business product loop that changes only occasionally, normalising the videos ahead of time may be easier to maintain than debugging a complex filter graph during a live session. If the playlist changes every day, an encoding workflow may be more practical, provided the machine can keep up. A reader who needs unattended operation beyond a local computer’s availability may choose a hosted workflow instead; that is a separate operational decision, not a requirement of FFmpeg.
Do not confuse this concat input manifest with YouTube HLS ingest. HLS uses a different protocol and media-playlist/segment workflow; FFmpeg’s list of local files is not an HLS output playlist. YouTube describes HLS as a separate ingestion path in its HLS setup guidance, including different latency and setup considerations. Use it only when you intentionally configure that workflow.
Test the stream and check logs
Test before you schedule the channel to depend on the playlist. Start with a short run containing at least one transition, then inspect the YouTube preview for picture, sound, aspect ratio and pacing. A file may play correctly on your desktop yet create a problem at the join, so include a representative boundary in the test. Listen through a full transition if speech, bhajans or music must remain intelligible.
Keep FFmpeg’s terminal output available during the test. Look for errors opening a file, timestamp warnings, dropped frames or reconnect attempts, and note which item was being read when an issue appeared. Do not share output without removing the stream key or any destination string containing it. If YouTube flags stream health, compare the actual encoder output with the profile in the current settings guide.
Check the upload connection under the conditions in which the channel will run, not only during a quiet daytime test. Other people using the same connection, router restarts and power interruptions can change the result. If YouTube reports dropped frames despite a seemingly good connection, review the encoder settings that can affect dropped frames. That is a diagnostic, not proof that the playlist itself is correct.
Plan the stream’s lifecycle as well as its loop. YouTube Help states that streams under 12 hours are automatically archived; do not assume a longer continuous broadcast will be archived in full under that rule. Verify current behaviour for your channel and plan how you will end, restart or schedule a longer run if an archive matters. A loop command can keep presenting input, but it cannot promise a fault-free broadcast or define the channel’s archive behaviour.
If the channel must keep going when a process or connection fails, document who checks the Live Control Room and what fallback they should use. A tested emergency playlist can be more useful than an improvised command change at night; see how to create a fallback playlist. If maintaining a local machine and its connection is itself the recurring problem, StreamNeo can remove the need to leave that computer running by taking an uploaded video and sending it as a YouTube live stream, but it does not replace checking that your content and channel are ready.
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
Can I use -c copy for every MP4 playlist?
No. MP4 is a container, not a promise that the contained streams and parameters match. Compare the video and audio streams in every file; use stream copy only when the inputs are suitable for the join and output.
What does -stream_loop -1 do?
It asks FFmpeg to repeat the input indefinitely. It does not repair incompatible media, restart FFmpeg after every possible failure or guarantee that YouTube will keep accepting the connection.
Should I use the concat demuxer or concat filter?
Use the demuxer for a file list when the media can be joined in the chosen workflow. Use a filter-based approach when decoding and re-encoding are needed to transform or reconcile streams, and tailor the graph to the actual files.
Will YouTube archive a stream that runs all day?
YouTube Help says streams under 12 hours are automatically archived. For longer broadcasts, check the current official guidance and plan the stream lifecycle separately rather than assuming the whole run will be retained.