feat: 集成 rtsp-client-android 零缓冲 RTSP 渲染,替换 ExoPlayer
- 新增本地模块 rtspclientlibrary/(拷贝自 rtsp-client-android v5.6.5 library-client-rtsp) - RtspVideoPlayer.kt 重写:RtspSurfaceView + AndroidView,VideoCodec 枚举映射 DecoderType.HARDWARE/SOFTWARE;指数退避自动重连(1s→30s,URL/codec 变化重置) - 延迟统计:每 2s 轮询 statistics 输出 videoDecoderLatencyMsec/networkLatencyMsec - 本地修复两个已知坑:CSD 参数集顺序 sps+pps+vps -> vps+sps+pps(H.265 起播黑屏根因), codecType 硬编码 H264 -> 按实际 MIME 标记(H.265 宽高解析) - FrameQueue 60 -> 20 压低积压延迟;剔除 camera 依赖(删除 bitmap 渲染路径) - app 移除 media3-exoplayer-rtsp/ui 依赖(media3-exoplayer 由库模块提供)
This commit is contained in:
@@ -37,7 +37,7 @@ com.example.m20_gamepad/
|
||||
│ ├── RobotConnection.kt # 连接状态机管理(连接/订阅/心跳/断线判定)
|
||||
│ └── JoystickController.kt # 摇杆输入 → 协议指令映射,含 HandStyle 多手型支持
|
||||
├── video/
|
||||
│ └── RtspVideoPlayer.kt # RTSP 视频流播放(ExoPlayer + Composable 包装)
|
||||
│ └── RtspVideoPlayer.kt # RTSP 视频流播放(rtspclientlibrary + Composable 包装)
|
||||
├── data/
|
||||
│ ├── SettingsRepository.kt # 设置项读写(DataStore 封装)
|
||||
│ └── AppSettings.kt # 配置数据类定义 + 默认值
|
||||
@@ -115,28 +115,35 @@ Yaw = (左履带 - 右履带) / 2
|
||||
|
||||
视频层作为 UI 最底层背景,摇杆等控件覆盖其上(半透明)。
|
||||
|
||||
### 当前实现(将废弃)
|
||||
### 当前实现(已集成 rtsp-client-android)
|
||||
|
||||
- 使用 **Media3 ExoPlayer + RTSP 扩展**(`media3-exoplayer-rtsp:1.5.1`),实现见 `video/RtspVideoPlayer.kt`
|
||||
- 已做:最小缓冲 LoadControl(200/500/100/200ms、无回退缓存、时间优先)、RTSP 自动重连(指数退避 1s→30s)、`VideoCodec` 枚举(AUTO/FORCE_HW/FORCE_SW_H264/FORCE_SW_H265)→ `MediaCodecSelector`
|
||||
- 默认 `MediaItem.fromUri(url)`,未自定义 `RtspMediaSource.Factory`(Media3 默认 UDP 优先、收不到数据自动回退 TCP interleaved)
|
||||
- 本地模块 **`rtspclientlibrary/`**(拷贝自 rtsp-client-android v5.6.5 的 `library-client-rtsp`),实现见 `video/RtspVideoPlayer.kt`(Compose 包装 `RtspSurfaceView` + AndroidView)
|
||||
- 架构:RTSP **仅 TCP interleaved**(无 UDP)→ RTP 解析(H.264/H.265)→ `FrameQueue(20)` → MediaCodec 直解 → 立即渲染。**无播放时钟 pacing,帧到即解即渲染** = 零缓冲低延迟
|
||||
- `VideoCodec` 枚举(AUTO/FORCE_HW/FORCE_SW_H264/FORCE_SW_H265)→ `DecoderType.HARDWARE/SOFTWARE`(库仅区分硬/软解,FORCE_SW_* 不支持按 MIME 分别指定)
|
||||
- 自动重连:指数退避 1s→30s(URL/codec 变化重置);`RtspSurfaceView` 无内置重连,重连逻辑在 Composable 层
|
||||
- 延迟统计:轮询 `rtspView.statistics` 每 2s 输出 `videoDecoderLatencyMsec`/`networkLatencyMsec` 到 Logcat(tag `RtspVideoPlayer`)
|
||||
- 本地修改(相对上游):
|
||||
- **CSD 参数集顺序修正**:`vps+sps+pps`(上游为 `sps+pps+vps`,H.265 起播黑屏根因)
|
||||
- **codecType 按实际 MIME 标记**(上游硬编码 H264,H.265 无法解析宽高)
|
||||
- **`FrameQueue(60)` → `FrameQueue(20)`**(压低积压延迟)
|
||||
- 剔除 camera 依赖(删除 `RtspImageView`/bitmap 渲染路径,仅保留 SurfaceView 路径)
|
||||
|
||||
### 延迟问题与决策(2026-08 调研结论)
|
||||
|
||||
- 现象:RTSP/TCP 延迟高、缓冲大;H.265 解码不高效
|
||||
- **根因**:不是解码器也不是 TCP 协议本身,而是 ExoPlayer 播放器架构(缓冲水位 + presentationTime pacing 渲染同步)为"平滑播放"设计,非实时
|
||||
- **决策**:弃用 ExoPlayer 的视频渲染路径,集成第三方库 **rtsp-client-android**(alexeyvasilyev,v5.6.5,纯 Kotlin 零缓冲直出架构)
|
||||
- 库源码位于:`/tmp/rtsp-client-android`(工作区外临时目录,后续需拷入项目,见 TODO)
|
||||
- 上游源码备份:`/tmp/rtsp-client-android`(工作区外临时目录);本地拷贝见 `rtspclientlibrary/`
|
||||
|
||||
### rtsp-client-android 关键技术要点(集成时必读)
|
||||
|
||||
- 架构:RTSP **仅 TCP interleaved**(无 UDP)→ RTP 解析(H.264/H.265)→ `FrameQueue(60)` → MediaCodec 直解 → 立即渲染。**无播放时钟 pacing,帧到即解即渲染** = 零缓冲低延迟(宣传 20ms 解码延迟)
|
||||
- 架构:RTSP **仅 TCP interleaved**(无 UDP)→ RTP 解析(H.264/H.265)→ `FrameQueue` → MediaCodec 直解 → 立即渲染。**无播放时钟 pacing,帧到即解即渲染** = 零缓冲低延迟(宣传 20ms 解码延迟)
|
||||
- H.265:`RtpH265Parser` 支持 single NAL + FU 分片重组;**AP 聚合包未实现**(有 TODO)
|
||||
- 硬解优先,`MediaCodecUtils.getLowLatencyDecoder` 挑专用低延迟解码器,失败自动回退软解;`MediaCodecHelper.setDecoderLowLatencyOptions` 设 `KEY_LOW_LATENCY`
|
||||
- 实验性 SPS 改写(仅 H.264):`maxDecFrameBuffering=1, numReorderFrames=0`,可砍半部分硬解器延迟
|
||||
- 实验性 SPS 改写(仅 H.264):`maxDecFrameBuffering=1, numReorderFrames=0`,可砍半部分硬解器延迟(已在 Composable 层开启)
|
||||
- **内置延迟统计**:`RtspSurfaceView.statistics` → `videoDecoderLatencyMsec`(解码渲染)+ `networkLatencyMsec`(网络),无需自写埋点
|
||||
- **已知坑**:`RtspProcessor` 拼 CSD 顺序为 `sps+pps+vps`(H.265 标准应为 VPS→SPS→PPS),多数硬解器容错,若 H.265 起播黑屏第一嫌疑在此
|
||||
- 依赖:`androidx.media3:media3-exoplayer`(仅用工具类如 NalUnitUtil,可剔除)、`androidx.camera`(YUV→BMP)、`org.jcodec`(SPS 改写)
|
||||
- **已知坑(均已本地修复)**:`RtspProcessor` 拼 CSD 顺序为 `sps+pps+vps`(H.265 标准应为 VPS→SPS→PPS,本地已改);`codecType` 硬编码 H264(本地已按 MIME 修正)
|
||||
- 依赖:`androidx.media3:media3-exoplayer`(仅用工具类 NalUnitUtil/MediaCodecUtil)、`org.jcodec`(SPS 改写);已剔除 `androidx.camera`(YUV→BMP 路径,本项目用 SurfaceView 直出无需)
|
||||
- 库活跃:2026-06 更新(v5.6.5),全库约 6300 行,无 JNI/无预编译 so,全量可改
|
||||
|
||||
---
|
||||
@@ -183,7 +190,7 @@ gradlew installDebug
|
||||
|
||||
- 虚拟摇杆库:本地模块 `joysticklibrary/`(Compose 原生,Kotlin DSL)
|
||||
- JSON 序列化:`kotlinx-serialization-json:1.7.3`
|
||||
- RTSP 播放:`androidx.media3:media3-exoplayer-rtsp:1.5.1`
|
||||
- RTSP 播放:本地模块 `rtspclientlibrary/`(rtsp-client-android,内部依赖 `media3-exoplayer:1.5.1` + `jcodec:0.2.5`)
|
||||
- 持久化:`androidx.datastore:datastore-preferences:1.1.3`
|
||||
- 导航:`androidx.navigation:navigation-compose:2.8.6`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user