将旧 Android 手机变为局域网监控摄像头,具备实时画面查看、运动检测报警、自动录像、连续录像、录像管理、后台保活等功能。特点:完全本地运行,无需第三方云服务,免费且开源。
| 功能 | 状态 | 说明 |
|---|---|---|
| 实时视频流 (MJPEG) | ✅ 已启用 | 通过浏览器查看,仅视频,无音频 |
| 摄像头切换(前后置) | ✅ 已启用 | 设置中可选镜头朝向,分辨率列表随镜头动态枚举;无前置摄像头时自动回退后置 |
| 本机预览画面 | ✅ 已启用 | 监控运行中主界面出现「预览画面」按钮,手机本机直接查看实时画面(含 HUD 状态信息) |
| 运动检测 | ✅ 已启用 | 画面变化时自动录像;报警由设置中「运动报警」开关统一控制(两种录像模式共享) |
| 运动触发短视频录制 | ✅ 已启用 | 检测到运动时自动录制指定时长 MP4(含音频),时长可配置(10秒~5分钟) |
| 连续录像(按时间分段) | ✅ 已启用 | 持续录制,可按配置的时长分段保存(1分钟~1小时) |
| 录像模式切换 | ✅ 已启用 | 主界面提供三档选择:连续录像 / 运动触发录像 / 仅预览(不录像不检测) |
| 录像存储循环覆盖 | 连续录像启用后可通过设置自动清理旧文件 | |
| 后台保活 | ✅ 已启用 | 前台服务、唤醒锁、引导忽略电池优化 |
| 开机自启 | ✅ 代码支持 | 部分手机需手动授权 |
| 控制界面 | ✅ 已启用 | 主界面可监控状态、控制模式、开关推流 |
| 录像管理(文件夹/视频) | ✅ 已启用 | 按日期分文件夹浏览、播放、搜索、多选删除、导出到外部存储 |
| 视频元数据显示 | ✅ 已启用 | 列表显示文件名、大小、录制日期时间、视频时长 |
| 横屏推流 | ✅ 已启用 | 推流画面强制横屏,与录像方向一致,无旋转虚影 |
| Web 端查看页面 | ✅ 已启用 | http://手机IP:8080/ 提供 HTML 页面:状态指示、自动重连、画面旋转、缩放平移、全屏 |
| 画面旋转(前端) | ✅ 已启用 | 查看页面支持 0°/90°/180°/270° 旋转,旋转时重置缩放和平移 |
| 画面缩放与平移 | ✅ 已启用 | 双指捏合缩放(1x~5x)、单指拖动平移、双击切换;电脑端鼠标滚轮+拖动 |
| 全屏查看 | ✅ 已启用 | 查看页面支持全屏模式,全屏时浮层按钮控制旋转和退出 |
| 连接状态指示 | ✅ 已启用 | 四种状态:连接中(黄)/已连接(绿)/已断开(红)/推流关闭(灰),断线自动重连 |
| 存储异常提示 | ✅ 已启用 | 磁盘写满录像失败时:通知栏提示 + 主界面「录像存储」红显并标注(已满),清理空间后自动恢复 |
启动 App 后直接显示的界面,用于控制摄像头服务和查看运行状态。
-
IP 地址 (
tvIpAddress)
显示手机当前 Wi‑Fi 局域网的 IPv4 地址,例如192.168.1.5。浏览器观看视频流时将使用此 IP 地址 + 端口8080:访问http://手机IP:8080/获取带状态指示和旋转/缩放/全屏功能的 HTML 查看页面(推荐手机端使用),或访问http://手机IP:8080/video直接获取 MJPEG 流(适合电脑端)。支持 HTTP 基本认证(用户名/密码在设置中配置)。 -
服务状态 (
tvStatus)
显示当前监控服务是否在运行,颜色区分:-
绿色 “服务运行中”:摄像头正在工作,可推流和检测运动。此时状态文字右侧出现 「预览画面」按钮(需同时开启 MJPEG 推流),点击进入本机预览页面。
-
红色 “服务已停止”:服务未启动,无法访问视频流。
-
-
预览画面 (
btnPreview)
本机查看摄像头实时画面,仅服务运行中且 MJPEG 推流开启时显示(推流关闭时该按钮不出现)。点击进入PreviewActivity,详见下文。 -
当前摄像头 (
tvCameraFacing)
显示服务实际绑定的镜头朝向(“前置”或“后置”)。注意此处反映真实使用的摄像头:若配置了前置但设备没有前置摄像头,会自动回退并显示“后置”。服务停止时显示当前摄像头: --。 -
当前分辨率 (
tvResolution)
显示相机绑定后实际生效的推流分辨率(如640x480)。该值来自 CameraX 绑定结果,经分辨率回退策略后可能与设置值不同(例如所选尺寸不被当前镜头支持时)。服务停止时显示当前分辨率: --。 -
MJPEG 推流开关 (
switchMjpeg)
打开时:相机画面实时编码为 MJPEG 流,可在浏览器访问。关闭时:视频流停止,但相机仍在后台(运动检测和录像仍可能工作)。实时生效。 -
录像模式选择(两个互斥
SwitchCompat)-
连续录像:开启后持续录制,并按设置的分段时长自动分割文件(默认 10 分钟一段)。
-
运动触发录像:开启后仅当检测到运动时录制短视频,时长可在设置中调整(默认 10 秒)。
-
两个开关不能同时打开,均不选时表示“仅预览”(不录像、不运动检测)。实时生效。
-
-
录像存储 (
tvStorage)
显示当前所有录像文件占用的总磁盘空间(单位:MB),递归计算所有日期子文件夹内的 MP4 文件。每次回到主界面自动刷新。
磁盘写满提示:录像因存储空间不足失败时,数值与单位变红并追加红色「(已满)」字样,同时通知栏提示「录像出错已停止」;此时推流与运动检测不受影响。清理空间(如删除录像)回到主界面后红显自动解除。 -
实际帧率 (
tvFps)
与「录像存储」同一行显示在右侧,实时显示当前实际推流帧率(每秒更新一次)。服务停止时显示帧率: -- fps。可用于判断设置的帧率与实际运行情况的差距。 -
启动监控 (
btnStart)
点击后依次检查相机权限、录音权限、引导忽略电池优化,然后启动CameraService前台服务,通知栏显示“监控运行中”。 -
停止监控 (
btnStop)
停止CameraService,释放摄像头,推流和检测全部停止。 -
查看录像 (
btnViewRecordings)
跳转到录像列表界面,按日期文件夹展示录像。 -
设置按钮(右上角齿轮图标)
跳转到SettingsActivity,配置详细参数(分辨率、帧率、灵敏度、报警 URL、录像时长等)。
监控手机本机查看摄像头实时画面,无需浏览器、无需认证。入口:主界面「预览画面」按钮。
-
实现方式:进程内直接订阅服务编码循环产出的 JPEG 帧(与推流共用编码结果),不新增相机用例、不影响推流帧率与录像。解码限速 10fps("看一眼画面"足够),页面关闭即停止解码。
-
顶部 HUD 信息栏(半透明黑底):
-
● REC(红色):录像进行中才显示
-
模式标签:连续录像 / 运动触发 / 仅预览
-
实际接收帧率:预览页自行统计,每秒刷新
-
存储剩余:每 10 秒刷新一次
-
时间码(右上):每秒走秒,用于对照报警时间
-
-
提示横幅(画面上方,琥珀色):
-
当前为非监控时段(画面可能暂停)
-
无画面(超 2.5 秒无帧:监控停止或推流关闭)
-
-
查看期间屏幕保持常亮;底部居中显示「预览画面」装饰文字。
点击主界面“查看录像”后进入,按日期文件夹(格式 yyyy-MM-dd)组织所有录像,每个文件夹显示包含的视频数量和总大小。
-
普通模式
-
顶部中央显示文件夹个数。
-
点击任意文件夹进入该文件夹的视频列表页面 (
VideoListActivity)。 -
长按文件夹进入多选模式。
-
-
多选模式
-
顶部改为:左侧“全选”按钮,中央“已选 X 项”,右侧“取消”按钮。
-
每个文件夹左侧出现复选框,可勾选多个文件夹。
-
底部显示“删除选中”和“导出选中”按钮。
-
点击“删除”会弹窗确认,递归删除文件夹内所有视频。
-
点击“导出”通过系统文档树选择目标目录,递归复制文件夹及内容。
-
按返回键退出多选模式。
-
展示单个日期文件夹内的所有 MP4 文件。
-
顶部工具栏
-
普通模式:左侧返回按钮,中央显示文件夹名,右侧显示文件总数。
-
多选模式:左侧“全选”按钮,中央“已选 X 项”,右侧“取消”按钮,同时隐藏返回按钮和文件计数。
-
-
搜索栏(始终显示)
-
输入框 + 清除按钮(X) + 搜索按钮。
-
输入文字时 X 出现,点击清空输入框并立即恢复全部视频列表(自动搜索空关键词)。
-
输入框为空时点击 X 则隐藏键盘并失去焦点。
-
点击搜索按钮或按键盘搜索键执行过滤(文件名包含关键词)。
-
-
视频列表
每一项显示:文件名、文件大小(自适应 KB/MB/GB 单位)、录制日期时间(从文件名解析或取文件修改时间)、视频时长(mm:ss 或 HH:mm:ss)。-
普通模式:短按播放视频,长按进入多选模式。
-
多选模式:每个视频左侧出现复选框,顶部计数实时更新,底部显示“删除选中”和“导出选中”。
-
删除视频时弹窗确认,支持批量删除。
-
导出功能:选择外部文件夹后,将选中的视频文件复制到目标位置(支持 MediaStore 刷新)。
-
按返回键退出多选模式。
-
-
元数据异步加载
首次进入或文件列表变化时,后台线程读取所有视频的时长(MediaMetadataRetriever)和日期时间,加载完成后刷新列表,避免 UI 阻塞。同时使用内存缓存避免重复解析。
-
视频设置
-
镜头选择(后置/前置;分辨率列表按所选镜头动态枚举;设备无前置摄像头时启动监控会自动回退后置)。
-
推流分辨率(动态读取摄像头支持的分辨率列表,例如 640x480、1280x720 等)。
-
推流帧率(1~30 fps,每秒平均策略,实际帧率取决于手机硬件性能)。
-
-
运动检测设置
-
动作识别灵敏度(0~100,数值越低越敏感)。
-
报警静默期(秒),避免频繁发送报警。仅在报警成功送达(接收方返回 2xx)后才开始计时;发送失败不占用静默期,恢复后下一次运动立即再发。
-
运动报警开关(默认关闭)。开启后检测到运动时向报警 URL 发送 POST 请求(JSON 数据包含事件类型与时间戳)。两种录像模式共享:连续录像模式下检测到运动只报警不触发录像;运动触发模式下控制是否发送报警。需先配置报警 URL 才能开启,URL 清空时开关自动关闭。
-
报警接收 URL(须填写接收方实际处理的完整路径,如
http://192.168.1.100:3000/alert,仅到端口会因接收方无根路由而返回 404)。
-
-
录像设置
-
录像模式(连续/运动触发/仅预览)—— 与主界面开关同步。
-
运动录像时长(10秒、30秒、1分钟、3分钟、5分钟)。
-
连续录像分段时长(1分钟、5分钟、10分钟、30分钟、1小时)。
-
注意:修改分段时长不会中断当前正在录制的片段,新设置对下一个片段生效。
-
-
系统设置
-
开机自启开关(保存到 SharedPreferences,由
BootReceiver实现)。 -
监控时间限制(可设置每日起止时间,非监控时段相机不分析也不录像)。
-
-
安全设置
- HTTP 认证用户名/密码(基本认证,保护 MJPEG 流)。
所有设置(除镜头朝向、分辨率、帧率需重启监控外)均实时生效,通过
RELOAD_CONFIG广播通知CameraService更新行为。
在手机启动监控服务后,同一 Wi‑Fi 下的任意设备(电脑、手机、平板)打开浏览器,访问 http://手机IP:8080/ 即可打开 HTML 查看页面。该页面提供以下功能:
-
实时画面:通过 fetch Streaming API 直接读取 MJPEG 流并解析 JPEG 帧,帧率接近推流原生帧率(非低帧率快照轮询)。
-
连接状态指示:顶部状态栏实时显示四种状态:
-
🟡 连接中(黄色闪烁):页面刚打开或正在重连
-
🟢 已连接(绿色):正常接收画面,同时显示实时 FPS
-
🔴 已断开(红色闪烁):服务器不可达,自动尝试重连
-
⚪ 推流已关闭(灰色):App 端已关闭 MJPEG 推流
-
-
画面旋转:点击「旋转」按钮循环切换 0° → 90° → 180° → 270°,旋转时自动重置缩放和平移。适用于手机竖放但需要横屏观看的场景。
-
画面缩放与平移:
-
手机端:双指捏合/张开缩放(1x~5x),单指拖动平移(缩放大于 1x 时生效),双击切换 1x ↔ 2x
-
电脑端:鼠标滚轮缩放,按住拖动平移,双击切换 1x ↔ 2x
-
缩放回 1x 时自动重置平移位置
-
-
全屏模式:点击「全屏」按钮进入全屏,顶部/底部栏隐藏,右上角浮层提供旋转和退出按钮。全屏和非全屏均可使用缩放平移。
-
自动重连:流连接断开后自动重连,无需手动刷新页面。
-
状态栏始终可见:放大画面被裁切在画面区域内,不会溢出遮挡状态栏。
也可直接访问
http://手机IP:8080/video获取裸 MJPEG 流(适合电脑浏览器原生渲染,但不具备上述交互功能)。
-
最低 Android 版本:8.0 (API 26)
-
开发语言:Kotlin
-
核心依赖:
-
CameraX(相机控制、图像分析、视频录制)
-
NanoHTTPD(HTTP 服务器,提供 MJPEG 流)
-
OkHttp(报警 HTTP 请求)
-
AndroidX Core(
ContextCompat.registerReceiver标准广播,服务与界面通信)
-
-
视频推流:
-
CameraX
ImageAnalysis获取 YUV 帧,转为 NV21,在 NV21 字节层面直接旋转(纯内存操作,避免 JPEG 二次编解码),再压缩为 JPEG,通过 NanoHTTPD 以 MJPEG 格式输出。 -
编码链路采用「最新帧槽位 + 单线程编码循环」:分析线程只做覆盖式投递,编码积压时旧帧在编码前即被丢弃(省 CPU、观看延迟恒定),单线程天然保序(避免多线程并发编码导致的画面乱序回跳)。
-
每个客户端独立线程推送,新帧自动覆盖未发送的旧帧(慢帧丢弃),帧到达通过
wait/notify事件唤醒推送线程(无固定轮询空转)。多客户端互不阻塞。 -
帧率限制采用「每秒平均」策略:每秒内最多处理 N 帧(N 为用户设置的帧率),比单帧硬间隔更准确、更接近设置值。
-
根据
ImageProxy的旋转角度(imageInfo.rotationDegrees)动态旋转,保证输出画面与录像方向一致。
-
-
本机预览:
PreviewActivity通过进程内帧监听接口直接订阅编码循环产出的 JPEG 帧,不新增相机用例(零额外相机成本、不影响推流帧率与录像)。解码限速 10fps 并覆盖式丢帧,页面不可见即注销监听。 -
镜头选择:
相机选择器(CameraSelector)按设置构造(后置/前置),绑定前通过hasCamera()检查所选镜头是否存在,不可用时自动回退后置摄像头并记录日志。画面方向由每帧的传感器朝向(rotationDegrees)自动校正,前后置无需额外处理。 -
运动检测:
相邻帧亮度差异算法,支持灵敏度调节。 -
录像存储:
-
录像文件按日期存放到
Recordings/yyyy-MM-dd/子文件夹。 -
连续录像:在
CameraService中维护一个Recording会话,通过Handler定时停止当前分段并自动开始下一段。 -
运动触发录像:单次录制指定时长(从设置读取)。
-
文件命名规则:
motion_<时间戳>.mp4(运动触发)或video_<时间戳>.mp4(连续录像),其中时间戳为毫秒级 Unix 时间戳。 -
用例动态绑定:仅预览模式下只绑定
ImageAnalysis(独占相机输出,推流帧率最高);录像模式(连续/运动触发)同时绑定ImageAnalysis + VideoCapture(相机输出共享,推流帧率略降但录像正常)。模式切换时自动重新绑定。
-
-
录像管理:
-
RecordingsActivity扫描Recordings下所有日期文件夹,统计视频数量和总大小。 -
VideoListActivity递归计算文件夹内 MP4 文件,异步加载时长和日期,支持搜索、多选、删除、导出。 -
导出使用
DocumentsContract/MediaStoreAPI,兼容 Android 10+ 分区存储。
-
-
后台保活:
前台服务 +PARTIAL_WAKE_LOCK,引导用户关闭电池优化。 -
横屏统一:
强制ImageAnalysis和VideoCapture的targetRotation = Surface.ROTATION_0,确保输出画面宽>高,与录像方向一致。 -
Web 端查看页面:
StreamServer在根路径/返回内嵌 HTML/CSS/JS 的查看页面,无需额外静态资源。页面用 fetch Streaming API 读取/video的 MJPEG 流,在 JS 中扫描 JPEG 帧标记(SOI0xFF 0xD8/ EOI0xFF 0xD9)提取完整帧并渲染,帧率接近推流原生帧率。帧渲染优先使用createImageBitmap+<canvas>(JPEG 解码移出主线程,绘制经串行 Promise 链保序),不支持的浏览器自动回退<img>(decoding="async")。通过streamGen代际计数器防止重连竞态,buffer 超 500KB 自动截断防止内存溢出。同时提供/status(JSON 状态)和/snapshot(最新 JPEG 帧)端点供心跳检测和备用访问。画面旋转/缩放/平移通过 CSStransform合并实现(translate → rotate → scale),GPU 加速,不影响帧渲染性能。
| 项目 | 现状 | 建议 |
|---|---|---|
| 远程音频监听 | 浏览器 MJPEG 不支持音频 | 可升级为 WebRTC 或 RTMP 推流 |
| 开机自启可靠性 | 部分厂商需手动授权;Android 14+ 系统限制开机自启动相机类前台服务(旧手机 Android 8-13 不受影响) | 增加引导说明 |
| 通知权限 | Android 13+ 需用户授予通知权限,未授予时前台服务通知不显示(App 启动时会请求,拒绝不阻断监控功能) | 引导用户授予 |
| 多路观看 | 支持多浏览器同时观看 MJPEG,每个客户端独立线程推送 | 可增加观看人数统计 |
| 录像循环覆盖 | 代码已预留,未启用 | 后续可根据总容量自动删除最早文件 |
| 帧率硬件上限 | 不同手机 CameraX 输出帧率上限不同(约 15-30fps),好手机上限更高 | 可在启动时动态探测硬件上限并反馈给用户 |
| 启动预热误报 | 相机启动初期自动曝光收敛可能触发一次假运动报警 | 检测器启动时跳过前几帧(待实现) |