Разработка HTML5 плеера

This post is also available in: Английский

HTML5 JS Player

С каждым годом медиа технологии все больше http://tutbelgorod.ru/  и больше входят в жизнь каждого человека. Глобальная сеть также подвержена данной тенденции, о чем свидетельствуют  успех и популярность таких сервисов как YouTube. Но работа подобных систем невозможна без технологий, позволяющих реализовать воспроизведение видео контента для пользователя.

Классическим подходом для реализации проигрывателя видео является использование технологии Flash.  Пожалуй, сложно найти более распространенный плагин для веб-браузера, чем Flash Player. Однако стоит заметить, что слабым местом подобного подхода является необходимость установки Flash plugin’а. А для многих устройств он вообще отсутствует. К примеру, настолько популярные iPhone и  iPad не имеют возможности запускать Flash-приложения в браузере. Выходом из подобной ситуации является использование HTML5.

Данный современный стандарт языка HTML принес множество нововведений. Но ключевым для нас является возможность воспроизведения видео стандартными средствами веб-браузера. Для этого в спецификацию HTML5 введен тэг <video>. Все, что Вам нужно для простого воспроизведения видео файла – лишь использовать данный тэг, правильно задав значение его атрибутам:

  • Autoplay — при наличии данного атрибута со значением ‘autoplay’ воспроизведение начнется сразу после того, как загрузиться достаточны объем видео.
  • Controls – значение данного атрибута, равное ‘controls’ отобразит элементы управления воспроизведением.
  • Height – высота проигрывателя в пикселах.
  • Loop – данный атрибут, установленный в значение ‘loop’ заставит воспроизведение видео циклично повторяться по окончанию.
  • Muted – при значении ‘muted’ отключит звук у видео файла.
  • Poster – данный атрибут принимает url изображения, которое будет отображаться до начала воспроизведения контента.
  • Preload – атрибут определяет стратегию браузера по загрузке видео контента.  Может принимать различные значения:
    • Auto’ —  если загрузка видео должна начаться при загрузке страницы.
    • Metadata’ – если с загрузкой страницы должны загрузиться лишь метаданные контента.
    • None’ – если при загрузке странице не должна начинаться загрузка видео.
  • Src – данный атрибут содержит url файла с видео контентом.
  • Width — содержит значение высоты проигрывателя.

Подводные камни и первые трудности

К сожалению не все так просто. HTML5 – достаточно молодой стандарт. Так что первое, что стоит отметить  — ограниченную поддержку браузерами.  Использование тега <video> возможно в браузерах следующих версий:

  • IE 9+
  • Firefox 4.0+
  • Chrome 6+
  • Safari 5+
  • Opera 10.6+

Также стоит заметить,  что различные браузеры воспроизводят различные форматы видео, т.е. необходимо подготовить один и тот же контент в разных форматах для разных браузеров. Ниже в таблице указаны основные браузеры с поддержкой ими форматов:

Браузер Форматы
Ogg Theora H.264 VP8 (WebM)
Internet Explorer Требует установки расширения 9.0 Требует установки расширения
Mozilla Firefox 3.5 Нет 4.0
Google Chrome 3.0 Да 6.0
Safari Требует установки расширения 3.1 Требует установки расширения
Opera 10.50 Нет 10.60

Существует механизм для кроссбраузерного воспроизведения. Он заключается в добавлении внутрь <video> тэгов <source>, указывающих на одинаковый контент в разных форматах. В  таком случае браузер определит первый доступный для проигрывания видео файл и загрузит его в проигрыватель. Пример реализации ниже:

<video>
  <source src=”path_to_ogg_file” type=”video/ogg” />
  <source src=”path_to_mp4_file” type=”video/mp4” />
  <source src=”path_to_webm_file” type=”video/webm” />
</video>

Обратите внимание,  что в разных браузерах элементы управления воспроизведением будут выглядеть по-разному. Поэтому для унификации необходимо разрабатывать собственный интерфейс проигрывателя. Идея крайне проста: элементы управления представляют собой div’ы, спозиционированные поверх контейнера с видео.

Стоит заметить, что разные браузеры могут иметь различное поведение при работе с HTML5 video. Например на iOS устройствах невозможен автостарт воспроизведения.

Также отсутствует возможность каким-любо образом управлять размером видео буфера.  Определить размер проигрываемого файла также не выйдет, равно как и скорость загрузки.

Начало работы

В стандарте HTML5 у элементов мультимедиа существует мощный API, позволяющий не только разработать единый интерфейс проигрывателя, но и реализовать дополнительный функционал. В качестве инструмента для работы с HTML5 Media API выступает JavaScript.

Допустим  на странице существует следующий контейнер с видео:

<video id=”player” src=”some_path_to_video_file” width=640 height=480>
</video>

Для начала работы с проигрывателем через API необходимо определить объект, содержащий контейнер с медиа-содержимым. Для этот используем JQuery селектор:

var player = $(‘#player’)[0];

Все, теперь мы готовы управлять нашим плеером через JS!  Для начала воспроизведения файла и для паузы достаточно выполнить следующий код соответственно:

player.start();
player.pause();

HTML5 Media API позволяет получить информацию о проигрываемом видео. Например можно производить все манипуляции с проигрываемым контентом работая с полем ‘currentSrc’:

var currentSource = player.currentSrc; // получаем url проигрываемого
                                       // контента
player.currentSrc = ‘path_to_new_media_source’; // заменяем контент
                                                // в проигрывателе

Аналогично обстоит работа с функцией перемотки видео. Данный функционал реализовывается через поле ‘currentTime (принимает знaчение типа float, означает текущее время воспроизведения в секундах):

var currentTime = player.currentTime; // текущее значение времени
                                      // воспроизведения
player.currentTime = 60 // перемотка на позицию 1 минуты

Получить длительность проигрываемого файла можно обратившись к полю ‘duration’. А вместе со значением текущего времени проигрывания несложно вычислить процент просмотренного контента:

var duration = player.duration; // длительность контента в секундах
var proportion = currentTime / duration; // соотношение времени
                                         // воспроизведения к
                                         // длительности видео.

Существует возможность управления громкостью воспроизведения через поле ‘volume’:

player.volume = 1; // включаем полную громкость (для выключения
                   // звука полю нужно присвоить значение “0”)

На этом возможности HTML5 Media API не заканчиваются. Реализации сложных элементов управления может быть осуществлена через механизм событий. Для этого достаточно использовать функцию addEventListenter(). Например,  для подписки на событие окончания воспроизведения достаточно выполнить следующее:

var onEndFunc = function() { // функция, вызываемая при окончании
                             // воспроизведения
  // какие-то действия
}

player.addEventListener(‘ended’, onEndFunc); // подписываем функцию
                                             // на окончание видео
player.removeEventListener(‘ended’, onEndFunc); // отписываем функции
                                                // от окончания видео

Ниже представлены все события, существующие в HTML5 Media API.

  • onabort – событие аварийного завершения проигрывания
  • oncanplay – событие готовности к воспроизведению видео после загрузки достаточной части в буффер
  • oncanplaythrough – событие готовности к воспроизведению после полной загрузки контента в буффер
  • ondurationchange —  событие изменения длительности контента
  • onemptied – событие , вызываемое при разрыве соединения
  • onended – событие окончания воспроизведения
  • onerror —  событие ошибки при загрузке файла контента
  • onloadeddata – событие загрузки контента
  • onloadedmetadata – событие загрузки метаданных.
  • onloadstart – событие начала загрузки файла
  • onpause – событие остановки воспроизведения
  • onplay —  событие начала воспроизведения
  • onplaying – событие воспроизведения (будет выполняться, пока видео не остановится)
  • onprogress – событие процесса загрузки видео (будет выполняться, пока не будет загружен весь контент)
  • onratechange – событие изменения скорости воспроизведения
  • onreadystatechange – событие изменения состояния готовности видео проигрывателя
  • onseeked – событие окончания перемотки видео
  • onseeking – событие, вызываемое при перемотке контента
  • onstalled – собтие, вызываемое при невозможности браузером получить контент
  • onsuspend – событие, вызываемое при остановке загрузки контента.
  • ontimeupdate – событие, вызываемое при изменении текущего положения воспроизведения
  • onvolumechange – событие изменения громкости звука
  • onwaiting – событие остановки воспроизведения для буферизации данных

С помощью данных событий можно реализовывать такие элементы видео плеера, как плейлисты, отображение субтитров, показ целевого кадра при перемотке и многие другие.

Динамическое изменения качества видео

Как было сказано выше, в HTML5 отсутствует инструмент для работы с размером буфера, однако Media API позволяет узнать длительность буферизованной части контента, что необходимо  для отображения на полосе перемотки части, доступной к просмотру. Для этого достаточно выполнить следующий код:

var bufferedTime = player.buffered.end(0); // временная граница
                                           // буферизованной части

А теперь самое интересное! Зная длительность контента в буфере можно реализовать динамическое определение качества проигрываемого контента. Для этого нужно реализовать метод, который будет оценивать скорость загрузки информации в буфер и в зависимости ее значения переключать качество видео.

Идея анализа заключается в следующем: мы рассчитываем скорость нарастания буфера, и если она ниже, чем скорость воспроизведения, то проводим расчёт времени до полной буферизации контента и сравниваем его с оставшимся временем воспроизведения. Если время буферизации с рассчитанной скоростью больше отставшего времени проигрывания, то переключаем контент на более низкое  качество.

var  bufferAnalizer = function (playbackStartPoint, playbackEndPoint,
                       bufferStartPoint , bufferEndPoint, duration) {
  var oldQualityObj = this.qualityObj,
    playbackStart = 0, // new playback start point
    bufferStart = 0; // new buffer start point

    if (oldQualityObj) {
      playbackStart =  oldQualityObj.playbackEndPoint;
      bufferStart = oldQualityObj.bufferEndPoint;
    } else {
       playbackStart = playbackStartPoint;
       bufferStart = bufferStartPoint;
    }

    this.qualityObj = {
      'playbackEndPoint': playbackEndPoint,
      'bufferEndPoint': bufferEndPoint,
      'deltaBuffer': bufferEndPoint - bufferStart, // сколько забуферизовано
      'bufferSpeed':  (bufferEndPoint - bufferStart) /
                      (playbackEndPoint - playbackStart),
      'deltaPlayback': playbackEndPoint - playbackStart, // сколько было
                                                         //воспроизведено
      'availTime': bufferEndPoint - playbackEndPoint // разница между буффером и
                                                     // позицией воспроизведения
    }

    var restTime = duration - playbackEndPoint,
        bufferTime = (duration - bufferEndPoint) / this.qualityObj.bufferSpeed;

    if ((bufferTime > restTime) && ((this.qualityObj.availTime /
                                        this.qualityObj.deltaPlayback ) < 2)) {
       if (this.quality == 'normal') {
          this.quality = 'low';
        }
    }
}

Подключение субтитров

В стандарте HTML5 существует специальный тэг <track> для отображения субтитров, однако ни один популярный браузер на время написания статьи не поддерживает его. Тем не менее можно  реализовать данный функционал руками. Пусть субтитры находятся в файле ‘subs.srt’. Для начала подключим его и занесем содержимое в специальный объект:

var toSeconds = function(time) {
  var seconds = 0.0;
  if (time) {
    var p = time.split(':');
    for (var i = 0; i < p.length; i++)
      seconds = seconds * 60 + parseFloat(p[i].replace(',', '.'))
    }

  return seconds;
}

$.get(subsSrc, function (data) {
  data = data.replace(/\r\n\r\n/g, "").split('');

  for (var item in data) {
    var subItem = data[item].split(/\r\n/g);

    if (subItem.length > 3) {
      for (var i = 3; i < subItem.length; i++) {
        subItem[2] += '<br />' + subItem[i];
       }
    }

    var time = subItem[1].split(' --> ');

    self.subs.push({
      id:subItem[0],
      sTime:self.toSeconds(time[0]),
      eTime:self.toSeconds(time[1]),
      text:subItem[2]
    });
  }
}, 'html');

Теперь осталось создать таймер, который будет в зависимости от текущего времени отображать нужные субтитры в div’е с классом ‘.subs’:

var self = this,
     lastSub = 'empty',
     currentSub;

this.subsTimer = setInterval(function () {
  for (var item in self.subs) {
    var currentTime = self.player.currentTime;

    if ((self.subs[item].eTime > currentTime) &&
       (self.subs[item].sTime <= currentTime)) {
      currentSub = self.subs[item].text;
    } else if ((self.subs[item].eTime < currentTime)) { // no subs now
      currentSub = ' ';
    }
 }

 if (currentSub && (currentSub != lastSub)) {
   self.container.find('.subs').html(currentSub);
    lastSub = currentSub;
 }
}, 500);

Preview thumbnails при перемотке

Навряд ли найдется человек, не оценивший удобство отображения кадра для перемещения при перемотке. Реализация данного функционала не является сложной задачей. Для создания preview необходимо поверх основного контейнера с видео создать новый меньшего размера с тем же видео контентом и спозиционировать его точно над курсором:

self.container.find('.controls').append('<video class="thumb" src="' +
                                  self.player.currentSrc + '"></video>');

А после перематываем в данном контейнере видео:

var self = this;

$('.seekbar).bind('mousemove', function (e) {
  self.cursorX = e.pageX;
  self.seek(self);
});

this.scale = this.container.find('.seekbar').width() / player.duration;

var seek = function (context) {
  $('.thumb')[0].currentTime = (self.cursorX -
           self.container.find('.seekbar').offset().left) / self.scale;
}

Переход в полноэкранный режим

В браузерах Firefox, Safari и Chrome существует специальный API для работы режимом полноэкранного отображения.  Механизм  позволяет отобразить выбранный div на весь экран. Функции, выполняющие данные действия в этих браузерах названы по-разному, так что  Вам придется реализовать вызов обоих.

var element = document.getElementById(‘player_container’);

if (element.mozRequestFullScreen) {
  element.mozRequestFullScreen(); // Разворачиваем для Firefox
} else if (element.webkitRequestFullScreen) {
  element.width('100%');
  element.height('100%');
  element.webkitRequestFullScreen(); // Разворачиваем для Chrome и Safari
}

Обратите внимание, что для Chrome и Safari помимо перевода контейнера в полный экран реализовано необходимо присвоить его высоте и ширине  значения 100%. Это связано с тем, что вызов метода  webkitRequestFullScreen() лишь затемнить весь экран и расположит по его центру целевой контейнер. Масштабирование контейнера целиком и полностью переложено на сторону разработчика.

Факт разворачивания на полный экран контейнера моно определить состоянием полей document.mozFullScreenElement для Firefox и document.webkitIsFullScreen для браузеров на WebKit.

Перевод  контейнера обратно в режим нормального отображения возможеy функциями отмены.

// Определяем факт полноэкранного отбражения какого-либо контейнера.
if (document.mozFullScreenElement || document.webkitIsFullScreen) {
  if (document.mozCancelFullScreen) {
     document.mozCancelFullScreen();  // Сворачиваем для Mozilla
  } else if (document.webkitCancelFullScreen) {
    document.webkitCancelFullScreen();  // Сворачиваем для Chrome и Safari
  }
}

FullScreen API также реализует события изменения состояния отображения – ‘mozfullscreenchange’ и ‘webkitfullscreenchange’ соответственно. Выше было указанно, что масштабирование контейнера в WebKit лежит на разработчике, соответственно при возвращении к нормальному отображению также нужно изменить размеры контейнера с плеером:

document.addEventListener('webkitfullscreenchange',
                   this.WebkitFullscreenEvent = function() {
  if (!document.webkitIsFullScreen) {
    self.container.width(width);
    self.container.height(height);
  }
});

Другой функционал

Определение размера загружаемого файла.

Как уже было сказано, у разработчика отсутствует возможность определить  размер видео файла средствами HTML5. Однако никто не запретит получать его размер от бэкэнда либо при формировании списка воспроизведения, либо при получении адреса файла, либо через обращение к специальному методу.

Переключение аудиодорожек

На данный момент отсутствует реализация переключения аудио дорожек. Конечно, можно создать на странице несколько аудио плееров с дорожками, однако в таком случае синхронизация проигрывания видео и нужного аудио является крайне нетривиальной задачей.

Получение скриншота проигрываемого видео

Если Вам потребовалось снимать скриншот видео, то о реализации этого с использованием <canvas> можно прочитать здесь.

Эффекты

Приятной особенностью HTML5 Video является то, что к проигрывателю могут быть применены любые эффекты, трансформации и маски, осуществимые через CSS3. Пример подобного функционала можно посмотреть здесь.

HTML5 vs Flash

Существует распространенное  мнение, что HTML5 Video является своеобразным «убийцей» Flash, т.к. предлагает схожий функционал. На деле все конечно не совсем так. Для разворачивания премиум видео сервисов HTML5 не подходит, т.к. не поддерживает следующие важные особенности:

  • Потоковое видео. HTML5 подходит лишь для воспроизведения видео файлов.
  • Защита контента. В тоже время Flash предоставляет возможность использование защитных технологий.
  • Единый API и его реализация для всех браузеров.
  • Стандартный формат видео. Разные браузеры = разные форматы видео для HTML5 плеера.

Правда стоит заметить, что работы по расширению возможностей HTML5 Video до функционала Flash уже ведутся.  В первую очередь данная инициатива исходит от поискового гиганта Google – подробнее можно прочитать в докладе о выступлении представителя компании на конференции Streaming Media.

Пример реализации видео плеера

Перейти на страницу с видеоплеером.

Выводы

Появление HTML5 принесло нам новый инструмент для доставки видео контента пользователю. Однако на данный момент в виду новизны стандарта существует ряд случаев, когда необходимо применение Flash-технологии.  Решение о том, какой механизм использовать целиком и полностью должен зависеть от целей и потребностей сервиса.

Полезные ссылки

PS

В ближайшее время ждите статью об аналитике проигрываемого HTML5 Video.