RTSP, HLS, DASH, MPEG-TS
Источники, за которыми сервер ходит сам: формы адресов, учётные данные, транспорт RTSP, приём MPEG-TS по UDP и multicast, файл и синтетический источник.
Во всех источниках этой страницы соединение устанавливает сервер. Он знает, жив ли источник, сам переподключается при обрыве и показывает состояние в панели и в API. Обратный случай — когда источник публикует поток к вам — описан в Приёме публикации.
Проверить любой пример до перезапуска службы:
hastreamer --validate /etc/hastreamer/hastreamer.conf
/etc/hastreamer/hastreamer.conf: OK (3 streams)
RTSP#
Основной источник для IP-камер и видеорегистраторов.
stream cam1 { input rtsp://10.0.0.10:554/Streaming/Channels/101; }
Учётные данные передаются в адресе — отдельных директив логина и пароля нет:
stream cam1 { input rtsp://admin:пароль@10.0.0.10:554/Streaming/Channels/101; }
Поддерживаются Basic и Digest — способ выбирает камера, настраивать его не нужно. Если пароль
содержит @, :, /, ? или #, закодируйте его процентами: p@ss → p%40ss. Строку с
пробелами или спецсимволами конфигурации возьмите в кавычки:
input "rtsp://admin:p%40ss@10.0.0.10:554/Streaming/Channels/101";
Подстановки переменных окружения в конфигурации нет — значения записываются как есть. Ограничьте
права на файл: chmod 640 /etc/hastreamer/hastreamer.conf. В журнале и в API адрес источника
отдаётся с вырезанными учётными данными.
Транспорт RTSP#
Медиа по RTSP переносится либо внутри того же TCP-соединения, либо отдельными потоками UDP. Выбор задаётся глобальной директивой:
source_transport tcp; # tcp (по умолчанию) | udp | udp-then-tcp
| Параметр | Тип и допустимые значения | По умолчанию | Применение |
|---|---|---|---|
source_transport |
tcp · udp · udp-then-tcp |
tcp |
рестарт |
| Значение | Поведение |
|---|---|
tcp |
RTP/AVP/TCP;interleaved= — одно соединение, дружелюбно к NAT и межсетевым экранам |
udp |
RTP/AVP;unicast;client_port= — отдельные UDP-порты. Отката на TCP нет |
udp-then-tcp |
сначала UDP; если камера ответила 461 Unsupported Transport на первую дорожку, вся сессия переходит на TCP (смешанных сессий не бывает) |
Ошибочное значение отвергается при загрузке конфигурации:
source_transport must be "tcp", "udp", or "udp-then-tcp" (got "auto")
source_transport с rtsp_transportssource_transport — как сервер забирает поток у камеры, директива глобальная и на отдельный
поток не переопределяется. rtsp_transports — что разрешено клиенту, который смотрит ваш поток
по RTSP; она задаётся внутри stream. Это самая частая путаница в этом разделе. См.
Протоколы раздачи.
RTSP поверх TLS#
stream cam1 { input rtsps://admin:пароль@10.0.0.10:322/Streaming/Channels/101; tls_verify system; }
Значение tls_verify |
Что означает |
|---|---|
"" или system |
проверять по системным корневым сертификатам (по умолчанию) |
insecure |
не проверять сертификат вовсе |
pin:<64 hex> |
принимать только сертификат с этим отпечатком SHA-256 от DER конечного сертификата |
ca:<путь> |
проверять по своему корневому сертификату из файла |
Значение проверяется при загрузке конфигурации. Если при работе спецификация оказалась негодной, поток паркуется с видимой ошибкой, а не уходит в бесконечный цикл переподключений.
pin:openssl s_client -connect 10.0.0.10:322 -servername 10.0.0.10 </dev/null 2>/dev/null \ | openssl x509 -outform der | openssl dgst -sha256 -r | cut -d' ' -f1
3b1f0c9d2a7e5148c6b0d4a29f8e7315c2d4b6a80f1e3c5978a2b4d6e8f0a1c3
HLS, DASH и MPEG-TS по HTTP#
Одна директива обслуживает три протокола: формат определяется автоматически по началу тела ответа.
stream relay1 { input https://origin.example.com/live/index.m3u8; } # HLS stream relay2 { input https://origin.example.com/live/manifest.mpd; } # DASH stream relay3 { input https://origin.example.com/live/stream.ts; } # непрерывный MPEG-TS
| Что обнаружено | Как обрабатывается |
|---|---|
адрес заканчивается на .mpd |
DASH — быстрый путь, без анализа тела |
тело начинается с #EXTM3U |
HLS: варианты fMP4 и TS, части LL-HLS, блокирующая перезагрузка |
тело начинается с байта 0x47 (подтверждено шагом в 188 байт) |
непрерывный MPEG-TS по HTTP |
| что-то другое | ошибка UnknownHttpSource |
Приём HLS понимает EXT-X-GAP, EXT-X-BYTERANGE, разрывы, многофрагментные CMAF-сегменты и
вариант TS без EXT-X-MAP. Приём LL-HLS и DASH встаёт на живой край с блокирующими запросами.
Учётные данные передаются так, как их ждёт источник: в адресе (https://логин:пароль@…) или
строкой запроса (?token=…). Доверие к сертификату настраивается тем же tls_verify.
Тайм-ауты для всего HTTP-семейства: подключение 5 с, чтение 10 с. Они не настраиваются; у непрерывного тела MPEG-TS намеренно нет общего срока на весь запрос.
MPEG-TS по UDP и multicast#
stream tv1 { input udp://@239.1.1.1:1234; } # групповой stream tv2 { input udp://239.1.1.1:1234; } # то же: @ необязателен stream tv3 { input udp://192.168.1.1:1234; } # одноадресный stream tv4 { input multicast://@239.1.1.1:1234?iface=10.0.0.5; } # синоним, тот же разбор stream tv5 { input udp://@239.1.1.1:1234?source=10.0.0.7&iface=10.0.0.5; } # SSM-подписка (S,G)
| Параметр адреса | Значение |
|---|---|
?iface=<IPv4> |
интерфейс, на котором выполняется подписка и приём. 0.0.0.0 означает «пусть выберет ядро», а не фильтр |
?source=<IPv4> |
подписка на конкретный источник (IGMPv3 SSM). Только Linux |
Имя хоста и IPv6 в этих схемах не принимаются. Адрес источника в ?source= обязан быть
одноадресным — групповой, широковещательный или испорченный отвергается при загрузке конфигурации,
молчаливого отката на «любой источник» нет:
stream "tv5": input "udp://@239.1.1.1:1234?source=239.9.9.9": SSM source "239.9.9.9" must be a unicast IPv4 (not multicast/broadcast/unspecified/garbage)
Внутри на порт и ядро создаётся один сокет, а датаграммы разбираются по группам; у каждого потока своя ограниченная очередь, поэтому один застрявший потребитель не блокирует исправных. Размер приёмного буфера и глубина очереди зашиты в сборку.
udp_ingest_* относятся не сюдаДирективы udp_ingest_rcvbuf, udp_ingest_shards, udp_ingest_pool_slots и udp_pull_rate
настраивают маршрутизатор RTSP поверх UDP, а не приём udp://. При значении
source_transport tcp по умолчанию они вообще ни на что не влияют.
RTMP: сервер забирает поток#
stream cam1 { input rtmp://origin.example.com/live/cam1; } stream cam2 { input rtmp://логин:пароль@origin.example.com/live/cam2; } stream cam3 { input rtmps://origin.example.com/live/cam3; }
Форма адреса — rtmp(s)://[логин:пароль@]хост[:порт]/<приложение>/<поток>. Поддерживается
запросно-ответная аутентификация, если её требует источник. Не путайте эту схему с publish://:
здесь сервер подключается к чужому RTMP-серверу, а не ждёт публикации.
Файл как живой поток#
vod library /srv/media; stream loop1 { input file:///srv/media/loop.mp4; }
Файл проигрывается по кругу в реальном темпе и во всём остальном ведёт себя как живой источник: записывается в архив, раздаётся по HLS, DASH, RTSP и WebRTC.
| Параметр адреса | По умолчанию | Значения |
|---|---|---|
loop |
true |
false, 0, no, off — проиграть один раз |
pace |
realtime |
burst — отдавать так быстро, как получится |
start |
0 |
смещение начала в секундах |
input file:///srv/media/loop.mp4?loop=false&pace=burst&start=30;
file:// обязательно нужна объявленная библиотека VoDПуть проверяется по списку корней, объявленных директивами vod <имя> <корень>, и проверка работает
«на запрет»: если ни одной библиотеки нет, список корней пуст и любой источник file://
отвергается. Проверка конфигурации это не покажет — поток не запустится при старте. Принимаются
только расширения .mp4, .m4v и .mov; путь приводится к каноническому виду, поэтому выйти за
корень по символической ссылке нельзя. См. VoD — видео по запросу.
Пока поток живёт, весь файл удерживается в памяти. Десять потоков одного файла на 1 ГиБ — это 10 ГиБ. Берите короткие ролики.
Синтетический источник demo://#
Встроенный тест-паттерн: аналоговый циферблат с секундной стрелкой, цифровые часы ЧЧ:ММ:СС,
счётчик кадров и полоса с кодеком, разрешением и частотой кадров. Ни камеры, ни файла не требуется.
stream demo { input demo://; } stream demo-hd { input demo://?res=1280x720&fps=25&vc=h264&ac=aac&motion=30; }
Принимаются формы demo://, demo:, demo и просто ?res=….
| Параметр | Синонимы | По умолчанию | Допустимые значения |
|---|---|---|---|
res |
— | 320x240 |
ШxВ (разделитель x или X); ширина 1–7680, высота 1–4320. Округляется вверх до кратного 16 |
fps |
— | 25 |
1–120 и обязательно делитель числа 90000 |
vc |
codec |
h264 |
h264 · avc · avc1 · пусто |
ac |
sound, audio |
opus |
opus · пусто · aac · g711 · pcmu · none · false · 0 · off |
motion |
— | 0 |
0–100 — доля кадра, занятая бегущей полосой шума. Ограничивается молча, ошибкой не является |
Неизвестные параметры игнорируются молча — это сделано намеренно, ради совместимости вперёд.
Звуковые дорожки: AAC-LC 48 кГц моно, Opus 48 кГц моно или G.711 µ-law 8 кГц моно, с жёстко синхронизированным звуком. Кодировщик намеренно дешёвый, поэтому демонстрационный поток не способен занять ядро; потоки на одном ядре с одинаковыми параметрами делят один экземпляр кодировщика.
demo:// полезен на практикеРазверните сервер и заведите stream demo { input demo://; } ещё до того, как появятся камеры: так
проверяется весь тракт — TLS, порты, плеер, архив. Параметр motion делает битрейт примерно
линейным по величине, что удобно для нагрузочных проверок, а ?ac=g711 воспроизводит поведение
типичной камеры со звуком G.711 (см. Протоколы раздачи).
Второй путь к тому же потоку#
stream lobby { input rtsp://admin:пароль@10.0.0.10:554/Streaming/Channels/101; } stream lobby-public { input copy://lobby; }
copy:// отдаёт уже принятый локальный поток под вторым именем, разделяя его кольцевые буферы по
счётчику ссылок: ни второго сокета, ни второго разбора, ни второй упаковки. Стоимость близка к нулю.
Ограничения:
- поток
copy://не пишет архив — своего корня записи у него нет; - он наследует метки авторизации исходного потока;
- он повторяет его состояние и заново подключается, когда исходный поток перезапускается;
- он не создаёт спроса для режима
on_demandисходного потока.
Типичное применение — отдать один и тот же поток под публичным именем и под внутренним, с разными настройками раздачи.
Полный пример#
http 8080; rtsp 8554; auth { admin_password "смените-этот-пароль"; } source_transport udp-then-tcp; media_timeout 20; vod library /srv/media; stream cam1 { input "rtsp://admin:p%40ss@10.0.0.10:554/Streaming/Channels/101"; on_demand; } stream relay1 { input https://origin.example.com/live/index.m3u8; tls_verify system; } stream tv1 { input udp://@239.1.1.1:1234?iface=10.0.0.5; } stream loop1 { input file:///srv/media/loop.mp4; } stream demo { input demo://?res=1280x720&fps=25&ac=aac; }
curl -sS "https://media.example.com/api/v1/streams/cam1" \ -H "Authorization: Bearer $TOKEN" \ | python3 -c 'import sys, json s = json.load(sys.stdin) print("alive =", s["alive"], "| health =", s["health"], "| fps =", round(s["fps"], 1)) print("tracks:", [(t["kind"], t["codec"]) for t in s["tracks"]])'
alive = True | health = ok | fps = 25.0
tracks: [('video', 'H.264'), ('audio', 'AAC')]
Куда двигаться дальше#
- Резервирование источников — как задать запасной адрес.
- Приём публикации: RTMP и WHIP — если к источнику не подключиться.
- Протоколы раздачи — какие кодеки доходят до какого зрителя.
- hastreamer.conf — все директивы — полный справочник.