The AOQ Client SDK supports custom audio playback. Through the audio frame callback mechanism, decoded PCM data is delivered to the application layer, letting you implement your own audio rendering logic.
Overview
By default, the AOQ Client SDK plays received remote audio data through the system speaker or earpiece. In some scenarios, the SDK's built-in audio playback module may not meet your needs. Custom audio playback is useful when you need to:
Output received audio data to a custom playback device or audio processing pipeline.
Post-process received audio data — for example, for AI speech recognition or audio effects.
Work around audio playback device conflicts.
The AOQ Client SDK provides flexible custom playback support. Through the audio frame callback mechanism, decoded PCM data is delivered to the application layer for you to render and play back.
Sample code
Coming soon.
Prerequisites
An engine instance has been created by calling
createEngine.A connection to the server has been established (the
onConnectionStatusChangecallback has reportedAoqConnectionStatusConnected).
Implementation
1. Start audio playback in external mode
When calling startAudioPlayer, set isExternal=true to disable the SDK's internal audio rendering device. The application layer is then responsible for playing back the audio.
AoqClientEngine.AoqAudioPlaybackConfig config = new AoqClientEngine.AoqAudioPlaybackConfig();
config.isExternal = true; // Disable SDK internal playback; application layer renders audio
config.channel = 1; // Number of channels
engine.startAudioPlayer(config);
Parameters:
|
Parameter |
Type |
Default |
Description |
|
isVoipMode |
boolean |
false |
Whether to enable VoIP mode (hardware AEC). Valid on mobile. |
|
isDefaultSpeaker |
boolean |
true |
Whether to use the speaker by default. Valid on mobile. |
|
isExternal |
boolean |
false |
Whether to use external playback mode. When true, the SDK does not open the playback device. |
|
channel |
int |
1 |
Number of channels |
2. Set the audio frame callback listener
Call setAudioFrameObserver to register the audio frame data callback listener. Implement the onPlaybackAudioFrame callback method to receive the playback PCM data.
engine.setAudioFrameObserver(new AoqClientListener.AoqAudioFrameListener() {
@Override
public void onPlaybackAudioFrame(@NonNull AoqClientEngine.AoqAudioFrameData frame) {
// Process the received playback audio data here
// frame.dataPtr: PCM data
// frame.numOfSamples: number of samples
// frame.numOfChannels: number of channels
// frame.samplesPerSec: sample rate
// frame.bytesPerSample: bytes per sample
playPcmData(frame);
}
});
3. Enable playback data callbacks
Call enableAudioFrameObserver to enable audio frame callbacks at the playback position. Specify the data source as AoqAudioSourcePlayback.
AoqClientEngine.AoqAudioObserverConfig observerConfig = new AoqClientEngine.AoqAudioObserverConfig();
observerConfig.sampleRate = 48000; // Callback audio sample rate
observerConfig.channels = 1; // Callback audio channel count
observerConfig.mode = AoqClientEngine.AoqAudioObserverMode.AoqAudioObserverModeReadOnly; // Read-only mode
engine.enableAudioFrameObserver(
true, // Enable callbacks
AoqClientEngine.AoqAudioSource.AoqAudioSourcePlayback, // Playback data source
observerConfig
);
Parameters:
|
Parameter |
Type |
Default |
Description |
|
sampleRate |
int |
48000 |
Callback audio sample rate (Hz) |
|
channels |
int |
1 |
Callback audio channel count |
|
mode |
AoqAudioObserverMode |
AoqAudioObserverModeReadOnly |
Read/write mode |
4. Implement custom audio rendering
When PCM data arrives in the onPlaybackAudioFrame callback, the application layer handles rendering and playback. Common approaches include:
Android AudioTrack: Write the PCM data to the system audio device using AudioTrack.
AI speech recognition: Pass the PCM data to an ASR engine for speech recognition.
Audio effects: Apply audio effects to the PCM data before playback.
File storage: Save the received audio data to a local file.
// Example: Playback using Android AudioTrack
private AudioTrack mAudioTrack;
private volatile boolean mPlayRunning = false;
private void initAudioTrack(int sampleRate, int channels) {
int channelConfig = (channels == 2)
? AudioFormat.CHANNEL_OUT_STEREO
: AudioFormat.CHANNEL_OUT_MONO;
int bufferSize = AudioTrack.getMinBufferSize(
sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack(
AudioManager.STREAM_VOICE_CALL,
sampleRate,
channelConfig,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
AudioTrack.MODE_STREAM);
mAudioTrack.play();
mPlayRunning = true;
}
private void playPcmData(AoqClientEngine.AoqAudioFrameData frame) {
if (!mPlayRunning || mAudioTrack == null) {
return;
}
if (frame.dataPtr != null && frame.dataSize > 0) {
mAudioTrack.write(frame.dataPtr, 0, frame.dataSize);
}
}
Important notes:
The
onPlaybackAudioFramecallback fires on an SDK internal thread. Theframe.dataPtris only valid during the callback. If you need to use the data asynchronously, copy it first.AudioTrack.writeis a blocking operation. Writing directly inside the callback is fine because the SDK delivers callbacks at a controlled rate.Maintain the
mPlayRunningflag so you can stop processing when the engine exits or playback stops.
5. Stop custom playback
When custom playback is no longer needed, disable the audio frame callback first, then stop the playback device and release the AudioTrack resources.
// 1. Disable audio frame callbacks at the playback position
AoqClientEngine.AoqAudioObserverConfig observerConfig = new AoqClientEngine.AoqAudioObserverConfig();
engine.enableAudioFrameObserver(
false, // Disable callbacks
AoqClientEngine.AoqAudioSource.AoqAudioSourcePlayback,
observerConfig
);
// 2. Remove the audio frame callback listener
engine.setAudioFrameObserver(null);
// 3. Stop SDK audio playback
engine.stopAudioPlayer();
// 4. Release AudioTrack resources
mPlayRunning = false;
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}