MiBee Eye 系列此前有两个实现,跑在树莓派这类 Linux 小板上;这次新增的一支面向笔记本、台式机与迷你主机,采集本机的摄像头和麦克风,把手上闲置的电脑变成一台能进监控系统的设备。
场景
办公室里换下来的笔记本,抽屉里放着的迷你主机,性能跑不动什么正经活了,但摄像头、麦克风、网口、硬盘都是齐的。想让它们进监控系统,缺的是一层把本机音视频变成标准设备信号的软件。
已有的做法要么太轻,只能推一路 RTSP 出去;要么太重,直接装一整套 NVR。中间那一层很少见:能自动被 NVR 发现,能注册进国标平台,能录像,还能被平台语音喊话。
MiBee Eye Notebook 做的是这一层。它在 PC 上跑,采集物理插在这台机器上的摄像头和麦克风,把音视频按标准协议送出去,日常操作全在浏览器里完成。
它是什么
一句话:部署在 PC 上的本地音视频采集代理。笔记本、台式机、迷你主机、自助终端都能装,作为系统服务运行。
几点边界值得先说清楚,因为它决定了这个工具适合什么场景:
- 只采本机设备。 USB 摄像头、笔记本内置摄像头、USB 麦克风、内置麦克风都在范围内;网络摄像头的 RTSP/ONVIF 拉流被明确定义为产品红线,永远不做。摄像头没插在这台机器上,就不在范围内。
- 不是 NVR。 它不管第三方摄像头,只录本机设备。
- Web UI 是主控制面。 实时预览、截图、录像开关、协议配置都在浏览器里;配置文件只是给高级用户的逃生口。
- 出站协议默认全关。 不主动暴露任何外部接口,要开哪个由用户在界面上显式打开。
本机内部的数据流是这样的:
#mermaid-svg-A6zVMLRTQvsQekVg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-A6zVMLRTQvsQekVg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-A6zVMLRTQvsQekVg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-A6zVMLRTQvsQekVg .error-icon{fill:#552222;}#mermaid-svg-A6zVMLRTQvsQekVg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-A6zVMLRTQvsQekVg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-A6zVMLRTQvsQekVg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-A6zVMLRTQvsQekVg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-A6zVMLRTQvsQekVg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-A6zVMLRTQvsQekVg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-A6zVMLRTQvsQekVg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-A6zVMLRTQvsQekVg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-A6zVMLRTQvsQekVg .marker.cross{stroke:#333333;}#mermaid-svg-A6zVMLRTQvsQekVg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-A6zVMLRTQvsQekVg p{margin:0;}#mermaid-svg-A6zVMLRTQvsQekVg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-A6zVMLRTQvsQekVg .cluster-label text{fill:#333;}#mermaid-svg-A6zVMLRTQvsQekVg .cluster-label span{color:#333;}#mermaid-svg-A6zVMLRTQvsQekVg .cluster-label span p{background-color:transparent;}#mermaid-svg-A6zVMLRTQvsQekVg .label text,#mermaid-svg-A6zVMLRTQvsQekVg span{fill:#333;color:#333;}#mermaid-svg-A6zVMLRTQvsQekVg .node rect,#mermaid-svg-A6zVMLRTQvsQekVg .node circle,#mermaid-svg-A6zVMLRTQvsQekVg .node ellipse,#mermaid-svg-A6zVMLRTQvsQekVg .node polygon,#mermaid-svg-A6zVMLRTQvsQekVg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-A6zVMLRTQvsQekVg .rough-node .label text,#mermaid-svg-A6zVMLRTQvsQekVg .node .label text,#mermaid-svg-A6zVMLRTQvsQekVg .image-shape .label,#mermaid-svg-A6zVMLRTQvsQekVg .icon-shape .label{text-anchor:middle;}#mermaid-svg-A6zVMLRTQvsQekVg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-A6zVMLRTQvsQekVg .rough-node .label,#mermaid-svg-A6zVMLRTQvsQekVg .node .label,#mermaid-svg-A6zVMLRTQvsQekVg .image-shape .label,#mermaid-svg-A6zVMLRTQvsQekVg .icon-shape .label{text-align:center;}#mermaid-svg-A6zVMLRTQvsQekVg .node.clickable{cursor:pointer;}#mermaid-svg-A6zVMLRTQvsQekVg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-A6zVMLRTQvsQekVg .arrowheadPath{fill:#333333;}#mermaid-svg-A6zVMLRTQvsQekVg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-A6zVMLRTQvsQekVg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-A6zVMLRTQvsQekVg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A6zVMLRTQvsQekVg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-A6zVMLRTQvsQekVg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A6zVMLRTQvsQekVg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-A6zVMLRTQvsQekVg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-A6zVMLRTQvsQekVg .cluster text{fill:#333;}#mermaid-svg-A6zVMLRTQvsQekVg .cluster span{color:#333;}#mermaid-svg-A6zVMLRTQvsQekVg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-A6zVMLRTQvsQekVg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-A6zVMLRTQvsQekVg rect.text{fill:none;stroke-width:0;}#mermaid-svg-A6zVMLRTQvsQekVg .icon-shape,#mermaid-svg-A6zVMLRTQvsQekVg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A6zVMLRTQvsQekVg .icon-shape p,#mermaid-svg-A6zVMLRTQvsQekVg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-A6zVMLRTQvsQekVg .icon-shape .label rect,#mermaid-svg-A6zVMLRTQvsQekVg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A6zVMLRTQvsQekVg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-A6zVMLRTQvsQekVg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-A6zVMLRTQvsQekVg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} USB 或内置摄像头
采集层
V4L2 读帧
H.264 编码
openh264
USB 或内置麦克风
音频编码
G.711 或 AAC
编码帧分发
本机输出
浏览器预览
MP4 分段录像
对外拉流
RTSP 服务
ONVIF 设备端
对外推流
RTMP 推流
国标设备端
默认端口:Web UI 8443(强制 TLS),RTSP 8554,RTMP 1935,SIP 5060。
和树莓派那条线什么关系
同属 MiBee Eye 家族,但针对的机器和场景不一样。三条线都实现同一套协议、同一套 SPEC v1 Web API,接同一批 NVR:
| mibee-eye-go / mibee-eye-rs | 本篇(notebook) | |
|---|---|---|
| 目标机器 | 树莓派等 Linux 小板 | 台式机、笔记本、迷你主机 |
| 摄像头 | CSI 排线模块或 USB 摄像头 | USB 摄像头或笔记本内置 |
| 音频 | 无麦克风采集管线 | 麦克风采集 + 国标语音对讲 |
| 主控制面 | 配置文件为主,Web UI 辅助 | 强制 TLS 的 Web UI |
| 编码 | V4L2 硬件编码或进程内软件编码 | 进程内 openh264 |
| 平台 | Linux(含 armv7) | 目前只有 Linux |
选择很直接:手上有小板就装那两个,手上有闲置 PC 又希望日常用浏览器管,就装这个。
#mermaid-svg-8AZDe381EYaBplro{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8AZDe381EYaBplro .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8AZDe381EYaBplro .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8AZDe381EYaBplro .error-icon{fill:#552222;}#mermaid-svg-8AZDe381EYaBplro .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8AZDe381EYaBplro .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8AZDe381EYaBplro .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8AZDe381EYaBplro .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8AZDe381EYaBplro .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8AZDe381EYaBplro .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8AZDe381EYaBplro .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8AZDe381EYaBplro .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8AZDe381EYaBplro .marker.cross{stroke:#333333;}#mermaid-svg-8AZDe381EYaBplro svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8AZDe381EYaBplro p{margin:0;}#mermaid-svg-8AZDe381EYaBplro .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8AZDe381EYaBplro .cluster-label text{fill:#333;}#mermaid-svg-8AZDe381EYaBplro .cluster-label span{color:#333;}#mermaid-svg-8AZDe381EYaBplro .cluster-label span p{background-color:transparent;}#mermaid-svg-8AZDe381EYaBplro .label text,#mermaid-svg-8AZDe381EYaBplro span{fill:#333;color:#333;}#mermaid-svg-8AZDe381EYaBplro .node rect,#mermaid-svg-8AZDe381EYaBplro .node circle,#mermaid-svg-8AZDe381EYaBplro .node ellipse,#mermaid-svg-8AZDe381EYaBplro .node polygon,#mermaid-svg-8AZDe381EYaBplro .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8AZDe381EYaBplro .rough-node .label text,#mermaid-svg-8AZDe381EYaBplro .node .label text,#mermaid-svg-8AZDe381EYaBplro .image-shape .label,#mermaid-svg-8AZDe381EYaBplro .icon-shape .label{text-anchor:middle;}#mermaid-svg-8AZDe381EYaBplro .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8AZDe381EYaBplro .rough-node .label,#mermaid-svg-8AZDe381EYaBplro .node .label,#mermaid-svg-8AZDe381EYaBplro .image-shape .label,#mermaid-svg-8AZDe381EYaBplro .icon-shape .label{text-align:center;}#mermaid-svg-8AZDe381EYaBplro .node.clickable{cursor:pointer;}#mermaid-svg-8AZDe381EYaBplro .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8AZDe381EYaBplro .arrowheadPath{fill:#333333;}#mermaid-svg-8AZDe381EYaBplro .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8AZDe381EYaBplro .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8AZDe381EYaBplro .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8AZDe381EYaBplro .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8AZDe381EYaBplro .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8AZDe381EYaBplro .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8AZDe381EYaBplro .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8AZDe381EYaBplro .cluster text{fill:#333;}#mermaid-svg-8AZDe381EYaBplro .cluster span{color:#333;}#mermaid-svg-8AZDe381EYaBplro div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8AZDe381EYaBplro .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8AZDe381EYaBplro rect.text{fill:none;stroke-width:0;}#mermaid-svg-8AZDe381EYaBplro .icon-shape,#mermaid-svg-8AZDe381EYaBplro .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8AZDe381EYaBplro .icon-shape p,#mermaid-svg-8AZDe381EYaBplro .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8AZDe381EYaBplro .icon-shape .label rect,#mermaid-svg-8AZDe381EYaBplro .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8AZDe381EYaBplro .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8AZDe381EYaBplro .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8AZDe381EYaBplro :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 要接的是哪种机器
主板加 CSI
或 USB 摄像头
PC 或笔记本
自带摄像头麦克风
mibee-eye-go
或 mibee-eye-rs
mibee-eye-notebook
国标这条线,外加语音对讲
国标设备端的能力来自协议库 gb28181-rs:SIP 注册(摘要认证)、目录与设备信息查询、实时流与回放的 RTP-PS 推送、平台抓拍指令、GB 35114 A 级(SM2 证书注册认证 + keyed-SM3)。实时流是收到平台 INVITE 时动态挂到分发总线上,收到 BYE 再摘掉,不占空转资源。
这条线里有个树莓派那两个实现没有的东西:语音对讲。
平台侧发起一路 audio-only 的 INVITE,设备把它解成 G.711,直接输出到本机扬声器。也就是说,值班的人可以在平台上对着这台 PC 讲话,声音从 PC 的音箱出来。这一项默认开启;如果配置里关掉了,或者机器上根本没有可用的输出声卡,设备对这类 INVITE 回 488,而不是先回 200 再把音频丢掉。
最后那一句是这个项目的取舍风格:宁可明确拒绝,也不假装接通。
音频是怎么走的
麦克风这条链路上有两条编码路径,用途不同:
#mermaid-svg-Exq43a9ydRfeKzmL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Exq43a9ydRfeKzmL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Exq43a9ydRfeKzmL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Exq43a9ydRfeKzmL .error-icon{fill:#552222;}#mermaid-svg-Exq43a9ydRfeKzmL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Exq43a9ydRfeKzmL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Exq43a9ydRfeKzmL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Exq43a9ydRfeKzmL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Exq43a9ydRfeKzmL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Exq43a9ydRfeKzmL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Exq43a9ydRfeKzmL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Exq43a9ydRfeKzmL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Exq43a9ydRfeKzmL .marker.cross{stroke:#333333;}#mermaid-svg-Exq43a9ydRfeKzmL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Exq43a9ydRfeKzmL p{margin:0;}#mermaid-svg-Exq43a9ydRfeKzmL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Exq43a9ydRfeKzmL .cluster-label text{fill:#333;}#mermaid-svg-Exq43a9ydRfeKzmL .cluster-label span{color:#333;}#mermaid-svg-Exq43a9ydRfeKzmL .cluster-label span p{background-color:transparent;}#mermaid-svg-Exq43a9ydRfeKzmL .label text,#mermaid-svg-Exq43a9ydRfeKzmL span{fill:#333;color:#333;}#mermaid-svg-Exq43a9ydRfeKzmL .node rect,#mermaid-svg-Exq43a9ydRfeKzmL .node circle,#mermaid-svg-Exq43a9ydRfeKzmL .node ellipse,#mermaid-svg-Exq43a9ydRfeKzmL .node polygon,#mermaid-svg-Exq43a9ydRfeKzmL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Exq43a9ydRfeKzmL .rough-node .label text,#mermaid-svg-Exq43a9ydRfeKzmL .node .label text,#mermaid-svg-Exq43a9ydRfeKzmL .image-shape .label,#mermaid-svg-Exq43a9ydRfeKzmL .icon-shape .label{text-anchor:middle;}#mermaid-svg-Exq43a9ydRfeKzmL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Exq43a9ydRfeKzmL .rough-node .label,#mermaid-svg-Exq43a9ydRfeKzmL .node .label,#mermaid-svg-Exq43a9ydRfeKzmL .image-shape .label,#mermaid-svg-Exq43a9ydRfeKzmL .icon-shape .label{text-align:center;}#mermaid-svg-Exq43a9ydRfeKzmL .node.clickable{cursor:pointer;}#mermaid-svg-Exq43a9ydRfeKzmL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Exq43a9ydRfeKzmL .arrowheadPath{fill:#333333;}#mermaid-svg-Exq43a9ydRfeKzmL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Exq43a9ydRfeKzmL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Exq43a9ydRfeKzmL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Exq43a9ydRfeKzmL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Exq43a9ydRfeKzmL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Exq43a9ydRfeKzmL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Exq43a9ydRfeKzmL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Exq43a9ydRfeKzmL .cluster text{fill:#333;}#mermaid-svg-Exq43a9ydRfeKzmL .cluster span{color:#333;}#mermaid-svg-Exq43a9ydRfeKzmL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Exq43a9ydRfeKzmL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Exq43a9ydRfeKzmL rect.text{fill:none;stroke-width:0;}#mermaid-svg-Exq43a9ydRfeKzmL .icon-shape,#mermaid-svg-Exq43a9ydRfeKzmL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Exq43a9ydRfeKzmL .icon-shape p,#mermaid-svg-Exq43a9ydRfeKzmL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Exq43a9ydRfeKzmL .icon-shape .label rect,#mermaid-svg-Exq43a9ydRfeKzmL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Exq43a9ydRfeKzmL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Exq43a9ydRfeKzmL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Exq43a9ydRfeKzmL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 麦克风
PCM 采样
G.711 编码
AAC 编码
可选特性
国标实时流
MP4 录像
RTMP 推流
平台语音对讲
G.711 解码
本机扬声器
- G.711 μ-law 是默认路径,纯 Rust 实现,走国标实时流,也是平台对讲接收时用的编码。
- AAC 走 MP4 录像和 RTMP 推流,但它挂在一个默认关闭的编译特性后面(
aac),需要显式打开才会编进二进制。原因是它依赖一个外部 AAC 编码库,而项目整体的取向是尽量少引入重量级组件。
录像这边:MP4 分段落盘,默认 900 秒一段,总容量上限默认 10,240 MB,到顶了按先进先出删最旧的,且只删自己写的文件。切段发生在关键帧边界上,不会出现半截片子。
安全默认值:出站协议一律先关着
这个项目的安全取向比较少见,值得单独说:所有对外协议默认全关。RTSP 服务、RTMP 推流、ONVIF 设备端、国标设备端,装完都是关的,要哪个自己在界面上打开,状态存在 SQLite 里,重启后保留。
其余几条:
- Web UI 强制 TLS,只走 rustls,没有 HTTP 回退,在 localhost 上也不回退。首次运行自动生成自签名证书。
- 管理员口令用 bcrypt 存,会话 cookie 24 小时,带
HttpOnly、Secure、SameSite=Strict。 - 认证接口按 IP 限流,默认 20 次 / 60 秒;登录成功重置计数;连续 5 次失败按用户锁定并指数退避。
- 状态变更请求校验双提交 CSRF,响应带严格 CSP 头。
- 匿名可访问的只有
/health和/metrics两个端点,其余全部要登录。 - 单管理员模型,没有多租户和按用户权限;双因素与双向 TLS 明确不做,v1 的认证就只有强口令这一层。
最后一条是明显的短板,公网直连的部署只能靠强口令加 TLS。定位上它是内网设备,不是给公网暴露用的。
没有 ffmpeg 的编解码栈
早期版本依赖 ffmpeg 子进程做转码,现在这五处调用全部换成了进程内实现:
| 原来的活 | 换成了什么 |
|---|---|
| H.264 编码 | openh264(Cisco,BSD-2,源码内置编译) |
| MJPEG 解码成像素 | jpeg-decoder |
| YUYV 转 JPEG 预览 | jpeg-encoder |
| 音频编码 | G.711 μ-law,或可选 FDK-AAC |
| MP4 分片复用 | muxide,纯 Rust 的 fMP4 复用器 |
换掉之后有两个直接变化:运行时不再需要装 ffmpeg(容器镜像、systemd 单元、裸机安装步骤里都不用),崩溃恢复也不用再靠进程守护。项目自己在变更记录里给的数字是,这一改动消除了每路约 64 MB 的子进程开销。这是项目自述口径,我没有做过第三方对比测试。
浏览器预览也不是靠转码实现的。取流顺序是按设备能力协商的:先试 MSE(分块 HTTP 传分片 MP4),不行退回 MJPEG,再不行退回快照轮询。抓帧是采集循环里维护的一路 JPEG 旁路,MJPG 摄像头直接把原始 JPEG 字节转出去,YUYV 摄像头按帧间隔重新编码一张 JPEG,快照和预览都消费这个旁路。
运维面
这些东西对单机部署其实挺关键:
- 协议热开关:在界面上开关 ONVIF、国标、RTMP 不用重启服务,状态落 SQLite。
- 摄像头热插拔:udev 监听增删,插上的摄像头自动出现,拔掉的标记离线并优雅关闭它的录像输出。
- 实时事件:SSE 通道把摄像头上下线事件推到浏览器,不用手动刷新。
- 可观测:Prometheus 指标 14 个以上;OpenTelemetry 追踪管线接了 132 个 span 标注;日志是结构化 JSON;可选把日志推到 Loki 兼容后端,后端不在也不影响运行。
平台现实与已知短板
- 还没有正式发布。 没有 tag、没有 release,变更记录全部挂在 Unreleased 下,版本号是 0.1.0。没有可对齐的预编译产物和镜像版本,现在只能自己编译。
- 只有 Linux。 Windows 和 macOS 都在计划里,但当前编译不过 :Windows 那边卡在
libc::getifaddrs只有 POSIX 实现、采集代码缺 Windows 分支、路径写死了/dev/videoN;macOS 就算编过,/dev/videoN也不存在,还需要 AVFoundation 那套设备枚举。而这个产品的主要用户画像恰恰是手上有闲置 Windows 笔记本的人,这条缺口不小。 - README 和实现已经对不上。 README 的 crate 行数表是旧值;预览那一节仍写成 ffmpeg 转码,而 ffmpeg 已经移除、现在的主路径是 MSE。
- 资源数字是目标值。 空闲 CPU 低于 5%、内存低于 200 MB、首帧低于 500 ms、并发不超过 16 路,这些都是设计目标,仓库里没有对外可引用的实测数据。这一点和树莓派那两个实现不同,它们的 README 里有带条件的实测记录,不要把两边的数字放一起比。
- 认证只有一层。 单管理员、强口令,没有双因素和双向 TLS。
- 没有云、没有 App、没有推送。 定位就是内网自托管,别指望移动端体验。
- 项目很新。 2026-09-16 建仓开源,1 个 star。维护节奏和响应速度我不写进文章,也不做承诺。
- Rust 工具链要求 1.88、edition 2024,偏新。
上手
Linux 上编译运行:
bash
# 系统依赖
sudo apt install libv4l-dev libasound2-dev libclang-dev
sudo usermod -aG video $USER # 摄像头访问权限,需重新登录生效
git clone https://github.com/xiqing85/mibee-eye-notebook
cd mibee-eye-notebook
cargo build --release
cp config.toml config.local.toml # config.local.toml 已在 gitignore 里
# 编辑录制路径、协议参数、平台地址等
cargo run --release -- --config config.local.toml
几个容易踩的点:首次运行会走一个初始化流程创建管理员账号并生成自签名证书,浏览器会提示证书不可信,这是预期行为;[web] advertised_host 留空会通过 UDP 探测本机局域网 IP,做 NAT 或机器有多个网卡时建议手动指定,否则返回给客户端的拉流地址可能是错的;[capture] video_device 默认 /dev/video0,多摄像头的机器要先确认设备号;忘掉口令可以用 --reset-password 重置。
项目地址
mibee-eye-notebook:https://github.com/xiqing85/mibee-eye-notebook
同族的另外两个实现(面向树莓派等 Linux 小板):
mibee-eye-go:https://github.com/xiqing85/mibee-eye-go
mibee-eye-rs:https://github.com/xiqing85/mibee-eye-rs
协议层来自这两个库:
onvif-rs:https://github.com/mickeyzzc/onvif-rs(crate 名 onvif-device-rs)
gb28181-rs:https://github.com/mickeyzzc/gb28181-rs
许可:Apache-2.0,可商用。协议库 onvif-rs 与 gb28181-rs 均为 MIT。