Pull to refresh

Трансляция RTSP в WEB. Конвертация в HLS. Коробочное решение

Reading time6 min
Views8.5K
Была задача: собрать все RTSP потоки с видео-регистратора (netsurveillance) и предоставить оперативный доступ к видео-потоку для нескольких человек. Так как ни один браузер не умеет самостоятельно отображать RTSP протокол, то необходимо было найти что угодно, лишь бы могло конвертировать этот поток в пригодный для WEB формат.

Оговорюсь сразу: в процессе эксплуатации данного решения обнаружилась возможность шаринга видео-архива средствами SAMBA. Эта возможность показалась очень удобной лично для меня и я решил реализовать её в данном контексте. Конечно, у кого-то может возникнуть вопрос: «Есть ведь Линия и зачем всё прочее нужно?» — в нашем случае это решение оказалось слишком дорогое. А еще они до сих пор считают в долларах. Итак, что имеется:

  • Корпоративная сеть на базе Mikrotik средствами обычного VPN
  • Несколько ССTV состоящих из самых разных камер и видео-регистратора
  • Linux машина с развернутым SAMBA-AD-DC и WEB сервером в центральном офисе

Стоит ли напоминать, что всё это дело у нас конвертируется из RTSP в HLS? Ставлю на Linux хост Shinobi по этой инструкции. В установке нету ничего сложного, требуется только установить git, некоторые зависимости и запустить скрипт установки. Технически та же Линия, только бесплатно. Возможно на первое время хватит. Интерфейс только кажется удобнее, в остальном тоже самое. После установки и запуска открываем localhost:8080/super логинимся как admin@shinobi.video с паролем admin и создаём основную запись для доступа к мониторингу.

Параметры хранения информации, используемые по-умолчанию, мне не подошли. Кроме того, в процессе эксплуатации я долго искал эти настройки, но срочность вынудила попросту отключить cron.js (sudo pm2 stop cron) и средствами уже Linux’a чистить каталоги видео-архива.

image

Number of Days to keep Videos — количество дней на хранение видеозаписей.
Number of Days to keep Events — количество дней на хранение событий (входы, смены пароля и прочее).
Number of Days to keep Logs — количество дней на хранение системных сообщений: сбои, ошибки, инициализация.

Все эти параметры задаются индивидуально для каждого пользователя, очень удобно. Но кроме этого существует еще и API. Его средствами можно получить все потоки конкретного монитора (монитор — это набор потоков для трансляции) и представить каждый из них по отдельности в виде онлайн трансляции на web-странице. Для начала необходимо добавить в мониторинг информацию о потоках с камер видео-наблюдения.

image

image

image

Mode — режим трансляции: Record — запись, Watch only — только просмотр.
Name — название видео-потока
Storage location — место хранения архива, если в Mode у вас выставлен Record
Full URL Path — ссылка на сам rtsp поток. Для netsurveillance как правило такая ссылка: rtsp://IP:554/user=USER&password=PASSWORD&channel=CHANNELNUMBER&stream=1.sdp?real_stream—rtp-caching=100

Информация о потреблении ресурсов отображаемая в Shinobi сильно разнится с тем, что показывает htop. В web-интерфейсе я постоянно вижу наполовину забитую память, но вот загрузка процессора, кстати, вполне соответствует тому, что видно из консоли.

image

Насколько было известно, так же есть возможность использовать системный GPU для конвертации потока. Но так как у нас он не установлен, то убедиться в этом не было возможности. Всё происходит средствами CPU. У нас задействована конвертация 17 потоков 3 из которых записываются локально. Приведу здесь информацию о нашем процессоре:

lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
Address sizes: 36 bits physical, 48 bits virtual
CPU(s): 8
On-line CPU(s) list: 0-7
Thread(s) per core: 2
Core(s) per socket: 4
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 30
Model name: Intel(R) Xeon(R) CPU X3440 @ 2.53GHz
Stepping: 5
CPU MHz: 1210.183
CPU max MHz: 2534,0000
CPU min MHz: 1200,0000
BogoMIPS: 5066.32
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 8192K
NUMA node0 CPU(s): 0-7


Если нужно только подключить онлайн трансляцию на веб страничке, то необходимо сперва добавить API ключ пользователю, чей монитор нас интересует. Полное руководство по API находится на официальном сайте приложения. Сверху помещаем курсор на email пользователя, кликаем, выбираем пункт API.

image

Основной параметр для нас — это Allowed IPs (разрешенные IP). У меня доступ открыт только для локальной сети, но если вы планируете стримить свои потоки в глобальный Интеренет, то необходимо указать 0.0.0.0/0 и пробросить порт Shinobi наружу.

Технически, Shinobi позволяет очень гибко организовать трансляцию RTSP потока, который поддерживается даже самыми простыми видео-регистраторами. Всё зависит от вашей фантазии и потребностей. Что касается технических возможностей: в случае если вы используете аналоговые камеры, вам 100% понадобится какой-никакой, но видео-регистратор с интерфейсом RJ-45. Всё куда проще, если вы используете IP камеры: них уже есть сетевой интерфейс и видео-поток генерируется аппаратными средствами самой камеры. Но стоят они на порядок дороже чем AHD камеры. Зато цифровые камеры выигрывают по таким параметрам как скорость передачи, и, что более значительно (если речь идет о расследовании происшествий) — качество изображения, не говоря уже удобстве развёртывания и реорганизации.

Информацию о веб-потоках можно получиться простым GET запросом, результат получаем в формате JSON, который легко преобразовать в данные. Пример простого скрипта на PHP:

<?php
 
$url = array(
    'host'          => '192.168.2.104',                      # Хост, на котором работает Shinobi
    'port'          => '8080',                               # Порт web-сервиса Shinobi
    'api'           => 'TyIp1yRhqPLnJmlDpMzAenWYqVW8vt', # API ключ
    'monitor'       => 'f2xnMN0VAv'                          # ID монитора, которы нужно прогрузить
);
 
$link = file_get_contents("http://". $url['host'] .":". $url['port'] ."/". $url['api'] ."/monitor/". $url['monitor'] ."/");
 
foreach(json_decode($link) as $data) {
     
    $source['title'][]  = $data->name;
    $source['url'][]    = "http://". $url['host'] .":". $url['port'] . $data->streams[0];
    $source['width'][]  = $data->width;
    $source['height'][] = $data->height;
    $source['status'][] = $data->status;
 
}
 
print "<html>
 
    <head>
        <title>Просмотр видеопотока</title>
        <link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous'>
        <link href='https://vjs.zencdn.net/7.2.3/video-js.css' rel='stylesheet'>
    </head>
    <body>
     
    <div class='container' style='padding: 30pt 0;'>
 
        <div class='row'>
        <div class='col-sm-12' style='margin: 10pt 0 10pt;'>  
 
            <div class='card'>
                <div class='card-body'>
 
                <form action='' method='POST'>
                  <select class='form-control form-control-lg' name='streamId' onchange='this.form.submit()'>
                    ";
 
for($i = 0; $i <= count($source['title']); $i++){
     
    if( $_POST['streamId'] == $i) {
        print "<option value=". $i ." selected>". $source['title'][$i] ."</option>";
    } else {
        print "<option value=". $i .">". $source['title'][$i] ."</option>";
    }
}
 
if( isset($_POST['streamId']) ) {
    $id = $_POST['streamId'];
} else {
    $id = 0;
}
 
print "         </select></form>
        </div>
      </div>
</div>
 
<div class='col-sm-12'>   
 
    <div class='card'>
        <div class='card-body'>
         <video id='hls-example' style='margin: 0 auto;' class='video-js vjs-default-skin' width='". $source['width'][$id] ."' height='". $source['height'][$id] ."' controls>
            <source type='application/x-mpegURL' src='". $source['url'][$id] ."'>
            </video>
         <!-- <p class='card-text'>This is another card with title and supporting text below. This card has some additional content to make it slightly taller overall.</p> -->
          <p class='card-text' style='text-align: center;'> <small class='text-muted'>Режим трансляции потока: ". $source['status'][$id] ."</small></p>
        </div>
      </div>
    </div>
 
</div>
 
 
    <script src='https://vjs.zencdn.net/ie8/ie8-version/videojs-ie8.min.js'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/videojs-contrib-hls/5.14.1/videojs-contrib-hls.js'></script>
    <script src='https://vjs.zencdn.net/7.2.3/video.js'></script>
 
    <script>
    var player = videojs('hls-example');
    player.play();
    </script>
 
 
 
    </div>
         
    </body>
</html>";
 
?>

Для некоторых камер у меня указан режим Recording. В данном случае, помимо конвертирования потока ведется еще и запись с RTSP на локальный жесткий диск. Если в параметрах Recording указанно Default то запись будет храниться в папке ./Shinobi/videos/[MinitorID]/[CameraID]. У меня же некоторые папки с основного монитора доступны по сети и монтируются средствами GPO для определенной группы как сетевые диски.

image

Зачем это сделано? Простая особенность логистики: бывает, что большая машина загружается товаром и уезжает в другой город, там разгружается уже покупателем, который может заявить что чего-то не хватает. А бывает, что и в своих магазинах уже не могут чего-то досчитаться. Поэтому для склада ведется отдельная запись с некоторых камер уже в mp4 формате. Это может сэкономить кучу времени на разборе полётов.
Tags:
Hubs:
Total votes 1: ↑1 and ↓0+1
Comments1

Articles