Categorías: Labs

Agregar leyendas y subtítulos a videos HTML5

Ésta es una traducción del artículo original en el blog de Mozilla Hacks, también disponible en MDN. Traducción por Carlos Chocobar.

Con la introducción de los elementos <video> y <audio> a HTML5, finalmente obtuvimos una manera nativa para agregar vídeo y audio a nuestros sitios web. También tenemos APIs JavaScript que nos permite interactuar con este contenido multimedia en diferentes formas, ya sea escribiendo nuestros propios controles o simplemente observando qué tan largo es un archivo de video. Como desarrolladores web responsables, también debemos estar constantemente pensando en hacer nuestro contenido más accesible, y esto no se detiene solo con el contenido de video y audio. Además, es un paso importante hacer nuestro contenido accesible para todos y la inclusión puede ser primordial, ya sea porque alguien tiene problemas de audición o que no entiende el idioma en el que está el contenido.

Afortunadamente, HTML5 también nos proporciona una forma nativa de hacer nuestro contenido multimedia más accesible agregando subtítulos y leyendas mediante el elemento <track>. La mayoría de los navegadores tienen soporte nativo a diferentes niveles, lo cual se muestra en la primera parte de este artículo, pero también proporciona una API JavaScript, por la cual podemos acceder y usar pistas de textos (ej. subtítulos) que estén disponibles. Este artículo también muestra cómo esta API puede utilizarse para detectar qué leyendas o subtítulos han sido agregados a un video HTML5, y cómo esa información puede utilizarse para crear un menú de pistas de texto disponibles y en última instancia proporcionar una interfaz más consistente a través de diferentes navegadores.

En artículos de MDN hemos examinado cómo construir un reproductor de video con soporte multi-navegador, utilizando las APIs HTMLMediaElement y window.fullScreen, y también cómo dar estilo al reproductor. Este artículo tomará el mismo reproductor y mostrará cómo agregar leyendas y subtítulos, utilizando Web_Video_Text_Tracks_Format y el elemento <track>.

Ejemplo de vídeo con leyenda

En este artículo nos referiremos al ejemplo del reproductor de vídeo con subtítulos. Este ejemplo utiliza un fragmento de la película abierta Sintel, creada por Blender Foundation (Fundación Blender).

Reproductor de vídeo con leyendas

Nota: Puedes encontrar la fuente en Github y también ver el ejemplo en vivo.

HTML5 y leyendas de vídeo

Antes de profundizar cómo agregar leyendas al reproductor de video, existe una serie de cosas que mencionaremos primero de las que debemos estar conscientes antes de empezar.

Leyendas vs. subtítulos

Las leyendas y los subtítulos no son lo mismo: ambos tienen significativamente diferentes audiencias y transmiten diferente información y recomendamos que investigues acerca de las diferencias si no estás seguro de lo que son. Sin embargo, técnicamente se implementan de la misma manera, por eso el material en este artículo aplica para ambos.

Para este artículo, haremos referencia a las pistas de textos visualizadas como leyendas, dado que su contenido es más para personas sin problemas de audición pero con problemas para entender el idioma de una película, y no tanto para personas sordas o con problemas de audición.

El elemento <track>

HTML5 nos permite especificar las leyendas para un video utilizando Web Video Text Tracks (WebVTT) (Pistas de Texto de Vídeos Web). Todavía se está trabajando en la especificación WebVTT, pero las partes principales son estables así que las podemos utilizar hoy en día.

Proveedores de vídeos (como Blender Foundation) proveen leyendas y subtítulos en formato de texto con sus vídeos, pero generalmente están en el formato SubRip (SRT). Estos pueden ser convertidos fácilmente a WebVTT utilizando un convertidor online como el srt2vtt.

Modificaciones al HTML y CSS

En esta sección se resumen las modificaciones realizadas al código del artículo anterior para facilitar la incorporación de subtítulos al video. Si no estás interesado en esto y sólo quieres llegar directamente a JavaScript y a lo más relevante de CSS, ve a la sección Implementación de leyenda.

En este ejemplo estamos utilizando un vídeo diferente, Sintel, ya que tiene conversaciones y por lo tanto es mejor para ilustrar cómo funcionan las leyendas.

Código HTML

Como lo mencionado anteriormente, necesitamos dar uso al nuevo elemento HTML5 <track> para agregar nuestros archivos de leyendas al video de HTML5. En efecto tenemos nuestras leyendas en tres diferentes idiomas – inglés, alemán y español – es por eso que los mencionaremos a los tres de los archivos relevantes VTT agregándolos dentro de los elementos <track> de nuestro elemento <video> HTML5:

<video id="video" controls preload="metadata">
   <source src="video/sintel-short.mp4" type="video/mp4"/>
   <source src="video/sintel-short.webm" type="video/webm"/>
   <track label="English" kind="captions" srclang="en" src="captions/vtt/sintel-en.vtt" default></track>
   <track label="Deutsch" kind="captions" srclang="de" src="captions/vtt/sintel-de.vtt"></track>
   <track label="Español" kind="captions" srclang="es" src="captions/vtt/sintel-es.vtt"></track>
</video>

Como puedes observar, cada elemento <track> tiene establecido los siguientes atributos:

  • kind (tipo), con un valor captions (leyendas), lo que indica el tipo de contenido de los archivos que contiene.
  • label (etiqueta), con un valor indicando el tipo de idioma para la que esa leyenda es establecida, por ejemplo inglés o alemán. Estas etiquetas aparecerán en la interfaz de usuario permitiéndole seleccionar con facilidad qué idioma de leyenda desea ver.
  • A src se le asigna una URL válida apuntando al archivo de leyenda WebVTT relevante en cada caso.
  • srclang indica en qué idioma está el contenido del archivo de leyenda.
  • El atributo default (predeterminado) está en el <track> de inglés, lo que indica al navegador que esta es la leyenda que se debe utilizar cuando las leyendas fueron activadas y el usuario no ha hecho una selección específica.

Además de agregar elementos <track>, también hemos agregado un nuevo botón para controlar el menú de leyenda que vamos a construir. En consecuencia ahora los controladores de video se ven así:

<div id="video-controls" class="controls" data-state="hidden">
   <button id="playpause" type="button" data-state="play">Play/Pause</button>
   <button id="stop" type="button" data-state="stop">Stop</button>
   <div class="progress">
      <progress id="progress" value="0" min="0">
         <span id="progress-bar"></span>
      </progress>
   </div>
   <button id="mute" type="button" data-state="mute">Mute/Unmute</button>
   <button id="volinc" type="button" data-state="volup">Vol+</button>
   <button id="voldec" type="button" data-state="voldown">Vol-</button>
   <button id="fs" type="button" data-state="go-fullscreen">Fullscreen</button>
   <button id="captions" type="button" data-state="captions">CC</button>
</div>

Cambios en CSS

Los controles de videos experimentaron algunos cambios menores para dar espacio al botón extra, pero estos son relativamente sencillos.

No se utiliza ninguna imagen para el botón de leyendas, por lo que se diseña simplemente como:

.controls button[data-state="captions"] {
    height:85%;
    text-indent:0;
    font-size:16px;
    font-size:1rem;
    font-weight:bold;
    color:#666;
    background:#000;
    border-radius:2px;
}

También se hicieron otros cambios en CSS que son específicos para otra implementación JavaScript extra, pero estos se mencionarán más abajo en el lugar apropiado.

Implementación de leyenda

Mucho de lo que hacemos para acceder a las leyendas de video gira en torno a JavaScript. Al igual que en los controles de vídeo, si un buscador soporta leyendas de video HTML5 habrá un botón proporcionado dentro del control nativo configurado para acceder a ellas. Sin embargo, si ya hemos definido nuestros propios controles de vídeo, este botón estará oculto y necesitaremos definir uno propio.

Los navegadores varían en cuanto a soporte, por eso trataremos de proveer una interfaz de usuario más unificada para cada navegador en donde sea posible. Más adelante trataremos sobre los asuntos de compatibilidad de los navegadores.

Configuración inicial

Como en todos los otros botones, una de las primeras cosas que necesitamos hacer es almacenar una referencia para el botón de la leyenda.

var captions = document.getElementById('captions');

Al principio también desactivamos todas las leyendas, en caso de que el navegador active alguna de ellas por defecto:

for (var i = 0; i &lt; video.textTracks.length; i++) {
   video.textTracks[i].mode = 'hidden';
}

La propiedad video.textTracks contiene una lista de todas las pistas de texto agregadas al vídeo. Recorremos cada una y configuramos su mode (modo) como hidden (oculto).

Nota: El API WebVTT nos otorga acceso a todas las pistas de texto que se definen para un vídeo HTML5 utilizando el elemento <track>.

Crear un menú de leyenda

Nuestro objetivo es utilizar el botón captions (leyendas) que agregamos anteriormente para mostrar un menú que permita a los usuarios elegir en qué idioma quieren que se muestren las leyendas, o desactivarlas por completo.

Hemos agregado el botón, pero antes hacer algo con el mismo, necesitaremos crear el menú que va junto a él. Este menú se crea dinámicamente para que los idiomas puedan ser agregados o quitados más adelante simplemente editando los elementos <tracks> en los enlaces del vídeo.

Todo lo que necesitamos hacer es ir a través de los textTracks del vídeo, leer sus propiedades y crear el menú a partir de allí:

var captionsMenu;
if (video.textTracks) {
   var df = document.createDocumentFragment();
   var captionsMenu = df.appendChild(document.createElement('ul'));
   captionsMenu.className = 'captions-menu';
   captionsMenu.appendChild(createMenuItem('captions-off', '', 'Off'));
   for (var i = 0; i < video.textTracks.length; i++) {
      captionsMenu.appendChild(createMenuItem('captions-' + video.textTracks[i].language, video.textTracks[i].language,         video.textTracks[i].label));
   }
   videoContainer.appendChild(captionsMenu);
}

Este código crea un documentFragment, que se utiliza para mantener una lista desordenada que contiene nuestro menú de leyendas. Antes que nada, se agrega una opción para permitir al usuario desactivar todas las leyendas, y luego se agregan los botones para cada pista de texto, que presenta el idioma y la etiqueta de cada una.

La creación de cada elemento de la lista y del botón se realiza mediante la función createMenuItem(), que se define así:

var captionMenuButtons = [];
var createMenuItem = function(id, lang, label) {
   var listItem = document.createElement('li');
   var button = listItem.appendChild(document.createElement('button'));
   button.setAttribute('id', id);
   button.className = 'captions-button';
   if (lang.length > 0) button.setAttribute('lang', lang);
   button.value = label;
   button.setAttribute('data-state', 'inactive');
   button.appendChild(document.createTextNode(label));
   button.addEventListener('click', function(e) {
      // Desactivar todos los botones
      captionMenuButtons.map(function(v, i, a) {
         captionMenuButtons[i].setAttribute('data-state', 'inactive');
      });
      // Encontrar el idioma a activar
      var lang = this.getAttribute('lang');
      for (var i = 0; i < video.textTracks.length; i++) {
         // Para el boton 'captions-off', la primera condicion nunca aplica, asi que se desactivan todas las leyendas
         if (video.textTracks[i].language == lang) {
            video.textTracks[i].mode = 'showing';
            this.setAttribute('data-state', 'active');
         }
         else {
            video.textTracks[i].mode = 'hidden';
         }
      }
      captionsMenu.style.display = 'none';
   });
   captionMenuButtons.push(button);
   return listItem;
}

Esta función crea los elementos requeridos <li> y <button>, y los regresa para ser agregados a la lista de menú de leyendas. También configura los manejadores (listeners) de eventos requeridos sobre el botón para que la leyenda relevante se encienda o se apague. Esto se realiza simplemente configurando el atributo mode (modo) de la leyenda requerida a showing (proyectada), y configurando las demás a hidden (oculta).

Una vez que se crea el menú, se lo inserta en el DOM en la parte de abajo de videoContainer.

Al principio, el menú está oculto por defecto, por eso se necesita agregar un manejador de eventos a nuestro botón de leyendas para mostrarlo o esconderlo:

captions.addEventListener('click', function(e) {
   if (captionsMenu) {
      captionsMenu.style.display = (captionsMenu.style.display == 'block' ? 'none' : 'block');
   }
});

CSS del menú de leyendas

También agregamos algunos estilos rudimentarios para el menú de leyendas recién creado:

.captions-menu {
    display:none;
    position:absolute;
    bottom:14.8%;
    right:20px;
    background:#666;
    list-style-type:none;
    margin:0;
    padding:0;
    width:100px;
    padding:10px;
}
 
.captions-menu li {
    padding:0;
    text-align:center;
}
 
.captions-menu li button {
    border:none;
    background:#000;
    color:#fff;
    cursor:pointer;
    width:90%;
    padding:2px 5px;
    border-radius:2px;
}

Dar estilo a las leyendas desplegadas

Una de las características menos conocidas y apoyadas de WebVTT es la habilidad de dar estilo a las leyendas individuales (conocidas como entradas de texto) por medio de CSS Extensions (extensiones CSS).

El pseudo-elemento ::cue es la clave para la selección de entradas individuales de la pista de texto para darle estilo, ya que coincide con cualquier entrada definida. Existen pocas propiedades de CSS que se pueden aplicar a una entrada de texto:

  • color
  • opacity
  • visibility
  • text-decoration
  • text-shadow
  • background, propiedades abreviadas
  • outline, propiedades abreviadas
  • font, propiedades abreviadas, incluyendo line-height
  • white-space

Por ejemplo, para cambiar el color del texto de las entradas de la pista de texto puedes escribir:

::cue {
   color:#ccc;
}

Si el archivo WebVTT utiliza voice spans (etiquetas de voz), lo cual permite definir a las entradas como si tuvieran una “voz” particular:

0
00:00:00.000 --> 00:00:12.000
<v Test>[Test]</v>

Entonces esta “voz” específica tendrá estilos como:

::cue(v[voice='Test']) {
   color:#fff;
   background:#0095dd;
}

Nota: algunos de los estilos de las entradas con ::cue actualmente funcionan en Chrome, Opera, y Safari, pero no todavía en Firefox.

Compatibilidad del navegador

El soporte del navegador para WebVTT y el elemento <track> es bastante bueno, aunque algunos navegadores difieren un poco en sus implementaciones.

Internet Explorer

Desde Internet Explorer 10+ las leyendas están activadas por defecto, y los controles predeterminados contienen un botón y un menú que ofrece la misma funcionalidad que el menú que acabamos de crear. El atributo default (predeterminado) también es soportado.

Nota: IE ignorará completamente los archivos WebVTT a menos que le configures el tipo MIME. Esto puede hacerse fácilmente al agregar un archivo .htaccess a un directorio apropiado que contenga AddType text/vtt .vtt.

Safari

Safari 6.1+ tiene un soporte similar al de Internet Explorer 11, desplegando un menú con las diferentes opciones disponibles, con la incorporación de una opción “Auto”, lo que le permite al navegador elegir.

Chrome y Opera

Nuevamente, estos navegadores tienen implementaciones similares: las leyendas están activadas por defecto y el conjunto del control predeterminado contiene un botón “cc”  que enciende o apaga las leyendas. Chrome y Opera ignoran el atributo default en el elemento <track> y en su lugar intentarán utilizar el idioma del navegador para seleccionar el idioma de la leyenda.

Firefox

La implementación en Firefox fue desactivada temporalmente. Sin embargo, esto ha sido arreglado a partir de Firefox 31, y ya todo funciona como debería.

Plugins

Si luego de leer todo este artículo decides que no quieres molestarte en hacer todo esto y quieres que alguien más lo haga por ti, existen bastantes plugins en otras partes que ofrecen soporte para las leyendas y subtítulos que puedes usar.

  • playr – este pequeño plugin implementa subtítulos, leyendas y títulos de capítulos como así también formatos de archivos WebVTT y SRT.
  • jwplayer – este reproductor de vídeos es muy amplio y hace mucho más que soportar leyendas de videos. Soporta formatos de archivos WebVTT, SRT y DFXP.
  • >MediaElement.js – otro reproductor de vídeo completo que también soporta leyendas de vídeos, sin embargo solo en formato SRT.
  • LeanBack Player – otro reproductor de vídeo que soporta leyendas WebVTT y también provee otra funcionalidad del reproductor estándar
  • SublimeVideo – este reproductor también soporta leyendas a través de archivos WebVTT y SRT.
  • Video.js – soporta subtítulos de video WebVTT.

Nota: Puedes encontrar una excelente lista de reproductores de vídeo de HTML5 y su estado actual en HTML5 Video Player Comparison (comparación de reproductores de vídeo HTML5).

The following two tabs change content below.

jorgev

Add-ons Developer Relations Lead at Mozilla
Jorge trabaja para el equipo de complementos de Mozilla, y se dedica a Mozilla Hispano y Mozilla Costa Rica en su tiempo libre. Actualmente está encargado del blog de Mozilla Hispano Labs.