Describe the bug
The headless audio device module (HeadlessAudioDeviceModule.cpp) suffers from a real-time timing defect where it pulls inbound audio from NetEq (the WebRTC jitter buffer) at an average rate of ~87 Hz instead of a strict 100 Hz (10ms intervals).
Expected behavior
When utilizing HeadlessAudioDeviceModule in a server-side, headless, or receive-only containerized environment, the internal playout thread (PlayThreadProcess) should pull inbound audio from NetEq at a strict 100 Hz cadence (exactly every 10ms) to ensure continuous, real-time media delivery to attached AudioTrack sinks.
Actual Behavior
The playout loop runs at approximately 81 Hz – 88 Hz (averaging ~87 Hz). Because the pull frequency is too slow, NetEq experiences structural buffer accumulation. To prevent compounding latency, NetEq intentionally drops or compresses approximately 13% of the inbound speech payload. This results in silent, severe audio degradation before the frames ever cross the JNI boundary to Java sinks.
Root Cause Analysis
The defect is located in HeadlessAudioDeviceModule.cpp within the PlayThreadProcess() loop
|
int64_t deltaTimeMillis = webrtc::TimeMillis() - currentTime; |
The loop calculates a stateless relative sleep: 10ms - work_duration.
However, on standard non-RT Linux kernels (such as typical cloud container hosts), OS context switching, cgroup scheduling, and timer slack cause webrtc::Thread::SleepMs to regularly overshoot its target by ~1.5ms.
Because the loop has no memory of this overshoot (no absolute deadline tracking and no catch-up mechanism), this timing error permanently accumulates on every single cycle.
Desktop (please complete the following information):
Describe the bug
The headless audio device module (HeadlessAudioDeviceModule.cpp) suffers from a real-time timing defect where it pulls inbound audio from NetEq (the WebRTC jitter buffer) at an average rate of ~87 Hz instead of a strict 100 Hz (10ms intervals).
Expected behavior
When utilizing HeadlessAudioDeviceModule in a server-side, headless, or receive-only containerized environment, the internal playout thread (PlayThreadProcess) should pull inbound audio from NetEq at a strict 100 Hz cadence (exactly every 10ms) to ensure continuous, real-time media delivery to attached AudioTrack sinks.
Actual Behavior
The playout loop runs at approximately 81 Hz – 88 Hz (averaging ~87 Hz). Because the pull frequency is too slow, NetEq experiences structural buffer accumulation. To prevent compounding latency, NetEq intentionally drops or compresses approximately 13% of the inbound speech payload. This results in silent, severe audio degradation before the frames ever cross the JNI boundary to Java sinks.
Root Cause Analysis
The defect is located in HeadlessAudioDeviceModule.cpp within the PlayThreadProcess() loop
webrtc-java/webrtc-jni/src/main/cpp/src/api/HeadlessAudioDeviceModule.cpp
Line 630 in c85507e
The loop calculates a stateless relative sleep: 10ms - work_duration.
However, on standard non-RT Linux kernels (such as typical cloud container hosts), OS context switching, cgroup scheduling, and timer slack cause webrtc::Thread::SleepMs to regularly overshoot its target by ~1.5ms.
Because the loop has no memory of this overshoot (no absolute deadline tracking and no catch-up mechanism), this timing error permanently accumulates on every single cycle.
Desktop (please complete the following information):