| OLD | NEW |
| 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_controller.h" | 5 #include "media/audio/audio_output_controller.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" |
| 9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
| 10 #include "base/synchronization/waitable_event.h" | 10 #include "base/synchronization/waitable_event.h" |
| (...skipping 24 matching lines...) Expand all Loading... |
| 35 buffer_(0, capacity), | 35 buffer_(0, capacity), |
| 36 pending_request_(false), | 36 pending_request_(false), |
| 37 sync_reader_(sync_reader), | 37 sync_reader_(sync_reader), |
| 38 message_loop_(NULL), | 38 message_loop_(NULL), |
| 39 number_polling_attempts_left_(0), | 39 number_polling_attempts_left_(0), |
| 40 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { | 40 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { |
| 41 } | 41 } |
| 42 | 42 |
| 43 AudioOutputController::~AudioOutputController() { | 43 AudioOutputController::~AudioOutputController() { |
| 44 DCHECK_EQ(kClosed, state_); | 44 DCHECK_EQ(kClosed, state_); |
| 45 if (message_loop_ == MessageLoop::current()) { | 45 DCHECK(message_loop_); |
| 46 |
| 47 if (!message_loop_.get() || message_loop_->BelongsToCurrentThread()) { |
| 46 DoStopCloseAndClearStream(NULL); | 48 DoStopCloseAndClearStream(NULL); |
| 47 } else { | 49 } else { |
| 48 DCHECK(message_loop_); | |
| 49 WaitableEvent completion(true /* manual reset */, | 50 WaitableEvent completion(true /* manual reset */, |
| 50 false /* initial state */); | 51 false /* initial state */); |
| 51 message_loop_->PostTask(FROM_HERE, | 52 message_loop_->PostTask(FROM_HERE, |
| 52 base::Bind(&AudioOutputController::DoStopCloseAndClearStream, | 53 base::Bind(&AudioOutputController::DoStopCloseAndClearStream, |
| 53 base::Unretained(this), | 54 base::Unretained(this), |
| 54 &completion)); | 55 &completion)); |
| 55 completion.Wait(); | 56 completion.Wait(); |
| 56 } | 57 } |
| 57 } | 58 } |
| 58 | 59 |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 // If |size| is set to 0, it indicates that the audio source doesn't have | 139 // If |size| is set to 0, it indicates that the audio source doesn't have |
| 139 // more data right now, and so it doesn't make sense to send additional | 140 // more data right now, and so it doesn't make sense to send additional |
| 140 // request. | 141 // request. |
| 141 if (size) { | 142 if (size) { |
| 142 buffer_.Append(data, size); | 143 buffer_.Append(data, size); |
| 143 SubmitOnMoreData_Locked(); | 144 SubmitOnMoreData_Locked(); |
| 144 } | 145 } |
| 145 } | 146 } |
| 146 | 147 |
| 147 void AudioOutputController::DoCreate(const AudioParameters& params) { | 148 void AudioOutputController::DoCreate(const AudioParameters& params) { |
| 148 DCHECK_EQ(message_loop_, MessageLoop::current()); | 149 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 149 | 150 |
| 150 // Close() can be called before DoCreate() is executed. | 151 // Close() can be called before DoCreate() is executed. |
| 151 if (state_ == kClosed) | 152 if (state_ == kClosed) |
| 152 return; | 153 return; |
| 153 DCHECK_EQ(kEmpty, state_); | 154 DCHECK_EQ(kEmpty, state_); |
| 154 | 155 |
| 155 DoStopCloseAndClearStream(NULL); | 156 DoStopCloseAndClearStream(NULL); |
| 156 stream_ = audio_manager_->MakeAudioOutputStreamProxy(params); | 157 stream_ = audio_manager_->MakeAudioOutputStreamProxy(params); |
| 157 if (!stream_) { | 158 if (!stream_) { |
| 158 // TODO(hclam): Define error types. | 159 // TODO(hclam): Define error types. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 178 handler_->OnCreated(this); | 179 handler_->OnCreated(this); |
| 179 | 180 |
| 180 // If in normal latency mode then start buffering. | 181 // If in normal latency mode then start buffering. |
| 181 if (!LowLatencyMode()) { | 182 if (!LowLatencyMode()) { |
| 182 base::AutoLock auto_lock(lock_); | 183 base::AutoLock auto_lock(lock_); |
| 183 SubmitOnMoreData_Locked(); | 184 SubmitOnMoreData_Locked(); |
| 184 } | 185 } |
| 185 } | 186 } |
| 186 | 187 |
| 187 void AudioOutputController::DoPlay() { | 188 void AudioOutputController::DoPlay() { |
| 188 DCHECK_EQ(message_loop_, MessageLoop::current()); | 189 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 189 | 190 |
| 190 // We can start from created or paused state. | 191 // We can start from created or paused state. |
| 191 if (state_ != kCreated && state_ != kPaused) | 192 if (state_ != kCreated && state_ != kPaused) |
| 192 return; | 193 return; |
| 193 | 194 |
| 194 if (LowLatencyMode()) { | 195 if (LowLatencyMode()) { |
| 195 state_ = kStarting; | 196 state_ = kStarting; |
| 196 | 197 |
| 197 // Ask for first packet. | 198 // Ask for first packet. |
| 198 sync_reader_->UpdatePendingBytes(0); | 199 sync_reader_->UpdatePendingBytes(0); |
| 199 | 200 |
| 200 // Cannot start stream immediately, should give renderer some time | 201 // Cannot start stream immediately, should give renderer some time |
| 201 // to deliver data. | 202 // to deliver data. |
| 202 number_polling_attempts_left_ = kPollNumAttempts; | 203 number_polling_attempts_left_ = kPollNumAttempts; |
| 203 message_loop_->PostDelayedTask( | 204 message_loop_->PostDelayedTask( |
| 204 FROM_HERE, | 205 FROM_HERE, |
| 205 base::Bind(&AudioOutputController::PollAndStartIfDataReady, | 206 base::Bind(&AudioOutputController::PollAndStartIfDataReady, |
| 206 weak_this_.GetWeakPtr()), | 207 weak_this_.GetWeakPtr()), |
| 207 base::TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); | 208 kPollPauseInMilliseconds); |
| 208 } else { | 209 } else { |
| 209 StartStream(); | 210 StartStream(); |
| 210 } | 211 } |
| 211 } | 212 } |
| 212 | 213 |
| 213 void AudioOutputController::PollAndStartIfDataReady() { | 214 void AudioOutputController::PollAndStartIfDataReady() { |
| 214 DCHECK_EQ(message_loop_, MessageLoop::current()); | 215 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 215 | 216 |
| 216 // Being paranoid: do nothing if state unexpectedly changed. | 217 // Being paranoid: do nothing if state unexpectedly changed. |
| 217 if ((state_ != kStarting) && (state_ != kPausedWhenStarting)) | 218 if ((state_ != kStarting) && (state_ != kPausedWhenStarting)) |
| 218 return; | 219 return; |
| 219 | 220 |
| 220 bool pausing = (state_ == kPausedWhenStarting); | 221 bool pausing = (state_ == kPausedWhenStarting); |
| 221 // If we are ready to start the stream, start it. | 222 // If we are ready to start the stream, start it. |
| 222 // Of course we may have to stop it immediately... | 223 // Of course we may have to stop it immediately... |
| 223 if (--number_polling_attempts_left_ == 0 || | 224 if (--number_polling_attempts_left_ == 0 || |
| 224 pausing || | 225 pausing || |
| 225 sync_reader_->DataReady()) { | 226 sync_reader_->DataReady()) { |
| 226 StartStream(); | 227 StartStream(); |
| 227 if (pausing) { | 228 if (pausing) { |
| 228 DoPause(); | 229 DoPause(); |
| 229 } | 230 } |
| 230 } else { | 231 } else { |
| 231 message_loop_->PostDelayedTask( | 232 message_loop_->PostDelayedTask( |
| 232 FROM_HERE, | 233 FROM_HERE, |
| 233 base::Bind(&AudioOutputController::PollAndStartIfDataReady, | 234 base::Bind(&AudioOutputController::PollAndStartIfDataReady, |
| 234 weak_this_.GetWeakPtr()), | 235 weak_this_.GetWeakPtr()), |
| 235 base::TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); | 236 kPollPauseInMilliseconds); |
| 236 } | 237 } |
| 237 } | 238 } |
| 238 | 239 |
| 239 void AudioOutputController::StartStream() { | 240 void AudioOutputController::StartStream() { |
| 240 DCHECK_EQ(message_loop_, MessageLoop::current()); | 241 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 241 state_ = kPlaying; | 242 state_ = kPlaying; |
| 242 | 243 |
| 243 // We start the AudioOutputStream lazily. | 244 // We start the AudioOutputStream lazily. |
| 244 stream_->Start(this); | 245 stream_->Start(this); |
| 245 | 246 |
| 246 // Tell the event handler that we are now playing. | 247 // Tell the event handler that we are now playing. |
| 247 handler_->OnPlaying(this); | 248 handler_->OnPlaying(this); |
| 248 } | 249 } |
| 249 | 250 |
| 250 void AudioOutputController::DoPause() { | 251 void AudioOutputController::DoPause() { |
| 251 DCHECK_EQ(message_loop_, MessageLoop::current()); | 252 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 252 | 253 |
| 253 if (stream_) | 254 if (stream_) |
| 254 stream_->Stop(); | 255 stream_->Stop(); |
| 255 | 256 |
| 256 switch (state_) { | 257 switch (state_) { |
| 257 case kStarting: | 258 case kStarting: |
| 258 // We were asked to pause while starting. There is delayed task that will | 259 // We were asked to pause while starting. There is delayed task that will |
| 259 // try starting playback, and there is no way to remove that task from the | 260 // try starting playback, and there is no way to remove that task from the |
| 260 // queue. If we stop now that task will be executed anyway. | 261 // queue. If we stop now that task will be executed anyway. |
| 261 // Delay pausing, let delayed task to do pause after it start playback. | 262 // Delay pausing, let delayed task to do pause after it start playback. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 275 } | 276 } |
| 276 | 277 |
| 277 handler_->OnPaused(this); | 278 handler_->OnPaused(this); |
| 278 break; | 279 break; |
| 279 default: | 280 default: |
| 280 return; | 281 return; |
| 281 } | 282 } |
| 282 } | 283 } |
| 283 | 284 |
| 284 void AudioOutputController::DoFlush() { | 285 void AudioOutputController::DoFlush() { |
| 285 DCHECK_EQ(message_loop_, MessageLoop::current()); | 286 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 286 | 287 |
| 287 // TODO(hclam): Actually flush the audio device. | 288 // TODO(hclam): Actually flush the audio device. |
| 288 | 289 |
| 289 // If we are in the regular latency mode then flush the push source. | 290 // If we are in the regular latency mode then flush the push source. |
| 290 if (!sync_reader_) { | 291 if (!sync_reader_) { |
| 291 if (state_ != kPaused) | 292 if (state_ != kPaused) |
| 292 return; | 293 return; |
| 293 base::AutoLock auto_lock(lock_); | 294 base::AutoLock auto_lock(lock_); |
| 294 buffer_.Clear(); | 295 buffer_.Clear(); |
| 295 } | 296 } |
| 296 } | 297 } |
| 297 | 298 |
| 298 void AudioOutputController::DoClose(const base::Closure& closed_task) { | 299 void AudioOutputController::DoClose(const base::Closure& closed_task) { |
| 299 DCHECK_EQ(message_loop_, MessageLoop::current()); | 300 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 300 | 301 |
| 301 if (state_ != kClosed) { | 302 if (state_ != kClosed) { |
| 302 DoStopCloseAndClearStream(NULL); | 303 DoStopCloseAndClearStream(NULL); |
| 303 | 304 |
| 304 if (LowLatencyMode()) { | 305 if (LowLatencyMode()) { |
| 305 sync_reader_->Close(); | 306 sync_reader_->Close(); |
| 306 } | 307 } |
| 307 | 308 |
| 308 state_ = kClosed; | 309 state_ = kClosed; |
| 309 } | 310 } |
| 310 | 311 |
| 311 closed_task.Run(); | 312 closed_task.Run(); |
| 312 } | 313 } |
| 313 | 314 |
| 314 void AudioOutputController::DoSetVolume(double volume) { | 315 void AudioOutputController::DoSetVolume(double volume) { |
| 315 DCHECK_EQ(message_loop_, MessageLoop::current()); | 316 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 316 | 317 |
| 317 // Saves the volume to a member first. We may not be able to set the volume | 318 // Saves the volume to a member first. We may not be able to set the volume |
| 318 // right away but when the stream is created we'll set the volume. | 319 // right away but when the stream is created we'll set the volume. |
| 319 volume_ = volume; | 320 volume_ = volume; |
| 320 | 321 |
| 321 switch (state_) { | 322 switch (state_) { |
| 322 case kCreated: | 323 case kCreated: |
| 323 case kStarting: | 324 case kStarting: |
| 324 case kPausedWhenStarting: | 325 case kPausedWhenStarting: |
| 325 case kPlaying: | 326 case kPlaying: |
| 326 case kPaused: | 327 case kPaused: |
| 327 stream_->SetVolume(volume_); | 328 stream_->SetVolume(volume_); |
| 328 break; | 329 break; |
| 329 default: | 330 default: |
| 330 return; | 331 return; |
| 331 } | 332 } |
| 332 } | 333 } |
| 333 | 334 |
| 334 void AudioOutputController::DoReportError(int code) { | 335 void AudioOutputController::DoReportError(int code) { |
| 335 DCHECK_EQ(message_loop_, MessageLoop::current()); | 336 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 336 if (state_ != kClosed) | 337 if (state_ != kClosed) |
| 337 handler_->OnError(this, code); | 338 handler_->OnError(this, code); |
| 338 } | 339 } |
| 339 | 340 |
| 340 uint32 AudioOutputController::OnMoreData( | 341 uint32 AudioOutputController::OnMoreData( |
| 341 AudioOutputStream* stream, uint8* dest, | 342 AudioOutputStream* stream, uint8* dest, |
| 342 uint32 max_size, AudioBuffersState buffers_state) { | 343 uint32 max_size, AudioBuffersState buffers_state) { |
| 343 TRACE_EVENT0("audio", "AudioOutputController::OnMoreData"); | 344 TRACE_EVENT0("audio", "AudioOutputController::OnMoreData"); |
| 344 | 345 |
| 345 // If regular latency mode is used. | 346 // If regular latency mode is used. |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 408 buffers_state.pending_bytes += buffer_.forward_bytes(); | 409 buffers_state.pending_bytes += buffer_.forward_bytes(); |
| 409 | 410 |
| 410 // If we need more data then call the event handler to ask for more data. | 411 // If we need more data then call the event handler to ask for more data. |
| 411 // It is okay that we don't lock in this block because the parameters are | 412 // It is okay that we don't lock in this block because the parameters are |
| 412 // correct and in the worst case we are just asking more data than needed. | 413 // correct and in the worst case we are just asking more data than needed. |
| 413 base::AutoUnlock auto_unlock(lock_); | 414 base::AutoUnlock auto_unlock(lock_); |
| 414 handler_->OnMoreData(this, buffers_state); | 415 handler_->OnMoreData(this, buffers_state); |
| 415 } | 416 } |
| 416 | 417 |
| 417 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent *done) { | 418 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent *done) { |
| 418 DCHECK_EQ(message_loop_, MessageLoop::current()); | 419 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 419 | 420 |
| 420 // Allow calling unconditionally and bail if we don't have a stream_ to close. | 421 // Allow calling unconditionally and bail if we don't have a stream_ to close. |
| 421 if (stream_ != NULL) { | 422 if (stream_ != NULL) { |
| 422 stream_->Stop(); | 423 stream_->Stop(); |
| 423 stream_->Close(); | 424 stream_->Close(); |
| 424 stream_ = NULL; | 425 stream_ = NULL; |
| 425 weak_this_.InvalidateWeakPtrs(); | 426 weak_this_.InvalidateWeakPtrs(); |
| 426 } | 427 } |
| 427 | 428 |
| 428 // Should be last in the method, do not touch "this" from here on. | 429 // Should be last in the method, do not touch "this" from here on. |
| 429 if (done != NULL) | 430 if (done != NULL) |
| 430 done->Signal(); | 431 done->Signal(); |
| 431 } | 432 } |
| 432 | 433 |
| 433 } // namespace media | 434 } // namespace media |
| OLD | NEW |