| 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 |