Участник:Clint/Черновики4
Материал из Xgu.ru
Содержание |
[править] Door cam on RPi
основная идея: web camera подключена к usb порту Rasberry Pi, данные из устройства /dev/video0 с помощью gstreamer копируются с некоторыми изменениями налету в /dev/video1, /dev/video2 и /dev/video3.
Затем на /dev/video1 запускается motion, который детектит движение и оправляет на почту соответствующие ролики.
На /dev/video2 запускается gstreamer, который пишет ролики по 60 sec.
На /dev/video3 запускается mjpegstreamer, который используется для простомтра с мобильного телефона или удаленного компьютера.
[править] Оборудование
- 046d:081b Logitech, Inc. Webcam C310
- Raspberry Pi 3 Model B+
- Корпус к Raspberry Pi
- USB Modem Huawei E3372h-153 переделанный под STICK (чтобы была возможность принимать или отправлять смс при помощи gammu) (https://4pda.ru/forum/index.php?showtopic=582284)
- Реле напряжения
- Источник бесперебойного питания, подключается к 220В через Реле напряжения, батарея на 7Ah - обеспечивает работу всего стенда ~7 часов.
- Понижающий преобразователь 5А, ток откручен на максимум, напряжение - 5,64 Вольт.
- Кабель питания от понижающего преобразователя к Raspberry Pi - не следует экономить на проводах, их сопротивление важно, может потребоваться много времени чтобы понять, что причина сбоев - некачественное питание.
Более подробно о камере c310:
список поддерживаемых форматов:
pi@rpi3:~ $ v4l2-ctl --list-formats -d /dev/video0
ioctl: VIDIOC_ENUM_FMT
Index : 0
Type : Video Capture
Pixel Format: 'YUYV'
Name : YUYV 4:2:2
Index : 1
Type : Video Capture
Pixel Format: 'MJPG' (compressed)
Name : Motion-JPEG
Есть в наличии Logitech, Inc. HD Pro Webcam C920 и есть желание переделать пайплайны для gstreamer-а, так как:
pi@rpi3:~ $ v4l2-ctl --list-formats -d /dev/video0
ioctl: VIDIOC_ENUM_FMT
Index : 0
Type : Video Capture
Pixel Format: 'YUYV'
Name : YUV 4:2:2 (YUYV)
Index : 1
Type : Video Capture
Pixel Format: 'H264' (compressed)
Name : H.264
Index : 2
Type : Video Capture
Pixel Format: 'MJPG' (compressed)
Name : MJPEG
эта камера может на аппаратном уровне сразу отдавать в H.264, тодга процессорные ресурсы RPi будут использоваться меньше. Ниже будет описан пайплайн для gstreamer и указано что может быть перенесено на камеру.
[править] Копирование из /dev/video0 в /dev/video{1,2,3}
Для выполнения этой задачи необходимо установить gstreamer-1.0 и собрать v4l2loopback модуль ядра (если его нет).
Затем, для упрощения конторля за запущенными процессами gstreamer сделаны sym links:
pi@rpi3:~ $ ls -l /usr/bin/gst-launch-1.0-* lrwxrwxrwx 1 root root 29 Jun 17 2017 /usr/bin/gst-launch-1.0-LOOP -> /usr/local/bin/gst-launch-1.0 lrwxrwxrwx 1 root root 29 Jun 17 2017 /usr/bin/gst-launch-1.0-MY -> /usr/local/bin/gst-launch-1.0
Скрипт /home/pi/bin/v4l2Mysink.sh для запуска gstreamer для копирования данных из /dev/video0 в /dev/video{1,2,3}:
#!/bin/bash
#set -x
export LD_LIBRARY_PATH=/usr/local/lib
export GST_PLUGIN_PATH=/usr/local/lib/gstreamer-1.0/
export GST_OMX_CONFIG_DIR=/usr/local/etc/xdg/
BIN='/usr/bin/gst-launch-1.0-MY'
DEB='/tmp/v4l2Mysink_deb.log'
ps -ef | grep -q uvcdynctrl && (kill -9 $(ps -ef | grep uvcdynctrl | grep -v grep | awk '{print $2}'))
lsmod | grep -q ^v4l2loopback && (modprobe -r uvcvideo; modprobe -r videodev; modprobe -r meida; modprobe -r v4l2loopback; modprobe videodev; modprobe uvcvideo; modprobe media)
sourceDEV="$(ls -tr1 /dev/video* | tail -1)"
if [ "x${sourceDEV}" == "x" ]
then
echo "sourceDEV empty, exit 0 now" >> ${DEB}
exit 0
fi
modprobe -v v4l2loopback devices=3
v4l2-ctl --set-parm=10/1 -d /dev/video1
v4l2-ctl --set-parm=10/1 -d /dev/video2
v4l2-ctl --set-parm=10/1 -d /dev/video3
MY_CLOCKOVERLAY='clockoverlay time-format="%d/%m/%Y %H:%M:%S" halignment=left valignment=top font-desc="Terminus bold 30px" wrap-mode=-1'
MY_VIDEO_CROP='videocrop left=268 right=372 top=42 bottom=30'
nice -n-5 ${BIN} --gst-debug-level=3 --gst-debug-no-color -e v4l2src device="${sourceDEV}" ! \
'video/x-raw,format=(string)YUY2,width=(int)1024,height=(int)576,pixel-aspect-ratio=(fraction)1/1,interlace-mode=(string)progressive,framerate=(fraction)10/1' ! \
queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! \
${MY_VIDEO_CROP} ! \
tee name=tp ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! v4l2sink device=/dev/video1 sync=false \
tp. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! v4l2sink device=/dev/video2 sync=false \
tp. ! ${MY_CLOCKOVERLAY} ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! v4l2sink device=/dev/video3 sync=false \
2>>${DEB}&
в результате выполнения будет запущен процесс:
pi@rpi3:~ $ ps -ef | grep gst-launch-1.0-MY | grep -v grep root 5870 1 5 Mar24 ? 11:16:35 /usr/bin/gst-launch-1.0-MY --gst-debug-level=3 --gst-debug-no-color -e v4l2src device=/dev/video0 ! video/x-raw,format=(string)YUY2,width=(int)1024,height=(int)576,pixel-aspect-ratio=(fraction)1/1,interlace-mode=(string)progressive,framerate=(fraction)10/1 ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! videocrop left=268 right=372 top=42 bottom=30 ! tee name=tp ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! v4l2sink device=/dev/video1 sync=false tp. ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! v4l2sink device=/dev/video2 sync=false tp. ! clockoverlay time-format="%d/%m/%Y %H:%M:%S" halignment=left valignment=top font-desc="Terminus bold 30px" wrap-mode=-1 ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! v4l2sink device=/dev/video3 sync=false
и лог файл для этого процесса будет /tmp/v4l2Mysink_deb.log
Замечено, что ошибок в логфайле гораздо меньше, если использовать следующие параметры к модулю uvcvideo:
pi@rpi3:~ $ cat /etc/modprobe.d/uvcvideo.conf options uvcvideo nodrop=1 timeout=100
gstreamer в данном случае собирался из исходных текстов:
pi@rpi3:~ $ ls -lhtra gstreamer_src_10.1.4/ total 25M -rw-r--r-- 1 pi pi 455K Aug 31 2016 orc-0.4.26.tar.xz -rw-r--r-- 1 pi pi 8.2M Feb 23 2017 gst-libav-1.10.4.tar.xz -rw-r--r-- 1 pi pi 492K Feb 23 2017 gst-omx-1.10.4.tar.xz -rw-r--r-- 1 pi pi 4.6M Feb 23 2017 gst-plugins-bad-1.10.4.tar.xz -rw-r--r-- 1 pi pi 3.0M Feb 23 2017 gst-plugins-base-1.10.4.tar.xz -rw-r--r-- 1 pi pi 3.3M Feb 23 2017 gst-plugins-good-1.10.4.tar.xz -rw-r--r-- 1 pi pi 888K Feb 23 2017 gst-plugins-ugly-1.10.4.tar.xz -rw-r--r-- 1 pi pi 3.7M Feb 23 2017 gstreamer-1.10.4.tar.xz drwxr-xr-x 10 pi pi 4.0K Mar 3 2017 orc-0.4.26 drwxr-xr-x 15 pi pi 4.0K Mar 3 2017 gst-plugins-base-1.10.4 drwxr-xr-x 14 pi pi 4.0K Mar 3 2017 gst-plugins-good-1.10.4 drwxr-xr-x 13 pi pi 4.0K Mar 3 2017 gst-plugins-ugly-1.10.4 drwxr-xr-x 10 pi pi 4.0K Mar 3 2017 gst-libav-1.10.4 drwxr-xr-x 15 pi pi 4.0K Mar 3 2017 gst-plugins-bad-1.10.4 drwxr-xr-x 10 pi pi 4.0K May 6 2017 . drwxr-xr-x 16 pi pi 4.0K Jun 16 2017 gstreamer-1.10.4 drwxr-xr-x 9 pi pi 4.0K Jun 16 2017 gst-omx-1.10.4 drwxr-xr-x 32 pi pi 4.0K Apr 2 11:43 ..
но лучше сначала попробовать apt-get install вместо сборки. Архивы получены по ссылке https://gstreamer.freedesktop.org/src/
Запуск gstreamer-а для копирования данных камеры выполняется на этапе старта ОС:
pi@rpi3:~ $ ls -lhtr /etc/rcS.d/S17v4l2MYsink
lrwxrwxrwx 1 root root 20 Oct 4 18:11 /etc/rcS.d/S17v4l2MYsink -> ../init.d/v4l2MYsink
pi@rpi3:~ $ cat /etc/init.d/v4l2MYsink
#! /bin/sh
### BEGIN INIT INFO
# Provides: v4l2Mysink
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: v4l2Mysink Enabler
### END INIT INFO
set -e
export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
case "$1" in
start)
if [ -L /dev/doorcam ]
then
/bin/bash /home/pi/bin/v4l2Mysink.sh &
fi
# log_end_msg 0
;;
stop)
;;
reload|force-reload)
;;
restart)
;;
try-restart)
;;
status)
;;
*)
log_action_msg "Usage: /etc/init.d/v4l2MYsink start"
esac
exit 0
Ссылка /dev/doorcam создается демоном udev:
pi@rpi3:~ $ cat /etc/udev/rules.d/11-mycam.rules
SUBSYSTEM=="video4linux", ATTR{name}=="UVC Camera (046d:081b)", SYMLINK+="doorcam", RUN+="/etc/udev/rules.d/11-mycam.rules.sh"
pi@rpi3:~ $ cat /etc/udev/rules.d/11-mycam.rules.sh
#!/bin/bash
/bin/chgrp video /dev/video*
/bin/chmod g+rw /dev/video*
/bin/chgrp video /dev/media*
/bin/chmod g+rw /dev/media*
[править] Запись 60 sec видео файлов
Запуск gstreamer для создания видео файлов по 60 сек выполняется скриптом /home/pi/bin/my_writer.sh:
#!/bin/bash
export GST_PLUGIN_PATH=/usr/local/lib/gstreamer-1.0/
export GST_OMX_CONFIG_DIR=/usr/local/etc/xdg/
export LD_LIBRARY_PATH=/usr/local/lib/
TARGETDIR="/camera/video/lastMins"
STOPfILE="/var/lib/motion/stop_sms"
LOOPFILE='/tmp/writer_loop_file'
if [ ! -f /tmp/writer_loop_file ]
then
touch /tmp/writer_loop_file
LASTLOOP=0
else
LASTLOOP=$(cat ${LOOPFILE})
fi
CURRLOOP=$((LASTLOOP+1))
echo ${CURRLOOP} > ${LOOPFILE}
seq=${CURRLOOP}
#MANDATORY, or see my_writer_controller.sh
fileMask="writer"
dateMask="$(date +%Y%m%d%H%M%S)"
fileName="${fileMask}_${dateMask}.avi"
fileNameNew="${fileMask}_${dateMask}_[0-9][0-9][0-9][0-9].avi"
fileName_0="${fileMask}_${dateMask}"
DEBUG_DIR='/dev/shm/writer'
if [ ! -d ${DEBUG_DIR} ]
then
mkdir ${DEBUG_DIR}
fi
pid_file="${DEBUG_DIR}/myAVIpid-${seq}"
GSTDEB="${DEBUG_DIR}/my_gst_handler-${seq}"
SKIP_LINES='No\ corresponding\ frame\ found\|Creating\ random\ stream-id\|Consider\ implementing\ group-id\ handling'
DEB="/tmp/writer_deb.log"
echo "" >> ${DEB}
echo "======================= START ${seq} $(date +%Y/%m/%d_%H:%M:%S)=====================" >> ${DEB}
echo "" >> ${DEB}
MYQUEUE='queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2'
MY_VIDEO_SETTINGS='video/x-raw,format=I420,width=384,height=504,framerate=10/1'
MY_AUDIO_SETTINGS='audio/x-raw,rate=48000,channels=1'
MY_AUDIO_ENC='voaacenc bitrate=64000'
MY_MUXSplit="splitmuxsink muxer=avimux name=my1avimux max-size-time=60000000000 location=${TARGETDIR}/${fileMask}_${dateMask}_%04d.avi"
MY_MUX='avimux'
MY_PULSE_SETTINGS='do-timestamp=true device="alsa_input.usb-046d_081b_0FE0F1A0-02-U0x46d0x81b.analog-mono" provide-clock=true buffer-time=30000 latency-time=1000 '
MY_CLOCKOVERLAY='clockoverlay time-format="%H:%M:%S" halignment=left valignment=top xpad=5 ypad=65 font-desc="Terminus bold 30px" ! clockoverlay time-format="%d/%m/%Y" halignment=right valignment=top xpad=0 ypad=65 font-desc="Terminus bold 30px"'
MY_VIDEO_CROP='videocrop top=95 bottom=50'
CARDNUM=$(/usr/bin/arecord -l | /bin/grep '0x46d:0x81b' | /bin/sed 's/card\ \([0-9]\):.*$/\1/')
/usr/bin/amixer -q -c ${CARDNUM} sset Mic 26dB cap unmute
MY_OMX_ENC_SETTINGS='omxh264enc target-bitrate=400000 control-rate=variable '
BIN='/usr/bin/gst-launch-1.0-LOOP'
function gstCMDsplit {
${BIN} --gst-debug-level=3 --gst-debug-no-color -e v4l2src device=/dev/video2 do-timestamp=true ! ${MY_VIDEO_SETTINGS} ! ${MY_CLOCKOVERLAY} ! ${MYQUEUE} ! ${MY_OMX_ENC_SETTINGS} ! video/x-h264,profile=high ! h264parse config-interval=2 ! ${MYQUEUE} ! ${MY_MUXSplit} pulsesrc ${MY_PULSE_SETTINGS} ! audioconvert qos=true ! ${MY_AUDIO_SETTINGS} ! ${MY_AUDIO_ENC} ! ${MYQUEUE} ! my1avimux.audio_%u 2>${GSTDEB} &
}
gstCMDsplit
echo "pid:${!}" > ${pid_file}
echo "file:${fileName_0}" >> ${pid_file}
echo "file $(echo $pid_file): " >> ${DEB}
cat "${pid_file}" >> ${DEB}
echo "" >> ${DEB}
echo "ls -l ${DEBUG_DIR}" >> ${DEB}
ls -l ${DEBUG_DIR} >> ${DEB}
echo "----------------" >> ${DEB}
echo "cat ${GSTDEB}" >> ${DEB}
cat ${GSTDEB} >> ${DEB}
echo "----------------" >> ${DEB}
echo "sleep 1 started" >> ${DEB}
sleep 1
echo "sleep 1 ended" >> ${DEB}
echo "ls -l ${DEBUG_DIR}" >> ${DEB}
ls -l ${DEBUG_DIR} >> ${DEB}
echo "----------------" >> ${DEB}
echo "cat ${GSTDEB}" >> ${DEB}
cat ${GSTDEB} >> ${DEB}
echo "----------------" >> ${DEB}
echo "ls -lh ${TARGETDIR}/${fileNameNew}" >> ${DEB}
ls -lh ${TARGETDIR}/${fileNameNew} >> ${DEB} 2>&1
echo "----------------" >> ${DEB}
gst_restart_needed="NO"
pulse_restart_needed="NO"
errorString='Caught\ SIGSEGV|busy|no\ buffers\ have\ been\ allocated\ yet|codec_data\ is\ not\ expected|Failed\ to\ allocate\ a\ buffer|could\ not\ link\ my1avimux\ to\ pulsesrc0|streaming\ stopped\,\ reason\ not\-negotiated'
PulseErrorString='Internal\ data\ flow\ error|streaming\ task\ paused|gst_pulsesrc_is_dead|Failed\ to\ connect\ stream\:\ No\ such\ entity|Failed\ to\ connect\:\ Connection\ refused|Connection\ terminated'
egrep -qi "${errorString}" ${GSTDEB} && gst_restart_needed="YES"
egrep -qi "${PulseErrorString}" ${GSTDEB} && pulse_restart_needed="YES"
echo "" >> ${DEB}
echo " gstreamer for sequence ${seq} restart needed: ${gst_restart_needed}" >> ${DEB}
echo "" >> ${DEB}
if [ "${pulse_restart_needed}" == "YES" ]
then
echo " pulseaudio for sequence ${seq} restart needed: ${pulse_restart_needed}" >> ${DEB}
gst_restart_needed="YES"
kill -9 $(pgrep pulseaudio)
pulseaudio --system=true -D&
if [ -f "${STOPfILE}" ]
then :
else
message="RK2 ${seq} pulse restarted: $(date +%d/%m/%y\ %H:%M:%S)"
LOG='/tmp/smsd.log'
NUMBER="+380XXXXXXXXX"
/usr/bin/sudo /usr/bin/gammu-smsd-inject TEXT "${NUMBER}" -text "${message}" 1>>$LOG 2>&1
/bin/rm -f $LOG
fi
fi
echo "sleep 1 started" >> ${DEB}
sleep 1
echo "sleep 1 ended" >> ${DEB}
echo "ls -lh ${TARGETDIR}/${fileNameNew}" >> ${DEB}
ls -lh ${TARGETDIR}/${fileNameNew} >> ${DEB} 2>&1
echo "----------------" >> ${DEB}
if [ "${gst_restart_needed}" == "YES" ]
then
if [ -f "${STOPfILE}" ]
then :
else
message="RK2 ${seq} gst restart needed: $(date +%d/%m/%y\ %H:%M:%S)"
#LOG='/tmp/smsd.log'
#NUMBER="+380XXXXXXXXX"
#/usr/bin/sudo /usr/bin/gammu-smsd-inject TEXT "${NUMBER}" -text "${message}" 1>>$LOG 2>&1
#/bin/rm -f $LOG
fi
echo "cat ${GSTDEB}" >> ${DEB}
cat ${GSTDEB} >> ${DEB}
echo "----------------" >> ${DEB}
echo "ps -auxww | grep gst-launch-1.0 | grep LOOP | grep -v grep" >> ${DEB}
ps -auxww | grep gst-launch-1.0 | grep LOOP | grep -v grep >> ${DEB}
echo "----------------" >> ${DEB}
b=0
while [ $b -lt 21 ]
do
killRegEx='ps -auxww | grep gst-launch-1.0 | grep LOOP | grep -v grep'
b=$((b+1))
echo "gstreamer SIGSEGV loop: $b" >> ${DEB}
currpid=$(grep pid: ${pid_file} | sed 's/^.*://g')
echo "trying killall by regex ${killRegEx} " >> ${DEB}
sudo kill -9 $(ps -auxww | grep gst-launch-1.0 | grep LOOP | grep -v grep | awk '{print $2}')
gstCMDsplit
lastPID=${!}
sed -i -e "s/^pid:.*$/pid:${lastPID}/g" ${pid_file}
echo "Pid chnaged to ${lastPID} in ${pid_file}" >> ${DEB}
echo "ps -auxww | grep gst-launch-1.0 | grep LOOP | grep -v grep" >> ${DEB}
ps -auxww | grep gst-launch-1.0 | grep LOOP | grep -v grep >> ${DEB}
echo "----------------" >> ${DEB}
echo "ps -auxww | grep ${lastPID}" >> ${DEB}
ps -auxww | grep ${lastPID} >> ${DEB}
echo "----------------" >> ${DEB}
echo "ls -l ${DEBUG_DIR}" >> ${DEB}
ls -l ${DEBUG_DIR} >> ${DEB}
echo "----------------" >> ${DEB}
echo "cat ${GSTDEB}" >> ${DEB}
cat ${GSTDEB} >> ${DEB}
echo "----------------" >> ${DEB}
echo "ls -lh ${TARGETDIR}/${fileNameNew}" >> ${DEB}
ls -lh ${TARGETDIR}/${fileNameNew} >> ${DEB} 2>&1
echo "----------------" >> ${DEB}
echo "sleep 1 started" >> ${DEB}
sleep 1
echo "sleep 1 ended" >> ${DEB}
echo "ls -l ${DEBUG_DIR}" >> ${DEB}
ls -l ${DEBUG_DIR} >> ${DEB}
echo "----------------" >> ${DEB}
echo "cat ${GSTDEB}" >> ${DEB}
cat ${GSTDEB} >> ${DEB}
echo "----------------" >> ${DEB}
echo "ls -lh ${TARGETDIR}/${fileNameNew}" >> ${DEB}
ls -lh ${TARGETDIR}/${fileNameNew} >> ${DEB} 2>&1
echo "----------------" >> ${DEB}
egrep -qi "${errorString}" ${GSTDEB} && gst_restarted="NO" || gst_restarted="YES"
echo "gst_restarted seq: ${seq}: ${gst_restarted}" >> ${DEB}
if [ "${gst_restarted}" == "YES" ]
then
if [ -f "${STOPfILE}" ]
then :
else
if [ $b -gt 1 ]
#if [ $b -gt 5 ]
then
message="RK2 ${seq} gst restarted,loop: ${b}, $(date +%d/%m/%y\ %H:%M:%S)"
LOG='/tmp/smsd.log'
NUMBER="+380XXXXXXXXX"
/usr/bin/sudo /usr/bin/gammu-smsd-inject TEXT "${NUMBER}" -text "${message}" 1>>$LOG 2>&1
/bin/rm -f $LOG
fi
fi
break
fi
sleep 2
done
if [ "${gst_restarted}" == "NO" ]
then
if [ -f "${STOPfILE}" ]
then :
else
message="RK2 ${seq} gst NOT restartied,loop ${b}, $(date +%d/%m/%y\ %H:%M:%S)"
LOG='/tmp/smsd.log'
NUMBER="+380XXXXXXXXX"
/usr/bin/sudo /usr/bin/gammu-smsd-inject TEXT "${NUMBER}" -text "${message}" 1>>$LOG 2>&1
/bin/rm -f $LOG
fi
fi
fi
Запуск этого скрипта происходит из /etc/rc.local:
#!/bin/sh -e
/bin/bash /home/pi/bin/set_performance_cpu.sh &
if [ -L /dev/doorcam ]
then
/home/pi/bin/disable_green_led_on_cam.sh &
mkdir /var/run/motion
chown motion:motion /var/run/motion
fi
/etc/init.d/dbus stop
/etc/init.d/cgmanager stop
/etc/init.d/cgproxy stop
/etc/init.d/triggerhappy stop
if [ -L /dev/doorcam ]
then
sudo -u motion sh -c 'sleep 4; /home/pi/motion-main/motion -c /etc/motion/motion.conf'
pulseaudio --system=true -D
sleep 2
/bin/bash /home/pi/bin/my_writer.sh &
/bin/bash /home/pi/bin/myMicrophoneStartupSound.sh &
/bin/bash /home/pi/bin/myPidMon.sh &
/bin/bash /home/pi/bin/my_mjpg_streamer_starter.sh &
fi
/bin/bash /home/pi/bin/myStartupSMS.sh &
if [ ! -L /dev/doorcam ]
then
MailFile=/tmp/$$_$$_log.txt
echo "" >> "${MailFile}"
echo "/dev/doorcam not found at startup: $(date)" >> "${MailFile}"
echo "" >> "${MailFile}"
echo "" >> "${MailFile}"
RCPTTO='email@gmail.com'
/usr/bin/mutt -e 'my_hdr From:email@gmail.com' $RCPTTO -s DOORcam_Not_Found_at_RPI3 < ${MailFile}
/bin/rm -rf ${MailFile}
fi
exit 0
Перед тем как запускать /home/pi/bin/my_writer.sh скрипт pulseaudio демон должен быть запущен (pulseaudio --system=true -D).
Процесс, который появится в результате работы скрипта:
pi@rpi3:~ $ ps -ef | grep LOOP | grep -v grep root 30202 1 8 Apr01 ? 01:43:22 /usr/bin/gst-launch-1.0-LOOP --gst-debug-level=3 --gst-debug-no-color -e v4l2src device=/dev/video2 do-timestamp=true ! video/x-raw,format=I420,width=384,height=504,framerate=10/1 ! clockoverlay time-format="%H:%M:%S" halignment=left valignment=top xpad=5 ypad=65 font-desc="Terminus bold 30px" ! clockoverlay time-format="%d/%m/%Y" halignment=right valignment=top xpad=0 ypad=65 font-desc="Terminus bold 30px" ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! omxh264enc target-bitrate=400000 control-rate=variable ! video/x-h264,profile=high ! h264parse config-interval=2 ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! splitmuxsink muxer=avimux name=my1avimux max-size-time=60000000000 location=/camera/video/lastMins/writer_20190401173004_%04d.avi pulsesrc do-timestamp=true device="alsa_input.usb-046d_081b_0FE0F1A0-02-U0x46d0x81b.analog-mono" provide-clock=true buffer-time=30000 latency-time=1000 ! audioconvert qos=true ! audio/x-raw,rate=48000,channels=1 ! voaacenc bitrate=64000 ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 leaky=2 ! my1avimux.audio_%u
Обратите внимание на
omxh264enc target-bitrate=400000 control-rate=variable
То есть, если камера может на аппаратном уровне отдавать h264 - то все что в пайплайне gstreamer-a до omxh264enc target-bitrate=400000 control-rate=variable ! video/x-h264,profile=high ! может быть опущено - надо проверить!!
В результате в $TARGETDIR начнут появляться видео файлы:
pi@rpi3:~ $ ls -lhtr /camera/video/lastMins/ | tail -5 -rw-r--r-- 1 root root 3.1M Apr 2 12:53 writer_20190401173004_1231.avi -rw-r--r-- 1 root root 3.1M Apr 2 12:54 writer_20190401173004_1232.avi -rw-r--r-- 1 root root 3.1M Apr 2 12:55 writer_20190401173004_1233.avi -rw-r--r-- 1 root root 3.1M Apr 2 12:56 writer_20190401173004_1234.avi -rw-r--r-- 1 root root 1.4M Apr 2 12:56 writer_20190401173004_1235.avi
pi@rpi3:~ $ mediainfo /camera/video/lastMins/writer_20190401173004_1234.avi General Complete name : /camera/video/lastMins/writer_20190401173004_1234.avi Format : AVI Format/Info : Audio Video Interleave File size : 3.06 MiB Duration : 55s 0ms Overall bit rate : 467 Kbps Video ID : 0 Format : AVC Format/Info : Advanced Video Codec Format profile : High@L4.0 Format settings, CABAC : Yes Format settings, ReFrames : 1 frame Format settings, GOP : M=1, N=60 Codec ID : H264 Duration : 55s 0ms Bit rate : 393 Kbps Width : 384 pixels Height : 504 pixels Display aspect ratio : 0.762 Frame rate : 10.000 fps Color space : YUV Chroma subsampling : 4:2:0 Bit depth : 8 bits Scan type : Progressive Bits/(Pixel*Frame) : 0.203 Stream size : 2.58 MiB (84%) Audio ID : 1 Format : AAC Format/Info : Advanced Audio Codec Format profile : LC Codec ID : FF Duration : 54s 400ms Bit rate : 64.0 Kbps Channel(s) : 1 channel Channel positions : Front: C Sampling rate : 48.0 KHz Compression mode : Lossy Stream size : 425 KiB (14%) Alignment : Split accross interleaves Interleave, duration : 22 ms (0.22 video frame)
[править] Motion
В данный момент используется motion версии:
pi@rpi3:~/motion-main $ ./version.sh 3.4.1+git7692717
из https://github.com/Motion-Project/motion
но лучше начать с apt-get install motion.
Запуск демона motion в данном случае выполнен из /etc/rc.local:
...
...
...
if [ -L /dev/doorcam ]
then
/home/pi/bin/disable_green_led_on_cam.sh &
mkdir /var/run/motion
chown motion:motion /var/run/motion
fi
...
...
sudo -u motion sh -c 'sleep 4; /home/pi/motion-main/motion -c /etc/motion/motion.conf'
...
...
Конфигурационный файл motion.conf:
sudo egrep -v '^#|^$|^;' /etc/motion/motion.conf daemon on process_id_file /var/run/motion/motion.pid setup_mode off logfile /tmp/motion.log log_level 6 log_type all videodevice /dev/video1 v4l2_palette 15 input -1 norm 0 frequency 0 rotate 0 width 384 height 448 framerate 5 minimum_frame_time 0 netcam_keepalive off netcam_tolerant_check off auto_brightness off brightness 0 contrast 0 saturation 0 hue 0 roundrobin_frames 1 roundrobin_skip 1 switchfilter off threshold 15 threshold_tune off noise_level 10 noise_tune off despeckle_filter EedDl smart_mask_speed 0 lightswitch 0 minimum_motion_frames 2 pre_capture 0 post_capture 0 event_gap 33 max_movie_time 0 emulate_motion off output_pictures off output_debug_pictures off quality 75 picture_type jpeg ffmpeg_output_movies off ffmpeg_output_debug_movies off ffmpeg_timelapse 0 ffmpeg_timelapse_mode daily ffmpeg_bps 9999999 ffmpeg_variable_bitrate 0 ffmpeg_video_codec mpeg4 ffmpeg_deinterlace off use_extpipe off snapshot_interval 0 locate_motion_mode off locate_motion_style box text_right %Y-%m-%d\n%T-%q text_changes on text_event %Y%m%d%H%M%S text_double on target_dir /var/lib/motion snapshot_filename %v-%Y%m%d%H%M%S-snapshot picture_filename %Y%m%d/%v-%Y%m%d%H%M%S-%q movie_filename %Y%m%d/%v-%Y%m%d%H%M%S timelapse_filename %Y%m%d-timelapse ipv6_enabled off stream_port 0 stream_quality 50 stream_motion on stream_maxrate 2 stream_localhost off stream_limit 0 stream_auth_method 1 stream_authentication user:password webcontrol_port 0 webcontrol_localhost on webcontrol_html_output on track_type 0 track_auto off track_iomojo_id 0 track_step_angle_x 10 track_step_angle_y 10 track_move_wait 10 track_speed 255 track_stepsize 40 quiet on on_event_start /var/lib/motion/bin/myrec_start.sh %v %v-%Y%m%d%H%M%S on_event_end /var/lib/motion/bin/myrec_stop.sh %v %v-%Y%m%d%H%M%S
тут много лишних опций, основная задача motion - определить начало/конец движения и запустить on_event_start/on_event_end скрипт с параметрами.
Скрипт /var/lib/motion/bin/myrec_start.sh:
#!/bin/bash
#set -x
if [ -z "${1}" ]
then
exit 0
fi
if [ -z "${2}" ]
then
exit 0
fi
STOPfILE="/var/lib/motion/stop_sms"
SEQ="${1}"
if [ -f "${STOPfILE}" ]
then :
else
message="+ $(date +%d/%m/%y\ %H:%M:%S) ${SEQ}"
LOG='/tmp/smsd.log'
NUMBER="+380XXXXXXXXX"
/usr/bin/sudo /usr/bin/gammu-smsd-inject TEXT "${NUMBER}" -text "${message}" 1>>$LOG 2>&1
/bin/rm -f $LOG
fi
stopTELEGRAMMfILE='/var/lib/motion/stop_telegramm'
if [ -f "$stopTELEGRAMMfILE" ]
then :
else
message="+ $(date +%d/%m/%y\ %H:%M:%S) ${SEQ}"
/home/pi/bin/notify_me.sh "${message}" &
fi
MOTION_OLD_INFO_FILE='/tmp/motion_info_file_old'
/bin/rm -f $MOTION_OLD_INFO_FILE
MOTION_LOG_FILE=/var/log/motion_history.log
echo "+ $(date +%d/%m/%y\ %H:%M:%S) ${SEQ}" >> /var/log/motion_history.log
/usr/bin/logger -p info -n 192.168.7.254 -d -t MOTION_UP "test"
/bin/bash /home/pi/bin/popup_android-start.sh &
/usr/bin/touch /tmp/motion_start_file_${SEQ}
/bin/bash /home/pi/bin/my_motion_watcher.sh "${SEQ}" &
/bin/bash /home/pi/bin/5min_mon.sh start "${SEQ}" &
основная задача этого скрипта - оповестить по смс или telegram о начале движения и запустить /home/pi/bin/my_motion_watcher.sh, который начнет отправлять ролики на почту до окончания движения.
В нем используется /usr/src/ffmpeg-2.7.2/ffmpeg для пересбора avi в mp4 контейнер, так как мощности raspberry pi не хватает налету сразу mp4 собирать, avi - еле еле получается собрать... - это еще один костыль, который надо исправить, есть надежда на c920 камеру с h264 на борту...
Скрипт /home/pi/bin/my_motion_watcher.sh:
#!/bin/bash
#set -x
MOTION_HISTORY_LOG=/var/log/motion_history.log
mailFile="/tmp/$$_1_$$_email_file"
PROCEEDED_FILES_LOG=/var/log/motion_watcher_files_sent_log
OWN_LOG=/var/log/motion_watcher_log
RCPTTO='email@gmail.com'
CURRENT_SEQ_OF_MOTION="$1"
eval $(grep ^TARGETDIR /home/pi/bin/my_writer.sh)
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::$0::TARGETDIR: ${TARGETDIR}" >> ${OWN_LOG}
AGE_DIFF_TRESHOLD=20
MOTION_STOP_FILE="/tmp/motion_stop_file_${CURRENT_SEQ_OF_MOTION}"
LOW_TIME_STAMP=''
HIGH_TIME_STAMP=$(date "+%Y-%m-%d %H:%M:%S")
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::HIGH_TIME_STAMP: ${HIGH_TIME_STAMP}" >> ${OWN_LOG}
file_cnt=0
while true
do
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::Inside while loop" >> ${OWN_LOG}
for file in $(find ${TARGETDIR} -type f -newermt "${HIGH_TIME_STAMP}")
# -a -not -newermt '-5 seconds')
do
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::file to work: $file" >> ${OWN_LOG}
PROCEEDED_FILE="NO"
grep -q "${file}" ${PROCEEDED_FILES_LOG} && PROCEEDED_FILE="YES" || PROCEEDED_FILE="NO"
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::PROCEEDED: ${PROCEEDED_FILE}" >> ${OWN_LOG}
if [ "x${PROCEEDED_FILE}" == "xNO" ]
then
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::file to work: ${file}" >> ${OWN_LOG}
AGE_DIFF=0
AGE_DIFF=$(expr `date +%s` - `stat -c %Y "${file}"`)
if [ ${AGE_DIFF} -le ${AGE_DIFF_TRESHOLD} ]
then
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::diff for file ${file} now is ${AGE_DIFF}, treshold is ${AGE_DIFF_TRESHOLD}" >> ${OWN_LOG}
fi
if [ ${AGE_DIFF} -ne 0 -a ${AGE_DIFF} -gt ${AGE_DIFF_TRESHOLD} ]
then
file_cnt=$((file_cnt+1))
echo "$(date)::${CURRENT_SEQ_OF_MOTION}:: FILE IS READY TO BE SENT: ${file}, FILE COUNT: ${file_cnt}" >> ${OWN_LOG}
echo "$file" >> ${PROCEEDED_FILES_LOG}
tail -3 ${MOTION_HISTORY_LOG} | grep ${CURRENT_SEQ_OF_MOTION}$ >> ${mailFile}
human_name=''
human_name=$(/bin/ls ${file} | sed 's/.*writer_\([0-9][0-9][0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)/\4:\5:\6_\3-\2-\1/g')
/bin/cp ${file} "${TARGETDIR}/../${CURRENT_SEQ_OF_MOTION}_${human_name}"
( mp4_human_name=$(echo ${human_name} | sed 's/\.avi/\.mp4/');\
nice -n19 /usr/src/ffmpeg-2.7.2/ffmpeg -threads 1 -i ${file} -c:a copy "${TARGETDIR}/../${CURRENT_SEQ_OF_MOTION}_${mp4_human_name}" -hide_banner -loglevel panic -y & (sleep 2; cpulimit -z -P /usr/src/ffmpeg-2.7.2/ffmpeg -l 50; /usr/bin/mutt ${RCPTTO} -s Motion_${CURRENT_SEQ_OF_MOTION}_${file_cnt}_Finished -a "${TARGETDIR}/../${CURRENT_SEQ_OF_MOTION}_${mp4_human_name}" < ${mailFile});\
(sleep 3; /home/pi/bin/my_black_frame_detector.sh "${TARGETDIR}/../${CURRENT_SEQ_OF_MOTION}_${mp4_human_name}")&
(sleep 1; /usr/bin/sudo /usr/sbin/exim4 -q -v 1>/dev/null 2>&1)&
rm -f ${mailFile};\
)&
if [ ! -z ${file_cnt} ]
then
if [ ${file_cnt} -gt 15 ]
then
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::SOMETHING WRONG WITH Start/Stop sequences, trying to fix now. :: FILE COUNT: ${file_cnt}" >> ${OWN_LOG}
# grep 518 /tmp/motion.log
# [1] [NTC] [ALL] [Jul 23 18:14:25] motion_detected: Motion detected - starting event 518
# [1] [NTC] [ALL] [Jul 23 18:15:08] motion_loop: End of event 518
MOTION_LOG_LINES="$(grep ${CURRENT_SEQ_OF_MOTION} /tmp/motion.log)"
log_start_found="NO"
log_finish_found="NO"
echo "$MOTION_LOG_LINES" | grep -q "Motion detected - starting event ${CURRENT_SEQ_OF_MOTION}" && log_start_found="YES"
echo "$MOTION_LOG_LINES" | grep -q "motion_loop: End of event ${CURRENT_SEQ_OF_MOTION}" && log_finish_found="YES"
if [ "x${log_start_found}" == "xYES" -a "x${log_finish_found}" == "xYES" ]
then
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::SOMETHING WRONG WITH Start/Stop sequences, start/stop found in motion.log. :: FILE COUNT: ${file_cnt}" >> ${OWN_LOG}
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::SOMETHING WRONG WITH Start/Stop sequences, part of motion.log. :: FILE COUNT: ${file_cnt}" >> ${OWN_LOG}
echo "${MOTION_LOG_LINES}" >> ${OWN_LOG}
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::SOMETHING WRONG WITH Start/Stop sequences, end of part of motion.log. :: FILE COUNT: ${file_cnt}" >> ${OWN_LOG}
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::SOMETHING WRONG WITH Start/Stop sequences, working_here.. kill bash with ${CURRENT_SEQ_OF_MOTION} in cmdline.. :: FILE COUNT: ${file_cnt}" >> ${OWN_LOG}
currentPID=$$
pid_to_kill=$(ps -ef | grep bash | grep my_motion_watcher | grep ${CURRENT_SEQ_OF_MOTION} | awk '{print $2}')
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::SOMETHING WRONG WITH Start/Stop sequences, Current PID: $currentPID; Determinated PID to kill: $pid_to_kill" >> ${OWN_LOG}
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::SOMETHING WRONG WITH Start/Stop sequences, Killing Determinated PID to kill: $pid_to_kill" >> ${OWN_LOG}
kill -9 $pid_to_kill
fi
fi
fi
fi
else
continue
fi
done
if [ -f "${MOTION_STOP_FILE}" ]
then
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::Motion finished" >> ${OWN_LOG}
if [ -z $LOW_TIME_STAMP ]
then
LOW_TIME_STAMP=$(date "+%Y-%m-%d %H:%M:%S" -d '+ 65 seconds' )
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::LOW TIME STAMP REMEMBERED: ${LOW_TIME_STAMP}, high timestamp: ${HIGH_TIME_STAMP}" >> ${OWN_LOG}
else
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::LOW TIME STAMP ALREADY REMEMBERED: ${LOW_TIME_STAMP}" ${OWN_LOG}
fi
ALL_FILES_SENT="START"
for filesLast in $(find ${TARGETDIR} -type f -newermt "${HIGH_TIME_STAMP}" -not -newermt "${LOW_TIME_STAMP}")
do
grep -q "${filesLast}" ${PROCEEDED_FILES_LOG} && ALL_FILES_SENT="${ALL_FILES_SENT}:YES" || ALL_FILES_SENT="${ALL_FILES_SENT}:NO"
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::File: ${filesLast} " >> ${OWN_LOG}
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::LOW TIME STAMP: ${LOW_TIME_STAMP}, high timestamp: ${HIGH_TIME_STAMP}" >> ${OWN_LOG}
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::$(ls -l ${filesLast}) " >> ${OWN_LOG}
done
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::ALL FILES SENT: ${ALL_FILES_SENT}" >> ${OWN_LOG}
READY_TO_EXIT="NO"
echo "${ALL_FILES_SENT}" | grep -q "NO" && READY_TO_EXIT="NO" || READY_TO_EXIT="YES"
if [ "x${READY_TO_EXIT}" == "xYES" ]
then
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::ALL FILE SENT. EXIT" >> ${OWN_LOG}
rm -f ${MOTION_STOP_FILE}
sleep 5
break
fi
fi
echo "$(date)::${CURRENT_SEQ_OF_MOTION}::Sleep 15 in while loop" >> ${OWN_LOG}
sleep 15
done
Скрипт /var/lib/motion/bin/myrec_stop.sh:
#!/bin/bash
DEB="/tmp/deb.log"
if [ -z ${1} ]
then
exit 0
fi
if [ -z ${2} ]
then
exit 0
fi
TARGETDIR="/tmp"
STOPfILE="/var/lib/motion/stop_sms"
if [ -f "${STOPfILE}" ]
then :
else
SEQ=${1}
message="- $(date +%d/%m/%y\ %H:%M:%S) ${SEQ}"
LOG='/tmp/smsd.log'
NUMBER="+380XXXXXXXXX"
/usr/bin/sudo /usr/bin/gammu-smsd-inject TEXT "${NUMBER}" -text "${message}" 1>>$LOG 2>&1
/bin/rm -f $LOG
fi
stopTELEGRAMMfILE='/var/lib/motion/stop_telegramm'
if [ -f "$stopTELEGRAMMfILE" ]
then :
else
SEQ=${1}
message="- $(date +%d/%m/%y\ %H:%M:%S) ${SEQ}"
/home/pi/bin/notify_me.sh "${message}" &
fi
SEQ="${1}"
FILE="${2}"
if [ -f /tmp/motion_start_file_${SEQ} ]
then
/usr/bin/touch /tmp/motion_stop_file_${SEQ}
/bin/rm -f /tmp/motion_start_file_${SEQ}
fi
/bin/bash /home/pi/bin/popup_android-stop.sh &
/bin/bash /home/pi/bin/5min_mon.sh stop "${SEQ}" &
MOTION_LOG_FILE=/var/log/motion_history.log
echo "- $(date +%d/%m/%y\ %H:%M:%S) ${SEQ}" >> ${MOTION_LOG_FILE}
[править] mjpg_streamer
В данный момент используется версия:
pi@rpi3:~/mjpg-streamer/mjpg-streamer $ ./mjpg_streamer -v MJPG Streamer Version: 3:172M Compilation Date.....: Sep 27 2016 Compilation Time.....: 05:24:36
Источник: https://sourceforge.net/projects/mjpg-streamer/
mjpg_streamer запускается при старте ОС из /etc/rc.local:
...
...
if [ -L /dev/doorcam ]
then
sudo -u motion sh -c 'sleep 4; /home/pi/motion-main/motion -c /etc/motion/motion.conf'
pulseaudio --system=true -D
sleep 2
/bin/bash /home/pi/bin/my_writer.sh &
/bin/bash /home/pi/bin/myMicrophoneStartupSound.sh &
/bin/bash /home/pi/bin/myPidMon.sh &
/bin/bash /home/pi/bin/my_mjpg_streamer_starter.sh &
fi
...
...
скриптом /home/pi/bin/my_mjpg_streamer_starter.sh:
#!/bin/bash
#set -x
UPTIME=0
UPTIME=$(/bin/cat /proc/uptime | /usr/bin/cut -f1 -d' ' | /bin/sed 's/\..*//g')
if [ ${UPTIME} -lt 80 ]
then
sleep 12
fi
(/usr/local/bin/mjpg_streamer -i "/usr/local/lib/input_uvc.so -q 100 -d /dev/video3 -r 384x504 -f 3 -y" -o "/usr/local/lib/output_http.so -n -w /home/pi/my_tmp_www" 2>>/tmp/v4l2Mysink_deb.log)&
В итоге будет процесс:
pi@rpi3:~ $ ps -ef | grep mjp root 5977 1 9 Mar24 ? 20:24:41 /usr/local/bin/mjpg_streamer -i /usr/local/lib/input_uvc.so -q 100 -d /dev/video3 -r 384x504 -f 3 -y -o /usr/local/lib/output_http.so -n -w /home/pi/my_tmp_www
И доступ к стриму по url http://RPi_ip:8080/?action=stream