|
935 | 935 | LOGE() << "Failed to set manual rendering mode: " << error.localizedDescription.UTF8String; |
936 | 936 | } |
937 | 937 |
|
938 | | - // Assign manual rendering block |
939 | | - render_block_ = engine_manual_input_.manualRenderingBlock; |
940 | | - |
941 | 938 | if (observer_ != nullptr) { |
942 | 939 | observer_->OnEngineDidCreate(engine_manual_input_); |
943 | 940 | } |
|
970 | 967 |
|
971 | 968 | if (state.next.IsOutputEnabled() && !state.prev.IsOutputEnabled()) { |
972 | 969 | LOGI() << "Enabling output for AVAudioEngine..."; |
| 970 | + RTC_DCHECK(!engine_manual_input_.running); |
973 | 971 |
|
974 | 972 | audio_device_buffer_->SetPlayoutSampleRate(manual_render_rtc_format_.sampleRate); |
975 | 973 | audio_device_buffer_->SetPlayoutChannels(manual_render_rtc_format_.channelCount); |
|
981 | 979 | RTC_DCHECK(!engine_manual_input_.running); |
982 | 980 | } |
983 | 981 |
|
984 | | - if (state.next.IsInputEnabled() && |
985 | | - (!state.prev.IsInputEnabled() || state.IsEngineRecreateRequired())) { |
| 982 | + if (state.next.IsInputEnabled() && !state.prev.IsInputEnabled()) { |
986 | 983 | LOGI() << "Enabling input for AVAudioEngine..."; |
| 984 | + RTC_DCHECK(!engine_manual_input_.running); |
987 | 985 |
|
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); |
990 | 988 | RTC_DCHECK(audio_device_buffer_ != nullptr); |
991 | 989 | fine_audio_buffer_.reset(new FineAudioBuffer(audio_device_buffer_.get())); |
992 | 990 |
|
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 | + |
994 | 1001 | } else if (state.prev.IsInputEnabled() && !state.next.IsInputEnabled()) { |
995 | 1002 | LOGI() << "Disabling input for AVAudioEngine..."; |
996 | 1003 | RTC_DCHECK(!engine_manual_input_.running); |
|
1043 | 1050 | DebugAudioEngine(); |
1044 | 1051 | } |
1045 | 1052 |
|
| 1053 | + // Assign manual rendering block |
| 1054 | + render_block_ = engine_manual_input_.manualRenderingBlock; |
| 1055 | + RTC_DCHECK(render_block_ != nullptr); |
| 1056 | + |
1046 | 1057 | // Create render thread |
1047 | 1058 | LOGI() << "Starting render thread..."; |
1048 | 1059 | RTC_DCHECK(render_thread_ == nullptr); |
|
1429 | 1440 | void AudioEngineDevice::StartRenderLoop() { |
1430 | 1441 | RTC_DCHECK_RUN_ON(render_thread_.get()); |
1431 | 1442 |
|
1432 | | - // Constants for timing and frame management |
1433 | 1443 | 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 |
1454 | 1447 |
|
1455 | 1448 | while (!render_thread_->IsQuitting()) { |
1456 | 1449 | RTC_DCHECK(render_buffer_ != nullptr); |
1457 | 1450 | 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); |
1475 | 1451 | abl->mBuffers[0].mDataByteSize = buffer_size; |
1476 | 1452 |
|
1477 | | - // Render audio with error handling |
1478 | 1453 | OSStatus err = noErr; |
1479 | | - AVAudioEngineManualRenderingStatus result = render_block_(frames_to_render, abl, &err); |
| 1454 | + AVAudioEngineManualRenderingStatus result = render_block_(frames_per_buffer, abl, &err); |
1480 | 1455 |
|
1481 | 1456 | if (result == AVAudioEngineManualRenderingStatusSuccess) { |
1482 | | - consecutive_errors = 0; // Reset error counter on success |
1483 | | - |
1484 | 1457 | RTC_DCHECK(abl->mNumberBuffers == 1); |
1485 | 1458 | const int16_t* rtc_buffer = |
1486 | 1459 | static_cast<const int16_t*>(static_cast<const void*>(abl->mBuffers[0].mData)); |
1487 | 1460 |
|
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_; |
1490 | 1463 |
|
1491 | | - // Process audio data |
1492 | 1464 | 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); |
1495 | 1467 | } 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; |
1506 | 1469 | } |
1507 | 1470 |
|
1508 | | - // Precise sleep timing |
1509 | 1471 | 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); |
1514 | 1473 | } |
1515 | 1474 | } |
1516 | 1475 | } |
|
1529 | 1488 |
|
1530 | 1489 | AVAudioOutputNode* AudioEngineDevice::OutputNode() { |
1531 | 1490 | 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()); |
1534 | 1492 |
|
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 | + } |
1536 | 1500 | } |
1537 | 1501 |
|
1538 | 1502 | // ---------------------------------------------------------------------------------------------------- |
|
0 commit comments