微信号:frontshow

介绍:关注前端发展,分享一线技术.不断学习,不断进步,登上前端之巅!

<img>标签动态图片的进化:不是GIF,胜过 GIF

2017-12-20 18:55 BrotherZhao 译
【圣诞礼物】2017 最火爆的线上游戏“娃娃机”,我们复刻了极客版送给你,点击文末“阅读原文”立刻参与。

长期存在并广泛应用的 GIF 格式存在图像质量损失严重和体积庞大等不足。在最新的 Safari 浏览器预览版中,可以在 <img> 标签播放视频,满足了和动态图一样的需求。经过对比测试, <img> 标签下视频的各项性能均优于 GIF 格式。本文为你详解这项技术的现状和未来前景。

概要
  • GIF 确实很好,但是却有着严重的图像质量损失和性能损耗。

  • <video> 替换 GIF 效果更好,但是会造成性能缺陷: 无法预加载,需要范围请求

  • 现在可以在 Safari Technology Preview 中使用 <img src=".mp4"> 标签了。

  • 初步测试结果显示, <img> 标签中的 MP4 比同等条件下的 GIF 播放快 20 倍、解码快 7 倍——而文件尺寸仅为 1/14

  • 背景 CSS 视频和响应式视频(Responsive Video )终于可以独立为一个“东西”了。

  • 动态照片(cinemagraphs)终于摆脱了 GIF 的固有弊病。

  • 很期待其他浏览器快速跟进:本文原文在 Chrome 中打开有 46 MB,但是在 Safari Technology Preview 中只有 2 MB。

特别致谢:Eric Portis, Jer Noble, Jon Davis, Doron Sherman, 和 Yoav Weiss.

引言

很多人对 GIF 都是既爱又恨。

Safari Tech Preview 的出现可能会改变这一切,让人们对 GIF 全都是爱。

实际上,动画 GIF 的用法脱离了 GIF 的本意,比如最初的 GIF89a 声明指出:

GIF 格式(Graphics Interchange Format)并非为动画设计的平台,尽管可以在有限程度上如此操作。

但是 GIF 却发展成了动态照片、表情包和创意展示的绝佳工具。当然了,这些绝佳应用都是有代价的。动画 GIF 的 web 端性能十分差劲:GIF 体积庞大、耗费移动蜂窝流量、需要更多的 CPU 和内存、导致重绘并严重降低电池性能。一般而言,GIF 是 H.264 视频体积的 12 倍,在浏览器中的加载和播放要消耗 2 倍的电量。结果,消耗如此多的资源,得到的效果却不尽人意——GIF 的 256 色的限制导致很多 GIF 特别难看(尽管有一些看起来很棒)。

小孩子通常都会很喜欢 GIF ——但是他们却不会明白,为什么用来看 GIF 的设备的电池总是很快就没电。

GIF 有很多优势:迅速响应浏览器的请求、自动播放而且循环播放、没有声音。这也意味着 GIF 会比较简短。市场调研表明,用户更喜欢一分钟以内的微视频和动态图片,而不是很长的视频和静态图片。从这里来看,GIF 对于用户体验是很好的。

如何让人们从对 GIF 既爱且恨转变为十分喜爱 Gif 的呢?(这里为了区分而故意改变了大小写)。

在最新的 Safari Tech Preview 中,多亏了 Jer Noble 的辛苦工作,人们可以直接在<img>标签中加载 MP4 文件了:这个用例不是很长的视频,而是简短的、无声的、循环播放的视频,就像 GIF 一样。你可以自己先看一下:

<img src="rocky.mp4">

这个效果很棒。这对于企业和个人用户,尤其是就 Web 性能而言,都是很了不起的。

但是我们已经有 video 标签了啊?

正如很多人指出的那样,直接采用 <video > 标签会比使用 GIF 的性能要好得多。这也是 Twitter 在 2014 年宣布增加对动画 GIF 的支持而不增加对 GIF 的支持的原因。Twitter 将 GIF 转换为 MP4 文件,并通过 <video > 标签进行传输。因为所有的浏览器都支持 H.264 视频格式,所以这个转换很简单。

<video autoplay loop muted inline>
  <source src="eye-of-the-tiger-video.webm" type="video/webm">
  <source src="eye-of-the-tiger-video.mp4" type="video/mp4">
  <img src="eye-of-the-tiger-fallback.gif"/>
</video>

将动画 GIF 转换为 MP4 十分简单,只需要运行一行命令就行可以:ffmpeg -i source.gif output.mp4。

然而,并不是所有人都能拆解自己的内容管理系统并将 <img> 转换为 <video >标签。就算你可以做到,这里也存在 3 个问题

1. 浏览器处理 video 标签的速度很慢

正如 Goug Sillars 最近在 HTTP Archive 的一篇文章指出,使用 <video > 标签会造成很大的性能损耗。

<img> 标签不同的是,浏览器不会预先加载 <video> 标签内容。一般而言,浏览器只会预加载 JS、CSS 和图像资源,因为他们对于页面展示是不可或缺的。由于 <video> 标签的内容尺寸不一,所以 <video> 标签是在主线程开始准备展示内容的时候才会加载的。这样就导致 <video> 标签加载延迟数百微秒。

例如,速度峰会页面(Velocity conference page)上方的视频需要 5 秒才能加载,而他是此页面中的第 27 个加载项,是在 Start Render 请求之后,也在网页字体加载之后。

更糟糕的是,很多浏览器认为<video> 标签包含着很长格式的内容。为了防止在不想看完视频的情况下浪费移动流量,这些视频不是一次性加载完毕的。浏览器会首先执行一个 1 比特的请求,以测试服务器是否支持 HTTP 范围请求。然后,会有一系列不同大小的范围请求,以保证视频得到足够而不过分的缓冲。

这样的结果就是,在浏览器开始解码内容以前就发生了多个 TCP 往返循环,显著延迟了用户看到视频的进程。在移动网络有较高延迟的情况下,这些往返循环可能导致视频播放有上百甚至上千微秒的延迟。

典型的 JS 视频播放器比本地的 <video> 标签表现更差劲。通常将视频嵌入网站的最简单的方法是利用本地化服务,就像 YouTube 和 Vimeo 做的那样,这样能够避免视频解码、播放和 UX 等复杂工作。这个方法很好,但是对于微视频或者像前述网站顶部的关键内容来说,只是徒增延迟而已——原因不外乎是 JS 播放器和这些本地服务需要注入的支撑资源(css/js/jpg/woff)。在 <video> 之外,浏览器不得不下载、评估和执行 JS 播放器程序——然后才能播放视频。

很多人都喜欢 Loki 夹克,但是看一下 Loki 美国官网——他们的首页视频寄存在 Vimeo 服务器:

仔细看就能发现 JS 播放器在 DOM 完成之后立马请求了,但是很久之后才完全加载并开始播放视频。

WPT Results:

https://www.webpagetest.org/result/171201_M1_8c08554c16f8e3e6dfc378289105d5e5/1/details/#waterfall_view_step1

2. 无法右键保存视频

大多数长篇视频内容都是基于 JS 播放器进行分发的——影像日志、电视、电影。通常这些播放器为用户提供了很方便的分享链接和书签工具,这样用户就可以随时返回 YouTube 或者其他任何地方并再次找到这个视频。作为对比,微视频内容——比如表情包和图片动画——通常不是经由播放器传递的,用户希望能够下载 GIF 并分发给朋友,就像他们对网络上其他图片进行的操作一样,比如“这只跳舞的猫咪太可爱了,我必须分享给所有的好友!”

如果你用的是 <video> 标签,那用户就不能右键另存为了。而前面提到的跳舞的猫咪也就是出色却让人失望了。

3. 自动播放被滥用

最后,采用 <video> 标签和 MP4 而不是 <img> 标签和 GIF,就会发生在你浏览网页时突然蹦出猫鼠游戏的视频,这就是为了吸引用户注意而滥用 <video autoplay> 自动播放属性的案例。移动浏览器都忽略或者禁用了自动播放属性,需要全屏才会启动这一属性。在过去几年中,苹果和谷歌都放松了这方面的限制,允许 <video>下类似 GIF 的内容自动播放。

然而,广告厂商又在滥用这一特性,引发了更加严格的限制:如果要用自动播放 <video> 的功能,必须将内容属性设置为静音 <muted> 或者完全去除声轨。

但是我们已经有了动态 WebP 和动态 PNG 啊!

GIF 并不是唯一的具有动画小姑的图片格式。WebP 和 PNG 也都支持动画功能。但是,与 GIF 类似的是,他们都不是为动画设计的。这就导致,和专注于视频编码的格式如 H.264、H.265、VP9 和 AV1 相比,他们的动画文件体积巨大。

动态 PNG 在各种浏览器中得到了广泛支持,但是他也有 GIF 存在的色彩问题,同时依旧不能高效压缩视频。

动态 WebP 能好一些,但是与真正的视频格式相比,还是有很多问题。除了不具备标准格式外,动态 WebP 缺少色度子采样和宽域支持。更进一步地,动态 WebP 的支持生态十分破碎,并非所有的 Android、Chrome 和 Opera 都支持动态 WebP,甚至浏览器都把支持 WebP 作为一项特性来打广告:Chrome 42、Opera 15+ 和 Android 5+ 才行。

所以尽管动态 WebP 比 动态 GIF 和 PNG 的压缩效率要高,但我们还可以做得更好(见下面的压缩对比)。

来见识 video 的图片方案

通过在<img>里真正的启用 video 格式,Safari Technology Preview 解决了性能和体验上的问题。现在我们的最先进的 video 可以做到尺寸很小,同时高效率(就和通过<video>加载的 MP4 一样),它们能方便的预加载、自动播放,并分享出去(和我们的老朋友 GIF 一样)。

<img src="ottawa-river.mp4">

所以到底能快多少?打开开发者工具来看它在 Safari Technology Preview 和其它浏览器的区别:

不幸的是 Safari 对 WebPageTest 支持的不是很好,但要建立一个可靠的 benchmark 并不简单。而且,Safari Tech Preview 版本的覆盖率很低,所以使用 RUM 工具来进行性能比较也不太实际。

但是,我们至少可以做两件事,首先,比较原始文件大小,然后使用Image.decode()来比较资源在限定硬件上的不同表现。

节省流量

首先要说明的是,图片体积就表征了流量节省情况。为了进行对比,实验者将 giphy.com 前 100 的动图 GIF 分别转换为 vp8/vp9/webp/h264/h265 格式.

声明:这些结果仅可作为方向示意。每个解码器都可以做更多调试,正如可以看到的, vp9 要比默认的 vp8 输出差很多。还需要进行更详细的、把 SSIM 考虑在内的测试。

以下是此次测试的中位数 (p50) 结果:

WebP 的体积比 GIF 要小,但是其他任何形式都比 WebP 还要小。这没什么奇怪的,因为这些新的视频编码就是为在线视频流高度优化的。H.265 正如预期的 AV1 一样表现不错。

这样的优势不仅是传输更快,而且是为终端用户大量节省费用。

总而言之,在 <img> 标签中使用视频在移动网络情况下速度更快。

解码和视觉效果的提升

下面要考虑的是解码和浏览器端的播放效果。H.264 和 H.265 的优势是硬件解码。

如何进行测量?

因为浏览器尚未完善首屏图片 API,可以采用 Steve Souder 的 User Timing and Custom Metric strategy 作为近似手段,评估图片合适向用户展示。他并不测量帧频,但是却大致告诉人们第一帧何时播放。更好的是,可以使用新采纳的 Image.decode() 测量解码性能。在下方的测试页面中,实验者在 <img> 标签中注入了一张特定的 GIF 和 MP4 100 次,并对比了解码和绘图性能。

let image = new Image;
t_startReq = new Date().getTime();
document.getElementById("testimg").appendChild(image);
image.onload = timeOnLoad;
image.src = src;
return image.decode().then(() => { resolve(image); });

结果令人印象深刻。在实验者所用的 2017 MacBook Pro 进行不联网本地测试的情况下,GIF 比 MP4 要多花 20 倍的时间才能绘制出第一帧(由 onload 事件信号而来),前者的解码时间是后者的 7 倍。

很好奇嘛?复制这个项目,自己测试下吧。需要注意的是,联网会对这个 GIF vs MP4 的试验结果造成扭曲。尤其是,在最后一个比特结束之前,解码就已经开始了,这样在传输、播放和解码之前的差距就变得小多了。这表明,节省流量可以大幅度提高用户体验。然而,仅在本地机器进行测试,你就能够发现,视频在节能方面也有着极佳的表现。

如何改进?

既然 Safari Technology Preview 已经支持这样的设计模式了,人们该如何利用这一功能,而不必在不支持的浏览器上显示破碎的图片?好消息是这个其实很简单。

方法 1 :使用响应式图片

理想情况下,最简单的是利用 HTML5 中 <picture>标签的 <source type> 特性:

<picture>
  <source type="video/mp4" srcset="cats.mp4">
  <source type="image/webp" srcset="cats.webp">
  <img src="cats.gif">
</picture>

其实事情就到此为止了。然而,Safari 中有一个 WebKit bug 导致预加载器会下载第一个 <source>,而无关乎类型声明。主 DOM 加载器能够发现这一错误并选择正确的加载,但是这一损害是不可逆的:预加载器充分利用了机会将图片下载下来,而更重要的是这浪费了流量。好消息是,有人上报了这一 Bug,应该会在 Safari TP 45 中得到修复。

简言之,在 Safari 下一版本达到 90% 的用户基数之前,不推荐使用 <picture><source type>

方法 2:使用 MP4、动画 WebP 和 GIF 回调

如果你不想改变 HTML 标记,你也可以使用 HTTP 将 MP4 附带内容协议发送给 Safari。要这么做,你需要基于 Accept 和 User-Agent 的 Header 生成动态图片(跟从前一样)和 Vary 回复的多个拷贝。

在 WebKit BUG 179178 发布后,这一切简单多了。你可以使用 Accept: video/* 进行测试(就像之前使用 Accept: image/webp 进行测试一样)。但是不同的浏览器有着不同的支持类型,具体如下

在 Nginx 中,将与下述类似:

map $http_user_agent $mp4_suffix {
    default   "";
    "~*Safari/605"  ".mp4";
}

location ~* .(gif)$ {
          add_header Vary Accept;
          try_files $uri$mp4_suffix $uri =404;
}

当然了,不要忘记利用 Vary: Accept, User-Agent 告诉他们分别缓存每项响应。实际上,你应该将 Cache-Control 设置为私密属性,并利用 TLS 来保证不太熟练的 ISP 性能提高代理不会缓存内容:

GET /example.gif HTTP/1.1
Accept: image/png; video/*; */*
User-Agent: User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/605.1.13 (KHTML, like Gecko) Version/11.1 Safari/605.1.13

…

HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Length: 22378567
Vary: Accept, User-Agent
方法 3:使用 RESS 和回调 video 标签

如果你可以自由操纵 HTML,你可以采用 RESS 技术(Responsive-Server-Side)。这样可以将浏览器的检测逻辑移动到 HTML 的输出中。

例如,对于 PHP 你可以这么做:

<?php if(strlen(strstr($_SERVER['HTTP_USER_AGENT'],"Safari/605")) <= 0 ){ // if not firefox ?>
<img src="example.mp4">
<?php } else {?>
<img src="example.gif">
<?php }?>

如上文所示,一定记得将 Vary: User-Agent 的响应提供给 CDN 供应商,告诉他们,你的 HTML 有不同版本的缓存内容。有些 CDN 厂商自动支持 Vary 抬头,而其他厂商需要经过简单的 CDN 配置才可以。

额外奖励:不要忘记删除音轨

现在既然你决定不将 GIF 转为 MP4 而是将 MP4 转为 GIF,需要记住的是,删除音轨以节省体积。既然已经知道这些动画都将是静音播放的,所以把声音和其他不必要的都删除就好了。对于 ffmpeg 最简单的方式是:

ffmpeg -i cats.mp4 -vcodec copy -an cats.mp4
是否有体积限制?

在撰写本文期间,Safari 会无区分地下载所有 <img> 标签中的视频,无论体积大小如何。一方面,这会帮助提高浏览器性能,所以是需要的;另一方面,如果向用户推送了一条 120 分钟的视频,这就很可怕了。实验者测试了不同体积的视频,发现只要用户在线,所有的视频都会下载下来。所以,要对用户友善些:如果想推送长视频,从性能考虑还是用 <video> 标签吧。

下一步是什么?交互式视频和背景视频

既然已经可以通过 <img> 标签传送视频, 那就会有很多新的用例了,比如交互式视频和背景视频。

比如,可以将 MP4 放在 srcset 中,利用 Client Hints 和 Content-DPR 给出不同的反应类型,并利用 <picture media> 添加艺术效果——这里有无限可能。

<img src="cat.mp4" alt="cat"
  srcset="cat-160.mp4 160w, cat-320.mp4 320w, cat-640.mp4 640w, cat-1280.mp4 1280w"
  sizes="(max-width: 480px) 100vw, (max-width: 900px) 33vw, 254px">

CSS 中的视频 background-image: url(.mp4) 同样也会奏效。

<div style="width:800px, height: 200px, background-image:url(colin.mp4)"/>
结论

Safari Technology Preview 为 <img> 标签引入视频功能,能够实现与 GIF 类似的良好体验,同时可以避免 GIF 文件的性能和质量损耗。对于用户、开发者、设计师和网络人员来说,这一功能真是非常赞。这一升级除了带来巨大的性能提升之外,还打开了媒体和电商企业数年来长期渴求的很多新的用例。希望其他浏览器能够马上跟进:谷歌、微软、火狐、三星……该你们接招了。

原文地址:

https://calendar.perfplanet.com/2017/animated-gif-without-the-gif/

前端之巅

「前端之巅」是 InfoQ 旗下关注前端技术的垂直社群,加入前端之巅学习群请关注「前端之巅」公众号后回复 “ 加群 ”。投稿请发邮件到 editors@cn.infoq.com,注明 “ 前端之巅投稿 ”。

福利推荐:

【世界欠你的娃娃,在圣诞节送给极客的你】

InfoQ 旗下经典技术大会门票 10+

极客时间付费精品专栏 200+

还有阳光普照的知识大礼包 10000+

 
前端之巅 更多文章 JavaScript的隐形成本 前端每周清单: 2017 JS调查报告、JS基础代码片 Apollo GraphQL技术栈概览:如何将所有的功能组合起来 Electron开发跨平台构建流程设计 ExtJS十年浮沉录
猜您喜欢 SDS技术的发展趋势与生态环境 &amp; Ceph投票结果 WSC“创+”系列沙龙,解构自媒体躁动之下的商业未来 寻人启示 Gitlab.com 因疲劳误删数据导致宕机超24小时,现已恢复 【Android源码 小Demo 】Volley+瀑布流+缓存