【译】iOS 上 video 标签的 的一些限制

原文地址: https://webkit.org/blog/6784/new-video-policies-for-ios/

@jernoble

如果接触过 ios 上的开发,在iphoneOS 3 的时候 safari 只允许用户与 video 进行交互的时候才会触发数据的加载。但是为了将更多的媒体播放控制权重新回到网页开发者,iOS 8放宽了这一限制:Safari开始支持 preload="metadata" 属性,允许<video><audio> 元素加载足够的媒体数据,以确定媒体的大小,持续时间和可用的轨迹。对于iOS 10中的 Safari,进一步放宽了对静音<video>元素的用户手势要求。

缘由

事实证明,人们很喜欢GIF。但与现代视频编解码器(如H.264)相比,GIF格式的编码方式与编码动画图像非常不具有性价比。我们发现,GIF的带宽可以高达十二倍。这是非常昂贵的,许多最大的 GIF 提供商已经从GIF转向 <video>元素。由于这些GIF大部分作为视频片段开始生活,被转换为GIF。如今我们需要直接播放这些片段。

但是,虽然这一举措可以节省网站的带宽成本以及节省用户的电池。但是你还需要付出一些成本。在iOS 9上,<video>只会由于用户手势触发而开始播放。那么,页面将有一个<img>替换<video> 从而将引导用户进行触发,并且,在iPhone上,在 <video> 开始播放时,会进入全屏模式。

关于用户手势要求的注意事项:当我们说一个操作必须发生“作为用户手势结果”时 去调用JavaScript 。例如:video.play() 必须当用户触发了 touchendclickdoubleclick,或keydown事件。所以,

button.addEventListener('click', () => {   
  video.play(); 
})

video.addEventListener('canplaythrough', () => { 
  video.play(); 
})

则不不会触发播放。

类似地,网络开发人员通过将<video>元素集成到其页面的中来做一些非常惊艳的效果。并且由于没有用户手势要求,这些页面在iOS上根本不起作用,或者<video>元素作为动画全屏背景的话,这些都是没有效果的。

WebKit的新的策略

从iOS 10开始,WebKit放宽其内置和自动播放策略,使某些效果得以实现,但是你还是需要考虑用户的流量和电量消耗。

默认情况下,WebKit将具有以下策略:

<video autoplay>元素现在 autoplay 符合下列条件情况能够触发:
  • <video>autoplay如果它们的源媒体不包含音频轨道,则元素将被允许没有用户手势的情况下播放。

  • <video muted> 设置静音的时候也允许自动播放

  • 如果某个<video>元素获得音轨或在没有用户手势的情况下不是静音,播放将暂停。

  • <video autoplay> 元素只能在屏幕上可见时开始播放,例如当它们滚动到视口中时,通过CSS可见,并插入到DOM中。

  • <video autoplay> 如果元素变得不可见,例如通过从视口滚动出来,元素将会暂停。

<video> 元素现在可以实现play()需要满足以下条件的元素:
  • <video> play() 如果它们的源媒体不包含音轨,或者如果它们的muted属性被设置,则元素将被允许不触发手势就能播放。

  • 如果某个<video>元素获得音轨或在没有用户手势的情况下变得未静音,播放将暂停。

  • <video>元素允许 play(),当它在屏幕上不可见或出现在视口之外时。

  • video.play() 将返回 Promise,如果不满足任何这些条件,将被拒绝。

  • 在iPhone上,<video playsinline> 元素现在可以在线播放,并且播放开始时不会自动进入全屏模式。

  • <video> 没有 playsinline 属性的元素将继续全屏模式以播放。

  • 当使用 手势 退出全屏时,<video>元素不会playsinline继续播放。

对于iOS上的WebKit框架的客户端,仍然可以通过API控制这些策略,从而满足自己具体的需求。有关自动播放策略的更细粒度控制,请参阅新的WKWebViewConfiguration 属性mediaTypesRequiringUserActionForPlayback。iOS 10上的Safari将使用WebKit的默认策略。

关于playsinline属性的注释:此属性最近已被添加到HTML规范中,而WebKit已经通过取消其旧webkit-playsinline属性来修改该属性。自从iPhoneOS 4.0以来,这种属性得到了支持,并且根据我们更新的未经修订的策略,我们很高兴能够进行未修改webkit-playsinline。不幸的是,这一变化并在iOS 10 Developer Seed 2成为截止。如果您想使用iOS Developer Seed 2来实验这项新政策,则前缀属性将会起作用,但是我们鼓励您转换到未定义的属性,直到未来可用。

example

作为开发者我们如何利用这些新政策呢?假设有一个博客文章或者有许多GIF的文章,那么它们更喜欢用作<video>元素。以下是简单GIF替换的示例:

<video autoplay loop muted playsinline>
  <source src="image.mp4">
  <source src="image.webm" onerror="fallback(parentNode)">
  <img src="image.gif">
</video>
function fallback(video)
{
  var img = video.querySelector('img');
  if (img)
    video.parentNode.replaceChild(img, video);
}

在iOS 10上,如果没有任何一个<video>的源不被支持,则可以提供Gif来进行播放。

如果您的页面设计需要不同的行为,允许内联播放,而在需要全屏播放时,请使用-webkit-video-playable-inline媒体查询来区分两者:

<div id="either-gif-or-video">
  <video src="image.mp4" autoplay loop muted playsinline></video>
  <img src="image.gif">
</div>
#either-gif-or-video video { display: none; }
@media (-webkit-video-playable-inline) {
    #either-gif-or-video img { display: none; }
    #either-gif-or-video video { display: initial; }
}

这些新政策意味着元素的更高级的使用成为可能,例如画使用 <canvas> 绘制 <video>

  var video;
  var canvas;

  function startPlayback()
  {
    if (!video) {
      video = document.createElement('video');
      video.src = 'image.mp4';
      video.loop = true;
      video.addEventListener('playing', paintVideo);
    }
    video.play();
  }

  function paintVideo()
  {
    if (!canvas) {
      canvas = document.createElement('canvas');
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
      document.body.appendChild(canvas);
    }
    canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
    if (!video.paused)
      requestAnimationFrame(paintVideo);
  }
<button onclick="startPlayback()">Start Playback</button> 

相同的方法可以用于渲染为WebGL。注意,在这个例子中,用户的手势(click事件)是必需的,因为<video> 元素不在DOM中,因此不可见。一个<video style="display:none"> 或者<video style="visibility:hidden"> 也是一样。

这些新政策真的使视频成为提升我们网站的交互和设计,而不会对降低用户的宽带和电量消。有关更多信息,请联系 @jernoble ,AppleWorks Web技术传播者Jonathan Davis,@ jonathandavis 或者 @WebKit