如何使用SDK播放音频裸数据或本地文件

本文介绍如何通过ARTC SDK 使用外部音频源,实现播放 PCM 音频裸数据或本地音频文件。

功能介绍

ARTC SDK支持通过两种方式使用外部音频源:注入 PCM 格式的音频裸数据,或播放本地音频文件(如 MP3、WAV、AAC 等)。您可以根据业务场景选择合适的方案。

音频源

主要接口

适用场景

PCM音频裸数据

  • addExternalAudioStream

  • pushExternalAudioStreamRawData

  • removeExternalAudioStream

适用于需要实时注入和处理原始音频数据(如 TTS、自定义音频效果器)的场景。此方案灵活性高,但需要您自行管理数据缓存和推送时序。

本地音频文件

  • startAudioAccompany

  • stopAudioAccompany

用于播放本地音频文件(如 MP3、WAV、AAC),SDK 自动处理解码和播放控制,适合伴奏、音效等文件类音频播放。

前提条件

在实现相关功能前,请确保满足以下条件:

功能实现

播放音频裸数据

image

1. (可选)提前启动播放器

如果您需要在加入频道前播放,请调用startAudioPlayer()提前启动播放器,否则不需要调用。

Android

mAliRtcEngine.startAudioPlayer();

iOS

self.rtcEngine?.startAudioPlayer()

2. 添加外部音频流(仅播放)

调用addExternalAudioStream接口向 AliRtcEngine 对象中添加外部音频流,添加成功后会返回改音频流的唯一标识 audioStreamID。

说明

如果只需要播放而不需要推流,需要设置推流音量为 0。

Android

/* 根据自己的业务设置对应的参数 */
AliRtcEngine.AliRtcExternalAudioStreamConfig config = new AliRtcEngine.AliRtcExternalAudioStreamConfig();
/* 设置播放音量为0 ,意味着不进行本地播放 */
config.playoutVolume = 100;
/* 设置推流音量为0, 意味着不进行推流 */
config.publishVolume = 0;
config.channels = 1;
config.sampleRate = 48000;
// 返回值为外部输入流ID,后续通过该ID将数据送入SDK
audioStreamID = mAliRtcEngine.addExternalAudioStream(config);

iOS

/* 根据自己的业务设置对应的参数 */
AliRtcExternalAudioStreamConfig *config = [AliRtcExternalAudioStreamConfig new];
config.channels = _pcmChannels;
config.sampleRate = _pcmSampleRate;
/* 设置播放音量为0 ,意味着不进行本地播放 */
config.playoutVolume = 0;
/* 设置推流音量为0, 意味着不进行推流 */
config.publishVolume = 100;
// 返回值为外部输入流ID,后续通过该ID将数据送入SDK
_externalPlayoutStreamId = [self.engine addExternalAudioStream:config];

3. 向音频流内送入PCM数据

将您获取到的音频流通过 pushExternalAudioStreamRawData 送入 SDK 进行播放。

Android

int duration = 30; // 单次送入SDK音频数据的时长,建议20~50ms
// 从缓存里获取接下来要播放的pcm数据(pcm播放时长不超过duration)
byte[] dataToSend = ...
// 检查dataToSend:如果未停止播放且数据太少可以sleep一下并返回等待更多缓存数据

// 计算dataToSend的采样点数
int numOfSamples = dataToSend的字节长度 / (2 * frameInfo.audio_channels);

// 构造AliRtcAudioFrame对象
AliRtcEngine.AliRtcAudioFrame rawData = new AliRtcEngine.AliRtcAudioFrame();
rawData.data = dataToSend;
rawData.numSamples = numOfSamples;
rawData.bytesPerSample = 2;
rawData.numChannels = mPcmChannels;
rawData.samplesPerSec = mPcmSampleRate;
// 向SDK送入数据
int ret = mAliRtcEngine.pushExternalAudioStreamRawData(audioStreamID, rawData);
if(ret == 0x01070101) {
    /* rtc buffer已经满,出现此错误时数据送入SDK失败需要重新送入  */
} 
else if(ret < 0) {
    /* 异常, 检查参数和推流状态 */
}
// 根据送入数据的固定时长睡眠
sleep(duration - 10);

iOS

guard let rtc = self.rtcEngine, self.streamId > 0 else {
    return
}

// 单次送给SDK的播放时长,建议为20~50ms
let duration = 30
// 从缓存里获取接下来要播放的pcm数据(pcm播放时长不超过duration)
let dataToSend = ...
// 检查dataToSend:如果未停止播放且数据太少可以sleep一下并返回等待更多缓存数据

// 计算dataToSend的采样点数
let numOfSamples = dataToSend的字节长度 / (2 * frameInfo.audio_channels)

let pushFrame = AliRtcAudioFrame()
pushFrame.dataPtr = dataToSend
pushFrame.numOfSamples = numOfSamples  // 采样点数
pushFrame.bytesPerSample = 2
pushFrame.samplesPerSec = self.pcmSampleRate
pushFrame.numOfChannels = self.pcmChannels
let result = rtc.pushExternalAudioStream(self.pushStreamId, rawData: pushFrame)
if result == 0x01070101 {
    /* rtc buffer已经满,出现此错误时数据送入SDK失败需要重新送入  */
}
else if result < 0 {
    /* 异常, 检查参数和推流状态 */
}
// 根据送入数据的固定时长睡眠
Thread.sleep(forTimeInterval: Double(duration - 10) / 1000.0)

4. 移除外部音频流

当播放结束的时候,通过removeExternalAudioStream接口移除对应的外部音频流。

Android

mAliRtcEngine.removeExternalAudioStream(audioStreamID);
audioStreamID = -1;

iOS

self.rtcEngine?.removeExternalAudioStream(self.streamId)
self.streamId = -1

播放本地文件

如果您的音频源为本地音频文件,可以通过伴奏的相关接口进行播放,同样可以无缝集成 RTC 的音频 3A 能力。

image

1. 开始播放

调用startAudioAccompany接口开始播放指定的音频文件。

说明

如果不需要推流,需要配置推流音量为 0。

Android

// 音频文件路径
private String mFilepath = "/assets/music.wav";

// 播放配置
AliRtcEngine.AliRtcAudioAccompanyConfig config = new AliRtcEngine.AliRtcAudioAccompanyConfig();
config.loopCycles = 1;     // 循环次数
config.publishVolume = 0;   // 只播放,不推流
config.playoutVolume = 100;
config.startPosMs = 0;      // 起始播放位置
// 开始播放
mRtcEngine.startAudioAccompany(mFilepath, config);

iOS

// 音频文件路径
let filePath = Bundle.main.path(forResource: "music", ofType: "wav")

// 播放配置
let config = AliRtcAudioAccompanyConfig()
config.loopCycles = 1   // 循环次数, -1表示无限循环
config.publishVolume = 0   // 只播放,不推流
config.playoutVolume = 100
config.startPosMs = 0
// 开始播放
let result = rtcEngine.startAudioAccompany(withFile: filePath, config: config)

开始播放后可以通过下面的接口进行播放控制:

  • pauseAudioAccompany:暂停播放

  • resumeAudioAccompany: 恢复播放

  • stopAudioAccompany:停止播放

2. 本地监听播放状态

启动播放后,本地会通过onAudioAccompanyStateChanged回调播放状态的变化。

Android

@Override
public void onAudioAccompanyStateChanged(ARTCAICallEngine.ARTCAICallAudioAccompanyStateCode
                                                 playState, ARTCAICallEngine.ARTCAICallAudioAccompanyErrorCode
                                                 errorCode) {
    if (playState == ARTCAICallEngine.ARTCAICallAudioAccompanyStateCode.ARTCAICallAudioAccompanyEnded) {
        // 处理播放结束
    }
}

iOS

func onAudioAccompanyStateChanged(state: ARTCAICallAudioAccompanyState, errorCode: ARTCAICallAudioAccompanyErrorCode) {
    if state == .ARTCAICallAudioAccompanyEnded {
        // 处理播放结束
    }
}

相关文档

常见问题

  1. startAudioAccompany支持播放多个文件吗?

    伴奏相关接口仅支持同一时间播放一个音频文件,如果需要播放多个文件可以使用音效相关接口,详细请参见播放与推流外部输入音频(包括音效、伴奏)

  2. 为什么调用 pushExternalAudioStreamRawData 返回 0x01070101

    返回值 0x01070101 表示 RTC 内部音频缓冲区已满,无法接收更多数据。建议

    适当降低送入数据的频率;增加 sleep 时间(如示例中的 duration - 10ms);检查是否送入数据过快或缓存管理不当。