Diff
Modified: trunk/LayoutTests/ChangeLog (281366 => 281367)
--- trunk/LayoutTests/ChangeLog 2021-08-21 06:10:53 UTC (rev 281366)
+++ trunk/LayoutTests/ChangeLog 2021-08-21 07:01:21 UTC (rev 281367)
@@ -1,3 +1,14 @@
+2021-08-21 Youenn Fablet <you...@apple.com>
+
+ Prevent AudioSession category from moving out of PlayAndRecord too quickly
+ https://bugs.webkit.org/show_bug.cgi?id=229327
+ <rdar://81997024>
+
+ Reviewed by Eric Carlson.
+
+ * http/tests/media/media-stream/audio-capture-and-category.https-expected.txt: Added.
+ * http/tests/media/media-stream/audio-capture-and-category.https.html: Added.
+
2021-08-20 Commit Queue <commit-qu...@webkit.org>
Unreviewed, reverting r281343.
Added: trunk/LayoutTests/http/tests/media/media-stream/audio-capture-and-category.https-expected.txt (0 => 281367)
--- trunk/LayoutTests/http/tests/media/media-stream/audio-capture-and-category.https-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/media/media-stream/audio-capture-and-category.https-expected.txt 2021-08-21 07:01:21 UTC (rev 281367)
@@ -0,0 +1,4 @@
+
+
+PASS Validate audio session category is set to PlayAndRecord after frame stopped capturing but continued playing audio
+
Added: trunk/LayoutTests/http/tests/media/media-stream/audio-capture-and-category.https.html (0 => 281367)
--- trunk/LayoutTests/http/tests/media/media-stream/audio-capture-and-category.https.html (rev 0)
+++ trunk/LayoutTests/http/tests/media/media-stream/audio-capture-and-category.https.html 2021-08-21 07:01:21 UTC (rev 281367)
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src=""
+<script src=""
+</head>
+<body>
+<video id="audio" autoplay playsInline></video>
+<script>
+async function negotiate(pc1, pc2)
+{
+ pc1._onicecandidate_ = (event) => { pc2.addIceCandidate(event.candidate) };
+ pc2._onicecandidate_ = (event) => { pc1.addIceCandidate(event.candidate) };
+
+ const offer = await pc1.createOffer();
+ await pc1.setLocalDescription(offer);
+ await pc2.setRemoteDescription(offer);
+ const answer = await pc2.createAnswer();
+ await pc2.setLocalDescription(answer);
+ await pc1.setRemoteDescription(answer);
+}
+
+promise_test(async (t) => {
+ if (!window.internals)
+ return Promise.reject("Test require internals API");
+ internals.settings.setShouldManageAudioSessionCategory(true);
+
+ const stream = await navigator.mediaDevices.getUserMedia({audio:true});
+
+ let counter = 0;
+ while (++counter < 100 && internals.audioSessionCategory() !== "PlayAndRecord")
+ await new Promise(resolve => setTimeout(resolve, 50));
+ assert_equals(internals.audioSessionCategory(), "PlayAndRecord", "category when capturing and not playing audio");
+
+ const pc1 = new RTCPeerConnection();
+ const pc2 = new RTCPeerConnection();
+
+ pc1.addTrack(stream.getAudioTracks()[0], stream);
+
+ await negotiate(pc1, pc2);
+
+ // We play the remote track, which will stay live even if the microphone track gets stopped.
+ audio.srcObject = new MediaStream([pc2.getReceivers()[0].track]);
+ await audio.play();
+
+ assert_equals(internals.audioSessionCategory(), "PlayAndRecord", "category when capturing and audio is being played");
+
+ stream.getAudioTracks()[0].stop();
+
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ assert_equals(internals.audioSessionCategory(), "PlayAndRecord", "category when capture has stopped but audio contines to play");
+
+ audio.pause();
+
+ counter = 0;
+ while (++counter < 100 && internals.audioSessionCategory() === "PlayAndRecord")
+ await new Promise(resolve => setTimeout(resolve, 50));
+ assert_not_equals(internals.audioSessionCategory(), "PlayAndRecord", "category when capture has stopped and audio stopped playing");
+}, "Validate audio session category is set to PlayAndRecord after frame stopped capturing but continued playing audio");
+</script>
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (281366 => 281367)
--- trunk/Source/WebCore/ChangeLog 2021-08-21 06:10:53 UTC (rev 281366)
+++ trunk/Source/WebCore/ChangeLog 2021-08-21 07:01:21 UTC (rev 281367)
@@ -1,3 +1,28 @@
+2021-08-21 Youenn Fablet <you...@apple.com>
+
+ Prevent AudioSession category from moving out of PlayAndRecord too quickly
+ https://bugs.webkit.org/show_bug.cgi?id=229327
+ <rdar://81997024>
+
+ Reviewed by Eric Carlson.
+
+ If category is PlayAndRecord, we stick to PlayAndRecord until audio is no longer playing at which point we
+ transition to whatever category is most appropriate.
+ Introduce PlatformMediaSession::isPlaying in addition to canProduceAudio to compute whether audio is being played.
+
+ Test: http/tests/media/media-stream/audio-capture-and-category.https.html
+
+ * Modules/webaudio/AudioContext.cpp:
+ (WebCore::AudioContext::isPlaying const):
+ * Modules/webaudio/AudioContext.h:
+ * html/HTMLMediaElement.h:
+ * platform/audio/PlatformMediaSession.cpp:
+ (WebCore::PlatformMediaSession::isPlaying const):
+ * platform/audio/PlatformMediaSession.h:
+ (WebCore::PlatformMediaSessionClient::isPlaying const):
+ * platform/audio/cocoa/MediaSessionManagerCocoa.mm:
+ (WebCore::MediaSessionManagerCocoa::updateSessionState):
+
2021-08-20 Tim Nguyen <n...@apple.com>
Walk up stacking contexts in RenderLayerBacking::compositingOpacity
Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp (281366 => 281367)
--- trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp 2021-08-21 06:10:53 UTC (rev 281366)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp 2021-08-21 07:01:21 UTC (rev 281367)
@@ -485,6 +485,11 @@
return !document() || document()->activeDOMObjectsAreSuspended() || document()->activeDOMObjectsAreStopped();
}
+bool AudioContext::isPlaying() const
+{
+ return state() == State::Running;
+}
+
void AudioContext::pageMutedStateDidChange()
{
if (document() && document()->page())
Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.h (281366 => 281367)
--- trunk/Source/WebCore/Modules/webaudio/AudioContext.h 2021-08-21 06:10:53 UTC (rev 281366)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.h 2021-08-21 07:01:21 UTC (rev 281367)
@@ -130,6 +130,7 @@
bool shouldOverrideBackgroundPlaybackRestriction(PlatformMediaSession::InterruptionType) const final { return false; }
bool canProduceAudio() const final { return true; }
bool isSuspended() const final;
+ bool isPlaying() const final;
MediaSessionGroupIdentifier mediaSessionGroupIdentifier() const final;
// MediaCanStartListener.
Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (281366 => 281367)
--- trunk/Source/WebCore/html/HTMLMediaElement.h 2021-08-21 06:10:53 UTC (rev 281366)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h 2021-08-21 07:01:21 UTC (rev 281367)
@@ -457,7 +457,7 @@
WEBCORE_EXPORT static void clearMediaCacheForOrigins(const String&, const HashSet<SecurityOriginData>&);
static void resetMediaEngines();
- bool isPlaying() const { return m_playing; }
+ bool isPlaying() const final { return m_playing; }
#if ENABLE(WEB_AUDIO)
MediaElementAudioSourceNode* audioSourceNode() { return m_audioSourceNode; }
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSession.cpp (281366 => 281367)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSession.cpp 2021-08-21 06:10:53 UTC (rev 281366)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSession.cpp 2021-08-21 07:01:21 UTC (rev 281367)
@@ -322,6 +322,11 @@
return m_client.isSuspended();
}
+bool PlatformMediaSession::isPlaying() const
+{
+ return m_client.isPlaying();
+}
+
bool PlatformMediaSession::shouldOverrideBackgroundLoadingRestriction() const
{
return m_client.shouldOverrideBackgroundLoadingRestriction();
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSession.h (281366 => 281367)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSession.h 2021-08-21 06:10:53 UTC (rev 281366)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSession.h 2021-08-21 07:01:21 UTC (rev 281367)
@@ -158,6 +158,7 @@
bool isHidden() const;
bool isSuspended() const;
+ bool isPlaying() const;
bool shouldOverrideBackgroundLoadingRestriction() const;
@@ -252,6 +253,7 @@
virtual bool canProduceAudio() const { return false; }
virtual bool isSuspended() const { return false; };
+ virtual bool isPlaying() const { return false; };
virtual bool shouldOverrideBackgroundPlaybackRestriction(PlatformMediaSession::InterruptionType) const = 0;
virtual bool shouldOverrideBackgroundLoadingRestriction() const { return false; }
Modified: trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm (281366 => 281367)
--- trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm 2021-08-21 06:10:53 UTC (rev 281366)
+++ trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm 2021-08-21 07:01:21 UTC (rev 281367)
@@ -86,6 +86,7 @@
int webAudioCount = 0;
int captureCount = countActiveAudioCaptureSources();
bool hasAudibleAudioOrVideoMediaType = false;
+ bool isPlayingAudio = false;
forEachSession([&] (auto& session) mutable {
auto type = session.mediaType();
switch (type) {
@@ -101,16 +102,19 @@
++audioCount;
break;
case PlatformMediaSession::MediaType::WebAudio:
- if (session.canProduceAudio())
+ if (session.canProduceAudio()) {
++webAudioCount;
+ isPlayingAudio |= session.isPlaying();
+ }
break;
}
if (!hasAudibleAudioOrVideoMediaType) {
- if ((type == PlatformMediaSession::MediaType::VideoAudio || type == PlatformMediaSession::MediaType::Audio) && session.canProduceAudio() && session.hasPlayedSinceLastInterruption())
+ bool isPotentiallyAudible = session.isPlayingToWirelessPlaybackTarget() || ((type == PlatformMediaSession::MediaType::VideoAudio || type == PlatformMediaSession::MediaType::Audio) && session.canProduceAudio() && session.hasPlayedSinceLastInterruption());
+ if (isPotentiallyAudible) {
hasAudibleAudioOrVideoMediaType = true;
- if (session.isPlayingToWirelessPlaybackTarget())
- hasAudibleAudioOrVideoMediaType = true;
+ isPlayingAudio |= session.isPlaying();
+ }
}
});
@@ -138,7 +142,7 @@
RouteSharingPolicy policy = RouteSharingPolicy::Default;
auto category = AudioSession::CategoryType::None;
- if (captureCount)
+ if (captureCount || (isPlayingAudio && AudioSession::sharedSession().category() == AudioSession::CategoryType::PlayAndRecord))
category = AudioSession::CategoryType::PlayAndRecord;
else if (hasAudibleAudioOrVideoMediaType) {
category = AudioSession::CategoryType::MediaPlayback;