FFmpeg’s overlay filter burns a logo or a copyright notice into a video. You can set where it sits, how transparent it is and when it shows.
Command Examples
1. Basic: Logo in the Top-Left Corner
ffmpeg -i input.mp4 -i logo.png -filter_complex "overlay=10:10" output.mp4
-i input.mp4: the main video-i logo.png: the watermark imageoverlay=X:Y: puts the logo’s top-left corner at (X, Y), counted in pixels from the video’s top-left corner
2. Bottom-Right Placement (Most Common)
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "overlay=main_w-overlay_w-20:main_h-overlay_h-20" \
output.mp4
main_w-overlay_w-20: X, leaving 20px to the right edgemain_h-overlay_h-20: Y, leaving 20px to the bottom edge
3. Resize Logo Before Compositing (Transparent PNG)
This shrinks the logo to 150px wide, then places it bottom-right:
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "[1:v]scale=150:-1[wm];[0:v][wm]overlay=main_w-overlay_w-20:main_h-overlay_h-20" \
output.mp4
How the filter graph works:
[1:v]scale=150:-1[wm]: resizes the logo (the second input) to 150px wide, keeps its aspect ratio, and names the result[wm][0:v][wm]overlay=...: places[wm]on the main video
FFmpeg reads a PNG’s transparency (its alpha channel) automatically. No extra options are needed.
4. Semi-Transparent Watermark (Opacity Control)
colorchannelmixer scales the alpha channel: 0.5 means 50% opacity. On a logo without an alpha channel (an RGB PNG or a JPEG), aa does nothing, so format=rgba adds one first:
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "[1:v]scale=150:-1,format=rgba,colorchannelmixer=aa=0.5[wm];[0:v][wm]overlay=main_w-overlay_w-20:main_h-overlay_h-20" \
output.mp4
aa=0.5: the alpha multiplier (0.0= fully transparent,1.0= unchanged, so transparent areas stay transparent)
5. Time-Limited Display (Show Only During Intro)
# Show logo only during seconds 0–5
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "overlay=main_w-overlay_w-20:main_h-overlay_h-20:enable='between(t,0,5)'" \
output.mp4
# Show logo from 10s to 30s
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "overlay=10:10:enable='between(t,10,30)'" \
output.mp4
t: the current time in secondsbetween(t, start, end): 1 (logo shown) whiletis inside the range
6. Scrolling Watermark (Left to Right)
The logo moves across the bottom of the frame:
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "[1:v]scale=200:-1[wm];[0:v][wm]overlay=x='mod(t*100,main_w+overlay_w)-overlay_w':y=main_h-overlay_h-20" \
output.mp4
mod(t*100, main_w+overlay_w)-overlay_w: moves 100px per second. The logo enters from the left edge, leaves at the right edge, then starts again.
7. Multiple Logos Simultaneously
Two logos at once, one top-left and one bottom-right. The result of the first overlay is named [tmp], and the second overlay draws on top of it:
ffmpeg -i input.mp4 -i logo1.png -i logo2.png \
-filter_complex "[1:v]scale=100:-1[wm1];[2:v]scale=150:-1[wm2];[0:v][wm1]overlay=10:10[tmp];[tmp][wm2]overlay=main_w-overlay_w-20:main_h-overlay_h-20" \
output.mp4
Position Variable Reference
| Variable | Meaning |
|---|---|
main_w / W |
Main video width |
main_h / H |
Main video height |
overlay_w |
Logo width |
overlay_h |
Logo height |
t |
Current time in seconds (for animation) |
Common Position Formulas
| Position | X Expression | Y Expression |
|---|---|---|
| Top-left | 10 |
10 |
| Top-right | main_w-overlay_w-10 |
10 |
| Bottom-left | 10 |
main_h-overlay_h-10 |
| Bottom-right (recommended) | main_w-overlay_w-20 |
main_h-overlay_h-20 |
| Center | (main_w-overlay_w)/2 |
(main_h-overlay_h)/2 |
Option Reference
| Option | Meaning | Example |
|---|---|---|
overlay=X:Y |
Logo placement (top-left origin) | overlay=10:10 |
enable='between(t,S,E)' |
Time-limited display (S to E seconds) | enable='between(t,0,5)' |
[N:v]scale=W:-1 |
Resize N-th input to W px wide | [1:v]scale=150:-1 |
colorchannelmixer=aa=A |
Alpha multiplier (0.0–1.0) | aa=0.5 (50% opacity) |
x='mod(t*speed,...)' |
Scrolling animation | x='mod(t*100,main_w+overlay_w)-overlay_w' |
Troubleshooting
Problem 1: Simple filtergraph ... was expected to have exactly 1 input and 1 output
Cause: the command uses -vf instead of -filter_complex. overlay takes two inputs, and -vf allows only one.
Fix: use -filter_complex:
# Wrong
ffmpeg -i input.mp4 -i logo.png -vf "overlay=10:10" output.mp4
# Correct
ffmpeg -i input.mp4 -i logo.png -filter_complex "overlay=10:10" output.mp4
Problem 2: Logo Is Invisible (Fully Transparent)
Cause: the PNG is fully transparent, or the logo is too small to see.
Fix: check the logo’s size and pixel format:
# Inspect logo dimensions and pixel format
ffprobe -v quiet -show_streams logo.png | grep -E "width|height|pix_fmt"
Problem 3: Error binding filtergraph inputs/outputs
Cause: the labels in the filter graph do not match. For example, the graph creates [wm] but the overlay asks for [logo]. The line above the error, Filter 'scale:default' has output 0 (wm) unconnected, names the label that leads nowhere.
Fix: make sure every label you create, such as [wm], is used exactly once as an output and once as an input.
Problem 4: Logo Appears Stretched or Distorted
Cause: scale=W:H set both the width and the height, so the shape changed.
Fix: set only the width, as in scale=150:-1. With -1 as the height, FFmpeg keeps the aspect ratio.
Problem 5: Output File Is Much Larger Than Expected
Cause: overlay always re-encodes the video stream.
Fix: raise the CRF value. The file gets smaller and the quality drops a little (libx264 uses 23 by default):
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "overlay=main_w-overlay_w-20:main_h-overlay_h-20" \
-c:v libx264 -crf 26 -preset fast -c:a copy output.mp4
FAQ
Q1. Can I use a JPEG logo?
A. Yes, but JPEG cannot store transparency. For a logo with a transparent background, use PNG or WebP. A rectangular logo on a solid-color background works fine as a JPEG.
Q2. Does using overlay always re-encode the video?
A. Yes. overlay changes the pixels of every frame, so the video must be re-encoded. The audio can still be copied unchanged with -c:a copy.
Q3. Will the logo position adjust automatically for different video resolutions?
A. Yes, if you use expressions such as main_w-overlay_w-20:main_h-overlay_h-20. FFmpeg works them out from each video’s size.
Q4. Can the logo fade in and out over time?
A. Yes, but not with colorchannelmixer=aa=. It takes a fixed number, not a time expression, so adding enable only switches the logo on or off. For a fade, read the logo with -loop 1 -i logo.png and fade its alpha, as in [1:v]fade=t=in:st=0:d=2:alpha=1[wm];[0:v][wm]overlay=10:10:shortest=1. To show or hide the logo without a fade, enable='between(t,0,5)' is the easiest way.
Q5. How do I add the same logo to all videos in a folder?
A. Run the command in a loop. In bash:
for f in *.mp4; do
ffmpeg -i "$f" -i logo.png \
-filter_complex "[1:v]scale=150:-1[wm];[0:v][wm]overlay=main_w-overlay_w-20:main_h-overlay_h-20" \
-c:v libx264 -crf 18 -c:a copy "watermarked_${f}" -y
done