Элементы <video>
и <audio>
предоставляют широкий спектр событий. В то время как некоторые из них довольно просты, как, например, самоочевидное событие "play"
, другие могут быть более сложными для понимания, особенно событие "progress"
.
Итак, давайте рассмотрим некоторые из наиболее важных событий в СМИ, посмотрим, когда и как они запускаются, и какие свойства имеют к ним отношение. Мы также попытаемся определить причуды их поведения в современных браузерах (ну, вы же не думали, что они все будут одинаковыми, не так ли?).
(Для справочного тестирования я буду использовать последние общедоступные версии наиболее распространенных браузеров — Opera 12, Chrome 28, IE10, Firefox 22, Safari 5 (для настольных ПК) и Mobile Safari 6 (iOS). Так что везде, где упоминается браузер только по имени (например, Opera
) это означает, что эта последняя версия.)
События воспроизведения
События воспроизведения — это события, которые запускаются в ответ на воспроизведение или приостановку медиа. Эти события довольно просты.
События "play"
и "pause"
запускаются, когда мультимедиа воспроизводится или ставится на паузу (соответственно), но есть также событие "ended"
которое запускается, когда заканчивается мультимедиа — либо потому, что обычное воспроизведение закончилось, либо потому, что пользователь вручную «Искал» это далеко.
Существуют мультимедийные функции, которые соответствуют первым двум событиям — неудивительно, что они называются play()
и pause()
. Есть также два свойства мультимедиа, которые соответствуют последним двум событиям .paused
свойство .paused
имеет значение true
по умолчанию или всякий раз, когда медиа- .ended
приостановлены, а свойство .ended
имеет значение false
по умолчанию, но затем становится true
когда воспроизведение достигает конца ( то есть в то же время, что и "ended"
событие).
Однако в Opera, Safari и .paused
существует существенная аномалия, заключающаяся в том, что флаг .paused
остается false
когда закончился носитель (но логически это должно быть true
поскольку носитель больше не воспроизводится). Практическим результатом этого является то, что простой обработчик кнопки воспроизведения / паузы, такой как этот, потерпит неудачу в этой ситуации (то есть кнопка вообще ничего не сделает):
button.addEventListener('click', function(e) { if(media.paused) { media.play(); } else { media.pause(); } }, false);
Но вы можете легко исправить эту причуду, запустив метод pause()
вручную в ответ на событие "ended"
:
media.addEventListener('ended', function(e) { media.pause(); }, false);
Firefox и Chrome уже исправили это внутренне, и точно так же — "pause"
событие "pause"
непосредственно перед событием "ended"
.
Загрузка событий
События загрузки — это события, которые запускаются в отношении загрузки (или не загрузки) медиа-данных. Распространенность этих событий зависит от состояния загрузки носителя, т. preload
Используется ли атрибут preload
и / или уже кэширован носитель.
Первым во всех случаях "loadstart"
событие "loadstart"
, которое означает, что браузер начал искать данные. Но это все, что это означает — это не значит, что какие-либо данные фактически загружены или что медиа-ресурс даже существует.
Если атрибут "loadstart"
имеет значение "none"
, то событие "loadstart"
является единственным, которое сработает до начала воспроизведения. Принимая во внимание, что если атрибут preload
имеет значение "metadata"
или "auto"
, то довольно скоро "loadedmetadata"
еще два события: "progress"
и "loadedmetadata"
. (Без предварительной загрузки эти события будут запускаться, но не раньше, чем начнется воспроизведение.)
Событие "progress"
довольно сложное, поэтому мы рассмотрим это отдельно в следующем разделе, но событие "loadedmetadata"
является простым, поскольку оно просто означает, что браузер загрузил достаточно метаданных, чтобы знать .duration
медиа. (как число с плавающей запятой, а не его значение по умолчанию NaN
).
Конечно, событие "loadedmetadata"
будет срабатывать только в том случае, если носитель может загружаться — в случае сбоя (например, если src
возвращает 404
), то вместо этого носитель выдаст событие "error"
, и больше не будет воспроизведение будет возможно.
Здесь мы снова сталкиваемся с некоторыми важными вариациями браузера. В Mobile Safari preload
настройки преднамеренно не реализованы , поэтому все значения для этого атрибута ведут себя так же, как если бы они были "none"
. В IE10, напротив, метаданные медиа всегда загружаются по умолчанию , поэтому значение preload
"none"
ведет себя так же, как если бы это были "metadata"
.
После того как "loadedmetadata"
запущены, следующим значимым событием является "canplay"
, которое браузер сработает, чтобы указать, когда загружено достаточно данных, чтобы он знал, что воспроизведение будет работать (то есть, что он может воспроизводиться
). Если preload
— "auto"
то событие "canplay"
сработает через пару секунд загрузки данных; если preload
— "metadata"
или "none"
он не сработает, пока не начнется воспроизведение. Единственным исключением из этого правила является Chrome, который всегда запускает "canplay"
во время начальной предварительной загрузки, даже если это только метаданные.
Существует также вторичное событие, называемое "canplaythrough"
, которое браузер должен "canplaythrough"
, когда он оценивает, что медиа-данные загружены достаточно для непрерывного воспроизведения. Предполагается, что это основано на оценке скорости вашего соединения, и поэтому оно не должно срабатывать до тех пор, пока данные не будут загружены как минимум за несколько секунд.
Однако на практике событие "canplaythrough"
в основном бесполезно — потому что Safari не "canplaythrough"
его вообще, в то время как Opera и Chrome "canplay"
его сразу после события "canplay"
, даже если ему еще предстоит предварительно загрузить целую четверть второй! Только Firefox и IE10, по-видимому, правильно реализуют это событие.
Но в любом случае вам это событие не нужно, так как вы можете отслеживать событие "progress"
чтобы определить, какой объем данных был предварительно загружен (и, если необходимо, рассчитать скорость загрузки самостоятельно):
Событие Прогресс
Событие "progress"
происходит постоянно, пока (и только во время) загрузки данных. Таким образом, когда preload
установлена в "none"
, она вообще не срабатывает, пока не начнется воспроизведение; с preload
установленной на "metadata"
она будет срабатывать в течение первых нескольких секунд, а затем остановится, пока не начнется воспроизведение; с preload
установленной на "auto"
он будет продолжать работать до тех пор, пока весь медиа-файл не будет загружен.
Но для всех настроек preload
, после начала воспроизведения , браузер продолжит загрузку всего медиа-файла, вызывая непрерывные события "progress"
пока не останется ничего для загрузки, что продолжается в фоновом режиме, даже если видео впоследствии приостанавливается.
Сами данные представлены набором временных интервалов (то есть дискретных отрезков времени), и очень важно понять, как они работают, прежде чем мы сможем использовать события "progress"
.
Когда носитель впервые начинает загружаться, он создает один временной диапазон, представляющий начальную часть. Так, например, после загрузки первых 10 секунд данных временной диапазон можно представить в виде массива времени начала и окончания:
[0,10]
Однако возможно (на самом деле очень вероятно) создание нескольких временных диапазонов. Например, если пользователь вручную ищет время, превышающее то, что уже было предварительно загружено, браузер откажется от своего текущего временного диапазона и создаст новый, который начинается с этой точки (вместо того, чтобы загружать все промежуточное, как базовый Flash). игроки делают).
Допустим, пользователь переходит вперед на две минуты и продолжает воспроизведение с этого момента, затем, если еще через 10 секунд будет предварительно загружена, у нас будет два диапазона, которые мы могли бы представить так:
[ [0,10], [120,130] ]
Если бы пользователь затем снова вернулся к среднему времени между двумя диапазонами, тогда был бы создан другой (третий) диапазон:
[ [0,10], [60,70], [120,130] ]
Затем, как только конец этого диапазона достигнет начальной точки последнего, диапазоны будут объединены вместе:
[ [0,10], [60,130] ]
Массивы в этих примерах являются просто представлениями , чтобы помочь объяснить концепцию — они не так, как на самом деле появляются данные временного диапазона; чтобы получить данные в этом формате, мы должны скомпилировать их вручную.
Носитель имеет объект .buffered
который представляет временные диапазоны. Объект .buffered
имеет свойство .length
для обозначения количества доступных диапазонов и пару методов, называемых start()
и end()
для получения синхронизации отдельного диапазона.
Таким образом, чтобы преобразовать буферизованные данные в эти двумерные массивы, мы можем скомпилировать их следующим образом:
var ranges = []; for(var i = 0; i < media.buffered.length; i ++) { ranges.push([ media.buffered.start(i), media.buffered.end(i) ]); }
И вот что мы делаем с "progress"
событий:
media.addEventListener('progress', function() { var ranges = []; for(var i = 0; i < media.buffered.length; i ++) { ranges.push([ media.buffered.start(i), media.buffered.end(i) ]); } }, false);
В конечном итоге мы можем использовать эти данные для создания чего-то более удобного для пользователя — например, визуального индикатора прогресса, как показано в следующей демонстрации. Это просто набор позиционированного <span>
внутри содержащего <div>
(мы не можем использовать элемент <progress>
потому что он не поддерживает несколько диапазонов):
Есть несколько примечательных особенностей браузера с событиями "progress"
и буферизованными данными. Во-первых, это разница в данных .buffered
при загрузке с самого начала — в то время как большинство браузеров создают один временной диапазон (как описано в начале этого раздела), Opera создает два диапазона , причем первый соответствует ожидаемому, и вторая — крошечный фрагмент времени в конце (примерно последние 200ms
). Таким образом, если бы носитель был длиной в две минуты и первые 10 секунд были загружены, диапазоны были бы примерно такими:
[ [0,10], [119.8,120] ]
Еще одна оговорка: Mobile Safari не сохраняет данные для нескольких диапазонов — он отбрасывает все, кроме активного диапазона (то есть диапазона, охватывающего текущую позицию воспроизведения). Это явно преднамеренное поведение, предназначенное для минимизации общего объема памяти, потребляемого медиа-элементами. Поэтому, чтобы снова использовать предыдущий пример, когда пользователь переходит вперед на две минуты, результирующие буферизованные данные будут по-прежнему содержать только один диапазон:
[ [120,130] ]
Об этих причудах стоит знать, но, как правило, они не будут иметь большого значения для разработки. Однако другой, гораздо более существенной особенностью является поведение браузеров в тех случаях, когда весь медиа-файл уже был предварительно загружен . В этом случае большинство браузеров будут запускать одно событие "progress"
, содержащее один временной диапазон, представляющий всю продолжительность. Однако Opera и IE10 не предоставляют эти данные о прогрессе — Opera запускает одно событие, в котором у буфера нет диапазонов (т. .buffered.length
равно нулю), в то время как IE10 вообще не .buffered.length
никаких событий "progress"
.
В случае визуального индикатора выполнения это будет означать, что индикатор остается пустым, а не заполненным. Но, тем не менее, это "loadedmetadata"
исправить, используя дополнительное событие "loadedmetadata"
— потому что, как только это событие запускается в этих браузерах, данные .buffered
теперь представляют полную продолжительность мультимедиа.
Сроки событий
Последнее, что мы кратко рассмотрим, "timeupdate"
событие "timeupdate"
медиа, которое непрерывно срабатывает во время воспроизведения медиа. Это событие можно использовать для синхронизации других вещей с воспроизведением мультимедиа, таких как создание заголовков вручную, выделение активной строки в транскрипте или даже для синхронизации нескольких источников мультимедиа — то, что я рассматривал в предыдущей статье: Доступные описания аудио для видео HTML5 ,
Частота, с которой "timeupdate"
событие "timeupdate"
не указана, и на практике она сильно варьируется в разных браузерах. Но в целом оно составляет 3-5 раз в секунду, что достаточно точно для большинства целей синхронизации.
Насколько я знаю, в этом событии нет ошибок или извращений браузера. Вносит приятные изменения, эй!
Послесловие
Эта статья не включает все возможные медиа-события — есть другие события воспроизведения и поиска, события для расширенных состояний сети и даже событие, которое срабатывает при изменении громкости. Но я рассмотрел то, что я считаю наиболее важным — достаточно для большинства простых сценариев, которые вы, возможно, захотите делать с видео и аудио, и достаточно для создания основного пользовательского интерфейса.
Вот последняя демонстрационная ссылка, чтобы помочь вам почувствовать эти медиа-события. Он создает динамический журнал событий воспроизведения и прогресса, которые мы обсуждали, показывая временные характеристики и данные связанных свойств, сопровождающих каждое событие: