
简介这是一份基于 Android 平台的手机远程视频监控系统完整源码面向具备一定 Android 基础、希望学习音视频采集与网络传输的开发者。压缩包内含 41 个文件主要包括 5 个 Java 源码、14 个 class 编译文件、6 个 XML 配置/布局文件、8 张 PNG 界面资源以及 APK、DEX 等构建产物整体仅 168KB代码量精简便于快速阅读和二次开发。项目围绕 ImageServer.java 图像处理、CameraTest 摄像头调用、Socket/HTTP 网络通信、异步多线程刷新 UI 等环节展开并涉及权限声明、数据存储和传输安全通过分析源码可以掌握远程监控应用的整体架构了解从摄像头预览到远程视频流传输的实现思路。该资源已有 289 人学习下载适合作为课程设计或毕业设计的参考。1. 拿到安卓远程视频监控源码包第一件事不是解压看代码这类标题为“安卓Android源码——基于手机的远程视频监控系统”的 zip 包市面上很多是学生项目或早期开源工程打包。解压以后你看到的不是一份能立刻跑的 App而是一套由采集端、播放端、信令约定组成的工程。真正的技术难点不在按钮和布局而在摄像头采集、硬编码、网络传输这三级管线。如果只改 IP 就跑通常只能在同一个 WiFi 下看想要跨网络可靠观看需要处理端口映射、码率控制、前后台切换这些细节。适合准备用 Android Studio 改造毕业设计、想快速搭自家看家工具的开发者。下面按拿到包后的实际顺序来讲。2. 先拆 AndroidManifest.xml再用 Android Studio 把源码跑起来2.1 从权限和组件判断这套源码的主流程拿到源码先打开app/src/main/AndroidManifest.xml不要先看 Java。Android 远程监控的架构差异会直接体现在组件声明上是后台服务常驻采集还是打开 Activity 才采集是走系统相机还是 SurfaceView是否申请了前台服务类型扫一眼清单就能判断个大概。很多旧包的清单文件里只有INTERNET、CAMERA两行这种包放到 Android 13 以上几乎跑不起来。常见采集端权限表权限用途Android 版本影响CAMERA采集视频数据运行时权限Android 14 需要前台服务声明相机类型RECORD_AUDIO采集音频含语音对讲才需要缺省会静音INTERNET网络收发无特殊ACCESS_NETWORK_STATE判断 WiFi/移动网络用于切换画质策略WAKE_LOCK防止采集时 CPU 休眠配合前台服务使用FOREGROUND_SERVICE声明前台服务能力Android 9 必须FOREGROUND_SERVICE_CAMERA声明相机前台服务Android 14 必须POST_NOTIFICATIONS前台服务常驻通知Android 13 需要运行时请求上面的权限表在源码里不一定全部出现。如果只有前三项说明项目把采集任务直接放在 Activity 生命周期里一旦 App 退后台onPause()关闭相机远程画面立即中断。这类源码要做后台监控就必须新增前台服务并且补上FOREGROUND_SERVICE与FOREGROUND_SERVICE_CAMERA权限。Android 14API 34开始如果service标签缺android:foregroundServiceTypecamera调用startForeground()会当场抛ForegroundServiceTypeNotAllowedException这不是设备兼容性问题是权限声明不完整。2.1.1 判断推流端和观看端在不在同一个包继续看service和activity标签。常见结构有三种MainActivity负责选择模式然后跳转到采集界面或播放界面MonitorService是采集后台任务持有关键的摄像头与编码器对象RTCPlayerActivity或PlayerActivity是远程观看界面。如果整个工程只有一个 app通常用按钮模式或编译参数区分两端如果拆成app和player两个 module要在根目录settings.gradle确认。还有一种情况同一个 Activity 根据 intent 的 extra 决定推流还是拉流这时候要搜ACTION_STREAM之类的常量名。2.2 FileProvider 与 content:// 路径监控类 App 不只是推流还经常把抓拍图片或录屏短视频存到应用目录再通过内容组件分享出去。从 Android 7.0 开始file://会被禁止跨应用暴露日志里常见的错误就是FileUriExposedException。正确做法是在源码中搜FileProvider.getUriForFile并且确认路径与res/xml/file_paths.xml里的external-path声明匹配。调试时经常能看到content://com.example.monitor.fileprovider/external_path/...这样的路径这是 FileProvider 对外暴露的合法 URI。注意日志里出现这类路径但目标应用仍打不开多半是少了Intent.FLAG_GRANT_READ_URI_PERMISSION。如果拿到的是旧源码只有/sdcard/DCIM/xxx.mp4这种硬编码路径要改成getExternalFilesDir()或MediaStore写入避免目标平台版本升级后被系统拦截。2.3 导入 Android Studio 的 5 个固定动作# 进入工作目录确认解压路径无中文 cd ~/MonitorProject # 解压源码包路径不要带空格和中文 unzip monitor_source.zip -d MonitorProject # 查看 Gradle 包装器版本决定是否升级工程 cat gradle/wrapper/gradle-wrapper.properties这条命令链的逻辑说明路径带中文或空格时Gradle 在配置 NDK 路径、生成 BuildConfig 时会出现诡异的符号错误。先确认 wrapper 版本如果写的是gradle-4.6一类老版本Android Studio 打开后要按提示升级否则compileSdkVersion无法改成 34。接下来按顺序检查依赖环境Android Studio 打开工程后先不管同步报错打开File Project Structure确认 Android SDK 版本。把compileSdk和targetSdk拉到本机已安装的版本。旧项目常见混乱写法是compileSdkVersion 28这种老式 DSL直接升级成compileSdk 34同步报错会立刻消失一批。检查 Gradle JDK 版本。Android Studio 4.x 以上默认用 JDK 17老项目用 JDK 8 会提示unsupported class file major version 61在gradle.properties或 Project Structure 里统一 JDK 路径即可。查看app/src/main/jniLibs下有没有armeabi-v7a和arm64-v8a目录。如果源码用到了 RTMP SDK 或 FFmpeg 的.so文件SDK Manager 里必须勾选 NDK 和 CMake否则编译到externalNativeBuild一步会直接报找不到工具链。最后执行一次./gradlew assembleDebug这一步不为安装只为让编译器的报错把隐藏依赖暴露出来。到这里这套远程视频监控系统源码才算是真正进入了可修改状态。3. 用 Camera2 和 MediaCodec 重写采集端输出 H.264 流3.1 为什么核心难点在编码器配置旧源码用 Camera SurfaceView 裸流 UDP 发送的做法在 4G 网络和现代 Android 上并不可行。常见做法是采集端直接输出 H.264 裸流观看端解码或者用标准协议封装。Android 原生提供的MediaCodec在大多数设备上使用硬件编码单元H.264 Main Profile 的实时编码可用关键就在参数。如果源码里集成的是 FFmpeg 软编码CPU 会被占满发热很快但好处是不挑设备便于先跑通流程。选 MediaCodec 而不是 FFmpeg核心理由是功耗和发热。手机做监控往往需要连续工作几小时软编码 720p 30fps 在低端机上能占到四核以上 CPU电池温度很快到 40°C 以上。硬件编码走系统芯片里的专用编码单元同样分辨率下功耗能差一个数量级。前提是严格按顺序初始化先配置编码器再打开摄像头最后把 YUV 帧送入编码器顺序反了会出现黑色画面或IllegalStateException。3.2 最小可用的 MediaCodec 初始化// 创建 720p H.264 编码器 val format MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720) format.setInteger( MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible ) format.setInteger(MediaFormat.KEY_BIT_RATE, 2_000_000) format.setInteger(MediaFormat.KEY_FRAME_RATE, 15) format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2) format.setInteger( MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR ) val codec MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC) codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) codec.start()这段代码的逻辑说明createVideoFormat里的分辨率必须和ImageReader的尺寸保持一致否则喂进去的YUV_420_888需要在转换阶段重新缩放白白增加一倍的拷贝开销。COLOR_FormatYUV420Flexible是系统推荐的灵活格式绝大多数设备编码器都支持能直接接受Image.getPlanes()的数据。CBR 码率控制会让输出速率比较平滑弱网丢包后播放端更容易恢复。喂数据的关键代码// 拿到输入 buffer把 YUV 数据塞进去 val inputIndex codec.dequeueInputBuffer(10_000) if (inputIndex 0) { val buffer codec.getInputBuffer(inputIndex) buffer?.put(yuvData) codec.queueInputBuffer( inputIndex, 0, yuvData.size, presentationTimeUs, 0 ) }参数说明presentationTimeUs必须用单调递增的微秒值推荐System.nanoTime() / 1000。很多源码用System.currentTimeMillis() * 1000这在系统时间被修改后会回跳远端画面会卡住几秒。另外编码输出侧要单独处理BUFFER_FLAG_CODEC_CONFIG这个 flag 代表 SPS/PPS 配置帧必须在首帧关键帧之前发送否则播放端解码器永远拿不到参数画面一直是黑屏。编码参数调整表参数推荐值说明分辨率1280x720家庭监控够用码率和功耗平衡点帧率15fps画面视觉流畅CPU 占用比 30fps 低三成码率1.5-2.5 Mbps720p 在 4G 上传约 250KB/sI 帧间隔2s播放端首帧等待时间上限码率控制CBR避免胸外码率突变导致播放卡顿3.3 后台持续采集前台服务与 Camera2 约束Android 9 及以上Activity 退到后台后摄像头会被系统回收服务里直接打开 Camera2 会抛CameraAccessException或RuntimeException: Camera is being used after CameraDevice was closed。要解决必须启动前台服务并且把采集 Surface 放到前台服务的生命周期里。很多源码其实只是起了一个普通 Service所以在 Android 12 上测试时一切后台就黑屏。前台服务声明uses-permission android:nameandroid.permission.FOREGROUND_SERVICE_CAMERA / uses-permission android:nameandroid.permission.POST_NOTIFICATIONS / service android:name.service.MonitorService android:enabledtrue android:exportedfalse android:foregroundServiceTypecamera /参数说明foregroundServiceTypecamera在 Android 14 是强制项缺少它会触发MissingForegroundServiceTypeException。用户点击开始采集后需要先请求CAMERA、RECORD_AUDIO和POST_NOTIFICATIONS三个运行时权限权限通过后再startForeground()。如果要做纯后台采集系统会认为摄像头没有可见的预览 Surface部分设备会强行关闭相机。常见做法是同时创建一个 1x1 像素的悬浮窗类型用TYPE_APPLICATION_OVERLAY画面仍走编码器悬浮窗只用来占住前台 UI 状态这是目前兼容性最高的方案。4. 远程视频监控系统的传输链路RTSP、RTMP 还是 WebRTC4.1 三种协议在手机监控场景下的取舍传输是整个远程视频监控系统里最关系到成败的部分。切到后台采集之后画面要送到几公里外的播放端协议选择直接决定能看清多远、掉不掉线。协议延迟跨网能力Android 原生程度改造量RTSP500ms-1s弱依赖端口映射播放端可原生拉流推流要集成库较小RTMP1-3s一般依赖公网服务器转发需要集成 RTMP SDK 或 FFmpeg中等WebRTC200-500ms强STUN/TURN 中继Android 系统自带 WebRTC 库较大需信令服务器基于手机的远程视频监控系统采集端通常在移动 4G/5G 或家庭 WiFi 下拿不到固定的公网 IP。RTSP 要直连就必须在路由器上做端口映射而运营商分配的家庭 IP 很多是 CGN 地址端口映射根本辐射不出去。RTMP 相对容易把流推到一台有公网 IP 的服务器播放端从服务器拉流但没有服务器就别想用。WebRTC 的设计目标是跨网连接自带 STUN 和 TURN 机制两端无法直连时自动走中继所以异地观看场景下最接近开箱即用。4.2 局域网模式下的端口映射和 adb 调试如果源码里只实现了rtsp://192.168.x.x:8554/live这样的局域网地址远程化改造有两步。第一步在路由器上做端口映射把公网某个端口映射给手机所在内网 IP 的 8554第二步让手机端把上报给客户端的地址从内网 IP 改成公网域名。这一步常常被忽略很多项目改了路由器但 App 里拼接地址仍然是192.168.1.100远程自然不通。调试期一个非常实用的技巧是 adb 端口映射adb forward tcp:8554 tcp:8554这条命令把电脑本机的 8554 端口映射到手机的 8554 端口。手机用 USB 连接电脑电脑上的 VLC 播放器访问rtsp://127.0.0.1:8554/live就能直接拉到手机上的画面。流量不经过 WiFi只走 USB 通道方便在办公室快速验证编码和推流逻辑。等确认没问题再把地址替换成实际的路由器端口映射地址。adb forward只是开发辅助手段生产环境不要依赖。4.3 从 RTSP 改造成 WebRTC 采集端如果不想维护开源 RTSP 服务器把采集端迁移到 WebRTC 是更接近生产的路。Android 端用 WebRTC 官方库可以复用前面 MediaCodec 编出的 H.264 流也可以直接用库内建的 VideoTrack。最容易被卡住的是信令交换两端需要先交换 SDP 和 ICE candidate这一步通常通过 WebSocket 服务器完成。PeerConnection 最小构建代码// 创建 PeerConnection配置 STUN 服务 val iceServers listOf( IceServer.builder(stun:your-stun-server.example.com:3478).createIceServer() ) val config PeerConnection.RTCConfiguration(iceServers) val pc factory.createPeerConnection(config, peerObserver) if (pc ! null) { pc.addTrack(videoTrack, listOf(monitor-stream)) }参数说明STUN 服务器只负责让双方找到可以直连的地址视频内容本身不经过 STUN。如果两端严格不能直连需要在 iceServers 里追加 TURN 中继地址用IceServer.builder(turn:your-turn-server.example.com:3478)再设置用户名和密码媒体包才会走中继。生产部署时只需要一台 coturn 服务信令服务可以和一个轻量 WebSocket 接口共用进程。改造的关键动作是采集端启动后创建 offer把 SDP 发到信令服务等待观看端 answer媒体协商成功后两端直接传视频信令服务器不再参与媒体流。5. 全链路验证与首帧延迟参数调整5.1 用 VLC 和 ffplay 验证局域网推流先不要急着接播放器 App。在电脑上下载 VLC打开网络串流输入rtsp://192.168.1.100:8554/live如果画面出来说明采集端、编码器、网络发送代码是通的。没有 VLC 时可以直接用 ffmpeg 自带的 ffplay 做更严格的验证ffplay -fflags nobuffer -analyzeduration 100000 \ -rtsp_transport tcp rtsp://192.168.1.100:8554/live参数说明-rtsp_transport tcp强制走 TCP局域网场景弱网丢包更少-analyzeduration缩小探测时间首帧更快出现。这个命令同时适合观察卡顿点如果 ffplay 流畅但你自己的 App 播放卡问题多在解码器缓冲而不是网络。5.2 远程视频监控系统首帧速度的 3 个关键参数参数位置推荐值作用I 帧间隔编码器1s播放端最多等 1 秒就能遇到关键帧开始出画初始码率编码器码率上限的一半起播时网络拥塞可以快速占到连接播放缓冲解码端100ms降低缓冲延迟代价是抖动增大I 帧间隔在 MediaFormat 中对应KEY_I_FRAME_INTERVAL从 2 改到 1 能明显缩短首屏时间。初始码率可以这样调整编码器启动时KEY_BIT_RATE给 1Mbps出图后通过codec.setParameters(Bundle().apply { putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 2_000_000) })动态升到 2Mbps避免一启动就把上传带宽占满。最后留一个排查技巧如果编码出的画面整体偏绿先把KEY_COLOR_FORMAT从COLOR_FormatYUV420Flexible换成COLOR_FormatSurface用createInputSurface()配合 EGL 喂数据兼容性比手动填 YUV 更稳也省掉一层颜色空间转换。本文还有配套的精品资源点击获取