Web 平台 API / Live Photo · 把一动一静放上网页 / 解得开哪一半 待审核 2 / 5
decode · demuxer / HEIC 墙

解得开哪一半

拆开封装之后(见双文件与配对),手上是一张静图与一段视频。搬上网页的下一个问题是浏览器解不解得开。

流传最广的答案是:Chrome 既不认 QuickTime 容器,也不解 HEVC,所以两半都得在服务端转码。这个说法有一个看起来很硬的依据——canPlayType('video/quicktime') 在 Chrome 里返回空字符串,按规范这意味着「无法播放」。

本页把它实测了一遍。结论是这条依据本身不可靠,而真正的障碍在另一半。

1 · demuxer 看不看 MIME 标签

第一组实测:录一段 H.264 字节,包成四个 blob,分别标 video/mp4video/quicktime、一个不存在的 nonsense/xyz、以及空字符串,各喂给一个 <video>

四个全部解出时长,数值一致。包括那个不存在的 MIME 类型。

这说明 <video> 在 blob 这条路径上完全不读标签,而是嗅探字节。canPlayType 回答的是「假如给我这个 MIME 字符串,我大概能不能播」,它是一个关于字符串的咨询接口,与 demuxer 实际接受什么并无因果关系。两者在 QuickTime 这一项上直接矛盾。

2 · demuxer 看不看容器品牌

标签不算数,那容器本身算不算?ISO-BMFF 的 ftyp box 里有一个 major brand,MP4 常写 isom(也见 mp42 / iso2 / avc1),QuickTime 写 qt ——注意 ftyp 在经典 .mov 里是可选的,早期文件可能根本没有这个 box。

第二组实测改写这四个字节:原样 isom、改成 qt 、改成垃圾值 XXXX,MIME 一律标 video/quicktime。三者全部照播,垃圾品牌也照播。

Chromium 的 demuxer 按 box 结构识别容器,ftyp 的品牌字段不参与判定。MP4 的容器规范由 QuickTime 派生而来,box 布局高度重合,于是同一个解析器把两者一并收下。

图 2-1 · 三组实测在运行期重跑。A 组换 blob 的 MIME 标签,B 组改写 ftyp 的容器品牌,C 组列出浏览器自报的支持情况并当场解一张真 HEIC。可对照 A、B 两组的实际结果与 C 组里 canPlayTypevideo/quicktime 的答复。

3 · 真机格式的实测

改写品牌是合成实验,说服力有限。更直接的做法是用真文件:以 ffmpeg 产出四个 1 秒片段,H.264 与 HEVC 各配 MP4 与 QuickTime 容器,其中 HEVC + QuickTime(ftyp 品牌为 qt ,编码标签 hvc1)正是 Live Photo 视频那一半的格式,再经 HTTP 提供,逐个改写响应头的 Content-Type

文件 响应头 Content-Type 结果
H.264 + MP4 video/mp4 解出 1.00s,160×90
H.264 + QuickTime video/quicktime 解出 1.00s,160×90
HEVC + MP4 video/mp4 解出 1.00s,160×90
HEVC + QuickTime video/quicktime 解出 1.00s,160×90
HEVC + QuickTime 不发 Content-Type 解出 1.00s,160×90
HEVC + QuickTime text/plain 解出 1.00s,160×90

最后一行值得单独看:一个 HEVC 编码、QuickTime 封装的文件,被声明为纯文本,仍然播了出来。HTTP 这条路径上的行为与 blob 一致。

同一环境下 canPlayType('video/mp4; codecs="hvc1.1.6.L93.B0"') 返回 probablycanPlayType('video/quicktime') 返回空字符串。前者与实测相符,后者与实测相反。

警示 · HEVC 在 Chromium 里走平台解码器,可用性随操作系统与硬件变化,不是一个纯软件结论。上表测于 macOS 上的 HeadlessChrome 151(核对于 2026-08),不能外推到其他平台。可以外推的是容器那一条:品牌与 MIME 均不参与判定,属解析器行为,与硬件无关。

4 · 静图那一半

视频这一半基本通行,静图那一半则相反。

用同一套手法测试:拿一张 macOS sips 产出的真 HEIC(ftyp 品牌 heichvc1 编码),分别标 image/heicimage/jpeg 交给 <img>。两者都触发 error 事件,没有画面。同尺寸的 JPEG 对照组正常解出。走 createImageBitmap 则抛 InvalidStateError: The source image could not be decoded.

同样的「谎报类型」在视频那边能让文件照播,在图片这边毫无作用。差别在于:视频那半的解码器存在,只是标签识别路径宽松;图片管线里没有能解 HEVC 图像项的解码器,改标签不会凭空造出一个——同为 ISOBMFF 的 AVIF 反而解得开(实测 ImageDecoder.isTypeSupportedimage/avif 为 true、对 image/heicimage/heif 为 false),可见卡住的是编码而不是容器。

Chrome 至今不在 <img> 里解 HEIF / HEIC,全平台如此;在 macOS 上即使系统层能解,Chrome 也不把 HEIF 数据交给自己的图片管线。Safari 自 17 起支持。(核对于 2026-08)

5 · 由此得出的分工

组成 Chrome Safari 工程处理
HEIC 静图 ❌ 无解码器 ✅ 17+ 必须转码
QuickTime 容器 ✅ 不参与判定 无需处理
HEVC 编码 ✅ 平台相关 建议转,为确定性

静图必须转码,这一条没有余地——它同时也是未触发时唯一在屏的那一半,<img> 里放不出来等于整个功能不存在。转成 JPEG、WebP 或 AVIF 皆可。

视频那一半理论上可以原样投放。实践中仍建议统一转 H.264 MP4,理由不是容器或编码不通,而是 HEVC 的平台相关性让「能不能播」变成一道与用户设备有关的概率题;转码把它变成确定的。

建议 · 不要用 canPlayType 决定是否投放某个容器。本页的三组实测里它错了一次、对了一次,无法据此分辨。要判断某段字节能否播放,可靠的做法是真去加载并监听 loadedmetadataerror——这也是图 2-1 里各组实测采用的判据。

一动一静都能到浏览器里之后,剩下的问题是让它们看起来是同一张照片,而不是一张图旁边多了个播放器。这道接缝见 静图与视频之间

6 · 参考文献

  1. WHATWG. HTML Standard — MIME types(living standard,节号会漂,按锚点 #mime-types 定位;核对于 2026-08). https://html.spec.whatwg.org/multipage/media.html#mime-types
  2. ISO/IEC 14496-12. Information technology — Coding of audio-visual objects, Part 12: ISO base media file format.