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:
Buat aplikasi ARTC dan peroleh AppID serta AppKey dari ApsaraVideo Live console.
Unduh dan integrasikan SDK ke dalam proyek Anda serta Implementasikan komunikasi audio/video.
Implementasi
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).
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.
Waktu pemanggilan API ini:
Jika Anda memerlukan pemrosesan 3A, panggil
addExternalAudioStreamsetelah aliran audio dipublikasikan dan modul penangkapan kustom telah memperoleh frame audio pertamanya. Secara spesifik, panggil setelah callbackonAudioPublishStateChangedmengembalikannewStatebernilaiAliRtcStatsPublished (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.
Anda harus mulai mendorong data hanya setelah aliran audio dipublikasikan (setelah callback
onAudioPublishStateChangedmelaporkan statusAliRtcStatsPublished).Atur properti
numSamplespada objekAliRtcAudioFramesesuai dengan panjang aktual data yang ditangkap. Pada beberapa perangkat, misalnya, panjang data audio yang diperoleh melaluiAudioRecord.readmungkin 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
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
enable3Asaat menambahkan aliran audio eksternal untuk mengaktifkan atau menonaktifkan pemrosesan 3A internal SDK.