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