5.4 字幕与轨道(textTrack)
5.4 字幕与轨道(textTrack)
WebVTT字幕系统
1. WebVTT文件格式规范
基础文件结构:
WEBVTT
00:00:01.000 --> 00:00:04.000
这是第一句字幕
00:00:05.500 --> 00:00:08.750
这是第二句字幕
<v 张三>说话人标签
高级特性:
STYLE
::cue {
background-color: rgba(0,0,0,0.5);
font-family: "Microsoft YaHei";
}
NOTE 这是注释行
00:00:10.000 --> 00:00:12.000 line:10% position:20% align:start
带定位的字幕
2. 字幕轨道集成
HTML实现:
<video controls>
<source src="video.mp4" type="video/mp4">
<track default
kind="subtitles"
srclang="zh"
label="中文"
src="subtitles/chinese.vtt">
<track kind="captions"
srclang="en"
label="English"
src="subtitles/english.vtt">
</video>
轨道属性详解:
| 属性 | 可选值 | 作用 |
|---|---|---|
kind |
subtitles/captions/descriptions/chapters/metadata | 轨道类型 |
srclang |
ISO语言代码(zh/en/es等) | 语言标识 |
label |
任意字符串 | 用户可见的轨道名称 |
default |
布尔属性 | 默认选中轨道 |
字幕动态控制
1. JavaScript轨道API
获取轨道实例:
const video = document.querySelector('video');
const textTracks = video.textTracks; // TextTrackList对象
// 遍历所有文本轨道
for(let i = 0; i < textTracks.length; i++) {
const track = textTracks[i];
console.log(`轨道${i}:`, track.kind, track.language, track.label);
}
轨道状态控制:
// 激活指定轨道
function activateTrack(trackIndex) {
// 先禁用所有轨道
Array.from(textTracks).forEach(track => {
track.mode = 'disabled';
});
// 启用选定轨道
if(trackIndex >= 0 && trackIndex < textTracks.length) {
textTracks[trackIndex].mode = 'showing';
}
}
// 监听轨道变化
const track = textTracks[0];
track.addEventListener('cuechange', () => {
const activeCues = track.activeCues;
if(activeCues && activeCues.length > 0) {
console.log('当前字幕:', activeCues[0].text);
}
});
2. 动态字幕加载
异步加载字幕:
async function loadSubtitle(lang) {
const response = await fetch(`subtitles/${lang}.vtt`);
const subtitleText = await response.text();
// 创建Blob URL
const blob = new Blob([subtitleText], { type: 'text/vtt' });
const subtitleUrl = URL.createObjectURL(blob);
// 动态创建轨道
const track = document.createElement('track');
track.kind = 'subtitles';
track.srclang = lang;
track.label = lang === 'zh' ? '中文' : 'English';
track.src = subtitleUrl;
video.appendChild(track);
// 轨道加载完成后启用
track.onload = () => {
track.mode = 'showing';
};
}
字幕样式定制
1. CSS伪元素控制
基础样式设置:
video::cue {
color: white;
background-color: rgba(0, 0, 0, 0.7);
font-size: 16px;
font-family: "Microsoft YaHei", sans-serif;
}
/* 说话人特定样式 */
video::cue(v[voice="张三"]) {
color: #ffcc00;
}
/* 定位特定字幕 */
video::cue(c.position-top) {
top: 15px;
}
动态样式切换:
function changeSubtitleStyle(style) {
const styleElement = document.getElementById('subtitle-style');
switch(style) {
case 'dark':
styleElement.textContent = `
video::cue {
background-color: rgba(0,0,0,0.8);
color: white;
}
`;
break;
case 'light':
styleElement.textContent = `
video::cue {
background-color: rgba(255,255,255,0.8);
color: black;
}
`;
break;
}
}
2. 自定义字幕渲染
替代默认渲染:
<div class="custom-cue-display"></div>
<script>
const cueDisplay = document.querySelector('.custom-cue-display');
const track = video.textTracks[0];
track.addEventListener('cuechange', () => {
cueDisplay.innerHTML = '';
if(track.activeCues) {
Array.from(track.activeCues).forEach(cue => {
const cueElement = document.createElement('div');
cueElement.className = 'custom-cue';
cueElement.textContent = cue.text;
// 解析说话人标签
if(cue.voice) {
const voice = document.createElement('span');
voice.className = 'voice';
voice.textContent = cue.voice + ': ';
cueElement.insertBefore(voice, cueElement.firstChild);
}
cueDisplay.appendChild(cueElement);
});
}
});
</script>
高级轨道应用
1. 章节导航系统
章节轨道实现:
WEBVTT
Chapter 1
00:00:00.000 --> 00:02:30.000
开场介绍
Chapter 2
00:02:30.000 --> 00:05:45.000
主要内容
JavaScript集成:
function setupChapterNavigation() {
const chaptersTrack = Array.from(video.textTracks)
.find(track => track.kind === 'chapters');
if(!chaptersTrack) return;
const chapterMenu = document.getElementById('chapter-menu');
chaptersTrack.oncuechange = () => {
const activeChapter = chaptersTrack.activeCues[0];
if(activeChapter) {
highlightCurrentChapter(activeChapter.id);
}
};
// 生成章节菜单
chaptersTrack.cues && Array.from(chaptersTrack.cues).forEach(cue => {
const button = document.createElement('button');
button.textContent = cue.text;
button.onclick = () => {
video.currentTime = cue.startTime;
};
chapterMenu.appendChild(button);
});
}
2. 元数据轨道交互
元数据轨道示例:
WEBVTT - 元数据
00:00:05.000 --> 00:00:10.000
{
"type": "product-info",
"id": "P12345",
"name": "示例产品"
}
元数据处理:
const metadataTrack = Array.from(video.textTracks)
.find(track => track.kind === 'metadata');
metadataTrack.addEventListener('cuechange', () => {
const activeCues = metadataTrack.activeCues;
if(activeCues && activeCues.length > 0) {
try {
const metadata = JSON.parse(activeCues[0].text);
showRelatedContent(metadata);
} catch(e) {
console.error('元数据解析错误:', e);
}
}
});
多语言支持
1. 自动语言检测
function setBestSubtitleLanguage() {
// 获取用户首选语言
const userLanguage = navigator.language.split('-')[0];
const availableLanguages = Array.from(video.textTracks)
.map(track => track.language);
// 尝试匹配精确语言代码
let trackToEnable = Array.from(video.textTracks)
.find(track => track.language === navigator.language);
// 回退到主语言代码
if(!trackToEnable) {
trackToEnable = Array.from(video.textTracks)
.find(track => track.language.split('-')[0] === userLanguage);
}
// 启用匹配的轨道
if(trackToEnable) {
trackToEnable.mode = 'showing';
} else if(video.textTracks.length > 0) {
// 默认启用第一条轨道
video.textTracks[0].mode = 'showing';
}
}
2. 实时翻译系统
async function translateSubtitles(targetLang) {
const activeTrack = Array.from(video.textTracks)
.find(track => track.mode === 'showing');
if(!activeTrack) return;
const translatedCues = [];
// 遍历所有cue进行翻译
for(let i = 0; i < activeTrack.cues.length; i++) {
const cue = activeTrack.cues[i];
const translation = await translateText(cue.text, targetLang);
const translatedCue = new VTTCue(
cue.startTime,
cue.endTime,
translation
);
translatedCues.push(translatedCue);
}
// 创建新轨道
const newTrack = video.addTextTrack('subtitles', targetLang, targetLang);
translatedCues.forEach(cue => newTrack.addCue(cue));
newTrack.mode = 'showing';
}
async function translateText(text, targetLang) {
// 实际项目中调用翻译API
const response = await fetch(`/translate?text=${encodeURIComponent(text)}&lang=${targetLang}`);
return await response.text();
}
无障碍优化
1. 屏幕阅读器集成
<video aria-describedby="caption-description">
<track id="captions" kind="captions" srclang="en" src="captions.vtt">
</video>
<div id="caption-description" class="sr-only">
本视频包含字幕,可通过播放器控件开启
</div>
2. 键盘导航支持
document.addEventListener('keydown', (e) => {
if(document.activeElement === video) {
switch(e.key) {
case 'c':
// 切换字幕
toggleSubtitles();
break;
case 'm':
// 切换元数据显示
toggleMetadataDisplay();
break;
}
}
});
function toggleSubtitles() {
const tracks = video.textTracks;
const currentTrack = Array.from(tracks).find(t => t.mode === 'showing');
const nextIndex = currentTrack ?
(Array.from(tracks).indexOf(currentTrack) + 1) % tracks.length : 0;
activateTrack(nextIndex);
}
问题排查与调试
1. 常见问题解决
字幕不显示检查清单:
- 确认文件路径正确且服务器配置了正确的MIME类型(
text/vtt) - 检查轨道
mode属性是否设置为showing - 验证WebVTT文件格式是否正确
- 确保字幕时间轴与视频匹配
- 检查CORS策略是否允许加载字幕文件
2. 调试工具
控制台检查:
// 检查加载的字幕轨道
console.log('可用字幕轨道:', Array.from(video.textTracks));
// 检查当前活动的cue
const activeTrack = Array.from(video.textTracks)
.find(track => track.mode === 'showing');
if(activeTrack) {
console.log('当前活动的cue:',
activeTrack.activeCues ? Array.from(activeTrack.activeCues) : null);
}
通过系统化地应用这些字幕与轨道技术,开发者可以为多媒体内容添加强大的文本支持,从基础的字幕显示到复杂的交互式元数据应用,全面提升视频内容的可访问性和用户体验。
#前端开发
分享于 2025-04-01