Часть 1, историческая
Часть 2, технологическая

Вот и настало время финальной части повествования, в которой я расскажу о практическом результате той эпопеи что растянулась на несколько лет.

Приложения реализованные на данный момент

  1. WebRTSP ReStreamer - основное приложение, которое может выступать как в качестве сервера, так и в качестве агента

  2. WebRTSP Record Streamer - приложение предназначенное для работы в качестве агента для отправки видео потока на сервер либо в постоянном режиме, либо по событию движения с ONVIF камеры

  3. WebRTSP Camera Streamer - приложение являющееся модификацией WebRTSP ReStreamer для стриминга видео потока с камеры подключенной к CSI разъему Raspberry PI

  4. Video Monitor - приложение созданное для запуска на Raspberry Pi (с подключенным через HDMI экраном), для отображения в режиме реального времени видео потока получаемого через RTSP, ONVIF или WebRTSP протокол

Что может понадобиться

  1. VPS/VDS с установленным Linux (способный запускать snap пакеты) для использования в качестве сервера. Практика показывает что достаточно самых минимальных конфигураций. Лично я использую самый дешевый VPS (512 мегабайт оперативной памяти, 1 ядро процессора, 10 гигабайт жесткого диска) предоставляемый Ru VDS стоимостью 139 рублей в месяц (или 111 рублей при оплате за год, не сочтите за рекламу)

  2. Raspberry Pi не ниже 3-ей версии (или любое другое Arm64 или Amd64 совместимое устройство способное запускать snap пакеты) для использования в качестве агента

  3. IP камера с поддержкой RTSP или ONVIF протоколов и h264 кодека. Поддержка ONVIF понадобится в случае если необходимо осуществлять запись видео при обнаружении движения в кадре

Немного теории о snap пакетах

Почему я решил использовать snap пакеты для упаковки приложений? Причин несколько:

  1. Однажды и пробовал создавать PPA пакеты для распространения приложений - воспоминания остались не самые радужные. Это боль и большое количество затрачиваемого времени на каждую новую версию. Допускаю что я просто не научился их готовить, но использование snap пакетов показало себя с гораздо лучшей стороны

  2. Snap пакет позволяет реализовывать фоновое выполнение процессов средствами самих snap пакетов без лишних проблем

  3. Все процессы исполняемые в рамках snap пакета по умолчанию изолированы от остальной системы. Приложения имеют доступ только к очень ограниченному списку директорий на диске (часть только для записи, часть только для чтения) и явно определяемому списку аппаратных ресурсов

  4. Программные компоненты включаемые в snap явно определяются разработчиком. При необходимости можно собирать компоненты из исходников, тем самым добавляя недостающие библиотеки либо заменяя их версию

  5. Есть централизованный магазин приложений и достаточно простой набор команд для установки snap пакетов и управления ими

  6. Есть механизм автоматического обновления snap пакетов при появлении новой версии в Snapcraft Store

  7. Возможна публикация нескольких вариантов snap пакета, и разделение на development и release версии snap пакета не является проблемой (на самом деле вариантов гораздо больше, но опущу эти детали для простоты изложения)

  8. Доступна автоматическая сборка и публикация новой версии snap пакета при добавлении коммита в GitHub репозиторий

Несколько полезных команд для работы с snap пакетами (в контексте Debian Linux и его вариантов)

  • для начала необходимо установить snapd (но возможно в вашем конкретном случае он уже установлен): sudo apt install snapd. В случае Raspbian/Raspberry Pi OS возможно понадобятся дополнительные действия описанные в https://forum.snapcraft.io/t/installing-snap-on-debian

  • установка snap пакета: sudo snap install имя-пакета

  • установка development варианта snap пакета: sudo snap install имя-пакета --edge

  • замена release варианта snap пакета на development вариант: sudo snap refresh имя-пакета --edge

  • замена development варианта snap пакета на release вариант: sudo snap refresh имя-пакета --stable

  • перезапуск фонового процесса (процессов) запущенного в рамках snap пакета: sudo snap restart имя-пакета

  • временная остановка фонового процесса (процессов) запущенного в рамках snap пакета: sudo snap stop имя-пакета

  • запуск ранее остановленного фонового процесса (процессов) запущенного в рамках snap пакета: sudo snap start имя-пакета

  • отключение фонового процесса (процессов) запущенного в рамках snap пакета: sudo snap disable имя-пакета

  • непрерывное отображение логов: sudo snap logs имя-пакета -f

  • отображение x последних строк логов: sudo snap logs имя-пакета -n=x

  • отображение всех доступных строк логов: sudo snap logs имя-пакета -n=all

Итак, приступим к практическому применению

Просмотр видео с IP камеры по локальной сети

  ┌─────────┐       HTTP       ┌────────────┐                               
  │         ├─────────────────►│            │                  ┌───────────┐
  │         │WebRTSP(WebSocket)│            │       RTSP       │           │
  │ Браузер ├─────────────────►│ ReStreamer ├─────────────────►│ IP камера │
  │         │      WebRTC      │            │                  │           │
  │         │◄────────────────►│            │                  └───────────┘
  └─────────┘                  └────────────┘                                
  1. Устанавливаем ReStreamer: sudo snap install rtsp-to-webrtsp

  2. Открываем конфигурационный файл для редактирования: sudoedit /var/snap/rtsp-to-webrtsp/common/restreamer.conf и заменяем его содержимое следующим:

    streamers: (
      {
        name: "Štrbské pleso"
        url: "rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream"
        force-h264-profile-level-id: "42c015"
      }
    )

    Если есть уже настроенная камера - меняем значения name на произвольное и urlна RTSP URL камеры. Что касается force-h264-profile-level-id - это активация того самого трюка, о котором я упоминал в одной из предыдущих частей, и позволяющего обмануть браузер и заставить его воспроизводить не только baseline profile h264 видео потоки

  3. Перезапускаем приложение: sudo snap restart rtsp-to-webrtsp

  4. Открываем в браузере http://127.0.0.1:5080 (если приложение установлено на другом хосте, например на Raspberry PI, очевидно что нужно использовать IP адрес этого хоста)

  5. Если через несколько секунд видео не начинает отображаться на открытой странице - пишем в комментариях, либо создаем новую тему в GitHub Discussions, либо создаем issue на GitHub

Просмотр видео с IP камеры через интернет

                                    Роутер(NAT/Firewall)       ┌───────────┐    
                                             ┌─┐               │           │    
                                             │ │               │ IP камера │    
                                             │ │               │           │    
                                             │ │               └───────────┘    
                                             │ │                     ▲          
                                             │ │                     │ RTSP     
┌─────────┐       HTTPS      ┌────────────┐  │ │  WebRTSP  ┌─────────┴─────────┐
│         ├─────────────────►│ ReStreamer │  │ │(WebSocket)│                   │
│         │WebRTSP(WebSocket)│     на     │◄─┼─┼───────────┤ ReStreamer(агент) │
│ Браузер ├─────────────────►│  VPS/VDS   │  │ │           │        на         │
│         │                  └────────────┘  │ │           │    Raspberry Pi   │
│         │◄─────────────────────────────────┼─┼──────────►│                   │
└─────────┘                       WebRTC     │ │           └───────────────────┘
                                             │ │                                
                                             └─┘                                
  1. Устанавливаем ReStreamer на VPS/VDS: sudo snap install rtsp-to-webrtsp

  2. Открываем конфигурационный файл для редактирования: sudoedit /var/snap/rtsp-to-webrtsp/common/restreamer.conf и заменяем его содержимое следующим:

    loopback-only: true
    
    streamers: (
      {
        name: "IP Cam"
        type: "proxy"
        agent-token: "любая очень длинная строка из случайных символов"
      }
    )
    
    users: (
      {
        login: "user",
        pass: "pass"
      }
    )
  3. Перезапускаем приложение: sudo snap restart rtsp-to-webrtsp

  4. Запускаем скрипт настройки HTTPS и WSS (используется сервис Let's Encrypt для генерации сертификата):

    ./enableTLS.sh root@your.server.dns.name:22 you@mail.com
  5. Устанавливаем ReStreamer на Raspberry Pi (или другом устройстве) находящемся в одной локальной сети с IP камерой: sudo snap install rtsp-to-webrtsp

  6. Открываем конфигурационный файл для редактирования: sudoedit /var/snap/rtsp-to-webrtsp/common/restreamer.conf и заменяем его содержимое следующим:

    signalling-server: {
      host: "your.server.dns.name"
      port: 5555
      tls: true
      uri: "IP Cam"
      token: "любая очень длинная строка из случайных символов"
    }
    
    streamers: (
      {
        name: "Štrbské pleso"
        url: "rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream"
        force-h264-profile-level-id: "42c015"
      }
    )

    где значение uri должно совпадать со значением name на стороне сервера, а значение token соответственно со значением agent-token

  7. Перезапускаем приложение на Raspberry Pi: sudo snap restart rtsp-to-webrtsp

  8. Открываем в браузере https://your.server.dns.name:5443/!, вводим имя пользователя/пароль, после этого в левом дереве распахиваем элемент IP Cam (или с тем именем что было указано в конфигурационном файле на стороне сервера) и кликаем на элемент Štrbské pleso (или имени указанном в конфигурационном файле на стороне Raspberry PI

  9. Если через несколько секунд видео не начинает отображаться - пишем в комментариях, либо создаем новую тему в GitHub Discussions, либо создаем issue на GitHub

Запись видео с IP камеры на сервер (Cloud NVR)

       Роутер(NAT/Firewall)                                             
                ┌─┐                                                     
                │ │  WebRTSP  ┌───────────────────┐                     
┌────────────┐  │ │(WebSocket)│                   │        ┌───────────┐
│ ReStreamer │◄─┼─┼───────────┤  Record Streamer  │  RTSP  │           │
│     на     │  │ │  WebRTC   │      агент на     ├───────►│ IP камера │
│  VPS/VDS   │◄─┼─┼──────────►│    Raspberry Pi   │        │           │
└────────────┘  │ │           │                   │        └───────────┘
                │ │           └───────────────────┘                     
                └─┘                                                     
  1. Настраиваем серверную часть как указано в пунктах с 1. по 4. в предыдущей главе, но используем следующее содержимое для restreamer.conf файла:

    loopback-only: true
    
    streamers: (
      {
        name: "NVR"
        type: "record"
        restream: false
        record-token: "любая очень длинная строка из случайных символов"
        recordings-dir: "recordings"
        recordings-dir-max-size: 1024 // Mb
        recording-chunk-size: 100 // Mb
      }
    )
    
    users: (
      {
        login: "user",
        pass: "pass"
      }
    )
  2. Устанавливаем Record Streamer на Raspberry Pi (или другом устройстве) находящемся в одной локальной сети с IP камерой: sudo snap install webrtsp-record-streamer

  3. Открываем конфигурационный файл для редактирования: sudoedit /var/snap/webrtsp-record-streamer/common/record-streamer.conf и заменяем его содержимое следующим

    target: {
      host: "your.server.dns.name"
      port: 5555
      tls: true
      uri: "NVR"
      token: "любая очень длинная строка из случайных символов"
    }
    
    source: {
      url: "rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream"
    }

    где значение uri должно совпадать со значением name на стороне сервера, а значение token соответственно со значением record-token

  4. Перезапускаем приложение на Raspberry Pi: sudo snap restart webrtsp-record-streamer

  5. С этого момента агент начнет отправку видео 24*7 на сервер, где он будет разбиваться на части(файлы) максимального размера заданного в recording-chunk-size, при этом максимальный размер записанных файлов не будет превышать recordings-dir-max-size. Для поддержания максимального размера будут удаляться самые старые файлы (что логично). Сохраненные файлы можно найти в директории /var/snap/rtsp-to-webrtsp/common/recordings/NVR/

Запись видео с ONVIF IP камеры на сервер при обнаружении движения в кадре (Cloud NVR)

       Роутер(NAT/Firewall)                                            
                ┌─┐                                                    
                │ │  WebRTSP  ┌───────────────────┐       ┌───────────┐
┌────────────┐  │ │(WebSocket)│                   │ ONVIF │           │
│ ReStreamer │◄─┼─┼───────────┤  Record Streamer  ├──────►│           │
│     на     │  │ │  WebRTC   │      агент на     │ RTSP  │ IP камера │
│  VPS/VDS   │◄─┼─┼──────────►│    Raspberry Pi   ├──────►│           │
└────────────┘  │ │           │                   │       │           │
                │ │           └───────────────────┘       └───────────┘
                └─┘                                                    

Настраиваем серверную часть и агента идентично предыдущей главе, за исключением конфигурационного файла на стороне агента:

target: {
  host: "your.server.dns.name"
  port: 5555
  tls: true
  uri: "NVR"
  token: "любая очень длинная строка из случайных символов"
}

source: {
  onvif: "http://your.ip.cam.address:port"
  track-motion-event: true
  motion-record-time: 15 // seconds
}

с приведенной конфигурацией, Record Streamer будет отправлять видео только при обнаружении камерой движения, и осуществлять отправку в течении как минимум 15 секунд (или более, при получении дополнительных оповещений о движении, пока отправка активна)

Воспроизведение записей

                ┌─────────┐      HTTPS       ┌────────────┐
                │         ├─────────────────►│            │
                │         │WebRTSP(WebSocket)│            │
                │ Браузер ├─────────────────►│ ReStreamer │
                │         │      WebRTC      │            │
                │         │◄────────────────►│            │
                └─────────┘                  └────────────┘

Для добавления функционала воспроизведения ранее записанных файлов необходимо использовать следующий конфигурационный файл на стороне сервера:

loopback-only: true

streamers: (
  {
    name: "NVR"
    type: "record"
    restream: false
    record-token: "любая очень длинная строка из случайных символов"
    recordings-dir: "recordings"
    recordings-dir-max-size: 1024 // Mb
    recording-chunk-size: 100 // Mb
  },
  {
    name: "Recordings",
    type: "player",
    dir: "recordings/NVR",
    force-h264-profile-level-id: "42c015",
  }
)

users: (
  {
    login: "user",
    pass: "pass"
  }
)

Аппаратный видео монитор для дверной камеры с отображением видео при обнаружении движения

              ┌───────────────────┐                     
              │   Video Monitor   │        ┌───────────┐
              │        на         │ ONVIF  │           │
              │   Raspberry Pi    ├───────►│ IP камера │
              │  с подключенным   │        │           │
              │  HDMI монитором   │        └───────────┘
              └───────────────────┘                     
  1. Важно! На Raspberry Pi необходимо использовать lite вариант Raspberry Pi OS (т.е. только с консолью, без графической оболочки)

  2. Устанавливаем Video Monitor на Raspberry Pi находящемся в одной локальной сети с IP камерой: sudo snap install video-monitor --edge

  3. Открываем конфигурационный файл для редактирования: sudoedit /var/snap/video-monitor/common/monitor.conf и заменяем его содержимое следующим:

    source: {
      onvif: "http://your.ip.cam.address:port"
      track-motion-event: true
      motion-preview-time: 15 // seconds
    }
  4. Перезапускаем Raspberry Pi: sudo restart

  5. С приведенной выше конфигурацией, при обнаружении движения на экране будет отображено видео в течении 15 секунд (или более, при получении дополнительных оповещений о движении, пока отображение активно). При этом видео будет отображаться поверх приглашения входа, т.е. для работы приложения нет необходимости вводить имя пользователя и пароль после запуска Raspberry Pi.

Замечание: на данный момент мне не удалось найти способа перевода монитора подключенного к HDMI выходу Raspbery Pi в спящий режим. В связи с чем пока видео не отображается будет на экране будет отображаться приглашение входа системы. Для того чтоб это выглядело более менее прилично, я настраивал запуск заставки для текстового режима сразу после запуска Raspberry Pi. В качестве заставки я использовал приложение cmatrix.

Для получения аналогичного эффекта нужно выполнить следующие действия:

  1. Установить cmatrix: sudo apt install cmatrix

  2. Создать файл замены конфигурации первой консоли (она отображается сразу после запуска Raspberry Pi):

    sudo mkdir -p /etc/systemd/system/getty@tty1.service.d
    sudoedit /etc/systemd/system/getty@tty1.service.d/override.conf

    и добавить в него следующее содержимое:

    [Service]
    ExecStart=
    ExecStartPre=/bin/sleep 10
    ExecStart=-/usr/bin/cmatrix -u 8
  3. Перезапустить Raspberry Pi: sudo restart

    Теперь через 10 секунд после запуска Raspberry Pi на экране будет появляться заставка из фильма "Матрица"

Заключение

Собственно это все что я хотел рассказать о той системе видеонаблюдения что мне удалось создать за последние годы. Я прекрасно понимаю что текущая реализация далека от идеальной, и необходимо вложить еще достаточно большое количество усилий и времени для доведения ее до приличного состояния. Но тем не менее ее вполне достаточно для использования. Надеюсь система пригодится не только мне.

Если у вас возникли вопросы, буду рад их услышать в комментариях к статье или Issues/Discussions проекта на GitHub.

Спасибо за внимание.

Комментарии (10)


  1. tklim
    06.01.2026 10:04

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

    Что насчёт нескольких камер? Можно добавить в один конфиг или несколько экземпляров служб поднимать?


    1. RSATom Автор
      06.01.2026 10:04

      Да, ReStreamer поддерживает возможность создания нескольких элементов в конфиге, но Record Streamer на данный момент поддерживает только подключение к одной камере. Технически добавить поддержку нескольких подключений в Record Streamer не сильно большая проблема - у меня просто руки не дошли. Если лично вам есть необходимость - можете создать issue на GitHub - отработаю пока есть свободное время.


      1. RSATom Автор
        06.01.2026 10:04

        Как оно выглядит можно посмотреть в демке, но боюсь свалится оно под натиском хабр эффекта...


        1. Viktor-T
          06.01.2026 10:04

          Очень здорово! Спасибо большое. Такое прямо сейчас возможно получить используя WebRTSP ReStreamer для просмотра нескольких камер в локальной сети?


          1. RSATom Автор
            06.01.2026 10:04

            Да, без проблем, достаточно иметь секцию streamers в конфигурационном файле следующего вида:

            streamers: (
              {
                name: "Price Center Plaza"
                url: "rtsp://132.239.12.145:554/axis-media/media.amp"
                description: "rtsp://132.239.12.145:554/axis-media/media.amp"
                force-h264-profile-level-id: "42c015"
              },
              {
                name: "Štrbské pleso"
                url: "rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream"
                description: "rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream"
                force-h264-profile-level-id: "42c015"
              },
              {
                name: "Western Cape"
                url: "rtsp://196.21.92.82/axis-media/media.amp"
                description: "rtsp://196.21.92.82/axis-media/media.amp"
                force-h264-profile-level-id: "42c015"
              },
              {
                name: "Nordland"
                url: "rtsp://77.110.228.219/axis-media/media.amp"
                description: "rtsp://77.110.228.219/axis-media/media.amp"
                force-h264-profile-level-id: "42c015"
              }
            )

            раздел streamers может содержать произвольное количество элементов произвольного типа. Причем работать это будет как для режима сервера, так и для режима агента.

            Один момент который может оказаться полезным, ReStreamer может одновременно работать и в режиме агента и в режиме сервера. Для этого необходимо добавить опцию disable-own-server: true в секцию signalling-server.

            Еще один момент, если вы не планируете использовать HTTPS/WSS доступ к серверу, то необходимо убрать опцию loopback-only: true(либо заменить значение на false). В этом случае сервер будет доступен на порту 5080(т.е. URL будет иметь вид http://192.168.0.x:5080/, но при этом заданные в конфигурационном файле пользователи и пароли работать не будут.

            С полным списком опций конфигурационного файла можно ознакомиться здесь.


            1. RSATom Автор
              06.01.2026 10:04

              Опечатка, для одновременной работы в режиме агента и сервера нужно использовать disable-own-server: false


            1. Viktor-T
              06.01.2026 10:04

              Спасибо, всё получилось.

              Один момент омрачает - долгая загрузка видео на 1-2 камерах и очень долгая загрузка видео на других 1-2 камерах. У меня 4 камеры в одной сети с компом, добавил все и настроил вид, чтобы сразу 4 были на одном экране. При запуске более менее быстро появляется видео на одной камере, через несколько секунд (до 15 примерно) на второй, а две оставшиеся нужно ощутимо подождать (до минуты). Каждый раз очерёдность загрузки видео разная - любая из камер может загрузиться быстрее и любая медленнее в разные запуски программы.

              Когда одна камера на странице, а слева список камер, то видео загружается быстрее, до 10 секунд.

              Не знаю, связано это или нет, в логах появляется сообщение о невозможности найти TURN-сервер и об использовании гугловского.