The birdhouse project consists sof two parts:
- birdhouse-rs, the webserver
- birdhouse-python, the camera control hosted on the raspberry pi in the birdhouse.
The webserver is built with Dioxus. The video feed is streamed to mediamtx and then proxied through the webserver with webRTC and using a turn server ( coturn).
Multiple docker container are required for this project:
- dioxus webserver
- mediamtx server
- coturn server
- grafana
The raspberry pi and the server are connected using netbird.
First, set up birdhouse-rs on a server (or synology NAS). Then set up netbird on both the raspberry pi and the server.
Enter all tokens and URLs in the .env file.
For database access, configure either POSTGRES_DSN or:
POSTGRES_HOSTPOSTGRES_PORTPOSTGRES_DBPOSTGRES_USERPOSTGRES_PASSWORD
Optional:
POSTGRES_TABLE(defaults toinflux_points)POSTGRES_BUCKET(defaults tovoegelior falls back toINFLUXDB_BUCKETif still present)
Install the requirements:
sudo apt-get update
sudo apt-get install portaudio19-dev python3-pyaudio
pip install -r requirements.txtTo stream the video from the raspberry pi camera to the mediamtx server, run the following command on the raspberry pi:
rpicam-vid -t 0 \
--codec h264 \
--profile high \
--level 4.2 \
--framerate 25 \
--width 1980 --height 1080 \
--bitrate 18000000 \
--intra 75 \
--denoise cdn_hq \
--inline \
--nopreview \
-o - | ffmpeg \
-fflags +genpts \
-f h264 -i - \
-c:v copy \
-rtsp_transport tcp \
-f rtsp rtsp://raspberrypi.netbird.cloud:8554/birdcamFor live-image capture, the recommended setup is to split the camera output before MediaMTX and keep a local rolling H.264 buffer on the Pi. Then set LOCAL_VIDEO_BUFFER_DIR in .env so birdhouse-python reads those local segments directly instead of subscribing back to RTSP.
Example shell pipeline:
set -euo pipefail
mkdir -p /home/birdie/birdhouse-buffer
rpicam-vid -t 0 \
--codec h264 \
--profile high \
--level 4.2 \
--framerate 25 \
--width 1920 --height 1080 \
--bitrate 18000000 \
--intra 20 \
--denoise cdn_hq \
--autofocus-mode manual \
--lens-position 3.5 \
--saturation 1.8 \
--gain 2 \
--exposure normal \
--inline \
--nopreview \
-o - | tee \
>(ffmpeg -re -fflags +genpts -r 25 -f h264 -i - -c:v copy -rtsp_transport tcp -f rtsp rtsp://raspberrypi.netbird.cloud:8554/birdcam) \
>(ffmpeg -fflags +genpts -r 25 -f h264 -i - -c:v copy -f segment -segment_time 1 -segment_wrap 16 -segment_list_size 16 -segment_format mpegts /home/birdie/birdhouse-buffer/segment_%03d.ts) \
>/dev/nullThe -r 25 on both FFmpeg branches is important here. Without it, FFmpeg can infer bad timestamps from the raw H.264 pipe. Do not synthesize packet timestamps on the local segment branch with setts=...; that can compress real elapsed time into an artificial 25 fps timeline and make exported clips look sped up. Keeping -re on the RTSP branch is fine if you want that leg paced in real time. Also avoid -reset_timestamps 1 on the local segment branch, because the Python side later concatenates those segments again and needs a continuous timeline across them.
Relevant .env entries:
LOCAL_VIDEO_BUFFER_DIR=/home/birdie/birdhouse-buffer
LIVE_VIDEO_ENCODER=libx264With this configuration, no additional code changes are needed in birdhouse-python. After updating .env, restart main.py and confirm startup logs include:
Using local video buffer directory /home/birdie/birdhouse-buffer
And in a separate session, run the birdhouse-python script to log the sensor data:
python3 main.py
