Appearance
HLS стриминг
ForgeVis предоставляет встроенный HLS (HTTP Live Streaming) сервер для веб-воспроизведения видео. HLS использует HTTP протокол, что делает его совместимым с файрволами и CDN.
Возможности
- ✅ Low-latency HLS с fMP4 сегментами
- ✅ Автоматическая инициализация потока при первом запросе
- ✅ Поддержка основного потока и субпотоков
- ✅ CORS включен для cross-origin воспроизведения
- ✅ Автоматическая очистка неактивных потоков
- ✅ По умолчанию без перекодирования: видео с камеры отдаётся как есть. Перекодирование включается, только когда зритель выбирает 720p
Конфигурация
Включите HLS в config.yaml:
yaml
hls:
enabled: true
address: "0.0.0.0:8888" # Адрес и порт для прослушивания
segmentDuration: "1s" # Длительность HLS сегментов
partDuration: "200ms" # Длительность LL-HLS parts
variant: "lowLatency" # "fmp4" (классический) или "lowLatency"
transcodeMaxConcurrent: 2 # Сколько ступеней 720p кодируется одновременноФормат URL
Основной поток
http://сервер:8888/{camera_id}/index.m3u8Пример:
http://localhost:8888/camera_001/index.m3u8Субпоток
http://сервер:8888/{camera_id}/sub/index.m3u8Пример:
http://localhost:8888/camera_001/sub/index.m3u8Выбор качества
index.m3u8 и master.m3u8 камеры — это всегда только её основной поток. Для плеера с выбором качества у камеры есть отдельный плейлист variants.m3u8:
http://сервер:8888/{camera_id}/variants.m3u8Он перечисляет варианты по порядку: исходный поток, ступень 720p, если её можно закодировать, и субпоток, если он задан.
#EXT-X-STREAM-INF:BANDWIDTH=1056000,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1920x1080,AUDIO="audio"
media_0.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=483000,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1280x720,AUDIO="audio"
720p/media_0.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=282000,AUDIO="audio"
sub/media_0.m3u8- Какой вариант играть, выбирает плеер. hls.js без указанного
startLevelначинает с того, что укладывается в его оценку канала, — обычно не с исходного потока. Поэтомуvariants.m3u8предназначен для плеера, который сам выставляет вариант, аmaster.m3u8гарантированно даёт основной поток любому клиенту. - Ступень 720p и субпоток запускаются только тогда, когда плеер выбрал этот вариант. Непросмотренный субпоток указан без разрешения и кодека — они станут известны после первого просмотра.
- Ступень 720p кодирует ffmpeg с RTSP-рестрима этого же узла, поэтому она предлагается, только если на узле есть ffmpeg и включён RTSP-сервер (
rtsp.enabled). Её нет у камеры не выше 720p и у камеры, субпоток которой сам не ниже 720p. Ключевые кадры ступени совпадают с ключевыми кадрами камеры, битрейт — доля битрейта основного потока по числу пикселей, не больше 1,5 Мбит/с. - Одновременно кодируется не больше
transcodeMaxConcurrentступеней (по умолчанию 2); когда все заняты, новая ступень в плейлисте не предлагается. Ступень, которую никто не смотрит, останавливается черезmuxerCloseAfter. - Время в сегментах всех вариантов идёт по реальным часам, а каждый сегмент несёт
EXT-X-PROGRAM-DATE-TIMEс моментом своего начала. Поэтому варианты совпадают по времени, и плеер может переключаться между ними. - Звук у всех вариантов общий — звук основного потока. Так плейлист одинаково принимают все плееры, включая Safari, а субпоток не нужно запускать, чтобы узнать, есть ли у него звук.
- Субпоток камера кодирует отдельным кодером, его ключевые кадры не совпадают с основным потоком. Переключение на него проходит с короткой подгрузкой, а не мгновенно.
- Узел кластера с ролью
streamerотдаёт чужую камеру без субпотока — там у неё нет вариантаsub.
Конфигурация камеры
yaml
cameras:
camera_001:
source: "rtsp://camera-ip:554/main" # Основной поток (HD)
sub: "rtsp://camera-ip:554/sub" # Субпоток (SD, опционально)HLS URL:
- Основной:
http://localhost:8888/camera_001/index.m3u8 - Субпоток:
http://localhost:8888/camera_001/sub/index.m3u8
Интеграция с веб-плеером
HTML5 Video
html
<!DOCTYPE html>
<html>
<head>
<title>ForgeVis HLS плеер</title>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>
<body>
<video id="video" controls width="640" height="480"></video>
<script>
const video = document.getElementById('video');
const source = 'http://localhost:8888/camera_001/index.m3u8';
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(source);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function() {
video.play();
});
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// Нативная поддержка HLS (Safari)
video.src = source;
video.addEventListener('loadedmetadata', function() {
video.play();
});
}
</script>
</body>
</html>Video.js
html
<!DOCTYPE html>
<html>
<head>
<link href="https://vjs.zencdn.net/8.6.1/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.6.1/video.min.js"></script>
</head>
<body>
<video id="player" class="video-js vjs-default-skin" controls preload="auto"
width="640" height="480">
<source src="http://localhost:8888/camera_001/index.m3u8"
type="application/x-mpegURL">
</video>
<script>
const player = videojs('player', {
liveui: true,
controls: true
});
</script>
</body>
</html>Мультикамерная панель
html
<div class="camera-grid">
<div class="camera">
<h3>Камера 001 - Основной</h3>
<video id="cam1" controls autoplay muted width="400"></video>
</div>
<div class="camera">
<h3>Камера 001 - Субпоток</h3>
<video id="cam1-sub" controls autoplay muted width="400"></video>
</div>
</div>
<script>
function setupHLS(videoId, url) {
const video = document.getElementById(videoId);
if (Hls.isSupported()) {
const hls = new Hls({
lowLatencyMode: true,
backBufferLength: 90
});
hls.loadSource(url);
hls.attachMedia(video);
}
}
setupHLS('cam1', 'http://localhost:8888/camera_001/index.m3u8');
setupHLS('cam1-sub', 'http://localhost:8888/camera_001/sub/index.m3u8');
</script>Сценарии использования
Основной поток (Высокое качество)
- Записи архива
- Детальный мониторинг
- Сбор доказательств
- Достаточная пропускная способность
http://localhost:8888/camera_001/index.m3u8Субпоток (Низкий битрейт)
- Мультикамерные панели
- Мобильные клиенты
- Удаленный просмотр через ограниченный канал
- Видеостены с множеством камер
http://localhost:8888/camera_001/sub/index.m3u8Производительность
Ленивая инициализация
HLS мультиплексоры создаются по запросу:
- Первый запрос к
/camera_001/index.m3u8создает мультиплексор - Мультиплексор подписывается на StreamHub (подключение к камере)
- Конвертирует H.264 кадры в HLS сегменты
- Последующие запросы используют существующий мультиплексор
Автоматическая очистка
Неактивные мультиплексоры автоматически удаляются:
- Таймаут по умолчанию: 60 секунд после последнего запроса
- Настраивается через
muxerCloseAfterв конфиге - Освобождает память и CPU ресурсы
Использование ресурсов
На каждый активный HLS поток:
- Память: ~5-10 МБ (буфер сегментов)
- CPU: Минимальное — поток камеры только переупаковывается
- Ступень 720p: отдельный процесс ffmpeg, пока её смотрят; нагрузка на CPU заметно выше, чем у переупаковки
- Сеть: Такой же как битрейт камеры
Решение проблем
Поток не загружается
- Проверьте, что HLS сервер включен и работает:
bash
curl http://localhost:8888/camera_001/index.m3u8Убедитесь, что камера настроена правильно
Проверьте консоль браузера на ошибки CORS
Ошибки CORS
HLS сервер разрешает все origins по умолчанию. Если проблемы сохраняются:
- Проверьте вкладку network в браузере на реальную ошибку
- Убедитесь в наличии заголовка
Access-Control-Allow-Origin: *в ответе - Для credentials настройте конкретный origin в nginx reverse proxy
Высокая задержка
HLS имеет задержку 3-6 секунд по своей природе. Для меньшей задержки:
- Используйте RTSP рестриминг
- Настройте
lowLatencyModeв hls.js - Уменьшите
segmentDurationиpartDurationв конфиге
Прерывистое воспроизведение
- Проверьте пропускную способность сети
- Используйте субпоток для сценариев с низким битрейтом
- Увеличьте длину буфера в конфигурации плеера
- Проверьте загрузку CPU сервера
Расширенная конфигурация
За обратным прокси
nginx
server {
listen 80;
server_name video.example.com;
location /hls/ {
proxy_pass http://forgevis:8888/;
proxy_http_version 1.1;
# CORS
add_header Access-Control-Allow-Origin *;
# Кеширование HLS сегментов
proxy_cache hls_cache;
proxy_cache_valid 200 1s;
}
}Балансировка нагрузки между серверами
nginx
upstream hls_backend {
least_conn;
server forgevis1:8888;
server forgevis2:8888;
server forgevis3:8888;
}
server {
location /hls/ {
proxy_pass http://hls_backend/;
}
}Сравнение с RTSP
| Функция | HLS | RTSP |
|---|---|---|
| Протокол | HTTP | RTSP/RTP |
| Задержка | 3-6 секунд | <1 секунды |
| Файрвол | ✅ Дружелюбен | ❌ Часто блокируется |
| Поддержка CDN | ✅ Да | ❌ Нет |
| Поддержка браузерами | ✅ Нативная/hls.js | ❌ Требует плагин |
| Мобильные приложения | ✅ Отлично | ⚠️ Ограничено |
| Перемотка | ✅ Быстро | ⚠️ Ограничено |
Когда использовать HLS:
- Веб-воспроизведение
- Мобильные приложения
- Через файрволы/NAT
- Раздача через CDN
- Задержка 3-6с приемлема
Когда использовать RTSP:
- Мониторинг в реальном времени (<1с задержка)
- VMS программное обеспечение
- Профессиональные NVR системы
- Только локальная сеть