一、多端适配的挑战与思路
做WebRTC开发,最头疼的问题是什么?我个人觉得,不是信令怎么设计,也不是媒体流怎么优化,而是——你的应用到底能在多少个平台上跑起来?
我见过太多团队,PC端跑得飞起,一到手机就卡成幻灯片。或者iOS没问题,Android直接黑屏。说白了,WebRTC本身是跨平台的,但每个平台的底层实现、硬件能力、系统限制都不一样。你想想看,同样的视频编码参数,在iPhone上可能流畅运行,在千元安卓机上就发热降频。
所以这一章,我们就来聊聊怎么搞定多端适配。我会从四个方向展开:移动端原生优化、React Native集成、Flutter插件方案、还有PWA这个轻量级选择。
二、移动端WebRTC优化要点
2.1 硬件编码器的选择
移动端最关键的优化点,就是硬件编码器。我在项目中遇到过,直接用软件编码器,720p视频在手机上CPU占用直接飙到80%,手机烫得能煎鸡蛋。
正确的做法是:优先使用硬件编码器。Android上通过MediaCodec,iOS上通过VideoToolbox。WebRTC默认会尝试硬件编码,但有些定制ROM会出问题。
// Android端强制开启硬件编码
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
// 注意:不同版本API有差异
PeerConnectionFactory factory = PeerConnectionFactory.builder()
.setVideoEncoderFactory(new HardwareVideoEncoderFactory(
rootEglBase.getEglBaseContext(),
false, // 是否使用表面纹理
true // 是否优先硬件编码
))
.createPeerConnectionFactory();
2.2 分辨率与码率自适应
移动端网络环境变化大,你不能固定用1080p。我习惯用WebRTC自带的VideoEncoderAdapter做动态码率调整,但还不够。
实际项目中,我加了一个网络探测模块:
- 当RTT > 200ms时,降低分辨率到480p
- 当丢包率 > 5%时,降低帧率到15fps
- 当带宽 < 500kbps时,关闭视频,只保留音频
嗯,这里要注意:不要频繁切换分辨率,否则画面会一直闪烁。我一般设置一个3秒的冷却期。
2.3 音频处理的坑
音频在移动端容易被忽略,但用户体验影响很大。我记得有一次,用户反馈通话有回声,查了半天发现是手机壳挡住了麦克风孔...好吧,这个不算。
正经的坑是:Android上不同机型的音频延迟差异巨大。从20ms到200ms都有。我建议用AudioTrack的setBufferSizeInFrames方法手动控制缓冲大小,减少延迟。
// 降低音频延迟的配置
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
audioManager.setParameters("audio.ns_mode=0"); // 关闭降噪,减少延迟
audioManager.setParameters("audio.ec_mode=0"); // 关闭回声消除,如果硬件支持
三、React Native集成WebRTC
3.1 选库与配置
React Native做WebRTC,最成熟的库是react-native-webrtc。但说实话,这个库的文档写得一般,我第一次集成时踩了不少坑。
安装步骤:
npm install react-native-webrtc
cd ios && pod install
Android端需要额外配置AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
3.2 核心API使用
在React Native中,你不能直接操作PeerConnection对象,而是通过RTCPeerConnection的JavaScript桥接。我习惯这样封装:
import {
mediaDevices,
RTCPeerConnection,
RTCSessionDescription,
RTCIceCandidate
} from 'react-native-webrtc';
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
// 获取本地流
const stream = await mediaDevices.getUserMedia({
video: { facingMode: 'user' },
audio: true
});
stream.getTracks().forEach(track => pc.addTrack(track, stream));
getUserMedia返回null的问题。原因是Info.plist中没有添加摄像头和麦克风权限描述。记得加上NSCameraUsageDescription和NSMicrophoneUsageDescription。
3.3 性能优化
React Native的JS线程和原生线程是分离的,频繁的媒体状态更新会导致UI卡顿。我建议:
- 使用
useRef保存PeerConnection实例,避免重新渲染 - 将视频渲染组件单独封装,用
React.memo减少重绘 - 远程流使用
RTCView组件,设置objectFit: 'cover'防止变形
四、Flutter WebRTC插件
4.1 为什么选Flutter
我个人觉得,Flutter在跨平台UI一致性上比React Native强。而且flutter_webrtc这个插件维护得不错,API设计更贴近原生WebRTC。
安装:
dependencies:
flutter_webrtc: ^0.9.0
4.2 核心用法
Flutter中创建PeerConnection的代码更简洁:
import 'package:flutter_webrtc/flutter_webrtc.dart';
final pc = await createPeerConnection({
'iceServers': [
{'urls': 'stun:stun.l.google.com:19302'}
]
});
// 获取本地流
final stream = await navigator.mediaDevices.getUserMedia({
'video': {'facingMode': 'user'},
'audio': true
});
stream.getTracks().forEach((track) {
pc.addTrack(track, stream);
});
4.3 平台差异处理
Flutter虽然跨平台,但底层还是调原生API。我在项目中遇到过一个典型问题:Android上视频渲染用TextureRenderer,iOS上用RTCMTLVideoRenderer。插件虽然封装了,但有些参数需要单独设置。
| 功能 | Android | iOS |
|---|---|---|
| 视频渲染 | SurfaceView / TextureView | MTKView / GLKView |
| 硬件编码 | MediaCodec | VideoToolbox |
| 音频采集 | AudioRecord | AVAudioEngine |
| 回声消除 | 系统自带或WebRTC内置 | 系统自带 |
flutter_webrtc在iOS模拟器上不支持摄像头。真机调试才能用。我第一次用模拟器测了半天,以为代码写错了...
五、PWA方案:轻量级选择
5.1 PWA能做什么
PWA(Progressive Web App)说白了就是一个能离线运行的网页。对于WebRTC来说,PWA的优势是:不需要安装,打开浏览器就能用。但缺点也很明显——不能访问原生硬件编码器。
我一般建议:如果应用场景是"临时通话"或"演示Demo",用PWA就够了。如果是生产环境,还是得上原生或React Native/Flutter。
5.2 关键配置
要让PWA支持WebRTC,需要:
- HTTPS协议(WebRTC强制要求)
- Service Worker注册(用于离线缓存)
- manifest.json配置(让应用可以添加到桌面)
// manifest.json
{
"name": "WebRTC通话",
"short_name": "通话",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
5.3 性能限制
PWA在移动端浏览器上跑WebRTC,性能大概只有原生的一半。我测试过:
- Chrome on Android:支持硬件编码,但延迟比原生高30-50ms
- Safari on iOS:支持有限,部分API不可用
- 微信内置浏览器:基本不可用,WebRTC支持不完整
六、知识体系总览
下面这张图,是我梳理的多端适配核心逻辑。你可以看到,从底层硬件到上层框架,每个层级都有不同的适配策略。
七、总结与建议
多端适配没有银弹。我个人的经验是:
- 如果团队有原生开发能力,直接做Android和iOS原生,性能最好
- 如果追求开发效率,Flutter比React Native更省心,UI一致性更好
- 如果只是Demo或内部工具,PWA够用了,但别指望它能承载高并发
- 无论选哪个方案,硬件编码器适配和网络自适应都是必须做的
嗯,最后说一句:别迷信跨平台框架。它们能解决80%的问题,但剩下20%的坑,还是得靠原生代码来填。我在项目中就遇到过Flutter插件不支持某个Android厂商的定制API,最后还是得写原生Module。
公众号:蓝海资料掘金营,微信deep3321