⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 266121 in webkit


Ignore:
Timestamp:
Aug 25, 2020, 9:15:32 AM (6 years ago)
Author:
eric.carlson@apple.com
Message:

[macOS] Update audio arbitration manager when audio transport changes
https://bugs.webkit.org/show_bug.cgi?id=215781
<rdar://problem/65920613>

Reviewed by Jer Noble.

Source/WebCore:

No new tests, updated AudioRoutingArbitration API test.

  • platform/audio/AudioSession.cpp:

(WebCore::AudioSession::audioOutputDeviceChanged): Add empty method.
(WebCore::setIsPlayingToBluetoothOverride): Ditto.

  • platform/audio/AudioSession.h:
  • platform/audio/cocoa/MediaSessionManagerCocoa.h:
  • platform/audio/cocoa/MediaSessionManagerCocoa.mm:

(WebCore::MediaSessionManagerCocoa::audioOutputDeviceChanged): Call AudioSession::audioOutputDeviceChanged.

  • platform/audio/mac/AudioSessionMac.mm:

(WebCore::defaultDeviceTransportIsBluetooth): New.
(WebCore::AudioSession::audioOutputDeviceChanged): Clear m_private->playingToBluetooth
if bluetooth transport has changed since the last arbitration update.
(WebCore::AudioSession::setIsPlayingToBluetoothOverride): Allow override of bluetooth
transport for testing.
(WebCore::AudioSession::setCategory): Update routing arbitration if audio session category
or bluetooth transport changes.

  • testing/Internals.cpp:

(WebCore::Internals::setIsPlayingToBluetoothOverride):

  • testing/Internals.h:
  • testing/Internals.idl:

Source/WebKit:

  • UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h:
  • UIProcess/API/Cocoa/WKWebViewTesting.mm:

(-[WKWebView _audioRoutingArbitrationUpdateTime]):

  • UIProcess/Media/AudioSessionRoutingArbitratorProxy.h:

(WebKit::AudioSessionRoutingArbitratorProxy::arbitrationUpdateTime const):

  • UIProcess/Media/cocoa/AudioSessionRoutingArbitratorProxyCocoa.mm:

(WebKit::AudioSessionRoutingArbitratorProxy::beginRoutingArbitrationWithCategory):

Tools:

  • TestWebKitAPI/Tests/WebKitCocoa/AudioRoutingArbitration.mm:

(AudioRoutingArbitration::statusShouldBecomeEqualTo): Add message string to help
debugging when the test fails.

  • TestWebKitAPI/Tests/WebKitLegacy/ios/video-with-audio.html:
Location:
trunk
Files:
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r266120 r266121  
     12020-08-25  Eric Carlson  <eric.carlson@apple.com>
     2
     3        [macOS] Update audio arbitration manager when audio transport changes
     4        https://bugs.webkit.org/show_bug.cgi?id=215781
     5        <rdar://problem/65920613>
     6
     7        Reviewed by Jer Noble.
     8
     9        No new tests, updated AudioRoutingArbitration API test.
     10
     11        * platform/audio/AudioSession.cpp:
     12        (WebCore::AudioSession::audioOutputDeviceChanged): Add empty method.
     13        (WebCore::setIsPlayingToBluetoothOverride): Ditto.
     14        * platform/audio/AudioSession.h:
     15
     16        * platform/audio/cocoa/MediaSessionManagerCocoa.h:
     17        * platform/audio/cocoa/MediaSessionManagerCocoa.mm:
     18        (WebCore::MediaSessionManagerCocoa::audioOutputDeviceChanged): Call AudioSession::audioOutputDeviceChanged.
     19
     20        * platform/audio/mac/AudioSessionMac.mm:
     21        (WebCore::defaultDeviceTransportIsBluetooth): New.
     22        (WebCore::AudioSession::audioOutputDeviceChanged): Clear m_private->playingToBluetooth
     23        if bluetooth transport has changed since the last arbitration update.
     24        (WebCore::AudioSession::setIsPlayingToBluetoothOverride): Allow override of bluetooth
     25        transport for testing.
     26        (WebCore::AudioSession::setCategory): Update routing arbitration if audio session category
     27        or bluetooth transport changes.
     28
     29        * testing/Internals.cpp:
     30        (WebCore::Internals::setIsPlayingToBluetoothOverride):
     31        * testing/Internals.h:
     32        * testing/Internals.idl:
     33
    1342020-08-25  Youenn Fablet  <youenn@apple.com>
    235
  • trunk/Source/WebCore/platform/audio/AudioSession.cpp

    r260102 r266121  
    164164#endif // !PLATFORM(COCOA)
    165165
     166#if !PLATFORM(MAC)
     167void AudioSession::audioOutputDeviceChanged()
     168{
     169    notImplemented();
     170}
     171
     172void AudioSession::setIsPlayingToBluetoothOverride(Optional<bool>)
     173{
     174    notImplemented();
     175}
     176#endif // !PLATFORM(COCOA)
     177
     178
    166179String convertEnumerationToString(RouteSharingPolicy enumerationValue)
    167180{
  • trunk/Source/WebCore/platform/audio/AudioSession.h

    r260102 r266121  
    9898    void removeMutedStateObserver(MutedStateObserver*);
    9999
     100    void audioOutputDeviceChanged();
     101    void setIsPlayingToBluetoothOverride(Optional<bool>);
     102
    100103    virtual bool isMuted() const;
    101104    virtual void handleMutedStateChange();
  • trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.h

    r264177 r266121  
    9797    void audioHardwareDidBecomeActive() final { }
    9898    void audioHardwareDidBecomeInactive() final { }
    99     void audioOutputDeviceChanged() final { updateSessionState(); }
     99    void audioOutputDeviceChanged() final;
    100100
    101101    // PAL::SystemSleepListener
  • trunk/Source/WebCore/platform/audio/cocoa/MediaSessionManagerCocoa.mm

    r261709 r266121  
    367367}
    368368
     369void MediaSessionManagerCocoa::audioOutputDeviceChanged()
     370{
     371    AudioSession::sharedSession().audioOutputDeviceChanged();
     372    updateSessionState();
     373}
     374
    369375} // namespace WebCore
    370376
  • trunk/Source/WebCore/platform/audio/mac/AudioSessionMac.mm

    r265173 r266121  
    5555}
    5656
     57#if ENABLE(ROUTING_ARBITRATION)
     58static Optional<bool> isPlayingToBluetoothOverride;
     59
     60static float defaultDeviceTransportIsBluetooth()
     61{
     62    if (isPlayingToBluetoothOverride)
     63        return *isPlayingToBluetoothOverride;
     64
     65    static const AudioObjectPropertyAddress audioDeviceTransportTypeProperty = {
     66        kAudioDevicePropertyTransportType,
     67        kAudioObjectPropertyScopeGlobal,
     68        kAudioObjectPropertyElementMaster,
     69    };
     70    UInt32 transportType = kAudioDeviceTransportTypeUnknown;
     71    UInt32 transportSize = sizeof(transportType);
     72    if (AudioObjectGetPropertyData(defaultDevice(), &audioDeviceTransportTypeProperty, 0, 0, &transportSize, &transportType))
     73        return false;
     74
     75    return transportType == kAudioDeviceTransportTypeBluetooth || transportType == kAudioDeviceTransportTypeBluetoothLE;
     76}
     77#endif
     78
    5779class AudioSessionPrivate {
    5880    WTF_MAKE_FAST_ALLOCATED;
     
    6385#if ENABLE(ROUTING_ARBITRATION)
    6486    bool setupArbitrationOngoing { false };
     87    Optional<bool> playingToBluetooth;
     88    Optional<bool> playingToBluetoothOverride;
    6589#endif
    6690    AudioSession::CategoryType m_categoryOverride;
     
    80104}
    81105
     106void AudioSession::audioOutputDeviceChanged()
     107{
     108#if ENABLE(ROUTING_ARBITRATION)
     109    if (!m_private->playingToBluetooth || *m_private->playingToBluetooth == defaultDeviceTransportIsBluetooth())
     110        return;
     111
     112    m_private->playingToBluetooth = WTF::nullopt;
     113#endif
     114}
     115
     116void AudioSession::setIsPlayingToBluetoothOverride(Optional<bool> value)
     117{
     118#if ENABLE(ROUTING_ARBITRATION)
     119    isPlayingToBluetoothOverride = value;
     120#else
     121    UNUSED_PARAM(value);
     122#endif
     123}
     124
    82125void AudioSession::setCategory(CategoryType category, RouteSharingPolicy)
    83126{
    84127#if ENABLE(ROUTING_ARBITRATION)
    85     if (category == m_private->category)
    86         return;
     128    bool playingToBluetooth = defaultDeviceTransportIsBluetooth();
     129    if (category == m_private->category && m_private->playingToBluetooth && *m_private->playingToBluetooth == playingToBluetooth)
     130        return;
     131
    87132    m_private->category = category;
    88133
     
    106151    using DefaultRouteChanged = AudioSessionRoutingArbitrationClient::DefaultRouteChanged;
    107152
     153    m_private->playingToBluetooth = playingToBluetooth;
    108154    m_private->setupArbitrationOngoing = true;
    109155    m_routingArbitrationClient->beginRoutingArbitrationWithCategory(m_private->category, [this] (RoutingArbitrationError error, DefaultRouteChanged defaultRouteChanged) {
  • trunk/Source/WebCore/testing/Internals.cpp

    r266116 r266121  
    49374937#endif
    49384938
     4939ExceptionOr<void> Internals::setIsPlayingToBluetoothOverride(Optional<bool> isPlaying)
     4940{
     4941#if ENABLE(ROUTING_ARBITRATION)
     4942    AudioSession::sharedSession().setIsPlayingToBluetoothOverride(isPlaying);
     4943    return { };
     4944#else
     4945    UNUSED_PARAM(isPlaying);
     4946    return Exception { NotSupportedError };
     4947#endif
     4948}
     4949
    49394950void Internals::reportBacktrace()
    49404951{
  • trunk/Source/WebCore/testing/Internals.h

    r265379 r266121  
    624624#endif
    625625
     626    ExceptionOr<void> setIsPlayingToBluetoothOverride(Optional<bool>);
     627
    626628    bool isSelectPopupVisible(HTMLSelectElement&);
    627629
  • trunk/Source/WebCore/testing/Internals.idl

    r265379 r266121  
    652652    [Conditional=VIDEO] double privatePlayerVolume(HTMLMediaElement media);
    653653
     654    [MayThrowException] void setIsPlayingToBluetoothOverride(optional boolean? isPlaying = null);
     655
    654656    [Conditional=LEGACY_ENCRYPTED_MEDIA] void initializeMockCDM();
    655657    [Conditional=ENCRYPTED_MEDIA] MockCDMFactory registerMockCDM();
  • trunk/Source/WebKit/ChangeLog

    r266116 r266121  
     12020-08-25  Eric Carlson  <eric.carlson@apple.com>
     2
     3        [macOS] Update audio arbitration manager when audio transport changes
     4        https://bugs.webkit.org/show_bug.cgi?id=215781
     5        <rdar://problem/65920613>
     6
     7        Reviewed by Jer Noble.
     8
     9        * UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h:
     10        * UIProcess/API/Cocoa/WKWebViewTesting.mm:
     11        (-[WKWebView _audioRoutingArbitrationUpdateTime]):
     12
     13        * UIProcess/Media/AudioSessionRoutingArbitratorProxy.h:
     14        (WebKit::AudioSessionRoutingArbitratorProxy::arbitrationUpdateTime const):
     15
     16        * UIProcess/Media/cocoa/AudioSessionRoutingArbitratorProxyCocoa.mm:
     17        (WebKit::AudioSessionRoutingArbitratorProxy::beginRoutingArbitrationWithCategory):
     18
    1192020-08-25  Youenn Fablet  <youenn@apple.com>
    220
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h

    r264138 r266121  
    7575- (BOOL)_hasSleepDisabler;
    7676- (WKWebViewAudioRoutingArbitrationStatus)_audioRoutingArbitrationStatus;
     77- (double)_audioRoutingArbitrationUpdateTime;
    7778@end
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm

    r264138 r266121  
    263263}
    264264
     265- (double)_audioRoutingArbitrationUpdateTime
     266{
     267#if ENABLE(ROUTING_ARBITRATION)
     268    return _page->process().audioSessionRoutingArbitrator().arbitrationUpdateTime().secondsSinceEpoch().seconds();
     269#else
     270    return 0;
     271#endif
     272}
     273
    265274@end
  • trunk/Source/WebKit/UIProcess/Media/AudioSessionRoutingArbitratorProxy.h

    r263328 r266121  
    3030#include "MessageReceiver.h"
    3131#include <WebCore/AudioSession.h>
     32#include <wtf/WallTime.h>
    3233#include <wtf/WeakPtr.h>
    3334
     
    6061
    6162    ArbitrationStatus arbitrationStatus() const { return m_arbitrationStatus; }
     63    WallTime arbitrationUpdateTime() const { return m_arbitrationUpdateTime; }
    6264
    6365private:
     
    7274    WebCore::AudioSession::CategoryType m_category { WebCore::AudioSession::None };
    7375    ArbitrationStatus m_arbitrationStatus { ArbitrationStatus::None };
     76    WallTime m_arbitrationUpdateTime;
    7477};
    7578
  • trunk/Source/WebKit/UIProcess/Media/cocoa/AudioSessionRoutingArbitratorProxyCocoa.mm

    r265089 r266121  
    179179    m_category = category;
    180180    m_arbitrationStatus = ArbitrationStatus::Pending;
     181    m_arbitrationUpdateTime = WallTime::now();
    181182    SharedArbitrator::sharedInstance().beginRoutingArbitrationForArbitrator(*this, [weakThis = makeWeakPtr(*this), callback = WTFMove(callback)] (RoutingArbitrationError error, DefaultRouteChanged routeChanged) mutable {
    182183        if (weakThis)
  • trunk/Tools/ChangeLog

    r266120 r266121  
     12020-08-25  Eric Carlson  <eric.carlson@apple.com>
     2
     3        [macOS] Update audio arbitration manager when audio transport changes
     4        https://bugs.webkit.org/show_bug.cgi?id=215781
     5        <rdar://problem/65920613>
     6
     7        Reviewed by Jer Noble.
     8
     9        * TestWebKitAPI/Tests/WebKitCocoa/AudioRoutingArbitration.mm:
     10        (AudioRoutingArbitration::statusShouldBecomeEqualTo): Add message string to help
     11        debugging when the test fails.
     12        * TestWebKitAPI/Tests/WebKitLegacy/ios/video-with-audio.html:
     13
    1142020-08-25  Youenn Fablet  <youenn@apple.com>
    215
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/AudioRoutingArbitration.mm

    r263328 r266121  
    3434#import <WebKit/WKWebViewPrivate.h>
    3535#import <WebKit/WKWebViewPrivateForTesting.h>
     36#import <wtf/WallTime.h>
    3637
    3738class AudioRoutingArbitration : public testing::Test {
     
    4243    {
    4344        auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     45        WKRetainPtr<WKContextRef> context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
     46        configuration.get().processPool = (WKProcessPool *)context.get();
    4447        configuration.get()._mediaDataLoadsAutomatically = YES;
    4548        configuration.get().mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
     
    5760    }
    5861
    59     void statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatus status)
     62    void statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatus status, const char* message)
    6063    {
    6164        int tries = 0;
     
    6770        } while (++tries <= 100);
    6871
    69         EXPECT_EQ(status, [webView _audioRoutingArbitrationStatus]);
     72        EXPECT_EQ(status, [webView _audioRoutingArbitrationStatus]) << message;
    7073    }
    7174};
     
    7376TEST_F(AudioRoutingArbitration, Basic)
    7477{
    75     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive);
     78    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive, "Basic");
    7679}
    7780
    7881TEST_F(AudioRoutingArbitration, Mute)
    7982{
    80     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive);
     83    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive, "Mute 1");
    8184
    8285    [webView objectByEvaluatingJavaScriptWithUserGesture:@"document.querySelector('video').muted = true"];
    8386
    84     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusNone);
     87    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusNone, "Mute 2");
    8588
    8689    [webView objectByEvaluatingJavaScriptWithUserGesture:@"document.querySelector('video').muted = false"];
    8790
    88     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive);
     91    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive, "Mute 3");
    8992}
    9093
    9194TEST_F(AudioRoutingArbitration, Navigation)
    9295{
    93     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive);
     96    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive, "Navigation 1");
    9497
    9598    [webView synchronouslyLoadHTMLString:@"<html>no contents</html>"];
    9699
    97     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusNone);
     100    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusNone, "Navigation 2");
    98101}
    99102
    100103TEST_F(AudioRoutingArbitration, Deletion)
    101104{
    102     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive);
     105    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive, "Deletion 1");
    103106
    104107    [webView objectByEvaluatingJavaScriptWithUserGesture:@"document.querySelector('video').parentNode.innerHTML = ''"];
    105108
    106     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusNone);
     109    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusNone, "Deletion 2");
    107110}
    108111
    109112TEST_F(AudioRoutingArbitration, Close)
    110113{
    111     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive);
     114    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive, "Close 1");
    112115
    113116    [webView _close];
    114117
    115     statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusNone);
     118    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusNone, "Close 2");
    116119}
    117120
     121TEST_F(AudioRoutingArbitration, Updating)
     122{
     123    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive, "Updating 1");
     124
     125    [webView evaluateJavaScript:@"document.querySelector('video').pause()" completionHandler:nil];
     126    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive, "Updating 2");
     127
     128    auto start = WallTime::now().secondsSinceEpoch().seconds();
     129    auto arbitrationUpdateTime = [webView _audioRoutingArbitrationUpdateTime];
     130    ASSERT_TRUE(arbitrationUpdateTime < start);
     131
     132    [webView evaluateJavaScript:@"document.querySelector('video').play()" completionHandler:nil];
     133    EXPECT_EQ(arbitrationUpdateTime, [webView _audioRoutingArbitrationUpdateTime]) << "Arbitration was unexpectedly updated";
     134    statusShouldBecomeEqualTo(WKWebViewAudioRoutingArbitrationStatusActive, "Updating 3");
     135
     136    [webView evaluateJavaScript:@"document.querySelector('video').pause()" completionHandler:nil];
     137    [webView stringByEvaluatingJavaScript:@"window.internals.setIsPlayingToBluetoothOverride(true)"];
     138
     139    [webView evaluateJavaScript:@"document.querySelector('video').play()" completionHandler:nil];
     140
     141    int tries = 0;
     142    do {
     143        if ([webView _audioRoutingArbitrationUpdateTime] > arbitrationUpdateTime)
     144            break;
     145
     146        TestWebKitAPI::Util::sleep(0.1);
     147    } while (++tries <= 100);
     148
     149    EXPECT_LT(arbitrationUpdateTime, [webView _audioRoutingArbitrationUpdateTime]) << "Arbitration was not updated";
     150
     151    [webView stringByEvaluatingJavaScript:@"window.internals.setIsPlayingToBluetoothOverride()"];
     152}
    118153
    119154#endif
  • trunk/Tools/TestWebKitAPI/Tests/WebKitLegacy/ios/video-with-audio.html

    r264106 r266121  
    33<head>
    44    <script>
    5     var timeout;
    6 
    75    function go() {
    86        var video = document.getElementsByTagName('video')[0];
     
    3028    }
    3129
     30    function pause() {
     31        let video = document.getElementsByTagName('video')[0];
     32        video.addEventListener("pause", paused, { once: true });
     33        video.pause();
     34    }
     35
     36    function play() {
     37        go();
     38    }
     39
    3240    document.addEventListener('pageshow', go);
    3341   </script>
Note: See TracChangeset for help on using the changeset viewer.