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

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: Comments. Created 7 years, 2 months 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 // Helper function for use with std::for_each().
19 static void CloseStreamHelper(AudioOutputStream* stream) {
20 stream->Close();
21 }
22
18 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( 23 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
19 AudioManager* audio_manager, 24 AudioManager* audio_manager,
20 const AudioParameters& params, 25 const AudioParameters& params,
21 const std::string& output_device_id, 26 const std::string& output_device_id,
22 const std::string& input_device_id, 27 const std::string& input_device_id,
23 const base::TimeDelta& close_delay) 28 const base::TimeDelta& close_delay)
24 : AudioOutputDispatcher(audio_manager, params, output_device_id, 29 : AudioOutputDispatcher(audio_manager,
25 input_device_id), 30 params,
26 pause_delay_(base::TimeDelta::FromMicroseconds( 31 output_device_id,
27 2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / 32 input_device_id),
28 static_cast<float>(params.sample_rate()))), 33 idle_proxies_(0),
29 paused_proxies_(0),
30 weak_this_(this),
31 close_timer_(FROM_HERE, 34 close_timer_(FROM_HERE,
32 close_delay, 35 close_delay,
33 this, 36 this,
34 &AudioOutputDispatcherImpl::ClosePendingStreams) { 37 &AudioOutputDispatcherImpl::CloseIdleStreams) {}
35 }
36 38
37 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { 39 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
38 DCHECK(proxy_to_physical_map_.empty()); 40 DCHECK(proxy_to_physical_map_.empty());
39 DCHECK(idle_streams_.empty()); 41 DCHECK(idle_streams_.empty());
40 DCHECK(pausing_streams_.empty());
41 } 42 }
42 43
43 bool AudioOutputDispatcherImpl::OpenStream() { 44 bool AudioOutputDispatcherImpl::OpenStream() {
44 DCHECK(message_loop_->BelongsToCurrentThread()); 45 DCHECK(message_loop_->BelongsToCurrentThread());
45 46
46 paused_proxies_++; 47 idle_proxies_++;
miu 2013/10/21 23:44:36 Can we just increment idle_proxies_ after the if-s
DaleCurtis 2013/10/22 20:51:40 Done.
47 48
48 // Ensure that there is at least one open stream. 49 // Ensure that there is at least one open stream.
49 if (idle_streams_.empty() && !CreateAndOpenStream()) { 50 if (idle_streams_.empty() && !CreateAndOpenStream()) {
50 paused_proxies_--; 51 idle_proxies_--;
51 return false; 52 return false;
52 } 53 }
53 54
54 close_timer_.Reset(); 55 close_timer_.Reset();
55 return true; 56 return true;
56 } 57 }
57 58
58 bool AudioOutputDispatcherImpl::StartStream( 59 bool AudioOutputDispatcherImpl::StartStream(
59 AudioOutputStream::AudioSourceCallback* callback, 60 AudioOutputStream::AudioSourceCallback* callback,
60 AudioOutputProxy* stream_proxy) { 61 AudioOutputProxy* stream_proxy) {
61 DCHECK(message_loop_->BelongsToCurrentThread()); 62 DCHECK(message_loop_->BelongsToCurrentThread());
62 63
63 if (idle_streams_.empty() && !CreateAndOpenStream()) 64 if (idle_streams_.empty() && !CreateAndOpenStream())
64 return false; 65 return false;
65 66
66 AudioOutputStream* physical_stream = idle_streams_.back(); 67 AudioOutputStream* physical_stream = idle_streams_.back();
67 DCHECK(physical_stream);
68 idle_streams_.pop_back(); 68 idle_streams_.pop_back();
69 69
70 DCHECK_GT(paused_proxies_, 0u); 70 DCHECK_GT(idle_proxies_, 0u);
71 --paused_proxies_; 71 --idle_proxies_;
72
73 close_timer_.Reset();
74
75 // Schedule task to allocate streams for other proxies if we need to.
76 message_loop_->PostTask(FROM_HERE, base::Bind(
77 &AudioOutputDispatcherImpl::OpenTask, weak_this_.GetWeakPtr()));
78 72
79 double volume = 0; 73 double volume = 0;
80 stream_proxy->GetVolume(&volume); 74 stream_proxy->GetVolume(&volume);
81 physical_stream->SetVolume(volume); 75 physical_stream->SetVolume(volume);
82 physical_stream->Start(callback); 76 physical_stream->Start(callback);
83 proxy_to_physical_map_[stream_proxy] = physical_stream; 77 proxy_to_physical_map_[stream_proxy] = physical_stream;
78
79 close_timer_.Reset();
84 return true; 80 return true;
85 } 81 }
86 82
87 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { 83 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) {
88 DCHECK(message_loop_->BelongsToCurrentThread()); 84 DCHECK(message_loop_->BelongsToCurrentThread());
89 85
90 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 86 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
91 DCHECK(it != proxy_to_physical_map_.end()); 87 DCHECK(it != proxy_to_physical_map_.end());
92 AudioOutputStream* physical_stream = it->second; 88 AudioOutputStream* physical_stream = it->second;
93 proxy_to_physical_map_.erase(it); 89 proxy_to_physical_map_.erase(it);
94 90
95 physical_stream->Stop(); 91 physical_stream->Stop();
92 ++idle_proxies_;
93 idle_streams_.push_back(physical_stream);
96 94
97 ++paused_proxies_; 95 close_timer_.Reset();
98
99 pausing_streams_.push_front(physical_stream);
100
101 // Don't recycle stream until two buffers worth of time has elapsed.
102 message_loop_->PostDelayedTask(
103 FROM_HERE,
104 base::Bind(&AudioOutputDispatcherImpl::StopStreamTask,
105 weak_this_.GetWeakPtr()),
106 pause_delay_);
107 } 96 }
108 97
109 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, 98 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy,
110 double volume) { 99 double volume) {
111 DCHECK(message_loop_->BelongsToCurrentThread()); 100 DCHECK(message_loop_->BelongsToCurrentThread());
112 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); 101 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
113 if (it != proxy_to_physical_map_.end()) { 102 if (it != proxy_to_physical_map_.end()) {
114 AudioOutputStream* physical_stream = it->second; 103 AudioOutputStream* physical_stream = it->second;
115 physical_stream->SetVolume(volume); 104 physical_stream->SetVolume(volume);
116 } 105 }
117 } 106 }
118 107
119 void AudioOutputDispatcherImpl::StopStreamTask() {
120 DCHECK(message_loop_->BelongsToCurrentThread());
121
122 if (pausing_streams_.empty())
123 return;
124
125 AudioOutputStream* stream = pausing_streams_.back();
126 pausing_streams_.pop_back();
127 idle_streams_.push_back(stream);
128 close_timer_.Reset();
129 }
130
131 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { 108 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) {
132 DCHECK(message_loop_->BelongsToCurrentThread()); 109 DCHECK(message_loop_->BelongsToCurrentThread());
133 110
134 while (!pausing_streams_.empty()) { 111 DCHECK_GT(idle_proxies_, 0u);
135 idle_streams_.push_back(pausing_streams_.back()); 112 idle_proxies_--;
miu 2013/10/21 23:44:36 It doesn't look like idle_proxies_ is used for any
DaleCurtis 2013/10/22 20:51:40 idle_proxies_ will always be greater than idle_str
miu 2013/10/22 21:45:29 Okay, so it's just for debug builds. Just a thoug
DaleCurtis 2013/10/22 21:52:58 Hmm, a nice thought, but I think it makes things a
136 pausing_streams_.pop_back(); 113
114 // Leave a single stream running until the close timer fires to help cycle
115 // time when streams are opened and closed repeatedly.
116 if (idle_streams_.size() > 1) {
117 std::for_each(
miu 2013/10/21 23:44:36 My two cents on the use of std::for_each() here: I
DaleCurtis 2013/10/22 20:51:40 I played with this a few different ways and eventu
118 idle_streams_.begin() + 1, idle_streams_.end(), CloseStreamHelper);
119 idle_streams_.erase(idle_streams_.begin() + 1, idle_streams_.end());
137 } 120 }
138 121 close_timer_.Reset();
139 DCHECK_GT(paused_proxies_, 0u);
140 paused_proxies_--;
141
142 while (idle_streams_.size() > paused_proxies_) {
143 idle_streams_.back()->Close();
144 idle_streams_.pop_back();
145 }
146 } 122 }
147 123
148 void AudioOutputDispatcherImpl::Shutdown() { 124 void AudioOutputDispatcherImpl::Shutdown() {
149 DCHECK(message_loop_->BelongsToCurrentThread()); 125 DCHECK(message_loop_->BelongsToCurrentThread());
150 126
151 // Cancel any pending tasks to close paused streams or create new ones. 127 // Close all idle streams immediately. The |close_timer_| will handle
152 weak_this_.InvalidateWeakPtrs(); 128 // invalidating any outstanding tasks upon its destruction.
153 129 CloseIdleStreams();
154 // No AudioOutputProxy objects should hold a reference to us when we get
155 // to this stage.
156 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
157
158 AudioOutputStreamList::iterator it = idle_streams_.begin();
159 for (; it != idle_streams_.end(); ++it)
160 (*it)->Close();
161 idle_streams_.clear();
162
163 it = pausing_streams_.begin();
164 for (; it != pausing_streams_.end(); ++it)
165 (*it)->Close();
166 pausing_streams_.clear();
167 } 130 }
168 131
169 bool AudioOutputDispatcherImpl::CreateAndOpenStream() { 132 bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
170 DCHECK(message_loop_->BelongsToCurrentThread()); 133 DCHECK(message_loop_->BelongsToCurrentThread());
171 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( 134 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
172 params_, output_device_id_, input_device_id_); 135 params_, output_device_id_, input_device_id_);
173 if (!stream) 136 if (!stream)
174 return false; 137 return false;
175 138
176 if (!stream->Open()) { 139 if (!stream->Open()) {
177 stream->Close(); 140 stream->Close();
178 return false; 141 return false;
179 } 142 }
143
180 idle_streams_.push_back(stream); 144 idle_streams_.push_back(stream);
181 return true; 145 return true;
182 } 146 }
183 147
184 void AudioOutputDispatcherImpl::OpenTask() { 148 void AudioOutputDispatcherImpl::CloseIdleStreams() {
185 DCHECK(message_loop_->BelongsToCurrentThread()); 149 DCHECK(message_loop_->BelongsToCurrentThread());
186 // Make sure that we have at least one stream allocated if there 150 std::for_each(idle_streams_.begin(), idle_streams_.end(), CloseStreamHelper);
187 // are paused streams. 151 idle_streams_.clear();
188 if (paused_proxies_ > 0 && idle_streams_.empty() &&
189 pausing_streams_.empty()) {
190 CreateAndOpenStream();
191 }
192
193 close_timer_.Reset();
194 }
195
196 // This method is called by |close_timer_|.
197 void AudioOutputDispatcherImpl::ClosePendingStreams() {
198 DCHECK(message_loop_->BelongsToCurrentThread());
199 while (!idle_streams_.empty()) {
200 idle_streams_.back()->Close();
201 idle_streams_.pop_back();
202 }
203 } 152 }
204 153
205 } // namespace media 154 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698