H5版
1. SDK运行原理
2. 接入方法
iframe支持在网页内引入其他网页,使用iframe引入WebSdk方式如下:
FAQ: 为什么使用iframe
使用iframe的方式,可以天然的和集成方代码形成隔离,形成单独的进程进行数字人的渲染工作,防范因js单线程设计可能造成的渲染卡顿问题。
// 用户侧
<iframe
id="myIframe"
src="https://open.xiling.baidu.com/cloud/realtime?token=自行填写&initMode=noAudio&cameraId=0
frameBorder="0"
allow="microphone;camera;midi;encrypted-media;autoplay;" // 可用权限
/>
3. demo使用
3.1 demo说明
此demo分别使用react,vue两种框架。针对如何发送消息驱动数字人,如何监听驱动数字人消息的完成状态,如何第一时间判断数字人已加载完成
如何解决浏览器静音策略,以及如何节省数字人资源等问题,进行了代码实现。
3.2 demo下载
React版本,下载地址https://sdk-demo.bj.bcebos.com/realtime-digital-human-demo.zip
Vue版本,下载地址https://sdk-demo.bj.bcebos.com/realtime-digital-human-vue-demo.zip
核心代码如下
/* eslint-disable @typescript-eslint/no-explicit-any */
// 参考文献 https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide
import { useCallback, useEffect, useState } from 'react'
import {v4 as uuidV4} from 'uuid';
import DHIframe from '@bddh/starling-dhiframe';
import useAudioRender from './hook/useAudioRender';
import './App.css'
const dhIframe = new DHIframe('digital-human-iframe');
enum ReadyState {
UNINSTANTIATED = -1,
CONNECTING = 0,
OPEN = 1,
CLOSING = 2,
CLOSED = 3,
}
// 可以有声播放则返回true,否则返回false
function checkPlayUnMute(): Promise<boolean> | boolean {
if (typeof navigator.userActivation !== 'undefined' && typeof navigator.userActivation.hasBeenActive !== 'undefined') {
return navigator.userActivation.hasBeenActive;
}
return new Promise((resolve) => {
const audioElem: HTMLAudioElement = document.createElement('audio');
audioElem.src = 'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBAAAAABAAEAESsAACJWAAACABAAZGF0YQAAAAA=';
audioElem.muted = false;
const playPromise: Promise<void> | undefined = audioElem.play();
if (playPromise !== undefined) {
playPromise
.then(() => {
// 自动播放成功
resolve(true);
})
.catch((error: Error) => {
if (error.name === "NotAllowedError" || error.name === "AbortError") {
// 自动播放被禁止或中止
resolve(false);
} else {
// 其他错误
resolve(false);
}
});
audioElem.remove();
} else {
// 如果 play() 返回 undefined,可能是浏览器不支持 Promise 风格的 play()
audioElem.remove();
resolve(false);
}
});
}
function App() {
// 实时视频流远端准备完毕,可以进行textRender等驱动指令的发送
const [realTimeVideoReady, setRealTimeVideoReady] = useState(false);
const [wsConnected, setWsConnected] = useState(false);
// 浏览器限制导致的有声视频无法自动播放,所以这里进行静音后自动播放
const [videoIsMuted, setVideoIsMuted] = useState(false);
const [commandId] = useState(uuidV4());
// 是否可以自动播放,检测完成状态
const [checkOver, setCheckOver] = useState(false);
// 超时即将消失tip
const [showTimeoutTip, setShowTimeoutTip] = useState(false);
// 数字人前置loading页面
const [showLoadingPage, setShowLoadingPage] = useState(false);
const {playAudio, playMultiAudio} = useAudioRender(dhIframe); // pcm格式音频播放
const onMessage = useCallback((msg: any) => {
if (msg.origin === 'https://open.xiling.baidu.com') {
const {type, content} = msg.data;
const {action, requestId} = content;
switch (type) {
case 'rtcState':
if (content.action === 'remoteVideoConnected') {
setRealTimeVideoReady(true);
}
if (content.action === 'localVideoMuted' && content.body) {
setVideoIsMuted(true);
}
break;
case 'wsState':
if (content.readyState === ReadyState.OPEN) {
setWsConnected(true);
}
else if (content.readyState === ReadyState.CLOSED || content.readyState === ReadyState.CLOSING) {
setWsConnected(false);
}
break;
case 'msg':
if (requestId === commandId && action === 'RENDER_COMPLETED') {
console.info(`数字人驱动完成, 驱动id为${requestId}`);
const newCommandId = uuidV4();
// 解开注释可以进行持续播报
// setCommandId(newCommandId);
dhIframe.sendMessage({
action: 'TEXT_RENDER',
body: '我已经完成一次驱动,这是我说的第n句话',
requestId: newCommandId
});
}
else if (action === 'DISCONNECT_ALERT') {
setShowTimeoutTip(true);
}
else if (action === 'TIMEOUT_EXIT') {
const iframeDom = document.getElementById('digital-human-iframe');
iframeDom?.remove();
}
break;
default:
break;
}
}
}, [commandId]);
// 播放开场白
const playWelcome = () => {
setVideoIsMuted(false);
dhIframe.sendCommand({
subType: 'muteAudio',
subContent: false
});
dhIframe.sendMessage({
action: 'TEXT_RENDER',
body: '你好,这是我的开场白自我介绍,我是数字人',
requestId: commandId
});
}
// 打断数字人
const handleInterrupt = () => {
dhIframe.sendMessage({
action: 'TEXT_RENDER',
body: '<interrupt></interrupt>',
requestId: commandId
});
}
const goNextPage = () => {
setShowLoadingPage(false);
setVideoIsMuted(false);
}
useEffect(() => {
const checkPlayUnMuteFun = async () => {
const result = await checkPlayUnMute(); // 500ms左右
setVideoIsMuted(!result);
setCheckOver(true);
}
checkPlayUnMuteFun();
}, [])
useEffect(() => {
if (realTimeVideoReady && wsConnected && checkOver && !videoIsMuted) {
dhIframe.sendMessage({
action: 'TEXT_RENDER',
body: '这是我的开场白自我介绍,我是数字人',
requestId: commandId
});
}
}, [realTimeVideoReady, wsConnected, checkOver, videoIsMuted, commandId]);
useEffect(() => {
dhIframe.registerMessageReceived(onMessage);
return () => {
dhIframe.removeMessageReceived(onMessage);
};
}, [onMessage]);
if (showLoadingPage) {
return (
<div onClick={goNextPage}>
前置loading页,点击进入下个页面
</div>
)
}
return (
<>
{videoIsMuted && <div className="tip1" onClick={playWelcome}>取消静音,播报开场白</div>}
{showTimeoutTip && <div className="tip2" onClick={playWelcome}>长时间无交互,数字人马上消失了,点我一下,进行交互</div>}
<div className="button-wrapper">
<button onClick={playWelcome}>文本播报</button>
<button onClick={playAudio}>音频播报</button>
<button onClick={playMultiAudio}>多段拼接音频播报</button>
<button onClick={handleInterrupt}>打断播报</button>
</div>
<iframe
id="digital-human-iframe"
src="https://open.xiling.baidu.com/cloud/realtime?token=自行填写&initMode=noAudio&cameraId=自行填写&figureId=自行填写"
width="450"
height="800"
allow="autoplay"
>
</iframe>
</>
)
}
export default App
3.3 demo运行和修改
- 解压后,进入目录:React版本目录名称:realtime-digital-human-demo、Vue 版本目录名称:realtime-digital-human-vue-demo
- 命令行执行 yarn,安装所需依赖包
- 命令行执行 npm run dev,启动项目
- 将控制台输出的ip地址,复制到浏览器地址栏进行项目预览,页面黑色并显示在 loading 中
- 在平台购买云渲染交互组件平台操作指南,新建应用关联组件,获取应用的appId和appkey
- 在公共形象库中查找组件对应的人像类型的人像 id 和机位 id,替换 demo 文件中 figureId 和 cameraId 参数,文件位置:React 版本是 src/App.tsx、Vue 版本是 src/App.vue,搜索『需替换』关键字
- 根据接口通用说明生成鉴权参数 token,替换 demo 文件中 token 参数,文件位置:React 版本是 src/App.tsx、Vue 版本是 src/App.vue,搜索『自行填写』关键字
- 保存文件,刷新页面可以看到数字人成功加载出来,可以点击播报按钮驱动数字人播报
4. url参数说明
4.1 常用基础参数
参数 | 类型 | 必填 | 默认 | 备注 |
---|---|---|---|---|
token | string | 是 | - | 平台购买所需组件平台操作指南,生成鉴权凭证:生成方法查阅接口通用说明(token=Authorization)的鉴权参数生成部分 |
figureId | string | 是 | - | 查找文档公共形象库,填写对应 figureId |
initMode | String | 是 | - | 用于针对特定应用场景快速批量设置关联配置的默认值,设置后关联配置无需填写,枚举类型:noAudio:无拾音模式 |
resolutionWidth | string | 当前设备宽度clientWidth | 视频分辨率宽,只能是偶数,最小值400,分辨率最大不超过 1080x1920、1920x1080 | |
resolutionHeight | string | 当前设备高度clientHeight | 视频分辨率高,只能是偶数,最小值400,分辨率最大不超过 1080x1920、1920x1080 | |
cameraId | int | - | 数字人预设机位 ID,控制数字人的位置和大小,建议使用和视频分辨率比例一致的机位:高精 2D 人像:0 (横屏半身)、1(竖屏半身)详细枚举参考3D数字人、2D精品数字人、2D小样本数字人 |
4.2 高级参数
参数 | 类型 | 默认 | 备注 |
---|---|---|---|
mode | string | inline | 画面裁剪参数(inline内嵌展示未填充的地方背景色展示 、crop裁剪) |
backgroundImageUrl | string | - | 设置数字人video背景图片,不支持直接配置颜色 autoChromaKey为 true 时不生效 |
videoBg | string | - | 设置页面颜色,默认黑色,iframe 宽高比和数字人视频宽高比不一致时,页面的填充色, 大多情况建议使用 transparent autoChromaKey为 true 时不生效 |
autoChromaKey | Boolean | false | 开启自动抠绿,实现数字人透明背景 |
ttsPer | string | - | 音色发音人ID,可用发音人列表参考:公共音色库 |
ttsPitch | number | 5 | 音调,5是正常值,0-15的取值范围,越大声音越尖,默认值5 (字面量需要是整数) |
ttsSpeed | number | 5 | 语速,5是正常值,0-15的取值范围。越大语速越快,默认值5 (字面量需要是整数) |
ttsVolume | number | 5 | 音量,5是正常值,0-15的取值范围,越大音量越大,默认值5 (字面量需要是整数) |
cp-autoAnimoji | Boolean | false | 是否开启自动添加数字人动作,只支持高精3D/2D人像 |
cp-inactiveDisconnectSec | int | 180 | 长时间无交互(交互指发送 text render 让数字人播报)的断连提醒,单位秒,0 表示不开启。达到阈值时,服务端会发送 TIMEOUT_EXIT 提醒消息,父页面在收到该消息时需销毁子页面,避免数字人渲染资源浪费,目前数字人后端服务不会主动断开。 |
cp-preAlertSec | int | 10 | 在开启长时间无交互断连提醒,且即将到达断连阈值时,后端会先发送一条 DISCONNECT_ALERT 预提醒消息,父页面收到消息可以弹窗提醒用户。该参数用于设置断连前多少秒发送该提醒,比如无交互超时参数 cp-inactiveDisconnectSec 设置为 180(3 分钟),提醒参数 cp-preAlertSec 设置为 10 (秒),则在用户最后一次交互后的 2 分 50 秒时会收到后端发送的 DISCONNECT_ALERT 提醒消息,在 3 分钟时收到 TIMEOUT_EXIT 断连消息。 |
showLogo | Boolean | false | 是否展示logo |
logoUrl | string | - | 自定义logo图片地址 |
entry | Boolean | false | 是否有入口页(开启后不会自动播放) 一般用于手机端iframe集成,手机端无法自动播放 |
4.3 debug参数设置
参数 | 类型 | 默认 | 备注 |
---|---|---|---|
appId | string | - | 应用标识和秘钥,用于鉴权,前端直接使用可能会有泄漏风险,不建议生产环境使用,推荐使用 token 鉴权。 |
appKey | string | - | |
showMessage | Boolean | false | 展示toast提示消息 |
textAssist | Boolean | false | 是否展示文本辅助, 可以进行驱动 |
showDebugger | Boolean | false | 展示vconsole |
5. 如何与Web-Sdk通信
使用iframe的内外层window可以通过window.postMessage这个API发送消息,基于window.postMessage封装好的通信npm包@bddh/starling-dhiframe,提供了收、发消息的方法,用户可直接使用。
5.1 @bddh/starling-dhiframe的安装和使用
工具包的安装
// npm 方式:
npm i @bddh/starling-dhiframe
// yarn 方式
yarn add @bddh/starling-dhiframe
工具包的使用
import DHIframe from '@bddh/starling-dhiframe';
// digital-human-iframe需要和 iframe 的 id 地址保持一致
const dhIframe = new DHIframe('digital-human-iframe');
// 使用方式
dhIframe.sendMessage({
action: 'TEXT_RENDER',
body: '你好,这是我的开场白自我介绍,我是数字人',
requestId: commandId
});
5.2 接收消息
用户侧可以通过如下方式接收leafletWeb抛出的消息
const onMessage = msg => {
console.log(msg.data);
// {type, content} = msg.data;
};
// 增加监听
dhIframe.registerMessageReceived(onMessage);
// 移除监听
dhIframe.removeMessageReceived(onMessage);
5.2.1 消息Type类型
用户侧能够接收到的消息(即Web-sdk抛出的消息)有三类,通过msg.data的type字段表示,三类消息说明如下:
type | content | 备注 |
---|---|---|
msg | msg | 数字人驱动消息回调通知 |
rtcState | rtcState | webrtc 的状态 |
wsState | wsState | webSocket 的链接状态 |
5.2.2 rtcState数据信息
type为 rtcState 的 content 的 格式 {action, body}
action | body | 备注 |
---|---|---|
success | true | rtc房间登录成功 |
error | error | rtc房间登录失败 |
remotevideoloading | id | 远端视频流开始加载 |
remoteVideoConnected | id | 成功获取远端视频流(不受视频暂停影响) |
remotevideoon | id | 远端视频流加载完毕 |
localVideoMuted | true | 首次加载无交互导致的video静音 |
5.2.3 wsState数据信息
type为 wsState 的 content.readyState
状态 | value | 备注 |
---|---|---|
UNINSTANTIATED | -1 | 默认值 无状态 |
CONNECTING | 0 | ws 连接中 |
OPEN | 1 | ws 开启成功 |
CLOSING | 2 | ws 关闭中 |
CLOSED | 3 | ws 关闭 |
enum ReadyState {
UNINSTANTIATED = -1,
CONNECTING = 0,
OPEN = 1,
CLOSING = 2,
CLOSED = 3,
}
5.2.4 msg数据信息
type为 msg 的 content.action
- 类别:驱动数字人回调消息
状态 | 备注 | 内容数据 |
---|---|---|
RENDER_START | 数字人收到驱动后,驱动开始 | - |
RENDER_COMPLETED | 数字人完成驱动指令,驱动完成 | - |
RENDET_ERROR | 文本驱动错误 | - |
DOWN_SUBTITLE | 播报内容 | body |
RENDER_INTERRUPTED | 数字人驱动被打断 | - |
- 类别:数字人退出回调消息
状态 | 备注 | 内容数据 |
---|---|---|
TIMEOUT_EXIT | 超时退出消息 |
- 类别:数字人初始化消息回调
状态 | 备注 | 内容数据 |
---|---|---|
CONNECT | 连接消息 | body |
DISCONNECT_ALERT | 长时间无交互提示 |
type为 msg 的 content.code
- 错误类型:connect连接,初始化数字人报错报错
错误码 | 错误信息 | 说明 |
---|---|---|
1001 | 服务器内部错误 | |
1002 | APP已失效 | |
1003 | APP token已过期 | |
1004 | 目前线上用户较多,请您稍后再试 | app 会话路数已经达到上限 |
1005 | 授权信息不存在 | figureId未授权不可用 |
1009 | App key非法 | |
1011 | 裁剪参数非法 | 仅2D数字人存在 |
1012 | 参数不能为空 | |
1014 | 分辨率/编码参数不合法 | |
1015 | 无效的媒体信息 | 例如:传递的RTMP参数无效 |
1202 | 正在连接或已连接 | |
1204 | 解析请求失败 | |
1206 | 读取json失败 | |
2001 | Fail to establish | 建立渲染任务失败 |
3001 | 目前线上用户较多,请您稍后再试 | 系统渲染资源不足导致 |
- 错误类型:驱动数字人报错
错误码 | 错误信息 | 说明 |
---|---|---|
-1 | body非法 | body内容含有非法字符 |
1013 | DRML非法 | 仅2D数字人存在,不支持做该动作同时做播报,通常是走入走出的情况,面部不完整 |
1208 | 富文本格式非法 | DRML格式错误 |
1201 | 操作尚未支持 |
5.3 发送消息
用户侧一般需要发送的消息类型有两类。
一类是发给sdk对应后端服务的消息,如驱动或查询数字人;
另一个是指令型消息,如音频静音、视频播放等。
5.3.1 sendMessage方式
playVideo针对不同消息类型分别封装了不同的方法,可直接使用。
代码实例
import {v4 as uuidV4} from 'uuid';
// data 格式类型
const jsonStr = {action: 'TEXT_RENDER', body: '你好', requestId: uuidV4()}; // requestId不加默认使用sdk自生成的id
// 发送消息
dhIframe.sendMessage({
action: 'TEXT_RENDER',
body: '你好,这是我的开场白自我介绍,我是数字人',
requestId: commandId
});
requestId说明:可自定义构造requestId进行驱动数字人,驱动的回调消息会沿用自定义构造的requestId。
action说明:可选值:TEXT_RENDER,含义为进行数字人语音驱动
body说明:
TEXT_RENDER时body的可选值 | 备注 |
---|---|
你好,我是数字人 | 播报内容 |
<interrupt></interrupt> | 打断当前数字人播报 |
<speak><say-as type="telephone">110</say-as> </speak> |
指定读法的高级播报内容,支持在线SSML(语音合成标记语言) |
<speak>开始说话<silence time="5s"></silence>停止5s后继续说话</speak> | 支持播报中停顿 |
<speak><audio src="http://meitron-test.bj.bcebos.com/mofachengbao_part.wav"/> </speak> | 音频播放 |
<speak interruptible="false">这段话不允许打断这段话不允许打断这段话不允许打断</speak> | 播报不受打断的内容,后续textRender会在上句播报完成后开始播报,但仍可被interrupt标签打断 |
驱动消息回调事件,查看 【5.2.4 msg数据信息】
5.3.2 sendCommand方式
实例代码
// 驱使数字人视频静音指令
dhIframe.sendCommand({
subType: 'muteAudio',
subContent: mute // mute: true/false
});
subType参数
subType可选值 | 说明 | dhIframe中已封装好的方法 | 使用方式 |
---|---|---|---|
muteAudio | 静音本地vidio, audio元素 | muteAudio | dhIframe.muteAudio(true) |
playVideo | 数字人视频被暂停时播放 | playVideo | dhIframe.playVideo(true) |
5.3.2 sendAudioData方式
实例代码
// tts数据驱动数字人播报
dhIframe.sendAudioData({
action: 'AUDIO_RENDER',
body: base64String,
requestId: uuidV4()
})
tips1:仅支持pcm数据,pcm数据大小 限制2M
tip2: 如果连续发两个AUDIO_RENDER, 这两个音频排队渲染
tips3:音频属性仅支持"16k"、"16bit" 和 "单声道"
16k:这通常指的是音频的采样率为16kHz(千赫兹),意味着音频信号每秒被采样16,000次。采样率越高,能够记录的声音频率范围越宽,声音的高频细节就越丰富。16kHz的采样率一般适用于语音录音,足以覆盖人声的频率范围。
16bit:这表示音频的位深度是16位。位深度决定了音频波形的动态范围,即最小声音和最大声音之间的区别。16位音频可以提供96分贝(dB)的动态范围,适合大多数音乐和语音应用。
单声道:单声道(Mono)音频指的是所有声音都混合到一个单一的音频通道中。与立体声(Stereo)不同,立体声至少使用两个通道(通常是左右两个通道),提供空间感和方向感。单声道适合许多应用,尤其是在只需要语音清晰传达的场合,比如新闻报道、有声书等。
参数解析
参数 | 解释 |
---|---|
action | 可选值:"AUDIO_RENDER",含义是音频驱动播报 |
body | pcm音频数据 => base64格式的音频数据 |
requestId | 生成的随机id |
6. sdk兼容性说明
1、pc端-web测试结论
- windows系统
- 系统版本号: 版本:windows 10 专业版 版本号:22H2 操作系统内部版本:19045.4170
说明:--表示无法安装对应版本的浏览器
浏览器 | 最新版本 | 拉流 | 最低兼容版本 | 拉流 |
---|---|---|---|---|
Chrome | 129.0.6668.101 | ✓ | 107.0.5304.107 | ✓ |
Safari | -- | -- | -- | -- |
Edge | 129.0.2792.89 | ✓ | 118.0.2088.69 | ✓ |
QQ浏览器 | 13.1.0 | ✓ | 12.1.1 | ✓ |
- MAC系统
- 系统版本号 芯片:Apple M1 序列号:C02F149YQ05D macOS:13.6(22G120)(Edge最低兼容版除外)
- 备注:Edge mac最低兼容使用系统版本:
芯片:Intel 序列号:FVFC226ZL412 macOS:13.6.4(22G513)
浏览器 | 最新版本 | 拉流 | 最低兼容版本 | 拉流 |
---|---|---|---|---|
Chrome | 129.0.6668.101 | ✓ | 107.0.5304.110 | ✓ |
Safari | 16.6 | ✓ | 16.6 | ✓ |
Edge | 129.0.2792.89 | ✓ | 107.0.1418.28 | ✓ |
QQ浏览器 | 5.0.7.203 | ✓ | -- | -- |
2、移动端-web测试结论
- Android
说明:--表示无法安装对应的浏览器
手机型号 | 系统 | 微信内置 | QQ浏览器 | Chrome | 内置浏览器 | UC浏览器 |
---|---|---|---|---|---|---|
华为Mate 50 | 鸿蒙4.2.0 | ✓ | ✓ | ✓ | ✓ | ✕ |
华为Mate 9 | 鸿蒙2.0.0 | ✓ | ✓ | -- | ✓ | ✕ |
vivo IQOOneo5 | 安卓13 | ✓ | ✓ | ✓ | ✓ | ✕ |
vivo IQOO9Pro | 安卓14 | ✓ | ✓ | ✓ | ✓ | ✕ |
红米K60 Ultra | 安卓13 | ✓ | ✓ | ✓ | ✕ | ✓ |
OPPO ace2 | 安卓12 | ✓ | ✓ | ✓ | ✓ | ✕ |
小米6 | 安卓8 | ✓ | ✓ | -- | ✕ | ✓ |
- iOS
手机型号 | 系统 | 微信内置 | Chrome | Safari |
---|---|---|---|---|
iphone13 | ios16.3.1 | ✓ | ✓ | ✓ |
iphone14 | ios17.5.1 | ✓ | ✓ | ✓ |
iphoneXS | ios15.3 | ✓ | ✓ | ✓ |
iphone11 | ios17.5.1 | ✓ | ✓ | ✓ |
iphone15 | ios17.5.1 | ✓ | ✓ | ✓ |
iphone 12 | ios 16.3.1 | ✓ | ✓ | ✓ |
iphone12 | ios14.6 | ✓ | ✓ | ✓ |
7. 常见问题
7.1 部分浏览器视频组件层级过高
移动端部分android浏览器对视频组件进行过特殊处理,导致视频组件悬浮于页面之上,该场景无法通过代码去解决
- 如果只是层级过高,并不会直接全屏,那么可以使用半屏数字人或数字人头像大小,不在数字人界面上方叠加组件
- 如果视频组件层级高且会直接全屏,那么可以直接拦截当前浏览器对数字人的使用
7.2 移动端集成,无法自动播放问题;(实现详见结尾demo)
移动端受限于浏览器策略,无法自动播放,需要一次用户交互行为(比如点击),常见处理方案
- 进入数字人前有个引导页,页面上进行业务介绍与开始业务的按钮,点击按钮触发数字人的播放
- 提前加载数字人iframe,然后将播放事件绑定在前置流程中的按钮上
相关配置:
entry=true
按钮触发方法:
dhIframe.playVideo(true);
注意:部分浏览器只是会继承上一次的用户交互时间,一段时间后也会失效,如果为了保障数字人的稳定性,建议前置加按钮触发
7.3 驱动数字人进行开场白话术,只有唇动无声音 (实现详见篇首demo)
移动端受限于浏览器策略,无法自动有声音视频播放,sdk侧逻辑进行静音播放;
这时需要集成侧监听sdk抛出的rtcState消息localVideoMuted,引导用户完成交互操作后,
使用dhIframe.sendCommand({ subType: 'muteAudio', subContent: false });进行取消静音
7.4 部分iphone微信浏览器打开无数字人,部分安卓浏览器打开数字人不动
iphone微信浏览器无数字人原因:
微信限制,页面需要点击后才可播放视频(静音也不可自动播放)
iphone解决方案:
同6.4,集成侧监听sdk抛出的rtcState消息localVideoMuted,引导用户完成交互操作后,
使用dhIframe.sendCommand({ subType: 'muteAudio', subContent: false });进行取消静音
部分安卓浏览器数字人不动原因:
浏览器限制,页面中用户需要点击iframe区域,才可解除播放限制
解决方案:
诱导用户点击iframe区域后,调用dhIframe.sendCommand({ subType: 'muteAudio', subContent: false })
7.5 驱动数字人后,怎么中途打断数字人播报,以及如何监听数字人播报完成(实现详见篇首demo)
打断发送消息
dhIframe.sendMessage({action: 'TEXT_RENDER', body: '<interrupt></interrupt>'});
监听正常播放完成,首先指定TEXT_RENDER的requestId, 然后监听指定requestId的RENDER_COMPLETED; 监听播报因打断而提前完成,监听指定requestId的RENDER_INTERRUPTED;
7.6 数字人路数较少,如何节约资源,尽可能让资源在客户不用的时候尽快释放掉 (实现详见篇首demo)
设置cp-inactiveDisconnectSec, cp-preAlertSec字段
并监听DISCONNECT_ALERT进行适当的交互提示
监听TIMEOUT_EXIT消息进行销毁iframe操作
7.7 拉流没有画面
- 请检查当前页面协议是否为HTTPS,若不是,请更换成HTTPS;
- 请按照下图所示步骤继续排查客户端是否收到流
- tab页开启h5-sdk保证已经拉流完成
- 新开tab页输入chrome://webrtc-internals/
- 点击列表中的最后一个链接
- 页面全局搜索bytesReceived, 一直键入回车,直到找到对应页面的[bytesReceived_in_bits/s]图表,检查图标是否为空
-
若该图表为空,可能是浏览器插件导致,为过滤浏览器插件影响,请执行以下操作之一:
- 关闭所有浏览器插件,重新打开页面
- 或打开chrome浏览器无痕窗口,重新打开页面
- 或更换其他浏览器,重新打开页面
目前已知会影响RTC正常拉流的浏览器插件:PureVPN Proxy;
如关闭所有浏览器插件并更换其他浏览器仍没有画面,请提交工单与我们联系;
7.8 音频驱动效果差
接受的音频数据必须是 pcm无损音频编码格式,需检测数据是否进行了压缩编码
如来源是音频 url,检测 url 后缀是否是 pcm。如是 mp3,m4a, wav等格式则不支持
另外检查音频属性是否为支持"16k"、"16bit" 和 "单声道"
7.9 如何控制数字人大小
步骤 1:通过设置 url 中参数mode=corp, resolutionWidth,resolutionHeight调整数字人原始视频流的分辨率,以及设置合适的cameraId 步骤 2:为iframe添加外层div包裹,并设置overflow: hidden 步骤 3:通过iframe 的style中的width, height, top, left等属性,调整数字人视频的大小到合适的尺寸,位置处于预期的位置
<div className="iframe-wrapper">
<iframe
id="digital-human-iframe"
src="https://open.xiling.baidu.com/cloud/realtime?token=自行填写&initMode=noAudio&cameraId=自行填写&figureId=自行填写&resolutionWidth=1080&resolutionHeight=1920&mode=corp"
allow="autoplay"
>
</div>
.iframe-wrapper {
position: absolute;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
overflow: hidden;
}
#digital-human-iframe {
position: absolute;
width: 140vw;
height: 120vh;
top: -20vh;
left: -20vw;
}