ffmpegsubtitlescaptionsvideo-processingapi

How to Add Subtitles to Video with FFmpeg

·Javid Jamae·7 min read
How to Add Subtitles to Video with FFmpeg

You need captions on your video. Maybe it's for accessibility, maybe TikTok and Instagram penalize uncaptioned content in their algorithms, or maybe you just want your audience to watch with the sound off. Whatever the reason, you're going to reach for FFmpeg.

And then you'll spend an hour fighting with filter syntax.

What Burned-In Subtitles Look Like

Before you write any code, it helps to see what you're building. These screenshots show the same video frame at three stages: no subtitles, default subtitles, and styled subtitles.

Original video frame without subtitles
Original video frame without subtitles
Same frame with default burn-in subtitles
Same frame with default burn-in subtitles
Styled subtitles with large yellow text and black outline
Styled subtitles with large yellow text and black outline

The default subtitles work, but they're small and hard to read on most screens. The styled version uses force_style to bump up the font size, change the color, and add an outline. We'll cover exactly how to do both below.

Adding Subtitles with FFmpeg CLI

FFmpeg supports two main approaches for burning subtitles into video: the subtitles filter (for SRT/ASS files) and the drawtext filter (for simple text overlays). The subtitles filter is what you want for actual captions.

Assuming you have an SRT file ready, the basic command looks like this:

ffmpeg -i input.mp4 -vf "subtitles=captions.srt" -c:v libx264 -crf 23 -c:a aac output.mp4

That works. But the moment you need to customize font size, color, or positioning, you're writing ASS override tags inside your SRT file or converting to ASS format entirely:

ffmpeg -i input.mp4 -vf "subtitles=captions.srt:force_style='FontSize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2'" -c:v libx264 -crf 23 -c:a aac output.mp4

That single line is already hard to read. Add font paths (which differ between Linux, macOS, and Windows), character encoding issues, and the fact that FFmpeg silently fails on malformed SRT files, and you've got a debugging session ahead of you.

If you only need to process a handful of videos on your own machine, the CLI approach is fine. But if you're building a product that captions videos at scale, you don't want this running on your server.

force_style Parameter Reference

The force_style parameter controls how your subtitles look. You pass it as a comma-separated string of key=value pairs inside the subtitles filter. Every parameter is optional, and you can combine as many as you need.

ParameterWhat it doesExample value
FontNameSets the font familyArial
FontSizeText size in pixels28
PrimaryColourText fill color (BGR format)&H0000FFFF (yellow)
SecondaryColourColor used for karaoke effects&H00FF0000 (blue)
OutlineColourBorder color around text&H00000000 (black)
BackColourShadow/background color&H80000000 (semi-transparent black)
OutlineOutline thickness in pixels2
ShadowShadow distance in pixels1
AlignmentText position (numpad layout)2 (bottom-center), 8 (top-center), 5 (middle)
MarginVVertical margin from edge in pixels30
BoldBold text (0 or 1)1

Color format

Colors use BGR (blue-green-red) format, not RGB. The format is &H00BBGGRR where each pair is a hex value from 00 to FF.

Common colors:

  • White: &H00FFFFFF
  • Black: &H00000000
  • Yellow: &H0000FFFF
  • Red: &H000000FF
  • Green: &H0000FF00
  • Blue: &H00FF0000

If you have an RGB hex color like #FF5733, flip the pairs: 3357FF, then prefix with &H00 to get &H003357FF.

Ready-to-use examples

Large yellow text with black outline (great for short-form video):

ffmpeg -i input.mp4 -vf "subtitles=captions.srt:force_style='FontSize=32,PrimaryColour=&H0000FFFF,OutlineColour=&H00000000,Outline=2,Shadow=1'" -c:v libx264 -crf 23 -c:a aac output.mp4

Centered white text with semi-transparent background:

ffmpeg -i input.mp4 -vf "subtitles=captions.srt:force_style='FontSize=24,PrimaryColour=&H00FFFFFF,BackColour=&H80000000,Outline=0,Shadow=4,Alignment=2'" -c:v libx264 -crf 23 -c:a aac output.mp4

Red text at the top of the frame:

ffmpeg -i input.mp4 -vf "subtitles=captions.srt:force_style='FontSize=20,PrimaryColour=&H000000FF,Alignment=8,MarginV=20'" -c:v libx264 -crf 23 -c:a aac output.mp4

Burning Subtitles via the FFmpeg Micro API

FFmpeg Micro wraps FFmpeg's subtitle capabilities into a REST API. You send your video and subtitle file, specify the filter, and get back a captioned video. No server, no font path issues, no dependency management.

The approach uses the filters field on the /v1/transcodes endpoint. You provide your video and SRT file as inputs, then apply the subtitles filter:

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [
      {"url": "https://storage.example.com/video.mp4"},
      {"url": "https://storage.example.com/captions.srt"}
    ],
    "outputFormat": "mp4",
    "filters": [
      {"filter": "subtitles=captions.srt"}
    ]
  }'

Both files get downloaded and processed together. The subtitles filter references the SRT file by name, and FFmpeg burns the captions directly into the video frames.

You can style the subtitles the same way you would with CLI, using the force_style parameter:

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [
      {"url": "https://storage.example.com/video.mp4"},
      {"url": "https://storage.example.com/captions.srt"}
    ],
    "outputFormat": "mp4",
    "filters": [
      {"filter": "subtitles=captions.srt:force_style=FontSize=28,PrimaryColour=&H00FFFFFF"}
    ]
  }'

The response gives you a job ID. Poll GET /v1/transcodes/{id} until the status is completed, then grab the output with GET /v1/transcodes/{id}/download.

Simple Text Overlays Without an SRT File

Sometimes you don't need timed captions. You just need a line of text burned into the video, like a watermark, a title card, or a call-to-action. FFmpeg Micro has a virtual option for that.

The @text-overlay option lets you add styled text without writing any FFmpeg filter syntax:

{
  "inputs": [
    {"url": "https://storage.example.com/video.mp4"}
  ],
  "outputFormat": "mp4",
  "options": [
    {
      "option": "@text-overlay",
      "argument": {
        "text": "Subscribe for more tips",
        "style": {
          "position": "bottom-center",
          "fontSize": 48,
          "fontColor": "#FFFFFF",
          "outlineThickness": 2
        }
      }
    }
  ]
}

Behind the scenes, this generates an ASS subtitle file with your text and styling, then applies the subtitles filter. You get clean, anti-aliased text without touching any FFmpeg syntax.

SRT File Format Quick Reference

If you're generating SRT files programmatically (which you probably are if you're reading this), here's everything you need to know about the format.

PartWhat it isExample
Line 1Sequence number (starts at 1)1
Line 2Timecode range (start --> end)00:00:01,000 --> 00:00:04,000
Line 3+Subtitle text (one or two lines)Welcome to this tutorial.
Blank lineSeparates each subtitle block(empty line)

Here's a complete example:

1
00:00:01,000 --> 00:00:04,000
Welcome to this tutorial.

2
00:00:04,500 --> 00:00:08,000
Today we're covering video captions.

3
00:00:08,500 --> 00:00:12,000
Burned-in subtitles work everywhere.

Five rules that will save you debugging time:

  • Use commas for milliseconds, not periods. 00:00:01,000 is correct. 00:00:01.000 will fail silently in some players.
  • Sequence numbers must be unique and sequential. Gaps or duplicates cause rendering issues.
  • Blank lines between blocks are required. Missing them merges subtitle entries together.
  • Save as UTF-8. Other encodings cause garbled text, especially with non-Latin characters.
  • Line breaks within a subtitle block create multi-line captions. Most players support two lines max.

If you're using a speech-to-text service like Whisper, Deepgram, or AssemblyAI, they can output SRT directly. Pipe that into the FFmpeg Micro API and you've got an automated captioning pipeline.

Automating Captions at Scale

The real value of an API-based approach shows up when you're processing more than a few videos. A typical automation flow:

  1. Video gets uploaded to your app
  2. Speech-to-text service generates an SRT file
  3. Your backend sends both files to FFmpeg Micro's /v1/transcodes endpoint
  4. Poll for completion or use a webhook
  5. Download the captioned video and serve it to your users

You can wire this up with n8n, Make.com, or just a simple script. The point is that you're not managing FFmpeg installations, font dependencies, or scaling infrastructure.

FFmpeg Micro has a free tier, so you can test this entire flow without paying anything. Sign up and grab an API key to try it with your own videos.

About Javid Jamae

Founder & CEO at FFmpeg Micro

Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Ready to process videos at scale?

Start using FFmpeg Micro's simple API today. No infrastructure required.

Get Started Free