Skip to content

Commit

Permalink
[iOS] unable to start playing audio when device is locked
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=239812
<rdar://90642648>

Reviewed by Jer Noble.

Source/WebCore:

Updated media/audio-session-category.html.

* platform/audio/cocoa/MediaSessionManagerCocoa.mm:
(WebCore::MediaSessionManagerCocoa::updateSessionState): Choose the appropriate
route sharing policy after choosing the audio session category because
setting MediaPlayback+Default makes a process ineligible for NowPlaying, and starting
playback in the background will be blocked.

* platform/audio/mac/AudioSessionMac.h:
* platform/audio/mac/AudioSessionMac.mm:
(WebCore::AudioSessionMac::setCategory): Updated for testing.

* testing/Internals.cpp:
(WebCore::Internals::routeSharingPolicy const): Added accessor for testing.
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

* media/audio-session-category-expected.txt:
* media/audio-session-category.html:

Canonical link: https://commits.webkit.org/250059@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@293530 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
eric-carlson committed Apr 27, 2022
1 parent 9684d65 commit 26e0840
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 14 deletions.
11 changes: 11 additions & 0 deletions LayoutTests/ChangeLog
@@ -1,3 +1,14 @@
2022-04-27 Eric Carlson <eric.carlson@apple.com>

[iOS] unable to start playing audio when device is locked
https://bugs.webkit.org/show_bug.cgi?id=239812
<rdar://90642648>

Reviewed by Jer Noble.

* media/audio-session-category-expected.txt:
* media/audio-session-category.html:

2022-04-27 Karl Rackler <rackler@apple.com>

[ macOS Debug wk2 ] fast/css/identical-logical-height-decl.html is a flaky image failure
Expand Down
9 changes: 9 additions & 0 deletions LayoutTests/media/audio-session-category-expected.txt
Expand Up @@ -14,19 +14,23 @@ EXPECTED (internals.audioSessionCategory() == 'None') OK
RUN(video.play())
EVENT(playing)
EXPECTED (internals.audioSessionCategory() == 'None') OK
EXPECTED (internals.routeSharingPolicy() == 'Default') OK

** Check category when an unmuted element is playing.
RUN(video.muted = false)
EVENT(volumechange)
EXPECTED (internals.audioSessionCategory() == 'MediaPlayback') OK
EXPECTED (internals.routeSharingPolicy() == 'LongFormAudio') OK

** Mute the element, check again after 500ms.
RUN(video.pause())
RUN(video.muted = true)
EXPECTED (internals.audioSessionCategory() == 'MediaPlayback') OK
EXPECTED (internals.routeSharingPolicy() == 'LongFormAudio') OK

** And check again after 3 seconds.
EXPECTED (internals.audioSessionCategory() == 'None') OK
EXPECTED (internals.routeSharingPolicy() == 'Default') OK


** AudioContext test **
Expand All @@ -39,12 +43,15 @@ EXPECTED (internals.audioSessionCategory() == 'None') OK

** Check category after starting oscillator.
EXPECTED (internals.audioSessionCategory() == 'AmbientSound') OK
EXPECTED (internals.routeSharingPolicy() == 'Default') OK

** Close the context, check again after 500ms.
EXPECTED (internals.audioSessionCategory() == 'AmbientSound') OK
EXPECTED (internals.routeSharingPolicy() == 'Default') OK

** And check again after 3 seconds.
EXPECTED (internals.audioSessionCategory() == 'None') OK
EXPECTED (internals.routeSharingPolicy() == 'Default') OK


** MediaStream test **
Expand All @@ -54,11 +61,13 @@ EXPECTED (internals.audioSessionCategory() == 'None') OK

** Check category when capturing.
EXPECTED (internals.audioSessionCategory() == 'PlayAndRecord') OK
EXPECTED (internals.routeSharingPolicy() == 'Default') OK

** Check after MediaStream is attached to audio element.
RUN(video.play())
EVENT(playing)
EXPECTED (internals.audioSessionCategory() == 'PlayAndRecord') OK
EXPECTED (internals.routeSharingPolicy() == 'Default') OK

** Check after MediaStream muting audio track.
EXPECTED (internals.audioSessionCategory() == 'PlayAndRecord') OK
Expand Down
12 changes: 11 additions & 1 deletion LayoutTests/media/audio-session-category.html
Expand Up @@ -36,19 +36,24 @@
runWithKeyDown(() => { run('video.play()') });
await waitFor(video, 'playing');
testExpected('internals.audioSessionCategory()', 'None');

testExpected('internals.routeSharingPolicy()', 'Default');

consoleWrite('<br>** Check category when an unmuted element is playing.');
runWithKeyDown(() => { run('video.muted = false') });
await waitFor(video, 'volumechange');
testExpected('internals.audioSessionCategory()', 'MediaPlayback');
testExpected('internals.routeSharingPolicy()', 'LongFormAudio');

consoleWrite('<br>** Mute the element, check again after 500ms.');
run('video.pause()');
runWithKeyDown(() => { run('video.muted = true') });
await sleepFor(500);
testExpected('internals.audioSessionCategory()', 'MediaPlayback');
testExpected('internals.routeSharingPolicy()', 'LongFormAudio');

await waitForCategory('None', 3, '<br>** And check again after 3 seconds.');
testExpected('internals.routeSharingPolicy()', 'Default');

video.src = '';
video.load();
}
Expand Down Expand Up @@ -77,13 +82,16 @@
oscillator.start(0);
await sleepFor(500);
testExpected('internals.audioSessionCategory()', 'AmbientSound');
testExpected('internals.routeSharingPolicy()', 'Default');

consoleWrite('<br>** Close the context, check again after 500ms.');
await context.close();
await sleepFor(500);
testExpected('internals.audioSessionCategory()', 'AmbientSound');
testExpected('internals.routeSharingPolicy()', 'Default');

await waitForCategory('None', 3, '<br>** And check again after 3 seconds.');
testExpected('internals.routeSharingPolicy()', 'Default');
}

async function testMediaStream()
Expand All @@ -94,12 +102,14 @@
consoleWrite('<br>** Check category when capturing.');
let stream = await navigator.mediaDevices.getUserMedia({audio : true});
testExpected('internals.audioSessionCategory()', 'PlayAndRecord');
testExpected('internals.routeSharingPolicy()', 'Default');

consoleWrite('<br>** Check after MediaStream is attached to audio element.');
video.srcObject = stream;
runWithKeyDown(() => { run('video.play()') });
await waitFor(video, 'playing');
testExpected('internals.audioSessionCategory()', 'PlayAndRecord');
testExpected('internals.routeSharingPolicy()', 'Default');

consoleWrite('<br>** Check after MediaStream muting audio track.');
const audioTrack = stream.getAudioTracks()[0];
Expand Down
25 changes: 25 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,28 @@
2022-04-27 Eric Carlson <eric.carlson@apple.com>

[iOS] unable to start playing audio when device is locked
https://bugs.webkit.org/show_bug.cgi?id=239812
<rdar://90642648>

Reviewed by Jer Noble.

Updated media/audio-session-category.html.

* platform/audio/cocoa/MediaSessionManagerCocoa.mm:
(WebCore::MediaSessionManagerCocoa::updateSessionState): Choose the appropriate
route sharing policy after choosing the audio session category because
setting MediaPlayback+Default makes a process ineligible for NowPlaying, and starting
playback in the background will be blocked.

* platform/audio/mac/AudioSessionMac.h:
* platform/audio/mac/AudioSessionMac.mm:
(WebCore::AudioSessionMac::setCategory): Updated for testing.

* testing/Internals.cpp:
(WebCore::Internals::routeSharingPolicy const): Added accessor for testing.
* testing/Internals.h:
* testing/Internals.idl:

2022-04-27 Tim Nguyen <ntim@apple.com>

Make -webkit-transform-style an alias of transform-style
Expand Down
12 changes: 6 additions & 6 deletions Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm
Expand Up @@ -178,14 +178,12 @@
if (!DeprecatedGlobalSettings::shouldManageAudioSessionCategory())
return;

RouteSharingPolicy policy = RouteSharingPolicy::Default;
auto category = AudioSession::CategoryType::None;
if (captureCount || (isPlayingAudio && AudioSession::sharedSession().category() == AudioSession::CategoryType::PlayAndRecord))
category = AudioSession::CategoryType::PlayAndRecord;
else if (hasAudibleAudioOrVideoMediaType) {
else if (hasAudibleAudioOrVideoMediaType)
category = AudioSession::CategoryType::MediaPlayback;
policy = RouteSharingPolicy::LongFormAudio;
} else if (webAudioCount)
else if (webAudioCount)
category = AudioSession::CategoryType::AmbientSound;

if (category == AudioSession::CategoryType::None && m_previousCategory != AudioSession::CategoryType::None) {
Expand All @@ -197,9 +195,11 @@
} else
m_delayCategoryChangeTimer.stop();

m_previousCategory = category;
RouteSharingPolicy policy = (category == AudioSession::CategoryType::MediaPlayback) ? RouteSharingPolicy::LongFormAudio : RouteSharingPolicy::Default;

ALWAYS_LOG(LOGIDENTIFIER, "setting category = ", category, ", policy = ", policy, ", previous category = ", m_previousCategory);

ALWAYS_LOG(LOGIDENTIFIER, "setting category = ", category, ", policy = ", policy);
m_previousCategory = category;
AudioSession::sharedSession().setCategory(category, policy);
}

Expand Down
3 changes: 2 additions & 1 deletion Source/WebCore/platform/audio/mac/AudioSessionMac.h
Expand Up @@ -50,6 +50,7 @@ class AudioSessionMac final : public AudioSession {

// AudioSession
CategoryType category() const final { return m_category; }
RouteSharingPolicy routeSharingPolicy() const { return m_policy; }
void audioOutputDeviceChanged() final;
void setIsPlayingToBluetoothOverride(std::optional<bool>) final;
void setCategory(CategoryType, RouteSharingPolicy) final;
Expand All @@ -58,7 +59,6 @@ class AudioSessionMac final : public AudioSession {
size_t numberOfOutputChannels() const final;
size_t maximumNumberOfOutputChannels() const final;
bool tryToSetActiveInternal(bool) final;
RouteSharingPolicy routeSharingPolicy() const final;
String routingContextUID() const final;
size_t preferredBufferSize() const final;
void setPreferredBufferSize(size_t) final;
Expand All @@ -70,6 +70,7 @@ class AudioSessionMac final : public AudioSession {
std::optional<bool> m_lastMutedState;
mutable WeakHashSet<ConfigurationChangeObserver> m_configurationChangeObservers;
AudioSession::CategoryType m_category { AudioSession::CategoryType::None };
RouteSharingPolicy m_policy { RouteSharingPolicy::Default };
#if ENABLE(ROUTING_ARBITRATION)
bool m_setupArbitrationOngoing { false };
bool m_inRoutingArbitration { false };
Expand Down
9 changes: 3 additions & 6 deletions Source/WebCore/platform/audio/mac/AudioSessionMac.mm
Expand Up @@ -209,14 +209,15 @@ static float defaultDeviceTransportIsBluetooth()
#endif
}

void AudioSessionMac::setCategory(CategoryType category, RouteSharingPolicy)
void AudioSessionMac::setCategory(CategoryType category, RouteSharingPolicy policy)
{
#if ENABLE(ROUTING_ARBITRATION)
bool playingToBluetooth = defaultDeviceTransportIsBluetooth();
if (category == m_category && m_playingToBluetooth && *m_playingToBluetooth == playingToBluetooth)
return;

m_category = category;
m_policy = policy;

if (m_setupArbitrationOngoing) {
RELEASE_LOG_ERROR(Media, "AudioSessionMac::setCategory() - a beginArbitrationWithCategory is still ongoing");
Expand Down Expand Up @@ -254,6 +255,7 @@ static float defaultDeviceTransportIsBluetooth()
});
#else
m_category = category;
m_policy = policy;
#endif
}

Expand Down Expand Up @@ -368,11 +370,6 @@ static float defaultDeviceTransportIsBluetooth()
return true;
}

RouteSharingPolicy AudioSessionMac::routeSharingPolicy() const
{
return RouteSharingPolicy::Default;
}

String AudioSessionMac::routingContextUID() const
{
return emptyString();
Expand Down
9 changes: 9 additions & 0 deletions Source/WebCore/testing/Internals.cpp
Expand Up @@ -5683,6 +5683,15 @@ auto Internals::audioSessionCategory() const -> AudioSessionCategory
#endif
}

auto Internals::routeSharingPolicy() const -> RouteSharingPolicy
{
#if USE(AUDIO_SESSION)
return AudioSession::sharedSession().routeSharingPolicy();
#else
return RouteSharingPolicy::Default;
#endif
}

#if ENABLE(VIDEO)
auto Internals::categoryAtMostRecentPlayback(HTMLMediaElement& element) const -> AudioSessionCategory
{
Expand Down
9 changes: 9 additions & 0 deletions Source/WebCore/testing/Internals.h
Expand Up @@ -902,6 +902,7 @@ class Internals final : public RefCounted<Internals>, private ContextDestruction

#if USE(AUDIO_SESSION)
using AudioSessionCategory = WebCore::AudioSessionCategory;
using RouteSharingPolicy = WebCore::RouteSharingPolicy;
#else
enum class AudioSessionCategory : uint8_t {
None,
Expand All @@ -912,10 +913,18 @@ class Internals final : public RefCounted<Internals>, private ContextDestruction
PlayAndRecord,
AudioProcessing,
};

enum class RouteSharingPolicy : uint8_t {
Default,
LongFormAudio,
Independent,
LongFormVideo
};
#endif

bool supportsAudioSession() const;
AudioSessionCategory audioSessionCategory() const;
RouteSharingPolicy routeSharingPolicy() const;
#if ENABLE(VIDEO)
AudioSessionCategory categoryAtMostRecentPlayback(HTMLMediaElement&) const;
#endif
Expand Down
8 changes: 8 additions & 0 deletions Source/WebCore/testing/Internals.idl
Expand Up @@ -115,6 +115,13 @@ enum AudioSessionCategory {
"AudioProcessing"
};

enum RouteSharingPolicy {
"Default",
"LongFormAudio",
"Independent",
"LongFormVideo"
};

enum AutoplayPolicy {
"Default",
"Allow",
Expand Down Expand Up @@ -980,6 +987,7 @@ typedef (FetchRequest or FetchResponse) FetchObject;
readonly attribute boolean supportsAudioSession;
AudioSessionCategory audioSessionCategory();
[Conditional=VIDEO] AudioSessionCategory categoryAtMostRecentPlayback(HTMLMediaElement element);
RouteSharingPolicy routeSharingPolicy();

double preferredAudioBufferSize();
double currentAudioBufferSize();
Expand Down

0 comments on commit 26e0840

Please sign in to comment.