虚拟形象Android开发文档
虚拟形象Android开发文档
简介
百度虚拟形象sdk是基于图像识别、语音识别与合成、人像建模等人工智能技术,以电子屏、VR等设备为载体,实现人机交互的虚拟人,面向餐饮、金融、广电、教育、营销等行业,提供全新智能对客服务,降低人力成本,提升服务质量和效率。同时基于百度AR技术,塑造卡通形象,打造更有亲和力的虚拟形象。
开发指南
概述
DigitalHuman SDK 以DigitalHuman API的形式提供给开发者,包含JAVA,Objective-C两种语言形式。
功能
DigitalHuman SDK 实现功能包括:
-
播报
输入文本进行语音播报
-
对话
输入文本或录音进行对话聊天
-
模型渲染
初始化完成即可将虚拟形象渲染在页面上
-
切换配饰
切换虚拟形象的配饰,包括但不限于服装和发型
-
动作和表情触发
输入不同参数来触发不同的动作和表情
-
视角变换
切换虚拟形象全身和半身
开发配置
- Android Studio 3.1及以上
- android系统4.4及以上
SDK Java API 快速入门
概述
demo文件夹内是使用DigitalHuman SDK相关api实现虚拟形象功能的demo工程源码。
文件列表
-
digitalhuman.aar
虚拟形象相关服务集合的SDK文件,与digitalhumansdk.jar、so可以二选一使用。点击下载
-
digitalhumansdk.jar、so
虚拟形象相关服务的SDK⽂件,与digitalhuman.aar可以二选一使用。点击下载
-
dumix.license
使用虚拟形象相关服务的鉴权文件,由开发者提供应用包名后生成;初始SDK中不包含license。
-
demo
sdk相关接口接入示例,点击下载
-
DigitalHumandemo.apk
虚拟形象样板体验demo,点击下载
工程设置
-
资源配置
sdk有两种接入方式,推荐使用jar方式引入(不可同时使用两种方式!!!)
(1)、aar方式接入。将digitalhuman.arr和dumix.license分别放入libs和assets工程目录下
(2)、jar和so接入。将digitalhumansdk.jar和相关so文件复制到libs目录
-
添加依赖
在app目录下的build.gradle中添加依赖
android{ ... // 其它配置 defaultConfig{ ... minSdkVersion 19 targetSdkVersion 28 } repositories { flatDir { dirs 'libs' } } // jar方式引入需添加如下代码 sourceSets { main { jniLibs.srcDirs = ['libs'] } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // aar形式引入SDK // implementation(name: 'digitalhumansdk', ext: 'aar') }
-
添加权限
配置清单文件 app/src/main/AndroidManifest.xml中添加以下权限
<!--访问网络权限--> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!--文件读写权限--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!--录音权限--> <uses-permission android:name="android.permission.RECORD_AUDIO" />
-
混淆忽略配置
### 使用digitalhumansdk时添加如下忽略 -keep class com.baidu.ar.** {*;} -keep interface com.baidu.ar.** { *; }
接入示例
-
权限申请
private static final String[] ALL_PERMISSIONS = new String[]{ Manifest.permission.INTERNET, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}; private void checkPermissions() { // 6.0以下版本直接同意使用权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!hasNecessaryPermission()) { requestPermissions(ALL_PERMISSIONS, REQUEST_CODE_ASK_ALL_PERMISSIONS); } else { initDigitalHuman(); // 权限申请成功后才能进行sdk初始化 } } else { initDigitalHuman(); } } /** * 检查权限 * * @return */ private boolean hasNecessaryPermission() { List<String> permissionsList = new ArrayList(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { for (String permission : ALL_PERMISSIONS) { if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { permissionsList.add(permission); } } } return permissionsList.size() == 0; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (hasNecessaryPermission()) { initDigitalHuman(); } }
-
sdk初始化
digitalHuman = DigitalHuman .getInstance() .setContext(ARActivity.this) // 设置上下文 .setResolution(720, 1280) // 设置渲染分辨率 // .setCasePath(casePath) // 可使用本地模型资源,默认下发 .setDumixListener(new DumixListener() { // 设置sdk中系列状态监听 @Override public void onStateChanged(final int type, final String msg) { runOnUiThread(new Runnable() { @Override public void run() { // 状态监听结果处理 stateChangeResult(type, msg); } }); } // 交互回调 @Override public void onDrawViewTouch(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; } } }) .initDumix(mDrawView); // 初始化并启动虚拟形象sdk
-
文本播报
digitalHuman.sendReadingMessage(sendMsg, new DigitalHuman.SubtitleCallback() { @Override public void subtitle(final String text) { // 虚拟形象播报的文本,在播报开始时返回 } });
-
文本输入对话
digitalHuman.sendChatMessage(sendMsg, new DigitalHuman.SubtitleCallback() { @Override public void subtitle(String text) { // 虚拟形象播报的文本,在播报开始时返回 } });
-
语音输入对话
// 开始录音 digitalHuman.startRecordAudio(new DigitalHuman.AudioProcessListener() { @Override public void audioProcessResult(final TextResponseBodyWrap responseBody) { runOnUiThread(new Runnable() { @Override public void run() { if (responseBody.isCompleted()) { mHandler.postDelayed(new Runnable() { @Override public void run() { // UI修改,停止录音,发送语音解析结果 mDigitalHuman.finishRecordAudio(); sendMsg(responseBody.getContent();); } }, 500); } } }); } }); // 结束录音。主动调用完成录音或者语句间隔超过2秒会自动结束录音 digitaHuman.finishRecordAudio(); /** * 发送文本开始对话 * sendMsg 录音语音解析结果 */ private void sendMsg(String sendMsg){ digitalHuman.sendChatMessage(sendMsg, new DigitalHuman.SubtitleCallback() { @Override public void subtitle(String text) { // 虚拟形象播报的文本,在播报开始时返回 } }); }
-
切换配饰
以切换服装为例,实际调用changeRenderParameter,参数列表见Api说明。
/** * 切换衣服 * * @param clothName */ private void changeClothes(String clothName) { HashMap<String, Object> message = new HashMap<>(); message.put("event_name", "change_outfit_operation"); HashMap<String, Object> dataWrapper = new HashMap<>(); dataWrapper.put("node", "avatar"); dataWrapper.put("outfit", clothName); message.put("event_data", dataWrapper); digitalHuman.changeRenderParameter(message); }
-
打断
该方法打断当前的数字人播报,动作和口型回归初始态,被打断的语句信息丢失。
// 打断当前的播报或者对话 private void interruptReading() { if (digitalHuman != null) { digitalHuman.breakAudio(); } }
-
暂停
系统暂停后,相关的渲染和交互都会停止,推荐在activity的onPause中调用。
if (digitalHuman != null) { digitalHuman.pause(); }
-
继续
继续之前暂停的渲染和交互,推荐在activity的onResume中调用
if (digitalHuman != null) { digitalHuman.resume(); }
-
退出
退出并销毁虚拟形象,推荐在activity的onDestroy中调用
if (digitalHuman != null) { digitalHuman.destroy(); }
-
通过pcm音频数据获取表情数据
// 发送开始标志 mDigitalHuman.queryBlendShape(BlendShapeRequest.begin()); while ((len = inputStream.read(buffer)) != -1) { // 发送 pcm 数据,需 Base64 转码 mDigitalHuman.queryBlendShape(BlendShapeRequest.audio(Base64.encodeToString(buffer, 0, len, Base64.NO_WRAP))); } // 发送结束标志,需发送文本和音频信息:采样率、采样位宽、声道数 mDigitalHuman.queryBlendShape(BlendShapeRequest.end(text, sampleRate, bitsPerSample, numChannel)); // 注册 BlendShape 数据结果回调 mDigitalHuman.setQueryBlendShapeListener(new IQueryBlendShapeCallback() { @Override public void onBlendShapeResponse(BlendShapeResponse blendShapeResponse) { // 获取 BlendShape 数据 List<HashMap<String, Double>> blendShapeMaps = blendShapeResponse.getBlendShapeMaps(); // 设置表情数据,并开始播放 blendShapePlayer.setDataSource(blendShapeResponse.getBlendShapeMaps()); blendShapePlayer.start(); } }); // 构造表情播放器 blendShapePlayer = new BlendShapePlayer(mDigitalHuman);
-
BlendShape表情播放
// 构造表情播放器 BlendShapePlayer blendShapePlayer = new BlendShapePlayer(digitalHuman); // 设置表情数据 blendShapePlayer.setDataSource(blendShapeMaps); // 开始播放 blendShapePlayer.start(); // 暂停播放 blendShapePlayer.pause(); // 恢复播放 blendShapePlayer.resume(); // 停止播放 blendShapePlayer.stop(); // 设置播放完成监听 blendShapePlayer.setOnCompletionListener(new BlendShapePlayer.OnCompletionListener() { @Override public void onCompletion() { } });
- 发送对话消息 For Unit
// 构造Unit消息参数
ReqExtra reqExtra = new ReqExtra(); reqExtra.setAccessToken("24.0ed6db25a484b872536031ae17403e10.2592000.1614333577.282335-23178123");
reqExtra.setLogId("UNITTEST_10000");
reqExtra.setServiceId("S42464");
reqExtra.setUserId("88888");
reqExtra.setSkillIds(""); 多个id用,分隔
reqExtra.setSessionId(mUnitSession); // session信息,通过14. Unit Session信息回调获取
digitalHuman.sendChatMessageForUnit(sendMsg,reqExtra, new DigitalHuman.SubtitleCallback() {
@Override
public void subtitle(String text) {
}
});
Unit消息参数信息,请通过智能对话定制与服务平台 UNIT获取
-
Unit Session信息回调
digitalHuman.setUnitSessionListener(new IUnitSessionListener() { @Override public void onUnitSessionResult(String sessionStr) { mUnitSession = sessionStr; } });
API参考
DigitalHuman.java
public static DigitalHuman getInstance();
获取DigitalHuman的实例化对象
public DigitalHuman setContext(Context context);
设置上下文
public DigitalHuman setRatio(int width, int height);
设置设置渲染分辨率
public DigitalHuman setDumixListener(DumixListener dumixListener);
public interface DumixListener {
void stateChange(int type, String msg);
void onDrawViewTouch(MotionEvent event);
}
设置状态监听。type取值参考DumixConstance.java
public DigitalHuman setCasePath(String casePath);
设置模型资源路径。需要使用本地的模型资源时调用该接口(资源模型默认下发)
public DigitalHuman setUrl(String url)
设置服务地址(如果是自己搭建的服务,可使用该设置)
public DigitalHuman initDumix(TextureView mSurfaceView);
初始化并启动SDK
public void sendReadingMessage(String textMsg, SubtitleCallback subtitleCallback);
发送文本进行播报
public void sendChatMessage(String textMsg, SubtitleCallback subtitleCallback);
发送文本进行聊天
/**
* 开始录音
* @param processListener 录音音频解析成文本的监听
*/
public void startRecordAudio(AudioProcessListener processListener);
/**
* 主动完成录音
*/
public void finishRecordAudio();
语音对话,录音解析完成后需要调用文本对话服务
public void interruptReading();
播报打断
/**
* 修改渲染参数
*
* @param params 参数
*/
public void changeRenderParameter(HashMap<String, Object> params);
切换配饰+动作+表情触发+视角变换
参数构造请参考changeRenderParameter参数说明
DumixConstance.java
DumixState public Eunm:
状态名 | 状态值 | 含义 |
---|---|---|
LOAD_CASE_COMPLETED | 0 | 资源加载成功 |
REQUEST_SUCCESS | 2 | 播报请求成功 |
REQUEST_FAIL | 3 | 播报请求失败 |
START_PLAY_RADIO | 4 | 开始播报 |
RADIO_PLAY_COMPLETED | 5 | 播报完毕 |
SOCKET_CONNECT | 6 | 流式服务连接成功 |
SOCKET_FAIL | 7 | 流式服务连接失败 |
changeRenderParameter参数说明
投建的HashMap需要包含event_name和event_data字段,字段里内容如下,event_data内部为多级HashMap:
含义 | event_name | event_data | 备注 | |
---|---|---|---|---|
换装(裙装/西装) | change_outfit_operation | node | avatar | 节点:预留字段 |
outfit | dress/suit | 服装:裙装/西装 | ||
换发型(短发/马尾辫) | change_hair_operation | node | avatar | 节点:预留字段 |
Hair | short/ponytail | 发型:短发/马尾 | ||
动作触发 | animation_operation | node | avatar | 节点:预留字段 |
repeat_count | n(整数) | 动画循环次数:0代表无穷 | ||
chip | idle(聆听态)/right_hand(右伸手)/front_hand(前伸手)/wave(招手)/emphasize(强调) | 动画短名称:聆听态/右伸手/前伸手/招手/强调 | ||
operation | play/stop | 操作:播放/停止 | ||
相机位置切换 | camera_position_operation | position | ArrayList |
全身相机姿态参数:0,8,47半身相机姿态参数:0,10.5,25调用例子中见调用示例,三个Float代表相机位置的三维坐标,其中第二个(y坐标)控制相机高低,第三个(z坐标)控制远近,如果效果不佳,可以根据这个参数意义微调。 |
胸章Logo切换 | change_logo_operation | node | avatar | |
logo | none/baidu/baidu_paw/net/net_icon | 对应胸牌:无胸牌、百度胸牌、百度熊掌、智能云胸牌、智能云图标调用例子中见调用示例 |