Custom audio playback

更新时间:
复制 MD 格式

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 onConnectionStatusChange callback has reported AoqConnectionStatusConnected).

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 onPlaybackAudioFrame callback fires on an SDK internal thread. The frame.dataPtr is only valid during the callback. If you need to use the data asynchronously, copy it first.

  • AudioTrack.write is a blocking operation. Writing directly inside the callback is fine because the SDK delivers callbacks at a controlled rate.

  • Maintain the mPlayRunning flag 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;
}