HTTP Live Streaming (HLS) — это технология воспроизведения потокового видео по протоколу HTTP, разработанная Apple. HLS видеопоток кодируется в H.264 и AAC и проигрывается на любом совместимом устройстве, браузере или плеере.
Web Call Server конвертирует в HLS видео, полученное из других поддерживаемых источников трансляции, таких как веб-камеры и профессиональные устройства видеозахвата, SIP-звонки и т.д..
Chrome | Firefox | Safari 11 | Edge | |
---|---|---|---|---|
Windows | + | + | + | |
Mac OS | + | + | + | |
Android | + | + | ||
iOS | + | + | + |
1. Для теста используем:
2. Откройте веб-приложение Two Way Streaming. Нажмите Connect, затем Publish. Скопируйте идентификатор потока:
3. Откройте веб-приложение HLS Player Minimal. Укажите в поле Stream идентификатор потока и нажмите Play. начнется воспроизведение потока:
Ниже описана последовательность вызовов при использовании примера HLS Player Minimal для воспроизведения потока по HLS
1. Обращение к серверу и воспроизведение.
var player = videojs('remoteVideo'); |
Настройка URL HLS
player.src({ src: $("#urlServer").val() + "/" + streamName + "/" + streamName + ".m3u8", type: "application/vnd.apple.mpegurl" }); |
Запуск воспроизведения
player.play(); |
2. Получение HLS-потока от сервера
При необходимости, может быть настроена аутентификация клиентов для воспроизведения потока по HLS. В файле flashphoner.properties должна быть установлена настройка
hls_auth_enabled=true |
При обращении к потоку на клиенте в HLS URL необходимо добавить параметр с указанием токена, полученного, например, от бэкенд-сервера. Наименование параметра задается настройкой
client_acl_property_name=aclAuth |
В этом случае, обращение к потоку должно быть сформировано следующим образом:
var src = $("#urlServer").val() + "/" + streamName + "/" + streamName + ".m3u8"; var token = $("#token").val(); if (token.length > 0) { src += "?aclAuth=" +token; } |
На бэкенд-сервере должен быть реализован REST hook /playHLS. WCS сервер отправлет на бэкенд запрос, содержащий полученный от клиента токен
URL:http://localhost:8081/apps/EchoApp/playHLS OBJECT: { "nodeId" : "NTk1tLorQ00llGbPJuFexrKceubGCR0k@192.168.1.5", "appKey" : "defaultApp", "sessionId" : "/192.168.1.100:59473/192.168.1.5:8445", "mediaSessionId" : "60709c5b-6950-40c3-8a3d-37ea0827ae32-727473703a2f2f73747238312e63726561636173742e636f6d2f6772616e646c696c6c6574762f6c6f77-HLS", "name" : "test", "mediaProvider" : "HLS", "custom" : { "aclAuth" : "12345789" } } |
Бэкенд сервер должен вернуть 200 OK, если токен клиента проходит проверку, и 403 Forbidden, если не проходит. В свою очередь, клиент получает либо HLS поток, либо 401 Unauthorized.
Настройка
hls_auth_token_cache=10 |
задает время кэширования токена в секундах (по умолчанию 10 секунд). До тех пор, пока токен находится в кэше, т.е. либо есть подписчик потока с таким токеном, либо не истекло указанное время, запросы /playHLS с этим токеном не отправляются на бэкенд. Если настройка кэширования установлена в 0
hls_auth_token_cache=0 |
запросы /playHLS отправляются на бэкенд при каждом HTTP GET запросе от клиента.
Эти настройки могут быть изменены без перезапуска сервера. При этом настройка hls_auth_enabled
влияет на существующих подписчиков, а настройка hls_auth_token_cache
на новые подключения.
Для снижения нагрузки на сервер, проверка токена, а также доступности потока в CDN производится при запросе плейлиста. Для защиты отдельных сегментов в сборке 5.2.436 добавлена настройка
hls_segment_name_suffix_randomizer_enabled=true |
В этом случае к имени файла сегмента добавляется случайным образом сгенерированный суффикс, например
test16d2da4658f4374953a120f3c95bc715ea.ts |
Таким образом, исключается перебор сегментов на стороне клиента.
Отметим, что суффикс не добавляется к сегментам прелоадера.
По умолчанию, в ответ 200 OK на запрос HTTP GET добавляются следующие заголовки:
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET Access-Control-Max-Age: 3000 |
При необходимости, если, например, воспроизводимый контент и страница HLS плеера находятся в разных доменах, можно добавить собственные заголовки при помощи следующей настройки в файле flashphoner.properties:
hls_access_control_headers=Access-Control-Allow-Origin: *;Access-Control-Allow-Methods: GET, HEAD;Access-Control-Max-Age: 3000;Access-Control-Expose-Headers: Accept-Ranges, Content-Range, Content-Encoding, Content-Length |
В этом случае в ответ 200 OK будут добавлены заголовки, перечисленные в настройке:
В некоторых случаях для воспроизведения потока с сервера по HLS может быть использован веб-сервер nginx в качестве обратного прокси. Как правило, это может потребоваться для обхода ограничений на кросс-доменные запросы к различным портам, если добавление HTTP-заголовков не помогает.
Например, если браузер требует, чтобы страница HLS-плеера и HLS-поток находились в одном домене your.domain
и были доступны по одному и тому же порту 443 (HTTPS), nginx должен быть настроен следующим образом:
# Перенаправляем HTTP-запросы с 80 порта на 443 server { listen 80; server_name docs.flashphoner.com; return 301 https://$server_name$request_uri; } # Сервер обслуживает HTTPS порт 443 server { listen 443 ssl; ssl_certificate /etc/letsencrypt/live/your.domain/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your.domain/privkey.pem; server_name your.domain; server_tokens off; client_max_body_size 500m; proxy_read_timeout 10m; root /usr/share/nginx/html; location / { } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } # Примеры веб-приложений будут доступны напрямую по адресу https://your.domain/client2 location /client2/ { alias /usr/local/FlashphonerWebCallServer/client2/; } # Плейлисты и сегменты проксируются в наш домен на 443 порт, например https://your.domain/test.m3u8 location ~* ^.+.(m3u8|ts)$ { proxy_pass https://localhost:8445; proxy_http_version 1.1; proxy_set_header Host $server_name:$server_port; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } |
Может оказаться полезным кэширование HLS-потока. В этом случае nginx должен быть настроен следующим образом:
1. В секции http
файла настроек /etc/nginx.conf указываются параметры кэша
proxy_cache_path /var/cache/nginx/proxy levels=1:2 keys_zone=proxy_cache:1024m max_size=2048m inactive=10d; proxy_cache_min_uses 1; proxy_ignore_headers X-Accel-Expires; proxy_ignore_headers Expires; proxy_ignore_headers Cache-Control; |
2. В секции server
файла настроек сайта настраивается кэширование HLS-сегментов, при этом плейлисты не должны кэшироваться:
location ~* ^.+.(ts)$ { proxy_pass https://localhost:8445; proxy_http_version 1.1; proxy_set_header Host $server_name:$server_port; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_cache proxy_cache; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 2m; } location ~* ^.+.(m3u8)$ { proxy_pass https://localhost:8445; proxy_http_version 1.1; proxy_set_header Host $server_name:$server_port; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_cache off; expires -1; } |
Еще один способ обхода ограничений на кросс-доменные запросы в браузере - отображение статического контента, например, страницы плеера, на том же порту, который отдает HLS контент. Чтобы включить данную возможность, необходимо указать следующую настройку в файле flashphoner.properties
hls_static_enabled=true |
Страница плеера должна располагаться в каталоге, определяемом настройкой
hls_static_dir=client2/examples/demo/streaming/hls_static |
В данном случае (по умолчанию), путь к файлам страницы указан относительно каталога установки WCS. Может быть указан и полный путь, например
hls_static_dir=/var/www/html/hls_static |
Если отображение статического контента включено, при обращении к WCS серверу по адресу https://host:8445/hls-player.html браузер отобразит страницу HLS плеера. Если данная возможность отключена, при обращении по такому адресу сервер вернет ошибку 404 Not found.
При подключении первого HLS-подписчика к потоку, в особенности к потоку из CDN, необходимо определенное время, чтобы началась нарезка потока на HLS-сегменты, и был сформирован плейлист. В результате, браузер Safari на устройствах iOS может не подключиться к потоку по HLS с первой попытки. Чтобы подключение всегда проходило успешно, в сборке 5.2.371 добавлено воспроизведение прелоадера. Прелоадер по умолчанию выглядит следующим образом
В сборке 5.2.408 прелоадеры разделены по соотношениям сторон картинки потока: 16:9, 4:3, 2:1
Сегменты прелоадера по умолчанию записываются в каталог /usr/local/FlashphonerWebCallserver/hls/.preloader
при запуске сервера
tree /usr/local/FlashphonerWebCallServer/hls/.preloader /usr/local/FlashphonerWebCallServer/hls/.preloader ├── 16x9 │ ├── index0.ts │ ├── index10.ts │ ├── index11.ts │ ├── index12.ts │ ├── index13.ts │ ├── index14.ts │ ├── index15.ts │ ├── index16.ts │ ├── index17.ts │ ├── index18.ts │ ├── index19.ts │ ├── index1.ts │ ├── index2.ts │ ├── index3.ts │ ├── index4.ts │ ├── index5.ts │ ├── index6.ts │ ├── index7.ts │ ├── index8.ts │ └── index9.ts ├── 2x1 │ ├── index0.ts │ ├── index10.ts │ ├── index11.ts │ ├── index12.ts │ ├── index13.ts │ ├── index14.ts │ ├── index15.ts │ ├── index16.ts │ ├── index17.ts │ ├── index18.ts │ ├── index19.ts │ ├── index1.ts │ ├── index2.ts │ ├── index3.ts │ ├── index4.ts │ ├── index5.ts │ ├── index6.ts │ ├── index7.ts │ ├── index8.ts │ └── index9.ts └── 4x3 ├── index0.ts ├── index10.ts ├── index11.ts ├── index12.ts ├── index13.ts ├── index14.ts ├── index15.ts ├── index16.ts ├── index17.ts ├── index18.ts ├── index19.ts ├── index1.ts ├── index2.ts ├── index3.ts ├── index4.ts ├── index5.ts ├── index6.ts ├── index7.ts ├── index8.ts └── index9.ts |
Минимальная длительность одного сегмента прелоадера по умолчанию составляет 2 секунды, и может быть задана в миллисекундах при помощи настройки
hls_preloader_time_min=2000 |
При необходимости, прелоадер может быть отключен, эта возможность доступна, начиная со сборки 5.2.396. Для отключения HLS прелоадера используется параметр
hls_preloader_enabled=false |
Чтобы заменить прелоадер по умолчанию на собственный, необходимо сделать следующее:
1. Выбрать видеоклип (например, логотип) в трех соотношениях сторон: 16:9, 4:3, 2:1
2. С помощью ffmpeg закодировать видео в H264, добавить к видеоклипу аудиодорожку, задать периодичность ключевых кадров и убрать B-фреймы
ffmpeg -i clip16x9.mp4 -f lavfi -i anullsrc=channel_layout=mono:sample_rate=44100 -c:v h264 -g 30 -bf 0 -shortest 16x9/preloader16x9.mp4 ffmpeg -i clip4x3.mp4 -f lavfi -i anullsrc=channel_layout=mono:sample_rate=44100 -c:v h264 -g 30 -bf 0 -shortest 4x3/preloader4x3.mp4 ffmpeg -i clip2x1.mp4 -f lavfi -i anullsrc=channel_layout=mono:sample_rate=44100 -c:v h264 -g 30 -bf 0 -shortest 2x1/preloader2x1.mp4 |
3. Загрузить и установить инструменты для подготовки HLS сегментов с сайта Apple
4. Подготовить HLS сегменты прелоадера, указав длительность сегмента, например, 2 секунды
cd 16x9 mediafilesegmenter -t 2 -B index -start-segments-with-iframe preloader16x9.mp4 tar -cvzf preloader.tar.gz index*.ts |
Этот шаг необходимо повторить для всех соотношений сторон.
5. На сервере создать каталог для прелоадера
mkdir /opt/custom_preloader mkdir /opt/custom_preloader/16x9 mkdir /opt/custom_preloader/4x3 mkdir /opt/custom_preloader/2x1 |
6. Распаковать прелоадер из архива, подготовленного на шаге 4
cd /opt/custom_preloader/16x9 tar -xvzf ~/preloader16x9.tar.gz |
Этот шаг также необходимо повторить для всех соотношений сторон
7. Указать в настройках сервера расположение прелоадера и длительность одного сегмента
hls_preloader_time_min=2000 hls_preloader_dir=/opt/custom_preloader |
REST-запрос должен быть HTTP/HTTPS POST запросом в таком виде:
Здесь:
REST-метод | Пример тела REST-запроса | Пример тела REST-ответа | Статусы ответа | Описание | |
---|---|---|---|---|---|
/hls/startup |
| 200 - OK 404 - Stream not found 500 - Internal error | Запустить HLS нарезку указанного потока | ||
/hls/find_all |
| 200 – OK 404 – Not found | Найти все потоки, для которых есть HLS нарезки | ||
/hls/terminate |
| 200 – OK 404 – Not found | Завершить или перезапустить HLS нарезку указанного потока |
Имя параметра | Описание | Пример |
---|---|---|
name | Имя потока, опубликованного на сервере | test |
1. Если HLS нарезка потока запущена при помощи REST запроса /hls/startup, и нет активных HLS подписчиков, нарезка остановится по истечении интервала в секундах
hls_manager_provider_timeout=300 |
По умолчанию, интервал составляет 5 минут. То же касается автоматически созданных HLS нарезок при установленной настройке
hls_auto_start=true |
2. Если HLS нарезка потока останавливается при помощи REST запроса /hls/terminate, и есть активные HLS подписчики, то нарезка будет перезапущена. При этом активные HLS подписчики должны повторно подключиться к потоку.
В сборке 5.2.484 добавлена поддержка HLS ABR плейлистов. Использование этой возможности включается при помощи настройки
hls_master_playlist_enabled=true |
Имя основного плейлиста указывается при помощи настройки
hls_manifest_file=index.m3u8 |
Браузер должен запросить основной плейлист по URL
https://wcs_address:8445/streamName/index.m3u8 |
Здесь
При запросе основного плейлиста сервер проверяет наличие потоков согласно профилям транскодинга, перечисленным в файле настроек cdn_profiles.yml, например:
profiles: -720p: video: height: 720 bitrate: 1000 codec: h264 -480p: video: height: 480 bitrate: 1000 codec: h264 -240p: video: height: 240 bitrate: 400 codec: h264 |
Все потоки по профилям, которые в момент запроса опубликованы на сервере, попадают в основной плейлист, например:
#EXTM3U #EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=1280x720,CODECS="avc1.42e01f,mp4a.40.2" ../streamName-720p/streamName-720p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=852x480,CODECS="avc1.42e01f,mp4a.40.2" ../streamName-480p/streamName-480p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=400000,RESOLUTION=426x240,CODECS="avc1.42e01f,mp4a.40.2" ../streamName-240p/streamName-240p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,CODECS="avc1.42e01f,mp4a.40.2" ../streamName/streamName.m3u8 |
Затем браузер, в зависимости от пропускной способности канала, переключается между потоками, перечисленными в основном плейлисте.
В момент, когда браузер запрашивает основной плейлист, потоки уже должны быть опубликованы на сервере и нарезаться на HLS сегменты |
Чтобы обеспечить наличие потоков, необходимо:
1. На отдельно стоящем сервере:
1.1. Периодически проверять, транскодируются ли потоки к указанным параметрам, и запускать транскодинг при необходимости при помощи REST API
curl -s -X POST -d "{\"uri\":\"transcoder://tcode_test-240p\",\"remoteStreamName\":\"test\",\"localStreamName\":\"test-240p\",\"encoder\":{\"width\":320,\"height\":240}}" http://localhost:8081/rest-api/transcoder/startup curl -s -X POST -d "{\"uri\":\"transcoder://tcode_test-480p\",\"remoteStreamName\":\"test\",\"localStreamName\":\"test-480p\",\"encoder\":{\"width\":640,\"height\":480}}" http://localhost:8081/rest-api/transcoder/startup curl -s -X POST -d "{\"uri\":\"transcoder://tcode_test-720p\",\"remoteStreamName\":\"test\",\"localStreamName\":\"test-720p\",\"encoder\":{\"width\":1280,\"height\":720}}" http://localhost:8081/rest-api/transcoder/startup |
1.2. Периодически запускать HLS потоки для включения в основной плейлист, например
curl -s -X POST -d "{\"name\":\"test\"}" http://localhost:8081/rest-api/hls/startup sleep 1 curl -s -X POST -d "{\"name\":\"test-240p\"}" http://localhost:8081/rest-api/hls/startup sleep 1 curl -s -X POST -d "{\"name\":\"test-480p\"}" http://localhost:8081/rest-api/hls/startup sleep 1 curl -s -X POST -d "{\"name\":\"test-720p\"}" http://localhost:8081/rest-api/hls/startup |
2. На Edge сервере в CDN периодически запрашивать HLS потоки по профилям, например
curl -s http://localhost:8082/test/test.m3u8 sleep 1 curl -s http://localhost:8082/test-240p/test-240p.m3u8 sleep 1 curl -s http://localhost:8082/test-480p/test-480p.m3u8 sleep 1 curl -s http://localhost:8082/test-720p/test-720p.m3u8 sleep 1 |
Для отчета об ошибке можно, используя CLI, включить сбор отладочных логов для HLS-сессий
update node-setting --value true hls_enable_session_debug |
Следует учесть, что файл настроек flashphoner.properties будет перезаписан после этой команды.
1. Невосстанавливаемый фриз HLS потока при воспроизведении в iOS Safari через CDN
Симптомы: через одну минуту после начала публикации изображение останавливается, звук продолжает воспроизводиться
Решение:
а) включить транскодинг на сервере при помощи настройки в файле flashphoner.properties
disable_streaming_proxy=true |
б) если включение транскодинга нежелательно, установить в файле flashphoner.properties
hls_discontinuity_enabled=true |
в этом случае возможны щелчки в аудиопотоке, но изображение останавливаться не будет.
2. Прекращение записи сегментов HLS при воспроизведении потока. опубликованного в браузере Firefox
Симптомы: через несколько минут после начала воспроизведения прекращается запись HLS-сегментов, при этом директория потока в директории hls не удаляется, в логе сервера продолжают появляться сообщения
INFO HLSStreamManager - HLSStreamProviderKeepaliveThread-80 Remove hls channel |
Для восстановления публикующая сторона должна заново опубликовать поток.
Решение: использовать другой браузер для публикации потока, который предполагается воспроизводить по HLS.
3. При воспроизведении HLS в Safari в iOS 12.4 не играет видео для первого подписчика
Симптомы: при подключении первого подписчика к потоку по HLS в Safari в iOS 12.4 видео не воспроизводится, если HLS-подписчики уже есть, видео играет нормально
Решение: установить минимальное количество сегментов в плейлисте HLS не менее 2
hls_min_list_size=2 |
4. При воспроизведении RTMP-потока как HLS в Safari в iOS 12.4 не играет видео для любого подписчика, если активна настройка
hls_auto_start=true |
Симптомы: при подключении подписчиков к RTMP потоку по HLS в Safari в iOS 12.4 видео не воспроизводится
Решение: при публикации файла со звуковой дорожкой стерео, использовать моно звук, например, для ffmpeg указать настройку
-acodec aac -ac 1 |
5. Если по HLS проигрывается поток, транскодированный в CDN, и если при этом изменяется соотношение сторон потока, HLS прелоадер отображается в соответствии с соотношением сторон оригинального потока
Симптомы: при заказе транскодированного потока с указанием профиля в имени, например test-640x480p, отображается прелоадер 16:9, если исходный поток опубликован в разрешении 1280x720
Решение: включить на транскодере сохранение соотношения сторон
video_transcoder_preserve_aspect_ratio=true |