移动端WebRTC开发:从零到一
说实话,移动端WebRTC开发是我个人觉得最有意思的部分。你想想看,把实时音视频能力塞进手机里,让用户随时随地都能视频通话——这本身就是件很酷的事。我在做第一个移动端项目时,踩了不少坑,今天把这些经验整理出来,希望能帮你少走弯路。
Android平台集成:Java与Kotlin
Android端的WebRTC集成,说白了就是两件事:一是把WebRTC库引进来,二是把原生API封装好。我个人习惯用Kotlin,但Java也完全没问题,核心逻辑是一样的。
第一步:添加依赖
在build.gradle里加上WebRTC的Maven依赖。注意版本号,我建议用最新的稳定版。
dependencies {
implementation 'org.webrtc:google-webrtc:1.0.32006'
}
第二步:初始化PeerConnectionFactory
这是WebRTC的核心工厂类。所有连接、媒体流都从这里创建。
// Kotlin版本
val initializationOptions = PeerConnectionFactory.InitializationOptions.builder(context)
.setFieldTrials("")
.createInitializationOptions()
PeerConnectionFactory.initialize(initializationOptions)
val factory = PeerConnectionFactory.builder()
.setVideoDecoderFactory(DefaultVideoDecoderFactory(rootEglBase.eglBaseContext))
.setVideoEncoderFactory(DefaultVideoEncoderFactory(rootEglBase.eglBaseContext, true, true))
.createPeerConnectionFactory()
嗯,这里要注意:rootEglBase 需要提前创建,它负责OpenGL的上下文管理。我刚开始做的时候忘了这步,结果画面死活出不来。
第三步:创建本地媒体流
音频和视频的采集,WebRTC已经封装好了。你只需要配置好约束条件。
val mediaConstraints = MediaConstraints().apply {
mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"))
}
val audioSource = factory.createAudioSource(MediaConstraints())
val audioTrack = factory.createAudioTrack("audio_track", audioSource)
val videoSource = factory.createVideoSource(false)
val surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase.eglBaseContext)
val videoCapturer = Camera2Enumerator(context).run {
deviceNames.firstOrNull { isFrontFacing(it) }?.let { createCapturer(it, null) }
}
videoCapturer?.initialize(surfaceTextureHelper, context, videoSource.capturerObserver)
videoCapturer?.startCapture(1280, 720, 30)
iOS平台集成:Swift与OC
iOS端的集成思路和Android类似,但API风格更Swift化。我个人更喜欢用Swift,代码更简洁。
引入WebRTC框架
推荐用CocoaPods或Swift Package Manager。我用的是SPM,省事。
// Package.swift 或直接在Xcode中添加
// https://github.com/stasel/WebRTC
创建PeerConnectionFactory
// Swift版本
let factory = RTCPeerConnectionFactory(
encoderFactory: RTCDefaultVideoEncoderFactory(),
decoderFactory: RTCDefaultVideoDecoderFactory()
)
// 配置媒体约束
let mediaConstraints = RTCMediaConstraints(
mandatoryConstraints: [
"OfferToReceiveAudio": "true",
"OfferToReceiveVideo": "true"
],
optionalConstraints: nil
)
这里有个坑:iOS的摄像头权限需要提前申请。我记得第一次跑demo时,直接crash了,就是因为忘了在Info.plist里加摄像头权限描述。
视频渲染
iOS端用RTCMTLVideoView来渲染视频,性能比OpenGL好很多。
let videoView = RTCMTLVideoView(frame: view.bounds)
videoView.videoContentMode = .scaleAspectFill
view.addSubview(videoView)
// 将视频轨道绑定到渲染器
videoTrack.add(videoView)
移动端适配与性能优化
说实话,集成只是第一步。真正考验人的是性能优化。我经历过视频卡顿、声音延迟、CPU过热……这些坑,今天一次性说清楚。
硬件编码器 vs 软件编码器
| 对比项 | 硬件编码 | 软件编码 |
|---|---|---|
| 性能 | 低功耗,高帧率 | CPU占用高,发热 |
| 画质 | 中等 | 较高 |
| 兼容性 | 依赖芯片 | 通用 |
| 推荐场景 | 实时通话 | 录屏、直播 |
我的建议是:实时通话用硬件编码,省电又流畅。录屏或直播场景可以用软件编码,画质更好。
网络自适应
移动网络不稳定,WebRTC内置了拥塞控制算法。但你也可以手动调整一些参数。
// 设置比特率范围
val constraints = MediaConstraints()
constraints.mandatory.add(MediaConstraints.KeyValuePair(
"googMinBitrate", "100000")) // 100kbps
constraints.mandatory.add(MediaConstraints.KeyValuePair(
"googMaxBitrate", "2000000")) // 2Mbps
渲染优化
移动端屏幕小,但渲染开销不小。我建议:
- 使用
SurfaceView(Android)或RTCMTLVideoView(iOS),避免CPU参与渲染 - 帧率控制在24-30fps,别追求60fps——手机屏幕刷新率有限
- 分辨率动态调整:强网用720p,弱网降到480p甚至360p
Native与Web互通
这是移动端WebRTC的终极目标:让手机App和浏览器能视频通话。核心就是信令和SDP交换。
信令协议设计
我推荐用JSON格式,简单通用。Web端和Native端都容易解析。
// 信令消息示例
{
"type": "offer",
"sdp": "v=0\r\no=- ...",
"from": "user_android",
"to": "user_web"
}
SDP协商注意事项
Web端和Native端的SDP格式略有不同。我遇到过最典型的问题是:
- Web端生成的offer里包含
a=ice-options:trickle,Native端需要支持 - Native端可能缺少某些codec,需要手动添加
// 手动添加H.264支持(Android)
val sdpMediaConstraints = MediaConstraints()
sdpMediaConstraints.mandatory.add(
MediaConstraints.KeyValuePair("googUseRtpMp4aAac", "true"))
知识体系总览
下面这张图概括了移动端WebRTC开发的核心脉络。你可以把它当作一个检查清单。
移动端WebRTC开发,说白了就是「平台适配 + 性能调优 + 协议统一」。Android和iOS的API虽然不同,但底层逻辑完全一样。你只要掌握了信令交换和媒体流管理,剩下的就是熟悉各平台的特性了。