When one video ends, insert a short slate instead of exposing a frozen frame, black output or an abrupt cut. In FFmpeg, the slate is a separate timed video segment: create a background, place text over it, then join it between the surrounding clips.
A fixed message needs only drawtext. A changing countdown needs a time-based expression tied to the slate's elapsed time, and that expression must be tested against your shell, FFmpeg build and chosen font. The examples below are patterns to adapt, not commands to run unchanged without checking their output.
Decide when a slate is useful
A transition slate is useful when the gap between videos has meaning. You might show “Next programme begins shortly”, display the name of an upcoming devotional session, or give viewers a brief interval to understand that the next clip is intentional. For a study channel, the slate could identify the next subject. For a local news loop, it could identify the next bulletin or segment.
The important distinction is between a holding screen and a countdown. A holding screen communicates that the channel is still active while waiting for the next item. A countdown communicates a defined remaining interval. If you do not know exactly when the next item will start, do not show a number that implies precise timing. Use a fixed message such as “Next video loading” or “More music follows”.
A countdown is appropriate when the slate has a deliberate, finite duration. For example, a ten-second interstitial can show the remaining whole seconds before the next clip. Decide its behaviour before writing the filter:
- Does the first visible value read 10 or 9?
- Does the number change once per second or on another schedule?
- Should 0 be visible, and if so, for how long?
- Should the next clip begin immediately when the display reaches 0?
These choices affect the expression and the viewer's experience. A countdown that displays 10 for a full second is not the same as one that begins at 9 because the first frame is treated as time already elapsed. Neither is automatically correct, but the result should be deliberate.
Keep the slate long enough to be understood, but do not use it to conceal an unreliable playlist. If a clip frequently ends early or the following file is not ready, fix the scheduling and timestamp problem rather than extending a countdown indefinitely. The daily schedule guide for a 24/7 YouTube music stream is useful when the larger issue is deciding what should play and when.
Create a background with FFmpeg's colour source
FFmpeg's color source can generate a solid video background without a separate image. It is useful for a plain black, dark grey or brand-coloured slate. The source can specify the size, frame rate and duration, so the transition becomes a normal video segment that can be joined to the clips around it.
A minimal source looks like this:
-f lavfi -i "color=c=black:s=1920x1080:r=30:d=10"
Here, c=black selects the colour, s=1920x1080 sets the frame size, r=30 sets the frame rate, and d=10 gives the generated source a finite duration. Change those values to match the output you intend to produce. A vertical channel, for example, should not quietly receive a landscape slate between portrait clips.
The color source only creates pictures. It does not provide the audio stream that may be present in the surrounding videos. If your final output is expected to carry audio continuously, create or add a matching audio stream for the slate. Silence is one possible choice, but it must be represented in a way that is compatible with the concat method and output muxer you use.
You can also use a still image as the base. A logo, programme card or illustrated background may be more recognisable than a solid colour. In that case, make sure the image lasts for the whole slate interval and that its timestamps start where you expect. FFmpeg's overlay documentation notes that inputs are consumed in timestamp order; resetting an input with setpts=PTS-STARTPTS can be important when the image or another overlay begins with an offset timestamp. See the official FFmpeg filters documentation for the documented color, overlay, loop and drawtext filters.
A generated solid background is easier to reproduce than an image whose dimensions, colour profile or alpha handling changes between machines. An image gives you more visual control. Choose the simpler option that matches the channel, then inspect the resulting slate before you place it in a live job.
Add fixed text with drawtext
Use drawtext when the wording does not change during the segment. A basic filter can centre a message on the generated background:
ffmpeg -f lavfi -i "color=c=black:s=1920x1080:r=30:d=10" \\
-vf "drawtext=fontfile=/path/to/font.ttf:text='Next video shortly':fontsize=64:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2" \\
-t 10 slate-fixed.mp4
This command is illustrative. Replace the font path and output settings, and check the quoting required by your operating system. The fontfile should point to a font that exists on the host running FFmpeg. A font name alone may depend on fontconfig support, while an explicit file path is easier to reproduce when you move the job to another machine.
The expressions x=(w-text_w)/2 and y=(h-text_h)/2 centre the rendered text using the video width, height and text dimensions. You can instead reserve space for a logo, position the message near the lower third, or add a box behind the text. Keep the message readable at the size used by the live output, not only in the full-resolution source file.
If the message contains punctuation, spaces, apostrophes or filter-special characters, shell quoting and filter escaping become part of the problem. A command that works in one shell can fail in another because the shell processes some characters before FFmpeg sees them. Start with a short message, run it locally, and read the complete FFmpeg error rather than assuming the filter itself is unavailable.
drawtext depends on how FFmpeg was built, including support for libfreetype. Check the installed build and its configuration if FFmpeg reports that the filter is missing. The official filter documentation describes drawtext as rendering text over video and documents its expression and positioning features. Do not treat a fixed text expression as a countdown. It will keep displaying the same string unless you deliberately provide a changing expression.
Create a changing countdown
A changing countdown is still a text overlay, but its value must be calculated from the slate's local elapsed time. The time variable should begin when the slate begins, rather than being based on the wall clock, unless you are intentionally showing a scheduled clock.
One illustrative pattern for a ten-second slate is:
ffmpeg -f lavfi -i "color=c=black:s=1920x1080:r=30:d=10" \\
-vf "drawtext=fontfile=/path/to/font.ttf:text='Next video in %{eif\\:max(0\\,10-t)\\:d}':fontsize=64:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2" \\
-t 10 slate-countdown.mp4
The expression uses elapsed time t, limits the result at zero, and formats the result as an integer. The repeated backslashes and escaped comma are not decoration. They pass through the shell and then the FFmpeg filter parser. The exact escaping can vary with the shell, operating system and FFmpeg version, so treat this as a starting point and verify it in the environment where the stream will run.
A time-based expression also raises a timing question. At a fractional time such as 8.4 seconds into the slate, should the display show 1, 2, or a value rounded in another way? The example's behaviour follows the expression used, but you should inspect the output rather than infer the visible sequence from the command. If you want a countdown based on whole seconds remaining, choose the appropriate rounding method and test the boundary frames.
Pay attention to the end of the segment. If the slate lasts ten seconds but the expression can display 0 for part of the final interval, the next video must begin after the intended final frame. If the displayed sequence matters to your viewers, use a short local render and step through the final seconds. A player showing rounded timestamps is not enough evidence that the frame sequence is correct.
You can add a prefix or change the layout without changing the basic timing idea. For instance, the text could read “Prayer begins in 5” or “Next lesson in 5”, provided the font has the required characters and the result remains legible. For multilingual channels, check the font's coverage rather than assuming that a Latin font will render Devanagari or other scripts correctly.
Insert the timed slate between clips
Once the slate exists as a video segment, place it between the outgoing and incoming clips. There are two main FFmpeg approaches, and the choice depends on whether the files already match.
The concat filter is generally the adaptable choice when the inputs need to be decoded, normalised and re-encoded together. It is suitable when your clips and generated slate differ in dimensions, frame rates, pixel formats, sample rates or channel layouts. The filtergraph must represent every segment, including audio if you want continuous audio output. This gives you control, but the final result requires encoding and a carefully mapped output.
The concat demuxer uses a text playlist and presents files sequentially. It is attractive when the clips and slate already have matching streams and you want to avoid re-encoding. The FFmpeg formats documentation states: “All files must have the same streams (same codecs, same time base, etc.).” That requirement applies to the slate as well as the surrounding files.
The demuxer also relies on file duration information to place the next segment. Incorrect duration metadata can produce an unwanted gap, overlap or timestamp artefact. Do not assume that several MP4 files are compatible merely because they play in the same media player, and do not assume that -c copy can join arbitrary files.
| Situation | More suitable starting point | Main check |
|---|---|---|
| Clips and slate differ in size, frame rate or stream layout | Concat filter with re-encoding | Normalise video and audio before joining |
| Every segment already has matching streams and trustworthy durations | Concat demuxer | Confirm codecs, time bases and duration metadata |
| The slate has no audio but the clips do | A workflow that creates or maps slate audio | Check for an audio gap or stream mismatch |
| The output is a persistent live service | A tested scheduler or playlist process around FFmpeg | Define what happens after the current input ends |
For a finite render, joining the files and writing a new output is relatively straightforward to reason about. For a continuous live service, you also need a process that knows what to play next and what to do when the queue is empty. A filtergraph that renders three files does not by itself create an indefinite playlist or keep every output protocol connected forever.
If you are using a computer or VPS to run the process, keep the transition file with the media it belongs to and record the exact command used to make it. The FFmpeg VPS guide for YouTube Live covers operational concerns that are separate from the slate itself, including the difference between preparing media and keeping a live process running.
Match audio, timing and output requirements
Video continuity is only half of the transition. If the clips contain audio and the slate does not, the output may have a gap or an incompatible stream layout. Decide whether the slate should be silent, carry music, or use another audio bed. Then create, map and encode that audio consistently with the surrounding segments.
A silent interval can be useful for a devotional or announcement channel, but it should be intentional. A low-level background may sound smoother for a lofi station, while a news loop may need a short announcement bed. The editorial choice is yours; the technical requirement is that the resulting stream has the audio structure expected by the concat method and destination.
Normalise timestamps when you combine unlike inputs. Different files may begin at different timestamp values, have variable frame rates, or contain audio that ends before the picture. The concat filter can be used as part of a re-encoding graph, but it does not remove the need to inspect each input and map the desired streams explicitly.
Use explicit -map options in a complex command so the intended video and audio outputs reach the destination. Confirm the output codec, pixel format, frame rate, audio codec and muxer requirements for the platform receiving the stream. A file that plays locally is not proof that a live ingest will accept the same arrangement.
For YouTube, treat the stream settings as a separate validation step. The YouTube Help guidance for live encoders is the appropriate place to check current platform requirements rather than relying on an old command copied from a forum. If you are still deciding whether to use a local machine, OBS or an FFmpeg process, the comparison of FFmpeg and OBS for 24/7 prerecorded streaming gives useful context without changing the slate design described here.
A cloud workflow can remove one particular operational burden: leaving a computer running and watching for a dropped process. StreamNeo turns an uploaded file into a YouTube-only 24/7 broadcast, so it can be useful when you want the prepared video, including its deliberate transition segments, to continue without your own computer switched on. You still need to check the finished media and the YouTube channel settings yourself.
Check the duration and transition
Before joining anything, inspect the slate as its own file. Confirm that it lasts the intended interval, starts on the expected frame rate, and ends without a frozen final frame or an unexpected black tail. If the output has audio, listen through the first and last seconds as well as the middle.
Use a media probe or a player that reports stream information to compare the actual duration with the requested duration. Metadata may be rounded, so also watch the file. A ten-second setting does not tell you whether the last video frame, audio sample or muxed timestamp lands exactly where you expect.
Check the transition in both directions. At the start, the final frame of the outgoing clip should not linger because its timestamps were carried into the slate. At the end, the incoming clip should not begin with a jump caused by an offset timestamp or an incorrect duration in the previous file.
For a countdown, write down the expected visible sequence and compare it with the rendered frames. Decide whether a display that reads 10, 9, 8 through 1 is correct for your use, or whether 0 should appear. If the expression gives a result you did not intend, change the expression or the segment duration rather than adjusting your expectations to fit an unverified output.
If you use a logo or still image, check its edges and aspect ratio at the final stream resolution. An image with a different pixel aspect ratio may look stretched, and an overlay that starts with a non-zero timestamp can appear late. The filters documentation is the right reference for the exact behaviour of overlay and timestamp expressions.
Test the full sequence before streaming
Create a small test sequence containing the end of one real clip, the generated slate and the beginning of the next real clip. This is more useful than testing the slate against two artificial colour sources because real files bring the timestamp, audio and codec differences that often cause the final problem.
Watch the sequence from start to finish without skipping the transition. Check the picture at the cut, the first countdown value, each value change, the final slate frame, and the first frame of the next clip. Listen for a click, sudden silence or duplicated audio. If the final destination is YouTube, use the same output settings and mapping in the test that you intend to use in the live process.
Then test a longer playlist. A single successful join does not prove that every item has compatible streams or reliable duration metadata. Include a clip with a different frame rate, a clip with a different audio layout, and the longest or shortest files that your channel normally uses. The point is not to manufacture unusual failures, but to find whether your normal library is actually uniform.
Run the process in the same environment used for streaming. A font available on your laptop may not exist on a VPS. A command that works in an interactive shell may behave differently in a service or scheduled task. Save the font path, filter string, output settings and playlist together so that a later restart does not depend on memory.
For an always-on channel, decide what happens when FFmpeg exits, when the playlist ends, or when a new file is not ready. A finite render can stop cleanly. A persistent channel needs a separate, tested policy for restarting, waiting or switching to a holding segment. Do not describe a countdown as a solution to a queue that has no next item.
Finally, make the slate part of your channel's editorial plan. A consistent background, readable message and predictable duration can make a transition feel intentional without pretending that the stream is showing live activity. If the channel is intended for worship, study or music, keep the wording and timing aligned with the expectation you set in the title and description. You can also review the practical setup in the guide to running a 24/7 worship stream on YouTube using OBS, even if FFmpeg is the process that ultimately renders your sequence.
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 create a holding screen without a countdown?
Yes. Generate a timed background with the color source and place fixed text over it with drawtext. A fixed expression displays fixed wording; it does not count down unless you add a time-based expression and verify its output.
Why does my drawtext command fail?
The installed FFmpeg build may not include the required libfreetype support, or the font path may not exist. Shell quoting and filter escaping can also change the expression before FFmpeg receives it, so test a short fixed message first and then add the more complex countdown expression.
Should I use the concat filter or concat demuxer?
Use the concat filter when the files need normalising or re-encoding. Consider the concat demuxer only when the segments already have matching streams, codecs and time bases, and their duration metadata is trustworthy.
Can a countdown keep an 24/7 stream alive indefinitely?
No. It creates a finite transition segment. An always-on service also needs a playlist or scheduler, compatible audio, output settings and a defined action when the next video is unavailable or the current queue ends.