Quick answer
Two paths. Free CLI: ffmpeg -i video.mp4 -vf subtitles=subs.srt -c:a copy output.mp4 burns the captions into the MP4 pixels. Online tools: Happy Scribe, Kapwing, VEED, Rev, and Flixier all offer free tiers (with watermarks on some free plans; Rev is watermark-free). Pick burn-in for social video that plays autoplay-muted; soft-subs sidecar for YouTube, Vimeo, and any player with a captions toggle.
The full workflow — where each tool fits
Getting from a raw video to a captioned MP4 is two steps. This guide covers step 2 (embed the SRT into the MP4). If you don't have an SRT yet, do step 1 first.
- Step 1 — Generate the SRT from your video's audio. Use VexaScribe's video-to-SRT tool (Whisper large-v3, 100+ languages, speaker labels, up to 5 GB, free tier) or self-host Whisper locally for full privacy. Output: a standard
.srtfile with timestamps. - Step 2 — Embed the SRT into your MP4. That's what the rest of this guide is about — FFmpeg burn-in, soft-sub mux, HandBrake, Premiere Pro, or an online tool.
Already have the SRT? Skip to the soft-subs vs burn-in decision.
Soft-subs vs burn-in — pick this first
Before you pick a tool, decide which output shape you need. This choice determines every downstream command and setting.
| Aspect | Soft-subs (sidecar / mux) | Burn-in (hardcoded) |
|---|---|---|
| Visibility | Viewer toggles on/off | Always visible |
| File size impact | Adds ~KB (text-only) | Re-encodes video (larger) |
| Multi-language support | Multiple tracks per file | One language per output |
| Player support | VLC, QuickTime, YouTube, Vimeo, Plex | Universal (any player, any surface) |
| Editability | SRT file can be edited without re-render | Requires full re-render to fix a typo |
| Best for | YouTube, Vimeo, LinkedIn, accessibility | TikTok, Instagram Reels, silent autoplay feeds |
Honest note — when MKV is the better container
MP4 supports soft-subs but requires SRT conversion to mov_text (Apple's MP4-specific subtitle format). Some browser-embedded players don't expose mov_text tracks, and multi-language toggling is inconsistent. MKV preserves SRT natively and every major player (VLC, mpv, Plex, Jellyfin) exposes tracks reliably. If soft-subs must work everywhere and MP4 isn't contractually required, MKV is the honest better choice. MP4 wins for social platforms + browser embeds.
Online tools: no install, no CLI
Five hosted tools handle the upload-SRT-plus-MP4 workflow with browser-based UI. All five default to burn-in output. Free-tier limits and watermark status verified 2026-08-15 against each tool's public page — check current plans directly since these change.
| Tool | Free tier | Watermark | Max size | Best for |
|---|---|---|---|---|
| Happy Scribe | Free tool page (paid plans for transcription) | Yes on free tool | 1 GB typical (not officially stated) | Batch workflow, subtitle translation add-on |
| Kapwing | Yes, 250 MB / 4 min video on free plan | Yes on free plan | 4 min video (free); larger on paid | Short social videos with styled captions |
| VEED | Yes, up to 10 min video / 500 MB | Yes on free plan | 10 min / 500 MB (free); larger on paid | Marketing videos, easy editor |
| Rev | Free burn-in tool | No | Up to 1 GB | One-off no-watermark burn-in |
| Flixier | Yes with limits | Yes on free plan | 500 MB (free) | Cloud editor, collaboration |
Common workflow across all five: upload the MP4, upload the SRT (or auto-generate one), preview burn-in position and styling, export. Processing time typically 1-3 minutes for a 5-10 minute video. Rev is the standout for watermark-free free-tier burn-in — the others require paid plans.
FFmpeg — burn subtitles into MP4
FFmpeg is free, cross-platform, and has zero file-size or duration limits. Trade-off: learn the command syntax. Basic burn-in for a plain SRT:
ffmpeg -i video.mp4 -vf "subtitles=subs.srt" -c:v libx264 -crf 22 -c:a copy output.mp4-i video.mp4— input video-vf "subtitles=subs.srt"— video filter that renders the SRT into every frame-c:v libx264 -crf 22— re-encode video with H.264 at CRF 22 (visually near-lossless; lower CRF = higher quality, larger file)-c:a copy— copy the audio stream without re-encoding (faster, no quality loss)output.mp4— output file
Re-encoding is unavoidable for burn-in (subtitles become part of the video pixels). Processing time ~1-3× real-time depending on GPU + preset. For faster encoding with GPU, add -c:v h264_nvenc (NVIDIA) or -c:v hevc_videotoolbox (Apple Silicon).
FFmpeg — ASS styling deep-dive
The force_style parameter accepts ASS/SSA style directives. This is where every "how do I make subtitles look good" question actually gets answered. Full working commands for common styles:
Basic styled burn-in
White text, black outline, size 26:
ffmpeg -i video.mp4 -vf "subtitles=subs.srt:force_style='Fontsize=26,PrimaryColour=&HFFFFFF&,OutlineColour=&H000000&,Outline=2,BorderStyle=1'" -c:v libx264 -crf 22 -c:a copy output.mp4TikTok/Reels vertical caption preset
Impact font, large size (48pt), heavy outline (4px), positioned bottom-center with vertical margin to sit above the app UI overlay:
ffmpeg -i video.mp4 -vf "subtitles=subs.srt:force_style='Fontname=Impact,Fontsize=48,PrimaryColour=&HFFFFFF&,OutlineColour=&H000000&,Outline=4,BorderStyle=1,Alignment=2,MarginV=120'" -c:v libx264 -crf 22 -c:a copy output.mp4Corporate lower-third preset
Arial, medium size (24pt), semi-transparent black background box, sits near bottom with clean padding:
ffmpeg -i video.mp4 -vf "subtitles=subs.srt:force_style='Fontname=Arial,Fontsize=24,PrimaryColour=&HFFFFFF&,BackColour=&H80000000&,BorderStyle=4,Outline=0,Shadow=0,Alignment=2,MarginV=40'" -c:v libx264 -crf 22 -c:a copy output.mp4ASS style keys reference
Every key accepted by the force_style parameter:
| Key | What it does | Example |
|---|---|---|
| Fontname | Font family name (must be installed system-wide) | Fontname=Arial |
| Fontsize | Point size | Fontsize=26 |
| PrimaryColour | Fill color in ASS ABGR hex format (&Halphabluegreenred&) | PrimaryColour=&HFFFFFF& (white) |
| OutlineColour | Outline color, same ABGR format | OutlineColour=&H000000& (black) |
| BackColour | Background box color, prefix with alpha for opacity | BackColour=&H80000000& (50% black box) |
| Outline | Outline thickness in pixels | Outline=2 |
| Shadow | Shadow depth in pixels (0 = no shadow) | Shadow=1 |
| BorderStyle | 1 = outline+shadow, 3 = opaque box, 4 = opaque box with margin | BorderStyle=1 |
| Alignment | Numpad layout: 1-3 bottom, 4-6 middle, 7-9 top; 2 = bottom-center (default) | Alignment=2 |
| MarginV | Vertical margin from top or bottom in pixels | MarginV=40 |
| MarginL | Left margin | MarginL=20 |
| MarginR | Right margin | MarginR=20 |
Color format gotcha
ASS colors use the &H prefix + ABGR hex (alpha-blue-green-red). This is reversed from the RGB you'd expect. Red is &H0000FF&, not &HFF0000&. Alpha byte controls transparency: &H00 = fully opaque, &H80 = 50% transparent, &HFF = fully transparent.
FFmpeg — soft-sub mux (no re-encode)
For soft-subs — viewer-toggleable captions embedded in the MP4 — use this command. Does not re-encode the video, so it's near-instant and lossless:
ffmpeg -i video.mp4 -i subs.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng output.mp4-c copy— copy all streams without re-encoding (fast, no quality loss)-c:s mov_text— convert SRT to mov_text (the only subtitle format MP4 natively accepts)-metadata:s:s:0 language=eng— tag the subtitle track with ISO 639-2 3-letter language code
Not every MP4 player exposes soft-sub tracks — VLC and QuickTime do; some browser-embedded players don't. See the multi-language section below for adding multiple tracks.
Multi-language MP4 — mux multiple SRTs
To offer viewers a language choice inside a single MP4, mux multiple SRT tracks with language metadata. Worked example — English, Spanish, French subtitle tracks in one MP4:
ffmpeg -i input.mp4 -i subs_en.srt -i subs_es.srt -i subs_fr.srt \
-map 0:v -map 0:a -map 1 -map 2 -map 3 \
-c:v copy -c:a copy -c:s mov_text \
-metadata:s:s:0 language=eng \
-metadata:s:s:1 language=spa \
-metadata:s:s:2 language=fre \
output.mp4Flag breakdown:
-i input.mp4 -i subs_en.srt -i subs_es.srt -i subs_fr.srt— four inputs (video + 3 subtitle files)-map 0:v -map 0:a— take video + audio from input #0-map 1 -map 2 -map 3— add each SRT as a separate subtitle track-c:v copy -c:a copy -c:s mov_text— no video/audio re-encode, convert SRTs to mov_text-metadata:s:s:N language=xxx— tag stream N with ISO 639-2 code (eng, spa, fre, ger, jpn, kor, chi, ara, hin, por, ita, dut, rus, tur, vie, tha, pol, swe, nor, dan, fin, ces, ukr, heb, fas)
Player support: VLC, QuickTime, mpv, and Plex all expose track selection cleanly. YouTube ignores muxed subtitle tracks entirely — upload each SRT separately via YouTube Studio's captions feature. Vimeo Pro supports muxed tracks. Browser-embedded HTML5 <video> support for multi-track MP4 subs is inconsistent; if you need reliable browser toggle, use MKV or upload separate SRTs via a captions API.
Fixing out-of-sync subtitles
SRT drift is the #3 subtitle question on Reddit. Two failure modes with different fixes.
Constant offset (SRT is X seconds too early or late throughout)
Use FFmpeg's -itsoffset flag to shift the entire SRT. Delay by 2.5 seconds:
ffmpeg -itsoffset 2.5 -i subs.srt -c copy subs_delayed.srtPositive values delay subtitles (push them later); negative values advance them (pull earlier). Test with 0.5 increments and check playback in VLC. For GUI: Subtitle Edit → Synchronization → Adjust all times (Shift+Ctrl+A) offers the same operation visually.
Progressive drift (starts synced, drifts more as video goes on)
A constant offset won't fix this. Root cause is usually a frame-rate mismatch (SRT was generated against 24 fps video, but you have 25 fps NTSC/PAL-converted video, or vice versa). Two working fixes:
- Re-transcribe — often the fastest path. Use a tool that outputs word-level timestamps (WhisperX, faster-whisper, VexaScribe) against the actual video's audio. New SRT will match the current video's frame rate.
- ffsubsync — Python tool that uses ASR to align an existing SRT to any audio track.
pip install ffsubsyncthenffsubsync video.mp4 -i drifting.srt -o synced.srt. - Subtitle Edit — GUI tool with visual sync (drag last cue to correct end time, tool interpolates all in between). Windows/Mono only.
Frame-rate conversion (NTSC 23.976 ↔ PAL 25)
24 fps SRT paired with 25 fps video accumulates ~4% drift over the file (2.4 minutes late per hour). Ratio-shift with ffsubsync or Subtitle Edit's "Change frame rate" option. Simple constant-offset won't work.
Batch processing — caption multiple MP4s at once
For a folder of MP4s each with a matching SRT (video.mp4 + video.srt), a shell loop caps hours of manual work at a single command. Assumes MP4 and SRT share base filename.
Bash (macOS / Linux)
for video in *.mp4; do
srt="${video%.mp4}.srt"
if [ -f "$srt" ]; then
ffmpeg -i "$video" -vf "subtitles=$srt" -c:v libx264 -crf 22 -c:a copy "captioned_$video"
fi
donePowerShell (Windows)
Get-ChildItem *.mp4 | ForEach-Object {
$video = $_.Name
$srt = [System.IO.Path]::GetFileNameWithoutExtension($video) + ".srt"
if (Test-Path $srt) {
ffmpeg -i $video -vf "subtitles=$srt" -c:v libx264 -crf 22 -c:a copy "captioned_$video"
}
}Adjust the -vf filter for burn-in styling or swap to the mux command for soft-subs. Add -hwaccel cuda (NVIDIA) or -hwaccel videotoolbox (Apple Silicon) for GPU-accelerated batch encoding. For hundreds of files, consider GNU parallel: parallel -j 4 ... to run 4 encodes concurrently.
HandBrake (GUI, free, cross-platform)
HandBrake is a free open-source transcoder with a subtitle-import UI. Best for people who want burn-in but don't want to touch the command line.
- Download HandBrake from handbrake.fr (Windows / macOS / Linux).
- Open HandBrake and click Open Source, pick your MP4.
- Go to the Subtitles tab.
- Click Tracks → Import Subtitle and pick your .srt file.
- Check the Burn In checkbox on that track (leave unchecked for soft-sub muxing).
- Confirm the output filename and click Start Encode.
HandBrake re-encodes when burn-in is enabled. For a 10-minute 1080p video, expect 3-8 minutes on a modern laptop. HandBrake's default preset (Fast 1080p30) is a good starting point.
Play MP4 with SRT sidecar in VLC (no merging)
If you only need to watch the video with subtitles — not distribute a merged file — VLC handles it with zero processing. Place the .srt in the same folder as the .mp4 with the same base filename, and VLC picks it up automatically.
- Put
video.mp4andvideo.srtin the same folder (same base name is the trick). - Open the MP4 in VLC. Subtitles appear automatically.
- If they don't: Subtitle → Add Subtitle File, then browse to the .srt.
- To disable: Subtitle → Sub Track → Disable.
Playback-only — the MP4 file itself is untouched, and other players won't see the subtitles unless the .srt travels with it. Use FFmpeg or online-tool paths above for a single-file distributable output.
Adobe Premiere Pro
Premiere Pro (2024 or later) has native SRT import via the Captions panel and both sidecar and burn-in export options.
- With your sequence open: Window → Captions.
- In the Captions panel, click + to create a new caption track.
- Open the panel's overflow menu (three dots) and choose Import Captions from File.
- Pick your .srt. Premiere aligns cues to the sequence timeline automatically.
- To burn in on export: File → Export → Media, expand the Captions section, choose Burn Captions Into Video.
- To export sidecar SRT instead: same panel, choose Create Sidecar File.
Premiere's caption import respects existing timing from your .srt cues. If timings are off, edit them directly in the Captions panel — you can drag cues on the timeline and the changes write back to the export.
iMovie (honest workaround)
Honest answer: iMovie (macOS and iOS) does not natively import SRT files. There is no Import Subtitles menu. If you're committed to iMovie, three workarounds:
- Burn-in outside iMovie first using HandBrake or FFmpeg (see sections above), then edit the captioned MP4 in iMovie. Practical for anything longer than a couple minutes.
- Manually add each caption as a Title: Titles → Standard, then type each line and re-time to match. Only reasonable for very short videos (under 30 seconds of dialogue).
- Use CapCut instead (free, Mac and iOS): imports SRT directly, exports MP4 with either burn-in or sidecar. Faster than fighting iMovie's limitations.
Apple has not added SRT import to iMovie despite years of user requests. If you regularly caption in an Apple ecosystem, CapCut or DaVinci Resolve (free) saves time.
Verify the output before you distribute
Whether burn-in or soft-sub, verify the output actually works before uploading to a platform, sending to a client, or archiving.
Burn-in verification
- Open in QuickTime (macOS) or Movies & TV (Windows). Captions should be visible immediately without touching settings.
- Open in a browser via
file://path/to/video.mp4— captions should still be visible. - Scrub through key cues at 25%, 50%, 75%, and end to check styling and sync throughout.
- Post a 30-second test to a private Instagram story or TikTok before mass uploading.
Soft-sub verification
- Open in VLC → Subtitle → Sub Track. Your track should appear (labeled with language code).
- Toggle the track on and off — subtitles should appear/disappear.
- For multi-language MP4s: switch between tracks and verify each language displays.
- Test on the target platform (YouTube, Vimeo, Plex) since browser-embedded players vary in mov_text support.
Common failure modes
- Captions invisible in browser — mov_text isn't supported by that player. Use MKV or burn-in.
- Wrong font on final output — font not installed system-wide when FFmpeg rendered. Install font, re-encode.
- Non-ASCII characters garbled — SRT wasn't UTF-8 encoded. Convert with
iconv -f cp1252 -t utf-8 in.srt > out.srt.
Don't have an SRT yet? Generate one first.
This guide assumes you already have both the MP4 and the SRT file. If you only have the video, generate the SRT before running any commands above.
VexaScribe's video-to-SRT tool extracts the audio track from your MP4, runs Whisper large-v3 to transcribe, and exports a standard SubRip .srt with HH:MM:SS,mmm timestamps ready for every command on this page. Auto-detects the language, supports speaker labels, handles files up to 5 GB. Free tier available. Once you have the .srt, come back to embed or burn it into the MP4.
Verified sources
Every command, style key, and tool claim on this page was cross-checked against these sources on 2026-08-15:
- ffmpeg.org/ffmpeg-filters.html#subtitles — subtitles filter reference, force_style syntax
- WikiBooks — FFmpeg subtitle options — muxing, mov_text, stream mapping
- Vendor tool pages verified live: Happy Scribe, Kapwing, VEED, Rev, Flixier
- handbrake.fr — current stable version + subtitle import UI
- nikse.dk/subtitleedit — visual sync workflow
- github.com/smacke/ffsubsync — ASR-based subtitle alignment tool
- ASS/SSA style spec — Aegisub Manual + libass documentation
Frequently asked questions
Can you add subtitles to an MP4 file?
Yes. Three main paths: (1) burn the subtitles into the video pixels so they always show — use FFmpeg (ffmpeg -i video.mp4 -vf subtitles=subs.srt output.mp4), HandBrake, or an online tool like Rev / Kapwing / VEED. (2) Mux the SRT as a soft-sub track inside the MP4 container using FFmpeg with -c:s mov_text (viewer toggles on/off). (3) Play the MP4 with the SRT as a sidecar in VLC without modifying the file.
How to add SRT file to MP4 in VLC?
VLC does not merge the SRT into the MP4 — it plays them together. Put the .srt in the same folder as the .mp4 with the same base filename (video.mp4 + video.srt) and VLC picks it up automatically on playback. If it doesn't: Subtitle → Add Subtitle File in the VLC menu, then browse to your .srt. This is playback-only; other players won't see the subtitles unless you also distribute the .srt file alongside.
How can I merge an SRT file with a video?
For a single-file MP4 with subtitles embedded, use FFmpeg. Burn-in (always visible): ffmpeg -i video.mp4 -vf "subtitles=subs.srt" -c:v libx264 -crf 22 -c:a copy output.mp4. Soft-subs (viewer toggle, no re-encode): ffmpeg -i video.mp4 -i subs.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng output.mp4. Both produce a single MP4 file with the SRT embedded — the difference is whether viewers can turn the captions off.
How to play MP4 file with SRT subtitles?
VLC, QuickTime Player (macOS), and MPC-HC (Windows) all play MP4 + SRT sidecar automatically if the two files share a base filename and folder. On mobile: VLC for iOS/Android does the same. On smart TVs: most modern Samsung / LG TVs load SRT sidecar from a USB drive alongside the MP4. YouTube: upload the video, then upload the .srt separately via YouTube Studio's caption feature — YouTube doesn't read sidecar files from your local folder.
How to burn SRT to MP4?
Burning (hardcoding) means the subtitles become part of the video pixels — permanent, no toggle. Free options: FFmpeg CLI (ffmpeg -i video.mp4 -vf subtitles=subs.srt -c:v libx264 -crf 22 -c:a copy output.mp4), HandBrake GUI (Subtitles tab → Import Subtitle → check Burn In). Online options: Rev (watermark-free free tier), Kapwing, VEED, Happy Scribe, Flixier (watermark on free plans). Burn-in is required for TikTok, Instagram Reels, and any surface where soft-subs won't display.
How can I convert an SRT file to an MP4 file?
You don't convert SRT to MP4 — you combine them. SRT is a subtitle file (text with timestamps); MP4 is a video container. The phrasing usually means one of: (1) burn the SRT into an existing MP4 (see the FFmpeg burn-in command above), (2) mux the SRT as a soft-sub track inside the MP4, or (3) generate a video from the SRT — for that, you need to record narration or use a text-to-video tool. If you don't have an MP4 yet and only have text you want to render, that's a different workflow (text-to-video, not covered here).
How to change SRT file to MP4?
Same as converting — you can't 'change' an SRT into an MP4 because they're different file types (subtitle text vs video container). What people usually mean: combine an existing MP4 with an SRT so the output is a single MP4 with subtitles. Use FFmpeg (ffmpeg -i video.mp4 -vf subtitles=subs.srt -c:v libx264 -crf 22 -c:a copy output.mp4) or upload both to an online tool like Kapwing or VEED. The result is a new MP4 file with the subtitles either burned into the video or embedded as a soft-sub track.
What's the difference between burn-in and soft-subs?
Burn-in (hardcoded, open captions) renders the caption text into the video pixels — always visible, no toggle, one language per file, larger file size (video is re-encoded). Soft-subs (sidecar or muxed) store the SRT as a separate text stream — viewers toggle on/off, multiple languages supported, near-zero file size overhead. Use burn-in for TikTok, Instagram Reels, and silent autoplay feeds. Use soft-subs for YouTube, Vimeo, LinkedIn, and any accessibility-required context where users need language choice.
Can I add subtitles to an MP4 without re-encoding?
Yes, if you use soft-subs. The FFmpeg mux command (ffmpeg -i video.mp4 -i subs.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng output.mp4) uses -c copy to skip re-encoding — it's near-instant and lossless. Burn-in always requires re-encoding because the subtitles are rendered into every video frame. If speed and quality preservation matter, always prefer soft-subs mux over burn-in.
What's the best free tool to add SRT to MP4?
For CLI comfort: FFmpeg (free, unlimited, no watermark, cross-platform). For a GUI: HandBrake (free, no watermark, Windows/Mac/Linux). For no-install online with no watermark: Rev's add-SRT-to-MP4 tool. Kapwing, VEED, and Happy Scribe all offer free tiers but add watermarks on the free plan. If you also need to generate the SRT, VexaScribe's free tier gives you 30 minutes of transcription plus SRT export.
Does MP4 support SRT natively?
Not exactly. MP4 as a container supports subtitle tracks, but only in mov_text format (Apple's subtitle format). SRT itself is not valid inside an MP4. That's why FFmpeg's soft-sub mux command requires -c:s mov_text — it converts SRT text to mov_text on the fly. Some players (VLC, QuickTime) read the mov_text track cleanly; some browser-embedded players don't expose it. If soft-subs must work in every player, use MKV as the container instead — MKV supports SRT natively.