Diff
Modified: trunk/Source/WebCore/ChangeLog (216023 => 216024)
--- trunk/Source/WebCore/ChangeLog 2017-05-01 20:40:23 UTC (rev 216023)
+++ trunk/Source/WebCore/ChangeLog 2017-05-01 21:06:39 UTC (rev 216024)
@@ -1,3 +1,60 @@
+2017-05-01 Jer Noble <jer.no...@apple.com>
+
+ Add audio device change notifications to AudioSession.
+ https://bugs.webkit.org/show_bug.cgi?id=171403
+
+ Reviewed by Eric Carlson.
+
+ Add notifications to AudioSession which fire when the current input and output audio devices
+ change. Also add a notification when audio services are lost (which will only fire on iOS).
+ Take this opportunity to make the existing hardwareMutedStateDidChange() notification be
+ platform agnostic, to move the outputDeviceSupportsLowPowerMode() code from AudioHardwareListener,
+ and to do a few more clean-ups of the AudioSession code.
+
+ * WebCore.xcodeproj/project.pbxproj:
+ * html/HTMLMediaElement.cpp:
+ (WebCore::HTMLMediaElement::HTMLMediaElement):
+ (WebCore::HTMLMediaElement::~HTMLMediaElement):
+ * html/HTMLMediaElement.h:
+ * platform/audio/AudioSession.cpp:
+ (WebCore::AudioSession::AudioSession):
+ * platform/audio/AudioSession.h:
+ (WebCore::AudioSession::Observer::hardwareMutedStateDidChange):
+ (WebCore::AudioSession::Observer::audioInputDeviceChanged):
+ (WebCore::AudioSession::Observer::audioOutputDeviceChanged):
+ (WebCore::AudioSession::Observer::audioServicesLost):
+ (WebCore::AudioSession::Observer::audioServicesReset):
+ (WebCore::AudioSession::MutedStateObserver::~MutedStateObserver): Deleted.
+ * platform/audio/ios/AudioSessionIOS.mm:
+ (WebCore::AudioSessionPrivate::AudioSessionPrivate):
+ (WebCore::AudioSession::AudioSession):
+ (WebCore::AudioSession::setCategoryOverride):
+ (WebCore::AudioSession::categoryOverride):
+ (WebCore::AudioSession::isMuted):
+ (WebCore::AudioSession::outputDeviceSupportsLowPowerMode):
+ (WebCore::AudioSession::addObserver):
+ (WebCore::AudioSession::removeObserver):
+ * platform/audio/mac/AudioSessionMac.mm: Renamed from Source/WebCore/platform/audio/mac/AudioSessionMac.cpp.
+ (WebCore::defaultDevice):
+ (WebCore::AudioSessionPrivate::AudioSessionPrivate):
+ (WebCore::AudioSession::AudioSession):
+ (WebCore::AudioSession::~AudioSession):
+ (WebCore::AudioSession::category):
+ (WebCore::AudioSession::setCategory):
+ (WebCore::AudioSession::categoryOverride):
+ (WebCore::AudioSession::setCategoryOverride):
+ (WebCore::AudioSession::sampleRate):
+ (WebCore::AudioSession::bufferSize):
+ (WebCore::AudioSession::numberOfOutputChannels):
+ (WebCore::AudioSession::tryToSetActive):
+ (WebCore::AudioSession::preferredBufferSize):
+ (WebCore::AudioSession::setPreferredBufferSize):
+ (WebCore::AudioSession::isMuted):
+ (WebCore::currentDeviceSupportsLowPowerBufferSize):
+ (WebCore::AudioSession::outputDeviceSupportsLowPowerMode):
+ (WebCore::AudioSession::addObserver):
+ (WebCore::AudioSession::removeObserver):
+
2017-05-01 Chris Dumez <cdu...@apple.com>
Do not dispatch SVG load event in frameless documents
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (216023 => 216024)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-05-01 20:40:23 UTC (rev 216023)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-05-01 21:06:39 UTC (rev 216024)
@@ -6116,7 +6116,7 @@
CD5393D4175E018600C07123 /* JSMemoryInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CD5393D2175E018600C07123 /* JSMemoryInfo.h */; };
CD54A762180F9F7000B076C9 /* AudioTrackPrivateMediaSourceAVFObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD54A760180F9F7000B076C9 /* AudioTrackPrivateMediaSourceAVFObjC.cpp */; };
CD54A763180F9F7000B076C9 /* AudioTrackPrivateMediaSourceAVFObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = CD54A761180F9F7000B076C9 /* AudioTrackPrivateMediaSourceAVFObjC.h */; };
- CD54DE4B17469C6D005E5B36 /* AudioSessionMac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD54DE4917469C6D005E5B36 /* AudioSessionMac.cpp */; };
+ CD54DE4B17469C6D005E5B36 /* AudioSessionMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD54DE4917469C6D005E5B36 /* AudioSessionMac.mm */; };
CD5596911475B678001D0BD0 /* AudioFileReaderIOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD55968F1475B678001D0BD0 /* AudioFileReaderIOS.cpp */; };
CD5596921475B678001D0BD0 /* AudioFileReaderIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = CD5596901475B678001D0BD0 /* AudioFileReaderIOS.h */; };
CD5896E11CD2B15100B3BCC8 /* WebPlaybackControlsManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD5896DF1CD2B15100B3BCC8 /* WebPlaybackControlsManager.mm */; };
@@ -14594,7 +14594,7 @@
CD5393D2175E018600C07123 /* JSMemoryInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMemoryInfo.h; sourceTree = "<group>"; };
CD54A760180F9F7000B076C9 /* AudioTrackPrivateMediaSourceAVFObjC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioTrackPrivateMediaSourceAVFObjC.cpp; sourceTree = "<group>"; };
CD54A761180F9F7000B076C9 /* AudioTrackPrivateMediaSourceAVFObjC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioTrackPrivateMediaSourceAVFObjC.h; sourceTree = "<group>"; };
- CD54DE4917469C6D005E5B36 /* AudioSessionMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioSessionMac.cpp; sourceTree = "<group>"; };
+ CD54DE4917469C6D005E5B36 /* AudioSessionMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioSessionMac.mm; sourceTree = "<group>"; };
CD55968F1475B678001D0BD0 /* AudioFileReaderIOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioFileReaderIOS.cpp; sourceTree = "<group>"; };
CD5596901475B678001D0BD0 /* AudioFileReaderIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioFileReaderIOS.h; sourceTree = "<group>"; };
CD5896DF1CD2B15100B3BCC8 /* WebPlaybackControlsManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebPlaybackControlsManager.mm; sourceTree = "<group>"; };
@@ -26172,7 +26172,7 @@
073B87631E43859D0071C0EC /* AudioSampleBufferList.h */,
073B87651E43859D0071C0EC /* AudioSampleDataSource.h */,
073B87641E43859D0071C0EC /* AudioSampleDataSource.mm */,
- CD54DE4917469C6D005E5B36 /* AudioSessionMac.cpp */,
+ CD54DE4917469C6D005E5B36 /* AudioSessionMac.mm */,
073B87571E40DCFD0071C0EC /* CAAudioStreamDescription.cpp */,
073B87581E40DCFD0071C0EC /* CAAudioStreamDescription.h */,
CDC734121977896C0046BFC5 /* CARingBuffer.cpp */,
@@ -30525,7 +30525,7 @@
FD8C46EB154608E700A5910C /* AudioScheduledSourceNode.cpp in Sources */,
CDA79824170A258300D45C55 /* AudioSession.cpp in Sources */,
CDA79827170A279100D45C55 /* AudioSessionIOS.mm in Sources */,
- CD54DE4B17469C6D005E5B36 /* AudioSessionMac.cpp in Sources */,
+ CD54DE4B17469C6D005E5B36 /* AudioSessionMac.mm in Sources */,
CD8A7BBB197735FE00CBD643 /* AudioSourceProviderAVFObjC.mm in Sources */,
FDB052DF1561A42C00B500D6 /* AudioSummingJunction.cpp in Sources */,
BE88E0D81715D2A200658D98 /* AudioTrack.cpp in Sources */,
Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (216023 => 216024)
--- trunk/Source/WebCore/html/HTMLMediaElement.cpp 2017-05-01 20:40:23 UTC (rev 216023)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp 2017-05-01 21:06:39 UTC (rev 216024)
@@ -511,8 +511,8 @@
registerWithDocument(document);
-#if USE(AUDIO_SESSION) && PLATFORM(MAC)
- AudioSession::sharedSession().addMutedStateObserver(this);
+#if USE(AUDIO_SESSION)
+ AudioSession::sharedSession().addObserver(*this);
#endif
}
@@ -528,8 +528,8 @@
setShouldDelayLoadEvent(false);
unregisterWithDocument(document());
-#if USE(AUDIO_SESSION) && PLATFORM(MAC)
- AudioSession::sharedSession().removeMutedStateObserver(this);
+#if USE(AUDIO_SESSION)
+ AudioSession::sharedSession().removeObserver(*this);
#endif
#if ENABLE(VIDEO_TRACK)
@@ -3420,7 +3420,7 @@
scheduleUpdatePlaybackControlsManager();
}
-#if USE(AUDIO_SESSION) && PLATFORM(MAC)
+#if USE(AUDIO_SESSION)
void HTMLMediaElement::hardwareMutedStateDidChange(AudioSession* session)
{
if (!session->isMuted())
Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (216023 => 216024)
--- trunk/Source/WebCore/html/HTMLMediaElement.h 2017-05-01 20:40:23 UTC (rev 216023)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h 2017-05-01 21:06:39 UTC (rev 216024)
@@ -51,7 +51,7 @@
#include "VideoTrack.h"
#endif
-#if USE(AUDIO_SESSION) && PLATFORM(MAC)
+#if USE(AUDIO_SESSION)
#include "AudioSession.h"
#endif
@@ -125,8 +125,8 @@
, private TextTrackClient
, private VideoTrackClient
#endif
-#if USE(AUDIO_SESSION) && PLATFORM(MAC)
- , private AudioSession::MutedStateObserver
+#if USE(AUDIO_SESSION)
+ , private AudioSession::Observer
#endif
{
public:
@@ -817,7 +817,7 @@
void pageMutedStateDidChange() override;
-#if USE(AUDIO_SESSION) && PLATFORM(MAC)
+#if USE(AUDIO_SESSION)
void hardwareMutedStateDidChange(AudioSession*) final;
#endif
Modified: trunk/Source/WebCore/platform/audio/AudioSession.cpp (216023 => 216024)
--- trunk/Source/WebCore/platform/audio/AudioSession.cpp 2017-05-01 20:40:23 UTC (rev 216023)
+++ trunk/Source/WebCore/platform/audio/AudioSession.cpp 2017-05-01 21:06:39 UTC (rev 216024)
@@ -44,7 +44,7 @@
};
AudioSession::AudioSession()
- : m_private(nullptr)
+ : m_private(makeUniqueRef<AudioSessionPrivate>())
{
notImplemented();
}
Modified: trunk/Source/WebCore/platform/audio/AudioSession.h (216023 => 216024)
--- trunk/Source/WebCore/platform/audio/AudioSession.h 2017-05-01 20:40:23 UTC (rev 216023)
+++ trunk/Source/WebCore/platform/audio/AudioSession.h 2017-05-01 21:06:39 UTC (rev 216024)
@@ -30,10 +30,9 @@
#if USE(AUDIO_SESSION)
-#include <memory>
-#include <wtf/HashSet.h>
-#include <wtf/NeverDestroyed.h>
+#include <wtf/Forward.h>
#include <wtf/Noncopyable.h>
+#include <wtf/UniqueRef.h>
namespace WebCore {
@@ -44,6 +43,19 @@
public:
WEBCORE_EXPORT static AudioSession& sharedSession();
+ class Observer {
+ public:
+ virtual ~Observer() = default;
+
+ virtual void hardwareMutedStateDidChange(AudioSession*) { };
+ virtual void currentAudioInputDeviceChanged() { };
+ virtual void currentAudioOutputDeviceChanged() { };
+ virtual void audioServicesLost() { };
+ virtual void audioServicesReset() { };
+ };
+ void addObserver(Observer&);
+ void removeObserver(Observer&);
+
enum CategoryType {
None,
AmbientSound,
@@ -68,18 +80,8 @@
size_t preferredBufferSize() const;
void setPreferredBufferSize(size_t);
- class MutedStateObserver {
- public:
- virtual ~MutedStateObserver() { }
-
- virtual void hardwareMutedStateDidChange(AudioSession*) = 0;
- };
-
- void addMutedStateObserver(MutedStateObserver*);
- void removeMutedStateObserver(MutedStateObserver*);
-
+ bool outputDeviceSupportsLowPowerMode() const;
bool isMuted() const;
- void handleMutedStateChange();
private:
friend class NeverDestroyed<AudioSession>;
@@ -86,8 +88,7 @@
AudioSession();
~AudioSession();
- std::unique_ptr<AudioSessionPrivate> m_private;
- HashSet<MutedStateObserver*> m_observers;
+ UniqueRef<AudioSessionPrivate> m_private;
};
}
Modified: trunk/Source/WebCore/platform/audio/ios/AudioSessionIOS.mm (216023 => 216024)
--- trunk/Source/WebCore/platform/audio/ios/AudioSessionIOS.mm 2017-05-01 20:40:23 UTC (rev 216023)
+++ trunk/Source/WebCore/platform/audio/ios/AudioSessionIOS.mm 2017-05-01 21:06:39 UTC (rev 216024)
@@ -32,7 +32,10 @@
#import "SoftLinking.h"
#import <AVFoundation/AVAudioSession.h>
#import <objc/runtime.h>
+#import <wtf/HashSet.h>
#import <wtf/RetainPtr.h>
+#import <wtf/Vector.h>
+#import <wtf/WeakPtr.h>
SOFT_LINK_FRAMEWORK(AVFoundation)
SOFT_LINK_CLASS(AVFoundation, AVAudioSession)
@@ -44,6 +47,10 @@
SOFT_LINK_POINTER(AVFoundation, AVAudioSessionCategoryPlayAndRecord, NSString *)
SOFT_LINK_POINTER(AVFoundation, AVAudioSessionCategoryAudioProcessing, NSString *)
SOFT_LINK_POINTER(AVFoundation, AVAudioSessionInterruptionTypeKey, NSString *)
+SOFT_LINK_POINTER(AVFoundation, AVAudioSessionRouteChangeNotification, NSString*)
+SOFT_LINK_POINTER(AVFoundation, AVAudioSessionMediaServicesWereLostNotification, NSString*)
+SOFT_LINK_POINTER(AVFoundation, AVAudioSessionMediaServicesWereResetNotification, NSString*)
+SOFT_LINK_POINTER(AVFoundation, AVAudioSessionRouteChangePreviousRouteKey, NSString*)
#define AVAudioSession getAVAudioSessionClass()
#define AVAudioSessionCategoryAmbient getAVAudioSessionCategoryAmbient()
@@ -53,7 +60,10 @@
#define AVAudioSessionCategoryPlayAndRecord getAVAudioSessionCategoryPlayAndRecord()
#define AVAudioSessionCategoryAudioProcessing getAVAudioSessionCategoryAudioProcessing()
#define AVAudioSessionInterruptionTypeKey getAVAudioSessionInterruptionTypeKey()
-
+#define AVAudioSessionRouteChangeNotification getAVAudioSessionRouteChangeNotification()
+#define AVAudioSessionMediaServicesWereLostNotification getAVAudioSessionMediaServicesWereLostNotification()
+#define AVAudioSessionMediaServicesWereResetNotification getAVAudioSessionMediaServicesWereResetNotification()
+#define AVAudioSessionRouteChangePreviousRouteKey getAVAudioSessionRouteChangePreviousRouteKey()
namespace WebCore {
#if !LOG_DISABLED
@@ -77,17 +87,19 @@
class AudioSessionPrivate {
public:
- AudioSessionPrivate(AudioSession*);
- AudioSession::CategoryType m_categoryOverride;
+ AudioSessionPrivate(AudioSession& audioSession)
+ : weakPtrFactory(&audioSession)
+ {
+ }
+
+ WeakPtrFactory<AudioSession> weakPtrFactory;
+ AudioSession::CategoryType categoryOverride { AudioSession::None };
+ HashSet<AudioSession::Observer*> observers;
+ Vector<RetainPtr<id>> notificationCallbacks;
};
-AudioSessionPrivate::AudioSessionPrivate(AudioSession*)
- : m_categoryOverride(AudioSession::None)
-{
-}
-
AudioSession::AudioSession()
- : m_private(std::make_unique<AudioSessionPrivate>(this))
+ : m_private(makeUniqueRef<AudioSessionPrivate>(*this))
{
}
@@ -154,16 +166,16 @@
void AudioSession::setCategoryOverride(CategoryType category)
{
- if (m_private->m_categoryOverride == category)
+ if (m_private->categoryOverride == category)
return;
- m_private->m_categoryOverride = category;
+ m_private->categoryOverride = category;
setCategory(category);
}
AudioSession::CategoryType AudioSession::categoryOverride() const
{
- return m_private->m_categoryOverride;
+ return m_private->categoryOverride;
}
float AudioSession::sampleRate() const
@@ -201,6 +213,90 @@
ASSERT(!error);
}
+bool AudioSession::isMuted() const
+{
+ return false;
}
+bool AudioSession::outputDeviceSupportsLowPowerMode() const
+{
+ return false;
+}
+
+void AudioSession::addObserver(Observer& observer)
+{
+ ASSERT(!m_private->observers.contains(&observer));
+ m_private->observers.add(&observer);
+
+ if (m_private->observers.size() != 1)
+ return;
+
+ auto* center = [NSNotificationCenter defaultCenter];
+ auto* session = [AVAudioSession sharedInstance];
+ auto weakThis = m_private->weakPtrFactory.createWeakPtr();
+
+ ASSERT(m_private->notificationCallbacks.isEmpty());
+ m_private->notificationCallbacks = {
+ [center addObserverForName:AVAudioSessionRouteChangeNotification object:session queue:nil usingBlock:[weakThis] (NSNotification *note) {
+ callOnMainThread([weakThis, note = RetainPtr<NSNotification>(note)] {
+ if (!weakThis)
+ return;
+
+ AVAudioSessionRouteDescription *oldRoute = [[note userInfo] valueForKey:AVAudioSessionRouteChangePreviousRouteKey];
+ AVAudioSessionRouteDescription *newRoute = [[AVAudioSession sharedInstance] currentRoute];
+
+ bool inputsChanged = [oldRoute.inputs isEqualToArray:newRoute.inputs];
+ bool outputsChanged = [oldRoute.outputs isEqualToArray:newRoute.outputs];
+ if (!inputsChanged && !outputsChanged)
+ return;
+
+ // Protect against the observers set being mutated mid-notification:
+ auto observers = weakThis->m_private->observers;
+ for (auto& observer : observers) {
+ if (inputsChanged)
+ observer->currentAudioInputDeviceChanged();
+ if (outputsChanged)
+ observer->currentAudioOutputDeviceChanged();
+ }
+ });
+ }],
+ [center addObserverForName:AVAudioSessionMediaServicesWereLostNotification object:session queue:nil usingBlock:[weakThis] (NSNotification *) {
+ callOnMainThread([weakThis] {
+ if (!weakThis)
+ return;
+
+ // Protect against the observers set being mutated mid-notification:
+ auto observers = weakThis->m_private->observers;
+ for (auto& observer : observers)
+ observer->audioServicesLost();
+ });
+ }],
+ [center addObserverForName:AVAudioSessionMediaServicesWereResetNotification object:session queue:nil usingBlock:[weakThis] (NSNotification *) {
+ callOnMainThread([weakThis] {
+ if (!weakThis)
+ return;
+
+ // Protect against the observers set being mutated mid-notification:
+ auto observers = weakThis->m_private->observers;
+ for (auto& observer : observers)
+ observer->audioServicesReset();
+ });
+ }],
+ };
+}
+
+void AudioSession::removeObserver(Observer& observer)
+{
+ ASSERT(m_private->observers.contains(&observer));
+ m_private->observers.remove(&observer);
+
+ if (m_private->observers.size())
+ return;
+
+ for (auto& observer : m_private->notificationCallbacks)
+ [[NSNotificationCenter defaultCenter] removeObserver:observer.get()];
+}
+
+}
+
#endif // USE(AUDIO_SESSION) && PLATFORM(IOS)
Deleted: trunk/Source/WebCore/platform/audio/mac/AudioSessionMac.cpp (216023 => 216024)
--- trunk/Source/WebCore/platform/audio/mac/AudioSessionMac.cpp 2017-05-01 20:40:23 UTC (rev 216023)
+++ trunk/Source/WebCore/platform/audio/mac/AudioSessionMac.cpp 2017-05-01 21:06:39 UTC (rev 216024)
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2013 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 "AudioSession.h"
-
-#if USE(AUDIO_SESSION) && PLATFORM(MAC)
-
-#include "FloatConversion.h"
-#include "Logging.h"
-#include "NotImplemented.h"
-#include <CoreAudio/AudioHardware.h>
-#include <wtf/MainThread.h>
-
-namespace WebCore {
-
-static AudioDeviceID defaultDevice()
-{
- AudioDeviceID deviceID = kAudioDeviceUnknown;
- UInt32 infoSize = sizeof(deviceID);
-
- AudioObjectPropertyAddress defaultOutputDeviceAddress = {
- kAudioHardwarePropertyDefaultOutputDevice,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultOutputDeviceAddress, 0, 0, &infoSize, (void*)&deviceID);
- if (result)
- return 0; // error
- return deviceID;
-}
-
-class AudioSessionPrivate {
-public:
- AudioSessionPrivate(bool mutedState)
- : lastMutedState(mutedState) { }
- bool lastMutedState;
-};
-
-AudioSession::AudioSession()
- : m_private(std::make_unique<AudioSessionPrivate>(isMuted()))
-{
-}
-
-AudioSession::~AudioSession()
-{
-}
-
-AudioSession::CategoryType AudioSession::category() const
-{
- notImplemented();
- return None;
-}
-
-void AudioSession::setCategory(CategoryType)
-{
- notImplemented();
-}
-
-AudioSession::CategoryType AudioSession::categoryOverride() const
-{
- notImplemented();
- return None;
-}
-
-void AudioSession::setCategoryOverride(CategoryType)
-{
- notImplemented();
-}
-
-float AudioSession::sampleRate() const
-{
- Float64 nominalSampleRate;
- UInt32 nominalSampleRateSize = sizeof(Float64);
-
- AudioObjectPropertyAddress nominalSampleRateAddress = {
- kAudioDevicePropertyNominalSampleRate,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyData(defaultDevice(), &nominalSampleRateAddress, 0, 0, &nominalSampleRateSize, (void*)&nominalSampleRate);
- if (result)
- return 0;
-
- return narrowPrecisionToFloat(nominalSampleRate);
-}
-
-size_t AudioSession::bufferSize() const
-{
- UInt32 bufferSize;
- UInt32 bufferSizeSize = sizeof(bufferSize);
-
- AudioObjectPropertyAddress bufferSizeAddress = {
- kAudioDevicePropertyBufferFrameSize,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyData(defaultDevice(), &bufferSizeAddress, 0, 0, &bufferSizeSize, &bufferSize);
-
- if (result)
- return 0;
- return bufferSize;
-}
-
-size_t AudioSession::numberOfOutputChannels() const
-{
- notImplemented();
- return 0;
-}
-
-bool AudioSession::tryToSetActive(bool)
-{
- notImplemented();
- return true;
-}
-
-size_t AudioSession::preferredBufferSize() const
-{
- UInt32 bufferSize;
- UInt32 bufferSizeSize = sizeof(bufferSize);
-
- AudioObjectPropertyAddress preferredBufferSizeAddress = {
- kAudioDevicePropertyBufferFrameSizeRange,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyData(defaultDevice(), &preferredBufferSizeAddress, 0, 0, &bufferSizeSize, &bufferSize);
-
- if (result)
- return 0;
- return bufferSize;
-}
-
-void AudioSession::setPreferredBufferSize(size_t bufferSize)
-{
- AudioValueRange bufferSizeRange = {0, 0};
- UInt32 bufferSizeRangeSize = sizeof(AudioValueRange);
- AudioObjectPropertyAddress bufferSizeRangeAddress = {
- kAudioDevicePropertyBufferFrameSizeRange,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster
- };
- OSStatus result = AudioObjectGetPropertyData(defaultDevice(), &bufferSizeRangeAddress, 0, 0, &bufferSizeRangeSize, &bufferSizeRange);
- if (result)
- return;
-
- size_t minBufferSize = static_cast<size_t>(bufferSizeRange.mMinimum);
- size_t maxBufferSize = static_cast<size_t>(bufferSizeRange.mMaximum);
- UInt32 bufferSizeOut = std::min(maxBufferSize, std::max(minBufferSize, bufferSize));
-
- AudioObjectPropertyAddress preferredBufferSizeAddress = {
- kAudioDevicePropertyBufferFrameSize,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
-
- result = AudioObjectSetPropertyData(defaultDevice(), &preferredBufferSizeAddress, 0, 0, sizeof(bufferSizeOut), (void*)&bufferSizeOut);
-
-#if LOG_DISABLED
- UNUSED_PARAM(result);
-#else
- if (result)
- LOG(Media, "AudioSession::setPreferredBufferSize(%zu) - failed with error %d", bufferSize, static_cast<int>(result));
- else
- LOG(Media, "AudioSession::setPreferredBufferSize(%zu)", bufferSize);
-#endif
-}
-
-bool AudioSession::isMuted() const
-{
- UInt32 mute = 0;
- UInt32 muteSize = sizeof(mute);
- AudioObjectPropertyAddress muteAddress = { kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
- AudioObjectGetPropertyData(defaultDevice(), &muteAddress, 0, nullptr, &muteSize, &mute);
-
- switch (mute) {
- case 0:
- return false;
- case 1:
- return true;
- default:
- ASSERT_NOT_REACHED();
- return false;
- }
-}
-
-static OSStatus handleMutePropertyChange(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void* inClientData)
-{
- callOnMainThread([inClientData] {
- reinterpret_cast<AudioSession*>(inClientData)->handleMutedStateChange();
- });
- return noErr;
-}
-
-void AudioSession::handleMutedStateChange()
-{
- if (!m_private)
- return;
-
- bool isCurrentlyMuted = isMuted();
- if (m_private->lastMutedState == isCurrentlyMuted)
- return;
-
- for (auto* observer : m_observers)
- observer->hardwareMutedStateDidChange(this);
-
- m_private->lastMutedState = isCurrentlyMuted;
-}
-
-void AudioSession::addMutedStateObserver(MutedStateObserver* observer)
-{
- m_observers.add(observer);
-
- if (m_observers.size() > 1)
- return;
-
- AudioObjectPropertyAddress muteAddress = { kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
- AudioObjectAddPropertyListener(defaultDevice(), &muteAddress, handleMutePropertyChange, this);
-}
-
-void AudioSession::removeMutedStateObserver(MutedStateObserver* observer)
-{
- if (m_observers.size() == 1) {
- AudioObjectPropertyAddress muteAddress = { kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
- AudioObjectRemovePropertyListener(defaultDevice(), &muteAddress, handleMutePropertyChange, this);
- }
-
- m_observers.remove(observer);
-}
-
-}
-
-#endif // USE(AUDIO_SESSION) && PLATFORM(MAC)
Copied: trunk/Source/WebCore/platform/audio/mac/AudioSessionMac.mm (from rev 216023, trunk/Source/WebCore/platform/audio/mac/AudioSessionMac.cpp) (0 => 216024)
--- trunk/Source/WebCore/platform/audio/mac/AudioSessionMac.mm (rev 0)
+++ trunk/Source/WebCore/platform/audio/mac/AudioSessionMac.mm 2017-05-01 21:06:39 UTC (rev 216024)
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2013 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 "AudioSession.h"
+
+#if USE(AUDIO_SESSION) && PLATFORM(MAC)
+
+#include "FloatConversion.h"
+#include "Logging.h"
+#include "NotImplemented.h"
+#include <CoreAudio/AudioHardware.h>
+#include <wtf/BlockPtr.h>
+#include <wtf/HashSet.h>
+#include <wtf/MainThread.h>
+#include <wtf/WeakPtr.h>
+
+namespace WebCore {
+
+static AudioDeviceID defaultDevice()
+{
+ AudioDeviceID deviceID = kAudioDeviceUnknown;
+ UInt32 infoSize = sizeof(deviceID);
+
+ AudioObjectPropertyAddress defaultOutputDeviceAddress = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster };
+ OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultOutputDeviceAddress, 0, 0, &infoSize, (void*)&deviceID);
+ if (result)
+ return 0; // error
+ return deviceID;
+}
+
+class AudioSessionPrivate {
+public:
+ AudioSessionPrivate(AudioSession& session, bool mutedState)
+ : weakPtrFactory(&session)
+ , lastMutedState(mutedState) { }
+ WeakPtrFactory<AudioSession> weakPtrFactory;
+ bool lastMutedState;
+ HashSet<AudioSession::Observer*> observers;
+ BlockPtr<void(UInt32, const AudioObjectPropertyAddress*)> muteChangedBlock;
+ BlockPtr<void(UInt32, const AudioObjectPropertyAddress*)> audioInputDeviceChangedBlock;
+ BlockPtr<void(UInt32, const AudioObjectPropertyAddress*)> outputDeviceChangedBlock;
+ mutable std::optional<bool> outputDeviceSupportsLowPowerMode;
+};
+
+AudioSession::AudioSession()
+ : m_private(makeUniqueRef<AudioSessionPrivate>(*this, isMuted()))
+{
+ m_private->outputDeviceChangedBlock = (AudioObjectPropertyListenerBlock)[this, weakThis = m_private->weakPtrFactory.createWeakPtr()] (UInt32, const AudioObjectPropertyAddress*) {
+ if (!weakThis)
+ return;
+
+ m_private->outputDeviceSupportsLowPowerMode = std::nullopt;
+
+ for (auto* observer : m_private->observers)
+ observer->currentAudioOutputDeviceChanged();
+ };
+}
+
+AudioSession::~AudioSession()
+{
+ AudioObjectPropertyAddress outputDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+ AudioObjectRemovePropertyListenerBlock(defaultDevice(), &outputDeviceAddress, dispatch_get_main_queue(), m_private->outputDeviceChangedBlock.get());
+}
+
+AudioSession::CategoryType AudioSession::category() const
+{
+ notImplemented();
+ return None;
+}
+
+void AudioSession::setCategory(CategoryType)
+{
+ notImplemented();
+}
+
+AudioSession::CategoryType AudioSession::categoryOverride() const
+{
+ notImplemented();
+ return None;
+}
+
+void AudioSession::setCategoryOverride(CategoryType)
+{
+ notImplemented();
+}
+
+float AudioSession::sampleRate() const
+{
+ Float64 nominalSampleRate;
+ UInt32 nominalSampleRateSize = sizeof(Float64);
+
+ AudioObjectPropertyAddress nominalSampleRateAddress = {
+ kAudioDevicePropertyNominalSampleRate,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster };
+ OSStatus result = AudioObjectGetPropertyData(defaultDevice(), &nominalSampleRateAddress, 0, 0, &nominalSampleRateSize, (void*)&nominalSampleRate);
+ if (result)
+ return 0;
+
+ return narrowPrecisionToFloat(nominalSampleRate);
+}
+
+size_t AudioSession::bufferSize() const
+{
+ UInt32 bufferSize;
+ UInt32 bufferSizeSize = sizeof(bufferSize);
+
+ AudioObjectPropertyAddress bufferSizeAddress = {
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster };
+ OSStatus result = AudioObjectGetPropertyData(defaultDevice(), &bufferSizeAddress, 0, 0, &bufferSizeSize, &bufferSize);
+
+ if (result)
+ return 0;
+ return bufferSize;
+}
+
+size_t AudioSession::numberOfOutputChannels() const
+{
+ notImplemented();
+ return 0;
+}
+
+bool AudioSession::tryToSetActive(bool)
+{
+ notImplemented();
+ return true;
+}
+
+size_t AudioSession::preferredBufferSize() const
+{
+ UInt32 bufferSize;
+ UInt32 bufferSizeSize = sizeof(bufferSize);
+
+ AudioObjectPropertyAddress preferredBufferSizeAddress = {
+ kAudioDevicePropertyBufferFrameSizeRange,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster };
+ OSStatus result = AudioObjectGetPropertyData(defaultDevice(), &preferredBufferSizeAddress, 0, 0, &bufferSizeSize, &bufferSize);
+
+ if (result)
+ return 0;
+ return bufferSize;
+}
+
+void AudioSession::setPreferredBufferSize(size_t bufferSize)
+{
+ AudioValueRange bufferSizeRange = {0, 0};
+ UInt32 bufferSizeRangeSize = sizeof(AudioValueRange);
+ AudioObjectPropertyAddress bufferSizeRangeAddress = {
+ kAudioDevicePropertyBufferFrameSizeRange,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ OSStatus result = AudioObjectGetPropertyData(defaultDevice(), &bufferSizeRangeAddress, 0, 0, &bufferSizeRangeSize, &bufferSizeRange);
+ if (result)
+ return;
+
+ size_t minBufferSize = static_cast<size_t>(bufferSizeRange.mMinimum);
+ size_t maxBufferSize = static_cast<size_t>(bufferSizeRange.mMaximum);
+ UInt32 bufferSizeOut = std::min(maxBufferSize, std::max(minBufferSize, bufferSize));
+
+ AudioObjectPropertyAddress preferredBufferSizeAddress = {
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster };
+
+ result = AudioObjectSetPropertyData(defaultDevice(), &preferredBufferSizeAddress, 0, 0, sizeof(bufferSizeOut), (void*)&bufferSizeOut);
+
+#if LOG_DISABLED
+ UNUSED_PARAM(result);
+#else
+ if (result)
+ LOG(Media, "AudioSession::setPreferredBufferSize(%zu) - failed with error %d", bufferSize, static_cast<int>(result));
+ else
+ LOG(Media, "AudioSession::setPreferredBufferSize(%zu)", bufferSize);
+#endif
+}
+
+bool AudioSession::isMuted() const
+{
+ UInt32 mute = 0;
+ UInt32 muteSize = sizeof(mute);
+ AudioObjectPropertyAddress muteAddress = { kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
+ AudioObjectGetPropertyData(defaultDevice(), &muteAddress, 0, nullptr, &muteSize, &mute);
+
+ switch (mute) {
+ case 0:
+ return false;
+ case 1:
+ return true;
+ default:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+}
+
+static bool currentDeviceSupportsLowPowerBufferSize()
+{
+ AudioDeviceID deviceID = kAudioDeviceUnknown;
+ UInt32 descriptorSize = sizeof(deviceID);
+ AudioObjectPropertyAddress defaultOutputDeviceDescriptor = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster };
+
+ if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultOutputDeviceDescriptor, 0, 0, &descriptorSize, (void*)&deviceID))
+ return false;
+
+ UInt32 transportType = kAudioDeviceTransportTypeUnknown;
+ descriptorSize = sizeof(transportType);
+ AudioObjectPropertyAddress tranportTypeDescriptor = {
+ kAudioDevicePropertyTransportType,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster,
+ };
+
+ if (AudioObjectGetPropertyData(deviceID, &tranportTypeDescriptor, 0, 0, &descriptorSize, &transportType))
+ return false;
+
+ // Only allow low-power buffer size when using built-in output device, many external devices perform
+ // poorly with a large output buffer.
+ return kAudioDeviceTransportTypeBuiltIn == transportType;
+}
+
+bool AudioSession::outputDeviceSupportsLowPowerMode() const
+{
+ if (!m_private->outputDeviceSupportsLowPowerMode)
+ m_private->outputDeviceSupportsLowPowerMode = currentDeviceSupportsLowPowerBufferSize();
+ return m_private->outputDeviceSupportsLowPowerMode.value();
+}
+
+void AudioSession::addObserver(Observer& observer)
+{
+ ASSERT(!m_private->observers.contains(&observer));
+ m_private->observers.add(&observer);
+
+ if (m_private->observers.size() > 1)
+ return;
+
+ auto weakThis = m_private->weakPtrFactory.createWeakPtr();
+ m_private->muteChangedBlock = (AudioObjectPropertyListenerBlock)[this, weakThis] (UInt32, const AudioObjectPropertyAddress*) {
+ if (!weakThis)
+ return;
+
+ bool isCurrentlyMuted = isMuted();
+ if (m_private->lastMutedState == isCurrentlyMuted)
+ return;
+ m_private->lastMutedState = isCurrentlyMuted;
+
+ for (auto* observer : m_private->observers)
+ observer->hardwareMutedStateDidChange(this);
+ };
+
+ m_private->audioInputDeviceChangedBlock = (AudioObjectPropertyListenerBlock)[this, weakThis] (UInt32, const AudioObjectPropertyAddress*) {
+ if (!weakThis)
+ return;
+
+ for (auto* observer : m_private->observers)
+ observer->currentAudioInputDeviceChanged();
+ };
+
+ AudioObjectPropertyAddress muteAddress = { kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
+ AudioObjectAddPropertyListenerBlock(defaultDevice(), &muteAddress, dispatch_get_main_queue(), m_private->muteChangedBlock.get());
+
+ AudioObjectPropertyAddress inputDeviceAddress = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+ AudioObjectAddPropertyListenerBlock(defaultDevice(), &inputDeviceAddress, dispatch_get_main_queue(), m_private->audioInputDeviceChangedBlock.get());
+}
+
+void AudioSession::removeObserver(Observer& observer)
+{
+ ASSERT(m_private->observers.contains(&observer));
+ m_private->observers.remove(&observer);
+
+ if (!m_private->observers.isEmpty())
+ return;
+
+ AudioObjectPropertyAddress muteAddress = { kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
+ AudioObjectRemovePropertyListenerBlock(defaultDevice(), &muteAddress, dispatch_get_main_queue(), m_private->muteChangedBlock.get());
+
+ AudioObjectPropertyAddress inputDeviceAddress = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+ AudioObjectRemovePropertyListenerBlock(defaultDevice(), &inputDeviceAddress, dispatch_get_main_queue(), m_private->audioInputDeviceChangedBlock.get());
+}
+
+}
+
+#endif // USE(AUDIO_SESSION) && PLATFORM(MAC)