Guides

YouTube Caption Formats: A Technical Deep Dive into SRT, VTT, SBV, DFXP, and TTML

Master YouTube caption formats with this technical deep dive. Learn the specifications, code examples, browser support, and conversion techniques for SRT, VTT, SBV, DFXP, and TTML.

July 21, 2026
5 min read

YouTube Caption Formats: A Technical Deep Dive into SRT, VTT, SBV, DFXP, and TTML


Table of Contents

  1. Introduction to Caption Formats
  2. SRT (SubRip) Format
  3. VTT (WebVTT) Format
  4. SBV (YouTube SBV) Format
  5. DFXP (Distribution Format Exchange Profile)
  6. TTML (Timed Text Markup Language)
  7. Format Comparison Matrix
  8. Code Examples and Parsing
  9. Browser and Player Support
  10. Conversion Techniques
  11. Best Practices
  12. Troubleshooting
  13. FAQ

Introduction to Caption Formats

Caption formats are standardized ways of storing timed text that accompanies video content. Each format has its own syntax, capabilities, and use cases. Understanding these formats is crucial for:

  • Developers building transcription tools
  • Content creators distributing videos across platforms
  • Educators creating accessible learning materials
  • Researchers analyzing video content
  • Localization teams translating subtitles

YouTube supports multiple caption formats, each with specific characteristics. This guide provides a comprehensive technical overview of each format, including specifications, code examples, and practical applications.


SRT (SubRip) Format

Overview

SRT (SubRip Subtitle) is the most widely used subtitle format. It's simple, human-readable, and supported by virtually every video player and platform.

File Extension

.srt

Format Specification

SRT files are plain text files with the following structure:

1
00:00:01,000 --> 00:00:04,500
This is the first subtitle.

2
00:00:05,000 --> 00:00:08,250
This is the second subtitle.
With multiple lines.

Structure Breakdown

Each subtitle entry consists of:

  1. Sequence Number - A sequential integer identifying the subtitle
  2. Timestamp - Start and end time in HH:MM:SS,mmm format
  3. Text - The subtitle content (can be multiple lines)
  4. Blank Line - Separates entries

Timestamp Format

  • Hours:Minutes:Seconds,Milliseconds
  • Milliseconds are separated by comma (not period)
  • Example: 01:23:45,678 = 1 hour, 23 minutes, 45 seconds, 678 milliseconds

Supported Features

| Feature | Support | Notes | |---------|---------|-------| | Text formatting | No | Plain text only | | Positioning | No | Centered by default | | Colors | No | Not supported | | Multiple languages | Yes | Separate files per language | | Karaoke effects | No | Not supported |

Code Example: Parsing SRT in Python

import re

def parse_srt(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()
    
    # Split into subtitle blocks
    blocks = re.split(r'\n\s*\n', content.strip())
    
    subtitles = []
    for block in blocks:
        lines = block.strip().split('\n')
        if len(lines) >= 3:
            # First line is sequence number
            seq = int(lines[0])
            
            # Second line is timestamp
            timestamp = lines[1]
            start, end = timestamp.split(' --> ')
            
            # Remaining lines are text
            text = '\n'.join(lines[2:])
            
            subtitles.append({
                'sequence': seq,
                'start': start,
                'end': end,
                'text': text
            })
    
    return subtitles

# Usage
subs = parse_srt('video.srt')
for sub in subs:
    print(f"{sub['start']} --> {sub['end']}: {sub['text']}")

Code Example: Generating SRT in JavaScript

function generateSRT(subtitles) {
    let srt = '';
    
    subtitles.forEach((sub, index) => {
        srt += `${index + 1}\n`;
        srt += `${formatTimestamp(sub.start)} --> ${formatTimestamp(sub.end)}\n`;
        srt += `${sub.text}\n\n`;
    });
    
    return srt.trim();
}

function formatTimestamp(seconds) {
    const date = new Date(seconds * 1000);
    const hours = String(date.getUTCHours()).padStart(2, '0');
    const minutes = String(date.getUTCMinutes()).padStart(2, '0');
    const secs = String(date.getUTCSeconds()).padStart(2, '0');
    const ms = String(date.getUTCMilliseconds()).padStart(3, '0');
    
    return `${hours}:${minutes}:${secs},${ms}`;
}

Best Use Cases

  • YouTube uploads - Most compatible format
  • Video editing software - Universal support
  • DVD/Blu-ray authoring - Industry standard
  • Simple subtitle distribution - Easy to create and edit

VTT (WebVTT) Format

Overview

WebVTT (Web Video Text Tracks) is a W3C standard designed for the web. It's more feature-rich than SRT and includes styling, positioning, and metadata capabilities.

File Extension

.vtt

Format Specification

WEBVTT

1
00:00:01.000 --> 00:00:04.500
This is the first subtitle.

2
00:00:05.000 --> 00:00:08.250
This is the second subtitle.
With multiple lines.

NOTE This is a comment

Key Differences from SRT

  1. Header - Must start with WEBVTT
  2. Timestamp separator - Uses period (.) instead of comma (,)
  3. Comments - Supports NOTE blocks
  4. Metadata - Supports STYLE and REGION blocks
  5. Cue identifiers - Optional text identifiers

Timestamp Format

  • Hours:Minutes:Seconds.milliseconds
  • Milliseconds separated by period
  • Example: 01:23:45.678 = 1 hour, 23 minutes, 45 seconds, 678 milliseconds

Advanced Features

Styling

STYLE
::cue {
    color: white;
    background-color: black;
    font-size: 1.2em;
}

Positioning

00:00:05.000 --> 00:00:08.250 position:50%,line:80%
This subtitle is positioned at 50% horizontal, 80% vertical

Voice Identification

00:00:05.000 --> 00:00:08.250
<v Roger Bingham>We are in New York City

Language Tags

00:00:05.000 --> 00:00:08.250
<00:00.500>en<00:02.000> This is English text

Code Example: Parsing VTT in Python

import re

def parse_vtt(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()
    
    # Remove WEBVTT header
    content = re.sub(r'^WEBVTT\s*', '', content, flags=re.MULTILINE)
    
    # Split into blocks
    blocks = re.split(r'\n\s*\n', content.strip())
    
    subtitles = []
    for block in blocks:
        lines = block.strip().split('\n')
        
        # Skip comments and empty blocks
        if not lines or lines[0].startswith('NOTE') or lines[0].startswith('STYLE'):
            continue
        
        # Check if first line is timestamp or identifier
        if '-->' in lines[0]:
            # First line is timestamp
            timestamp = lines[0]
            text_lines = lines[1:]
        elif len(lines) > 1 and '-->' in lines[1]:
            # First line is identifier, second is timestamp
            timestamp = lines[1]
            text_lines = lines[2:]
        else:
            continue
        
        start, end = timestamp.split(' --> ')
        text = '\n'.join(text_lines)
        
        subtitles.append({
            'start': start,
            'end': end,
            'text': text
        })
    
    return subtitles

Code Example: VTT with HTML5 Video

<!DOCTYPE html>
<html>
<body>
    <video controls>
        <source src="video.mp4" type="video/mp4">
        <track kind="captions" src="captions.vtt" srclang="en" label="English">
    </video>
</body>
</html>

Best Use Cases

  • HTML5 video - Native browser support
  • Modern web applications - Rich styling capabilities
  • Interactive subtitles - Clickable and dynamic
  • Accessibility - Better screen reader support

SBV (YouTube SBV) Format

Overview

SBV (Simple Bitrate Video) is YouTube's proprietary subtitle format. It's similar to SRT but uses a slightly different timestamp format.

File Extension

.sbv

Format Specification

00:00:01.000,00:00:04.500
This is the first subtitle.

00:00:05.000,00:00:08.250
This is the second subtitle.

Key Characteristics

  1. No sequence numbers - Timestamps define order
  2. Timestamp format - HH:MM:SS.mmm,HH:MM:SS.mmm
  3. Comma separator - Between start and end timestamps
  4. Period separator - In timestamp values
  5. Simple structure - Minimal overhead

Timestamp Format

  • Start,End format on first line
  • Both timestamps use HH:MM:SS.mmm format
  • Example: 00:00:01.000,00:00:04.500

Code Example: Converting SRT to SBV

def srt_to_sbv(srt_content):
    lines = srt_content.strip().split('\n')
    sbv_blocks = []
    current_block = []
    
    for line in lines:
        if line.strip() == '':
            if current_block:
                sbv_blocks.append(current_block)
                current_block = []
        else:
            current_block.append(line)
    
    if current_block:
        sbv_blocks.append(current_block)
    
    sbv_content = ''
    for block in sbv_blocks:
        if len(block) >= 3:
            # Skip sequence number
            timestamp = block[1]
            start, end = timestamp.split(' --> ')
            
            # Convert comma to period in timestamps
            start = start.replace(',', '.')
            end = end.replace(',', '.')
            
            text = '\n'.join(block[2:])
            sbv_content += f"{start},{end}\n{text}\n\n"
    
    return sbv_content.strip()

Best Use Cases

  • YouTube uploads - Native YouTube format
  • Quick conversions - Simple structure
  • Legacy systems - Older YouTube tools

DFXP (Distribution Format Exchange Profile)

Overview

DFXP (Distribution Format Exchange Profile) is an XML-based format for timed text. It's part of the TTML (Timed Text Markup Language) family and is used in professional broadcasting.

File Extension

.dfxp, .xml

Format Specification

<?xml version="1.0" encoding="UTF-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" 
    xmlns:tts="http://www.w3.org/ns/ttml#styling">
    <head>
        <styling>
            <style id="default" tts:color="white" tts:fontFamily="proportionalSansSerif"/>
        </styling>
    </head>
    <body>
        <div>
            <p begin="00:00:01.000" end="00:00:04.500">
                This is the first subtitle.
            </p>
            <p begin="00:00:05.000" end="00:00:08.250">
                This is the second subtitle.
            </p>
        </div>
    </body>
</tt>

Key Features

  1. XML-based - Structured and extensible
  2. Styling support - Fonts, colors, sizes, backgrounds
  3. Layout control - Positioning, regions, alignment
  4. Metadata - Title, copyright, language information
  5. Multi-language - Multiple language tracks in one file

Styling Elements

<tts:color>white</tts:color>
<tts:fontFamily>proportionalSansSerif</tts:fontFamily>
<tts:fontSize>1.2em</tts:fontSize>
<tts:fontStyle>italic</tts:fontStyle>
<tts:fontWeight>bold</tts:fontWeight>
<tts:textAlign>center</tts:textAlign>
<tts:backgroundColor>black</tts:backgroundColor>

Regions and Layout

<layout>
    <region id="top" tts:origin="10% 10%" tts:extent="80% 20%"/>
    <region id="bottom" tts:origin="10% 70%" tts:extent="80% 20%"/>
</layout>

Code Example: Generating DFXP

from xml.etree import ElementTree as ET

def create_dfxp(subtitles):
    # Create root element
    tt = ET.Element('tt', {
        'xmlns': 'http://www.w3.org/ns/ttml',
        'xmlns:tts': 'http://www.w3.org/ns/ttml#styling'
    })
    
    # Create head with styling
    head = ET.SubElement(tt, 'head')
    styling = ET.SubElement(head, 'styling')
    ET.SubElement(styling, 'style', {
        'id': 'default',
        'tts:color': 'white',
        'tts:fontFamily': 'proportionalSansSerif'
    })
    
    # Create body
    body = ET.SubElement(tt, 'body')
    div = ET.SubElement(body, 'div')
    
    # Add subtitles
    for sub in subtitles:
        p = ET.SubElement(div, 'p', {
            'begin': sub['start'],
            'end': sub['end']
        })
        p.text = sub['text']
    
    # Convert to string
    xml_str = ET.tostring(tt, encoding='unicode')
    return f'<?xml version="1.0" encoding="UTF-8"?>\n{xml_str}'

Best Use Cases

  • Broadcast television - Professional standards
  • DVD/Blu-ray - High-quality subtitle support
  • Corporate videos - Advanced styling needs
  • International distribution - Multi-language support

TTML (Timed Text Markup Language)

Overview

TTML (Timed Text Markup Language) is the W3C standard for timed text. It's more comprehensive than DFXP and includes additional features for complex subtitle scenarios.

File Extension

.ttml, .xml

Format Specification

<?xml version="1.0" encoding="UTF-8"?>
<tt xmlns="http://www.w3.org/ns/ttml"
    xmlns:tts="http://www.w3.org/ns/ttml#styling"
    xmlns:ttml="http://www.w3.org/ns/ttml">
    <head>
        <metadata>
            <ttm:title>Sample Subtitles</ttm:title>
            <ttm:copyright>2026 TranscribeYT</ttm:copyright>
        </metadata>
        <styling>
            <style xml:id="s1" tts:color="white" tts:fontFamily="Arial"/>
            <style xml:id="s2" tts:color="yellow" tts:fontStyle="italic"/>
        </styling>
    </head>
    <body style="s1">
        <div>
            <p begin="00:00:01.000" end="00:00:04.500">First subtitle</p>
            <p begin="00:00:05.000" end="00:00:08.250" style="s2">
                Second subtitle with different style
            </p>
        </div>
    </body>
</tt>

Advanced Features

Animation

<p begin="00:00:05.000" end="00:00:08.250">
    <span tts:textDecoration="underline" 
          tts:backgroundColor="red">Animated text</span>
</p>

Ruby Annotations

<p begin="00:00:05.000" end="00:00:08.250">
    <ruby>
        <rb>日本語</rb>
        <rt>にほんご</rt>
    </ruby>
</p>

Conditional Styling

<p begin="00:00:05.000" end="00:00:08.250">
    <span condition="parameter('ffr') &gt; 0">
        High frame rate content
    </span>
</p>

Code Example: Parsing TTML

import xml.etree.ElementTree as ET

def parse_ttml(file_path):
    tree = ET.parse(file_path)
    root = tree.getroot()
    
    # Define namespace
    ns = {'ttml': 'http://www.w3.org/ns/ttml'}
    
    subtitles = []
    
    # Find all paragraph elements
    for p in root.findall('.//ttml:p', ns):
        begin = p.get('begin', '00:00:00.000')
        end = p.get('end', '00:00:00.000')
        text = ''.join(p.itertext())
        
        subtitles.append({
            'start': begin,
            'end': end,
            'text': text.strip()
        })
    
    return subtitles

Best Use Cases

  • Streaming services - Netflix, Amazon Prime
  • Broadcast television - Advanced subtitle requirements
  • Educational content - Complex formatting needs
  • International distribution - Multi-language support

Format Comparison Matrix

| Feature | SRT | VTT | SBV | DFXP | TTML | |---------|-----|-----|-----|------|------| | Human-readable | ✅ | ✅ | ✅ | ❌ | ❌ | | Styling support | ❌ | ✅ | ❌ | ✅ | ✅ | | Positioning | ❌ | ✅ | ❌ | ✅ | ✅ | | Multi-language | ❌ | ❌ | ❌ | ✅ | ✅ | | Comments | ❌ | ✅ | ❌ | ❌ | ✅ | | Metadata | ❌ | ✅ | ❌ | ✅ | ✅ | | Browser support | ✅ | ✅ | ❌ | ❌ | ❌ | | File size | Small | Small | Small | Large | Large | | Complexity | Low | Medium | Low | High | High |

Browser and Player Support

| Format | Chrome | Firefox | Safari | Edge | VLC | MPC-HC | YouTube | |--------|--------|---------|--------|------|-----|--------|---------| | SRT | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | VTT | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | SBV | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | | DFXP | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | | TTML | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |


Conversion Techniques

SRT to VTT

def srt_to_vtt(srt_content):
    vtt = "WEBVTT\n\n"
    
    lines = srt_content.strip().split('\n')
    blocks = []
    current_block = []
    
    for line in lines:
        if line.strip() == '':
            if current_block:
                blocks.append(current_block)
                current_block = []
        else:
            current_block.append(line)
    
    if current_block:
        blocks.append(current_block)
    
    for block in blocks:
        if len(block) >= 3:
            # Skip sequence number
            timestamp = block[1]
            start, end = timestamp.split(' --> ')
            
            # Convert comma to period
            start = start.replace(',', '.')
            end = end.replace(',', '.')
            
            text = '\n'.join(block[2:])
            vtt += f"{start} --> {end}\n{text}\n\n"
    
    return vtt.rstrip()

VTT to SRT

def vtt_to_srt(vtt_content):
    srt = ""
    
    # Remove WEBVTT header
    vtt_content = vtt_content.replace('WEBVTT', '').strip()
    
    lines = vtt_content.split('\n')
    blocks = []
    current_block = []
    
    for line in lines:
        if line.strip() == '':
            if current_block:
                blocks.append(current_block)
                current_block = []
        else:
            current_block.append(line)
    
    if current_block:
        blocks.append(current_block)
    
    for i, block in enumerate(blocks, 1):
        if len(block) >= 2:
            timestamp = block[0]
            start, end = timestamp.split(' --> ')
            
            # Convert period to comma
            start = start.replace('.', ',')
            end = end.replace('.', ',')
            
            text = '\n'.join(block[1:])
            srt += f"{i}\n{start} --> {end}\n{text}\n\n"
    
    return srt.rstrip()

Best Practices

1. Choose the Right Format

  • SRT for maximum compatibility
  • VTT for web applications
  • DFXP/TTML for professional broadcasting
  • SBV for YouTube-specific workflows

2. Maintain Quality

  • Use UTF-8 encoding
  • Keep lines under 42 characters
  • Limit to 1-2 lines per subtitle
  • Include proper punctuation
  • Use consistent timing

3. Test Across Platforms

  • Test on multiple browsers
  • Verify player compatibility
  • Check mobile rendering
  • Validate with accessibility tools

4. Optimize for Performance

  • Minimize file size
  • Remove unnecessary whitespace
  • Use efficient encoding
  • Cache subtitle files

Troubleshooting

Common Issues

Issue: Subtitles Not Displaying

Causes:

  • Incorrect file encoding
  • Wrong timestamp format
  • Missing header (VTT)
  • File path errors

Solutions:

  • Save as UTF-8
  • Verify timestamp format
  • Add WEBVTT header
  • Check file paths

Issue: Timing Problems

Causes:

  • Wrong timestamp format
  • Frame rate mismatch
  • Encoding issues

Solutions:

  • Use correct timestamp format
  • Match video frame rate
  • Re-encode with proper settings

Issue: Styling Not Applied

Causes:

  • Player doesn't support styling
  • Incorrect CSS selectors
  • Missing style definitions

Solutions:

  • Check player support
  • Use standard CSS
  • Include all style definitions

FAQ

Q: What's the difference between SRT and VTT?

A: SRT uses comma-separated milliseconds and has no styling. VTT uses period-separated milliseconds, requires a WEBVTT header, and supports styling and positioning.

Q: Which format should I use for YouTube?

A: YouTube accepts SRT, VTT, SBV, DFXP, and TTML. SRT is recommended for maximum compatibility.

Q: Can I use VTT for HTML5 video?

A: Yes, VTT is the native format for HTML5 video captions.

Q: How do I convert between formats?

A: Use online converters, FFmpeg, or write custom scripts. The conversion logic is straightforward for most formats.

Q: What's the best format for accessibility?

A: VTT is best for web accessibility due to its styling and metadata support.


Conclusion

Understanding caption formats is essential for anyone working with video content. Each format has its strengths and use cases:

  • SRT for simplicity and compatibility
  • VTT for web applications and styling
  • SBV for YouTube-specific workflows
  • DFXP/TTML for professional broadcasting

By choosing the right format and following best practices, you can ensure your subtitles are accessible, compatible, and professional.


Need help with caption formats? Go to transcribeyt.com for tools that support all major caption formats and automatic conversion.

Free Tool

Ready to Transcribe Your Videos?

Extract transcripts, generate AI summaries, and export to PDF, SRT, Markdown — all in seconds.

No credit card required • Cancel anytime

Share this Article

← Browse more articles
Generate Transcript