全部產品
Search
文件中心

ApsaraVideo Live:自訂音頻採集

更新時間:Dec 10, 2025

ARTC SDK提供了靈活的自訂音頻採集功能。

功能介紹

ARTC SDK 內部音頻模組可滿足您在應用中對基本音頻功能的需求,但是在特定情境中,SDK 內部的音頻採集模組可能無法滿足開發需求,需要實現自訂音頻採集功能,例如:

  • 解決音頻採集裝置被佔用問題。

  • 開發人員需要從定製的採集系統、音頻檔案中擷取音頻資料後交給 SDK 傳輸。

ARTC SDK 支援靈活的自訂採集功能,允許使用者根據業務情境自行管理音訊裝置與音頻源。

範例程式碼

Android端自訂音頻採集Android/ARTCExample/AdvancedUsage/src/main/java/com/aliyun/artc/api/advancedusage/CustomAudioCaptureAndRender/CustomAudioCaptureActivity.java

iOS端自訂音頻採集iOS/ARTCExample/AdvancedUsage/CustomAudioCapture/CustomAudioCaptureVC.swift

前提條件

在設定視頻配置之前,請確保達成以下條件:

功能實現

1.開啟或關閉內部採集

當您需要使用 SDK 的自訂音頻採集功能時,通常需要關閉 SDK 內部音頻採集,推薦在調用getInstance建立引擎時傳入 extras 參數來關閉 SDK 內部採集,相關參數如下:

user_specified_use_external_audio_record:表示是否使用外部採集(關閉 SDK 內部採集)。

  • "TRUE":使用外部音頻採集,即關閉 SDK 內部採集

  • "FALSE":不使用外部音頻採集,即開啟 SDK 內部採集。

說明

extras 為一個 JSON 字串。

Android

String extras = "{\"user_specified_use_external_audio_record\":\"TRUE\"}";
mAliRtcEngine = AliRtcEngine.getInstance(this, extras);

iOS

// 建立並初始化引擎
var customAudioCaptureConfig: [String: String] = [:]
// 使用外部採集
customAudioCaptureConfig["user_specified_use_external_audio_record"] = "TRUE"
// 序列化為Json
guard let jsonData = try? JSONSerialization.data(withJSONObject: customAudioCaptureConfig, options: []),
let extras = String(data: jsonData, encoding: .utf8) else {
     print("JSON 序列化失敗")
     return
 }
let engine = AliRtcEngine.sharedInstance(self, extras:extras)

Windows

/* Windows支援建立時指定開啟/關閉音頻採集 */
/* 關閉阿里內部採集 */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"TRUE\", \"user_specified_use_external_audio_record\":\"TRUE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

/* 開啟阿里內部採集 */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"FALSE\", \"user_specified_use_external_audio_record\":\"FALSE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

2.添加外部音頻流

調用addExternalAudioStream介面添加外部音頻流,並擷取音頻流 ID。如果需要音頻 3A(回聲消除 AEC、自動增益控制 AGC、雜訊抑制 ANS),請配置AliRtcExternalAudioStreamConfig中的 enable3A 參數。

說明

介面調用時機:

  • 如果需要使用 3A 的情境,推薦在音頻推流成功且自訂採集模組擷取到第一幀音頻後調用addExternalAudioStream添加,即onAudioPublishStateChanged介面返回newStateAliRtcStatsPublished(3)之後添加。

  • 如果不需要經過 3A 的情境例如傳輸本地檔案、網路檔案、TTS 產生的音頻資料等,可以在建立引擎後添加,然後在音頻推流成功後開始推送音頻資料。

Android

AliRtcEngine.AliRtcExternalAudioStreamConfig config = new AliRtcEngine.AliRtcExternalAudioStreamConfig();
config.sampleRate = SAMPLE_RATE; // 採樣率
config.channels = CHANNEL; // 通道數
// 推流音量
config.publishVolume = 100;
// 本地播放音量
config.playoutVolume = isLocalPlayout ? 100 : 0;
config.enable3A = true;

int result = mAliRtcEngine.addExternalAudioStream(config);
if (result <= 0) {
    return;
}
// 傳回值為streamid,後續向SDK內push資料需要用到
mExternalAudioStreamId = result;

iOS

/* 根據自己的業務設定對應的參數 */
AliRtcExternalAudioStreamConfig *config = [AliRtcExternalAudioStreamConfig new];
//需要和外部PCM音頻流的聲道數相同,如果是單聲道設定成1,雙聲道設定成2
config.channels = _pcmChannels;
//需要和外部PCM音頻流的採樣率相同
config.sampleRate = _pcmSampleRate;
config.playoutVolume = 0;
config.publishVolume = 100;
_externalPlayoutStreamId = [self.engine addExternalAudioStream:config];

Windows

/* 擷取媒體引擎 */
IAliEngineMediaEngine* mAliRtcMediaEngine = nullptr;
    
mAliRtcEngine->QueryInterface(AliEngineInterfaceMediaEngine, (void **)&mAliRtcMediaEngine);
/* 根據自己的業務設定對應的參數 */
AliEngineExternalAudioStreamConfig config;
config.playoutVolume = currentAudioPlayoutVolume;
config.publishVolume = currentAudioPublishVolume;
config.channels = 1;
config.sampleRate = 48000;
config.publishStream = 0;
audioStreamID = mAliRtcMediaEngine->AddExternalAudioStream(config);

mAliRtcMediaEngine->Release();

3.實現音視頻自採集模組

自訂採集功能需要根據您的業務情境自行採集並處理音頻資料,之後將資料傳入 SDK 進行傳輸。

阿里雲提供了範例程式碼,示範從本地 PCM 檔案或者麥克風讀取 PCM 格式的資料,相關實現請參考自採集樣本

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

在音頻推流成功後(onAudioPublishStateChanged 回調狀態變為AliRtcStatsPublished),調用pushExternalAudioStreamRawData介面,將mExternalAudioStreamId參數設定為步驟 2 擷取的音頻流 ID,將採集到的音頻資料傳入 SDK。採集到的音頻資料需要轉化為 AliRtcAudioFrame 對象,相關配置:

  • data:音頻資料。

  • numSamples:採樣點數,表示傳入的資料總共有多少個採樣點(單個聲道)。

  • bytesPerSample:每個採樣點有多少位元組,該節點與採樣點的位元位深相關聯,通常採用 16bit 位深 PCM,對應的位元組數就是 2 位元組。

  • numChannels:聲道數。

  • samplesPerSec:採樣率,例如 16000、48000 等。

說明
  • 需要在音頻推流成功(回調onAudioPublishStateChanged 狀態為AliRtcStatsPublished )後再開始送入資料。

  • 需要按照資料的實際長度設定 AliRtcAudioFramenumSamples。部分裝置上例如通過 AudioRecord.read 擷取的音頻資料長度可能小於傳入的 buffer,需要根據傳回值確定真實的音頻資料長度。

  • 調用pushExternalAudioStreamRawData傳入資料時,可能出現內部緩衝區滿而導致失敗的情形,需要進行處理並重傳。

  • 通常每隔 10ms 調用pushExternalAudioStreamRawData送入一次資料。

Android

// 假設您採集到的音頻資料位元於audioData中,資料大小為bytesRead 位元組,為10ms資料
if (mAliRtcEngine != null && bytesRead > 0) {
    // 構造AliRtcAudioFrame對象,bitsPerSample為位元位深通常為16
    AliRtcEngine.AliRtcAudioFrame sample = new AliRtcEngine.AliRtcAudioFrame();
    sample.data = audioData;
    sample.numSamples = bytesRead / (channels * (bitsPerSample / 8)); // 根據實際讀取的位元組數計算樣本數
    sample.numChannels = channels;
    sample.samplesPerSec = sampleRate;
    sample.bytesPerSample = bitsPerSample / 8;

    int ret = 0;
    // 當緩衝區滿導致push失敗的時候需要進行重試
    int retryCount = 0;
    final int MAX_RETRY_COUNT = 20;
    final int BUFFER_WAIT_MS = 10;
    do {
        // 將擷取的資料送入SDK
        ret = mAliRtcEngine.pushExternalAudioStreamRawData(mExternalAudioStreamId, sample);
        if(ret == ErrorCodeEnum.ERR_SDK_AUDIO_INPUT_BUFFER_FULL) {
            // 處理緩衝區滿的情況,等待一段時間重試,最多重試幾百ms
            retryCount++;
            if(mExternalAudioStreamId <= 0 || retryCount >= MAX_RETRY_COUNT) {
                // 已經停止推流或者重試次數過多,退出迴圈
                break;
            }

            try {
                // 暫停一段時間
                Thread.sleep(BUFFER_WAIT_MS);
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        } else {
            // 推送成功或者其他錯誤直接退出迴圈
            break;
        }
    } while (retryCount < MAX_RETRY_COUNT);
}

iOS

// 將採集到的音頻資料構造為AliRtcAudioFrame對象
let sample = AliRtcAudioFrame()
sample.dataPtr = UnsafeMutableRawPointer(mutating: pcmData)
sample.samplesPerSec = pcmSampleRate
sample.bytesPerSample = Int32(MemoryLayout<Int16>.size)
sample.numOfChannels = pcmChannels
sample.numOfSamples = numOfSamples

var retryCount = 0

while retryCount < 20 {
    if !(pcmInputThread?.isExecuting ?? false) {
        break
    }
    // 推送音頻資料到SDK
    let rc = rtcEngine?.pushExternalAudioStream(externalPublishStreamId, rawData: sample) ?? 0

    // 處理緩衝區滿
    // 0x01070101 SDK_AUDIO_INPUT_BUFFER_FULL 緩衝區滿了需要重傳
    if rc == 0x01070101 && !(pcmInputThread?.isCancelled ?? true) {
        Thread.sleep(forTimeInterval: 0.03) // 30ms
        retryCount += 1;
    } else {
        if rc < 0 {
            "pushExternalAudioStream error, ret: \(rc)".printLog()
        }
        break
    }
}

Windows

說明

Windows 端在實現自訂採集功能之前,需要調用QueryInterface介面擷取媒體引擎對象。

/* 擷取媒體引擎 */
IAliEngineMediaEngine* mAliRtcMediaEngine = nullptr;   
mAliRtcEngine->QueryInterface(AliEngineInterfaceMediaEngine, (void **)&mAliRtcMediaEngine);

// 將資料構造為音訊框架
AliEngineAudioRawData rawData;
rawData.dataPtr = frameInfo.audio_data[0];
rawData.numOfSamples = (int) (frameInfo.audio_data[0].length / (2 * frameInfo.audio_channels));
rawData.bytesPerSample = 2;
rawData.numOfChannels = frameInfo.audio_channels;
rawData.samplesPerSec = frameInfo.audio_sample_rate;
// 推送資料到SDK
int ret = mAliRtcMediaEngine->PushExternalAudioStreamRawData(audioStreamID, rawData);
// 處理緩衝區滿等錯誤

// 釋放媒體引擎
mAliRtcMediaEngine->Release();

5.移除外部音頻流

如果您需要停止發布自訂音頻採集的音頻,調用removeExternalAudioStream介面移除外部音頻流。

Android

mAliRtcEngine.removeExternalAudioStream(mExternalAudioStreamId);

iOS

[self.engine removeExternalAudioStream:_externalPublishStreamId];

Windows

/* 擷取媒體引擎 */
IAliEngineMediaEngine* mAliRtcMediaEngine = nullptr;
    
mAliRtcEngine->QueryInterface(AliEngineInterfaceMediaEngine, (void **)&mAliRtcMediaEngine);

mAliRtcMediaEngine->RemoveExternalAudioStream(audioStreamID);
mAliRtcMediaEngine->Release();

6.(可選)動態開啟或關閉 SDK 內部採集

如果您的業務情境中需要在通話中動態開啟/關閉 SDK 的內部採集,請調用setParameter介面動態開啟/關閉 SDK 內部採集。

Android

/* 動態關閉阿里內部採集 */
String parameter = "{\"audio\":{\"enable_system_audio_device_record\":\"FALSE\"}}";
mAliRtcEngine.setParameter(parameter);

/* 動態開啟阿里內部採集 */
String parameter = "{\"audio\":{\"enable_system_audio_device_record\":\"TRUE\"}}"; 
mAliRtcEngine.setParameter(parameter);

iOS

// 動態關閉阿里內部採集
engine.setParameter("{\"audio\":{\"enable_system_audio_device_record\":\"FALSE\"}}")
// 動態開啟阿里內部採集
engine.setParameter("{\"audio\":{\"enable_system_audio_device_record\":\"TRUE\"}}")

Windows

/* Windows支援建立時指定開啟/關閉音頻採集 */
/* 關閉阿里內部採集 */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"TRUE\", \"user_specified_use_external_audio_record\":\"TRUE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

/* 開啟阿里內部採集 */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"FALSE\", \"user_specified_use_external_audio_record\":\"FALSE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

常見問題

  • 調用自訂音頻採集pushExternalAudioStream的頻率怎麼設定?

    • 推薦按照物理音訊裝置的時鐘驅動,在物理裝置採集到資料的時候調用;

    • 如果沒有具體的物理裝置來驅動,建議 10~50ms 傳入一次。

  • 使用自訂音頻採集是否可以使用 SDK 內部的音頻 3A(回聲消除 AEC、自動增益控制 AGC、雜訊抑制 ANS)?

    • 可以,第 2 步添加外部音頻流時可以設定 enable3A 參數決定是否開啟 SDK 內部的音頻 3A。