前言

公众号:【可乐前端】,期待关注交换,分享一些有意思的前端常识

在平时的生存中,咱们遇到的流媒体播放很多都是有字幕的,字幕的呈现能够大幅度的晋升用户的观感。那么在Web中字幕到底是如何出现的?明天就让咱们一起来一探到底。

onTimeUpdate

假如咱们有一个这样的json,示意字幕数据:

  const jsonContent = [    {      start: "00:00:01.000",      end: "00:00:02.000",      text: "这是第一句字幕",    },    {      start: "00:00:03.500",      end: "00:00:04.000",      text: "这是第二句字幕",    },    {      start: "00:00:05.000",      end: "00:00:06.000",      text: "这是第三句字幕",    },  ];

其中start示意该条字幕的开始工夫,end示意该条字幕的完结工夫,text示意字幕的内容。在Web的流媒体中,有一个事件叫做onTimeUpdate,它会在媒体播放时以固定的距离触发,咱们能够监听这个事件而后来响应一些播放的变动。

<video  id="video"  controls  autoplay  crossorigin="anonymous"  src="/test.mp4"  width="500"></video>const handleTimeUpdate = () => {  const video = document.querySelector("#video");  video.addEventListener("timeupdate", function () {    const currentTime = video.currentTime;    console.log("currentTime", currentTime);  });};handleTimeUpdate();

从下面的小例子中能够看到onTimeUpdate的触发机制以及后果。依据currentTime的变动,能够找到以后工夫落在了哪一条字幕中,这样就能够实现字幕渲染的性能。

先来改一下dom构造,用一个div来展现字幕

<div class="video-container">  <video    id="video"    controls    autoplay    crossorigin="anonymous"    src="/test.mp4"    width="500"  ></video>  <div class="subtitle"></div></div>

而后解析字幕数据,配合onTimeUpdate实现字幕的展现播放:

const findSubtitle = (currentTime, jsonContent) => {  for (let i = 0; i < jsonContent.length; i++) {    const subtitle = jsonContent[i];    const startTime = parseTime(subtitle.start);    const endTime = parseTime(subtitle.end);    if (currentTime >= startTime && currentTime < endTime) {      return subtitle;    }  }  return null;};const parseTime = (timeStr) => {  const [hours, minutes, seconds] = timeStr.split(":").map(parseFloat);  return hours * 3600 + minutes * 60 + seconds;};const handleTimeUpdate = () => {  const video = document.querySelector("#video");  video.addEventListener("timeupdate", function () {    const currentTime = video.currentTime;    const res = findSubtitle(currentTime, jsonContent);    const subTitle = res.text;    const subTitleDom = document.querySelector(".subtitle");    if (res) {      subTitleDom.innerHTML = subTitle;    } else {      subTitleDom.innerHTML = "";    }  });};handleTimeUpdate();

这样咱们就实现了字幕的性能,但这个实现计划是十拿九稳的吗?来思考一个场景:如果我在0.1s-0.3s中有一句字幕,0.5s-0.8s中有一句字幕,因为timeUpdate是浏览器依据肯定的频率去触发的,假如呈现了这样的一个状况,第一次触发在0.4s,第二次触发在0.9s,那这两个字幕是不是就丢了呢?

Track

<track> 标签是 HTML5 中用于提供轨道的元素之一。次要用于提供视频和音频的文本轨道(如字幕或者描述性文本)以及音频形容。

个别状况下,<track> 元素用于在 <video><audio> 元素中嵌入内部资源,以加强媒体内容的可拜访性和可了解性。它能够用来增加字幕、章节题目、描述性正文等内容。<track> 标签的常见属性包含:

  • src:指定轨道文件的 URL
  • kind:指定轨道的类型,能够是 subtitles(字幕)、captions(题目)、descriptions(形容)、metadata(源数据)等。
  • srclang:指定轨道的语言,应用 ISO 639-1 语言代码示意。
  • label:指定轨道的标签或题目,用于在用户界面上显示。
  • default:可选属性,示意该轨道是否默认加载。如果存在多个轨道,只有一个能够设置为默认加载,通常用于抉择用户首选的轨道。

track如果用来增加字幕的话,须要与一些字幕文件搭配应用,比方VTT文件,上面简略介绍一下VTT文件。

VTT(WebVTT)文件是一种用于示意视频文本轨道的格局。它是一种简略的文本文件,通常用于存储字幕、题目、描述性文本等内容,以便在 HTML5 视频和音频元素中进行显示。VTT 文件的名称通常以 .vtt 作为文件扩展名。

VTT 文件的构造绝对简略,它由几个要害局部组成:

  • 文件头(Header) :文件头通常蕴含 WEBVTT 字样,用于批示这是一个 WebVTT 文件。
  • 工夫标识(Timing) :工夫标识局部用于指定每个文本块的起始工夫和完结工夫。工夫格局通常为 HH:MM:SS.fff,其中 HH 示意小时,MM 示意分钟,SS 示意秒,fff 示意毫秒。工夫标识通常以 --> 分隔起始工夫和完结工夫,例如:

    00:00:01.000 --> 00:00:02.000
    • 文本内容(Text) :文本内容局部蕴含在工夫标识之后,用于示意在指定工夫范畴内要显示的文本内容。每个文本块能够蕴含一行或多行文本,它们将在指定的时间段内逐行显示。

上面是一个简略的 VTT 文件示例:

WEBVTT00:00:01.000 --> 00:00:02.000这是第一句字幕。00:00:03.500 --> 00:00:04.000这是第二句字幕。

而后能够依照上面的代码应用:

  <video id="video" width="500" controls>    <source src="/test.mp4" type="video/mp4">    <track src="/en.vtt" kind="subtitles" srclang="en" label="English">    <track src="/zh.vtt" kind="subtitles" srclang="zh" label="中文">  </video>

zh.vtt的内容如下:

WEBVTT00:00:01.000 --> 00:00:02.000这是第一句字幕。00:00:03.500 --> 00:00:04.000这是第二句字幕。

en.vtt的内容如下:

WEBVTT00:00:01.000 --> 00:00:02.000This is the first subtitle.00:00:03.500 --> 00:00:04.000This is the second subtitle.

WebVTT中还有一些精细化管制的API,能够参照这个文档WebVTT。上面就简略举两个例子,其余的能力就请查阅文档,这里就不一一开展。

比如说能够通过lineposition来调整字幕的地位:

WEBVTT00:00:01.000 --> 00:00:02.000 line:38% position:35%这是第一句字幕。00:00:03.500 --> 00:00:04.000 line:40% position:35%这是第二句字幕。
  • line 属性用于设置文本的垂直地位。它示意文本绝对于媒体区域高度的百分比地位。
  • position 属性用于设置文本的程度地位。它示意文本绝对于媒体区域宽度的百分比地位。

再比方通过伪类来设置字幕的一些款式:

#video::cue(c.red) {    color: red;}
WEBVTT00:00:01.000 --> 00:00:02.000 line:38% position:35%<c.red>这是第一句字幕。</c.red>00:00:03.500 --> 00:00:04.000 line:40% position:35%这是第二句字幕。

齐全自定义字幕款式

但认真看下面的字幕,能够发现字幕有一个半透明的彩色背景,我尝试了很多办法都无奈将这个背景去掉,查阅材料也是说这是因为不同浏览器对于流媒体的字幕编码导致的,咱们曾经齐全管制了字幕的机会,然而没有完完全全管制字幕的款式。

咱们能够将track标签的kind属性赋值为metadata,当字幕变更时会触发一个cueChange事件,通过这个事件拿到字幕,而后塞进某个dom中来齐全管制字幕的款式。

    const fileVtt = () => {      const video = document.querySelector("#video");      const track = document.createElement("track");      track.default = true;      track.kind = "metadata";      function convertJsonToVtt(jsonContent) {        let vttContent = "WEBVTT\n\n";        jsonContent.forEach((item, index) => {          vttContent += `${index + 1}`;          vttContent += "\n";          vttContent += `${item.start} --> ${item.end}`;          vttContent += "\n";          if (item.type) {            vttContent += `<c.mn>${item.text}</c.mn>`;          } else {            vttContent += `${item.text}`;          }          vttContent += "\n\n";        });        return vttContent;      }      const vttContent = convertJsonToVtt(jsonContent);      // 将VTT内容保留为文件      const blob = new Blob([vttContent], {        type: "text/vtt;charset=utf-8",      });      const url = URL.createObjectURL(blob);      track.src = url;      video.appendChild(track);      setTimeout(() => {        const track = video.textTracks[0];        track.addEventListener("cuechange", function () {          const cues = track.activeCues;          for (var i = 0; i < cues.length; i++) {            const cue = cues[i];            const div = document.createElement("div");            div.append(cue.getCueAsHTML());            const subTitleDom = document.querySelector(".subtitle");            subTitleDom.innerHTML = `<div style="color:yellow">${div.innerHTML}<div>`          }        });      });    };    fileVtt();

简略介绍一下下面的实现:

  • json格局的字幕数据转换成VTT格局
  • 监听cueChange事件获取到字幕
  • 动静插入字幕

最初

以上就是本文对流媒体字幕的一些意识,如果你感觉有意思的话,点点关注点点赞吧。欢送评论区交换