HTML5 所有类型均不受支持时的视频回退

HTML5 video fallback when all types unsupported

在 HTML5 规范中,它建议您将回退 material 放在 <video> 标签中,以供不支持它的旧浏览器使用。

<video width="400" controls>
    <source src="mov_bbb.mp4" type="video/mp4">
    <source src="mov_bbb.ogg" type="video/ogg">
    Your browser does not support HTML5 video.
</video>

但是,当所有源类型均不受支持时,我无法找到任何后备方案。例如,我的 Chromium 浏览器无法播放 video/mp4,但可以播放 video/ogg。所以我希望这会呈现后备文本。

<video width="400" controls>
    <source src="mov_bbb.mp4" type="video/mp4">
    Your browser does not support HTML5 video.
</video>

相反,我得到了一个没有任何内容的视频播放器,因为它无法加载 mp4 文件。

当没有可用的视频源时,有没有办法在 HTML 5 视频中回退?我知道我尝试的回退仅适用于旧浏览器,但我仍然需要没有可用源的回退。

@Jaw.sh 有两个常用的回退选项。

  1. Fallback to Flash version of the video.

今天的浏览器(Opera 我不确定也不关心)都可以播放 MP4 H.264。所以你不必太担心不兼容,除非你的大多数观众都住在中国。

实际上,当您尝试在 <source> 元素中加载不受支持的媒体类型时,将触发 error 事件。
然后您可以收听这些事件,如果识别出 none 个来源,则触发回退:

var sources = document.querySelectorAll('source');
var source_errors = 0;
for (var i = 0; i < sources.length; i++) {
  sources[i].addEventListener('error', function(e) {
    if (++source_errors >= sources.length)
      fallBack();
  });
}

function fallBack() {
  document.body.removeChild(document.querySelector('video'));
  document.body.appendChild(document.createTextNode('No video with supported media and MIME type found'));
}
<video controls>
  <source src="foo.bar" type="video/foo" />
  <source src="bar.foo" type="video/bar" />
</video>

这里没有 HTML 行为,因此我们必须使用 JavaScript 添加我们自己的行为。

(function() {
  "use strict";

  function insertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  }

  function setVideoFallback(lazyArea) {
    var lowData = false;
    if ("connection" in navigator) {
      lowData = navigator.connection.saveData === true ||
        navigator.connection.effectiveType === "slow-2g" ||
        navigator.connection.effectiveType === "2g";
    }
    //DocumentFragments don't support getElementsByTagName
    //oldIE doesn't support querySelectorAll
    var lazyVideos = lazyArea.querySelectorAll ?
      lazyArea.querySelectorAll("video") :
      lazyArea.getElementsByTagName("video");
    for (var i = lazyVideos.length; i--;) {
      var lazyVideo = lazyVideos[i];
      var cantPlay = true;
      if (lazyVideo.canPlayType) {
        //Loop through the various source elements, and check if
        //the browser thinks it can play them
        //This works better if we specify the codec along with
        //the MIME type
        var sources = lazyVideo.getElementsByTagName("source");
        for (var i2 = sources.length; i2--;) {
          if (lazyVideo.canPlayType(sources[i2].type)) {
            cantPlay = false;
            break;
          }
        }
      }
      //If on a low-data connection, remove the autoplay attribute
      //(it's only polite)
      if (lowData) {
        lazyVideo.removeAttribute("autoplay");
        lazyVideo.setAttribute("controls", "");
      }
      //If you can't play any of the available formats, skip straight to fallback content
      if (cantPlay) {
        //Extract the fallback and replace the video with it
        var children = lazyVideo.childNodes;
        for (var i3 = children.length; i3--;) {
          var childNode = children[i3];
          if (childNode.tagName !== "TRACK" && childNode.tagName !== "SOURCE") {
            insertAfter(childNode, lazyVideo);
          }
        }
        lazyVideo.parentNode.removeChild(lazyVideo);
      }
    }
  }
  /**
   * Retrieve the elements from the 'lazy load' noscript tags and prepare them for display
   */
  function setUp() {
    //Get all the noscript tags on the page
    var lazyLoadAreas = document.getElementsByTagName("noscript");
    var supportsTemplates = typeof HTMLTemplateElement === "function";
    for (var i = lazyLoadAreas.length; i--;) {
      var noScriptTag = lazyLoadAreas[i];
      //only process the ones marked for lazy loading
      if (!noScriptTag.hasAttribute("data-lazy-load")) continue;
      // The contents of a noscript tag are treated as text to JavaScript
      var lazyAreaHtml = noScriptTag.textContent || noScriptTag.innerHTML;
      // So we stick them in the innerHTML of a new div tag to 'load' them
      var lazyArea;
      if (supportsTemplates) {
        //(if possible, templates are better as they won't start any network calls)
        var lazyTemplate = document.createElement("template");
        lazyTemplate.innerHTML = lazyAreaHtml;
        lazyArea = lazyTemplate.content;
      } else {
        lazyArea = document.createElement("div");
        lazyArea.innerHTML = lazyAreaHtml;
      }
      setVideoFallback(lazyArea);
      noScriptTag.parentNode.replaceChild(lazyArea, noScriptTag);
    }
  }
  //If the page has loaded already, run setup - if it hasn't, run as soon as it has.
  if (document.readyState !== "loading") {
    setUp();
  } else {
    document.addEventListener("DOMContentLoaded", setUp);
  }
})();
<main>
  <figure>
    <!--[if !IE]><!-->
    <noscript data-lazy-load>
   <video height="338" width="600" autoplay loop muted>
    <!--<source src="./Sample.mp4" type="video/mp4; codecs=avc1.42E01E,mp4a.40.2">-->
    <source src="http://dl3.webmfiles.org/big-buck-bunny_trailer.webm" type="video/webm; codecs=vp8,vorbis">
    <source src="https://upload.wikimedia.org/wikipedia/commons/0/07/Backgammon_example.ogv" type="video/ogg; codecs=theora,vorbis">
    <!--<![endif]-->
    <img src="https://media2.giphy.com/media/BfbUe877N4xsUhpcPc/giphy.gif?cid=790b76115cadcffa59306b73776453f3" height="360" width="480"/>
    <!--[if !IE]><!-->
   </video>
  </noscript>
    <!--<![endif]-->
    <figcaption>
      A bunny emerging from his den and stretching.
      <!--[if !IE]><!-->
      <noscript aria-hidden="true"><p>
        Note: Without JavaScript, the above animation might not play. In that case, the animation can be directly accessed
     <a href="./giphy.gif">here</a>.
      </p></noscript>
      <!--<![endif]-->
    </figcaption>
  </figure>
</main>

使用 canPlayType 函数,我们询问浏览器是否认为它可以播放任何源类型。如果没有,我们将提取后备内容。

我们将视频封装在 noscript 标签中,这样在我们 运行 脚本之前它不会开始加载(除非禁用脚本,这是所需的行为)。

我们也使用IE条件标签,因为老IE无法用脚本读取noscript标签的内容。

(使用 Edge、Firefox、Chrome 以及 IE 的所有兼容模式进行了测试。Webm 显示在所有浏览器栏 IE 中,它在所有兼容模式下显示 GIF。)

the specs 一种后备方法中提到了它。

“监听最后一个源元素的错误事件并触发回退行为”

<div>
 <video width="400" controls>
  <source src="mov_bbb.mp4" type="video/mp4">
  <source src="mov_bbb.ogg" type="video/ogg"
         onerror="parentNode.parentElement.innerText = 'Your browser does not support the video codec' ">
 </video>
</div>