I recently got myself an ArduCam 16MP Autofocus camera to run as a monitoring camera with my Raspberry Pi 4B. It uses an IMX519 sensor, which has
libcamera support in Bullseye, but not
raspicam. In fact, from Raspberry Pi OS Bullseye and on, only
libcamera is supported. As such, it does not work with OctoPi out of the box.
ArduCam has published instructions on how to get OctoPi working with the
libcamera stack (Solution Of Using Autofocus Camera On OctoPrint - Arducam), but in my opinion, their approach is not suitable for running a real OctoPi instance for several reasons:
- It involves modifying a nightly version of OctoPi.
- It is not a good idea to run something as sensitive and potentially dangerous as a 3D printer long term on an unstable nightly build.
- The modifications are not trivially reversible and are difficult to document.
The Docker Way
This method of using libcamera and OctoPrint eschews OctoPi entirely. Instead, this will use the official Raspberry Pi OS image and run a modified
mjpg_streamer and OctoPrint in Docker containers. This approach has several benefits:
- OctoPrint is completely decoupled from the host OS. This means that any urgent OS updates can be immediately applied with negligible risk of affecting the OctoPrint installation.
mjpg_streamercan be easily swapped out with a better solution for streaming
libcameracameras in the future, since it runs as just a basic Docker container.
- Everything is documented as Dockerfiles and YAML files, meaning the setup is portable and can be easily replicated across multiple installations.
Warning: This is a technical guide
This guide assumes that you are capable of setting up and maintaining a Docker installation. If you are not comfortable with Docker, this may be too complex.
Setting up the camera
Before you can stream from a
libcamera camera, you should first make sure that the camera functions as expected. In my case, I installed the kernel driver given by ArduCam (see ArduCam instructions above):
wget -O install_pivariety_pkgs.sh https://github.com/ArduCAM/Arducam-Pivariety-V4L2-Driver/releases/download/install_script/install_pivariety_pkgs.sh chmod +x install_pivariety_pkgs.sh ./install_pivariety_pkgs.sh -p imx519_kernel_driver
You can test whether the camera works by installing and running a simple
sudo apt install libcamera-apps-lite libcamera-still
mjpg_streamer with support for
libcamera, I opted to use the fork that ArduCam provided: GitHub - ArduCAM/mjpg-streamer: Fork of http://sourceforge.net/projects/mjpg-streamer/.
I created a Docker image that would download, patch, and build this fork:
FROM debian:bullseye ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y cmake build-essential curl git RUN echo "deb http://archive.raspberrypi.org/debian/ bullseye main" > /etc/apt/sources.list.d/raspi.list RUN curl "https://archive.raspberrypi.org/debian/raspberrypi.gpg.key" | apt-key add - RUN apt-get update && apt-get install -y libcamera-dev libjpeg-dev libgphoto2-dev RUN git clone https://github.com/ArduCAM/mjpg-streamer.git /src WORKDIR /src/mjpg-streamer-experimental RUN sed -i "/input_libcamera/p;/input_/d;/output_http/p;/output_/d" CMakeLists.txt RUN make && make install COPY entrypoint.sh entrypoint.sh ENV INPUT_FLAGS "-r 1920x1080 -f 60" ENV OUTPUT_FLAGS "-w ./www" ENTRYPOINT ["./entrypoint.sh"]
#!/bin/sh input="input_libcamera.so $INPUT_FLAGS" output="output_http.so $OUTPUT_FLAGS" ./mjpg_streamer -i "$input" -o "$output"
Things to note about this Dockerfile:
- The Raspberry Pi repository was added so that the
libcamera-devdependency could be downloaded.
- All inputs other than
input_libcameraand all outputs other than
output_httpwere removed from
- This is primarily to remove the dependency on
libopencv-dev, which would have involved downloading over 700MB of data.
- This is primarily to remove the dependency on
- This image is not hermetic, meaning ArduCam could make changes to their fork that break the image or even introduce nefarious backdoors at any time.
- Always vet any code you compile and run.
To use this image, all
/dev/v4l2* devices need to be passed through to the container. udev also needs to be passed through to the container. In my case, I was lazy and simply ran the container in privileged mode and mounted
Input flags and output flags are defined using the
OUTPUT_FLAGS environment variables.
Adding OctoPrint is very straightforward, since an official image is provided: octoprint/octoprint.
Being lazy yet again, I opted to run OctoPrint in privileged mode as well, but you only need to pass through the printer to the container (
/dev/ttyACM0 in my case for a Prusa Mini+).
To get a couple of plugins to work better, I also mounted:
- CameraSettings (done implicitly through privileged mode)
mjpg_streamer is run in a separate container, the OctoPrint container does not need to be configured with
A couple of other settings that will need to be changed from default for OctoPrint and optionally Octolapse to function correctly:
- Settings > OctoPrint > Server > Commands > Restart OctoPrint
s6-svc -r /var/run/s6/services/octoprint
- This allows OctoPrint to signal to the supervisor to restart.
- Settings > Features > Webcam & Timelapse > Webcam > Stream URL
<LOCAL_IP>:<PORT>should point to your
- Settings > Plugins > Octolapse > Camera > [Your Camera] > Webcam Setup
- Base Address:
- Snapshot Address:
- Stream Address Template:
- Base Address:
docker-compose.yaml I used for the setup was:
version: "3" services: mjpg-streamer: build: context: ./mjpg-streamer dockerfile: Dockerfile restart: always privileged: true ports: - 8080:8080 environment: - INPUT_FLAGS=-r 1920x1080 -f 30 - OUTPUT_FLAGS=-w ./www volumes: - /run/udev:/run/udev:ro octoprint: image: octoprint/octoprint:latest restart: always privileged: true ports: - 80:80 devices: - /dev/ttyACM0:/dev/ttyACM0 volumes: - ./data/octoprint:/octoprint - /proc/cpuinfo:/proc/cpuinfo:ro - /usr/bin/vcgencmd:/usr/bin/vcgencmd:ro - /usr/lib/aarch64-linux-gnu/libvcos.so.0:/usr/lib/aarch64-linux-gnu/libvcos.so.0:ro - /usr/lib/aarch64-linux-gnu/libvchiq_arm.so.0:/usr/lib/aarch64-linux-gnu/libvchiq_arm.so.0:ro
This approach of using Docker to create a separation of concerns between the OS, OctoPrint, and
mjpg_streamer is, in my opinion, a cleaner and better way to run OctoPrint than through the OctoPi image.
Will I create an image for this?
No. That would defeat the entire point.
Will there be degraded performance due to Docker overhead?
Compared to running natively, there will definitely be a non-zero amount of reduced performance. However, I am running this setup on a Raspberry Pi 4B with 2GB RAM, and performance has not been an issue.
In addition to the file contents I've pasted above, all the files that make up this setup are in GitHub - ruiqimao/octoprint-libcamera: libcamera with OctoPrint using Docker.