motionEye 0.44.0 Docker Compose 部署指南

motionEyemotionEyeNVR摄像头Docker视频监控2026/10/29

motionEye 0.44.0 Docker Compose 部署指南

目标:在一台 Ubuntu 22.04 服务器上用 Docker Compose 从零部署 motionEye 0.44.0 NVR,并在无真实摄像头环境下用 ffmpeg 生成的 MJPEG 模拟网络摄像头完成「接入 → 实时画面 → 运动侦测 → 录像 → 快照 → 时间轴」全链路实测。所有命令与回显均为部署实测过程的真实记录。

一、部署目标与方案设计

1.1 目标拓扑

本次部署共启动两个容器:motionEye 服务本体,以及一路 ffmpeg 模拟网络摄像头源。整体拓扑如下:

┌──────────────────────┐        ┌────────────────────────────────┐
│ motioneye-0.44.0-camsrc │  MJPEG │ motioneye-0.44.0-nvr            │
│ ffmpeg testsrc 模拟源  │ ─────► │ motionEye 0.44.0 + motion 4.7.1 │
│ 容器内 8090 端口       │ 容器网 │ 容器内 8765 端口                 │
└──────────┬───────────┘        └───────────────┬────────────────┘
           │ 8151→8090(宿主验证用)              │ 8150→8765
           ▼                                    ▼
   宿主 8151 端口(可直连看流)          用户浏览器 http://<宿主IP>:8150

设计要点:motionEye 访问摄像头源走容器网络内网(motioneye-0.44.0-camsrc:8090),不绕行宿主端口;8151→8090 映射仅供部署者从宿主侧验证流是否正常,真实摄像头接入时此容器整体省略。

1.2 端口、目录与网络规划

项取值说明
Web 端口宿主 8150 → 容器 8765motionEye 官方镜像内固定监听 8765,宿主侧按需重映射
模拟源端口宿主 8151 → 容器 8090ffmpeg mpjpeg 流,仅验证用
配置目录./config → /etc/motioneyemotioneye.conf、motion.conf、camera-N.conf
媒体目录./media → /var/lib/motioneye录像/抓图落盘
容器网络motioneye-0.44.0-net(bridge,10.89.60.0/24)专用网络隔离摄像头源流量

1.3 硬件前置说明

⚠️ 硬件前置:motionEye 是纯软件 NVR,必须接入外部视频源才能工作。真实环境的三类常见接入(网络摄像机 RTSP、USB 摄像头 V4L2、远端 motionEye 互联)在部署层面与本文完全一致,唯一差别是添加摄像头时填写的流地址与协议。本文环境无真实摄像头,全程使用 ffmpeg testsrc 生成的 MJPEG 网络摄像头模拟——它输出的测试画面持续变化,恰好能稳定触发运动侦测,适合把整条链路验证完整;但画质(640x480)、编码(MJPEG 帧序列)与真实硬件(H.264/H.265)不同,测得的 CPU/带宽数据不能直接外推到真实摄像头。

二、环境准备

2.1 Docker 环境检查

docker --version && docker compose version

实测回显:

Docker version 27.5.1, build 9f9e405
Docker Compose version v2.38.2

2.2 目录规划

约定部署根目录为 motioneye/,其下三个子目录分工如下(config/ 与 media/ 由 bind mount 挂进容器,数据全部留在宿主):

mkdir -p motioneye/{compose,config,media} && find motioneye -maxdepth 1 -type d

实测回显:

motioneye
motioneye/compose
motioneye/config
motioneye/media

2.3 获取官方镜像

docker pull ghcr.io/motioneye-project/motioneye:0.44.0

实测回显(本地已有同版本镜像时为 up to date;首次拉取会逐层显示下载进度):

0.44.0: Pulling from motioneye-project/motioneye
Digest: sha256:7da31d62e6c46ee8b61280f872801e429ec6f3db81bb23aca11c83863939cade
Status: Image is up to date for ghcr.io/motioneye-project/motioneye:0.44.0
ghcr.io/motioneye-project/motioneye:0.44.0

💡 该官方镜像内已集成 motionEye 0.44.0、motion 4.7.1 与 ffmpeg 7.1.4,实测核验:

docker exec motioneye-0.44.0-nvr meyectl -v && docker exec motioneye-0.44.0-nvr motion -h | head -1 && docker exec motioneye-0.44.0-nvr ffmpeg -version | head -1

实测回显:

motionEye 0.44.0
motion Version 4.7.1, Copyright 2000-2024 Jeroen Vreeken/Folkert van Heusden/Kenneth Lavrsen/Motion-Project maintainers
ffmpeg version 7.1.4-0+deb13u1 Copyright (c) 2000-2026 the FFmpeg developers

三、编写 docker-compose.yml

3.1 完整文件

在 compose/ 目录下创建 docker-compose.yml(全文照抄即可,见 3.2 的逐段解说):

name: motioneye-0.44.0-stack

services:
  motioneye:
    image: ghcr.io/motioneye-project/motioneye:0.44.0
    container_name: motioneye-0.44.0-nvr
    restart: unless-stopped
    ports:
      - "8150:8765"
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - ../config:/etc/motioneye
      - ../media:/var/lib/motioneye
    networks:
      - motioneye-0.44.0-net

  camsrc:
    image: ghcr.io/motioneye-project/motioneye:0.44.0
    container_name: motioneye-0.44.0-camsrc
    restart: unless-stopped
    entrypoint: ["/bin/sh", "-c"]
    command:
      - >
        ffmpeg -hide_banner -re -f lavfi
        -i "testsrc=size=640x480:rate=15"
        -c:v mjpeg -q:v 6
        -f mpjpeg -boundary_tag motioneye -listen 1 http://0.0.0.0:8090/cam-a
    ports:
      - "8151:8090"
    networks:
      - motioneye-0.44.0-net

networks:
  motioneye-0.44.0-net:
    name: motioneye-0.44.0-net
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.60.0/24

3.2 逐段解说

  • name: motioneye-0.44.0-stack:固定 compose 项目名,避免多套部署互相干扰;container_name 同理逐个固定,便于日志与命令定位。
  • motioneye 服务:即 NVR 本体。ports 把容器内固定监听的 8765 重映射到宿主 8150(按宿主空闲端口自行调整);volumes 两条 bind mount 把配置与媒体数据全部留在宿主目录,容器可随时重建。
  • camsrc 服务:复用 motionEye 官方镜像(省一份下载,且镜像内自带 ffmpeg),仅以 entrypoint 覆盖的方式跑一条 ffmpeg 命令充当摄像头源。-re 让画面按真实时间节奏输出(模拟摄像头帧率);testsrc 是 ffmpeg 内置测试画面(滚动的彩条与秒计数器);-f mpjpeg -boundary_tag motioneye -listen 1 使 ffmpeg 作为 HTTP 服务器在 8090 端口提供 multipart MJPEG 流,流路径为 /cam-a。
  • networks:显式创建专用 bridge 网络,并以 ipam 固定子网段(本文为 10.89.60.0/24),多套监控栈共存时可按网段区分。真实摄像头环境直接删除整个 camsrc 服务即可,motionEye 容器访问局域网内 IPC 的 RTSP 地址不受影响。

3.3 关键参数速查

参数位置作用
8150:8765motioneye.portsWeb 界面入口,宿主侧访问 http://<宿主IP>:8150
TZ=Asia/Shanghaimotioneye.environment媒体目录按日期分组使用时区,不设则 UTC 分组
../config:/etc/motioneyemotioneye.volumes持久化全部配置文件
../media:/var/lib/motioneyemotioneye.volumes持久化录像/抓图媒体
restart: unless-stopped两个服务容器异常退出自动拉起(模拟源靠它自愈,见 10.4)

四、启动服务与初始化

4.1 启动并确认状态

cd compose && docker compose up -d

实测回显(首次创建为 Creating/Created + Starting/Started 序列;已存在时容器显示 Running):

 Network motioneye-0.44.0-net Creating
 Network motioneye-0.44.0-net Created
 Container motioneye-0.44.0-nvr Creating
 Container motioneye-0.44.0-camsrc Creating
 Container motioneye-0.44.0-nvr Created
 Container motioneye-0.44.0-camsrc Created
 Container motioneye-0.44.0-camsrc Starting
 Container motioneye-0.44.0-nvr Starting
 Container motioneye-0.44.0-camsrc Started
 Container motioneye-0.44.0-nvr Started
docker compose ps

实测回显:

NAME                      IMAGE                                        COMMAND                  SERVICE     CREATED                  STATUS                  PORTS
motioneye-0.44.0-camsrc   ghcr.io/motioneye-project/motioneye:0.44.0   "/bin/sh -c 'ffmpeg …"   camsrc      Less than a second ago   Up Less than a second   8765/tcp, 0.0.0.0:8151->8090/tcp, [::]:8151->8090/tcp
motioneye-0.44.0-nvr      ghcr.io/motioneye-project/motioneye:0.44.0   "/entrypoint.sh"         motioneye   Less than a second ago   Up Less than a second   0.0.0.0:8150->8765/tcp, [::]:8150->8765/tcp

4.2 服务日志检查

docker logs motioneye-0.44.0-nvr 2>&1 | head -6

实测回显:

configure_logging cmd motioneye: False
configure logging to file: None
    INFO: hello! this is motionEye server 0.44.0
    INFO: cleanup started
    INFO: wsswitch started
    INFO: tasks started

cleanup/wsswitch/tasks 三个后台任务就绪即代表服务启动完成。确认 Web 端口可访问:

curl -s -o /dev/null -w 'http_code=%{http_code}\n' http://127.0.0.1:8150/

实测回显:

http_code=200

4.3 首次登录与设置密码(实测实录)

motionEye 首次访问时 admin 用户无密码,此时未认证的登录探测返回 unauthorized:

curl -s http://127.0.0.1:8150/login/

实测回显:

{"error": "unauthorized", "prompt": true}

浏览器打开 http://<宿主IP>:8150 会弹出登录对话框。首次必须以空密码登录,服务端返回强制改密标记:

curl -s -c cookies.txt -X POST -d 'username=admin&password=' http://127.0.0.1:8150/login/

实测回显:

{"user": "admin", "force_password_change": true}

按界面提示分别设置管理员(admin)与普通用户(user)的密码后重新登录。本文演示统一使用 admin / q1w2e3r4。密码不落明文,以 argon2id 哈希写进宿主 config/motion.conf:

grep '^# @admin_password' config/motion.conf | cut -c1-60

实测回显:

# @admin_password $argon2id$v=19$m=65536,t=3,p=4$E3Xs/FuOAq5sh

⚠️ 注意:0.44.0 在「未设密码」状态下提交非空密码会直接 400(本节实测见排障实录 10.1),不要在首次登录框里预填新密码,先空密码进入再按向导设置。

4.4 验证会话与配置可读

用新密码重新登录并读取全局配置列表:

curl -s -c cookies.txt -X POST -d 'username=admin&password=q1w2e3r4' http://127.0.0.1:8150/login/ && echo && curl -s -b cookies.txt http://127.0.0.1:8150/config/list/

实测回显:

{"user": "admin"}
{"cameras": [{"name": "Camera1", "enabled": true, "id": 1, "proto": "mjpeg", "url": "http://motioneye-0.44.0-camsrc:8090/cam-a", "admin_only": false, "actions": []}, {"name": "Camera2", "enabled": true, "id": 2, "admin_only": false, "auto_brightness": false, "framerate": 2, "rotation": 0, ...

返回了已添加的两路摄像头摘要(Camera1 简单 MJPEG 形态带 proto/url;Camera2 为 motion 本地摄像头,带 framerate/rotation 等完整界面配置),会话与配置读取正常。

五、模拟网络摄像头源(无摄像头环境)

5.1 模拟源方案说明

真实摄像头接入 motionEye 的三种主流形态(RTSP/H.264、HTTP/MJPEG、V4L2)中,HTTP MJPEG 是「一条 ffmpeg 命令就能凭空造出一台摄像头」的最简形态:ffmpeg 的 lavfi 虚拟设备生成 testsrc 测试画面,转成 MJPEG 帧序列后以 mpjpeg(multipart JPEG over HTTP)格式对外提供。motion 的 netcam 模块原生消费这种流,motionEye 界面上它就是一台标准的「网络摄像头」。

5.2 容器内源进程核验

官方镜像内无 ps 工具,用 /proc 命令行核验 ffmpeg 源进程:

docker exec motioneye-0.44.0-camsrc sh -c 'for p in /proc/[0-9]*/cmdline; do tr "\0" " " < $p; echo; done' | grep ffmpeg | head -1

实测回显:

ffmpeg -hide_banner -re -f lavfi -i testsrc=size=640x480:rate=15 -c:v mjpeg -q:v 6 -f mpjpeg -boundary_tag motioneye -listen 1 http://0.0.0.0:8090/cam-a

5.3 从宿主验证流可用

curl -s -m 3 -D - http://127.0.0.1:8151/cam-a -o /dev/null

实测回显:

HTTP/1.1 200 OK
Content-Type: application/octet-stream
Transfer-Encoding: chunked

抓取流前 120 字节确认 MJPEG 帧结构(--motioneye 边界 + image/jpeg 子段):

curl -s -m 2 http://127.0.0.1:8151/cam-a | head -c 120 | od -c | head -4

实测回显:

0000000    -   -   m   o   t   i   o   n   e   y   e  \r  \n   C   o   n
0000020    t   e   n   t   -   t   y   p   e   :       i   m   a   g   e
0000040    /   j   p   e   g  \r  \n   C   o   n   t   e   n   t   -   l
0000060    e   n   g   t   h   :       1   6   8   7   7  \r  \n  \r  \n

流结构正确,可以接入 motionEye。

六、添加摄像头(两种形态实测)

6.1 形态一:简单 MJPEG 摄像头(仅转发画面)

Web 界面添加对话框里选「简单 MJPEG 摄像头」并填 http://motioneye-0.44.0-camsrc:8090/cam-a(容器网络内直连,无认证)。命令行等价操作:

curl -s -b cookies.txt -H 'Content-Type: application/json' \
  -d '{"proto":"mjpeg","scheme":"http","host":"motioneye-0.44.0-camsrc","port":8090,"path":"/cam-a"}' \
  http://127.0.0.1:8150/config/add/ | head -c 120

实测回显:

{"name": "Camera1", "enabled": true, "id": 1, "admin_only": false, "auto_brightness": false, ...

注意这种形态 motionEye 不把画面交给 motion 引擎,仅在自身 Web 服务里转发 MJPEG,因此没有运动侦测/录像/抓图能力(实测差异见 7.1)。

6.2 形态二:网络摄像头(motion 引擎接入,全功能)

Web 界面添加对话框里选「网络摄像头」,协议选 http(源为 RTSP 时选 rtsp:// 并填 IPC 地址)。命令行等价操作:

curl -s -b cookies.txt -H 'Content-Type: application/json' \
  -d '{"proto":"netcam","scheme":"http","host":"motioneye-0.44.0-camsrc","port":8090,"path":"/cam-a","username":"","password":""}' \
  http://127.0.0.1:8150/config/add/ | head -c 120

实测回显:

{"name": "Camera2", "enabled": true, "id": 2, "admin_only": false, "auto_brightness": false, ...

添加成功后 motionEye 自动拉起 motion 守护进程(容器内精简镜像无 ps,读内核进程表核验),宿主 config/ 下同步生成 motion.conf 与 camera-2.conf:

docker exec motioneye-0.44.0-nvr sh -c 'for p in /proc/[0-9]*/cmdline; do tr "\0" " " < $p; echo; done' | grep '/usr/bin/motion' && ls config/

实测回显:

/usr/bin/motion -n -c /etc/motioneye/motion.conf -d 5
camera-1.conf
camera-2.conf
motion.conf
motioneye.conf

motion 日志确认摄像头线程加载(服务启动即重读配置文件并初始化 motion):

docker exec motioneye-0.44.0-nvr tail -5 /var/log/motioneye/motion.log

实测回显:

[0:motion] [NTC] [ALL] config_camera: Processing camera config file camera-2.conf
[0:motion] [NTC] [ALL] motion_startup: Logging to syslog
[0:motion] [NTC] [ALL] motion_startup: Motion 4.7.1 Started
[0:motion] [NTC] [ALL] motion_startup: Using default log type (ALL)
[0:motion] [NTC] [ALL] motion_startup: Using log type (ALL) log level (WRN)

6.3 两种形态对比

对比项简单 MJPEG(Camera1)网络摄像头(Camera2)
实时画面✔(motionEye 转发)✔(motion 内置 MJPEG 流,默认端口 9082)
当前帧接口 /picture/N/current/✘(HTTP 400,见 7.1)✔
运动侦测✘✔
录像/抓图✘✔
motion 线程不占用占用一路 camera 线程

七、功能逐项实测

7.1 实时画面

Camera2(motion 本地摄像头)当前帧接口实测——返回一张真实 JPEG:

curl -s -b cookies.txt -o frame2.jpg -w 'http_code=%{http_code} type=%{content_type} bytes=%{size_download}\n' http://127.0.0.1:8150/picture/2/current/ && file frame2.jpg | cut -c1-60

实测回显:

http_code=200 type=image/jpeg bytes=18664
frame2.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1

Camera1(简单 MJPEG)同接口实测返回 400(形态差异,非故障):

curl -s -b cookies.txt http://127.0.0.1:8150/picture/1/current/

实测回显:

{"error": "unknown operation"}

7.2 运动侦测

testsrc 画面持续变化,motion 侦测连续触发。当前帧接口的响应 Cookie 直接携带侦测状态:

curl -s -D - -o /dev/null -b cookies.txt http://127.0.0.1:8150/picture/2/current/ | grep -i motion_detected

实测回显:

Set-Cookie: motion_detected_2=true; Path=/

motion 自身的 HTTP 控制接口(webcontrol,容器内 7999 端口)也可核验侦测开关与阈值:

docker exec motioneye-0.44.0-nvr curl -s "http://127.0.0.1:7999/0/detection/status" && docker exec motioneye-0.44.0-nvr curl -s "http://127.0.0.1:7999/0/config/get?query=threshold"

实测回显:

Camera 1 Detection status ACTIVE
Camera 0
threshold = 1500
Done

💡 说明:webcontrol 编号从 0 起计(0 号即 motion.conf 主线程引述的第一路摄像头),与 motionEye 界面的摄像头 ID(从 1 起)编号不同。threshold = 1500 即帧差分判定阈值:一帧内变化像素超过 1500 判为运动。

7.3 录像

先在界面上把 Camera2 的「录像模式」设为运动触发、格式选 mp4(H.264 软编码),命令行验证配置落盘:

grep -E '^(movie_output|movie_codec|movie_format)' config/camera-2.conf

实测回显:

movie_output on
movie_codec libx264
movie_format mp4

侦测触发后媒体目录自动按「摄像头名/日期/时分秒」落盘录像:

find media -type f -name '*.mp4' -exec ls -la {} \;

实测回显:

-rw-r--r-- 1 1000 1000 786011 10月29日 20:49 media/Camera2/2026-10-29/20-49-29.mp4

用 ffmpeg 读取录像文件元数据,核验封装与编码:

ffmpeg -hide_banner -i media/Camera2/2026-10-29/20-49-29.mp4 2>&1 | grep -E 'Duration|Stream #'

实测回显:

  Duration: 00:00:37.51, start: 0.000000, bitrate: 454 kb/s
  Stream #0:0[0x1](und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 640x480, 454 kb/s, 2.03 fps, 2 tbr, 16384 tbn (default)

7.4 快照

手动快照(Web 界面为摄像头画面右下角「抓拍」按钮):

curl -s -b cookies.txt -X POST http://127.0.0.1:8150/action/2/snapshot && sleep 4 && find media -type f -name '*.jpg' -exec ls -la {} \;

实测回显:

OK
-rw-r--r-- 1 1000 1000 26140 10月29日 20:50 media/Camera2/2026-10-29/20-50-05.jpg
-rw-r--r-- 1 1000 1000 25657 10月29日 20:50 media/Camera2/2026-10-29/20-50-31.jpg

另外两种快照来源同样有效:motion webcontrol 的 POST /0/action/snapshot(实测返回 200)与摄像头配置里的「间隔快照」。

7.5 时间轴媒体列表

motionEye 的时间轴页面按摄像头与日期聚合展示媒体,后端接口为媒体列表(图片)与录像列表两组:

curl -s -b cookies.txt "http://127.0.0.1:8150/picture/2/list/" | python3 -m json.tool | head -18

实测回显:

{
    "mediaList": [
        {
            "path": "2026-10-29/20-50-31.jpg",
            "momentStr": "2026-10-29 20:50",
            "details": {
                "sizeKb": 26,
                "timestamp": 1769667031,
                "frameTypes": "stream",
                "width": 640,
                "height": 480
            },
            "groupKey": "20:50",
            "type": "picture"
        },
        ...
curl -s -b cookies.txt "http://127.0.0.1:8150/movie/2/list/" | head -c 420

实测回显(两条录像,可见 mimeType/momentStr/sizeStr 等展示字段):

{"mediaList": [{"path": "/2026-10-29/20-50-36.mp4", "mimeType": "video/mp4", "momentStr": "29 October 2026, 21:01", "momentStrShort": "29 Oct, 21:01", "sizeStr": "35.5 MB", "timestamp": 1793278894.5664306}, {"path": "/2026-10-29/20-49-29.mp4", "mimeType": "video/mp4", "momentStr": "29 October 2026, 20:50", "momentStrShort": "29 Oct, 20:50", "sizeStr": "2.0 MB", "timestamp": 1793278219.424825}, {"path": "/2026-10-29/2...

时间轴页面上按 momentStr(时刻)分组平铺缩略图与录像卡片,点击可直接回放(/movie/2/playback/2026-10-29/20-49-29.mp4 返回视频流)。

八、数据持久化与备份

8.1 目录结构核验

两个 bind mount 卷的宿主侧内容(配置 + 媒体全部落宿主):

find config -type f | sort && echo '---' && find media -type f | sort

实测回显:

config/camera-1.conf
config/camera-2.conf
config/motion.conf
config/motioneye.conf
---
media/Camera2/2026-10-29/20-49-29.mp4
media/Camera2/2026-10-29/20-49-29.mp4.thumb
media/Camera2/2026-10-29/20-50-05.jpg
media/Camera2/2026-10-29/20-50-31.jpg

备份只需打包这两个宿主目录;此外 motionEye 自带配置备份接口(界面:设置 → 常规 → 保存/加载配置文件):

curl -s -b cookies.txt -o backup.tar.gz -w 'http_code=%{http_code} type=%{content_type} bytes=%{size_download}\n' http://127.0.0.1:8150/config/backup/ && tar tzf backup.tar.gz

实测回显:

http_code=200 type=application/x-compressed bytes=1528
motion.conf
camera-2.conf
camera-1.conf

恢复时在界面上传该 tar 包即可整体还原摄像头配置(对应接口 /config/restore/)。

8.2 媒体清理

motionEye 默认按「保留天数/磁盘占用百分比」自动清理媒体(设置 → 媒体 → 保留策略);也可在宿主侧用 find media -mtime +30 -delete 之类脚本自行清理,两者择一即可,避免叠加。

九、升级与卸载

9.1 升级流程

镜像 tag 即版本号,升级 = 改 tag → 拉取 → 重建(配置/媒体在宿主卷中不受影响):

docker compose pull motioneye && docker compose up -d motioneye

实测回显(当前已是最新版 0.44.0 时,拉取仅刷新元数据、容器无变化):

 Image ghcr.io/motioneye-project/motioneye:0.44.0 Pulling
 Image ghcr.io/motioneye-project/motioneye:0.44.0 Pulled
 Container motioneye-0.44.0-nvr Running

升级后有新版本时,up -d 会显示 Recreate/Started 序列完成容器替换;配置与媒体在宿主卷中不受影响。升级后核对版本:

docker exec motioneye-0.44.0-nvr meyectl -v

实测回显:

motionEye 0.44.0

9.2 卸载与数据处置

docker compose down -v && cd .. && rm -rf config media compose

说明:down -v 移除容器与 compose 网络;最后一步删除宿主数据目录,执行前确认媒体不再需要。仅停用不删数据时用 docker compose down。

十、常见问题排障实录

10.1 首次登录提交非空密码返回 400

现象:首次登录框直接填入自定义密码提交:

curl -s -X POST -d 'username=admin&password=q1w2e3r4' http://127.0.0.1:8150/login/

实测回显:

{"error": "invalid credentials"}

原因:0.44.0 在 admin 无密码状态下只接受空密码认证,认证通过后返回 force_password_change 引导设置密码(见 4.3)。处置:先空密码登录,再按向导设置;已设密码后此现象不复现。

10.2 简单 MJPEG 摄像头没有当前帧接口

现象:Camera1 访问 /picture/1/current/ 返回 {"error": "unknown operation"}(见 7.1),服务日志:

[config] 20:51:24 ERROR: HTTP 400: Bad Request (unknown operation)

原因与处置:简单 MJPEG 形态仅做画面转发,实时画面由前端直连其 MJPEG 流。需要侦测/录像/抓图时,按 6.2 用「网络摄像头」形态重新添加。

10.3 修改摄像头配置返回错误

现象:只提交增量字段修改摄像头配置:

curl -s -b cookies.txt -H 'Content-Type: application/json' -d '{"still_images":true,"movies":true}' http://127.0.0.1:8150/config/2/set/

实测回显:

{"error": "internal server error"}

原因与处置:/config/<id>/set/ 不是增量合并语义,motionEye 按「整份 UI 配置」重建摄像头,缺字段的请求在服务端处理链上直接失败。正确做法是先 GET /config/2/get/ 取全量,在其 JSON 上改目标字段后整体提交:

curl -s -b cookies.txt http://127.0.0.1:8150/config/2/get/ > cam2.json \
  && python3 -c "import json;d=json.load(open('cam2.json'));d.update({'still_images':True,'movies':True,'movie_format':'mp4:libx264','capture_mode':'motion-triggered'});json.dump(d,open('cam2_set.json','w'))" \
  && curl -s -b cookies.txt -H 'Content-Type: application/json' --data @cam2_set.json http://127.0.0.1:8150/config/2/set/ | head -c 80

实测回显:

{"success": true}

⚠️ 浏览器操作不受影响(界面提交本就是全量配置),此坑仅影响直接调用 HTTP 接口的自动化脚本。

10.4 模拟源容器运行一段时间后自动重启

现象:docker compose ps 中 camsrc 容器 CREATED 24 minutes ago 但 STATUS Up 6 minutes——中间发生过重启。

原因:ffmpeg ... -listen 1 作为单次 HTTP 服务,部分客户端断开(如 curl 无参数超时截断)后会退出进程,容器随之停止;restart: unless-stopped 将其拉回。处置:保持 restart 策略即可(流会自动恢复,motionEye 侧表现为短暂「摄像头离线」后自动恢复);更稳的写法是在 command 外再包一层循环:

    command:
      - >
        while true; do
          ffmpeg -hide_banner -re -f lavfi
          -i "testsrc=size=640x480:rate=15"
          -c:v mjpeg -q:v 6
          -f mpjpeg -boundary_tag motioneye -listen 1 http://0.0.0.0:8090/cam-a
          || sleep 1;
        done

10.5 宿主端口 8765 冲突

现象:docker compose up -d 报端口绑定失败。原因:8765 是 motionEye 容器内固定端口,与其他监控软件(如 Home Assistant 默认 8123)无冲突,但若宿主已有进程占用重映射端口会失败。处置:先用 ss -ltn | grep <端口> 确认宿主端口空闲,再调整 compose 里的映射;容器内 8765 不需要改动。

十一、参考链接