Solución:
<video autoplay loop autobuffer muted playsinline>
<source src="https://foroayuda.es/video/video-hat.mp4" type="video/mp4">
</video>
El problema es que Google quiere que los usuarios inicien por sí mismos cualquier medio, por lo que si depura el navegador Chrome de su dispositivo, recibirá la advertencia “Error al ejecutar ‘play’ en ‘HTMLMediaElement’: la API solo puede iniciarse mediante un gesto del usuario.“Eso significa que debe adjuntar la inicialización del video, por ejemplo, con un evento de clic
No parece haber una gran información sobre esto, así que pensé en publicar mis hallazgos.
He estado depurando la reproducción de video html5 en Chrome para escritorio y móvil en un Android 5.0.1 Samsung S4 con Chrome 61 y el navegador integrado, y Safari 9 y 11, usando una reproducción / pausa automática de javascript escrita en AngularJS (abajo). El video está incrustado en un carrusel, por lo que a veces es visible, a veces no. En resumen:
- Recomendaría tener formatos webm (vp8 / vorbis) y mp4 (h264 / aac). Estos son los formatos más compatibles y tienen una calidad equivalente para la misma tasa de bits. ffmpeg puede codificar ambos.
- Parece que Chrome Mobile prefiere webm si puede conseguirlo, así que pon eso primero.
- Si un navegador reproduce un archivo cuando lo dirige a la URL del archivo, este no significa que lo reproducirá cuando esté incrustado en una etiqueta de video, aunque le dirá si el formato y los códecs son compatibles si se reproduce. Chrome Mobile parece muy quisquilloso a la hora de tener una fuente de video cuya resolución sea demasiado alta.
- Safari (y probablemente iOS) no reproducir un video a menos que lo proporcione un servidor que admita rangos de bytes. Apache, nginx y Amazon S3, por ejemplo, los admiten, pero muchos servidores web más pequeños (como los servidores WSGI) no los admiten.
- El orden de los videos importa más que la fuente.
media
atributo. Siempre tenga primero versiones de baja resolución de un video. El siguiente ejemplo utiliza 1920×1080 y 1280×720. Parece que si el navegador móvil encuentra un video que es de “demasiado alta resolución”, simplemente deja de procesar las otras fuentes y prefiere el póster. - teniendo un
controls
El atributo y el juego manual frente a jugar a través de JavaScript no parecen hacer ninguna diferencia. - los
muted
El atributo evita que Android coloque un pequeño icono de altavoz en la barra de estado cuando se reproduce, pero fuera de la pantalla, incluso cuando el video no tiene audio. Como nota al margen, también pensaría realmente en su audiencia si tiene la intención de reproducir automáticamente el video con sonido. Personalmente creo que es una mala idea. - los
preload
atributo no parece hacer mucha diferencia. El navegador tenderá a precargar automáticamente los metadatos de video seleccionados de todos modos. - tener una fuente
type
El atributo no detiene la reproducción del video. En todo caso, ayuda al navegador a elegir qué fuente elegir para mejor - el JS
video.oncanplay
evento es la mejor manera de ver si la etiqueta de video se ha realizado correctamente. Si no lo obtiene, el video no se reproducirá, pero el navegador no le dirá por qué.
HTML:
<video class="img-responsive-upscale ng-scope"
video-auto-ctrl loop muted preload poster="0022.png">
<source src="https://foroayuda.es/vid_small.webm" media="(max-width: 1280px)" type="video/webm">
<source src="vid_small.mp4" media="(max-width: 1280px)" type="video/mp4">
<source src="vid.webm" media="(max-width: 1920px)" type="video/webm">
<source src="vid.mp4" type="video/mp4">
<img src="0022.png" alt="something"
title="Your browser does not support the <video> tag">
</video>
Javascript:
<script type="text/javascript">
angular.module('myproducts.videoplay', []).directive('videoAutoCtrl',
function() {
return {
require: '^uibCarousel',
link: function(scope, element, attrs) {
var video = element[0];
var canplay = false;
var rs = ["HAVE_NOTHING", "HAVE_METADATA", "HAVE_CURRENT_DATA", "HAVE_FUTURE_DATA", "HAVE_ENOUGH_DATA"];
var ns = ["NETWORK_EMPTY", "NETWORK_IDLE", "NETWORK_LOADING", "NETWORK_NO_SOURCE"];
function vinfo() {
console.log("currentSrc = "https://foroayuda.es/+ video.currentSrc);
console.log("readyState = " + rs[video.readyState]);
console.log("networkState = " + ns[video.networkState]);
bufinfo();
}
function bufinfo() {
// tr is a TimeRanges object
tr = video.buffered
if (tr.length > 0) {
var ranges = ""
for (i = 0; i < tr.length; i++) {
s = tr.start(i);
e = tr.end(i);
ranges += s + '-' + e;
if (i + 1 < tr.length) {
ranges += ', '
}
}
console.log("buffered time ranges: " + ranges);
}
}
video.onerror = function () {
console.log(video.error);
}
video.oncanplay = function () {
canplay = true;
if (!playing) {
console.log("canplay!");
vinfo();
}
}
var playing = false;
function playfulfilled(v) {
console.log("visible so playing " + video.currentSrc.split("https://foroayuda.es/").pop());
playing = true;
}
function playrejected(v) {
console.log("play failed", v);
}
function setstate(visible) {
if (canplay) {
if (visible) {
p = video.play();
if (p !== undefined) {
p.then(playfulfilled, playrejected);
}
} else if (playing) {
video.pause();
console.log("invisible so paused");
playing = false;
}
} else {
console.log("!canplay, visible:", visible);
vinfo();
}
}
// Because $watch calls $parse on the 1st arg, the property doesn't need to exist on first load
scope.$parent.$watch('active', setstate);
}
};
});
</script>