AudioStreamPlayerController

class AudioStreamPlayerController : Closeable

Central controller for managing PCM audio stream playback.

This class provides granular control over audio stream operations including playback state management, real-time audio data injection, and playback parameter adjustments. It serves as the primary interface between application logic and the underlying audio system.

Note: This controller only supports playback of PCM audio streams. Therefore, when using writeStreamData to write or AudioStreamDataCallback to send audio stream data, only raw PCM audio data is accepted. Any audio file format headers must be removed before writing or sending the audio stream data—for example, skip the first 44 bytes of a WAV file header.

This controller provides two modes of data delivery:

  1. Push mode: Data is written directly to the audio stream buffer using writeStreamData.

  2. Pull mode: Data is provided by an external source, such as an audio file, using AudioStreamDataCallback.

In push mode, the application writes audio data directly to the audio stream buffer using writeStreamData. The controller will then play the audio data from the buffer.

In pull mode, the application provides an AudioStreamDataCallback to the controller via Entity.playAudioStream and Entity.prepareAudioStream, which will produce the callback to require more audio data. The controller will then play the audio data from the callback.

When to use null callback:

When to provide callback:

Key capabilities:

  1. Full lifecycle control (play/pause/stop/resume)

  2. Precision timing via nanosecond timestamps

  3. Dynamic playback speed adjustment

  4. Volume fading with multiple interpolation modes

  5. Dual-mode data delivery (push/pull)

  6. Thread-safe main-thread enforcement

Usage scenarios:

  • Real-time audio streaming applications

  • Interactive audio environments

  • Synchronized multimedia playback

  • Dynamic audio effect systems

Example: Basic playback control

val controller = entity.playAudioStream(config)
controller.play()
controller.setVolume(0.8f)
controller.fade(targetVolume = 0.0f, duration = 2000L) // Fade out over 2 seconds

Example: Push mode data writing

val pushConfig = AudioStreamConfig(
AudioChannelLayoutType.STANDARD,
"mix_group_1",
AudioChannelLayout.OUTPUT_LAYOUT_STEREO,
AmbisonicsType.NONE,
AudioFormat(sampleRate = 48000),
null)

val controller = entity.prepareAudioStream(pushConfig)

// Calculate the delay time and buffer size
val delayTime = 120
val channelCnt = pushConfig.audioChannelCount
val audioFormat = pushConfig.audioFormat!!
val audioTrackBufferFrameCnt = (audioFormat.sampleRate / 1000) * delayTime
val frameSize = audioFormat.getFrameSize(channelCnt)
val audioTrackBufferSize = audioTrackBufferFrameCnt * frameSize
val mappedBuffer = ByteBuffer.allocateDirect(audioTrackBufferSize)

// Prepare to write audio data in IO thread with coroutine
CoroutineScope(Dispatchers.IO).launch {
var remainSize = mappedBuffer.remaining()
while (remainSize > 0) {
// wait for controller playing
if (controller.isPlaying() != true) {
delay(30)
continue
}
remainSize = mappedBuffer.remaining()
val chunkSize = min(audioTrackBufferSize, remainSize)
val sliceBuffer = mappedBuffer.slice().limit(chunkSize) as ByteBuffer
val actualFrames = chunkSize / frameSize
val writeResult = controller.writeStreamData(sliceBuffer, actualFrames, false)
if (writeResult == actualFrames.toLong()) {
mappedBuffer.position(mappedBuffer.position() + chunkSize)
val bufferDurationMs = (actualFrames * 1000L) / audioFormat.sampleRate
delay(bufferDurationMs)
} else if (writeResult > 0 && writeResult < actualFrames.toLong()) {
mappedBuffer.position(mappedBuffer.position() + (writeResult * frameSize).toInt())
val bufferDurationMs = (writeResult * 1000L) / audioFormat.sampleRate
delay(bufferDurationMs)
} else {
// Log.w("PcmStreamHelper", "writeStreamData failed writeResult: $writeResult")
delay(20)
}
}
}

// Start audio stream playback
controller.play()

// Release audio stream resource when no longer needed
controller.stop()
controller.close()
mappedBuffer.clear()
pushConfig.close()

Example: Timestamp synchronization

val timestamp = controller.getStreamTimestamp()
videoPlayer.syncWithAudio(timestamp.timeNs)

See also

For configuration of audio stream parameters

For available fade interpolation algorithms

Properties

Link copied to clipboard

The entity that the audio is playing on.

Link copied to clipboard
@get:JvmName(name = "isValid")
val valid: Boolean

The controller is valid.

Functions

Link copied to clipboard
open override fun close()
Link copied to clipboard
fun fade(targetVolume: Float = 1.0f, duration: Long, fadeMode: AudioInterpolatorType = AudioInterpolatorType.CUBIC): Boolean

Fade the audio to the specified volume over a specified duration.

Link copied to clipboard
fun flushStream(): Boolean

Flush the audio stream to discard any remaining data in player buffer.

Link copied to clipboard
fun getPlaybackSpeed(): Float

Get the audio playback speed.

Link copied to clipboard
fun getStreamPosition(): Long

Retrieves the current stream position in bytes.

Link copied to clipboard

Retrieves high-precision playback timing information.

Link copied to clipboard
fun getVolume(): Float

Gets the playback volume.

Link copied to clipboard
fun isPlaying(): Boolean

Check audio is playing or not.

Link copied to clipboard
fun pause(): Boolean

Pause the playing state audio.

Link copied to clipboard
fun play(): Boolean

Play audio.

Link copied to clipboard
fun resume(): Boolean

Resume the pausing state audio.

Link copied to clipboard
fun setPlaybackSpeed(rate: Float): Boolean

Set the audio playback speed.

Link copied to clipboard
fun setVolume(@FloatRange(from = 0.0, to = 1.0) volume: Float = 1.0f): Boolean

Sets the playback volume.

Link copied to clipboard
fun stop(): Boolean

Stop audio.

Link copied to clipboard
@Synchronized
fun writeStreamData(buffer: ByteBuffer, frameCount: Int, blocking: Boolean): Long

Writes pcm audio data in push mode operation, which is suitable for real-time audio,the buffer must be direct buffer and the owner of the buffer must be the caller, and the buffer must be released after no longer needed.