实现1对1呼叫(不含UI集成-V1)

更新时间: 2023/08/08 09:49:51

本文介绍如何通过集成呼叫组件(NERTCCallkit)基础包(不含 UI),实现 1 对 1 呼叫相关业务逻辑。根据业务需求,您需要自行实现相关 UI 界面。本文介绍呼叫组件 V1 版本的集成和实现方法。

推荐您集成呼叫组件 V2 版本,请参见实现1对1呼叫(含UI集成-V2)实现1对1呼叫(不含UI集成-V2)

注意事项

  • 针对呼叫组件中的回调信息,开发者要做好相应回调数据的上报及存储,以便于后期上线之后排查问题。

基本概念

  • accid:accid 是 IM 账号,用于登录 IM。注册云信 IM 账号时, IM 服务器会返回对应的 accid 和 Token,应用客户端需要负责保存 accid 和 IM token 的映射关系。
  • Token:呼叫组件中涉及的 Token 包括 IM Token 和 RTC Token。两者不是同一个 Token,请根据实际使用场景填入对应的 Token。
    • IM Token:用于登录 IM 时进行 IM 账号鉴权。应用服务器调用 IM 服务器的 create.action注册云信 IM 账号时,获取的 IM Token。
    • RTC Token:用于加入 RTC 房间时进行 RTC 账号鉴权。应用服务器调用 NERTC 服务器的 getToken,获取的 RTC Token。

开发环境

环境要求 说明
JDK 版本 1.8.0 及以上版本
Android API 版本 API 31、Android 5.0 及以上版本
CPU 架构 ARM64、ARMV7
IDE Android Studio
其他 依赖 Androidx,不支持 support 库。Android 系统 5.0 或以上版本的移动设备。

准备工作

集成呼叫组件

呼叫组件(NERTCCallkit)基于网易云信 NIM SDK 和 NERTC SDK 实现通话呼叫,呼叫组件中已集成 NERTC SDK,您只需集成指定版本的 NIM SDK 即可。

步骤 1: 集成 NIM SDK

单击查看集成 NIM SDK 的操作步骤。如果您的项目中已经集成了 NIM SDK,请忽略该步骤。
  1. 若您需要创建新项目,在 Android Studio 里,在顶部菜单依次选择 File > New > New Project 新建工程,再依次选择 Phone and Tablet > Empty Activity,单击 Next
    image

    创建 Android 项目成功后,Android Studio 会自动开始同步 gradle, 您需要等同步成功后再进行下一步操作。

  2. 在项目根目录下的 “build.gradle” 文件中,配置 repositories(使用 maven)。示例代码如下:

    groovyallprojects {
        repositories {
            mavenCentral()
        }
    }
    
  3. 在 “app” 目录下的 “build.gradle” 文件中,配置支持的 SO 库架构。示例代码如下:

    groovyandroid {
    defaultConfig {
        ndk {
            //设置支持的SO库架构
            abiFilters "armeabi-v7a", "x86","arm64-v8a","x86_64"
            }
    }
    }
    
  4. 根据开发者项目的需求,添加对应的依赖。

    groovydependencies {
        implementation fileTree(dir: 'libs', include: '*.jar')
        // 添加依赖。
        
        // 基础功能 (必需)
        implementation "com.netease.nimlib:basesdk:${LATEST_VERSION}"
    }
    
  5. 添加权限。

    根据实际应用需求,在 AndroidManifest.xml 设置权限(请将 com.netease.nim.demo 替换为自己的包名)。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.netease.nim.demo">
    
        <!-- 权限声明 -->
        <!-- 访问网络状态-->
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <!-- 音视频呼叫-->
        <uses-permission android:name="android.permission.CAMERA"/>
        <uses-permission android:name="android.permission.RECORD_AUDIO"/>
        <application
            ...>
        <!-- APP Key, 可以在这里设置,也可以在 SDKOptions 中提供。
                如果 SDKOptions 中提供了,则取 SDKOptions 中的值。 -->
            <meta-data
                android:name="com.netease.nim.appKey"
                android:value="key_of_your_app" />
        </application>
    </manifest>
    

SDK 版本限制

  • 呼叫组件与 NIM SDK、NERTC SDK 之间存在映射关系,使用呼叫组件时,必须使用指定版本的 NIM SDK 和 NERTC SDK。呼叫组件 V1.8.2 版本适配 NIM SDK V9.8.0 和 NERTC SDK V4.6.29,其他版本的适配关系请参考更新日志

  • 如果您已经集成了 NIM SDK 和 NERTC SDK,或因业务需求等原因无法使用指定版本 SDK,请联系网易云信技术支持确认是否能替换 SDK 版本,并参考 Gradle 引入呼叫组件中的步骤 3 去除呼叫组件依赖的版本。

步骤 2:初始化 NIM SDK

ApplicationonCreate 中,调用 init 方法 初始化 NIM SDK。

示例代码如下:

public class NimApplication extends Application {
    public void onCreate() {
        NIMClient.init(this, loginInfo(), options());
    // 如果提供用户信息,将同时进行自动登录。如果当前还没有登录用户,请传入null。
    private LoginInfo loginInfo() {
        return null;
    }
    // 设置初始化配置参数,如果返回值为 null,则全部使用默认参数。
    private SDKOptions options() {
        SDKOptions options = new SDKOptions();
        return options;
      }// 可在 SDKOptions 中配置 App Key
    }
}

以上提供了一个简化的初始化示例,更多初始化信息请参见初始化 SDK

步骤 3:实现登录 IM

调用login 方法登录 IM。

本文以实现静态 Token 登录为例,动态 Token 登录以及自动登录的实现方法请参见登录 IM

示例代码如下:

public class LoginActivity extends Activity {
  public void doLogin() {
      LoginInfo info = new LoginInfo(); //传入accid和token
      RequestCallback<LoginInfo> callback =
          new RequestCallback<LoginInfo>() {
                  @Override
                  public void onSuccess(LoginInfo param) {
                      LogUtil.i(TAG, "login success");
                      // your code
                  }
                  @Override
                  public void onFailed(int code) {
                      if (code == 302) {
                          LogUtil.i(TAG, "账号密码错误");
                          // your code
                      } else {
                          // your code
                      }
                  }
                  @Override
                  public void onException(Throwable exception) {
                      // your code
                  }
      };
      NIMClient.getService(AuthService.class).login(info).setCallback(callback);
  }
}

步骤 4: Gradle 引入呼叫组件

  1. 在工程根部目录的 build.gradle 文件中添加如下代码。

    allprojects {
        repositories {
            //...
            mavenCentral()
            //...
        }
    }
    
  2. 在主工程 build.gradle 文件中添加如下代码,引入呼叫组件。

    // 若出现 More than one file was found with OS independent path 'lib/arm64-v8a/libc++_shared.so'.
    // 可以在主 module 的 build.gradle 文件中 android 闭包内追加如下 packageOptions 配置
    android{
        //......
        packagingOptions {
        pickFirst 'lib/arm64-v8a/libc++_shared.so'
        pickFirst 'lib/armeabi-v7a/libc++_shared.so'
        }
    }
    // 引入呼叫组件安装包,导入以下其中一个
    dependencies {
    
        implementation 'com.netease.yunxin.kit.call:call:2.1.0'     // 基础组件包,不含UI 集成时请导入这个包
    
    }
    
  3. (可选)去除呼叫组件依赖的 SDK。

    如果因业务需求或其他原因无法使用指定版本 SDK,您可以参考如下步骤去除呼叫组件依赖的 IM 和 RTC 的 SDK。

    implementation('com.netease.yunxin.kit.call:call:2.1.0') {
            exclude group: 'com.netease.nimlib'// 去除组件依赖的 IM sdk
            exclude group: 'com.netease.yunxin', module: 'nertc-base' // 去除组件依赖的 NERTC sdk
    }
    
  4. 配置防代码混淆。

    代码混淆是指使用简短无意义的名称重命名类、方法、属性等,增加逆向工程的难度,保障 Android 程序源码的安全性。为了避免因重命名类,导致调用呼叫组件异常,您需要配置防代码混淆。

    请在 proguard-rules.pro 配置文件中加入以下代码防止混淆:

    # NIM SDK的类,如果集成IM时已经添加,请忽略
    -dontwarn com.netease.nim.**
    -keep class com.netease.nim.** {*;}
    
    -dontwarn com.netease.nimlib.**
    -keep class com.netease.nimlib.** {*;}
    
    -dontwarn com.netease.share.**
    -keep class com.netease.share.** {*;}
    
    -dontwarn com.netease.mobsec.**
    -keep class com.netease.mobsec.** {*;}
    
    # NERTC SDK的类
    -keep class com.netease.lava.** {*;}
    -keep class com.netease.yunxin.** {*;}
    
    # 呼叫组件的类
    -dontwarn com.netease.yunxin.kit.**
    -keep class com.netease.yunxin.kit.** {*;}
    -keep public class * extends com.netease.yunxin.kit.corekit.XKitInitOptions
    -keep class * implements com.netease.yunxin.kit.corekit.XKitService {*;}
    

步骤 5: 初始化呼叫组件

IM 登录成功之后,需要调用 init 接口进行呼叫组件的初始化。初始化时需要设置以下必选参数,其他参数可以按需配置。

  • rtcAppKey:网易云信应用的 AppKey,请在控制台中应用详情页面中查看指定应用的 AppKey。
  • currentUserAccId:当前用户的 IM accId。

App 用户在本端完成初始化后才可以正常接收其他人的的呼叫,或主动发起呼叫。若未完成初始化,组件可能会提示应用进行初始化,或被叫方 App 在被呼叫时无提示。若 App 重复调用初始化接口,受内部依赖的 RTC SDK 以及 IM SDK 限制,会出现崩溃,需要重新初始化需要先完成组件的销毁。

呼叫组件初始化相关代码内容可以放在工程的 MainActivity 中执行,尽量避免在 MainActivity#onDestroy() 方法中做组件的释放。建议在 App 用户登出时释放,登入时进行初始化。

  • 初始化呼叫组件相关步骤需要在 IM 登录成功之后进行。
  • rtcTokenService 参数只有 V 1.8.0 之前的版本才需要配置,V1.8.0及之后版本无需配置。
  • 核心功能参数说明请参见初始化参数配置

示例代码:

java// NERTC SDK 初始化配置
NERtcOption rtcOption = new NERtcOption();
// 呼叫组件初始化配置
VideoCallOptions videoCallOptions = new VideoCallOptions(rtcOption);
// 设置当前用户登录 IM SDK 的 accId,字符串类型,!!必须!!
videoCallOptions.currentUserAccId = currentUserAccId;
// 设置对应 RTC SDK uid,长整形,默认为 0 时组件会自动生成;
videoCallOptions.currentUserRtcUId = 0;
// 是否允许组件发送未接通话单,默认 true,发送;
videoCallOptions.enableOrder = true;
// 是否允许被叫用户收到呼叫邀请后自动加入 IM 信令通道,默认 false;注意,此功能和多端登录功能互斥;
videoCallOptions.enableAutoJoinWhenCalled = false;
// 在 IM sdk 初始化并完成登录后,调用呼叫组件初始化
NERTCVideoCall.sharedInstance().setupAppKey(context, appKey, videoCallOptions);

实现 1 对 1 呼叫(点对点呼叫)

API 时序图

sequenceDiagram
    autonumber
    participant 应用层
    participant NERTCCallkit

    
    应用层->>NERTCCallkit: addDelegate 添加监听
    应用层->>NERTCCallkit: call 发起呼叫
    NERTCCallkit-->>应用层: onInvited 收到呼叫邀请
    应用层->>NERTCCallkit: accept 接听呼叫
    NERTCCallkit-->>应用层: onJoinChannel 主叫加入RTC房间的回调
    NERTCCallkit-->>应用层: onUserEnter 被叫加入RTC房间的回调,通话建立
    应用层->>NERTCCallkit: reject 拒接来电
    NERTCCallkit-->>应用层:onRejectByUserId 通话被拒接的回调
    应用层->>NERTCCallkit: hangup 挂断通话
    NERTCCallkit-->>应用层: onCallEnd 通话挂断的回调
  

实现方法

  1. 添加呼叫监听。

在初始化呼叫组件后,调用 addDelegate 方法,添加回调监听。

java// 监听呼叫邀请,此处可在全局处设置,避免被意外释放导致来电丢失
NERTCVideoCall.sharedInstance().addDelegate(new AbsNERtcCallingDelegate() {
  @Override
  public void onInvited(InvitedInfo invitedInfo) {
    // 收到来电后可进行展示 notification,或者将 invitedInfo 传入被叫页面并调起被叫页面;
  }
});
  1. 主叫发起呼叫。

进入自己的呼叫页面,调用 addDelegate 方法添加通话中监听,页面销毁时调用 removeDelegate避免内存泄漏,并调用 call 接口发起呼叫。

SingleCallParam 相关参数说明如下表所示。

参数 类型 是否必选 描述
calledUserAccId String 必选 被叫用户的 IM accId。注册云信 IM 账号时, IM 服务器会返回对应的 accid 和 Token。
channelType ChannelType 必选 呼叫类型,包括:
  • ChannelType.AUDIO: 音频
  • ChannelType.VIDEO:视频
extraInfo String 可选 扩展信息,透传到到被叫 onInvited
globalExtraCopy String 可选 自定义的呼叫全局抄送信息,用户服务端接收抄送时设置自己的业务标识
rtcChannelName String 可选 自定义channelName,不传会默认生成

示例代码如下:

javaSingleCallParam param = new SingleCallParam("calledUserAccId", ChannelType.VIDEO , null, null, null);
JoinChannelCallBack callback = new JoinChannelCallBack() {
  @Override
  public void onJoinChannel(ChannelFullInfo channelFullInfo) {
    // 呼叫成功,返回对应呼叫相关信息。
    // 如信令通道的 channelId = channelFullInfo.getChannelBaseInfo().getChannelId()
  }
  @Override
  public void onJoinFail(String msg, int code) {
    if (code == ResponseCode.RES_PEER_NIM_OFFLINE) {
      // 呼叫成功但被叫用户处于离线,不用关闭页面。若在呼叫过程中被叫用户上线,可收到对应的呼叫邀请
      return;
    }
    // 呼叫失败关闭呼叫页面,停止提示音等
  }
};
// 发起呼叫,可在此时做呼叫提示音
NERTCVideoCall.sharedInstance().call(param, callback);
  1. 主叫取消呼叫。

当用户已经完成呼叫动作时,可以随时调用 cancel 取消本次呼叫,此时被叫的监听 onCancelByUserId 也会收到对应回调。

javaRequestCallback<Void> callback  = new RequestCallback<Void>() {
  @Override
  public void onSuccess(Void aVoid) {
    // 取消成功,关闭相关页面等操作
  }
  @Override
  public void onFailed(int i) {
    // 邀请已经被被叫用户接受了,此时取消失败,被叫进入音视频房间,此时看作取消动作无效,若仍需取消可使用挂断接口
    if (i == ResponseCode.RES_INVITE_HAS_ACCEPT) {
      return;
    }
    // 取消失败,可做 hangup 挂断动作完成相关页面销毁
  }
  @Override
  public void onException(Throwable throwable) {
    // 取消出现异常,同取消失败      
  }
};
// 执行组件取消动作
NERTCVideoCall.sharedInstance().cancel(callback);
  1. 被叫收到呼叫邀请。

当收到呼叫邀请时,被叫会收到 onInvited 回调,并在回调中告知主叫的相关信息,包括主叫的accid、呼叫类型、信令通道的 ID、自定义信息。

详细的回调参数 InvitedInfo 如下:

参数 类型 描述
invitor String 主叫用户的 IM accId。注册云信 IM 账号时, IM 服务器会返回对应的 accid 和 Token。
currentAccId String 当前用户的 IM accid。
channelType int 呼叫类型,包括:
  • ChannelType.AUDIO.getValue(): 音频
  • ChannelType.VIDEO.getValue():视频
attachment String 扩展信息,主叫用户呼叫参数中 extraInfo 字段。
requestId String 通话请求 ID。
channelId String IM 信令 channelId。
channelName String 主叫呼叫要求被叫加入的 RTC 房间名称。

被叫用户在收到邀请信息时可根据呼叫类型(VIDEO/AUDIO)来调用不同的方法直接启动被叫页面的 Activity。

若被叫方系统在 Android Q 及以上时,系统限制不允许后台弹出页面,此时会弹出对应的 Notification,被叫方可通过点击 Notification 跳转至对应的被叫页面。

若用户通过 launcher 或其他方式唤起 app 时,通话仍有效则同样会展示被叫页面,进入被叫页面后同样可以调用 addDelegate 方法用于监听通话中相关回调,页面销毁时调用 removeDelegate避免内存泄漏。

  1. 被叫接听。

被叫调用accept接口,接听当前的呼叫邀请,此时会触发onJoinChannel回调,主叫加入 RTC 成功;以及onUserEnter回调,被叫加入 RTC 成功,认为此时通话建立成功。

当被叫用户点击呼叫页面的中接听按钮时,若此时通话仍在呼叫中则可接通此次通话并加入对应的音视频房间内,和主叫方进行音视频通话。

javaJoinChannelCallBack callback = new JoinChannelCallBack() {
  @Override
  public void onJoinChannel(ChannelFullInfo channelFullInfo) {
    // 接听成功
  }
  @Override
  public void onJoinFail(String msg, int code) {  
    // 接听失败,可做被叫页面销毁等动作
  }
};
// 执行通话接听动作
NERTCVideoCall.sharedInstance().accept(callback);
  1. 被叫拒接来电。

被叫调用 reject 拒接来电或挂断电话,此时应销毁页面,主叫收到onRejectByUserId回调。

javaRequestCallback<Void> callback = new RequestCallback<Void>() {
  @Override
  public void onSuccess(Void aVoid) {
    // 拒接成功,关闭对应页面等销毁动作
  }
  @Override
  public void onFailed(int code) {
    // 拒接失败,若 code 为 ResponseCode.RES_CHANNEL_NOT_EXISTS 、ResponseCode.RES_INVITE_NOT_EXISTS 、ResponseCode.RES_INVITE_HAS_REJECT、ResponseCode.RES_PEER_NIM_OFFLINE 、ResponseCode.RES_PEER_PUSH_OFFLINE) 说明主叫方存在问题可直接关闭页面做销毁动作,若非以上 code 则说明主叫方未收到拒接指令,则无法完成拒接动作,若仍想挂断可尝试使用挂断接口
  }
  @Override
  public void onException(Throwable throwable) {
    // 出现异常直接关闭当前页面,主叫方等待超时接听退出呼叫状态
  }
}
// 执行通话拒接动作
NERTCVideoCall.sharedInstance().reject(callback);
  1. 通话中挂断。

主叫/被叫均可调用 hangup 接口实现挂断通话中的通话。

java// (可选)主叫通过呼叫回调结果获取,被叫通过 onInvited 回调获取,若填入 null 则直接挂断当前通话,若填入制定值,则内部校验当前通话和传入的 channelId 是否为同一通话,若不同则挂断失败,否则挂断成功;
String channelId = null; 
RequestCallback<Void> callback = new RequestCallback<Void>() {
  @Override
  public void onSuccess(Void result) {
  }
  @Override
  public void onFailed(int code) {
  }
  @Override
  public void onException(Throwable exception) {
  }
}
// 执行挂断操作
NERTCVideoCall.sharedInstance().hangup(channelId, callback);
  1. 忙线。

当被叫用户不在 STATE_IDLE 状态下接收到其他主叫用户的呼叫邀请时,被叫方内部会自动执行 NERTCVideoCall.reject 动作,主叫方接收到对方的 reject 消息后会回调 NERTCCallingDelegate.onUserBusy 方法用于 UI 展示,主叫方本地发送忙线话单消息。

进阶功能

多端登录

云信 IM SDK 支持多端或单端登录,若此时正在通过呼叫组件进行音视频通话时,其他端登录相同账号:

  1. IM 关闭多端登录:此时由于关闭多端登录导致信令通道同时被踢出无法通过信令完成消息通知。此时会直接做离开音视频房间操作,对端用户感知到本端离开动作后,会做挂断挂断操作。
  2. IM 开启多端登录:其他端的用户登录不会影响当前通过组件发起的音视频通话。但若多端同时在线时,收到呼叫邀请时会同时展示被邀请页面,如果其中一端接听或拒绝,则其他端会收到相应错误回调。错误码为 20012002

呼叫/被叫超时

主叫方发起呼叫被叫方时,若主叫方不取消,被叫方既不接听也不挂断,此时会触发超时限制。目前超时限制时间最长为 2分钟,触发超时限制后主叫方和被叫方都会触发 onTimeout 回调,同时主叫方会做取消动作,被叫方会做挂断操作。

用户可通过如下接口实现更改超时时间,但不能超过 2分钟。此修改在发生呼叫或收到呼叫邀请前对本次通话生效,否则对下次通话生效。

javaNERTCVideoCall.sharedInstance().setTimeOut(long time);// 单位为毫秒

视频通话设置本地预览与订阅远端画面

用户发起呼叫后可以调用如下接口设置本地预览画面:

javaNERtcVideoView videoView;// 用于展示本端画面的布局UI
NERTCVideoCall.sharedInstance().setupLocalView(videoView);

调用如下接口设置远端画面,此方法可在 onUserEnter 回调中调用。

javaNERtcVideoView videoView;// 用于展示远端画面的布局UI
String userId = "远端待订阅视频用户的 IM 账号 Id";
NERTCVideoCall.sharedInstance().setupRemoteView(videoView, userId);

设置通话分辨率及相关参数

通过设置调用 setCallExtension 方法实现修改分辨率等参数。

javaNERTCVideoCall.sharedInstance().setCallExtension(new NERtcCallExtension(){
  @Override
  protected void configAudioProfileBeforeJoin() {
    // 音频设置standard + speech
    NERtcEx.getInstance().setAudioProfile(NERtcConstants.AudioProfile.STANDARD, NERtcConstants.AudioScenario.SPEECH);
  }
  @Override
  protected void configVideoConfigBeforeJoin(){
    // 设置一个默认的videoConfig
    NERtcVideoConfig videoConfig = new NERtcVideoConfig();
    // 帧率:15
    videoConfig.frameRate = NERtcEncodeConfig.NERtcVideoFrameRate.FRAME_RATE_FPS_15;
    // 用户修改分辨率不要使用 profile,应该使用width/height修改,两种方式都存在时,profile 方式失效。
    //分辨率:360*640(注意此处需要设置宽大于高不用考虑具体角度旋转)
    videoConfig.width = 640;
    videoConfig.height = 360;
    NERtcEx.getInstance().setLocalVideoConfig(videoConfig);
  }
  @Override
  protected void configChannelProfileBeforeJoin(){
    // 设置channel profile
    NERtcEx.getInstance().setChannelProfile(NERtcConstants.RTCChannelProfile.COMMUNICATION);
  }
});
此文档是否对你有帮助?
有帮助
去反馈
  • 注意事项
  • 基本概念
  • 开发环境
  • 准备工作
  • 集成呼叫组件
  • 步骤 1: 集成 NIM SDK
  • 步骤 2:初始化 NIM SDK
  • 步骤 3:实现登录 IM
  • 步骤 4: Gradle 引入呼叫组件
  • 步骤 5: 初始化呼叫组件
  • 实现 1 对 1 呼叫(点对点呼叫)
  • API 时序图
  • 实现方法
  • 进阶功能
  • 多端登录
  • 呼叫/被叫超时
  • 视频通话设置本地预览与订阅远端画面
  • 设置通话分辨率及相关参数