Участник: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