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 > 方式三 手动集成 SDK。集成 SDK 后按以下步骤初始化 ZegoExpressEngine。

1
在 Info.plist 文件声明必要的权限
Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    ...
    <key>UIBackgroundModes</key>
    <array>
        <string>audio</string>
    </array>
    <key>NSMicrophoneUsageDescription</key>
    <string>需要访问麦克风以进行语音聊天</string>
</dict>
</plist>
1
Copied!
2
运行时申请录音权限
Untitled
- (void)requestAudioPermission:(void(^)(BOOL granted))completion {
    /// 需要在项目的 Info.plist 文件中添加麦克风权限的使用说明
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    [audioSession requestRecordPermission:^(BOOL granted) {
        dispatch_async(dispatch_get_main_queue(), ^{
            completion(granted);
        });
    }];
}
1
Copied!
3
创建并初始化 ZegoExpressEngine
Untitled
-(void)initZegoExpressEngine{
    ZegoEngineProfile* profile = [[ZegoEngineProfile alloc]init];
    profile.appID = kZegoPassAppId;
    profile.scenario = ZegoScenarioHighQualityChatroom; //设置该场景可以避免申请相机权限,接入方应按自己的业务场景设置具体值
    
    [ZegoExpressEngine createEngineWithProfile:profile eventHandler:self];
}
1
Copied!

集成数字人 SDK

1
下载最新版本的SDK

请下载最新版本的 SDK

2
解压SDK

将 SDK 包解压至项目目录下,例如 “libs” 文件夹下。

20250626-153505.jpeg
3

选择 “TARGETS > General > Frameworks,Libraries,and Embedded Content” 菜单,添加 ZegoDigitalMobile.xcframework”,将 “Embed” 设置为 “Embed & Sign”。

20250626-153759.jpeg

通知业务后台开始通话

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

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

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

初始化数字人 SDK 实例

需要先在 View 中添加数字人预览 view,数字人会渲染到该视图上。

1
声明数字人实例和View
Untitled
#import <ZegoDigitalMobile/ZegoDigitalMobile.h>

// 数字人 SDK 实例,可以创建多个实例显示不同的数字人
@property (nonatomic, strong) id<IZegoDigitalMobile> digitalMobile;
// 数字人预览 view,数字人会渲染到该 view 上
@property (nonatomic, strong) ZegoPreviewView *previewView;
1
Copied!
2
创建并添加previewView
Untitled
- (void)setupPreviewView {
    self.previewView = [[ZegoPreviewView alloc] init];
    self.previewView.backgroundColor = [UIColor whiteColor];

    [self.view addSubview:self.previewView];
    [self.previewView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view);
    }];
}
1
Copied!
3
初始化数字人 SDK 实例和绑定渲染View
Untitled
// 创建数字人 SDK 实例
self.digitalMobile = [ZegoDigitalMobileFactory create];
// 数字人配置,从业务后台创建数字人智能体实例接口返回的 DigitalHumanConfig 中获取
NSString *digitalHumanEncodeConfig = @"";
// 初始化数字人 SDK 实例,传入数字人配置
[self.digitalMobile start:digitalHumanEncodeConfig delegate:self];
// 绑定上面创建的预览 view,数字人会渲染到该 view 上
[self.digitalMobile attach:self.previewView];
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
- (BOOL)enableCustomVideoRender {
    // 自定义渲染
    ZegoCustomVideoRenderConfig *renderConfig =
    [[ZegoCustomVideoRenderConfig alloc] init];
    // 选择 RawData 类型视频帧数据
    renderConfig.bufferType = ZegoVideoBufferTypeRawData;
    // 选择 RGB 色系数据格式
    renderConfig.frameFormatSeries = ZegoVideoFrameFormatSeriesRGB;
    // 指定在自定义视频渲染的同时引擎也渲染
    renderConfig.enableEngineRender = NO;
    
    ZegoExpressEngine *engine = [ZegoExpressEngine sharedEngine];
    if (!engine) {
        return NO;
    }
    
    [engine enableCustomVideoRender:YES config:renderConfig];
    [engine setCustomVideoRenderHandler:self];
    
    return YES;
}

#pragma mark - ZegoEventHandler

- (void)onRemoteVideoFrameRawData:(unsigned char **)data
                       dataLength:(unsigned int *)dataLength
                            param:(ZegoVideoFrameParam *)param
                         streamID:(NSString *)streamID {
    // 转换参数格式
    ZDMVideoFrameParam *digitalParam = [[ZDMVideoFrameParam alloc] init];
    digitalParam.format = (ZDMVideoFrameFormat)param.format;
    digitalParam.width = param.size.width;
    digitalParam.height = param.size.height;
    digitalParam.rotation = param.rotation;
    
    for (int i = 0; i < 4; i++) {
        [digitalParam setStride: param.strides[i] atIndex:i];
    }
    
    // 遍历所有数字人API进行数据回调
    for (id<IZegoDigitalMobile> digitalMobile in self.digitalMobileArray) {
        [digitalMobile onRemoteVideoFrameRawData:data dataLength:dataLength param:digitalParam streamID:streamID];
    }
}

- (void)onPlayerSyncRecvSEI:(NSData *)data streamID:(NSString *)streamID{
    // 遍历所有数字人API进行SEI数据回调
    for (id<IZegoDigitalMobile> digitalMobile in self.digitalMobileArray) {
        [digitalMobile onPlayerSyncRecvSEI:streamID data:data];
    }
}
1
Copied!

用户进入房间并推流

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

说明

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

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

说明

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

  • roomID: 由用户自己定义生成规则,会用来登录 Express SDK 的房间。仅支持数字,英文字符 和 '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '`', ';', '’', ',', '.', '<', '>', ''。如果需要与 Web SDK 互通,请不要使用 '%'。
  • userID: 长度不超过32字节。仅支持数字,英文字符 和 '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '`', ';', '’', ',', '.', '<', '>', ''。如果需要与 Web SDK 互通,请不要使用 '%'。
  • streamID: 长度不超过256字节。仅支持数字,英文字符 和 '-', '_'。
客户端登录房间并推流
// 记录智能体
self.streamToPlay = [self getAgentStreamID];

ZegoEngineConfig* engineConfig = [[ZegoEngineConfig alloc] init];
engineConfig.advancedConfig = @{
    @"set_audio_volume_ducking_mode":@1,/**该配置是用来做音量闪避的**/
    @"enable_rnd_volume_adaptive":@"true",/**该配置是用来做播放音量自适用**/
    //数字人
    @"sideinfo_callback_version":@(3),
    @"sideinfo_bound_to_video_decoder":@"true"
};
[ZegoExpressEngine setEngineConfig:engineConfig];

//这个设置只影响AEC(回声消除),我们这里设置为ModeGeneral,是会走我们自研的回声消除,这比较可控,
//如果其他选项,可能会走系统的回声消除,这在iphone手机上效果可能会更好,但如果在一些android机上效果可能不好
[[ZegoExpressEngine sharedEngine] setAudioDeviceMode:ZegoAudioDeviceModeGeneral];

//请注意:开启 AI 回声消除需要联系 ZEGO 技术支持获取对应的ZegoExpressionEngine.xcframework,具备该能力的版本还未发布官网
[[ZegoExpressEngine sharedEngine] enableAGC:TRUE];
[[ZegoExpressEngine sharedEngine] enableAEC:TRUE];
[[ZegoExpressEngine sharedEngine] setAECMode:ZegoAECModeAIAggressive2];
[[ZegoExpressEngine sharedEngine] enableANS:TRUE];
[[ZegoExpressEngine sharedEngine] setANSMode:ZegoANSModeMedium];

// 登录房间
[self loginRoom:^(int errorCode, NSDictionary *extendedData) {
    if (errorCode!=0) {
        NSString* errorMsg =[NSString stringWithFormat:@"进入语音房间失败:%d", errorCode];
        completion(NO, errorMsg);
        return;
    }
    
    //进房后开始推流
    [self startPushlishStream];
}];
1
Copied!

拉智能体流

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

客户端拉智能体的流
//监听房间流信息更新状态,拉取智能体流播放
- (void)onRoomStreamUpdate:(ZegoUpdateType)updateType
                streamList:(NSArray<ZegoStream *> *)streamList
              extendedData:(nullable NSDictionary *)extendedData
                    roomID:(NSString *)roomID{    
    if (updateType == ZegoUpdateTypeAdd) {
        for (int i=0; i<streamList.count; i++) {
            ZegoStream* item = [streamList objectAtIndex:i];
            
            [self startPlayStream:item.streamID];
        }
    } else if(updateType == ZegoUpdateTypeDelete) {
        for (int i=0; i<streamList.count; i++) {
            ZegoStream* item = [streamList objectAtIndex:i];
            [[ZegoExpressEngine sharedEngine] stopPlayingStream:item.streamID];
        }
    }
}
1
Copied!

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

退出房间结束通话

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

Untitled
/**
 * 通知业务后台结束通话
 * 
 * @param completion 完成回调,返回操作结果
 * @discussion 该方法会向服务器发送结束通话的请求,用于释放AI智能体实例
 */
- (void)doStopCallWithCompletion:(void (^)(NSInteger code, NSString *message, NSDictionary *data))completion {
    // 构建请求URL
    NSString *url = [NSString stringWithFormat:@"%@/api/stop", self.currentBaseURL];
    NSURL *requestURL = [NSURL URLWithString:url];
    
    // 创建请求
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:requestURL];
    request.HTTPMethod = @"POST";
    
    // 设置请求头
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
    // 创建请求参数
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:nil];
    request.HTTPBody = jsonData;
    
    // 创建会话
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 发送请求
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                           completionHandler:^(NSData * _Nullable data,
                                                            NSURLResponse * _Nullable response,
                                                            NSError * _Nullable error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error) {
                if (completion) {
                    completion(-1, @"网络请求失败", nil);
                }
                return;
            }
            
            NSHTTPURLResponse *httpUrlResponse = (NSHTTPURLResponse *)response;
            if (httpUrlResponse.statusCode != 200) {
                if (completion) {
                    completion(httpUrlResponse.statusCode, 
                             [NSString stringWithFormat:@"服务器错误: %ld", (long)httpUrlResponse.statusCode],
                             nil);
                }
                return;
            }
            
            NSError *jsonError;
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
            if (jsonError) {
                if (completion) {
                    completion(-2, @"解析响应数据失败", nil);
                }
                return;
            }
            
            // 解析响应数据
            NSInteger code = [dict[@"code"] integerValue];
            NSString *message = dict[@"message"];
            NSDictionary *responseData = dict[@"data"];
            
            if (completion) {
                completion(code, message, responseData);
            }

            // 退出房间
            [[ZegoExpressEngine sharedEngine] logoutRoom];
        });
    }];
    
    [task resume];
}
1
Copied!

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

ZEGO Express SDK 最佳配置实践

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

进房间前设置:

  • 开启传统音频 3A 处理(回声消除AEC、自动增益控制AGC、噪声抑制ANS)
  • 设置房间的使用场景为高品质语聊房场景,SDK 会针对不同的场景采取不同的优化策略
  • 设置音频设备模式为默认模式
  • 开启 AI 回声消除,提高回声消除效果(该功能需要联系 ZEGO 技术支持获取对应版本的 ZEGOExpress SDK)
  • 配置音量闪避,避免声音冲突
  • 启用播放音量自适应,提升用户体验
  • 启用 AI 降噪,设置适当的噪声抑制级别
  • 启用 SEI 和视频帧同步回调,确保数字人能够正确渲染视频帧
Untitled
ZegoEngineProfile* profile = [[ZegoEngineProfile alloc]init];
profile.appID = kZegoAppId;
profile.scenario = ZegoScenarioHighQualityChatroom; //高品质语聊房场景,设置该场景可以避免申请相机权限,接入方应按自己的业务场景设置具体值
ZegoEngineConfig* engineConfig = [[ZegoEngineConfig alloc] init];
engineConfig.advancedConfig = @{
    @"set_audio_volume_ducking_mode":@1,/**配置音量闪避,避免声音冲突**/
    @"enable_rnd_volume_adaptive":@"true",/**启用播放音量自适应**/
    @"sideinfo_callback_version":@(3),
    @"sideinfo_bound_to_video_decoder":@"true"
};
[ZegoExpressEngine setEngineConfig:engineConfig];
[ZegoExpressEngine createEngineWithProfile:profile eventHandler:self];
// 开启传统音频 3A 处理
[[ZegoExpressEngine sharedEngine] enableAGC:TRUE];
[[ZegoExpressEngine sharedEngine] enableAEC:TRUE];
[[ZegoExpressEngine sharedEngine] enableANS:TRUE];
// 开启 AI 回声消除,请注意:开启 AI 回声消除需要联系 ZEGO 技术支持获取对应版本的 ZEGOExpress SDK
[[ZegoExpressEngine sharedEngine] setAECMode:ZegoAECModeAIAggressive2];
// 开启 AI 降噪,适度的噪声抑制
[[ZegoExpressEngine sharedEngine] setANSMode:ZegoANSModeMedium];

1
Copied!

拉流前设置:

  • 设置拉流播放缓存自适应调整的区间范围,优化拉流体验
Untitled
    [[ZegoExpressEngine sharedEngine] setPlayStreamBufferIntervalRange:streamId min:100 max:2000];

    [[ZegoExpressEngine sharedEngine] startPlayingStream:streamId];
1
Copied!

Previous

快速发起语音通话

Next

展示字幕