rtc-room组件
收藏
我的收藏

基础库 2.42.0 开始支持本组件。​
RTC (Real Time Communication)多人音视频通话视频画面展示组件,相关 API 请参考 tt.createRtcRoomContext。​
接入 RTC 前置工作:​
    1.该能力需申请权限使用,满足申请条件后,开发者登录控制台,在小程序应用中筛选“能力-互动能力-小程序直播”,申请RTC 能力,能力使用规范详情查看RTC能力开通及使用规范;申请条件如下:​
    a.信用分 >= 90分​
    b.服务类目​
一级类目
二级类目
三级类目
教育 ​
教育 ​
在线教育 ​
医疗 ​
医疗 ​
公立医疗机构、私立医疗机构、互联网医院 ​
备注:具体开放类目依据后台申请入口显示状态为准,更多类目开放可关注平台公告或进入rtc官网交流群咨询。​
    2.需要申请开通 rtc 服务,注册生成 AppId,参考:https://www.volcengine.com/docs/6348/69865
    3.需要申请 Token,Token 申请可参考:https://www.volcengine.com/docs/6348/70121

属性说明​

属性
类型
默认值
必填
说明
最低支持版本
user-id​
string​
是​
进入房间用户的 userId(开发者自定义,唯一区分用户即可)​
2.42.0​
mode​
string​
camera​
否​
对话窗口类型,可选值:​
camera:自身摄像头预览​
video:远端用户普通画面​
screen: 远端用户共享屏幕画面​
2.42.0​
binderror​
eventhandle​
否​
创建对话窗口失败时触发​
2.42.0​

binderror 事件对象的 detail​

object 类型,属性如下:​
属性名
类型
说明
errNo​
number​
错误码​
errMsg​
string​
错误信息​
错误说明​
errNo
errMsg
说明
最低支持版本
10101​
platform auth deny​
权限校验失败(需要申请白名单才能使用 rtc-room 能力)​
2.53.0​
21200​
rtc-room is not available​
rtc-room组件不可用(Android 可以使用 rtc-room 组件,iOS 系统小于等于 10 情况下,rtc-room 组件无法使用)​
2.68.0​
21301​
rtcRoom creation failed​
Android rtc-room 不支持​
2.68.0​

小程序全功能 Demo​

抖音开放平台提供了 RTC 小程序相关全功能示例项目。获取该项目后,你可以参照本文进行跑通,体验小程序提供的 RTC 相关全部小程序能力。你也可以通道阅读代码,以了解相关 API 的最佳实践。​
该示例项目由 rtc-room 组件tt.createRtcRoomContext 相关 API 组成。rtc-room 组件为多人音视频通话视频画面展示组件,tt.createRtcRoomContext 相关 API 为实现具体实时音视频能力 API。​

前提条件

    已申请开通小程序 RTC 组件能力权限​

获取示例项目​

你可点击工程示例获取相关代码项目。​
该工程有效期至2025/01/31​
示例项目的目录结构如下:​
- 项目 - pages - index - rtc-room // rtc 示例 - utils - constant.js // rtc AppId 和 Token 的配置 - app.js - app.json - app.ttss - project.config.json // 抖音小程序项目配置

配置示例项目​

    1.打开文件 constant.js,你需要在该文件的 RTC_APP_IDRTC_TOKEN 分别填入你的 AppId 和临时 Token。​

编译并运行示例项目​

若有疑问,通过飞书扫码进入话题群咨询​

代码示例​

调试时,需要修改 demo 中的 RTC_APP_IDRTC_TOKEN信息​
<view class="title">rtc 组件</view> <view class="rtc-container"> <view>{{ error }}</view> <rtc-room tt:for="{{userIdList}}" tt:key="item.userId" id="{{ item.userId }}" class="rtc-item {{ item.class }}" userId="{{ item.userId }}" device-position="front" bindtap="scaleCamera" binderror="handleError" mode="{{ item.mode }}"> </rtc-room> </view> <view class="title">房间信息</view> <view class="rtc-api-name">appId</view> <view class="room-message">{{ appId }}</view> <view class="rtc-api-name">token</view> <view class="room-message token">{{ token }}</view> <view class="rtc-api-name">roomId</view> <view class="room-message">{{ roomId }}</view> <view class="rtc-api-name">useId</view> <view class="room-message">{{ userId }}</view> <view class="title">rtc Api</view> <view class="rtc-api-name">api名称: joinRtcRoom</view> <button class="rtc-api" type="default" size="default" bindtap="joinRtcRoom">加入房间</button> <view class="rtc-api-name">api名称: exitRtcRoom</view> <button class="rtc-api" type="default" size="default" bindtap="exitRtcRoom">退出房间</button> <view class="rtc-api-name">api名称: switchCamera</view> <button class="rtc-api" type="default" size="default" bindtap="switchCamera">切换前后置摄像头</button> <view class="rtc-api-name">api名称: changeVideoCapture</view> <button class="rtc-api" type="default" size="default" bindtap="changeVideoCapture">切换开启/关闭摄像头采集</button> <view class="rtc-api-name">api名称: changeAudioCapture</view> <button class="rtc-api" type="default" size="default" bindtap="changeAudioCapture">切换开启/关闭麦克风采集</button> <view class="rtc-api-name">api名称: changeUserVisibility</view> <button class="rtc-api" type="default" size="default" bindtap="changeUserVisibility">切换用户是否对房间内其他用户可见</button> <view class="rtc-api-name">api名称: setVideoEncoderConfig</view> <button class="rtc-api" type="default" size="default" bindtap="setVideoEncoderConfig">设置视频发布的最大视频流参数</button> <view class="rtc-api-name">api名称: setAudioPlaybackDevice</view> <button class="rtc-api" type="default" size="default" bindtap="setAudioPlaybackDevice">切换音频播放设备</button> <view class="rtc-api-name">api名称: changeStreamPublishState</view> <button class="rtc-api" type="default" size="default" bindtap="changeStreamPublishState">切换开启/关闭发布流</button> <view class="rtc-api-name">api名称: changeStreamSubscribeState</view> <button class="rtc-api" type="default" size="default" bindtap="changeStreamSubscribeState">切换开启/关闭订阅流</button> <view class="rtc-api-name">api名称: changeScreenSubscribeState</view> <button class="rtc-api" type="default" size="default" bindtap="changeScreenSubscribeState">切换开启/关闭订阅屏幕流</button> <view class="rtc-api-name">api名称: updateToken</view> <button class="rtc-api" type="default" size="default" bindtap="updateToken">更新 token</button> <view class="rtc-api-name">api名称: offRtcVideoMembersChanged</view> <button class="rtc-api" type="default" size="default" bindtap="offRtcVideoMembersChanged">取消订阅视频成员变化</button> <view class="rtc-api-name">api名称: offRtcChatMembersChanged</view> <button class="rtc-api" type="default" size="default" bindtap="offRtcChatMembersChanged">取消订阅音频(在线、离线)</button> <view class="rtc-api-name">api名称: offRtcChatSpeakersChanged</view> <button class="rtc-api" type="default" size="default" bindtap="offRtcChatSpeakersChanged">取消订阅音频(开始、停止说话)</button> <view class="rtc-api-name">api名称: offRtcStateChanged</view> <button class="rtc-api" type="default" size="default" bindtap="offRtcStateChanged">取消订阅状态变化</button> <view class="rtc-api-name">api名称: offRtcChatInterrupted</view> <button class="rtc-api" type="default" size="default" bindtap="offRtcChatInterrupted">取消监听被动断开实时音视频通话事件</button> <view class="rtc-api-name">api名称: offRtcPublishScreenMembersChanged</view> <button class="rtc-api" type="default" size="default" bindtap="offRtcPublishScreenMembersChanged">取消监听实时共享屏幕用户改变事件</button> <view class="rtc-api-name">api名称: offRoomTokenWillExpire</view> <button class="rtc-api" type="default" size="default" bindtap="offRoomTokenWillExpire">取消监听房间token即将过期通知事件</button>
import { RTC_APP_ID } from "../../utils/constant"; Page({ data: { ctx: null, userIdList: [], videoState: 0, audioState: 0, streamPublishState: 0, streamSubscribeState: 0, screenSubscribeState: 0, visible: false, device: "speakerphone", appId: RTC_APP_ID, // 每个应用的唯一标识符,由 RTC 控制台随机生成的。不同的 AppId 生成的实例在 RTC 中进行音视频通话完全独立,无法互通。 token: "", roomId: "", userId: "", camera: "front", scaleUserId: "", error: "no error", }, onLoad(options) { const { userId, roomId, token } = options; this.setData({ userId, roomId, token: decodeURIComponent(token), }); this.data.ctx = tt.createRtcRoomContext({ appId: this.data.appId, }); // 注册事件监听回调 this.data.ctx.onRtcStateChanged(this.onRtcStateChanged); this.data.ctx.onRtcVideoMembersChanged(this.onRtcVideoMembersChanged); this.data.ctx.onRtcChatMembersChanged(this.onRtcChatMembersChanged); this.data.ctx.onRtcChatSpeakersChanged(this.onRtcChatSpeakersChanged); this.data.ctx.onRtcChatInterrupted(this.onRtcChatInterrupted); this.data.ctx.onRtcPublishScreenMembersChanged( this.onRtcPublishScreenMembersChanged ); this.data.ctx.onRoomTokenWillExpire(this.onRoomTokenWillExpire); }, handleError(e) { const { errNo, errMsg } = e.detail; this.setData({ error: `errNo = ${errNo}, errMsg = ${errMsg}`, }); }, scaleCamera(e) { const userIdList = this.data.userIdList; if (this.data.scaleUserId === e.currentTarget.id) { for (let i = 0, len = userIdList.length; i < len; i++) { if (userIdList[i].userId === this.data.scaleUserId) { this.data.userIdList[i].class = "rtc-default"; } } this.setData({ scaleUserId: "" }); } else { this.setData({ scaleUserId: e.currentTarget.id }); for (let i = 0, len = userIdList.length; i < len; i++) { if (userIdList[i].userId === this.data.scaleUserId) { this.data.userIdList[i].class = "rtc-select"; } else { this.data.userIdList[i].class = "rtc-default"; } } } this.setData({ userIdList: this.data.userIdList, }); }, onRtcVideoMembersChanged({ userIdList }) { console.log("onRtcVideoMembersChanged res ", userIdList); const resOpenId = []; for (let i = 0; i < userIdList.length; i += 1) { resOpenId.push({ class: "rtc-default", userId: userIdList[i], mode: this.data.userId === userIdList[i] ? "camera" : "video", }); } this.setData({ userIdList: resOpenId, }); }, onRtcChatMembersChanged(res) { console.log("onRtcChatMembersChanged ", res); }, onRtcStateChanged(res) { console.log("onRtcStateChanged ", res); }, onRtcChatSpeakersChanged(res) { console.log("onRtcChatSpeakersChanged ", res); }, onRtcChatInterrupted(res) { console.log("onRtcChatInterrupted ", res); }, onRtcPublishScreenMembersChanged(res) { console.log("onRtcPublishScreenMembersChanged ", res); }, onRoomTokenWillExpire() { console.log("onRoomTokenWillExpire"); }, offRtcVideoMembersChanged() { this.data.ctx.offRtcVideoMembersChanged(this.onRtcVideoMembersChanged); }, offRtcStateChanged() { this.data.ctx.offRtcStateChanged(this.onRtcStateChanged); }, offRtcChatMembersChanged() { this.data.ctx.offRtcChatMembersChanged(this.onRtcChatMembersChanged); }, offRtcChatSpeakersChanged() { this.data.ctx.offRtcChatSpeakersChanged(this.onRtcChatSpeakersChanged); }, offRtcChatInterrupted() { this.data.ctx.offRtcChatInterrupted(this.onRtcChatInterrupted); }, offRtcPublishScreenMembersChanged() { this.data.ctx.offRtcPublishScreenMembersChanged( this.onRtcPublishScreenMembersChanged ); }, offRoomTokenWillExpire() { this.data.ctx.offRoomTokenWillExpire(this.onRoomTokenWillExpire); }, joinRtcRoom() { this.data.ctx.joinRtcRoom({ roomId: this.data.roomId, token: this.data.token, userId: this.data.userId, success: (res) => { console.log("joinRtcRoom success ", res); }, fail(res) { console.log("joinRtcRoom fail ", res); }, complete(res) { console.log("joinRtcRoom complete ", res); }, }); }, switchCamera() { const camera = this.data.camera === "front" ? "back" : "front"; this.setData({ camera }); this.data.ctx.switchCamera({ camera, success(res) { console.log("switchCamera success ", res); }, fail(res) { console.log("switchCamera fail ", res); }, complete(res) { console.log("switchCamera complete ", res); }, }); }, exitRtcRoom() { this.data.ctx.exitRtcRoom({ roomId: this.data.roomId, success(res) { console.log("exitRtcRoom success ", res); }, fail(res) { console.log("exitRtcRoom fail ", res); }, complete(res) { console.log("exitRtcRoom complete ", res); }, }); }, changeVideoCapture() { const videoState = this.data.videoState === 0 ? 1 : 0; this.setData({ videoState }); this.data.ctx.changeVideoCapture({ state: videoState, success(res) { console.log("changeVideoCapture success ", res); }, fail(res) { console.log("changeVideoCapture fail ", res); }, complete(res) { console.log("changeVideoCapture complete ", res); }, }); }, changeAudioCapture() { const audioState = this.data.audioState === 0 ? 1 : 0; this.setData({ audioState }); this.data.ctx.changeAudioCapture({ state: audioState, success(res) { console.log("changeAudioCapture success ", res); }, fail(res) { console.log("changeAudioCapture fail ", res); }, complete(res) { console.log("changeAudioCapture complete ", res); }, }); }, changeUserVisibility() { const visible = this.data.visible ? false : true; this.setData({ visible }); this.data.ctx.changeUserVisibility({ visible, success(res) { console.log("changeUserVisibility success ", res); }, fail(res) { console.log("changeUserVisibility fail ", res); }, complete(res) { console.log("changeUserVisibility complete ", res); }, }); }, setVideoEncoderConfig() { this.data.ctx.setVideoEncoderConfig({ maxSolution: { width: 640, height: 360, frameRate: 24, }, success(res) { console.log("setVideoEncoderConfig success ", res); }, fail(res) { console.log("setVideoEncoderConfig fail ", res); }, complete(res) { console.log("setVideoEncoderConfig complete ", res); }, }); }, setAudioPlaybackDevice() { const device = this.data.device === "speakerphone" ? "earpiece" : "speakerphone"; this.setData({ device }); this.data.ctx.setAudioPlaybackDevice({ device, success(res) { console.log(" setAudioPlaybackDevice success ", res); }, fail(res) { console.log(" setAudioPlaybackDevice fail ", res); }, complete(res) { console.log(" setAudioPlaybackDevice complete ", res); }, }); }, changeStreamPublishState() { const streamPublishState = this.data.streamPublishState ? 0 : 1; this.setData({ streamPublishState }); this.data.ctx.changeStreamPublishState({ state: streamPublishState, streamType: "both", success(res) { console.log("changeStreamPublishState success ", res); }, fail(res) { console.log("changeStreamPublishState fail ", res); }, complete(res) { console.log("changeStreamPublishState complete ", res); }, }); }, changeStreamSubscribeState() { const streamSubscribeState = this.data.streamSubscribeState ? 0 : 1; this.setData({ streamSubscribeState }); this.data.ctx.changeStreamSubscribeState({ state: streamSubscribeState, userId: this.data.userId === "user1" ? "user2" : "user1", streamType: "both", success(res) { console.log("changeStreamSubscribeState success ", res); }, fail(res) { console.log("changeStreamSubscribeState fail ", res); }, complete(res) { console.log("changeStreamSubscribeState complete ", res); }, }); }, changeScreenSubscribeState() { const screenSubscribeState = this.data.screenSubscribeState ? 0 : 1; this.setData({ screenSubscribeState }); this.data.ctx.changeScreenSubscribeState({ state: screenSubscribeState, userId: this.data.userId === "user1" ? "user2" : "user1", streamType: "both", success(res) { console.log("changeScreenSubscribeState success ", res); }, fail(res) { console.log("changeScreenSubscribeState fail ", res); }, complete(res) { console.log("changeScreenSubscribeState complete ", res); }, }); }, updateToken() { this.data.ctx.updateToken({ token: "0015dc114b50538d600388f7b18PgDxNOADsJDmYTDL72EDADAwMgUAdXNlcjcGAAAAMMvvYQEAMMvvYQIAMMvvYQMAMMvvYQQAMMvvYQUAMMvvYSAAYUqG/tHPKk44g5iN6UQFcQugzPDX7ryKQ2Qiu0V/EmA=", success(res) { console.log("updateToken success ", res); }, fail(res) { console.log("updateToken fail ", res); }, complete(res) { console.log("updateToken complete ", res); }, }); }, });

Bug & Tip​

无​