Descreve como implementar a captura de áudio personalizada com o AOQ Client SDK, incluindo a adição de fluxos de áudio externos, o envio de dados PCM e o gerenciamento do ciclo de vida do fluxo.
Visão geral
O módulo de áudio integrado do AOQ Client SDK atende às necessidades básicas, mas em alguns cenários o módulo de captura padrão pode não ser suficiente. A captura de áudio personalizada é útil quando você precisa:
- Contornar conflitos de dispositivos de captura de áudio.
- Inserir dados de áudio de um sistema de captura personalizado ou de um arquivo de áudio no SDK para transmissão.
- Publicar áudio gerado por TTS de IA por meio do SDK.
O AOQ Client SDK oferece suporte flexível à captura personalizada, permitindo que você gerencie seus próprios dispositivos e fontes de áudio conforme o caso de uso. Os dados do fluxo de áudio externo são mixados com o áudio capturado internamente antes da publicação.
Código de exemplo
Em breve.
Pré-requisitos
- Uma instância de engine foi criada chamando
createEngine. - Uma conexão com o servidor foi estabelecida (o callback
onConnectionStatusChangereportouAoqConnectionStatusConnected).
Implementação
1. Iniciar ou parar a captura de áudio
Inicie primeiro a captura de áudio. Os dados do fluxo de áudio externo são mixados com os dados de captura interna antes da publicação. Se não for necessária a captura interna pelo microfone, defina isExternal=true para desativar o dispositivo de captura interno.
// Option 1: Internal capture — external audio stream data is mixed with microphone data
AoqClientEngine.AoqAudioCaptureConfig config = new AoqClientEngine.AoqAudioCaptureConfig();
config.isExternal = false; // Use internal microphone capture
config.isVoipMode = false;
engine.startAudioCapture(config);
// Option 2: No internal capture — only external audio stream data is published
AoqClientEngine.AoqAudioCaptureConfig config = new AoqClientEngine.AoqAudioCaptureConfig();
config.isExternal = true; // Do not open the microphone; external audio stream provides the data
engine.startAudioCapture(config);
2. Após conectar, adicione o fluxo de áudio externo
Assim que o callback onConnectionStatusChange reportar AoqConnectionStatusConnected, chame addAudioExternalStream para adicionar o fluxo de áudio externo. Atribua um streamId exclusivo, que será usado para enviar dados e gerenciar o fluxo.
Caso seja necessário processamento 3A (cancelamento de eco, supressão de ruído e controle automático de ganho), configure o parâmetro enable3A em AoqAudioExternalStreamConfig.
// Add the stream after confirming connection in the onConnectionStatusChange callback
@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; // Sample rate — must match the actual audio data
config.channels = 1; // Channel count
config.publishVolume = 100; // Publishing volume [0-100]
config.playoutVolume = 0; // Local playback volume [0-100]; 0 = no local playback
config.maxBufferDuration = 1000; // Maximum buffer duration (milliseconds)
config.enable3A = true; // Whether to apply 3A processing to input PCM
String streamId = "external_audio_1";
int ret = engine.addAudioExternalStream(streamId, config);
if (ret == 0) {
mExternalStreamId = streamId;
}
}
Parâmetros:
Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
trackType | AoqTrackType | AoqTrackTypeAudio | Tipo de faixa de áudio |
codecType | AoqEncoderType | AoqEncoderTypeAudioPCM | Formato do fluxo de áudio |
channels | int | 1 | Número de canais |
sampleRate | int | 48000 | Taxa de amostragem (Hz) |
playoutVolume | int | 100 | Volume de reprodução [0–100] |
publishVolume | int | 100 | Volume de publicação [0–100] |
maxBufferDuration | int | 1000 | Duração máxima do buffer (milissegundos) |
enable3A | boolean | false | Indica se deve aplicar processamento 3A ao PCM de entrada |
3. Capturar ou obter dados PCM
Implemente sua própria captura de áudio ou source adequada ao seu caso de uso e insira os dados no SDK. As fontes de dados mais comuns incluem:
- Captura por microfone: Utilize o AudioRecord do Android para capturar dados PCM.
- Leitura de arquivo: Extraia dados PCM de um arquivo de áudio local nos formatos PCM ou WAV.
- TTS de IA: Obtenha dados PCM de um mecanismo de síntese de fala.
- Fluxo de rede: Decodifique dados PCM a partir de um fluxo de áudio em rede.
Os dados de áudio devem estar no formato PCM. Registre a taxa de amostragem, o número de canais e outros parâmetros para construir o objeto AoqAudioFrameData.
4. Enviar dados de áudio para o SDK via stream ID
Chame pushAudioExternalStreamData para inserir os dados PCM capturados no SDK.
- Para captura por hardware: utilize quadros de 10 ms e envie sempre que houver dados disponíveis.
- Para entrada baseada em arquivo: utilize quadros de 40 ms e aguarde 30 ms entre os envios.
- Mantenha uma flag
running. Saia do loop de envio quando a engine for encerrada ou o stream ID for removido.
// Member variable: flag to control the push loop
private volatile boolean mPushRunning = false;
// Push a single audio frame
private void pushAudioData(byte[] audioData, int bytesRead) {
if (engine == null || mExternalStreamId == null || bytesRead <= 0) {
return;
}
int channels = 1;
int bytesPerSample = 2; // 16-bit PCM
int sampleRate = 48000;
// Build the audio frame data object
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;
// Push data and handle buffer-full errors
int ret;
final int WAIT_MS = 30;
do {
// Check whether the running flag and stream ID are still 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);
}
Observações importantes:
- Comece a enviar dados somente após estabelecer a conexão e adicionar o fluxo de áudio externo.
- Configure
numOfSamplesdeAoqAudioFrameDatapara corresponder ao tamanho real dos dados. - A chamada a
pushAudioExternalStreamDatapode falhar se o buffer interno estiver cheio (código de erro 110). Tente novamente após um curto intervalo. - Em capturas em tempo real, use quadros de 10 ms e envie sempre que houver dados disponíveis. Trate o código de erro 110 adequadamente.
- Para entrada baseada em arquivo, utilize quadros de 40 ms e envie a cada 30 ms. Trate o código de erro 110 adequadamente.
- Antes de encerrar a engine (
destroy) ou remover um stream ID, definamPushRunning = falsepara interromper o loop de envio e evitar acesso a recursos já liberados.
5. Remover o fluxo de áudio externo
Quando a captura personalizada não for mais necessária, pare primeiro o loop de envio e depois chame removeAudioExternalStream para remover o fluxo de áudio externo.
// Stop pushing first
stopPushAudio();
// Then remove the external audio stream
engine.removeAudioExternalStream(mExternalStreamId);
mExternalStreamId = null;