2. На каждый элемент аудио: new Audio() или <audio> нужно разрешение пользователя — пользовательское действие на странице.
Предыстория
Наверное, каждый, кто хоть раз в своей жизни писал аудиоплеер для браузеров, сталкивался с проблемой кроссбраузерности и кроссплатформенности.
Вот и я во время работы над новым MVP столкнулся с различными особенностями в отношении проигрывания аудио в браузерах.
А началось всё с того, что нужно было сделать плавное сведение (crossfade) двух треков при воспроизведении – это первая особенность. Наша команда хотела сделать смену треков как на радио. И вторая особенность — каждый последующий трек запрашивается из сети.
![](https://habrastorage.org/webt/7h/o6/4h/7ho64hihtgz3atuttk1vm0mucpq.png)
Изыскания
Тогда почти во всех наших проектах использовалась библиотека Sound Manager 2.
Практически сразу понимаешь, что воспроизведение одновременно двух аудио файлов на мобильных устройствах не везде одинаково работает!
В Chrome (~62 версия) для ПК треки воспроизводились как надо. На мобильных устройствах (тоже в Chrome) воспроизведение треков работало, но только при активном экране. Когда экран блокировался, следующий трек за текущим играющим не воспроизводился. Что касается iOS / macOS — воспроизведение работало аналогичным образом. Больше информации можно получить тут – раздел “Единичный аудиопоток”.
Так начались
Хорошо, пробую решение с Web Audio без использования каких-либо библиотек. Да, эта технология предназначена для иных целей: синтез, обработка звука, для игр и т.д., нежели простое воспроизведение треков. Но ради эксперимента нужно было попробовать, так как она позволяет компоновать звуки из разных источников на один звуковой выход — колонки/наушники/динамик телефона/и т.п. Есть ребята, которые целенаправленно занимаются исследованием возможностями воспроизведения звука на мобильных устройствах с использованием Web Audio API.
После реализации выяснились определенные нюансы.
Во-первых, необходимо дожидаться полной загрузки всего трека. При медленном соединении с интернетом будут заметны паузы из-за того, что второй трек может не успеть загрузиться к моменту окончания первого трека. Полной загрузки можно избежать, если использовать связку с HTML5 Audio тегами, которые будут выступать в качестве источников звука для Web Audio, но в этом случае снова становится невозможным воспроизведение двух звуков одновременно.
Во-вторых, если загружать трек по сети фрагментами и декодировать их программно, то это увеличивает нагрузку на CPU. Для ПК было приемлемо, а вот для мобильных устройств критично.
В-третьих, возникли проблемы с декодированием. Если на клиент приходили фрагменты mp3/ogg/wav файлов, то эти кусочки спокойно декодировались и воспроизводились. Но если в браузер приходили чанки mp4 файла, который выступал контейнером для HE-AAC, то их тогда декодировать не удалось. Это в некоторой степени касается и браузера Opera, в котором от версии к версии нестабильно работает воспроизведение MP3 файлов — то воспроизводит, то выдает ошибку, что данный формат не поддерживается.
В-четвертых, не отображалось / не менялось название трека на заблокированном экране на плашке с нативным аудио плеером (на iPad), в т.ч. при переключении между треками. Возможно из-за того, что для тестов использовался iPad с 9 версией iOS — другого на тот момент не было.
В итоге, на данном этапе от Web Audio пришлось отказаться. Всё-таки crossfade не для браузеров, стандартные музыкальные композиций в хорошем качестве достаточно много весят.
Раз от crossfade отказываемся, то реализуем простой fade in и fade out, в начале и в конце музыкального трека соответственно.
Код на позапрошлом шаге был немного доработан и протестирован. В результате тестов всплыли различные нюансы (показаны в таблице). Все это с использованием библиотеки Sound Manager 2.
![](https://habrastorage.org/webt/b5/bj/wg/b5bjwgh2mds3pn1x8ghbp3nuyue.png)
Добавляем логирование всех событий, чтобы определить момент перехода между треками и понять в какой момент они перестают воспроизводиться.
![](https://habrastorage.org/webt/z_/nr/0g/z_nr0g8j0zxskeevs2e3loddlok.png)
Из этого можно предположить, что выполнение JS в фоне подвергается throttling’у или поток выполнения полностью останавливается (события и таймеры). Однако, позже станет понятно, что это был отчасти корректный вывод. Ниже будет рассмотрен еще 1 нюанс, связанный с воспроизведением треков и осознанием почему звук не появляется.
В этот же момент возник вопрос: а как же быть с автовоспроизведением треков на ПК и на мобильных устройствах?
Под автовоспроизведением понимается — автоматический старт проигрывания трека без каких-либо действий пользователя при загрузке страницы.Что касается Safari в отношении автоматического воспроизведения при загрузке страницы, то это невозможно, нужно взаимодействие пользователя со страницей, как и на мобильных устройствах. Это касается как видеоконтента, так и аудиоконтента.
И так, на тот момент было следующее:
- нельзя (не желательно) воспроизводить два и более звуков одновременно;
- для псевдо “автовоспроизведения” трека необходимо разрешение пользователя — первое взаимодействие, позже это было названо “Продать пальчик устройству”;
- в фоне (background tab / lock screen) JS (все зависит от браузера):
либо замирает полностью;
либо подвергается throttling’у;
либо работает также, как и при активной вкладке;
- можно автоматически стартовать воспроизведение без звука, но непонятно зачем (для аудио контента)?
- где-то далеко начинает маячить мысль, а как сделать так, чтобы JS в фоне продолжал выполняться?
В дело пошли другие библиотеки реализующие функции плеера с предположением, что возможно там есть решение для этой задачи. Несмотря на то, что было просмотрено множество issues на GitHub с описание проблем при воспроизведении треков в различных браузерах, все же была надежда на то, что вот-вот доберешься до сути: почему не работает и как сделать, чтобы работало. Как оказалось, нет…
Несколько примеров кода с видео демонстрацией работы библиотек:
- Sound Manager 2 — github pages, github репозиторий, видео: macOS Safari 12; iOS Safari 10 при разблокированном экране
- Howler
Howler v2.0.9 — github pages, github репозиторий, видео: macOS Safari 12, iOS Safari 10
Howler v2.0.15 — github pages, github репозиторий, видео: macOS Safari 12
Howler v2.1.1 — github pages, github репозиторий, видео: macOS Safari 12, iOS Safari 10
Для macOS запись видео сделана без звука, поэтому нужно смотреть на индикатор громкости — изображение динамика, на вкладке.В чем причина неработоспособности этих библиотек?
В репозитории доступно больше видео примеров.
В интерактивном примере для Howler v2.1.1 — иногда можно услышать несколько звуков одновременно, это связано с добавлением пула разблокированных пользователем audio элементов (в будущих версиях библиотеки это должны исправить).
Выше я писал: “В фоне (background tab) JS либо замирает полностью, либо подвергается throttling’у”. Так вот тут всплывает другой момент: библиотеки в коде используют создание новых аудио объектов через new Audio(). Если они создаются динамически, т.е. не используется уже существующий аудио объект, и при этом пользователь никак не взаимодействует с сайтом, неактивна вкладка или заблокирован экран, то некоторые браузеры могут посчитать, что воспроизводить звук от этого аудио элемента не следует, пока вкладка не будет снова активна или пользователь не совершит какое-либо действие.
Пример теста на github pages и в репозитории на github с использованием new Audio(). Видео: macOS Safari 12; iOS Safari 10 с разблокированным экраном.
Похоже, что какого-то универсального инструмента не существует и нужно искать какое-то другое компромиссное решение.
Дальше садимся с ребятами из команды обсудить, а что действительно важно в работе аудио плеера? Ибо продолжать эксперименты можно было бы до бесконечности, но нужно двигаться вперед.
Сначала были выявлены важные моменты, которые мешали достичь желаемого результата:
- в браузере Safari на macOS не воспроизводятся треки при неактивной вкладке;
- отсутствует возможность слушать музыку в фоне (при заблокированном экране) на смартфонах, работающих на iOS и Android, хотелось бы избежать агрессивного перенаправления пользователей в мобильное приложение (в дальнейшем), так как предыдущий опыт показывает, что довольно большая часть пользователей не желает ставить мобильное приложение;
- плеер некорректно работает с динамическим плейлистом, т.е. когда заранее не известно, какой будет следующий трек.
Далее это позволило сформулировать цели, которые было необходимо достичь:
- обеспечить работу плеера в фоновом режиме — в различных браузерах и на различных платформах;
- позволить пользователю самому выбирать чем пользоваться: прослушивать музыку на сайте или в мобильном приложении;
- обеспечить возможность использовать плеер (или подход) в различных будущих проектах.
Начался новый этап поиска решения поставленной задачи. На этом этапе уже не использовались различные библиотеки, все исследования велись с использование HTML5 Audio. Итогом стало то, что был найден вариант с использованием dedicated workers. iOS это решение победить опять не позволило — воспроизведение в фоне не работает, зато получилось добиться работоспособности в Android (Chrome, Opera, Safari).
Пример теста HTML5 Audio + Dedicated Workers на github pages и в репозитории на github.
При инициализации Worker'а запрашиваются данные о текущем треке. Worker также занимается отправкой сигнала на получением состояния прогресса — сколько времени трек играет — из основного потока и на основе этих данных решает когда запросить данные о следующем треке из сети.
![](https://habrastorage.org/webt/ln/t0/sh/lnt0shcslejecuvcenkh-rlv28y.png)
Также в то время был протестирован следующий пример (github pages, репозиторий на github), когда HTML5 audio тег встраивается в DOM (видео: macOS Safari 12, iOS Safari 10) и у него просто подменяется SRC при переключении между треками. На сегодняшний день на macOS в 12 Safari этот пример работает. К сожалению, сейчас нет возможности проверить работоспособность этого примера на macOS в Safari 10 и 11 версии, но на тот момент при проведении тестов этот пример не работал (autoplay policies, autoplay restrictions).
Если подытожить, то для iOS и macOS браузер Safari не считает новый экземпляр аудио элемента активированным пользователем, если он был создан в фоновом режиме внутри какого-либо события, например, ajax, setTimeout, onended.
Далее, что касается воспроизведения треков в iOS Safari и iOS Chrome, была найдена возможность воспроизводить треки в фоновом режиме (при заблокированном экране) только с использованием HLS. Для платформ iOS и macOS этот формат является стандартом и вещание поддерживается операционной системой. Для Android Chrome и Edge также доступна нативная реализация. А для ПК в Chrome — можно использовать программные обработчики, например, hls.js, Bitmovin Player и т.д.
По ссылке на github репозиторий доступен пример кода, который охватывает самый простой вариант использования – простое проигрывание генерируемого на сервере потока воспроизведения без возможности перемотки, переключения на следующий трек и т.д. Представлены примеры с использованием: тега audio, тега video, библиотеки hls.js, и плеера от Bitmovin. Для запуска требуется Node.js.
Выводы
Первый момент, к сожалению, из-за всего многообразия браузеров нет какого-то универсального решения, которое бы позволило везде одинаково хорошо прослушивать музыку в браузерах. Везде есть свои ограничения и как показывает практика с ними можно, вполне, комфортно жить.
Второй момент, иногда стоит как можно быстрее проверить пограничные случаи, например, нативную реализацию. Найти какой-то минимально приемлемый набор требований и достаточно быстро проверить его работоспособность, а не брать за основу какую-либо библиотеку. Это даст больше понимания, как эти библиотеки устроены внутри и почему работают или не работают те или иные функции. Иначе можно убежать довольно далеко в проекте и после понять, что что-то идет не так. И может оказаться так, что отказаться от библиотеки будет довольно затратно. Потребуется переписать значительную часть кода.
Третий момент, обязательно обращайте внимание на аудиторию вашего сервиса — с каких браузеров и операционных систем приходят ваши пользователи. Это довольно легко отследить с помощью различных метрик и систем мониторинга ошибок. Такой подход позволит понять какие платформы и браузеры поддерживать важно, а на поддержку каких можно не тратить силы.
И напоследок
Объявляю небольшой конкурс, связанный с воспроизведением музыки на iOS с использованием технологии HLS.
Описание можно увидеть по ссылке на github.
Комментарии (23)
KYuri
06.02.2019 12:15Похоже, что какого-то универсального инструмента не существует и нужно искать какое-то другое компромиссное решение.
Если речь про автопереключение треков — со штатным (HTML5) аudio работает и на Safari под macOS, и на Safari под iOS.dipiash Автор
06.02.2019 12:23Я же правильно вижу, что это эмулятор?
Когда я тестировал на эмуляторе, то сталкивался с таким (на каких-то работает, на каких-то нет). Предположу, что вы заменяете src у audio тэга. На реальном девайсе это не работает :(KYuri
06.02.2019 12:33dipiash Автор
06.02.2019 13:05Что бы не быть голословным, взял реальный девайс для теста)
Залил видео в github или ссылка на прямое скачивание с github.
Просто показалось странным снимать экран телефона и выкладывать в статью, поэтому делал на эмуляторе.
Все может зависить от устройства, поэтому на 100% утверждать не могу, что нигде не работает. Но у коллег с iPhone'ами на работе тоже в фоне не воспроизводится.
upd.
Если не блокировать экран, то треки переключаются как надо.
KYuri
06.02.2019 12:32Да, эмулятор. На ваших видео тоже эмулятор)
Предположение правильное.
Сейчас железного девайса нету. Но когда был (iPad Air под iOS 11, кажется) — всё тоже работало.
Alexey2005
06.02.2019 18:04Благодаря стремительному прогрессу платформ, железа и средств разработки, мы наконец пришли к ситуации, когда разработать звуковой плеер для STM32 или AVR стало в разы проще, чем для десктопа или смартфона.
Master255
Есть универсальное решение!
Могли бы просто спросить.
Локальное приложение — прокси \\127.0.0.1\, запускающее другое приложение плеер (плееры) по команде браузера (ajax запросы).
У меня в плеере всё видео так воспроизводится. Сделать аудио и в фоне… не составит труда.
+ я сделал обратную связь даже, от приложения в браузер по окончанию трека. Просто надо записать нужные данные в сокет.
Необходимо сделать инсталлятор. Минус конечно в том что нужно ставить программу. Зато играют все форматы.
Добавлю, что браузерные HTML5 аудио модули на сегодня в пререлизном состоянии. Не рекомендую ими пользоваться. Видео HTML5 — вообще в диковинку… наверное ещё на стадии разработки))). На сегодня мой способ — единственное нормальное решение.
Пример на моём сайте в самом низу. Там инсталлятор видео плеера.
п.с. генерировать m3u8 для mp3 это у вас хорошая трава))). Я разрабатываю алгоритмы для возможности воспроизведения 4к. (фильмы 100Гбайт) без задержек, а вы в 2019 году mp3 делить решили. И точно скажу, мой алгоритм не совместим с m3u8 HLS мракобесием)). Уже готов и работает.
serf
Тогда уже лучше на electron запилить полноценное локальное приложение чем прокси какой-то.
Master255
а как вы туда внедрите видеоплеер? И чем это будет лучше (меньше и быстрее) нативного приложения (прокси) для операционной системы?
Всё же сайт предполагает гораздо более высокую скорость доступа к контенту, чем установка целого приложения.
barbanel
Я предпочту несколько нативных приложений установить чем один комбайн в электроне.
serf
Вы ошибаетесь… электрон нужно писать с большой буквы.
cjbars
Я так понял, что у автора речь идет о веб версии плеера, к тому же речь о серсиве а не о личном приложении. Поправьте меня если я ошибаюсь )))
Master255
так всё верно… нажимаешь в браузер и играет музыка. Интерфейс плеера, даже можно перенести в браузер. Никто же не мешает. Надо делать просто…
cjbars
а как это сделать без лишних телодвижений со стороны пользователя? Просто зашел на страничку включил и поет
Master255
никак. А зачем изучать чьи-то баги, когда можно сделать красиво, просто, быстро и более функционально! Спросите пользователей, они скорее поставят программу и будут пользоваться полноценным функционалом, чем не поставят и звук будет заикаться или вовсе перестанет играть.
А если вспомнить про аппаратное ускорение и приоритеты процессов браузера, то они совсем не такие, каких ждут пользователи. Браузер — это не мультимедиа приложение, что бы работать в фоне ещё…
Fragster
Т.е. чтобы послушать музыку на сайте — ставить приложение? Административные права в ОС, настройка файрволла и вот это все?
dipiash Автор
И потреять львиную долю пользователей из за того, что человек не может получить доступ к какой-то «условной фиче» здесь и сейчас?
Нет уж, в такие игры я не играю. И предпочту охватить большинство платформ через различные решения, что бы дать человеку чем-то пользоваться в том месте, куда он пришел сразу, а не прошел через 100500 редиректов.
Опыт показывает, что большинство пользователей даже не задумываются о приорететах процессов браузера, они просто пользуются.
Master255
ну если ваша цель срубить побольше бабла, то да. Мой вариант не подойдёт. А если дать нормальный сервис, то только мой вариант.
Популярность != качество.
Стать ещё одним яндекс музыка или гугл музыка… или сервисом, каких миллион… ничего интересного… да и зачем тогда играть в фоне. Копируйте код у яндекса… только название поменять не забудьте))).
KYuri
Инфа 100%?
А я — рекомендую. Почему Вы думаете, что Ваша рекомендация более авторитетная?)
Master255
Инфа сотка.
на Android/iOS/masOS/Linux/BSD работает.
потому что я протестировал их и браузерные плееры не воспроизводят все треки, которые поддерживают. Ещё не умеют. Это мы можем обсудить в профильной теме или чате, но не здесь.
KYuri
Дайте мне ссылочку и инструкцию по установке/настройке для Debian и Android?
Я тоже тестировал. Да, не всё поддерживают. Но у Вас же самого есть статья про aurora.js, которая расширяется плагинами под различные форматы (правда, с тех пор и браузеры уже многому научились).
А моя позиция такая: если речь идёт об удобстве пользователя, то подход должен быть другим: если что-то не поддерживается на клиенте — транскодируем на сервере.
dipiash Автор
Я, в этой статье, говорю про воспроизведение музыки на web сайтах через браузеры, а не через какие-то нативные решения типа приложений на Android.
Если быть честным и говорить об универсальности, то в контексте какой либо платформы. В универсальность нативных приложений на iOS / Android — я могу еще поверить, но утверждать со 100% гарантией я бы не стал. Что касается web браузеров, я бы вообще не говорил о какой-то универсальноти с учетом всего зоопарка.
Master255
дальнейшее обсуждение платное. +в карму и + во все мои комментарии.