◆ ForgeVis
Skip to content

История изменений ​

[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/N pass на 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 без resolvable record_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 теперь несёт label camera в дополнение к 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 и управлением жизненным циклом сессии (POST offer + 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-трафика между узлами.

Изменено ​

  • Политика назначения в кластере: Планировщик исключает Draining recorder-узлы из новых назначений камер и перераспределяет нагрузку на доступные узлы.
  • 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 для лучшей совместимости с плеерами.
  • Длительность файлов: Исправлено обновление длительности видео (mvhd atom) даже при аварийном завершении записи.

[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 во время записи

Proprietary software.