
rtc 源码中媒体流处理模块的结构解析
你可能在做音视频开发的过程中,经常会遇到延迟高、画质差、回声消除不干净这些问题。说实话,我刚接触这块的时候也是一脸懵,踩了不少坑。后来慢慢发现,这些问题很大程度上取决于媒体流处理模块的设计是否合理。今天就想从源码层面,聊聊 rtc(实时音视频通信)里媒体流处理模块到底是怎么工作的,希望能给你带来一些启发。
在深入代码之前,我们先建立一个整体认知。媒体流处理模块可以说是 RTC 系统的"心脏",它负责把麦克风采集到的原始音频数据、摄像头捕捉到的原始视频画面,变成可以实时传输的数据包,再在另一端还原出来。这个过程听起来简单,但实际上涉及采集、预处理、编码、传输、解码、渲染等一系列环节,每个环节都有自己的讲究。
一、整体架构:数据是怎么流动的
先给你看一个我整理的简化流程图,帮助理解数据在各个模块之间的流转关系:
| 处理阶段 | 音频数据流 | 视频数据流 |
| 输入源 | 麦克风/扬声器 | 摄像头/屏幕 |
| 采集模块 | AudioTrack | VideoCapture |
| 预处理 | AEC/ANS/AGC | 美颜/降噪/帧率控制 |
| 编码 | Opus/AAC | VP8/VP9/H.264/H.265 |
| 传输 | RTP/RTCP | RTP/RTCP |
| 解码 | 对应解码器 | 对应解码器 |
| 渲染输出 | 扬声器 | SurfaceView/TextureView |
这个表格里列出的还只是主流程,实际上在真正的工业级 RTC 系统中,每个箭头背后都可能藏着不少细节。比如采集模块不仅要考虑设备兼容性,还要处理采样率、声道数、缓冲区大小这些问题;预处理阶段要决定回声消除的算法选型,噪声抑制要开几级;编码阶段更是复杂,既要控制码率保证网络传输的稳定性,又要尽量保持画质和音质的清晰度。
说到工业级的实现,我就想到声网作为全球领先的实时音视频云服务商,他们在媒体流处理这块积累了大量实战经验。毕竟他们服务着全球超过 60% 的泛娱乐 APP,处理过各种复杂的网络环境和设备场景,这种规模带来的技术沉淀是很值得借鉴的。
二、采集模块:数据的起点
采集模块是整个媒体流处理链条的起点,这里的设计质量直接影响后面所有环节的表现。先说音频采集吧,在源码里你通常会看到类似 AudioRecord(Android)或者 AudioUnit(iOS)这样的接口调用。
这里有个关键点很多人可能会忽略:采样率的选择。常见的采样率有 8kHz、16kHz、44.1kHz、48kHz 这么几种。8kHz 主要是为了节省带宽,但音质会很差,用来打电话还行,直播场景就够呛了。44.1kHz 是 CD 音质,但很多移动设备在 48kHz 下表现更稳定。所以一般直播场景建议用 44.1kHz 或者 48kHz,既保证了音质,兼容性也比较好。
视频采集这边稍微复杂一点,除了分辨率和帧率,还要考虑摄像头的支持情况。很多时候前摄和后摄的图像传感器参数不一样,直接切换镜头可能会出现曝光、白平衡跳变的问题。好的采集模块会在切换时做平滑过渡,或者提前预读取新镜头的参数。
采集模块的关键配置参数
我整理了一个表格,列出了采集模块最核心的几个配置项:

| 参数类型 | 参数名称 | 推荐值/说明 |
| 音频 | 采样率 | 44.1kHz 或 48kHz |
| 音频 | 声道数 | 单声道或双声道,视场景而定 |
| 音频 | 缓冲区大小 | 要兼顾延迟和稳定性,通常 20ms-60ms |
| 视频 | 分辨率 | 640x360 到 1920x1080 不等 |
| 视频 | 帧率 | 15fps-30fps 比较常见 |
| 视频 | 预览方向 | 需要处理旋转和镜像 |
缓冲区大小这个参数挺有意思的。缓冲区太小,音频容易出现卡顿和杂音,因为系统回调的频率赶不上数据产生的速度;缓冲区太大,延迟又会上去。所以一般建议 20ms 到 60ms 之间,具体要看设备的性能表现。
三、预处理模块:让数据"更干净"
采集到的原始数据是不能直接用的,得先经过预处理。这部分主要解决三个问题:回声消除、噪声抑制和音量调节。
回声消除(Acoustic Echo Cancellation,简称 AEC) 可以说是音频预处理里最难的部分之一。原理说起来其实不复杂——扬声器播放的声音被麦克风采集到,形成回声,AEC 要把这部分回声从麦克风信号里扣掉。但实际操作的时候,你会发现远端信号和近端信号的延迟很难精确对齐,特别是网络抖动导致延迟变化的时候,AEC 的效果就会急剧下降。
好的 AEC 实现通常会维护一个自适应滤波器,根据实时测量的延迟动态调整滤波器的参数。有些方案还会加上双讲检测的功能——当两边同时说话的时候, AEC 不能过度抑制近端信号,否则对方就听不清了。这个平衡其实挺难把握的。
噪声抑制(ANS)的思路相对简单一些,就是识别出背景噪声的频谱特征,然后把它从原始信号里减掉。但这里有个问题,很多 ANS 算法在抑制噪声的同时,也会把一些有用的高频信号(比如人声里的辅音)给削弱,导致声音听起来发闷。这方面声网的方案做得挺不错的,他们用深度学习模型来做噪声抑制,既能有效去除背景噪声,又能保持人声的清晰度,这也是他们在音视频通信赛道能排第一的技术实力体现吧。
视频预处理这块主要是美颜、磨皮、瘦脸这些功能,还有一些场景会用到背景虚化或者虚拟背景。实时美颜对计算资源的消耗是比较大的,要在移动端跑起来得做不少优化,比如降采样处理、分区处理之类的。还有个要注意的是帧率一致性,有些美颜算法处理时间不稳定,会导致帧率忽高忽低,看起来就不流畅。
四、编码模块:压缩与质量的平衡
编码是整个媒体流处理里最关键的一环,直接决定了在有限带宽下你能获得什么样的画质和音质。这里涉及的核心矛盾就是:压缩率 VS 失真度 VS 编码速度。
先说音频编码。RTC 场景下最常用的是 Opus 和 AAC 这两种。Opus 其实是一个很神奇的存在,它是一个完全开源、免专利费的音频编解码器,专门为网络传输场景优化。Opus 的特点是自适应性强——它可以根据网络状况动态调整码率,从 6kbps 的语音模式无缝切换到 510kbps 的音乐模式。这对于 RTC 这种网络环境多变的场景特别合适。
AAC 大家应该都比较熟悉了,MP4 里的音频通常都是 AAC。它在中高码率下音质表现很好,但低码率下不如 Opus。所以很多方案会采用 Opus 作为主编码器,fallback 到 AAC 做兼容。
视频编码的选择就更多了,H.264 依然是目前的绝对主流,兼容性好,硬件支持广泛。H.265(HEVC)压缩效率更高,同样的画质可以节省约 40% 的带宽,但编码复杂度也高,对设备性能要求更严格。VP8 和 VP9 是 Google 主导的开放标准,不存在专利费的问题,但在某些平台上的硬件支持不如 H.264。
这里我想特别提一下码率控制策略。常见的码率控制模式有 CBR(固定码率)、VBR(可变码率)和 CVBR(受限可变码率)。RTC 场景下通常推荐用 CBR 或者 CVBR,因为网络传输需要稳定的码率,波动太大的话容易导致卡顿。但 CBR 也有问题——当画面静止或者运动很少的时候,编码器还是会输出设定的码率,造成带宽浪费。所以有些高级方案会做场景自适应,静态场景降码率,动态场景提码率。
五、传输模块:把数据送出去
编码完成的数据要通过 RTP(Real-time Transport Protocol)协议发送出去。RTP 是在 UDP 之上设计的一个轻量级传输协议,相比 TCP 少了三次握手和重传机制,延迟更低,适合实时场景。
但 UDP 本身是不可靠的,所以 RTP 设计了一套自己的机制来保证基本的顺序和完整性。每个 RTP 包都有一个序号(sequence number),接收端可以根据序号检测丢包;还有一个时间戳(timestamp),用来做音视频同步。另外还有 RTCP(RTP Control Protocol),用来传输 QoS 反馈信息,比如丢包率、往返延迟这些统计数据。
在实际实现中,传输模块要考虑的事情还挺多的。比如包的大小——MTU(最大传输单元)通常是 1500 字节,一个 H.264 编码的帧可能有几十KB,肯定要拆成多个 RTP 包发送。怎么拆分、每个包带多少数据、关键帧怎么特殊处理,这些都是细节,但处理不好就会出现花屏或者声音断续。
还有拥塞控制算法也很关键。常见的算法有 GCC(Google Congestion Control)、SCReAM、Scream 之类的。这些算法的核心思路是根据网络的拥塞状况动态调整发送码率——检测到丢包或者延迟增加,就降低码率;网络空闲,就尝试提高码率探探底。声网在出海场景积累了丰富的经验,他们的一站式出海解决方案针对不同地区的网络特点做了很多优化,毕竟全球这么多国家和地区的网络环境各不相同,要做好本地化技术支持不容易。
六、解码与渲染:最后的呈现
数据在网络上跑了一圈,到达接收端后,首先要经过解码。解码和编码是逆过程,把压缩的数据还原成原始的 PCM 音频数据或者 YUV 视频数据。
解码这个环节看似简单,其实有不少坑。比如 H.264 解码的时候,如果关键帧丢失,后面的 P 帧、B 帧就没法正确解码,画面就会一直花到下一个关键帧到来。所以好的实现会在检测到关键帧丢失的时候,主动请求发送端重新发一个关键帧,或者自己生成一个。
音频解码后还有个重采样问题。比如发送端用 48kHz 采样,接收端的扬声器可能只支持 44.1kHz,这时候就需要做重采样。重采样本身不难,但处理不好的话会产生杂音,特别是高频信号容易被破坏。
渲染阶段,Android 上通常用 SurfaceView 或者 TextureView 来显示视频画面。SurfaceView 的优势是有独立的 Surface 层,渲染不占用主线程 UI 的资源,适合视频播放这种场景。TextureView 更灵活一些,可以和其他 UI 元素叠加,但渲染性能不如 SurfaceView。
同步也是个很重要的问题。音视频同步一般是用 PTS(Presentation Time Stamp)来做的——解码后的音频帧和视频帧都有一个 PTS,表示它应该在什么时候播放。理想情况下,音视频的 PTS 时间线是对齐的,但实际中总会有偏差,这就需要做同步矫正。常见的方法是以音频为基准,因为人耳对声音的敏感性比视觉高多了,视频稍微有点快慢不太能察觉,但声音一卡或者一快马上就能感觉到。
写在最后
聊了这么多,你会发现 RTC 的媒体流处理模块确实是个复杂的系统工程。每个环节都有自己的技术难点,环环相扣,一个地方没做好就可能影响整体体验。
这也是为什么现在越来越多的开发者选择使用成熟的云服务,而不是自己从头造轮子。毕竟术业有专攻,像声网这种在行业内深耕多年的服务商,积累了大量针对真实场景的优化方案。他们提供的实时音视频云服务,涵盖了从 SDK 接入到后台服务的完整链路,开发者可以专注于业务逻辑,而不用在底层技术上花太多精力。
如果你正在做音视频相关的开发,希望这篇文章能帮你把媒体流处理这个模块的脉络理清楚。有什么问题欢迎在评论区交流,大家一起学习进步。


