Как программно определить, поддерживает ли браузер HTML5 Audio/Video и нужные форматы/кодеки

В продолжение темы работы с аудио и видео в HTML5 хочу остановиться на еще одном важном аспекте — программной вставке аудио и видео через JavaScript, и, если быть точнее, программном (через JavaScript) определении поддержки медиа-элементов и различных форматов и кодеков.

Хотя многие библиотеки добавляют свои обертки, упрощающую эту задачу, важно также понимать, что там происходит внутри.

Определение поддержки аудио и видео

Начнем с простой задачи: как понять, поддерживает ли браузер пользователя вставку аудио- и видео-элементов?

Для решения этой задачи достаточно попытаться создать новый элемент с соответствующим тегом и посмотреть на результат:

var audio = document.createElement("audio");
// audio 
/* [object HTMLAudioElement] {
 autobuffer : false,
 autoplay : false,
 buffered : [object TimeRanges],
 controls : false,
 currentSrc : "",
 currentTime : 0,
 defaultPlaybackRate : 1,
 duration : NaN,
 ended : false,
 error : null ... }
 */


В случае, если браузер поддерживает аудио или видео-элементы, результатом создания нового элемента, согласно спецификации, должен быть объект типа HTMLAudioElement (HTMLVideoElement для видео).

audio instanceof HTMLAudioElement;// true


(Если браузер поддерживает создание новых элементов, в том числе с произвольным названием, но не умеет поддерживать конкретный тип элемента, то результатом будет объект HTMLUnknownElement. Для старых браузеров это может быть не верно — и результатом будет объект какого-нибудь другого типа или просто Object с некоторыми свойствами, характерными для HTML-элементов.)

На этом этапе должно быть очевидно, что в принципе, достаточно через блок try-catch проверять поддержку браузером объекта HTMLAudioElement или HTMLVideoElement соответственно.

var videoSupported = false;try {   HTMLVideoElement;   
videoSupported = true;} catch (e) {};videoSupported;// IE9 - true// IE8 - false


Но если вы хотите использовать созданный медиа-элемент дальше, то пример, приведенный в начале, выглядит более практичным вследствие трюка, который мы увидим в следующем разделе.

Определение поддержки формата/кодека

К этому моменту мы уже научились определять, поддерживает ли браузер медиа-элементы. Следующая возникающая проблема — понять, какие кодеки (форматы медиа-файлов) поддерживает браузер.

Напомню, что на сегодня эта картина весьма драматична и единого согласия хотя бы по одному общему формату для “аудио и для видео из коробки”, в общем-то, нет. В случае, если вы вставляете аудио или видео декларативно, вы можете сразу указать альтернативы, переложив задачу выбора на плечи самого браузера.

Чтобы решить эту задачу программно, в медиа-элементах есть специальный метод, общий для аудио и видео (формально, HTMLAudioElement и HTMLVideoElement оба реализуют интерфейс HTMLMediaElement), — canPlayType(), принимающий в качестве параметра строку описания типа медиа-файла.

var support = videoElement.canPlay
Type('video/x-new-fictional-format;codecs="kittens,bunnies"');

Если браузер знает, что он не поддерживает данный формат, например, из-за того, что он не умеет работать с указанным типом контейнера или нужного кодека нет в его списке поддерживаемых кодеков, — он должен вернуть пустую строку.

Пустая строка также возвращается, если в качестве типа указан “application/octet-stream” — общий mime-тип для бинарных файлов.

Если браузер уверен, что он сможет проиграть медиа-файл в заданном формате, он возвращает “probably”. В остальных случаях возвращается “maybe”.

Разница между “probably” и “maybe” может быть неочевидной, особенно с учетом близости значений двух слов (по крайней мере, в русском языке).

На языке спецификации, надо понимать два ключевых момента:

  1. И “probably” и “maybe” — это вероятностные вещи. Конечно, вы не получите числового выражения от 0 до 1 для каждого из этих значений, но важно понимать, что ни одно из них не равно 1.
  2. Ключевой момент различия между “probably” и “maybe” заключается в том, что если указан формат и известно, что для этого формата могут быть разные кодеки, но кодек не указан, браузер никогда не должен возвращать “probably” — он вернет “maybe”, так как могут быть комбинации кодеков, которые он не поддерживает.

Например, вот так это выглядит для IE9:

video.canPlayType('video/x-new-fictional-format;codecs="kittens,bunnies"'); 
// "" video.canPlayType('video/mp4'); 
// "maybe" video.canPlayType('video/mp4;codecs="avc1.42E01E, mp4a.40.2"'); 
// "probably" video.canPlayType('video/mp4; codecs="avc1.58A01E, mp4a.40.2"'); 
// "probably" video.canPlayType('video/mp4; codecs="mp4v.20.8, mp4a.40.2"'); // ""

Важная деталь, о которой важно помнить, заключается также в том, что сервер, который отдает вам медиа-файлы, должен быть корректно настроен, то есть отдавать видео и аудио с правильным mime-type. В противном случае, из соображений безопасности, браузер может блокировать работу с этими файлами.

Например, для IIS это можно проверить в панели управления и добавить недостающие форматы там же или через web.config:

<system.webServer>
   <staticContent>
      <mimeMap fileExtension=".mp4" mimeType="video/mp4" />
      <mimeMap fileExtension=".m4v" mimeType="video/m4v" />
      <mimeMap fileExtension=".webm" mimeType="video/webm" />
   </staticContent>
</system.webServer>

Теперь, возвращаемся к обещанному трюку. Учитывая, что метод canPlayType есть только у медиа-элементов (в отличие от остальных типов элементов), его наличие является хорошим способом определить, что созданный элемент является именно медиа-элементом.

На практике проверку поддержки медиа-элементов и поддержки нужных форматов часто объединяют в общее выражение, которое к тому же оказывается короче, чем длинный поэтапный путь:

var audio = document.createElement("audio");
if (audio != null && audio.canPlayType && audio.canPlayType("audio/mpeg"))
{
   audio.src = "audio/sample.mp3";
   audio.play();
}

Наконец, давайте посмотрим, как проверка поддержка аудио и видео осуществляется через популярную библиотеку Modernizr. (Для любителей ASP.NET MVC напомню, что она также включена в состав ASP.NET MVC 3.)

Использование Modernizr

Для начала, давайте я приведу внутренний код из библиотеки — исходя из изложенного выше, вам в нем практически все должно быть сходу понятно.

Проверка поддержки аудио-элемента и популярных кодеков:

tests['audio'] = function() {
   var elem = document.createElement('audio'), bool = false;
   try {
      if ( bool = !!elem.canPlayType ) {
         bool = new Boolean(bool);
         bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"');
         bool.mp3 = elem.canPlayType('audio/mpeg;');
         bool.wav = elem.canPlayType('audio/wav; codecs="1"');
         bool.m4a = elem.canPlayType('audio/x-m4a;') || elem.canPlayType('audio/aac;');
      }
   }
 catch(e) { }   return bool; };


Проверка поддержки видео-элемента и популярных кодеков:

tests['video'] = function() {
   var elem = document.createElement('video'), bool = false;
   try {
      if ( bool = !!elem.canPlayType ) {
         bool = new Boolean(bool);
         bool.ogg = elem.canPlayType('video/ogg; codecs="theora"');
         // Workaround required for IE9, which doesn't
         //  report video support without audio codec specified.
          // bug 599718 @ msft connect
          var h264 = 'video/mp4; codecs="avc1.42E01E';
         bool.h264 = elem.canPlayType(h264 + '"') || elem.canPlayType(h264 + ', mp4a.40.2"');
         bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"');
       }
   } catch(e) { }
    return bool;
};


(Про упоминаемый баг IE9 — надеюсь, в Modernizr это обновят, т.к. в финальной версии IE9 это давно поправлено.;)

Из приведенных примеров должен быть очевиден способ использования библиотеки для определения поддержки аудио/видео и соответствующих кодеков:

if (Modernizr.audio) {
   var audio = new Audio();
   audio.src = Modernizr.audio.mp3 ? 'background.mp3' :
                     Modernizr.audio.ogg ? 'background.ogg' :
                                                         'background.m4a';
   audio.play();
} else {
   // Ooops.
}


Другие тонкости использования медиа-элементов HTML5 мы рассмотрим как-нибудь в другой раз ;)