12. 流媒体协议支持:HTTP Live Streaming (HLS)、MPEG-DASH、RTSP协议栈解析
流媒体协议这块,说实话是Stagefright里最让我头疼又最兴奋的部分。为什么?因为你要面对的不是一个协议,而是三个完全不同的“祖宗”——HLS、DASH、RTSP。每个都有自己的脾气,每个都有一堆坑等着你踩。
我个人习惯把这三者分成两类:基于HTTP的(HLS和DASH)和基于RTP的(RTSP)。前者走的是“下载-播放”模式,后者走的是“实时流”模式。你想想看,这两者的设计哲学完全不同,但Stagefright硬是把它们整合到了一套框架里。
12.1 HLS:苹果的“封闭”标准
HLS(HTTP Live Streaming)是苹果搞出来的。我记得2010年左右第一次接触它时,心里还在嘀咕:这玩意儿不就是把视频切成小段,然后用一个m3u8文件索引一下吗?后来才发现,事情远没那么简单。
HLS的核心是m3u8播放列表。这个列表有两种:
- 主播放列表(Master Playlist):包含多个不同码率的流,供客户端自适应选择
- 媒体播放列表(Media Playlist):包含实际的TS或fMP4片段列表
在Stagefright里,HLS的解析流程大致是这样的:
// 伪代码:HLS解析核心流程
HLSDataSource::parsePlaylist() {
// 1. 下载m3u8文件
// 2. 解析标签(#EXTINF, #EXT-X-STREAM-INF等)
// 3. 构建片段列表
// 4. 根据带宽和分辨率选择最佳流
// 5. 开始下载并解码第一个片段
}
关键点:HLS的#EXT-X-DISCONTINUITY标签是个大坑。它表示前后片段可能编码参数不同,比如分辨率变了、采样率变了。如果你不处理这个,播放器会直接崩掉。
我曾经在一个项目中遇到这样的问题:用户上传的视频,前10秒是720p,后面突然变成了480p,而且音频采样率从44100变成了48000。播放器直接花屏加无声。后来我加了个discontinuity检测逻辑,每次遇到这个标签就重新初始化解码器,问题才解决。
12.2 MPEG-DASH:开放但复杂
DASH(Dynamic Adaptive Streaming over HTTP)是MPEG推出的标准,说白了就是HLS的“官方版”。它用MPD(Media Presentation Description)文件来描述流媒体内容,格式是XML。
DASH比HLS灵活得多,但也复杂得多。一个MPD文件可以包含:
- Period:时间段,每个Period可以有不同的编码参数
- AdaptationSet:自适应集,比如视频流、音频流、字幕流
- Representation:具体码率的流,包含带宽、分辨率等信息
- Segment:实际的媒体片段
在Stagefright里,DASH的解析是通过DashPlayer和MPDParser完成的。我记得有一次调试一个DASH直播流,发现MPD文件里minimumUpdatePeriod设成了0,结果播放器不停地重新下载MPD,导致卡顿。后来我加了个缓存机制,才搞定。
小技巧:DASH的SegmentTimeline模式比SegmentTemplate模式更精确,但解析起来也更复杂。如果你遇到时间戳不对齐的问题,多半是这里出的错。
12.3 RTSP:实时流的“老大哥”
RTSP(Real Time Streaming Protocol)是这三个里最“古老”的。它不像HLS和DASH那样走HTTP下载,而是走RTP/UDP实时传输。说白了,RTSP是控制协议,真正传数据的是RTP。
RTSP的典型流程是:
- DESCRIBE:客户端请求媒体描述(SDP)
- SETUP:建立传输通道(RTP端口)
- PLAY:开始播放
- PAUSE/TEARDOWN:暂停/停止
在Stagefright里,RTSP的支持是通过ARTSPController和ARTPConnection实现的。嗯,这里要注意:RTSP的NAT穿透是个大问题。我曾经在一个项目中,用户在内网用RTSP看监控,结果视频流死活出不来。后来发现是RTP端口被防火墙封了,改成了TCP隧道才解决。
警告:RTSP的Transport头里,interleaved模式(RTP over RTSP)虽然能解决NAT问题,但会增加延迟。如果你做的是低延迟场景(比如无人机图传),建议还是用UDP。
12.4 协议栈对比:一张表说清楚
| 特性 | HLS | MPEG-DASH | RTSP |
|---|---|---|---|
| 传输协议 | HTTP | HTTP | RTP/UDP |
| 描述文件 | m3u8 | MPD (XML) | SDP |
| 自适应码率 | 支持 | 支持 | 不支持(需扩展) |
| 延迟 | 高(10-30秒) | 中(5-15秒) | 低(1-3秒) |
| 复杂度 | 低 | 高 | 中 |
| Stagefright组件 | HLSDataSource | DashPlayer | ARTSPController |
12.5 核心逻辑:一张SVG图
下面这张图展示了Stagefright里三个协议栈的核心流程。我特意把HLS和DASH归为“HTTP下载”类,RTSP归为“RTP实时”类,这样你一眼就能看出它们的区别。
12.6 避坑指南:我踩过的那些坑
做流媒体协议支持这几年,我踩过的坑能写一本书。这里挑几个典型的:
- HLS的#EXT-X-KEY:加密的HLS流,密钥获取失败会导致播放黑屏。我曾经遇到一个CDN配置问题,密钥URL返回了404,排查了整整两天。
- DASH的SegmentBase:有些DASH流用
SegmentBase而不是SegmentList,这意味着整个视频只有一个大文件,需要靠indexRange来定位。如果你解析错了,播放器会直接跳到错误位置。 - RTSP的Keep-Alive:RTSP连接如果长时间不发送
OPTIONS请求,服务器会断开。我见过一个项目,播放器在后台挂了一夜,第二天早上起来发现流断了,就是因为没有做心跳检测。
总结一下:HLS和DASH适合点播和直播,RTSP适合低延迟场景。在Stagefright里,这三者的实现路径完全不同,但最终都汇聚到MediaCodec解码。理解它们的差异,你就能在项目中做出正确的选择。