После переезда в новый офис, дабы не расслаблять сотрудников, было решено установить систему видеонаблюдения. Но, как обычно, основным условием было: дешево и сердито :) После обдумывания и подсчетов было решено остановиться на следующем: IP-камеры и компьютер в качестве регистратора.
Для начала попробовали небезызвестный ZoneMinder. Но отчего-то с 9 камерами он грузил систему нещадно. После некоторой борьбы с ним, было решено отказаться от него. Поискав на просторах Internet была найдена программка под названием motion (http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome). Почитав немного про нее, было решено остановиться на ней.
Итак, у нас были IP камеры, компьютер в качестве регистратора, motion в качестве софта и желание подружить весь этот колхоз. И что же умеет motion из того, что нам надо? :)
- Работа с камерами, подключенными через карты захвата либо с IP камерами.
- Выполнять скрипты при наступлении и окончании события.
Ну чтож, приступим. Для начала попробуем с одной камерой. Устанавливаем 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=640x480
где camera_ip — IP адрес камеры.
3. далее находим строку target_dir — здесь указываем путь к папке, где будемхранить данные с камеры.
4. а теперь начинается самое интересное. Находим строку on_event_start. Здесь мы прописываем команду, которая будет выполняться при наступлении события, то есть при движении. Что нам это дает? А дает нам это то, что мы не будем постояно вести запись, а только по необходимости — есть движение, идет запись, закончилось, остановились и мы. Таким образом получается экономия. Для сравнения — у нас ведется запись с 10 камер — цветная картинка 640х480 со звуком, записи хранятся 21 день и все это дело занимает на диске в среднем 135GB, а всесте с фото, которые делает 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${1}»
dt=`date +%Y%m%d%H%M%S`
FILENAME="${start}-${dt}.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${1} | awk '{ print $1 }'`
# Kill process
`kill -15 $MPID`
Скрипт принимает один параметр — все тот же последний октет из IP адреса камеры.
Соответсвенно в строке on_event_end файла конфигурации прописываем что-то вроде:
on_event_end "/root/bin/recordstop.sh 210"Ну вот примерно и все, что касается конфигурации.
Теперь перейдем к рутинным операциям. У меня используются две, которые отрабатывают по крону.
- Удаляет устаревшие файлы, которые страрше 21 дня.
- Объединяет все файлы за день в один.
Рассмотрим скрипт для чистки. У меня он располагается в директории /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: будьте внимательны с скриптом на питоне, так как сбилась вся табуляция, что для него очень критично.
UPD2: отступы восстановлены, но не факт что правильно.