Video.js 博客

Greg Smith2018-03-13

videojs-contrib-ads 6

videojs-contrib-ads 发布了重大更新。本文将介绍 videojs-contrib-ads 的功能以及版本 6 中有哪些改进。

contrib-ads 是什么?

Videojs-contrib-ads 是一个用于创建 Video.js 广告插件的框架。将广告支持无缝集成到视频播放器中可能是一项艰巨的任务,特别是当您有其他可能受影响的插件时。例如,播放广告可能会导致额外的媒体事件。想象一个监听“loadstart”事件的分析系统:当前贴片广告导致额外的 loadstart 事件时,它将开始看到重复的事件。contrib-ads 有一个名为 Redispatch 的功能,可确保广告不会触发额外的媒体事件。这使得一切尽可能简单,而不会破坏其他插件。使用 contrib-ads 的额外好处在 readme 中列出

版本 6

contrib-ads 的版本 6 是一个维护版本,包含了一次重大的代码重构。项目的核心行为由一个状态机处理,该状态机随着播放器播放内容、贴片广告、中插广告和片尾广告而通过各种状态推进。最初的状态机在项目首次构思时,旨在实现一组特定的功能。多年来,该项目变得功能更完善,并出现了许多新场景和边缘情况。在此次重构中,状态机已更新,以精确匹配现代功能集。这使得代码库更易于维护和更可靠。

工作原理

广告模式被严格定义内容播放是否被广告插件阻塞

contrib-ads 有一个名为广告模式的概念。广告模式被严格定义内容播放是否被广告插件阻塞。这不一定意味着广告正在播放。例如,如果有一个贴片广告,在播放请求后我们会显示一个加载指示器,直到广告播放开始。这被认为是广告模式的一部分。公共方法 player.ads.isInAdMode() 可用于检查我们是否处于广告模式。

在版本 6 中,状态机经过重构以匹配广告模式的这一定义。有两种类型的状态:内容状态和广告状态。如果 contrib-ads 处于广告状态,则它处于广告模式。

这是新状态的图表。蓝色状态是内容状态,黄色状态是广告状态

Diagram of ad states
广告状态图

有关状态机如何随时间演变的详细历史,可以在此 Github issue 中找到详细信息。在本文中,我们将重点介绍它在版本 6 中的工作原理。

让我们来看一个简单的贴片广告场景中的状态。contrib-ads 从 BeforePreroll 状态开始。这被视为内容状态,因为尚未请求内容播放,因此广告未阻塞播放。在此期间,广告插件可以异步地从广告服务器请求广告,即使该插件处于内容模式。广告插件在准备就绪时触发 adsready 事件。contrib-ads 知道一旦按下播放按钮,广告就已准备就绪。

现在用户点击播放。此时,广告模式开始,contrib-ads 进入 Preroll 状态。Preroll 状态显示加载指示器,直到广告播放开始。Preroll 状态知道广告插件已准备就绪且已点击播放,因此触发 readyforpreroll 以通知广告插件是时候播放广告了。一旦广告播放开始,控制栏将变为黄色,并且不允许快进/快退。如果广告播放时间过长,将发生超时,内容将立即恢复播放。广告完成后,contrib-ads 恢复内容视频。只有当内容播放触发 playing 事件时,广告模式才结束。

现在我们处于 ContentPlayback 状态。此时,广告插件可以播放中插广告,导致短暂进入 Midroll 状态,或者内容可以不间断地继续播放。

当内容结束时,通常我们会立即看到一个 ended 事件。相反,contrib-ads 会发送一个 contentended 事件,这是广告插件播放片尾广告的机会。也许广告插件决定不播放片尾广告,因此它通过触发 nopostroll 进行响应。contrib-ads 知道现在真的完成了,因此会触发一个 ended 事件。

最后,由于内容首次结束,我们现在过渡到 AdsDone 状态。即使用户向后快进/快退并再次播放内容,也不会再有广告。

结论

广告可能不是您在线观看视频最喜欢的部分,但 contrib-ads 确保它们不会破坏您的网站,并且即使广告失败内容也能播放。如果您有兴趣了解更多或为项目做贡献,请在 Github 上查看

Gary Katsevman2018-03-09

Video.js 7 路线图

今天,我很高兴宣布 Video.js 7 的路线图!虽然这是一个主要版本更新,但实际上并没有太多破坏性改动。两个主要变化是增加了 videojs-http-streaming(简称 VHS),以及取消了对旧版 Internet Explorer 的支持。

VJS 7
VJS 7

Video.js HTTP 流媒体

VHS 以前是 videojs-contrib-hls,但随着 MPEG-DASH 变得越来越流行,我们意识到我们也希望支持它,并且可以在 HLS 和 DASH 的实现之间共享大量代码。敬请关注 VHS 即将发布的公告,了解更多详情!VHS 将默认随 Video.js 一起发布,因为 Video.js 的指导原则之一是让用户能够轻松地将其放到页面上,并拥有一个在不同浏览器中都能工作的播放器。随着 HLS 和 DASH 变得如此流行,我们认为现在是时候为最常见的流媒体格式提供一个即插即用的 Video.js 了。除了默认包含它之外,我们还将确保为那些不需要它或正在使用其他播放引擎(如 HLS.js)的用户提供排除 VHS 的构建版本。

旧版 IE

IE8
IE8

Video.js 在支持 IE 方面有着悠久的历史,最初为 IE8 创建了 Flash 回退机制。Video.js 5 发布时,我们有很多代码支持 IE8;当 Video.js 6 发布时,随着 Flash 的逐渐淘汰,我们将 Flash 支持转移到了一个独立项目 videojs-flash 中。现在,根据使用数据,我们计划取消对 IE8、IE9 和 IE10 的支持。根据我们收集的数据,IE 的总份额约为 4%。在这 4% 中,IE8、IE9 和 IE10 合计仅占 5% 左右,而 IE11 的使用率约为 91%。这意味着 IE8、IE9 和 IE10 合计仅占 Video.js 总使用量的 0.002%。基于如此小的占用量,我们认为为维护对这些浏览器的支持而付出的巨大努力是不值得的。好消息是,对于那些担心的人来说,Video.js 6 不会很快消失,并且将继续提供以满足您旧版 IE 的支持需求。除了这些统计数据,我们目前的测试在 IE8 上运行需要 5 到 10 分钟。取消对它的支持将使我们能够大大缩短测试套件的运行时间。更糟糕的是,有时测试会超时并重试长达 40 分钟,但当它们重新启动时,它们会在第一次尝试时通过。不必处理这些类型的问题将使我们能够增强我们的测试基础设施并提供更好的产品。

感谢 Browserstack 赞助 Video.js 的基于浏览器的测试!

Video.js 5.x

当 Video.js 7 发布时,从支持角度来看,Video.js 5.x 将停止维护。当然,我们不会从 npm 取消发布此版本或从 CDN 中删除文件,因此您可以继续使用它。但是,我们敦促您尽早更新,因为我们将不再接受修复请求。

Google Analytics

感谢 Fastly 赞助 vjs.zencdn.net

Video.js 提供了由 Fastly 赞助的 Video.js CDN 托管文件——感谢 Fastly!这些文件目前包含一个精简的 Google Analytics (GA) 像素跟踪,它会向我们拥有的 GA 帐户发送有限的信息。我们在沟通这件事情以及用户如何选择退出方面做得非常糟糕——在加载 Video.js 之前将 window.HELP_IMPROVE_VIDEOJS 设置为 false——或者使用其他 CDN,如 unpkg 或 CDNJS。最近我们做了一个更改,使这个跟踪像素尊重用户在浏览器中设置的“禁止跟踪”标志,但是,这不会影响之前发布的 Video.js 版本。

此外,从 Video.js 7.0 开始(可能还会反向移植到 6.x 的较新版本),我们将不再在我们的 CDN 构建中包含此跟踪像素。相反,我们正在研究通过 Fastly 使用 CDN 日志的选项。随着 Video.js 7 发布日期的临近,我们对将要做的事情有了更清晰的了解,届时我们预计会分享更多细节。

构建工具

目前,Video.js 使用 rollup 将所有 Video.js 文件合并为两个文件,以适应不同的构建系统。一个版本排除依赖项,用于打包工具;另一个版本包含依赖项,用于作为 <script> 在页面上使用的 UMD 文件。我们正在寻求将我们的构建和测试工具从 rollup 和 browserify 迁移到 Webpack 4.0,以提供更大的构建灵活性,并在与 VHS 构建方面做得更好(很多人都记得 contrib-hls 仓库上臭名昭著的第 600 个问题)。此外,我们还将研究在打包 Video.js 时允许自定义构建。例如,如果用户不想要 VHS 或不想要可见控件(播放器中相当大的一部分),他们可以在打包应用程序时设置 VJS_VHS=false VJS_CONTROLS=false,将这些部分排除在构建之外。

时间线

我们仍在努力完成上述大部分工作,但我们希望在 3 月底之前发布 7.0 的第一个预发布版本。

我们对这次并非“重大”的更新感到非常兴奋,希望您也会如此!

Brandon Casey2018-02-21

videojs-vr 现已归入 Video.js 组织

Brightcove 和 Video.js 已将 videojs-vr 插件作为一级插件采用。现已发布,并支持 Video.js 6!

它从何而来?

这个插件历史悠久,拥有许多维护者。它最初是 slawrence/videojs-vr。之后变成了 metacdn/videojs-vr。随后,StreamShark.ioBrightcoveHapYak 之间进行了私密合作。最终,我们决定将这个插件视为一级公民(first-class citizen)。因此,我们将其更新到高级插件 API,修复了一些长期存在的问题,在此过程中记录了其他问题,并将其移至 Video.js 组织。

如何判断视频是 360/VR 视频?

在桌面播放器上

在非浏览器环境中,这个问题很容易回答,因为有一个普遍认可的标准。视频本身应包含元数据,指示该视频是 360/VR 视频以及如何显示它。即使在浏览器之外,这也有点棘手,因为大多数程序(甚至 ffmpeg)都不支持插入此元数据。事实上,许多程序会删除这些元数据,因为它们认为这些元数据无效。这就是名为 Spatial Media Metadata Injector 的项目的作用所在。Spatial Media Metadata Injector 将 360/VR 元数据注入视频中,而不会改变其他任何内容。这使得 VLC 等播放器能够识别视频是 360/VR 视频。

videojs-vr 是如何做到的?

浏览器没有在 API 中暴露视频元数据,因此我们必须自己解析它,但这并不是一个可行的选项。因此,在 videojs-vr 中,我们有一个 projection 选项,可以在插件初始化期间传入。

projection 的第一个也是默认设置为 'AUTO'。将 projection 设置为 'AUTO' 告诉 videojs-vr 查看 player.mediainfo.projectionplayer.mediainfo.projection 将需要由某个外部插件/脚本设置,该插件/脚本由服务器告知当前视频是 360/VR 视频。视频的 player.mediainfo.projection 可以是以下任何一种:

  • '360', 'Sphere', 或 'equirectangular': 视频是球形
  • 'Cube''360_CUBE': 视频是立方体
  • 'NONE': 此视频不是 360 视频,videojs-vr 插件不应执行任何操作。这无需设置,因为如果不存在投影,将默认如此。
  • '360_LR': 用于左右并排的 360 视频
  • '360_TB': 用于上下并排的 360 视频

否则,projection 可以在插件初始化时手动设置为上述任何值。然后可以为每个视频销毁并重新初始化插件,并使用不同的设置。

当我们知道视频是 360/VR 之后会发生什么?

使用当前的投影值,我们确定需要在我们的 three.js 画布上显示什么。然后,我们使用(通过 webvr-polyfill 或真实的 VRDisplay API)填充的 API 来设置控件。如果设备连接了 VIVE、Oculus 等,我们就使用它们。如果未连接,我们则以 360 模式显示视频,通过鼠标/键盘/加速度计控制视频。在移动设备上,还有一个按钮可以进入纸板模式(cardboard mode),它会将视频显示为由一条线分隔的两个较小的视频。这样,视频就可以通过伪 VR 头戴设备(如 Google Cardboard)和手机进行观看。当视频播放时,我们使用动画帧来动画 three.js 画布上的 VideoTexture。这使我们能够根据用户输入的控制来显示视频的正确部分。

为什么它这么大?

我们都同意这个插件很大,但由于所有浏览器都不原生支持 VRDisplay API,我们不得不将许多大型依赖项包含在我们的项目中。主要尺寸来自以下方面:

  1. [three.js](https://github.com/mrdoob/three.js) 我们用它来创建 VR/360 画布,自己实现它会很痛苦

  2. [webvr-polyfill](https://github.com/googlevr/webvr-polyfill) 为当前不支持 VR(实验性标志和构建版本除外)的浏览器提供 VR 功能的 polyfill

调试

来自 three.js 和 webvr manager(来自 webvr-boilerplate)的每个对象都可以在 player.vr() 返回的 VR 插件实例上找到。

已知问题

  • 同一页面上的多个播放器将共享控件,在一个视频中平移也会影响另一个视频的平移。

未来

  • 实现播放器级别的控件,而非窗口级别的控件。这将允许根据当前焦点单独控制每个视频。
  • 提供一个菜单,以便可以使用多个 VR 显示器,而不仅仅是我们找到的第一个。

查看代码、提交问题、建议功能或提交 PR

请参阅 GitHub 仓库 videojs-vr

Gary Katsevman2018-02-01

Video.js 6.7.1 发布

Video.js 6.7.1 于本周发布。这距离第一次 6.6.0 版本发布(我们忘记撰写博客)已经一个半月,距离最新的补丁版本(6.6.3,现在是最新版)也才一周。

6.7 版本有两个很棒的新功能和一些辅助工具

  • 一个可用的 playerresize 事件
  • 用于中间件的新型调解器
  • videojs 上的 getPlayergetAllPlayers 辅助函数。

Netlify

最近,我们还将所有在线资产切换到在 Netlify 上运行。这很棒,因为它通过 Let's Encrypt 为我们提供了 HTTPS,同时也为网站、文档网站和博客提供了更好的自动化功能。由于文档网站与 Video.js 主仓库关联,并且每个 PR 都会进行构建,我们还让 Netlify 根据沙盒示例为该 PR 生成了一个示例页面。这是一个近期 PR示例页面

playerresize

这个新事件将在播放器每次调整大小时触发。它将在进入和退出全屏时,以及通过尺寸方法调整播放器大小,或者在播放器处于流体模式且窗口调整大小时触发。它使用 Chrome 64 及其他可用浏览器中新的 ResizeObserver。如果不可用,可以传入 polyfill,或者它将使用其备用方案。备用方案使用一个绝对定位的隐藏 iframe,其大小与播放器相同,然后在防抖处理程序上重新触发 iframe 的 resize 事件。此外,我想指出,resize 播放器事件并非指播放器本身的大小,而是指当 videoHeight 或 videoWidth 发生变化时触发的原始 resize 事件

中间件的协调器类型

这是中间件自 6.0 版发布以来的第一个主要功能。协调器的主要目的是让中间件能够取消对 play() 的请求。因此,它们目前仅限于 play()pause() 调用。一旦我们确定了细节,我们希望将其启用以实现更多功能。协调器中间件允许您拦截对协调器方法(如 play())的调用,然后阻止这些调用在技术上发生。这对于 play() 等方法很重要,因为在 play() 之后立即调用 pause() 可能会成功“取消”播放,但在某些情况下也会产生一些意外的副作用。协调器中间件可以决定 play() 是否应该执行,从而无需调用 pause()。之后,所有中间件都将收到通知,告知调用是否已终止,或者将获得返回值。对于 play(),返回值将是播放 promise 本身。

这是一个简单的例子

videojs.use('*', (player) => ({
  setSource(src, next) {
    next(null, src);
  }

  callPlay() {
    // return this value to terminate the method call
    return videojs.middleware.TERMINATOR;
  }

  play(terminated, playPromise) {
    if (terminated) {
      return console.error(terminated, 'play was terminated');
    }

    playPromise
    .then(() => console.log('play succeeded!'))
    .catch(() => console.log('play failed :('));
  }
});

播放器辅助方法

这些是为了更容易管理您的播放器并减少意外的副作用。很多时候,播放器通过 data-setup 自动创建,但随后为了通过代码引用它,会调用 videojs()。在某些情况下,这实际上会初始化播放器,因为它最终在自动设置之前运行。现在,可以使用 videojs.getPlayer() 代替,它绝不会创建播放器。这是在播放器创建后获取播放器的首选方式。videojs.getAllPlayers() 只是获取页面上所有当前可用播放器列表的一种好方法。

贡献者

原始更新日志

## [6.7.1](https://github.com/videojs/video.js/compare/v6.7.0...v6.7.1) (2018-01-31)

Bug 修复

  • middleware: 在协调器方法中进行空值检查 (#4913) (7670db6)
# [6.7.0](https://github.com/videojs/video.js/compare/v6.6.3...v6.7.0) (2018-01-30)

新功能

  • 向 Video.js 添加 getPlayer 方法。(#4836) (a15e616)
  • 添加 videojs.getAllPlayers 以获取播放器数组。(#4842) (6a00577)
  • 为 play() 添加协调器中间件类型 (#4868) (bf3eb45)
  • 所有情况下的 playerresize 事件 (#4864) (9ceb4e4)

Bug 修复

  • 在 Android Chrome 上不修补 canplaytype (#4885) (f03ac5e)

杂项

文档

  • 更新 COLLABORATOR_GUIDE.md 和 CONTRIBUTING.md 以包含标签含义 (#4874) (a345971)

测试

  • 将项目和构建名称添加到 browserstack (#4903) (41fd5cb)
Gary Katsevman2017-11-27

Video.js 6.5.0 版本发布

11 月 17 日是 Video.js 6.5.0 的预发布日期。它紧随 6.4.0 版本发布之后,现在 6.4.0 已被提升为最新版本!这是一个相当令人兴奋的版本,因为我们终于拥有了自己的元素!我喜欢称之为“我不敢相信它不是自定义元素”,因为它不是一个真正的自定义元素,但它也不是一个标准的 HTML 元素。此外,还要感谢一位首次贡献者带来了一个流畅的进度条;感谢 @vhmth!一个相当重要的内存泄漏修复,以及许多代码重构和错误修复。我还要感谢所有贡献者和四位首次向 Video.js 贡献的人:@vhmth@FirefoxMetzger@EhsanCh@shahlabs。这篇帖子晚了一周发布,因为发布后在美国是感恩节假期,我正在休假,如果你庆祝火鸡日 🦃,希望你过得愉快!

显著变更

  • “我不敢相信它不是自定义元素”现在拥有一个 <video-js> 元素。它的工作方式与 <video> 嵌入完全相同,但无需使用 <video> 元素及其带来的优缺点。它还会自动为您添加 video-js 类名,因此无需手动添加。

  • 一个流畅的进度条!

    before the change变更前
    after the change变更后
  • 经过对代码库和开发工具的大量探索,我们已经修复了 Video.js 中大多数由于保留的 DOM 元素导致的内存泄漏!

  • @kocoten1992 对我们旧代码风格到新代码风格的许多代码重构!

  • 以前,您可能会不小心使用鼠标中键或右键进行定位或切换播放。现在,尽管去试试吧!

  • 关于标题工具提示和菜单项的可访问性修复。

  • 在我们新的异步世界中,更好地处理 play(),尤其是在更改源之后。

贡献者

原始更新日志

### [6.5.0](https://github.com/videojs/video.js/compare/v6.4.0...v6.5.0) (2017-11-17)

新功能

Bug 修复

  • 避免菜单项和可点击组件中显示空的 title 属性 (#4746) (dc588dd)
  • Player#play: 在 play() 中更改源时等待 loadstart,而不是只等待 ready。(#4743) (26b0d2c)
  • 能够使用中键点击切换播放 (#4756) (7a776ee),关闭 #4689
  • 使进度条平滑地前进 (#4591) (acc641a)
  • 仅允许在进度条和音量控制上进行左键拖动 (#4613) (79b4355)
  • 仅在播放器创建时打印“元素不在 DOM 中”的警告 (#4755) (bbea5cc)
  • 在定位期间触发 timeupdate (#4754) (1fcd5ae)

杂项

代码重构

文档

性能改进

  • 在销毁时将元素置空以最小化分离的元素 (#4745) (2da7af1)

测试

加里·卡采夫曼2017-11-02

Video.js 6.4.0 版本发布

大家好,好久不见!昨天,我预发布了 Video.js 6.4.0 版本。这是一个相当重要的版本,包含了来自13位作者的27个合并拉取请求,其中有7位是贡献者!我要感谢所有贡献者,特别是这七位。其中六位是首次为 Video.js 提交拉取请求:@estim@seggev319@nicolaslevy@MarcAMo@knilob@odisei369。而 @kocoten1992 则带着一些出色的重构拉取请求回归。

在此版本中,我们修复了大量错误,重构了一些内容,并更新了一些测试。我们还添加了一些新功能。

与其他版本一样,此版本将作为 next 标签发布一小段时间,然后提升为 latest 标签。您可以从 GitHub Releases 或从 npm 获取这些版本。

显著变化

  • 现已提供希伯来语翻译。俄语和波兰语翻译也已更新。
  • 进度控制现在可以禁用。这在您不希望用户与它互动但仍希望显示播放进度时非常有用。
    • 视频结束时,进度控制也将完全填满,以避免因时长和当前时间的一些“怪异”导致进度条略短或略长。
  • 现在可以添加一个 hook,它在被 hookOnce 调用后会自动移除。这与我们的 ononce 事件方法相呼应。
  • 如果在模态对话框打开之前禁用控件,那么当对话框关闭时,控件将保持关闭状态。
  • player.src() 现在将返回一个空字符串,当未设置源时,以匹配 player.currentSrc() 和原生视频元素。
  • 当 Video.js 被赋予的元素不在 DOM 中时,它现在会发出警告。这是作为“首次贡献者专属”问题的一部分完成的,我们希望将来能做更多类似的工作。

Google Analytics 注意事项

我们已更新项目的 README,明确说明我们在 vjs.zencdn.net 上托管的 Video.js 版本中使用了 Google Analytics。它是 Google Analytics pixel 的简化版本,会在 1% 的加载中发送数据。通过在加载 zencdn 托管的 Video.js 版本之前将以下内容添加到页面中,即可选择退出:

<script>window.HELP_IMPROVE_VIDEOJS = false;</script>

我们的 GitHub 版本和 npm 版本包含 Google Analytics。unpkg 或 CDNjs 等第三方 CDN 也不包含。

提交者

完整更新日志

### [6.4.0](https://github.com/videojs/video.js/compare/v6.3.3...v6.4.0) (2017-11-01)

功能

  • 语言: 添加希伯来语翻译 (#4675) (32caf35)
  • 语言: 更新俄语翻译 (#4663) (45e21fd)
  • 添加 videojs.hookOnce 方法以允许单次运行的钩子。 (#4672) (85fe685)
  • 如果给定 Video.js 的元素不在 DOM 中,则添加警告 (#4698) (6f713ca)
  • 允许禁用进度控制 (#4649) (a3c254e)
  • 在播放结束时将播放进度条设置为100% (#4648) (5e9655f)

错误修复

  • CSS: 更新 user-select none (#4678) (43ddc72)
  • aria-labelledby 属性多了一个空格 (#4708) (855adf3), closes #4688
  • 如果在模态对话框关闭时,播放器控件被禁用,则不要启用它们。 (#4690) (afea980)
  • 不要限制时长更改更新 (#4635) (9cf9800)
  • 如果 Object.prototype 有额外的可枚举属性,Events#off 会抛出错误;如果 off 接收到假值,则不要移除所有事件 (#4669) (7963913)
  • 使 parseUrl 辅助函数始终具有协议 (#4673) (bebca9c), closes #3100
  • 确保在所有适当的情况下从播放切换器中移除 vjs-ended。 (#4661) (0287f6e)
  • 如果未设置源,player.src() 应返回空字符串 (#4711) (9acbcd8)

杂项

代码重构

文档

测试

加里·卡采夫曼2017-04-03

Video.js 6.0 发布!

经过数月的努力,我很自豪地宣布 Video.js 6.0 发布了 🎉!

这个版本相当令人兴奋。它极大地改善了控件和组件的可访问性,我们致力于将 Video.js 打造成最具可访问性的播放器。Video.js 还为开发者在中间件高级插件方面提供了一些闪亮且出色的新功能。Video.js 6.0 也是第一个将 Flash 从核心解绑的版本——不过,如有必要,它仍然可以作为插件使用。

今天的发布是一个预发布版本,它将保持这种状态约一到两周,然后才会晋升为 latest。只是为了确保所有最后的 Bug(如果有的话)都能被解决。

您应该了解的事项

5.x 和 6.x 之间的大多数内容都没有改变。事实上,在我们维护的大多数插件工作中,主要的工作是使用新方法并回退到旧方法,因为它们正在记录弃用警告。否则,这些插件将继续正常工作。

但是,确实有一些突破性更改,需要您采取行动。例如,如果您需要 Flash,现在就需要手动包含它。

另一个重大变化是源选择现在是异步的。这对于中间件支持是必需的,并且如果用户在与播放器交互之前等待播放器准备就绪,则最有可能不会影响他们。

这些内容已在我们的维基上列出。如果遗漏了任何内容,我们将确保进行更新。

需要反馈

如果您正在使用 Video.js 并有任何意见或问题,请访问我们的 Slack 频道。如果您发现错误,请在 GitHub 上提交问题,最好附带一个精简的测试用例

5.x 支持

我们仍将支持 Video.js 5.x 系列版本。这主要将是 Bug 修复,但新功能将根据具体情况考虑。

如果仍需要 IE8 支持,最好继续使用 5.x 版本。

npm 标签

一旦 Video.js 6 被提升为最新版本,它将接管 npm 上的 nextlatest 标签。5.x 发布系列将获得自己的标签集:latest-5next-5

行为准则

我们努力做到开放和包容,因此我们采纳了一份基于贡献者盟约行为准则,该准则适用于所有 Video.js 项目。

总结

我们对这个版本感到非常兴奋!请从 npm 上的 next 标签或从 CDN 试用。欢迎加入我们的 Slack 频道进行交流。

加里·卡采夫曼2017-03-27

功能聚焦:中间件

中间件是 Video.js 6.0 版本中即将推出的一项很酷的新功能

通过中间件,您现在能够与播放器和技术进行交互,并改变它们之间的对话方式。技术(Tech)是Video.js对播放器的抽象,它将播放器API与播放技术分离。有了技术,我们就可以将Flash回退或YouTube嵌入等内容插入到Video.js中,而无需改变外部API或播放器的外观和体验。

Video.js 中间件类似于 Express 中间件,但路由是基于视频的 MIME 类型。

许多 Video.js 用户可能熟悉 Express 等项目中的中间件。Video.js 中间件与它们没有太大区别。在这两种情况下,您都将中间件注册到特定路由,以便在路由触发时按链式调用。在 Express 中,路由基于 URL 路径。在 Video.js 中,这些路由基于视频的 MIME 类型。而且,与 Express 一样,存在匹配所有路由的“星号”(*)中间件。

中间件有两个重要方面需要注意

  1. 动态源处理

  2. 拦截播放器和技术交互。

视频目录

通过动态源处理,您可以加载具有自定义类型和源的视频,并异步解析它。一个很好的例子是视频目录系统。页面可以使用特定的目录ID和特殊的MIME类型进行渲染,如下所示

<video controls class="video-js">
  <source src="123" type="video/my-catalog">
</video>

然后,您可以为该路由注册一个中间件——类型为video/my-catalog

// middleware methods get the player instance as an argument
videojs.use('video/my-catalog', function(player) {

  // middleware are expected to return an object with the methods on it.
  // It can be a plain object or an instance of something.
  return {

    // setSource allows you to tell Video.js whether you're going to be handling the source or not
    setSource(srcObj, next) {
      const id = srcObj.src;

      videojs.xhr({
        uri: '/getVideo?id=' + id
      }, function(err, res, body) {

        // pass null as the first argument to say everything is going fine and we can handle it.
        next(null, {
          src: body.sourceUrl,
          type: body.sourceType
        })
      });
    }
  };
});

然后,当 Video.js 初始化时,它将遍历并调用为 video/my-catalog 设置的中间件。

服务端广告插入

服务端广告插入(SSAI)非常适合中间件。它展示了拦截播放和技术交互的能力。例如,您的 HLS 清单中有一个 30 秒的广告,后面跟着一个 5 分钟的视频。您希望时间线在播放广告和内容时分别显示相应的广告时间和内容时间。目前,显示的持续时间将是 5 分 30 秒 (5:30) 的总和。解决方案是添加一个中间件,该中间件知道何时播放广告,并告诉播放器持续时间为 30 秒;当播放内容时,告诉播放器持续时间为 5 分钟。

// register a star-middleware because HLS has two mimetypes
videojs.use('*', function(player) {
  return {
    setSource(srcObj, next) {
      const type = srcObj.type;

      if (type !== 'application/x-mpegurl' && type !== 'application/vnd.apple.mpegurl') {

        // call next with an error to signal you cannot handle the source
        next(new Error('Source is not an HLS source'));
      } else {

        // in here we know we're playing back an HLS source.
        // We don't want to do anything special for it, so, pass along the source along with a null.
        next(null, srcObj);
      }
    },

    // this method gets called on the tech and then up the middleware chain providing the values as you go along
    duration(durationFromTech) {
      if (areWeCurrentlyPlayingAnAd(durationFromTech)) {
        // since we're now in an ad, return the ad duration
        // in a real example you'd calculate this based on your playlist
        // rather than hardcode a value in here
        return 30;
      } else {
        // we're playing back content, so, return that duration
        return 5 * 60;
      }
    }
  }
});

播放速率调整——案例研究

一个简单但有趣的中间件是 playbackrate-adjuster。这个中间件会根据当前速率改变控件的时间。例如,如果您正在播放一个 20 分钟的视频,并将速率改为 2 倍,控件将调整为显示 10 分钟。让我们来看看代码。

videojs.use('*', function(player) {
  /* ... */

  return {
    setSource(srcObj, next) {
      next(null, srcObj);
    },

    duration(dur) {
      return dur / player.playbackRate();
    },

    /* ... */
  };
});

因此,在这里,我们附加了一个星号中间件,因为我们希望它适用于任何视频,无论 MIME 类型如何。在 setSource 中,我们还直接使用 nullsrcObj 调用 next,因为我们希望这个中间件适用于所有来源。我们还设置了 duration 方法,它从前一个中间件获取持续时间,并将其除以我们从播放器获取的播放速率。

如果您查看代码,您会看到持续时间旁边的其他方法。它们是为了确保依赖时间的其他方法得到更新。需要注意的两个方法是currentTimesetCurrentTimecurrentTime在我们想知道当前时间时被调用。setCurrentTime在我们进行跳播时被调用。因为用户正在偏移时间中进行跳播,我们希望反向应用我们的更改操作。我们不应该除以它,而是希望乘以它。

    currentTime(ct) {
      return ct / player.playbackRate();
    },

    setCurrentTime(ct) {
      return ct * player.playbackRate();
    },

如果您应用我们目前所做的,您会注意到没有任何变化,控制条仍然显示 20 分钟的持续时间。这是因为据 Video.js 所知,没有任何变化。因此,我们需要告诉 Video.js 持续时间已经改变。我们可以通过在源选择完成后存储 Video.js 给我们的技术来做到这一点。

videojs.use('*', function(player) {
  let tech;

  return {
    setTech(newTech) {
      tech = newTech;
    }

    /* ... */
  };
});

然后,当 ratechange 事件触发时,我们告诉 Video.js 持续时间已更改,Video.js 将相应地更新控件。

videojs.use('*', function(player) {
  let tech;

  player.on('ratechange', function() {
    tech.trigger('durationchange');
    tech.trigger('timeupdate');
  });

  return {
   /* ... */
  }
});

您可以在这里查看实时示例这里查看完整代码

Michael Roca2017-02-08

Video.js 从核心播放器中移除 Flash

在 2016 年 8 月,我们宣布我们有意将 Flash 从 Video.js 核心项目中移除。随着 Html5 视频成为标准播放技术,而 Flash 逐渐淘汰,是时候将 Flash 从核心播放器中移除,并将其转移到单独的代码库了。这将使我们能够允许开发人员通过自行添加技术来继续支持旧版浏览器,同时允许我们最小化 Video.js 中的旧版代码并减小播放器的占用空间。

这遵循了 Chrome、Safari 和 Firefox 的脚步,它们都在采取措施废弃 Flash。

Chrome:Chrome 56 默认禁用 Flash

Safari:Safari 将 Flash 设为旧版插件

Firefox:Firefox 减少 Adobe Flash 使用

随着 Video.js 6.0 版本的发布,无 Flash 的未来将更接近现实。

同时,为了支持 Flash 技术,已经创建了独立的 videojs-flash 项目。当 videojs-flash 插件被添加到播放器时,Flash 技术就会被添加到技术顺序中。

<link rel="stylesheet" href="path/video.js/dist/video-js.css">
<script src="path/video.js/dist/video.js"></script>
<script src="path/videojs-flash/dist/videojs-flash.js"></script>

<video  id='vid' class='video-js' controls height=300 width=600>
  <source src="video.mp4" type="video/mp4">
</video>

<script>
  var player = videojs('vid');
</script>
Brandon Casey2017-02-03

功能焦点:辅助功能

可访问性!您从未了解过的最重要功能。

在 Video.js 组织中,我们努力实现良好的可访问性。与大多数其他软件一样,任何更改都可能以意想不到的方式影响系统。例如,在 Video.js 5 中,MuteToggleVolumeControl 合并到 VolumeMenuButton 中。虽然这个更改确实允许这些控件在视觉上协同工作,但它也造成了一些意想不到的问题。它破坏了可访问性。在这篇文章中,我们将回顾损坏的部分、修复方法、什么是可访问性,以及如何测试和确保其正常工作。

如果您已经了解什么是辅助功能,请随意跳到最后一节

可访问性?那是什么?

可访问软件支持有视力、听力、运动/灵活性或其他障碍的用户。它还帮助希望使用键盘导航的用户。开箱即用的 Web 应用程序由于 HTML 的特性而具有一定的可访问性,但这仅在您以预期方式使用原生元素时才适用。如果您不能使用原生 DOM 元素,如 <button>,而必须使用 <div> 作为按钮,那么您需要担心页面中的可访问性问题。

支持听力障碍用户并非我们能直接为 Video.js 用户做到的。相反,我们必须通过在视频中添加字幕支持来间接支持这些用户。在 Video.js 中,我们已经支持字幕和副标题一段时间了,内部它们被称为TextTracks。事实上,Video.js 从版本 4 开始就支持WebVTT格式的TextTrack,这更具可访问性。

支持视力障碍用户更难,但部分在我们的控制范围内。为了支持这部分用户,我们的播放器必须对屏幕阅读器可访问。屏幕阅读器是一种向用户朗读屏幕元素的应用程序(顾名思义)。除了从屏幕朗读之外,它还允许用户仅使用键盘或触摸屏上的特定手势与页面交互(不使用鼠标或需要直接触摸可见项目)。HTML 有某些必须遵循的规则,以便页面可访问。我们将在下一节中介绍这些规则的基础知识。屏幕阅读器通过提供可在视频播放期间朗读的描述轨道来进一步支持。描述轨道是TextTrack的一种子类型,如前所述,我们无法自动将其添加到视频中,我们只能在 Video.js 中支持它们。

有关屏幕阅读器列表,请参阅本文末尾的资源部分

如何使 Web 应用程序对屏幕阅读器可访问?

如果您将原生元素用于其预期目的,那么您的大部分工作都已完成。这就是为什么使用原生元素是使任何内容对屏幕阅读器可访问的推荐方法。例如,如果您使用 <button> 元素,您将获得以下可访问性属性(尽管它们实际上不在按钮上)

  • tabIndex 允许用户通过 Tab 键导航到按钮
  • role="button" 告诉屏幕阅读器这是一个按钮
  • 空格键和回车键都将按下按钮

在某些情况下,例如 Video.js,将无法使用原生 <button> 元素。您将不得不模仿上面列表中的可访问功能,并使用 div。以下是您必须添加的列表

  • 您必须添加 role="button" 属性以将其归类为按钮。
  • 您必须添加 tabIndex,这将允许通过 tab 键导航到该 div
  • 您必须添加对空格键和回车键按下按钮的处理

可以在 Mozilla Developer Network 上找到 role 属性值的列表。

在网页上的控件和内容中模仿或添加原生可访问性后,接下来要查看的是 aria 属性。例如,我们为 ProgressBar 滑块使用 aria-live="polite"。默认情况下,aria-live 设置为 off,这意味着除非用户取消聚焦并重新聚焦元素,否则控件的更新不应读给用户。polite 的值(我们使用的)允许我们在不改变控件焦点的情况下向屏幕阅读器传达滑块的位置。这很有用,因为 ProgressBar 在视频播放时会不断更新。polite 的值还会等待,直到屏幕阅读器完成向用户阅读其他信息后,才传达这些更新。

有关 ARIA 属性的更完整列表,请参阅规范

最后,您需要为元素添加一个可访问的“名称”,以便可以引用它。一个很好的例子可以在 MuteToggle 控件中看到。由于它不是一个简单的“按钮”,我们以一种对大多数用户隐藏但对屏幕阅读器宣布的方式包含“Mute”或“Unmute”的 innerHTML/innerText。在 Video.js 中,我们将可访问名称和控件执行的操作称为“控件文本”。在大多数情况下,控件文本还会更新元素的 title 属性,这对于视觉可访问性很重要。当控件执行的操作改变时,控件文本也会随之改变。这将允许屏幕阅读器将 MuteToggle 称为“Mute Toggle”而不是“button”。它还将传达 MuteToggle 的当前操作。在这种情况下,根据按下按钮后会做什么(即按钮的状态),它将是“Mute”或“Unmute”。

以下是 Video.js 中辅助功能的一些示例:

  • MuteToggle <button>
    • aria-live设置为polite,而不是默认值offaria-live的任何值(除off外)都表示innerText/innerHTML的更新可以在用户无需将焦点从控件上移开的情况下发送给屏幕阅读器。polite的值表示屏幕阅读器应等待其说完话后再将这些更新传达给用户。
    • 具有“静音”或“取消静音”的控制文本,指示按钮对用户的当前状态
  • VolumeBar 滑块 <div>
    • 具有 role 属性,值为 slider。例如:role="slider"
    • 具有 tabIndex 属性,因为它不是原生控制元素
    • 具有监听以下事件的 EventHandlers
      • 向上和向右箭头键用于增加音量和滑块百分比
      • 向下和向左箭头键用于减小音量和滑块百分比
    • 具有 aria-label 属性,值为“volume level”,这是一个可访问的标签,屏幕阅读器将使用它来引用
    • 具有 aria-valuenowaria-valuetext 属性,这些属性会更新以指示当前音量级别(以便屏幕阅读器可以读取)
    • aria-live设置为polite,而不是默认值offaria-live的任何值(除off外)都表示innerText/innerHTML的更新可以在用户无需将焦点从控件上移开的情况下发送给屏幕阅读器。polite的值表示屏幕阅读器应等待其说完话后再将这些更新传达给用户。

问题与解决方案

现在我们来谈谈 Video.js 5 中屏幕阅读器可访问性是如何被破坏的。首先,VolumeMenuButton 取代了 ControlBar 上的 MuteToggleVolumeControlVolumeMenuButton 在点击时被设置为模仿 MuteToggle。它还会鼠标悬停或聚焦时显示 VolumeControl。这是一个问题,因为 VolumeControl 现在是按钮的子元素,而按钮不应该包含其他控件。对于屏幕阅读器和 DOM 来说,存在两个 MuteToggle button 控件。而视觉上只有一个 VolumeControl 和一个 MuteToggle。下面您可以看到此行为的 gif 动画。

macOS `VoiceOver` Before The FixmacOS `VoiceOver` 修复前

解决这个问题的方法是使用一个普通的 div 来容纳 MuteToggleVolumeControl。这个普通的 div 没有角色或控制文本,因此它对屏幕阅读器是不可见的。从那时起,我们只需要模仿旧的用户界面。对于那些想知道的人,这个新的 Component 叫做 VolumePanel。请看下面的 gif 中的新行为

macOS `VoiceOver` After The FixmacOS `VoiceOver` 修复后

轮廓

控件的另一个重要的辅助功能修复来自于移除了一小段 CSS 规则

outline: none;

我们为什么这么做?根据社区的反馈和外部资源,我们了解到轮廓应该始终存在。没有轮廓,就没有视觉指示键盘焦点在控制元素上,没有它,没有视力障碍的键盘用户很难使用这些控件。

总结

希望这篇博文能让您对如何使 Web 应用程序可访问有所了解。如果您发现任何问题或对我们的可访问性有任何建议,或者有一般性建议,请随时为 Video.js 贡献

如果您想了解可访问性工作的最新状态,请查看拉取请求问题上的 a11y 标签。

资源

以下是一些实际使用的流行屏幕阅读器:

了解更多 Web 辅助功能的资源