Investigation and Fix of Audio Playback Stutter on iOS 14.5 Using Audio Queue and AVPlayer
The article details a systematic investigation of intermittent audio playback stutter on iOS 14.5, tracing the issue from AVPlayer playback through Audio Queue recording, analyzing AudioStreamBasicDescription parameters and buffer size inconsistencies, and presenting a code‑level fix that aligns buffer sizes before invoking AudioConverterFillComplexBuffer.
When iOS 14.5 was released, users reported that audio recorded during a quiz would play back with intermittent stutter, making continuous playback impossible. A cross‑system matrix showed the problem only when both recording and playback occurred on iOS 14.5, while iOS 14.0 behaved normally.
Initial debugging focused on the playback side, where the app uses the simple call AVPlayer(url: audioUrl).play() . Because the playback code contains no custom logic, the suspicion shifted to the recording pipeline.
The recording implementation relies on Audio Queue Services to capture audio in real time and stream the encoded Opus data via WebSocket. Audio Queues are software objects that manage a series of buffers; each buffer is filled from the input device (microphone) and later processed by a callback.
Key functions used in the business code include AudioQueueNewInput , AudioQueueStart , AudioQueueStop , AudioQueueAllocateBuffer , and AudioQueueEnqueueBuffer . The callback signature is:
AudioQueueInputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions, const AudioStreamPacketDescription *inPacketDescs);During the investigation the team examined the AudioStreamBasicDescription structure. The original parameters set mBitsPerChannel = 0 and left mReserved non‑zero, which violates Apple’s specifications for lossless formats. Adjusting the description to a proper PCM layout (e.g., 44.1 kHz, 16‑bit, interleaved) resolved the issue for MP3 but not for ALAC.
Further testing revealed that on iOS 14.5 the size of each audio queue buffer varies, whereas on iOS 14.0 it remains constant. The encoder requires a minimum buffer size of 8192 bytes (ALAC 4096 × 2 frames per packet). The solution was to align the accumulated source buffer to this threshold before calling AudioConverterFillComplexBuffer .
/// Adapt iOS 14.5 bug – only process when bufferSize ≥ 8192
func handleRawLPCMAudioBufferData(buffer: UnsafeMutableRawPointer, bufferSize: UInt32, packetCount: UInt32) throws -> ConverterOutputData {
var error = noErr
memcpy(settings.sourceBuffer.advanced(by: Int(settings.sourceBufferSize)), buffer, Int(bufferSize))
settings.sourceBufferSize += bufferSize
if settings.sourceBufferSize < KALACMaxBuffSize {
throw getError(code: 2, description: "Buffer Too Small \(settings.sourceBufferSize)")
}
// ... processing logic ...
memcpy(settings.sourceBuffer, settings.sourceBuffer.advanced(by: Int(KALACMaxBuffSize)), Int(settings.sourceBufferSize - KALACMaxBuffSize))
settings.sourceBufferSize -= KALACMaxBuffSize
return output
}In addition, the AudioConverterComplexInputDataProc callback was simplified by removing unnecessary modifications to the mbuffers pointer, further stabilizing the conversion pipeline.
After these changes the recorded files play back smoothly on iOS 14.5, confirming that the root cause was buffer‑size misalignment during ALAC encoding. The investigation deepened the team’s understanding of PCM capture, Audio Queue mechanics, and format‑specific encoder requirements on iOS.
Liulishuo Tech Team
Help everyone become a global citizen!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.