2D端渲染升级版- Android
一、产品介绍
提供端渲染交互SDK-2D小样本数字人,可以通过文本或PCM音频数据完成对虚拟人实时驱动,若需形象定制,请购买 「形象定制-2D端渲染数字人(升级版)」
1、适用场景特点
部署成本低:支持移动端 APP 快速集成,人像渲染及唇形推理本地化完成,无需依赖云端持续算力支持;
网络依赖小:适配移动网络(4G/5G)与 WiFi 环境,可落地于金融 APP 客服、政务移动端咨询、教育类 APP 伴学等场景;
2、核心功能
公共形象租赁:提供半身、全身的2D小样本数字人形象,覆盖多种场景,支持客户快速集成使用;
专属形象定制:支持定制专属的2D小样本数字人形象;
实时播报渲染:支持数字人实时播报,适配移动端对话问答交互场景;
性能指标:支持安卓大屏rk3588及以上性能设备中使用。
3、SDK下载链接
4、SDK的详细使用
4.1、在初始化页面:
第一次进入demo之后,请填写appId、appKey,点击初始化SDK按钮,再点击模型加载按钮,最后点击SDK授权+启动数字人(SURFACEVIEW) / SDK授权+启动数字人(图片帧) 进入数字人渲染页面。
4.2、进入数字人页面:
流式播报就是可持续的播报,可把新的播报追加到当前播报后边
(1)播报功能的使用:点击持续PCM或持续TXT,其中文本驱动是持续TXT,持续PCM播放的是本地PCM文件。
可以持续播报多条数据,就是把新的播报追加到当前播报后边(在播报期间点击持续PCM或持续TXT就是把新的PCM播报或TXT播报追加在当前播报的后边进行播报了)。
(2)切换形象:如需切换其他形象必须要有一个定制的形象包《具体可参考本文档8.1、定制人像包替换本地数字人像包》(注意:在播报中不可切换形象)。
(3)暂停/继续播报:暂停播报可使正在播报的播报暂停,继续播报可使已经暂停的播报恢复播报。
(4)打断播报:打断播报可使所有的流式播报(包括插入的所有播报)停止,不可恢复。
(5)能力释放:可释放资源并且退出数字人页面。
二、SDK集成
1、支持的系统和硬件版本
请注意:这是对于开发人员来说的,请开发人员按照此要求进行集成
适用于安卓大屏rk3588及以上性能的安卓版卡
| 系统 | 支持Android 9+(API Level 28)及以上 |
|---|---|
| CPU架构 | arm64-v8a |
| 硬件要求 | 性能要求:硬件设备CPU 8核及以上,支持高通骁龙 660+ 、海思麒麟 980+、联发科天玑 800U+ |
| 网络 | 支持WIF及移动网络。如果使用云端TTS(以文本方式驱动数字人),设备带宽(用于数字人的实际带宽)期望10mbps及以上。 |
| 开发IDE | Android Studio Panda 2 2025.3.2 |
| 内存要求 | SDK可用于数字人的内存 >= 1G |
2、SDK集成
引入sdk aar 包:module_core-release.aar,acg_virtual-release.aar
2.1、app目录新建libs目录,放入aar包,在build.gradle中增加配置如下
1 implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
2
3 implementation 'com.google.code.gson:gson:2.8.9'
4 implementation("com.squareup.okhttp3:okhttp:4.11.0")
2.2、android studio 配置
2.2.1、设置-> Build, Execution, Deployment-> Build Tools -> Grale -> Gradle JDK, 设置java 17

2.2.2、工程设置(mac 下,快捷键commond+;)Android Gradle plugin Verison

2.2.3、Android SDK & java 版本设置
以下配置为建议配置,其他版本可能存在问题。

3、系统权限配置以及申请
3.1、权限配置
1<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
2<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
3<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
4<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
5<uses-permission android:name="android.permission.INTERNET" />
6<uses-permission android:name="android.permission.READ_PHONE_STATE" />
7<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
8<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
9<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
3.2、权限申请
请参考demo BaseActivity示例,所列举的权限为最低权限要求,如果缺少某一个,会导致sdk出现异常。
三、SDK调用及API说明
1、Engine相关
1.1、SDK 初始化
1int init(Context context, String appId, String appKey);
参数说明:
| 参数名 | 取值 | 说明 |
|---|---|---|
| context | Context | 非空 |
| appId | 组件平台创建应用的appId | 获取方式请参考文档:https://cloud.baidu.com/doc/AI_DH/s/plyy6xhi0。 |
| appKey | 组件平台创建应用的appKey | - |
返回参数说明:
| code | 说明 |
|---|---|
| 0 | 初始化成功 |
| -1000 | Context为null |
| -1001 | AppId为空 |
| -1002 | AppKey为空 |
| -1003 | 引擎状态错误,重复初始化 |
调用示例
1 int ret = VirtualFactory.getEngine().init(this, appId, appKey);
2 boolean isOk = ret == VirtualCodes.VirtualInitType.INIT_SUCCESS.getValue();
1.2、模型加载
1 boolean downloadFigure(Context context, String figureId, DownloadCallBack callback);
2 boolean downloadFigure(Context context, String figureId, String appId, String appKey, DownloadCallBack callback);
参数说明:
| 参数名 | 取值 | 说明 |
|---|---|---|
| context | 上下文 | |
| figureId | figureId | 人像ID,人像生成平台获取 |
| appId | appId | 人像生成时绑定组件应用ID |
| appKey | appKey | 人像生成时绑定组件应用Key |
| callback | 回调 | onSuccess(int code, int subCode, String msg) 加载成功 |
DownloadCallBack 取值范围
| 取值 | 说明 |
|---|---|
| 0 | 加载成功 |
| -1000 | 参数错误 |
| -1001 | 引擎状态错误,未初始化 |
| -1002 | 后台返回为空 |
| -1003 | 后台状态不成功 |
| -1004 | 下载失败 |
| -1005 | 解析失败 |
| -1006 | model 下载&校验失败 |
| -1007 | model 解压失败 |
调用示例
1VirtualFactory.getEngine().downloadFigure(mContext, figureId, appId, appKey, (code, subCode, msg) -> {
2 runOnUiThread(new Runnable() {
3 @Override
4 public void run() {
5 if (code == 0) {
6
7 } else {
8 Toast.makeText(SplashActivity.this, "人像模型加载失败: " + msg, Toast.LENGTH_SHORT).show();
9 }
10 }
11 });
12 }
13 );
1.3、设置Log打印开关
1 void setLog(VirtualLogType logType);
参数说明
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| logType | VirtualLogType | 默认:INFO |
VirtualLogType
| 取值 | 说明 |
|---|---|
| NONE | 关闭日志 |
| ERROR | err级别日志 |
| WARN | warn级别日志及以下级别日志 |
| DEBUG | debug级别日志及以下级别日志 |
| INFO | info级别日志及以下级别日志 |
| VERBOSE | 全量日志 |
调用示例
1VirtualFactory.getEngine().setLog(VirtualLogType.INFO);
2、授权相关
2.1、SDK授权
数字人能力授权, 成功后可执行数字人相关逻辑
1boolean initLicenseBatch(final Context context, final String licenseKey, final AuthCallBack authCallback);
参数说明
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| context | Context | 上下文 | |
| licenseKey | String | 购买组件后的License | 组件管理-> 选择购买的组件-> 查看详情-> License字段值 |
| authCallback | AuthCallBack | code:0:成功,其他:失败 subCode:后端返回状态码msg: 提示消息 | subCode为后端返回状态码。由于该码可能会有变动,所以不可作为业务逻辑判断使用 |
code取值(目前取值如下,未来会增加):
| 取值 | 说明 |
|---|---|
| 0 | 授权成功 |
| -1000 | 参数错误 |
| -1001 | 引擎状态异常 |
| -1002 | 后台返回为空 |
| -1003 | 后台状态不成功 |
| -1004 | 解析失败 |
| -1005 | 授权异常,一般是网络问题导致 |
调用示例
1VirtualAuth vhAuth = new VirtualAuth()
2vhAuth.initLicenseBatch(mContext, licenseKey, (code, subCode, msg) -> {
3
4 });
2.2、设备换绑
当一个授权码还存在有效期,但是设备坏了的情况下,可以使用这个接口进行授权设备的换绑,购买应用授权方式组件时不可进行换绑。
换绑接口有次数限制,非特殊情况不宜调用
1boolean changeDeviceBind(final Context context, final String licenseKey, final ChangeDevCallBack callBack);
参数说明
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| context | Context | 上下文 | |
| licenseKey | String | 购买组件后的License | 组件管理-> 选择购买的组件-> 查看详情->License字段值 |
| callBack | AuthStateCallBack | code:0:成功,其他:失败subCode:后端返回状态码msg: 提示消息 | subCode为后端返回状态码。由于该码可能会有变动,所以不可作为业务逻辑判断使用 |
code取值
| 取值 | 说明 |
|---|---|
| 0 | 授权成功 |
| -1000 | 参数错误 |
| -1001 | 切换失败,SDK状态异常 |
| -1002 | 切换失败,后台返回为空 |
| -1003 | 切换失败,后台状态不成功 |
| -1004 | 切换失败,解析失败 |
| -1005 | 切换失败,一般是网络问题导致 |
调用示例
1VirtualAuth vhAuth = new VirtualAuth()
2vhAuth.changeDeviceBind(mContext, licenseKey, (code, subCode, msg) -> {
3
4 });
3、数字人形象
数字人是通过Player 对象进行管理, 每次初始化要求为一个Player对象,用于调用数字人相关操作接口,具体接口参考接口文档,数字人显示部分可参考demo MainActivity.java 示例代码
静默态说明: 当开启静默态时(isCloseSilent = false)未进行播报时可以展示形象和背景; 当未开启静默态时(isCloseSilent = true)未播报时不会展示形象和背景,播报时会展示形象和背景,播报完成后会定在最后一帧,如果这时退出后台再次进入则会进入默认态(不会展示形象和背景)
3.1、数字人形象初始化
3.1.1、初始化Player
请注意:初始化Player后,请先设置数字人形象《具体可参考本文档3.1.4、设置数字人形象》。
1public static Player getPlayer(Context context)
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| context | Context | 上下文 | |
| 返回值 | Player | 用于Player 相关接口后续调用 |
调用示例
1Player player = VirtualFactory.getPlayer(mContext);
3.1.2、设置背景图
1boolean setBackground(Bitmap bitmap)
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| bitmap | Bitmap | 背景图片 |
调用示例
1player.setBackground(BitmapFactory.decodeResource(getResources(), R.mipmap.back));
3.1.3、添加player 消息回调
1boolean addCallback(Callback callback);
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| callback | Callback | 设置回调 |
Callback 回调说明
| 参数名 | 取值 | 说明 |
|---|---|---|
| void onVirtualHumanStart() | 数字人播放开始 | |
| void onVirtualHumanFinish() | 数字人播放结束 | |
| void onVirtualHumanData(VirtualHumanDataBean frameBean) | frameBean.getAudioStreams() PCM 音频数据frameBean.getVideoStreams() 数字人形象RGBA 数据 | 数字人音频数据和形象RGBA 数据, 使用前需做判空处理 |
| void onRemainBuffer(int remainBytes, intremainInsertBytes) | remainBytes: 剩余未播放的bufferremainInsertBytes: 剩余未播放插播的buffer数据 | 数字人剩余未播放的buffer长度 |
| void onVirtualHumanPause() | 暂停播放 | |
| void onPcmVolumeError(int code, String message, float vol) | code: -1:扩大失败_message: 错误信息_vol: 1倍-10倍 :当前设置的倍数 | 幅值放大后出现截幅失真会进行触发 |
| void onError(int code, intsubCode, String msg) | code:-1000:tts数据异常 -1001: 播放器状态不合适subCode: 详情参考 7.3错误信息参数说明msg: 错误信息 | 出错消息 |
调用示例
1player.addCallback(new Player.Callback() {
2 @Override
3 public void onVirtualHumanStart() {
4
5 }
6
7 @Override
8 public void onVirtualHumanFinish() {
9
10 }
11
12 @Override
13 public void onVirtualHumanData(VirtualHumanDataBean frameBean) {
14
15 }
16
17 @Override
18 public void onRemainBuffer(int remainBytes, int remainInsertBytes) {
19
20 }
21
22 @Override
23 public void onPcmVolumeError(int code, String message, float vol) {
24
25 }
26
27 @Override
28 public void onVirtualHumanPause() {
29
30 }
31
32 @Override
33 public void onError(int code, int subCode, String msg) {
34
35 }
36 });
3.1.4、设置数字人形象
1void setVirtualModel(Model model, DisplayMode displayMode, ChangeVirtualModelCallback callback)
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| model | Model | visualize:形象ID isCloseAudio:true:关闭声音; false:打开声音(默认)isCloseSilent:true:关闭静默态; false: 打开静默态(默认) | 人像模型 |
| displayMode | DisplayMode | RENDER_ONLY: surface模式 FRAME_ONLY: 图片帧模式 | |
| callback | ChangeVirtualModelCallback | code:0:成功,其他:失败 subCode:code非0时,可能会有非0的取值msg: 提示消息 | code、subCode 参考7.3、错误信息参数说明 |
调用示例
1Model model = new Model(useVirtualId);
2DisplayMode displayMode = new DisplayMode();
3displayMode.setDataMode(DisplayMode.DataMode.RENDER_ONLY); // surface模式
4player.setVirtualModel(model, displayMode, (msgType, msgSubType, msg) -> {
5
6 });
3.2、数字人形象启动
3.2.1、设置显示解码
1boolean setDisplay(Surface surface, int width, int height)
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| surface | Surface | 展示形象的surface | |
| width | int | 展示形象的宽 | |
| height | int | 展示形象的高 |
调用示例
1/**
2 * Surface 模式下
3 */
4 @Override
5 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
6 player.setDisplay(holder.getSurface(), width, height);
7 }
8
9/**
10 * 图片帧 模式下
11 */
12 player.setDisplay(null, 1080, 1920);
3.2.2、数字人形象展示
1void onResume()
调用示例
1@Override
2 protected void onResume() {
3 player.onResume();
4 super.onResume();
5 }
3.3、数字人形象暂停
1void onPause();
调用示例
1@Override
2 protected void onPause() {
3 player.onPause();
4 super.onPause();
5 }
3.4、数字人形象切换
1void changeVirtualModel(Model model, ChangeVirtualModelCallback callback)
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| model | Model | visualize:形象ID | 人像模型 |
| callback | ChangeVirtualModelCallback | code:0:成功,其他:失败subCode:code非0时,可能会有非0的取值msg: 提示消息 | code、subCode 参考7.3、错误信息参数说明 |
调用示例
1Model model = new Model(useVirtualId);
2 player.changeVirtualModel(model, (code, subCode, msg) -> {
3
4 });
3.5、数字人形象释放
1、surface模式下释放接口需在view销毁完成后进行调用; 例如: 有一个surface view, 需要在surface view 执行surfaceDestroyed 回调内进行调用release() 方法
1void release();
调用示例
1/**
2 * 移除surfaceView
3 */
4 ViewGroup parent = (ViewGroup) mSurfaceView.getParent();
5 parent.removeView(mSurfaceView);
6 mSurfaceView.getHolder().getSurface().release();
7 mSurfaceView = null;
8
9 /**
10 * surfaceDestroyed 内执行release方法
11 */
12 @Override
13 public void surfaceDestroyed(SurfaceHolder holder) {
14 player.release();
15 finish();
16 }
4、驱动数字人
需要注意pcm的格式(单声道,16k,16bit);播放buffer最大只支持10分钟。每次调用speakFlowOnPcm时byte[]的长度需大于16000,开启降噪时需保证降噪后的有效音频byte[]长度大于16000
4.1、PCM播报
1boolean speakFlowOnPcm(Context context, byte[] bytes)
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| context | Context | 上下文 | |
| bytes | byte[] | 需要插播的pcm数据 | |
| 返回值 | boolean | true:PCM 数据设置成功false: PCM 数据设置失败 |
调用示例
1byte[] pcmBytes = FileUtils.getByte(pcmPath + "input/1_16.pcm");
2player.speakFlowOnPcm(mContext, pcmBytes);
4.2、流式TXT播报
1PlayTxtResult speakFlowOnSegTxt(Context context, String segTxt, SpeakerConf conf)
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| context | Context | 上下文 | |
| segTxt | String | 需要插播的文本数据 | |
| conf | SpeakerConf | 设置音库, 具体参数请参考 7.2 SpeakerConf 参数 | |
| 返回值 | PlayTxtResult | 该文本的唯一标识和执行结果 |
调用示例
1PlayTxtResult result = player.speakFlowOnSegTxt(mContext, "你好,我是百度数字人。百度(Baidu)是拥有强大互联网基础的领先AI公司。百度愿景是:"+
2 "成为最懂用户,并能帮助人们成长的全球顶级高科技公司。“百度”二字,来自于八百年前南宋词人辛弃疾的一句词:"+
3 "众里寻他千百度。",SpeakerConf.builder().setPerson(Config.sTtsPerson));
4.3、暂停播报
1boolean pausePlay()
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| 返回值 | boolean | true: 暂停播放false:停止异常 | 如果已经停止的状态,也会返回true |
调用示例
1player.pausePlay();
4.4、继续播报
1boolean resumePlay()
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| 返回值 | boolean | true: 暂停播放false:停止异常 | 如果已经停止的状态,也会返回true |
调用示例
1player.resumePlay();
4.5、打断播报
打断当前正在进行播报包含插播数据, 打断后不可恢复
1void interrupt()
调用示例
1player.interrupt()
4.6、是否正在播放中
用于播放状态判断
1boolean isPlaying()
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| 返回值 | boolean | true: 正在播放中false:停止播放 |
调用示例
1player.isPlaying()
4.7、是否在暂停状态
用于播放状态判断
1boolean isPause()
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| 返回值 | boolean | true: 暂停状态false:非暂停状态 |
调用示例
1player.isPause()
4.8、设置播报音量
按需使用,该能力仅用于音频数据本身较小、存在数学放大条件的前提下可用,若音频原始数据幅值已经过大(最大值参考:-32768 ~ 32768 ),请使用硬件方式进行功率放大,代码中该能力不可用; 错误信息参考 3.1.4、添加player消息回调中的onPcmVolumeError回调
1void setPcmVolume(float vol)
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| vol | float | 默认值为1,范围[1~10] |
调用示例
1player.setPcmVolume(1);
5、工具类
5.1、SpeakerConf 参数
需要注意的是,需要和百度方确认,per取值的范围
| 参数名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| spd | int | 语速,取值0-15,默认为5中语速 | 语速 |
| pit | int | 音调,取值0-15,默认为5中音调, 克隆音色暂不生效 | 音调 |
| vol | int | 音量,取值0-15,默认为5中音量,克隆音色暂不生效 | 音量 |
| aue | int | 6:wav(内容同pcm-16k)默认 | 音频格式 |
| per | int | 默认:度小希=5116更多音色请参考:https://cloud.baidu.com/doc/AI_DH/s/Slywt3fxy | 音色 |
5.2、错误信息参数说明
| code | subCode | 参数含义 |
|---|---|---|
| 0 | 0 | 成功 |
| -1000 | -2000 | 参数错误 |
| -2006 | 获取tts异常 | |
| -2007 | tts 返回null | |
| -1001 | -2001 | 重复设置模型 |
| -2003 | 调用顺序异常 | |
| -2005 | 正在播放中 | |
| -2009 | 没有播报 | |
| -1002 | -2002 | 形象包不存在 |
| -2008 | 缓存已满 | |
| -1003 | -2008 | 插播缓存已满 |
| -1004 | -2004 | 数字人切换频繁 |
6、常见问题
6.1、定制人像包替换本地数字人像包
评价此篇文章
