diff --git a/modules/audio_device/audio_engine_device.h b/modules/audio_device/audio_engine_device.h index b9a8c26e54..f5ceab9c6c 100644 --- a/modules/audio_device/audio_engine_device.h +++ b/modules/audio_device/audio_engine_device.h @@ -122,6 +122,11 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver bool output_enabled = false; bool output_running = false; + // Default true: the engine may activate on init. + // Set false to gate start/stop (e.g. CallKit). + bool output_available = true; + bool input_available = true; + // Output will be enabled when input is enabled bool input_follow_mode = true; bool input_enabled_persistent_mode = false; @@ -151,6 +156,7 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver bool operator==(const EngineState& rhs) const { return input_enabled == rhs.input_enabled && input_running == rhs.input_running && output_enabled == rhs.output_enabled && output_running == rhs.output_running && + input_available == rhs.input_available && output_available == rhs.output_available && input_follow_mode == rhs.input_follow_mode && input_enabled_persistent_mode == rhs.input_enabled_persistent_mode && input_muted == rhs.input_muted && is_interrupted == rhs.is_interrupted && @@ -172,32 +178,31 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver bool IsOutputInputLinked() const { return input_follow_mode && voice_processing_enabled; } bool IsOutputEnabled() const { - return IsOutputInputLinked() ? (IsInputEnabled() || output_enabled) : output_enabled; + bool result = IsOutputInputLinked() ? (IsInputEnabled() || output_enabled) : output_enabled; + return output_available && result; } bool IsOutputRunning() const { - return IsOutputInputLinked() ? (IsInputRunning() || output_running) : output_running; + bool result = IsOutputInputLinked() ? (IsInputRunning() || output_running) : output_running; + return output_available && result; } bool IsInputEnabled() const { - return !(mute_mode == MuteMode::RestartEngine && input_muted) && - (input_enabled || input_enabled_persistent_mode); + bool result = !(mute_mode == MuteMode::RestartEngine && input_muted) && + (input_enabled || input_enabled_persistent_mode); + return input_available && result; } bool IsInputRunning() const { - return !(mute_mode == MuteMode::RestartEngine && input_muted) && input_running; + bool result = !(mute_mode == MuteMode::RestartEngine && input_muted) && input_running; + return input_available && result; } bool IsAnyEnabled() const { return IsInputEnabled() || IsOutputEnabled(); } bool IsAnyRunning() const { return IsInputRunning() || IsOutputRunning(); } - bool IsAllEnabled() const { - return IsOutputInputLinked() ? IsInputEnabled() : IsInputEnabled() && output_enabled; - } - - bool IsAllRunning() const { - return IsOutputInputLinked() ? input_running : input_running && output_running; - } + bool IsAllEnabled() const { return IsInputEnabled() && IsOutputEnabled(); } + bool IsAllRunning() const { return IsInputRunning() && IsOutputRunning(); } bool IsOutputDefaultDevice() const { #if TARGET_OS_OSX @@ -318,6 +323,9 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver int32_t SetEngineState(EngineState enable); int32_t GetEngineState(EngineState* enabled); + int32_t SetEngineAvailability(bool input_available, bool output_available); + int32_t EngineAvailability(bool* input_available, bool* output_available); + int32_t SetObserver(AudioDeviceObserver* observer) override; bool IsAudioEngineDevice() const override { return true; } @@ -440,6 +448,7 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver bool IsMicrophonePermissionGranted(); void ResetEngineState(); int32_t ModifyEngineState(std::function state_transform); + int32_t ApplyDeviceEngineState(EngineStateUpdate& state); int32_t ApplyManualEngineState(EngineStateUpdate& state); diff --git a/modules/audio_device/audio_engine_device.mm b/modules/audio_device/audio_engine_device.mm index 18b417404f..270e030d9d 100644 --- a/modules/audio_device/audio_engine_device.mm +++ b/modules/audio_device/audio_engine_device.mm @@ -1260,6 +1260,33 @@ return result; } +int32_t AudioEngineDevice::SetEngineAvailability(bool input_available, bool output_available) { + RTC_DCHECK_RUN_ON(thread_); + LOGI() << "SetEngineAvailability: " << input_available << " " << output_available; + + int32_t result = + ModifyEngineState([input_available, output_available](EngineState state) -> EngineState { + state.input_available = input_available; + state.output_available = output_available; + return state; + }); + + return result; +} + +int32_t AudioEngineDevice::EngineAvailability(bool* input_available, bool* output_available) { + RTC_DCHECK_RUN_ON(thread_); + + if (input_available == nullptr || output_available == nullptr) { + return -1; + } + + *input_available = engine_state_.input_available; + *output_available = engine_state_.output_available; + + return 0; +} + int32_t AudioEngineDevice::ManualRenderingMode(bool* enabled) { LOGI() << "ManualRenderingMode"; RTC_DCHECK_RUN_ON(thread_); @@ -2816,6 +2843,8 @@ << ", IsAnyRunning: " << state.IsAnyRunning() << ", IsAllEnabled: " << state.IsAllEnabled() << ", IsAllRunning: " << state.IsAllRunning() + << ", InputAvailable: " << state.input_available + << ", OutputAvailable: " << state.output_available << ", AdvancedDucking: " << state.advanced_ducking << ", DuckingLevel: " << state.ducking_level << ", MuteMode: " << static_cast(state.mute_mode) diff --git a/sdk/objc/api/peerconnection/RTCAudioDeviceModule.h b/sdk/objc/api/peerconnection/RTCAudioDeviceModule.h index ae038f48ce..6123010caf 100644 --- a/sdk/objc/api/peerconnection/RTCAudioDeviceModule.h +++ b/sdk/objc/api/peerconnection/RTCAudioDeviceModule.h @@ -49,13 +49,18 @@ typedef struct { RTC_OBJC_TYPE(RTCAudioEngineMuteMode) muteMode; } RTC_OBJC_TYPE(RTCAudioEngineState); -typedef struct { - bool voiceProcessingEnabled; - bool voiceProcessingBypassed; - bool voiceProcessingAGCEnabled; - bool stereoPlayoutEnabled; +typedef struct { + bool voiceProcessingEnabled; + bool voiceProcessingBypassed; + bool voiceProcessingAGCEnabled; + bool stereoPlayoutEnabled; } RTC_OBJC_TYPE(RTCAudioProcessingState); +typedef struct { + bool isInputAvailable; + bool isOutputAvailable; +} RTC_OBJC_TYPE(RTCAudioEngineAvailability); + RTC_EXTERN NSString *const RTC_CONSTANT_TYPE(RTCAudioEngineInputMixerNodeKey); @class RTC_OBJC_TYPE(RTCAudioDeviceModule); @@ -153,6 +158,8 @@ RTC_OBJC_EXPORT - (NSInteger)initAndStartRecording; +- (NSInteger)setEngineAvailability:(RTC_OBJC_TYPE(RTCAudioEngineAvailability))availability; + // For testing purposes @property(nonatomic, readonly) BOOL isPlayoutInitialized; @property(nonatomic, readonly) BOOL isRecordingInitialized; @@ -205,6 +212,8 @@ RTC_OBJC_EXPORT @property(nonatomic, assign) BOOL prefersStereoPlayout; - (void)refreshStereoPlayoutState; +@property(nonatomic, readonly) RTC_OBJC_TYPE(RTCAudioEngineAvailability) engineAvailability; + @end NS_ASSUME_NONNULL_END diff --git a/sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm b/sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm index e8e2baf5b8..ab8565cb4e 100644 --- a/sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm +++ b/sdk/objc/api/peerconnection/RTCAudioDeviceModule.mm @@ -566,6 +566,30 @@ - (void)setEngineState:(RTC_OBJC_TYPE(RTCAudioEngineState))state { #pragma mark - Unique to AudioEngineDevice +- (NSInteger)setEngineAvailability:(RTC_OBJC_TYPE(RTCAudioEngineAvailability))availability { + webrtc::AudioEngineDevice *module = static_cast(_native.get()); + if (module == nullptr) return -1; + + return _workerThread->BlockingCall([module, availability] { + return module->SetEngineAvailability(availability.isInputAvailable, + availability.isOutputAvailable); + }); +} + +- (RTC_OBJC_TYPE(RTCAudioEngineAvailability))engineAvailability { + webrtc::AudioEngineDevice *module = static_cast(_native.get()); + if (module == nullptr) return RTC_OBJC_TYPE(RTCAudioEngineAvailability)(NO, NO); + + return _workerThread->BlockingCall([module] { + bool input_available = false; + bool output_available = false; + int32_t result = module->EngineAvailability(&input_available, &output_available); + if (result != 0) return RTC_OBJC_TYPE(RTCAudioEngineAvailability)(NO, NO); + + return RTC_OBJC_TYPE(RTCAudioEngineAvailability)(input_available, output_available); + }); +} + - (BOOL)isRecordingAlwaysPreparedMode { #if !defined(WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE) return NO;