26、移动端适配:Android WebRTC、iOS WebRTC、移动端性能调优

移动端做WebRTC,说白了就是一场与硬件和系统的博弈。

我最早在Android上跑通第一个视频通话时,心里还挺美。结果一上真机,发热、卡顿、掉帧全来了。嗯,那时候我才意识到——桌面端的代码,搬到手机上,根本跑不动。

这一章,我就把Android和iOS上WebRTC的适配要点,以及性能调优的实战经验,一次性讲清楚。

Android WebRTC:碎片化是最大的敌人

Android的碎片化问题,做过的都懂。芯片、摄像头、编码器、系统版本,排列组合下来几百种。WebRTC在Android上,最头疼的就是硬编硬解的兼容性。

核心原则:不要依赖默认的编解码器选择逻辑。一定要自己做降级策略。

我个人习惯的做法是:

  • 先尝试H264硬编,因为兼容性最好
  • 如果硬编失败,降级到H264软编
  • 再不行,才考虑VP8

代码上,初始化PeerConnectionFactory时,要显式指定编码器优先级:

// 推荐:显式设置编码器优先级
PeerConnectionFactory.InitializationOptions initOptions =
    PeerConnectionFactory.InitializationOptions.builder(context)
        .setFieldTrials("WebRTC-H264HighProfile/Enabled/")
        .createInitializationOptions();
PeerConnectionFactory.initialize(initOptions);

// 创建工厂时指定编码器
VideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory(
    rootEglBase.getEglBaseContext(),
    true,   // enableIntelVp8Encoder
    true    // enableH264HighProfile
);

避坑指南:我曾经在小米某款机型上,硬编H264一直崩溃。后来发现是MediaCodec的异步模式没处理好。建议统一使用同步模式,配合超时机制。

iOS WebRTC:注意后台和权限

iOS这边,WebRTC的集成相对顺滑一些。但有两个坑,我每次都要提醒团队注意。

第一个坑:后台音频。App切到后台,WebRTC的音频流会断。解决方案是启用Audio Session的后台模式:

// 启用后台音频
RTCAudioSession *session = [RTCAudioSession sharedInstance];
[session lockForConfiguration];
NSError *error = nil;
[session setCategory:AVAudioSessionCategoryPlayAndRecord
         withOptions:AVAudioSessionCategoryOptionMixWithOthers |
                     AVAudioSessionCategoryOptionAllowBluetooth |
                     AVAudioSessionCategoryOptionDefaultToSpeaker
               error:&error];
[session setActive:YES error:&error];
[session unlockForConfiguration];

第二个坑:摄像头权限。iOS 14之后,权限弹窗的时机变了。一定要在调用摄像头之前,先检查权限状态。否则直接黑屏。

注意:iOS上WebRTC的硬编解码,对A系列芯片优化得很好。但如果你用VP9,发热会非常明显。我个人建议iOS端只用H264。

移动端性能调优:三板斧

性能调优,我总结了三个方向。你想想看,移动端资源就那么点,CPU、内存、网络,哪个都不能爆。

1. 分辨率与帧率的动态调整

不要固定720P 30帧。要根据网络和CPU负载,动态降级。我常用的策略是:

  • 网络抖动时,先降分辨率(从720P降到480P)
  • CPU超过80%时,降帧率(从30帧降到15帧)
  • 两者都差时,同时降

WebRTC自带的Simulcast可以帮我们做这件事,但要注意移动端不要开太多流。我一般只开两层:高清层和低清层。

2. 硬编硬解的正确使用姿势

硬编硬解省电,但坑也多。我遇到过的问题包括:

  • 编码器输出延迟(尤其是低端芯片)
  • 解码器花屏(切换分辨率时)
  • 内存泄漏(MediaCodec没及时释放)

我的解决方案是:每次切换分辨率时,重建编码器/解码器。不要复用旧的实例。

// 切换分辨率时,重建编码器
private fun recreateEncoder(width: Int, height: Int) {
    videoEncoder?.release()
    videoEncoder = VideoCodec.createEncoder(
        VideoCodecType.H264,
        width, height,
        30, // fps
        2000 // bitrate kbps
    )
}

3. 网络抗丢包与带宽估计

移动网络丢包率经常到5%以上。WebRTC默认的NACK+FEC组合,在弱网下效果还行。但我建议做两件事:

  • 开启Transport-CC(传输层拥塞控制),比GCC更准
  • 设置合理的FEC比例,不要超过20%,否则带宽浪费太大

我的经验:在4G网络下,丢包率3%以内,WebRTC默认配置就能扛住。超过5%,建议开启SVC(可伸缩视频编码),让接收端自己决定收多少数据。

移动端适配知识体系

下面这张图,是我自己整理的移动端WebRTC适配核心逻辑。你看一眼,基本就全了。

移动端WebRTC适配核心逻辑 Android适配 iOS适配 性能调优 硬编兼容 降级策略 MediaCodec 碎片化处理 后台音频 权限管理 AudioSession A芯片优化 动态分辨率 帧率控制 硬编重建 网络抗丢包 核心目标:低延迟 + 低功耗 + 高兼容 每个模块内部都有降级和容错机制,不要依赖单一方案

最后说几句

移动端WebRTC适配,没有银弹。每个机型、每个系统版本,都可能出幺蛾子。我的建议是:

  • 建立自己的兼容性测试矩阵
  • 线上加日志,出了问题能快速定位
  • 不要追求极致画质,流畅度优先

嗯,这一章就到这里。移动端的水很深,但掌握了这些核心思路,你至少不会翻船。


公众号:蓝海资料掘金营,微信deep3321