Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(113)

Side by Side Diff: media/audio/audio_output_dispatcher_impl.cc

Issue 27605002: Improve and simplify AudioOutputDispatcher. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase. Cleanup. Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/audio/audio_output_dispatcher_impl.h" 5 #include "media/audio/audio_output_dispatcher_impl.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
11 #include "base/message_loop/message_loop.h" 11 #include "base/message_loop/message_loop.h"
12 #include "base/time/time.h" 12 #include "base/time/time.h"
13 #include "media/audio/audio_io.h" 13 #include "media/audio/audio_io.h"
14 #include "media/audio/audio_output_proxy.h" 14 #include "media/audio/audio_output_proxy.h"
15 15
16 namespace media { 16 namespace media {
17 17
18 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( 18 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
19 AudioManager* audio_manager, 19 AudioManager* audio_manager,
20 const AudioParameters& params, 20 const AudioParameters& params,
21 const std::string& output_device_id, 21 const std::string& output_device_id,
22 const std::string& input_device_id, 22 const std::string& input_device_id,
23 const base::TimeDelta& close_delay) 23 const base::TimeDelta& close_delay)
24 : AudioOutputDispatcher(audio_manager, params, output_device_id, 24 : AudioOutputDispatcher(audio_manager,
25 input_device_id), 25 params,
26 pause_delay_(base::TimeDelta::FromMicroseconds( 26 output_device_id,
27 2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / 27 input_device_id),
28 static_cast<float>(params.sample_rate()))), 28 idle_proxies_(0),
29 paused_proxies_(0),
30 weak_this_(this),
31 close_timer_(FROM_HERE, 29 close_timer_(FROM_HERE,
32 close_delay, 30 close_delay,
33 this, 31 this,
34 &AudioOutputDispatcherImpl::ClosePendingStreams) { 32 &AudioOutputDispatcherImpl::CloseAllIdleStreams) {}
35 }
36 33
37 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { 34 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
35 DCHECK_EQ(idle_proxies_, 0u);
38 DCHECK(proxy_to_physical_map_.empty()); 36 DCHECK(proxy_to_physical_map_.empty());
39 DCHECK(idle_streams_.empty()); 37 DCHECK(idle_streams_.empty());
40 DCHECK(pausing_streams_.empty());
41 } 38 }
42 39
43 bool AudioOutputDispatcherImpl::OpenStream() { 40 bool AudioOutputDispatcherImpl::OpenStream() {
44 DCHECK(message_loop_->BelongsToCurrentThread()); 41 DCHECK(message_loop_->BelongsToCurrentThread());
45 42
46 paused_proxies_++; 43 // Ensure that there is at least one open stream.
44 if (idle_streams_.empty() && !CreateAndOpenStream())
45 return false;
47 46
48 // Ensure that there is at least one open stream. 47 ++idle_proxies_;
49 if (idle_streams_.empty() && !CreateAndOpenStream()) {
50 paused_proxies_--;
51 return false;
52 }
53
54 close_timer_.Reset(); 48 close_timer_.Reset();
55 return true; 49 return true;
56 } 50 }
57 51
58 bool AudioOutputDispatcherImpl::StartStream( 52 bool AudioOutputDispatcherImpl::StartStream(
59 AudioOutputStream::AudioSourceCallback* callback, 53 AudioOutputStream::AudioSourceCallback* callback,
60 AudioOutputProxy* stream_proxy) { 54 AudioOutputProxy* stream_proxy) {
61 DCHECK(message_loop_->BelongsToCurrentThread()); 55 DCHECK(message_loop_->BelongsToCurrentThread());
62 DCHECK(proxy_to_physical_map_.find(stream_proxy) == 56 DCHECK(proxy_to_physical_map_.find(stream_proxy) ==
63 proxy_to_physical_map_.end()); 57 proxy_to_physical_map_.end());
64 58
65 if (idle_streams_.empty() && !CreateAndOpenStream()) 59 if (idle_streams_.empty() && !CreateAndOpenStream())
66 return false; 60 return false;
67 61
68 AudioOutputStream* physical_stream = idle_streams_.back(); 62 AudioOutputStream* physical_stream = idle_streams_.back();
69 DCHECK(physical_stream);
70 idle_streams_.pop_back(); 63 idle_streams_.pop_back();
71 64
72 DCHECK_GT(paused_proxies_, 0u); 65 DCHECK_GT(idle_proxies_, 0u);
73 --paused_proxies_; 66 --idle_proxies_;
74
75 close_timer_.Reset();
76 67
77 double volume = 0; 68 double volume = 0;
78 stream_proxy->GetVolume(&volume); 69 stream_proxy->GetVolume(&volume);
79 physical_stream->SetVolume(volume); 70 physical_stream->SetVolume(volume);
80 physical_stream->Start(callback); 71 physical_stream->Start(callback);
81 proxy_to_physical_map_[stream_proxy] = physical_stream; 72 proxy_to_physical_map_[stream_proxy] = physical_stream;
73
74 close_timer_.Reset();
82 return true; 75 return true;
83 } 76 }
84 77
85 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { 78 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) {
86 DCHECK(message_loop_->BelongsToCurrentThread()); 79 DCHECK(message_loop_->BelongsToCurrentThread());
87 80
88 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 81 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
89 DCHECK(it != proxy_to_physical_map_.end()); 82 DCHECK(it != proxy_to_physical_map_.end());
90 AudioOutputStream* physical_stream = it->second; 83 AudioOutputStream* physical_stream = it->second;
91 proxy_to_physical_map_.erase(it); 84 proxy_to_physical_map_.erase(it);
92 85
93 physical_stream->Stop(); 86 physical_stream->Stop();
87 ++idle_proxies_;
88 idle_streams_.push_back(physical_stream);
scherkus (not reviewing) 2013/12/06 23:04:14 what's the difference between idle_streams_ and id
DaleCurtis 2013/12/07 00:33:33 idle_streams_ may be empty when idle_proxies_ > 0.
scherkus (not reviewing) 2013/12/09 18:53:12 I suppose I don't understand what an idle_proxy_ i
DaleCurtis 2013/12/09 19:16:36 An idle proxy is an AudioOutputProxy object in the
94 89
95 ++paused_proxies_; 90 close_timer_.Reset();
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 } 91 }
106 92
107 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, 93 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy,
108 double volume) { 94 double volume) {
109 DCHECK(message_loop_->BelongsToCurrentThread()); 95 DCHECK(message_loop_->BelongsToCurrentThread());
110 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 96 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
111 if (it != proxy_to_physical_map_.end()) { 97 if (it != proxy_to_physical_map_.end()) {
112 AudioOutputStream* physical_stream = it->second; 98 AudioOutputStream* physical_stream = it->second;
113 physical_stream->SetVolume(volume); 99 physical_stream->SetVolume(volume);
114 } 100 }
115 } 101 }
116 102
117 void AudioOutputDispatcherImpl::StopStreamTask() {
118 DCHECK(message_loop_->BelongsToCurrentThread());
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
129 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { 103 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) {
130 DCHECK(message_loop_->BelongsToCurrentThread()); 104 DCHECK(message_loop_->BelongsToCurrentThread());
131 105
132 while (!pausing_streams_.empty()) { 106 DCHECK_GT(idle_proxies_, 0u);
133 idle_streams_.push_back(pausing_streams_.back()); 107 --idle_proxies_;
134 pausing_streams_.pop_back();
135 }
136 108
137 DCHECK_GT(paused_proxies_, 0u); 109 // Leave at least a single stream running until the close timer fires to help
138 paused_proxies_--; 110 // cycle time when streams are opened and closed repeatedly.
139 111 CloseIdleStreams(idle_proxies_ + 1);
140 while (idle_streams_.size() > paused_proxies_) { 112 close_timer_.Reset();
141 idle_streams_.back()->Close();
142 idle_streams_.pop_back();
143 }
144 } 113 }
145 114
146 void AudioOutputDispatcherImpl::Shutdown() { 115 void AudioOutputDispatcherImpl::Shutdown() {
147 DCHECK(message_loop_->BelongsToCurrentThread()); 116 DCHECK(message_loop_->BelongsToCurrentThread());
148 117
149 // Cancel any pending tasks to close paused streams or create new ones. 118 // Close all idle streams immediately. The |close_timer_| will handle
150 weak_this_.InvalidateWeakPtrs(); 119 // invalidating any outstanding tasks upon its destruction.
151 120 CloseAllIdleStreams();
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 } 121 }
166 122
167 bool AudioOutputDispatcherImpl::CreateAndOpenStream() { 123 bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
168 DCHECK(message_loop_->BelongsToCurrentThread()); 124 DCHECK(message_loop_->BelongsToCurrentThread());
169 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( 125 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
170 params_, output_device_id_, input_device_id_); 126 params_, output_device_id_, input_device_id_);
171 if (!stream) 127 if (!stream)
172 return false; 128 return false;
173 129
174 if (!stream->Open()) { 130 if (!stream->Open()) {
175 stream->Close(); 131 stream->Close();
176 return false; 132 return false;
177 } 133 }
134
178 idle_streams_.push_back(stream); 135 idle_streams_.push_back(stream);
179 return true; 136 return true;
180 } 137 }
181 138
182 // This method is called by |close_timer_|. 139 void AudioOutputDispatcherImpl::CloseAllIdleStreams() {
183 void AudioOutputDispatcherImpl::ClosePendingStreams() {
184 DCHECK(message_loop_->BelongsToCurrentThread()); 140 DCHECK(message_loop_->BelongsToCurrentThread());
185 while (!idle_streams_.empty()) { 141 CloseIdleStreams(0);
186 idle_streams_.back()->Close(); 142 }
187 idle_streams_.pop_back(); 143
188 } 144 void AudioOutputDispatcherImpl::CloseIdleStreams(int keep_alive) {
145 DCHECK(message_loop_->BelongsToCurrentThread());
146 if (idle_streams_.size() <= static_cast<size_t>(keep_alive))
147 return;
148 for (size_t i = keep_alive; i < idle_streams_.size(); ++i)
149 idle_streams_[i]->Close();
150 idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end());
189 } 151 }
190 152
191 void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() { 153 void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() {
192 DCHECK(message_loop_->BelongsToCurrentThread()); 154 DCHECK(message_loop_->BelongsToCurrentThread());
193 while (!pausing_streams_.empty()) { 155 CloseAllIdleStreams();
194 idle_streams_.push_back(pausing_streams_.back());
195 pausing_streams_.pop_back();
196 }
197 ClosePendingStreams();
198 } 156 }
199 157
200 void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() { 158 void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() {
201 DCHECK(message_loop_->BelongsToCurrentThread()); 159 DCHECK(message_loop_->BelongsToCurrentThread());
202 160
203 // Should only be called when the dispatcher is used with fake streams which 161 // Should only be called when the dispatcher is used with fake streams which
204 // don't need to be shutdown or restarted. 162 // don't need to be shutdown or restarted.
205 CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE); 163 CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE);
206 } 164 }
207 165
208 } // namespace media 166 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698