当前页

实现数字人实时播报

2026-07-27

本文档用于说明如何快速集成客户端 SDK(ZEGO Express SDK 和数字人 SDK)实现数字人实时播报。
与数字人视频通话不同,数字人播报是单向观看场景——即用户不和数字人进行互动,只是拉数字人播报内容进行观看。
客户端只需要登录 RTC 房间拉流,不需要采集或推流用户的音视频流。创建播报实例后,服务端可以通过 TTS 接口主动让数字人播报指定文本。
适用于数字人直播、新闻播报、活动主持和固定话术播报等场景。

与数字人视频通话的区别

对比项数字人视频通话数字人实时播报
交互方式双向,用户说话后数字人回答单向,用户仅观看数字人播报内容
适用场景AI 互动、AI 客服、数字人老师数字人直播、新闻播报
客户端是否需要采集用户音频
数字人驱动机制收到用户音频后,LLM 输出回复,通过 TTS 驱动数字人播报内容完全由服务端控制,通过 TTS 驱动数字人
创建实例接口CreateDigitalHumanAgentInstanceCreateLiveDigitalHumanAgentInstance
是否需要 user_id、user_stream_id

前提条件

  • 已在 ZEGO 控制台 创建项目,并申请有效的 AppID 和 ServerSecret(用于服务端 API 签名与 RTC Token04 生成),详情请参考 控制台 - 项目信息
  • 已联系 ZEGO 技术支持开通数字人 PaaS 服务和相关接口的权限。
  • 已获取有效的 digital_human_id(测试时可使用公共 ID:c4b56d5c-db98-4d91-86d4-5a97b507da97)。
  • 已按 业务后台快速开始指引 集成播报数字人相关服务端 API。
  • 已从下载页面下载针对 AI Agent 优化的 ZEGO Express SDK,并集成到项目中。
注意

数字人播报不需要录音权限,也不会推送本地流。若同一个应用还包含语音通话或数字人视频通话入口,其他入口仍需要按对应文档申请录音权限。

示例代码

以下是接入实时互动 AI Agent API 的业务后台示例代码,您可以参考示例代码来实现自己的业务逻辑。

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

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

整体业务流程

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

完成以上两个步骤后即可实现观看数字人播报。

核心能力实现

集成 ZEGO Express SDK

请参考 集成 SDK > 2.2 > 方式 2 手动集成 SDK。示例工程使用 ZegoExpressEngineZegoDigitalMobile

app/build.gradle 中添加数字人 SDK:

app/build.gradle
dependencies {
    implementation 'im.zego:digitalmobile:1.3.0.43'
}

AndroidManifest.xml 中声明网络权限。播报数字人场景不需要声明或申请 RECORD_AUDIO

AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
与视频通话的差异

Android quickstart 为了同时支持语音通话和数字人视频通话,Manifest 中仍保留了 RECORD_AUDIO;进入 LiveDigitalHumanActivity 时不会申请该权限,也不会创建本地音频流。只保留播报入口时可以移除该权限。

播报场景无需运行时申请录音权限,进入页面后可直接初始化 ZEGO Express SDK:

ZegoEngineProfile profile = new ZegoEngineProfile();
profile.appID = appID; // 从 ZEGO 控制台获取
// !mark
profile.scenario = ZegoScenario.HIGH_QUALITY_CHATROOM;
profile.application = getApplication();
ZegoExpressEngine.createEngine(profile, null);
模拟器兼容说明

若在 Android 模拟器上运行,部分音视频能力和数字人渲染依赖真机硬件解码与 GPU 加速,可能无法正常显示画面或出现黑屏。建议在真机上调试和验证数字人播报效果。

集成数字人 SDK

数字人 SDK 已经发布在 Maven 仓库,可参考以下步骤将 SDK 集成到项目中。

1

添加 `maven` 配置

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

2

修改您的 app 级别的 build.gradle 文件

dependencies {
    ...
    // 数字人 SDK 依赖
    implementation "im.zego:digitalmobile:1.3.0.43"
}
注意
支持 Android 6.0 (API 23) 及以上版本系统。

通知业务后台创建播报数字人实例

客户端调用业务后台的 POST /api/start-live-digital-human。业务后台收到请求后,会调用 ZEGO 的 CreateLiveDigitalHumanAgentInstance 接口创建播报数字人实例,接口详细参数请参考创建数字人播报实例。RTC 模式下请求体至少包含 room_iddigital_human_idconfig_id,不需要传 user_iduser_stream_id

{
  "room_id": "room_xxxxxxxx",
  "digital_human_id": "digital_human_xxxxxxxx",
  "config_id": "mobile"
}

digital_human_id 对应服务端 DigitalHuman 结构,其核心字段如下:

字段说明
digital_human_id数字人 ID,用于指定数字人形象。
encode_code编码类型,仅支持 mobile(Android/iOS)和 web(Web)。Android 与 iOS 使用 mobile,Web 使用 web

其中 Android 和 iOS 的 config_id 使用 mobile,Web 使用 webDigitalHumanConfig.EncodeCode 合法取值仅 mobileweb

业务后台在调用 ZEGO API 时,需要将 digital_human_idconfig_id 构造为 DigitalHuman 对象:

{
    "DigitalHumanId": "c4b56d5c-db98-4d91-86d4-5a97b507da97",
    "ConfigId": "mobile",
    "EncodeCode": "H264"
}

服务端成功响应中需要将以下信息返回给客户端:

字段用途
agent_instance_id调用主动 TTS 和停止实例接口
agent_stream_idRTC 房间内数字人流的 ID
agent_user_id数字人在 RTC 房间内的用户 ID
digital_human_config初始化 Android/iOS 数字人 SDK 的配置

Android 使用 OkHttp 直接请求业务后台:

private void startLiveDigitalHuman(String baseUrl, String digitalHumanId,
                                   String configId, String roomId) {
    JSONObject bodyJson = new JSONObject();
    bodyJson.put("digital_human_id", digitalHumanId);
    bodyJson.put("config_id", configId);
    bodyJson.put("room_id", roomId);

    RequestBody body = RequestBody.create(
        bodyJson.toString(), MediaType.parse("application/json; charset=utf-8"));
    Request request = new Request.Builder()
        .url(baseUrl + "/api/start-live-digital-human")
        .post(body)
        .build();

    new OkHttpClient().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 {
            JSONObject result = new JSONObject(response.body().string());
            if (result.getInt("code") == 0) {
                String agentInstanceId = result.getString("agent_instance_id");
                String agentStreamId = result.getString("agent_stream_id");
                String digitalHumanConfig = result.getString("digital_human_config");
                // 保存 agentInstanceId,后续用于 TTS 和 stop
                startPlayingStream(agentStreamId);
                initDigitalMobileSDK(digitalHumanConfig);
            }
        }
    });
}

用户进入房间(不推流)

播报数字人只需要登录 RTC 房间并接收数字人流,客户端不需要创建本地流或调用 startPublishingStream

Android 需要先用 OkHttp 从业务后台获取 Token,再直接调用 ZEGO Express SDK 登录房间。登录成功后开启自定义视频渲染,再创建播报数字人实例:

String tokenUrl = baseUrl + "/api/zego-token?userId=" + userId;
Request tokenRequest = new Request.Builder().url(tokenUrl).get().build();
new OkHttpClient().newCall(tokenRequest).enqueue(new Callback() {
    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response)
        throws IOException {
        String token = new JSONObject(response.body().string()).getString("token");

        ZegoEngineConfig engineConfig = new ZegoEngineConfig();
        engineConfig.advancedConfig = new HashMap<String, String>() {{
            //===== 数字人专用 ====//
            put("set_audio_volume_ducking_mode", "1");           // 音量闪避开关,默认开启
            put("enable_rnd_volume_adaptive", "true");           // 播放音量自适应开关,默认开启
            put("sideinfo_callback_version", "3");               // 让 SEI 和 frame 一一对应,针对 ZEGO Express SDK 3.17 版本
            put("sideinfo_bound_to_video_decoder", "true");      // 让 SEI 和 frame 一一对应,针对 ZEGO Express SDK 3.18 版本
        }};
        ZegoExpressEngine.setEngineConfig(engineConfig);

        ZegoRoomConfig roomConfig = new ZegoRoomConfig();
        roomConfig.isUserStatusNotify = true;
        roomConfig.token = token;
        ZegoExpressEngine.getEngine().loginRoom(
            roomId, new ZegoUser(userId, userId), roomConfig,
            (errorCode, extendedData) -> {
                if (errorCode == 0) {
                    openExpressCustomRender();
                    startLiveDigitalHuman(baseUrl, digitalHumanId, "mobile", roomId);
                }
            });
    }

    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
        // 处理获取 Token 失败
    }
});

初始化数字人 SDK 和自定义渲染

Android 和 iOS 需要将 ZEGO Express SDK 收到的原始视频帧与 SEI 数据传递给数字人 SDK,再由数字人 SDK 渲染数字人画面。必须在调用 startPlayingStream 之前开启自定义视频渲染。

Android 初始化数字人 SDK。示例中涉及三个视图成员变量,需提前在布局中声明并初始化:

  • digitalView:用于承载数字人 SDK 渲染画面的容器,数字人画面最终绘制在该 View 上,初始化时通过 attach 传给数字人 SDK。
  • loadingView:加载中占位视图,数字人首帧渲染前展示,首帧绘制回调中隐藏。
  • digitalPic:静态封面占位图,与 loadingView 类似在首帧绘制回调中隐藏,避免画面停留在静态图。
private void initDigitalMobileSDK(String digitalHumanConfig) {
    digitalMobileSDK = ZegoDigitalHuman.create(this);
    digitalMobileSDK.start(digitalHumanConfig,
        new IZegoDigitalMobile.ZegoDigitalMobileListener() {
            @Override
            public void onSurfaceFirstFrameDraw() {
                loadingView.setVisibility(View.GONE);
                digitalPic.setVisibility(View.GONE);
            }
        });
    digitalMobileSDK.attach(digitalView);
}

openExpressCustomRender 中配置 RAW_DATA,并将回调数据转发给数字人 SDK。其中 onPlayerSyncRecvSEI 用于接收 ZEGO Express SDK 解析出的 SEI(Supplemental Enhancement Information)数据并转发给数字人 SDK,SEI 中携带数字人驱动所需的口型、表情等附加信息,是数字人口型准确的关键。关于 SEI 的详细说明请参考SEI 高级功能

private void openExpressCustomRender() {
    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];
                }

                if (digitalMobileSDK != null) {
                    digitalMobileSDK.onRemoteVideoFrameRawData(
                        data, dataLength, digitalParam, streamID);
                }
            }
        });

    ZegoExpressEngine.getEngine().setEventHandler(new IZegoEventHandler() {
        @Override
        public void onPlayerSyncRecvSEI(String streamID, byte[] data) {
            if (digitalMobileSDK != null) {
                digitalMobileSDK.onPlayerSyncRecvSEI(streamID, data);
            }
        }
    });
}

拉取数字人流

创建实例后,客户端使用服务端返回的 agent_stream_id 拉取数字人流。不同平台 quickstart 的拉流时机略有不同:Android 在创建实例接口成功后直接拉流;iOS 和 Web 通过房间流更新回调,匹配目标流后再拉流。

Android quickstart 在创建播报数字人实例成功后直接拉取 agent_stream_id,不使用 onRoomStreamUpdate

// 设置拉流缓冲区间,缓解网络抖动导致的画面卡顿;min 为起始缓冲(ms),max 为最大缓冲(ms)
ZegoExpressEngine.getEngine()
    .setPlayStreamBufferIntervalRange(agent_stream_id, 100, 2000);
ZegoExpressEngine.getEngine().startPlayingStream(agent_stream_id);
CDN 模式拉流

以上为 RTC 模式拉流方式。若使用 CDN 模式,客户端无需登录 RTC 房间,也不需要按上述流程拉取 RTC 流,而是直接通过通用播放器(如 HLS / FLV 播放器)拉取业务后台返回的 cdn_url 即可观看数字人播报。CDN 模式不需要集成数字人 SDK。

主动让数字人播报文本

数字人实例创建成功后,客户端调用业务后台 POST /api/send-agent-instance-tts,传入实例 ID 和文本:

{
  "agent_instance_id": "2075416950128779264",
  "text": "尊敬的开发者你好,欢迎使用 ZEGO AI Agent。"
}

text 最大长度不超过 300 个字符。还可以按业务需要传入 add_historyprioritysame_priority_option 等参数。详细说明请参考主动调用 TTS

实例生命周期

播报数字人实例在空闲(无播报任务)超过 900 秒后会自动销毁,业务侧可通过 MaxIdleTime 调整该空闲超时时间。如需长时间保活,请定期调用主动 TTS 或调大 MaxIdleTime

Android 使用 OkHttp 直接调用 TTS 接口:

JSONObject bodyJson = new JSONObject();
bodyJson.put("agent_instance_id", agentInstanceId);
bodyJson.put("text", text);

Request request = new Request.Builder()
    .url(baseUrl + "/api/send-agent-instance-tts")
    .post(RequestBody.create(bodyJson.toString(),
        MediaType.parse("application/json; charset=utf-8")))
    .build();
new OkHttpClient().newCall(request).enqueue(new Callback() {
    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response)
        throws IOException {
        JSONObject result = new JSONObject(response.body().string());
        if (result.getInt("code") != 0) {
            // 处理 TTS 请求失败
        }
    }

    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
        // 处理网络错误
    }
});

退出房间结束播报

退出时需要停止 Agent 实例、停止拉流、退出 RTC 房间并销毁客户端 SDK。无论停止接口是否成功,都应释放 RTC 资源,避免房间和引擎残留。

@Override
protected void onDestroy() {
    super.onDestroy();
    JSONObject bodyJson = new JSONObject();
    bodyJson.put("agent_instance_id", agentInstanceId);
    Request request = new Request.Builder()
        .url(baseUrl + "/api/stop")
        .post(RequestBody.create(bodyJson.toString(),
            MediaType.parse("application/json; charset=utf-8")))
        .build();
    new OkHttpClient().newCall(request).enqueue(new Callback() {
        @Override
        public void onResponse(@NonNull Call call, @NonNull Response response) {
            ZegoExpressEngine.getEngine().stopPlayingStream(agentStreamId);
            ZegoExpressEngine.getEngine().logoutRoom();
            digitalMobile.stop();
            ZegoExpressEngine.destroyEngine(null);
        }

        @Override
        public void onFailure(@NonNull Call call, @NonNull IOException e) {
            // 即使请求失败,也应释放本地 RTC 和数字人 SDK 资源
            ZegoExpressEngine.getEngine().logoutRoom();
            digitalMobile.stop();
            ZegoExpressEngine.destroyEngine(null);
        }
    });
}

RTC 和 CDN 模式

本文以 RTC 模式为例:客户端登录房间并拉取 agent_stream_id。如果需要大规模直播,可以使用 CDN 模式:

模式创建实例参数客户端播放方式适用场景
RTCroom_idZEGO Express SDK 拉取 RTC 流低延迟、小范围互动
CDNcdn_url使用播放器拉取 CDN 流大规模直播

RTC 和 CDN 模式都使用 agent_instance_id 调用主动 TTS 和停止实例接口。CDN 模式不需要客户端登录 RTC 房间,也不需要集成数字人 SDK。CDN 拉流方式:可以用任何支持 CDN 播放的方式进行拉流,不做特殊说明。

监听回调

请监听 ZEGO Express SDK 的房间登录、拉流状态和错误回调,并在业务后台记录 agent_instance_idagent_stream_idrequest_id 和错误信息,便于定位创建实例、拉流或 TTS 失败原因。其中 request_id 由 ZEGO 服务端返回,是排查服务端链路问题的关键标识,请务必在出现异常时连同上述信息一起反馈给 ZEGO 技术支持。

接入测试过程中,强烈建议监听业务后台接收回调(Event 为 Exception 的事件),通过 Data.CodeData.Message 快速定位 LLM/TTS 等参数配置问题。回调与错误码详情请参考接收回调异常事件错误码

注意

如果数字人画面停留在静态图,请重点检查:数字人配置是否有效、agent_stream_id 是否正确、是否在 startPlayingStream 前开启了自定义视频渲染,以及是否将视频帧和 SEI 数据传给了数字人 SDK。

排查清单

接入过程中如遇异常,可对照以下清单逐项排查。

Android 排查清单

现象排查方向
登录房间失败检查 Token 是否有效、AppID 是否匹配、网络是否可达 ZEGO 服务。
创建实例返回失败确认 digital_human_idconfig_id(Android 为 mobile)、room_id 是否正确,业务后台签名是否有效。
拉不到流 / 无画面确认 agent_stream_id 与服务端返回一致;检查是否在 startPlayingStream 前调用 enableCustomVideoRender
画面停留在静态图检查 digitalView/loadingView/digitalPic 是否正确 attach;确认视频帧和 SEI 已转发给 digitalMobileSDK;真机运行而非模拟器。
口型不准确认 SEI 数据已通过 onPlayerSyncRecvSEI 转发;确认 advanceConfig 中 SEI 相关参数与 ZEGO Express SDK 版本匹配。
TTS 不播报确认 agent_instance_id 正确、text ≤ 300 字符、实例未因 900 秒空闲被销毁。

更多错误码请参考异常事件错误码,回调监听请参考接收回调

上一篇

实现数字人视频通话

下一篇

展示字幕