功能描述
本文将介绍如何使用 AudioPlayer 插件实现 PCM 音频播放功能。PCM 播放器适用于 AI 对话、语音合成(TTS)、实时音频流等场景,通过 input() 方法持续输入 PCM 数据进行播放,并可将音频混入发布流推送给远端用户。
PCM 播放器支持两种工作模式:
- Realtime 模式(默认):适用于 AI 实时对话等低延迟场景,pause/stop 时清空缓冲区
- 非 Realtime 模式:适用于预加载音频后完整播放的场景,pause/stop 时保留缓冲区数据
体验在线 Demo
前提条件
-
TRTC 版本 > 5.18.0
-
支持 PCM 音频播放的平台如下
操作系统 浏览器类型 浏览器最低版本要求 Mac OS 桌面版 Chrome 浏览器 56+ Mac OS 桌面版 Safari 浏览器 11+ Mac OS 桌面版 Firefox 浏览器 56+ Mac OS 桌面版 Edge 80+ Windows 桌面版 Chrome 浏览器 56+ Windows 桌面版 QQ 浏览器(极速模式) 10.4+ Windows 桌面版 Firefox 浏览器 56+ Windows 桌面版 Edge 80+ iOS 移动版 Safari 浏览器 14+ iOS 微信内嵌网页 ✖ Android 移动版 Chrome 浏览器 81+ Android 微信内嵌网页(TBS 内核) ✔ Android 移动版 QQ 浏览器 ✖
实现流程
1. 安装与注册插件
import TRTC from 'trtc-sdk-v5';
import AudioPlayer from 'trtc-sdk-v5/plugins/audio-player';
const trtc = TRTC.create({ plugins: [AudioPlayer] });
2. 进房并打开麦克风
await trtc.enterRoom({ roomId: 8888, sdkAppId, userId, userSig });
await trtc.startLocalAudio();
3. 创建 PCM 播放器
使用 trtc.startPlugin 创建一个 PCM 播放器实例。
Realtime 模式(适用于 AI 实时对话)
const player = await trtc.startPlugin('AudioPlayer', {
id: 'ai-voice',
sourceType: 'pcm',
realtime: true, // 默认值,可省略
publish: true, // 混入发布流推送给远端用户
onTimeUpdate: (currentTime, duration) => {
console.log(`播放时间: ${currentTime.toFixed(2)}s`);
},
onDurationChange: (duration) => {
console.log(`缓冲区时长: ${duration.toFixed(2)}s`);
},
onEnded: () => {
console.log('缓冲区播放完毕');
},
});
非 Realtime 模式(适用于预加载完整播放)
const player = await trtc.startPlugin('AudioPlayer', {
id: 'tts-player',
sourceType: 'pcm',
realtime: false,
publish: false,
onTimeUpdate: (currentTime, duration) => {
console.log(`${currentTime.toFixed(2)}s / ${duration.toFixed(2)}s`);
},
onEnded: () => {
console.log('播放结束');
},
});
4. 输入 PCM 数据并播放
PCM 播放器通过 input() 方法输入音频数据,支持 Float32Array 和 Int16Array 格式,支持任意采样率(内部自动重采样到 48kHz)。
注意:创建播放器后即可调用
input()添加数据到缓冲区,无需等待start()。
Realtime 模式示例
// 开始播放
await player.start();
// 持续输入 PCM 数据(如来自 AI 语音合成的流式数据)
player.input(float32Data, 48000); // Float32Array, 48kHz
player.input(int16Data, 16000); // Int16Array, 16kHz(自动转换并重采样)
// 暂停:清空缓冲区
player.pause();
// 恢复:等待新数据输入
player.resume();
player.input(newData, 16000);
// 停止:清空缓冲区
player.stop();
// 重新开始新一轮对话
player.input(newSessionData, 16000);
await player.start();
非 Realtime 模式示例
// 先加载所有 PCM 数据(start 前即可 input)
for (const chunk of pcmChunks) {
player.input(chunk, 16000);
}
// 数据加载完毕后开始播放
await player.start();
// 暂停:保留缓冲区和播放位置
player.pause();
// 恢复:从暂停位置继续
player.resume();
// 停止:保留缓冲区,重置到头部
player.stop();
// 重新从头播放(无需重新 input)
await player.start();
// 停止后继续追加数据
player.stop();
player.input(additionalData, 16000);
await player.start(); // 从头播放所有数据(包括新追加的)
5. 多声道 PCM 数据
当输入的 PCM 数据为多声道交错格式(如双声道 L R L R...)时,需要通过 channels 参数指定声道数。插件会自动将多声道数据下混为单声道后播放。
const player = await trtc.startPlugin('AudioPlayer', {
id: 'stereo-pcm',
sourceType: 'pcm',
channels: 2, // 指定输入数据为双声道
publish: true,
});
await player.start();
// 输入双声道交错数据 [L0, R0, L1, R1, ...]
player.input(stereoInt16Data, 44100);
⚠️ 重要:
channels默认为 1(单声道)。如果不指定channels而输入多声道数据,会导致播放速度异常(如双声道数据以单声道播放会变为半速)。
6. 销毁播放器
不再需要播放器时,调用 stopPlugin 销毁:
// 销毁指定播放器
await trtc.stopPlugin('AudioPlayer', { id: 'ai-voice' });
player = null; // 主动解除引用,避免内存泄漏
// 销毁所有播放器
await trtc.stopPlugin('AudioPlayer', { id: '*' });
⚠️ 重要:调用
stopPlugin销毁播放器后,务必将播放器实例引用设为null,否则实例对象无法被垃圾回收,会造成内存泄漏。
API 说明
trtc.startPlugin('AudioPlayer', options)
创建一个 PCM 音频播放器实例。
options
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| id | string |
✅ | - | 播放器实例唯一标识 |
| sourceType | string |
✅ | - | 音频源类型,设置为 'pcm' |
| publish | boolean |
❌ | false |
是否混入发布流推送给远端用户 |
| channels | number |
❌ | 1 |
PCM 输入数据声道数(1~8),多声道数据自动下混为单声道 |
| realtime | boolean | object |
❌ | true |
实时模式配置,详见下方说明 |
| onTimeUpdate | function |
❌ | - | 播放进度回调,参数为 (currentTime: number, duration: number),单位为秒 |
| onDurationChange | function |
❌ | - | 缓冲区时长变化回调(input/clear 后触发),参数为 (duration: number),单位为秒 |
| onEnded | function |
❌ | - | 缓冲区数据播放完毕或调用 stop() 时触发 |
| onInputError | function |
❌ | - | 输入数据错误回调,参数为 (errMsg: string, inputIndex: number) |
返回值: Promise<AudioPlayerContext> — 播放器实例
realtime 参数说明
| 值 | 说明 |
|---|---|
true(默认) |
实时模式,pause/stop 时清空缓冲区 |
false |
非实时模式,pause/stop 时保留缓冲区数据 |
{ maxDelay: number, discardAll: boolean } |
实时模式高级配置 |
高级配置:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
maxDelay |
number |
300 |
最大延迟阈值(毫秒)。当缓冲区中未播放的数据时长超过此值时,触发丢弃策略 |
discardAll |
boolean |
false |
丢弃策略。设置为 true 时,超过 maxDelay 会清空所有缓冲数据;设置为 false 时,不做丢弃处理,数据继续累积 |
discardAll 行为差异:
| 场景 | discardAll: true |
discardAll: false(默认) |
|---|---|---|
| 缓冲区 < maxDelay | 正常播放 | 正常播放 |
| 缓冲区 > maxDelay | 清空缓冲区,从新输入的数据开始播放 | 不做处理,数据继续累积,延迟持续增大 |
| 网络波动恢复后数据积压 | 丢弃积压数据,快速恢复到实时 | 积压数据全部播放完才能追上实时 |
💡 建议:对于 AI 实时对话等对延迟敏感的场景,建议设置
discardAll: true,避免因网络波动或数据积压导致播放延迟持续增大。discardAll: false适用于不希望丢失任何音频数据的场景。
// AI 实时对话推荐配置
const player = await trtc.startPlugin('AudioPlayer', {
id: 'ai-voice',
sourceType: 'pcm',
realtime: {
maxDelay: 500, // 最大延迟 500ms(默认 300ms)
discardAll: true, // 推荐:超过 maxDelay 时丢弃所有缓冲数据,保持低延迟
},
});
播放器实例方法
| 方法 | 说明 |
|---|---|
input(pcmData, sampleRate) |
输入 PCM 数据,pcmData 支持 Float32Array 和 Int16Array,sampleRate 为采样率(如 16000、48000),内部自动重采样到 48kHz |
clearInput() |
清空所有缓冲数据,duration 和 currentTime 重置为 0 |
start() |
开始播放(从缓冲区头部),返回 Promise |
pause() |
暂停播放(realtime 模式会清空缓冲区) |
resume() |
恢复播放(仅在 paused 状态有效,stop 后无效需调用 start) |
stop() |
停止播放(realtime 模式清空缓冲区,非 realtime 保留缓冲区并重置播放位置) |
播放器实例只读属性
| 属性 | 类型 | 说明 |
|---|---|---|
id |
string |
播放器实例 ID |
sourceType |
string |
音频源类型 |
currentTime |
number |
当前播放时间(秒) |
duration |
number |
缓冲区总时长(秒) |
isStop |
boolean |
是否已停止 |
isPause |
boolean |
是否已暂停 |
isPlayEnd |
boolean |
是否播放结束(缓冲区播完) |
播放器实例可读写属性
| 属性 | 类型 | 说明 |
|---|---|---|
publish |
boolean |
是否混入发布流 |
trtc.stopPlugin('AudioPlayer', options)
销毁播放器实例。
options
| 参数 | 类型 | 说明 |
|---|---|---|
| id | string |
目标播放器实例 ID,传 '*' 销毁所有实例 |
示例:
// 销毁指定播放器
await trtc.stopPlugin('AudioPlayer', { id: 'ai-voice' });
// 销毁所有播放器
await trtc.stopPlugin('AudioPlayer', { id: '*' });
// 释放播放器引用
player = null;
⚠️ 重要:调用
stopPlugin销毁播放器后,务必将播放器实例引用设为null,否则实例对象无法被垃圾回收,会造成内存泄漏。
常见问题
1. 输入多声道 PCM 数据后播放速度异常(变慢)
原因:多声道交错数据(如双声道 L R L R...)被当作单声道处理,样本数翻倍导致播放时长翻倍。
解决:创建播放器时通过 channels 参数指定正确的声道数:
const player = await trtc.startPlugin('AudioPlayer', {
id: 'pcm',
sourceType: 'pcm',
channels: 2, // 双声道
});
2. publish 设置为 true 但远端听不到
解决:使用 publish: true 时,需要先进入房间并发布音频流(调用 trtc.startLocalAudio()),确保本地音频已发布后再创建播放器。
3. iOS 设备无法播放
解决:iOS 浏览器要求音频播放必须由用户手势触发,请确保 player.start() 在用户点击事件的回调中调用。
4. Realtime 模式下 pause 后数据丢失
这是预期行为。Realtime 模式下 pause() 会清空缓冲区,适用于 AI 对话场景中打断当前回复。如果需要保留缓冲区数据,请使用非 realtime 模式(realtime: false)。
5. 非 Realtime 模式下 stop 后能否从头重新播放
可以。非 realtime 模式下 stop() 会保留缓冲区数据并重置播放位置,再次调用 start() 即可从头播放,无需重新 input() 数据。
6. 如何同时播放多个 PCM 音频
AudioPlayer 支持多实例管理,通过不同的 id 创建多个播放器即可:
const voice1 = await trtc.startPlugin('AudioPlayer', {
id: 'voice1',
sourceType: 'pcm',
publish: true,
});
const voice2 = await trtc.startPlugin('AudioPlayer', {
id: 'voice2',
sourceType: 'pcm',
publish: true,
});
await voice1.start();
await voice2.start();
voice1.input(data1, 16000);
voice2.input(data2, 16000);
7. PCM 数据采样率与播放器不匹配
input() 方法支持任意采样率输入(如 8000、16000、24000、44100、48000 等),插件内部会自动重采样到 48kHz,无需手动转换。只需在调用 input() 时传入正确的采样率参数即可:
// 16kHz 的 TTS 数据
player.input(ttsData, 16000);
// 44.1kHz 的音频数据
player.input(audioData, 44100);
8. Realtime 模式下播放延迟越来越大
原因:当 input() 的数据速率大于播放消费速率(如网络波动恢复后大量数据一次性到达),缓冲区会持续积压,导致用户听到的音频越来越滞后于实时。
解决:在创建播放器时启用 discardAll: true,当缓冲区积压超过 maxDelay 时自动丢弃旧数据,保持播放延迟可控:
const player = await trtc.startPlugin('AudioPlayer', {
id: 'ai-voice',
sourceType: 'pcm',
realtime: {
maxDelay: 300, // 最大允许延迟 300ms
discardAll: true, // 超过阈值时丢弃所有积压数据
},
});
注意:
discardAll默认为false,即不会自动丢弃数据。在正常的 AI TTS 流式输出场景中(数据速率 ≈ 实时播放速率),通常不会出现积压问题。但如果对延迟敏感,建议显式设置discardAll: true。