Skip to content

Commit 98dc0ac

Browse files
committed
Rendering fix
1 parent 345f8b7 commit 98dc0ac

File tree

1 file changed

+37
-73
lines changed

1 file changed

+37
-73
lines changed

modules/audio_device/audio_engine_device.mm

Lines changed: 37 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -935,9 +935,6 @@
935935
LOGE() << "Failed to set manual rendering mode: " << error.localizedDescription.UTF8String;
936936
}
937937

938-
// Assign manual rendering block
939-
render_block_ = engine_manual_input_.manualRenderingBlock;
940-
941938
if (observer_ != nullptr) {
942939
observer_->OnEngineDidCreate(engine_manual_input_);
943940
}
@@ -970,6 +967,7 @@
970967

971968
if (state.next.IsOutputEnabled() && !state.prev.IsOutputEnabled()) {
972969
LOGI() << "Enabling output for AVAudioEngine...";
970+
RTC_DCHECK(!engine_manual_input_.running);
973971

974972
audio_device_buffer_->SetPlayoutSampleRate(manual_render_rtc_format_.sampleRate);
975973
audio_device_buffer_->SetPlayoutChannels(manual_render_rtc_format_.channelCount);
@@ -981,16 +979,25 @@
981979
RTC_DCHECK(!engine_manual_input_.running);
982980
}
983981

984-
if (state.next.IsInputEnabled() &&
985-
(!state.prev.IsInputEnabled() || state.IsEngineRecreateRequired())) {
982+
if (state.next.IsInputEnabled() && !state.prev.IsInputEnabled()) {
986983
LOGI() << "Enabling input for AVAudioEngine...";
984+
RTC_DCHECK(!engine_manual_input_.running);
987985

988-
audio_device_buffer_->SetPlayoutSampleRate(manual_render_rtc_format_.sampleRate);
989-
audio_device_buffer_->SetPlayoutChannels(manual_render_rtc_format_.channelCount);
986+
audio_device_buffer_->SetRecordingSampleRate(manual_render_rtc_format_.sampleRate);
987+
audio_device_buffer_->SetRecordingChannels(manual_render_rtc_format_.channelCount);
990988
RTC_DCHECK(audio_device_buffer_ != nullptr);
991989
fine_audio_buffer_.reset(new FineAudioBuffer(audio_device_buffer_.get()));
992990

993-
RTC_DCHECK(!engine_manual_input_.running);
991+
if (!(this->observer_ != nullptr &&
992+
this->observer_->OnEngineWillConnectOutput(
993+
engine_manual_input_, engine_manual_input_.mainMixerNode, this->OutputNode(),
994+
manual_render_rtc_format_))) {
995+
// Default implementation.
996+
[engine_manual_input_ connect:engine_manual_input_.mainMixerNode
997+
to:this->OutputNode()
998+
format:manual_render_rtc_format_];
999+
}
1000+
9941001
} else if (state.prev.IsInputEnabled() && !state.next.IsInputEnabled()) {
9951002
LOGI() << "Disabling input for AVAudioEngine...";
9961003
RTC_DCHECK(!engine_manual_input_.running);
@@ -1043,6 +1050,10 @@
10431050
DebugAudioEngine();
10441051
}
10451052

1053+
// Assign manual rendering block
1054+
render_block_ = engine_manual_input_.manualRenderingBlock;
1055+
RTC_DCHECK(render_block_ != nullptr);
1056+
10461057
// Create render thread
10471058
LOGI() << "Starting render thread...";
10481059
RTC_DCHECK(render_thread_ == nullptr);
@@ -1429,88 +1440,36 @@
14291440
void AudioEngineDevice::StartRenderLoop() {
14301441
RTC_DCHECK_RUN_ON(render_thread_.get());
14311442

1432-
// Constants for timing and frame management
14331443
const double sample_rate = manual_render_rtc_format_.sampleRate;
1434-
const double target_frame_count = sample_rate / 100; // 10ms chunks
1435-
const double nanoseconds_per_frame = 1e9 / sample_rate;
1436-
const double target_cycle_time_ns = target_frame_count * nanoseconds_per_frame;
1437-
1438-
// Timing management with exponential moving average
1439-
uint64_t last_cycle_time = mach_absolute_time();
1440-
double sleep_time_ms = 5.0;
1441-
const double min_sleep_time_ms = 0.5;
1442-
const double max_sleep_time_ms = 10.0;
1443-
1444-
// EMA coefficient (α) - higher value means more weight on recent samples
1445-
const double alpha = 0.2;
1446-
double ema_cycle_time = target_cycle_time_ns;
1447-
1448-
// Pre-allocate buffer for performance
1449-
const size_t buffer_size = static_cast<size_t>(target_frame_count) * kAudioSampleSize;
1450-
1451-
// Error recovery
1452-
int consecutive_errors = 0;
1453-
const int max_consecutive_errors = 3;
1444+
const size_t frames_per_buffer = static_cast<size_t>(sample_rate / 100); // 10ms chunks
1445+
const size_t buffer_size = frames_per_buffer * kAudioSampleSize;
1446+
const int sleep_ms = 5; // Fixed sleep time
14541447

14551448
while (!render_thread_->IsQuitting()) {
14561449
RTC_DCHECK(render_buffer_ != nullptr);
14571450
AudioBufferList* abl = const_cast<AudioBufferList*>(render_buffer_.audioBufferList);
1458-
1459-
// Precise timing calculation
1460-
uint64_t current_time = mach_absolute_time();
1461-
double elapsed_time_ns = (current_time - last_cycle_time) * machTickUnitsToNanoseconds_;
1462-
1463-
// Update EMA of cycle time
1464-
ema_cycle_time = (alpha * elapsed_time_ns) + ((1.0 - alpha) * ema_cycle_time);
1465-
1466-
// Dynamic sleep time adjustment using PID-like control
1467-
const double error = target_cycle_time_ns - ema_cycle_time;
1468-
const double kP = 0.2; // Proportional gain
1469-
const double adjustment = (error / target_cycle_time_ns) * kP;
1470-
sleep_time_ms =
1471-
std::clamp(sleep_time_ms * (1.0 + adjustment), min_sleep_time_ms, max_sleep_time_ms);
1472-
1473-
// Optimize buffer management
1474-
const unsigned int frames_to_render = static_cast<unsigned int>(target_frame_count);
14751451
abl->mBuffers[0].mDataByteSize = buffer_size;
14761452

1477-
// Render audio with error handling
14781453
OSStatus err = noErr;
1479-
AVAudioEngineManualRenderingStatus result = render_block_(frames_to_render, abl, &err);
1454+
AVAudioEngineManualRenderingStatus result = render_block_(frames_per_buffer, abl, &err);
14801455

14811456
if (result == AVAudioEngineManualRenderingStatusSuccess) {
1482-
consecutive_errors = 0; // Reset error counter on success
1483-
14841457
RTC_DCHECK(abl->mNumberBuffers == 1);
14851458
const int16_t* rtc_buffer =
14861459
static_cast<const int16_t*>(static_cast<const void*>(abl->mBuffers[0].mData));
14871460

1488-
// Update timing before processing
1489-
last_cycle_time = mach_absolute_time();
1461+
const uint64_t capture_time = mach_absolute_time();
1462+
const int64_t capture_time_ns = capture_time * machTickUnitsToNanoseconds_;
14901463

1491-
// Process audio data
14921464
fine_audio_buffer_->DeliverRecordedData(
1493-
rtc::ArrayView<const int16_t>(rtc_buffer, frames_to_render), kFixedRecordDelayEstimate,
1494-
absl::nullopt);
1465+
rtc::ArrayView<const int16_t>(rtc_buffer, frames_per_buffer), kFixedRecordDelayEstimate,
1466+
capture_time_ns);
14951467
} else {
1496-
consecutive_errors++;
1497-
LOGW() << "Render error: " << err << " frames: " << frames_to_render
1498-
<< " consecutive errors: " << consecutive_errors;
1499-
1500-
if (consecutive_errors >= max_consecutive_errors) {
1501-
// Reset timing on persistent errors
1502-
sleep_time_ms = 5.0;
1503-
ema_cycle_time = target_cycle_time_ns;
1504-
consecutive_errors = 0;
1505-
}
1468+
LOGW() << "Render error: " << err << " frames: " << frames_per_buffer;
15061469
}
15071470

1508-
// Precise sleep timing
15091471
if (!render_thread_->IsQuitting()) {
1510-
const int sleep_ms = static_cast<int>(std::round(sleep_time_ms));
1511-
if (sleep_ms > 0) {
1512-
render_thread_->SleepMs(sleep_ms);
1513-
}
1472+
render_thread_->SleepMs(sleep_ms);
15141473
}
15151474
}
15161475
}
@@ -1529,10 +1488,15 @@
15291488

15301489
AVAudioOutputNode* AudioEngineDevice::OutputNode() {
15311490
RTC_DCHECK_RUN_ON(thread_);
1532-
RTC_DCHECK(engine_device_ != nil);
1533-
RTC_DCHECK(engine_state_.IsOutputEnabled() || engine_state_.render_mode == RenderMode::Manual);
1491+
RTC_DCHECK(engine_state_.IsOutputEnabled());
15341492

1535-
return engine_device_.outputNode;
1493+
if (engine_state_.render_mode == RenderMode::Manual) {
1494+
RTC_DCHECK(engine_manual_input_ != nil);
1495+
return engine_manual_input_.outputNode;
1496+
} else {
1497+
RTC_DCHECK(engine_device_ != nil);
1498+
return engine_device_.outputNode;
1499+
}
15361500
}
15371501

15381502
// ----------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)