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 "base/bind.h"
8#include "base/files/file_path.h"
9#include "base/logging.h"
10#include "base/mac/mac_logging.h"
11#include "base/mac/mac_util.h"
12#include "base/message_loop/message_loop.h"
13#include "base/pending_task.h"
14
15namespace media {
16
17// Property address to monitor for device changes.
18const AudioObjectPropertyAddress
19AudioDeviceListenerMac::kDeviceChangePropertyAddress = {
20  kAudioHardwarePropertyDefaultOutputDevice,
21  kAudioObjectPropertyScopeGlobal,
22  kAudioObjectPropertyElementMaster
23};
24
25// Callback from the system when the default device changes; this must be called
26// on the MessageLoop that created the AudioManager.
27// static
28OSStatus AudioDeviceListenerMac::OnDefaultDeviceChanged(
29    AudioObjectID object, UInt32 num_addresses,
30    const AudioObjectPropertyAddress addresses[], void* context) {
31  if (object != kAudioObjectSystemObject)
32    return noErr;
33
34  for (UInt32 i = 0; i < num_addresses; ++i) {
35    if (addresses[i].mSelector == kDeviceChangePropertyAddress.mSelector &&
36        addresses[i].mScope == kDeviceChangePropertyAddress.mScope &&
37        addresses[i].mElement == kDeviceChangePropertyAddress.mElement &&
38        context) {
39      static_cast<AudioDeviceListenerMac*>(context)->listener_cb_.Run();
40      break;
41    }
42  }
43
44  return noErr;
45}
46
47AudioDeviceListenerMac::AudioDeviceListenerMac(
48    const base::Closure& listener_cb) {
49  OSStatus result = AudioObjectAddPropertyListener(
50      kAudioObjectSystemObject, &kDeviceChangePropertyAddress,
51      &AudioDeviceListenerMac::OnDefaultDeviceChanged, this);
52
53  if (result != noErr) {
54    OSSTATUS_DLOG(ERROR, result)
55        << "AudioObjectAddPropertyListener() failed!";
56    return;
57  }
58
59  listener_cb_ = listener_cb;
60}
61
62AudioDeviceListenerMac::~AudioDeviceListenerMac() {
63  DCHECK(thread_checker_.CalledOnValidThread());
64  if (listener_cb_.is_null())
65    return;
66
67  // Since we're running on the same CFRunLoop, there can be no outstanding
68  // callbacks in flight.
69  OSStatus result = AudioObjectRemovePropertyListener(
70      kAudioObjectSystemObject, &kDeviceChangePropertyAddress,
71      &AudioDeviceListenerMac::OnDefaultDeviceChanged, this);
72  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
73      << "AudioObjectRemovePropertyListener() failed!";
74}
75
76}  // namespace media
77