更新时间:2023年10月08日14:04:13
本文介绍了如何使用硅基数字人服务提供的DUIX Android SDK,包括下载安装、关键接口及代码示例。
duix_sdk_release_2.0.2.aar
工程下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" />
/**
* @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接口包含如下回调:
```
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 | 资源总并发不足 |
```
/**
* @param msg 错误信息描述
*/
default void onIMConnectError(String msg)
```
```
/**
* 会话已销毁
* @param cause 销毁原因
*/
void onRender(String cause)
```
```
/**
* 返回后台控制消息。可以根据不同的时间类型做相应的业务处理
*
* @param topic 消息返回的订阅地址
* @param message 数据完整结构
*/
void onMessage(String topic, String message);
```
```
/**
* 识别结果返回
*
* @param content ASR识别的内容
*/
void onAsr(String content)
```
```
/**
* 使用answer发起的答疑内容回调
* @param filePath 音频文件的URL地址
* @param content 答疑的文案
*/
void onAnswer(String filePath, String content);
```
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代理关闭,不再应答任何请求。 |
onAddTrack: 远端媒体流回调
/**
* UNIFIED_PLAN使用该回调获取远端媒体流
*
* @param track 媒体对象基类。可以是VideoTrack和AudioTrack
*/
default void onAddTrack(MediaStreamTrack track, MediaStream[] mediaStreams) {
}
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()
/**
* 通知后台主动关闭会话
*/
public void sendStop()
/**
* DUIX bot 答疑
* @param question 提交的问题,获取ASR识别返回的内容
*/
public void answer(String question)
/**
* 客戶端驱动数字人播放话术
* @param wavPath 数字人播放音频路径
* @param text 音频对应的文案s
*/
public void playAudio(String wavPath, String text)
setMicrophone: 设置麦克风是否静音,需要在会话连接成功后调用
/**
* 设置麦克风是否静音
* @param enable true: 传输声音,false:静音
*/
public boolean setMicrophone(boolean enable)
如果代码使用了混淆,请在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();
}
}