初探 HTML video 视频字幕

2,011 阅读6分钟

什么是外挂字幕?

我们在观看 电视、电影 时能够看到在播放区域下方,会展示与视频中人物说话所对应的字幕。字幕给观影者看不懂的内容提供了翻译。比如英文电影里能够展示中文字幕。

早期很多视频都是采用「内嵌字幕」:视频文件和字幕文件集成在一起。而「外挂字幕」则是视频文件与字幕文件分离,在播放时倒入字幕文件进行展示。

外挂字幕的好处是同一个视频,能够支持导入和呈现多种国家的语言,并支持随时切换字幕。

HTML Video 标签使用外挂字幕

在前端,我们使用 HTML <Video /> 标签实现视频播放,若要使用外挂字幕,只需借助 <track> 标签来为视频指定字幕。

HTML <track> 元素会被当作媒体元素 <audio><video> 的子元素来使用,它允许指定时序文本字幕文件(或者基于时间的数据),并在合适的时间自动展示字幕。

它对字幕格式文件有要求:需要是 WebVTT 格式(.vtt 后缀文件)

如下是一个 视频播放展示字幕 的简单示例:

<video controls>
  <source src="./video.mp4">
  <track src="./video.vtt" kind="subtitles" srclang="en" label="English" default="true">
</video>

注意:上述示例代码需要运行在一个服务器上,或者使用 VSCode 通过启动本地服务打开。如果是以 file://Users... 形式打开,你将会看到类似错误:'file:' URLs are treated as unique security origins.

其中,<source> src 用于指定视频源,<track> src 用于指定外挂字幕文件,.vtt 字幕文件内容参考如下:

WEBVTT

00:00:00.000 --> 00:00:05.000
This is the first subtitle.

00:00:05.000 --> 00:00:10.000
This is the second subtitle.

它会在视频播放 0 - 5s 内展示第一段字幕,5 - 10s 展示第二段字幕,效果图如下:

1.jpg

<track> 标签呈现字幕文件除了 src 属性外,还有几个重要的属性:

  1. kind 属性,定义了文字以哪种形式展示,默认是以 subtitles 字幕形式使用,允许的其他关键字有:captions、descriptions、chapters、metadata。
  2. default 属性,定义了默认要启用哪一个字幕文件;一般一个视频会提供多种语言的字幕文件。
  3. label 属性,字幕文件呈现给用户可读的标题;
  4. srclang 属性,字幕文件所属语种。

了解 Web 视频文本轨道(WebVTT)

在上面,我们为 <track> src 指向的字幕资源格式为 .vtt 字幕文件。

.vtt 格式文件属于 Web Video Text Tracks Format(WebVTT),是一种基于文本 UTF-8 编码的格式,为 Web 媒体元素提供字幕数据文件。WebVTT 的 MIME 格式是 text/vtt

1、如何编写 WebVTT 格式内容

一个 WebVTT 文件(.vtt)以 WEBVTT 作为开头,包含一个或者多个的 时间提示性内容(cue),如下所示:

WEBVTT

00:01.000 --> 00:04.000
Never drink liquid <b>nitrogen</b>.

00:05.000 --> 00:09.000
- It will perforate your stomach.
- You could die.

每个 cue 的作用如下:

  1. 第一行决定字幕以时间开始展示 --> 到什么时间结束展示;
  2. 可以有一个或多个行,每个行都是展示的字幕的一部分。

2、如何为 WebVTT 中的字幕添加样式

CSS 提供了查找与 ::cue 伪元素 匹配的元素来为 WebVTT 添加样式。如下:

video::cue {
  background-image: linear-gradient(to bottom, dimgray, lightgray);
  color: papayawhip;
}

video::cue(b) {
  color: peachpuff;
}

在这里,所有字幕风格都使用一个灰色的线性渐变作为它们的背景,字体颜色为 "papayawhip"。此外,使用 <b> 元素加粗的字幕使用 "peachpuff" 颜色。

允许为 WebVTT 字幕设置的 CSS 样式属性参考:

color
opacity
visibility
text-decoration及相关属性
text-shadow
background及相关属性
outline及相关属性
font及相关属性,包括line-height
white-space
text-combine-upright
ruby-position

另外,在 WebVTT 文件中,每个 cue 同时支持设置在视频中的具体位置,如将第一个字幕居中展示在顶部,第二个字幕居中展示在底部,示例如下:

WEBVTT

00:01.000 --> 00:04.000 line:0% position:50% align:center
Never drink liquid <b>nitrogen</b>.

00:05.000 --> 00:09.000 line:100% position:50% align:center
- It will perforate your stomach.
- You could die.

此外,cue 内容同样支持一些文本标签一起使用,具体介绍可查看 WebVTT

自定义 Video 播放器字幕

当在 <video> 标签中加入 <track> 字幕标签后,video 原生 controls 功能栏中会展示 字幕 入口。

然而,视频播放器通常会根据业务需求及 UI 样式采用自定义方式实现,下面我们来看看自定义播放器中如何集成 字幕 功能并实现多字幕切换。

字幕切换列表的配置我们这样设计:由外部传入 <track> 标签所需的信息,如下,

config = {
  ...,
  nativeTextTrack: [
    {
      src: "./vtt/textTrack-1.vtt",
      kind: "subtitles",
      label: "中文字幕",
      srclang: "zh",
      default: true,
    },
    {
      src: "./vtt/textTrack-2.vtt",
      kind: "subtitles",
      label: "英文字幕",
      srclang: "en",
      default: false,
    },
  ],
}

// textTrack 类型接口参考
export interface TextTrack {
  src: string;
  kind: string;
  label: string;
  srclang: string;
  default: boolean;
}

这里我们定义了视频的两种不同语种字幕,下面基于这个字幕配置信息,为 <video> 标签创建 <track> 字幕元素,这个逻辑一般在初始化播放器时来实现。

const { nativeTextTrack } = config; // 外部传入的配置

// 字幕
let textTrackDom = '';
if (Array.isArray(nativeTextTrack) && nativeTextTrack.length > 0) {
  // 应用默认字幕
  if (!nativeTextTrack.some(track => track.default)) {
    nativeTextTrack[0].default = true;
  }
  // 创建 <track> 字幕标签
  nativeTextTrack.forEach(track => {
    if (track.src && track.label && track.srclang) {
      textTrackDom += `<track src="${track.src}" kind="${track.kind || 'subtitles'}" label="${track.label}" srclang="${track.srclang}" ${track.default ? 'default' : ''}>`;
    }
  });
}

// 播放器 video 元素实例,添加 <track> 为子元素
video.appendChild(textTrackDom);

添加后看到的 video 元素内部结构如下:

2.jpg

接下来,我们需要在 Controls 区域增加字幕切换入口,并且渲染出切换字幕的选项列表,列表中默认包含一个可供 Off 关闭字幕 的选项。

const { state, config, controls, root, video } = player;

const textTrackArea = createDOM(
  'div',
  `
    <div class="dsplayer-texttrack-name">字幕</div> // 字幕在 Controls 中展示的信息
    <ul class="dsplayer-subtitles-menu"> // 字幕选项列表
      <li class="dsplayer-subtitles-menu__item" lang="off">Off</li>
    </ul>
  `,
  {},
  'dsplayer-control-texttrack',
);
controls.appendChild(textTrackArea); // 添加到 Controls 区域

const textTrackMenu = textTrackArea.querySelector('.dsplayer-subtitles-menu') as HTMLElement;
// 根据 nativeTextTrack 创建选项列表
config.nativeTextTrack.forEach(track => {
  textTrackMenu.appendChild(createDOM('li', track.label, { lang: track.srclang }, 'dsplayer-subtitles-menu__item'));
});

要实现切换字幕(切换为另一种语种字幕),可通过设定 video.track 元素的 mode 属性来控制。mode = showing 代表当前要使用的字幕,mode = hidden 则是将当前字幕隐藏。参考示例代码如下:

// 事件委托
textTrackMenu.addEventListener('click', event => {
  const ele = event.target as HTMLElement;
  if (ele.nodeName === 'LI') {
    const lang = ele.getAttribute('lang');
    for (let i = 0; i < player.video.textTracks.length; i ++) {
      const track = player.video.textTracks[i];
      if (track.language === lang) {
        track.mode = 'showing'; // 应用字幕
      } else {
        track.mode = 'hidden'; // 隐藏字幕
      }
    }
  }
});

文末

有一些情况下提供的字幕文件是 .srt 格式,无法直接被 使用,通常都需要做一次 vtt 字幕格式转换。可以通过 在线工具 来完成。

参考

1. mdn web docs
2. mdn web docs WebVTT
3. 常见的字幕文件格式
4. 玩转HTML5 Video视频WebVTT字幕使用样式与制作
5. 向 HTML 视频中添加字幕
6. xgplayer 外挂字幕