# DUIX_SDK_Android使用文档

更新时间:2023年10月08日14:04:13

本文介绍了如何使用硅基数字人服务提供的DUIX Android SDK,包括下载安装、关键接口及代码示例。

# 物料准备

duix_sdk_release_2.0.2.aar

# 下载安装

  1. 下载duix_sample_android_2.0.2.zip (opens new window)
  2. 解压zip包,在duix_sample/app/libs目录下获取AAR格式的SDK包,将AAR包集成到您的工程项目中进行依赖。
  3. 使用Android Studio打开此工程查看参考代码实现,其中duix交互的主要代码在DisplayActivity.java文件中。

# Gradle配置

工程下app模块添加如下配置:

dependencies {
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
    implementation 'org.webrtc:google-webrtc:1.0.32006'
    implementation 'com.auth0:java-jwt:3.8.3'

    ...
}

# 权限处理

在SDK中manifest已经添加网络、音频、蓝牙等权限申请,如果需要开启摄像头需要添加camera权限

// 依赖模块中已经添加了以下权限,因此不需要在项目中申明
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

# SDK关键接口

  • 初始化DUIXCore实例

/**
* @param AppID 在DUIX平台"资源和管理"->"应用"->对应应用的AppID
* @param AppSecret 在DUIX平台"资源和管理"->"应用"->对应应用的密钥
* @param DIGITAL_ID 在DUIX平台"会话管理"->对应会话的会话ID
*/
DUIXOptions duixOptions = new DUIXOptions(AppID, AppSecret, DIGITAL_ID);
/**
* @param context 系统上下文
* @param eglBaseContext 数字人GL绘制上下文
* @param duixOptions 初始化需要的一些参数
* @param callback DUIX会话的回调接口
*/
DUIXCore mDUIXCore = new DUIXCore(mContext, eglBaseContext, duixOptions, new DUIXCallback());

其中DUIXCallback接口包含如下回调:

  1. onConnectError: 连接DUIX异常,网络、鉴权、并发限制等都会导致该接口回调。
```
void onConnectError(int code, String msg);
```

常见的错误码类型:

| 错误码 | 说明                                                      |
| ------ | --------------------------------------------------------- |
| -1     | 客户端网络请求异常                                        |
| -2     | 客户端接口请求http状态码异常。                            |
| -3     | 客户端获取DUIX资源异常                                    |
| -4     | 客户端MQTT连接失败                                        |
| -5     | 客户端WebRTC的SDP创建失败,一般为iceServer异常。          |
| -6     | 客户端WebRTC的SDP设置失败,一般为iceServer异常。          |
| -7     | 客户端会话交互异常,常见的有鉴权失败、ASR资源启动失败等。 |
| 1005   | 服务端token不能为空                                       |
| 1006   | 服务端toke失效                                            |
| 1007   | 服务端话术信息不存在                                      |
| 1009   | 服务端用户可用次数不足                                    |
| 2002   | 服务端会话信息不存在                                      |
| 40001  | 服务端appid或者appscrect无效                              |
| 40002  | 服务端内部错误                                            |
| 50006  | 服务端签名失败                                            |
| 50007  | 资源总并发不足                                            |
  1. onIMConnectLost: MQTT连接丢失,网络不稳定会导致该问题,MQTT会有自动重连的机制
```
/**
* @param msg  错误信息描述
*/
default void onIMConnectError(String msg)
```
  1. onStop: 后台返回DUIX数字人已销毁
```
/**
     * 会话已销毁
     * @param cause 销毁原因
     */
void onRender(String cause)
```
  1. onMessage: 返回后台控制消息
```
/**
     * 返回后台控制消息。可以根据不同的时间类型做相应的业务处理
     *
     * @param topic  消息返回的订阅地址
     * @param message 数据完整结构
     */
void onMessage(String topic, String message);
```
  1. onAsr: 返回识别结果
```
/**
     * 识别结果返回
     *
     * @param content ASR识别的内容
     */
void onAsr(String content)
```
  1. onAnswer: 使用anser(String question)接口处理的回调内容
```
/**
     * 使用answer发起的答疑内容回调
     * @param filePath 音频文件的URL地址
     * @param content 答疑的文案
     */
void onAnswer(String filePath, String content);
```
  1. onIceConnectionChange: RTC连接状态

    /**
    * rtc连接状态回调
    * @param iceConnectionState 当前rtc状态
    */
    default void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
        }
    

    状态列表:

    名称 说明
    NEW ICE 代理正在搜集地址或者等待远程候选可用。
    CHECKING ICE 代理已收到至少一个远程候选,并进行校验,无论此时是否有可用连接。同时可能在继续收集候选。
    CONNECTED ICE代理至少对每个候选发现了一个可用的连接,此时仍然会继续测试远程候选以便发现更优的连接。同时可能在继续收集候选。
    COMPLETED ICE代理已经发现了可用的连接,不再测试远程候选。
    FAILED ICE候选测试了所有远程候选没有发现匹配的候选。也可能有些候选中发现了一些可用连接。
    DISCONNECTED 测试不再活跃,这可能是一个暂时的状态,可以自我恢复。
    CLOSED ICE代理关闭,不再应答任何请求。
  2. onAddTrack: 远端媒体流回调

    /**
         * UNIFIED_PLAN使用该回调获取远端媒体流
         *
         * @param track 媒体对象基类。可以是VideoTrack和AudioTrack
         */
        default void onAddTrack(MediaStreamTrack track, MediaStream[] mediaStreams) {
        }
    
  3. onAddStream: 远端媒体流回调,默认使用UNIFIED_PLAN协议的onAddTrack回调。

    /**
         * PLAN_B模式下会使用改对象封装VideoTrack和AudioTrack
         *
         * @param mediaStream 流媒体对象封装
         */
        default void onAddStream(MediaStream mediaStream) {
        }
    
  • connect: 连接DUIX服务

     /**
         * 连接DUIX服务
         */
    public void connect()
    
  • release: 释放DUIX服务资源

// 断开DUIX服务连接,释放资源。在关闭页面时一定要调用
public void release()
  • sendStop: 主动关闭会话
/**
     * 通知后台主动关闭会话
     */
public void sendStop()
  • answer: 通过问题发起DUIX答疑,会在DUIX回调中的onAnswer接口函数回调数据
/**
* DUIX bot 答疑
* @param question 提交的问题,获取ASR识别返回的内容
*/
public void answer(String question)
  • playAudio: 驱动数字人播放话术
/**
* 客戶端驱动数字人播放话术
* @param wavPath 数字人播放音频路径
* @param text 音频对应的文案s
*/
public void playAudio(String wavPath, String text)
  • setMicrophone: 设置麦克风是否静音,需要在会话连接成功后调用

    /**
    * 设置麦克风是否静音
    * @param enable  true: 传输声音,false:静音
    */
    public boolean setMicrophone(boolean enable)
    

# 调用步骤

  1. 根据实际情况使用DUIXOptions duixOptions = new DUIXOptions(AppID, AppSecret, DIGITAL_ID)构建启动参数.
  2. 使用new DUIXCore(mContext, eglBaseContext, duixOptions, new Callback());创建DUIX服务对象.
  3. 使用mDUIXCore.connect();发起服务连接。
  4. onAddTrack回调中获取媒体流并绑定到renderer中
  5. 在onMessage中处理业务消息,比如SessionState、render等类型的消息。
  6. 使用answer将识别的内容做答疑操作。
  7. 使用playAudio驱动数字人播放话术
  8. 使用sendClose通知后台关闭会话
  9. 使用release释放本地资源

# Proguard配置

如果代码使用了混淆,请在proguard-rules.pro中配置:

-keep class org.webrtc.**{ *; }

# 代码示例

  • DUIX参数设置及连接

    DUIXOptions duixOptions = new DUIXOptions(AppID, AppSecret, DIGITAL_ID);
    	// .setHttpHost("https://prevshow.guiji.ai/");  // 设置后台对接的环境地址。
    	// .setHttpTimeout(30000)			// 设置http请求超时时间,单位毫秒
    	// .setMqttTimeout(30)			// 设置mqtt连接的超时时间,单位秒
    mDUIXCore = new DUIXCore(mContext, eglBaseContext, duixOptions, new Callback());
    mDUIXCore.connect(mContext, imOptions);           // 启动连接
    
  • 会话展示及业务逻辑处理

    // 在onMessage回调中处理后台返回的控制消息
    @Override
    protected void onMessage(String topic, String message){
    		try {
                    JSONObject dataObject = new JSONObject(message);
                    String event = dataObject.optString("event", "");
                    if ("SessionState".equals(event)){
                        JSONObject params = dataObject.optJSONObject("params");
                        if (params != null){
                            String state = params.optString("state");
                            Log.e(TAG, "SessionState=========> " + state);
                            if ("Ready".equals(state)){
                                // 视频流可以展示的通知
                                runOnUiThread(DisplayActivity.this::dismissLoadingDialog);
                            }
                            // 处理其他会话事件
                        }
                    } else if ("busy".equals(event)){
                        runOnUiThread(() -> {
                            showErrorDialog("资源不足");
                        });
                    }
    
                } catch (JSONException e) {
                    e.printStackTrace();
                    Log.e(TAG, "onMessage json解析异常");
                }
    }
    
  • 发送消息与后台交互

    // 在onAsr回调中获取识别文案。通过answer发起后台模型答疑
    @Override
    public void onAsr(String content) {
        // 根据识别文本获取答案
        if (mDUIXCore != null) {
            mDUIXCore.answer(content);
        }
    }
    
    // 在onAnswer回调中获取答疑结果,将音频播放地址和文案发送后台驱动数字人说话。
    @Override
    public void onAnswer(String filePath, String content) {
        if (mDUIXCore != null) {
            mDUIXCore.playAudio(filePath, content);
        }
    }
    
    • 视频预览控件初始化

      // rtc模块和控件共享该上下文
      eglBaseContext = EglBase.create().getEglBaseContext();
      remote_renderer.init(eglBaseContext, null);
      remote_renderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
      remote_renderer.setMirror(false);						// 使用该标记对画面镜像处理
      remote_renderer.setEnableHardwareScaler(false /* enabled */);
      
  • 视频信号对接

    @Override
    public void onAddTrack(MediaStreamTrack track, MediaStream[] mediaStreams) {
        if (track instanceof VideoTrack) 
           // 展示远端信号
           VideoTrack remoteVideoTrack = (VideoTrack) track;
           remoteVideoTrack.addSink(fullscreen_renderer);
        }
    }
    
    
  • 关闭会话并释放资源

    // 通知远端关闭会话
    if (mDUIXCore != null) {
    	mDUIXCore.sendStop();
    	finish();
    }
    
    // 释放资源
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mDUIXCore != null) {
            mDUIXCore.release();
            mDUIXCore = null;
        }
        if (remote_renderer != null) {
        	remote_renderer.release();
        }
    }
    

# 注意事项

  1. rtc模块需要使用麦克风,需要动态申请权限。
  2. 初始化DUIXOptions中的DIGITAL_ID可以从会话管理中的复制会话ID中获取。
  3. mqtt模块使用的localbroadcastmanager支持的最高sdk编译版本是30,导致集成项目需要targetSdkVersion<=30