Développeur web

Vidéo HTML5 : l'API Javascript

L'intégration de contenu audio et vidéo sans recourir à un plugin (Flash ou Silverlight) est sans doute l'innovation la plus commentée du standard HTML5. La promesse était séduisante, avec une syntaxe simple, inspirée de la balise <img>, mais l'implémentation est bien plus complexe qu'elle ne le semble au premier abord pour l'intégrateur HTML5, en particulier lorsque le site nécessite des manipulations de la vidéo en Javascript.

Les bases : markup, encodage et compatibilité

L'élément <video> rappelle <img>, dont elle reprend quelques attributs tout en en introduisant de nouveaux :


// version basique
<video src="mavideo.mp4"></video>

// version avec divers attributs
<video src="mavideo.mp4" width="640" height="480" autostart="true" controls="false" loop="true"></video>
	

La plupart des attributs ont des valeurs booléennes, qui peuvent donc être ignorées : inclure uniquement l'attribut sans valeur suffit à le définir comme true.

Si l'élément <video> est très bien supportée par tous les navigateurs modernes (9+ pour IE), le support des vidéos elles-mêmes est assez variable. Pour diverses raisons commerciales et idéologiques, les principaux browser vendors ont poussé des formats d'encodage différents pour leur navigateur :

Par conséquent, toute vidéo doit être encodée et proposée dans ces trois formats afin de maximiser la compatibilité. La notation est heureusement adaptée :


<video autostart loop controls>
  <source src="mavideo.mp4" type="video/mp4">
  <source src="mavideo.webem" type="video/webm">
  <source src="mavideo.ogg" type="video/ogg">
  votre navigateur ne supporte pas la vidéo. //fallback
</video>

La présence du MIME-Type est facultative (elle pose problème sous Androïd). La vidéo H264 est généralement placée en premier à cause d'un bug d'IOS. Paradoxalement, cela signifie que Chrome lira la plupart du temps le format vidéo que Google ne soutient pas, le support du H264 n'ayant pas été supprimé de Chrome (ça ne saurait tarder.)

Cette notation n'est toutefois toujours pas suffisante pour assurer un parfait support de la vidéo : les trois formats ci-dessus ont en effet des types récents, que les serveurs ne connaissent généralement pas. Il faut donc stipuler les types MIME dans leur configuration, par exemple sous Apache via un fichier .htaccess :


AddType video/ogg    ogv
AddType video/mp4    mp4 m4v
AddType video/webm   webm

En théorie, une balise bien formée et cette configuration du serveur suffisent. En pratique, il y a un piège peu documenté : Safari est extrêmement difficile dans son support du H264. Curieusement, malgré le soutien affirmé d'Apple au HTML5, Safari ne supporte pas la vidéo nativement mais fait appel à Quicktime pour la lecture (qui doit donc être installé, ce qui est le cas par défaut sous Mac, pas sous Windows.) D'après mes observations, les paramètres d'encodage H264 ont une influence majeure sur les performances de lecture sous Safari. Une lourde vidéo encodée avec les mauvais paramètres risque de bloquer le Javascript du site pendant plusieurs secondes. Hanbrake propose des presets parfaitement compatibles. Androïd présente les mêmes problèmes d'encodage.

Dernier point sur le support : iOS et Androïd sont tout à fait capables de lire la vidéo, mais avec plusieurs caveat :

Ces limites sont justifiées par la puissance et l'autonomie limitées des appareils mobiles ainsi que par les débits 3G. La situation devrait toutefois se normaliser, les smartphones rattrapant petit à petit les PC en terme de puissance et les connexions mobiles s'améliorant lentement.

Cette implémentation en HTML est pratique, mais extrèmement limitée par rapport aux possibilités offertes par un plugin. L'API Javascript est donc indispensable pour toute intégration un peu complexe.

L'API Vidéo

Cette API est assez complète bien qu'elle reste assez limitée par rapport aux possibilités du Flash. Elle gère toutefois la plupart des opérations indispensables.

Du fait des problèmes de codecs évoqués ci-dessus, Modernizr est un outil précieux pour travailler la vidéo en Javascript. Il permet en effet de se libérer des problèmes de compatibilité et de se concentrer sur les fonctionnalités. Plus que la simple detection du support de la balise <video>, Modernizr permet aussi de detecter le support du codec de manière assez robuste. Les exemples suivant l'utilisent donc par défaut.

Création de vidéo

Créer un élément vidéo se fait de la même manière que n'importe quel autre élément. Modernizr facilite la gestion de la propriété src.


if (Modernizr.video) {
  var maVideo = document.createElement('video');	

  if (Modernizr.video.h264) 
    maVideo.src = "mavideo.m4v";
  else if (Modernizr.video.webm) 
    maVideo.src = "mavideo.webem";
  else (Modernizr.video.ogg) 
    maVideo.src = "mavideo.ogg";

  maVideo.loop = true;
  maVideo.control = false;
  maVideo.width = 640;
  maVideo.height = 480;

  document.body.appendChild(maVideo);
} else {
  // fallback, texte, image ou flash
}

Le code est transparent : un premier test sur le support de l'élément suivi de sa création, de la définition de ses propriétés et de son intégration dans la page. Pour plus de clarté, j'ai ici défini le src via une structure if/else classique alors qu'une structure ternaire serait plus efficiente.

Attention : Modernizr.video.codec n'est pas un booléen : il retourne "probably", "maybe" ou empty.

Dans cet exemple, les contrôles sont masqués et la vidéo ne démarre pas automatiquement. Il convient donc de suppléer un lecteur maison.

Lecture et contrôles

Paragraphe très rapide, car fonctions très simples :


maVideo.play(); // lecture
maVideo.pause(); // pause
maVideo.paused; // si la vidéo est en pause (bool)
maVideo.ended; // si la vidéo est terminée (bool)

maVideo.volume; // le volume, entre 0.0 et 1.0

maVideo.duration; // la durée de la vidéo (si connue)
maVideo.currentTime; // l'avancement de la lecture (en secondes)

maVideo.seekable // la vidéo peut être parcourue (bool)
maVideo.seekable.start();  // le début de la partie parcourable
maVideo.seekable.end(); // la fin de la partie parcourable

Ces quelques propriétés sont assez transparentes dans leur utilisation, en particulier la lecture et la pause, très simple à implémenter. J'utilise ici JQuery afin de rappeler que les propriétés ci-dessus ne peuvent pas être accédées directement via un objet JQuery, ce dernier ne supportant pas encore vraiment la vidéo :


$('.playpause').on("click",function()
{
  var maVideo = $('video').get(0); // on accède directement au DOM
  if (maVideo.paused || maVideo.ended)
    maVideo.play();
  else
    maVideo.pause(); 
});

La création d'une seekbar dépend de très nombreux paramètres : l'apparence voulue, les fonctionnalités, sa précision, le support du tactile, la nature des vidéos... par conséquent je ne la traiterai pas en détails, ça sera peut-être l'occasion d'un prochain article.

Rapidement, une seekbar dépend de l'utilisation de seekable, duration et currentTime. Elle peut-être intégrée via un <div> ou un <input type="range">. De nombreux plugins proposent des lecteurs assez complets. Video.js est robuste et complet.

Qui dit vidéo dit une taille de fichier conséquente, plusieurs Mo au minimum à injecter dans une page. Pour assurer le confort de l'utilisateur, il faut donc fignoler le préchargement.

Buffering des vidéos

Lorsque la vidéo est crée, elle est chargée par le navigateur. Ce dernier peut débuter la lecture presque immédiatement, la vidéo n'a pas à être complète. Une lecture trop rapide alors que la vidéo n'est pas assez chargée entraine cependant des blocages. Divers event et méthodes renseignent sur l'état de ce chargement et permettent d'agir plus précisément. Les deux qui nous intéressent ici sont :


readyState // l'état actuel de préparation de la vidéo.
canplaythrough // event : la vidéo peut-être lue sans interruption

La démarche est simple, à partir de la création et de l'injection de la vidéo évoquée plus haut :


maVideo.play();

if (maVideo.readyState !== 4) {

  maVideo.addEventListener('canplaythrough', videoPrete, false);
  maVideo.addEventListener('load', videoPrete, false); 
  setTimeout(function(){ maVideo.pause(); }, 1);
	
} else {
  maVideo.play();
}

function videoPrete() {

  maVideo.removeEventListener('canplaythrough', videoPrete, false);
  maVideo.removeEventListener('load', videoPrete, false);
  setTimeout(function(){ maVideo.play(); }, 10);
}

Si readyState est égal à 4, la vidéo est suffisamment chargée pour être lancée. Sinon, il faut attendre un peu.

Ce code est plus complexe qu'il ne devrait l'être en théorie : la présence de plusieurs play/pause, de setTimeout ou de deux events écoutés permet de répondre à l'ensemble des bugs que j'ai pu rencontrés sur tel ou tel navigateur.

Dernier point : iOS refuse de lancer une vidéo sans intervention de l'internaute. Un bug permettait de passer outre, mais ce n'est plus le cas depuis deux versions. Tant que l'utilisateur n'a pas interagi avec sa page, aucune opération conduisant à la lecture automatique de la vidéo ne peut être débutée. La méthode s'en approchant le plus consiste à associer le lancement de la vidéo à n'importe quelle action.


if (navigator.userAgent.match(/iPad/i) != null)
  $(document).one("touchstart",function() { videoLauch(); });
else
  videoLauch(); 

Ici avec l'excellent gestionnaire d'event de JQuery : un peu d'UA sniffing est nécéssaire pour n'inclure que l'appareil désiré (ici juste l'iPad.) Tout geste tactile sur l'écran lance le script préchargeant la vidéo (le script ci-dessus par exemple.)

Conclusion

Cet article ne fait qu'éffleurer les possibilités de la vidéo en HTML5, ainsi par exemple il ne traite pas :

La jeunesse (relative) du standard rend donc pour le moment les utilisations complexes de vidéo assez difficiles à mettre en place par rapport à ce que permet le Flash (ou une intégration via Youtube.) La situation devrait cependant s'améliorer très vite, et dans tous les cas se contenter d'une lecture via un plugin sera de moins en moins pertinent entre l'arrivée prochaine de Windows 8 et l'omniprésence du mobile. L'intégration de fonctionnalités basiques comme ici décrites est par contre plutôt simple, hormis la nécéssité d'encoder chaque vidéo dans plusieurs formats.

retour