# 实现视频通话
本文档为您展示通过 SDK 实现视频通过(访客)的相关步骤,帮助您在远程银行和视频客服的场景下实现智能排队、屏幕共享、全景录像、访客管理的相关能力。
# 前提条件
请确认您已完成以下操作:
- 已获取 App Key。
AppKey 作为同个环境的分域依据,同一个域的终端才能实现互通,AppKey 由 Juphoon 视频平台提供。
- 集成 SDK。
# 1. 快速跑通 Sample
1、在Juphoon RTC SDK 文档中心,选择视频客服-访客选择项,在SDK下载页面下载体验 Sample 示例项目。
访问下载地址 (opens new window),示例如下:
2、下载完成后,解压出JCCSample,双击 index.html 即可运行。
# 2. 实现视频通话
# 初始化
在使用业务接口前,需对 Juphoon RTC SDK 进行初始化操作。
JRTCClient (opens new window) | 基础模块 | 负责视频平台的登录登出,只有登录到视频平台才可以使用视频相关的业务,AppKey 作为同个环境的分域依据,同一个域的终端才能实现互通。 |
---|---|---|
JRTCMediaDevice (opens new window) | 媒体模块 | 负责本地的媒体设备操作,视频画面渲染等功能。 |
JRTCGuest (opens new window) | 访客模块 | 负责访客的视频业务的处理 |
AppKey 作为同个环境的分域依据,同一个域的终端才能实现互通,AppKey 由 Juphoon 视频平台提供。
在使用业务接口前,需对 Juphoon SDK 进行初始化操作。
初始化 JRTCClient (opens new window) 参数详见 JRTCClientInitParam (opens new window) ;
初始化 JRTCGuest (opens new window) 参数详见 JRTCCallCenterInitParam (opens new window) ;
示例代码:
const clientInitParam = new JRTCClientInitParam();
clientInitParam.appName = "JCCSample-H5";
clientInitParam.server = "server";
clientInitParam.appKey = "appKey";
clientState = ClientState.IDLE;
initClientCallback();
client = JRTCClient.create(clientCallback, clientInitParam);
initMediaDeviceCallback();
mediaDevice = JRTCMediaDevice.create(mediaDeviceCallback, {});
initGuestCallback();
let initParam = new JRTCCallCenterInitParam();
initParam.roomServer = "roomServer";
initParam.protocol = 'WEBRTC';
guest = JRTCGuest.create(client, mediaDevice, initParam, guestCallback);
/**
* 增加JRTCClientCallback监听回调
*/
initClientCallback() {
clientCallback = {
onLogin: (result, reason) => {
},
onLogout: (reason) => {
},
onClientStateChanged: (state, oldState) => {
},
onSDKEvent: (event) => {
}
}
},
/**
* 增加JRTCMediaDeviceCallback监听回调
*/
initMediaDeviceCallback() {
mediaDeviceCallback = {
onCameraUpdate: () => {
},
onVolumeChanged: (volume, userId) => {
}
}
},
/**
* 增加 JRTCGuestCallback 监听回调
*/
initGuestCallback() {
guestCallback = {
onGetAllGroups: (result, groups) => {
},
onCallStateChanged: (type, incomingType, inviter, reason) => {
},
onCallQueueCount: (count, time) => {
},
onCallPropertyChanged: (propChangeParam) => {
},
onMemberJoin: (participant) => {
},
onMemberLeave: (participant) => {
},
onMemberUpdate: (participant, changeParam) => {
},
onHoldStateChanged: (hold) => {
},
onCallTypeChanged: (callType) => {
},
onMessageReceived: (content, contentType, messageType, fromUserId) => {
},
onSignRequest: (fromUserId, extraInfo)=> {
},
onOnewayVideoChanged: (turnOn) => {
},
onDeliveryAbort: (isShutDown, deliveryUri, reason) => {
},
onNotifyMessageReceived: (notifyMessage, fromUserId) => {
},
onUrgentResultResponse: (agree) => {
}
}
},
# 登录
SDK 初始化之后,即可进行登录的集成,登录接口调用流程如下所示:
登录到 Juphoon 视频平台主要调用的是 JRTCClient (opens new window) 的登录接口 login (opens new window)
/**
* 登录 Juphoon RTC 平台,只有登录成功后才能进行平台上的各种业务
*
* 登录结果通过 {@link JRTCClientCallback.onLogin onLogin} 回调通知
*
* @param userId 用户ID
* @param password 密码,不能为空
* @param clientLoginParam 登录参数,一般不需要设置,不设置则按默认值
* @returns 接口调用结果
* - true: 接口调用成功
* - false: 接口调用异常
* @note 目前只支持免鉴权模式,服务器不校验账号密码,免鉴权模式下当账号不存在时会自动去创建该账号
* @note 用户名为英文数字和'+' '-' '_' '.',长度不要超过64字符,'-' '_' '.'不能作为第一个字符
*/
public login(userId: string, password: string, clientLoginParam?: JRTCClientLoginParam): boolean;
JRTCClientLoginParam (opens new window) 参数介绍
token (opens new window) | token |
---|---|
tokenType (opens new window) | token校验类型 |
token认证服务,主要用于登录时 token 验证,由第三方服务获取 token,将 token 下发给集成的终端,由 SDK 发起登录时带上 token ,进行认证。详见 token 流程介绍 (opens new window)。
允许用户登录时,带入 token 。如果未使用,可以不带。
登录的结果将会通过 JRTCClientCallback (opens new window) 中的 onLogin (opens new window) 接口上报。
/**
* 登录结果回调
*
* @param result true 表示登录成功,false 表示登录失败
* @param reason 登录失败原因,当 result 为 false 时该值有效
*/
onLogin(result: boolean, reason: ReasonCode): void;
示例代码:
// 创建登录配置参数
let loginParam = new JRTCClientLoginParam();
loginParam.token = "token";
loginParam.tokenType = "tokenType";
client.setServer("server");
client.setAppKey("appKey");
// 登录
client.login("userId", "123456", loginParam);
/**
* 调用登录接口返回回调消息
*/
onLogin: (result, reason) => {
if (result) {
//登录成功
} else {
//登录失败
}
},
# 获取业务号列表
在发起呼叫前,可以先调用 queryAllGroups (opens new window) 接口获取业务号、业务描述等详细信息;业务号一般由业务管理人员在业务管理平台上配置业务,然后将业务号给开发人员,或者也可以通过该接口查询到所有的业务号列表进行业务。
/**
* 获取当前业务号列表
*
* @returns 接口调用结果
* - true: 接口调用成功,查询结果通过 {@link JRTCGuestCallback.onGetAllGroups onGetAllGroups} 回调上报
* - false: 接口调用异常
*/
public queryAllGroups(): boolean {}
获取结果在 JRTCGuestCallback (opens new window) 中的 onGetAllGroups (opens new window) 接口上报
/**
* 获取业务号列表结果回调
*
* 访客调用 {@link JRTCGuest.queryAllGroups queryAllGroups} 接口获取业务号列表,会收到此回调。
* @param groups 座席业务实体对象列表,获取失败时为 undefined
* @param result 获取结果,true 表示获取成功,false 表示获取失败
*/
onGetAllGroups(result: boolean, groups: Array<JRTCCallCenterGroupItem>): void;
示例代码:
//获取业务号列表
guest.queryAllGroups();
/**
* 获取业务号列表回调消息
*/
onGetAllGroups: (result, groups)=> {
if(result) {
//获取成功
} else {
//获取失败
}
},
# 发起呼叫
在登录成功之后,访客就可以通过发起呼叫的接口来呼叫自己要做的业务;也可以发起呼叫的同时,设置呼叫参数;JRTCCallCenterCallParam (opens new window) 包含了很多呼叫参数,比如随路参数、SVC等。
访客有两个呼叫方法,分别为 call (opens new window) 和 oneToOneCall (opens new window) 如下:
/**
* 呼叫指定业务
*
* @param number 业务号,如10087,一般由业务管理人员在业务管理平台上配置业务,然后将业务号给开发人员
* @param param 呼叫参数设置,可以设置通话分辨率、全局宽高比等参数,此参数不传则使用默认配置
* @returns 接口调用结果
* - true: 接口调用成功,通话状态会通过 {@link JRTCGuestCallback.onCallStateChanged onCallStateChanged} 回调上报
* - false: 接口调用异常
*/
public call(number: string, param?: JRTCCallCenterCallParam): boolean;
/**
* 呼叫指定座席
*
* @param userId 座席 id,如agent1,一般由业务管理人员在业务管理平台上配置座席id,然后将座席id给开发人员
* @param param 呼叫参数设置,可以设置通话分辨率、全局宽高比等参数,此参数不传则使用默认配置
* @returns 接口调用结果
* - true: 接口调用成功,通话状态会通过 {@link JRTCGuestCallback.onCallStateChanged onCallStateChanged} 回调上报
* - false: 接口调用异常
*/
public oneToOneCall(userId:string, param?: JRTCCallCenterCallParam): boolean;
示例代码:
// 创建呼叫配置参数
let callParam = new JRTCCallCenterCallParam();
//呼叫随路参数
callParam.extraInfo = "extraInfo";
// 呼叫指定业务
guest.call("10086",callParam);
// 呼叫指定座席
guest.oneToOneCall("agent1",callParam);
# 通话状态改变通知
当访客发起呼叫、通话建立或者通话结束,都会通过 JRTCGuestCallback (opens new window) 里的 onCallStateChanged (opens new window) 接口进行上报。
/**
* 通话状态改变回调
*
* @param type 访客通话状态改变类型,即以下情况会收到此回调:
* - {@link GuestCallStateChangeType.CALLING CALLING} 访客呼叫成功
* - {@link GuestCallStateChangeType.WAITING WAITING} 访客呼叫成功后等待座席接听
* - {@link GuestCallStateChangeType.INCOMING INCOMING} 作为第三方访客收到通话邀请
* - {@link GuestCallStateChangeType.TALKING TALKING} 通话接通(第三方访客)或被接通(主访客)
* - {@link GuestCallStateChangeType.TERMED TERMED} 通话挂断或被挂断
* @param incomingType 来电类型,当 type == {@link GuestCallStateChangeType.INCOMING INCOMING} 时有效
* @param inviter 邀请成员对象,当 type == {@link GuestCallStateChangeType.INCOMING INCOMING} 时有效
* @param reason 挂断原因,只在 type 为 {@link GuestCallStateChangeType.TERMED TERMED} 时需要关注
*/
onCallStateChanged(type: GuestCallStateChangeType, incomingType: CallIncomingType, inviter: JRTCInviter | undefined, reason: CallTermReason): void;
通话状态详见:GuestCallStateChangeType (opens new window)
通话结束原因详见:CallTermReason (opens new window)
示例代码:
onCallStateChanged: (type, incomingType, inviter, reason)=> {
switch (type) {
case GuestCallStateChangeType.CALLING:
//呼叫成功
break;
case GuestCallStateChangeType.TALKING:
//通话建立
break;
case GuestCallStateChangeType.TERMED:
//通话挂断
break;
}
},
# 创建本地视频画面
初始化成功后,可以创建本地的视频画面,创建本地视频画面的时机没有具体要求,在通话前通话中皆可。
- 通过调用 JRTCMediaDevice (opens new window) 里的 startCameraVideo (opens new window) 方法获取本地的视频对象。
- 通过 startCameraVideo (opens new window) 方法里传入渲染的画布的句柄进行视频画面渲染。
/**
* 开始本端身视频渲染
*
* 获取本端视频预览对象 JRTCMediaDeviceVideoCanvas,通过此对象能获得视图用于UI显示
* @param renderType 视频渲染模式
* @param videoBox 需要包含视频视图的组件
* @returns Promise
*/
public async startCameraVideo(renderType: RenderType, videoBox: HTMLElement): Promise<JRTCMediaDeviceVideoCanvas>;
RenderType (opens new window) 决定了视频的渲染模式:
- RENDER_FULL_SCREEN (opens new window) 为填充模式:即将画面内容居中等比缩放以充满整个显示区域,超出显示区域的部分将会被裁剪掉,此模式下画面可能不完整;
- RENDER_FULL_CONTENT (opens new window) 为适应模式:即按画面长边进行缩放以适应显示区域,短边部分会被填充为黑色,此模式下图像完整但可能留有黑边。
示例代码:
let viewId = 'local-video';
// html element
let box = document.querySelector(`#${viewId}`); // 查找viewId的element
this.mediaDevice.startCameraVideo(RenderType.RENDER_FULL_SCREEN, box).then((canvas) => {
this.localCanvas = canvas;
resolve();
});
# 创建远端视频画面
当通话建立后,除了本地的视频画面,还有通话成员的远端视频画面,如果通话成员有视频流的上传,访客端可以获取到座席的视频流并进行渲染。
访客可在收到以下回调时进行界面处理:
- 收到 onCallStateChanged (opens new window) 回调且通话状态改为 TALKING (opens new window) 时,表示座席加入了通话。
- 收到 onMemberJoin (opens new window) 回调时,表示有其他成员加入通话。
- 收到 onMemberUpdate (opens new window) 回调时,表示通话中用户的通话状态变化。例如:可通过 JRTCRoomParticipantChangeParam (opens new window) 的 video 属性判断用户视频流状态是否发送改变;当 JRTCRoomParticipant.video (opens new window) 的值为 true ,表示远端用户已上传视频流,本端通过订阅远端视频流,获取远端视图渲染对象。
远端视图渲染调用 startVideo (opens new window) 接口渲染远端视频对象画面。
/**
* 开始远端的视频渲染
*
* @param streamId 远端视频拉流地址
* @param renderType 视频渲染模式
* @param mediaStream 媒体流对象,可以通过订阅远端视频流获取
* @param videoBox 需要包含视频视图的组件
* @returns
* - JCMediaDeviceVideoCanvas 对象: 开始远端视频渲染成功
* - undefined: 开始远端视频渲染失败
*/
public startVideo(streamId: string, renderType: RenderType, mediaStream: MediaStream, videoBox: HTMLElement): JRTCMediaDeviceVideoCanvas | undefined;
示例代码:
this.guest.requestVideo(participant, {width: 360, height: 640}).then((result) => {
let viewId = 'remote-video';
if (result) {
let box = document.querySelector(`#${viewId}`); // 查找viewId的element
this.remoteCanvas = this.mediaDevice.startVideo(participant.streamId, RenderType.RENDER_FULL_CONTENT, result, box);
}
});
# 结束通话
访客可以在呼叫等待或者通话中调用 term (opens new window) 接口主动取消或者结束通话。
/**
* 结束通话
*
* @note
* - 主访客调用此接口会结束通话,通话中所有成员都会离开,此通通话销毁,所有成员会收到 {@link JRTCGuestCallback.onCallStateChanged onCallStateChanged} 通话结束回调。
* - 第三方访客调用此接口仅自身离开通话,通话中其他成员会收到该成员离开的回调 {@link JRTCGuestCallback.onMemberLeave onMemberLeave} 回调,通话继续进行。
* @returns 接口调用结果
* - true: 接口调用成功,会收到 {@link JRTCGuestCallback.onCallStateChanged onCallStateChanged} 回调
* - false: 接口调用异常
*/
public term():boolean;
主动和被动的挂断事件与来电、接通一样,都通过 onCallStateChanged (opens new window) 接口上报,其中 CallTermReason (opens new window) 可以用来判断挂断原因,如:主动挂断、对端挂断等,详细可参考 API 文档。
示例代码:
// 访客主动结束通话
guest.term();
// 通话状态改变回调
onCallStateChanged: (type, incomingType, inviter, reason) => {
if (type == GuestCallStateChangeType.GUEST_CHANGE_TYPE_TERMED) {
// 通话结束,结束原因查看 reason
}
}
# 销毁本地和远端画面
当不再需要查看视频画面,包括通话其他成员离开,或者通话结束,需要调用 JRTCMediaDevice (opens new window) 中的 stopVideo (opens new window) 方法来释放渲染的资源;该方法需传入要释放的 JRTCMediaDeviceVideoCanvas (opens new window) 对象;必须进行这步操作,不然会造成渲染内存不释放。
/**
* 停止视频渲染
*
* @param canvas JRTCMediaDeviceVideoCanvas 对象,由 {@link startVideo} 或 {@link startCameraVideo} 接口返回
*/
public stopVideo(canvas: JRTCMediaDeviceVideoCanvas): void;
示例代码:
// 通话结束
onCallStateChanged: (type, incomingType, inviter, reason) => {
if (type == GuestCallStateChangeType.GUEST_CHANGE_TYPE_TERMED) {
mediaDevice.stopVideo(localCanvas);
mediaDevice.stopVideo(remoteCanvas);
}
}
// 成员离开
onMemberLeave: (participant) => {
// 停止该成员画面渲染
mediaDevice.stopVideo(remoteCanvas);
}
# 登出
访客结束通话后,可以做登出操作;登出接口调用流程如下所示:
访客可以登出视频平台,与平台断开一切连接。
/**
* 登出 Juphoon RTC 平台,登出后不能进行平台上的各种业务
*
* 登出结果通过 {@link JRTCClientCallback.onLogout onLogout} 回调通知
* @returns 接口调用结果
* - true: 接口调用成功
* - false: 接口调用异常
*/
public logout(): boolean {}
登出结果通过 JRTCClientCallback (opens new window) 中的 onLogout (opens new window) 接口上报:
/**
* 登出回调
*
* @param reason 登出原因
*/
onLogout(reason: ReasonCode): void;
示例代码:
// 调用登出接口
client.logout();
// 监听登出结果回调
onLogout: (reason) =>{
// 登出完成
}
# 登录状态改变通知
登录状态通过 JRTCClientCallback (opens new window) 中的 onClientStateChanged (opens new window) 接口上报。
/**
* 登录状态变化通知
*
* @param state 当前状态值
* @param oldState 之前状态值
*/
onClientStateChanged(state: ClientState, oldState: ClientState): void;
登录状态详见 ClientState (opens new window)
示例代码:
// 登录状态改变通知
onClientStateChanged: (state, oldState) => {
// state 当前状态
// oldState 之前状态
}
# 销毁SDK
每个模块都有对应的销毁接口。如不需再使用 SDK 的相关功能,可以强制释放 SDK 的资源。
注:该方法为同步调用,调用此方法后,你将无法再使用该模块的其它方法和回调。 我们不建议在 JRTCSDK 的所有回调方法中调用此方法销毁对象,否则可能出现崩溃现象。
JRTCClient.destroy();
JRTCMediaDevice.destroy();
JRTCGuest.destroy();