RenderIO
Guides

Add Watermark

Overlay images or text on video as watermarks.

Add Watermark

Adding a watermark to video is essential for branding, copyright protection, and content attribution. FFmpeg's overlay filter lets you position an image anywhere on the video frame with full control over placement and opacity.

Image watermark (bottom-right)

Overlay a logo image in the bottom-right corner with a 10-pixel margin from the edges.

ffmpeg -i {{in_video}} -i {{in_logo}} -filter_complex "overlay=W-w-10:H-h-10" {{out_video}}

In the overlay expression:

  • W and H are the main video width and height
  • w and h are the overlay image width and height
  • W-w-10 places the logo 10 pixels from the right edge
  • H-h-10 places the logo 10 pixels from the bottom edge

Image watermark (top-left)

Place the logo in the top-left corner with a 10-pixel margin.

ffmpeg -i {{in_video}} -i {{in_logo}} -filter_complex "overlay=10:10" {{out_video}}

Other common positions:

  • Top-right: overlay=W-w-10:10
  • Bottom-left: overlay=10:H-h-10
  • Center: overlay=(W-w)/2:(H-h)/2

Image watermark with opacity

Make the watermark semi-transparent by adjusting the alpha channel. The colorchannelmixer=aa=0.5 sets 50% opacity.

ffmpeg -i {{in_video}} -i {{in_logo}} -filter_complex "[1:v]format=rgba,colorchannelmixer=aa=0.5[logo];[0:v][logo]overlay=W-w-10:H-h-10" {{out_video}}

This filter chain:

  1. Takes the second input ([1:v], the logo) and converts it to RGBA format
  2. Adjusts the alpha channel to 50% opacity
  3. Labels the result as [logo]
  4. Overlays [logo] onto the main video ([0:v]) in the bottom-right corner

Full API example

Add a logo watermark to the bottom-right corner of a video. This example uses two input files: the source video and the logo image.

curl -X POST https://renderio.dev/api/v1/run-ffmpeg-command \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: ffsk_your_api_key_here" \
  -d '{
    "input_files": {
      "in_video": "https://example.com/source.mp4",
      "in_logo": "https://example.com/logo.png"
    },
    "output_files": {
      "out_video": "watermarked.mp4"
    },
    "ffmpeg_command": "ffmpeg -i {{in_video}} -i {{in_logo}} -filter_complex \"overlay=W-w-10:H-h-10\" -c:a copy {{out_video}}"
  }'
import requests

response = requests.post(
    "https://renderio.dev/api/v1/run-ffmpeg-command",
    headers={
        "Content-Type": "application/json",
        "X-API-KEY": "ffsk_your_api_key_here",
    },
    json={
        "input_files": {
            "in_video": "https://example.com/source.mp4",
            "in_logo": "https://example.com/logo.png",
        },
        "output_files": {
            "out_video": "watermarked.mp4",
        },
        "ffmpeg_command": 'ffmpeg -i {{in_video}} -i {{in_logo}} -filter_complex "overlay=W-w-10:H-h-10" -c:a copy {{out_video}}',
    },
)

data = response.json()
print("Command ID:", data["command_id"])
interface RunCommandResponse {
  command_id: string;
}

const response = await fetch("https://renderio.dev/api/v1/run-ffmpeg-command", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-KEY": "ffsk_your_api_key_here",
  },
  body: JSON.stringify({
    input_files: {
      in_video: "https://example.com/source.mp4",
      in_logo: "https://example.com/logo.png",
    },
    output_files: {
      out_video: "watermarked.mp4",
    },
    ffmpeg_command:
      'ffmpeg -i {{in_video}} -i {{in_logo}} -filter_complex "overlay=W-w-10:H-h-10" -c:a copy {{out_video}}',
  }),
});

const { command_id } = (await response.json()) as RunCommandResponse;
console.log("Command ID:", command_id);
const response = await fetch("https://renderio.dev/api/v1/run-ffmpeg-command", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-KEY": "ffsk_your_api_key_here",
  },
  body: JSON.stringify({
    input_files: {
      in_video: "https://example.com/source.mp4",
      in_logo: "https://example.com/logo.png",
    },
    output_files: {
      out_video: "watermarked.mp4",
    },
    ffmpeg_command:
      'ffmpeg -i {{in_video}} -i {{in_logo}} -filter_complex "overlay=W-w-10:H-h-10" -c:a copy {{out_video}}',
  }),
});

const { command_id } = await response.json();
console.log("Command ID:", command_id);
<?php
$ch = curl_init("https://renderio.dev/api/v1/run-ffmpeg-command");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Content-Type: application/json",
    "X-API-KEY: ffsk_your_api_key_here",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    "input_files" => [
        "in_video" => "https://example.com/source.mp4",
        "in_logo" => "https://example.com/logo.png",
    ],
    "output_files" => [
        "out_video" => "watermarked.mp4",
    ],
    "ffmpeg_command" => 'ffmpeg -i {{in_video}} -i {{in_logo}} -filter_complex "overlay=W-w-10:H-h-10" -c:a copy {{out_video}}',
]));

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
echo "Command ID: " . $data["command_id"] . "\n";

Notice that input_files contains two entries: in_video for the source video and in_logo for the watermark image. Both are referenced in the ffmpeg_command using {{in_video}} and {{in_logo}} placeholders.

The API returns a command_id immediately. Poll GET /api/v1/commands/:commandId to check when the watermarking is complete, then download the output from the storage_url in the response.

Tips and variations

  • -c:a copy: Adding -c:a copy copies the audio stream without re-encoding, which is faster and preserves the original audio quality.
  • Logo sizing: Resize the logo within the filter chain before overlaying: [1:v]scale=100:-1[logo];[0:v][logo]overlay=W-w-10:H-h-10. This scales the logo to 100 pixels wide with automatic height.
  • PNG with transparency: Use PNG format for your logo to take advantage of built-in alpha transparency. The overlay filter respects PNG alpha channels automatically.
  • Tiled watermark: Repeat a watermark across the entire frame for stronger protection. Use the overlay filter with the shortest=1 flag and a tiled input generated with the tile filter.
  • Time-limited watermark: Show the watermark only during a specific time range by adding enable='between(t,5,30)' to the overlay filter, which shows it from 5 to 30 seconds.

Further reading

On this page