Большой брат в Linux или видеонаблюдение своими руками
После переезда в новый офис, дабы не расслаблять сотрудников, было решено
установить систему видеонаблюдения. Но, как обычно, основным условием было:
дешево и сердито 🙂 После обдумывания и подсчетов было решено остановиться на
следующем:
IP камеры и компьютер в качестве регистратора.
Для начала попробовали небезызвестный ZoneMinder. Но отчего-то с 9 камерами он
грузил систему нещадно. После некоторой борьбы с ним, было решено отказаться от
него. Поискав на просторах Internet была найдена программка под названием
motion (http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome). Почитав немного
про нее, было решено остановиться на ней.
Итак, у нас были IP камеры, компьютер в качестве регистратора, motion в
качестве софта и желание подружить весь этот колхоз 🙂
И что же умеет motion из того, что нам надо? 🙂
1. Работа с камерами, подключенными через карты захвата либо с IP камерами.
2. Выполнять скрипты при наступлении и окончании события.
Ну чтож, приступим. Для начала попробуем с одной камерой 🙂
Устанавливаем motion.
Далее, в папке /etc/motion/ копируем файл с конфигурацией (motion.conf) в файл
для нашей камеры, например motion1.conf. А теперь начинаем его править под
себя:
1. закомментируем строчку
videodevice /dev/video0
2. в строке netcam_url прописываем URL для доступа к нашей камере. Мы
использовали IP камеры Axis, модель 207 (со встроенным микрофоном).
Для нашего случая выглядело так:
netcam_url
http://camera_ip/axis-cgi/mjpg/video.cgi?resolution=640×480
где camera_ip — IP адрес камеры 🙂
3. далее находим строку target_dir — здесь указываем путь к папке, где будем
хранить данные с камеры.
4. а теперь начинается самое интересное. Находим строку on_event_start. Здесь
мы прописываем команду, которая будет выполняться при наступлении события, то
есть при движении. Что нам это дает? А дает нам это то, что мы не будем
постояно вести запись, а только по необходимости — есть движение, идет запись,
закончилось, остановились и мы. Таким образом получается экономия. Для
сравнения — у нас ведется запись с 10 камер — цветная картинка 640х480 со
звуком, записи хранятся 21 день и все это дело занимает на диске в среднем 135
GB, а всесте с фото, которые делает motion около 200 GB.
5. следующая строка: on_event_end — это для команды, отрабатывающей по
окончанию события.
6. И еще один нюанс, motion поднимает небольшой http сервер, а так как для
каждой камеры запускается своя инстанция программы с отдельным конфигом, то
важно, чтобы он слушал на разных портах. Для этого проверяем, чтобы следующие
строки отличались в каждом файле:
webcam_port и control_port
Для примера: для первой камеры ставим значения
webcam_port 8081
control_port 8080
для второй
webcam_port 8083
control_port 8082
и так далее.
Это в принципе основные изменения. Думаю при внимательном прочтении конфига вы
сами найдете нужные вам параметры, так как там все довольно хорошо
задокументировано.
Теперь вернемся к скриптам, которые отрабатывают при начале и окончании
события.
Так как нам надо писать видео с камеры, то для этого я использовал mencoder.
В папке /root/bin/ создаем скрипты recordstart.sh и recordstop.sh, которые
будут запускаться при наступлеии и окончании события соответсвенно.
Давайте заглянем внутрь recordstart.sh:
[root@video bin]# cat /root/bin/recordstart.sh
#!/bin/bash
# We are using two variables
# 1 — camera name
# 2 — directory name (where file will be stored)
# Generate filename
start=«cam$»
dt=`date +%Y%m%d%H%M%S`
FILENAME=»$-$.avi»
echo $FILENAME
if [ «$1» -eq 218 ]
then
`/usr/bin/mencoder -ovc copy -oac pcm -delay 1.5 -mc 10 -o
/video/motion/$2/$FILENAME rtsp://192.168.95.218/mpeg4/media.amp > /dev/null`
elif [ «$1» -eq 219 ]
then
`/usr/bin/mencoder -ovc copy -oac pcm -delay 1.5 -mc 10 -o
/video/motion/$2/$FILENAME rtsp://192.168.95.219/mpeg4/media.amp > /dev/null`
else
`/usr/bin/mencoder -ovc copy -oac pcm -mc 10 -o /video/motion/$2/$FILENAME
rtsp://192.168.95.$1/mpeg4/media.amp > /dev/null`
fi
Скрипт принимает два параметра:
1 — имя камеры, хотя имя не совсем верно, так как это последний октет из IP
адреса камеры. То есть, как в нашем случае (мы используем 95 подсеть — 192.168.95.0/24). Таким образом, при передаче значения 211 — будет ясно, что
хотим обратиться к камере с IP 192.168.95.211
2 — директория, где будет храниться записанный файл. Сделано для того, чтобы
легче было искать. Опять же в нашем случае: имеется папка /video/motion в
которой хранятся записи, но для того, чтобы не мешать все в кучу, она содержит
поддиректории зон: kuhnya, balkon и т.д. Значит при передаче значения balcon — запись будеть производиться в директорию /video/motion/balkon.
Итак, при вызове скрипта в виде /root/bin/recordstart.sh 211 balkon — скрипт
будет писать с камеры с IP 192.168.95.211 в директорию /video/motion/balkon.
Надеюсь тут все более-неменее ясно 🙂
Как видно, вначале мы генерируем уникальное имя для файла, в который будем
писать, которое состоит из следующих частей:
start — номер камеры, вернее последний октет из ее IP с предшествующим ей cam.
dt — текущая дата в формате ГГГГММДДччммсс.
Пример имени файла: cam218-20090924131847.avi — то есть, пишется с камеры с IP
192.168.95.218, дата начала: 2009 год, 09 месяц, 24 число, 13 часов, 18 минут,
47 секунд.
Небольшое пояснение: так уж оказалось, что у нас разные модели камер, которые
предоставляют разные способы доступа к потоку видео данных, из-за этого
пришлось ввести в скрипте проверки.
Теперь пройдемся по mencoder-у:
-ovc copy — означает, что видеоряд копируем, так как с камеры сразу идет в
mpeg4
-oac pcm — какой кодек использовать для звуковой дорожки, если камера позволяет
писать звук.
-mc 10 — Максимальная величина корректировки A-V синхронизации на один кадр (в
секундах)
-delay 1.5 — Задержка в мс, которая должна вноситься в каждый канал
Если у вас проблемы с синхронизацией видео и звукового ряда, тогда надо менять
значения для последних двух параметров — mc и delay.
Для теста можно использовать mplayer.
Ну вот, теперь при наступлении события, мы можем запускать данный скрипт.
Таким образом в конфиге для motion для нашей камеры прописываем в строке
on_event_start что-то похожее:
on_event_start «/root/bin/recordstart.sh 210 koridor1»
И он начнет писать :)))
Но ведь это все еще надо остановить :)))
Для этого используем второй скрипт /root/bin/recordstop.sh.
[root@video bin]# cat /root/bin/recordstop.sh
#!/bin/bash
# We are using one variable
# 1 — camera name
# Get string with mencoder process PID
MPID=`ps ax | grep mencoder | grep cam$ | awk ‘< print $1 >‘`
# Kill process
`kill -15 $MPID`
Скрипт принимает один параметр — все тот же последний октет из IP адреса
камеры.
Соответсвенно в строке on_event_end файла конфигурации прописываем что-то
вроде:
on_event_end «/root/bin/recordstop.sh 210»
Ну вот примерно и все, что касается конфигурации.
Теперь перейдем к рутинным операциям 🙂
У меня используются две, которые отрабатывают по крону.
1. Удаляет устаревшие файлы, которые страрше 21 дня.
2. Объединяет все файлы за день в один.
Рассмотрим скрипт для чистки. У меня он располагается в директории /root/sbin
и,
для того, чтобы враги не догадались, называется clean.sh
[root@video ~]# cat /root/sbin/clean.sh
#!/bin/sh
/bin/find /video/balkon -name «*.*» -mtime +21 -delete
/bin/find /video/motion/balkon -name «*.*» -mtime +21 -delete
В первой директории хранятся фотографии, сделанные motion — это параметр из
файла конфигурации в строке target_dir.
Вторая директория — куда пишется видео, запущенное из скрипта recordstart.sh
А вот второй скрипт посложнее.
[root@video ~]# cat /root/sbin/concatfiles.py
import os, stat, types, commands
workDir = ‘/video/motion/’
dirs = os.listdir(workDir)
oldDate = summaryFile = summaryFileTmp = »
for dir in dirs:
filesList = os.listdir(workDir + dir)
filesList.sort()
for file in filesList:
tmpN = file.split(‘.’)
nm = tmpN[0]
tmpN = nm.split(‘-‘)
if tmpN[0] != ‘S’:
nm = tmpN[1]
year = nm[0:4]
month = nm[4:6]
day = nm[6:8]
date = year + month + day
if oldDate != date:
summaryFile = workDir + dir + ‘/S-‘ + tmpN[0] + ‘-‘ + year +
month + day + ‘.avi’
summaryFileTmp = workDir + dir + ‘/S-‘ + tmpN[0] + ‘-‘ + year +
month + day + ‘-tmp.avi’
#conactenate files
command = ‘/usr/bin/mencoder -oac copy -ovc copy ‘ + workDir +
dir + ‘/’ + tmpN[0] + ‘-‘ + date + ‘*.avi -o ‘ + summaryFileTmp
os.system(command)
#fix key frame
command = ‘ffmpeg -i ‘ + summaryFileTmp + ‘ -ss 0.04 -vcodec
copy -acodec copy -vcodec copy -acodec copy ‘ + summaryFile
os.system(command)
#remove tmp file
command = ‘/bin/rm -f ‘ + summaryFileTmp
os.system(command)
#remove old files
command = ‘/bin/rm -f ‘ + workDir + dir + ‘/’+ tmpN[0] + ‘-‘ +
date + ‘*.avi’
os.system(command)
oldDate = date
Это для объединения коротких роликов за день в один суммарный. (Для тех кто не
в курсе — он на питоне).
Единственное, что нужно менять — это переменная workDir — путь, куда mencoder
пишет свои файлы, все из того же recordstart.sh
Прописываем их в крон на выполнение раз в сутки, желательно ночью, пока карета
не превратится в тыкву 🙂
Запускаем motion следующим образом:
motion -c /path/to/config/file
где
/path/to/config/file — путь к нашему файлу с конфигом 🙂
А далее запускаем их при загрузке системы.
Ну вот наверное и все 🙂
Если будут вопросы — задавайте 🙂
UPD: будьте внимательны с скриптом на питоне, так как сбилась вся
табуляция, что для него очень критично.