Appearance
История изменений
[0.4.5] - 2026-10-04
Добавлено
- Изменения пользователей в
security.usersприменяются без перезапуска: удалённый пользователь и сменённый пароль перестают действовать сразу. - Защита от подбора пароля на всех серверах, включая RTSP: после 10 неудачных входов подряд пользователь блокируется с этого адреса на 5 минут (
security.lockout). allowOriginво всех разделах принимает несколько источников через запятую; значение, не являющееся источником, пропускается с предупреждением в журнале.
Изменено
- Служба из пакета работает от пользователя
forgevisв изоляции systemd, архив по умолчанию —/var/lib/forgevis/recordings. Установка, работавшая от root, после обновления остаётся на root; как перевести её — в разделе «Безопасность». - Публикация по WHIP требует права
publish, а неread. /cluster/proposeудалён./api/health/readyбез авторизации отдаёт только состояние и версию; подробности проверок — тому, кому разрешён Management API.
Исправлено
- Пользователь с доступом к одной камере (
path) открывает её по WebSocket и не видит по нему другие камеры. websocket.allowOriginдействует: WebSocket открывается только со страниц из списка.- Камера, чей идентификатор не является одним именем каталога (содержит
/,\или равен..), не записывается и не проходит проверку конфигурации.
[0.4.4] - 2026-10-03
Добавлено
- Секция
reconnectзадаёт паузы между попытками подключения к камере: по умолчанию одна попытка сразу, затем паузы 5, 10, 15 и 30 секунд со случайным разбросом; расписание одно для записи, HLS, рестрима и зрителей. /api/archive/{camera_id}/segmentsпринимаетtz_offset_minutesи считает дни вdaysпо часам зрителя.
Изменено
- Запрос потока камеры, которая не отвечает, между попытками сразу получает отказ с причиной и временем следующей попытки.
- В журнале на каждую неудачную попытку подключения к камере пишется одна строка с причиной и временем следующей попытки; восстановление записи отмечается строкой с длительностью простоя.
- При остановке сервис сначала перестаёт принимать запросы API, воспроизведения и потоков, затем закрывает запись и выходит, как только сегменты закрыты.
/api/healthудалён: живость процесса —/api/health/live, готовность и версия —/api/health/ready./api/archive/{camera_id}/segmentsотвечает400на интервал шире суток, неверные или перевёрнутые границы и503, когда база с индексом архива недоступна, — а не пустым списком.
Исправлено
- Камера, которая держит соединение и перестаёт присылать видео, через 5 секунд подключается заново — для записи, HLS, RTSP-рестрима и субпотока.
- Сегмент, закрытый при остановке сервиса, виден в архиве.
- Узел кластера с ролью streamer отдаёт субпоток камеры, которую пишет другой узел.
- Сегмент, оборванный потерей потока с камеры, попадает в архив в базе данных, и для него запускается
runOnSegmentComplete. - Изменение конфигурации на лету проходит ту же проверку, что и запуск; узел с базой данных и пустым
nodeNameпродолжает писать свои камеры.
[0.4.3] - 2026-09-30
Добавлено
- Эндпоинт
/api/cameras/{id}/snapshotотдаёт кадр с камеры по её адресуsnapshotв конфигурации илиsnapuriв базе и держит его 15 секунд для всех запросов к этой камере.
Изменено
- Путь записи задаётся для всего узла в
cameraDefaults.record_path; конфигурация, гдеrecord_pathуказан у отдельной камеры, не проходит проверку при запуске.
Исправлено
- Поток по запросу, например субпоток, подключается для следующего зрителя, даже если предыдущий ушёл, не дождавшись подключения.
- Перезапуск потока сразу закрывает прежнее соединение с камерой.
- PTZ работает для камер, добавленных после запуска узла, и для камер кластера.
[0.4.2] - 2026-09-29
Добавлено
- Плейлист
variants.m3u8перечисляет варианты камеры для выбора качества в плеере: исходный поток, ступень 720p и субпоток. Ступень кодируется, а субпоток подключается только тогда, когда плеер выбрал этот вариант;master.m3u8остаётся исходным потоком. - Параметр
hls.transcodeMaxConcurrentограничивает число одновременно кодируемых ступеней 720p.
Изменено
- Время сегментов HLS идёт по реальным часам, и каждый сегмент в каждом HLS-плейлисте, в том числе Low-Latency, несёт
EXT-X-PROGRAM-DATE-TIMEс моментом своего начала.
Исправлено
- В HLS звук и видео начинаются одновременно.
- RTSP-рестрим передаёт видео с метками времени камеры, и поток с любой частотой кадров идёт в реальном времени.
- Low-Latency HLS указывает у каждой части её настоящую длительность в пределах, которые принимает Safari, и законченный сегмент сразу появляется в плейлисте.
- HLS режет сегменты и части по времени камеры.
- Плейлист HLS всегда охватывает не меньше трёх целевых длительностей сегмента.
- Звук G.711 в HLS идёт в одном времени с видео.
- Первый запрос плейлиста субпотока или ступени 720p дожидается первого сегмента, и плеер остаётся на выбранном варианте.
[0.4.1] - 2026-09-24
Добавлено
- Запись указывает в своих метаданных (
©too) версию ForgeVis, которая её сделала.
Исправлено
- Записи со звуком AAC описывают аудиодорожку в точности по ISO/IEC 14496-1, а прогрессивный MP4 указывает битрейт дорожки.
[0.4.0] - 2026-09-11
Добавлено
- Команда
forgevis --retentionудаляет записи старшеretention_days— параметр задаётся вcameraDefaultsи, если камера отличается, на самой камере. Без параметра, а также приretention_days: 0, записи хранятся бессрочно. Записи камер, которых больше нет в конфигурации, хранятся по значению изcameraDefaults. forgevis --retention --dry-runпоказывает, что было бы удалено, по камерам: сколько записей уходит, сколько остаётся и какой глубины будет архив после удаления.- Камера из базы данных и камера, полученная от кластера, несут свою глубину архива: значение, заданное на камере, попадает в её настройки на том узле, который её пишет.
- Очистка работает и на узле с базой данных, и в кластере: узел удаляет только те записи, что записал сам, а вместе с файлом убирает его строку в таблице записей.
Изменено
- Тип ошибки записи называет причину:
connect— сеть или камера,stream— камера ответила и замолчала,write— диск узла,internal— ошибка сервиса. Ошибка, которую сервис не узнаёт, помечаетсяunclassifiedи пишется в журнал целиком. - Список сегментов отвечает объектом:
archive— сегменты запрошенного интервала,days— все дни, за которые у камеры есть записи. Календарь и таймлиния строятся из одного запроса. - Без
fromиtoсписок сегментов отдаётся за текущие сутки. - В список попадает сегмент, начавшийся до начала интервала и заходящий в него.
Исправлено
- Метрики камеры исчезают вместе с ней: серии последнего кадра, переподключений и ошибок записи удаляются, когда камера уходит из конфигурации.
- Таймаут подключения к камере записывается в журнал один раз.
- Экспорт и таймлапс заканчиваются на конце выбранного окна, даже если внутри окна есть разрыв записи.
- Зависший ffmpeg в экспорте или таймлапсе останавливается по таймауту
api.archiveJobTimeoutSec(по умолчанию час), и задание завершается ошибкой, не занимая место следующих. - Готовые файлы экспорта хранятся в каталоге
api.archiveExportDir(по умолчанию/var/lib/forgevis/exports) с доступом только для сервиса и удаляются через час после готовности, включая оставшиеся от прошлого запуска. Там же собираются части, которые узел-сборщик скачивает с других узлов. - При записи в
mp4экспорт и таймлапс обходят сегмент, который ещё пишется, — до закрытия его нельзя прочитать. Приfmp4он входит в выгрузку до последнего записанного фрагмента.
[0.3.10] - 2026-09-10
Исправлено
- Камеры, передающие SSRC в нестандартном виде в ответе на SETUP (например, Rubetek), подключаются и записываются.
[0.3.9] - 2026-08-29
Исправлено
- Аудио-сэмплы во фрагментированных записях помечены как независимо декодируемые — как их и декодируют сторонние инструменты.
[0.3.8] - 2026-08-28
Исправлено
- Таймлапс покрывает запрошенный диапазон: окно вырезается до ускорения записи.
- Задание экспорта или таймлапса, не собравшее ни одного кадра, завершается ошибкой с указанием причины.
[0.3.7] - 2026-08-27
Изменено
- Горячая перезагрузка сообщает итог одной строкой: сколько задач камер запущено, остановлено, удалено, оставлено без изменений и работает сейчас. По отдельным камерам в журнал идут только изменения.
[0.3.6] - 2026-08-27
Добавлено
- HTTPS для всех HTTP-слушателей узла — управляющего API, HLS, сигнализации WebRTC, воспроизведения архива и метрик. Включается секцией
tls: один сертификат на узел и один переключатель на все слушатели сразу, потому что браузер, открывший интерфейс поhttps, не загрузит плейлист HLS поhttp. Учётные данные перестают ходить открытым текстом; проверит ли клиент, с кем он говорит, зависит от того, знает ли он выдавший сертификат центр. Значениеtls.enabled: trueбез читаемыхcertиkeyзавершает запуск с ошибкой. Сертификат читается при старте, поэтому его замена требует перезапуска.
Изменено
- TLS настраивается один раз для всего узла:
encryption,serverCertиserverKeyвнутри секцийapiиwebrtcзаменены секциейtlsверхнего уровня. - Документация о сертификатах кластера дополнена сроком их действия и порядком выпуска для установки без платформы.
[0.3.5] - 2026-08-26
Добавлено
- Сервер воспроизведения архива: запрос называет камеру и момент времени, а запись начинается с этого момента. Узел находит нужный кадр по индексу, который каждый файл хранит в своём заголовке, поэтому плееру не нужно самому разбирать файл и высчитывать смещения. Воспроизведение начинается с ближайшего опорного кадра, так что первые секунды после перемотки не рассыпаются, а момент, на который ничего не записано, отвечает следующей существующей записью. Браузеру запись отдаётся обычным файлом (
format=mp4), инструментам — фрагментами. Отдельный слушатель, по умолчанию выключен — см. секциюplaybackв конфигурации. - Вывод узла из кластера работает с любого узла, а не только с лидера. Состав участников меняет лидер, и уходящий узел просит его об этом по каналу консенсуса, предъявляя сертификат. Уходящий передаёт только свой идентификатор: новый состав лидер считает по своему представлению, потому что представление уходящего может отставать.
- Вывод текущего лидера из состава голосующих доводит кластер до новых выборов. Просьба к оставшимся голосующим начать выборы идёт по каналу консенсуса, где узлы предъявляют сертификаты. Толчок нужен потому, что выведенный лидер остаётся learner'ом и продолжает слать heartbeat: сам по себе таймер выборов у остальных не истечёт.
- Запрос состояния кластера, пришедший не на лидера, возвращает перенаправление на лидера: запрос повторяется с учётными данными вызывающего. Узлы обращаются друг к другу только по каналу консенсуса, где предъявляют сертификаты.
- Узел сообщает своё имя в
/api/metrics. Платформа берёт имя оттуда при заведении сервера, а не из формы: камеры раздаются рекордеру по имени, и расхождение в один символ нигде не даёт ошибки — узел читает пустой список камер и продолжает считаться здоровым. - Структурой базы данных распоряжается платформа: узел не создаёт в ней ничего и не требует прав на изменение схемы — ему достаточно чтения
camerasи записи вrecordings. Триггер уведомлений об изменении камер устанавливается вместе со схемой. Для установки с собственной базой и без платформы схема публикуется отдельным SQL-файлом. - Запись, которая ведётся прямо сейчас, доступна в списке архива: узел отвечает, какой файл он пишет для камеры и с какого момента, поэтому на шкале времени видно и последние минуты, не только закрытые сегменты. Работает при
record.format: fmp4— незакрытая прогрессивная запись ещё не воспроизводится. - Узлы кластера аутентифицируют друг друга сертификатами. Сертификаты выпускает Control Plane: узел создаёт закрытый ключ и запрос на подпись, наружу уходит только запрос. Пока сертификата нет, порт консенсуса закрыт, о чём узел сообщает в журнале.
Изменено
POST /cluster/initтребуетcluster_id, и он должен быть UUID. На эту идентичность ссылается строка камеры, когда называет владеющий ею кластер, и кластер читает ровно те строки, где стоит это значение. Кластер без неё читал бы все включённые камеры базы как свои, а выдать идентичность переинициализацией уже нельзя — поэтому запрос безcluster_idотклоняется. Значение, не являющееся UUID, отклоняется там же: колонкаcameras.cluster_idимеет тип uuid, и иначе отказ всплыл бы позже и выглядел как недоступность базы.- Камера с выключенной записью не заводит рекордер: в журнал идёт сообщение уровня INFO о том, что камера транслируется, но не записывается. Уровень соответствует происходящему — это штатная конфигурация, а не сбой.
- Имя узла задаётся верхнеуровневой настройкой
nodeName, а не внутри секцииcluster: оно нужно и одиночному узлу с базой, где кластера нет вовсе. Пустое значение означает системный hostname. Встроенного имени по умолчанию нет — общее означало бы, что два узла докладываются одним именем и берут одни и те же камеры; при настроенной базе узел, которому неоткуда взять имя, не стартует. Слишком длинное имя тоже отклоняется при старте: база сверяет его с колонкой в 64 символа. Разрешённое имя и его источник узел пишет в журнал при запуске. - Защита канала консенсуса включена всегда; параметры
cluster.tls.enabledиcluster.tls.require_client_authудалены. Пути к собственным сертификатам по-прежнему задаются вcluster.tls. - Управляющий запрос, пришедший не на лидера, возвращает перенаправление на лидера: запрос повторяется с учётными данными вызывающего, поэтому узлам не нужны учётные данные друг друга.
- Сведения о живости узлов передаются по каналу консенсуса, поэтому состояние кластера не зависит от того, закрыт ли управляющий API паролем.
- Неверный адрес
cluster.rpc_addrотклоняется при старте. - Список сегментов
/api/archive/{камера}/segmentsвключает и запись, которая ведётся прямо сейчас: она помечена полемis_open, поэтому один запрос отдаёт весь архив камеры вплоть до текущей минуты. Остальные сегменты приходят сis_open: false. - Каталог состояния Raft задаётся в
cluster.dataDir; по умолчанию/var/lib/forgevis/data. Путь абсолютный, поэтому не зависит от того, из какого каталога запущена служба. Там же, в подкаталогеtls, лежат сертификаты узла. Значение по умолчанию совпадает с каталогом работающих установок, поэтому при обновлении настройку можно не задавать. - Узел, настроенный на работу в кластере, предупреждает при старте, если каталог состояния пуст.
- В поставляемой конфигурации
record.format—fmp4. Сервер воспроизведения и выдача текущей записи работают только с ним: прогрессивныйmp4хранит заголовок в конце файла и до закрытия не воспроизводится. - Идентификатор камеры в запросах архива состоит только из букв, цифр,
_и-; всё остальное отклоняется с400. - Запрос архива по камере кластера возвращает перенаправление на узел, который её пишет, если запрос пришёл на другой узел. Так
/api/archive/{камера}/segmentsи/api/archive/{камера}/currentотвечают одинаково на любом узле кластера, и обращаться к конкретному узлу не нужно.
Исправлено
- Кластер не прекращает запись, если узел не смог прочитать список камер из базы: недоступный список не принимается за пустой, и назначения остаются на месте до успешного чтения.
- Сбой чтения камер из базы не останавливает камеры на отдельном узле: перезагрузка настроек пропускается, текущий набор камер сохраняется.
- Узел, выведенный из кластера, принимается в кластер снова без перезапуска сервиса: команда выхода сразу стирает локальное состояние кластера, и узел начинает вести назначенные ему камеры, как только снова оказывается в составе кластера.
- Сбой механизма кластера обнаруживается автоматически: узел сообщает о неготовности и восстанавливается сам.
- Вывод узла, которого уже нет в составе кластера, завершается успешно.
- Камера, для которой выбран конкретный узел кластера, ведётся именно им — в том числе когда список камер приходит из базы данных.
- Настройка
hls.allowOriginприменяется: браузерная страница обращается к HLS с адреса, разрешённого в конфигурации.
[0.3.4] - 2026-08-13
Исправлено
- Исправлена перемотка в записях fMP4: фрагменты нарезаются по ключевым кадрам, поэтому переход по времени попадает на кадр, с которого действительно можно начать воспроизведение.
- Исправлена длительность аудиодорожки в записях MP4.
- Исправлено описание профиля и уровня H.265 в записях: раньше оно не соответствовало потоку, из-за чего строгие декодеры и браузерное воспроизведение могли отказать.
- Записи начинаются с ключевого кадра, поэтому воспроизведение с начала файла больше не даёт артефактов в первую секунду.
- Сегмент, прерванный обрывом связи с камерой или простоем потока, теперь корректно закрывается: в формате MP4 такой файл раньше вообще не открывался.
- Длительности кадров в записи соответствуют реальному потоку камеры: время в архиве больше не расходится с реальным (примерно 80 секунд в сутки на камере с частотой 25.02 кадра/с) и пропуски кадров не «схлопываются».
- Перезапуск часов камеры больше не портит запись: сегмент закрывается и следующий начинается с новой временной шкалы.
- Время начала сегмента в базе теперь соответствует первому записанному кадру — экспорт по временному диапазону точнее попадает в границы файлов.
- Добавлены таймауты на подключение к камерам и на внутренние сетевые вызовы: недоступная камера или узел больше не удерживают задачу до истечения системного таймаута TCP.
- Исправлено повторное подключение к камере при одновременном обращении нескольких потребителей — соединение остаётся единственным.
- Клиент RTSP-ретрансляции, переставший читать поток, больше не удерживает подключение к камере.
Изменено
- Слишком большое значение
record.segmentDurationтеперь отклоняется при старте: индекс сегмента не помещался бы в файл, из-за чего терялась перемотка по времени.
[0.3.3] - 2026-08-05
Исправлено
- Исправлено применение настроек камеры (запись, звук, дополнительный поток) при переносе камеры на другую ноду кластера.
[0.3.2] - 2026-08-04
Исправлено
- Повышена устойчивость хранилища состояния кластера к внезапному отключению питания.
- Исправлена синхронизация аудио и видео в архивных записях; длительность аудио-кадров теперь учитывает кодек (AAC, G.711, Opus).
- Исправлена RTSP-ретрансляция камер с аудио G.711 и Opus.
- Улучшена регистрация адресов ноды в кластере при запуске; смена статуса ноды больше не сбрасывает её данные.
[0.3.1] - 2026-08-04
Изменено
- Оптимизация синхронизации состояния кластера: данные о живости и адресах рекордер-нод теперь передаются через единый Raft-ключ на ноду вместо шести отдельных. После минуты работы ноды повторяющийся heartbeat-payload сокращается примерно в 6 раз, а Raft-снапшоты несут меньше ключей на участника. Внешнее поведение не меняется — эффект исключительно в фоновом трафике между нодами. Наиболее заметен в развёртываниях с большим числом рекордеров.
[0.3.0] - 2026-06-03
Добавлено
- Cross-node архивный экспорт: если камера в запрошенном окне экспорта мигрировала между рекордер-нодами, итоговый MP4 теперь бесшовно собирается из фрагментов с нескольких серверов. Платформа управления (backend) оркестрирует: читает таблицу
recordings, партиционирует окно на per-node runs, отправляет каждому рекордеру свой sub-export stream-copy'ем, затем делегирует финальный concat одной из нод («assembler») — чтобы тяжёлый I/O не нагружал backend-хост. Финальный MP4 стримится клиенту через backend как тонкий прокси. Поддерживаются канонические сценарии миграции — одна миграция, несколько прыжков, повторное возвращение на ту же ноду (напримерS1 → S2 → S3 → S2собирается в один воспроизводимый MP4 без внутренних разрывов). Подробности — руководство по multi-node архиву. - Cross-node таймлапс: тот же fan-out что и для обычного экспорта, плюс финальный
setpts=PTS/Npass на assembler-ноде. Коэффициент скорости — целое число в диапазоне[2, 120]; длительность выхода ≈raw_duration / speed. - Устойчивость задач к рестартам: multi-node экспорт переживает рестарт рекордера (каждый sub-export пере-submit'ится с новым id при
404, до 3 попыток на run) и рестарт backend'а (in-flight задачи вpending/exporting/assemblingподнимают свои оркестраторы при следующем старте backend'а и продолжают с того места, где остановились). DELETE /api/archive/jobs/{id}: позволяет оператору (и backend-оркестратору при cleanup'е) явно удалить готовый архив с рекордера, не дожидаясь TTL-уборщика (1 час). Возвращает204при успехе,404если job'а уже нет.
Изменено
recordings.node_idпереименовано вrecordings.node_name: колонка, в которой хранится имя рекордера-владельца сегмента, теперь содержит человекочитаемый идентификатор (config.cluster.node_name, напримерS1) вместо числового Raft-id. Это сводит join с таблицейserversк одному equality по колонке, без необходимости запрашивать живое состояние кластера. Alembic-миграция применяется автоматически при апгрейде backend'а.
0.2.x
[0.2.11] - 2026-05-31
Изменено
- Connection pool базы данных увеличен: лимит подключений Postgres-бэкенда поднят с 5 до 10. Старое значение ставило в очередь metadata-вставки за любым долгим запросом (например, archive-скан) на нагруженной ноде.
Исправлено
- Потеря cluster state events при бёрстах: внутренний канал событий состояния кластера (назначения камер, смена ролей, переходы storage state) имел ёмкость 100 событий. При бёрсте — например, массовое перераспределение камер после демоушна ноды — самый медленный подписчик мог отстать и молча терять события. Ёмкость теперь 1000.
- Сломанный snapshot index маскировался молча: при применении снапшота от лидера непарсимое значение
snapshot_idдефолтилось в0, что выглядело неотличимо от свежеинициализированного стора и могло маскировать расхождение состояния между лидером и фолловерами. Сломанное значение теперь громко логируется, а текущий snapshot index сохраняется до следующего валидного снапшота. - Менее загадочные ошибки RocksDB recovery: если встроенное key-value хранилище не открывается (путь не writable, занят другим процессом, повреждённый column family) — сообщение об ошибке теперь явно указывает на причину вместо generic "failed to open" / "cf not found".
Добавлено
- Строгая валидация конфигурации при старте: сервер теперь отказывается стартовать, если
config.yamlсодержит любую из следующих проблем, агрегируя все нарушения в одно сообщение вместо падения на первом. Ловится при загрузке, а не на первом запросе / первой записи / первом TLS-хендшейке.- Две службы биндятся на один порт (например, RTSP и API оба на 8554).
- Камера с
record: trueбез resolvablerecord_path(ни на самой камере, ни вcameraDefaults.record_path). - URL камеры в
sourceпустой, без схемыrtsp:///rtsps://, либо не парсится как URL. Учётные данные маскируются в сообщениях об ошибках. encryption: trueна API или WebRTC сервере с отсутствующими/нечитаемыми файламиserverCert/serverKey.
[0.2.10] - 2026-05-30
Исправлено
- Устойчивость hot-reload: если reload сработал в момент когда редактор писал YAML-файл (получился усечённый файл), теперь делается до трёх попыток с интервалом 300мс прежде чем сдаться. Раньше транзитная ошибка парсинга молча оставляла старый конфиг активным без объяснений.
- Автоинициализация схемы БД: при
database.enabled=trueсхема и notification-триггер ставятся на старте (раньше путь был закомментирован). Если таблицыcamerasещё нет — ошибка логируется как warning, а не пропускается молча; hot-reload через DB-уведомления работает только после создания схемы. - Ограничение архивных запросов: запросы архивных сегментов через БД (бэкенд
database:) теперь возвращают максимум 10000 строк за запрос и логируют warning при достижении лимита. Защита от запросов с диапазоном в месяцы, выгребающих огромный результат через маленький connection pool. - Экранирование ONVIF SOAP-учётных данных: имена пользователей и строки topic-фильтров в SOAP-запросах теперь экранируются по XML. Пароль или имя пользователя с
&,<или"больше не ломает WS-Security envelope. - Hardening ONVIF-ответов: SOAP-ответы читаются с жёстким лимитом 10 MiB и отвергаются если содержат
<!DOCTYPEили<!ENTITY. Защита от компрометированной камеры, пытающейся истощить память или прозондировать XXE. - Лимит SDP-оффера WebRTC: WHEP/WHIP оффер больше 128 KiB отклоняется с понятной ошибкой. Реальные SDP на порядок меньше; защита от вредоносных или ломаных клиентов.
- Hardening нормализации временных меток кадров: краевой случай в обработке per-frame timestamp теоретически мог обрушить recorder, если audio-frame приходил раньше любого video-frame в той же сессии. На практике путь был недостижим, но фикс устраняет латентный риск.
[0.2.9] - 2026-05-30
Добавлено
- Per-camera health-метрики: новые Prometheus-серии для алертов о застывших или нестабильных камерах.
camera_last_frame_timestamp_seconds{camera="..."}обновляется на каждом видеокадре — алерт в Grafana видаtime() - camera_last_frame_timestamp_seconds > Nсработает когда камера замолчала.camera_reconnects_total{camera="..."}считает попытки переподключения по камере. Существующийrecording_errors_totalтеперь несёт labelcameraв дополнение кtype.
Изменено
- Graceful shutdown — полный охват: по SIGTERM/SIGINT теперь корректно останавливаются все фоновые сервисы (recorders, RTSP, WebRTC, management API, metrics-сервер). Recorder'ам даётся 3 секунды на финализацию открытых сегментов; вся последовательность ограничена 15 секундами, чтобы systemd никогда не доходил до SIGKILL. Ранее сигнал получали только camera-задачи и HLS-сервер.
Исправлено
- Утечка WebRTC-сессий: WHEP и WHIP сессии теперь удаляются через 30 минут бездействия (нет обмена ICE-кандидатами, нет переходов состояния). Фоновая зачистка работает каждые 60 секунд, закрывает peer connection и убирает запись из карты. До этого исправления брошенные сессии (клиент упал, сеть оборвалась без DELETE) копились в памяти на всё время жизни процесса.
[0.2.8] - 2026-05-30
Добавлено
- Асинхронные архивные операции (Export и Time-Lapse, единый job API):
POST /api/archive/{camera}/export?from&toиPOST /api/archive/{camera}/timelapse?from&to&speedвозвращают202 Acceptedсjob_id. Жизненный цикл задания черезGET /api/archive/jobs/{job_id}(pending→running→done/failed). Результат черезGET /api/archive/jobs/{job_id}/download(inline по умолчанию;?download=1форсит attachment). Интервал ограничен 4 часами; завершённые задания удаляются через 1 час. Параллелизм: 4 export'а (stream-copy) и 2 timelapse'а (re-encode) на ноду. - Periodic Camera Hook: новый блок
record.runPeriodicallyзапускает пользовательский скрипт для каждой камеры с фиксированным интервалом (по умолчанию 60 сек). Глобальный лимит конкурентности (maxConcurrent, дефолт 50) ограничивает суммарную CPU-нагрузку; первый запуск каждой камеры рандомно сдвигается чтобы 1000 камер не били одновременно. Основной use case — генерация live-превью через ffmpeg без блокировки recorder'а. - Health-пробы: новые
/api/health/live(процесс жив) и/api/health/ready(все зависимости доступны: storage, cluster store, каталог записей, лицензия). Прежний/api/healthостаётся для обратной совместимости. - Rate limit на API: per-IP token-bucket throttling на management API. Настройки в
api.rateLimit(enabled,perMinute,burst); дефолты 600 req/min, burst 60. При превышении возвращает429 Too Many Requests. - Crash dumps в файл: при panic'е процесса backtrace записывается в
<logging.directory>/crashes/<unix_ts>_<thread>.logдля пост-мортема. Stderr/journald output не отключается.
Известные ограничения
- Все архивные операции (
/archive/...,runPeriodically) работают только с файлами текущей ноды. В кластерных развёртываниях сегменты, созданные на других нодах ранее, ещё не доступны через API текущей ноды. Запланировано к доработке.
[0.2.7] - 2026-05-28
Добавлено
- Byte-range seek в архиве: записанные фрагментированные MP4-архивы теперь содержат индекс сегментов — ускоряет перемотку внутри файла через HTTP byte-range в плеерах и анализаторах.
- Хэш паролей Argon2id: в
security.users[].passпоявился новый префиксargon2:(формат PHC). Рекомендуется для production. Сравнение выполняется за константное время.
Изменено
- HLS Master Playlist BANDWIDTH: значение теперь рассчитывается по реальному размеру сегментов, а не из статической оценки — adaptive bitrate клиенты получают корректный ввод.
- HLS Media Playlist таймингы: каждый сегмент в
EXTINFотдаёт свою фактическую длительность,TARGETDURATIONадаптируется к наблюдаемому максимуму — меньше stall'ов на сегментах с разной длиной. - HLS Low-Latency совместимость: для парта на границе сегмента теперь выставляется
INDEPENDENT=YES, чтобы Safari и другие строгие плееры могли подключиться к live-потоку с середины сегмента. - Ужесточение формата учётных данных:
security.users[].passобязан содержать явный префикс (argon2:,sha256:,plain:). Значения без префикса отклоняются (fail-closed) — это исключает ситуацию, когда опечатка в префиксе молча превращает хеш в plaintext.
Исправлено
- ONVIF PTZ — переиспользование подключения: при отсутствии явных ONVIF-настроек PTZ теперь берёт endpoint, порт и учётные данные из RTSP-источника камеры, включая нестандартный порт.
- Большие MP4-записи: записи свыше 4 ГиБ больше не подмешивают молча обрезанные внутренние offset'ы — файлы корректно растут за границу 32-bit.
- Хвост аудио на конце сессии: аудио RTP-пакеты больше не теряются, когда видеотрек завершается чуть раньше в той же streaming-сессии.
- Лимит камер в standalone-лицензии: пустой или отсутствующий лимит больше не трактуется как «без ограничений» — применяется дефолт нелицензированного режима.
Несовместимые изменения
- Обязательный префикс пароля:
security.users[].passдолжен использовать один из префиксовargon2:,sha256:,plain:. Миграция: добавитьplain:к существующим значениям без префикса либо перехешировать вargon2:/sha256:.
[0.2.6] - 2026-04-10
Добавлено
- API управления WebRTC-сессиями: Добавлены endpoints
/api/webrtcдля получения списка активных WHEP-сессий, просмотра сессии по ID и принудительного завершения сессии из management API. - Встроенные WebRTC тестовые страницы: Добавлены HTML-страницы диагностики (
webrtc-test.html,webrtc-test-ptz.html) для быстрой проверки WebRTC playback и PTZ в браузере.
Изменено
- Маршруты сигналинга WHEP/WHIP: WebRTC маршруты приведены к стандартизованному виду (
/{camera_id}/whep,/{camera_id}/whip), добавлена обработка OPTIONS/PATCH и ICE-заголовковLinkдля клиентов. - Выдача статических HTML через API: Management API теперь отдает верхнеуровневые
*.htmlстраницы из packaged/dev путей с проверкой безопасности имени файла.
[0.2.5] - 2026-04-05
Добавлено
- Нативный WebRTC Playback (WHEP): Добавлен встроенный WebRTC сервер с WHEP endpoint и управлением жизненным циклом сессии (
POSToffer +DELETEзавершение сессии). - Расширение аудиокодеков WebRTC: Добавлена передача аудио AAC, G.711 A-law (PCMA) и G.711 μ-law (PCMU) в WebRTC playback.
- PTZ в основном API: Добавлены ONVIF PTZ endpoints в основной API ForgeVis (
/api/ptz/move,/api/ptz/stop) с проверкой возможностей камеры.
Изменено
- Контракт камеры в Control Plane: Расширены поля модели/DTO камеры в Control Plane (флаги поведения стриминга и входной признак назначения узла).
Исправлено
- Источник ONVIF-данных для PTZ: Данные endpoint/auth для PTZ ONVIF теперь последовательно вычисляются из RTSP source камеры.
[0.2.4] - 2026-03-20
Добавлено
- Watchdog для recorder в standalone: Добавлен периодический supervisor, который обнаруживает неожиданно завершившиеся recorder-задачи и автоматически перезапускает их в standalone-режиме.
Изменено
- Консолидация Cluster Health API: Данные о здоровье узлов, ролях и storage-состоянии теперь отдаются через
/cluster/metrics. - Совместимость Control Plane: Представления кластера и лицензий обновлены для работы с консолидированным ответом метрик кластера.
Исправлено
- Подсчет лицензируемых камер в standalone: Исправлена логика подсчета количества лицензируемых камер в standalone-развертываниях.
- Надежность восстановления recorder: Снижено количество случаев, когда запись оставалась остановленной после длительных проблем источника, за счет перезапуска умерших recorder-задач по watchdog-интервалу.
Ломающие изменения
- Удален endpoint:
GET /cluster/statusудален. ИспользуйтеGET /cluster/metrics.
[0.2.3] - 2026-03-13
Исправлено
- Включение аудио через hot-reload: При изменении камеры с
audio: falseнаaudio: trueнастройки потока теперь применяются без удаления и повторного создания конфигурации камеры. - Согласованность live-стриминга при reload: Обновление параметров потока теперь перезапускает активные live-сессии на уровне хаба, включая камеры без записи, используемые только для HLS/RTSP.
- Устойчивость к RTCP-ошибкам: Улучшена обработка некорректных коротких RTCP-пакетов от нестабильных прошивок камер, чтобы снизить количество лишних обрывов потока.
[0.2.2] - 2026-03-08
Добавлено
- Storage-aware failover в кластере: Узлы передают метрики диска и автоматически переходят в
Drainingпри низком свободном месте (<=8%) с возвратом вNormalпри восстановлении (>=18%). - Ручной Drain/Resume: Добавлены API и действия в дашборде для принудительной смены
Draining/Normalс поддержкой manual override. - Расширенные метрики кластера: Эндпоинт
/cluster/metricsтеперь включает per-node использование диска и состояние storage lifecycle. - Поддержка сборки для CentOS 10: Добавлена поддержка packaging pipeline для CentOS/RHEL 10.
- TLS для межузлового кластера: Добавлена TLS-защита для внутреннего cluster-трафика между узлами.
Изменено
- Политика назначения в кластере: Планировщик исключает
Drainingrecorder-узлы из новых назначений камер и перераспределяет нагрузку на доступные узлы. - UI топологии кластера: Карточки узлов теперь показывают lifecycle badge (
Online/Draining/Offline), progress bar использования диска и счетчик draining-узлов. - Архитектура метрик: Подсистема метрик разделена на collector/registry/server, а
cluster_nodes_*считаются лениво на запросе/metrics.
Исправлено
- Выбор лидера при demote: Улучшена обработка смены лидера при понижении узла.
- Защита от offline-операций: Cluster API отклоняет изменение роли и storage-state для offline-узлов.
[0.2.1] - 2026-03-04
Добавлено
- Роли узлов кластера: Добавлено ролевое поведение узлов (
recorder/streamer) с динамической сменой через cluster API и отображением ролей в статусе кластера. - Хеширование учетных данных во внутренней авторизации: Добавлена поддержка
sha256:дляsecurity.users.userиsecurity.users.pass. - Управление размещением потоков в кластере: Добавлена role-aware обработка потоков с учетом назначения камеры и протокольных политик.
Изменено
- Применение HLS/RTSP политик в кластере: Планировщик и runtime теперь согласованно применяют per-camera настройки
hls,rtspиalwaysRemuxв cluster mode. - Поведение HLS для per-camera overrides: Уточнен приоритет глобальных и камерных remux-настроек, а также применение запрета HLS на уровне камеры.
Исправлено
- Надежность перезапуска Recorder: Улучшен перезапуск после изменений состояния камер и циклов перераспределения.
- Безопасность hot-reload лицензии: Усилена валидация лицензии при перезагрузке конфигурации.
[0.2.0] - 2026-01-10
Добавлено
- High Availability Кластеризация: Полностью новая распределённая архитектура на базе алгоритма консенсуса Raft со встроенным key-value хранилищем для реплицируемого состояния.
- Общее Состояние: Все узлы поддерживают согласованное состояние назначений камер и здоровья узлов.
- Автоматический Failover: Лидер автоматически обнаруживает "мертвые" узлы (таймаут > 15с) и перераспределяет их камеры на живые узлы.
- Внутреннее Проксирование: API запросы к Follower-узлам прозрачно проксируются Лидеру через внутренний HTTP клиент.
- Auto-Join: Новые узлы с пустой базой и настроенными
peersавтоматически находят кластер и присоединяются в роли Learners. - Миграция без простоя: Камеры можно динамически переносить между узлами через API.
- Мониторинг Кластера:
- Новый эндпоинт
/cluster/statusпоказывает здоровье (Online/Offline) всех узлов в реальном времени. - Интеграция с Frontend Dashboard для визуализации состояния кластера.
- Новый эндпоинт
- Backend Хранения:
- Встроенное key-value хранилище: высокопроизводительная локальная персистентность для логов Raft и снепшотов машины состояний.
- Log Compaction: Автоматическая очистка старых логов и создание снапшотов для экономии места.
0.1.x — ранние релизы
[0.1.16] - 2026-02-21
Исправлено
- Прогрессивное воспроизведение MP4: Улучшена структура сегментов обычного MP4, теперь воспроизведение начинается сразу, без ожидания полной загрузки файла.
- Совместимость атомов fMP4: Структура атомов и заголовков фрагментов fMP4 приведена к ffmpeg-совместимому виду (
ftyp, порядокmoov, флагиtfhd/trun) для лучшей совместимости с плеерами.
Улучшено
- Точность проверки fMP4: Уточнён процесс box-level валидации для сравнения порядка атомов и ключевых метаданных фрагментов с эталонным выводом ffprobe.
[0.1.15] - 2026-02-20
Добавлено
- Два формата записи: Добавлен выбор формата записи через
record.formatс поддержкой и fragmented MP4 (fMP4), и обычного MP4. - Обычный MP4 для архива: Добавлен режим записи в стандартный MP4 с поддержкой видео H.264/H.265 и аудио AAC/G.711.
Изменено
- Архитектура Recorder: Логика записи приведена к модульной структуре writer-компонентов для более чистого разделения форматов и дальнейшего расширения.
[0.1.14] - 2026-02-20
Добавлено
- Быстрая перемотка: Реализован атом
mfra(Movie Fragment Random Access) в конце MP4 файлов для поддержки мгновенной перемотки в плеерах (VLC, QuickTime, Web).
Оптимизировано
- Завершение файлов: Улучшен процесс финализации записи с добавлением индексов доступа (Random Access Table).
[0.1.13] - 2026-02-20
Исправлено
- Seeking & Progressive Playback: Исправлена структура FMP4 (
trunиtfhdатомы) для корректной поддержки перемотки (seeking) в браузерах без полной загрузки файла. - Совместимость MP4: Изменен
brandформата сiso5наisomдля лучшей совместимости с плеерами. - Длительность файлов: Исправлено обновление длительности видео (
mvhdatom) даже при аварийном завершении записи.
[0.1.12] - 2026-02-14
Оптимизировано
- Производительность: Отключены высокочастотные метрики для снижения конкуренции за блокировки (lock contention) при нагрузке >1000 камер.
- Disk I/O: Оптимизировано обновление заголовков MP4, что снизило количество операций ввода-вывода при записи.
[0.1.11] - 2026-02-10
Исправлено
- Стабильность HLS: Исправлена проблема с бесконечным перезапуском плейлиста при реконнекте RTSP потока.
- Логирование: Снижен объем логов для более чистого вывода в продакшене.
[0.1.10] - 2026-02-05
Исправлено
- Поддержка Аудио: Исправлено воспроизведение G.711 (PCMU/PCMA) за счет обновления библиотеки RTSP клиента.
- Обработка ошибок: Улучшена отчетность об ошибках при сбоях RTSP подключения.
[0.1.9] - 2026-01-28
Добавлено
- Аудио кодеки: Добавлена поддержка кодеков G.711u (PCMU) и G.711a (PCMA).
Исправлено
- Синхронизация видео: Исправлены проблемы рассинхронизации A/V на потоках с нестабильными метками времени.
[0.1.8] - 2026-01-15
Исправлено
- Обработка Timestamp: Улучшена обработка немонотонных временных меток от некоторых моделей камер.
[0.1.7] - 2025-12-30
Добавлено
- Безопасность потоков (внутренняя авторизация): Новый блок конфигурации
securityс внутренними пользователями, IP/CIDR‑фильтрами и разрешениями по действиям (publish,read,api,metrics) для единой модели управления доступом. - Защита live‑потоков и метрик: При непустом
security.usersвсе live‑эндпоинты (RTSP, HLS, WebSocket) требуют разрешенияread, а endpoint/metrics— разрешенияmetrics, с поддержкой анонимного пользователяuser: anyи IP‑исключений (например, для локального Prometheus).
Изменено
- Обратная совместимость по умолчанию: Если блок
securityотсутствует или списокusersпуст, все эндпоинты продолжают работать без аутентификации, как в предыдущих версиях.
[0.1.6] - 2025-12-28
Добавлено
- Метрики дискового I/O процесса (Linux): Новые метрики Prometheus на основе
/proc/self/ioдля отслеживания байт чтения/записи для процесса ForgeVis:process_io_read_bytes_total,process_io_write_bytes_total,process_io_cancelled_write_bytes_total. - Панель Grafana для I/O: Обновлён стандартный дашборд Grafana — добавлена панель "Process Disk IO (Read/Write MB/s)" для визуализации дискового I/O процесса и корреляции с
Frames Lostи событиями отставания буфера.
[0.1.5] - 2025-12-24
Добавлено
- Low-Latency HLS (LL-HLS): Собственная реализация Low-Latency HLS поверх fMP4 сегментов с поддержкой HLS parts. Новый режим
hls.variant: "lowLatency"с настраиваемымиsegmentDurationиpartDuration, оптимизированный под hls.js в режимеlowLatencyMode.
Исправлено
- Поддержка B-frames: Реализована поддержка B-кадров в fMP4. Добавлен атом
ctts(Composition Time to Sample) и логика переупорядочивания кадров (reordering buffer) для корректного вычисления Presentation Time Stamp (PTS) и Decode Time Stamp (DTS). Это устраняет "дергание" видео на потоках с B-кадрами. - Синхронизация (tfdt drift): Исправлен дрейф времени в атоме
tfdt. Теперь базовое время декодирования фрагмента берется из реального DTS первого кадра, а не накапливается суммированием длительностей, что предотвращает рассинхронизацию при длительной записи.
[0.1.4] - 2025-12-24
Исправлено
- Запись архива: Исправлена проблема с нулевой длительностью в заголовке
mvhdфайлов fMP4. Теперь плееры корректно отображают длительность и позволяют перематывать видео. - Синхронизация: Исправлена проблема с жестко заданной длительностью кадров. Теперь длительность рассчитывается динамически на основе временных меток, что устраняет рассинхронизацию аудио и видео.
- Производительность: Обновление метаданных файла вынесено в фоновый процесс, чтобы исключить задержки между записью сегментов.
[0.1.3] - 2025-12-23
Исправлено
- Перемотка архива: Критическое исправление флагов атома
trunи флагов сэмплов в fMP4. Браузеры теперь корректно находят ключевые кадры внутри фрагментов. - Стабильность WebSocket: Исправлена проблема с бесконечным переподключением в React-плеере при переключении между режимами Live и Archive.
[0.1.2] - 2025-12-22
Добавлено
- Редизайн фронтенда: Миграция UI компонентов (CameraList, ArchiveList) на Bootstrap 5 для более чистого и адаптивного вида.
- Улучшения плеера:
- Улучшенное поведение в полноэкранном режиме с сохранением пропорций.
- Кастомный таймлайн.
Исправлено
- Перемотка архива: Исправлено точное позиционирование при перемотке архивных записей.
[0.1.1] - 2025-12-21
Добавлена поддержка низколатентного стриминга через WebSocket (MSE).
Стриминг
- Low-Latency MSE (WebSocket)
- Прямой стриминг fMP4 фрагментов в браузер через WebSocket
- Сверхнизкая задержка (< 1 секунды)
- Поддержка Media Source Extensions (MSE)
[0.1.0] - 2025-12-20
Крупный релиз, включающий Management API, Graceful Shutdown и интеграцию с Legacy системами.
Management API
- HTTP интерфейс управления
- Новый сервер Management API на порту 9997 (настраивается)
- Эндпоинт
/healthдля мониторинга состояния системы - Эндпоинт
/cameras/startдля массового запуска всех камер - Эндпоинт
/cameras/stopдля массовой остановки всех камер - Поддержка CORS для веб-интеграции
Надежность
- Graceful Shutdown
- Корректная обработка сигналов SIGINT (Ctrl+C) и SIGTERM
- Гарантированное завершение записи и сохранение файлов перед выходом
- Корректная остановка RTSP и HLS серверов
[0.0.10] - 2025-12-19
Добавлена поддержка интеграции событий ONVIF.
Интеграция ONVIF
- Мониторинг событий
- Подписка на события камеры через ONVIF PullPoint
- Поддержка детекции движения, цифровых входов, пересечения линий и др.
- Настраиваемый интервал опроса и топики событий
- Автоматическое извлечение учетных данных из RTSP URL
- Индивидуальная настройка ONVIF для каждой камеры в
camera.yaml
[0.0.9] - 2025-12-16
Исправления совместимости HLS с hls.js для работы с дефолтными настройками плеера.
HLS улучшения
- Динамические параметры плейлиста
#EXT-X-TARGETDURATIONтеперь берётся из конфигаsegmentDuration#EXTINFиспользует значение из конфига вместо захардкоженного- Удалён тег
#EXT-X-STARTдля совместимости с дефолтными настройками hls.js - Поддержка настраиваемой длительности сегментов (1s, 4s и т.д.)
Исправления ошибок
- Совместимость плейлистов
- Исправлена генерация плейлистов для работы с hls.js без кастомных настроек
- Плейлисты теперь соответствуют формату ffmpeg
- Убраны теги, вызывающие проблемы с дефолтной конфигурацией hls.js
Конфигурация
- Параметр
hls.segmentDurationтеперь влияет на:#EXT-X-TARGETDURATIONв плейлисте#EXTINFдля каждого сегмента- Фактическую длительность создаваемых сегментов
[0.0.8] - 2025-12-12
Система мониторинга и метрик для production с дашбордом Grafana.
Мониторинг и наблюдаемость
Метрики Prometheus
- 14 агрегированных метрик для мониторинга системы (масштабируется до 2000+ камер)
- HTTP endpoint
/metrics(порт 9998 по умолчанию) - Автоматическая маркировка hostname для мультисерверных развертываний
- Статус камер: зарегистрировано, записывается, ошибки
- Метрики потоков: активные подключения (основной/субпоток)
- Производительность: частота видеокадров, статус broadcast буферов
- HLS метрики: активные мюксеры, размер сегментов
- Отслеживание ошибок: ошибки записи по типам, события отставания broadcast
- Системные метрики: память процесса (RSS)
- Поддержка CORS для кросс-доменного доступа
Файловое логирование
- Ежедневная ротация логов с
tracing-appender - Отдельные файлы:
forgevis-info-YYYY-MM-DD.log,forgevis-error-YYYY-MM-DD.log - Настраиваемые уровни логирования (trace, debug, info, warn, error)
- Вывод в консоль с правильной фильтрацией
- Убраны дублирующиеся логи от внутренних компонентов
- Ежедневная ротация логов с
Дашборд Grafana
- Готовый JSON дашборд с 8 панелями визуализации
- Компоновка из 4 рядов: Статус камер, Производительность, Ресурсы, Ошибки
- Поддержка множества серверов с выпадающим фильтром hostname
- Автообновление каждые 5 секунд для мониторинга в реальном времени
- Использование памяти в MB (правильная конвертация единиц)
- Полный справочник метрик в документации
Исправления ошибок
Метрики HLS мюксера
- Исправлен счётчик HLS мюксеров, который не уменьшался при очистке
- Реализовано правильное отслеживание жизненного цикла через Drop trait
- Добавлен канал shutdown для чистой остановки задач
- Предотвращены дублирующиеся уменьшения метрик с координационным флагом
- Гарантирована точность подсчёта активных HLS мюксеров
Управление памятью
- Задачи HLS мюксера теперь правильно завершаются при очистке
- Исправлена утечка памяти от осиротевших фоновых задач
- Сигнал shutdown распространяется на все компоненты мюксера
Документация
- Руководство по мониторингу (английский и русский)
- Полные инструкции по настройке Prometheus
- Руководство по импорту дашборда Grafana
- Все метрики с описаниями
- Паттерны мультисерверного развертывания
- Диагностика проблем с памятью и производительностью
Конфигурация
- Новые настройкиyaml
logging: directory: /var/log/forgevis level: info console: true metrics: enabled: true address: :9998 allowOrigin: '*'
[0.0.7] - 2025-12-11
Изменено соглашение об именовании HLS плейлистов для лучшей консистентности API.
Улучшения HLS
- Именование плейлистов
- Видео плейлист:
video.m3u8→media_0.m3u8 - Аудио плейлист:
audio.m3u8→media_1.m3u8 - Видео init сегмент:
init_video.mp4→init_media_0.mp4 - Аудио init сегмент:
init_audio.mp4→init_media_1.mp4 - Видео сегменты:
video_{N}.m4s→seg_media_0_{N}.m4s - Аудио сегменты:
audio_{N}.m4s→seg_media_1_{N}.m4s - Централизованные константы именования HLS в одном внутреннем модуле
- Видео плейлист:
[0.0.6] - 2025-12-11
Поддержка UDP транспорта для RTSP.
Улучшения RTSP
- UDP транспорт
- Поддержка UDP транспорта наряду с TCP (interleaved)
- Настраиваемые UDP порты для RTP/RTCP (
udp_rtp_port,udp_rtcp_port) - Общие UDP сокеты для множества клиентов (эффективное использование ресурсов)
- Автоматическое определение транспорта из SETUP запроса клиента
- Работает с VLC, ffplay и другими RTSP клиентами
Конфигурация
- Новые настройки RTSP
rtsp.udp_rtp_port(по умолчанию: 8000) — UDP порт для RTP пакетовrtsp.udp_rtcp_port(по умолчанию: 8001) — UDP порт для RTCP пакетов
[0.0.5] - 2025-12-09
Поддержка аудио AAC для записи и стриминга.
Видео кодеки
- Поддержка H.265/HEVC
- Полная поддержка кодека H.265 наряду с H.264
- Автоматическое определение кодека (H.264 или H.265)
- RTP пакетизация H.265 (RFC 7798)
- Извлечение параметров VPS/SPS/PPS
- Запись в формате HVCC
Запись аудио
Кодек AAC
- Автоматическое обнаружение аудио потока AAC через RTSP
- Парсинг AudioSpecificConfig (частота дискретизации, каналы)
- Запись fMP4 с двумя дорожками (отдельные видео и аудио дорожки)
- Идеальная синхронизация аудио/видео в записях
Настройка аудио
- Включение/отключение аудио для каждой камеры (
audio: true/false) - Глобальная настройка аудио по умолчанию
- Автоматический откат к видео-only если аудио недоступно
- Включение/отключение аудио для каждой камеры (
Стриминг аудио
RTSP ретрансляция с аудио
- Мультитрековые RTSP сессии (видео + аудио)
- RFC 3640 AAC RTP payload формат
- RTCP Sender Reports для обеих дорожек
- Переиспользование сессии для SETUP запросов (совместимость с VLC)
- Работает с VLC, ffplay и другими RTSP клиентами
HLS стриминг с аудио
- Архитектура мультивариантного плейлиста (master playlist)
- Раздельные плейлисты для видео и аудио
- Отдельные файлы сегментов видео/аудио (video_N.m4s, audio_N.m4s)
- Раздельные инициализационные сегменты (init_video.mp4, init_audio.mp4)
- Стандартная совместимость с HLS.js
Техническая реализация
Аудио пайплайн
- RTSP клиент retina с депакетизацией аудио
- Широковещание AAC фреймов через StreamHub
- Двухканальная архитектура (video_receiver + audio_receiver)
- Расчёт длительности сэмплов (1024 сэмпла на AAC фрейм)
Улучшение FMP4 Writer
- Запись двухтрековых mdat/moof боксов
- Отдельный аудио trak в moov боксе
- Таблица аудио сэмплов (stbl) с корректным timescale
- AudioSampleEntry (mp4a) с esds дескриптором
Качество кода
- Удалены неиспользуемые переменные и функции
- Очищен dead code (сохранены публичные API функции)
- Ноль предупреждений компиляции
- Рефакторинг CameraRecorder (удалены лишние параметры)
[0.0.4] - 2025-12-09
Горячая перезагрузка и продвинутое управление стримами.
Система горячей перезагрузки
Мониторинг конфигурации
- Автоматическое отслеживание
config.yamlиconf.d/*.yaml - Защита от дребезга (500ms) при быстрых изменениях файлов
- Обновление конфигурации камер в реальном времени без перезапуска
- Автоматическое отслеживание
Динамическое управление камерами
- Добавление/удаление камер на лету
- Изменение источников камер (изменение URL)
- Включение/отключение записи для отдельных камер
- Корректное закрытие стримов при удалении камеры
Архитектура управления стримами
Централизованный реестр камер
- StreamHub как единственный источник истины о конфигурации камер
- Автоматическая регистрация/разрегистрация при горячей перезагрузке
- Поддержка основного потока и субпотока
Стриминг по требованию
- HLS мюксеры создаются только при запросе клиента
- RTSP стримы только для зарегистрированных камер
- Валидация перед созданием мюксера (устраняет лишнее выделение ресурсов)
- Корректные 404 ответы для несуществующих камер
Управление жизненным циклом стримов
- Остановка стримов через CancellationToken
- Немедленная очистка при удалении камеры из конфигурации
- Автоматическая остановка основного и суб-потока при удалении камеры
- Отключение RTSP клиентов при отмене стрима
Технические улучшения
- Удалена дублирующаяся конфигурация из HLS/RTSP серверов
- Унифицированная валидация стримов через StreamHub
- Улучшенная обработка ошибок для отсутствующих камер
- Потокобезопасный реестр камер
Исправления
- Исправлено продолжение RTSP стримов после удаления камеры
- Исправлено создание HLS мюксеров для несуществующих камер
- Исправлено создание субстримов (/sub) по требованию
- Устранены все предупреждения компиляции
[0.0.3] - 2025-12-08
Автоматизация и интеграция через Event Hooks.
Event Hooks
Хуки на события сегментов
runOnSegmentCreate- выполняется при начале записи сегментаrunOnSegmentComplete- выполняется при завершении записи сегмента- Передача переменных окружения: SF_PATH, SF_SEGMENT_PATH, SF_SEGMENT_DURATION, RTSP_PORT
Возможности интеграции
- Уведомления в Discord/Slack/Telegram
- Загрузка в облачное хранилище (S3, GCS, Azure)
- Генерация превью и миниатюр
- Экспорт метрик в Prometheus
- Автоматическая очистка старых записей
- Пользовательская обработка видео
Техническая реализация
- Асинхронное выполнение хуков без блокировки записи
- Логирование успеха/ошибок выполнения хуков
- Поддержка shell команд и скриптов
- Документация с примерами интеграций
[0.0.2] - 2025-12-07
Веб-интерфейс и функции воспроизведения архива.
[0.0.1] - 2025-12-04
Начальный релиз ForgeVis - сервера записи видео и ретрансляции.
Функции записи
Фрагментированная запись MP4
- Полная реализация ISO BMFF (Base Media File Format)
- Формат fMP4 с правильной структурой блоков (ftyp, moov, moof/mdat)
- Формат AVCC для H.264 (4-байтные префиксы длины)
- Файлы воспроизводятся во время записи
- Интервалы фрагментов 2 секунды для низкой задержки доступа
Управление сегментами
- Сегментация по времени (настраиваемая, по умолчанию 60 секунд)
- Автоматическое создание директорий
- Шаблоны путей с плейсхолдерами
{camera_id}и{timestamp} - Последовательная нумерация с 6-значным заполнением
Потоковая передача и распределение
Архитектура StreamHub
- Одно RTSP-соединение на камеру
- Broadcast (fan-out) паттерн для раздачи кадров нескольким потребителям
- Буфер на 1000 кадров для нескольких потребителей
- Автоматическое извлечение SPS/PPS
- Прозрачное распределение кадров для рекордера и сервера ретрансляции
RTSP сервер ретрансляции
- Полная реализация протокола RTSP (OPTIONS, DESCRIBE, SETUP, PLAY, TEARDOWN)
- Генерация SDP с параметрами H.264 (sprop-parameter-sets)
- RTP over TCP (режим interleaved)
- RTP пакетизация H.264 (совместимость с RFC 6184)
- Поддержка нескольких клиентов на камеру
- Автоматический парсинг NAL-юнитов из формата AVCC
Конфигурация
- Система конфигурации YAML
- Настройки хранения (длительность сегмента, шаблон пути, формат)
- Конфигурация RTSP сервера (порт)
- Настройки для каждой камеры (URL источника, флаг enabled)
- Учётные данные встроены в RTSP URL
Техническая реализация
- Чистый Rust: Нет системных зависимостей
- Zero-Copy: Эффективная обработка кадров
- Правильная обработка H.264: Хранение в формате AVCC с корректными флагами
Производительность
- Одно соединение на камеру предотвращает перегрузку
- Broadcast канал поддерживает неограниченное количество подписчиков
- Обработка задержек для медленных потребителей (пропуск кадров при необходимости)
- Эффективный RTP стриминг (тактовая частота 90kHz, правильный тайминг)
Известные ограничения
- Только H.264 (пока нет поддержки H.265/HEVC)
- Только видео (нет записи аудио)
- Только TCP interleaved (нет RTP over UDP)
- Нет DASH вывода
- Нет UI для метрик/мониторинга
Протестированные конфигурации
- ✅ Реальная RTSP IP камера (1920x1080, 25fps, H.264)
- ✅ Запись с правильной структурой MP4
- ✅ Ретрансляция проверена с VLC
- ✅ Одновременная работа рекордера + несколько RTSP клиентов
- ✅ Файлы воспроизводятся в VLC во время записи