Technical note

用OctoPrint和Restreamer远程查看3D打印机

OctoPrint已经可以远程控制3D打印机,但手头的网络摄像头只提供RTSP。浏览器不能直接播放RTSP,OctoPrint的Classic Webcam兼容层需要MJPEG流和一张可以单独请求的快照,于是在两者之间增加了Restreamer。

数据链路很简单:摄像头通过RTSP把视频交给Restreamer,Restreamer再向OctoPrint提供浏览器能够访问的流和快照。打印机串口仍然由OctoPrint直接管理,视频转码服务不接触打印控制。

参考

OctoPrint容器

OctoPrint官方镜像需要持久化/octoprint,并把打印机串口映射进容器。下面以常见的/dev/ttyACM0为例,实际设备名可以先通过ls -l /dev/serial/by-id/确认:

services:
  octoprint:
    image: octoprint/octoprint:latest
    container_name: octoprint
    restart: unless-stopped
    ports:
      - "8081:80"
    devices:
      - /dev/ttyACM0:/dev/ttyACM0
    volumes:
      - octoprint-data:/octoprint

volumes:
  octoprint-data:
docker compose up -d
docker compose logs -f octoprint

如果摄像头是USB设备,OctoPrint官方镜像可以映射/dev/video0并启用内置MJPG-Streamer。这里使用的是独立网络摄像头,不需要把视频设备交给OctoPrint容器。

只为RTSP输入启动Restreamer

Restreamer官方示例默认开放HTTP、HTTPS、RTMP、RTMPS和SRT端口。这个场景只从局域网摄像头拉取RTSP,再给OctoPrint提供HTTP地址,因此先开放管理和播放使用的8080端口即可:

services:
  restreamer:
    image: datarhei/restreamer:latest
    container_name: restreamer
    restart: unless-stopped
    ports:
      - "192.0.2.10:8080:8080"
    volumes:
      - ./config:/core/config
      - ./data:/core/data

192.0.2.10是文档示例地址,部署时改成Restreamer主机实际使用的局域网地址。这样不会把8080监听到所有网卡;如果OctoPrint和Restreamer在同一台机器上,也可以只绑定127.0.0.1

mkdir -p config data
docker compose up -d
docker compose logs -f restreamer

容器运行后打开http://<Restreamer主机>:8080/ui,设置独立的管理密码,再通过向导添加网络视频源。RTSP地址和摄像头凭据直接填入Restreamer,不写进Compose文件或OctoPrint配置。

网络RTSP输入不需要访问宿主机摄像头设备,因此也不需要--privileged。早期在LXC中出现过AppArmor与runc权限错误,但把LXC改成unconfined、屏蔽AppArmor状态或给容器放开seccomp都会扩大权限,不适合作为通用解决办法。无法在受限LXC中正常运行时,换成具备标准Docker能力的虚拟机或主机更省事。

找到MJPEG流和快照

Restreamer界面可以复制HLS与Snapshot地址。Snapshot可以直接交给OctoPrint;HLS地址通常以.m3u8结尾,不能直接填进Classic Webcam的Stream URL,因为OctoPrint兼容层要求MJPEG。

当时使用的Restreamer版本还提供了这个MJPEG地址:

http://192.0.2.10:8080/stream.mjpg?type=mjpeg

这个路径没有出现在当前Restreamer文档中,升级后不能默认它仍然存在。填写OctoPrint之前,先检查响应类型:

curl -sS --max-time 5 \
  -D - -o /dev/null \
  'http://192.0.2.10:8080/stream.mjpg?type=mjpeg'

MJPEG响应的Content-Type应为multipart/x-mixed-replace一类的多段图片流。如果返回HTML、JSON、404或普通HLS内容,就不能把它当作OctoPrint视频源;此时应改用明确支持RTSP转MJPEG的服务,而不是把.m3u8硬填进去。

Snapshot地址包含Restreamer生成的通道标识,直接从界面复制,避免把示例中的固定UUID当成自己的地址。可以先确认它返回图片:

curl -sS --max-time 5 \
  -D - -o /dev/null \
  'http://192.0.2.10:8080/memfs/<通道标识>.jpg'

这里的<通道标识>只是占位说明,执行前必须替换。响应的Content-Type应是image/jpeg或其他实际图片类型。

填入OctoPrint

在OctoPrint的Webcam设置中填写:

  • Stream URL:已经确认可用的MJPEG地址;
  • Snapshot URL:Restreamer界面复制出的快照地址;
  • Path to FFMPEG:容器镜像内使用/usr/bin/ffmpeg
  • Stream aspect ratio:按摄像头实际画面选择16:94:3

浏览器和OctoPrint服务端都要能访问这些地址。Stream URL由浏览器加载,而Snapshot通常由OctoPrint在生成延时摄影时请求;只在某一端能解析的主机名会造成“控制页有画面但快照失败”,或反过来。

不把摄像头跟着OctoPrint暴露到公网

OctoPrint的登录权限不会自动保护外部摄像头地址。官方文档明确说明,OctoPrint只是嵌入视频流,知道摄像头URL的人仍可能绕过OctoPrint直接观看。

因此Restreamer和摄像头都只保留在可信网络中,通过VPN访问最简单。确实需要反向代理时,应单独为视频流增加认证和HTTPS,并确认浏览器不会因为OctoPrint页面是HTTPS、视频地址是HTTP而拦截混合内容。摄像头原始RTSP密码一旦出现在日志、截图或旧配置中,也应立即更换。