From 44b882d921efca8b88c4fdbee8991dfa18fe492c Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Thu, 1 Sep 2022 02:47:26 -0700 Subject: [PATCH] Cherry-pick 78a2a0119f7b. rdar://problem/100335624 [iOS] Fire configurationchange event in case of switching automatically to another microphone while capturing https://bugs.webkit.org/show_bug.cgi?id=244535 rdar://problem/99331061 Reviewed by Eric Carlson. On iOS, the user may change of audio device through control center, while capturing. In that case, the microphone automatically switches to the new device. To expose this to the WebPage, we can use configurationchange event. The flow is: - AVAudioSessionCaptureDeviceManager detects microphone change - CoreAudioSharedUnit notifies CoreAudioCaptureSource in GPUProcess. - UserMediaCaptureManagerProxy is notified by CoreAudioCaptureSource and notifies UserMediaCaptureManager through IPC. - UserMediaCaptureManager in WebProcess notifies its source which notifies its MediaStreamTrack(s). - At the same time, settings and capabilities are updated. We add a mock API to test this code path. Covered by LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html. * LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt: Added. * LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html: Added. * Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp: (WebCore::MediaStreamTrack::trackConfigurationChanged): * Source/WebCore/Modules/mediastream/MediaStreamTrack.h: * Source/WebCore/Modules/mediastream/MediaStreamTrack.idl: * Source/WebCore/dom/EventNames.h: * Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp: (WebCore::MediaStreamTrackPrivate::sourceConfigurationChanged): * Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h: * Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp: (WebCore::RealtimeMediaSource::RealtimeMediaSource): (WebCore::RealtimeMediaSource::setPersistentId): (WebCore::RealtimeMediaSource::initializePersistentId): * Source/WebCore/platform/mediastream/RealtimeMediaSource.h: * Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h: * Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm: (WebCore::AVAudioSessionCaptureDeviceManager::retrieveAudioSessionCaptureDevices const): * Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.cpp: (WebCore::BaseAudioSharedUnit::handleNewCurrentMicrophoneDevice): * Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.h: * Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp: (WebCore::CoreAudioCaptureSource::handleNewCurrentMicrophoneDevice): * Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h: * Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.mm: (WebCore::RealtimeMediaSourceCenter::shouldInterruptAudioOnPageVisibilityChange): * Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp: (WebCore::MockRealtimeMediaSourceCenter::triggerMockMicrophoneConfigurationChange): * Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h: * Source/WebKit/GPUProcess/GPUProcess.cpp: (WebKit::GPUProcess::triggerMockMicrophoneConfigurationChange): * Source/WebKit/GPUProcess/GPUProcess.h: * Source/WebKit/GPUProcess/GPUProcess.messages.in: * Source/WebKit/UIProcess/API/C/WKPage.cpp: (WKPageTriggerMockMicrophoneConfigurationChange): * Source/WebKit/UIProcess/API/C/WKPagePrivate.h: * Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp: * Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp: (WebKit::GPUProcessProxy::triggerMockMicrophoneConfigurationChange): * Source/WebKit/UIProcess/GPU/GPUProcessProxy.h: * Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp: (WebKit::RemoteRealtimeMediaSource::configurationChanged): * Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h: * Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp: (WebKit::UserMediaCaptureManager::sourceConfigurationChanged): * Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h: * Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.messages.in: * Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl: * Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp: (WTR::TestRunner::triggerMockMicrophoneConfigurationChange): * Tools/WebKitTestRunner/InjectedBundle/TestRunner.h: * Tools/WebKitTestRunner/TestController.cpp: (WTR::TestController::triggerMockMicrophoneConfigurationChange): * Tools/WebKitTestRunner/TestController.h: * Tools/WebKitTestRunner/TestInvocation.cpp: (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle): Canonical link: https://commits.webkit.org/254035@main Canonical link: https://commits.webkit.org/252432.808@safari-7614.3.7.1-branch --- ...reamtrack-configurationchange-expected.txt | 4 +++ .../mediastreamtrack-configurationchange.html | 35 +++++++++++++++++++ LayoutTests/platform/glib/TestExpectations | 2 ++ .../Modules/mediastream/MediaStreamTrack.cpp | 10 ++++++ .../Modules/mediastream/MediaStreamTrack.h | 1 + .../Modules/mediastream/MediaStreamTrack.idl | 1 + Source/WebCore/dom/EventNames.h | 1 + .../mediastream/MediaStreamTrackPrivate.cpp | 9 +++++ .../mediastream/MediaStreamTrackPrivate.h | 2 ++ .../mediastream/RealtimeMediaSource.cpp | 11 ++++++ .../mediastream/RealtimeMediaSource.h | 11 +++++- .../ios/AVAudioSessionCaptureDeviceManager.h | 1 + .../ios/AVAudioSessionCaptureDeviceManager.mm | 15 ++++++-- .../mediastream/mac/BaseAudioSharedUnit.cpp | 7 ++++ .../mediastream/mac/BaseAudioSharedUnit.h | 2 ++ .../mac/CoreAudioCaptureSource.cpp | 18 ++++++++++ .../mediastream/mac/CoreAudioCaptureSource.h | 2 ++ .../mac/RealtimeMediaSourceCenterMac.mm | 2 ++ .../mock/MockRealtimeMediaSourceCenter.cpp | 11 ++++++ .../mock/MockRealtimeMediaSourceCenter.h | 2 ++ Source/WebKit/GPUProcess/GPUProcess.cpp | 5 +++ Source/WebKit/GPUProcess/GPUProcess.h | 1 + .../WebKit/GPUProcess/GPUProcess.messages.in | 1 + Source/WebKit/UIProcess/API/C/WKPage.cpp | 9 +++++ Source/WebKit/UIProcess/API/C/WKPagePrivate.h | 1 + .../Cocoa/UserMediaCaptureManagerProxy.cpp | 5 +++ .../WebKit/UIProcess/GPU/GPUProcessProxy.cpp | 5 +++ Source/WebKit/UIProcess/GPU/GPUProcessProxy.h | 1 + .../cocoa/RemoteRealtimeMediaSource.cpp | 12 +++++++ .../cocoa/RemoteRealtimeMediaSource.h | 2 ++ .../cocoa/UserMediaCaptureManager.cpp | 13 +++++++ .../cocoa/UserMediaCaptureManager.h | 2 ++ .../cocoa/UserMediaCaptureManager.messages.in | 1 + .../InjectedBundle/Bindings/TestRunner.idl | 1 + .../InjectedBundle/TestRunner.cpp | 5 +++ .../InjectedBundle/TestRunner.h | 1 + Tools/WebKitTestRunner/TestController.cpp | 5 +++ Tools/WebKitTestRunner/TestController.h | 1 + Tools/WebKitTestRunner/TestInvocation.cpp | 5 +++ 39 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt create mode 100644 LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html diff --git a/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt b/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt new file mode 100644 index 000000000000..9764691bb570 --- /dev/null +++ b/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange-expected.txt @@ -0,0 +1,4 @@ + + +PASS Trigger configurationchange event in case OS changes microphone on its own + diff --git a/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html b/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html new file mode 100644 index 000000000000..b571976e9e67 --- /dev/null +++ b/LayoutTests/fast/mediastream/mediastreamtrack-configurationchange.html @@ -0,0 +1,35 @@ + + + + + configurationchange event. + + + + + + + + diff --git a/LayoutTests/platform/glib/TestExpectations b/LayoutTests/platform/glib/TestExpectations index 07e0b7a06265..52fd257ae3af 100644 --- a/LayoutTests/platform/glib/TestExpectations +++ b/LayoutTests/platform/glib/TestExpectations @@ -809,6 +809,8 @@ webkit.org/b/230415 fast/mediastream/RTCPeerConnection-iceconnectionstatechange- webkit.org/b/79203 fast/mediastream/RTCRtpSender-replaceTrack.html [ Failure ] webkit.org/b/187603 fast/mediastream/media-stream-track-source-failure.html [ Timeout Failure Pass ] +fast/mediastream/mediastreamtrack-configurationchange.html [ Failure ] + webkit.org/b/223508 imported/w3c/web-platform-tests/mediacapture-streams/MediaStream-MediaElement-srcObject.https.html [ Failure Pass ] webkit.org/b/218317 media/media-source/media-source-trackid-change.html [ Failure ] diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp b/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp index fc6a10ac566b..fee90dc46498 100644 --- a/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp +++ b/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp @@ -618,6 +618,16 @@ void MediaStreamTrack::trackSettingsChanged(MediaStreamTrackPrivate&) configureTrackRendering(); } +void MediaStreamTrack::trackConfigurationChanged(MediaStreamTrackPrivate&) +{ + queueTaskKeepingObjectAlive(*this, TaskSource::Networking, [this] { + if (!scriptExecutionContext() || scriptExecutionContext()->activeDOMObjectsAreStopped() || m_private->muted() || ended()) + return; + + dispatchEvent(Event::create(eventNames().configurationchangeEvent, Event::CanBubble::No, Event::IsCancelable::No)); + }); +} + void MediaStreamTrack::trackEnabledChanged(MediaStreamTrackPrivate&) { configureTrackRendering(); diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrack.h b/Source/WebCore/Modules/mediastream/MediaStreamTrack.h index bcafbde10724..6ede7dc01939 100644 --- a/Source/WebCore/Modules/mediastream/MediaStreamTrack.h +++ b/Source/WebCore/Modules/mediastream/MediaStreamTrack.h @@ -192,6 +192,7 @@ class MediaStreamTrack void trackMutedChanged(MediaStreamTrackPrivate&) final; void trackSettingsChanged(MediaStreamTrackPrivate&) final; void trackEnabledChanged(MediaStreamTrackPrivate&) final; + void trackConfigurationChanged(MediaStreamTrackPrivate&) final; // PlatformMediaSession::AudioCaptureSource bool isCapturingAudio() const final; diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrack.idl b/Source/WebCore/Modules/mediastream/MediaStreamTrack.idl index bf06541a97ee..56c3ed4da3c3 100644 --- a/Source/WebCore/Modules/mediastream/MediaStreamTrack.idl +++ b/Source/WebCore/Modules/mediastream/MediaStreamTrack.idl @@ -42,6 +42,7 @@ enum MediaStreamTrackState { "live", "ended" }; [ImplementedAs=mutedForBindings] readonly attribute boolean muted; attribute EventHandler onmute; attribute EventHandler onunmute; + attribute EventHandler onconfigurationchange; readonly attribute MediaStreamTrackState readyState; attribute EventHandler onended; diff --git a/Source/WebCore/dom/EventNames.h b/Source/WebCore/dom/EventNames.h index 6c1b98796d02..61aabd0895b4 100644 --- a/Source/WebCore/dom/EventNames.h +++ b/Source/WebCore/dom/EventNames.h @@ -88,6 +88,7 @@ namespace WebCore { macro(compositionend) \ macro(compositionstart) \ macro(compositionupdate) \ + macro(configurationchange) \ macro(connect) \ macro(connectionstatechange) \ macro(connecting) \ diff --git a/Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp b/Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp index 71ed8f8d3951..3e25fdafe9c2 100644 --- a/Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp +++ b/Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp @@ -243,6 +243,15 @@ void MediaStreamTrackPrivate::sourceSettingsChanged() }); } +void MediaStreamTrackPrivate::sourceConfigurationChanged() +{ + ALWAYS_LOG(LOGIDENTIFIER); + + forEachObserver([this](auto& observer) { + observer.trackConfigurationChanged(*this); + }); +} + bool MediaStreamTrackPrivate::preventSourceFromStopping() { ALWAYS_LOG(LOGIDENTIFIER, m_isEnded); diff --git a/Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h b/Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h index 6814558b30af..e607fddd0ade 100644 --- a/Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h +++ b/Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h @@ -57,6 +57,7 @@ class MediaStreamTrackPrivate final virtual void trackEnded(MediaStreamTrackPrivate&) = 0; virtual void trackMutedChanged(MediaStreamTrackPrivate&) = 0; virtual void trackSettingsChanged(MediaStreamTrackPrivate&) = 0; + virtual void trackConfigurationChanged(MediaStreamTrackPrivate&) { }; virtual void trackEnabledChanged(MediaStreamTrackPrivate&) = 0; virtual void readyStateChanged(MediaStreamTrackPrivate&) { }; }; @@ -133,6 +134,7 @@ class MediaStreamTrackPrivate final void sourceStopped() final; void sourceMutedChanged() final; void sourceSettingsChanged() final; + void sourceConfigurationChanged() final; bool preventSourceFromStopping() final; void audioUnitWillStart() final; void hasStartedProducingData() final; diff --git a/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp b/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp index 50bd0a652119..fbffb66563bc 100644 --- a/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp +++ b/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp @@ -54,6 +54,17 @@ RealtimeMediaSource::RealtimeMediaSource(Type type, AtomString&& name, String&& , m_persistentID(WTFMove(deviceID)) , m_type(type) , m_name(WTFMove(name)) +{ + initializePersistentId(); +} + +void RealtimeMediaSource::setPersistentId(const String& persistentID) +{ + m_persistentID = persistentID; + initializePersistentId(); +} + +void RealtimeMediaSource::initializePersistentId() { if (m_persistentID.isEmpty()) m_persistentID = createVersion4UUIDString(); diff --git a/Source/WebCore/platform/mediastream/RealtimeMediaSource.h b/Source/WebCore/platform/mediastream/RealtimeMediaSource.h index a81760fbbb61..1d47926bd056 100644 --- a/Source/WebCore/platform/mediastream/RealtimeMediaSource.h +++ b/Source/WebCore/platform/mediastream/RealtimeMediaSource.h @@ -85,6 +85,7 @@ class WEBCORE_EXPORT RealtimeMediaSource virtual void sourceMutedChanged() { } virtual void sourceSettingsChanged() { } virtual void audioUnitWillStart() { } + virtual void sourceConfigurationChanged() { } // Observer state queries. virtual bool preventSourceFromStopping() { return false; } @@ -137,7 +138,6 @@ class WEBCORE_EXPORT RealtimeMediaSource virtual bool interrupted() const { return false; } const AtomString& name() const { return m_name; } - void setName(const AtomString& name) { m_name = name; } unsigned fitnessScore() const { return m_fitnessScore; } @@ -259,6 +259,9 @@ class WEBCORE_EXPORT RealtimeMediaSource void setType(Type); + void setName(const AtomString&); + void setPersistentId(const String&); + private: virtual void startProducingData() { } virtual void stopProducingData() { } @@ -270,6 +273,7 @@ class WEBCORE_EXPORT RealtimeMediaSource virtual void didEnd() { } void updateHasStartedProducingData(); + void initializePersistentId(); #if !RELEASE_LOG_DISABLED RefPtr m_logger; @@ -327,6 +331,11 @@ struct CaptureSourceOrError { String convertEnumerationToString(RealtimeMediaSource::Type); +inline void RealtimeMediaSource::setName(const AtomString& name) +{ + m_name = name; +} + inline void RealtimeMediaSource::whenReady(CompletionHandler&& callback) { callback({ }); diff --git a/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h b/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h index 0ff72e2bcb53..a87ade8c074d 100644 --- a/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h +++ b/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h @@ -72,6 +72,7 @@ class AVAudioSessionCaptureDeviceManager final : public CaptureDeviceManager { Vector retrieveAudioSessionCaptureDevices() const; void setAudioCaptureDevices(Vector&&); bool setPreferredAudioSessionDeviceUIDInternal(const String&); + void notifyNewCurrentMicrophoneDevice(CaptureDevice&&); enum class AudioSessionState { NotNeeded, Inactive, Active }; diff --git a/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm b/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm index 4b62f1c3667d..e738449cca2b 100644 --- a/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm +++ b/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm @@ -29,6 +29,7 @@ #if ENABLE(MEDIA_STREAM) && PLATFORM(IOS_FAMILY) #import "AVAudioSessionCaptureDevice.h" +#import "CoreAudioSharedUnit.h" #import "Logging.h" #import "RealtimeMediaSourceCenter.h" #import @@ -277,15 +278,23 @@ - (void)routeDidChange:(NSNotification *)notification Vector AVAudioSessionCaptureDeviceManager::retrieveAudioSessionCaptureDevices() const { auto defaultMicrophoneInformation = computeDefaultMicrophoneInformation(); - if (!defaultMicrophoneInformation && !m_lastDefaultMicrophone) - m_lastDefaultMicrophone = [m_audioSession currentRoute].inputs.firstObject; + auto currentInput = [m_audioSession currentRoute].inputs.firstObject; + if (currentInput) { + if (currentInput != m_lastDefaultMicrophone.get()) { + auto device = AVAudioSessionCaptureDevice::create(currentInput, currentInput); + callOnWebThreadOrDispatchAsyncOnMainThread(makeBlockPtr([device = crossThreadCopy(WTFMove(device))] () mutable { + CoreAudioSharedUnit::singleton().handleNewCurrentMicrophoneDevice(WTFMove(device)); + }).get()); + } + m_lastDefaultMicrophone = currentInput; + } auto availableInputs = [m_audioSession availableInputs]; Vector newAudioDevices; newAudioDevices.reserveInitialCapacity(availableInputs.count); for (AVAudioSessionPortDescription *portDescription in availableInputs) { - auto device = AVAudioSessionCaptureDevice::create(portDescription, m_lastDefaultMicrophone.get()); + auto device = AVAudioSessionCaptureDevice::create(portDescription, currentInput); if (defaultMicrophoneInformation) device.setIsDefault((defaultMicrophoneInformation->isBuiltInMicrophoneDefault && portDescription.portType == getAVAudioSessionPortBuiltInMic()) || [portDescription.UID isEqualToString: defaultMicrophoneInformation->routeUID]); newAudioDevices.uncheckedAppend(WTFMove(device)); diff --git a/Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.cpp b/Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.cpp index 7a52447683f8..12ed0e5affa0 100644 --- a/Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.cpp +++ b/Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.cpp @@ -294,6 +294,13 @@ void BaseAudioSharedUnit::whenAudioCaptureUnitIsNotRunning(Function&& ca m_whenNotRunningCallbacks.append(WTFMove(callback)); } +void BaseAudioSharedUnit::handleNewCurrentMicrophoneDevice(CaptureDevice&& device) +{ + forEachClient([&device](auto& client) { + client.handleNewCurrentMicrophoneDevice(device); + }); +} + } // namespace WebCore #endif // ENABLE(MEDIA_STREAM) diff --git a/Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.h b/Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.h index 6f0d3eb9d35b..68072b6c4f3b 100644 --- a/Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.h +++ b/Source/WebCore/platform/mediastream/mac/BaseAudioSharedUnit.h @@ -85,6 +85,8 @@ class BaseAudioSharedUnit : public CanMakeWeakPtrfirst : emptyString(); } + void handleNewCurrentMicrophoneDevice(CaptureDevice&&); + protected: void forEachClient(const Function&) const; bool hasClients() const { return !m_clients.isEmpty(); } diff --git a/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp b/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp index bbe6d4a1eda6..e6695509c1be 100644 --- a/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp +++ b/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp @@ -335,6 +335,24 @@ void CoreAudioCaptureSource::audioUnitWillStart() }); } +void CoreAudioCaptureSource::handleNewCurrentMicrophoneDevice(const CaptureDevice& device) +{ + if (!isProducingData() || persistentID() == device.persistentId()) + return; + + RELEASE_LOG_INFO(WebRTC, "CoreAudioCaptureSource switching from '%s' to '%s'", name().string().utf8().data(), device.label().utf8().data()); + + setName(AtomString { device.label() }); + setPersistentId(device.persistentId()); + + m_currentSettings = { }; + m_capabilities = { }; + + forEachObserver([](auto& observer) { + observer.sourceConfigurationChanged(); + }); +} + } // namespace WebCore #endif // ENABLE(MEDIA_STREAM) diff --git a/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h b/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h index ab3a277c4953..7a734a08eae2 100644 --- a/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h +++ b/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h @@ -59,6 +59,8 @@ class CoreAudioCaptureSource : public RealtimeMediaSource { CMClockRef timebaseClock(); + void handleNewCurrentMicrophoneDevice(const CaptureDevice&); + protected: CoreAudioCaptureSource(String&& deviceID, AtomString&& label, String&& hashSalt, uint32_t persistentID, BaseAudioSharedUnit*, PageIdentifier); virtual ~CoreAudioCaptureSource(); diff --git a/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.mm b/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.mm index 7715ec22ea83..601ae6872f30 100644 --- a/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.mm +++ b/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.mm @@ -42,11 +42,13 @@ if (!modes) return true; + RELEASE_LOG_ERROR(WebRTC, "RealtimeMediaSourceCenter::shouldInterruptAudioOnPageVisibilityChange2"); int modesCount = [modes count]; for (int i = 0; i < modesCount; i++) { if ([[modes objectAtIndex:i] isEqual: @"audio"]) return false; } + RELEASE_LOG_ERROR(WebRTC, "RealtimeMediaSourceCenter::shouldInterruptAudioOnPageVisibilityChange3"); return true; #else return false; diff --git a/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp b/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp index fad8e9b156e6..e1c41fef2841 100644 --- a/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp +++ b/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp @@ -44,6 +44,7 @@ #if PLATFORM(COCOA) #include "CoreAudioCaptureSource.h" #include "DisplayCaptureSourceCocoa.h" +#include "MockAudioSharedUnit.h" #include "MockRealtimeVideoSourceMac.h" #endif @@ -319,6 +320,16 @@ void MockRealtimeMediaSourceCenter::setMockCaptureDevicesInterrupted(bool isCame MockRealtimeAudioSource::setIsInterrupted(isMicrophoneInterrupted); } +void MockRealtimeMediaSourceCenter::triggerMockMicrophoneConfigurationChange() +{ +#if PLATFORM(COCOA) + auto devices = audioCaptureDeviceManager().captureDevices(); + if (devices.size() <= 1) + return; + MockAudioSharedUnit::singleton().handleNewCurrentMicrophoneDevice(WTFMove(devices[1])); +#endif +} + void MockRealtimeMediaSourceCenter::setDevices(Vector&& newMockDevices) { microphoneDevices().clear(); diff --git a/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h b/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h index 699884f3119c..4f545f1cd8f3 100644 --- a/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h +++ b/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h @@ -49,6 +49,8 @@ class MockRealtimeMediaSourceCenter { WEBCORE_EXPORT static void resetDevices(); WEBCORE_EXPORT static void setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted); + WEBCORE_EXPORT void triggerMockMicrophoneConfigurationChange(); + void setMockAudioCaptureEnabled(bool isEnabled) { m_isMockAudioCaptureEnabled = isEnabled; } void setMockVideoCaptureEnabled(bool isEnabled) { m_isMockVideoCaptureEnabled = isEnabled; } void setMockDisplayCaptureEnabled(bool isEnabled) { m_isMockDisplayCaptureEnabled = isEnabled; } diff --git a/Source/WebKit/GPUProcess/GPUProcess.cpp b/Source/WebKit/GPUProcess/GPUProcess.cpp index 6f6f4daec3b0..ab7d5c99e9ed 100644 --- a/Source/WebKit/GPUProcess/GPUProcess.cpp +++ b/Source/WebKit/GPUProcess/GPUProcess.cpp @@ -428,6 +428,11 @@ void GPUProcess::setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool { MockRealtimeMediaSourceCenter::setMockCaptureDevicesInterrupted(isCameraInterrupted, isMicrophoneInterrupted); } + +void GPUProcess::triggerMockMicrophoneConfigurationChange() +{ + MockRealtimeMediaSourceCenter::singleton().triggerMockMicrophoneConfigurationChange(); +} #endif // ENABLE(MEDIA_STREAM) #if HAVE(SC_CONTENT_SHARING_SESSION) diff --git a/Source/WebKit/GPUProcess/GPUProcess.h b/Source/WebKit/GPUProcess/GPUProcess.h index fb481eb9415c..d4966d261cc6 100644 --- a/Source/WebKit/GPUProcess/GPUProcess.h +++ b/Source/WebKit/GPUProcess/GPUProcess.h @@ -158,6 +158,7 @@ class GPUProcess : public AuxiliaryProcess, public ThreadSafeRefCounted GPUProcess LegacyReceiver { RemoveMockMediaDevice(String persistentId) ResetMockMediaDevices() SetMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted) + TriggerMockMicrophoneConfigurationChange() #endif #if PLATFORM(MAC) DisplayConfigurationChanged(CGDirectDisplayID displayID, CGDisplayChangeSummaryFlags flags) diff --git a/Source/WebKit/UIProcess/API/C/WKPage.cpp b/Source/WebKit/UIProcess/API/C/WKPage.cpp index 2d2ca701c909..a072d762383f 100644 --- a/Source/WebKit/UIProcess/API/C/WKPage.cpp +++ b/Source/WebKit/UIProcess/API/C/WKPage.cpp @@ -3151,6 +3151,15 @@ void WKPageSetMockCaptureDevicesInterrupted(WKPageRef pageRef, bool isCameraInte #endif } +void WKPageTriggerMockMicrophoneConfigurationChange(WKPageRef pageRef) +{ + CRASH_IF_SUSPENDED; +#if ENABLE(MEDIA_STREAM) && ENABLE(GPU_PROCESS) + auto& gpuProcess = toImpl(pageRef)->process().processPool().ensureGPUProcess(); + gpuProcess.triggerMockMicrophoneConfigurationChange(); +#endif +} + void WKPageLoadedSubresourceDomains(WKPageRef pageRef, WKPageLoadedSubresourceDomainsFunction callback, void* callbackContext) { CRASH_IF_SUSPENDED; diff --git a/Source/WebKit/UIProcess/API/C/WKPagePrivate.h b/Source/WebKit/UIProcess/API/C/WKPagePrivate.h index 0f70d7e081a1..0364a23f3007 100644 --- a/Source/WebKit/UIProcess/API/C/WKPagePrivate.h +++ b/Source/WebKit/UIProcess/API/C/WKPagePrivate.h @@ -202,6 +202,7 @@ WK_EXPORT void WKPageSetPrivateClickMeasurementAppBundleIDForTesting(WKPageRef p WK_EXPORT void WKPageSetMockCameraOrientation(WKPageRef page, uint64_t orientation); WK_EXPORT bool WKPageIsMockRealtimeMediaSourceCenterEnabled(WKPageRef page); WK_EXPORT void WKPageSetMockCaptureDevicesInterrupted(WKPageRef page, bool isCameraInterrupted, bool isMicrophoneInterrupted); +WK_EXPORT void WKPageTriggerMockMicrophoneConfigurationChange(WKPageRef page); typedef void (*WKPageLoadedSubresourceDomainsFunction)(WKArrayRef domains, void* functionContext); WK_EXPORT void WKPageLoadedSubresourceDomains(WKPageRef page, WKPageLoadedSubresourceDomainsFunction callback, void* callbackContext); diff --git a/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp b/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp index a9435f1da129..8c34e526b95f 100644 --- a/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp +++ b/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp @@ -136,6 +136,11 @@ class UserMediaCaptureManagerProxy::SourceProxy m_connection->send(Messages::UserMediaCaptureManager::SourceSettingsChanged(m_id, m_source->settings()), 0); } + void sourceConfigurationChanged() final + { + m_connection->send(Messages::UserMediaCaptureManager::SourceConfigurationChanged(m_id, m_source->persistentID(), m_source->settings(), m_source->capabilities()), 0); + } + // May get called on a background thread. void audioSamplesAvailable(const MediaTime& time, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t numberOfFrames) final { if (m_description != description || m_shouldReset) { diff --git a/Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp b/Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp index 395b6a5fc10d..06f651dd1b30 100644 --- a/Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp +++ b/Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp @@ -350,6 +350,11 @@ void GPUProcessProxy::setMockCaptureDevicesInterrupted(bool isCameraInterrupted, { send(Messages::GPUProcess::SetMockCaptureDevicesInterrupted { isCameraInterrupted, isMicrophoneInterrupted }, 0); } + +void GPUProcessProxy::triggerMockMicrophoneConfigurationChange() +{ + send(Messages::GPUProcess::TriggerMockMicrophoneConfigurationChange { }, 0); +} #endif // ENABLE(MEDIA_STREAM) #if HAVE(SC_CONTENT_SHARING_SESSION) diff --git a/Source/WebKit/UIProcess/GPU/GPUProcessProxy.h b/Source/WebKit/UIProcess/GPU/GPUProcessProxy.h index 9b4106f9d6ff..f9825e923e49 100644 --- a/Source/WebKit/UIProcess/GPU/GPUProcessProxy.h +++ b/Source/WebKit/UIProcess/GPU/GPUProcessProxy.h @@ -87,6 +87,7 @@ class GPUProcessProxy final : public AuxiliaryProcessProxy, private ProcessThrot void removeMockMediaDevice(const String&); void resetMockMediaDevices(); void setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted); + void triggerMockMicrophoneConfigurationChange(); void updateSandboxAccess(bool allowAudioCapture, bool allowVideoCapture, bool allowDisplayCapture); #endif diff --git a/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp b/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp index 2f23ca9f2d90..33dd0678c89d 100644 --- a/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp +++ b/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp @@ -105,6 +105,18 @@ void RemoteRealtimeMediaSource::setSettings(RealtimeMediaSourceSettings&& settin notifySettingsDidChangeObservers(changed); } +void RemoteRealtimeMediaSource::configurationChanged(String&& persistentID, WebCore::RealtimeMediaSourceSettings&& settings, WebCore::RealtimeMediaSourceCapabilities&& capabilities) +{ + setPersistentId(WTFMove(persistentID)); + setSettings(WTFMove(settings)); + setCapabilities(WTFMove(capabilities)); + setName(m_settings.label()); + + forEachObserver([](auto& observer) { + observer.sourceConfigurationChanged(); + }); +} + void RemoteRealtimeMediaSource::applyConstraintsSucceeded(WebCore::RealtimeMediaSourceSettings&& settings) { setSettings(WTFMove(settings)); diff --git a/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h b/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h index 2bced27be226..65ed18ad18fe 100644 --- a/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h +++ b/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h @@ -55,6 +55,8 @@ class RemoteRealtimeMediaSource : public WebCore::RealtimeMediaSource void captureStopped(bool didFail); void sourceMutedChanged(bool value, bool interrupted); + void configurationChanged(String&& persistentID, WebCore::RealtimeMediaSourceSettings&&, WebCore::RealtimeMediaSourceCapabilities&&); + protected: void createRemoteMediaSource(); void removeAsClient(); diff --git a/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp b/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp index 98fa37d85575..3a31a74a3fc4 100644 --- a/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp +++ b/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp @@ -155,6 +155,19 @@ void UserMediaCaptureManager::sourceSettingsChanged(RealtimeMediaSourceIdentifie }, [](std::nullptr_t) { }); } +void UserMediaCaptureManager::sourceConfigurationChanged(RealtimeMediaSourceIdentifier identifier, String&& persistentID, RealtimeMediaSourceSettings&& settings, RealtimeMediaSourceCapabilities&& capabilities) +{ + auto iterator = m_sources.find(identifier); + if (iterator == m_sources.end()) + return; + + switchOn(iterator->value, [&](Ref& source) { + source->configurationChanged(WTFMove(persistentID), WTFMove(settings), WTFMove(capabilities)); + }, [&](Ref& source) { + source->configurationChanged(WTFMove(persistentID), WTFMove(settings), WTFMove(capabilities)); + }, [](std::nullptr_t) { }); +} + void UserMediaCaptureManager::applyConstraintsSucceeded(RealtimeMediaSourceIdentifier identifier, RealtimeMediaSourceSettings&& settings) { auto iterator = m_sources.find(identifier); diff --git a/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h b/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h index 2934c5fdfcee..53e1eb4a3e87 100644 --- a/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h +++ b/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h @@ -126,6 +126,8 @@ class UserMediaCaptureManager : public WebProcessSupplement, public IPC::Message void sourceMutedChanged(WebCore::RealtimeMediaSourceIdentifier, bool muted, bool interrupted); void sourceSettingsChanged(WebCore::RealtimeMediaSourceIdentifier, WebCore::RealtimeMediaSourceSettings&&); + void sourceConfigurationChanged(WebCore::RealtimeMediaSourceIdentifier, String&&, WebCore::RealtimeMediaSourceSettings&&, WebCore::RealtimeMediaSourceCapabilities&&); + void applyConstraintsSucceeded(WebCore::RealtimeMediaSourceIdentifier, WebCore::RealtimeMediaSourceSettings&&); void applyConstraintsFailed(WebCore::RealtimeMediaSourceIdentifier, String&&, String&&); diff --git a/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.messages.in b/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.messages.in index b036299cb371..dd63567fdcf9 100644 --- a/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.messages.in +++ b/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.messages.in @@ -27,6 +27,7 @@ messages -> UserMediaCaptureManager NotRefCounted { SourceStopped(WebCore::RealtimeMediaSourceIdentifier id, bool didFail) SourceMutedChanged(WebCore::RealtimeMediaSourceIdentifier id, bool muted, bool interrupted) SourceSettingsChanged(WebCore::RealtimeMediaSourceIdentifier id, WebCore::RealtimeMediaSourceSettings settings) + SourceConfigurationChanged(WebCore::RealtimeMediaSourceIdentifier id, String persistentID, WebCore::RealtimeMediaSourceSettings settings, WebCore::RealtimeMediaSourceCapabilities capabilities) ApplyConstraintsSucceeded(WebCore::RealtimeMediaSourceIdentifier id, WebCore::RealtimeMediaSourceSettings settings) ApplyConstraintsFailed(WebCore::RealtimeMediaSourceIdentifier id, String failedConstraint, String message) } diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl index 98f69ea6c79e..6c4a93783b97 100644 --- a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl +++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl @@ -388,6 +388,7 @@ interface TestRunner { undefined setMockCameraOrientation(unsigned long orientation); boolean isMockRealtimeMediaSourceCenterEnabled(); undefined setMockCaptureDevicesInterrupted(boolean isCameraInterrupted, boolean isMicrophoneInterrupted); + undefined triggerMockMicrophoneConfigurationChange(); boolean hasAppBoundSession(); undefined clearAppBoundSession(); diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp index 822557499eaf..48122e228f91 100644 --- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp +++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp @@ -1837,6 +1837,11 @@ void TestRunner::setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool })); } +void TestRunner::triggerMockMicrophoneConfigurationChange() +{ + postSynchronousMessage("TriggerMockMicrophoneConfigurationChange"); +} + #if ENABLE(GAMEPAD) void TestRunner::connectMockGamepad(unsigned index) diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h index c91460a18efb..dd74ad934a2c 100644 --- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h +++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h @@ -499,6 +499,7 @@ class TestRunner : public JSWrappable { void setMockCameraOrientation(unsigned); bool isMockRealtimeMediaSourceCenterEnabled(); void setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted); + void triggerMockMicrophoneConfigurationChange(); bool hasAppBoundSession(); void clearAppBoundSession(); diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp index 6473bce7ac24..2f62a74e3adb 100644 --- a/Tools/WebKitTestRunner/TestController.cpp +++ b/Tools/WebKitTestRunner/TestController.cpp @@ -3693,6 +3693,11 @@ void TestController::setMockCaptureDevicesInterrupted(bool isCameraInterrupted, WKPageSetMockCaptureDevicesInterrupted(m_mainWebView->page(), isCameraInterrupted, isMicrophoneInterrupted); } +void TestController::triggerMockMicrophoneConfigurationChange() +{ + WKPageTriggerMockMicrophoneConfigurationChange(m_mainWebView->page()); +} + struct InAppBrowserPrivacyCallbackContext { explicit InAppBrowserPrivacyCallbackContext(TestController& controller) : testController(controller) diff --git a/Tools/WebKitTestRunner/TestController.h b/Tools/WebKitTestRunner/TestController.h index 823da69ed332..cbf39d251c78 100644 --- a/Tools/WebKitTestRunner/TestController.h +++ b/Tools/WebKitTestRunner/TestController.h @@ -320,6 +320,7 @@ class TestController { void setMockCameraOrientation(uint64_t); bool isMockRealtimeMediaSourceCenterEnabled() const; void setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted); + void triggerMockMicrophoneConfigurationChange(); bool hasAppBoundSession(); void injectUserScript(WKStringRef); diff --git a/Tools/WebKitTestRunner/TestInvocation.cpp b/Tools/WebKitTestRunner/TestInvocation.cpp index c0f48c71aac7..c4a4f1c4cb6a 100644 --- a/Tools/WebKitTestRunner/TestInvocation.cpp +++ b/Tools/WebKitTestRunner/TestInvocation.cpp @@ -976,6 +976,11 @@ WKRetainPtr TestInvocation::didReceiveSynchronousMessageFromInjectedB TestController::singleton().setMockCaptureDevicesInterrupted(isCameraInterrupted, isMicrophoneInterrupted); return nullptr; } + + if (WKStringIsEqualToUTF8CString(messageName, "TriggerMockMicrophoneConfigurationChange")) { + TestController::singleton().triggerMockMicrophoneConfigurationChange(); + return nullptr; + } if (WKStringIsEqualToUTF8CString(messageName, "HasAppBoundSession")) return adoptWK(WKBooleanCreate(TestController::singleton().hasAppBoundSession()));