|
52 | 52 | const UInt16 kStartEngineMaxRetries = 10; // Maximum blocking 1sec. |
53 | 53 | const useconds_t kStartEngineRetryDelayMs = 100; |
54 | 54 |
|
55 | | -// const size_t kMaximumFramesPerBuffer = 3072; // Maximum slice size for VoiceProcessingIO |
| 55 | +const size_t kMaximumFramesPerBuffer = 3072; |
56 | 56 | const size_t kAudioSampleSize = 2; // Signed 16-bit integer |
57 | 57 |
|
58 | 58 | AudioEngineDevice::AudioEngineDevice(bool bypass_voice_processing) |
|
857 | 857 | RTC_DCHECK(new_state.IsOutputEnabled()); |
858 | 858 | } |
859 | 859 |
|
860 | | - // Apply engine state changes |
| 860 | + // Save new state |
861 | 861 | engine_state_ = new_state; |
862 | | - UpdateEngineState(state); |
| 862 | + |
| 863 | + // Did switch Device -> Manual rendering |
| 864 | + if (state.DidEnableManualRenderingMode()) { |
| 865 | + EngineStateUpdate shutdown_state = state; // Copy current state |
| 866 | + shutdown_state.next = {}; // Reset next state to default |
| 867 | + UpdateDeviceEngineState(shutdown_state); // Shutdown device rendering |
| 868 | + EngineStateUpdate startup_state = state; // Copy current state |
| 869 | + shutdown_state.prev = {}; // |
| 870 | + UpdateManualEngineState(startup_state); // Start manual mode |
| 871 | + } else if (state.DidEnableDeviceRenderingMode()) { |
| 872 | + EngineStateUpdate shutdown_state = state; |
| 873 | + shutdown_state.next = {}; // Reset next state to default |
| 874 | + UpdateManualEngineState(shutdown_state); // Shutdown manual rendering |
| 875 | + EngineStateUpdate startup_state = state; // Copy current state |
| 876 | + shutdown_state.prev = {}; // |
| 877 | + UpdateDeviceEngineState(startup_state); // Start device mode |
| 878 | + } else if (new_state.render_mode == RenderMode::Device) { |
| 879 | + UpdateDeviceEngineState(state); |
| 880 | + } else if (new_state.render_mode == RenderMode::Manual) { |
| 881 | + UpdateManualEngineState(state); |
| 882 | + } |
863 | 883 |
|
864 | 884 | // Buffer should be playing if output is running. |
865 | 885 | if (new_state.IsOutputEnabled()) { |
|
876 | 896 | } |
877 | 897 | } |
878 | 898 |
|
879 | | -void AudioEngineDevice::UpdateEngineState(EngineStateUpdate state) { |
| 899 | +void AudioEngineDevice::UpdateManualEngineState(EngineStateUpdate state) { |
| 900 | + RTC_DCHECK_RUN_ON(thread_); |
| 901 | + RTC_DCHECK(engine_device_ == nullptr); |
| 902 | + |
| 903 | + if (state.prev.IsAnyRunning() && !state.next.IsAnyRunning()) { |
| 904 | + LOGI() << "Stopping AVAudioEngine..."; |
| 905 | + RTC_DCHECK(engine_manual_input_ != nil); |
| 906 | + [engine_manual_input_ stop]; |
| 907 | + |
| 908 | + LOGI() << "Stopping render thread..."; |
| 909 | + RTC_DCHECK(render_thread_ != nullptr); |
| 910 | + render_thread_->Stop(); |
| 911 | + render_thread_ = nullptr; |
| 912 | + |
| 913 | + LOGI() << "Releasing render buffer..."; |
| 914 | + RTC_DCHECK(render_buffer_ != nullptr); |
| 915 | + render_buffer_ = nullptr; |
| 916 | + |
| 917 | + if (observer_ != nullptr) { |
| 918 | + observer_->OnEngineDidStop(engine_manual_input_, state.next.IsOutputEnabled(), |
| 919 | + state.next.IsInputEnabled()); |
| 920 | + } |
| 921 | + } |
| 922 | + |
| 923 | + if (state.next.IsAnyEnabled() && !state.prev.IsAnyEnabled()) { |
| 924 | + LOGI() << "Creating AVAudioEngine (manual)..."; |
| 925 | + RTC_DCHECK(engine_manual_input_ == nullptr); |
| 926 | + engine_manual_input_ = [[AVAudioEngine alloc] init]; |
| 927 | + |
| 928 | + NSError* error = nil; |
| 929 | + BOOL result = |
| 930 | + [engine_manual_input_ enableManualRenderingMode:AVAudioEngineManualRenderingModeRealtime |
| 931 | + format:manual_render_rtc_format_ |
| 932 | + maximumFrameCount:kMaximumFramesPerBuffer |
| 933 | + error:&error]; |
| 934 | + if (!result) { |
| 935 | + LOGE() << "Failed to set manual rendering mode: " << error.localizedDescription.UTF8String; |
| 936 | + } |
| 937 | + |
| 938 | + // Assign manual rendering block |
| 939 | + render_block_ = engine_manual_input_.manualRenderingBlock; |
| 940 | + |
| 941 | + if (observer_ != nullptr) { |
| 942 | + observer_->OnEngineDidCreate(engine_manual_input_); |
| 943 | + } |
| 944 | + } |
| 945 | + |
| 946 | + if (!state.next.IsOutputEnabled() && audio_device_buffer_->IsPlaying()) { |
| 947 | + LOGI() << "Stopping Playout buffer..."; |
| 948 | + if (engine_device_ != nullptr) { |
| 949 | + // Rendering must be stopped first. |
| 950 | + RTC_DCHECK(!engine_device_.running); |
| 951 | + } |
| 952 | + audio_device_buffer_->StopPlayout(); |
| 953 | + } |
| 954 | + |
| 955 | + if (!state.next.IsInputEnabled() && audio_device_buffer_->IsRecording()) { |
| 956 | + LOGI() << "Stopping Record buffer..."; |
| 957 | + if (engine_device_ != nullptr) { |
| 958 | + // Rendering must be stopped first. |
| 959 | + RTC_DCHECK(!engine_device_.running); |
| 960 | + } |
| 961 | + audio_device_buffer_->StopRecording(); |
| 962 | + } |
| 963 | + |
| 964 | + if (state.DidAnyEnable() && observer_ != nullptr) { |
| 965 | + // Invoke here before configuring nodes. In iOS, session configuration is required before |
| 966 | + // enabling AGC, muted talker etc. |
| 967 | + observer_->OnEngineWillEnable(engine_manual_input_, state.next.IsOutputEnabled(), |
| 968 | + state.next.IsInputEnabled()); |
| 969 | + } |
| 970 | + |
| 971 | + if (state.next.IsOutputEnabled() && !state.prev.IsOutputEnabled()) { |
| 972 | + LOGI() << "Enabling output for AVAudioEngine..."; |
| 973 | + |
| 974 | + audio_device_buffer_->SetPlayoutSampleRate(manual_render_rtc_format_.sampleRate); |
| 975 | + audio_device_buffer_->SetPlayoutChannels(manual_render_rtc_format_.channelCount); |
| 976 | + RTC_DCHECK(audio_device_buffer_ != nullptr); |
| 977 | + fine_audio_buffer_.reset(new FineAudioBuffer(audio_device_buffer_.get())); |
| 978 | + |
| 979 | + } else if (state.prev.IsOutputEnabled() && !state.next.IsOutputEnabled()) { |
| 980 | + LOGI() << "Disabling output for AVAudioEngine..."; |
| 981 | + RTC_DCHECK(!engine_manual_input_.running); |
| 982 | + } |
| 983 | + |
| 984 | + if (state.next.IsInputEnabled() && |
| 985 | + (!state.prev.IsInputEnabled() || state.IsEngineRecreateRequired())) { |
| 986 | + LOGI() << "Enabling input for AVAudioEngine..."; |
| 987 | + |
| 988 | + audio_device_buffer_->SetPlayoutSampleRate(manual_render_rtc_format_.sampleRate); |
| 989 | + audio_device_buffer_->SetPlayoutChannels(manual_render_rtc_format_.channelCount); |
| 990 | + RTC_DCHECK(audio_device_buffer_ != nullptr); |
| 991 | + fine_audio_buffer_.reset(new FineAudioBuffer(audio_device_buffer_.get())); |
| 992 | + |
| 993 | + RTC_DCHECK(!engine_manual_input_.running); |
| 994 | + } else if (state.prev.IsInputEnabled() && !state.next.IsInputEnabled()) { |
| 995 | + LOGI() << "Disabling input for AVAudioEngine..."; |
| 996 | + RTC_DCHECK(!engine_manual_input_.running); |
| 997 | + } |
| 998 | + |
| 999 | + if (state.DidAnyDisable() && observer_ != nullptr) { |
| 1000 | + observer_->OnEngineDidDisable(engine_manual_input_, state.next.IsOutputEnabled(), |
| 1001 | + state.next.IsInputEnabled()); |
| 1002 | + } |
| 1003 | + |
| 1004 | + // Start playout buffer if output is running |
| 1005 | + if (state.next.IsOutputEnabled() && !audio_device_buffer_->IsPlaying()) { |
| 1006 | + if (engine_device_ != nullptr) { |
| 1007 | + // Rendering must be stopped first. |
| 1008 | + RTC_DCHECK(!engine_device_.running); |
| 1009 | + } |
| 1010 | + LOGI() << "Starting Playout buffer..."; |
| 1011 | + audio_device_buffer_->StartPlayout(); |
| 1012 | + fine_audio_buffer_->ResetPlayout(); |
| 1013 | + } |
| 1014 | + |
| 1015 | + // Start recording buffer if input is running |
| 1016 | + if (state.next.IsInputEnabled() && !audio_device_buffer_->IsRecording()) { |
| 1017 | + if (engine_device_ != nullptr) { |
| 1018 | + // Rendering must be stopped first. |
| 1019 | + RTC_DCHECK(!engine_device_.running); |
| 1020 | + } |
| 1021 | + LOGI() << "Starting Record buffer..."; |
| 1022 | + audio_device_buffer_->StartRecording(); |
| 1023 | + fine_audio_buffer_->ResetRecord(); |
| 1024 | + } |
| 1025 | + |
| 1026 | + if (state.next.IsAnyRunning() && !state.prev.IsAnyRunning()) { |
| 1027 | + if (observer_ != nullptr) { |
| 1028 | + observer_->OnEngineWillStart(engine_manual_input_, state.next.IsOutputEnabled(), |
| 1029 | + state.next.IsInputEnabled()); |
| 1030 | + } |
| 1031 | + |
| 1032 | + LOGI() << "Allocating render buffer..."; |
| 1033 | + RTC_DCHECK(render_buffer_ == nullptr); |
| 1034 | + render_buffer_ = [[AVAudioPCMBuffer alloc] initWithPCMFormat:manual_render_rtc_format_ |
| 1035 | + frameCapacity:kMaximumFramesPerBuffer]; |
| 1036 | + |
| 1037 | + LOGI() << "Starting AVAudioEngine..."; |
| 1038 | + NSError* error = nil; |
| 1039 | + |
| 1040 | + BOOL start_result = [engine_manual_input_ startAndReturnError:&error]; |
| 1041 | + if (!start_result) { |
| 1042 | + LOGE() << "Failed to start engine after " << kStartEngineMaxRetries << " attempts"; |
| 1043 | + DebugAudioEngine(); |
| 1044 | + } |
| 1045 | + |
| 1046 | + // Create render thread |
| 1047 | + LOGI() << "Starting render thread..."; |
| 1048 | + RTC_DCHECK(render_thread_ == nullptr); |
| 1049 | + render_thread_ = rtc::Thread::Create(); |
| 1050 | + render_thread_->SetName("render_thread", nullptr); |
| 1051 | + render_thread_->Start(); |
| 1052 | + render_thread_->PostTask([this] { |
| 1053 | + // RTC_DCHECK_RUN_ON(thread_); |
| 1054 | + this->StartRenderLoop(); |
| 1055 | + }); |
| 1056 | + } |
| 1057 | + |
| 1058 | + if (state.prev.IsAnyEnabled() && !state.next.IsAnyEnabled()) { |
| 1059 | + if (observer_ != nullptr) { |
| 1060 | + observer_->OnEngineWillRelease(engine_manual_input_); |
| 1061 | + } |
| 1062 | + LOGI() << "Releasing AVAudioEngine..."; |
| 1063 | + engine_manual_input_ = nil; |
| 1064 | + } |
| 1065 | +} |
| 1066 | + |
| 1067 | +void AudioEngineDevice::UpdateDeviceEngineState(EngineStateUpdate state) { |
880 | 1068 | RTC_DCHECK_RUN_ON(thread_); |
| 1069 | + RTC_DCHECK(engine_manual_input_ == nullptr); |
881 | 1070 |
|
882 | 1071 | if (state.prev.IsAnyRunning() && |
883 | 1072 | (!state.next.IsAnyRunning() || state.DidUpdateAudioGraph() || state.DidBeginInterruption() || |
|
902 | 1091 |
|
903 | 1092 | if (state.next.IsAnyEnabled() && |
904 | 1093 | (!state.prev.IsAnyEnabled() || state.IsEngineRecreateRequired())) { |
905 | | - LOGI() << "Creating AVAudioEngine..."; |
| 1094 | + LOGI() << "Creating AVAudioEngine (device)..."; |
906 | 1095 | RTC_DCHECK(engine_device_ == nullptr); |
907 | 1096 | engine_device_ = [[AVAudioEngine alloc] init]; |
908 | 1097 |
|
|
0 commit comments