一对一通话
本页介绍了一对一呼叫的主要功能,包括如何在应用中拨打、接听、处理和结束呼叫。
关键类介绍
- RCCallPlusClient:
RCCallPlusClient单例对象是 CallPlus for Android SDK 的核心类,用于管理客户端呼叫行为,例如发起、接听、挂断通话,操作音视频设备,管理通话记录等。 - RCCallPlusSession:
RCCallPlusSession对象代表一则通话的所有信息,提供getCallId、getCallType、getMediaType、getRemoteUserList等获取通话属性的方法。 - RCCallPlusCallRecord:
RCCallPlusCallRecord对象代表一则通话记录,其中包含了与RCCallPlusSession类似的通话信息,还提供了通话开始与结束的时间戳、通话时长、通话结束原因等信息。 - IRCCallPlusEventListener:监听器
IRCCallPlusEventListener提供了来电事件(onReceivedCall)、通话建立成功(onCallConnected)、收到通话记录(onReceivedCallRecord)等事件相关回调。 - IRCCallPlusResultListener:监听器
IRCCallPlusResultListener提供了调用 CallPlus API 后的异步执行结果回调。
设置监听器
CallPlus for Android SDK 提供了两个监听器:
- IRCCallPlusEventListener 监听器用于接收来自远端用户或服务端的事件。
- IRCCallPlusResultListener 监听器用于处理与呼叫相关 API 的异步调用结果。
请在应用程序初始化或呼叫模块初始化时设置监听器:
-
调用
RCCallPlusClient.getInstance().setCallPlusEventListener方法设置IRCCallPlusEventListener监听器。如需反注册,调用setCallPlusEventListener(null)方法,设置为null即可。JavaRCCallPlusClient.getInstance().setCallPlusEventListener(new IRCCallPlusEventListener() {
/**
* 本端用户通过该回调接收到通话呼叫
*
* @param callSession 通话实体信息
*/
@Override
public void onReceivedCall(RCCallPlusSession callSession, String extra) {
RCCallPlusSession currentCallSession = RCCallPlusClient.getInstance().getCurrentCallSession();
if (currentCallSession != null && !TextUtils.equals(callSession.getCallId(), currentCallSession.getCallId())) {
// 可用该判断识别:已有通话进行中,又有第二通通话呼入
// 第二通通话可直接调用 RCCallPlusClient.getInstance().accept 接听,SDK 内部会将第一通通话挂断
}
// SDK 的回调均在子线程执行;showDialog() 涉及 UI 操作,需要切换到主线程执行
runOnUiThread(new Runnable() {
@Override
public void run() {
// 打开摄像头采集;请提前完成摄像头、麦克风权限的动态申请
RCCallPlusClient.getInstance().startCamera();
RCCallPlusClient.getInstance().enableMicrophone(true);
setLocalVideoView(); // 复用发起通话逻辑中的"设置本地视频渲染视图"方法
showDialog(CallPlusActivity.this, "收到通话,是否接听?", "接听", new OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
acceptCall(callSession);
}
}, "挂断", new OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
RCCallPlusClient.getInstance().hangup();
}
});
}
});
}
@Override
public void onCallEnded(RCCallPlusSession session, RCCallPlusReason reason) {
IRCCallPlusEventListener.super.onCallEnded(session, reason);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(CallPlusActivity.this,"通话结束,callId: "+session.getCallId() +" 通话结束原因:"+ reason.getValue(), Toast.LENGTH_SHORT).show();
}
});
}
/**
* 远端用户状态改变监听
*
* @param callId 通话 ID
* @param userId 用户 ID
* @param status 该用户当前状态
* @param reason 该用户当前状态原因
*/
@Override
public void onRemoteUserStateChanged(String callId, String userId, RCCallPlusUserSessionStatus status, RCCallPlusReason reason) {
IRCCallPlusEventListener.super.onRemoteUserStateChanged(callId, userId, status, reason);
runOnUiThread(new Runnable() {
@Override
public void run() {
StringBuilder stringBuilder = new StringBuilder("通话 ");
stringBuilder.append(callId).append(" 中的远端用户 ").append(userId).append(" 当前状态为 ");
switch (status) {
case IDLE:
stringBuilder.append("空闲");
break;
case CALLING:
stringBuilder.append("呼叫中");
break;
case INVITED:
stringBuilder.append("被邀请中");
break;
case RINGING:
stringBuilder.append("响铃中");
break;
case BUSY_LINE_RINGING:
stringBuilder.append("忙线(响铃中)");
break;
case BUSY_LINE_WAIT:
stringBuilder.append("忙线(通话中)");
break;
case CONNECTING:
stringBuilder.append("已接听,连接中");
break;
case ON_CALL:
stringBuilder.append("通话中");
break;
case ENDED:
stringBuilder.append("通话已结束");
break;
case NO_ANSWER:
stringBuilder.append("未应答");
break;
case MISSED:
stringBuilder.append("未接听");
break;
case CANCELED:
stringBuilder.append("已取消");
break;
case DECLINED:
stringBuilder.append("已拒绝");
break;
case ERROR:
stringBuilder.append("错误");
break;
}
Toast.makeText(CallPlusActivity.this, stringBuilder.toString(), Toast.LENGTH_SHORT).show();
}
});
}
}); -
调用
RCCallPlusClient.getInstance().setCallPlusResultListener方法设置IRCCallPlusResultListener监听器。如需反注册监听器,调用setCallPlusResultListener(null)方法,设置为 null 即可。JavaRCCallPlusClient.getInstance().setCallPlusResultListener(new IRCCallPlusResultListener() {
/**
* 发起通话方法结果回调
*
* @param code 方法请求结果
* @param callId 通话Id
* @param busyUserList 呼叫成功后,返回被邀请人列表中的忙线用户列表
*/
@Override
public void onStartCall(RCCallPlusCode code, String callId, List<RCCallPlusUser> busyUserList) {
IRCCallPlusResultListener.super.onStartCall(code, callId, busyUserList);
}
/**
* 接听通话结果回调
*
* @param code 方法请求结果
* @param callId 通话Id
*/
@Override
public void onAccept(RCCallPlusCode code, String callId) {
IRCCallPlusResultListener.super.onAccept(code, callId);
}
/**
* 挂断指定通话结果回调
*
* @param code 方法请求结果
* @param callId 通话Id
*/
@Override
public void onHangup(RCCallPlusCode code, String callId) {
IRCCallPlusResultListener.super.onHangup(code, callId);
}
});
连接融云服务器
要使用 CallPlus SDK 的通话能力,必须先通过 RongCoreClient 的 connect 方法连接融云服务器,传入用户身份令牌(Token),向融云服务器验证用户身份。连接成功后,使用 RCCallPlusClient.getInstance().init() 方法初始化和配置 CallPlus SDK。
注意
必须在连接成功之后调用
RCCallPlusClient.getInstance().init方法初始化 CallPlus SDK。反初始化使用unInit()方法。
String token = "用户Token";
RongCoreClient.connect(token, new IRongCoreCallback.ConnectCallback() {
/**
* 成功回调
* @param userId 当前用户 ID
*/
@Override
public void onSuccess(String userId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//todo 尽管在主线程初始化 RCCallPlusClient 不是必需的,但考虑到代码示例后续对 RCCallPlusClient 的调用都在主线程进行,所以目前选择在主线程进行初始化。
//todo 请确保在同一个线程进行 RCCallPlusClient 的初始化、反初始化和使用,以确保操作的一致性。
RCCallPlusConfig config = RCCallPlusConfig.Builder.create().build();
/**
* 初始化并设置通话全局配置,重复调用该方法时SDK内部会重新初始化
* @param config 设置通话全局配置
* @return 方法调用后同步返回结果,可以在这里得到初始化是否成功
*/
RCCallPlusResultCode resultCode = RCCallPlusClient.getInstance().init(config);
}
});
}
/**
* 错误回调
* @param errorCode 错误码
*/
@Override
public void onError(IRongCoreEnum.ConnectionErrorCode errorCode) {
}
/**
* 数据库回调.
* @param code 数据库打开状态. DATABASE_OPEN_SUCCESS 数据库打开成功; DATABASE_OPEN_ERROR 数据库打开失败
*/
@Override
public void onDatabaseOpened(DatabaseOpenStatus code) {
}
});
处理本地与远端视频视图
在主叫方发起通话前,被叫方接听通话时需要使用 setVideoView(2.34.0版本起请使用setVideoView2) 方法设置视频视图:
- RCCallPlusLocalVideoView 或 RCCallPlusLocalVideoTextureView:本地视频视图
- RCCallPlusRemoteVideoView 或 RCCallPlusRemoteVideoTextureView:远端视频视图
删除已设置的用户视频视图请调用 removeVideoView 方法。若远端用户没有设置视频渲染视图,则不会产生该用户的视频流的下行流量。
本地视频视图
您可以在应用程序的实现一个 setLocalVideoView 方法,在该方法调用 CallPlus SDK 的 setVideoView(2.34.0版本起请使用setVideoView2)方法,传入本地视频视图,设置本地摄像头渲染视图。
/**
* 设置本地视频渲染视图
*/
private void setLocalVideoView() {
// 创建本地视图对象
RCCallPlusLocalVideoView localVideoView = new RCCallPlusLocalVideoView(context);
//或使用 TextureView 渲染本地视频
//IRCCallPlusLocalVideoView localVideoView = new RCCallPlusLocalVideoTextureView(context);
// FIT:视频帧通过保持宽高比(可能显示黑边)来缩放以适应视图大小
localVideoView.setRenderMode(RCCallPlusRenderMode.FIT);
// 设置本地视图给 SDK,从2.34.0版本开始,使用 setVideoView2() 方法代替 setVideoView() 方法
RCCallPlusClient.getInstance().setVideoView2(localVideoView);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER_HORIZONTAL; // 在父布局中横向居中显示
// 将本地视图添加到 XML 中显示
// 示例代码中 mLocalVideoViewFrameLayout 为 android.widget.FrameLayout 对象
mLocalVideoViewFrameLayout.removeAllViews();
mLocalVideoViewFrameLayout.addView(localVideoView, params);
}
远端视频视图
您可以在应用程序的实现一个 setRemoteUserVideoView 方法,在该方法中调用 CallPlus SDK 的 setVideoView (2.34.0版本起请使用setVideoView2)方法,传入远端视频视图列表,设置远端用户视频流的渲染视图。
注意
如果远程视图需要置于最顶层显示,为解决其被其他布局遮挡的问题,可以使用
setZOrderOnTop和setZOrderMediaOverlay方法进行设置。
/**
* 设置远端用户视频渲染视图
*/
private void setRemoteUserVideoView(List<RCCallPlusUser> remoteUserList) {
List<IRCCallPlusRemoteVideoView> remoteVideoViewList = new ArrayList<>();
for (RCCallPlusUser callPlusUser : remoteUserList) {
IRCCallPlusRemoteVideoView remoteVideoView = new RCCallPlusRemoteVideoView(callPlusUser.getUserId(), context, isTiny);
//2.34.0版本开始支持使用TextureView渲染视频
//IRCCallPlusRemoteVideoView remoteVideoView = new RCCallPlusRemoteVideoTextureView(context, callPlusUser.getUserId(), isTiny);
// 视频帧通过保持宽高比(可能显示黑边)来缩放以适应视图大小
remoteVideoView.setRenderMode(RCCallPlusRenderMode.FIT);
remoteVideoViewList.add(remoteVideoView);
// 远端视图置于最顶层,为防止被底部控件(视图)遮挡,进行如下设置:
remoteVideoView.setZOrderOnTop(true);
remoteVideoView.setZOrderMediaOverlay(true);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
// 将每个远端视图(remoteVideoView)添加到 XML 中显示;多人时需添加多个控件显示,本示例仅展示一个远端用户
mRemoteVideoViewFrameLayout.removeAllViews();
mRemoteVideoViewFrameLayout.addView(remoteVideoView, params);
}
/**
* 设置远端用户视频流渲染视图给 SDK
* 若未为远端用户设置视频渲染视图,则不会产生该用户的视频流的下行流量
*/
//RCCallPlusClient.getInstance().setVideoView(remoteVideoViewList);
//从2.34.0版本开始,调用setVideoView2()方法设置视图
RCCallPlusClient.getInstance().setVideoView2(remoteVideoViewList);
}
发起呼叫
注意
从 CallPlus 2.0 开始,新增 startCall 重载方法,支持在发起呼叫时通过配置推送属性自定义远程推送标题等属性。支持携带自定义数据。
CallPlus 定义了一对一通话类型(RCCallPlusType.PRIVATE)。您可以使用 startCall 方法来发起一对一通话。
List<String> userIds = new ArrayList<>();
userIds.add(remoteUserId);
RCCallPlusPushConfig pushConfig = RCCallPlusPushConfig.Builder
.create()
.build();
RCCallPlusClient.getInstance().startCall(userIds, RCCallPlusType.PRIVATE, RCCallPlusMediaType.VIDEO, pushConfig, "extra");
该方法调用后,SDK 内部会以异步方式执行。在主叫与被叫端触发以下回调:
- 本地主叫用户通过 IRCCallPlusResultListener 的 onStartCall 回调来获取方法的执行结果。
- 远端被叫用户通过 IRCCallPlusEventListener 的 onReceivedCall 回调获取来电通知。
本地主叫用户将通过 IRCCallPlusEventListener 的 onRemoteUserStateChanged 回调获取到被叫用户状态变更,其中 RCCallPlusUserSessionStatus 枚举为 CONNECTING 时表示用户已经调用 accept 方法接听,通话正在连接中。
通话建立成功后,主叫端已经设置的远端视频视图会自动渲染远端被叫用户的视频。
注意
当发生网络断开或 IM 连接断开等情况导致请求失败时,startCall 方法会根据 SDK 内部的策略进行重试。如果重试一直失败,最长等待时间约为 47 秒,然后返回失败监听。
接听通话
重要
非兼容性变更:从 CallPlus 2.0 开始,
onReceivedCall方 法发生非兼容变更,新的方法会携带 extra 字段。请您在升级时注意修改相关代码。
被叫方的客户端应用程序中必须先注册 IRCCallPlusEventListener 监听器,才能通过 onReceivedCall 接收来电通知。onReceivedCall 方法中会返回 RCCallPlusSession 对象,通过 getCallId() 可获取 Call ID,使用 getCallType() 可获取通话类型。
调用以下方法选择是否接听来电:
- 接听来电,使用
RCCallPlusClient.getInstance().accept(callId)方法。如果呼叫被接受,CallPlus SDK 将自动建立媒体会话。 - 挂断来电,使用
RCCallPlusClient.getInstance().hangup(callId)方法。
注意
当发生网络断开或 IM 连接断开等情况导致请求失败时,accept 方法会根据 SDK 内部的策略进行重试。如果重试一直失败,最长等待时间约为 47 秒,然后返回失败监听。
您可以在收到来电事件后打开摄像头采集,并完成本地视图设置。
RCCallPlusClient.getInstance().setCallPlusEventListener(new IRCCallPlusEventListener() {
/**
* 本端用户通过该回调接收到通话呼叫
*
* @param callSession 通话实体信息
*/
@Override
public void onReceivedCall(RCCallPlusSession callSession, String extra) {
RCCallPlusSession currentCallSession = RCCallPlusClient.getInstance().getCurrentCallSession();
if (currentCallSession != null && !TextUtils.equals(callSession.getCallId(), currentCallSession.getCallId())) {
// 可用该判断识别:已有通话进行中,又有第二通通话呼入
// 第二通通话可直接调用 RCCallPlusClient.getInstance().accept 接听,SDK 内部会将第一通通话挂断
}
// SDK 的回调均在子线程执行;showDialog() 涉及 UI 操作,需要切换到主线程执行
runOnUiThread(new Runnable() {
@Override
public void run() {
// 打开摄像头采集;请提前完成摄像头、麦克风权限的动态申请
RCCallPlusClient.getInstance().startCamera();
RCCallPlusClient.getInstance().enableMicrophone(true);
setLocalVideoView(); // 复用发起通话逻辑中的"设置本地视频渲染视图"方法
showDialog(CallPlusActivity.this, "收到通话,是否接听?", "接听", new OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
acceptCall(callSession);
}
}, "挂断", new OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
RCCallPlusClient.getInstance().hangup();
}
});
}
});
}
@Override
public void onCallEnded(RCCallPlusSession session, RCCallPlusReason reason) {
}
@Override
public void onRemoteUserStateChanged(String callId, String userId, RCCallPlusUserSessionStatus status, RCCallPlusReason reason) {
}
});
调用 accept 方法接听前,需要提前设置远端视频视图:
private void acceptCall(RCCallPlusSession callSession) {
setRemoteUserVideoView(callSession.getRemoteUserList());
/**
* 开始接听通话
* 该方法内部为异步执行,结果回调是注册的{@link RCCallPlusClient#setCallPlusResultListener(IRCCallPlusResultListener)} 监听的 {@link IRCCallPlusResultListener#onAccept(RCCallPlusCode, String)}方法
*/
RCCallPlusClient.getInstance().accept(callSession.getCallId());
}
处理当前通话
开关麦克风
在通话过程中,您可以使用 enableMicrophone 方法来开关麦克风。可通过 IRCCallPlusEventListener 获取麦克风状态改变、音量变化等通知。
// 开启本端麦克风(请先完成麦克风权限的动态申请)
RCCallPlusClient.getInstance().enableMicrophone(true);
当麦克风状态改变时,另一方将通过 IRCCallPlusEventListener 的 onRemoteMicrophoneStateChanged 回调方法接收事件回调。
RCCallPlusClient.getInstance().setCallPlusEventListener(new IRCCallPlusEventListener() {
@Override
public void onReceivedCall(RCCallPlusSession callSession, String extra) {
}
/**
* 远端用户麦克风状态改变监听
*
* @param callId 通话 ID
* @param userId 用户 ID
* @param disabled 麦克风是否可用;true:关闭,false:开启
*/
@Override
public void onRemoteMicrophoneStateChanged(String callId, String userId, boolean disabled) {
IRCCallPlusEventListener.super.onRemoteMicrophoneStateChanged(callId, userId, disabled);
}
/**
* 用户音量改变回调,每秒回调一次
*
* @param hashMap key:用户Id value:音量值大小(取值:0-9)
*/
@Override
public void onUserAudioLevelChanged(HashMap<String, Integer> hashMap) {
IRCCallPlusEventListener.super.onUserAudioLevelChanged(hashMap);
}
});