audio_output_dispatcher_impl.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/single_thread_task_runner.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 base::TimeDelta& close_delay)
23    : AudioOutputDispatcher(audio_manager,
24                            params,
25                            output_device_id),
26      idle_proxies_(0),
27      close_timer_(FROM_HERE,
28                   close_delay,
29                   this,
30                   &AudioOutputDispatcherImpl::CloseAllIdleStreams),
31      audio_log_(
32          audio_manager->CreateAudioLog(AudioLogFactory::AUDIO_OUTPUT_STREAM)),
33      audio_stream_id_(0) {}
34
35AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
36  DCHECK_EQ(idle_proxies_, 0u);
37  DCHECK(proxy_to_physical_map_.empty());
38  DCHECK(idle_streams_.empty());
39}
40
41bool AudioOutputDispatcherImpl::OpenStream() {
42  DCHECK(task_runner_->BelongsToCurrentThread());
43
44  // Ensure that there is at least one open stream.
45  if (idle_streams_.empty() && !CreateAndOpenStream())
46    return false;
47
48  ++idle_proxies_;
49  close_timer_.Reset();
50  return true;
51}
52
53bool AudioOutputDispatcherImpl::StartStream(
54    AudioOutputStream::AudioSourceCallback* callback,
55    AudioOutputProxy* stream_proxy) {
56  DCHECK(task_runner_->BelongsToCurrentThread());
57  DCHECK(proxy_to_physical_map_.find(stream_proxy) ==
58         proxy_to_physical_map_.end());
59
60  if (idle_streams_.empty() && !CreateAndOpenStream())
61    return false;
62
63  AudioOutputStream* physical_stream = idle_streams_.back();
64  idle_streams_.pop_back();
65
66  DCHECK_GT(idle_proxies_, 0u);
67  --idle_proxies_;
68
69  double volume = 0;
70  stream_proxy->GetVolume(&volume);
71  physical_stream->SetVolume(volume);
72  const int stream_id = audio_stream_ids_[physical_stream];
73  audio_log_->OnSetVolume(stream_id, volume);
74  physical_stream->Start(callback);
75  audio_log_->OnStarted(stream_id);
76  proxy_to_physical_map_[stream_proxy] = physical_stream;
77
78  close_timer_.Reset();
79  return true;
80}
81
82void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) {
83  DCHECK(task_runner_->BelongsToCurrentThread());
84
85  AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
86  DCHECK(it != proxy_to_physical_map_.end());
87  AudioOutputStream* physical_stream = it->second;
88  proxy_to_physical_map_.erase(it);
89
90  physical_stream->Stop();
91  audio_log_->OnStopped(audio_stream_ids_[physical_stream]);
92  ++idle_proxies_;
93  idle_streams_.push_back(physical_stream);
94
95  close_timer_.Reset();
96}
97
98void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy,
99                                                double volume) {
100  DCHECK(task_runner_->BelongsToCurrentThread());
101  AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
102  if (it != proxy_to_physical_map_.end()) {
103    AudioOutputStream* physical_stream = it->second;
104    physical_stream->SetVolume(volume);
105    audio_log_->OnSetVolume(audio_stream_ids_[physical_stream], volume);
106  }
107}
108
109void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) {
110  DCHECK(task_runner_->BelongsToCurrentThread());
111
112  DCHECK_GT(idle_proxies_, 0u);
113  --idle_proxies_;
114
115  // Leave at least a single stream running until the close timer fires to help
116  // cycle time when streams are opened and closed repeatedly.
117  CloseIdleStreams(std::max(idle_proxies_, static_cast<size_t>(1)));
118  close_timer_.Reset();
119}
120
121void AudioOutputDispatcherImpl::Shutdown() {
122  DCHECK(task_runner_->BelongsToCurrentThread());
123
124  // Close all idle streams immediately.  The |close_timer_| will handle
125  // invalidating any outstanding tasks upon its destruction.
126  CloseAllIdleStreams();
127}
128
129bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
130  DCHECK(task_runner_->BelongsToCurrentThread());
131  AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
132      params_, device_id_);
133  if (!stream)
134    return false;
135
136  if (!stream->Open()) {
137    stream->Close();
138    return false;
139  }
140
141  const int stream_id = audio_stream_id_++;
142  audio_stream_ids_[stream] = stream_id;
143  audio_log_->OnCreated(
144      stream_id, params_, device_id_);
145
146  idle_streams_.push_back(stream);
147  return true;
148}
149
150void AudioOutputDispatcherImpl::CloseAllIdleStreams() {
151  DCHECK(task_runner_->BelongsToCurrentThread());
152  CloseIdleStreams(0);
153}
154
155void AudioOutputDispatcherImpl::CloseIdleStreams(size_t keep_alive) {
156  DCHECK(task_runner_->BelongsToCurrentThread());
157  if (idle_streams_.size() <= keep_alive)
158    return;
159  for (size_t i = keep_alive; i < idle_streams_.size(); ++i) {
160    AudioOutputStream* stream = idle_streams_[i];
161    stream->Close();
162
163    AudioStreamIDMap::iterator it = audio_stream_ids_.find(stream);
164    DCHECK(it != audio_stream_ids_.end());
165    audio_log_->OnClosed(it->second);
166    audio_stream_ids_.erase(it);
167  }
168  idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end());
169}
170
171void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() {
172  DCHECK(task_runner_->BelongsToCurrentThread());
173  CloseAllIdleStreams();
174}
175
176void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() {
177  DCHECK(task_runner_->BelongsToCurrentThread());
178
179  // Should only be called when the dispatcher is used with fake streams which
180  // don't need to be shutdown or restarted.
181  CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE);
182}
183
184}  // namespace media
185