Финальные требования и настройки у нас сейчас такие. ## Финальный пресет чанкования ffmpeg (RTSP → MPEG‑TS → stdout) Мы остановились на сценарии, где ffmpeg: - тянет RTSP **по TCP** с камер, - минимизирует внутреннюю буферизацию и время старта, - не перекодирует видео (копирует H.264 как есть); если возникнут проблемы с нагрузкой на сеть, можно поискать варианты снизить; но на данном этапе важнее "накормить" видеобуфер воспроизведения хоть чем-нибудь; - выдаёт результат в **stdout (`pipe:1`)**, откуда его забирает mqtt транспорт. Финальный набор аргументов: ```js [ '-hide_banner', '-fflags', 'nobuffer', // не буферировать при probe stream info '-flags', 'low_delay', // включить low-delay на уровне декодера '-analyzeduration', '100000', // ограничить анализ потока 100 ms (уменьшить стартовую задержку) '-probesize', '32768', // зондировать только 32 KB (быстрее старт, меньше буфера) '-flush_packets', '1', // сразу сбрасывать пакеты, не копить их внутри muxer'а '-rtsp_transport', 'tcp', // тянуть RTSP по TCP (надёжнее на плохой сети) '-i', 'rtsp://admin:admin@192.168.0.11:554/live/sub', // sub-поток камеры '-an', // отключить аудио — нужен только видеофрейм '-c:v', 'copy', // не перекодировать видео, просто копировать кадры '-f', 'mpegts', // контейнер MPEG-TS для дальнейшего трансмультиплексирования '-mpegts_flags', 'resend_headers', // периодически повторять заголовки для надёжного декода 'pipe:1' // выводить поток в stdout (дальше забирает твой транспорт) ] ``` Ключевые моменты: - `-c:v copy` сохраняет оригинальный кадр и GOP камеры — минимум задержки и вычислительной нагрузки. - `-fflags nobuffer + -flags low_delay + -flush_packets 1` минимизируют буферизацию и увеличивают «прозрачность» для твоего транспорта. - `-rtsp_transport tcp` это про **вход** (камера → ffmpeg), а `pipe:1` — про **выход** (ffmpeg → mqtt). *** ## Финальный пресет воспроизведения (mpegts.js + HTML5 video) На клиенте мы на данном этапе определяемся работать как с «live‑видео с буфером», а не как с чистым real‑time: - формат источника: **MPEG‑TS/H.264** по WebSocket; - плеер: `mpegts.js`, режим `type: 'mpegts', isLive: true`; - буфер: небольшой stash + несколько секунд latency для сглаживания провалов. Финальные параметры инициализации: ```js const wsUrl = 'wss://...'; const player = mpegts.createPlayer( { type: 'mpegts', // MPEG-TS поток по WS isLive: true, url: wsUrl, hasAudio: false, // видео без аудио }, { enableWorker: true, // Включаем stash, чтобы сглаживать редкий / рваный поток enableStashBuffer: true, stashInitialSize: 512, // немного увеличенный stash для накопления autoCleanupSourceBuffer: true, autoCleanupMaxBackwardDuration: 10, // держим минимум истории autoCleanupMinBackwardDuration: 5, // Разрешаем latency-chasing с «комфортной» задержкой liveBufferLatencyChasing: true, liveBufferLatencyMaxLatency: 5.0, // целевая задержка до ~5 секунд liveBufferLatencyMinRemain: 2.0, // оставляем ~2 секунды буфера после сброса liveSync: false, // не пытаемся мягко догонять — приоритет стабильности lazyLoad: false, deferLoadAfterSourceOpen: false, } ); player.attachMediaElement(document.getElementById('v')); player.load(); player.play(); ``` Смысл этих настроек: - **Stash‑buffer включён**: mpegts.js может аккумулировать некоторое количество данных, прежде чем кормить MSE (браузеру), что хорошо при «дырявом» канале. - **Latency‑chasing с MaxLatency ≈ 5 s**: браузер допускает несколько секунд отставания, но не позволяет задержке расти бесконечно; при накоплении лишнего буфера он «подпрыгивает» ближе к live‑позиции, сохраняя `MinRemain` ~2 секунды запаса. - `liveSync: false` — мы не гонимся за минимально возможной задержкой любой ценой, а принимаем небольшой «запас» как разумный компромисс. При улучшении канала или выполнениии работ по снижению нагрузки на сеть (уменьшения битрейта, fps) нужно грамотно управлять буфером на стороне клиента (stash + latency).