OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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_controller.h" | 5 #include "media/audio/audio_output_controller.h" |
6 | 6 |
7 #include "base/message_loop.h" | |
8 | |
9 // The following parameters limit the request buffer and packet size from the | 7 // The following parameters limit the request buffer and packet size from the |
10 // renderer to avoid renderer from requesting too much memory. | 8 // renderer to avoid renderer from requesting too much memory. |
11 static const uint32 kMegabytes = 1024 * 1024; | 9 static const uint32 kMegabytes = 1024 * 1024; |
12 static const uint32 kMaxHardwareBufferSize = 2 * kMegabytes; | 10 static const uint32 kMaxHardwareBufferSize = 2 * kMegabytes; |
13 static const int kMaxChannels = 32; | 11 static const int kMaxChannels = 32; |
14 static const int kMaxBitsPerSample = 64; | 12 static const int kMaxBitsPerSample = 64; |
15 static const int kMaxSampleRate = 192000; | 13 static const int kMaxSampleRate = 192000; |
16 | 14 |
17 // Return true if the parameters for creating an audio stream is valid. | 15 // Return true if the parameters for creating an audio stream is valid. |
18 // Return false otherwise. | 16 // Return false otherwise. |
(...skipping 11 matching lines...) Expand all Loading... |
30 } | 28 } |
31 return true; | 29 return true; |
32 } | 30 } |
33 | 31 |
34 namespace media { | 32 namespace media { |
35 | 33 |
36 AudioOutputController::AudioOutputController(EventHandler* handler, | 34 AudioOutputController::AudioOutputController(EventHandler* handler, |
37 uint32 capacity, | 35 uint32 capacity, |
38 SyncReader* sync_reader) | 36 SyncReader* sync_reader) |
39 : handler_(handler), | 37 : handler_(handler), |
40 stream_(NULL), | |
41 volume_(1.0), | 38 volume_(1.0), |
42 state_(kEmpty), | 39 state_(kEmpty), |
43 hardware_pending_bytes_(0), | 40 hardware_pending_bytes_(0), |
44 buffer_capacity_(capacity), | 41 buffer_capacity_(capacity), |
45 sync_reader_(sync_reader) { | 42 sync_reader_(sync_reader), |
| 43 thread_("AudioOutputControllerThread") { |
46 } | 44 } |
47 | 45 |
48 AudioOutputController::~AudioOutputController() { | 46 AudioOutputController::~AudioOutputController() { |
49 DCHECK(kClosed == state_); | 47 DCHECK(kClosed == state_); |
50 } | 48 } |
51 | 49 |
52 // static | 50 // static |
53 scoped_refptr<AudioOutputController> AudioOutputController::Create( | 51 scoped_refptr<AudioOutputController> AudioOutputController::Create( |
54 EventHandler* event_handler, | 52 EventHandler* event_handler, |
55 AudioManager::Format format, | 53 AudioManager::Format format, |
56 int channels, | 54 int channels, |
57 int sample_rate, | 55 int sample_rate, |
58 int bits_per_sample, | 56 int bits_per_sample, |
59 uint32 hardware_buffer_size, | 57 uint32 hardware_buffer_size, |
60 uint32 buffer_capacity) { | 58 uint32 buffer_capacity) { |
61 | 59 |
62 if (!CheckParameters(channels, sample_rate, bits_per_sample, | 60 if (!CheckParameters(channels, sample_rate, bits_per_sample, |
63 hardware_buffer_size)) | 61 hardware_buffer_size)) |
64 return NULL; | 62 return NULL; |
65 | 63 |
66 // Starts the audio controller thread. | 64 // Starts the audio controller thread. |
67 scoped_refptr<AudioOutputController> controller = new AudioOutputController( | 65 scoped_refptr<AudioOutputController> controller = new AudioOutputController( |
68 event_handler, buffer_capacity, NULL); | 66 event_handler, buffer_capacity, NULL); |
69 | 67 |
70 controller->message_loop_ = | 68 // Start the audio controller thread and post a task to create the |
71 AudioManager::GetAudioManager()->GetMessageLoop(); | 69 // audio stream. |
72 controller->message_loop_->PostTask( | 70 controller->thread_.Start(); |
| 71 controller->thread_.message_loop()->PostTask( |
73 FROM_HERE, | 72 FROM_HERE, |
74 NewRunnableMethod(controller.get(), &AudioOutputController::DoCreate, | 73 NewRunnableMethod(controller.get(), &AudioOutputController::DoCreate, |
75 format, channels, sample_rate, bits_per_sample, | 74 format, channels, sample_rate, bits_per_sample, |
76 hardware_buffer_size)); | 75 hardware_buffer_size)); |
77 return controller; | 76 return controller; |
78 } | 77 } |
79 | 78 |
80 // static | 79 // static |
81 scoped_refptr<AudioOutputController> AudioOutputController::CreateLowLatency( | 80 scoped_refptr<AudioOutputController> AudioOutputController::CreateLowLatency( |
82 EventHandler* event_handler, | 81 EventHandler* event_handler, |
83 AudioManager::Format format, | 82 AudioManager::Format format, |
84 int channels, | 83 int channels, |
85 int sample_rate, | 84 int sample_rate, |
86 int bits_per_sample, | 85 int bits_per_sample, |
87 uint32 hardware_buffer_size, | 86 uint32 hardware_buffer_size, |
88 SyncReader* sync_reader) { | 87 SyncReader* sync_reader) { |
89 | 88 |
90 DCHECK(sync_reader); | 89 DCHECK(sync_reader); |
91 | 90 |
92 if (!CheckParameters(channels, sample_rate, bits_per_sample, | 91 if (!CheckParameters(channels, sample_rate, bits_per_sample, |
93 hardware_buffer_size)) | 92 hardware_buffer_size)) |
94 return NULL; | 93 return NULL; |
95 | 94 |
96 // Starts the audio controller thread. | 95 // Starts the audio controller thread. |
97 scoped_refptr<AudioOutputController> controller = new AudioOutputController( | 96 scoped_refptr<AudioOutputController> controller = new AudioOutputController( |
98 event_handler, 0, sync_reader); | 97 event_handler, 0, sync_reader); |
99 | 98 |
100 controller->message_loop_ = | 99 // Start the audio controller thread and post a task to create the |
101 AudioManager::GetAudioManager()->GetMessageLoop(); | 100 // audio stream. |
102 controller->message_loop_->PostTask( | 101 controller->thread_.Start(); |
| 102 controller->thread_.message_loop()->PostTask( |
103 FROM_HERE, | 103 FROM_HERE, |
104 NewRunnableMethod(controller.get(), &AudioOutputController::DoCreate, | 104 NewRunnableMethod(controller.get(), &AudioOutputController::DoCreate, |
105 format, channels, sample_rate, bits_per_sample, | 105 format, channels, sample_rate, bits_per_sample, |
106 hardware_buffer_size)); | 106 hardware_buffer_size)); |
107 return controller; | 107 return controller; |
108 } | 108 } |
109 | 109 |
110 void AudioOutputController::Play() { | 110 void AudioOutputController::Play() { |
111 DCHECK(message_loop_); | 111 DCHECK(thread_.IsRunning()); |
112 message_loop_->PostTask( | 112 thread_.message_loop()->PostTask( |
113 FROM_HERE, | 113 FROM_HERE, |
114 NewRunnableMethod(this, &AudioOutputController::DoPlay)); | 114 NewRunnableMethod(this, &AudioOutputController::DoPlay)); |
115 } | 115 } |
116 | 116 |
117 void AudioOutputController::Pause() { | 117 void AudioOutputController::Pause() { |
118 DCHECK(message_loop_); | 118 DCHECK(thread_.IsRunning()); |
119 message_loop_->PostTask( | 119 thread_.message_loop()->PostTask( |
120 FROM_HERE, | 120 FROM_HERE, |
121 NewRunnableMethod(this, &AudioOutputController::DoPause)); | 121 NewRunnableMethod(this, &AudioOutputController::DoPause)); |
122 } | 122 } |
123 | 123 |
124 void AudioOutputController::Flush() { | 124 void AudioOutputController::Flush() { |
125 DCHECK(message_loop_); | 125 DCHECK(thread_.IsRunning()); |
126 message_loop_->PostTask( | 126 thread_.message_loop()->PostTask( |
127 FROM_HERE, | 127 FROM_HERE, |
128 NewRunnableMethod(this, &AudioOutputController::DoFlush)); | 128 NewRunnableMethod(this, &AudioOutputController::DoFlush)); |
129 } | 129 } |
130 | 130 |
131 void AudioOutputController::Close() { | 131 void AudioOutputController::Close() { |
132 { | 132 if (!thread_.IsRunning()) { |
133 AutoLock auto_lock(lock_); | 133 // If the thread is not running make sure we are stopped. |
134 // Don't do anything if the stream is already closed. | 134 DCHECK_EQ(kClosed, state_); |
135 if (state_ == kClosed) | 135 return; |
136 return; | |
137 state_ = kClosed; | |
138 } | 136 } |
139 | 137 |
140 message_loop_->PostTask( | 138 // Wait for all tasks to complete on the audio thread. |
| 139 thread_.message_loop()->PostTask( |
141 FROM_HERE, | 140 FROM_HERE, |
142 NewRunnableMethod(this, &AudioOutputController::DoClose)); | 141 NewRunnableMethod(this, &AudioOutputController::DoClose)); |
| 142 thread_.Stop(); |
143 } | 143 } |
144 | 144 |
145 void AudioOutputController::SetVolume(double volume) { | 145 void AudioOutputController::SetVolume(double volume) { |
146 DCHECK(message_loop_); | 146 DCHECK(thread_.IsRunning()); |
147 message_loop_->PostTask( | 147 thread_.message_loop()->PostTask( |
148 FROM_HERE, | 148 FROM_HERE, |
149 NewRunnableMethod(this, &AudioOutputController::DoSetVolume, volume)); | 149 NewRunnableMethod(this, &AudioOutputController::DoSetVolume, volume)); |
150 } | 150 } |
151 | 151 |
152 void AudioOutputController::EnqueueData(const uint8* data, uint32 size) { | 152 void AudioOutputController::EnqueueData(const uint8* data, uint32 size) { |
153 // Write data to the push source and ask for more data if needed. | 153 // Write data to the push source and ask for more data if needed. |
154 AutoLock auto_lock(lock_); | 154 AutoLock auto_lock(lock_); |
155 push_source_.Write(data, size); | 155 push_source_.Write(data, size); |
156 SubmitOnMoreData_Locked(); | 156 SubmitOnMoreData_Locked(); |
157 } | 157 } |
158 | 158 |
159 void AudioOutputController::DoCreate(AudioManager::Format format, int channels, | 159 void AudioOutputController::DoCreate(AudioManager::Format format, int channels, |
160 int sample_rate, int bits_per_sample, | 160 int sample_rate, int bits_per_sample, |
161 uint32 hardware_buffer_size) { | 161 uint32 hardware_buffer_size) { |
162 DCHECK_EQ(message_loop_, MessageLoop::current()); | 162 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); |
163 | 163 DCHECK_EQ(kEmpty, state_); |
164 AutoLock auto_lock(lock_); | |
165 | |
166 // Close() can be called before DoCreate() is executed. | |
167 if (state_ == kClosed) | |
168 return; | |
169 DCHECK(state_ == kEmpty); | |
170 | 164 |
171 // Create the stream in the first place. | 165 // Create the stream in the first place. |
172 stream_ = AudioManager::GetAudioManager()->MakeAudioOutputStream( | 166 stream_ = AudioManager::GetAudioManager()->MakeAudioOutputStream( |
173 format, channels, sample_rate, bits_per_sample); | 167 format, channels, sample_rate, bits_per_sample); |
174 | 168 |
175 if (!stream_) { | 169 if (!stream_) { |
176 // TODO(hclam): Define error types. | 170 // TODO(hclam): Define error types. |
177 handler_->OnError(this, 0); | 171 handler_->OnError(this, 0); |
178 return; | 172 return; |
179 } | 173 } |
(...skipping 10 matching lines...) Expand all Loading... |
190 stream_->SetVolume(volume_); | 184 stream_->SetVolume(volume_); |
191 | 185 |
192 // Finally set the state to kCreated. | 186 // Finally set the state to kCreated. |
193 state_ = kCreated; | 187 state_ = kCreated; |
194 | 188 |
195 // And then report we have been created. | 189 // And then report we have been created. |
196 handler_->OnCreated(this); | 190 handler_->OnCreated(this); |
197 | 191 |
198 // If in normal latency mode then start buffering. | 192 // If in normal latency mode then start buffering. |
199 if (!LowLatencyMode()) { | 193 if (!LowLatencyMode()) { |
| 194 AutoLock auto_lock(lock_); |
200 SubmitOnMoreData_Locked(); | 195 SubmitOnMoreData_Locked(); |
201 } | 196 } |
202 } | 197 } |
203 | 198 |
204 void AudioOutputController::DoPlay() { | 199 void AudioOutputController::DoPlay() { |
205 DCHECK_EQ(message_loop_, MessageLoop::current()); | 200 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); |
| 201 |
| 202 // We can start from created or paused state. |
| 203 if (state_ != kCreated && state_ != kPaused) |
| 204 return; |
206 | 205 |
207 State old_state; | 206 State old_state; |
208 // Update the |state_| to kPlaying. | 207 // Update the |state_| to kPlaying. |
209 { | 208 { |
210 AutoLock auto_lock(lock_); | 209 AutoLock auto_lock(lock_); |
211 // We can start from created or paused state. | |
212 if (state_ != kCreated && state_ != kPaused) | |
213 return; | |
214 old_state = state_; | 210 old_state = state_; |
215 state_ = kPlaying; | 211 state_ = kPlaying; |
216 } | 212 } |
217 | 213 |
218 // We start the AudioOutputStream lazily. | 214 // We start the AudioOutputStream lazily. |
219 stream_->Start(this); | 215 stream_->Start(this); |
220 | 216 |
221 // Tell the event handler that we are now playing. | 217 // Tell the event handler that we are now playing. |
222 handler_->OnPlaying(this); | 218 handler_->OnPlaying(this); |
223 } | 219 } |
224 | 220 |
225 void AudioOutputController::DoPause() { | 221 void AudioOutputController::DoPause() { |
226 DCHECK_EQ(message_loop_, MessageLoop::current()); | 222 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); |
| 223 |
| 224 // We can pause from started state. |
| 225 if (state_ != kPlaying) |
| 226 return; |
227 | 227 |
228 // Sets the |state_| to kPaused so we don't draw more audio data. | 228 // Sets the |state_| to kPaused so we don't draw more audio data. |
229 { | 229 { |
230 AutoLock auto_lock(lock_); | 230 AutoLock auto_lock(lock_); |
231 // We can pause from started state. | |
232 if (state_ != kPlaying) | |
233 return; | |
234 state_ = kPaused; | 231 state_ = kPaused; |
235 } | 232 } |
236 | 233 |
237 // Then we stop the audio device. This is not the perfect solution because | 234 // Then we stop the audio device. This is not the perfect solution because |
238 // it discards all the internal buffer in the audio device. | 235 // it discards all the internal buffer in the audio device. |
239 // TODO(hclam): Actually pause the audio device. | 236 // TODO(hclam): Actually pause the audio device. |
240 stream_->Stop(); | 237 stream_->Stop(); |
241 | 238 |
242 handler_->OnPaused(this); | 239 handler_->OnPaused(this); |
243 } | 240 } |
244 | 241 |
245 void AudioOutputController::DoFlush() { | 242 void AudioOutputController::DoFlush() { |
246 DCHECK_EQ(message_loop_, MessageLoop::current()); | 243 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); |
| 244 |
| 245 if (state_ != kPaused) |
| 246 return; |
247 | 247 |
248 // TODO(hclam): Actually flush the audio device. | 248 // TODO(hclam): Actually flush the audio device. |
249 | 249 |
250 // If we are in the regular latency mode then flush the push source. | 250 // If we are in the regular latency mode then flush the push source. |
251 if (!sync_reader_) { | 251 if (!sync_reader_) { |
252 AutoLock auto_lock(lock_); | 252 AutoLock auto_lock(lock_); |
253 if (state_ != kPaused) | |
254 return; | |
255 push_source_.ClearAll(); | 253 push_source_.ClearAll(); |
256 } | 254 } |
257 } | 255 } |
258 | 256 |
259 void AudioOutputController::DoClose() { | 257 void AudioOutputController::DoClose() { |
260 DCHECK_EQ(message_loop_, MessageLoop::current()); | 258 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); |
261 DCHECK_EQ(kClosed, state_); | 259 DCHECK_NE(kClosed, state_); |
| 260 |
262 // |stream_| can be null if creating the device failed in DoCreate(). | 261 // |stream_| can be null if creating the device failed in DoCreate(). |
263 if (stream_) { | 262 if (stream_) { |
264 stream_->Stop(); | 263 stream_->Stop(); |
265 stream_->Close(); | 264 stream_->Close(); |
266 // After stream is closed it is destroyed, so don't keep a reference to it. | 265 // After stream is closed it is destroyed, so don't keep a reference to it. |
267 stream_ = NULL; | 266 stream_ = NULL; |
268 } | 267 } |
| 268 |
| 269 // Update the current state. Since the stream is closed at this point |
| 270 // there's no other threads reading |state_| so we don't need to lock. |
| 271 state_ = kClosed; |
269 } | 272 } |
270 | 273 |
271 void AudioOutputController::DoSetVolume(double volume) { | 274 void AudioOutputController::DoSetVolume(double volume) { |
272 DCHECK_EQ(message_loop_, MessageLoop::current()); | 275 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); |
273 | 276 |
274 // Saves the volume to a member first. We may not be able to set the volume | 277 // Saves the volume to a member first. We may not be able to set the volume |
275 // right away but when the stream is created we'll set the volume. | 278 // right away but when the stream is created we'll set the volume. |
276 volume_ = volume; | 279 volume_ = volume; |
277 | 280 |
278 { | 281 if (state_ != kPlaying && state_ != kPaused && state_ != kCreated) |
279 AutoLock auto_lock(lock_); | 282 return; |
280 if (state_ != kPlaying && state_ != kPaused && state_ != kCreated) | |
281 return; | |
282 } | |
283 | 283 |
284 stream_->SetVolume(volume_); | 284 stream_->SetVolume(volume_); |
285 } | 285 } |
286 | 286 |
287 void AudioOutputController::DoReportError(int code) { | 287 void AudioOutputController::DoReportError(int code) { |
288 DCHECK_EQ(message_loop_, MessageLoop::current()); | 288 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); |
289 handler_->OnError(this, code); | 289 handler_->OnError(this, code); |
290 } | 290 } |
291 | 291 |
292 uint32 AudioOutputController::OnMoreData(AudioOutputStream* stream, | 292 uint32 AudioOutputController::OnMoreData(AudioOutputStream* stream, |
293 void* dest, | 293 void* dest, |
294 uint32 max_size, | 294 uint32 max_size, |
295 uint32 pending_bytes) { | 295 uint32 pending_bytes) { |
296 // If regular latency mode is used. | 296 // If regular latency mode is used. |
297 if (!sync_reader_) { | 297 if (!sync_reader_) { |
298 AutoLock auto_lock(lock_); | 298 AutoLock auto_lock(lock_); |
(...skipping 26 matching lines...) Expand all Loading... |
325 if (LowLatencyMode()) { | 325 if (LowLatencyMode()) { |
326 sync_reader_->Close(); | 326 sync_reader_->Close(); |
327 } else { | 327 } else { |
328 AutoLock auto_lock(lock_); | 328 AutoLock auto_lock(lock_); |
329 push_source_.OnClose(NULL); | 329 push_source_.OnClose(NULL); |
330 } | 330 } |
331 } | 331 } |
332 | 332 |
333 void AudioOutputController::OnError(AudioOutputStream* stream, int code) { | 333 void AudioOutputController::OnError(AudioOutputStream* stream, int code) { |
334 // Handle error on the audio controller thread. | 334 // Handle error on the audio controller thread. |
335 message_loop_->PostTask( | 335 thread_.message_loop()->PostTask( |
336 FROM_HERE, | 336 FROM_HERE, |
337 NewRunnableMethod(this, &AudioOutputController::DoReportError, code)); | 337 NewRunnableMethod(this, &AudioOutputController::DoReportError, code)); |
338 } | 338 } |
339 | 339 |
340 void AudioOutputController::SubmitOnMoreData_Locked() { | 340 void AudioOutputController::SubmitOnMoreData_Locked() { |
341 lock_.AssertAcquired(); | 341 lock_.AssertAcquired(); |
342 | 342 |
343 if (push_source_.UnProcessedBytes() > buffer_capacity_) | 343 if (push_source_.UnProcessedBytes() > buffer_capacity_) |
344 return; | 344 return; |
345 | 345 |
346 base::Time timestamp = last_callback_time_; | 346 base::Time timestamp = last_callback_time_; |
347 uint32 pending_bytes = hardware_pending_bytes_ + | 347 uint32 pending_bytes = hardware_pending_bytes_ + |
348 push_source_.UnProcessedBytes(); | 348 push_source_.UnProcessedBytes(); |
349 | 349 |
350 // If we need more data then call the event handler to ask for more data. | 350 // If we need more data then call the event handler to ask for more data. |
351 // It is okay that we don't lock in this block because the parameters are | 351 // It is okay that we don't lock in this block because the parameters are |
352 // correct and in the worst case we are just asking more data than needed. | 352 // correct and in the worst case we are just asking more data than needed. |
353 AutoUnlock auto_unlock(lock_); | 353 AutoUnlock auto_unlock(lock_); |
354 handler_->OnMoreData(this, timestamp, pending_bytes); | 354 handler_->OnMoreData(this, timestamp, pending_bytes); |
355 } | 355 } |
356 | 356 |
357 } // namespace media | 357 } // namespace media |
OLD | NEW |