从零搭建数字人口播系统,撑起一个 24h 电商无人值守直播间

这篇教程基于一个完整的 Web 端数字人口播 Demo,使用 ZEGO数字人 API搭建,覆盖从创建数字人视频流任务到前端实时播放的全部流程。代码从实际项目中提取,可以直接运行。

引言:为什么需要数字人口播

电商直播有三个绕不开的痛点:主播成本高、排班困难、无法 7×24 在线。一个成熟主播的月均成本在 2-5 万,而直播间日均在线时长超过 12 小时才能覆盖流量高峰与长尾时段。MCN 机构同时运营多个直播间时,人力调度更加紧张,主播状态波动直接影响转化率。

数字人口播解决了这些问题。预置形象与音色组合可以稳定输出,无需休息,不受情绪影响。一个数字人可以同时跑在多个直播间,边际成本趋近于零。对于”内容批量生产”场景(商品讲解录播、活动促销循环播报),数字人更是天然匹配:写好话术文本,数字人自动播报,无需真人一遍遍重复。

这篇教程基于一个完整的 Web 端数字人口播 Demo,使用 ZEGO数字人 API搭建,覆盖从创建数字人视频流任务到前端实时播放的全部流程。代码从实际项目中提取,可以直接运行。

技术方案介绍:服务端驱动 + RTC 推流

ZEGO 数字人口播方案的核心架构是服务端驱动 + RTC 推流,与纯客户端 SDK 方案有本质区别。

纯客户端方案(如 Unity/UE 集成数字人渲染 SDK)需要在本地运行渲染引擎,对终端 GPU 有硬性要求,同时渲染与推流逻辑耦合在客户端,难以做集中化调度。这种方式适合单机互动场景,不适合多直播间并发播报。

ZEGO 的方案把渲染放在云端:服务端调用 CreateDigitalHumanStreamTask 创建任务,ZEGO 云端完成数字人渲染和音频合成,生成的音视频流通过 RTC 协议推送到房间。客户端只需用 ZEGO Express SDK 登录房间并拉流,不需要任何渲染能力。文本驱动也走服务端 API(DriveByText),编排端可以随时注入新话术。

两种方案的对比:

维度纯客户端渲染ZEGO 服务端驱动
终端要求需要 GPU,最低 4GB 显存无 GPU 要求,浏览器即可
渲染位置本地设备ZEGO 云端
并发能力受本地资源限制云端弹性扩展
调度方式客户端各自独立服务端集中控制
延迟渲染延迟低,约 100ms端到端约 200-400ms
适用场景单机互动、高质量渲染多直播间并发、无人值守播报

对于电商/MCN 的”无人值守直播”需求,服务端驱动方案是更务实的选择:运维成本低、扩展容易、客户端零渲染依赖。

核心概念

在开始编码之前,需要理解四个核心概念。

数字人形象(Digital Human):在 ZEGO 控制台上传或选择的虚拟人形象,每个形象有唯一的 DigitalHumanId。形象包含外观、表情基和动作基,渲染时由云端驱动。

音色(Timbre)​:数字人使用的语音风格,由 TTS 引擎合成。音色分为公共音色私有音色:公共音色所有项目可用,私有音色绑定到特定数字人形象。每个音色有唯一的 TimbreId,TTS 参数(语速、音调、音量)可以在驱动时动态调整。

视频流任务(Stream Task):调用 CreateDigitalHumanStreamTask 后,ZEGO 云端为指定数字人创建一个渲染任务,产出一路音视频流推送到 RTC 房间。任务有唯一的 TaskId,对应一个 RoomId 和 StreamId

文本驱动(DriveByText):向运行中的视频流任务注入文本,ZEGO 云端将文本经 TTS 合成为语音,同时驱动数字人口型和表情同步。支持设置 InterruptMode 控制新文本是否打断当前播报。

架构

整个系统分为四层:编排端(Next.js 页面)、服务端(API Routes)、ZEGO 云端、播放端(Express SDK)。

graph TB
    subgraph 编排端
        A[page.jsx] -->|选择形象/音色/话术| B[控制面板]
        A -->|播放音视频流| C[视频区域]
    end

    subgraph 服务端 - API Routes
        D[/api/broadcast] -->|创建/停止任务| E[播报管理]
        F[/api/drive] -->|文本驱动| G[DriveByText]
        H[/api/token] -->|生成Token04| I[鉴权]
        J[/api/digital-humans] -->|获取形象列表| K[GetDigitalHumanList]
        L[/api/timbres] -->|获取音色列表| M[GetTimbreList]
    end

    subgraph ZEGO云端
        N[Digital Human API] -->|渲染+TTS| O[数字人引擎]
        O -->|推流| P[RTC 房间]
    end

    B -->|POST /api/broadcast| D
    B -->|POST /api/drive| F
    B -->|GET /api/token| H
    A -->|GET /api/digital-humans| J
    A -->|GET /api/timbres| L
    E -->|CreateStreamTask| N
    G -->|DriveByText| N
    E -->|StopStreamTask| N

    C -->|loginRoom + startPlayingStream| P

核心交互流程如下:

sequenceDiagram
    participant U as 编排端
    participant S as 服务端
    participant Z as ZEGO Digital Human API
    participant R as ZEGO RTC 云端

    Note over U,R: 阶段1: 页面加载与选择
    U->>S: GET /api/digital-humans
    S->>Z: GetDigitalHumanList
    Z-->>S: 形象列表
    S-->>U: 渲染选择面板
    U->>S: GET /api/timbres(公共+私有)
    S->>Z: GetTimbreList
    Z-->>S: 音色列表
    S-->>U: 渲染音色选择

    Note over U,R: 阶段2: 开始播报
    U->>S: POST /api/broadcast(digitalHumanId, timbreId, text...)
    S->>Z: CreateDigitalHumanStreamTask
    Z-->>S: TaskId
    S->>Z: DriveByText(taskId, text, TTS参数)
    Z->>R: 渲染数字人并推流
    S-->>U: {taskId, roomId, streamId}
    U->>S: GET /api/token?userId=xxx
    S-->>U: Token04
    U->>R: loginRoom(roomId, token)
    U->>R: startPlayingStream(streamId)
    R-->>U: 远端音视频流

    Note over U,R: 阶段3: 追加话术
    U->>S: POST /api/drive(taskId, text, TTS参数)
    S->>Z: DriveByText
    Z->>R: 更新数字人流

    Note over U,R: 阶段4: 停止播报
    U->>S: DELETE /api/broadcast?index=0
    S->>Z: StopDigitalHumanStreamTask
    U->>R: stopPlayingStream + logoutRoom

关键设计决策:所有对 ZEGO Digital Human API 的调用都走服务端 API Routes,而非直接从浏览器调用。原因有两点:第一,API 签名需要 ServerSecret,不能暴露给客户端;第二,服务端可以做任务状态管理,为多直播间调度留出扩展空间。

前置准备

  • Node.js >= 18
  • ZEGO 账号,已在 ZEGO 控制台 创建项目
  • AppID 和 ServerSecret(32 位字符串),从控制台”项目信息”页面获取
  • Digital Human API 权限,需联系 ZEGO 技术支持开通 CreateDigitalHumanStreamTaskDriveByTextStopDigitalHumanStreamTaskGetDigitalHumanListGetTimbreList 五个接口
  • 数字人形象和音色,在 ZEGO 控制台上传或选用预置形象,确保至少有一个形象和一个公共音色可用
  • 项目环境变量配置(.env 文件):
# .env.example
# ZEGO App ID(数字类型,从控制台获取)
APP_ID=your_app_id

# Server Secret(32 位字符串,用于 API 签名和 Token 生成)
# 注意:此密钥仅在服务端使用,不可暴露给浏览器
SERVER_SECRET=your_server_secret

# Token 有效期(秒),默认 3600
TOKEN_EXPIRE_SECONDS=3600

# 客户端 App ID(NEXT_PUBLIC_ 前缀会暴露给浏览器,必须与 APP_ID 一致)
NEXT_PUBLIC_APP_ID=your_app_id

分步实现教程

项目基于 Next.js 16 + ZEGO Express WebRTC SDK 3.11,采用单项目架构:前端页面和服务端 API Routes 在同一个 Next.js 应用中。

步骤 1:项目初始化

创建 Next.js 项目并安装 ZEGO Express WebRTC SDK,这是数字人音视频流在浏览器端播放的必要依赖。

npx create-next-app@latest digital-human-broadcasting
cd digital-human-broadcasting
npm install zego-express-engine-webrtc@^3.11.0

项目依赖说明:

依赖版本用途
next^16.1.5应用框架,提供 API Routes
react^19.2.4前端 UI 渲染
zego-express-engine-webrtc^3.11.0RTC 拉流,播放数字人音视频
tailwindcss^4.1.18样式(可替换为任意 CSS 方案)

步骤 2:API 签名封装

ZEGO Digital Human API 的每次请求都需要 MD5 签名鉴权。签名算法固定:MD5(AppId + SignatureNonce + ServerSecret + Timestamp)。将签名逻辑封装为 buildCommonParams 和 post 两个函数,所有 API Route 共用。

buildCommonParams 负责生成签名和公共参数:

// app/api/broadcast/route.js
import crypto from "crypto";

const buildCommonParams = (action) => {
  const appId = process.env.APP_ID || process.env.ZEGO_APPID || "";
  const serverSecret = process.env.SERVER_SECRET || process.env.ZEGO_SERVER_SECRET || "";
  const signatureNonce = crypto.randomBytes(8).toString("hex");
  const timestamp = Math.floor(Date.now() / 1000);
  const signature = crypto
    .createHash("md5")
    .update(`${appId}${signatureNonce}${serverSecret}${timestamp}`)
    .digest("hex");

  return new URLSearchParams({
    Action: action,
    AppId: appId.toString(),
    SignatureNonce: signatureNonce,
    Timestamp: timestamp.toString(),
    Signature: signature,
    SignatureVersion: "2.0",
  });
};

post 函数将签名参数拼接到 URL,发送 POST 请求并校验返回码:

const post = async (action, body) => {
  const params = buildCommonParams(action);
  const url = `https://aigc-digitalhuman-api.zegotech.cn/?${params.toString()}`;
  const response = await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  const data = await response.json();
  if (data.Code !== 0) {
    throw new Error(`Digital Human API failed: ${data.Code} ${data.Message}`);
  }
  return data.Data;
};

注意几点:签名参数通过 URL Query 传递,请求体放业务参数;SignatureNonce 每次请求随机生成,防止重放攻击;AppId 和 ServerSecret 支持双环境变量名(APP_ID / ZEGO_APPID),方便不同部署环境使用。

步骤 3:获取数字人列表和音色列表

页面加载时需要拉取可用的数字人形象和音色,供用户选择。这两个接口调用逻辑简单,关键在于区分公共音色和私有音色。

获取数字人列表:

// app/api/digital-humans/route.js
const getDigitalHumanList = async (params) => {
  const body = {};
  if (params.fetchMode !== undefined) {
    body.FetchMode = params.fetchMode;
  }
  if (params.offset !== undefined) {
    body.Offset = params.offset;
  }
  if (params.limit !== undefined) {
    body.Limit = params.limit;
  }
  const data = await post("GetDigitalHumanList", body);
  return data;
};

获取音色列表:不带 digitalHumanId 返回公共音色,带 digitalHumanId 返回该形象的私有音色。

// app/api/timbres/route.js
const getTimbreList = async (params) => {
  const body = {};
  if (params.digitalHumanId) {
    body.DigitalHumanId = params.digitalHumanId;
  }
  if (params.offset !== undefined) {
    body.Offset = params.offset;
  }
  if (params.limit !== undefined) {
    body.Limit = params.limit;
  }
  const data = await post("GetTimbreList", body);
  return data;
};

前端页面在挂载时调用这两个接口,同时监听数字人选择变化来拉取私有音色,两者合并后去重展示:

// app/page.jsx
const allTimbres = useMemo(() => {
  const timbreMap = new Map();
  publicTimbres.forEach((t) => timbreMap.set(t.TimbreId, t));
  privateTimbres.forEach((t) => timbreMap.set(t.TimbreId, t));
  return Array.from(timbreMap.values());
}, [publicTimbres, privateTimbres]);

用 Map 按 TimbreId 去重,确保私有音色覆盖同 ID 的公共音色。这是实际场景中常见的处理方式:部分形象有专属音色,优先级高于公共音色。

步骤 4:创建视频流任务

这一步是整个流程的核心:调用 CreateDigitalHumanStreamTask 让 ZEGO 云端开始渲染数字人,并将音视频流推送到指定的 RTC 房间。

// app/api/broadcast/route.js
const createStreamTask = async (params) => {
  const data = await post("CreateDigitalHumanStreamTask", {
    DigitalHumanConfig: { DigitalHumanId: params.digitalHumanId },
    RTCConfig: { RoomId: params.roomId, StreamId: params.streamId },
  });
  return data.TaskId;
};

请求体包含两个配置块:DigitalHumanConfig 指定用哪个数字人形象,RTCConfig 指定音视频流推送的目标房间和流 ID。返回的 TaskId 是后续文本驱动和停止任务的唯一标识。

播报管理逻辑将创建任务和首次驱动封装在一起:

const startBroadcast = async (options) => {
  const { digitalHumanId, timbreId, roomId, streamId, text,
          speechRate, pitchRate, volume, broadcastIndex } = options;

  // 如果同 index 已有任务,先停止
  if (globalState.__DH_BROADCASTS__[broadcastIndex]) {
    await stopBroadcast(broadcastIndex);
  }

  // 创建流任务
  const taskId = await createStreamTask({ digitalHumanId, roomId, streamId });

  // 首次文本驱动
  if (text) {
    try {
      await driveByText({
        taskId, text, timbreId, speechRate, pitchRate, volume,
        interruptMode: 1,
      });
    } catch (error) {
      console.log("Initial drive failed (task may need time to initialize):", error.message);
    }
  }

  // 记录任务状态
  globalState.__DH_BROADCASTS__[broadcastIndex] = {
    taskId, roomId, streamId, digitalHumanId, timbreId, speechRate, pitchRate, volume,
  };
};

这里有两个细节值得注意。第一,首次驱动可能失败,因为流任务初始化需要时间,catch 中只记录日志不中断流程,后续可以通过 Drive Text 按钮再次驱动。第二,任务状态存在 globalThis 上,这是 Demo 的简化方案,生产环境应替换为数据库或缓存。

步骤 5:文本驱动数字人

流任务创建后,通过 DriveByText 接口注入文本,ZEGO 云端完成 TTS 合成和口型驱动。

// app/api/broadcast/route.js
const driveByText = async (params) => {
  const result = await post("DriveByText", {
    TaskId: params.taskId,
    Text: params.text,
    InterruptMode: params.interruptMode ?? 1,
    TTSConfig: {
      TimbreId: params.timbreId,
      SpeechRate: params.speechRate ?? 0,
      PitchRate: params.pitchRate ?? 0,
      Volume: params.volume ?? 50,
    },
  });
  return result;
};

InterruptMode 设为 1 表示新文本打断当前播报,立即生效。TTS 参数含义:

参数范围默认值说明
SpeechRate-500 ~ 5000语速,正值加快,负值减慢
PitchRate-500 ~ 5000音调,正值升高,负值降低
Volume1 ~ 10050音量

独立的 /api/drive 路由处理播报过程中的追加话术,与创建任务时的首次驱动共享同一签名和调用逻辑:

// app/api/drive/route.js
export const POST = async (request) => {
  try {
    const body = await request.json();
    const { taskId, text, timbreId, speechRate, pitchRate, volume } = body;

    if (!taskId) {
      return NextResponse.json({ success: false, error: "taskId is required" }, { status: 400 });
    }

    const result = await post("DriveByText", {
      TaskId: taskId,
      Text: text,
      InterruptMode: 1,
      TTSConfig: {
        TimbreId: timbreId,
        SpeechRate: speechRate ?? 0,
        PitchRate: pitchRate ?? 0,
        Volume: volume ?? 50,
      },
    });

    return NextResponse.json({ success: true, driveId: result?.DriveId });
  } catch (error) {
    return NextResponse.json({ success: false, error: error.message }, { status: 400 });
  }
};

前端触发驱动的逻辑:用户在播报进行中输入新文本,点击 “Drive Text” 按钮,将当前 taskId 和新文本发送到服务端。

步骤 6:Token 生成和 RTC 房间登录

客户端要拉取数字人音视频流,需要先用 Token 登录 RTC 房间。Token 生成使用 ZEGO Token04 算法,基于 AES-CBC 加密,必须在服务端完成。

Token04 的生成逻辑:

// app/api/token/route.js
import { createCipheriv } from "crypto";

const generateToken04 = (appId, userId, secret, effectiveTimeInSeconds, payload = "") => {
  if (!appId || typeof appId !== "number") {
    throw new Error("Invalid appId");
  }
  if (!secret || secret.length !== 32) {
    throw new Error("ServerSecret must be a 32-character string");
  }

  const createTime = Math.floor(Date.now() / 1000);
  const tokenInfo = {
    app_id: appId,
    user_id: userId,
    nonce: makeNonce(),
    ctime: createTime,
    expire: createTime + effectiveTimeInSeconds,
    payload,
  };

  const plainText = JSON.stringify(tokenInfo);
  const iv = makeRandomIv();
  const encryptBuf = aesEncrypt(plainText, secret, iv);

  // 二进制拼接: expire(8) + ivLen(2) + iv + encryptLen(2) + encryptBuf
  const b1 = new Uint8Array(8);
  const b2 = new Uint8Array(2);
  const b3 = new Uint8Array(2);
  new DataView(b1.buffer).setBigInt64(0, BigInt(tokenInfo.expire), false);
  new DataView(b2.buffer).setUint16(0, iv.length, false);
  new DataView(b3.buffer).setUint16(0, encryptBuf.byteLength, false);

  const buf = Buffer.concat([
    Buffer.from(b1),
    Buffer.from(b2),
    Buffer.from(iv),
    Buffer.from(b3),
    Buffer.from(encryptBuf),
  ]);

  return `04${Buffer.from(buf).toString("base64")}`;
};

AES 密钥长度决定加密算法:16 字节用 AES-128-CBC,24 字节用 AES-192-CBC,32 字节用 AES-256-CBC。ZEGO 的 ServerSecret 固定 32 字节,因此实际使用 AES-256-CBC。

API Route 暴露为 GET 接口,接收 userId 参数:

// app/api/token/route.js
export const GET = async (request) => {
  const appId = Number(process.env.APP_ID || process.env.ZEGO_APPID || 0);
  const serverSecret = process.env.SERVER_SECRET || process.env.ZEGO_SERVER_SECRET || "";
  const tokenExpireSeconds = Number(process.env.TOKEN_EXPIRE_SECONDS) || 3600;

  const { searchParams } = new URL(request.url);
  const userId = searchParams.get("userId");

  if (!userId) {
    return NextResponse.json({ error: "Missing userId parameter" }, { status: 400 });
  }

  try {
    const token = generateToken04(appId, userId, serverSecret, tokenExpireSeconds, "");
    return NextResponse.json({ token });
  } catch (error) {
    return NextResponse.json({ error: error.message }, { status: 500 });
  }
};

步骤 7:前端播放数字人音视频流

前端用 ZEGO Express WebRTC SDK 登录房间并拉流播放,核心代码集中在 page.jsx 的 handleStartBroadcast 函数中。

完整流程分为六步:创建播报任务、获取任务信息、获取 Token、初始化 Express SDK、登录房间、拉流播放。

// app/page.jsx
const handleStartBroadcast = async () => {
  // 步骤 1: 创建播报任务
  const newRoomId = `room_dh_${Date.now()}`;
  const newStreamId = `stream_dh_${Date.now()}`;

  const broadcastRes = await fetch("/api/broadcast", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      broadcastIndex: 0,
      digitalHumanId: selectedDigitalHumanId,
      timbreId: selectedTimbreId,
      roomId: newRoomId,
      streamId: newStreamId,
      text: broadcastingText.trim(),
      speechRate, pitchRate, volume,
    }),
  });
  const broadcastData = await broadcastRes.json();

  // 步骤 2: 获取任务信息
  const infoRes = await fetch("/api/broadcast");
  const infoData = await infoRes.json();
  const task = Object.values(infoData.broadcastList)[0];
  setTaskId(task.taskId);
  setRoomId(task.roomId);
  setStreamId(task.streamId);

  // 步骤 3: 获取 Token
  const userId = `user_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
  const tokenRes = await fetch(`/api/token?userId=${userId}`);
  const tokenData = await tokenRes.json();

  // 步骤 4: 初始化 ZEGO Express SDK
  const { ZegoExpressEngine } = await import("zego-express-engine-webrtc");
  const appId = Number(process.env.NEXT_PUBLIC_APP_ID || process.env.ZEGO_APPID || 0);
  const engine = new ZegoExpressEngine(appId, "");
  engineRef.current = engine;

  // 步骤 5: 登录房间
  await engine.loginRoom(task.roomId, tokenData.token, {
    userID: userId,
    userName: userId,
  });

  // 步骤 6: 拉流播放
  const remoteStream = await engine.startPlayingStream(task.streamId);
  const remoteView = engine.createRemoteStreamView(remoteStream);
  remoteView.play("remote-video");
};

remoteView.play("remote-video") 将音视频流渲染到页面中 id="remote-video" 的 DOM 元素。这个元素同时作为播放容器和占位区域:

<div id="remote-video" className="w-full aspect-video bg-black flex items-center justify-center">
  {!isRunning && (
    <p className="text-gray-500 text-sm">
      Digital human video will appear here after broadcasting starts
    </p>
  )}
</div>

停止播报时需要清理资源:停止拉流、退出房间、销毁引擎实例。错误处理路径中同样需要清理,避免残留连接:

const handleStopBroadcast = async () => {
  await fetch("/api/broadcast?index=0", { method: "DELETE" });

  if (engineRef.current && currentRoomRef.current) {
    engineRef.current.stopPlayingStream(currentRoomRef.current.streamId);
    engineRef.current.logoutRoom(currentRoomRef.current.roomId);
    engineRef.current.destroyEngine();
    engineRef.current = null;
    currentRoomRef.current = null;
  }

  setIsRunning(false);
  setTaskId("");
  setRoomId("");
  setStreamId("");
};

组件卸载时也需清理,通过 useEffect 的返回函数处理:

useEffect(() => {
  return () => {
    if (engineRef.current && currentRoomRef.current) {
      try {
        engineRef.current.stopPlayingStream(currentRoomRef.current.streamId);
        engineRef.current.logoutRoom(currentRoomRef.current.roomId);
        engineRef.current.destroyEngine();
      } catch (_) {}
    }
  };
}, []);

运行效果

页面初始状态:左侧为黑色视频占位区域,右侧为状态面板(显示 “Idle”)。下方控制面板包含四个区域:数字人形象选择网格、音色选择网格、话术文本输入框、TTS 参数滑块(语速/音调/音量),以及三个操作按钮。

播报进行中:视频区域显示数字人实时画面,数字人口型与播报文本同步。状态面板显示 “Playing”,同时展示当前 Room ID、Stream ID 和 Task ID。

追加话术:播报进行中,在文本框输入新话术,点击 “Drive Text” 按钮,数字人切换为新话术播报。

停止播报:点击 “Stop Broadcasting” 后,视频区域恢复黑色占位,状态归位。

进阶方向

对接直播平台推流:当前 Demo 在浏览器端播放数字人流。生产环境中,需要将 RTC 流推送到抖音、快手、淘宝等直播平台。方案是使用 ZEGO 的旁路推流功能,将 RTC 流转为 RTMP 推送到平台推流地址,实现数字人画面直接进入直播间。

AI 大模型驱动对话:将 DriveByText 的输入源从人工文本替换为大模型输出。接入 ChatGPT、通义千问等 LLM,根据直播间弹幕或预设话术生成回复文本,实时驱动数字人播报。InterruptMode: 1 配置下,新文本会打断当前播报,适合互动场景。

多语言多音色切换DriveByText 每次调用都可以指定不同的 TimbreId,实现同一数字人在不同场景下切换音色。配合多语言 TTS,可以构建跨境电商的多语言直播矩阵,一个形象覆盖多个语种。

多直播间并发调度:当前 Demo 用 globalThis 存储单个任务状态。生产环境中,将任务状态迁移到 Redis 或数据库,配合任务队列管理多个直播间的并发播报,实现真正的”一人多播”。

总结

数字人口播系统解决的核心问题是:用服务端渲染 + RTC 推流替代真人主播,实现稳定的 7×24 直播输出。本教程覆盖了从 API 签名、形象/音色选择、流任务创建、文本驱动到前端拉流播放的完整链路,所有代码基于实际运行的 Demo 项目提取。对于电商和 MCN 团队,下一步是将这个 Demo 扩展为生产级方案:加入旁路推流对接直播平台、接入大模型实现智能对话、构建多直播间调度系统。

原创文章,作者:ZEGO即构科技,如若转载,请注明出处:https://market-blogs.zego.im/reports-technique/3568/

(0)
上一篇 2天前
下一篇 3月 5, 2024 6:59 上午

相关推荐

发表回复

登录后才能评论