Menjelaskan cara mengimplementasikan custom audio capture dengan AOQ Client SDK, termasuk menambahkan aliran audio eksternal, mendorong data PCM, dan mengelola siklus hidup aliran.
Overview
Modul audio bawaan AOQ Client SDK mencakup kebutuhan audio dasar, tetapi dalam beberapa skenario modul capture bawaan mungkin tidak mencukupi. Custom audio capture berguna ketika Anda perlu:
- Mengatasi konflik perangkat capture audio.
- Memasukkan data audio dari sistem capture kustom atau file audio ke dalam SDK untuk transmisi.
- Memublikasikan audio yang dihasilkan oleh AI TTS melalui SDK.
AOQ Client SDK menyediakan dukungan custom capture yang fleksibel, memungkinkan Anda mengelola perangkat dan sumber audio sendiri sesuai kasus penggunaan. Data aliran audio eksternal akan dicampur dengan audio yang ditangkap secara internal sebelum dipublikasikan.
Sample code
Segera hadir.
Prerequisites
- Sebuah instans engine telah dibuat dengan memanggil
createEngine. - Koneksi ke server telah terjalin (callback
onConnectionStatusChangetelah melaporkanAoqConnectionStatusConnected).
Implementation
1. Mulai atau hentikan perekaman audio
Mulailah audio capture terlebih dahulu. Data aliran audio eksternal akan dicampur dengan data capture internal sebelum dipublikasikan. Jika Anda tidak memerlukan capture mikrofon internal, atur isExternal=true untuk menonaktifkan perangkat capture internal.
// Opsi 1: Capture internal — data aliran audio eksternal dicampur dengan data mikrofon
AoqClientEngine.AoqAudioCaptureConfig config = new AoqClientEngine.AoqAudioCaptureConfig();
config.isExternal = false; // Gunakan capture mikrofon internal
config.isVoipMode = false;
engine.startAudioCapture(config);
// Opsi 2: Tanpa capture internal — hanya data aliran audio eksternal yang dipublikasikan
AoqClientEngine.AoqAudioCaptureConfig config = new AoqClientEngine.AoqAudioCaptureConfig();
config.isExternal = true; // Jangan buka mikrofon; data disediakan oleh aliran audio eksternal
engine.startAudioCapture(config);
2. Setelah terhubung, tambahkan aliran audio eksternal
Setelah callback onConnectionStatusChange melaporkan AoqConnectionStatusConnected, panggil addAudioExternalStream untuk menambahkan aliran audio eksternal. Tetapkan streamId unik yang akan Anda gunakan untuk mendorong data dan mengelola aliran tersebut.
Jika Anda memerlukan pemrosesan 3A (echo cancellation, noise suppression, automatic gain control), atur parameter enable3A dalam AoqAudioExternalStreamConfig.
// Tambahkan aliran setelah koneksi dikonfirmasi dalam callback 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; // Laju sampel — harus sesuai dengan data audio aktual
config.channels = 1; // Jumlah channel
config.publishVolume = 100; // Volume publikasi [0-100]
config.playoutVolume = 0; // Volume pemutaran lokal [0-100]; 0 = tanpa pemutaran lokal
config.maxBufferDuration = 1000; // Durasi buffer maksimum (milidetik)
config.enable3A = true; // Apakah menerapkan pemrosesan 3A pada PCM input
String streamId = "external_audio_1";
int ret = engine.addAudioExternalStream(streamId, config);
if (ret == 0) {
mExternalStreamId = streamId;
}
}
Parameter:
Parameter | Type | Default | Description |
|---|---|---|---|
trackType | AoqTrackType | AoqTrackTypeAudio | Jenis track audio |
codecType | AoqEncoderType | AoqEncoderTypeAudioPCM | Format aliran audio |
channels | int | 1 | Jumlah channel |
sampleRate | int | 48000 | Laju sampel (Hz) |
playoutVolume | int | 100 | Volume pemutaran [0–100] |
publishVolume | int | 100 | Volume publikasi [0–100] |
maxBufferDuration | int | 1000 | Durasi buffer maksimum (milidetik) |
enable3A | boolean | false | Apakah menerapkan pemrosesan 3A pada PCM input |
3. Tangkap atau peroleh data PCM
Implementasikan capture audio atau sumber data Anda sendiri sesuai kasus penggunaan, lalu masukkan data tersebut ke dalam SDK. Sumber data umum meliputi:
- Capture mikrofon: Gunakan Android AudioRecord untuk menangkap data PCM.
- Pembacaan file: Parsing data PCM dari file audio PCM atau WAV lokal.
- AI TTS: Peroleh data PCM dari mesin sintesis suara.
- Aliran jaringan: Decode data PCM dari aliran audio jaringan.
Data audio harus dalam format PCM. Catat laju sampel, jumlah channel, dan parameter lainnya untuk membuat objek AoqAudioFrameData.
4. Dorong data audio ke SDK melalui stream ID
Panggil pushAudioExternalStreamData untuk memasukkan data PCM yang ditangkap ke dalam SDK.
- Untuk capture perangkat keras: gunakan frame 10 ms dan panggil push setiap kali data tersedia.
- Untuk input berbasis file: gunakan frame 40 ms dan tidur selama 30 ms di antara pemanggilan push.
- Pertahankan flag
running. Hentikan loop push saat engine keluar atau stream ID dihapus.
// Variabel anggota: flag untuk mengontrol loop push
private volatile boolean mPushRunning = false;
// Dorong satu frame audio
private void pushAudioData(byte[] audioData, int bytesRead) {
if (engine == null || mExternalStreamId == null || bytesRead <= 0) {
return;
}
int channels = 1;
int bytesPerSample = 2; // PCM 16-bit
int sampleRate = 48000;
// Buat objek data frame audio
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;
// Dorong data dan tangani error buffer penuh
int ret;
final int WAIT_MS = 30;
do {
// Periksa apakah flag running dan stream ID masih valid
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);
}
Catatan penting:
- Mulai mendorong data hanya setelah koneksi terjalin dan aliran audio eksternal telah ditambahkan.
- Atur
numOfSamplespadaAoqAudioFrameDataagar sesuai dengan panjang aktual data. pushAudioExternalStreamDatadapat gagal jika buffer internal penuh (kode kesalahan 110). Ulangi setelah penundaan singkat.- Untuk capture real-time, gunakan frame 10 ms dan panggil push setiap kali data tersedia. Tangani kode kesalahan 110.
- Untuk input berbasis file, gunakan frame 40 ms dan panggil push setiap 30 ms. Tangani kode kesalahan 110.
- Sebelum engine keluar (
destroy) atau stream ID dihapus, aturmPushRunning = falseuntuk menghentikan loop push dan menghindari akses ke sumber daya yang telah dilepas.
5. Hapus aliran audio eksternal
Saat custom capture tidak lagi diperlukan, hentikan loop push terlebih dahulu, lalu panggil removeAudioExternalStream untuk menghapus aliran audio eksternal.
// Hentikan push terlebih dahulu
stopPushAudio();
// Lalu hapus aliran audio eksternal
engine.removeAudioExternalStream(mExternalStreamId);
mExternalStreamId = null;