家中闲置三台可开机旧机:小米MIX一代、魅族M15、华为P8Lite。性能孱弱无备用机价值,回收报价近乎为零,换盆又觉可惜。遂决定以小米MIX为核心中枢,其余作硬件拓展,改造为家用NAS+监控。
注:此次改造我的试错成本(刷机、固件)远超硬件残值,纯属玩。
Android基于Linux内核,但为移动端做了极致裁剪:
最终方案:不刷独立Linux,在Android内核上补全GNU环境,通过Chroot运行Linux程序。 虽然安卓自带的linux内核残缺,但作为边缘服务器完成一些基础服务器功能足够了。
要将手机改作服务器,ROOT(UID 0) 是前提,也是突破Android权限枷锁的核心。
3003(INET)联网、1015(SDCARD_RW)存储读写、1006(CAMERA)摄像头传统ROOT向/system写入文件,易触发安全限制;Magisk通过修补boot镜像实现无侵入提权:
magiskinit劫持init进程su与配置,还原原厂init核心价值:绕过SELinux强制访问控制,获取标准端口(80/443/22)使用权。
在手机存储中划分一块区域作为隔离容器,内置完整Linux所需的标准目录结构、glibc运行库、GNU工具链与动态链接器。 通过chroot切换当前会话的虚拟根目录/,该进程及其所有子进程会被锁定在容器文件系统内,无法逃逸到外部安卓系统。
这一过程不会覆盖/替换安卓原系统文件,仅通过修改进程的根目录视图实现环境切换: 程序所有绝对路径的库查找、命令调用、配置读取,都会自动指向容器内部目录。隔离环境天然屏蔽安卓的Bionic库与安卓原生工具,强制使用容器内的glibc+GNU工具链,底层仍共用安卓Linux内核,最终实现用户空间完整替换、运行环境完全隔离、内核层无缝复用。
/proc(进程)、/sys(硬件)、/dev(设备)挂载至容器,直通内核接口/etc/resolv.conf为systemd软链接,chroot无systemd导致DNS失效。启动时删除死链,写入谷歌/阿里云DNS于是,一个开机自启动的容器模块就完成了。 DebianChroot
开机之后,模块会自动挂载容器所需的临时内存盘,然后启动runit守护进程。 容器内如果需要配置ssh或者ddns等服务的自启动与管理,只需要写一个简单的runit脚本即可。
服务器需常年通电,与其自己瞎改直流,不如直接采用内核软件控制PMIC(电荷管理IC),将电量锁定在30%-60%(电池化学稳定性最优区间),由原厂电源管理保障安全。
MI MIX通过DebianChroot容器,通过runit维护一些服务:
其余设备作为硬件拓展,只需要配置好openlist,在局域网内通过webdav进行存储扩充至Mi Mix的openlist上。
既然要让利用设备,就必须狠狠使用尽可能多的硬件。看着没用到的手机摄像头与屏幕,萌生了改造为监控的想法。
ROOT后直接访问/dev/videoX失败,硬件资源被Android系统服务独占,需改用上层应用调用摄像头,没招只能使用安卓软件实现。
最终我选择了软件ipwebcam,调用将边缘设备的摄像头,通过Rtsp(应用层:推流协议)将摄像头内容通过局域网推送至服务器(虽然服务器也是安卓)。软件的设计目标与我监控的需求大体重合。
ipwebcam工作流程: 打开app👉点击菜单👉点击启动服务👉进入rolling服务(录像+推流)
同时,如果希望把屏幕利用上,ipwebcam也提供了内置浏览器。不过,启动浏览器必须: 进入rolling服务👉点击菜单👉点击启动浏览器👉在弹出框中输入网址👉确认
但设备作为硬件拓展,我们不希望手动控制,但:
因此,我尝试修改这个软件,尽量移除需要手动操作的部分,至少开机自动启动后,能正常进入 录制+展示浏览器页面的状态。
由于安卓使用Java虚拟机(ART)运行Java程序,相比于二进制反汇编,dex二进制解码为字节码助记符(smali),逻辑更加清晰。
于是先对ipwebcam软件解包,使用jadx将dex反编译为Java,找到rolling活动的启动函数名和内置浏览器的启动函数名。
再将dex转为smali,根据函数名定位,将rolling启动函数中添加内置浏览器的启动流程。
最后将smali编译回二进制dex,实现启动录制活动,自动展示内置浏览器页面。
为了能够通过命令调用rolling活动,再修改AndroidManifest.xml,把rolling activity导出。
至此,修改后的软件可以通过pm命令直接调用rolling服务,同时自动启动内置浏览器展示我想指定的网页。
同时,我还发现软件似乎有一个pro去广告的校验,顺手一并绕过了。
上一节改造中实现了安卓摄像头的利用,但那只是采集端。我需要一个监控中枢,承接所有监控采集推流,作为整个监控生态的核心枢纽。
但我调查后发现,一般小区商用监控基本上是软硬件生态一体。也就是说,开发商/物业 统一采购品牌摄像头,品牌方交付部署好的摄像头、网络、服务器软硬件。
我先是寄希望于成熟开源的集成软件,比如Shinobi,frigate,ZoneMinder
但后来发现,它们要么是python+x的混合复杂生态,要么带着AI检测画面等需要强硬件性能的功能,因此他们也几乎推荐docker部署,运行负担也很大。
很不巧的是,我使用安卓改的服务器:
当时我就在想,有没有一款软件,足够轻量化,不需要配置复杂的环境就能实现基础监控中枢功能:
其实是有的,tinyCam Monitor。不过这怎么是移动端应用?虽然我的服务器是安卓改的,但我将其改为微型中枢服务器之后,他就只会放在角落默默运行。而一切关于服务器的交互,都会在chroot的shell中实现。而且我需要的交互不再是手机屏幕上,而是直接提供端口网页交互。
于是我又进一步查询,发现linux中确实没有软件处于tinyCam Monitor相同的生态位。
没办法,只能再往下层生态找:
单个可执行文件,追求极致的协议性能。它在流媒体转发(Ingest/Publish)方面做得极其出色。但它的问题在于,它仅仅是一个“服务器”(Server),而不是一个“录像机”(NVR)。它没有内置的录像管理界面,也没有成熟的磁盘自动清理和历史检索逻辑。
也就是说,需要通过通过命令行操作视频流保存;无法实施展示视频。
虽然结合脚本能够完成录像任务,但它缺乏统一的流媒体管理能力,更无法提供一个便于长期使用的交互能力。
我觉得在“集成监控系统”与“原始命令行工具”之间有生态位缺失:缺少一款类似tinyCam Monitor的,足够轻量纯粹的NVR中枢软件。
既然没有,那我感觉可以做一个。得益于AI日益强大,我对AI协助我完成此次开发有信心。
先前探索开源监控方案时,了解到的是frigate是 Home Assistant的插件,而 Home Assistant又使用go2rtc接入视频流。
go2rtc是一个由go开发的单文件流媒体网关,为上层应用提供基础的视频流接入,协议转换甚至视频流转发功能。
我期望的基础监控中枢功能:1. 接收视频推流 2. 按需保存视频流 3. 实时展示视频流
go2rtc直接满足了我需要的1、3两点功能。我想这就是一个很好的切入点。于是我fork了这个项目,为其补充功能:
运动检测触发了录制API时,如果简单的把后续的视频流转码保存到存储,那势必导致首次检测到运动时关键的画面丢失;同时我们还需要触发帧的画面作为本次录制的快照,以便在查看录像时,能快速了解本段录像的触发主体。 因此,我为运动检测的视频流设置了的环形缓冲区,视频流新帧到时,放入缓冲区同时与上一帧进行灰度对比。超过阈值时,新帧直接转码成快照,将缓冲区全部数据转码写入文件,在收到停止录制信号之前,新接收的视频流数据也转码写入文件。自此,实现了录制快照与预录制功能。
于是有了如下项目: 57Darling02/go2nvr (github.com)
go2nvr继承了go2rtc的依赖快速部署和高性能特性。 同类型的软件中,功能更多的软件,在性能和易用性上都不如他;性能持平或更强的软件,功能不如go2nvr多; 暂时没有发现功能与go2nvr相近(或者覆盖)的软件。
因此我自认为go2nvr的产品定位是合理的。
其实大多数的摄像头并不仅仅是一个采集端,往往还能够输出(如对讲机功能)和控制(转向等)。
那天在宿舍楼道上,摄像头一直在喊:请勿抽烟。。。 我忽然想起ipwebcam软件似乎也提供了使用扬声器的接口,要不我也做这样的功能
于是我突发奇想,可以做一个TTSPUSH功能,将文字转成语音,输入各家摄像头提供的音频播放接口。
虽然我看起来像是啥都想自己做一下,但其实我是不会重复造轮子的。
我直接在开源社区搜索GO语言开发的语言转文字库,果然让我发现了别人造好的轮子 difyz9/edge-tts-go
在此基础上,实现了go2nvr文字转语音功能。
但由于我没有任何品牌摄像头,因此我只能先适配ipwebcam摄像头。
ipwebcam软件提供“双向通讯”的功能,在其原生提供的web界面中,能够实现对讲机的效果。
我对其进行抓包后,成功抓到了这个接口的拼接实现。
于是在go2nvr中新增了API,用于调用文字转语音,并且将生成的音频推送至相应的接口。
三台旧手机中,性能最强的MI MIX作为中枢服务器,接入电源后自动开机(并控制电量)。 通过ddnsgo绑定域名,公网ipv6提供①openlist②监控中枢表盘;
其余两台作为硬件拓展,在同一局域网下向中枢服务器提供:①openlist存储②监控视频流;同时,其屏幕会显示时钟。把他们摆在合适的位置,技能看时间,又能不被察觉的暗中观察。通过浏览器进入监控中枢表盘,还能够打字说话。