MENU

「完全理解」video 标签到底能播放什么

December 3, 2022 • Read: 5595 • 学习记录

「完全理解」video 标签到底能播放什么

本文的主要内容转载于QQQQQCY'S BLOG,少部分内容是自己补充的。
  1. 理解为什么有的 mp4 媒体文件(也称 mp4 视频)用 video 标签可以播放,有的 mp4 媒体文件不可以播放视频,但可以播放音频。
  2. 了解到什么是封装格式、视频编码、音频编码。
  3. 了解 FFmpeg 这个强大的工具。

0. 写在前面

笔者是某次发现 xx.mp4 文件在 Microsoft Edge 浏览器竟然不能播放,出乎我的意料,所以才有下面这篇文章。

现如今,越来越多的项目都希望在自己的页面中插入视频,如果现在哪个应用说不支持视频播放,就和 N 年前的应用里看不了图片一样无法想象

播放视频当然就离不开 HTML5 标准中的 video 元素。所以让我们从视频的本质开始,弄懂 video 标签的原理和表现

一、使用


MDN:HTML <video> 元素,用于在 HTML 或者 XHTML 文档中嵌入媒体播放器,用于支持文档内的视频播放。

<video controls width="250">
    <source src="/media/cc0-videos/flower.webm"
            type="video/webm">
    <source src="/media/cc0-videos/flower.mp4"
            type="video/mp4">
    Sorry, your browser doesn't support embedded videos.
</video>

上面的例子展示了 <video> 元素的用法。和 <img> 元素的使用类似,在 src 属性里加入一个我们需要展示的视频地址,同时也可以用其他属性来定义视频的宽度高度、是否自动或者循环播放、是否展示浏览器默认的视频控件等信息。

<video></video> 标签中间的内容,是针对浏览器不支持此元素时候的降级处理。

浏览器并不是都支持相同的视频格式,所以你可以在 <source> 元素里提供多个视频源,然后浏览器将会使用它所支持的第一个源。

二、浏览器与播放器


用法很简单,值得注意的是在「浏览器并不是都支持相同的视频格式」这句话里,包含了两个重要信息

1. 不同浏览器支持的视频格式不同

或者换一种说法:因为不同浏览器内置的视频播放器不同,所以支持的视频格式不同。

video 元素是可替换元素。可替换元素就是浏览器根据元素的标签和属性,来决定元素的具体显示内容。

这些元素往往没有实际的内容,即是一个空元素。

简单来说就是不同浏览器里的视频播放器有不同的实现,可以抽象成,在 Chrome 中预置的播放器是 CPlayer,Safari 中则是 SPlayer。

不同播放器支持的视频格式当然也就不尽相同。不仅如此,在 UI、可控性和一些其他地方表现都各有差异

1599298176975-de9361f0-57ec-4b67-abb2-1fc79d8ad738.png

1599566957425-4b52b28f-cc67-4b5b-9fed-2aff06852183.png

所以要谈 video 标签支持的视频格式,必须先指定浏览器

2. 浏览器只支持特定的视频格式

下图是关于主流浏览器对主流视频格式的支持情况

BrowserOperating SystemH.264(MP4)HEVC(MP4)VP8(WebM)VP9(WebM)AV1(WebM)Theora(Ogg)
Google ChromeUnix-like, Android, macOS, iOS, and WindowsSince 3.0NoSince 6.0Since 29.0Since 70Since 3.0
Microsoft EdgeWindowsSince 12.0Needs hardware decoderSince 17.0 (supports Only enabled by default if hardware decoder presentSince 17.0Since 18.0 (with AV1 Video Extension)Since 17.0 (with Web Media Extensions)
Mozilla FirefoxWindowsSince 21.0NoSince 4.0Since 28.0Since 65.0Since 3.5
Mozilla FirefoxAndroidSince 17.0NoSince 4.0Since 28.0in NightlySince 3.5
Mozilla FirefoxmacOSSince 34.0NoSince 4.0Since 28.0Since 66.0Since 3.5
Safari)iOSSince 3.1Since 11Since 12.1 (only supports WebRTC)NoNoNo
Safari)macOSSince 3.1Since 11Since 12.1 (only supports WebRTC)NoNoVia Xiph QuickTime Components(macOS 10.11and earlier)

看表格中第一行 chrome 支持的格式可以发现,只有括号中的 Ogg、MP4 和 WebM 三种格式看起来比较眼熟。然而实际上真正决定浏览器能不能支持的,是 H.264、HEVC、VP8 这些一般人都没听过的格式

所以视频的格式到底是怎么一回事,还得要从视频的构成说起:

三、视频格式


视频的本质很简单,快速播放的图片而已。

几个简单的概念:

  • 帧:每帧代表一幅静止的图像
  • 帧率(fps):每秒显示的图片数。帧率越大,画面越流畅;帧率越小,画面越卡
    • 12 fps:由于人类眼睛的特殊生理结构,如果所看画面之帧率高于每秒约10-12帧的时候,就会认为是连贯的
    • 24 fps:有声电影的拍摄及播放帧率均为每秒24帧,对一般人而言已算可接受
    • 30 fps:早期的高动态电子游戏,帧率少于每秒30帧的话就会显得不连贯,这是因为没有动态模糊使流畅度降低
    • 60 fps:在实际体验中,60帧相对于30帧有着更好的体验
    • 85 fps:一般而言,大脑处理视频的极限

1. 源文件

我们计算一下按照最简单定义的视频大小

  • 用 RGB 来描述颜色,0~255 等于 256 种可能,刚好等于 1 个字节。所以一个像素 255*255*255 的大小为 3B
  • 分辨 980*720 的普通清晰度图片需要 980*720*3B ≈ 2M
  • 正常视频帧率为 30,表示 1s 的时间里播放 30 张图片。也就是说一个 1 分钟的视频大小为 2M*30*60 ≈ 3.5G

帧率 30,时长 1 分钟的 980 分辨率纯视频大小约为 3.5G。

原始的 YUV、RGB 格式的视频源文件就是这么大。

2. 编码

这么大的视频显然没法用,按照现在的网速,一个几分钟的视频估计得下载半天。

所以首要任务就是压缩,专业术语叫编码(encode)。视频一般通过两种方式编码。

2.1 帧内编码

帧内编码就是压缩图片,比如把所有图片都压缩成 jpeg(原理是利用人眼对亮度的敏感程度要高于对色彩的敏感程度)。通常这一步之后就已经能减小视频 90% 的大小。文件格式系列科普之.JPEG/.JPG

2.2 帧间编码

帧间编码是利用帧与帧之间的相关性。例如下面的第二帧与第一帧对比,只有太阳与人改变了,那么第二帧只需要存储相应的变化(类似 git,只保存 diff)

1591172572030-631dcfbb-ada6-48bd-8b54-690176c03412.png

经过压缩后的帧分为:I 帧P 帧B 帧

  • I 帧:关键帧,采用帧内压缩技术
  • P 帧:向前参考帧,在压缩时,只参考前面已经处理的帧。采用帧间压缩技术
  • B 帧:双向参考帧,在压缩时,它即参考前而的帧,又参考它后面的帧。采用帧间压缩技术

除了 I/P/B 帧外,还有图像序列 GOP

  GOP:两个 I 帧之间是一个图像序列,在一个图像序列中只有一个 I 帧

1591187375536-86430c9f-7939-4dbf-86b9-6203406dc3e2.png

推荐阅读:H.264基本原理与编码流程

3. 编码格式

编码思路大致都能归为这两种,具体怎么实现,那就大家各显神通了

不同公司、机构,不同年代都会有不同的方案。效率、成本也完全不同

常见的编码标准(大致按照编码效率从低到高,越下面的越好)

标准推出机构说明
H.262、MPEG-2 part 2MPEG/ITU-T作为 MPEG-4 的前身,MPEG-2 是用于 DVD 和早期蓝光光盘的标准编解码器。它不常用于流媒体视频。
AVC (H.264)、MPEG-4 part 10MPEG/ITU-TH.264 能够在低码率情况下提供高质量的视频图像。是目前最常见、主流的标准
VP8Google类似于 H.264
HEVC (H.265)MPEG/ITU-TH.265 旨在在有限带宽下传输更高质量的网络视频,与 H.264相比,同样的视觉质量的视频只占用一半的空间
VP9Google类似于 H.265
AV1AOMAV1 具体的目标是,与 VP9 和 HEVE 相比,码率需要大幅度降低,目标是减少 30% 左右。解码是比较低的复杂度,目标大概是 VP9 的两倍

当然,这些都只是规范而已(就和我们前端的 es6、es10 一样)。就算再先进,没人去推广、使用也白搭

一个编码标准能否被推广和使用,不仅取决于效率,还和背后的组织、专利等一系列因素相关。目前最流行、广泛使用的是 H.264 编码规范

换种说法,目前最流行的视频格式就是 H.264

4. 封装格式

平常我们看到的 xxx.MP4、xxx.AVI 文件,准确来说叫做视频封装格式。是一个容器,容器中包含了视频编码(如 H.264)、音频编码(如 AAC),此外还可以包含同步信息、字幕和元数据(如标题)等

编码 Codec name (short)全称 Full codec name封装格式 Container support
AV1AOMedia Video 1MP4, WebM
AVC (H.264)Advanced Video Coding3GP, MP4, WebM,TS,FLV
H.263H.263 Video3GP
HEVC (H.265)High Efficiency Video CodingMP4
MP4V-ESMPEG-4 Video Elemental Stream3GP, MP4
MPEG-1MPEG-1 Part 2 VisualMPEG, QuickTime
MPEG-2MPEG-2 Part 2 VisualMP4, MPEG, QuickTime
TheoraTheoraOgg
VP8Video Processor 83GP, Ogg, WebM
VP9Video Processor 9MP4, Ogg, WebM

所以再回看开始那个支持情况表格,用 Chrome 列举。H.264、H.265(HEVC)都能被封装到 MP4 中,但 Chrome 只支持前一种

1599567605606-7bae9401-588b-4193-b0d5-8bda30c23d86.png

5. 音频

一个完整的「视频」是包含了视频编码、音频编码、字幕等众多信息的。

视频编码和音频编码一般是一起存在的和视频类似,音频也是从源文件到编码,再到封装。比对理解即可,此文不做其他解释

常见的音频编码标准

标准推出机构说明
AACMPEG各个领域(新)
AC-3Dolby Inc.电影
MP3MPEG各个领域(旧)
WMAMicrosoft Inc.微软平台

目前使用最广泛的音频编码格式为 AAC

四、视频处理

从上面我们了解到,播放器能否播放一个视频,关键取决于其编码格式,但是封装格式也无法被忽略

以被广泛使用的 FLV 格式举例:

1. 封装与解包

H.264 和 AAC,可以被封装成 MP4 或 FLV。但是 Chrome 只能播放前者而不支持后者,细想其实很没道理。就好比两个东西被装在 A 盒子里的时候能拆开用,但是装在 B 盒子里就不行了

因为本质上重要的是东西(H.264、H.265),而不是盒子(MP4、FLV)

1599448961509-93e8d6e2-5668-4fc2-84f7-f62d61ef1d86.png

这是浏览器对不同视频编码的支持情况:

1599449069571-033d39a9-f966-4348-aaac-1694c3560b11.png

查表可知,Chrome 是支持 H.264 的。所以还是得明白东西盒子之间是怎面转化的

把编码封装成文件的步骤叫做封装(remux),文件拆解成编码的步骤叫解包(demux)

1605250335519-9ad68e8e-4f43-479c-9bce-c9a963888c82.png

1605250335558-90eebfb7-45e6-464f-994c-62b6be352931.png

封装,抽象的说其实就是把东西不同排列码到一块

假设 H.264 的二进制文件是 123456789 ,不同封装如下所示(数字之外的可以代指音频、字幕等其它玩意)

  • MP4
ab
123456789
b
c
d
  • FLV
a
b
123
c
456
d
789

所以看起来,我们完全可以把 FLV 先解包回 H.264 和音频、字幕登其他,再封包成 MP4

2. MSE

其实还有更简单的方法,我们根本不用再封包了

通过 Media Source Extensions API,解包出视频编码和音频编码之后,已经可以直接播放了

媒体源扩展 API(MSE) 提供了实现无插件且基于 Web 的流媒体的功能。使用 MSE,媒体串流能够通过 JavaScript 创建,并且能通过使用 <audio><video> 元素进行播放。

业界比较有名的相关库是 B 站前员工开源的 flv.js,可以把指定编码的 flv 解包成 H.264、ACC 并通过 MSE 播放。
1599459590979-ce22b801-9a88-4f6f-8b26-70e9229bbc71.png

目前除了 iOS 和 IE 之外,基本都支持 MSE

3. 其他

再深入一点可以想到,能用 js 解包,那么解码是不是也可以?

1599568075880-5f3edbec-2e25-428d-b9bb-ff020fb6c5ba.png

查看 video 的实现可以发现,浏览器是通过 FFmpeg 来解码的,实际上用 js 或者 Wasm 都可以替代这一步骤。

假设现在我们拿到一个浏览器不支持的视频格式。

  1. 先用 js 或者 Wasm 来对封装格式进行解包。
  2. 解包完发现得到的编码格式也不支持。
  3. 心一狠,通过 js 或者 Wasm 编译过的 FFmpeg 完成了解码,得到了视频源文件。

之后有两个思路

  1. 编码成 H.264 再通过 MSE 播放。
  2. 根据源文件 YUV、RGB,得到每一帧的原始图片。在 Canvas 中连续播放图片而形成视频,在 audio 中同步播放音频。

这两个方法业界也有对应的实现,不过都因为性能和兼容性问题等没有大规模被使用。

因此理论上来说,任何格式的视频都能被播放。

五、直播


以上说的都是播放一个完整视频。实际情况下一个几分钟的视频就肯定大于 10M,各大视频网站更多的是几十分钟以上的剧集、综艺,还有大小不固定的直播。

1604041455423-e9866fb0-d2f4-4ff1-817d-1abe233c4461.png

1. 视频传输

这些情况下肯定不能等视频完全下载完再播放,目前有两个主流方案处理大尺寸的视频数据:

  • 切片文件:把视频分割成 3~10s 的视频片段,一段段下载播放
  • 连续流:把视频做成流,按数据帧传输、播放(本质就是更小的分段)

常用的视频传输协议有如下 4 种:

协议http-flvrtmphlsdash
传输方式httptcphttphttp
视频封装格式flvflv、tagTs文件Mp4、3gp、webm 切片
延时
数据分段连续流连续流切片文件切片文件
HTML5 播放可通过 html5 解包播放(flv.js)不支持原生支持、或通过(hls.js)直接播放或者 HTML5 解封包播放

总结就是切片文件兼容性更好,更适合视频点播。连续流时效性更好,适合直播。

两个方案其实只解决了传输问题,例如一个 10s 的视频被分成 10 个 1s 的视频之后,每播放完 1s video 都得重新加载。

实际上就是每播放 1s,video 上的进度条都会从头跳到尾。代码层面则每过 1s,video 的初始化和结束都会被重新执行。这种体验肯定是无法满足正常使用。

2. 流媒体播放

有两种解决方案:

2.1 数据解包之后通过 MSE 播放

MSE 控制的二进制文件可以任意删减。所以通过流拿到新的数据之后直接添加到视频源上,video 也不会重新加载。

但是对 MSE 支持不佳的 iOS Safari 用不了此方案。

2.2 浏览器自主处理

第一个方案自己需要处理的部分如下:

  • 持续发送请求获取最新视频
  • 解包出视频、音频编码
  • 推送编码文件给 MSE

HTTP Live Streaming(HLS),是 Apple 提出的直播流协议。所以在其平台的浏览器上原生就支持,只用把 HLS 地址直接传递给 video 的 src 属性,浏览器相当于把上述步骤全部完成了。而且最开始说过,video 是可替换元素。所以很多浏览器(尤其是国内的移动端浏览器)会使用自己开发过的,不同于标准的播放器。

  • 坏处是不像标准的 video 那么可控。比如无法控制显示层级,永远在页面的最高层...
  • 好处是会帮你处理很多视频甚至流媒体协议

例如 QQ 浏览器、UC 浏览器。可以直接播放 FLV、HLS 流

所以我整理了一份,一直使用最优方案的直播实现:

1593573650354-597fa75e-4908-4ee7-8573-17a4b811011d.png

(极少数浏览器劫持后也不支持 HTTP-FLV 流,但是一定会支持 HLS 流,类似情况白名单切换即可)

六、FFmpeg

官网github 地址

推荐阅读图书:FFmpeg原理

一个很强大的工具,可以将很多类型的视频编码文件转换成你想要的视频编码文件。

参考:

Last Modified: May 8, 2023
Leave a Comment

已有 1 条评论
  1. Coelacanthus Coelacanthus     Linux /    FireFox

    Git 恰好与帧间编码相反,Git 保存的是每个 commit 的快照。保存差异是 SVN 的行为。