@hxa-rn/react-native-webrtc
v1.0.0
Published
WebRTC for React Native — HarmonyOS (OpenHarmony) adaptation of @livekit/react-native-webrtc
Readme
@hxa-rn/react-native-webrtc for HarmonyOS
本项目基于 @livekit/react-native-webrtc 开发,为 React Native 鸿蒙(OpenHarmony)适配版本。
版本对应关系
| 鸿蒙适配包版本 | 原始库版本 | 支持 RN 版本 | Autolink | 编译 API 版本 | | ------------ | ---------- | ------------ | -------- | ------------- | | 1.0.0 | 144.0.0 | 0.72+ | 是 | API12+ |
最低 HarmonyOS API 版本:12。HAR 在
oh-package.json5中以compatibleSdkVersion: 12声明。 底线由实际调用的接口决定:Crypto Architecture Kit 的HKDFSpec(API 12+,数据通道 E2EE 用)、PiPWindow.create(config, contentNode)(API 12+,画中画用),以及原生引擎@ohos/webrtc自身的基线。 npm 包使用独立的鸿蒙适配版本号;本版1.0.0对应上游@livekit/[email protected]。HAR 内的 ohpm 模块版本仍保留为144.0.0,便于追踪上游基线。
原生依赖:本 HAR 依赖 ohpm 官方仓的 WebRTC 引擎
@ohos/webrtc(Apache-2.0), 已写入harmony/webrtc/oh-package.json5的dependencies,宿主工程ohpm install会自动拉取。 引擎基线为 libwebrtc M120;上游库标称 M144。差异体现在编解码器集合与个别新接口上, 能力域本身不受影响 —— 编解码能力请以RTCRtpSender.getCapabilities()的实际返回为准,不要硬编码。
🔴 媒体帧级 E2EE 需要「增补引擎 HAR」,官方引擎给不了:官方
@ohos/webrtc的 NAPI 层没有把 libwebrtc 的帧变换器挂接点引出来(随包webrtc.d.ts全文检索framecryptor|insertable|encodedstream|keyprovider|cryptor|transform零命中), 因此frameCryptor*6 个方法 +keyProviderSetSifTrailer在官方引擎上无法工作。 本仓在同一基线(@ohos/[email protected]对应提交306d400f)上补齐了这层 NAPI 绑定并重建引擎, 以分叉 HAR 形式分发(ohos-webrtc-<基线>-e2ee.<n>.har),宿主通过overrides接入 —— 见 Link 章「步骤 1. Overrides」。 不接入也能用:除帧侧那 7 个方法外的全部能力(含数据通道 E2EE)在官方引擎上完整可用。 引擎说明与分发脚本见engine/README.md和scripts/README-webrtc-packaging.md。
安装
npm install @hxa-rn/[email protected] --save-exact使用
import { useEffect, useState } from 'react';
import { Button, StyleSheet, View } from 'react-native';
import {
mediaDevices,
permissions,
RTCView,
type MediaStream,
} from '@livekit/react-native-webrtc';
const styles = StyleSheet.create({ fill: { flex: 1 } });
export default function LocalPreview() {
const [stream, setStream] = useState<MediaStream | null>(null);
// 1) 权限:camera / microphone 均为 user_grant,未授权时会弹系统弹窗
const start = async () => {
await permissions.request({ name: 'camera' });
// 2) 设备枚举:videoinput / audioinput / audiooutput
const devices = await mediaDevices.enumerateDevices();
console.log(devices);
// 3) 本地采集:返回的轨道此时已经真实在采集
const s = await mediaDevices.getUserMedia({
audio: true,
video: { width: 1280, height: 720, facingMode: 'user' },
});
setStream(s as MediaStream);
};
useEffect(() => () => stream?.release(), [stream]);
return (
<View style={styles.fill}>
<Button title="开始采集" onPress={start} />
{/* 4) 预览:streamURL 取 stream.toURL() */}
<RTCView
style={styles.fill}
streamURL={stream ? stream.toURL() : ''}
mirror
objectFit="cover"
onDimensionsChange={e => console.log(e.nativeEvent)}
/>
{/* 5) 轨道控制 */}
<Button
title="切换前后置"
onPress={() => stream?.getVideoTracks()[0]?._switchCamera()}
/>
</View>
);
}JS/TS import 继续使用原库名
'@livekit/react-native-webrtc',而不是 npm 包名@hxa-rn/react-native-webrtc或 ohpm 模块名@oh-rn/react-native-webrtc(由 RNOH alias 映射)。
平台差异:
- 本适配提供完整的端到端通话能力:对等连接与协商、收发管线、数据通道、媒体设备枚举与采集、
屏幕内容捕获、视频渲染、画中画,底层为 ohpm 上的
@ohos/webrtc引擎。 - 数据通道端到端加密(KeyProvider + DataPacketCryptor,12 个方法) 由本适配基于 Crypto Architecture Kit 自建(AES-256-GCM + HKDF/PBKDF2 棘轮,纯软件、免额外权限)。
- 媒体帧级端到端加密(FrameCryptor,7 个方法)需接入增补引擎 HAR:官方
@ohos/webrtc的 NAPI 层 未暴露编码后 / 解码前的帧管线挂接点,在它之上这 7 个方法无法工作;接入本仓的增补引擎(Link 步骤 1 的 override ②)后完整可用,密文线格式与 LiveKit 生态逐字节一致。详见「未实现功能」末尾说明。 ScreenCapturePickerView在鸿蒙无对应物(渲染为零内容占位);屏幕共享本身可用, 直接调用mediaDevices.getDisplayMedia()即可,系统会弹出自己的捕获确认。mediaDevices.ondevicechange由 Audio Kit 的路由变化与引擎设备变化共同驱动。track.applyConstraints()/track._switchCamera()通过「按新约束重新采集 + 换到所有 sender 与所属 MediaStream」实现,JS 侧track.id保持不变、无需重协商。
权限要求:
- 需在应用
entry/src/main/module.json5声明ohos.permission.CAMERA与ohos.permission.MICROPHONE(二者均为user_grant,usedScene.when建议inuse),并配置对应的reason字符串资源。 - 运行时由
permissions.request({ name })触发abilityAccessCtrl.requestPermissionsFromUser动态申请;getUserMedia()内部也会先申请再采集。已授权时不再弹窗。
Link
| 版本 | 是否支持 Autolink | |------|------------------| | 1.0.0 | 是 |
本包随附 harmony/webrtc.har 与 Autolinking 元数据。Autolink 与下面的 Manual Link
必须二选一;本仓示例保留 autolinking: {},由 RNOH 插件生成 CMake 链接配置。
说明:本模块需要同时在 C++ 侧和 ETS 侧注册 Package。
1. Overrides(必需):RN SDK + WebRTC 引擎
本 HAR 的 oh-package.json5 把 @rnoh/react-native-openharmony 声明为 file:../react_native_openharmony_release.har——那是本库构建工程内的相对路径,在宿主工程里不可解析。因此消费方必须在工程根目录 oh-package.json5 的 overrides 中把它指向自己的 RNOH HAR,否则 ohpm install 会解析失败:
{
"overrides": {
// ① 必需:指向宿主工程实际使用的 RNOH HAR,路径按自己的工程结构调整
"@rnoh/react-native-openharmony": "file:../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har",
// ② 仅当需要**媒体帧级 E2EE**(frameCryptor* / keyProviderSetSifTrailer)时加这一行:
// 把 WebRTC 引擎重定向到本仓的增补引擎 HAR(官方引擎没引出帧变换器挂接点,见文首说明)。
// ⚠️ 文件名必须带版本号——`file:` 依赖的 HAR 换了内容却不换文件名时,宿主会用**陈旧缓存**(rn 分册坑 RK-13)。
"@ohos/webrtc": "file:./libs/ohos-webrtc-1.0.2-e2ee.1.har"
}
}若 RNOH 以源码目录形式接入,则写成目录路径(例如 "./react_native_openharmony")。
②接入后的自检(两项同核,缺一不可)——ohpm install 之后:
# a. 引擎实现在位:须 11,286,064 B
# (官方版 = 10,691,296 B〔1.0.2〕/ 11,150,120 B〔1.0.3〕,读到这两个数说明 override 没生效)
ls -l oh_modules/.ohpm/@ohos+webrtc*/oh_modules/@ohos/webrtc/libs/arm64-v8a/libohos_webrtc.so
# b. 类型声明在位:官方 = 0,接入增补引擎后 > 0
# ⚠️ 探针必须用 FrameCryptor,不能用 onstatechange —— 官方 d.ts 本来就有 3 个 onstatechange
# (RTCDtlsTransport / RTCIceTransport / RTCSctpTransport 各一),拿它当探针区分不出来
grep -c FrameCryptor oh_modules/.ohpm/@ohos+webrtc*/oh_modules/@ohos/webrtc/src/main/libohos_webrtc/webrtc.d.ts
# c. 采集启停声明在位:官方基线可能缺失,接入增补引擎后必须命中
grep -c "startCapture(): void" oh_modules/.ohpm/@ohos+webrtc*/oh_modules/@ohos/webrtc/src/main/libohos_webrtc/webrtc.d.ts三项必须同时满足:只有 a 会编译报错(声明缺失),只有 b 则编译通过但真机上帧侧 E2EE 静默失效,没有任何编译期信号。
增补引擎 HAR 怎么来:由本仓
engine/pack-engine-har.js从官方基线包打出 ——curl -sSL -o webrtc-1.0.2.har https://ohpm.openharmony.cn/ohpm/@ohos/webrtc/-/webrtc-1.0.2.har sha256sum webrtc-1.0.2.har # 须 = a1f0e71a3d0462fcdfb835397b178fd91fb55bc4f445d2e72796c155e3a1b6b6 node engine/pack-engine-har.js --base webrtc-1.0.2.har --tag 1 # → engine/dist/ohos-webrtc-1.0.2-e2ee.1.har(不入仓)基线取 1.0.2 而非 1.0.3 有实测依据(门面能力须 ≤
.so能力;且 1.0.3 起官方包不再带libc++_shared.so,而自建.soDT_NEEDED它)——见scripts/README-webrtc-packaging.md; 引擎本身的由来见engine/README.md。 当前 demo / auto 已改为npm install准备本地harmony/webrtc.har与engine/dist/ohos-webrtc-1.0.2-e2ee.1.har, DevEco 编译时通过file:依赖直接安装补丁版@ohos/webrtc。如果本机残留旧oh_modules,先清理后重新执行npm install/ DevEco Sync。
2. 引入原生端依赖
打开 entry/oh-package.json5,添加:
"dependencies": {
"@oh-rn/react-native-webrtc": "file:../../node_modules/@hxa-rn/react-native-webrtc/harmony/webrtc.har"
}执行 ohpm install。
3. 配置 CMakeLists
打开 entry/src/main/cpp/CMakeLists.txt,添加:
set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
add_subdirectory("${OH_MODULES}/@oh-rn/react-native-webrtc/src/main/cpp" ./webrtc)
target_link_libraries(rnoh_app PUBLIC webrtc)4. 注册 Package(C++ 侧)
打开 entry/src/main/cpp/PackageProvider.cpp,添加:
#include "WebrtcPackage.h"
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<WebrtcPackage>(ctx),
};
}5. 注册 Package(ETS 侧)
打开 entry/src/main/ets/RNPackagesFactory.ets,添加:
import { WebrtcPackage } from '@oh-rn/react-native-webrtc/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
new WebrtcPackage(ctx),
];
}属性 / API
| API | 描述 | 参数 | 返回值 | HarmonyOS 支持 |
|-----|------|------|--------|----------------|
| permissions.query | 查询 camera / microphone 权限态 | { name: 'camera' \| 'microphone' } | Promise<'granted' \| 'prompt'> | 支持(鸿蒙无法区分「未申请」与「已拒绝」,未授权统一为 prompt) |
| permissions.request | 申请权限(已授权不弹窗) | { name } | Promise<boolean> | 完全支持 |
| mediaDevices.enumerateDevices | 枚举摄像头 / 音频输入输出设备;每项 deviceId / kind / label / groupId,videoinput 项另带 facing('front' / 'environment' / 'unknown',同上游) | 无 | Promise<MediaDeviceInfo[]> | 完全支持 |
| mediaDevices.getUserMedia | 打开摄像头 / 麦克风并返回媒体流 | constraints: { audio?, video? } | Promise<MediaStream> | 完全支持 |
| mediaDevices.getDisplayMedia | 屏幕内容捕获(实时帧流,非录制落盘) | 无 | Promise<MediaStream> | 完全支持(无需 ohos.permission.CAPTURE_SCREEN) |
| mediaDevices.ondevicechange | 设备变化事件 | (event) => void | — | 支持 |
| MediaStream 容器操作 | new / addTrack / removeTrack / getTracks / getTrackById / toURL / active / release | 见原库 | 见原库 | 完全支持 |
| MediaStreamTrack.enabled / stop() / release() | 启停与释放轨道 | 见原库 | 见原库 | 完全支持 |
| MediaStreamTrack.getSettings() / getConstraints() | 读取生效值 / 请求约束 | 无 | 见原库 | 完全支持 |
| MediaStreamTrack.applyConstraints() | 按新约束重采并热替换底层轨道 | constraints? | Promise<void> | 支持(约束无法满足时按 W3C 语义 reject,不回填请求值) |
| MediaStreamTrack._switchCamera() | 前 / 后置切换(走 applyConstraints) | 无 | void | 完全支持 |
| MediaStreamTrack._setVolume() | 音量增益 | volume: number | void | 支持(引擎 AudioSource.setVolume) |
| MediaStreamTrack._setVideoEffects() | 视频特效(模糊 / 虚拟背景) | names: string[] | void | 不支持(引擎无特效管线,见「未实现功能」) |
| track.onended / onmute / onunmute | 轨道生命周期事件 | — | — | 支持(引擎无轨道级回调,由原生侧状态观察器上报真实状态迁移,粒度约 1s) |
| RTCPeerConnection 全家族 | 协商 / ICE / 收发轨道 / getStats / restartIce / setConfiguration | 见原库 | 见原库 | 完全支持 |
| RTCDataChannel / createDataChannel | 数据通道(文本 + 二进制) | 见原库 | 见原库 | 完全支持 |
| RTCRtpSender / RTCRtpReceiver / RTCRtpTransceiver | 收发参数、替换轨道、方向、编解码偏好、统计 | 见原库 | 见原库 | 完全支持 |
| RTCRtpSender.getCapabilities / RTCRtpReceiver.getCapabilities | 查询编解码能力 | kind: 'audio' \| 'video' | RTCRtpCapabilities | 支持(返回引擎 M120 的实际能力集,与 M144 略有差异) |
| RTCFrameCryptorFactory.createDefaultKeyProvider | 创建密钥提供者 | RTCKeyProviderOptions | RTCKeyProvider | 完全支持(数据侧 E2EE 的唯一入口) |
| RTCKeyProvider 的 setSharedKey / ratchetSharedKey / exportSharedKey / setKey / ratchetKey / exportKey / dispose | 密钥管理与棘轮 | 见原库 | 见原库 | 完全支持 |
| RTCDataPacketCryptorFactory / RTCDataPacketCryptor | 数据包加解密 | 见原库 | 见原库 | 完全支持(AES-256-GCM) |
| RTCKeyProvider.setSifTrailer | 服务端注入帧标识 | Uint8Array | Promise | 完全支持(帧侧能力,由 engine/ 自建引擎增补承担) |
| RTCFrameCryptorFactory.createFrameCryptorForRtpSender/Receiver、RTCFrameCryptor 全部方法 | 媒体帧级 E2EE | 见原库 | 见原库 | 完全支持(engine/ 自建引擎补齐 KeyProvider / FrameCryptor NAPI 绑定,密文线格式与上游一致;升级 @ohos/webrtc 须重新应用 engine/src/bindings.patch 并重建 .so) |
| RTCView / RTCPIPView | 视频渲染组件 | streamURL、mirror、objectFit、zOrder、iosPIP、onDimensionsChange | — | 完全支持 |
| startIOSPIP / stopIOSPIP | 画中画开关 | ref | Promise<boolean> | 支持(映射到 @ohos.PiPWindow,需为该视图传入 iosPIP 且其 enabled 为 true) |
| ScreenCapturePickerView | iOS 广播选择器视图 | ViewProps | — | 不支持(零内容占位;屏幕共享请直接用 getDisplayMedia) |
| RTCAudioSession | iOS AVAudioSession 通知 | 无 | void | 显式空操作(鸿蒙无该概念,方法保留以维持接口完整) |
| registerGlobals() | 注入 navigator.mediaDevices / RTCPeerConnection 等全局量 | 无 | void | 完全支持 |
| 纯 JS 数据类 | RTCIceCandidate、RTCSessionDescription、RTCRtpEncodingParameters 等 | 见原库 | 见原库 | 完全支持(与平台无关) |
宿主侧原生扩展点(WebRTCModuleOptions)
上游 Android / iOS 各有一个宿主原生侧单例 WebRTCModuleOptions,供宿主 App 在模块初始化前注入编解码工厂、
音频设备模块、原生日志等(JS 不可达)。鸿蒙侧对等物 = HAR 导出的 ArkTS 单例 WebRTCModuleOptions
(harmony/webrtc/src/main/ets/WebRTCModuleOptions.ets),在 RN 实例创建之前(如 EntryAbility.onCreate)设置,
WebRTCModule 首次初始化时读取一次:
// entry/src/main/ets/entryability/EntryAbility.ets
import webrtc from '@ohos/webrtc';
import { WebRTCModuleOptions } from '@oh-rn/react-native-webrtc';
const options = WebRTCModuleOptions.getInstance();
options.videoEncoderFactory = new webrtc.SoftwareVideoEncoderFactory(); // 缺省:引擎硬编优先
options.audioDeviceModule = new webrtc.AudioDeviceModule({ useHardwareAcousticEchoCanceler: true });
options.audioProcessing = new webrtc.AudioProcessingFactory().create();
options.injectableLogger = { logMessage: (msg, severity, tag) => console.log(`[${tag}] ${msg}`) };
options.loggingSeverity = 1; // 引擎 severity:0 VERBOSE / 1 INFO / 2 WARNING / 3 ERROR
options.defaultTrackVolume = 0.8; // 远端音轨到达时应用,缺省 1.0| 上游字段(Android / iOS) | 鸿蒙字段 | 引擎承载 |
|---|---|---|
| videoEncoderFactory / videoDecoderFactory | 同名 | PeerConnectionFactoryOptions.videoEncoderFactory / videoDecoderFactory;可注入 HardwareVideoEncoderFactory / SoftwareVideoEncoderFactory / HardwareVideoDecoderFactory / SoftwareVideoDecoderFactory |
| audioDeviceModule / audioDevice | audioDeviceModule | PeerConnectionFactoryOptions.adm;注入的是引擎自带 AudioDeviceModule 的配置实例(采样率 / 声道 / 硬件 AEC·NS 等),引擎不提供自定义 ADM 协议 |
| audioProcessingFactoryFactory / audioProcessingModule | audioProcessing | PeerConnectionFactoryOptions.audioProcessing(new AudioProcessingFactory().create()) |
| injectableLogger + loggingSeverity | 同名 | NativeLogging.injectLoggable;只设 loggingSeverity 则 NativeLogging.enableLogToDebugOutput |
| defaultTrackVolume | 同名 | 远端音轨首次到达时 setVolume;1.0 时不调用 |
| fieldTrials | 无 | 引擎 NAPI 层无对应入口(随包 webrtc.d.ts 检索 fieldTrial 零命中),已登记为平台缺口 |
| enableMediaProjectionService / enableMultitaskingCameraAccess | 无 | Android / iOS 平台专属开关,鸿蒙无对应概念 |
设置了任一工厂级字段(编解码工厂 / ADM / 音频处理)时,本库以这些选项构造引擎 PeerConnectionFactory 并调用
PeerConnectionFactory.setDefault,使引擎的 getUserMedia / getDisplayMedia 采集也走同一 ADM / 工厂;
一个字段都不设时与既往行为完全一致(new PeerConnectionFactory(),不动引擎默认工厂)。
平台差异
- 权限态:
abilityAccessCtrl.checkAccessToken只返回 GRANTED / DENIED,无法区分「从未申请」与「已永久拒绝」。 未授权统一映射为 W3C 的'prompt'(与上游 Android 分支granted ? GRANTED : PROMPT一致), 避免应用误判为不可再申请。 enumerateDevices的facing:引擎@ohos/webrtc的设备项只有deviceId/kind/label/groupId,朝向在其 NAPI 边界被丢弃; 适配层用相机 kitgetCameraManager(context).getSupportedCameras()按cameraId(= 引擎视频deviceId)回取cameraPosition映射为'front'/'environment',拿不到 context 或相机服务异常时按 label 关键字回退,仍辨不出写'unknown'(与 iOS 同值)。 该查询不需要相机权限,失败不影响枚举本身。applyConstraints:引擎VideoSource无运行期切换摄像头 / 重配分辨率的接口,故按新约束重新采集一路 视频轨道并热替换到所有 sender 与所属MediaStream,JS 侧track.id不变。新轨道采集失败时抛错、保留原轨道。onended/onmute/onunmute:引擎的MediaStreamTrack继承的是空EventTarget,没有轨道级回调。 适配层以约 1s 粒度读取readyState/MediaSource.state的真实取值并在跃迁时上报; 本地轨道被显式release()时立即上报,不等轮询。无轨道被观察时定时器自动停摆。- 数据侧 E2EE 的密码学约定(跨端互通须一致):
- 密钥材料 → 加密密钥:
keyDerivationAlgorithm为HKDF(1)时用 HKDF-SHA256(ikm=材料, salt=ratchetSalt, info=128 字节 0x00, L=32);为PBKDF2(0)时用 PBKDF2-SHA256(password=材料, salt=ratchetSalt, c=100000, L=32)。 - 棘轮恒用 PBKDF2-SHA256(与上游
ratchet()固定取 PBKDF2 参数一致)。 - 数据包:AES-256-GCM,12 字节随机 IV,16 字节 authTag 追加在密文尾部,无 AAD。
- 解密失败(密钥不符 / 密文被篡改)reject(
await decrypt()抛Error('packet decryption failed'),与上游 Android / iOS 的dataPacketCryptorDecryptFailedreject 同形态);ratchetWindowSize > 0时会向前追赶至多 N 次棘轮再判失败。
- 密钥材料 → 加密密钥:
- 画中画的自动进入 / 退出(
iosPIP.startAutomatically/stopAutomatically):由应用前后台切换驱动, 监听走免权限的ApplicationContext.on('applicationStateChange')(不是需要 system_basic 级ohos.permission.RUNNING_STATE_OBSERVER的appManager.on('applicationState'),普通应用申请不到该权限, 用它会导致这两个配置项在真机上永远不生效)。该监听在个别设备形态上可能不可用,注册失败时仅记录日志, 手动的startIOSPIP/stopIOSPIP不受影响。 zOrder映射为该视图根节点的 ArkUIzIndex;跨 RN 兄弟视图的最终层叠仍受 RN 自身视图顺序影响。
未实现功能
| API | 原因 |
|-----|------|
| MediaStreamTrack._setVideoEffects() | 上游 Android/iOS 把宿主 App 经原生 ProcessorProvider.addProcessor 注册的帧处理器链挂到采集源上(Android GetUserMediaImpl.java:440-465、iOS WebRTCModule+RTCMediaStream.m:498-521)。@ohos/[email protected] / 1.0.3 的 NAPI 绑定层未把 VideoSource 的帧处理挂接点引出(随包 webrtc.d.ts 检索 effect、blur、background、beauty、VideoProcessor 零命中;VideoSource 只有采集启停 / 切换摄像头事件与 switchCamera() / release();原件归档 engine/reference/webrtc-1.0.{2,3}.d.ts),本仓亦无宿主侧处理器注册入口。桥方法保留在 ABI 上,仅记录日志、不伪装成功。补齐路线同 engine/ 的帧变换器增补(须重建 .so),已挂账、待客户用例触发后实施。 |
| ScreenCapturePickerView | 对应 iOS RPSystemBroadcastPickerView(ReplayKit 广播扩展选择器),鸿蒙由系统在 getDisplayMedia() 时自行弹出确认,无对应的应用内选择器视图。屏幕共享功能本身可用。 |
| RTCAudioSession.audioSessionDidActivate / ...DidDeactivate | 对应 iOS CallKit / AVAudioSession 语义,鸿蒙无该概念。方法保留在 ABI 上并记录日志,调用安全。 |
媒体帧级 E2EE(
RTCFrameCryptor全部方法与setSifTrailer)已实现:由engine/归档的自建引擎增补 提供KeyProvider/FrameCryptorNAPI 绑定(LiveKitFrameCryptorTransformer的直接绑定), 工厂方法只在对等连接 / sender / receiver 无法解析时返回空 id(触发上游既有的Error when creating frame cryptor)。
使用限制
- 需要
ohos.permission.INTERNET、ohos.permission.CAMERA、ohos.permission.MICROPHONE(后两者为user_grant,需运行时申请)。屏幕共享不需要ohos.permission.CAPTURE_SCREEN。 - 引擎基线为 libwebrtc M120,与上游标称的 M144 在编解码器集合与个别新接口上有差异;
能力查询请以
getCapabilities()的实际返回为准。 - 数据侧 E2EE 与其它端互通时,双方须采用上文列出的同一套密码学约定。
- 真机验证请使用本仓库
example与example_auto按宿主工程流程逐项走查。
快速验证(运行 Example)
前置条件
| 依赖 | 版本要求 | |------|----------| | Node.js | >= 18 | | DevEco Studio | 5.0+ / 6.0+ | | HarmonyOS SDK | API 12+ |
运行步骤
1. 克隆仓库
git clone <仓库地址>
cd <仓库目录>2. 安装依赖并构建
cd example
npm install示例工程从 npm 安装精确版本 @hxa-rn/[email protected]。安装包已包含
harmony/webrtc.har 与增补引擎 HAR,不会在使用者机器上重新编译原生库。
3. 如需运行自动化示例,安装 example_auto 依赖
cd ../example_auto
npm install4. 确认 JS Bundle
npm install 的 postinstall 会准备 Hvigor 插件并自动生成内置包:
harmony/entry/src/main/resources/rawfile/bundle.harmony.js如需手工重建,可执行:
npm run dev5. 用 DevEco Studio 打开鸿蒙工程
- 打开 DevEco Studio
- 普通示例选择
example/harmony,自动化示例选择example_auto/harmony - 等待 Sync 完成
6. 编译并运行 HAP
在 DevEco Studio 中点击运行按钮,将 HAP 安装到设备/模拟器。
注意:Example 中已预置插件依赖和 Package 注册,无需手动配置 Link。
约束与限制
兼容性
- RNOH: 0.72+
- HarmonyOS SDK: API 12+
- DevEco Studio: 5.0+
遗留问题
- 媒体帧级 E2EE 依赖增补引擎:帧侧 7 个桥方法由
engine/prebuilt/的增补引擎承担; 升级@ohos/webrtc时须在新基线上重新应用engine/src/bindings.patch并重建.so,否则帧侧能力回退。frameCryptionStateChanged事件需 2026-08-14 之后重建的.so。 分发方式正在切换(2026-09-15 拍板):旧 =ohpm install后手工跑apply-engine.js覆盖oh_modules—— 该做法有一个无声失效模式:ohpm install/ DevEco Sync 会把.so还原成官方版,而 d.ts 的追加是幂等的, 于是出现「声明在、实现没了 ⇒ 编译通过、真机静默失效」;新 = 发分叉引擎 HAR 由overrides接入(Link 步骤 1), 装完即是对的、没有可被还原的补丁。方案与待实测清单见scripts/README-webrtc-packaging.md, 切换尚未在构建机上验完,当前仍以旧链路为准。 _setVideoEffects未实现:引擎 NAPI 层未引出视频帧处理挂接点,见「未实现功能」。- 真机验证:未覆盖的场景建议用本仓库
example逐条走查。 - 数据侧 E2EE 的跨端互通未经对端实测:实现遵循上游 LiveKit 客户端 SDK 的密码学约定(见「平台差异」), 与其它端联调前建议先做一次往返比对。
开源协议
本项目基于 MIT License,详见 LICENSE 文件。
