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 有两个常用的回退选项。
- 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>
在 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 有两个常用的回退选项。
- 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>