Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 74 additions & 2 deletions common/darwin/Classes/FlutterWebRTCPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,9 @@ - (instancetype)initWithChannel:(FlutterMethodChannel*)channel
object:session];
#endif

// Observe audio device module events.
_peerConnectionFactory.audioDeviceModule.observer = self;
// NOTE: do not set the ADM observer here - _peerConnectionFactory is created lazily in
// initialize:, so at this point it is nil and the assignment would be a silent no-op.
// The observer is set right after the factory is created.

return self;
}
Expand Down Expand Up @@ -327,6 +328,11 @@ - (void)initialize:(NSArray*)networkIgnoreMask
decoderFactory:decoderFactory
audioProcessingModule:_audioManager.audioProcessingModule];

// Observe audio device module events (device changes + AudioEngine lifecycle).
// Must happen after the factory exists; the property is weak, but the plugin
// instance is retained by the Flutter registrar for the app's lifetime.
_peerConnectionFactory.audioDeviceModule.observer = self;

#if TARGET_OS_OSX
// CoreAudio ADM requires explicit device initialization on macOS
RTCAudioDeviceModule* audioDeviceModule = [_peerConnectionFactory audioDeviceModule];
Expand Down Expand Up @@ -2681,4 +2687,70 @@ - (void)audioDeviceModuleDidUpdateDevices:(RTCAudioDeviceModule *)audioDeviceMod
}
}

#if TARGET_OS_IPHONE
// AudioEngine ADM delegate. Only device updates are handled; the engine-lifecycle methods
// are no-op stubs, implemented because the ADM invokes every protocol method without
// respondsToSelector checks (all methods are required) - a partial adoption would crash
// with unrecognized selector once the observer is registered.
- (void)audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule
didReceiveSpeechActivityEvent:(RTCSpeechActivityEvent)speechActivityEvent {
}

- (NSInteger)audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule
didCreateEngine:(AVAudioEngine *)engine {
return 0;
}

- (NSInteger)audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule
willEnableEngine:(AVAudioEngine *)engine
isPlayoutEnabled:(BOOL)isPlayoutEnabled
isRecordingEnabled:(BOOL)isRecordingEnabled {
return 0;
}

- (NSInteger)audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule
willStartEngine:(AVAudioEngine *)engine
isPlayoutEnabled:(BOOL)isPlayoutEnabled
isRecordingEnabled:(BOOL)isRecordingEnabled {
return 0;
}

- (NSInteger)audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule
didStopEngine:(AVAudioEngine *)engine
isPlayoutEnabled:(BOOL)isPlayoutEnabled
isRecordingEnabled:(BOOL)isRecordingEnabled {
return 0;
}

- (NSInteger)audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule
didDisableEngine:(AVAudioEngine *)engine
isPlayoutEnabled:(BOOL)isPlayoutEnabled
isRecordingEnabled:(BOOL)isRecordingEnabled {
return 0;
}

- (NSInteger)audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule
willReleaseEngine:(AVAudioEngine *)engine {
return 0;
}

- (NSInteger)audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule
engine:(AVAudioEngine *)engine
configureInputFromSource:(AVAudioNode *)source
toDestination:(AVAudioNode *)destination
withFormat:(AVAudioFormat *)format
context:(NSDictionary *)context {
return 0; // no input-graph changes; the ADM applies its default wiring
}

- (NSInteger)audioDeviceModule:(RTCAudioDeviceModule *)audioDeviceModule
engine:(AVAudioEngine *)engine
configureOutputFromSource:(AVAudioNode *)source
toDestination:(AVAudioNode *)destination
withFormat:(AVAudioFormat *)format
context:(NSDictionary *)context {
return 0; // no output-graph changes; the ADM applies its default wiring
}
#endif

@end