简介

EaseCallKit是一套基于环信IM和声网音视频结合开发的音视频UI库,实现了1v1语音通话、视频通话,以及多人音视频通话的功能, EaseCallKit可以快速实现一些通用的音视频功能。
EaseCallKit在通话过程中,使用环信ID加入频道,方便音视频视图中显示用户名。如果用户不使用EaseCallKit而直接调用声网API,也可以直接使用数字uid加入频道。
注:本UI库只和移动端3.8.0以上版本Demo互通。3.8.1的UI库使用声网数字uid加入频道,而3.8.0使用字符串加入频道,3.8.1版本不与3.8.0互通, demo中EaseCallKit使用的token和Uid都是需要从声网申请的,如果您需要使用声网对应的音视频服务,也需单独要去声网申请。

跑通Demo

EaseCallKit集成在环信开源IMDemo中,你可以通过进入环信Demo及源码下载页面,选择Android端进行下载,直接下载: Android IM 源码

环境准备

  • Android Studio 3.2以上
  • Gradle4.6以上
  • targetVersion 26以上
  • Android SDK API 19以上
  • Java JDK 1.8以上

运行

  • 下载源码后,用Android Studio打开项目, 连接手机,然后就可以运行了

快速集成

以下展示如何快速集成EaseCallKit进行音视频通话的开发

在集成该库前,你需要满足以下条件:

使用EaseCallKitUI库完成音视频通话的基本流程如下:

  1. EaseCallKitUI库进行初始化并设置callkit监听(接口调用)
  2. 主叫方调用发起通话邀请接口,进入通话界面(接口调用)
  3. 被叫方收到邀请自动弹出通话邀请界面,在通话邀请界面选择接通或者拒绝(UI操作)
  4. 主叫或者被叫挂断通话(UI操作)

具体流程如下

导入UI库

EaseCallKitUI 主要依赖于com.hyphenate:hyphenate-chat:xxx版本,io.agora.rtc:full-sdk:xxx版本等库;

集成EaseCallKit库有两种方式,分别是 Gradle方式集成 和 Module源码集成

Gradle 方式集成

重大变动:远程仓库统一由JCenter迁移到MavenCentral,依赖库的域名由“com.hyphenate”修改为“io.hyphenate”,详见Android SDK 介绍及导入

  • 在build.gradle中增加以下内容,重新build 即可导入
implementation 'io.hyphenate:ease-call-kit:xxx版本'

EaseCallKitUI 必须依赖环信IM SDK(也就是hyphenate-chat) ,因而在使用EaseCallKitUI 时必须同时添加环信IM SDK依赖。

Module源码集成

implementation project(':ease-call-kit')

ease-call-kit中如果要修改hyphenate-chat和agora.rtc中版本号,可修改以下依赖

//环信SDK
implementation 'io.hyphenate:hyphenate-chat:xxx版本'  (hyphenate-chat 只支持3.8.0及以上版本)
//声网SDK
implementation 'io.agora.rtc:full-sdk:xxx版本'

添加权限

根据场景需要,本库需要增加 麦克风 相机 悬浮窗等权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hyphenate.easeim">
 
    <!-- 悬浮窗权限 -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <!-- 访问网络权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 麦克风权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!-- 相机权限 -->
    <uses-permission android:name="android.permission.CAMERA" />
    ...
</manifest>

添加CallkitActivity

在清单中增加ease-call-kit 中的 EaseVideoCallActivity 和 EaseMultipleVideoActivity:

//增加 Activity
<activity
    android:name="com.hyphenate.easecallkit.ui.EaseVideoCallActivity"
    android:configChanges="orientation|keyboardHidden|screenSize"
    android:excludeFromRecents="true"
    android:launchMode="singleInstance"
    android:screenOrientation="portrait"/>
<activity
    android:name="com.hyphenate.easecallkit.ui.EaseMultipleVideoActivity"
    android:configChanges="orientation|keyboardHidden|screenSize"
    android:excludeFromRecents="true"
    android:launchMode="singleInstance"
    android:screenOrientation="portrait"/>

初始化

在环信IM SDK初始化完成后,可以开始初始化EaseCallKit,同时增加监听回调,设置常用配置项。代码如下:

//初始化 calluikit
EaseCallKitConfig callKitConfig = new EaseCallKitConfig();
//设置默认头像
String headImage = EaseFileUtils.getModelFilePath(context,"watermark.png");
callKitConfig.setDefaultHeadImage(headImage);
//设置振铃文件
String ringFile = EaseFileUtils.getModelFilePath(context,"huahai.mp3");
callKitConfig.setRingFile(ringFile);
//设置呼叫超时时间
callKitConfig.setCallTimeOut(30 * 1000);
//设置声网AgoraAppIdappId
callKitConfig.setAgoraAppId("*****");
Map<String, EaseCallUserInfo> userInfoMap = new HashMap<>();
userInfoMap.put("***",new EaseCallUserInfo("****",null));
userInfoMap.put("***",new EaseCallUserInfo("****",null));
callKitConfig.setUserInfoMap(userInfoMap);
EaseCallUIKit.getInstance().init(context,callKitConfig);
addCallkitListener();

可设置的配置项包括以下内容:

/**
 *
 * 有关callkit 用户配置选项
 * defaultHeadImage  用户默认头像(为本地文件绝对路径 或者url)
 * userInfoMap      有关用户信息(key为环信Id , value为EaseCallUserInfo);
 * callTimeOut      呼叫超时时间(单位ms 默认30s)
 * audioFile       震铃文件(本地文件绝对路径)
* enableRTCToken  是否需要RTC验证(需要声网后台去控制 默认为关闭)
 */
public class EaseCallKitConfig {
    private String defaultHeadImage;
    private Map<String,EaseCallUserInfo> userInfoMap = new HashMap<>();
    private String RingFile;
    private String agoraAppId = "****";
    private long callTimeOut = 30 * 1000;
    public EaseCallKitConfig(){
  ...
}

发起通话邀请

初始化完成后,可以发起音视频通话

1v1音视频通话

1v1通话可分为视频通话和语音通话,接口如下所示:

/**
* 加入1v1通话
* @param type 通话类型(只能为SIGNAL_VOICE_CALL或SIGNAL_VIDEO_CALL类型)
* @param user 被叫用户ID(也就是环信ID)
* @param ext  扩展字段(用户扩展字段)
*/
public void startSingleCall(final EaseCallType type, final String user,final  String ext){}

多人音视频通话

用户自己从群组成员列表或者好友列表中选择,发起多人音视频邀请,具体实现可参考demo中的 ConferenceInviteActivity 选择邀请人员列表及调用,接口如下所示:

/**
 * 邀请加入多人通话
 * @param users 用户ID列表(环信ID列表)
 * @param ext  扩展字段(用户扩展字段)
 */
public void startInviteMultipleCall(final String[] users,final String ext){}

发起通话后的UI界面如下:

被叫收到通话邀请

主叫方发起邀请后,如果被叫方在线,且当前不在通话中,将弹起邀请通话界面,可以选择接听或者拒绝,通话页面如下所示

被叫收到邀请后 会触发EaseCallKitListener中的以下回调:

/**
 * 收到通话邀请回调
 * @param callType  通话类型
 * @param userId   邀请方userId
 * @param ext     自定义扩展字段
 */
void onRevivedCall(EaseCallType callType, String userId,String ext){}

收到通话邀请后的界面如下

多人通话中邀请

多人通话中,当前用户可以再次向其他用户发起邀请。用户点击多人通话界面右上角的邀请按钮,

会触发EaseCallKitListener中的以下回调:

/**
 * 邀请好友进行多人通话
 * @param context
 * @param users   当前通话中已经存在的成员
 * @param ext     自定义扩展字段
 */
public void onInviteUsers(Context context,String userId[],String ext) {
}

1v1音视频通话中,对方挂断,本地会自动挂断结束,多人音视频通话中需要主动挂断才能结束通话,

通话结束后,会触发EaseCallKitListener中的以下回调:

加入频道成功回调

3.8.1新增接口,在自己或者他人加入会议后,会收到EaseCallKitListener中的以下回调:

@Override
public void onRemoteUserJoinChannel(String channelName, String userName, int uid, EaseGetUserAccountCallback callback){
    //此时,可以获取当前频道中已有用户的声网id与环信id的映射表,并将映射表设置到UI库,同时也可以更新用户的头像和昵称
    // callback.onUserAccount(accounts);
}

通话结束

单人通话中,对方挂断,本地会自动挂断结束,多人通话中需要主动挂断才能结束通话。通话结束后,会触发以下回调

//**
 * 通话结束
 * @param callType    通话类型
 * @param reason     通话结束原因
 * @param callTime  通话时长
*/
void onEndCallWithReason(EaseCallType callType, String channelName, EaseCallEndReason reason, long callTime){}
 
//通话结束原因如下
public enum EaseCallEndReason {
    EaseCallEndReasonHangup(0), //正常挂断
    EaseCallEndReasonCancel(1), //自己取消通话
    EaseCallEndReasonRemoteCancel(2), //对方取消通话
    EaseCallEndReasonRefuse(3),//拒绝接听
    EaseCallEndReasonBusy(4), //忙线中
    EaseCallEndReasonNoResponse(5), //自己无响应
    EaseCallEndReasonRemoteNoResponse(6), //对端无响应
    EaseCallEndReasonHandleOnOtherDevice(7); //在其他设备处理
   ....
}

获取声网token

用户加入音视频时,需要获取声网token以进行鉴权,获取token的过程由用户自己完成,需要在EaseCallKitListener中把token回调给callkit

如果不需要鉴权,可以直接回调token 为null,或者不实现该回调, 具体接口和使用如下:

/**
 * 用户生成Token回调
 * @param userId       用户自己Id(环信Id)
 * @param channelName  频道名称
 * @param agoraAppId   声网appId
 * @param callback     生成的Token回调(成功为Token,失败为 errorCode和errorMsg)
 */
 default void onGenerateToken(String userId,String channelName,String agoraAppId,EaseCallKitTokenCallback callback){};
 
 @Override
 public void onGenerateToken(String userId, String channelName, String agoraAppId, EaseCallKitTokenCallback callback){
         if(callback != null){
               // 不需要进行Token鉴权,传null,Uid传0
               //callback.onSetToken(null, 0);
               
               // 需要Token鉴权,则调用自己的AppServer生成Token,然后把生成的Token回调给callkit
               // url为拼接参数请求token的url,可参考EaseIM App中实现
               getRtcToken(url, callback);
               //获取到token及uid后,将其回调给callback即可
               //callback.onSetToken(token, uid);
         }
}

3.8.1后使用数字uid加入声网频道,上面的接口中的EaseCallKitTokenCallback中的onSetToken方法增加uid这个参数,如下:

void onSetToken(String token, int uId);

通话异常回调

通话过程中如果有异常或者错误发生,会触发EaseCallKitListener中的以下回调:

/**
  * 通话错误回调
  * @param type            错误类型
  * @param errorCode      错误码
  * @param description   错误描述
*/
void onCallError(EaseCallUIKit.EaseCallError type, int errorCode, String description){}

EaseCallError异常包括业务逻辑异常,音视频异常以及业务逻辑异常,定义如下

/**
 * 通话错误类型
 *
*/
public enum EaseCallError{
   PROCESS_ERROR, //业务逻辑异常
   RTC_ERROR, //音视频异常
   IM_ERROR  //IM异常
}

配置修改

callkit库初始化之后,可以再次修改有关配置,接口和示例如下:

/**
* 获取当前callKitConfig
*
*/
public EaseCallKitConfig getCallKitConfig(){}
 
//如修改配置默认头像
EaseCallKitConfig config = EaseCallUIKit.getInstance().getCallKitConfig();
if(config != null){
     String Image = EaseFileUtils.getModelFilePath(context,"bryant.png");
     callKitConfig.setDefaultHeadImage(Image);
}

头像昵称修改

3.8.1新增了修改头像昵称的接口,用户可以在加入频道后,修改自己和会议中其他人的头像昵称,修改方法如下:

@Override
public void onRemoteUserJoinChannel(String channelName, String userName, int uid, EaseGetUserAccountCallback callback){
    if(userName == null || userName == ""){
        // url为获取用户信息的请求url,实现可参考EaseIM App中实现
        getUserIdAgoraUid(uid, url, callback);
        // 最后将获取到的用户信息设置给callback,具体实现请参考EasaIM App中实现
        //callback.onUserAccount(userAccounts);
    }else{
        //设置用户昵称 头像
        setEaseCallKitUserInfo(userName);
        EaseUserAccount account = new EaseUserAccount(uid,userName);
        List<EaseUserAccount> accounts = new ArrayList<>();
        accounts.add(account);
        callback.onUserAccount(accounts);
    }
}

离线推送

为保证被叫用户APP退到后台,或离线时也能收到通话请求,用户需要开启离线推送,离线推送场景方案参见:http://docs-im.easemob.com/im/other/integrationcases/iosimnotifi

离线推送开启过程参见:推送集成

开启离线推送后,被叫用户在离线情况下收到呼叫请求时,会在手机通知页面弹出一条通知消息,用户点击该消息,可以唤醒App,并进入振铃页面

EaseCallKit中提供的接口如下

方法 说明

init 初始化方法
setCallKitListener 设置监听
startSingleCall 发起单人通话
startInviteMultipleCall 发起多人通话
getCallKitConfig 获取有关配置

回调模块EaseCallKitListener的API列表如下

事件 说明
onEndCallWithReason 通话结束时触发该事件
onInviteUsers 多人会议中点击邀请按钮触发该事件
onReceivedCall 振铃时触发该事件
onGenerateToken 获取声网token回调,用户自己获取到Token, 把Token回调到callkit
onCallError 通话异常时触发该回调
onInViteCallMessageSent 通话邀请消息回调
onRemoteUserJoinChannel 3.8.1以上,用户加入频道时触发

EaseGetUserAccountCallback的API列表如下

事件 说明
onUserAccount 3.8.1以上,传入环信名称与声网uid的映射
onSetUserAccountError 3.8.1以上,获取用户信息失败