Подключение видеокамер
Камера в платформе — это коннектор с адресом RTSP и тег типа «Видеопоток». Кадры не проходят через RabbitMQ и не пишутся в PostgreSQL или VictoriaMetrics. Их принимает сервис подхвата: один сеанс RTSP на камеру и архив на диске. Сервис отдачи забирает у него байты по HTTP и отдаёт их платформе.
Оба сервиса входят в обычный запуск ./run_one_app.sh.
Подхват — контейнер prs-video-capture, архив лежит в томе
/var/lib/peresvet/video. Отдача — prs-video-delivery, тома архива
у неё нет. Порты обоих контейнеров наружу не публикуются.
Два сервиса
Подхват и отдача наследуют AppSvc. Список nodes — поле
конфигурации процесса (AppSvcSettings). Это не узел каталога
и не атрибут LDAP.
Подхват обслуживает коннекторы-камеры. Пустой
nodesзначит все камеры. Процесс держит RTSP, пишет архив, говорит с OnVif и открывает MQTT-сессию индикатора связи. Свой адрес для соседей он берёт изhttp_base_urlи сообщает его коротким RPCvideo.capture.lookup.<id коннектора>.Отдача обслуживает теги. Пустой
nodesзначит все теги видеопотока. НаprsTag.modelона не подписывается: голос в создании и удалении тега ей не принадлежит. Адрес она сообщает RPCvideo.delivery.locate.<id тега>.
Браузер ходит только в /v1/data/ и /v1/ptz/. Платформа проверяет право,
по RPC узнаёт отдачу и проксирует запрос по HTTP. Отдача так же узнаёт подхват
и забирает поток, звук, снимок или фрагмент по HTTP внутри сети compose.
В теле сообщения брокера только идентификатор и базовый URL.
Один id камеры обслуживает один подхват. Либо один процесс с пустым
nodes, либо несколько с непересекающимися списками. Два процесса
на одну камеру откроют два сеанса RTSP. Два процесса с одним и тем же
первым id в nodes делят одну очередь брокера.
При непустом nodes базовый AppSvc не подписывает подхват на
создание коннектора. Подхват подписывается на prsConnector.model.created
сам и оставляет только камеры из списка. Правка хранилища видео приходит
сообщением prsDataStorage.model.created, updated.<id> или
deleted.<id> и применяется сразу. При старте подхват один раз читает
уже существующие камеры: брокер не повторяет старые события.
Контракт GET /v1/data/ и POST /v1/ptz/ разобран в Видеопоток (тег типа 6).
Здесь — порядок действий в конфигураторе.
Что нужно от камеры
Поток
rtsp://илиrtsps://. Адрес пути смотрите в паспорте камеры. Логин и пароль можно вписать в сам адрес:rtsp://user:password@192.168.1.72:554/live.Для поворота, зума и параметров изображения — OnVif. Хост берётся из адреса RTSP. Порт по умолчанию
8899, его меняют ключомonvifPort.Платформа уже запущена, конфигуратор открыт.
Коннектор
В папке «Коннекторы» создайте коннектор.
Дайте ему имя, например
rear-cam.В поле Конфигурация вставьте JSON и нажмите Записать.
{
"source": {
"rtspUrl": "rtsp://192.168.1.72:554/live/ch00_0",
"rtspTransport": "tcp",
"onvifPort": 8899,
"onvifUser": "",
"onvifPassword": ""
},
"log": {
"level": "INFO",
"fileName": "logs/prs_connector_{id}.log",
"maxBytes": 10485760,
"backupCount": 10,
"sendToPlatform": false
}
}
Как у остальных коннекторов, параметры источника лежат в source,
а журнал — в log. Ключ source.rtspUrl обязателен. По нему платформа
и считает коннектор камерой. Плоский JSON, где rtspUrl стоит в корне,
тоже принимается. Дополнительно можно выставить код типа узла 10 —
этого достаточно, чтобы узел считался камерой, но адрес потока код не заменяет.
Ключ log тот же, что у коннектора в prs-connector-core.
Ключ |
Смысл |
|---|---|
|
Минимальный уровень строк файла: |
|
Путь к файлу журнала на машине подхвата.
|
|
Максимальный размер одного файла в байтах. По умолчанию 10485760. |
|
Сколько предыдущих файлов хранить. По умолчанию 10. |
|
Флажок Лог в платформу. Пока выключен, строки остаются в файле.
Когда включён, те же строки уходят сообщением |
Строки декодера ffmpeg в этот журнал не пишутся: их слишком много, они остаются в журнале контейнера.
Ключ |
Смысл |
|---|---|
|
Адрес потока |
|
|
|
Порт OnVif, от 1 до 65535. По умолчанию 8899. |
|
Логин OnVif. Пустая строка — камера логин не требует. |
|
Пароль OnVif. |
Сохранение коннектора публикует в брокер сообщение
prsConnector.model.updated.<id>. Видеосервер заново читает конфигурацию.
Новый адрес или транспорт открывают сеанс заново.
Хранилище видео
Каталог архива и срок хранения задаются у хранилища данных с кодом типа 3,
а не у коннектора. Коннектор только поставляет поток.
{
"archivePath": "/var/lib/peresvet/video",
"retentionHours": 24,
"archiveWidth": null,
"archiveFps": null
}
archivePath — абсолютный каталог. Каждая камера пишется в подкаталог
<archivePath>/<id коннектора>. Каталог вне тома
/var/lib/peresvet/video пропадёт вместе с контейнером.
retentionHours — срок хранения в часах. Если ключ опущен — 24.
archiveWidth — ширина картинки архива в пикселях, чётное число.
Высота подстраивается под кадр. null — ширина камеры, поток копируется.
archiveFps — частота кадров архива. null — частота камеры.
Число больше нуля задаёт частоту явно и включает кодирование архива.
Сохранение хранилища публикует prsDataStorage.model.updated.<id>.
Подхват по этому сообщению заново читает путь, срок, ширину и частоту.
Новые ширина, частота или путь открывают сеанс заново. Новый срок
удаляет лишние фрагменты и не переоткрывает поток.
Тег видеопотока привязывают и к коннектору-камере, и к этому хранилищу. Пока такой пары нет, сеанс RTSP не открывается и клиент не получает кадр. Подхват забирает поток у коннектора и пишет архив. Отдача отвечает на чтение тега.
Пока сеанс RTSP жив, кружок у коннектора зелёный. Коды качества 100 и 101 в теги камеры не пишутся.
Тег «Видеопоток»
Под объектом или в папке «Теги» создайте тег.
В поле Тип значений выберите «Видеопоток».
На вкладке Коннектор выберите коннектор камеры.
Нажмите Записать.
История такого тега в хранилище данных не ведётся.
Если при создании тег успел привязаться к хранилищу истории,
запись свойств снимает эту связь. Хранилище видео (тип 3) и
коннектор-камера остаются.
Просмотр открывается на вкладке Хранилище, даже если хранилище не выбрано.
Просмотр
На вкладке Хранилище блок Данные тега у видеопотока открыт без привязки к истории.
Кнопка с треугольником читает тег по тем же правилам, что GET /v1/data/.
Пустые
startиfinish— живой поток.Только
finish— снимок на этот момент. Если фрагмента нет, картинки не будет.startиfinish— фрагмент за период. Пока подхват склеивает mp4, на месте кадра написано «Загрузка…».
Пока картинка открыта, та же кнопка показывает паузу и закрывает просмотр в браузере. Запись архива при этом не останавливается: сеанс держит сервис подхвата, а не открытая страница. Кнопка в углу кадра распахивает картинку на весь экран; повторное нажатие или Escape возвращает её в панель.
Кнопка Микрофон говорит в динамик камеры, пока её держат. Браузер отдаёт микрофон только со страницы по HTTPS и только если для этого сайта микрофон разрешён (значок замка слева от адреса). Звук идёт на подхват по HTTP и оттуда в камеру отдельным каналом, не вторым RTSP и не через RabbitMQ. Если камера не принимает разговор, над кадром появляется текст ошибки.
Выключенный тег видеопотока останавливает сеанс этой камеры: приём RTSP, запись архива и индикатор связи. Включение тега поднимает сеанс снова. Если к одной камере привязано несколько таких тегов, сеанс идёт, пока включён хотя бы один.
Подсказка под кадром повторяет эти три режима.
Метку времени кадра ставит подхват: это момент, когда кадр пришёл в сервис, а не часы камеры. У H.264 в RTSP шкала всегда 90 кГц. Камера, которая сдвигает её на один тик за пакет, для ffmpeg выглядит как 90 000 кадров в секунду: архив раздувается, живой просмотр кадров почти не получает. Кадры ближе 20 мс считаются одним кадром и в запись не попадают.
Для картинки в браузере подхват один раз кодирует H.264: 15 кадров в
секунду и ширина 960 пикселей, профиль Baseline. Этот поток копируется
всем зрителям по WebRTC, отдельного кодирования на каждого нет.
Если сеанс уже идёт, новый зритель к нему подключается и камеру заново
не открывает. У камеры без звуковой дорожки следующий сеанс звук не пробует.
Сигналинг — POST /v1/video/webrtc через nginx. Кадры тоже идут в nginx,
на порт 8189 (UDP и TCP), и он передаёт их подхвату. Картинка звук не несёт. Если в том
же RTSP есть звук, конфигуратор включает его отдельным проигрывателем:
к адресу чтения добавляется audio=1, ответ — audio/mpeg. Второй
сеанс к камере не открывается. В архив звук не пишется: по умолчанию
туда копируется поток камеры, без второго кодирования.
Поворот и параметры изображения
Кнопки стоят слева от кадра и ходят по OnVif, второе RTSP-подключение при этом не открывается.
Стрелки двигают камеру, пока их держат. Если камера объявляет шкалу скорости больше 1, платформа один раз поднимает скорость до максимума: иначе команда принимается сразу, а объектив ползёт.
Ближе и Дальше делают один шаг зума.
Стоп останавливает поворот и наклон сразу.
Домой возвращает камеру по движениям, сделанным с этой страницы с момента старта сервиса подхвата, и снимает накопленный зум. Сама камера положение не запоминает.
Под стрелками — параметры, которые камера отдала по OnVif: яркость, контраст, насыщенность, резкость, засветка, динамический диапазон, ИК-фильтр (день, ночь, авто) и баланс белого. Ползунок или список отправляет только изменённый параметр.
Камера может ответить успехом и при этом не изменить кадр: прошивка принимает зум и параметры изображения, сохраняет числа и не применяет их к объективу. Поворот и наклон при этом могут работать.
Архив
Архив — фрагменты MPEG-TS. Имя файла — время начала в секундах Unix,
например 1791117621.ts. Номинальная длина фрагмента —
VIDEO_SEGMENT_SECONDS, по умолчанию 10 секунд.
Если сеанс перезапускался, промежуток между файлами бывает длиннее.
Файлы старше retentionHours удаляет сам открытый сеанс.
Новый срок приходит сообщением prsDataStorage.model.updated.<id>
и применяется сразу.
Список фрагментов:
docker exec prs-video-capture ls -lh /var/lib/peresvet/video/rear-cam
Запись за период смотрят в конфигураторе: у тега задают start и finish
и нажимают кнопку воспроизведения. Плеер получает один mp4,
склеенный из покрывающих период фрагментов. Период длиннее
VIDEO_MAX_FRAGMENT_SECONDS (по умолчанию 7200 секунд) платформа отклоняет.
Объём архива одной камеры примерно часы хранения × битрейт, Мбит/с × 0,44 гигабайт.
Битрейт зависит от сюжета и от archiveWidth / archiveFps,
его меряют по уже записанным файлам.
Нагрузка считается по числу камер: на каждую один процесс приёма RTSP,
записи и подготовки просмотра в контейнере подхвата. Число открытых страниц
на это кодирование не влияет. Перед следующей камерой смотрите память
контейнера prs-video-capture.
Своя страница
Живой поток можно поставить на страницу того же хоста, что и платформа.
Адрес /v1/data/ уже проксируется nginx.
<img id="cam" alt="Камера"
src="/v1/data/?tagId=67bde18c-5431-1041-925c-470278307e33">
При включённой авторизации браузер отправит только куку этого хоста,
поэтому страница должна открываться с него же.
Чтобы остановить просмотр, уберите адрес из src. Архив продолжит писаться.
Снимок и фрагмент, коды ошибок и тело POST /v1/ptz/ —
в Видеопоток (тег типа 6).
Если картинки нет
В конфигурации коннектора нет
rtspUrlвидаrtsp://хост/путь, или коннектор выключен. Кружок связи при этом красный или серый.Тег не типа «Видеопоток» или не привязан к этому коннектору. Платформа отвечает 424: «Тег видеопотока не привязан к активному коннектору-камере.»
Тег не привязан к хранилищу видео (тип
3). Платформа отвечает 424: «Тег видеопотока не привязан к хранилищу видео.»Тег выключен — ответ 409.
Каталог
archivePathхранилища не абсолютный или лежит вне тома подхвата: сеанс может подняться, а архив не переживёт перезапуск контейнера.Надпись «Не удалось открыть поток» или ответ 502 — камера или ffmpeg не отдали кадр. Проверьте адрес, транспорт и доступ контейнера
prs-video-captureдо камеры в сети.Ответ 503 «Сервис отдачи видео не запущен.» — нет процесса, который ответил на
video.delivery.locate. 503 «Сервис подхвата видео не запущен.» значит, что отдача не нашла владельца камеры.«Нет записи камеры на этот момент» — для снимка или фрагмента на диске нет файла, покрывающего метку.