本文将介绍如何快速实现一个简单的实时音视频通话。
相关概念解释:
更多相关概念请参考 术语说明。
目前支付宝小程序仅支持 1v1 音视频通话。
在实现基本的实时音视频功能之前,请确保:
已联系 ZEGO 技术支持,提供您的 支付宝 APPID,开通了相关权限,并获取到了接入 MRTC 的相应参数,以及完成了挂包(Android 设备需要下载挂包)后,才可以正常使用 RTC room 组件。
确保您的支付宝小程序符合以下类目,并开通实时音视频权限(获取到对应的小程序 AppID),详情请参考 申请开通。
请参考 基础推拉流 1v1。
用户通过 ZEGO Express SDK 进行视频通话的基本流程为:
用户 A、B 加入房间,用户 B 预览并将音视频流推送到 ZEGO 云服务(推流),用户 A 收到用户 B 推送音视频流的通知之后,在通知中播放用户 B 的音视频流(拉流)。
在初始化 SDK 前,需要在 支付宝平台 中进行如下配置:
服务器 IP 白名单:在 “开放平台控制台 > 对应应用概览页 > 开发设置 > 开发信息 > 服务器IP白名单”,点击 “设置/查看”,进入 IP 白名单管理页面,按照协议分类,将 ZEGO 的 LogUrl、以及用户业务需要用到的地址填到指定的“服务器 IP 白名单”中,详情请参考 控制台 - 项目信息。
输入 RTC room 组件 所必须的 MRTC 参数、以及身份验证签名 signature,支付宝服务器会直接和 ZEGO 的 RTMP 网关进行推拉流对接。
1. 创建界面
根据场景需要,为您的项目创建视频通话的用户界面。我们推荐您在项目中添加如下元素:
参考界面代码:
<view class="body">
<view class="containerBase">
<rtc-room
a:if="{{showRtcroom}}"
class="rtcContent"
id="{{rtcroomID}}"
roomId="{{rtcroom.roomId}}"
token="{{rtcroom.token}}"
userId="{{rtcroom.userId}}"
signature="{{rtcroom.signature}}"
autoplay="{{rtcroom.autoplay}}"
enable-camera="{{rtcroom.enableCamera}}"
mute="{{rtcroom.mute}}"
fps="{{rtcroom.fps}}"
resolution="{{rtcroom.resolution}}"
record="{{rtcroom.record}}"
min-bitrate="{{rtcroom.minBitrate}}"
max-bitrate="{{rtcroom.maxBitrate}}"
extraInfo="{{rtcroom.extraInfo}}"
onError="onError"
onRoomInfo="onRoomInfo"
onParticipantEnter="onParticipantEnter"
onParticipantLeave="onParticipantLeave"
onAudioPlayoutMode="onAudioPlayoutMode"
onReceiveRecordId="onReceiveRecordId"
onRtmpEvent="onRtmpEvent"
/>
</view>
<view class="index-container">
<view class='input-container'>
<input
value="{{roomID}}"
onInput="keyInput"
placeholder="请输入房间 ID"
placeholder-style='color: #b3b3b3; font-size: 14px;'
class="room-input"
/>
<text class="tip"></text>
</view>
<view class="button-container">
<button type="primary" onTap="openRoom" data-role="1" class="btn">
加入房间(推流)
</button>
<button type="primary" onTap="openRoom2" data-role="0" class="btn">
加入房间(不推流)
</button>
<button type="primary" onTap="publishStream" data-role="1" class="btn">
推流
</button>
<button type="primary" onTap="stopPushStream" data-role="1" class="btn">
停止推流
</button>
<button type="primary" onTap="stopPullStream" data-role="0" class="btn">
停止拉流
</button>
<button type="primary" onTap="enableCamera" data-role="0" class="btn">
{{ rtcroom.enableCamera ? "关闭摄像头" : "开启摄像头"}}
</button>
<button type="primary" onTap="enableMicrophone" data-role="0" class="btn">
{{ rtcroom.mute ? "开启麦克风" : "关闭麦克风"}}
</button>
<button type="primary" onTap="logout" class="logoutBtn">退出房间</button>
</view>
</view>
</view>
2. 创建引擎
创建 ZegoExpressEngine 引擎实例,将申请到的 AppID 传入参数 “appID”,将获取到的 Server 地址传入参数 “server”。
// 初始化实例
zg = new ZegoExpressEngine(appID, server);
如果需要注册回调,开发者可根据实际需要,实现 ZegoEvent 中的某些方法,创建引擎后可通过调用 on 接口设置回调。
zg.on('roomStateUpdate', (roomID, state, errorCode, extendedData) => {
if (state == 'DISCONNECTED') {
// 与房间断开了连接
// ...
}
if (state == 'CONNECTING') {
// 与房间尝试连接中
// ...
}
if (state == 'CONNECTED') {
// 与房间连接成功
// ...
}
})
1. 获取登录 Token
登录房间需要用于验证身份的 Token,获取方式请参考 用户权限控制。如需快速调试,建议使用控制台生成的临时 Token,生成临时 Token 的具体操作请参考 控制台 - 项目管理。
2. 登录房间
您可以调用 SDK 的 loginRoom 接口,传入房间 ID 参数 “roomID”、“token” 和用户参数 “user”,登录房间。如果房间不存在,调用该接口时会创建并登录此房间。
您可通过监听 roomStateUpdate 回调实时监控自己在本房间内的连接状态,具体请参考 4.1 常见通知回调 中的“4.1.1 我在房间内的连接状态变化通知”。
roomID 和 user 的参数由您本地生成,但是需要满足以下条件:
为避免错过任何通知,您需要在登录房间前先设置所有的监听回调(如房间状态、用户状态、流状态、推拉流状态等),具体请参考 4.1 常见通知回调 。
// 登录房间,成功则返回 true
const result = await zg.loginRoom(roomID, token, {
userID: "user1", // userID,需用户自己定义,保证全局唯一,建议设置为业务系统中的用户唯一标识
userName: "user1_name" // userName 用户名
}, {
userUpdate: true // 是否接收用户进出房间的回调,设置为 true 才能接收到房间内其他用户进出房间的回调
});
必须完成创建业务场景的 AXML 之后,才能调用 SDK 接口创建推流和拉流实例。
调用 startPublishingStream 获取推流地址,并将推流地址赋值给 <rtc-room> 组件 的 extraInfo 扩展参数的 rtmpPushUrl
属性。请注意,必须在创建组件时传入,直接通过 setData 更新无效。
“streamID” 由您本地生成,但是需要保证:
// 控制 <rtc-room> 组件的显示隐藏
handleShowRtcroom() {
const extraInfo = this.data.rtcroom.extraInfo
const isShow = Boolean(extraInfo.rtmpPullUrl || extraInfo.rtmpPushUrl);
this.setData({
showRtcroom: false,
}, () => {
this.data.rtcroomContext && this.data.rtcroomContext.stop();
const id = "rtcroom-" + Date.now()
this.setData({
rtcroomID: id,
showRtcroom: isShow,
rtcroomContext: my.createRtcRoomContext(id)
})
})
}
// 推流方登录房间成功后触发推流
/** 开始推流,返回推流地址 */
const pushStreamID = this.data.pushStreamID;
const {
url
} = await zg.startPublishingStream(pushStreamID, publishOption);
const rtcroom = this.data.rtcroom;
console.warn(`startPush 推流地址${url},推流类型:${publishOption && publishOption.sourceType}`);
rtcroom.extraInfo.rtmpPushUrl = url;
rtcroom.enableCamera = true;
this.setData({
rtcroom: rtcroom,
needRepublish: false
}, () => {
this.handleShowRtcroom()
});
进行视频通话时,我们需要拉取到其他用户的音视频。
通过调用startPlayingStream 获取拉流地址,并将拉流地址赋值给 <rtc-room> 组件 的 extraInfo 扩展参数的 rtmpPullUrl
属性。请注意,必须在创建组件时传入,直接通过 setData 更新无效。
您可通过监听 playerStateUpdate 回调知晓是否成功拉取音视频,具体请参考 4.1 常见通知回调 中的“4.1.5 用户拉取音视频流的状态通知”。
远端用户推送的 “streamID” 可以从 roomStreamUpdate 回调中获得,具体回调设置请参考 4.1 常见通知回调 中的“4.1.3 房间内流状态变更的通知”。
如果您的业务场景中需要拉空流,请联系 ZEGO 技术支持进行特殊配制。
zg.on("roomStreamUpdate", async (roomID, updateType, streamList) => {
console.warn("roomStreamUpdate", roomID, updateType, streamList);
const rtcroom = this.data.rtcroom;
//拉流列表,一次只拉一条
const pullList = this.data.pullList;
if (updateType === "ADD") {
pullList.push(...streamList);
} else {
streamList.forEach(i => {
if (this.data.pullStreamID === i.streamID) {
// 停止拉流
zg.stopPlayingStream(i.streamID);
rtcroom.extraInfo.rtmpPullUrl = ""
this.setData({
rtcroom: rtcroom
}, () => {
this.handleShowRtcroom();
})
}
const index = pullList.findIndex(item => item.streamID === i.streamID);
if (index !== -1) {
pullList.splice(index, 1);
}
})
}
if (!rtcroom.extraInfo.rtmpPullUrl && pullList.length) {
const pullStreamID = pullList[0].streamID;
try {
// 开始拉流
const {
url
} = await zg.startPlayingStream(this.data.pullList[0].streamID)
rtcroom.extraInfo.rtmpPullUrl = url
this.setData({
pullStreamID: pullStreamID,
rtcroom: rtcroom
}, () => {
this.handleShowRtcroom();
})
} catch (error) {
console.error(error)
}
}
this.setData({
pullList: pullList
})
});
如果用户在音视频通话的过程中,遇到相关错误,可查询 错误码。
roomStateUpdate:本地调用 loginRoom 加入房间时,您可通过监听该回调实时监控自己在该房间内的连接状态。
用户可以在回调中根据不同状态处理业务逻辑。
zg.on('roomStateUpdate', (roomID, state, errorCode, extendedData) => {
if (state == 'DISCONNECTED') {
// 与房间断开了连接
// ...
}
if (state == 'CONNECTING') {
// 与房间尝试连接中
// ...
}
if (state == 'CONNECTED') {
// 与房间连接成功
// ...
}
})
状态 | 含义 |
---|---|
DISCONNECTED |
未连接状态,在登录房间前/退出房间后进入该状态。如果登录房间的过程中出现稳态异常,比如 AppID 不正确,或者有相同用户名在其他地方登录导致本端被 KickOut,都会进入该状态。 |
CONNECTING |
正在请求连接状态,登录房间动作执行成功后会进入该状态。通常情况下,可通过该状态进行 UI 界面的展示。如果是因为网络质量不佳产生的中断,SDK 内部会进行重试,也会进入正在请求连接状态。 |
CONNECTED |
连接成功状态,成功登录房间后进入该状态。此时,用户可以正常收到房间内的用户和流信息增删变化的回调通知。 |
roomUserUpdate:同一房间内的其他用户进出房间时,您可通过此回调收到通知。登录房间后,当房间内有用户新增或删除时,SDK 会通过该回调通知。
只有调用 loginRoom 接口登录房间时传入 ZegoRoomConfig 配置,且 “userUpdate” 参数取值为 “true” 时,用户才能收到 roomUserUpdate 回调。
// 用户状态更新回调
zg.on('roomUserUpdate', (roomID, updateType, userList) => {
console.warn(
`roomUserUpdate: room ${roomID}, user ${updateType === 'ADD' ? 'added' : 'left'} `,
JSON.stringify(userList),
);
});
roomStreamUpdate:流状态更新回调。登录房间后,当房间内有用户新推送或删除音视频流时,SDK 会通过该回调通知。
// 流状态更新回调
zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => {
if (updateType == 'ADD') {
// 流新增,开始拉流
} else if (updateType == 'DELETE') {
// 流删除,停止拉流
}
});
支付宝小程序会在 <rtc-room> 组件 的 “onRtmpEvent” 属性绑定的方法中,通知出推流状态事件,开发者需要:
在 “onRtmpEvent” 绑定的回调函数中,调用 SDK 的 updatePlayerState 接口,将推流状态事件透传给 SDK。
在 SDK 的 publisherStateUpdate 回调中,处理推流的开始、失败状态。
// rtc-room 绑定的 onRtmpEvent 事件,更新推拉流状态
onRtmpEvent(e) {
console.warn("onRtmpEvent, e=" + JSON.stringify(e));
if ([3800, 3801].includes(e.detail.code)) {
// 推流
zg.updatePlayerState(this.data.pushStreamID, e);
}
},
// 推流后,服务器主动推过来的,流状态更新
// NO_PUBLISH:未推流状态,PUBLISH_REQUESTING:正在请求推流状态,PUBLISHING:正在推流状态
// state: "PUBLISHING" | "NO_PUBLISH" | "PUBLISH_REQUESTING";
zg.on("publisherStateUpdate", (result) => {
console.log("publishStateUpdate", result.state);
});
支付宝小程序会在 <rtc-room> 组件 的 “onRtmpEvent” 属性绑定的方法中,通知出拉流状态事件,开发者需要:
在 “onRtmpEvent” 绑定的回调函数中,调用 SDK 的 updatePlayerState 接口,将拉流状态事件透传给 SDK。
在 SDK 提供的 playerStateUpdate 回调中,处理拉流的开始、失败状态。
// rtc-room 绑定的onRtmpEvent事件,更新推拉流状态
onRtmpEvent(e) {
console.warn("onRtmpEvent, e=" + JSON.stringify(e));
if ([3800, 3801].includes(e.detail.code)) {
// 推流
zg.updatePlayerState(this.data.pushStreamID, e);
} else if ([3802, 3803].includes(e.detail.code)) {
// 拉流
zg.updatePlayerState(this.data.pullStreamID, e);
}
},
// 服务器主动推过来的流的播放状态
// 视频播放状态通知;state: "NO_PLAY" | "PLAY_REQUESTING" | "PLAYING";
zg.on("playerStateUpdate", (result) => {
console.log("playStateUpdate", result.state);
});
1. 停止推流
调用 stopPublishingStream 方法停止推流,并重新渲染 <rtc-room> 组件。
// 停止推流
stopPushStream() {
zg.stopPublishingStream(this.data.pushStreamID);
this.data.rtcroom.extraInfo.rtmpPushUrl = ""
this.setData({
rtcroom: this.data.rtcroom,
});
this.handleShowrtcRoom()
},
2. 停止拉流
调用 stopPlayingStream 方法停止拉流,并重新渲染 <rtc-room> 组件。
如果开发者通过 roomStreamUpdate 回调收到了音视频流 “减少” 的通知,请及时调用 stopPlayingStream 接口停止拉流,避免拉到空流、产生额外的成本;或者,开发者可以根据自己的业务需求,选择合适的时机,主动调用 stopPlayingStream 接口停止拉流。
// 停止拉流
zg.stopPlayingStream(this.data.pullStreamID);
this.data.rtcroom.extraInfo.rtmpPullUrl = ""
this.setData({
rtcroom: rtcroom
}, () => {
this.handleShowRtcroom();
})
调用 SDK 的 logoutRoom 接口退出房间。
zg.logoutRoom(roomID);
在真机中运行项目,运行成功后,可以看到本端视频画面。
为方便体验,ZEGO 提供了一个 Web 端调试示例 ,在该页面下,输入相同的 AppID、RoomID,输入不同的 UserID、以及对应的 Token,即可加入同一房间与真机设备互通。当成功开始音视频通话时,可以听到远端的音频,看到远端的视频画面。
整个推拉流过程的 API 调用时序可参考下图:
联系我们
文档反馈