audio_output_dispatcher_impl.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
1// Copyright (c) 2012 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/audio_output_dispatcher_impl.h"
6
7#include <algorithm>
8
9#include "base/bind.h"
10#include "base/compiler_specific.h"
11#include "base/message_loop/message_loop.h"
12#include "base/time/time.h"
13#include "media/audio/audio_io.h"
14#include "media/audio/audio_output_proxy.h"
15
16namespace media {
17
18AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
19    AudioManager* audio_manager,
20    const AudioParameters& params,
21    const std::string& output_device_id,
22    const std::string& input_device_id,
23    const base::TimeDelta& close_delay)
24    : AudioOutputDispatcher(audio_manager, params, output_device_id,
25          input_device_id),
26      pause_delay_(base::TimeDelta::FromMicroseconds(
27          2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond /
28          static_cast<float>(params.sample_rate()))),
29      paused_proxies_(0),
30      weak_this_(this),
31      close_timer_(FROM_HERE,
32                   close_delay,
33                   this,
34                   &AudioOutputDispatcherImpl::ClosePendingStreams) {
35}
36
37AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
38  DCHECK(proxy_to_physical_map_.empty());
39  DCHECK(idle_streams_.empty());
40  DCHECK(pausing_streams_.empty());
41}
42
43bool AudioOutputDispatcherImpl::OpenStream() {
44  DCHECK(message_loop_->BelongsToCurrentThread());
45
46  paused_proxies_++;
47
48  // Ensure that there is at least one open stream.
49  if (idle_streams_.empty() && !CreateAndOpenStream()) {
50    paused_proxies_--;
51    return false;
52  }
53
54  close_timer_.Reset();
55  return true;
56}
57
58bool AudioOutputDispatcherImpl::StartStream(
59    AudioOutputStream::AudioSourceCallback* callback,
60    AudioOutputProxy* stream_proxy) {
61  DCHECK(message_loop_->BelongsToCurrentThread());
62
63  if (idle_streams_.empty() && !CreateAndOpenStream())
64    return false;
65
66  AudioOutputStream* physical_stream = idle_streams_.back();
67  DCHECK(physical_stream);
68  idle_streams_.pop_back();
69
70  DCHECK_GT(paused_proxies_, 0u);
71  --paused_proxies_;
72
73  close_timer_.Reset();
74
75  double volume = 0;
76  stream_proxy->GetVolume(&volume);
77  physical_stream->SetVolume(volume);
78  physical_stream->Start(callback);
79  proxy_to_physical_map_[stream_proxy] = physical_stream;
80  return true;
81}
82
83void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) {
84  DCHECK(message_loop_->BelongsToCurrentThread());
85
86  AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
87  DCHECK(it != proxy_to_physical_map_.end());
88  AudioOutputStream* physical_stream = it->second;
89  proxy_to_physical_map_.erase(it);
90
91  physical_stream->Stop();
92
93  ++paused_proxies_;
94
95  pausing_streams_.push_front(physical_stream);
96
97  // Don't recycle stream until two buffers worth of time has elapsed.
98  message_loop_->PostDelayedTask(
99      FROM_HERE,
100      base::Bind(&AudioOutputDispatcherImpl::StopStreamTask,
101                 weak_this_.GetWeakPtr()),
102      pause_delay_);
103}
104
105void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy,
106                                                double volume) {
107  DCHECK(message_loop_->BelongsToCurrentThread());
108  AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
109  if (it != proxy_to_physical_map_.end()) {
110    AudioOutputStream* physical_stream = it->second;
111    physical_stream->SetVolume(volume);
112  }
113}
114
115void AudioOutputDispatcherImpl::StopStreamTask() {
116  DCHECK(message_loop_->BelongsToCurrentThread());
117
118  if (pausing_streams_.empty())
119    return;
120
121  AudioOutputStream* stream = pausing_streams_.back();
122  pausing_streams_.pop_back();
123  idle_streams_.push_back(stream);
124  close_timer_.Reset();
125}
126
127void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) {
128  DCHECK(message_loop_->BelongsToCurrentThread());
129
130  while (!pausing_streams_.empty()) {
131    idle_streams_.push_back(pausing_streams_.back());
132    pausing_streams_.pop_back();
133  }
134
135  DCHECK_GT(paused_proxies_, 0u);
136  paused_proxies_--;
137
138  while (idle_streams_.size() > paused_proxies_) {
139    idle_streams_.back()->Close();
140    idle_streams_.pop_back();
141  }
142}
143
144void AudioOutputDispatcherImpl::Shutdown() {
145  DCHECK(message_loop_->BelongsToCurrentThread());
146
147  // Cancel any pending tasks to close paused streams or create new ones.
148  weak_this_.InvalidateWeakPtrs();
149
150  // No AudioOutputProxy objects should hold a reference to us when we get
151  // to this stage.
152  DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
153
154  AudioOutputStreamList::iterator it = idle_streams_.begin();
155  for (; it != idle_streams_.end(); ++it)
156    (*it)->Close();
157  idle_streams_.clear();
158
159  it = pausing_streams_.begin();
160  for (; it != pausing_streams_.end(); ++it)
161    (*it)->Close();
162  pausing_streams_.clear();
163}
164
165bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
166  DCHECK(message_loop_->BelongsToCurrentThread());
167  AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
168      params_, output_device_id_, input_device_id_);
169  if (!stream)
170    return false;
171
172  if (!stream->Open()) {
173    stream->Close();
174    return false;
175  }
176  idle_streams_.push_back(stream);
177  return true;
178}
179
180// This method is called by |close_timer_|.
181void AudioOutputDispatcherImpl::ClosePendingStreams() {
182  DCHECK(message_loop_->BelongsToCurrentThread());
183  while (!idle_streams_.empty()) {
184    idle_streams_.back()->Close();
185    idle_streams_.pop_back();
186  }
187}
188
189}  // namespace media
190