ReactNative与Flutter
React Native 与 Flutter
跨端 App 框架里 RN 和 Flutter 是绕不开的两家。WebRTC 在这两边都有"事实标准"的开源库:react-native-webrtc 和 flutter_webrtc,都基于各平台原生 libwebrtc 做桥接。本篇讲集成、性能、版本同步、踩坑。
1. 选型对比
| 维度 | react-native-webrtc | flutter_webrtc |
|---|---|---|
| 维护方 | react-native-webrtc 社区(核心成员含 Jitsi) | flutter-webrtc 社区(CloudWebRTC 维护) |
| 底层 | iOS 用官方 WebRTC.xcframework,Android 用 aar | 同左 |
| API 风格 | 几乎 1:1 映射浏览器 WebRTC | 同左 + Dart 风格封装 |
| TS / Dart 类型 | 有官方 d.ts | 完整 Dart 类型 |
| Web 端 | 走原生 WebRTC(RN Web 部分场景) | flutter_webrtc Web 走 dart:html |
| 视频渲染 | RTCView(基于原生 View) | RTCVideoView(PlatformView) |
| 生态成熟度 | 高,Jitsi 在用 | 高,OpenIMSDK / Janus dart-client 在用 |
什么时候适用:业务团队已选定 RN 或 Flutter 作为 App 技术栈;要直接复用前端的 WebRTC 心智模型;不打算自己维护 libwebrtc 编译。 什么时候不适用:需要在采集前做 GPU pipeline 美颜并且与现有原生美颜框架深度集成;要做厂商私有协议(这种通常厂商有自己的 RN/Flutter 包装)。
2. 版本同步问题(再次强调)
两个库都在 README 写明对应的 libwebrtc milestone:
| 库版本 | iOS libwebrtc | Android libwebrtc |
|---|---|---|
| react-native-webrtc 124.x | M124 | M124 |
| react-native-webrtc 118.x | M118 | M118 |
| flutter_webrtc 0.10.x | M122 | M122 |
| flutter_webrtc 0.11.x | M125 | M125 |
如果业务里同时有 H5、RN、Flutter、原生四端,需要把它们都对齐到同一个 milestone(±1 个版本)。否则会出现:H5 (Chrome M125) ↔ RN (M118) 的 simulcast SSRC 不一致 / a=rid 解析差异等问题。
操作要点:
- 每季度同步一次。
- 升级前在测试房间跑互通矩阵(每端互拉)。
- 把 milestone 写进 release notes。
3. react-native-webrtc 集成
3.1 安装
npm install react-native-webrtc
cd ios && pod install
3.2 iOS 配置
ios/Podfile:
platform :ios, '13.0'
use_modular_headers!
Info.plist:
<key>NSCameraUsageDescription</key><string>视频通话</string>
<key>NSMicrophoneUsageDescription</key><string>语音通话</string>
<key>UIBackgroundModes</key>
<array><string>audio</string><string>voip</string></array>
iOS 13 是 react-native-webrtc 124 的最低要求。如果业务必须支持 iOS 12,要回退到老版本(带的 libwebrtc 也老)。
3.3 Android 配置
android/app/src/main/AndroidManifest.xml:
<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" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
android/app/build.gradle:
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 24 // 24 是 react-native-webrtc 124 的底线
targetSdkVersion 34
}
packagingOptions {
pickFirst '**/libc++_shared.so'
pickFirst '**/libjsc.so'
}
}
3.4 TypeScript 用法
import {
RTCPeerConnection,
RTCIceCandidate,
RTCSessionDescription,
mediaDevices,
RTCView,
MediaStream,
} from 'react-native-webrtc';
async function startCall(signaling: Signaling) {
const local = await mediaDevices.getUserMedia({
audio: true,
video: {
facingMode: 'user',
width: 1280,
height: 720,
frameRate: 30,
},
}) as MediaStream;
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
bundlePolicy: 'max-bundle',
});
local.getTracks().forEach(t => pc.addTrack(t, local));
pc.addEventListener('icecandidate', (e) => {
if (e.candidate) signaling.send({ type: 'candidate', candidate: e.candidate });
});
const offer = await pc.createOffer({});
await pc.setLocalDescription(offer);
signaling.send({ type: 'offer', sdp: offer.sdp });
return { pc, local };
}
3.5 视频渲染
import { RTCView } from 'react-native-webrtc';
<RTCView
streamURL={localStream.toURL()}
style={{ width: 240, height: 320 }}
objectFit="cover"
mirror={true}
zOrder={1}
/>
streamURL 是个 hack:拿到的不是 URL,是注册过的 stream id。改了 stream 的 track 后必须重新 toURL() 拿新值。
3.6 InCallManager
通话路由(听筒/扬声器/蓝牙)官方没封装,要配合:
npm install react-native-incall-manager
import InCallManager from 'react-native-incall-manager';
InCallManager.start({ media: 'audio/video' });
InCallManager.setForceSpeakerphoneOn(true);
// 结束
InCallManager.stop();
4. flutter_webrtc 集成
4.1 安装
# pubspec.yaml
dependencies:
flutter_webrtc: ^0.11.0
flutter pub get
cd ios && pod install
4.2 iOS / Android 配置
与 RN 完全一致(Info.plist / AndroidManifest)。Flutter 还要在 ios/Podfile 把 platform :ios, '13.0' 打开,并 use_frameworks!。
4.3 Dart 用法
import 'package:flutter_webrtc/flutter_webrtc.dart';
class RtcEngine {
RTCPeerConnection? _pc;
MediaStream? _local;
Future<void> start(Signaling sig) async {
_local = await navigator.mediaDevices.getUserMedia({
'audio': true,
'video': {
'facingMode': 'user',
'width': 1280,
'height': 720,
'frameRate': 30,
},
});
_pc = await createPeerConnection({
'iceServers': [
{'urls': 'stun:stun.l.google.com:19302'}
],
'sdpSemantics': 'unified-plan',
'bundlePolicy': 'max-bundle',
});
_local!.getTracks().forEach((t) {
_pc!.addTrack(t, _local!);
});
_pc!.onIceCandidate = (c) => sig.send({'type': 'candidate', 'candidate': c.toMap()});
final offer = await _pc!.createOffer({});
await _pc!.setLocalDescription(offer);
sig.send({'type': 'offer', 'sdp': offer.sdp});
}
}
4.4 视频渲染
final RTCVideoRenderer _renderer = RTCVideoRenderer();
await _renderer.initialize();
_renderer.srcObject = _local;
// 在 widget tree
RTCVideoView(
_renderer,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
mirror: true,
)
坑:RTCVideoRenderer 必须 initialize 之后才能赋 srcObject,而且 dispose() 之后不能再用,得新建。
5. 性能与渲染
| 维度 | RN | Flutter |
|---|---|---|
| 视频帧路径 | 原生 View 直接渲染(不过 JS bridge) | PlatformView,纹理共享 |
| 多路视频同屏(9 宫格) | 60 fps 没问题 | 60 fps 没问题 |
| 远端订阅切换 | 修改 streamURL 流畅 | 替换 srcObject 流畅 |
| JS / Dart 侧 CPU 占用 | 1~3% | 1~3% |
| 自定义采集(美颜) | RN 老社区方案少,需要原生模块 | Flutter 有 getDisplayMedia 桥,美颜需原生 |
| 桌面端 | RN-Windows / RN-macOS 实验 | Flutter 桌面成熟 |
整体性能瓶颈在底层 libwebrtc(编解码、网络),RN/Flutter 这一层不显著影响实时性。
6. RN ↔ Flutter ↔ 浏览器互通
互通能跑通的前提:
- 三端 libwebrtc milestone 接近。
- 信令服务格式统一(SDP 字符串 + ICE candidate)。
- Codec 在三端交集里——H.264 永远 OK,VP8 在大多数版本也 OK,VP9 在 Safari 16+ 才行。
- Simulcast 字段
a=rid三端实现一致;若不一致退化为单层。
实战建议:先用 1v1 SDP 跑通,再开 Simulcast;不要一上来就堆 SVC。
7. 失败时的样子
| 现象 | 大概原因 | 处理 |
|---|---|---|
iOS 上 pod install 报 use_frameworks 冲突 | 与 Firebase / 其他 pod 冲突 | 改 use_frameworks! :linkage => :static |
| Android 启动 NDK 冲突 | 多个 native lib 都带 libc++_shared.so | pickFirst |
| RTCView 切流后画面不更新 | streamURL 没重新 toURL | 改 state 触发 re-render |
| Flutter RTCVideoView 黑屏 | _renderer.initialize() 未 await | 加 await |
| Android 14 通话进程被杀 | 没启 FGS | 启 microphone / camera 类型 FGS |
| iOS 后台 30s 断 | 没勾 audio background mode | Info.plist 补 UIBackgroundModes |
| 远端订阅大房间 OOM | iOS / 低端 Android | 限制订阅路数 + simulcast 拉低层 |
| 蓝牙耳机切换无声 | 路由没切 | RN 用 InCallManager,Flutter 用 Helper.selectAudioOutput |
8. 调试技巧
8.1 RN
import { registerGlobals } from 'react-native-webrtc';
registerGlobals(); // 让 window.RTCPeerConnection 也指向 RN 实现
// 拿 stats
const stats = await pc.getStats(null);
stats.forEach(r => {
if (r.type === 'inbound-rtp' && r.kind === 'video') {
console.log(r.framesPerSecond, r.bytesReceived);
}
});
8.2 Flutter
final stats = await _pc!.getStats();
for (final r in stats) {
if (r.type == 'inbound-rtp' && r.values['kind'] == 'video') {
print(r.values);
}
}
两个库都能拿到完整 RTCStatsReport,监控/质量评估代码可以和 H5 共用结构。
9. 反模式
| 反模式 | 后果 |
|---|---|
| RN 与 H5 / 原生用不同 milestone | 互通时 simulcast / rid 解析 bug |
| RN 用 Bridge 转 base64 传视频帧 | 帧率/CPU 灾难,应走原生 View |
| Flutter 用 Image widget 显示视频 | 帧不连续,必须用 RTCVideoView |
| 不 dispose RTCVideoRenderer | 内存泄漏,多次进房后崩 |
| 自己写 SDP 字符串拼接做 codec 过滤 | 维护成本极高,应用 setCodecPreferences |
| Android 不配 ProGuard keep | release 包 crash 在反射 |
直接调浏览器 setSinkId | RN / Flutter 都不支持,要用 native helper |
| 在 RN JS 线程做大量帧处理 | 卡 UI,应通过原生模块走 GPU |
10. 与 Jitsi / LiveKit / TRTC SDK 的关系
- Jitsi Meet SDK (RN):基于 react-native-webrtc 二次封装的会议 SDK,方便快速接入完整会议室。代价是定制空间小,业务 UI 必须用它的 React Component。
- LiveKit Client SDK (RN/Flutter):基于 react-native-webrtc / flutter_webrtc 封装 LiveKit 协议(SFU)。开源、可自部署。
- 腾讯 TRTC RN / Flutter SDK:腾讯官方包,底层不是 react-native-webrtc,而是 TRTC 原生 SDK 桥接,互通封闭在 TRTC 体系内。
- 声网 Agora RN / Flutter SDK:同 TRTC,独立体系。
选择维度:
| 场景 | 建议 |
|---|---|
| 自有 SFU + 标准 WebRTC | react-native-webrtc / flutter_webrtc |
| LiveKit 部署 | LiveKit SDK |
| Jitsi 部署 | Jitsi Meet SDK |
| 与小程序无缝互通 | TRTC / 声网 各自 RN/Flutter 包 |
| 全球节点 + 一站式 | TRTC / 声网 |
11. 权威资料
- react-native-webrtc: https://github.com/react-native-webrtc/react-native-webrtc
- react-native-incall-manager: https://github.com/react-native-webrtc/react-native-incall-manager
- flutter_webrtc: https://github.com/flutter-webrtc/flutter-webrtc
- LiveKit Client SDK: https://docs.livekit.io/client-sdk-js/
- Jitsi Meet SDK: https://jitsi.github.io/handbook/docs/dev-guide/mobile-sdk-react-native/
- 核对日期:2026-06-22