用小米摄像头+Raspberry Pi打造监控系统
最近用小米摄像头+树莓派制作了一个基于HTML5的监控系统,上班的时候可以看看家里的狗在干啥。用小米摄像头是因为它价格很便宜,前两天买的时候30刀https://www.amazon.com/gp/product/B019MMRV1M/,然后有修改的固件可以关掉原版的连接到小米云并且打开RTSP直播流,配合树莓派特别合适。树莓派是最新的第三代四核版本安装Raspbian Jessie,带Wifi也免了布线的麻烦。
小米摄像头安装修改版固件需要一张tf卡,从https://github.com/fritz-smh/yi-hack下载之后将sd目录内的文件拷贝到tf卡根目录,并且需要修改两个文件:
test/yi-hack.cfg
控制摄像头的基本参数,包括root密码,IP地址(需要静态IP),时区设置和NTP服务器。test/wpa_supplicant.conf
控制摄像头连接的无线网络,默认是WPA2-PSK的配置。修改好之后就可以将tf卡插进摄像头,插上电源启动了。黄灯代表正在启动,闪烁蓝灯代表正在设置网络,稳定蓝灯代表启动完成,这时候就可以用浏览器打开摄像头的IP地址,会出现一个页面提供摄像头的基本信息,如图:
如图所见,里面提供了三个RTSP源地址,用VLC打开就可以观看直播了。然而这个固件有一个问题,RTSP源是没有任何认证方式的,也不能直接用浏览器打开。我不太喜欢这种没有身份认证的方式,于是决定将RTSP转换成HTTP流,然后嵌入网页直播。这样的话就可以方便的使用http auth来认证身份并且还可以使用https加密连接。上网google了一圈,将rtsp流转换成http流一般有两种方式:ffmpeg或者是vlc。经过权衡和实验之后,我决定使用vlc来转换rtsp流,理由如下:
- ffmpeg在树莓派上需要从源代码编译,而作为替代的avconv并不提供等价于ffserver的avserver。我个人对于不能用二进制包安装的软件态度是能避免就避免,因为维护起来太过麻烦。
- 小米摄像头的输出是h264,其实并不需要重新转码(而且树莓派用ffmpeg转压成webm效果简直惨不忍睹),vlc足以搞定。
使用vlc将rtsp转换为http并且不转压的话,选择挺多,个人觉得最方便的办法是转换成HLS(HTTP Live Streaming),这是由Apple开发的流媒体传输协议,在移动平台上的效果很好(讽刺的是桌面平台的支持反而不太好,见后),然后用vlc部署也很容易。HLS的原理是把流分成一个个小文件,每次客户端下载一个小文件,以一个m3u8文件作为索引,m3u8中的分片读完后客户端会重新请求这个文件,内容会被刷新包含新的分片。
使用vlc来直播HLS流的话需要一个http服务器,最简单的办法装个nginx,不需要其他任何插件(包括RTMP),Raspbian的nginx-light就可以了。假设http的根目录在/var/www,建立一个文件夹用来放流文件分片,比如streaming。这个文件夹需要vlc有可写权限。
VLC安装和配置
首先是配置vlc。在Raspbian上用
apt-get install vlc-nox
来安装vlc。nox版本不需要X Window环境,适合树莓派这样的headless server。安装好之后给vlc建立一个用户:
sudo useradd -d /nonexistent -G audio -M -p ! -r -s /bin/false -u 1000 -U vlcd
然后在/var/www下建立文件夹streaming,chown保证vlcd这个用户可写这个目录。
可以用以下命令测试vlc转发是否工作:
/usr/bin/cvlc -I dummy rtsp://192.168.1.3:554/ch0_0.h264 --sout='#std{access=livehttp{seglen=10,delsegs=true,numsegs=5,index=/var/www/streaming/mystream.m3u8,index-url=http://mydomain.com/streaming/mystream-########.ts},mux=ts{use-key-frames},dst=/var/www/streaming/mystream-########.ts}' :demux=h264
其中192.168.1.3是小米摄像头的IP,mydomain.com是对外的域名。
要让vlc开机自启动,需要将其建为一个服务。然而systemd的脚本中命令行包含井号的话加载会出错,因此再写一个脚本来处理vlc的启动,参考https://github.com/jtyr/legoirc-server:
#!/bin/bash
# Return value
RETVAL=0
function start() {
# Start the streaming on background
/usr/bin/cvlc --syslog -I dummy rtsp://192.168.1.3:554/ch0_0.h264 --sout='#std{access=livehttp{seglen=10,delsegs=true,numsegs=5,index=/var/www/streaming/mystream.m3u8,index-url=http://mydomain.com/streaming/mystream-########.ts},mux=ts{use-key-frames},dst=/var/www/streaming/mystream-########.ts}' :demux=h264
}
function stop() {
# Kill the main process and all its children
killall vlc
}
PARAM=$1
case $PARAM in
'start')
start
shift
;;
'stop')
stop
shift
;;
*)
echo "ERROR: Unknown action: $PARAM"
shift
;;
esac
exit $RETVAL
存为/usr/bin/vlc-camera-stream,然后写一个systemd脚本放到/lib/systemd/system/vlc.service
调用:
[Unit]
Description=VideoOnLAN Service
After=network.target
[Service]
Type=forking
User=vlcd
ExecStart=/usr/bin/vlc-camera-stream start
ExecStop=/usr/bin/vlc-camera-stream stop
Restart=on-abort
[Install]
WantedBy=multi-user.target
最后systemctl enable vlc开机启用,systemctl start vlc启动。
接下来在网页上嵌入这个直播视频。HLS默认在桌面上只有Edge浏览器支持,桌面版Firefox和Chrome都不支持。然而Android上的原生浏览器,Chrome和iOS的Safari都支持HLS。因此在桌面浏览器上要在网页内直接播放这个视频的话,不能直接把m3u8嵌入video标签,需要用到第三方的库,比如hls.js(https://github.com/dailymotion/hls.js)。
然后就纯粹是nginx的配置来加上http auth和https,用
apt-get install apache2-utils
安装htpasswd,然后用
htpasswd -c /etc/nginx/.htpasswd username
生成用户名和密码,然后在nginx的配置段内加上
location /streaming {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
try_files $uri $uri/ =404;
}
来加入http auth。https的配置很容易找到不多说了。最后就是在路由上做端口转发到nginx的https端口,这样就实现了https加密+认证的视频流直播。
用hls的缺点是直播有延迟,时间大体上取决于分片的数目和每个分片的长度,这个例子里面是5个10秒的分片,也就是说至少有50秒的延迟。
PS:发现有时候HLS的直播会莫名其妙停掉,看m3u8文件发现里面有tag EXT-X-ENDLIST,于是写了个script每五分钟跑一次监视,如果出现这个tag就重启vlc。
#!/bin/bash
streamlist='/var/www/streaming/mystream.m3u8'
if [-f $streamlist]; then
status=`cat $streamlist | grep EXT-X-ENDLIST`
if [! -z $status]; then
systemctl restart vlc
logger 'VLC has been restarted for stopped live streaming'
exit 1
fi
exit 0
fi