Skip to content

Commit 56cf721

Browse files
Fix headless ADM playout drift below 100 Hz (#244)
1 parent 8d14974 commit 56cf721

2 files changed

Lines changed: 45 additions & 15 deletions

File tree

webrtc-jni/src/main/cpp/include/api/HeadlessAudioDeviceModule.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,11 @@ namespace jni
166166

167167
size_t playoutFramesIn10MS_;
168168
size_t recordingFramesIn10MS_;
169-
int64_t lastCallPlayoutMillis_;
170-
int64_t lastCallRecordMillis_;
169+
// Absolute wall-clock deadline (ms) of the next 10 ms tick. Advanced by a
170+
// fixed +10 each tick so scheduling/wake-up latency is corrected against the
171+
// grid rather than accumulating into the frame period.
172+
int64_t nextPlayoutMillis_;
173+
int64_t nextRecordMillis_;
171174

172175
mutable webrtc::Mutex mutex_;
173176
std::unique_ptr<webrtc::AudioDeviceBuffer> audio_device_buffer_ RTC_GUARDED_BY(mutex_);

webrtc-jni/src/main/cpp/src/api/HeadlessAudioDeviceModule.cpp

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ namespace jni
2525
channels_(channels ? channels : 1),
2626
playoutFramesIn10MS_(0),
2727
recordingFramesIn10MS_(0),
28-
lastCallPlayoutMillis_(0),
29-
lastCallRecordMillis_(0),
28+
nextPlayoutMillis_(0),
29+
nextRecordMillis_(0),
3030
audio_callback_(nullptr)
3131
{
3232
audio_device_buffer_ = std::make_unique<webrtc::AudioDeviceBuffer>(&env.task_queue_factory());
@@ -553,22 +553,35 @@ namespace jni
553553
int64_t currentTime = webrtc::TimeMillis();
554554
mutex_.Lock();
555555

556-
if (lastCallPlayoutMillis_ == 0 || currentTime - lastCallPlayoutMillis_ >= 10) {
556+
// Seed the grid on the first tick.
557+
if (nextPlayoutMillis_ == 0) {
558+
nextPlayoutMillis_ = currentTime;
559+
}
560+
561+
if (currentTime >= nextPlayoutMillis_) {
557562
mutex_.Unlock();
558563
audio_device_buffer_->RequestPlayoutData(playoutFramesIn10MS_);
559564
mutex_.Lock();
560565

561566
audio_device_buffer_->GetPlayoutData(play_buffer_.data());
562567

563-
lastCallPlayoutMillis_ = currentTime;
568+
// Advance the grid by a fixed 10 ms rather than re-anchoring to currentTime,
569+
// so wake-up latency is corrected on the next tick instead of accumulating
570+
// into the frame period (which otherwise pulls the effective rate below 100 Hz).
571+
nextPlayoutMillis_ += 10;
572+
573+
// If we fell far behind (e.g. the thread was descheduled), resync to now
574+
// instead of bursting frames to catch up.
575+
if (nextPlayoutMillis_ < currentTime - 100) {
576+
nextPlayoutMillis_ = currentTime;
577+
}
564578
}
565579

580+
int64_t sleepMillis = nextPlayoutMillis_ - webrtc::TimeMillis();
566581
mutex_.Unlock();
567582

568-
int64_t deltaTimeMillis = webrtc::TimeMillis() - currentTime;
569-
570-
if (deltaTimeMillis < 10) {
571-
webrtc::Thread::SleepMs(10 - deltaTimeMillis);
583+
if (sleepMillis > 0) {
584+
webrtc::Thread::SleepMs(sleepMillis);
572585
}
573586

574587
return true;
@@ -588,7 +601,12 @@ namespace jni
588601
int64_t currentTime = webrtc::TimeMillis();
589602
mutex_.Lock();
590603

591-
if (lastCallRecordMillis_ == 0 || currentTime - lastCallRecordMillis_ >= 10) {
604+
// Seed the grid on the first tick.
605+
if (nextRecordMillis_ == 0) {
606+
nextRecordMillis_ = currentTime;
607+
}
608+
609+
if (currentTime >= nextRecordMillis_) {
592610
size_t nSamplesOut = 0;
593611
const size_t nBytesPerSample = sizeof(int16_t);
594612
const size_t nChannels = channels_;
@@ -617,19 +635,28 @@ namespace jni
617635
audio_device_buffer_->SetRecordedBuffer(record_buffer_.data(), recordingFramesIn10MS_);
618636
audio_device_buffer_->SetVQEData(/*play_delay_ms*/ 0, /*rec_delay_ms*/ 0);
619637

620-
lastCallRecordMillis_ = currentTime;
638+
// Advance the grid by a fixed 10 ms rather than re-anchoring to currentTime,
639+
// so wake-up latency is corrected on the next tick instead of accumulating
640+
// into the frame period (which otherwise pulls the effective rate below 100 Hz).
641+
nextRecordMillis_ += 10;
642+
643+
// If we fell far behind (e.g. the thread was descheduled), resync to now
644+
// instead of bursting frames to catch up.
645+
if (nextRecordMillis_ < currentTime - 100) {
646+
nextRecordMillis_ = currentTime;
647+
}
621648

622649
mutex_.Unlock();
623650
audio_device_buffer_->DeliverRecordedData();
624651
mutex_.Lock();
625652
}
626653
}
627654

655+
int64_t sleepMillis = nextRecordMillis_ - webrtc::TimeMillis();
628656
mutex_.Unlock();
629657

630-
int64_t deltaTimeMillis = webrtc::TimeMillis() - currentTime;
631-
if (deltaTimeMillis < 10) {
632-
webrtc::Thread::SleepMs(10 - deltaTimeMillis);
658+
if (sleepMillis > 0) {
659+
webrtc::Thread::SleepMs(sleepMillis);
633660
}
634661

635662
return true;

0 commit comments

Comments
 (0)