Технология WebRTC (голосовые и видеозвонки) хороша тем, что встроена прямо в веб, который, разумеется, прекрасно подходит для WebRTC. Однако иногда веб доставляет немало хлопот, когда нужды WebRTC идут вразрез с общими требованиями к использованию браузеров. Последний пример – автовоспроизведение (далее – «автоплей») аудио/видео, когда у многих пользователей внезапно пропадал звук. Бывший автор webrtcHacks – Dag-Inge Aas – лично столкнулся с этой проблемой. Ниже его размышления: чего ожидать от браузеров в плане автовоспроизведения, последние изменения в Chrome 66+, а также пару советов, как жить с этими ограничениями.


Браузеры не хотят слышать Зло, поэтому политики автоплея выключают звук в любом медиа. Это может быть проблемой для WebRTC-приложений.

Если вы это читаете, то очень вероятно, что вы сталкивались со странным багом в в браузерах Safari 11+ и Chrome 66+. Речь про звуки интерфейса, которые вдруг стало не слышно (например, сигнал входящего звонка), или про неработающий аудиовизуализатор, или про то как не слышно собеседников.

На данный момент баг затронул почти все популярные WebRTC-плееры. Забавно, но похоже что Meet от Google и Chromebox for meetings тоже затронуты.

Корень всех зол: изменения в политиках автоплея (autoplay policies). В этом посте я расскажу про нововведения, как они касаются WebRTC и как бороться с этим в ваших приложениях.


Ошибка года: Uncaught Error: The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page. https://goo.gl/7K7WLu

Изменения


Все началось в 2007 году, когда появились iPhone и iOS. Если вы в прошлом работали с Safari для iOS, то возможно замечали, что Safari нужно участие пользователя, чтобы проигрывать элементы <audio> и <video> со звуком. Позже это требование немного ослабили, когда iOS 10 позволила видеоэлементам проигрываться автоматически, но без звука. Это стало проблемой для WebRTC, т.к. элемент <video> должен «видеть» и «слышать» медиапоток. В контексте WebRTC позволять видео автоматически запускаться без звука – это бесполезно, потому что во время видеозвонка надо по умолчанию слышать собеседников, а не нажимать «play» для этого. Как бы то ни было, мало WebRTC-разработчиков занимались Safari для iOS, потому что платформа не поддерживала WebRTC до недавнего времени. До выхода iOS 11.

Впервые я столкнулся с багом, когда тестировал последнюю (на тот момент) рабочую реализацию видеозвонков для iOS. К моему удивлению, оно перестало работать, при этом я был не одинок. Пользователь Github kylemcdonald зарепортил баг getUserMedia на iOS. Решение? Добавить видеоэлементу новое свойство playsinline, что разрешит видео воспроизводиться со звуком. К сожалению, в оригинальном посте про изменения автоплея в Safari разработчики не упоминали WebRTC, но они все же написали про WebRTC отдельно, до релиза. В статье сказано следующее про MediaStreams и воспроизведение звука:

  • медиа, использующее MediaStream, будет автоматически воспроизводиться, если веб-страница уже использует камеру/микрофон;
  • медиа, использующее MediaStream, будет автоматически воспроизводиться, если веб-страница уже воспроизводит звук. Чтобы воспроизведение звука началось, все еще нужно участие пользователя.

Итак, в этом документе не упоминается playsinline, но если совместить два анонса, то можно понять, как заставить WebRTC-приложение работать в Safari на iOS.

Почему вообще ограничивают автоплей?


Изначально пользователей хотели уберечь от лишних расходов на трафик. В 2007 году передача данных была дорогой (и остается таковой в большей части мира) и лишь немногие сайты были адаптированы под мобильные. Вдобавок автоплей звука был и остается самой раздражающей вещью во всем вебе. Поэтому для воспроизведения (и загрузки) видео требовалось действие пользователя; это давало гарантию, что пользователь в курсе происходящего.

Затем пришел GIF. Гифки – это всего лишь анимации внутри <img>, поэтому им не требовалось «разрешение» пользователя. Однако они могут быть тяжелыми и поэтому доставлять боль пользователям мобильного интернета. Видео щадит трафик, но требует согласия для загрузки – поэтому веб-страницы продолжили использовать GIF. Все изменилось в iOS 10, когда Safari разрешил автоплей с выключенным звуком. С тех пор оптимизация нагрузки – это вопрос разрешенного видео и уход в небытие трехминутных гифок.

Ограничение автоплея в десктопных браузерах


Если поискать, как остановить автоплей звука, то вы найдете немало способов. Недавно новостные агентства выяснили, что когда они используют ПО-НАСТОЯЩЕМУ ГРОМКОЕ аудио после загрузки страницы, пользователи проводят больше времени на сайте и кликают по рекламе. Конечно, так делать на стоит, но все-таки так делают. Поэтому десктопные браузеры последовали примеру Safari и запретили автоплей звука – особенно Chrome, который выкатил новые политики автоплея в 66 версии.

Однако Chrome неожиданно обратился к оригинальному Media Engagement Index (MEI).

The Media Engagement Index (MEI)


MEI – это то, как Chrome измеряет готовность пользователя разрешать автоплей на странице; этот индекс зависит от предыдущего поведения на странице. Посмотреть, как это выглядит, можно здесь: chrome://media-engagement/. MEI считается отдельно для каждого пользовательского профиля и работает в режиме инкогнито (из-за этого разработчикам очень трудно тестировать страницы с нулевым MEI, прежде чем выкатываться в продакшн). Кто-нибудь угадает, что будет дальше?


Скриншот из внутренней страницы chrome://media-engagement/ (источник: developers.google.com/web/updates/2017/09/autoplay-policy-changes)

Не только <audio> и <video>


Выяснилось, что новые политики затронули не только теги <audio> и <video>. Популярный UX-паттерн для WebRTC – показывать пользователю уровень громкости микрофона. Для этого звук анализируется через AudioContext, который берет MediaStream и выводит его сигнал в виде гистограммы. В этом случае не воспроизводится звук, но все же аудиоанализ блокируется из-за AudioContext, который, в теории, позволяет выводить звук.

Пример проверки микрофона

О проблеме впервые сообщили в багтрекере Webkit в декабре, и через шесть дней в Webkit прилетело исправление. Исправление? Не блокировать AudioContext, если страница уже получает аудио и видео.

Так почему вы все еще читаете эту статью? Оказалось, что Chrome сделал ту же ошибку, что и Safari. Хоть это и коснулось многих WebRTC-сервисов, Google помалкивает по этому поводу. Было много попыток заставить их сделать публичное заявление про влияние автоплея на WebRTC, но этого еще не случилось.

Значения MEI мешают тестам


Как мы попали в эту заварушку? Конечно, разработчики должны были тестировать AudioContext eще до изменений в Chrome 66, которые коснулись каждого пользователя. И вот здесь появляется MEI; понимаете, частое взаимодействие со страницей дает вам более высокий MEI, соответственно, разработчики имеют меньше шансов столкнуться с багом, так как аудио уже давно разрешено проигрываться и анализироваться. И да, режим инкогнито не помогает, ведь MEI продолжает считаться и там. Только запуск Chrome с новой учеткой позволит обнаружить баг – факт, который легко могут забыть даже опытные QA-инженеры из Google.

Что должны сделать производители браузеров?


Изменения в основной функциональности трудно выполнить в правильном ключе. Chrome выпустил множество изменений политик автоплея, но ни одно из них не касалось WebRTC или MediaStreams. По-видимому, забытое Permissions API не обновили, так что разработчики не могут тестировать запрос пользовательских действий. Как вариант, можно разрешить AudioContext выводить звук, если страница уже работает с камерой/микрофоном (как это сделал Safari), но это больше смахивает на хак, чем на решение. А еще это не поможет в других случаях анализа звука, когда не используется getUserMedia.

Железобетонное решение для производителей браузеров – позволить media permissions влиять на MEI. Если пользователь дал постоянный доступ к своим камере и микрофону, то логично предположить, что веб-странице можно достаточно доверять, чтобы она воспроизводила звук без дополнительных действий и вне зависимости от того, работает она с камерой/микрофоном или нет. В конце концов, пользователь верит, что вы не будете расшаривать его камеру и микрофон миллионам людей без их ведома. В этом случае воспроизводить звуки интерфейса – наименьшая из проблем.

Как помочь своему приложению


К счастью, есть пара трюков, которые вам помогут. Вот список того, что мы добавили в Confrere, когда столкнулись с проблемой в Safari для iOS.

Добавили playsinline


Чтобы вернуть звук в видео, добавьте атрибут playsinline к видеоэлементу. Этот атрибут хорошо документирован, работает в Safari и Chrome и не имеет побочных эффектов в других браузерах.

Действия пользователя


Чтобы «вылечить» аудиовизуализатор, просто добавьте пользовательское действие. Нам повезло, потому что мы могли добавить (и добавили) на наш тестовый экран множество шагов, подразумевающих явное участие пользователя. Возможно, вам повезло меньше. Пока Google не возьмется за починку, нет другого способа, кроме как вовлекать пользователя.

Нельзя пофиксить звуки интерфейса


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

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

Комментарии (5)


  1. JTG
    16.07.2018 12:34
    +1

    > Как мы попали в эту заварушку?
    C Safari всё ясно, у Apple там своя атмосфера, выбора у пользователя фактически нет и они творят что хотят. Но Chrome семимильными шагами идёт в сторону IE6, несмотря на наличие конкурентов:

    • Chrome игнорирует «autocomplete=off» в полях форм «для удобства пользователей». И без хаков нельзя перекрыть CSS-правило «input:-webkit-autofill» (https://bugs.chromium.org/p/chromium/issues/detail?id=587466)
    • Chrome меняет сигнатуру addEventListener «чтобы сделать прокрутку [в своём браузере] на мобильных устройствах быстрее», делает код несовместимым без соответствующего feature detection (https://developers.google.com/web/updates/2017/01/scrolling-intervention)
    • У Chrome своё понимание CSS-свойства «image-rendering», вероятно, «чтобы сделать отображение сайтов [в своём браузере] быстрее». Если вы хотите получить качество изображений, сжатых CSS, сопоставимое с другими браузерами, нужно использовать «image-rendering: -webkit-optimize-contrast;» (https://bugs.chromium.org/p/chromium/issues/detail?id=562162)
    • Chrome принуждает использовать HSTS в зоне .dev, и это зашито настолько глубоко, что не отключается флагами в chrome://net-internals/#hsts. Да, потому что Google владеец зоны .dev, и её не рекомендуют использовать для локальной разработки, но блин, какого хрена? (https://chromium-review.googlesource.com/c/chromium/src/+/669396/10/net/http/transport_security_state_static.json)

    И это лишь то, с чем лично сталкивался.


    1. sena
      16.07.2018 15:16

      Похожий диалог у меня был с Алиэкспресс — у них не работала загрузка видео через Огнелиса. Потом, правда, исправили.


    1. Anynickname
      16.07.2018 23:49

      Но Chrome семимильными шагами идёт в сторону IE6, несмотря на наличие конкурентов:
      А были ж когда-то всякие Acid Tests, которые нормально так стимулировали браузерописак устранять разного рода HTML-несовместимости. Или сейчас речь о фичах, никакими стандартами неоговоренных?


    1. dartraiden
      17.07.2018 00:37

      Chrome игнорирует «autocomplete=off» в полях форм «для удобства пользователей».

      А вот Firefox поддерживает этот атрибут, в результате, пользователи испытывают неудобство.

      Всё-таки, контент и браузер — для пользователей, а не наоборот.


  1. dartraiden
    17.07.2018 00:52

    Как мы попали в эту заварушку?
    Благодаря рукожопам, которые лепят аудиорекламу на открытие страницы. А благодаря рукожопам, которые закрывают весь контент баннером, скоро браузеры станут такие баннеры резать.