Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // THREAD SAFETY | 5 // THREAD SAFETY |
| 6 // | 6 // |
| 7 // The AlsaPcmOutputStream object's internal state is accessed by two threads: | 7 // AlsaPcmOutputStream object is *not* thread-safe -- we assume that |
| 8 // | |
| 9 // client thread - creates the object and calls the public APIs. | 8 // client thread - creates the object and calls the public APIs. |
| 10 // message loop thread - executes all the internal tasks including querying | 9 // message loop thread - executes all the internal tasks including querying |
| 11 // the data source for more data, writing to the alsa device, and closing | 10 // the data source for more data, writing to the alsa device, and closing |
| 12 // the alsa device. It does *not* handle opening the device though. | 11 // the alsa device. |
| 13 // | 12 // is actually the same thread. |
| 14 // The class is designed so that most operations that read/modify the object's | |
| 15 // internal state are done on the message loop thread. The exception is data | |
| 16 // conatined in the |shared_data_| structure. Data in this structure needs to | |
| 17 // be accessed by both threads, and should only be accessed when the | |
| 18 // |shared_data_.lock_| is held. | |
| 19 // | |
| 20 // All member variables that are not in |shared_data_| are created/destroyed on | |
| 21 // the |message_loop_|. This allows safe access to them from any task posted to | |
| 22 // |message_loop_|. The values in |shared_data_| are considered to be read-only | |
| 23 // signals by tasks posted to |message_loop_| (see the SEMANTICS of | |
| 24 // |shared_data_| section below). Because of these two constraints, tasks can, | |
| 25 // and must, be coded to be safe in the face of a changing |shared_data_|. | |
| 26 // | |
| 27 // | |
| 28 // SEMANTICS OF |shared_data_| | |
| 29 // | |
| 30 // Though |shared_data_| is accessable by both threads, the code has been | |
| 31 // structured so that all mutations to |shared_data_| are only done in the | |
| 32 // client thread. The message loop thread only ever reads the shared data. | |
| 33 // | |
| 34 // This reduces the need for critical sections because the public API code can | |
| 35 // assume that no mutations occur to the |shared_data_| between queries. | |
| 36 // | |
| 37 // On the message loop side, tasks have been coded such that they can | |
| 38 // operate safely regardless of when state changes happen to |shared_data_|. | |
| 39 // Code that is sensitive to the timing of state changes are delegated to the | |
| 40 // |shared_data_| object so they can executed while holding | |
| 41 // |shared_data_.lock_|. | |
| 42 // | 13 // |
| 43 // | 14 // |
| 44 // SEMANTICS OF CloseTask() | 15 // SEMANTICS OF CloseTask() |
| 45 // | 16 // |
| 46 // The CloseTask() is responsible for cleaning up any resources that were | 17 // The CloseTask() is responsible for cleaning up any resources that were |
| 47 // acquired after a successful Open(). After a CloseTask() has executed, | 18 // acquired after a successful Open(). After a CloseTask() has executed, |
| 48 // scheduling of reads should stop. Currently scheduled tasks may run, but | 19 // scheduling of reads should stop. Currently scheduled tasks may run, but |
| 49 // they should not attempt to access any of the internal structures beyond | 20 // they should not attempt to access any of the internal structures beyond |
| 50 // querying the |stop_stream_| flag and no-oping themselves. This will | 21 // querying the |stop_stream_| flag and no-oping themselves. This will |
| 51 // guarantee that eventually no more tasks will be posted into the message | 22 // guarantee that eventually no more tasks will be posted into the message |
| 52 // loop, and the AlsaPcmOutputStream will be able to delete itself. | 23 // loop. |
| 53 // | 24 // |
| 54 // | 25 // |
| 55 // SEMANTICS OF ERROR STATES | 26 // SEMANTICS OF ERROR STATES |
| 56 // | 27 // |
| 57 // The object has two distinct error states: |shared_data_.state_| == kInError | 28 // The object has two distinct error states: |state_| == kInError |
| 58 // and |stop_stream_|. The |shared_data_.state_| state is only settable | 29 // and |stop_stream_|. The |stop_stream_| variable is used to indicate |
| 59 // by the client thread, and thus cannot be used to signal when the ALSA device | |
| 60 // fails due to a hardware (or other low-level) event. The |stop_stream_| | |
| 61 // variable is only accessed by the message loop thread; it is used to indicate | |
| 62 // that the playback_handle should no longer be used either because of a | 30 // that the playback_handle should no longer be used either because of a |
| 63 // hardware/low-level event, or because the CloseTask() has been run. | 31 // hardware/low-level event, or because the CloseTask() has been run. |
| 64 // | 32 // |
| 65 // When |shared_data_.state_| == kInError, all public API functions will fail | 33 // When |state_| == kInError, all public API functions will fail |
| 66 // with an error (Start() will call the OnError() function on the callback | 34 // with an error (Start() will call the OnError() function on the callback |
| 67 // immediately), or no-op themselves with the exception of Close(). Even if an | 35 // immediately), or no-op themselves with the exception of Close(). Even if an |
| 68 // error state has been entered, if Open() has previously returned successfully, | 36 // error state has been entered, if Open() has previously returned successfully, |
| 69 // Close() must be called to cleanup the ALSA devices and release resources. | 37 // Close() must be called to cleanup the ALSA devices and release resources. |
| 70 // | 38 // |
| 71 // When |stop_stream_| is set, no more commands will be made against the | 39 // When |stop_stream_| is set, no more commands will be made against the |
| 72 // ALSA device, and playback will effectively stop. From the client's point of | 40 // ALSA device, and playback will effectively stop. From the client's point of |
| 73 // view, it will seem that the device has just clogged and stopped requesting | 41 // view, it will seem that the device has just clogged and stopped requesting |
| 74 // data. | 42 // data. |
| 75 | 43 |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 211 // Since we expect to only be able to wake up with a resolution of | 179 // Since we expect to only be able to wake up with a resolution of |
| 212 // kSleepErrorMilliseconds, double that for our minimum required latency. | 180 // kSleepErrorMilliseconds, double that for our minimum required latency. |
| 213 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = | 181 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = |
| 214 kSleepErrorMilliseconds * 2 * 1000; | 182 kSleepErrorMilliseconds * 2 * 1000; |
| 215 | 183 |
| 216 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, | 184 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, |
| 217 AudioParameters params, | 185 AudioParameters params, |
| 218 AlsaWrapper* wrapper, | 186 AlsaWrapper* wrapper, |
| 219 AudioManagerLinux* manager, | 187 AudioManagerLinux* manager, |
| 220 MessageLoop* message_loop) | 188 MessageLoop* message_loop) |
| 221 : shared_data_(MessageLoop::current()), | 189 : requested_device_name_(device_name), |
| 222 requested_device_name_(device_name), | |
| 223 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), | 190 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), |
| 224 channels_(params.channels), | 191 channels_(params.channels), |
| 225 sample_rate_(params.sample_rate), | 192 sample_rate_(params.sample_rate), |
| 226 bytes_per_sample_(params.bits_per_sample / 8), | 193 bytes_per_sample_(params.bits_per_sample / 8), |
| 227 bytes_per_frame_(channels_ * params.bits_per_sample / 8), | 194 bytes_per_frame_(channels_ * params.bits_per_sample / 8), |
| 228 should_downmix_(false), | 195 should_downmix_(false), |
| 229 packet_size_(params.GetPacketSize()), | 196 packet_size_(params.GetPacketSize()), |
| 230 micros_per_packet_(FramesToMicros( | 197 micros_per_packet_(FramesToMicros( |
| 231 params.samples_per_packet, sample_rate_)), | 198 params.samples_per_packet, sample_rate_)), |
| 232 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros, | 199 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros, |
| 233 micros_per_packet_ * 2)), | 200 micros_per_packet_ * 2)), |
| 234 bytes_per_output_frame_(bytes_per_frame_), | 201 bytes_per_output_frame_(bytes_per_frame_), |
| 235 alsa_buffer_frames_(0), | 202 alsa_buffer_frames_(0), |
| 236 stop_stream_(false), | 203 stop_stream_(false), |
| 237 wrapper_(wrapper), | 204 wrapper_(wrapper), |
| 238 manager_(manager), | 205 manager_(manager), |
| 239 playback_handle_(NULL), | 206 playback_handle_(NULL), |
| 240 frames_per_packet_(packet_size_ / bytes_per_frame_), | 207 frames_per_packet_(packet_size_ / bytes_per_frame_), |
| 241 client_thread_loop_(MessageLoop::current()), | 208 message_loop_(message_loop), |
| 242 message_loop_(message_loop) { | 209 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), |
| 210 state_(kCreated), | |
| 211 volume_(1.0f), | |
| 212 source_callback_(NULL) { | |
| 213 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 243 | 214 |
| 244 // Sanity check input values. | 215 // Sanity check input values. |
| 245 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { | 216 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { |
| 246 LOG(WARNING) << "Unsupported audio frequency."; | 217 LOG(WARNING) << "Unsupported audio frequency."; |
| 247 shared_data_.TransitionTo(kInError); | 218 TransitionTo(kInError); |
| 248 } | 219 } |
| 249 | 220 |
| 250 if (AudioParameters::AUDIO_PCM_LINEAR != params.format && | 221 if (AudioParameters::AUDIO_PCM_LINEAR != params.format && |
| 251 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { | 222 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { |
| 252 LOG(WARNING) << "Unsupported audio format"; | 223 LOG(WARNING) << "Unsupported audio format"; |
| 253 shared_data_.TransitionTo(kInError); | 224 TransitionTo(kInError); |
| 254 } | 225 } |
| 255 | 226 |
| 256 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { | 227 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { |
| 257 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; | 228 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; |
| 258 shared_data_.TransitionTo(kInError); | 229 TransitionTo(kInError); |
| 259 } | 230 } |
| 260 } | 231 } |
| 261 | 232 |
| 262 AlsaPcmOutputStream::~AlsaPcmOutputStream() { | 233 AlsaPcmOutputStream::~AlsaPcmOutputStream() { |
| 263 InternalState state = shared_data_.state(); | 234 InternalState current_state = state(); |
| 264 DCHECK(state == kCreated || state == kIsClosed || state == kInError); | 235 DCHECK(current_state == kCreated || |
| 236 current_state == kIsClosed || | |
| 237 current_state == kInError); | |
| 265 | 238 |
| 266 // TODO(ajwong): Ensure that CloseTask has been called and the | 239 // TODO(ajwong): Ensure that CloseTask has been called and the |
| 267 // playback handle released by DCHECKing that playback_handle_ is NULL. | 240 // playback handle released by DCHECKing that playback_handle_ is NULL. |
| 268 // Currently, because of Bug 18217, there is a race condition on destruction | 241 // Currently, because of Bug 18217, there is a race condition on destruction |
| 269 // where the stream is not always stopped and closed, causing this to fail. | 242 // where the stream is not always stopped and closed, causing this to fail. |
| 270 } | 243 } |
| 271 | 244 |
| 272 bool AlsaPcmOutputStream::Open() { | 245 bool AlsaPcmOutputStream::Open() { |
| 273 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 246 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 274 | 247 |
| 275 if (shared_data_.state() == kInError) { | 248 if (state() == kInError) { |
| 276 return false; | 249 return false; |
| 277 } | 250 } |
| 278 | 251 |
| 279 if (!shared_data_.CanTransitionTo(kIsOpened)) { | 252 if (!CanTransitionTo(kIsOpened)) { |
| 280 NOTREACHED() << "Invalid state: " << shared_data_.state(); | 253 NOTREACHED() << "Invalid state: " << state(); |
| 281 return false; | 254 return false; |
| 282 } | 255 } |
| 283 | 256 |
| 284 // We do not need to check if the transition was successful because | 257 // We do not need to check if the transition was successful because |
| 285 // CanTransitionTo() was checked above, and it is assumed that this | 258 // CanTransitionTo() was checked above, and it is assumed that this |
| 286 // object's public API is only called on one thread so the state cannot | 259 // object's public API is only called on one thread so the state cannot |
| 287 // transition out from under us. | 260 // transition out from under us. |
| 288 shared_data_.TransitionTo(kIsOpened); | 261 TransitionTo(kIsOpened); |
| 289 message_loop_->PostTask( | 262 message_loop_->PostTask( |
| 290 FROM_HERE, | 263 FROM_HERE, |
| 291 NewRunnableMethod(this, &AlsaPcmOutputStream::OpenTask)); | 264 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::OpenTask)); |
| 292 | 265 |
| 293 return true; | 266 return true; |
| 294 } | 267 } |
| 295 | 268 |
| 296 void AlsaPcmOutputStream::Close() { | 269 void AlsaPcmOutputStream::Close() { |
| 297 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 270 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 298 | 271 |
| 299 // Sanity check that the transition occurs correctly. It is safe to | 272 // Sanity check that the transition occurs correctly. It is safe to |
| 300 // continue anyways because all operations for closing are idempotent. | 273 // continue anyways because all operations for closing are idempotent. |
| 301 if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) { | 274 if (TransitionTo(kIsClosed) != kIsClosed) { |
| 302 NOTREACHED() << "Unable to transition Closed."; | 275 NOTREACHED() << "Unable to transition Closed."; |
| 303 } | 276 } |
| 304 | 277 |
| 305 message_loop_->PostTask( | 278 message_loop_->PostTask( |
| 306 FROM_HERE, | 279 FROM_HERE, |
| 307 NewRunnableMethod(this, &AlsaPcmOutputStream::CloseTask)); | 280 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::CloseTask)); |
| 308 | 281 |
| 309 // Signal to the manager that we're closed and can be removed. Since | 282 // Signal to the manager that we're closed and can be removed. Since |
| 310 // we just posted a CloseTask to the message loop, we won't be deleted | 283 // we just posted a CloseTask to the message loop, we won't be deleted |
| 311 // immediately, but it will happen soon afterwards. | 284 // immediately, but it will happen soon afterwards. |
| 312 manager()->ReleaseOutputStream(this); | 285 manager()->ReleaseOutputStream(this); |
|
scherkus (not reviewing)
2011/06/07 00:19:01
hmm.. if I follow this correctly:
1) We're execu
enal
2011/06/07 17:29:00
Fixed. Don't see how to fully test it, though...
| |
| 313 } | 286 } |
| 314 | 287 |
| 315 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { | 288 void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) { |
| 316 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 289 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 317 | 290 |
| 318 CHECK(callback); | 291 CHECK(callback); |
| 319 | 292 |
| 320 shared_data_.set_source_callback(callback); | 293 set_source_callback(callback); |
| 321 | 294 |
| 322 // Only post the task if we can enter the playing state. | 295 // Only post the task if we can enter the playing state. |
| 323 if (shared_data_.TransitionTo(kIsPlaying) == kIsPlaying) { | 296 if (TransitionTo(kIsPlaying) == kIsPlaying) { |
| 324 message_loop_->PostTask( | 297 message_loop_->PostTask( |
| 325 FROM_HERE, | 298 FROM_HERE, |
| 326 NewRunnableMethod(this, &AlsaPcmOutputStream::StartTask)); | 299 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::StartTask)); |
| 327 } | 300 } |
| 328 } | 301 } |
| 329 | 302 |
| 330 void AlsaPcmOutputStream::Stop() { | 303 void AlsaPcmOutputStream::Stop() { |
| 331 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 304 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 332 | 305 |
| 333 // Reset the callback, so that it is not called anymore. | 306 // Reset the callback, so that it is not called anymore. |
| 334 shared_data_.set_source_callback(NULL); | 307 set_source_callback(NULL); |
| 335 | 308 |
| 336 shared_data_.TransitionTo(kIsStopped); | 309 TransitionTo(kIsStopped); |
| 337 } | 310 } |
| 338 | 311 |
| 339 void AlsaPcmOutputStream::SetVolume(double volume) { | 312 void AlsaPcmOutputStream::SetVolume(double volume) { |
| 340 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 313 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 341 | 314 |
| 342 shared_data_.set_volume(static_cast<float>(volume)); | 315 set_volume(static_cast<float>(volume)); |
| 343 } | 316 } |
| 344 | 317 |
| 345 void AlsaPcmOutputStream::GetVolume(double* volume) { | 318 void AlsaPcmOutputStream::GetVolume(double* pointer_to_volume) { |
| 346 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 319 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 347 | 320 |
| 348 *volume = shared_data_.volume(); | 321 *pointer_to_volume = volume(); |
| 349 } | 322 } |
| 350 | 323 |
| 351 void AlsaPcmOutputStream::OpenTask() { | 324 void AlsaPcmOutputStream::OpenTask() { |
| 352 DCHECK_EQ(message_loop_, MessageLoop::current()); | 325 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 353 | 326 |
| 354 // Try to open the device. | 327 // Try to open the device. |
| 355 if (requested_device_name_ == kAutoSelectDevice) { | 328 if (requested_device_name_ == kAutoSelectDevice) { |
| 356 playback_handle_ = AutoSelectDevice(latency_micros_); | 329 playback_handle_ = AutoSelectDevice(latency_micros_); |
| 357 if (playback_handle_) | 330 if (playback_handle_) |
| 358 VLOG(1) << "Auto-selected device: " << device_name_; | 331 VLOG(1) << "Auto-selected device: " << device_name_; |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 389 } | 362 } |
| 390 } | 363 } |
| 391 | 364 |
| 392 void AlsaPcmOutputStream::StartTask() { | 365 void AlsaPcmOutputStream::StartTask() { |
| 393 DCHECK_EQ(message_loop_, MessageLoop::current()); | 366 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 394 | 367 |
| 395 if (stop_stream_) { | 368 if (stop_stream_) { |
| 396 return; | 369 return; |
| 397 } | 370 } |
| 398 | 371 |
| 399 if (shared_data_.state() != kIsPlaying) { | 372 if (state() != kIsPlaying) { |
| 400 return; | 373 return; |
| 401 } | 374 } |
| 402 | 375 |
| 403 // Before starting, the buffer might have audio from previous user of this | 376 // Before starting, the buffer might have audio from previous user of this |
| 404 // device. | 377 // device. |
| 405 buffer_->Clear(); | 378 buffer_->Clear(); |
| 406 | 379 |
| 407 // When starting again, drop all packets in the device and prepare it again | 380 // When starting again, drop all packets in the device and prepare it again |
| 408 // incase we are restarting from a pause state and need to flush old data. | 381 // incase we are restarting from a pause state and need to flush old data. |
| 409 int error = wrapper_->PcmDrop(playback_handle_); | 382 int error = wrapper_->PcmDrop(playback_handle_); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 464 // delay (in bytes) for the requested data to be played. | 437 // delay (in bytes) for the requested data to be played. |
| 465 | 438 |
| 466 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / | 439 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / |
| 467 bytes_per_output_frame_; | 440 bytes_per_output_frame_; |
| 468 | 441 |
| 469 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; | 442 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; |
| 470 | 443 |
| 471 scoped_refptr<media::DataBuffer> packet = | 444 scoped_refptr<media::DataBuffer> packet = |
| 472 new media::DataBuffer(packet_size_); | 445 new media::DataBuffer(packet_size_); |
| 473 size_t packet_size = | 446 size_t packet_size = |
| 474 shared_data_.OnMoreData( | 447 OnMoreData(this, packet->GetWritableData(), packet->GetBufferSize(), |
| 475 this, packet->GetWritableData(), packet->GetBufferSize(), | 448 AudioBuffersState(buffer_delay, hardware_delay)); |
| 476 AudioBuffersState(buffer_delay, hardware_delay)); | |
| 477 CHECK(packet_size <= packet->GetBufferSize()) << | 449 CHECK(packet_size <= packet->GetBufferSize()) << |
| 478 "Data source overran buffer."; | 450 "Data source overran buffer."; |
| 479 | 451 |
| 480 // This should not happen, but in case it does, drop any trailing bytes | 452 // This should not happen, but in case it does, drop any trailing bytes |
| 481 // that aren't large enough to make a frame. Without this, packet writing | 453 // that aren't large enough to make a frame. Without this, packet writing |
| 482 // may stall because the last few bytes in the packet may never get used by | 454 // may stall because the last few bytes in the packet may never get used by |
| 483 // WritePacket. | 455 // WritePacket. |
| 484 DCHECK(packet_size % bytes_per_frame_ == 0); | 456 DCHECK(packet_size % bytes_per_frame_ == 0); |
| 485 packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_; | 457 packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_; |
| 486 | 458 |
| 487 if (should_downmix_) { | 459 if (should_downmix_) { |
| 488 if (media::FoldChannels(packet->GetWritableData(), | 460 if (media::FoldChannels(packet->GetWritableData(), |
| 489 packet_size, | 461 packet_size, |
| 490 channels_, | 462 channels_, |
| 491 bytes_per_sample_, | 463 bytes_per_sample_, |
| 492 shared_data_.volume())) { | 464 volume())) { |
| 493 // Adjust packet size for downmix. | 465 // Adjust packet size for downmix. |
| 494 packet_size = | 466 packet_size = |
| 495 packet_size / bytes_per_frame_ * bytes_per_output_frame_; | 467 packet_size / bytes_per_frame_ * bytes_per_output_frame_; |
| 496 } else { | 468 } else { |
| 497 LOG(ERROR) << "Folding failed"; | 469 LOG(ERROR) << "Folding failed"; |
| 498 } | 470 } |
| 499 } else { | 471 } else { |
| 500 // TODO(ajwong): Handle other channel orderings. | 472 // TODO(ajwong): Handle other channel orderings. |
| 501 | 473 |
| 502 // Handle channel order for 5.0 audio. | 474 // Handle channel order for 5.0 audio. |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 518 Swizzle51Layout(packet->GetWritableData(), packet_size); | 490 Swizzle51Layout(packet->GetWritableData(), packet_size); |
| 519 } else if (bytes_per_sample_ == 4) { | 491 } else if (bytes_per_sample_ == 4) { |
| 520 Swizzle51Layout(packet->GetWritableData(), packet_size); | 492 Swizzle51Layout(packet->GetWritableData(), packet_size); |
| 521 } | 493 } |
| 522 } | 494 } |
| 523 | 495 |
| 524 media::AdjustVolume(packet->GetWritableData(), | 496 media::AdjustVolume(packet->GetWritableData(), |
| 525 packet_size, | 497 packet_size, |
| 526 channels_, | 498 channels_, |
| 527 bytes_per_sample_, | 499 bytes_per_sample_, |
| 528 shared_data_.volume()); | 500 volume()); |
| 529 } | 501 } |
| 530 | 502 |
| 531 if (packet_size > 0) { | 503 if (packet_size > 0) { |
| 532 packet->SetDataSize(packet_size); | 504 packet->SetDataSize(packet_size); |
| 533 // Add the packet to the buffer. | 505 // Add the packet to the buffer. |
| 534 buffer_->Append(packet); | 506 buffer_->Append(packet); |
| 535 } else { | 507 } else { |
| 536 *source_exhausted = true; | 508 *source_exhausted = true; |
| 537 } | 509 } |
| 538 } | 510 } |
| 539 } | 511 } |
| 540 | 512 |
| 541 void AlsaPcmOutputStream::WritePacket() { | 513 void AlsaPcmOutputStream::WritePacket() { |
| 542 DCHECK_EQ(message_loop_, MessageLoop::current()); | 514 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 543 | 515 |
| 544 // If the device is in error, just eat the bytes. | 516 // If the device is in error, just eat the bytes. |
| 545 if (stop_stream_) { | 517 if (stop_stream_) { |
| 546 buffer_->Clear(); | 518 buffer_->Clear(); |
| 547 return; | 519 return; |
| 548 } | 520 } |
| 549 | 521 |
| 550 if (shared_data_.state() == kIsStopped) { | 522 if (state() == kIsStopped) { |
| 551 return; | 523 return; |
| 552 } | 524 } |
| 553 | 525 |
| 554 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); | 526 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); |
| 555 | 527 |
| 556 const uint8* buffer_data; | 528 const uint8* buffer_data; |
| 557 size_t buffer_size; | 529 size_t buffer_size; |
| 558 if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) { | 530 if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) { |
| 559 buffer_size = buffer_size - (buffer_size % bytes_per_output_frame_); | 531 buffer_size = buffer_size - (buffer_size % bytes_per_output_frame_); |
| 560 snd_pcm_sframes_t frames = buffer_size / bytes_per_output_frame_; | 532 snd_pcm_sframes_t frames = buffer_size / bytes_per_output_frame_; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 572 frames_written, | 544 frames_written, |
| 573 kPcmRecoverIsSilent); | 545 kPcmRecoverIsSilent); |
| 574 } | 546 } |
| 575 | 547 |
| 576 if (frames_written < 0) { | 548 if (frames_written < 0) { |
| 577 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping | 549 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping |
| 578 // the pcm playback? | 550 // the pcm playback? |
| 579 if (frames_written != -EAGAIN) { | 551 if (frames_written != -EAGAIN) { |
| 580 LOG(ERROR) << "Failed to write to pcm device: " | 552 LOG(ERROR) << "Failed to write to pcm device: " |
| 581 << wrapper_->StrError(frames_written); | 553 << wrapper_->StrError(frames_written); |
| 582 shared_data_.OnError(this, frames_written); | 554 OnError(this, frames_written); |
| 583 stop_stream_ = true; | 555 stop_stream_ = true; |
| 584 } | 556 } |
| 585 } else { | 557 } else { |
| 586 if (frames_written > frames) { | 558 if (frames_written > frames) { |
| 587 LOG(WARNING) | 559 LOG(WARNING) |
| 588 << "snd_pcm_writei() has written more frame that we asked."; | 560 << "snd_pcm_writei() has written more frame that we asked."; |
| 589 frames_written = frames; | 561 frames_written = frames; |
| 590 } | 562 } |
| 591 | 563 |
| 592 // Seek forward in the buffer after we've written some data to ALSA. | 564 // Seek forward in the buffer after we've written some data to ALSA. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 603 } | 575 } |
| 604 } | 576 } |
| 605 | 577 |
| 606 void AlsaPcmOutputStream::WriteTask() { | 578 void AlsaPcmOutputStream::WriteTask() { |
| 607 DCHECK_EQ(message_loop_, MessageLoop::current()); | 579 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 608 | 580 |
| 609 if (stop_stream_) { | 581 if (stop_stream_) { |
| 610 return; | 582 return; |
| 611 } | 583 } |
| 612 | 584 |
| 613 if (shared_data_.state() == kIsStopped) { | 585 if (state() == kIsStopped) { |
| 614 return; | 586 return; |
| 615 } | 587 } |
| 616 | 588 |
| 617 bool source_exhausted; | 589 bool source_exhausted; |
| 618 BufferPacket(&source_exhausted); | 590 BufferPacket(&source_exhausted); |
| 619 WritePacket(); | 591 WritePacket(); |
| 620 | 592 |
| 621 ScheduleNextWrite(source_exhausted); | 593 ScheduleNextWrite(source_exhausted); |
| 622 } | 594 } |
| 623 | 595 |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 648 } else { | 620 } else { |
| 649 next_fill_time_ms -= kSleepErrorMilliseconds; | 621 next_fill_time_ms -= kSleepErrorMilliseconds; |
| 650 } | 622 } |
| 651 | 623 |
| 652 // Avoid busy looping if the data source is exhausted. | 624 // Avoid busy looping if the data source is exhausted. |
| 653 if (source_exhausted) { | 625 if (source_exhausted) { |
| 654 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); | 626 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); |
| 655 } | 627 } |
| 656 | 628 |
| 657 // Only schedule more reads/writes if we are still in the playing state. | 629 // Only schedule more reads/writes if we are still in the playing state. |
| 658 if (shared_data_.state() == kIsPlaying) { | 630 if (state() == kIsPlaying) { |
| 659 if (next_fill_time_ms == 0) { | 631 if (next_fill_time_ms == 0) { |
| 660 message_loop_->PostTask( | 632 message_loop_->PostTask( |
| 661 FROM_HERE, | 633 FROM_HERE, |
| 662 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask)); | 634 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::WriteTask)); |
| 663 } else { | 635 } else { |
| 664 // TODO(ajwong): Measure the reliability of the delay interval. Use | 636 // TODO(ajwong): Measure the reliability of the delay interval. Use |
| 665 // base/metrics/histogram.h. | 637 // base/metrics/histogram.h. |
| 666 message_loop_->PostDelayedTask( | 638 message_loop_->PostDelayedTask( |
| 667 FROM_HERE, | 639 FROM_HERE, |
| 668 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask), | 640 method_factory_.NewRunnableMethod(&AlsaPcmOutputStream::WriteTask), |
| 669 next_fill_time_ms); | 641 next_fill_time_ms); |
| 670 } | 642 } |
| 671 } | 643 } |
| 672 } | 644 } |
| 673 | 645 |
| 674 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { | 646 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { |
| 675 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; | 647 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; |
| 676 } | 648 } |
| 677 | 649 |
| 678 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { | 650 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 835 default_channels, sample_rate_, | 807 default_channels, sample_rate_, |
| 836 pcm_format_, latency)) != NULL) { | 808 pcm_format_, latency)) != NULL) { |
| 837 return handle; | 809 return handle; |
| 838 } | 810 } |
| 839 | 811 |
| 840 // Unable to open any device. | 812 // Unable to open any device. |
| 841 device_name_.clear(); | 813 device_name_.clear(); |
| 842 return NULL; | 814 return NULL; |
| 843 } | 815 } |
| 844 | 816 |
| 845 AudioManagerLinux* AlsaPcmOutputStream::manager() { | 817 AudioManagerLinux* AlsaPcmOutputStream::manager() { |
|
scherkus (not reviewing)
2011/06/07 00:19:01
inline this method as it no longer needs to be spl
enal
2011/06/07 17:29:00
Done.
| |
| 846 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 818 CHECK_EQ(MessageLoop::current(), message_loop_); |
| 847 return manager_; | 819 return manager_; |
| 848 } | 820 } |
| 849 | 821 |
| 850 AlsaPcmOutputStream::SharedData::SharedData( | 822 bool AlsaPcmOutputStream::CanTransitionTo(InternalState to) { |
| 851 MessageLoop* state_transition_loop) | |
| 852 : state_(kCreated), | |
| 853 volume_(1.0f), | |
| 854 source_callback_(NULL), | |
| 855 state_transition_loop_(state_transition_loop) { | |
| 856 } | |
| 857 | |
| 858 bool AlsaPcmOutputStream::SharedData::CanTransitionTo(InternalState to) { | |
| 859 base::AutoLock l(lock_); | |
| 860 return CanTransitionTo_Locked(to); | |
| 861 } | |
| 862 | |
| 863 bool AlsaPcmOutputStream::SharedData::CanTransitionTo_Locked( | |
| 864 InternalState to) { | |
| 865 lock_.AssertAcquired(); | |
| 866 | |
| 867 switch (state_) { | 823 switch (state_) { |
| 868 case kCreated: | 824 case kCreated: |
| 869 return to == kIsOpened || to == kIsClosed || to == kInError; | 825 return to == kIsOpened || to == kIsClosed || to == kInError; |
| 870 | 826 |
| 871 case kIsOpened: | 827 case kIsOpened: |
| 872 return to == kIsPlaying || to == kIsStopped || | 828 return to == kIsPlaying || to == kIsStopped || |
| 873 to == kIsClosed || to == kInError; | 829 to == kIsClosed || to == kInError; |
| 874 | 830 |
| 875 case kIsPlaying: | 831 case kIsPlaying: |
| 876 return to == kIsPlaying || to == kIsStopped || | 832 return to == kIsPlaying || to == kIsStopped || |
| 877 to == kIsClosed || to == kInError; | 833 to == kIsClosed || to == kInError; |
| 878 | 834 |
| 879 case kIsStopped: | 835 case kIsStopped: |
| 880 return to == kIsPlaying || to == kIsStopped || | 836 return to == kIsPlaying || to == kIsStopped || |
| 881 to == kIsClosed || to == kInError; | 837 to == kIsClosed || to == kInError; |
| 882 | 838 |
| 883 case kInError: | 839 case kInError: |
| 884 return to == kIsClosed || to == kInError; | 840 return to == kIsClosed || to == kInError; |
| 885 | 841 |
| 886 case kIsClosed: | 842 case kIsClosed: |
| 887 default: | 843 default: |
| 888 return false; | 844 return false; |
| 889 } | 845 } |
| 890 } | 846 } |
| 891 | 847 |
| 892 AlsaPcmOutputStream::InternalState | 848 AlsaPcmOutputStream::InternalState |
| 893 AlsaPcmOutputStream::SharedData::TransitionTo(InternalState to) { | 849 AlsaPcmOutputStream::TransitionTo(InternalState to) { |
| 894 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | 850 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 895 | 851 |
| 896 base::AutoLock l(lock_); | 852 if (!CanTransitionTo(to)) { |
| 897 if (!CanTransitionTo_Locked(to)) { | |
| 898 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; | 853 NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to; |
| 899 state_ = kInError; | 854 state_ = kInError; |
| 900 } else { | 855 } else { |
| 901 state_ = to; | 856 state_ = to; |
| 902 } | 857 } |
| 903 return state_; | 858 return state_; |
| 904 } | 859 } |
| 905 | 860 |
| 906 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::SharedData::state() { | 861 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { |
|
scherkus (not reviewing)
2011/06/07 00:19:01
I'd drop this method as it's private
enal
2011/06/07 17:29:00
It is used in the unit tests, I'd prefer tests to
| |
| 907 base::AutoLock l(lock_); | |
| 908 return state_; | 862 return state_; |
| 909 } | 863 } |
| 910 | 864 |
| 911 float AlsaPcmOutputStream::SharedData::volume() { | 865 float AlsaPcmOutputStream::volume() { |
|
scherkus (not reviewing)
2011/06/07 00:19:01
I'd drop this method as it's private
enal
2011/06/07 17:29:00
Done.
| |
| 912 base::AutoLock l(lock_); | |
| 913 return volume_; | 866 return volume_; |
| 914 } | 867 } |
| 915 | 868 |
| 916 void AlsaPcmOutputStream::SharedData::set_volume(float v) { | 869 void AlsaPcmOutputStream::set_volume(float v) { |
|
scherkus (not reviewing)
2011/06/07 00:19:01
I'd drop this method as it's private
enal
2011/06/07 17:29:00
Done.
| |
| 917 base::AutoLock l(lock_); | |
| 918 volume_ = v; | 870 volume_ = v; |
| 919 } | 871 } |
| 920 | 872 |
| 921 uint32 AlsaPcmOutputStream::SharedData::OnMoreData( | 873 uint32 AlsaPcmOutputStream::OnMoreData(AudioOutputStream* stream, |
|
scherkus (not reviewing)
2011/06/07 00:19:01
it looks like we pass "this" for the stream parame
enal
2011/06/07 17:29:00
Name and arguments are defined in the parent class
scherkus (not reviewing)
2011/06/07 23:01:59
the abstract virtual function is actually from Aud
| |
| 922 AudioOutputStream* stream, uint8* dest, uint32 max_size, | 874 uint8* dest, |
| 923 AudioBuffersState buffers_state) { | 875 uint32 max_size, |
| 924 base::AutoLock l(lock_); | 876 AudioBuffersState buffers_state) { |
| 925 if (source_callback_) { | 877 if (source_callback_) { |
| 926 return source_callback_->OnMoreData(stream, dest, max_size, buffers_state); | 878 return source_callback_->OnMoreData(stream, dest, max_size, buffers_state); |
| 927 } | 879 } |
| 928 | 880 |
| 929 return 0; | 881 return 0; |
| 930 } | 882 } |
| 931 | 883 |
| 932 void AlsaPcmOutputStream::SharedData::OnError(AudioOutputStream* stream, | 884 void AlsaPcmOutputStream::OnError(AudioOutputStream* stream, int code) { |
|
scherkus (not reviewing)
2011/06/07 00:19:01
ditto, maybe RunErrorCallback()
enal
2011/06/07 17:29:00
Name and arguments are defined in the parent class
scherkus (not reviewing)
2011/06/07 23:01:59
Ditto
| |
| 933 int code) { | |
| 934 base::AutoLock l(lock_); | |
| 935 if (source_callback_) { | 885 if (source_callback_) { |
| 936 source_callback_->OnError(stream, code); | 886 source_callback_->OnError(stream, code); |
| 937 } | 887 } |
| 938 } | 888 } |
| 939 | 889 |
| 940 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 890 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 941 // release ownership of the currently registered callback. | 891 // release ownership of the currently registered callback. |
| 942 void AlsaPcmOutputStream::SharedData::set_source_callback( | 892 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
|
scherkus (not reviewing)
2011/06/07 00:19:01
I'd drop this method as it's private
enal
2011/06/07 17:29:00
Called in the unit tests.
| |
| 943 AudioSourceCallback* callback) { | 893 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 944 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | |
| 945 base::AutoLock l(lock_); | |
| 946 source_callback_ = callback; | 894 source_callback_ = callback; |
| 947 } | 895 } |
| OLD | NEW |