TRTC Web SDKAPI 文档事件错误码类型教程更新日志
En

Tutorial: PCM 播放方案

PCM 播放方案

功能描述

本文将介绍如何使用 AudioPlayer 插件实现 PCM 音频播放功能。PCM 播放器适用于 AI 对话、语音合成(TTS)、实时音频流等场景,通过 input() 方法持续输入 PCM 数据进行播放,并可将音频混入发布流推送给远端用户。

PCM 播放器支持两种工作模式:

  • Realtime 模式(默认):适用于 AI 实时对话等低延迟场景,pause/stop 时清空缓冲区
  • 非 Realtime 模式:适用于预加载音频后完整播放的场景,pause/stop 时保留缓冲区数据

体验在线 Demo

点击运行 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() 方法输入音频数据,支持 Float32ArrayInt16Array 格式,支持任意采样率(内部自动重采样到 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 支持 Float32ArrayInt16ArraysampleRate 为采样率(如 16000、48000),内部自动重采样到 48kHz
clearInput() 清空所有缓冲数据,durationcurrentTime 重置为 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