Skip to main content
功能參考

自訂音頻採集

介紹如何使用 AOQ Client SDK 實現自訂音頻採集功能,包括外部音頻流的添加、PCM 資料推送和管理。

功能介紹

AOQ Client SDK 內部音頻模組可滿足應用中對基本音頻功能的需求,但在特定情境中,SDK 內部的音頻採集模組可能無法滿足開發需求,需要實現自訂音頻採集功能,例如:
  • 解決音頻採集裝置被佔用問題。
  • 需要從定製的採集系統、音頻檔案中擷取音頻資料後交給 SDK 傳輸。
  • 需要將 AI TTS 產生的音頻資料通過 SDK 推流傳輸。
AOQ Client SDK 支援靈活的自訂採集功能,允許使用者根據業務情境自行管理音訊裝置與音頻源。外部音頻流的資料會與內部採集的音頻資料混音後一起推流發送。

範例程式碼

暫無

前提條件

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

功能實現

1. 開啟或關閉音頻採集

需要先開啟音頻採集,外部音頻流輸入的資料會與內部採集資料混音後一起推流。如果不需要內部麥克風採集,可以設定 isExternal=true 關閉內部採集裝置。
// 方式一:開啟內部採集,外部音頻流資料會與麥克風資料混音推流
AoqClientEngine.AoqAudioCaptureConfig config = new AoqClientEngine.AoqAudioCaptureConfig();
config.isExternal = false; // 使用內部麥克風採集
config.isVoipMode = false;
engine.startAudioCapture(config);

// 方式二:關閉內部採集,僅推送外部音頻流資料
AoqClientEngine.AoqAudioCaptureConfig config = new AoqClientEngine.AoqAudioCaptureConfig();
config.isExternal = true; // 不開啟麥克風,由外部音頻流提供資料
engine.startAudioCapture(config);

2. 串連成功後,添加外部音頻流

onConnectionStatusChange 回調狀態變為 AoqConnectionStatusConnected 後,調用 addAudioExternalStream 添加外部音頻流。需要指定一個唯一的 streamId 用於後續推送資料和管理。 如果需要音頻 3A 處理(回聲消除、雜訊抑制、自動增益),請配置 AoqAudioExternalStreamConfig 中的 enable3A 參數。
// 在 onConnectionStatusChange 回調中確認串連成功後添加
@Override
public void onConnectionStatusChange(AoqClientEngine.AoqConnectionStatus status) {
    if (status == AoqClientEngine.AoqConnectionStatus.AoqConnectionStatusConnected) {
        addExternalAudioStream();
    }
}

private void addExternalAudioStream() {
    AoqClientEngine.AoqAudioExternalStreamConfig config = new AoqClientEngine.AoqAudioExternalStreamConfig();
    config.sampleRate = 48000;       // 採樣率,需與實際音頻資料一致
    config.channels = 1;             // 聲道數
    config.publishVolume = 100;      // 推流音量 [0-100]
    config.playoutVolume = 0;        // 本地播放音量 [0-100],0 表示不本地播放
    config.maxBufferDuration = 1000; // 最大緩衝時間長度(毫秒)
    config.enable3A = true;          // 是否對輸入 PCM 進行 3A 處理

    String streamId = "external_audio_1";
    int ret = engine.addAudioExternalStream(streamId, config);
    if (ret == 0) {
        mExternalStreamId = streamId;
    }
}
參數說明:

參數

類型

預設值

說明

trackType

AoqTrackType

AoqTrackTypeAudio

音頻軌道類型

codecType

AoqEncoderType

AoqEncoderTypeAudioPCM

音頻流格式

channels

int

1

聲道數

sampleRate

int

48000

採樣率(Hz)

playoutVolume

int

100

播放音量 [0-100]

publishVolume

int

100

推流音量 [0-100]

maxBufferDuration

int

1000

最大緩衝時間長度(毫秒)

enable3A

boolean

false

是否對輸入 PCM 進行 3A 處理

3. 實現自採集模組或從檔案擷取 PCM 資料

自訂採集功能需要根據業務情境自行採集並處理音頻資料,之後將資料傳入 SDK 進行傳輸。常見的資料來源:
  • 麥克風採集:通過 Android AudioRecord 採集 PCM 資料。
  • 檔案讀取:從本地 PCM/WAV 音頻檔案中解析擷取 PCM 資料。
  • AI TTS:從語音合成引擎擷取 PCM 資料。
  • 網路流:從網路音頻流中解碼擷取 PCM 資料。
音頻資料需要為 PCM 格式,並記錄對應的採樣率、聲道數等參數,用於構造 AoqAudioFrameData 對象。

4. 通過外部音頻流 ID 推送音頻資料到 SDK

調用 pushAudioExternalStreamData 介面,將採集到的 PCM 音頻資料傳入 SDK。
  • 從硬體採集:建議採集 10ms 為一幀資料,採集到資料就 push 給 SDK。
  • 從檔案解析:建議 40ms 為一幀資料,每 push 一幀 Sleep 30ms 後 push 下一幀。
  • 需要維護一個 running 標記,當引擎退出或 stream ID 被刪除時退出推送迴圈。
// 成員變數:控制推送迴圈的運行標記
private volatile boolean mPushRunning = false;

// 推送單幀音頻資料

private void pushAudioData(byte[] audioData, int bytesRead) {

    if (engine == null || mExternalStreamId == null || bytesRead <= 0) {
        return;
    }

    int channels = 1;
    int bytesPerSample = 2; // 16bit PCM
    int sampleRate = 48000;

    // 構造音訊框架資料
    AoqClientEngine.AoqAudioFrameData frameData = new AoqClientEngine.AoqAudioFrameData();
    frameData.dataPtr = audioData;
    frameData.dataSize = bytesRead;
    frameData.numOfSamples = bytesRead / (channels * bytesPerSample);
    frameData.bytesPerSample = bytesPerSample;
    frameData.numOfChannels = channels;
    frameData.samplesPerSec = sampleRate;

    // 推送資料,處理緩衝區滿的情況
    int ret;
    final int WAIT_MS = 30;

    do {
        // 檢查運行標記和 stream ID 是否仍有效
        if (!mPushRunning || mExternalStreamId == null) {
            break;
        }
        ret = engine.pushAudioExternalStreamData(mExternalStreamId, frameData);
        if (ret == 110) { // AoqErrorCodeAudioExternalBufferFull
            try {
                Thread.sleep(WAIT_MS);
            } catch (InterruptedException e) {
                break;
            }
        } else {
            break;
        }
    } while (true);
}
注意事項:
  • 需要在串連成功且添加外部音頻流之後再開始推送資料。
  • 需要按照資料的實際長度設定 AoqAudioFrameDatanumOfSamples
  • 調用 pushAudioExternalStreamData 時,可能出現內部緩衝區滿(錯誤碼 110)而導致失敗,需要等待重試。
  • 即時採集建議 10ms 一幀資料 push,有資料就調用 push,注意處理內部緩衝區滿(錯誤碼 110)。
  • 從檔案解析建議 40ms 一幀資料,間隔 30ms 調用 push,注意處理內部緩衝區滿(錯誤碼 110)。
  • 引擎退出(destroy)或 stream ID 被移除前,必須先設定 mPushRunning = false 停止推送迴圈,避免在已釋放的資源上操作。

5. 移除外部音頻流

當不再需要發布自訂採集的音頻時,先停止推送迴圈,再調用 removeAudioExternalStream 介面移除外部音頻流。
// 先停止推送
stopPushAudio();
// 再移除外部音頻流
engine.removeAudioExternalStream(mExternalStreamId);
mExternalStreamId = null;
自訂音頻採集 - Alibaba Cloud Model Studio