家里有两只虎斑猫,aki 和 kuro。上一篇把客厅的 Tapo C210 接进 Frigate,做出了录影回看、猫咪日记和每日精华影片。但 C210 挂在墙上拍客厅全景,喂食区刚好在它的死角。
我想找一台同时有摄影机和自动喂食功能的装置,补上这个视角。看起来很简单:一台宠物喂食器摄影机,能把影像串流接进 Frigate 就好。
结果我研究了十个品牌,才搞清楚一件事:这个产品品类里没有任何一台开放影像串流。不是某几台不支援 ,是整个品类结构上就没有。
每一台都锁云端,这不是巧合#
最先看的是米家智慧宠物喂食器 2 可视版。2026 年 3 月众筹上市,500 万画素、150° 超广角、940nm 红外夜视、5L 粮仓、还有秤重功能。而且 的原始码里,xiaomi 来源的装置过滤条件明确包含 .feeder.——看起来完美。
但读完 go2rtc 的 xiaomi 模组原始码才发现问题:它的影像传输只有两条路,小米自家的云端中继(CS2)或 ThroughTek Kalay(TUTK)P2P,全都要经过云端。不是「金钥交换要连网、影像走区网」,是连影像本身都走云端。挡掉外网,串流直接断。
对照我已经在用的 C210:Tapo 的 RTSP 是纯区网的,路由器断了外网,录影侦测全部照常。小米的摄影机在架构上跟它不是同一种东西。
IMPORTANT
后来在 go2rtc 社群翻到过一个重要的观察:go2rtc 的 xiaomi 来源支援清单里有 50 几台摄影机的回报,零台喂食器。作者自己也说过:「If a camera isn't on the list, don't ask if it works. No one knows until you try it.」
台湾官方只卖无镜头的标准版;可视版是中国机(人民币 449 ≈ NT$2,000),要淘宝或代购,没有保固。加上小米的资安纪录——2020 年有人在 Google Nest Hub 上看到别人家里的小米摄影机画面、2021 年 ThroughTek Kalay 被揭露 CVSS 9.6 的漏洞(影响约 8,300 万台装置)——我决定不买。
PETKIT + LocalKit:去云端化的代价#
接着找到了 YumShare Dual Gen2(P592)。它本身不支援本地串流,但 GitHub 上有一个叫 LocalKit 的第三方专案,宣称能把 PETKIT 装置完全去云端化,包含摄影机串流。
看了部署文件之后放弃了。它需要的东西:
- DNS 改写——把 PETKIT 的云端网域导向本地伺服器,让装置以为连到了原厂
- 凭证信任修改——装置有 cert pinning,要进去改凭证信任链
- 本地 MQTT broker——跑一个容器模拟 PETKIT 的云端 broker
- 装置上跑 go2rtc——每台有镜头的装置自己跑一份 go2rtc
- 韧体锁版本 895——安装脚本只认特定韧体版本,不能让 PETKIT 自动更新
而且这是一个人在维护的专案。Tapo 的 RTSP 是原厂支援的功能,PETKIT 的本地串流是踩在原厂疏忽上的临时立足点——原厂一推 OTA 就可能整套报废。
P592 台湾售价大约 NT$5,000。花这个钱买一台要锁韧体、搞 DNS 改写的喂食器,感觉方向就不对。
结构性原因:为什么没有厂商愿意开放#
研究完前几台之后,我开始理解为什么这整个品类都不开放影像串流。
摄影机品牌(Tapo、Reolink、EZVIZ、Imou)靠硬体赚钱,开放 RTSP 是增加卖点。喂食器品牌靠的是 App 黏着度和云端订阅——云端储存、AI 宠物辨识、远端观看,全部绑在自家 App 里。开放串流等于把使用者推给第三方 NVR,订阅就没人买了。
NOTE
连自家有 RTSP 传统的摄影机厂商,跨到喂食器品类也收掉了。Imou(大华消费线)的 PF1 喂食器摄影机,我找了三个来源都没有 RTSP 的证据:iSpy 的 Imou 装置表没有 PF1、HA 的 imou_life 整合零个相关 issue、Frigate 社群零回报。而且台湾没有通路。
买之前怎么知道:拆 APK 看影像 SDK#
既然卖场页面不会写「本机支援 RTSP」,要怎么在买之前判断一台喂食器的影像能不能接出来?
方法其实很简单:下载它的 App 安装档,解压缩看里面用了哪家影像 SDK。
本质上是一个 ZIP 档,lib/ 资料夹里的原生函式库(.so 档)会透露它用了哪家的影像传输 SDK。不需要反编译,不需要安装,只要看档名:
| 看到这些函式库 | 代表用了 | 能接进 Frigate 吗 |
|---|---|---|
libtuya_*.so | 涂鸦(Tuya) | go2rtc 有 tuya://,但信令走云端 |
libmiot*.so、libmiss*.so | 米家 | go2rtc 有 xiaomi://,影像强制走云端 |
libIOTCAPIs.so、libAVAPIs.so | ThroughTek Kalay(TUTK) | go2rtc 没有 tutk,要自己逆 |
libagora-*.so | 声网(Agora) | 商用云端 RTC,完全没有本地路径 |
光「看到 TUTK 函式库」还不够。关键是 App 有没有呼叫 SDK 里的区网直连功能——有些 App 虽然包了 TUTK SDK,但只走云端中继,根本不尝试区网。这个要再深一层看 dex 位元组码里的呼叫,不过对判断「完全没机会」vs「可能有机会」已经足够。
这就是零成本的买前筛选#
我用这个方法扫了十个台湾买得到(或可代购)的品牌:
展開
| 品牌 | App 套件名 | 影像 SDK | App 有呼叫区网直连 | 判断 |
|---|---|---|---|---|
| FurSpark | com.ddpai.cpp_overseas | TUTK Kalay | 有(12 处) | ✅ 有机会 |
| Dogness 多尼斯 | — | TUTK Kalay | 有(17 处) | ✅ 有机会 |
| CATLINK | — | TUTK Kalay | 有(15 处) | 台湾只卖无镜头款 |
| 小米米家 | — | TUTK + CS2 | 有 | 影像强制走云端 |
| PETLIBRO | — | TUTK | 没有 | 只走云端中继 |
| Petwant | — | TUTK | 没有 | 只走云端中继 |
| PETKIT | — | Agora RTC | — | App 侧无本地路径 |
| Petoneer | — | Agora RTC | — | 同上 |
| Hama Home(德国) | — | Tuya | — | App 底层 100% Tuya |
| maoxga 喵享家 | — | Agora RTC | — | 镜头确定接不进 |
有几个意外发现:
- PETKIT 用的是 Agora,不是 TUTK。我一开始以为它跟 PETLIBRO 一样走 Kalay,结果完全不是——这也解释了为什么 LocalKit 非得进机器不可:App 端根本没有区网路径可以利用。
- 德国 Hama 的自有 App 底层 100% 是 Tuya。外面看不出来,拆开一目了然。「查不出来它是不是涂鸦」这件事是真的,但「没办法查」不是。
- maoxga(喵享家)的 App 里意外发现了 Matter SDK,代表喂食那半可能支援 Matter。但摄影机部分是 Agora,确定没有本地路径。
WARNING
App 里呼叫了区网直连函式,不代表韧体那边也支援。App 有呼叫只证明「开发者写了这段程式码」,装置端有没有对应的处理,要机器在手才知道。但反过来说,App 完全没有区网直连的呼叫,就可以直接排除。
最后选了 FurSpark#
筛完剩两台有机会:Dogness F10 和 FurSpark Big Eye。
| FurSpark Big Eye | Dogness F10 | PETKIT P592 | |
|---|---|---|---|
| 解析度 | 2K | 1080P | 1080P |
| 视角 | 广角(官方不公布数值) | 165° | 140° |
| 粮仓 | 3.7L | 3.6L | 双料仓 |
| 储存 | 内建 64GB、48h 回放 | — | — |
| 特殊功能 | 机械眼睑实体遮蔽镜头 | 干电池备援 | 猫脸辨识 |
| 影像 SDK | TUTK(有区网直连) | TUTK(有区网直连) | Agora(无本地路径) |
| 接进 Frigate | 需逆向 | 需逆向 | 需 LocalKit 改装 |
| 台湾最低价 | NT$3,990 | NT$4,200(momo 促销) | ~NT$5,000 |
FurSpark 的硬体最好、价格最低,而且 App 的套件名 com.ddpai.cpp_overseas 透露了一件有趣的事:开发商是深圳盯盯拍(DDPai),一家做行车记录器的公司。「美国 Furspark」是行销包装。
这反而是好消息——DDPai 的行车记录器有人公开拆解过,有一条本地 HTTP 介面的前例。这代表除了 TUTK P2P 之外,可能还有第二条路。
IMPORTANT
我没办法保证这台一定能接进 Frigate。所有资讯都指向「有机会」,但没有人做过。最后决定买的理由很简单:就算整合失败,它照样是一台能定时喂、有 App 看的喂食器。不碰韧体、原厂功能全部保留,最差的情况就是多一台普通喂食器。
影像接入:一个私有的 TCP 埠#
2026 年 9 月 13 日,喂食器到手。装好、喂完猫、连上 Wi-Fi,开始找影像。
没有 RTSP,但 daemon 活着#
用 nmap 扫装置开放的埠,554 上跑着 Hisilicon RTSP Streaming Media Server/1.0.0。但试了十几条路径,全部回 400 或 500——媒体源没有挂上去。RTSP daemon 是活着的,只是没人喂它内容。
NOTE
第一次探测的时候犯了一个低级错误:送出的 RTSP 请求用了相对 URI 而不是绝对 URI(rtsp://host:554/path),所以被回 400。用正确格式重送之后,OPTIONS 回 200、CSeq 正确回应,daemon 是有在解析的。但 DESCRIBE 某些路径回 500(路径认得、取不到媒体),其余回 400(路径不认得)。没有任何路径会自己吐出 SDP。
没有 ONVIF。装置回报的型号是 DDPaiQ3,SoC 是 Rockchip RV1126——hostname 直接留着原厂的 RV1126_RV1109 没改。
私有 TCP 6200#
转头去看其他埠。TCP 6200 有东西:连上去之后开始吐二进位资料。每个封包前面有 8 bytes 的标头,用十六进位看:
0a 0b 0c 00 [4-byte 大端长度] → 关键帧(I-frame)
0a 0b 0c 01 [4-byte 大端长度] → 一般帧(P-frame)
0a 0b 0c 是固定魔术位元组,第四个 byte 区分帧类型,接着四个 byte 是这个 NAL 单元的长度。把标头剥掉,剩下的直接丢给 ffmpeg,零解码错误。
用 ffprobe 量出的规格:
| 项目 | 值 |
|---|---|
| 解析度 | 1664 × 2224(直式) |
| 帧率 | 25 fps |
| 编码 | H.264 High profile, level 5.0 |
| 码率 | ~2530 kbps(≈ 15 GB/天) |
| 音讯 | 无(这条路径上没有音轨) |
| 关键帧间隔 | 4 秒 |
直式影像。官方页面有一段文字提到「expanding the upper and lower visual range from the previous 16:9 aspect ratio」——上一代是 16:9,新款的感光元件旋转 90° 安装,往上下扩展视野。从行车记录器的料改的。
WARNING
这条串流的规格会变。 我用历史录影的 ffprobe 验出来:上线第一天是 576×768 / level 3.1,隔天就跳到 1664×2224 / level 5.0。推测是 App 上的「视频品质」被改过(目前设在「超清」)。Frigate 的 detect 解析度必须跟着改,否则会降采样或过度采样,影响侦测品质。
架构:装置 → go2rtc → Frigate#
go2rtc 用 exec:ffmpeg 来源连接装置的 TCP 6200,ffmpeg 负责接收原始 H.264 位元组流并转成 RTSP。Frigate 从 go2rtc 拉 RTSP,同时负责侦测和录影。浏览器透过 WebRTC 直接从 go2rtc 拿画面。
有几个坑:
- 只能开一条连线。 同时开第二条 TCP 6200 连线,会造成 camera_fps 飙升到 100 以上、NAL 错误,整个串流坏掉。手机 App 连线时也可能有同样的竞争。
- go2rtc 必须绑全介面(
:8554)。 ffmpeg producer 内部会把媒体 publish 到rtsp://127.0.0.1:${port},如果 loopback 没监听就会 self-loop 失败。要靠防火墙规则保护——只允许 Frigate 容器的网段拉 RTSP,其余拒绝。 - 浮水印不需要遮。 画面左下角有「Forspark Big Eye」和逐秒时间戳,但缩到 Frigate 的 motion 侦测解析度之后只剩约 1 个像素的面积,远小于
contour_area门槛,自动被滤掉。
开画面要等几秒#
装置的关键帧间隔固定 4 秒,新连线要等下一个关键帧才能开始解码。平均等 2 秒、最坏 4 秒。C210 大约 0.1 到 1.7 秒。App 没有调整关键帧间隔的选项。
音讯那边的 ffmpeg 探测输入格式也会拖慢开画面(go2rtc 是依序启动 producer 的),加上 -f aac -probesize 32 -analyzeduration 0 之后从 ~3.2 秒降到 0.2 秒。两段加起来,开画面大约 3 秒。
喂食控制:云端 MQTT 桥接到 Home Assistant#
影像走本地,喂食控制走云端。两条路完全分开。
装置的即时状态和控制指令走的是厂商自己的 broker。我从 App 里逆出了连线方式和指令格式,写了一支 Python daemon(furspark_ha.py),让它一边连厂商的 MQTT 读状态,一边用 MQTT discovery 推到本地的 Home Assistant。
IMPORTANT
喂食控制的具体指令格式和云端端点是这台装置的私有协定,涉及其他使用者的装置安全,不在这里公开。下面只讲「做出了什么」,不讲「怎么发指令」。
22 个 entity,全挂在同一台装置底下#
| 类型 | 项目 |
|---|---|
| sensor | 余粮等级、喂食计划、今日计划出粮份数、下次投喂时间、干燥剂剩余天数、储存空间剩余与使用率、视讯品质、韧体版本 |
| binary_sensor | 线上状态、故障 |
| switch | 摄影机开关、指示灯、童锁、麦克风、喂食提醒、喂食语音 |
| number | 喇叭音量、按键每次份数、喂食份量 |
| button | 立即喂食、喂 1 份 |
| text + button | 计划编辑器(格式 07:30x40, 18:00x2)、套用计划、计划启用 |
有几个容易搞错的地方:
- 余粮是等级不是百分比。 装置回传的值是 0(充足)、1(偏少)、2(耗尽),不是 0–100。我一开始当成百分比用,结果「装满的显示 0%」。
- 1 份 ≈ 1 克。 实测量的,说明书写「随饲料密度变动、不可调」。两只猫早上吃 40 克,就设 40 份。份数上限实测到 99 都能接受。
- 只有常驻模式(
--daemon)才会推控制项。 一次性模式只推 sensor——因为开关和按钮需要有人监听指令才有意义。
daemon 装成 systemd 服务,开机自启。每 10 分钟轮询一次状态。HA 里切开关会即时生效——实测过指示灯和摄影机开关,指令送出到装置状态改变不到一秒。喂食按钮没实测(会真的出粮),留给手动按。
即时音讯与双向对讲#
TCP 6200 那条串流没有音轨。但装置的录影档里有 AAC 音讯,代表麦克风是好的——声音只是不在 6200 这条路径上。
TUTK P2P 桥接#
音讯走的是 ThroughTek Kalay(TUTK)的 P2P 协定。go2rtc 的 41 种来源协定里没有 tutk,所以要自己建桥。
做法是从 App 的 APK 里提取 arm64 版的 TUTK SDK 函式库(libIOTCAPIs.so、libAVAPIs.so 等),用 在 x86 主机上跑起来,再用 Python 的 ctypes 呼叫 SDK 函式建立 P2P session、收音讯。
这是整个专案里最难的部分。TUTK SDK 是为 Android(bionic libc)编译的,要在 Linux(glibc)上跑,有好几个 ABI 不相容的地方需要用 shim 拦截:
展開
| 问题 | 症状 | 解法 |
|---|---|---|
pthread_mutex_t 大小不同 | bionic 4 bytes、glibc arm64 48 bytes,TUTK FIFO 内嵌 bionic 大小的 mutex → segfault | shim 拦截 pthread_mutex_*,用物件位址做 key、旁边维护真正的 glibc mutex |
struct addrinfo 栏位顺序不同 | bionic 的 ai_canonname 和 ai_addr 对调 → 野指标 | shim 拦截 getaddrinfo,翻译成 bionic 的布局 |
| init_array 里有 null 项目 | bionic linker 会跳过,glibc 直接呼叫位址 0 → segfault | 扫描每个函式库,全 null 的 init_array 归零 DT_INIT_ARRAYSZ |
__errno 符号名不同 | bionic 用 __errno,glibc 用 __errno_location | shim 转接 |
_ctype_ 是指标不是阵列 | bionic 的 _ctype_ 是 const char*,给阵列会被当野指标 | shim 提供正确的指标版本 |
桥接的架构是一个按需 TCP server。go2rtc 连进来(有人在看画面)时才连 TUTK、开启音讯接收、输出 AAC-ADTS;没人看的时候释放 session,避免占用装置的 P2P 连线数。
稳态资源消耗:CPU ~5%(一颗核)、RAM 67MB、音讯流量 ~65kbps。之前担心「24/7 跑 qemu 桥接很吃资源」是错的——那是建置时的瞬间尖峰,稳态极轻。
踩过的几个坑#
认证会过期。 连线需要的密码是从云端 API 算出来的,几天之内就会失效。失效时 DTLS 握手静静失败,错误码是「逾时」而不是「密码错误」——完全看不出是帐密问题。现在用 crontab 每 6 小时重算。
音讯断了会拖垮影像。 go2rtc 的多来源合轨是「全有全无」,音讯来源一连不上,整个串流就起不来,连影像一起黑掉。修法是让桥接「只要有 client 就永远吐得出音轨」——真音讯断超过 600ms 就补静音帧,TUTK 连线失败用指数退避重试。
WebRTC 需要 Opus。 音讯只给 AAC 的话,WebRTC 带不了(不支援 AAC),浏览器会退回 MSE,外网就会卡。加上 Opus 转码之后,WebRTC 能带音讯了,外网流畅。
双向对讲#
送话的协定也逆出来了。装置接受 AAC-LC 16kHz mono ADTS 格式的音讯(跟收音端一致)。辨识正确编码的方法很暴力:四种候选格式各配不同声数的音档(1/2/3/4 声),一次送完,让人在喂食器旁边数声音——只听到第三组的声数,就是那个格式。
NOTE
送话途中发现一个 bug 藏了六天:Python 的 struct.pack 用有号 int32 塞毫秒时间戳,值超过 2^31 直接丢 struct.error——送话在送出第一帧前就挂了,一帧都没送出去。前面四轮盲扫不同编码格式,测的全是一个根本没执行的路径。教训:确认「有没有真的送出去」比「送的格式对不对」更重要。
即时对讲(从浏览器麦克风直接说话)没做成。go2rtc 的 exec: 来源有 backchannel 支援,理论上可以,但浏览器切到对讲模式时 go2rtc 没有启动 producer。试了各种音讯格式和架构都一样,没查到根因。目前的替代方案是 HA 里放一个按钮,按了播放预录的提示音——用来叫猫吃饭够用了。
喂食器镜头上的分猫辨识#
喂食器的镜头装在机器上方,不锈钢碗在画面最下缘。猫吃饭的时候脸正对镜头,距离大约 40 公分,脸在画面里占约 37%——比 C210 从 3 公尺外拍到的 6% 大得多。理论上分辨 aki 和 kuro 应该更容易。
碗前区域#
在 Frigate 里定义了一个 feeder_bowl 区域,涵盖画面底部 5%。只有在这个区域里待满 10 秒的猫才算「到访」,过滤掉路过碗边的情况。
用历史事件验证这个设定:框底座标 ≥ 0.95 的事件,抽 8 张、8 张都在吃。0.88 到 0.95 之间的,8 张只有 2 张在吃。10 秒门槛加上区域限制,误判率很低。
加 zone 之前,daemon 算的是「猫在整个客厅停留 ≥10 秒」——镜头拍得到整个客厅,一天被灌到 30 次以上,多半是猫在餐桌或柜子上,根本不是吃饭。加了之后降到每天约 7.5 次,合理多了。
每餐合并#
daemon 从 Frigate 的事件 API 拉「今天在 feeder_bowl 区域被侦测到的猫」,同一只猫两笔事件间隔小于 2 分钟的合并成同一餐(Frigate 常把一顿饭切成好几段事件)。HA 里有两个 sensor:aki 和 kuro 的今日到访次数。
NOTE
命名是「到访」不是「进食」。镜头只能确定猫在碗前面待了多久,不能确定牠真的在吃——可能只是蹲着或理毛。最初命名成「今日进食」,被指正了:把推论当成事实。
整套跑起来的样子#
| 项目 | 规格 |
|---|---|
| 硬体成本 | NT$3,990(FurSpark Big Eye,quoview 最低价) |
| 月费 | NT$0 |
| 影像 | 1664×2224 直式、25fps、本地即时串流(TCP 6200 → go2rtc → Frigate) |
| 录影 | 连续录影 3 天(~15 GB/天,跟 C210 的 ~10 GB/天 在同一个碟上) |
| 喂食控制 | HA 里可切开关、调份量、编辑排程、一键喂食 |
| 音讯 | 即时收音(TUTK 桥接 → Opus/AAC → WebRTC),HA 按钮播提示音 |
| 分猫 | 碗前区域 + 10 秒门槛,每日到访次数 × 两只 |
| 侦测 | Frigate 物件侦测(cat / person),GPU 使用率 6% |
| 常驻服务 | furspark-ha.service(喂食控制)、tutk-audio-bridge(音讯)、go2rtc(串流) |
跟 Furbo 对照:
| Furbo 360° + 宠物保姆订阅 | 这套 | |
|---|---|---|
| 硬体 | NT$5,399(不绑约单机) | NT$3,990 |
| 年费 | NT$1,840 起 | NT$0 |
| 录影 | AI 片段,保留 3–7 天 | 连续录影,保留天数自己设 |
| 回看 | 限订阅 | 随时 |
| 双向通话 | 有 | 播预录提示音 |
| 丢零食 | 有 | 定时喂食(不丢零食) |
| 宠物日记 | 订阅功能 | 上一篇做的 |
| 分辨个体 | 订阅功能(多宠辨识) | Frigate 自训分类(碗前近拍命名率 ~89%) |
| 资料位置 | Furbo 云端 | 本地硬碟 |
最后一件事。这台喂食器的 RTSP daemon 是活着的,媒体源没挂上去。韧体里有两个看起来像是「开启 RTSP」的指令(名字是从社群公开的行车记录器韧体字串表里找到的),试了的话说不定能直接拿到带音轨的 RTSP 串流。但那会改变装置状态,有中断现在已经稳定的 6200 直播的风险,我还没试。等哪天喂食器韧体更新坏了什么东西、被迫重新设定的时候再说。