Skip to main content
功能參考

自訂音頻播放

AOQ Client SDK 支援自訂音頻播放功能,通過音訊框架回調機制將解碼後的 PCM 資料回調給應用程式層,由開發人員自行實現音頻渲染播放。

功能介紹

AOQ Client SDK 內部音頻模組預設會將接收到的遠端音頻資料通過系統擴音器/耳機播放,但在特定情境中,SDK 內部的音頻播放模組可能無法滿足開發需求,需要實現自訂音頻播放功能,例如:
  • 需要將接收到的音頻資料輸出到自訂的播放裝置或音頻處理管線。
  • 需要對接收到的音頻資料進行二次處理(如 AI 語音辨識、音效處理等)。
  • 解決音頻播放裝置被佔用的問題。
AOQ Client SDK 支援靈活的自訂播放功能,通過音訊框架回調機制,將解碼後的 PCM 資料回調給應用程式層,由開發人員自行實現音頻渲染播放。

範例程式碼

暫無

前提條件

  • 已建立引擎執行個體(調用 createEngine)。
  • 已成功串連伺服器(onConnectionStatusChange 回調狀態為 AoqConnectionStatusConnected)。

功能實現

1. 開啟音頻播放(外部模式)

調用 startAudioPlayer 時設定 isExternal=true,關閉 SDK 內部的音頻渲染裝置,由應用程式層自行處理音頻播放。
AoqClientEngine.AoqAudioPlaybackConfig config = new AoqClientEngine.AoqAudioPlaybackConfig();
config.isExternal = true;  // 關閉 SDK 內部播放,由應用程式層自行渲染
config.channel = 1;        // 聲道數
engine.startAudioPlayer(config);
參數說明:

參數

類型

預設值

說明

isVoipMode

boolean

false

是否啟用 VoIP 模式(硬體AEC),移動端有效

isDefaultSpeaker

boolean

true

是否預設擴音器,移動端有效

isExternal

boolean

false

是否外部播放模式,true 時 SDK 不開啟播放裝置

channel

int

1

聲道數

2. 設定音訊框架回調監聽

調用 setAudioFrameObserver 設定音訊框架資料回調監聽器,實現 onPlaybackAudioFrame 回調方法接收播放 PCM 資料。
engine.setAudioFrameObserver(new AoqClientListener.AoqAudioFrameListener() {
    @Override
    public void onPlaybackAudioFrame(@NonNull AoqClientEngine.AoqAudioFrameData frame) {
        // 在此處理接收到的播放音頻資料
        // frame.dataPtr: PCM 資料
        // frame.numOfSamples: 採樣點數
        // frame.numOfChannels: 聲道數
        // frame.samplesPerSec: 採樣率
        // frame.bytesPerSample: 每採樣點位元組數
        playPcmData(frame);
    }
});

3. 開啟播放資料回調

調用 enableAudioFrameObserver 開啟播放位置的音訊框架回調,指定資料來源為 AoqAudioSourcePlayback
AoqClientEngine.AoqAudioObserverConfig observerConfig = new AoqClientEngine.AoqAudioObserverConfig();
observerConfig.sampleRate = 48000;  // 回調音頻採樣率
observerConfig.channels = 1;       // 回調音頻聲道數
observerConfig.mode = AoqClientEngine.AoqAudioObserverMode.AoqAudioObserverModeReadOnly; // 唯讀模式

engine.enableAudioFrameObserver(
    true,  // 開啟回調
    AoqClientEngine.AoqAudioSource.AoqAudioSourcePlayback,  // 播放資料來源
    observerConfig
);
參數說明:

參數

類型

預設值

說明

sampleRate

int

48000

回調音頻採樣率(Hz)

channels

int

1

回調音頻聲道數

mode

AoqAudioObserverMode

AoqAudioObserverModeReadOnly

讀寫入模式

4. 實現自訂音頻渲染

onPlaybackAudioFrame 回調中接收到 PCM 資料後,由應用程式層自行實現音頻渲染播放。常見的實現方式:
  • Android AudioTrack:通過 AudioTrack 將 PCM 資料寫入系統音訊裝置播放。
  • AI 語音辨識:將 PCM 資料傳入 ASR 引擎進行語音辨識。
  • 音效處理:對 PCM 資料進行音效處理後再播放。
  • 檔案儲存體:將接收到的音頻資料儲存到本地檔案。
// 樣本:使用 Android AudioTrack 播放
private AudioTrack mAudioTrack;
private volatile boolean mPlayRunning = false;

private void initAudioTrack(int sampleRate, int channels) {
    int channelConfig = (channels == 2)
        ? AudioFormat.CHANNEL_OUT_STEREO
        : AudioFormat.CHANNEL_OUT_MONO;
    int bufferSize = AudioTrack.getMinBufferSize(
        sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT);

    mAudioTrack = new AudioTrack(
        AudioManager.STREAM_VOICE_CALL,
        sampleRate,
        channelConfig,
        AudioFormat.ENCODING_PCM_16BIT,
        bufferSize,
        AudioTrack.MODE_STREAM);
    mAudioTrack.play();
    mPlayRunning = true;
}

private void playPcmData(AoqClientEngine.AoqAudioFrameData frame) {
    if (!mPlayRunning || mAudioTrack == null) {
        return;
    }
    if (frame.dataPtr != null && frame.dataSize > 0) {
        mAudioTrack.write(frame.dataPtr, 0, frame.dataSize);
    }
}
注意事項:
  • onPlaybackAudioFrame 回調在 SDK 內部線程觸發,回調中的 frame.dataPtr 僅在回調期間有效,非同步使用需自行拷貝。
  • AudioTrack.write 是阻塞操作,在回調中直接寫入即可,SDK 內部會按節奏回調。
  • 需要維護 mPlayRunning 標記,當引擎退出或停止播放時退出處理邏輯。

5. 停止自訂播放

當不再需要自訂播放時,先關閉音訊框架回調,再停止播放裝置,釋放 AudioTrack 資源。
// 1. 關閉播放位置的音訊框架回調
AoqClientEngine.AoqAudioObserverConfig observerConfig = new AoqClientEngine.AoqAudioObserverConfig();
engine.enableAudioFrameObserver(
    false,  // 關閉回調
    AoqClientEngine.AoqAudioSource.AoqAudioSourcePlayback,
    observerConfig
);

// 2. 移除音訊框架回調監聽
engine.setAudioFrameObserver(null);

// 3. 停止 SDK 音頻播放
engine.stopAudioPlayer();

// 4. 釋放 AudioTrack 資源
mPlayRunning = false;
if (mAudioTrack != null) {
    mAudioTrack.stop();
    mAudioTrack.release();
    mAudioTrack = null;
}