Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML5 video fallback when all types unsupported

In the HTML5 spec, it suggests you put fallback material in the <video> tag for older browsers that do not support it.

<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>

However, I cannot find anything for fallbacks when all source types are unsupported. For instance, my Chromium browser cannot play video/mp4, but it can play video/ogg. So I would expect this to render the fallback text.

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

Instead, I just get a video player with nothing in it because it can't load the mp4 file.

Is there a way to have a fallback in HTML 5 video when there is no usable video source? I am aware that the fallback I was attempting is only for old browsers, but I still need a fallback for no available source.

like image 596
Josh Avatar asked Sep 07 '15 02:09

Josh


People also ask

What video format does HTML5 video element support?

The HTML5 video format capabilities include three options to play: MP4, WebM, and Ogg.

Why is my video not working in HTML5?

If your browser error "HTML5 video file not found", it means that your browser is not up to date or website pages does not have a suitable video codec. It would help if you communicated with the developer to solve the issue and install all the required codecs.

Which video formats are not supported in HTML?

A video exists in different formats such as MP4, MPEG, WebM, Ogg, AVI, QuickTime, etc. But HTML supports only 3 types of video formats, which include MP4, Ogg, and WebM.

What is fallback in HTML5?

Definition of "Fallback Content" in HTML5 Editor's Draft Embedded content elements can have fallback content: content that is to be used when the external resource cannot be used (e.g. because it is of an unsupported format). The element definitions state what the fallback is, if any.


2 Answers

Actually, when you try to load unsupported media types in <source> element, an error event will fire.
You could then listen to these events, and if none of the sources is recognized, trigger the fallback :

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>
like image 171
Kaiido Avatar answered Oct 05 '22 23:10

Kaiido


There's no HTML behaviour for this, so we'll have to add our own behaviour with 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>

Using the canPlayType function, we ask the browser if it thinks it can play any of the source types. If it doesn't, we pull out the fallback content.

We encase the video in noscript tags so that it won't start loading until we've run the script (unless scripting is disabled, which is the desired behaviour).

We also use IE conditional tags, because oldIE can't read the contents of noscript tags with script.

(Tested with Edge, Firefox, Chrome, and every compatibility mode IE has. The Webm shows in all browers bar IE, which shows the GIF in every compatibility mode.)

like image 31
Sora2455 Avatar answered Oct 06 '22 00:10

Sora2455