更新记录
1.0.4(2026-04-19)
优化一对一通话。更新文档
1.0.3(2026-04-17)
更新安卓版
1.0.2(2026-04-16)
更新文档,优化一对一通话视图,增加音频设置
查看更多
平台兼容性
uni-app(4.81)
| Vue2 |
Vue2插件版本 |
Vue3 |
Vue3插件版本 |
Chrome |
Safari |
app-vue |
app-nvue |
app-nvue插件版本 |
Android |
iOS |
iOS插件版本 |
鸿蒙 |
| √ |
1.0.0 |
√ |
1.0.0 |
- |
- |
- |
√ |
1.0.0 |
5.0 |
12 |
1.0.0 |
- |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| - |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
RTC VolcEngine 组件
火山引擎实时音视频 SDK UTS 插件,支持 AI 语音通话、视频通话、字幕、录制等功能。
安装
将 rtc-volcengine 插件放入 uni_modules 目录。
基础用法
<template>
<view class="container">
<!-- RTC 组件,会自动创建本地和远端视频视图 -->
<rtc-volcengine
ref="rtcRef"
@onEngineReady="onEngineReady"
@onRoomStateChanged="onRoomStateChanged"
@onUserJoined="onUserJoined"
@onUserLeave="onUserLeave"
@onAudioRouteChanged="onAudioRouteChanged"
@onLog="onRTCLog"
@onTokenWillExpire="handleTokenWillExpire"
/>
<button @click="joinRoom">加入房间</button>
<button @click="leaveRoom">离开房间</button>
<button @click="interruptAI">打断AI</button>
<button @click="toggleMute">静音/取消静音</button>
<button @click="toggleSpeaker">扬声器/听筒</button>
<button @click="switchCamera">切换摄像头</button>
</view>
</template>
<script>
export default {
data() {
return {
isMuted: false,
isSpeaker: true
}
},
methods: {
// 初始化并加入房间
joinRoom() {
this.$refs.rtcRef.initEngine('YOUR_APP_ID', false)
},
// 引擎初始化完成回调
onEngineReady(e) {
const { success, message } = e.detail
if (!success) return
// 设置音频场景
this.$refs.rtcRef.setAudioScenario(5) // AI客户端
this.$refs.rtcRef.setAnsMode(4) // 自动降噪
this.$refs.rtcRef.enablePlaybackDucking(true)
this.$refs.rtcRef.enableVocalInstrumentBalance(true)
this.$refs.rtcRef.enableAudioPropertiesReport(500)
// 设置默认音频路由为扬声器
this.$refs.rtcRef.setDefaultAudioRoute(3)
this.$refs.rtcRef.setAudioRoute(-1)
// 显示本地视频
this.$refs.rtcRef.showLocalVideo()
// 加入房间
this.$refs.rtcRef.joinRoom(
'ROOM_ID', 'USER_ID', 'TOKEN',
true, true, // 发布音频、发布视频
true, true, // 订阅音频、订阅视频
true, true, // 启动音频采集、启动视频采集
19 // Call 通话模式
)
},
// 房间状态变化
onRoomStateChanged(e) {
const { roomId, userId, state, extraInfo } = e.detail
// state: 0=加入房间成功, !0=失败/异常退房/警告或错误
if (state === 0) {
console.log('加入房间成功')
} else {
console.log('房间异常, state=', state)
}
},
// 远端用户加入 - 绑定远端视频
onUserJoined(e) {
const remoteUserId = e.detail.userId
this.$refs.rtcRef.setupRemoteVideo(remoteUserId)
},
// 用户离开
onUserLeave(e) {
this.$refs.rtcRef.hideRemoteVideo()
},
// 离开房间
leaveRoom() {
this.$refs.rtcRef.hideLocalVideo()
this.$refs.rtcRef.hideRemoteVideo()
this.$refs.rtcRef.leaveRoom()
this.$refs.rtcRef.destroyEngine()
},
// 打断AI说话
interruptAI() {
this.$refs.rtcRef.sendInterruptMessage('AI_BOT_ID')
},
// 静音/取消静音
toggleMute() {
this.isMuted = !this.isMuted
this.$refs.rtcRef.muteAudioCapture(this.isMuted)
},
// 扬声器/听筒切换
toggleSpeaker() {
this.isSpeaker = !this.isSpeaker
this.$refs.rtcRef.setDefaultAudioRoute(this.isSpeaker ? 3 : 2)
this.$refs.rtcRef.setAudioRoute(-1)
},
// 切换摄像头
switchCamera() {
this.$refs.rtcRef.switchCamera()
},
// 音频路由变化回调
onAudioRouteChanged(e) {
const route = e.detail.route
console.log('音频路由变化:', route)
},
// 原生日志回调
onRTCLog(e) {
console.log('[Native]', e.detail.message)
},
// Token 即将过期
handleTokenWillExpire() {
// 调用后端获取新 Token,然后更新
this.$refs.rtcRef.updateToken(newToken)
}
},
onUnload() {
this.$refs.rtcRef.leaveRoom()
this.$refs.rtcRef.destroyEngine()
}
}
</script>
事件回调列表
所有事件回调的数据在 e.detail 中获取。
| 事件名 |
参数 (e.detail) |
说明 |
onEngineReady |
{ success: boolean, message: string } |
引擎初始化结果 |
onRoomStateChanged |
{ roomId, userId, state, extraInfo } |
房间状态变化,state: 0=加入房间成功, !0=失败/异常退房/警告或错误 |
onUserJoined |
{ roomId, userId, elapsed } |
远端用户加入 |
onUserLeave |
{ roomId, userId, reason } |
远端用户离开 |
onLeaveRoom |
{ roomId, stats } |
离开房间统计,stats 含 duration/txBytes/rxBytes |
onNetworkQuality |
{ roomId, userId, txQuality, rxQuality } |
网络质量 |
onError |
{ errorCode, errorMessage } |
错误回调 |
onLocalAudioProperties |
{ volume } |
本地音频音量,需先调用 enableAudioPropertiesReport |
onRemoteAudioProperties |
{ userId, volume } |
远端音频音量 |
onSubtitleMessage |
{ userId, text, language, mode, sequence, definite } |
字幕消息,需先调用 startSubtitle |
onRoomBinaryMessage |
{ msgid, userId, messageType, message } |
房间二进制消息,messageType: "subtitle"/"conversation" |
onRecordingState |
{ state, errorCode, filePath, duration, fileSize } |
录制状态变化 |
onConnectionStateChanged |
{ state } |
SDK 与信令服务器连接状态变化 |
onTokenWillExpire |
{} |
Token 即将过期 |
onLog |
{ message } |
原生日志回调,所有 Swift 端日志通过此回调返回 |
onAudioRouteChanged |
{ route } |
音频路由变化,route 值参见下方路由说明 |
方法列表
引擎管理
| 方法 |
参数 |
返回值 |
说明 |
initEngine(appId, isGameScene) |
appId: string, isGameScene?: boolean (默认 false) |
void |
初始化引擎,结果通过 onEngineReady 事件通知 |
destroyEngine() |
- |
void |
销毁引擎,释放所有资源 |
isReady() |
- |
boolean |
引擎是否已初始化 |
房间管理
| 方法 |
参数 |
返回值 |
说明 |
joinRoom(roomId, userId, token, ...) |
见下方详细参数 |
void |
加入房间 |
leaveRoom() |
- |
void |
离开房间,自动停止音视频采集 |
updateToken(token) |
token: string |
void |
更新 Token(收到 onTokenWillExpire 时调用) |
isJoined() |
- |
boolean |
是否已加入房间 |
音频控制
| 方法 |
参数 |
返回值 |
说明 |
enableLocalAudio(enable) |
enable: boolean |
void |
启用/禁用本地音频采集 |
muteAudioCapture(mute) |
mute: boolean |
void |
静音/取消静音本地音频采集(采集仍继续,只是不发数据) |
setVolume(volume) |
volume: number |
void |
设置播放音量 |
setAudioScenario(scenario) |
scenario: number (0-5) |
void |
设置音频场景,0=默认, 1=聊天室, 2=游戏串流, 3=合唱, 4=教育, 5=AI客户端 |
enablePlaybackDucking(enable) |
enable: boolean |
void |
开关音量闪避,开启后检测到远端人声时本地媒体音量自动减弱 |
enableVocalInstrumentBalance(enable) |
enable: boolean |
void |
开关音量均衡,开启后人声响度调整为 -16lufs |
setAnsMode(mode) |
mode: number (0-4) |
void |
设置音频降噪模式,0=禁用, 1=微弱, 2=中度, 3=高度(AI降噪), 4=自动 |
enableAudioPropertiesReport(interval) |
interval: number (毫秒) |
void |
启用音频属性上报,建议间隔不低于 100ms |
音频路由
| 方法 |
参数 |
返回值 |
说明 |
setAudioRoute(route) |
route: number |
number (0=成功) |
强制切换音频播放路由,仅支持扬声器(3)和默认路由(-1) |
setDefaultAudioRoute(route) |
route: number |
number (0=成功) |
设置默认音频路由,仅支持听筒(2)或扬声器(3) |
getAudioRoute() |
- |
number |
获取当前音频路由值 |
音频路由值说明:
| 值 |
含义 |
备注 |
| -1 |
Default |
使用 setDefaultAudioRoute 设置的默认路由 |
| 1 |
Headset |
有线耳机 |
| 2 |
Earpiece |
听筒 |
| 3 |
Speakerphone |
扬声器 |
| 4 |
HeadsetBluetooth |
蓝牙耳机 |
| 5 |
HeadsetUSB |
USB耳机 |
切换扬声器/听筒的正确方式:
// 切换到扬声器
this.$refs.rtcRef.setDefaultAudioRoute(3) // 设默认路由为扬声器
this.$refs.rtcRef.setAudioRoute(-1) // 切到默认路由
// 切换到听筒
this.$refs.rtcRef.setDefaultAudioRoute(2) // 设默认路由为听筒
this.$refs.rtcRef.setAudioRoute(-1) // 切到默认路由
注意:setAudioRoute 仅支持扬声器和默认路由,不能直接传入听筒(2)。切换听筒必须通过 setDefaultAudioRoute(2) + setAudioRoute(-1) 组合实现。
视频控制
| 方法 |
参数 |
返回值 |
说明 |
enableLocalVideo(enable) |
enable: boolean |
void |
启用/禁用本地视频采集 |
switchCamera() |
- |
void |
切换前后摄像头 |
showLocalVideo() |
- |
void |
显示本地视频小窗 |
hideLocalVideo() |
- |
void |
隐藏本地视频小窗 |
setupRemoteVideo(userId) |
userId: string |
void |
绑定远端用户视频视图(使用组件内部视图),Swift 端会自动用正确的 streamId 重新绑定 |
hideRemoteVideo() |
- |
void |
隐藏远端视频并清除头像占位 |
toggleVideoLayout() |
- |
void |
切换大小窗布局(本地大窗↔远端大窗),零布局开销 |
setVideoOrientation(orientation) |
orientation: number |
void |
设置视频帧朝向:0=Adaptive(跟随相机), 1=Portrait(竖屏), 2=Landscape(横屏) |
setDummyCaptureImagePath(imagePath) |
imagePath: string |
void |
摄像头关闭时用静态图片填充本地推送视频流,传空字符串停止发送 |
setRemoteAvatar(imagePath) |
imagePath: string |
void |
设置远端用户头像占位图,远端未加入时显示。支持本地路径和网络URL(http/https),传空字符串清除 |
setLocalVideoView(view) |
view: UIView |
void |
设置本地视频渲染视图(高级用法) |
setRemoteVideoView(userId, view) |
userId: string, view: UIView |
void |
设置远端用户视频渲染视图(高级用法) |
消息发送
| 方法 |
参数 |
返回值 |
说明 |
sendUserBinaryMessage(userId, jsonString) |
userId: string, jsonString: string |
number (消息ID) |
发送点对点二进制消息,JSON 字符串会转为二进制发送 |
sendInterruptMessage(aiUserId) |
aiUserId: string |
number (消息ID) |
发送打断 AI 的消息,内部使用 ctrl+interrupt 二进制协议 |
字幕
| 方法 |
参数 |
返回值 |
说明 |
startSubtitle(mode, targetLanguage) |
mode: number (0=识别, 1=翻译), targetLanguage: string |
void |
开始字幕 |
stopSubtitle() |
- |
void |
停止字幕 |
本地录制
| 方法 |
参数 |
返回值 |
说明 |
startFileRecording(dirPath, type, fileType) |
dirPath: string, type?: number (默认2), fileType?: number (默认1) |
number |
开始录制。type: 0=仅音频, 1=仅视频, 2=音视频; fileType: 0=AAC, 1=MP4 |
stopFileRecording() |
- |
number |
停止录制 |
joinRoom 参数说明
joinRoom(
roomId, // string - 房间ID
userId, // string - 用户ID
token, // string - Token
publishAudio, // boolean - 是否发布音频,默认 true
publishVideo, // boolean - 是否发布视频,默认 true
subscribeAudio, // boolean - 是否订阅音频,默认 true
subscribeVideo, // boolean - 是否订阅视频,默认 true
startAudioCapture, // boolean - 是否启动音频采集,默认 true
startVideoCapture, // boolean - 是否启动视频采集,默认 true
roomProfile // number - 房间模式,默认 0 (Communication),进房后不可更改
)
注意:joinRoom 不支持回调参数,加入房间的结果通过 onRoomStateChanged 事件获取。当 state === 0 时表示加入成功,state !== 0 时表示失败或异常。
roomProfile 枚举值
| 值 |
模式 |
说明 |
| 0 |
Communication |
通用通信模式 |
| 2 |
Game |
游戏语音模式 |
| 3 |
CloudGame |
云游戏模式 |
| 4 |
LowLatency |
云渲染低延迟模式 |
| 6 |
ChatRoom |
语音聊天室 |
| 10 |
InteractivePodcast |
互动直播 |
| 12 |
Chorus |
合唱模式 |
| 14 |
GameStreaming |
游戏串流 |
| 16 |
Meeting |
会议模式 |
| 17 |
MeetingRoom |
会议室终端 |
| 18 |
Classroom |
课堂互动 |
| 19 |
Call |
通话模式 |
| 20 |
Live |
直播互动 |
典型场景示例
AI 语音通话 + 字幕 + 录制
<template>
<rtc-volcengine
ref="rtcRef"
@onEngineReady="onEngineReady"
@onRoomStateChanged="onRoomStateChanged"
@onUserJoined="onUserJoined"
@onRemoteAudioProperties="onRemoteAudioProperties"
@onSubtitleMessage="onSubtitleMessage"
@onRoomBinaryMessage="onRoomBinaryMessage"
@onTokenWillExpire="handleTokenWillExpire"
@onRecordingState="onRecordingState"
@onLog="onRTCLog"
/>
</template>
<script>
export default {
methods: {
onEngineReady(e) {
if (!e.detail.success) return
// 设置 AI 语音场景
this.$refs.rtcRef.setAudioScenario(5) // AI客户端
this.$refs.rtcRef.setAnsMode(4) // 自动降噪
this.$refs.rtcRef.enablePlaybackDucking(true)
this.$refs.rtcRef.enableVocalInstrumentBalance(true)
// 启用音量回调
this.$refs.rtcRef.enableAudioPropertiesReport(200)
// 设置默认音频路由为扬声器
this.$refs.rtcRef.setDefaultAudioRoute(3)
this.$refs.rtcRef.setAudioRoute(-1)
// 加入房间(结果通过 onRoomStateChanged 事件获取)
this.$refs.rtcRef.joinRoom(
'ROOM_ID', 'USER_ID', 'TOKEN',
true, false, // 发布音频,不发布视频
true, false, // 订阅音频,不订阅视频
true, false, // 启动音频采集,不启动视频采集
19 // Call 通话模式
)
},
onRoomStateChanged(e) {
const { state } = e.detail
// state: 0=加入房间成功, !0=失败/异常
if (state === 0) {
// 加入成功后开启字幕
this.$refs.rtcRef.startSubtitle(0, '') // 识别模式
// 开始录制
this.$refs.rtcRef.startFileRecording('/path/to/save', 0, 1) // 仅音频,MP4
}
},
onUserJoined(e) {
// 远端用户加入,绑定远端视频
this.$refs.rtcRef.setupRemoteVideo(e.detail.userId)
},
onRemoteAudioProperties(e) {
console.log(`远端 ${e.detail.userId} 音量: ${e.detail.volume}`)
},
onSubtitleMessage(e) {
const { userId, text, definite } = e.detail
console.log(`字幕 [${userId}]: ${text} (确定: ${definite})`)
},
onRoomBinaryMessage(e) {
const { messageType, message, userId } = e.detail
if (messageType === 'conversation') {
console.log(`AI状态 [${userId}]: ${message}`)
} else if (messageType === 'subtitle') {
console.log(`字幕消息 [${userId}]: ${message}`)
}
},
onRecordingState(e) {
const { state, errorCode, filePath } = e.detail
console.log(`录制状态: ${state}, 路径: ${filePath}`)
},
onRTCLog(e) {
// 原生日志,可用于调试
console.log('[Native]', e.detail.message)
},
// 打断 AI 说话
interruptAI() {
this.$refs.rtcRef.sendInterruptMessage('AI_USER_ID')
},
// 发送自定义消息给 AI
sendCustomMessage() {
const json = JSON.stringify({ type: 'command', action: 'pause' })
this.$refs.rtcRef.sendUserBinaryMessage('AI_USER_ID', json)
},
// Token 过期处理
handleTokenWillExpire() {
// 调用后端获取新 Token
const newToken = fetchNewToken()
this.$refs.rtcRef.updateToken(newToken)
}
}
}
</script>
视频通话示例
<template>
<rtc-volcengine
ref="rtcRef"
style="width: 750rpx; height: 1000rpx;"
@onEngineReady="onEngineReady"
@onRoomStateChanged="onRoomStateChanged"
@onUserJoined="onUserJoined"
@onUserLeave="onUserLeave"
@onAudioRouteChanged="onAudioRouteChanged"
/>
</template>
<script>
export default {
data() {
return {
isMuted: false,
isVideoOff: false,
isSpeaker: true
}
},
methods: {
onEngineReady(e) {
if (!e.detail.success) return
// 通话场景设置
this.$refs.rtcRef.setAudioScenario(5)
this.$refs.rtcRef.setAnsMode(4)
this.$refs.rtcRef.enablePlaybackDucking(true)
this.$refs.rtcRef.enableVocalInstrumentBalance(true)
this.$refs.rtcRef.enableAudioPropertiesReport(500)
// 默认扬声器
this.$refs.rtcRef.setDefaultAudioRoute(3)
this.$refs.rtcRef.setAudioRoute(-1)
// 显示本地视频
this.$refs.rtcRef.showLocalVideo()
// 加入视频通话
this.$refs.rtcRef.joinRoom(
'ROOM_ID', 'USER_ID', 'TOKEN',
true, true, // 发布音频+视频
true, true, // 订阅音频+视频
true, true, // 启动音频+视频采集
19 // Call 通话模式
)
},
onRoomStateChanged(e) {
if (e.detail.state === 0) {
console.log('加入房间成功')
}
},
onUserJoined(e) {
// 绑定远端视频
this.$refs.rtcRef.setupRemoteVideo(e.detail.userId)
},
onUserLeave(e) {
this.$refs.rtcRef.hideRemoteVideo()
},
// 音频路由变化
onAudioRouteChanged(e) {
const routeMap = { '-1': '默认', '1': '有线耳机', '2': '听筒', '3': '扬声器', '4': '蓝牙耳机', '5': 'USB耳机' }
this.isSpeaker = (String(e.detail.route) === '3')
},
// 控制方法
toggleMute() {
this.isMuted = !this.isMuted
this.$refs.rtcRef.muteAudioCapture(this.isMuted)
},
toggleVideo() {
this.isVideoOff = !this.isVideoOff
this.$refs.rtcRef.enableLocalVideo(!this.isVideoOff)
this.isVideoOff ? this.$refs.rtcRef.hideLocalVideo() : this.$refs.rtcRef.showLocalVideo()
},
switchCamera() {
this.$refs.rtcRef.switchCamera()
},
toggleSpeaker() {
this.isSpeaker = !this.isSpeaker
this.$refs.rtcRef.setDefaultAudioRoute(this.isSpeaker ? 3 : 2)
this.$refs.rtcRef.setAudioRoute(-1)
},
leaveRoom() {
this.$refs.rtcRef.hideLocalVideo()
this.$refs.rtcRef.hideRemoteVideo()
this.$refs.rtcRef.leaveRoom()
this.$refs.rtcRef.destroyEngine()
}
},
onUnload() {
this.$refs.rtcRef.leaveRoom()
this.$refs.rtcRef.destroyEngine()
}
}
</script>
Token 过期处理流程
1. 加入房间时传入 Token
│
▼
2. 正常通话中...
│
▼
3. Token 过期前 30 秒,触发 onTokenWillExpire 回调
│
▼
4. 调用后端接口获取新 Token
│
▼
5. 调用 updateToken(newToken) 更新 Token
│
▼
6. 继续正常通话...
注意事项
- 平台支持:支持 iOS 12+ 和 Android
- 初始化顺序:必须先调用
initEngine,等待 onEngineReady 回调成功后才能调用其他方法
- Token 有效期:建议设置合理的有效期,并在过期前通过
onTokenWillExpire 回调更新
- 打断 AI:
sendInterruptMessage 使用特殊的 ctrl+interrupt 二进制协议,内部已处理
- 静音 vs 停用:
muteAudioCapture(true) 静音但采集继续;enableLocalAudio(false) 完全停止采集
- 录制:
startFileRecording 的 type 和 fileType 可选,默认录制音视频 MP4
- 内存管理:离开页面时务必调用
destroyEngine() 释放资源,组件卸载时会自动离开房间
- 回调线程:所有回调已在内部切换到主线程,可直接操作 UI
- joinRoom 回调:
joinRoom 不支持传入 success/fail 回调参数,传入会导致 iOS 闪退。加入房间结果请通过 onRoomStateChanged 事件监听(state === 0 表示加入成功)
- 音频路由:
setAudioRoute 仅支持扬声器(3)和默认路由(-1),切换听筒必须通过 setDefaultAudioRoute(2) + setAudioRoute(-1) 组合
- 远端视频:调用
setupRemoteVideo(userId) 绑定远端视频后,Swift 端会在收到首帧解码时自动用正确的 streamId 重新绑定画布
- 原生日志:Swift 端所有日志通过
onLog 事件回调返回,不在 Xcode 控制台打印
- 大小窗切换:
toggleVideoLayout 仅交换渲染目标,不改 LayoutParams,零布局开销,切换非常快
- 视频朝向:
setVideoOrientation 是 SDK 层面的视频帧朝向设置,影响整个 RTC 链路(采集→编码→传输→渲染),不是简单的 View 旋转
- 头像占位:
setRemoteAvatar 在远端未加入时显示头像占位,远端加入后自动隐藏;退出房间时头像会自动清除
- 占位图片:
setDummyCaptureImagePath 在摄像头关闭(stopVideoCapture)后开始推送静态图片,重新开启采集或传空字符串可停止