Sin
In a
Nutshell
Electron 中实现多种视频格式播放的一些思路

Electron 中实现多种视频格式播放的一些思路

April 18, 2018 /专业打杂一百年

毋庸置疑,electron 的出现极大地提升了桌面应用的开发效率。 越来越多的人也逐渐开始尝试用 electron 去实现一些原本被认为是原生技术专属的应用领域,视频播放器就是其中之一。 前段时间因为一些原因,去了解了一下用 electron 实现视频播放的可能性,这里总结一下思路。

对于 HTML5 中已经支持的格式(H.264, WebM 和 Ogg 等),显然没有讨论的必要,用 video 标签直接放就行了。 但既然是视频播放器,难免需要支持其他一些曾经流行过的格式,比如 rmvb 什么的。 浏览器对这些可没有开箱即用的支持,就需要自己去琢磨一些其他的方案了。

实时转码通过 video 标签播放

最先想到的肯定是把不支持的格式转成支持的格式。 例如通过 node-fluent-ffmpeg 将视频转成 Fragmented mp4,然后通过 Media Source Extensions API 喂给 video 标签。

思路倒是很顺,但实地试了一下发现播放起来卡到怀疑人生。 毕竟转码和播放都是非常消耗资源的操作,两者一叠加更是 hold 不住,转码甚至经常跟不上播放的速度。 所以这个方案就算是流产了。

修改 Chromium 内核添加更多格式支持

第二想到的就是魔改内核了。 因为我们知道 electron 基于 chromium 内核,而 chromium 对视频播放的支持其实来自 ffmpeg。 作为开源音视频解码库的王者,ffmpeg 都支持不了的格式也很少有其他人能支持得了。 所以能不能放开限制让 chromium 也能支持其他格式支持呢?

由于梯子不给力,拉了一个星期都没把 chromium 源码库拉下来(反正我也对 C++ 一窍不通),所以也就没有实际尝试。 不过找到了一些前人的经验,比如这篇《让Chrome的HTML5 video/audio tag支持更多种音视频格式》和这个 针对 nwjs 的包含私有 codec 的 ffmpeg 构建

总的来说不太乐观, 一个问题是 chromium 的媒体播放引擎设计时可能并不准备支持这些格式,扩展起来还是比较困难; 另一个问题是维护成本较高,对于 chromium 内核版本的更新,必须同时跟进更新自己的 patch 或构建脚本。

优势倒也很明显:对上层完全透明而且性能应该是最高的。可以作为一个备选方案。

解码播放后渲染到 canvas 标签

另一个思路是,能不能把 web 前端当成一个纯粹的渲染层,其他的工作都交给播放后端呢——当然可以,有了 canvas 什么东西画不出来?

如果只用三方库(如 ffmpeg)去实现解码,那就需要自己完成整个播放控制相关的操作, 相当于从头去实现一个 video 标签,成本就比较高了。 不过后来发现了一个比较有创意的项目:WebChimera.js。 它集成了整个 libvlc (也有一个试验性的 GStreamer 实现), 这样可以通过 vlc 的对象和接口去控制视频播放,省去了非常多的工作。

比较遗憾的是这个项目现在被作者放弃了,处于归档停止更新的状态。 看了下 issues,大概的原因是 vlc 升级到 3.0 后,vmem 的实现和之前不兼容, 并且又多做了一次 memcpy 操作导致性能变得更差了(现在的版本试下来卡顿已经比较明显)。

还是比较遗憾的,毕竟连 vlc 的维护者都跑来 issues 里围观和沟通了。 不过这次停更事件炸出了另外一个有意思的实现思路,见下一节。

通过 Chromium PPAPI 嵌入播放插件

在 WebChimera 的探讨中有人提到了一个 boram 项目, 它的思路是实现了一个很小的 Pepper 插件,使得 mpv 可以直接通过 OpenGL 在 electron 里进行渲染,相当于内嵌了一个 mpv 播放器。 作者看到以后,索性将这块独立了出来,于是就有了 mpv.js

尝试了一下,性能跟原生 mpv 不相上下,比起前一个方案还是很有优势的。 vlc 有很多优点,但性能方面我觉得还是比不上 mpv, 同一个 H.265 片源,mpv 可以流畅播放,vlc 则掉帧明显。

这个方案的风险是 Google 已经正式宣布将放弃对 PPAPI 的支持,转向 WebAssembly。 而目前 WebAssembly 的成熟度还稍欠一点火候,也不一定能完全适用这个场景——事实上已经有人尝试过编译 ffmpeg 到 wasm,转码速度还是很感人的……

结论

  • 目前最简单完善高效的方案是使用 mpv.js
  • 如果有人有钱有闲,可以修改 Chromium 内核
  • 持续关注 WebAssembly,也许以后有更好的实现