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 |
7 // The following parameters limit the request buffer and packet size from the | 9 // The following parameters limit the request buffer and packet size from the |
8 // renderer to avoid renderer from requesting too much memory. | 10 // renderer to avoid renderer from requesting too much memory. |
9 static const uint32 kMegabytes = 1024 * 1024; | 11 static const uint32 kMegabytes = 1024 * 1024; |
10 static const uint32 kMaxHardwareBufferSize = 2 * kMegabytes; | 12 static const uint32 kMaxHardwareBufferSize = 2 * kMegabytes; |
11 static const int kMaxChannels = 32; | 13 static const int kMaxChannels = 32; |
12 static const int kMaxBitsPerSample = 64; | 14 static const int kMaxBitsPerSample = 64; |
13 static const int kMaxSampleRate = 192000; | 15 static const int kMaxSampleRate = 192000; |
14 | 16 |
15 // Return true if the parameters for creating an audio stream is valid. | 17 // Return true if the parameters for creating an audio stream is valid. |
16 // Return false otherwise. | 18 // Return false otherwise. |
(...skipping 11 matching lines...) Expand all Loading... |
28 } | 30 } |
29 return true; | 31 return true; |
30 } | 32 } |
31 | 33 |
32 namespace media { | 34 namespace media { |
33 | 35 |
34 AudioOutputController::AudioOutputController(EventHandler* handler, | 36 AudioOutputController::AudioOutputController(EventHandler* handler, |
35 uint32 capacity, | 37 uint32 capacity, |
36 SyncReader* sync_reader) | 38 SyncReader* sync_reader) |
37 : handler_(handler), | 39 : handler_(handler), |
| 40 stream_(NULL), |
38 volume_(1.0), | 41 volume_(1.0), |
39 state_(kEmpty), | 42 state_(kEmpty), |
40 hardware_pending_bytes_(0), | 43 hardware_pending_bytes_(0), |
41 buffer_capacity_(capacity), | 44 buffer_capacity_(capacity), |
42 sync_reader_(sync_reader), | 45 sync_reader_(sync_reader) { |
43 thread_("AudioOutputControllerThread") { | |
44 } | 46 } |
45 | 47 |
46 AudioOutputController::~AudioOutputController() { | 48 AudioOutputController::~AudioOutputController() { |
47 DCHECK(kClosed == state_); | 49 DCHECK(kClosed == state_); |
48 } | 50 } |
49 | 51 |
50 // static | 52 // static |
51 scoped_refptr<AudioOutputController> AudioOutputController::Create( | 53 scoped_refptr<AudioOutputController> AudioOutputController::Create( |
52 EventHandler* event_handler, | 54 EventHandler* event_handler, |
53 AudioManager::Format format, | 55 AudioManager::Format format, |
54 int channels, | 56 int channels, |
55 int sample_rate, | 57 int sample_rate, |
56 int bits_per_sample, | 58 int bits_per_sample, |
57 uint32 hardware_buffer_size, | 59 uint32 hardware_buffer_size, |
58 uint32 buffer_capacity) { | 60 uint32 buffer_capacity) { |
59 | 61 |
60 if (!CheckParameters(channels, sample_rate, bits_per_sample, | 62 if (!CheckParameters(channels, sample_rate, bits_per_sample, |
61 hardware_buffer_size)) | 63 hardware_buffer_size)) |
62 return NULL; | 64 return NULL; |
63 | 65 |
64 // Starts the audio controller thread. | 66 // Starts the audio controller thread. |
65 scoped_refptr<AudioOutputController> controller = new AudioOutputController( | 67 scoped_refptr<AudioOutputController> controller = new AudioOutputController( |
66 event_handler, buffer_capacity, NULL); | 68 event_handler, buffer_capacity, NULL); |
67 | 69 |
68 // Start the audio controller thread and post a task to create the | 70 controller->message_loop_ = |
69 // audio stream. | 71 AudioManager::GetAudioManager()->GetMessageLoop(); |
70 controller->thread_.Start(); | 72 controller->message_loop_->PostTask( |
71 controller->thread_.message_loop()->PostTask( | |
72 FROM_HERE, | 73 FROM_HERE, |
73 NewRunnableMethod(controller.get(), &AudioOutputController::DoCreate, | 74 NewRunnableMethod(controller.get(), &AudioOutputController::DoCreate, |
74 format, channels, sample_rate, bits_per_sample, | 75 format, channels, sample_rate, bits_per_sample, |
75 hardware_buffer_size)); | 76 hardware_buffer_size)); |
76 return controller; | 77 return controller; |
77 } | 78 } |
78 | 79 |
79 // static | 80 // static |
80 scoped_refptr<AudioOutputController> AudioOutputController::CreateLowLatency( | 81 scoped_refptr<AudioOutputController> AudioOutputController::CreateLowLatency( |
81 EventHandler* event_handler, | 82 EventHandler* event_handler, |
82 AudioManager::Format format, | 83 AudioManager::Format format, |
83 int channels, | 84 int channels, |
84 int sample_rate, | 85 int sample_rate, |
85 int bits_per_sample, | 86 int bits_per_sample, |
86 uint32 hardware_buffer_size, | 87 uint32 hardware_buffer_size, |
87 SyncReader* sync_reader) { | 88 SyncReader* sync_reader) { |
88 | 89 |
89 DCHECK(sync_reader); | 90 DCHECK(sync_reader); |
90 | 91 |
91 if (!CheckParameters(channels, sample_rate, bits_per_sample, | 92 if (!CheckParameters(channels, sample_rate, bits_per_sample, |
92 hardware_buffer_size)) | 93 hardware_buffer_size)) |
93 return NULL; | 94 return NULL; |
94 | 95 |
95 // Starts the audio controller thread. | 96 // Starts the audio controller thread. |
96 scoped_refptr<AudioOutputController> controller = new AudioOutputController( | 97 scoped_refptr<AudioOutputController> controller = new AudioOutputController( |
97 event_handler, 0, sync_reader); | 98 event_handler, 0, sync_reader); |
98 | 99 |
99 // Start the audio controller thread and post a task to create the | 100 controller->message_loop_ = |
100 // audio stream. | 101 AudioManager::GetAudioManager()->GetMessageLoop(); |
101 controller->thread_.Start(); | 102 controller->message_loop_->PostTask( |
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(thread_.IsRunning()); | 111 DCHECK(message_loop_); |
112 thread_.message_loop()->PostTask( | 112 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(thread_.IsRunning()); | 118 DCHECK(message_loop_); |
119 thread_.message_loop()->PostTask( | 119 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(thread_.IsRunning()); | 125 DCHECK(message_loop_); |
126 thread_.message_loop()->PostTask( | 126 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 if (!thread_.IsRunning()) { | 132 { |
133 // If the thread is not running make sure we are stopped. | 133 AutoLock auto_lock(lock_); |
134 DCHECK_EQ(kClosed, state_); | 134 // Don't do anything if the stream is already closed. |
135 return; | 135 if (state_ == kClosed) |
| 136 return; |
| 137 state_ = kClosed; |
136 } | 138 } |
137 | 139 |
138 // Wait for all tasks to complete on the audio thread. | 140 message_loop_->PostTask( |
139 thread_.message_loop()->PostTask( | |
140 FROM_HERE, | 141 FROM_HERE, |
141 NewRunnableMethod(this, &AudioOutputController::DoClose)); | 142 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(thread_.IsRunning()); | 146 DCHECK(message_loop_); |
147 thread_.message_loop()->PostTask( | 147 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(thread_.message_loop(), MessageLoop::current()); | 162 DCHECK_EQ(message_loop_, MessageLoop::current()); |
163 DCHECK_EQ(kEmpty, state_); | 163 |
| 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); |
164 | 170 |
165 // Create the stream in the first place. | 171 // Create the stream in the first place. |
166 stream_ = AudioManager::GetAudioManager()->MakeAudioOutputStream( | 172 stream_ = AudioManager::GetAudioManager()->MakeAudioOutputStream( |
167 format, channels, sample_rate, bits_per_sample); | 173 format, channels, sample_rate, bits_per_sample); |
168 | 174 |
169 if (!stream_) { | 175 if (!stream_) { |
170 // TODO(hclam): Define error types. | 176 // TODO(hclam): Define error types. |
171 handler_->OnError(this, 0); | 177 handler_->OnError(this, 0); |
172 return; | 178 return; |
173 } | 179 } |
(...skipping 10 matching lines...) Expand all Loading... |
184 stream_->SetVolume(volume_); | 190 stream_->SetVolume(volume_); |
185 | 191 |
186 // Finally set the state to kCreated. | 192 // Finally set the state to kCreated. |
187 state_ = kCreated; | 193 state_ = kCreated; |
188 | 194 |
189 // And then report we have been created. | 195 // And then report we have been created. |
190 handler_->OnCreated(this); | 196 handler_->OnCreated(this); |
191 | 197 |
192 // If in normal latency mode then start buffering. | 198 // If in normal latency mode then start buffering. |
193 if (!LowLatencyMode()) { | 199 if (!LowLatencyMode()) { |
194 AutoLock auto_lock(lock_); | |
195 SubmitOnMoreData_Locked(); | 200 SubmitOnMoreData_Locked(); |
196 } | 201 } |
197 } | 202 } |
198 | 203 |
199 void AudioOutputController::DoPlay() { | 204 void AudioOutputController::DoPlay() { |
200 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); | 205 DCHECK_EQ(message_loop_, MessageLoop::current()); |
201 | |
202 // We can start from created or paused state. | |
203 if (state_ != kCreated && state_ != kPaused) | |
204 return; | |
205 | 206 |
206 State old_state; | 207 State old_state; |
207 // Update the |state_| to kPlaying. | 208 // Update the |state_| to kPlaying. |
208 { | 209 { |
209 AutoLock auto_lock(lock_); | 210 AutoLock auto_lock(lock_); |
| 211 // We can start from created or paused state. |
| 212 if (state_ != kCreated && state_ != kPaused) |
| 213 return; |
210 old_state = state_; | 214 old_state = state_; |
211 state_ = kPlaying; | 215 state_ = kPlaying; |
212 } | 216 } |
213 | 217 |
214 // We start the AudioOutputStream lazily. | 218 // We start the AudioOutputStream lazily. |
215 stream_->Start(this); | 219 stream_->Start(this); |
216 | 220 |
217 // Tell the event handler that we are now playing. | 221 // Tell the event handler that we are now playing. |
218 handler_->OnPlaying(this); | 222 handler_->OnPlaying(this); |
219 } | 223 } |
220 | 224 |
221 void AudioOutputController::DoPause() { | 225 void AudioOutputController::DoPause() { |
222 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); | 226 DCHECK_EQ(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; |
231 state_ = kPaused; | 234 state_ = kPaused; |
232 } | 235 } |
233 | 236 |
234 // Then we stop the audio device. This is not the perfect solution because | 237 // Then we stop the audio device. This is not the perfect solution because |
235 // it discards all the internal buffer in the audio device. | 238 // it discards all the internal buffer in the audio device. |
236 // TODO(hclam): Actually pause the audio device. | 239 // TODO(hclam): Actually pause the audio device. |
237 stream_->Stop(); | 240 stream_->Stop(); |
238 | 241 |
239 handler_->OnPaused(this); | 242 handler_->OnPaused(this); |
240 } | 243 } |
241 | 244 |
242 void AudioOutputController::DoFlush() { | 245 void AudioOutputController::DoFlush() { |
243 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); | 246 DCHECK_EQ(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; |
253 push_source_.ClearAll(); | 255 push_source_.ClearAll(); |
254 } | 256 } |
255 } | 257 } |
256 | 258 |
257 void AudioOutputController::DoClose() { | 259 void AudioOutputController::DoClose() { |
258 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); | 260 DCHECK_EQ(message_loop_, MessageLoop::current()); |
259 DCHECK_NE(kClosed, state_); | 261 DCHECK_EQ(kClosed, state_); |
260 | |
261 // |stream_| can be null if creating the device failed in DoCreate(). | 262 // |stream_| can be null if creating the device failed in DoCreate(). |
262 if (stream_) { | 263 if (stream_) { |
263 stream_->Stop(); | 264 stream_->Stop(); |
264 stream_->Close(); | 265 stream_->Close(); |
265 // After stream is closed it is destroyed, so don't keep a reference to it. | 266 // After stream is closed it is destroyed, so don't keep a reference to it. |
266 stream_ = NULL; | 267 stream_ = NULL; |
267 } | 268 } |
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; | |
272 } | 269 } |
273 | 270 |
274 void AudioOutputController::DoSetVolume(double volume) { | 271 void AudioOutputController::DoSetVolume(double volume) { |
275 DCHECK_EQ(thread_.message_loop(), MessageLoop::current()); | 272 DCHECK_EQ(message_loop_, MessageLoop::current()); |
276 | 273 |
277 // Saves the volume to a member first. We may not be able to set the volume | 274 // Saves the volume to a member first. We may not be able to set the volume |
278 // right away but when the stream is created we'll set the volume. | 275 // right away but when the stream is created we'll set the volume. |
279 volume_ = volume; | 276 volume_ = volume; |
280 | 277 |
281 if (state_ != kPlaying && state_ != kPaused && state_ != kCreated) | 278 { |
282 return; | 279 AutoLock auto_lock(lock_); |
| 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(thread_.message_loop(), MessageLoop::current()); | 288 DCHECK_EQ(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 thread_.message_loop()->PostTask( | 335 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 |