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

说实话,移动端做WebRTC开发,跟PC端完全是两码事。我在2018年第一次把WebRTC塞进Android项目时,光是编译库就折腾了两天。那时候官方文档还不像现在这么完善,踩了不少坑。今天我把这些经验整理出来,希望能帮你少走弯路。

Android端集成:从零开始

Android集成WebRTC,核心就三步:引入库、初始化PeerConnectionFactory、建立连接。但每一步都有讲究。

1. 引入WebRTC库

现在官方推荐用Maven依赖,简单直接:

// build.gradle (app级别)
dependencies {
    implementation 'org.webrtc:google-webrtc:1.0.32006'
}

嗯,这里要注意版本号。我习惯用稳定版,比如1.0.32006。太新的版本可能有兼容问题,太旧的又缺功能。

如果你需要定制化编译,比如裁剪不需要的编解码器,那就得走源码编译了。我个人建议:初期先用预编译库,等业务稳定了再考虑定制。

2. 初始化PeerConnectionFactory

这是WebRTC的核心工厂类。初始化代码大概长这样:

// 初始化
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();

我在项目中遇到过一个问题:初始化必须在主线程调用,否则会崩溃。你想想看,如果放在子线程里,那调试起来得多头疼。

3. 创建PeerConnection

有了工厂,接下来就是创建连接:

// ICE服务器配置
List<PeerConnection.IceServer> iceServers = new ArrayList<>();
iceServers.add(PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());

PeerConnection.RTCConfiguration config = new PeerConnection.RTCConfiguration(iceServers);
config.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;

PeerConnection connection = factory.createPeerConnection(config, observer);

这里有个坑:sdpSemantics一定要设成UNIFIED_PLAN。为什么?因为PLAN_B已经被废弃了,不设的话,跟新版浏览器互通时会出问题。我曾经因为这个排查了一整天。

iOS端集成:Swift与Objective-C的抉择

iOS端我推荐用CocoaPods集成,省心:

# Podfile
pod 'GoogleWebRTC'

然后pod install就完事了。但要注意,GoogleWebRTC这个pod体积不小,大概80MB左右。如果你的App对包大小敏感,可以考虑用源码编译裁剪。

初始化流程

iOS的初始化跟Android类似,但API风格不同:

// 初始化SSL
RTCDefaultSSLAdapter.initializeSSL()

// 创建工厂
let factory = RTCPeerConnectionFactory()

// 创建连接
let config = RTCConfiguration()
config.iceServers = [RTCIceServer(urlStrings: ["stun:stun.l.google.com:19302"])]
config.sdpSemantics = .unifiedPlan

let connection = factory.peerConnection(with: config, constraints: constraints, delegate: self)

我个人习惯用Swift写iOS端,但如果你维护的是老项目,Objective-C也完全没问题。WebRTC的Objective-C接口设计得挺清晰的。

注意:iOS上调用摄像头需要权限。记得在Info.plist中添加NSCameraUsageDescriptionNSMicrophoneUsageDescription。否则一启动就闪退,用户体验极差。

移动端适配与性能优化

移动端跟PC最大的区别是什么?资源有限。CPU、内存、电池,样样都得省着用。

1. 硬件编码器与解码器

Android和iOS都支持硬件编解码。但Android碎片化严重,不同厂商的硬件编码器质量参差不齐。我建议:

  • 优先使用硬件编码,省电且发热低
  • 如果硬件编码质量不行,回退到软件编码
  • iOS端硬件编码质量普遍较好,可以放心用

代码里怎么控制?很简单:

// Android端启用硬件编码
PeerConnectionFactory.builder()
    .setVideoEncoderFactory(new DefaultVideoEncoderFactory(
        rootEglBase.getEglBaseContext(),
        true,   // 启用硬件编码
        true    // 启用硬件解码
    ))
    .createPeerConnectionFactory();

2. 分辨率与码率自适应

移动端网络环境变化大。WiFi切4G、地铁隧道、电梯里...信号说断就断。我建议开启WebRTC的带宽估计和码率自适应:

// 设置码率范围
MediaConstraints constraints = new MediaConstraints();
constraints.mandatory.add(
    new MediaConstraints.KeyValuePair("minBitrate", "100000")   // 100kbps
);
constraints.mandatory.add(
    new MediaConstraints.KeyValuePair("maxBitrate", "2000000")  // 2Mbps
);

说白了,就是让WebRTC自己根据网络状况调整码率。你只需要设个范围,剩下的交给它。

3. 屏幕适配

Android屏幕千奇百怪,刘海屏、挖孔屏、瀑布屏...渲染视频时要注意:

  • 使用SurfaceViewRenderer而不是TextureViewRenderer,性能更好
  • 设置setScalingTypeSCALE_ASPECT_FILLSCALE_ASPECT_FIT
  • 注意安全区域,别让视频被刘海挡住
小技巧:iOS端用RTCMTLVideoView,它基于Metal,性能比OpenGL好不少。Android端也有对应的SurfaceViewRenderer,记得开启硬件加速。

Native与Web互通

这是整个课程的核心。Native端和Web端能不能打通?能。但有几个关键点必须一致。

1. SDP格式统一

Web端默认用UNIFIED_PLAN,Native端也必须用这个。否则SDP解析会失败。

// Android端
config.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;

// Web端(JavaScript)
const config = { sdpSemantics: 'unified-plan' };

2. 编解码器协商

Web端通常支持H.264和VP8/VP9。Native端建议也支持这些。我遇到过一个问题:Android端默认只开了VP8,Web端只支持H.264,结果两边协商失败,画面出不来。

解决办法:手动指定编解码器优先级。

// Android端设置H.264优先
List<PeerConnection.IceServer> codecs = new ArrayList<>();
// 把H.264放在前面
codecs.add(PeerConnection.IceServer.builder("H264").createIceServer());

3. 信令协议一致

Web端和Native端必须用同一套信令协议。我习惯用JSON格式,简单明了:

// 信令消息格式
{
  "type": "offer",       // offer | answer | candidate
  "sdp": "v=0\r\n...",   // SDP内容
  "candidate": ""        // ICE候选
}

Web端用WebSocket,Native端也用WebSocket。两边解析逻辑保持一致就行。

知识体系总览

下面这张图总结了移动端WebRTC的核心流程:

移动端WebRTC集成与互通流程 Android端 iOS端 ① 引入WebRTC库(Maven/源码) ② 初始化PeerConnectionFactory ③ 创建PeerConnection ① 引入GoogleWebRTC(CocoaPods) ② 初始化SSL与RTCPeerConnectionFactory ③ 创建RTCPeerConnection 信令服务器(WebSocket) Web端(JavaScript) SDP格式统一 · 编解码器协商 · 信令协议一致

性能优化实战清单

优化项 Android iOS 说明
硬件编码 DefaultVideoEncoderFactory RTCVideoEncoderFactoryH264 优先使用,省电省CPU
渲染器 SurfaceViewRenderer RTCMTLVideoView 性能优于TextureView/OpenGL
码率自适应 minBitrate/maxBitrate RTCMediaConstraints 根据网络动态调整
分辨率 640x480(默认) 640x480(默认) 根据场景调整,视频通话建议720p
ICE服务器 STUN+TURN STUN+TURN 生产环境必须配TURN
核心要点:移动端WebRTC开发,说白了就是三件事——库集成、性能优化、协议统一。库集成要选对版本,性能优化要关注硬件编码和渲染,协议统一要确保SDP和信令一致。把这三点吃透了,Native与Web互通就不是问题。

最后说一句:移动端调试比PC端麻烦得多。我习惯用adb logcat抓Android日志,用Xcode控制台看iOS日志。遇到问题别慌,先看日志,八成能找到线索。

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