YouTube Caption Formats: A Technical Deep Dive into SRT, VTT, SBV, DFXP, and TTML
Table of Contents
- Introduction to Caption Formats
- SRT (SubRip) Format
- VTT (WebVTT) Format
- SBV (YouTube SBV) Format
- DFXP (Distribution Format Exchange Profile)
- TTML (Timed Text Markup Language)
- Format Comparison Matrix
- Code Examples and Parsing
- Browser and Player Support
- Conversion Techniques
- Best Practices
- Troubleshooting
- 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:
- Sequence Number - A sequential integer identifying the subtitle
- Timestamp - Start and end time in
HH:MM:SS,mmmformat - Text - The subtitle content (can be multiple lines)
- 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
- Header - Must start with
WEBVTT - Timestamp separator - Uses period (
.) instead of comma (,) - Comments - Supports
NOTEblocks - Metadata - Supports
STYLEandREGIONblocks - 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
- No sequence numbers - Timestamps define order
- Timestamp format -
HH:MM:SS.mmm,HH:MM:SS.mmm - Comma separator - Between start and end timestamps
- Period separator - In timestamp values
- Simple structure - Minimal overhead
Timestamp Format
- Start,End format on first line
- Both timestamps use
HH:MM:SS.mmmformat - 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
- XML-based - Structured and extensible
- Styling support - Fonts, colors, sizes, backgrounds
- Layout control - Positioning, regions, alignment
- Metadata - Title, copyright, language information
- 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') > 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.