# How youtube-dl's Subtitle Extraction and Embedding System Works: A Deep Dive

> Explore how youtube-dl extracts and embeds subtitles in a three-stage pipeline. Learn to download and integrate subtitle tracks seamlessly with your videos.

- Repository: [youtube-dl/youtube-dl](https://github.com/ytdl-org/youtube-dl)
- Tags: deep-dive
- Published: 2026-02-25

---

**youtube-dl processes subtitles through a three-stage pipeline that extracts track metadata from video pages, downloads selected language files during the main download loop, and optionally embeds them into the final video container using FFmpeg.**

The subtitle extraction and embedding system in youtube-dl is a modular architecture that separates concerns between extractors, the core downloader, and post-processors. This design allows the tool to handle diverse subtitle formats across hundreds of video platforms while providing a consistent interface for users to download, convert, and embed captions into MP4, MKV, or WebM containers.

## The Three-Stage Subtitle Pipeline

### Stage 1: Extraction in the Extractor Layer

Every extractor in youtube-dl is responsible for parsing the video page or API response to locate subtitle tracks. In [`youtube_dl/extractor/youtube.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/extractor/youtube.py), the `process_subtitles` inner function (lines 2920-2943) builds two dictionary entries:

- **`subtitles`**: Manual captions uploaded by the content creator
- **`automatic_captions`**: Auto-generated captions provided by the platform

The extractor uses a helper called `process_language` to collect a list of URLs per language and format, storing them in the `info_dict` that gets passed downstream. This ensures that by the time the core downloader receives the metadata, all available subtitle tracks are already enumerated with their direct download URLs.

### Stage 2: Selection and Download Logic

Once the extractor populates the subtitle metadata, `YoutubeDL.process_subtitles()` in [`youtube_dl/YoutubeDL.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/YoutubeDL.py) (lines 1872-1900) handles user preferences. This method evaluates command-line flags such as `--sub-lang`, `--all-subs`, and `--sub-format` to filter the available tracks.

The function returns a *sub-dict* containing only the chosen format per language. During the main download loop in `process_info()`, youtube-dl calls `subtitles_filename()` from [`youtube_dl/utils.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/utils.py) (lines 3282-3284) to generate the output path using the convention `<video-name>.<lang>.<ext>`. Each subtitle file is then downloaded and written to disk alongside the video.

### Stage 3: Embedding with FFmpeg

When the `--embed-subs` flag is present, youtube-dl invokes the `FFmpegEmbedSubtitlePP` post-processor after the video download completes. Located in [`youtube_dl/postprocessor/ffmpeg.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/postprocessor/ffmpeg.py) (lines 71-104), the `run()` method performs the following:

1. Creates a temporary file list mapping each subtitle file to its language code
2. Constructs an FFmpeg command that copies the video and audio streams (`-c copy`) while mapping each subtitle stream
3. Executes the muxing operation to produce a single container file with embedded subtitle tracks
4. Replaces the original video file with the muxed result

This stage only supports MP4, MKV, and WebM containers. The subtitles become internal tracks within the video file, accessible through media players that support subtitle selection.

## Key Implementation Files and Functions

- **[`youtube_dl/extractor/youtube.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/extractor/youtube.py)** – Contains `process_subtitles` (lines 2920-2943) which parses YouTube's player response to populate `subtitles` and `automatic_captions` dictionaries.

- **[`youtube_dl/YoutubeDL.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/YoutubeDL.py)** – Houses `process_subtitles()` (lines 1872-1900) for filtering subtitle tracks based on user preferences, and coordinates the download via `process_info()`.

- **[`youtube_dl/postprocessor/ffmpeg.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/postprocessor/ffmpeg.py)** – Implements `FFmpegEmbedSubtitlePP.run()` (lines 71-104) which muxes external subtitle files into the final video container using FFmpeg.

- **[`youtube_dl/utils.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/utils.py)** – Provides `subtitles_filename()` (lines 3282-3284) for generating standardized subtitle filenames.

- **[`youtube_dl/options.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/options.py)** – Defines CLI flags including `--write-sub`, `--sub-lang`, `--embed-subs`, and `--convert-subs` (lines 438-564).

## Command-Line Usage Examples

### Downloading Subtitles to Separate Files

To download English subtitles in the best available format without embedding:

```bash
youtube-dl --write-sub --sub-lang en "https://www.youtube.com/watch?v=abc123"

```

This creates a file named `video-title.en.vtt` (or `.srt`, `.ass`, depending on the source) alongside the video file.

### Embedding Subtitles into Video Containers

To download auto-generated captions and embed them directly into the video file:

```bash
youtube-dl \
  --write-auto-sub \
  --sub-lang en \
  --embed-subs \
  "https://www.youtube.com/watch?v=abc123"

```

The result is a single MP4, MKV, or WebM file containing an internal subtitle track. No separate subtitle files remain on disk.

### Converting Subtitle Formats Before Embedding

To force conversion to SubRip format before embedding:

```bash
youtube-dl \
  --write-sub \
  --sub-lang en \
  --convert-subs srt \
  --embed-subs \
  "https://www.youtube.com/watch?v=abc123"

```

The `--convert-subs` option triggers FFmpeg to convert the downloaded subtitle file to the specified format before the embedding post-processor muxes it into the video container.

## Programmatic Access via Python API

You can access the subtitle extraction and embedding system programmatically through the `YoutubeDL` class:

```python
from youtube_dl import YoutubeDL

ydl_opts = {
    'writesubtitles': True,
    'subtitleslangs': ['en', 'es'],
    'subtitlesformat': 'srt',
    'embedsubtitles': True,
}

with YoutubeDL(ydl_opts) as ydl:
    info = ydl.extract_info(
        'https://www.youtube.com/watch?v=abc123', 
        download=True
    )

# Access the downloaded subtitle metadata

print(info['requested_subtitles'])

```

The API mirrors the CLI pipeline: the extractor populates `info['subtitles']`, the downloader filters based on `subtitleslangs` and `subtitlesformat`, and the `FFmpegEmbedSubtitlePP` post-processor runs when `embedsubtitles` is enabled.

## Summary

- **youtube-dl** implements subtitle handling through a three-stage pipeline: extraction in site-specific extractors, selection/download in the core `YoutubeDL` class, and optional embedding via `FFmpegEmbedSubtitlePP`.

- The extractor layer populates `subtitles` and `automatic_captions` dictionaries with direct URLs for each language and format variant.

- User preferences are processed by `process_subtitles()` in [`youtube_dl/YoutubeDL.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/YoutubeDL.py), which filters available tracks based on `--sub-lang`, `--sub-format`, and related flags.

- The `FFmpegEmbedSubtitlePP` post-processor muxes external subtitle files into MP4, MKV, or WebM containers when `--embed-subs` is specified, creating internal subtitle tracks.

- Subtitle filenames follow the pattern `<video-name>.<lang>.<ext>`, generated by `subtitles_filename()` in [`youtube_dl/utils.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/utils.py).

## Frequently Asked Questions

### What subtitle formats does youtube-dl support for embedding?

youtube-dl supports embedding subtitles into **MP4**, **MKV**, and **WebM** containers through the `FFmpegEmbedSubtitlePP` post-processor. The tool can download subtitles in various source formats (VTT, SRT, ASS, TTML) and optionally convert them using FFmpeg's conversion filters before embedding. However, the final container must be one of the three supported formats for the embedding step to succeed.

### How does youtube-dl handle auto-generated captions versus manual subtitles?

youtube-dl distinguishes between manual subtitles and auto-generated captions through separate dictionary keys in the extractor output. The `subtitles` key contains manually uploaded captions, while `automatic_captions` contains machine-generated transcripts. In [`youtube_dl/extractor/youtube.py`](https://github.com/ytdl-org/youtube-dl/blob/main/youtube_dl/extractor/youtube.py), the `process_subtitles` function populates both fields separately. Users can download auto-generated captions using the `--write-auto-sub` flag instead of `--write-sub`, and the selection logic in `YoutubeDL.process_subtitles()` treats these as distinct track types with separate availability checks.

### Can I convert subtitle formats during the download process?

Yes, youtube-dl supports converting subtitle formats before writing or embedding them. By using the `--convert-subs FORMAT` flag (where FORMAT can be `srt`, `vtt`, `ass`, etc.), you trigger FFmpeg to transcode the downloaded subtitle file from its native format to your specified target format. This conversion happens in the post-processing phase, using the same FFmpeg infrastructure that handles embedding. When combined with `--embed-subs`, the conversion occurs before the subtitle stream is muxed into the final video container, ensuring the embedded track uses your preferred format.

### Why are my embedded subtitles not showing in the video player?

If subtitles are not visible after using `--embed-subs`, the issue typically relates to player compatibility or subtitle stream mapping. First, verify that your video player supports the specific container format (MP4, MKV, or WebM) and the subtitle codec used. Some players require you to manually enable subtitle tracks or select the correct language stream. Second, check that the subtitle file was successfully downloaded before embedding; if the extractor found no matching subtitles for your `--sub-lang` criteria, FFmpeg has nothing to embed. Finally, certain container combinations (like MP4 with specific subtitle codecs) have limited player support compared to MKV, which has broader subtitle compatibility.