◆ ForgeVis
Skip to content

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 мультиплексоры создаются по запросу:

  1. Первый запрос к /camera_001/index.m3u8 создает мультиплексор
  2. Мультиплексор подписывается на StreamHub (подключение к камере)
  3. Конвертирует H.264 кадры в HLS сегменты
  4. Последующие запросы используют существующий мультиплексор

Автоматическая очистка ​

Неактивные мультиплексоры автоматически удаляются:

  • Таймаут по умолчанию: 60 секунд после последнего запроса
  • Настраивается через muxerCloseAfter в конфиге
  • Освобождает память и CPU ресурсы

Использование ресурсов ​

На каждый активный HLS поток:

  • Память: ~5-10 МБ (буфер сегментов)
  • CPU: Минимальное — поток камеры только переупаковывается
  • Ступень 720p: отдельный процесс ffmpeg, пока её смотрят; нагрузка на CPU заметно выше, чем у переупаковки
  • Сеть: Такой же как битрейт камеры

Решение проблем ​

Поток не загружается ​

  1. Проверьте, что HLS сервер включен и работает:
bash
curl http://localhost:8888/camera_001/index.m3u8
  1. Убедитесь, что камера настроена правильно

  2. Проверьте консоль браузера на ошибки CORS

Ошибки CORS ​

HLS сервер разрешает все origins по умолчанию. Если проблемы сохраняются:

  1. Проверьте вкладку network в браузере на реальную ошибку
  2. Убедитесь в наличии заголовка Access-Control-Allow-Origin: * в ответе
  3. Для credentials настройте конкретный origin в nginx reverse proxy

Высокая задержка ​

HLS имеет задержку 3-6 секунд по своей природе. Для меньшей задержки:

  • Используйте RTSP рестриминг
  • Настройте lowLatencyMode в hls.js
  • Уменьшите segmentDuration и partDuration в конфиге

Прерывистое воспроизведение ​

  1. Проверьте пропускную способность сети
  2. Используйте субпоток для сценариев с низким битрейтом
  3. Увеличьте длину буфера в конфигурации плеера
  4. Проверьте загрузку 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 ​

ФункцияHLSRTSP
ПротоколHTTPRTSP/RTP
Задержка3-6 секунд<1 секунды
Файрвол✅ Дружелюбен❌ Часто блокируется
Поддержка CDN✅ Да❌ Нет
Поддержка браузерами✅ Нативная/hls.js❌ Требует плагин
Мобильные приложения✅ Отлично⚠️ Ограничено
Перемотка✅ Быстро⚠️ Ограничено

Когда использовать HLS:

  • Веб-воспроизведение
  • Мобильные приложения
  • Через файрволы/NAT
  • Раздача через CDN
  • Задержка 3-6с приемлема

Когда использовать RTSP:

  • Мониторинг в реальном времени (<1с задержка)
  • VMS программное обеспечение
  • Профессиональные NVR системы
  • Только локальная сеть

См. также ​

Proprietary software.