【视频物联网 App】RN 双端适配与原生核心实现深度剖析
本文探讨了使用React Native (RN)构建千万级用户视频物联网App的技术方案。核心思路是通过RN统一UI和业务逻辑,同时将高性能要求的音视频功能下沉到原生层实现,兼顾开发效率和性能体验。 技术架构分为四层: RN应用层:处理UI、状态管理和业务逻辑 2.桥接层:通过Native Bridge连接RN与原生模块 3.原生核心层:分别实现Android/iOS的音视频处理和物联网通信 4.
关于使用 React Native (RN) 构建千万级用户体量视频物联网 (Video IoT) App 的深度的、全栈式的技术方案剖析。
这个方案的核心哲学是:使用 RN 统一 UI 业务逻辑,通过“原生桥接 (Native Bridge)”和“原生模块 (Native Modules)”将极致性能和稳定性要求最高的核心音视频功能下沉到 Android (Kotlin/Java) 和 iOS (Swift/Obj-C) 原生层实现。
这是唯一能同时满足“多端代码复用”和“千万级用户体验”的成熟路径。
视频物联网 App RN 双端适配与原生核心实现深度剖析
第一部分:整体技术架构与跨端策略
视频物联网 App 的复杂性在于它融合了:
- 移动端 UI/UX (RN 的强项)
- 物联网设备通信协议 (MQTT, CoAP, HTTPS)
- 核心音视频流媒体处理 (WebRTC, RTSP/RTMP, H.264/H.265 编解码, 原生核心)
1.1 总体架构图
%% App 层 (React Native)
subgraph App_Layer [React Native App (JS/TS)]
UI[UI Components (Login, DeviceList, PlayerUI)]
Redux[State Management (Redux/Zustand)]
Logic[Business Logic (Device Mgmt, User Auth)]
end
%% 原生桥接层 (The Core Strategy)
subgraph Bridge_Layer [Cross-Platform Native Bridge]
RN_Bridge[React Native Bridge / JSI]
subgraph Native_Modules [Target Native Modules]
AV_NM[Audio/Video Native Module (Custom)]
IoT_NM[IoT Control Native Module (Custom/SDK)]
Auth_NM[Secure Storage/Auth Native Module]
end
end
%% 原生 Android 实现层
subgraph Android_Native [Android Platform (Kotlin/Java)]
Android_AV[Core AV: WebRTC-Android, ExoPlayer]
Android_IoT[IoT: MQTT-Android, CoAP]
Android_OS[OS: Camera2, AudioRecord, MediCodec]
end
%% 原生 iOS 实现层
subgraph iOS_Native [iOS Platform (Swift/Obj-C)]
iOS_AV[Core AV: WebRTC-iOS, AVFoundation]
iOS_IoT[IoT: CocoaMQTT, CoAP-iOS]
iOS_OS[OS: AVFoundation, VideoToolbox]
end
%% 外部服务层
subgraph External_Services [Cloud & Hardware]
IoT_Cloud[IoT Cloud Service (AWS IoT, Aliyun IoT, etc.)]
Signaling[WebRTC Signaling Server]
STUN_TURN[STUN/TURN Servers]
Device[IoT Camera Hardware]
end
%% 连接关系
UI --> RN_Bridge
RN_Bridge --> AV_NM
RN_Bridge --> IoT_NM
RN_Bridge --> Auth_NM
AV_NM -->|Calls| Android_AV
IoT_NM -->|Calls| Android_IoT
AV_NM -->|Calls| iOS_AV
IoT_NM -->|Calls| iOS_IoT
Android_AV -->|Drivers| Android_OS
iOS_AV -->|Drivers| iOS_OS
Android_IoT -->|Protocol| IoT_Cloud
iOS_IoT -->|Protocol| IoT_Cloud
Android_AV -->|Media P2P/Stream| Device
iOS_AV -->|Media P2P/Stream| Device
Device -->|Status/Control| IoT_Cloud
Android_AV -.->|Signaling| Signaling
iOS_AV -.->|Signaling| Signaling
1.2 核心跨端适配策略深度剖析
1.2.1 UI 与业务逻辑层:React Native
-
原则:除音视频渲染视窗和极个别特定硬件交互外,100% 的 UI 和业务逻辑(用户登录、设备列表展示、设置界面、控制指令下发)都使用 RN 编写。
-
千万级适配关键点:
-
性能优化:必须极其关注 RN 的性能。
-
使用
FlatList优化长列表。 -
避免在渲染循环中进行复杂的逻辑计算。
-
使用
React.memo,useMemo,useCallback减少不必要的重渲染。 -
响应式布局:全面使用 Flexbox 和
PixelRatio来适配各种 Android 和 iOS 屏幕尺寸和分辨率。
1.2.2 状态管理:Zustand 或 Redux
对于千万级用户体量,状态管理必须高效且可预测。
- 策略:将设备状态(在线/离线、报警状态)和用户信息(Token、权限)集中管理。
- 关键点:物联网设备状态更新频繁。建议使用轻量级的 Zustand,并配合 Immutable.js 或 Immer,避免复杂的深拷贝操作影响 UI。
1.2.3 物联网通信 (Control Plane):Native Bridge
移动端与物联网云平台的控制面通信(如下发“转动摄像头”指令、获取历史报警记录)通常不要求极低的延迟,但需要高可靠性。
- 最佳实践:不要在 RN (JS) 层直接使用 Node.js 的 MQTT 库。
- 原生做法:在 Android 和 iOS 原生层分别封装成熟的 MQTT (如 Eclipse Paho for Android, CocoaMQTT for iOS) 库,然后通过 RN 的 Native Modules 暴露出干净的异步接口给 RN 层调用。
1.2.4 核心音视频 (Data Plane):原生核心下沉 (Key Path)
这是最关键的部分。在 RN (JS) 层处理每秒数兆的音视频流是不现实的。 千万级用户要求:极速秒开、低延时(<200ms)、无卡顿、高编解码效率。
-
技术选择:
-
P2P 双向对讲:WebRTC (Google 官方库的原生版本) 是不二之选。
-
HLS/RTSP 单向点播/回看:原生播放器 (iOS 的
AVPlayer, Android 的ExoPlayer) 是最稳定的。 -
技术实现细节:
- 原生模块创建:分别创建
AVNativeModule.kt(Android) 和AVNativeModule.m/.swift(iOS)。 - 核心库接入:在 Android 接入原生的 WebRTC for Android AAR 包;在 iOS 通过 CocoaPods 接入 WebRTC.framework。
- 音视频采集与编码:使用原生的
Camera2/AVFoundation进行采集,使用MediaCodec/VideoToolbox进行硬编码。 - 渲染桥接:创建一个自定义的 RN View Component (例如
<RTCViewNative />)。
- Android:将原生的
SurfaceView或TextureView桥接给 RN。WebRTC 解码后的视频帧将直接绘制在这个 Surface 上,不经过 JS 层。 - iOS:将原生的
RTCEAGLVideoView或RTCMTLVideoView桥接给 RN。
- 桥接接口封装:给 RN 暴露简洁的方法,如
startCall(deviceId)、stopCall()、muteMicrophone(isMuted),以及回调事件如onRemoteStreamReady、onConnectionFailed。
1.2.5 编译与发布流程 (CI/CD for Millions)
对于千万级用户,编译和发布不能依赖手动。
- 策略:构建完善的 CI/CD 流程 (例如使用 GitHub Actions, GitLab CI, Fastlane)。
- 自动化:自动运行 JS 单元测试、原生 Android/iOS 单元测试、端到端 (E2E) 测试(如 Detox),自动生成 Android AAB 和 iOS IPA 签名包,并上传到 App Store Connect 和 Google Play Console。
第二部分:千万级生产环境音视频核心实现 Demo 剖析 (基于 WebRTC)
这个 Demo 的核心目标是展示如何通过 RN 的自定义 Native Module 将一个完全原生的 WebRTC 呼叫/接收流程暴露给 RN。这不是一个完整的、可直接从 GitHub 克隆的仓库,而是一个详细的代码层面的核心架构剖析,你可以据此构建你的生产环境代码。
2.1 Demo 目标
- RN 发起呼叫:用户在 RN 界面点击“呼叫”,RN 调用原生模块方法。
- 原生 WebRTC 连接:原生层处理信令交互(示意,需配合云端信令服务器)、建立 P2P 连接、设置 PeerConnection。
- 原生渲染:将远程视频流直接在原生创建的 View 上渲染,并把这个 View 桥接到 RN 的布局中。
- 双端适配:提供 Android Kotlin 和 iOS Swift 的核心代码结构。
2.2 React Native (JS/TS) 层实现
这是千万级 App 的 UI。
2.2.1 定义 Native Module 接口 (AVNativeModule.ts)
// AVNativeModule.ts - 类型定义文件
import { NativeModules, NativeEventEmitter, ViewProps } from 'react-native';
// 1. 定义原生音视频模块暴露出的方法
interface AVNativeModuleInterface {
startCall(deviceId: string): Promise<void>;
stopCall(): void;
muteMicrophone(isMuted: boolean): void;
}
// 2. 获取原生模块实例 (Android/iOS 必须对应此名称)
const { AVNativeModule } = NativeModules;
// 3. 定义原生事件发射器
const AVNativeEventEmitter = new NativeEventEmitter(AVNativeModule);
// 4. 定义自定义原生视图组件,用于 RN 布局
// 这个组件在底层对应 Android 的 TextureView 和 iOS 的 RTCMTLVideoView
interface RTCViewNativeProps extends ViewProps {
onRemoteStreamAvailable?: () => void; // 可选的远程流就绪回调
}
const RTCViewNative = requireNativeComponent<RTCViewNativeProps>('RTCViewNative');
export { AVNativeModule, AVNativeEventEmitter, RTCViewNative };
2.2.2 UI 呼叫页面 (VideoCallScreen.tsx)
// VideoCallScreen.tsx - RN 业务逻辑与 UI
import React, { useState, useEffect } from 'react';
import { View, Text, Button, StyleSheet, Alert } from 'react-native';
import { AVNativeModule, AVNativeEventEmitter, RTCViewNative } from './AVNativeModule';
const VideoCallScreen = ({ route }) => {
const { deviceId } = route.params || { deviceId: 'dummy-camera-1' };
const [isCalling, setIsCalling] = useState(false);
const [isRemoteStreamReady, setIsRemoteStreamReady] = useState(false);
useEffect(() => {
// 监听原生层发出的事件
const callStatusListener = AVNativeEventEmitter.addListener('onCallStatusChanged', (status) => {
console.log('Call status from native:', status);
if (status === 'connected') {
setIsRemoteStreamReady(true);
} else if (status === 'failed' || status === 'disconnected') {
setIsCalling(false);
setIsRemoteStreamReady(false);
Alert.alert('呼叫失败', '连接断开');
}
});
// 清理函数
return () => {
callStatusListener.remove();
AVNativeModule.stopCall(); // 退出页面时自动断开
};
}, []);
const handleStartCall = async () => {
try {
setIsCalling(true);
await AVNativeModule.startCall(deviceId);
} catch (error) {
console.error('Error starting call:', error);
setIsCalling(false);
Alert.alert('呼叫错误', '无法发起呼叫');
}
};
const handleStopCall = () => {
AVNativeModule.stopCall();
setIsCalling(false);
setIsRemoteStreamReady(false);
};
return (
<View style={styles.container}>
<Text style={styles.title}>设备呼叫: {deviceId}</Text>
{/* 核心:原生视频渲染视图,在 RN 布局中定义 */}
{isCalling && (
<View style={styles.videoWrapper}>
<RTCViewNative
style={styles.remoteVideo}
onRemoteStreamAvailable={() => console.log('RN: Remote stream available')}
/>
</View>
)}
<View style={styles.controls}>
{!isCalling ? (
<Button title="发起呼叫 (P2P)" onPress={handleStartCall} color="#2196F3" />
) : (
<Button title="挂断" onPress={handleStopCall} color="#F44336" />
)}
</View>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#f0f0f0', alignItems: 'center', justifyContent: 'center' },
title: { fontSize: 18, marginBottom: 20 },
videoWrapper: { width: '90%', height: 300, backgroundColor: 'black', marginBottom: 20, borderRadius: 10, overflow: 'hidden' },
remoteVideo: { flex: 1, width: '100%', height: '100%' }, // 这里是原生视图
controls: { flexDirection: 'row', gap: 20 },
});
export default VideoCallScreen;
2.3 Android 原生实现 (Kotlin)
这是千万级性能的核心。
2.3.1 原生模块核心逻辑 (AVNativeModule.kt)
// Android/app/src/main/java/com/yourcompany/app/avnative/AVNativeModule.kt
package com.yourcompany.app.avnative
import com.facebook.react.bridge.*
import com.facebook.react.modules.core.DeviceEventManagerModule
import org.webrtc.*
import java.util.*
class AVNativeModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
private var peerConnectionFactory: PeerConnectionFactory? = null
private var peerConnection: PeerConnection? = null
private var localAudioSource: AudioSource? = null
private var localAudioTrack: AudioTrack? = null
private var isCallInProgress = false
override fun getName(): String {
return "AVNativeModule" // 必须与 RN 中 requireNativeComponent 的名称一致
}
@ReactMethod
fun startCall(deviceId: String, promise: Promise) {
if (isCallInProgress) {
promise.reject("CALL_IN_PROGRESS", "A call is already in progress.")
return
}
try {
initWebRTC()
// 1. 初始化 WebRTC
// 2. 创建 PeerConnection
// 3. 将本地音频/视频流添加到 PeerConnection (如果是对讲)
// 4. 创建 Offer (或等待 Answer)
// 5. 你的信令服务器逻辑(连接,发送 Offer)
// 6. 如果连接成功,向 RN 发送事件
sendEvent("onCallStatusChanged", "connecting")
// 这里我们模拟一个成功的 P2P 连接建立
Timer().schedule(object : TimerTask() {
override fun run() {
sendEvent("onCallStatusChanged", "connected")
}
}, 1500)
isCallInProgress = true
promise.resolve(null)
} catch (e: Exception) {
promise.reject("CALL_FAILED", "Failed to start call: ${e.message}")
}
}
@ReactMethod
fun stopCall() {
// 关闭 PeerConnection
peerConnection?.close()
peerConnection = null
// 销毁 Factory
peerConnectionFactory?.dispose()
peerConnectionFactory = null
isCallInProgress = false
sendEvent("onCallStatusChanged", "disconnected")
}
@ReactMethod
fun muteMicrophone(isMuted: boolean) {
localAudioTrack?.setEnabled(!isMuted)
}
// 内部方法:向 RN 发送事件
private fun sendEvent(eventName: String, params: Any?) {
reactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit(eventName, params)
}
// 这里包含 WebRTC 的所有核心原生设置
private fun initWebRTC() {
// WebRTC 原生设置
val eglBase = EglBase.create()
PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(reactApplicationContext).createInitializationOptions())
val options = PeerConnectionFactory.Options()
val defaultVideoEncoderFactory = DefaultVideoEncoderFactory(eglBase.eglBaseContext, true, true)
val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(eglBase.eglBaseContext)
peerConnectionFactory = PeerConnectionFactory.builder()
.setOptions(options)
.setVideoEncoderFactory(defaultVideoEncoderFactory)
.setVideoDecoderFactory(defaultVideoDecoderFactory)
.createPeerConnectionFactory()
val rtcConfig = PeerConnection.RTCConfiguration(emptyList()) // 生产环境需要设置 STUN/TURN
// 这是一个示意性设置,不包含完整的 Offer/Answer 流程
peerConnection = peerConnectionFactory?.createPeerConnection(rtcConfig, object : PeerConnection.Observer {
override fun onIceCandidate(candidate: IceCandidate?) {
// 发送 Candidate 到信令服务器
}
override fun onAddStream(stream: MediaStream?) {
// 千万级关键点:这里接收到流,需要通知自定义的原生 View 进行渲染
stream?.videoTracks?.get(0)?.let { remoteVideoTrack ->
// 这里我们通过一个单例或者某种方式找到那个自定义的原生 View
// 并调用原生渲染方法,将 remoteVideoTrack 绑定给它
// 我们稍后在 RTCViewManager 中实现
}
}
// ... 实现了其他 Observer 方法
})
}
}
2.3.2 自定义原生渲染视图及其 Manager (RTCViewManager.kt)
// Android/app/src/main/java/com/yourcompany/app/avnative/RTCViewManager.kt
package com.yourcompany.app.avnative
import android.view.View
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.common.MapBuilder
import org.webrtc.EglBase
import org.webrtc.SurfaceViewRenderer
import org.webrtc.VideoTrack
class RTCViewManager : SimpleViewManager<SurfaceViewRenderer>() {
companion object {
const val REACT_CLASS = "RTCViewNative" // 与 RN 中 requireNativeComponent 的名称一致
private var sharedEglBase: EglBase? = null // 共享 EGL 上下文,关键性能点
}
override fun getName(): String {
return REACT_CLASS
}
override fun createViewInstance(reactContext: ThemedReactContext): SurfaceViewRenderer {
if (sharedEglBase == null) {
sharedEglBase = EglBase.create() // 确保在主线程创建
}
val view = SurfaceViewRenderer(reactContext)
view.init(sharedEglBase!!.eglBaseContext, null)
view.setMirror(true) // 默认开启镜像,如果是前置对讲
view.setEnableHardwareScaler(true) // 千万级必须开启硬解硬件加速
// 千万级关键点:将该渲染视图注册给一个单例对象
// 以便 AVNativeModule 在接收到远程流时,能够找到它
VideoRenderRegistry.registerRemoteView(view)
return view
}
override fun onDropViewInstance(view: SurfaceViewRenderer) {
VideoRenderRegistry.unregisterRemoteView(view)
view.release()
super.onDropViewInstance(view)
}
}
// 简单的单例,用于在原生层进行跨组件的数据流传递
object VideoRenderRegistry {
private var remoteViewRef: SurfaceViewRenderer? = null
private var remoteVideoTrackRef: VideoTrack? = null
fun registerRemoteView(view: SurfaceViewRenderer) {
remoteViewRef = view
// 如果流已经准备好,直接绑定
remoteVideoTrackRef?.addSink(view)
}
fun unregisterRemoteView(view: SurfaceViewRenderer) {
remoteVideoTrackRef?.removeSink(view)
remoteViewRef = null
}
// 这个方法由 AVNativeModule 在 onAddStream 中调用
fun onRemoteStreamReady(videoTrack: VideoTrack) {
remoteVideoTrackRef = videoTrack
remoteViewRef?.let {
videoTrack.addSink(it)
}
}
}
2.3.3 Android 模块注册 (AVPackage.kt)
// Android/app/src/main/java/com/yourcompany/app/avnative/AVPackage.kt
package com.yourcompany.app.avnative
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class AVPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return listOf(AVNativeModule(reactContext))
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return listOf(RTCViewManager())
}
}
在你的 MainApplication.java 的 getPackages() 方法中添加 new AVPackage() 即可接入。
2.4 iOS 原生实现 (Swift)
使用 Swift 实现原生部分,代码更简洁。
2.4.1 原生模块核心逻辑 (AVNativeModule.swift)
// iOS/AVNativeModule.swift
import Foundation
import React
import WebRTC
@objc(AVNativeModule)
class AVNativeModule: RCTEventEmitter {
private var peerConnectionFactory: RTCPeerConnectionFactory?
private var peerConnection: RTCPeerConnection?
private var localAudioTrack: RTCAudioTrack?
private var isCallInProgress = false
override func getName() -> String! {
return "AVNativeModule" // 必须与 RN 中 requireNativeComponent 的名称一致
}
// 指定允许发送给 RN 的事件名称
override func supportedEvents() -> [String]! {
return ["onCallStatusChanged"]
}
// 必须指定方法在主线程还是后台线程运行
@objc override static func requiresMainQueueSetup() -> Bool {
return false // WebRTC 核心逻辑通常运行在自己的后台线程,避免阻塞 UI
}
@objc(startCall:resolver:rejecter:)
func startCall(deviceId: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
if isCallInProgress {
rejecter("CALL_IN_PROGRESS", "A call is already in progress.", nil)
return
}
do {
try initWebRTC()
// 1. 初始化 WebRTC
// 2. 创建 PeerConnection
// 3. 将本地音/视频流添加到 PeerConnection
// 4. 创建 Offer/等待 Answer
// 5. 你的信令服务器逻辑
self.sendEvent(withName: "onCallStatusChanged", body: "connecting")
// 这里模拟一个成功的 P2P 连接建立
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
self.sendEvent(withName: "onCallStatusChanged", body: "connected")
}
isCallInProgress = true
resolver(nil)
} catch {
rejecter("CALL_FAILED", "Failed to start call: \(error.localizedDescription)", nil)
}
}
@objc(stopCall)
func stopCall() {
// 关闭和清理 PeerConnection
peerConnection?.close()
peerConnection = nil
peerConnectionFactory = nil
isCallInProgress = false
self.sendEvent(withName: "onCallStatusChanged", body: "disconnected")
}
@objc(muteMicrophone:)
func muteMicrophone(isMuted: Bool) {
localAudioTrack?.isEnabled = !isMuted
}
// 这里包含 WebRTC 的所有核心原生设置
private func initWebRTC() throws {
// 1. 初始化 Factory
let encoderFactory = RTCDefaultVideoEncoderFactory()
let decoderFactory = RTCDefaultVideoDecoderFactory()
peerConnectionFactory = RTCPeerConnectionFactory(encoderFactory: encoderFactory, decoderFactory: decoderFactory)
// 2. 配置 PeerConnection
let config = RTCConfiguration() // 生产环境需要设置 STUN/TURN
let constraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
peerConnection = peerConnectionFactory?.peerConnection(with: config, constraints: constraints, delegate: self)
}
}
// 实现了 WebRTC Delegate 方法
extension AVNativeModule: RTCPeerConnectionDelegate {
func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
// 千万级关键点:这里接收到远程流
if let remoteVideoTrack = stream.videoTracks.first {
// 通过单例找到自定义原生视图,并绑定
VideoRenderRegistry.shared.onRemoteStreamReady(videoTrack: remoteVideoTrack)
}
}
// ... 实现了其他 Delegate 方法
}
2.4.2 自定义原生渲染视图 (RTCViewNative.swift) 和 Manager
// iOS/RTCViewNative.swift
import UIKit
import WebRTC
import React
// 自定义视图,用于封装 RTCMTLVideoView
class RTCViewNative: UIView {
private let videoView: RTCMTLVideoView // 使用 Metal 的硬件加速渲染,性能最佳
override init(frame: CGRect) {
self.videoView = RTCMTLVideoView(frame: .zero)
super.init(frame: frame)
self.setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
videoView.videoContentMode = .scaleAspectFill // 设置填充模式,适配千萬级 App UI
videoView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(videoView)
NSLayoutConstraint.activate([
videoView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
videoView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
videoView.topAnchor.constraint(equalTo: self.topAnchor),
videoView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
// 千万级关键点:将该渲染视图注册给单例
VideoRenderRegistry.shared.registerRemoteView(view: self.videoView)
}
// 如果需要传递事件给 RN
@objc var onRemoteStreamAvailable: RCTDirectEventBlock?
}
// 简单的 Swift 单例,用于在原生层传递数据流
class VideoRenderRegistry {
static let shared = VideoRenderRegistry()
private var remoteViewRef: RTCMTLVideoView?
private var remoteVideoTrackRef: RTCVideoTrack?
private init() {} // 阻止外部初始化
func registerRemoteView(view: RTCMTLVideoView) {
self.remoteViewRef = view
// 如果流已经准备好,直接绑定
self.remoteVideoTrackRef?.add(view)
}
// 这个方法由 AVNativeModuleDelegate 在接收到远程流时调用
func onRemoteStreamReady(videoTrack: RTCVideoTrack) {
self.remoteVideoTrackRef = videoTrack
self.remoteViewRef?.let {
videoTrack.add(it)
}
}
}
// Manager:负责向 RN 注册和创建这个自定义视图组件
@objc(RTCViewNativeManager)
class RTCViewNativeManager: RCTViewManager {
override func view() -> UIView! {
return RTCViewNative()
}
override class func requiresMainQueueSetup() -> Bool {
return true // UIKit 组件必须在主线程初始化
}
}
2.4.3 iOS 模块桥接文件 (AVNativeModule.m / RTCViewNativeManager.m)
在 iOS 中,即使使用 Swift,也必须编写 Objective-C 桥接文件来将这些方法暴露出给 RN 的 JS 引擎。
// iOS/AVNativeModule.m - 暴露方法和事件
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface RCT_EXTERN_MODULE(AVNativeModule, RCTEventEmitter) // Swift 类和基类
RCT_EXTERN_METHOD(startCall:(NSString *)deviceId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(stopCall)
RCT_EXTERN_METHOD(muteMicrophone:(BOOL)isMuted)
@end
// iOS/RTCViewNativeManager.m - 暴露自定义组件
#import <React/RCTViewManager.h>
@interface RCT_EXTERN_MODULE(RTCViewNativeManager, RCTViewManager) // Swift Manager 类
RCT_EXPORT_VIEW_PROPERTY(onRemoteStreamAvailable, RCTDirectEventBlock) // 如果需要暴露属性或事件给 RN 组件
@end
第三部分:总结与建议
这份技术方案和代码剖析提供了一个坚实、成熟的路径,用于通过单一 React Native 代码库,构建同时满足 Android 和 iOS 千万级用户体验的视频物联网 App。
3.1 方案核心总结:
- 架构:RN 处理 UI 与业务逻辑,原生核心模块处理高性能、不稳定的音视频和 IoT 协议栈。
- 音视频:必须使用WebRTC 的原生版本 (Kotlin/Swift),严禁使用 JS WebRTC 库。视频渲染必须使用硬件加速的自定义 View (如 Metal 的
RTCMTLVideoViewfor iOS, 使用SurfaceViewRendererfor Android)。 - 连接策略:在 Android 和 iOS 原生层实现 Offer/Answer 和 ICE Candidate 的交换(配合云端信令),并通过单例模式将接收到的原生流对象 (VideoTrack) 直接绑定到桥接给 RN 的原生 View 上。
3.2 对构建千万级 App 的进一步建议:
- 信令服务器与 STUN/TURN:千万级 App 的成功关键在于连接建立成功率和稳定性。你需要自行部署并优化高可用的 STUN/TURN 服务器阵列(或使用 AWS Kinesis Video Streams 或阿里云音视频服务),并确保信令服务器是可横向扩展的。
- A/B 测试:对于核心功能,如“秒开时间”、“视频延时”,应使用 A/B 测试来持续优化性能。
- 端到端监控:构建完善的视频监控系统。监控 WebRTC 的丢包率 (Packet Loss), 帧率 (FPS), 连接时间 (Setup Time), 以及用户端的网络状态,以便快速定位和解决个别设备或网络环境的体验问题。
希望这个深度方案和 Demo 剖析能成为你构建世界级 React Native 视频物联网 App 的一个极具价值的参考指南!
更多推荐

所有评论(0)