Building a bird camera
I had to build a web camera for watching some newly hatched birds. From previous experiments I already had a spare Raspberry Pi wit an attached camera. Due to bandwidth limitations in the wireless network I wanted to host the resulting media on one of my public servers. I’m using ffmpeg with HLS streaming as this works in most browsers out-of-the-box.
Prepare Apache server
We need a place where ffmpeg can upload its files. I use Apache with DAV enabled:
a2enmod dav_fs dav
The requests timeout module has to be disabled as ffmpeg
will otherwise timeout.
a2dismod reqtimeout
Now modify /etc/apache2/sites-available/default-ssl.conf
and add something like this at the end
<Directory /var/www/html/cam>
Require all granted
Dav filesystem
<LimitExcept GET POST OPTIONS>
<RequireAll>
# This provides only limited security
Require ip 127.0.0.1
Require expr %{HTTP_USER_AGENT} == 'Lavf/58.20.100'
Require expr %{REQUEST_FILENAME} =~ /cam\.m3u8|cam[0-9]+\.ts/
</RequireAll>
</LimitExcept>
</Directory>
Restart Apache:
apachectl configtest
apachectl graceful
Setup simple page
Put this in /var/www/html/cam.html
or somewhere else:
<html>
<head>
<title>Cam</title>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>
<body>
<video controls="controls" width="1280" height="720" autoplay="autoplay" id="video" >
<source src="cam/cam.m3u8" type="application/x-mpegURL" />
</video>
<script>
var video = document.getElementById('video');
var videoSrc = 'https://birdy.pmhahn.de/cam/cam.m3u8';
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function() {
video.play();
});
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
video.addEventListener('loadedmetadata', function() {
video.play();
});
}
</script>
</body>
</html>
Actually HLS does not work with Chromium and I had to add the HLS.js fallback.
Setup camera
On the Raspberry Pi install ffmpeg
:
apt install ffmpeg
Setup script /home/pi/ffcam
to take two picture a second, but create a h.264 stream with 25 frames for compatibility reasons.
#!/bin/bash
LEN='10' # length of each fragment in seconds
declare -a ARGS=(
-hide_banner
-nostats
-v level+error
# -v trace
-f video4linux2
-input_format h264
-video_size 1280x720
-framerate 2
-i /dev/video0
-r 25
-vcodec copy
-an -sn
-flags +cgop
-g 30
-f hls
-hls_init_time "$LEN"
-hls_time "$LEN"
-hls_list_size 60
-hls_delete_threshold 10
-hls_allow_cache 1
# -hls_segment_filename 'cam%04d.ts'
-hls_segment_type mpegts
-hls_flags delete_segments+program_date_time # +temp_file
# -hls_playlist_type event
# -master_pl_name cam.m3u8
# -master_pl_publish_rate 1
-method PUT
# -ignore_io_errors
-timeout 120
# -headers 'Token: N6TXb058UGWh32MCeA9U'
https://birdy.pmhahn.de/cam/cam.m3u8
)
exec ffmpeg "${ARGS[@]}"
And a systemd service file /etc/systemd/system/ffcam.service
to start that:
[Unit]
Description=Run bird stream
ConditionPathExists=/dev/video0
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/home/pi/ffcam
Restart=always
RestartSec=10
User=pi
WorkingDirectory=/home/pi
[Install]
WantedBy=multi-user.target
Start the service now and also on next reboot:
systemctl start ffcam.service
systemctl enable ffcam.service
Gotchas
- You have to use a minimum frame rate of 2 or get black frames only otherwise
- Do not specify
-hls_segment_filename
or otherwise the segments will not get uploaded - only the.m3u8
will. - Do not specify
-hls_playlist_type …
as they implicitly reset to-hls_list_size 0
, which leads to an unbound number of segments. - Adding additional HTTP
-headers
does not work for HLS.
Links
- How can I stream H.264 video from the Raspberry Pi camera module via a web server?
- hls.js
- RasPi: Official V4L2 driver
- VLC: avformat/hlsenc: avformat/hlsenc: reopen new http session for http_persistent
Other plays
Live streaming via UDP broadcast:
ffmpeg -hide_banner -f video4linux2 -input_format h264 -video_size 640x480 -framerate 2 -i /dev/video0 -vcodec copy -an -sn -f mpegts udp://192.168.178.45:1234
ffplay -i udp://192.168.178.45:1234