移动端WebRTC:Android与iOS集成实战

说实话,移动端WebRTC开发是我个人觉得最"坑"也最有意思的部分。为什么?因为桌面端跑得好好的代码,一上手机就各种翻车。我最早做移动端WebRTC时,光是把Demo跑起来就折腾了两天——嗯,后来才发现是权限没配全。

这一章,我把Android和iOS的集成要点、性能优化技巧、还有那些年我踩过的坑,一次性讲清楚。

Android WebRTC集成:从零开始

Android集成WebRTC,说白了就三步:引入库、配权限、写代码。但每一步都有讲究。

1. 引入WebRTC库

我个人习惯用Google官方的Maven仓库,版本号跟着Chrome走就行。

// build.gradle (app级别)
dependencies {
    implementation 'org.webrtc:google-webrtc:1.0.32006'
}
注意: 我曾经遇到过版本冲突的问题——项目中同时用了旧版WebRTC和某个第三方SDK,结果编译直接报错。建议统一版本,别混用。

2. 权限配置

Android 6.0以上需要动态申请权限。这里有个坑:CAMERA和RECORD_AUDIO是必须的,但很多人忘了INTERNET权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

3. 核心代码骨架

初始化PeerConnectionFactory是第一步。你想想看,没有工厂,哪来的连接对象?

// 初始化PeerConnectionFactory
PeerConnectionFactory.InitializationOptions initOptions =
    PeerConnectionFactory.InitializationOptions.builder(context)
        .setFieldTrials("")
        .createInitializationOptions();
PeerConnectionFactory.initialize(initOptions);

PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
PeerConnectionFactory factory = PeerConnectionFactory.builder()
    .setOptions(options)
    .createPeerConnectionFactory();
小技巧: 初始化最好放在Application的onCreate里做一次,别每次创建连接都重新初始化。我见过有人这么干,结果内存直接炸了。

iOS WebRTC集成:Swift与OC的抉择

iOS端集成WebRTC,我个人推荐用CocoaPods。为什么?省心。手动编译WebRTC的iOS版本,那编译时间够你喝三杯咖啡。

1. Podfile配置

platform :ios, '12.0'
target 'YourApp' do
  pod 'GoogleWebRTC'
end

这里要注意:iOS 12.0是最低要求。我试过在iOS 11上跑,结果某些API直接崩溃——说白了,苹果对旧版本的支持越来越少了。

2. 权限与后台模式

Info.plist里需要加两样东西:摄像头权限描述、麦克风权限描述。还有,如果你要做后台通话,记得开启Audio, AirPlay, and Picture in Picture后台模式。

<key>NSCameraUsageDescription</key>
<string>需要访问摄像头进行视频通话</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要访问麦克风进行语音通话</string>

3. Swift集成示例

import GoogleWebRTC

class VideoCallManager {
    private var factory: RTCPeerConnectionFactory?
    private var peerConnection: RTCPeerConnection?
    
    func setup() {
        // 初始化SSL(必须!)
        RTCInitializeSSL()
        
        // 创建工厂
        let encoderFactory = RTCDefaultVideoEncoderFactory()
        let decoderFactory = RTCDefaultVideoDecoderFactory()
        factory = RTCPeerConnectionFactory(
            encoderFactory: encoderFactory,
            decoderFactory: decoderFactory
        )
    }
}
警告: RTCInitializeSSL() 必须调用,而且只能调用一次。我见过有人每次创建连接都调一次,结果应用直接闪退。这个坑,我替你们踩过了。

移动端性能优化:别让手机变暖手宝

移动端性能优化,核心就三个字:省、稳、快。省电、稳定、快速。我总结了一张优化清单,直接拿去用。

优化项 具体做法 效果
分辨率自适应 根据网络带宽动态调整分辨率 减少卡顿,降低CPU负载
硬件编码优先 优先使用H.264硬件编码 降低功耗,提升帧率
帧率控制 非通话场景降低到15fps 省电效果明显
音频降噪 开启内置降噪算法 提升通话质量
内存管理 及时释放PeerConnection 防止OOM

分辨率自适应实现

说白了,就是根据网络状况动态调整。我习惯用WebRTC自带的带宽估计器,再结合自己的策略。

// 设置视频发送参数
RTCVideoEncoderSettings *settings = [[RTCVideoEncoderSettings alloc] init];
settings.name = @"H264";
settings.width = 640;
settings.height = 480;
settings.startBitrate = 500; // 初始500kbps
settings.maxBitrate = 1500;
settings.minBitrate = 100;
settings.maxFramerate = 30;

// 监听网络变化,动态调整
- (void)onNetworkChanged:(NSInteger)bitrate {
    if (bitrate < 300) {
        // 网络差,降低分辨率
        settings.width = 320;
        settings.height = 240;
    } else if (bitrate > 1000) {
        // 网络好,提升分辨率
        settings.width = 1280;
        settings.height = 720;
    }
}
经验之谈: 别把分辨率调得太频繁。我试过每秒钟调整一次,结果视频反而更卡了。建议每5-10秒评估一次,平滑过渡。

移动端常见问题:那些年我踩过的坑

问题1:Android端摄像头黑屏

我曾经遇到过这个问题,排查了半天,最后发现是摄像头被其他应用占用了。比如你开了微信视频,再打开自己的App,摄像头就黑了。

解决方案: 在打开摄像头前检查可用性,捕获异常后提示用户关闭其他应用。

问题2:iOS端音频突然消失

这个坑我印象特别深。有一次用户反馈说通话到一半,突然听不到声音了。后来发现是音频会话被系统中断了——比如来了个电话,或者闹钟响了。

解决方案: 监听AVAudioSession的 interruption 通知,中断结束后自动恢复音频。

// 监听音频中断
NotificationCenter.default.addObserver(
    self,
    selector: #selector(handleInterruption),
    name: AVAudioSession.interruptionNotification,
    object: nil
)

@objc func handleInterruption(_ notification: Notification) {
    guard let info = notification.userInfo,
          let type = info[AVAudioSessionInterruptionTypeKey] as? UInt else { return }
    
    if type == AVAudioSession.InterruptionType.ended.rawValue {
        // 恢复音频会话
        try? AVAudioSession.sharedInstance().setActive(true)
    }
}

问题3:内存泄漏

移动端WebRTC最容易出现的问题就是内存泄漏。PeerConnection、MediaStream、VideoTrack这些对象,如果不及时释放,内存会一直涨。

解决方案: 遵循"谁创建,谁释放"的原则。离开房间时,一定要调用close()和dispose()。

// 正确释放资源
peerConnection?.close()
peerConnection = nil
factory = nil
核心要点: 移动端WebRTC开发,集成只是第一步。真正的挑战在于性能优化和问题排查。记住:权限、内存、音频会话,这三个地方最容易出问题。我建议你在开发阶段就做好日志记录,方便线上排查。
移动端WebRTC集成知识体系 移动端WebRTC Android集成 iOS集成 性能优化 Maven依赖 权限配置 PeerConnection CocoaPods 权限描述 RTCInitializeSSL 分辨率自适应 硬件编码 内存管理 核心:权限 + 内存 + 音频会话 = 稳定运行

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