全部产品
Search
文档中心

ApsaraVideo Live:Penangkapan audio kustom

更新时间:Dec 30, 2025

Alibaba Real-Time Communication (ARTC) SDK menyediakan fitur penangkapan audio kustom yang fleksibel.

Pengenalan fitur

Meskipun modul audio internal ARTC SDK dapat memenuhi kebutuhan audio dasar untuk sebagian besar aplikasi, modul tersebut mungkin tidak mencukupi dalam skenario tertentu. Dalam kasus demikian, Anda perlu mengimplementasikan penangkapan audio kustom. Beberapa kasus penggunaan umum meliputi:

  • Mengatasi masalah ketika perangkat penangkapan audio default sedang digunakan oleh proses lain.

  • Menangkap data audio dari sumber kustom—seperti sistem penangkapan proprietary atau file audio—lalu meneruskannya ke SDK untuk transmisi.

ARTC SDK mendukung penangkapan audio kustom yang fleksibel, memungkinkan Anda mengelola perangkat dan sumber audio sesuai kebutuhan.

Kode contoh

Android: Android/ARTCExample/AdvancedUsage/src/main/java/com/aliyun/artc/api/advancedusage/CustomAudioCaptureAndRender/CustomAudioCaptureActivity.java

iOS: iOS/ARTCExample/AdvancedUsage/CustomAudioCapture/CustomAudioCaptureVC.swift

Prasyarat

Pastikan Anda memenuhi persyaratan berikut:

Implementasi

image

1. Aktifkan atau nonaktifkan penangkapan internal

Untuk menggunakan fitur penangkapan audio kustom, Anda biasanya perlu menonaktifkan modul penangkapan audio internal SDK. Caranya, teruskan parameter extras saat memanggil getInstance untuk membuat engine. Parameter terkait adalah:

user_specified_use_external_audio_record: Menentukan apakah akan menggunakan penangkapan audio eksternal (yang menonaktifkan penangkapan internal SDK).

  • TRUE: Gunakan penangkapan audio eksternal (menonaktifkan penangkapan internal SDK).

  • FALSE: Jangan gunakan penangkapan audio eksternal (mengaktifkan penangkapan internal SDK).

Catatan

Parameter extras berupa string JSON.

Android

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

iOS

// Buat dan inisialisasi engine.
var customAudioCaptureConfig: [String: String] = [:]
// Gunakan penangkapan eksternal.
customAudioCaptureConfig["user_specified_use_external_audio_record"] = "TRUE"
// Serialisasi ke JSON.
guard let jsonData = try? JSONSerialization.data(withJSONObject: customAudioCaptureConfig, options: []),
let extras = String(data: jsonData, encoding: .utf8) else {
     print("Serialisasi JSON gagal")
     return
 }
let engine = AliRtcEngine.sharedInstance(self, extras:extras)

Windows

/* Windows mendukung pengaktifan atau penonaktifan penangkapan audio saat pembuatan. */
/* Nonaktifkan modul penangkapan internal. */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"TRUE\", \"user_specified_use_external_audio_record\":\"TRUE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

/* Aktifkan modul penangkapan internal. */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"FALSE\", \"user_specified_use_external_audio_record\":\"FALSE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

2. Tambahkan aliran audio eksternal

Panggil metode addExternalAudioStream untuk menambahkan aliran audio eksternal dan memperoleh ID alirannya. Jika Anda memerlukan fitur pemrosesan audio 3A SDK (AEC, AGC, ANS), atur parameter enable3A dalam objek AliRtcExternalAudioStreamConfig.

Catatan

Waktu pemanggilan API ini:

  • Jika Anda memerlukan pemrosesan 3A, panggil addExternalAudioStream setelah aliran audio dipublikasikan dan modul penangkapan kustom telah memperoleh frame audio pertamanya. Secara spesifik, panggil setelah callback onAudioPublishStateChanged mengembalikan newState bernilai AliRtcStatsPublished (3).

  • Jika Anda tidak memerlukan pemrosesan 3A (misalnya saat melakukan streaming audio dari file lokal, aliran jaringan, atau data yang dihasilkan TTS), Anda dapat memanggil metode ini segera setelah membuat engine. Anda kemudian dapat mulai mendorong data audio setelah aliran audio dipublikasikan.

Android

AliRtcEngine.AliRtcExternalAudioStreamConfig config = new AliRtcEngine.AliRtcExternalAudioStreamConfig();
config.sampleRate = SAMPLE_RATE; // Laju sampel
config.channels = CHANNEL; // Jumlah channel
// Volume publikasi
config.publishVolume = 100;
// Volume pemutaran lokal
config.playoutVolume = isLocalPlayout ? 100 : 0;
config.enable3A = true;

int result = mAliRtcEngine.addExternalAudioStream(config);
if (result <= 0) {
    return;
}
// Nilai kembali adalah ID aliran. Anda akan memerlukannya untuk mendorong data ke SDK nanti.
mExternalAudioStreamId = result;

iOS

/* Atur parameter sesuai kebutuhan bisnis Anda. */
AliRtcExternalAudioStreamConfig *config = [AliRtcExternalAudioStreamConfig new];
// Ini harus sama dengan jumlah channel pada aliran audio PCM eksternal. Atur ke 1 untuk mono atau 2 untuk stereo.
config.channels = _pcmChannels;
// Ini harus sama dengan laju sampel pada aliran audio PCM eksternal.
config.sampleRate = _pcmSampleRate;
config.playoutVolume = 0;
config.publishVolume = 100;
_externalPlayoutStreamId = [self.engine addExternalAudioStream:config];

Windows

/* Dapatkan engine media. */
IAliEngineMediaEngine* mAliRtcMediaEngine = nullptr;
    
mAliRtcEngine->QueryInterface(AliEngineInterfaceMediaEngine, (void **)&mAliRtcMediaEngine);
/* Atur parameter sesuai kebutuhan bisnis Anda. */
AliEngineExternalAudioStreamConfig config;
config.playoutVolume = currentAudioPlayoutVolume;
config.publishVolume = currentAudioPublishVolume;
config.channels = 1;
config.sampleRate = 48000;
config.publishStream = 0;
audioStreamID = mAliRtcMediaEngine->AddExternalAudioStream(config);

mAliRtcMediaEngine->Release();

3. Implementasikan modul penangkapan audio kustom

Anda harus mengimplementasikan logika sendiri untuk menangkap dan memproses data audio berdasarkan skenario bisnis Anda, lalu meneruskan data tersebut ke SDK untuk transmisi.

Alibaba Cloud menyediakan kode contoh yang menunjukkan cara membaca data PCM dari file lokal atau mikrofon. Untuk detail implementasi, lihat Contoh penangkapan kustom.

4. Dorong data audio ke SDK berdasarkan ID aliran

Setelah aliran audio dipublikasikan (ketika callback onAudioPublishStateChanged melaporkan status AliRtcStatsPublished), panggil metode pushExternalAudioStreamRawData. Atur parameter mExternalAudioStreamId ke ID aliran yang diperoleh pada Langkah 2, lalu teruskan data audio yang ditangkap ke SDK. Data audio yang ditangkap harus dikonversi ke objek AliRtcAudioFrame dengan properti berikut:

  • data: Data audio.

  • numSamples: Jumlah titik sampel per channel dalam data yang diberikan.

  • bytesPerSample: Jumlah byte per titik sampel, yang berkaitan dengan kedalaman bit. Untuk PCM 16-bit, nilai ini adalah 2.

  • numChannels: Jumlah channel audio.

  • samplesPerSec: Laju sampel, seperti 16.000 atau 48.000.

Catatan
  • Anda harus mulai mendorong data hanya setelah aliran audio dipublikasikan (setelah callback onAudioPublishStateChanged melaporkan status AliRtcStatsPublished).

  • Atur properti numSamples pada objek AliRtcAudioFrame sesuai dengan panjang aktual data yang ditangkap. Pada beberapa perangkat, misalnya, panjang data audio yang diperoleh melalui AudioRecord.read mungkin lebih kecil daripada ukuran buffer. Anda harus menggunakan nilai kembali untuk menentukan panjang data aktual.

  • Saat memanggil pushExternalAudioStreamRawData, operasi dapat gagal jika buffer internal penuh. Anda harus menangani pengecualian ini dan mencoba mengirim ulang data.

  • Kami merekomendasikan mengirim data setiap 10 ms.

Android

// Asumsikan data audio yang ditangkap berada dalam audioData, ukuran data adalah bytesRead byte, dan merupakan data 10 ms.
if (mAliRtcEngine != null && bytesRead > 0) {
    // Buat objek AliRtcAudioFrame. bitsPerSample adalah kedalaman bit, biasanya 16.
    AliRtcEngine.AliRtcAudioFrame sample = new AliRtcEngine.AliRtcAudioFrame();
    sample.data = audioData;
    sample.numSamples = bytesRead / (channels * (bitsPerSample / 8)); // Hitung jumlah sampel berdasarkan jumlah byte yang benar-benar dibaca.
    sample.numChannels = channels;
    sample.samplesPerSec = sampleRate;
    sample.bytesPerSample = bitsPerSample / 8;

    int ret = 0;
    // Coba ulang jika dorongan gagal karena buffer penuh.
    int retryCount = 0;
    final int MAX_RETRY_COUNT = 20;
    final int BUFFER_WAIT_MS = 10;
    do {
        // Dorong data yang diperoleh ke SDK.
        ret = mAliRtcEngine.pushExternalAudioStreamRawData(mExternalAudioStreamId, sample);
        if(ret == ErrorCodeEnum.ERR_SDK_AUDIO_INPUT_BUFFER_FULL) {
            // Tangani kasus buffer penuh. Tunggu sejenak lalu coba ulang. Durasi maksimum percobaan ulang adalah beberapa ratus ms.
            retryCount++;
            if(mExternalAudioStreamId <= 0 || retryCount >= MAX_RETRY_COUNT) {
                // Aliran telah berhenti atau jumlah maksimum percobaan ulang telah tercapai. Keluar dari loop.
                break;
            }

            try {
                // Jeda sejenak.
                Thread.sleep(BUFFER_WAIT_MS);
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        } else {
            // Dorongan berhasil atau terjadi error lain. Keluar dari loop langsung.
            break;
        }
    } while (retryCount < MAX_RETRY_COUNT);
}

iOS

// Buat objek AliRtcAudioFrame dari data audio yang ditangkap.
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
    }
    // Dorong data audio ke SDK.
    let rc = rtcEngine?.pushExternalAudioStream(externalPublishStreamId, rawData: sample) ?? 0

    // Tangani buffer penuh.
    // 0x01070101 SDK_AUDIO_INPUT_BUFFER_FULL: Buffer penuh. Perlu pengiriman ulang.
    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

Catatan

Sebelum mengimplementasikan penangkapan kustom di Windows, Anda harus memanggil metode QueryInterface untuk mendapatkan objek engine media.

/* Dapatkan engine media. */
IAliEngineMediaEngine* mAliRtcMediaEngine = nullptr;   
mAliRtcEngine->QueryInterface(AliEngineInterfaceMediaEngine, (void **)&mAliRtcMediaEngine);

// Buat frame audio dari data.
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;
// Dorong data ke SDK.
int ret = mAliRtcMediaEngine->PushExternalAudioStreamRawData(audioStreamID, rawData);
// Tangani buffer penuh dan error lainnya.

// Lepaskan engine media.
mAliRtcMediaEngine->Release();

5. Hapus aliran audio eksternal

Untuk berhenti mempublikasikan audio dari sumber audio kustom, panggil metode removeExternalAudioStream untuk menghapus aliran audio eksternal.

Android

mAliRtcEngine.removeExternalAudioStream(mExternalAudioStreamId);

iOS

[self.engine removeExternalAudioStream:_externalPublishStreamId];

Windows

/* Dapatkan engine media. */
IAliEngineMediaEngine* mAliRtcMediaEngine = nullptr;
    
mAliRtcEngine->QueryInterface(AliEngineInterfaceMediaEngine, (void **)&mAliRtcMediaEngine);

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

6. (Opsional) Aktifkan atau nonaktifkan penangkapan internal secara dinamis

Jika skenario bisnis Anda memerlukan pengaktifan atau penonaktifan penangkapan internal SDK secara dinamis selama panggilan, panggil metode setParameter.

Android

/* Nonaktifkan modul penangkapan internal secara dinamis. */
String parameter = "{\"audio\":{\"enable_system_audio_device_record\":\"FALSE\"}}";
mAliRtcEngine.setParameter(parameter);

/* Aktifkan modul penangkapan internal secara dinamis. */
String parameter = "{\"audio\":{\"enable_system_audio_device_record\":\"TRUE\"}}"; 
mAliRtcEngine.setParameter(parameter);

iOS

// Nonaktifkan modul penangkapan internal secara dinamis.
engine.setParameter("{\"audio\":{\"enable_system_audio_device_record\":\"FALSE\"}}")
// Aktifkan modul penangkapan internal secara dinamis.
engine.setParameter("{\"audio\":{\"enable_system_audio_device_record\":\"TRUE\"}}")

Windows

/* Windows mendukung pengaktifan atau penonaktifan penangkapan audio saat pembuatan. */
/* Nonaktifkan modul penangkapan internal internal. */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"TRUE\", \"user_specified_use_external_audio_record\":\"TRUE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

/* Aktifkan modul penangkapan internal. */
char* extra = "{\"user_specified_enable_use_virtual_audio_device\":\"FALSE\", \"user_specified_use_external_audio_record\":\"FALSE\"}";
mAliRtcEngine = AliRtcEngine.Create(extra);

FAQ

  • Berapa frekuensi yang direkomendasikan untuk memanggil pushExternalAudioStream?

    • Panggil berdasarkan clock perangkat audio fisik setiap kali perangkat menangkap data.

    • Jika tidak ada perangkat fisik yang mengatur waktu, kami merekomendasikan mengirim data setiap 10 hingga 50 ms.

  • Dapatkah saya menggunakan pemrosesan audio 3A internal SDK (AEC, AGC, ANS) dengan penangkapan audio kustom?

    • Ya. Seperti dijelaskan pada Langkah 2, Anda dapat mengatur parameter enable3A saat menambahkan aliran audio eksternal untuk mengaktifkan atau menonaktifkan pemrosesan 3A internal SDK.