logo
当前页

实现数字人视频通话

本文档用于说明如何快速集成客户端 SDK (ZEGO Express SDK 和数字人 SDK)并实现与智能体进行视频互动。

数字人介绍

仅需一张上半身的真人或二次元的照片或图片,即可获得1080P、口型准确、形象逼真的数字人。通过搭配AI Agent产品使用,即可快速实现整体2s内的与AI数字人的视频互动聊天,适用于数字人 1V1 互动视频、数字人客服、数字人直播等多种场景。

  • 更自然的驱动效果:支持轻微的身体动作,面部表情自然不变形,相较语音通话互动更真实沉浸;
  • 多语种口型准确:唇形准确自然,尤其针对中英文效果更佳;
  • 互动超低延迟:数字人驱动延迟 < 500ms,结合 AI Agent 互动延迟 < 2s;
  • 更高清晰度:真实 1080P 效果,比传统图片数字人清晰度提高20%+

可跳转至体验 Demo下载体验。

前提条件

  • 已在 ZEGO 控制台 创建项目,并申请有效的 AppID 和 AppSign,详情请参考 控制台 - 项目信息
  • 已联系 ZEGO 技术支持开通数字人 PaaS 服务和相关接口的权限。
  • 已联系 ZEGO 技术支持创建数字人。
  • 已联系 ZEGO 技术支持获取支持 AI 回声消除的 ZEGO Express SDK,并集成到您的项目中。

示例代码

以下是客户端示例代码,,您可以参考示例代码来实现自己的业务逻辑。

以下视频演示了如何跑通服务端和客户端(Web)示例代码并跟数字人智能体进行视频互动。

整体业务流程

  1. 服务端,参考业务后台快速开始文档跑通业务后台示例代码,部署好业务后台
    • 接入实时互动 AI Agent API 管理智能体。
  1. 客户端,跑通示例代码
    • 通过业务后台创建和管理智能体。
    • 集成 ZEGO Express SDK 和数字人 SDK 完成实时通信。

完成以上两个步骤后即可实现将智能体加入房间并与真实用户进行实时互动。

sequenceDiagram participant 客户端 participant 业务后台 participant AI Agent 后台 业务后台->>业务后台: 注册智能体 业务后台->>AI Agent 后台: 注册智能体 AI Agent 后台-->>业务后台: 客户端->>业务后台: 通知业务后台开始通话 业务后台->>AI Agent 后台: 创建数字人智能体实例 AI Agent 后台->>AI Agent 后台: 数字人智能体登录房间并推流、拉用户流 AI Agent 后台-->>业务后台: 数字人配置 业务后台-->>客户端: 数字人配置 客户端->>客户端: 初始化数字人 SDK 并设置数字人配置 客户端->>业务后台: 请求 Token 业务后台-->>客户端: Token 客户端->>客户端: 初始化 ZEGO Express SDK 后登录房间并推流 客户端->>客户端: 用户拉智能体流并且把帧数据和 SEI 数据传给数字人 SDK 客户端->>业务后台: 通知业务后台停止通话 业务后台->>AI Agent 后台: 删除智能体实例 AI Agent 后台-->>业务后台: 业务后台-->>客户端: 客户端->>客户端: 用户停止推流并退出房间 客户端->>客户端: 用户退出数字人 SDK

核心能力实现

集成 ZEGO Express SDK

请参考 集成 SDK > 2.2 > 方式2 手动集成 SDK。集成 SDK 后按以下步骤初始化 ZegoExpressEngine。

1
添加权限声明

进入 "app/src/main" 目录,打开 "AndroidManifest.xml" 文件,添加权限。

AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> 
1
Copied!
2
运行时申请录音权限
Untitled
private final ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(
    new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>() {
        @Override
        public void onActivityResult(Boolean isGranted) {
            if (isGranted) {
                // 同意权限
            }
        }
    });
//发起请求
requestPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO);
1
Copied!
3
创建并初始化 ZegoExpressEngine
Untitled
ZegoEngineProfile zegoEngineProfile = new ZegoEngineProfile();
zegoEngineProfile.appID = ; // 从即构控制台获取的 appId
zegoEngineProfile.scenario = ZegoScenario.HIGH_QUALITY_CHATROOM;
zegoEngineProfile.application = getApplication();
ZegoExpressEngine.createEngine(zegoEngineProfile, null);
1
Copied!

集成数字人 SDK

数字人 SDK 已经发布在 maven 仓库,可参考以下代码集成 SDK 进项目。

1
添加 `maven` 配置

根据您的 Android Gradle 插件版本,选择对应的实现步骤。

2
修改您的 app 级别的 `build.gradle` 文件
Untitled
dependencies {
    ...
    // 数字人 SDK 依赖
    implementation 'im.zego:digitalmobile:+'   

    // 数字人 SDK 用到的第三方库依赖
    implementation 'com.squareup.okhttp3:okhttp:4.9.3'
    implementation "com.google.code.gson:gson:2.9.1"
    implementation 'com.liulishuo.okdownload:okdownload:1.0.7'
    implementation 'com.liulishuo.okdownload:sqlite:1.0.7'
    implementation 'com.liulishuo.okdownload:okhttp:1.0.7'
}
1
Copied!

通知业务后台开始通话

可在客户端真实用户进入房间后立即通知业务后台开始通话,异步调用可加降低通话接通时间。业务后台收到开始通话通知后,使用与客户端相同的 roomID 及关联的 userID 和 streamID 创建数字人智能体实例,这样数字人智能体就能与真实用户在同一个房间内进行相互推拉流实现视频互动。

请求业务后台的时候需要带上数字人参数,参数包括 digital_human_idconfig_id

  • digital_human_id 是数字人 ID,请联系 ZEGO 技术支持获取。
  • config_id 是数字人的配置 ID,不同平台使用不同的数字人配置, 数字人服务会根据 config_id 优化不同平台上的性能和效果,Android/iOS 请填写 mobile,Web 请填写 web

初始化数字人 SDK 实例

需要先在 android 布局文件中添加数字人预览 view,数字人画面会渲染到该视图上。

Untitled
<im.zego.digitalmobile.ZegoPreviewView
    android:id="@+id/preview_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
1
Copied!
Untitled
String digitalHumanConfig = xxx; // 数字人配置,从业务后台创建数字人智能体实例接口返回的 DigitalHumanConfig 中获取
IZegoDigitalMobile digitalMobile = ZegoDigitalMobileFactory.create(this);   // 创建数字人 SDK 实例,可以创建多个实例显示不同的数字人
digitalMobile.start(digitalHumanConfig, null);   // 初始化数字人 SDK 实例,传入数字人配置
digitalMobile.attach(previewView);   // 绑定上面创建的预览 view,数字人会渲染到该 view 上
1
Copied!

将 Express 数据同步给数字人 SDK

数字人 SDK 渲染画面时依赖 ZEGO Express SDK 的视频帧和 SEI 数据,所以需要开启 ZEGO Express SDK 的自定义视频渲染能力并且将 ZEGO Express SDK 的视频帧和 SEI 数据同步给数字人 SDK。

注意
  • 开启 ZEGO Express SDK 自定义视频渲染能力需要在调用 ZEGO Express SDK startPublishingStreamstartPlayingStream 接口之前设置,否则无效。
Express
// 开启 Express 自定义渲染
ZegoCustomVideoRenderConfig renderConfig = new ZegoCustomVideoRenderConfig();
renderConfig.bufferType = ZegoVideoBufferType.RAW_DATA;
renderConfig.frameFormatSeries = ZegoVideoFrameFormatSeries.RGB;
renderConfig.enableEngineRender = false;
ZegoExpressEngine.getEngine().enableCustomVideoRender(true, renderConfig);
// 监听视频帧回调
ZegoExpressEngine.getEngine().setCustomVideoRenderHandler(new IZegoCustomVideoRenderHandler() {
    @Override
    public void onRemoteVideoFrameRawData(ByteBuffer[] data, int[] dataLength, ZegoVideoFrameParam param,
                                            String streamID) {
        IZegoDigitalMobile.ZegoVideoFrameParam digitalParam = new IZegoDigitalMobile.ZegoVideoFrameParam();
        digitalParam.format = IZegoDigitalMobile.ZegoVideoFrameFormat.getZegoVideoFrameFormat(param.format.value());
        digitalParam.height = param.height;
        digitalParam.width = param.width;
        digitalParam.rotation = param.rotation;
        for (int i = 0; i < 4; i++) {
            digitalParam.strides[i] = param.strides[i];
        }
        // 把 Express 视频帧数据传给数字人 SDK
        digitalMobile.onRemoteVideoFrameRawData(data, dataLength, digitalParam, streamID);
    }
});

// 监听 Express SEI 数据
ZegoExpressEngine.getEngine().setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPlayerSyncRecvSEI(String streamID, byte[] data) {
        // 把 Express SEI 数据传给数字人 SDK
        digitalMobile.onPlayerSyncRecvSEI(streamID, data);
    }
});
1
Copied!

用户进入房间并推流

真实用户登录房间后推流。

说明

在此场景下需要开启 AI 回声消除以获得更好的效果。

登录用的 token 需要从您的业务后台获取,请参考完整示例代码。

说明

请确保 roomID、userID、streamID 在一个 ZEGO APPID 下是唯一的。

  • roomID: 由用户自己定义生成规则,会用来登录 Express SDK 的房间。仅支持数字,英文字符 和 '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '`', ';', '’', ',', '.', '<', '>', ''。如果需要与 Web SDK 互通,请不要使用 '%'。
  • userID: 长度不超过32字节。仅支持数字,英文字符 和 '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '`', ';', '’', ',', '.', '<', '>', ''。如果需要与 Web SDK 互通,请不要使用 '%'。
  • streamID: 长度不超过256字节。仅支持数字,英文字符 和 '-', '_'。
客户端登录房间并推流
private void loginRoom(String userId, String userName, String userName, String token,
    IZegoRoomLoginCallback callback) {
    ZegoEngineConfig config = new ZegoEngineConfig();
    HashMap<String, String> advanceConfig = new HashMap<String, String>();
    advanceConfig.put("set_audio_volume_ducking_mode", "1");
    advanceConfig.put("enable_rnd_volume_adaptive", "true");
    advanceConfig.put("sideinfo_callback_version", "3");                
    advanceConfig.put("sideinfo_bound_to_video_decoder", "true");       
    config.advancedConfig = advanceConfig;
    ZegoExpressEngine.setEngineConfig(config);
    ZegoExpressEngine.getEngine().setRoomScenario(ZegoScenario.HIGH_QUALITY_CHATROOM);
    ZegoExpressEngine.getEngine().setAudioDeviceMode(ZegoAudioDeviceMode.GENERAL);

    ZegoExpressEngine.getEngine().enableAEC(true);
    //请注意:开启 AI 回声消除需要联系 ZEGO 技术支持获取对应版本的 ZEGOExpress SDK
    ZegoExpressEngine.getEngine().setAECMode(ZegoAECMode.AI_AGGRESSIVE2);
    ZegoExpressEngine.getEngine().enableAGC(true);
    ZegoExpressEngine.getEngine().enableANS(true);
    ZegoExpressEngine.getEngine().setANSMode(ZegoANSMode.MEDIUM);

    ZegoRoomConfig roomConfig = new ZegoRoomConfig();
    roomConfig.isUserStatusNotify = true;
    roomConfig.token = token;  // 需要 token 鉴权,从您的业务后台获取,生成方式请参考ZEGO官网文档

    String roomId ;   // 自定义用来登录的房间的Id,格式请参考说明
    String userSteamID // 自定义用于推流的流Id,格式请参考说明
    ZegoExpressEngine.getEngine()
        .loginRoom(roomId, new ZegoUser(userId, userName), roomConfig, (errorCode, extendedData) -> {
            Timber.d(
                "loginRoom() called with: errorCode = [" + errorCode + "], extendedData = [" + extendedData + "]");
            if (errorCode == 0) {
                // 登录成功以后进行推流
                ZegoExpressEngine.getEngine().startPublishingStream(userSteamID);
                // 设置麦克风静音状态,false 表示不静音,true 表示静音
                ZegoExpressEngine.getEngine().muteMicrophone(false);
            }
            if (callback != null) {
                callback.onRoomLoginResult(errorCode, extendedData);
            }

        });
}
1
Copied!

拉智能体流

默认只有一个真实用户及智能体在同一个房间内,所以拉流时默认新增的就是智能体流。

客户端拉智能体的流
// 监听流回调和拉流
ZegoExpressEngine.getEngine().setEventHandler(new IZegoEventHandler() {
    @Override
    // 房间内其他用户推流/停止推流时,我们会在这里收到相应用户的音视频流增减的通知
    public void onRoomStreamUpdate(String roomID, ZegoUpdateType updateType, ArrayList<ZegoStream> streamList, JSONObject extendedData) {
        super.onRoomStreamUpdate(roomID, updateType, streamList, extendedData);
        //当 updateType 为 ZegoUpdateType.ADD 时,代表有音视频流新增,此时我们可以调用 startPlayingStream 接口拉取该音视频流
        if (updateType == ZegoUpdateType.ADD) {
            ZegoStream stream = streamList.get(0);
            // 默认新增是智能体流,直接拉流
            ZegoExpressEngine.getEngine().setPlayStreamBufferIntervalRange(stream.streamID, 100, 2000);  // 设置 buffer 优化体验
            ZegoExpressEngine.getEngine().startPlayingStream(stream.streamID);
        }
    }
});
1
Copied!

恭喜你🎉!完成这一步骤后,您已经可以用语音问智能体任何问题,智能体都会回答您的问题!

退出房间结束通话

客户端调用退出登录接口退出房间,并停止推拉流。同时通知业务后台本次通话结束。业务后台收到结束通话通知后会删除智能体实例,智能体实例会自动退出房间并停止推拉流。最后再调用数字人 SDK 退出接口,这样一次完整的互动就结束了。

Untitled
// 通知业务后台结束通话
private void stop() {
    RequestBody body = RequestBody.create("", MediaType.parse("application/json; charset=utf-8"));
    Request request = new Request.Builder().url(YOUR_SERVER_URL + "/api/stop").post(body).build();

    new OkHttpClient.Builder().build().newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(@NonNull Call call, @NonNull IOException e) {

        }

        @Override
        public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
            if (response.isSuccessful()) {
                // 退出房间
                ZegoExpressEngine.getEngine().logoutRoom();
                // 退出数字人 SDK
                digitalMobile.stop();
            }
        }
    });
}

1
Copied!

以上就是您实现与数字人智能体进行实时互动的完整核心流程。

ZEGO Express SDK 最佳配置实践

为了获得最佳的音频通话体验,建议按照以下最佳实践配置 ZEGO Express SDK。这些配置可以显著提升智能体视频交互的质量。

进房间前设置:

  • 开启传统音频 3A 处理(回声消除AEC、自动增益控制AGC、噪声抑制ANS)
  • 设置房间的使用场景为高品质语聊房场景,SDK 会针对不同的场景采取不同的优化策略
  • 设置音频设备模式为默认模式
  • 开启 AI 回声消除,提高回声消除效果(该功能需要联系 ZEGO 技术支持获取对应版本的 ZEGOExpress SDK)
  • 配置音量闪避,避免声音冲突
  • 启用播放音量自适应,提升用户体验
  • 启用 AI 降噪,设置适当的噪声抑制级别
  • 启用 SEI 和视频帧同步回调,确保数字人能够正确渲染视频帧
Untitled
ZegoEngineConfig config = new ZegoEngineConfig();
HashMap<String, String> advanceConfig = new HashMap<String, String>();
// 配置音量闪避,避免声音冲突
advanceConfig.put("set_audio_volume_ducking_mode", "1");
// 启用播放音量自适应
advanceConfig.put("enable_rnd_volume_adaptive", "true");
// 启用 SEI 和视频帧同步回调,确保数字人能够正确渲染视频帧
advanceConfig.put("sideinfo_callback_version", "3");                
advanceConfig.put("sideinfo_bound_to_video_decoder", "true");
config.advancedConfig = advanceConfig;
ZegoExpressEngine.setEngineConfig(config);
//设置房间的使用场景为高品质语聊房场景
ZegoExpressEngine.getEngine().setRoomScenario(ZegoScenario.HIGH_QUALITY_CHATROOM);
// 设置音频设备模式  默认模式
ZegoExpressEngine.getEngine().setAudioDeviceMode(ZegoAudioDeviceMode.GENERAL);
// 开启传统音频 3A 处理
ZegoExpressEngine.getEngine().enableAEC(true);
ZegoExpressEngine.getEngine().enableAGC(true);
ZegoExpressEngine.getEngine().enableANS(true);
// 开启 AI 回声消除,请注意:开启 AI 回声消除需要联系 ZEGO 技术支持获取对应版本的 ZEGOExpress SDK
ZegoExpressEngine.getEngine().setAECMode(ZegoAECMode.AI_AGGRESSIVE2);
// 开启 AI 降噪,适度的噪声抑制
ZegoExpressEngine.getEngine().setANSMode(ZegoANSMode.MEDIUM);
1
Copied!

拉流前设置:

  • 设置拉流播放缓存自适应调整的区间范围,优化拉流体验
Untitled
ZegoExpressEngine.getEngine().setPlayStreamBufferIntervalRange(stream.streamID, 100, 2000); 
ZegoExpressEngine.getEngine().startPlayingStream(stream.streamID);
1
Copied!

Previous

快速发起语音通话

Next

展示字幕