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