Add an option to mute audio capture automatically when page is not visible
authoryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 31 May 2019 23:49:31 +0000 (23:49 +0000)
committeryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 31 May 2019 23:49:31 +0000 (23:49 +0000)
https://bugs.webkit.org/show_bug.cgi?id=198307

Reviewed by Eric Carlson.

Source/WebCore:

Reuse video capture mechanism for audio capture.
In case document gets in the background, interrupt the audio track if the audio factory requires it.
CoreAudioCaptureSourceIOS requires the audio source be interrupted if the app has not the right background mode.
It also allows interrupting the audio capture based on a runtime flag.

Add a runtime flag to control this.
Internals API is used to set it for test purposes, off by default.
For regular cases, the runtime flag is set through web preferences.

Test: platform/ios/mediastream/audio-muted-in-background-tab.html

* dom/Document.cpp:
(WebCore::Document::notifyMediaCaptureOfVisibilityChanged):
* page/RuntimeEnabledFeatures.h:
(WebCore::RuntimeEnabledFeatures::interruptAudioOnPageVisibilityChangeEnabled const):
(WebCore::RuntimeEnabledFeatures::setInterruptAudioOnPageVisibilityChangeEnabled):
* platform/mediastream/RealtimeMediaSourceCenter.cpp:
(WebCore::RealtimeMediaSourceCenter::RealtimeMediaSourceCenter):
(WebCore::RealtimeMediaSourceCenter::initializeShouldInterruptAudioOnPageVisibilityChange):
(WebCore::RealtimeMediaSourceCenter::setCapturePageState):
(WebCore::RealtimeMediaSourceCenter::visibilityDidChange):
* platform/mediastream/RealtimeMediaSourceCenter.h:
(WebCore::RealtimeMediaSourceCenter::shouldInterruptAudioOnPageVisibilityChange):
* platform/mediastream/RealtimeMediaSourceFactory.h:
(WebCore::AudioCaptureFactory::setAudioCapturePageState):
(WebCore::VideoCaptureFactory::setVideoCapturePageState):
* platform/mediastream/ios/CoreAudioCaptureSourceIOS.h:
* platform/mediastream/ios/CoreAudioCaptureSourceIOS.mm:
(WebCore::CoreAudioCaptureSourceFactory::setAudioCapturePageState):
(WebCore::CoreAudioCaptureSourceFactoryIOS::shouldInterruptAudioOnPageVisibilityChange):
* platform/mediastream/mac/CoreAudioCaptureSource.h:
* platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp:
(WebCore::RealtimeMediaSourceCenter::initializeShouldInterruptAudioOnPageVisibilityChange):
* testing/Internals.cpp:
(WebCore::Internals::resetToConsistentState):
(WebCore::Internals::setShouldInterruptAudioOnPageVisibilityChange):
* testing/Internals.h:
* testing/Internals.idl:

Source/WebKit:

Add API to set the new runtime flag.
Make source proxy factories implement this automatic muting.

* Shared/WebPreferences.yaml:
* UIProcess/API/Cocoa/WKPreferences.mm:
(-[WKPreferences _interruptAudioOnPageVisibilityChangeEnabled]):
(-[WKPreferences _setInterruptAudioOnPageVisibilityChangeEnabled:]):
* UIProcess/API/Cocoa/WKPreferencesPrivate.h:
* WebProcess/cocoa/UserMediaCaptureManager.cpp:
(WebKit::UserMediaCaptureManager::setAudioCapturePageState):
(WebKit::UserMediaCaptureManager::setVideoCapturePageState):
* WebProcess/cocoa/UserMediaCaptureManager.h:

LayoutTests:

* platform/ios/mediastream/audio-muted-in-background-tab-expected.txt: Added.
* platform/ios/mediastream/audio-muted-in-background-tab.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@246002 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/ios/mediastream/audio-muted-in-background-tab-expected.txt [new file with mode: 0644]
LayoutTests/platform/ios/mediastream/audio-muted-in-background-tab.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/SourcesCocoa.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/Document.cpp
Source/WebCore/page/RuntimeEnabledFeatures.h
Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.cpp
Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.h
Source/WebCore/platform/mediastream/RealtimeMediaSourceFactory.h
Source/WebCore/platform/mediastream/ios/CoreAudioCaptureSourceIOS.mm
Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h
Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp
Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.mm [new file with mode: 0644]
Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebKit/ChangeLog
Source/WebKit/Scripts/PreferencesTemplates/WebPreferencesStoreDefaultsMap.cpp.erb
Source/WebKit/Shared/WebPreferences.yaml
Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm
Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h
Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp
Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h

index a311d70..555fd94 100644 (file)
@@ -1,3 +1,13 @@
+2019-05-31  Youenn Fablet  <youenn@apple.com>
+
+        Add an option to mute audio capture automatically when page is not visible
+        https://bugs.webkit.org/show_bug.cgi?id=198307
+
+        Reviewed by Eric Carlson.
+
+        * platform/ios/mediastream/audio-muted-in-background-tab-expected.txt: Added.
+        * platform/ios/mediastream/audio-muted-in-background-tab.html: Added.
+
 2019-05-31  Shawn Roberts  <sroberts@apple.com>
 
         Updating expectations for failing tests
diff --git a/LayoutTests/platform/ios/mediastream/audio-muted-in-background-tab-expected.txt b/LayoutTests/platform/ios/mediastream/audio-muted-in-background-tab-expected.txt
new file mode 100644 (file)
index 0000000..408e29c
--- /dev/null
@@ -0,0 +1,7 @@
+
+PASS Setup stream 
+PASS Hide page, video and audio should be muted 
+PASS Show page, video and audio should be unmuted 
+PASS Hide and mute page, video and audio should be muted 
+PASS Show page, video and audio should remain muted 
+
diff --git a/LayoutTests/platform/ios/mediastream/audio-muted-in-background-tab.html b/LayoutTests/platform/ios/mediastream/audio-muted-in-background-tab.html
new file mode 100644 (file)
index 0000000..80e49ed
--- /dev/null
@@ -0,0 +1,72 @@
+
+<!doctype html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Don't unmute audio when a tab becomes visible unless it was muted when the tab was hidden</title>
+        <script src="../../../resources/testharness.js"></script>
+        <script src="../../../resources/testharnessreport.js"></script>
+    </head>
+    <body>
+        <script>
+            let audioTrack;
+            let videoTrack;
+
+            promise_test((t) => {
+                if (window.testRunner)
+                    testRunner.setUserMediaPermission(true);
+                if (!window.internals)
+                    return Promise.reject("this test needs internals API");
+
+                internals.setShouldInterruptAudioOnPageVisibilityChange(true);
+
+                return navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then((stream) => {
+                    audioTrack = stream.getAudioTracks()[0];
+                    videoTrack = stream.getVideoTracks()[0];
+
+                    assert_false(audioTrack.muted, "audio track is active");
+                    assert_false(videoTrack.muted, "video track is active");
+                })
+
+                .then(() => {
+                    test(() => {
+                        if (window.internals)
+                            window.internals.setPageVisibility(false);
+                        assert_true(audioTrack.muted, "audio track is muted");
+                        assert_true(videoTrack.muted, "video track is muted");
+                    }, "Hide page, video and audio should be muted");
+                })
+
+                .then(() => {
+                    test(() => {
+                        if (window.internals)
+                            window.internals.setPageVisibility(true);
+                        assert_false(audioTrack.muted, "audio track is active");
+                        assert_false(videoTrack.muted, "video track is active");
+                    }, "Show page, video and audio should be unmuted");
+                })
+
+                .then(() => {
+                    test(() => {
+                        if (window.internals) {
+                            window.internals.setPageVisibility(false);
+                            window.internals.setPageMuted("capturedevices");
+                        }
+                        assert_true(audioTrack.muted, "audio track is muted");
+                        assert_true(videoTrack.muted, "video track is muted");
+                    }, "Hide and mute page, video and audio should be muted");
+                })
+
+                .then(() => {
+                    test(() => {
+                        if (window.internals)
+                            window.internals.setPageVisibility(true);
+                        assert_true(audioTrack.muted, "audio track is muted");
+                        assert_true(videoTrack.muted, "video track is muted");
+                    }, "Show page, video and audio should remain muted");
+                })
+            }, "Setup stream");
+
+        </script>
+    </body>
+</html>
index 6d41d46..2458cfb 100644 (file)
@@ -1,3 +1,49 @@
+2019-05-31  Youenn Fablet  <youenn@apple.com>
+
+        Add an option to mute audio capture automatically when page is not visible
+        https://bugs.webkit.org/show_bug.cgi?id=198307
+
+        Reviewed by Eric Carlson.
+
+        Reuse video capture mechanism for audio capture.
+        In case document gets in the background, interrupt the audio track if the audio factory requires it.
+        CoreAudioCaptureSourceIOS requires the audio source be interrupted if the app has not the right background mode.
+        It also allows interrupting the audio capture based on a runtime flag.
+
+        Add a runtime flag to control this.
+        Internals API is used to set it for test purposes, off by default.
+        For regular cases, the runtime flag is set through web preferences.
+
+        Test: platform/ios/mediastream/audio-muted-in-background-tab.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::notifyMediaCaptureOfVisibilityChanged):
+        * page/RuntimeEnabledFeatures.h:
+        (WebCore::RuntimeEnabledFeatures::interruptAudioOnPageVisibilityChangeEnabled const):
+        (WebCore::RuntimeEnabledFeatures::setInterruptAudioOnPageVisibilityChangeEnabled):
+        * platform/mediastream/RealtimeMediaSourceCenter.cpp:
+        (WebCore::RealtimeMediaSourceCenter::RealtimeMediaSourceCenter):
+        (WebCore::RealtimeMediaSourceCenter::initializeShouldInterruptAudioOnPageVisibilityChange):
+        (WebCore::RealtimeMediaSourceCenter::setCapturePageState):
+        (WebCore::RealtimeMediaSourceCenter::visibilityDidChange):
+        * platform/mediastream/RealtimeMediaSourceCenter.h:
+        (WebCore::RealtimeMediaSourceCenter::shouldInterruptAudioOnPageVisibilityChange):
+        * platform/mediastream/RealtimeMediaSourceFactory.h:
+        (WebCore::AudioCaptureFactory::setAudioCapturePageState):
+        (WebCore::VideoCaptureFactory::setVideoCapturePageState):
+        * platform/mediastream/ios/CoreAudioCaptureSourceIOS.h:
+        * platform/mediastream/ios/CoreAudioCaptureSourceIOS.mm:
+        (WebCore::CoreAudioCaptureSourceFactory::setAudioCapturePageState):
+        (WebCore::CoreAudioCaptureSourceFactoryIOS::shouldInterruptAudioOnPageVisibilityChange):
+        * platform/mediastream/mac/CoreAudioCaptureSource.h:
+        * platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp:
+        (WebCore::RealtimeMediaSourceCenter::initializeShouldInterruptAudioOnPageVisibilityChange):
+        * testing/Internals.cpp:
+        (WebCore::Internals::resetToConsistentState):
+        (WebCore::Internals::setShouldInterruptAudioOnPageVisibilityChange):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2019-05-31  Geoffrey Garen  <ggaren@apple.com>
 
         Some WeakPtr typedef cleanup
index c081198..bd29e5b 100644 (file)
@@ -542,6 +542,7 @@ platform/mediastream/mac/DisplayCaptureSourceCocoa.cpp
 platform/mediastream/mac/RealtimeIncomingAudioSourceCocoa.cpp
 platform/mediastream/mac/RealtimeIncomingVideoSourceCocoa.mm
 platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp
+platform/mediastream/mac/RealtimeMediaSourceCenterMac.mm
 platform/mediastream/mac/RealtimeOutgoingAudioSourceCocoa.cpp
 platform/mediastream/mac/RealtimeOutgoingVideoSourceCocoa.cpp
 platform/mediastream/mac/ScreenDisplayCaptureSourceMac.mm
index 3c91f43..8334a9f 100644 (file)
                4162A44F101145AE00DFF3ED /* DedicatedWorkerGlobalScope.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DedicatedWorkerGlobalScope.idl; sourceTree = "<group>"; };
                4162A4551011464700DFF3ED /* JSDedicatedWorkerGlobalScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDedicatedWorkerGlobalScope.cpp; sourceTree = "<group>"; };
                4162A4561011464700DFF3ED /* JSDedicatedWorkerGlobalScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDedicatedWorkerGlobalScope.h; sourceTree = "<group>"; };
+               416CE4A4229DF12E00A8A686 /* RealtimeMediaSourceCenterMac.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RealtimeMediaSourceCenterMac.mm; sourceTree = "<group>"; };
                416D759F20C6441300D02D2C /* NetworkLoadInformation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkLoadInformation.h; sourceTree = "<group>"; };
                416E0B37209BC3C2004A95D9 /* FetchIdentifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FetchIdentifier.h; sourceTree = "<group>"; };
                416E29A5102FA962007FC14E /* WorkerReportingProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WorkerReportingProxy.h; sourceTree = "<group>"; };
                                5CDD833A1E4324BB00621E83 /* RealtimeIncomingVideoSourceCocoa.h */,
                                5CDD83391E4324BB00621E83 /* RealtimeIncomingVideoSourceCocoa.mm */,
                                4A0FFAA31AAF5EF60062803B /* RealtimeMediaSourceCenterMac.cpp */,
+                               416CE4A4229DF12E00A8A686 /* RealtimeMediaSourceCenterMac.mm */,
                                41103AA71E39790A00769F14 /* RealtimeOutgoingAudioSourceCocoa.cpp */,
                                41103AA81E39790A00769F14 /* RealtimeOutgoingAudioSourceCocoa.h */,
                                5CDD833B1E4324BB00621B83 /* RealtimeOutgoingVideoSourceCocoa.cpp */,
                115CFA9A208BC140001E6991 /* inlineformatting */ = {
                        isa = PBXGroup;
                        children = (
-                               6F0CD694229ED32700C5994E /* InlineLine.h */,
-                               6F0CD692229ED31900C5994E /* InlineLine.cpp */,
                                6FE7DDDD20EC6E8B008B5B4E /* text */,
                                6F7CA3C9208C2B2E002F29AB /* InlineFormattingContext.cpp */,
                                6F7CA3C8208C2B2E002F29AB /* InlineFormattingContext.h */,
                                1123AFDD209ABBBA00736ACC /* InlineInvalidation.cpp */,
                                1123AFDC209ABBBA00736ACC /* InlineInvalidation.h */,
                                6FE7CFA02177EEF1005B1573 /* InlineItem.h */,
+                               6F0CD692229ED31900C5994E /* InlineLine.cpp */,
+                               6F0CD694229ED32700C5994E /* InlineLine.h */,
                                6FB47E612277425A00C7BCB0 /* InlineLineBox.h */,
                                6FE198132178397B00446F08 /* InlineLineBreaker.cpp */,
                                6FE198152178397C00446F08 /* InlineLineBreaker.h */,
                                714C7C661FDAD2A100F2BEE1 /* AnimationPlaybackEvent.h in Headers */,
                                714C7C671FDAD2A900F2BEE1 /* AnimationPlaybackEventInit.h in Headers */,
                                71025ECD1F99F0CE004A250C /* AnimationTimeline.h in Headers */,
-                               6F0CD695229ED32700C5994E /* InlineLine.h in Headers */,
                                0F580FAF149800D400FB5BD8 /* AnimationUtilities.h in Headers */,
                                57152B5A21CB3E88000C37CA /* ApduCommand.h in Headers */,
                                57152B5C21CC1902000C37CA /* ApduResponse.h in Headers */,
                                11310CF820BA4A6A0065A8D0 /* InlineInvalidation.h in Headers */,
                                6FE7CFA22177EEF2005B1573 /* InlineItem.h in Headers */,
                                BCE789161120D6080060ECE5 /* InlineIterator.h in Headers */,
+                               6F0CD695229ED32700C5994E /* InlineLine.h in Headers */,
                                6FB47E632277425A00C7BCB0 /* InlineLineBox.h in Headers */,
                                6FE198172178397C00446F08 /* InlineLineBreaker.h in Headers */,
                                AA4C3A770B2B1679002334A2 /* InlineStyleSheetOwner.h in Headers */,
index 2c4d0b6..a998242 100644 (file)
@@ -7648,7 +7648,7 @@ void Document::notifyMediaCaptureOfVisibilityChanged()
     if (!page())
         return;
 
-    RealtimeMediaSourceCenter::singleton().setVideoCapturePageState(hidden(), page()->isMediaCaptureMuted());
+    RealtimeMediaSourceCenter::singleton().setCapturePageState(hidden(), page()->isMediaCaptureMuted());
 #endif
 }
 
index a40c762..9734f0a 100644 (file)
@@ -357,6 +357,9 @@ public:
     bool pageAtRuleSupportEnabled() const { return m_pageAtRuleSupportEnabled; }
     void setPageAtRuleSupportEnabled(bool isEnabled) { m_pageAtRuleSupportEnabled = isEnabled; }
 
+    bool interruptAudioOnPageVisibilityChangeEnabled() const { return m_interruptAudioOnPageVisibilityChangeEnabled; }
+    void setInterruptAudioOnPageVisibilityChangeEnabled(bool enabled) { m_interruptAudioOnPageVisibilityChangeEnabled = enabled; }
+
     WEBCORE_EXPORT static RuntimeEnabledFeatures& sharedFeatures();
 
 private:
@@ -540,6 +543,7 @@ private:
     bool m_isITPFirstPartyWebsiteDataRemovalEnabled { false };
 
     bool m_referrerPolicyAttributeEnabled { false };
+    bool m_interruptAudioOnPageVisibilityChangeEnabled { false };
 
     friend class WTF::NeverDestroyed<RuntimeEnabledFeatures>;
 };
index 3706fad..b53f1f7 100644 (file)
@@ -38,6 +38,7 @@
 #include "CaptureDeviceManager.h"
 #include "Logging.h"
 #include "MediaStreamPrivate.h"
+#include "RuntimeEnabledFeatures.h"
 #include <wtf/SHA1.h>
 
 namespace WebCore {
@@ -262,8 +263,10 @@ void RealtimeMediaSourceCenter::validateRequestConstraints(ValidConstraintsHandl
     validHandler(WTFMove(audioDevices), WTFMove(videoDevices), WTFMove(deviceIdentifierHashSalt));
 }
 
-void RealtimeMediaSourceCenter::setVideoCapturePageState(bool interrupted, bool pageMuted)
+void RealtimeMediaSourceCenter::setCapturePageState(bool interrupted, bool pageMuted)
 {
+    if (RuntimeEnabledFeatures::sharedFeatures().interruptAudioOnPageVisibilityChangeEnabled())
+        audioCaptureFactory().setAudioCapturePageState(interrupted, pageMuted);
     videoCaptureFactory().setVideoCapturePageState(interrupted, pageMuted);
 }
 
@@ -317,6 +320,13 @@ DisplayCaptureFactory& RealtimeMediaSourceCenter::displayCaptureFactory()
     return m_displayCaptureFactoryOverride ? *m_displayCaptureFactoryOverride : defaultDisplayCaptureFactory();
 }
 
+#if !PLATFORM(COCOA)
+bool RealtimeMediaSourceCenter::shouldInterruptAudioOnPageVisibilityChange()
+{
+    return false;
+}
+#endif
+
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM)
index 3bb2594..3738768 100644 (file)
@@ -86,10 +86,12 @@ public:
 
     WEBCORE_EXPORT void setDevicesChangedObserver(std::function<void()>&&);
 
-    void setVideoCapturePageState(bool, bool);
+    void setCapturePageState(bool interrupted, bool pageMuted);
 
     void captureDevicesChanged();
 
+    WEBCORE_EXPORT static bool shouldInterruptAudioOnPageVisibilityChange();
+
 private:
     RealtimeMediaSourceCenter();
     friend class NeverDestroyed<RealtimeMediaSourceCenter>;
@@ -113,6 +115,8 @@ private:
     AudioCaptureFactory* m_audioCaptureFactoryOverride { nullptr };
     VideoCaptureFactory* m_videoCaptureFactoryOverride { nullptr };
     DisplayCaptureFactory* m_displayCaptureFactoryOverride { nullptr };
+
+    bool m_shouldInterruptAudioOnPageVisibilityChange { false };
 };
 
 } // namespace WebCore
index 4897c64..a3d52f0 100644 (file)
@@ -57,6 +57,11 @@ public:
     virtual ~AudioCaptureFactory() = default;
     virtual CaptureSourceOrError createAudioCaptureSource(const CaptureDevice&, String&&, const MediaConstraints*) = 0;
     virtual CaptureDeviceManager& audioCaptureDeviceManager() = 0;
+    virtual void setAudioCapturePageState(bool interrupted, bool pageMuted)
+    {
+        UNUSED_PARAM(interrupted);
+        UNUSED_PARAM(pageMuted);
+    }
 
 protected:
     AudioCaptureFactory() = default;
@@ -71,7 +76,11 @@ public:
     virtual ~VideoCaptureFactory() = default;
     virtual CaptureSourceOrError createVideoCaptureSource(const CaptureDevice&, String&&, const MediaConstraints*) = 0;
     virtual CaptureDeviceManager& videoCaptureDeviceManager() = 0;
-    virtual void setVideoCapturePageState(bool, bool) { }
+    virtual void setVideoCapturePageState(bool interrupted, bool pageMuted)
+    {
+        UNUSED_PARAM(interrupted);
+        UNUSED_PARAM(pageMuted);
+    }
 
 protected:
     VideoCaptureFactory() = default;
@@ -82,7 +91,6 @@ public:
     virtual ~DisplayCaptureFactory() = default;
     virtual CaptureSourceOrError createDisplayCaptureSource(const CaptureDevice&, const MediaConstraints*) = 0;
     virtual CaptureDeviceManager& displayCaptureDeviceManager() = 0;
-    virtual void setDisplayCapturePageState(bool , bool) { }
 
 protected:
     DisplayCaptureFactory() = default;
index 6fb3dad..4c82648 100644 (file)
@@ -126,6 +126,12 @@ CoreAudioCaptureSourceFactory& CoreAudioCaptureSourceFactory::singleton()
     return factory.get();
 }
 
+void CoreAudioCaptureSourceFactory::setAudioCapturePageState(bool interrupted, bool pageMuted)
+{
+    if (auto* activeSource = this->activeSource())
+        activeSource->setInterrupted(interrupted, pageMuted);
+}
+
 }
 
 #endif // ENABLE(MEDIA_STREAM) && PLATFORM(IOS_FAMILY)
index 1ab816c..adbefe7 100644 (file)
@@ -142,6 +142,9 @@ private:
     }
 
     CaptureDeviceManager& audioCaptureDeviceManager() final;
+#if PLATFORM(IOS_FAMILY)
+    void setAudioCapturePageState(bool interrupted, bool pageMuted) final;
+#endif
 };
 
 } // namespace WebCore
index a6a0dab..a328cd3 100644 (file)
@@ -38,6 +38,7 @@
 #include "DisplayCaptureManagerCocoa.h"
 #include "Logging.h"
 #include "MediaStreamPrivate.h"
+#include "RuntimeEnabledFeatures.h"
 #include "ScreenDisplayCaptureSourceMac.h"
 #include "WindowDisplayCaptureSourceMac.h"
 #include <wtf/MainThread.h>
diff --git a/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.mm b/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.mm
new file mode 100644 (file)
index 0000000..0aa220a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "RealtimeMediaSourceCenter.h"
+
+#if ENABLE(MEDIA_STREAM)
+
+namespace WebCore {
+
+bool RealtimeMediaSourceCenter::shouldInterruptAudioOnPageVisibilityChange()
+{
+#if PLATFORM(IOS)
+    NSArray *modes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
+    if (!modes)
+        return true;
+    
+    int modesCount = [modes count];
+    for (int i = 0; i < modesCount; i++) {
+        if ([[modes objectAtIndex:i] isEqual: @"audio"])
+            return false;
+    }
+    return true;
+#else
+    return false;
+#endif
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)
index 69fad4b..6d83b50 100644 (file)
@@ -96,7 +96,7 @@ public:
 
 private:
 #if PLATFORM(IOS_FAMILY)
-    void setVideoCapturePageState(bool interrupted, bool pageMuted)
+    void setVideoCapturePageState(bool interrupted, bool pageMuted) final
     {
         if (activeSource())
             activeSource()->setInterrupted(interrupted, pageMuted);
@@ -139,6 +139,13 @@ public:
         return MockRealtimeAudioSource::create(String { device.persistentId() }, String { device.label() }, WTFMove(hashSalt), constraints);
     }
 private:
+#if PLATFORM(IOS_FAMILY)
+    void setAudioCapturePageState(bool interrupted, bool pageMuted) final
+    {
+        if (activeSource())
+            activeSource()->setInterrupted(interrupted, pageMuted);
+    }
+#endif
     CaptureDeviceManager& audioCaptureDeviceManager() final { return MockRealtimeMediaSourceCenter::singleton().audioCaptureDeviceManager(); }
 };
 
index 2730dfe..c596a58 100644 (file)
@@ -532,6 +532,10 @@ void Internals::resetToConsistentState(Page& page)
     page.setFullscreenControlsHidden(false);
 
     MediaEngineConfigurationFactory::disableMock();
+
+#if ENABLE(MEDIA_STREAM)
+    RuntimeEnabledFeatures::sharedFeatures().setInterruptAudioOnPageVisibilityChangeEnabled(false);
+#endif
 }
 
 Internals::Internals(Document& document)
@@ -1502,6 +1506,10 @@ void Internals::applyRotationForOutgoingVideoSources(RTCPeerConnection& connecti
 #endif
 
 #if ENABLE(MEDIA_STREAM)
+void Internals::setShouldInterruptAudioOnPageVisibilityChange(bool shouldInterrupt)
+{
+    RuntimeEnabledFeatures::sharedFeatures().setInterruptAudioOnPageVisibilityChangeEnabled(shouldInterrupt);
+}
 
 void Internals::setMockMediaCaptureDevicesEnabled(bool enabled)
 {
index 46d426a..675c80a 100644 (file)
@@ -516,6 +516,7 @@ public:
 #endif
 
 #if ENABLE(MEDIA_STREAM)
+    void setShouldInterruptAudioOnPageVisibilityChange(bool);
     void setMockMediaCaptureDevicesEnabled(bool);
     void setMediaCaptureRequiresSecureConnection(bool);
     void setCustomPrivateRecorderCreator();
index 3a59b4d..0af3375 100644 (file)
@@ -672,6 +672,7 @@ enum CompositingPolicy {
     [Conditional=WEB_RTC] void setH264HardwareEncoderAllowed(boolean allowed);
     [Conditional=WEB_RTC] void applyRotationForOutgoingVideoSources(RTCPeerConnection connection);
 
+    [Conditional=MEDIA_STREAM] void setShouldInterruptAudioOnPageVisibilityChange(boolean shouldInterrupt);
     [Conditional=MEDIA_STREAM] void setCameraMediaStreamTrackOrientation(MediaStreamTrack track, short orientation);
     [Conditional=MEDIA_STREAM] void observeMediaStreamTrack(MediaStreamTrack track);
     [Conditional=MEDIA_STREAM] Promise<ImageData> grabNextMediaStreamTrackFrame();
index c58acdf..48ccd55 100644 (file)
@@ -1,3 +1,23 @@
+2019-05-31  Youenn Fablet  <youenn@apple.com>
+
+        Add an option to mute audio capture automatically when page is not visible
+        https://bugs.webkit.org/show_bug.cgi?id=198307
+
+        Reviewed by Eric Carlson.
+
+        Add API to set the new runtime flag.
+        Make source proxy factories implement this automatic muting.
+
+        * Shared/WebPreferences.yaml:
+        * UIProcess/API/Cocoa/WKPreferences.mm:
+        (-[WKPreferences _interruptAudioOnPageVisibilityChangeEnabled]):
+        (-[WKPreferences _setInterruptAudioOnPageVisibilityChangeEnabled:]):
+        * UIProcess/API/Cocoa/WKPreferencesPrivate.h:
+        * WebProcess/cocoa/UserMediaCaptureManager.cpp:
+        (WebKit::UserMediaCaptureManager::setAudioCapturePageState):
+        (WebKit::UserMediaCaptureManager::setVideoCapturePageState):
+        * WebProcess/cocoa/UserMediaCaptureManager.h:
+
 2019-05-31  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [iOS] Autocorrection menu font is Times New Roman when using font-family: UICTFontTextStyle*
index 7825e12..17bae37 100644 (file)
@@ -36,6 +36,7 @@
 // FIXME: These should added via options in WebPreferences.yaml, rather than hardcoded.
 #include <WebCore/DeprecatedGlobalSettings.h>
 #include <WebCore/LibWebRTCProvider.h>
+#include <WebCore/RealtimeMediaSourceCenter.h>
 #include <WebCore/SecurityOrigin.h>
 #include <WebCore/Settings.h>
 #include <WebCore/TextEncodingRegistry.h>
index e59efb8..72798c1 100644 (file)
@@ -545,6 +545,12 @@ PeerConnectionEnabled:
   webcoreBinding: RuntimeEnabledFeatures
   condition: ENABLE(WEB_RTC)
 
+InterruptAudioOnPageVisibilityChangeEnabled:
+  type: bool
+  defaultValue: WebCore::RealtimeMediaSourceCenter::shouldInterruptAudioOnPageVisibilityChange()
+  webcoreBinding: RuntimeEnabledFeatures
+  condition: ENABLE(MEDIA_STREAM)
+
 WebRTCUnifiedPlanEnabled:
   type: bool
   defaultValue: true
index 65384ec..df0b002 100644 (file)
@@ -649,6 +649,16 @@ static _WKStorageBlockingPolicy toAPI(WebCore::SecurityOrigin::StorageBlockingPo
     _preferences->setInactiveMediaCaptureSteamRepromptIntervalInMinutes(interval);
 }
 
+- (BOOL)_interruptAudioOnPageVisibilityChangeEnabled
+{
+    return _preferences->interruptAudioOnPageVisibilityChangeEnabled();
+}
+
+- (void)_setInterruptAudioOnPageVisibilityChangeEnabled:(BOOL)enabled
+{
+    _preferences->setInterruptAudioOnPageVisibilityChangeEnabled(enabled);
+}
+
 - (BOOL)_enumeratingAllNetworkInterfacesEnabled
 {
     return _preferences->enumeratingAllNetworkInterfacesEnabled();
index 077a393..ef18193 100644 (file)
@@ -120,6 +120,7 @@ typedef NS_ENUM(NSInteger, _WKEditableLinkBehavior) {
 @property (nonatomic, setter=_setICECandidateFilteringEnabled:) BOOL _iceCandidateFilteringEnabled WK_API_AVAILABLE(macos(10.13.4), ios(11.3));
 @property (nonatomic, setter=_setWebRTCLegacyAPIEnabled:) BOOL _webRTCLegacyAPIEnabled WK_API_AVAILABLE(macos(10.13), ios(11.0));
 @property (nonatomic, setter=_setInactiveMediaCaptureSteamRepromptIntervalInMinutes:) double _inactiveMediaCaptureSteamRepromptIntervalInMinutes WK_API_AVAILABLE(macos(10.13.4), ios(11.3));
+@property (nonatomic, setter=_setInterruptAudioOnPageVisibilityChangeEnabled:) BOOL _interruptAudioOnPageVisibilityChangeEnabled WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @property (nonatomic, setter=_setJavaScriptCanAccessClipboard:) BOOL _javaScriptCanAccessClipboard WK_API_AVAILABLE(macos(10.13), ios(11.0));
 @property (nonatomic, setter=_setDOMPasteAllowed:) BOOL _domPasteAllowed WK_API_AVAILABLE(macos(10.13), ios(11.0));
index d19436d..2720b86 100644 (file)
@@ -366,6 +366,20 @@ void UserMediaCaptureManager::applyConstraintsFailed(uint64_t id, String&& faile
         source->applyConstraintsFailed(WTFMove(failedConstraint), WTFMove(message));
 }
 
+#if PLATFORM(IOS_FAMILY)
+void UserMediaCaptureManager::setAudioCapturePageState(bool interrupted, bool pageMuted)
+{
+    if (auto* activeSource = static_cast<AudioCaptureFactory*>(this)->activeSource())
+        activeSource->setInterrupted(interrupted, pageMuted);
+}
+
+void UserMediaCaptureManager::setVideoCapturePageState(bool interrupted, bool pageMuted)
+{
+    if (auto* activeSource = static_cast<VideoCaptureFactory*>(this)->activeSource())
+        activeSource->setInterrupted(interrupted, pageMuted);
+}
+#endif
+
 }
 
 #endif
index b11ae74..baed0cd 100644 (file)
@@ -79,6 +79,11 @@ private:
     WebCore::CaptureDeviceManager& videoCaptureDeviceManager() final { return m_noOpCaptureDeviceManager; }
     WebCore::CaptureDeviceManager& displayCaptureDeviceManager() final { return m_noOpCaptureDeviceManager; }
 
+#if PLATFORM(IOS_FAMILY)
+    void setAudioCapturePageState(bool interrupted, bool pageMuted) final;
+    void setVideoCapturePageState(bool interrupted, bool pageMuted) final;
+#endif
+
     // IPC::MessageReceiver
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) final;