Android 流媒体播放内核实践:基于 Media3(ExoPlayer)的 features/player 模块解析
本文仅围绕本仓库 features/player 模块(包名 com.example.playercore)展开,对应职责可概括为:网络流拉取与建源、解码与渲染策略、播放控制与状态管理、缓冲与卡顿治理、断线自动恢复。适合作为掘金技术社区长文或团队内部设计说明的底稿。
依赖版本:androidx.media3:media3-exoplayer:1.9.0(见 features/player/build.gradle)。
一、模块职责与对外 API
| 类型 | 说明 |
|---|---|
| StreamPlayerEngine | 封装 ExoPlayer 生命周期、play(url) / onStart / onStop / release,统一向外抛出业务状态 |
| StreamPlayerConfig | 缓冲档位、重连策略、缓冲卡死超时、遥测开关等 |
| StreamPlayerState | IDLE / PREPARING / RECONNECTING / BUFFERING / READY / PLAYING / PAUSED / ENDED / ERROR |
| LatencyProfile | 映射到 DefaultLoadControl 与 DefaultRenderersFactory 的一组可配置参数 |
| ReconnectPolicy | 最大重试、指数退避、抖动(多路同时断流时减轻「惊群」) |
UI 层(如 PlayerView)放在 App 模块,通过 StreamPlayerEngine.player 绑定即可;渲染 Surface 不由本库直接创建,符合「引擎与界面分离」。
二、建源与「拉流」:从 URL 到 MediaSource
引擎根据协议分支(StreamPlayerEngine.applyMediaForUrl / prepareRtsp):
| 协议 | 实现要点 |
|---|---|
| RTSP | RtspMediaSource.Factory(),setForceUseRtpTcp(true) —— 固定走 RTP over TCP(interleaved),利于穿越典型防火墙/NAT |
| HLS / 普通 HTTP | MediaItem.fromUri + 默认 DefaultMediaSourceFactory;需依赖 media3-exoplayer-hls |
| RTMP | 同样走 MediaItem + 内置对 RTMP 数据源的支持;依赖 media3-datasource-rtmp,本工程排除了未做 16 KB 页面对齐 的默认 native,改用 JitPack fork(见 features/player/build.gradle 注释),以适配 Google Play 新要求 |
小结:「订阅」在 ExoPlayer 语义里体现为 设置 MediaItem 或 MediaSource 并 prepare();网络拉取由 DataSource 链 + 各协议 MediaSource 完成,业务侧只需给 合法 URL。
三、解码与渲染:RenderersFactory 与音画同步
创建播放器时(createPlayer):
- DefaultRenderersFactory:EXTENSION_RENDERER_MODE_ON + setEnableDecoderFallback(true) —— 硬解优先,不支持时回退,减少「有流无画面」类问题。
- setAllowedVideoJoiningTimeMs:与 LatencyProfile 绑定,影响视频轨拼接等待(直播场景常用)。
- setAudioAttributes + setHandleAudioFocus:统一 USAGE_MEDIA 与焦点策略,作为基础音画同步与多路场景下的可配置项。
- ABR:DefaultTrackSelector + DefaultBandwidthMeter,为 HLS 等自适应流提供带宽估计(本模块未再强行改码率策略,避免与 Media3 版本 API 差异冲突)。
画面输出:由宿主 PlayerView / SurfaceView / TextureView 与 player 绑定完成;本模块只暴露 ExoPlayer 实例。
四、播放控制与状态管理
4.1 控制面
- play(url):记录 lastPlayedUrl,applyMediaForUrl,prepare(),playWhenReady = true。
- onStart / onStop:与 Activity 生命周期联动 playWhenReady。
- release:移除监听、stop、清 item、release,并取消重连与缓冲诊断任务。
4.2 状态面(Player.Listener)
将 Player.STATE_* 与 isPlaying 组合映射为 StreamPlayerState,例如:
- BUFFERING → 对外 BUFFERING
- READY + isPlaying → PLAYING;否则 READY 或 PAUSED
- RECONNECTING:非 Exo 原生状态,由本引擎在安排重连时 主动派发,便于 UI 展示「退避等待中」
这样上层只需订阅 onStateChanged,即可做 加载圈、重连文案、错误提示 等,而无需直接处理 Exo 细节。
五、卡顿与延迟:LoadControl 与 LatencyProfile
5.1 为何需要「双份」缓冲参数
Media3 对 流媒体 会单独使用 streaming 一套缓冲阈值。若只调用 setBufferDurationsMs,仍可能出现 ahead 数秒 的现象。本模块在 buildLoadControl 中 同时设置:
- setBufferDurationsMs(min, max, forPlay, afterRe)
- setBufferDurationsMsForStreaming(…)(与 profile 对齐)
并可选:
- setPrioritizeTimeOverSizeThresholds 与 setPrioritizeTimeOverSizeThresholdsForStreaming:更贴 时间阈值(低延迟场景)
- setBackBuffer:工业直播常 0 回看
- setTargetBufferBytes:按字节限制堆积(与码率、弱网相关)
5.2 档位枚举 LatencyProfile
提供多档 min/max buffer、起播与重缓冲门槛、回看、target bytes、allowedVideoJoining、trimBufferAhead 等(见 LatencyProfile.kt)。
5.3 可选:ahead 裁剪(trimBufferAheadMs)
当 bufferedPosition − currentPosition 超过阈值时,通过 seekTo(buffered − trimMs) 丢掉多余前向缓冲,降低观播延迟;弱网下可能与 BUFFERING 互相触发,需按现场评估是否开启。
5.4 缓冲诊断
enableBufferDiagnosticsLog 为 true 时周期性打印 currentPosition / bufferedPosition,便于 Logcat 过滤分析 ahead 与卡顿关系。
六、错误恢复:重连策略与三种触发源
所有自动重连走 scheduleReconnectWithPolicy,共用 reconnectAttemptIndex,与 ReconnectPolicy.maxRetries 对齐:
- onPlayerError:典型网络/解码错误。
- reconnectOnStreamEnded:推流结束导致 ENDED(无 Error),对 rtsp:// / rtmp:// 可选 自动重拉(避免与点播 ENDED 混淆)。
- bufferingStallTimeoutMs:长时间 BUFFERING 且无 Error(弱网卡死),超时后 主动重拉。
退避延迟由 ReconnectPolicy.computeDelayMsForAttempt 计算(指数退避 + 抖动)。
超过最大次数:不再排队重连,对外 ERROR(文案区分错误原因与「已达最大重连次数」);恢复成功播放(进入 READY 等逻辑)后 计数清零。
七、小结表
| 岗位关键词 | 本模块落点 |
|---|---|
| 视频流拉取 | MediaItem / RtspMediaSource、HLS/RTMP 依赖扩展 |
| 解码渲染 | DefaultRenderersFactory、硬解优先与回退、allowedVideoJoiningTimeMs |
| 播放控制 | play / prepare / playWhenReady、生命周期封装 |
| 播放状态 | StreamPlayerState + Player.Listener 映射 |
| 卡顿优化 | DefaultLoadControl、streaming 对齐、可选 trim、诊断日志 |
| 画面呈现 | 由宿主绑定 PlayerView;引擎暴露 ExoPlayer |
八、扩展阅读(仓库内)
- features/player/src/main/java/com/example/playercore/StreamPlayerEngine.kt —— 实现入口
- StreamPlayerConfig.kt / LatencyProfile.kt / ReconnectPolicy.kt —— 可调参数
- 根目录 README.md —— 工程级使用说明(含非 player 模块,阅读时注意筛选)
版权声明:本文描述基于开源工程内部实现整理;Media3 以 Google AndroidX 许可证为准。转载至掘金等平台请注明原文与仓库出处。