1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "media/audio/mac/audio_device_listener_mac.h"
6 
7 #include <vector>
8 
9 #include "base/bind.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/mac/mac_logging.h"
13 #include "base/optional.h"
14 #include "base/single_thread_task_runner.h"
15 #include "media/audio/audio_manager.h"
16 #include "media/audio/mac/core_audio_util_mac.h"
17 #include "media/base/bind_to_current_loop.h"
18 
19 namespace media {
20 
21 const AudioObjectPropertyAddress
22     AudioDeviceListenerMac::kDefaultOutputDeviceChangePropertyAddress = {
23         kAudioHardwarePropertyDefaultOutputDevice,
24         kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
25 
26 const AudioObjectPropertyAddress
27     AudioDeviceListenerMac::kDefaultInputDeviceChangePropertyAddress = {
28         kAudioHardwarePropertyDefaultInputDevice,
29         kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
30 
31 const AudioObjectPropertyAddress
32     AudioDeviceListenerMac::kDevicesPropertyAddress = {
33         kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
34         kAudioObjectPropertyElementMaster};
35 
36 const AudioObjectPropertyAddress kPropertyOutputSourceChanged = {
37     kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput,
38     kAudioObjectPropertyElementMaster};
39 
40 const AudioObjectPropertyAddress kPropertyInputSourceChanged = {
41     kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeInput,
42     kAudioObjectPropertyElementMaster};
43 
44 class AudioDeviceListenerMac::PropertyListener {
45  public:
PropertyListener(AudioObjectID monitored_object,const AudioObjectPropertyAddress * property,base::RepeatingClosure callback)46   PropertyListener(AudioObjectID monitored_object,
47                    const AudioObjectPropertyAddress* property,
48                    base::RepeatingClosure callback)
49       : monitored_object_(monitored_object),
50         address_(property),
51         callback_(std::move(callback)) {}
52 
monitored_object() const53   AudioObjectID monitored_object() const { return monitored_object_; }
callback() const54   const base::RepeatingClosure& callback() const { return callback_; }
property() const55   const AudioObjectPropertyAddress* property() const { return address_; }
56 
57  private:
58   AudioObjectID monitored_object_;
59   const AudioObjectPropertyAddress* address_;
60   base::RepeatingClosure callback_;
61 };
62 
63 // Callback from the system when an event occurs; this must be called on the
64 // MessageLoop that created the AudioManager.
65 // static
OnEvent(AudioObjectID object,UInt32 num_addresses,const AudioObjectPropertyAddress addresses[],void * context)66 OSStatus AudioDeviceListenerMac::OnEvent(
67     AudioObjectID object,
68     UInt32 num_addresses,
69     const AudioObjectPropertyAddress addresses[],
70     void* context) {
71   PropertyListener* listener = static_cast<PropertyListener*>(context);
72   if (object != listener->monitored_object())
73     return noErr;
74 
75   for (UInt32 i = 0; i < num_addresses; ++i) {
76     if (addresses[i].mSelector == listener->property()->mSelector &&
77         addresses[i].mScope == listener->property()->mScope &&
78         addresses[i].mElement == listener->property()->mElement && context) {
79       listener->callback().Run();
80       break;
81     }
82   }
83 
84   return noErr;
85 }
86 
AudioDeviceListenerMac(const base::RepeatingClosure listener_cb,bool monitor_default_input,bool monitor_addition_removal,bool monitor_sources)87 AudioDeviceListenerMac::AudioDeviceListenerMac(
88     const base::RepeatingClosure listener_cb,
89     bool monitor_default_input,
90     bool monitor_addition_removal,
91     bool monitor_sources)
92     : weak_factory_(this) {
93   listener_cb_ = std::move(listener_cb);
94 
95   // Changes to the default output device are always monitored.
96   default_output_listener_ = std::make_unique<PropertyListener>(
97       kAudioObjectSystemObject, &kDefaultOutputDeviceChangePropertyAddress,
98       listener_cb_);
99   if (!AddPropertyListener(default_output_listener_.get()))
100     default_output_listener_.reset();
101 
102   if (monitor_default_input) {
103     default_input_listener_ = std::make_unique<PropertyListener>(
104         kAudioObjectSystemObject, &kDefaultInputDeviceChangePropertyAddress,
105         listener_cb_);
106     if (!AddPropertyListener(default_input_listener_.get()))
107       default_input_listener_.reset();
108   }
109   if (monitor_addition_removal) {
110     addition_removal_listener_ = std::make_unique<PropertyListener>(
111         kAudioObjectSystemObject, &kDevicesPropertyAddress,
112         monitor_sources ? media::BindToCurrentLoop(base::BindRepeating(
113                               &AudioDeviceListenerMac::OnDevicesAddedOrRemoved,
114                               weak_factory_.GetWeakPtr()))
115                         : listener_cb_);
116     if (!AddPropertyListener(addition_removal_listener_.get()))
117       addition_removal_listener_.reset();
118 
119     // Sources can be monitored only if addition/removal is monitored.
120     if (monitor_sources)
121       UpdateSourceListeners();
122   }
123 }
124 
~AudioDeviceListenerMac()125 AudioDeviceListenerMac::~AudioDeviceListenerMac() {
126   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
127 
128   // Since we're running on the same CFRunLoop, there can be no outstanding
129   // callbacks in flight.
130   if (default_output_listener_)
131     RemovePropertyListener(default_output_listener_.get());
132   if (default_input_listener_)
133     RemovePropertyListener(default_input_listener_.get());
134   if (addition_removal_listener_)
135     RemovePropertyListener(addition_removal_listener_.get());
136   for (const auto& entry : source_listeners_)
137     RemovePropertyListener(entry.second.get());
138 }
139 
AddPropertyListener(AudioDeviceListenerMac::PropertyListener * property_listener)140 bool AudioDeviceListenerMac::AddPropertyListener(
141     AudioDeviceListenerMac::PropertyListener* property_listener) {
142   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
143   OSStatus result = AudioObjectAddPropertyListener(
144       property_listener->monitored_object(), property_listener->property(),
145       &AudioDeviceListenerMac::OnEvent, property_listener);
146   bool success = result == noErr;
147   if (!success)
148     OSSTATUS_DLOG(ERROR, result) << "AudioObjectAddPropertyListener() failed!";
149 
150   return success;
151 }
152 
RemovePropertyListener(AudioDeviceListenerMac::PropertyListener * property_listener)153 void AudioDeviceListenerMac::RemovePropertyListener(
154     AudioDeviceListenerMac::PropertyListener* property_listener) {
155   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
156   OSStatus result = AudioObjectRemovePropertyListener(
157       property_listener->monitored_object(), property_listener->property(),
158       &AudioDeviceListenerMac::OnEvent, property_listener);
159   OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
160       << "AudioObjectRemovePropertyListener() failed!";
161 }
162 
OnDevicesAddedOrRemoved()163 void AudioDeviceListenerMac::OnDevicesAddedOrRemoved() {
164   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
165   UpdateSourceListeners();
166   listener_cb_.Run();
167 }
168 
UpdateSourceListeners()169 void AudioDeviceListenerMac::UpdateSourceListeners() {
170   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
171   std::vector<AudioObjectID> device_ids =
172       core_audio_mac::GetAllAudioDeviceIDs();
173   for (bool is_input : {true, false}) {
174     for (auto device_id : device_ids) {
175       const AudioObjectPropertyAddress* property_address =
176           is_input ? &kPropertyInputSourceChanged
177                    : &kPropertyOutputSourceChanged;
178       SourceListenerKey key = {device_id, is_input};
179       auto it_key = source_listeners_.find(key);
180       bool is_monitored = it_key != source_listeners_.end();
181       if (core_audio_mac::GetDeviceSource(device_id, is_input)) {
182         if (!is_monitored) {
183           // Start monitoring if the device has source and is not currently
184           // being monitored.
185           std::unique_ptr<PropertyListener> source_listener =
186               std::make_unique<PropertyListener>(device_id, property_address,
187                                                  listener_cb_);
188           if (AddPropertyListener(source_listener.get())) {
189             source_listeners_[key] = std::move(source_listener);
190           } else {
191             source_listener.reset();
192           }
193         }
194       } else if (is_monitored) {
195         // Stop monitoring if the device has no source but is currently being
196         // monitored.
197         RemovePropertyListener(it_key->second.get());
198         source_listeners_.erase(it_key);
199       }
200     }
201   }
202 }
203 
204 }  // namespace media
205