Введение в HTTP Live Streaming: HLS на Android и не только

Опубликовано: 2022-03-11

Потоковое видео является неотъемлемой частью современного интернет-опыта. Он везде: на мобильных телефонах, настольных компьютерах, телевизорах и даже носимых устройствах. Он должен безупречно работать на любом устройстве и в любом типе сети, будь то медленное мобильное соединение, Wi-Fi, защита от брандмауэров и т. д. HTTP Live Streaming (HLS) от Apple был создан именно с учетом этих проблем.

Почти все современные устройства оснащены современным оборудованием, достаточно быстрым для воспроизведения видео, поэтому скорость и надежность сети становятся самой большой проблемой. Почему это? Еще несколько лет назад каноническим способом хранения и публикации видео были протоколы на основе UDP, такие как RTP. Это оказалось проблематичным во многих отношениях, перечислим лишь некоторые из них:

  1. Вам нужен сервер (демон) для потоковой передачи контента.
  2. Большинство брандмауэров настроены на разрешение только стандартных портов и типов сетевого трафика, таких как http, электронная почта и т. д.
  3. Если ваша аудитория глобальна, вам нужна копия службы демона потоковой передачи, работающая во всех основных регионах.

Конечно, вы можете подумать, что все эти проблемы легко решить. Просто храните видеофайлы (например, файлы mp4) на своем http-сервере и используйте свой любимый сервис CDN для их доставки в любую точку мира.

Где традиционное потоковое видео терпит неудачу

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

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

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

Потоковое вещание с адаптивным битрейтом

Хотя вы можете загружать разные версии видео для разных пользователей, вам потребуется иметь возможность управлять их проигрывателями и вычислять, какой поток лучше всего подходит для их соединения и устройства. Затем игроку нужно переключаться между ними (например, когда пользователь переключается с 3G на WiFi). И даже тогда, что если клиент изменит тип сети? Затем плеер должен переключиться на другое видео, но оно должно начать воспроизводиться не с начала, а где-то с середины видео. Так как же рассчитать диапазон байтов для запроса?

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

Это именно то, что решает потоковая передача с адаптивным битрейтом.

Примечание. В этом руководстве по HLS не рассматриваются шифрование, синхронизированное воспроизведение и IMSC1.

Что такое ЗОЖ?

HTTP Live Streaming — это протокол потоковой передачи с адаптивным битрейтом, представленный Apple в 2009 году. Он использует файлы m3u8 для описания медиапотоков и использует HTTP для связи между сервером и клиентом. Это протокол потоковой передачи мультимедиа по умолчанию для всех устройств iOS, но его можно использовать в Android и веб-браузерах.

Иллюстрация обложки HTTP Live Streaming

Основные строительные блоки потоков HLS:

  1. Плейлисты M3U8
  2. Медиафайлы для различных потоков

Плейлисты M3U8

Давайте начнем с ответа на основной вопрос: что такое файлы M3U8 ?

M3U (или M3U8) — это формат простого текстового файла, изначально созданный для организации коллекций файлов MP3. Формат расширен для HLS, где он используется для определения медиапотоков. В HLS есть два типа файлов m3u8:

  • Список воспроизведения мультимедиа: содержит URL-адреса файлов, необходимых для потоковой передачи (т. е. воспроизводимых фрагментов исходного видео).
  • Основной список воспроизведения: содержит URL-адреса списков воспроизведения мультимедиа, которые, в свою очередь, содержат варианты одного и того же видео, подготовленные для разной пропускной способности.

Так называемый URL-адрес прямой трансляции M3U8 — это не что иное, как URL-адреса файлов M3U8, например: https://s3-us-west-2.amazonaws.com/hls-playground/hls.m3u8.

Образец файла M3U8 для потока HLS

Файл M3U8 содержит список URL-адресов или путей к локальным файлам с некоторыми дополнительными метаданными. Строки метаданных начинаются с #.

В этом примере показано, как выглядит файл M3U8 для простого потока HLS:

 #EXTM3U #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-ALLOW-CACHE:YES #EXT-X-TARGETDURATION:11 #EXTINF:5.215111, 00000.ts #EXTINF:10.344822, 00001.ts #EXTINF:10.344822, 00002.ts #EXTINF:9.310344, 00003.ts #EXTINF:10.344822, 00004.ts ... #EXT-X-ENDLIST
  • Первые четыре строки представляют собой глобальные (заголовочные) метаданные для этого плейлиста M3U8.
  • EXT-X-VERSION — это версия формата M3U8 (должно быть не менее 3, если мы хотим использовать записи EXTINF ).
  • Тег EXT-X-TARGETDURATION содержит максимальную продолжительность каждого фрагмента видео. Как правило, это значение составляет около 10 с.
  • Остальная часть документа содержит пары строк, например:
 #EXTINF:10.344822, 00001.ts

Это видео «кусок». Этот представляет фрагмент 00001.ts , который имеет длину ровно 10,344822 секунды. Когда клиентскому видеоплееру необходимо запустить видео с определенного момента в указанном видео, он может легко вычислить, какой файл .ts ему нужно запросить, суммируя продолжительность ранее просмотренных фрагментов. Вторая строка может быть локальным именем файла или URL-адресом этого файла.

Файл M3U8 с его файлами .ts представляет собой простейшую форму потока HLS — список воспроизведения мультимедиа.

Имейте в виду, что не каждый браузер может воспроизводить потоки HLS по умолчанию.

Основной список воспроизведения или индексный файл M3U8

Предыдущий пример M3U8 указывает на серию фрагментов .ts . Они создаются из исходного видеофайла, размер которого изменяется, кодируется и разбивается на куски.

Это означает, что у нас все еще есть проблема, описанная во введении — как насчет клиентов в очень медленных (или необычно быстрых) сетях? Или клиенты в быстрых сетях с очень маленьким размером экрана? Нет смысла транслировать файл в максимальном разрешении, если он не может быть показан во всей красе на вашем блестящем новом телефоне.

M3U8 в HSL

HLS решает эту проблему, вводя еще один «слой» M3U8. Этот файл M3U8 не будет содержать указателей на файлы .ts , но содержит указатели на другие файлы M3U8, которые, в свою очередь, содержат видеофайлы, заранее подготовленные для определенных битрейтов и разрешений.

Вот пример такого файла M3U8:

 #EXTM3U #EXT-X-STREAM-INF:BANDWIDTH=1296,RESOLUTION=640x360 https://.../640x360_1200.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=264,RESOLUTION=416x234 https://.../416x234_200.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=464,RESOLUTION=480x270 https://.../480x270_400.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=1628,RESOLUTION=960x540 https://.../960x540_1500.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=2628,RESOLUTION=1280x720 https://.../1280x720_2500.m3u8

Видеопроигрыватель выберет пары строк, например:

 #EXT-X-STREAM-INF:BANDWIDTH=1296,RESOLUTION=640x360 https://.../640x360_1200.m3u8

Так называются варианты одного и того же видео, подготовленные для разных скоростей сети и разрешений экрана. Этот конкретный файл M3U8 ( 640x360_1200.m3u8 ) содержит фрагменты видеофайла, измененного до размера 640x360 пикселей и подготовленного для битрейта 1296 кбит/с . Обратите внимание, что сообщаемый битрейт должен учитывать как видео-, так и аудиопотоки в видео.

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

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

Если первый поток в списке индекса M3U8 не самый лучший, клиенту потребуется один или два цикла, пока он не остановится на правильном варианте.

Итак, теперь у нас есть три слоя HLS:

  1. Индексный файл M3U8 (основной список воспроизведения) , содержащий указатели (URL) на варианты.
  2. Варианты файлов M3U8 (список воспроизведения мультимедиа) для разных потоков для разных размеров экрана и скорости сети. Они содержат указатели (URL) на файлы .ts.
  3. .ts файлы (чанки) , которые представляют собой двоичные файлы с частями видео.

Вы можете посмотреть пример индексного файла M3U8 здесь (опять же, это зависит от вашего браузера/ОС).

Иногда вы заранее знаете, что клиент находится в медленной или быстрой сети. В этом случае вы можете помочь клиенту выбрать правильный вариант, предоставив индексный файл M3U8 с другим первым вариантом. Есть два способа сделать это.

  • Во-первых, нужно подготовить несколько индексных файлов для разных типов сетей и заранее подготовить клиента для запроса правильного файла. Клиент должен будет проверить тип сети, а затем запросить, например http://.../index_wifi.m3u8 или http://.../index_mobile.m3u8 .
  • Вы также можете убедиться, что клиент отправляет тип сети как часть HTTP-запроса (например, если он подключен к Wi-Fi или мобильному 2G/3G/…), а затем динамически подготавливать индексный файл M3U8 для каждого запроса. Только индексный файл M3U8 нуждается в динамической версии, отдельные потоки (вариантные файлы M3U8) по-прежнему могут храниться как статические файлы.

Подготовка видеофайлов для HLS

Есть два важных строительных блока HTTP Live Streaming от Apple. Один из них — это способ хранения видеофайлов (которые позже будут обслуживаться через HTTP), а другой — это индексные файлы M3U8 , которые сообщают проигрывателю (клиентскому приложению потоковой передачи), где взять какой видеофайл.

Начнем с видеофайлов. Протокол HLS предполагает, что видеофайлы хранятся в меньших фрагментах одинаковой длины, обычно по 10 секунд каждый. Первоначально эти файлы должны были храниться в файлах MPEG-2 TS ( .ts ) и кодироваться в формате H.264 со звуком в формате MP3, HE-AAC или AC-3.

ЗОЖ видео

Это означает, что 30-секундное видео будет разделено на 3 небольших файла .ts , каждый примерно по 10 секунд.

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

Ключевые кадры

Фрагменты должны быть закодированы с ключевым кадром в начале каждого файла. Каждое видео содержит кадры. Кадры — это изображения, но видеоформаты не хранят полные изображения, которые заняли бы слишком много места на диске. Они кодируют только отличие от предыдущего кадра. Когда вы переходите к средней точке видео, плееру нужна «начальная точка», откуда нужно применить все эти различия, чтобы показать начальное изображение, а затем начать воспроизведение видео.

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

Точки останова HLS

Бывают ситуации, когда вы хотите воспроизвести несколько видеоклипов подряд. Один из способов сделать это — объединить исходные видеофайлы, а затем создать потоки HLS с этим файлом, но это проблематично по нескольким причинам. Что делать, если вы хотите показать рекламу до или после вашего видео? Возможно, вы не хотите делать это для всех пользователей и, возможно, вам нужны разные объявления для разных пользователей. И, конечно же, вы не хотите заранее готовить HLS-файлы с разной рекламой.

Чтобы решить эту проблему, существует тег #EXT-X-DISCONTINUITY , который можно использовать в плейлисте m3u8. Эта строка фактически говорит видеоплееру заранее подготовиться к тому, что с этого момента файлы .ts могут создаваться с другой конфигурацией (например, может измениться разрешение). Игроку нужно будет все пересчитывать и, возможно, переходить на другой вариант и нужно быть готовым к таким точкам «разрыва».

Прямая трансляция с HLS

В основном существует два вида «потокового видео». Одним из них является видео по запросу ( VOD ) для видео, записанных заранее и транслируемых пользователю, когда он решит. И есть прямая трансляция . Несмотря на то, что HLS является аббревиатурой HTTP Live Streaming, все, что объяснялось до сих пор, было сосредоточено вокруг VOD, но есть способ сделать прямую трансляцию с помощью HLS.

В ваших файлах M3U8 есть несколько изменений. Во-первых, в вариантном файле M3U8 должен быть тег #EXT-X-MEDIA-SEQUENCE:1 . Затем файл M3U8 не должен заканчиваться #EXT-X-ENDLIST (в противном случае он всегда должен быть помещен в конец).

Пока вы записываете свой стрим, у вас постоянно будут появляться новые файлы .ts . Вам нужно добавить их в список воспроизведения M3U8, и каждый раз, когда вы добавляете новый, счетчик в #EXT-X-MEDIA-SEQUENCE:<counter> должен увеличиваться на 1.

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

ВТТ

Еще одна интересная особенность потоков HLS заключается в том, что вы можете встраивать в них файлы Web Video Text Track (VTT). Файлы VTT можно использовать для различных целей. Например, для веб-проигрывателя HLS можно указать снимки изображений для различных частей видео. Когда пользователь наводит указатель мыши на область таймера видео (под видеопроигрывателем), проигрыватель может отображать снимки с этой позиции в видео.

Еще одно очевидное применение файлов VTT — субтитры. Поток HLS может указывать несколько субтитров для нескольких языков:

 #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-,NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="en",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/eng/prog_index.m3u8"

Тогда theprog_index.m3u8 выглядит так:

 #EXTM3U #EXT-X-TARGETDURATION:30 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:30, 0000.webvtt #EXTINF:30, 0001.webvtt ...

Фактический VTT (например 0000.webvtt ):

 WEBVTT X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000 00:00:01.000 --> 00:00:03.000 Subtitle -Unforced- (00:00:01.000) 00:00:03.000 --> 00:00:05.000 <i>...text here... -Unforced- (00:00:03.000)</i> <i>...text here...</i>

В дополнение к файлам VTT Apple недавно объявила, что HLS будет поддерживать IMSC1, новый формат субтитров, оптимизированный для потоковой доставки. Его наиболее важным преимуществом является то, что его можно стилизовать с помощью CSS.

Инструменты HTTP Live Streaming и потенциальные проблемы

Apple представила ряд полезных инструментов HSL, которые более подробно описаны в официальном руководстве по HLS.

  • Для прямых трансляций Apple подготовила инструмент под названием mediastreamsegmenter для создания файлов сегментов на лету из текущего видеопотока.
  • Еще один важный инструмент — mediastreamvalidator . Он проверит ваши списки воспроизведения M3U8, загрузит видеофайлы и сообщит о различных проблемах. Например, когда сообщаемый битрейт не совпадает с рассчитанным по файлам .ts.
  • Конечно, когда вам нужно кодировать/декодировать/мультиплексировать/демультиплексировать/фрагментировать/разделить/объединить/объединить/… видео/аудио файлы, есть ffmpeg. Будьте готовы скомпилировать свои собственные версии ffmpeg для конкретных случаев использования.

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

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

Пример HTTP-трансляции в прямом эфире

Я подготовил приложение HLS для Android, которое транслирует предопределенный HLS с помощью проигрывателя ExoPlayer от Google. Он покажет видео и список «событий» HLS под ним. Эти события включают в себя: каждый загруженный файл .ts или каждый раз, когда проигрыватель решает переключиться на поток с более высоким или более низким битрейтом.

Давайте пройдемся по основным частям инициализации вьювера. На первом этапе мы получим текущий тип подключения устройства и используем эту информацию, чтобы решить, какой файл m3u8 нужно получить.

 String m3u8File = "hls.m3u8"; ConnectivityManager connectivity = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = connectivity.getActiveNetworkInfo(); if (activeNetwork != null && activeNetwork.isConnectedOrConnecting()) { int type = activeNetwork.getType(); int subType = activeNetwork.getSubtype(); if (type == ConnectivityManager.TYPE_MOBILE && subType == TelephonyManager.NETWORK_TYPE_GPRS) { m3u8File = "hls_gprs.m3u8"; } } String m3u8URL = "https://s3-us-west-2.amazonaws.com/hls-playground/" + m3u8File;

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

Помните, что первый вариант в файле m3u8 — это тот, с которого начнет просмотрщик. Так как мы находимся на стороне клиента и можем определить тип подключения, мы можем хотя бы попытаться избежать переключения начального игрока между вариантами, запросив файл m3u8 , который заранее подготовлен для этого типа подключения.

На следующем шаге мы инициализируем и запускаем наш HLS-плеер:

 Handler mainHandler = new Handler(); DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder() .setEventListener(mainHandler, bandwidthMeterEventListener) .build(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); LoadControl loadControl = new DefaultLoadControl(); SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);

Затем подготавливаем плеер и скармливаем ему нужный m3u8 для данного типа сетевого подключения:

 DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "example-hls-app"), bandwidthMeter); HlsMediaSource videoSource = new HlsMediaSource(Uri.parse(m3u8URL), dataSourceFactory, 5, mainHandler, eventListener); player.prepare(videoSource);

И вот результат:

Пример потоковой передачи HLS в Android

Совместимость с браузером HLS, будущие разработки

Apple требует , чтобы приложения для потоковой передачи видео на iOS использовали HLS, если видео длится более 10 минут или превышает 5 МБ. Это само по себе является гарантией того, что HLS никуда не денется. Были некоторые опасения по поводу HLS и MPEG-DASH и того, какой из них станет победителем на арене веб-браузеров. HLS реализован не во всех современных браузерах (вы, вероятно, заметили это, если щелкнули предыдущие примеры URL-адресов m3u8). На Android, например, в версиях ниже 4.0 вообще работать не будет. С 4.1 до 4.4 работает только частично (например, отсутствует звук, или отсутствует видео, но работает звук).

Но в последнее время эта «битва» стала немного проще. Apple объявила, что новый протокол HLS позволит использовать фрагментированные файлы mp4 ( fMP4 ). Раньше, если вы хотели иметь поддержку HLS и MPEG-DASH, вам приходилось дважды кодировать видео. Теперь вы сможете повторно использовать одни и те же видеофайлы и переупаковывать только файлы метаданных ( .m3u8 для HLS и .mpd для MPEG-DASH).

Еще одно недавнее объявление — поддержка высокоэффективного видеокодека (HEVC). Если он используется, он должен быть упакован в фрагментированные файлы mp4. И это, вероятно, означает, что будущее HLS — это fMP4 .

Текущая ситуация в мире браузеров такова, что только некоторые браузерные реализации <video> будут воспроизводить HLS из коробки. Но есть решения с открытым исходным кодом и коммерческие решения, которые предлагают совместимость с HLS. Большинство из них предлагают HLS с запасным вариантом Flash, но есть несколько реализаций, полностью написанных на JavaScript.

Подведение итогов

Эта статья посвящена исключительно HTTP Live Streaming, но концептуально ее также можно рассматривать как объяснение того, как работает Adaptive Bitrate Streaming (ABS). Подводя итог, можно сказать, что HLS — это технология, которая решает множество важных проблем в потоковом видео:

  • Это упрощает хранение видеофайлов
  • CDN
  • Клиентские проигрыватели, работающие с разной пропускной способностью клиента и переключающиеся между потоками
  • Субтитры, шифрование, синхронизированное воспроизведение и другие функции, не описанные в этой статье.

Независимо от того, используете ли вы в конечном итоге HLS или MPEG-DASH, оба протокола должны предлагать схожие функции, а с введением фрагментированного mp4 (fMP4) в HLS вы можете использовать одни и те же видеофайлы. Это означает, что в большинстве случаев вам необходимо понимать основы обоих протоколов. К счастью, кажется, что они движутся в одном направлении, что должно облегчить их освоение.