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 // THREAD SAFETY | 5 // THREAD SAFETY |
6 // | 6 // |
7 // The AlsaPcmOutputStream object's internal state is accessed by two threads: | 7 // The AlsaPcmOutputStream object's internal state is accessed by two threads: |
8 // | 8 // |
9 // client thread - creates the object and calls the public APIs. | 9 // client thread - creates the object and calls the public APIs. |
10 // message loop thread - executes all the internal tasks including querying | 10 // message loop thread - executes all the internal tasks including querying |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
77 | 77 |
78 #include <algorithm> | 78 #include <algorithm> |
79 | 79 |
80 #include "base/logging.h" | 80 #include "base/logging.h" |
81 #include "base/message_loop.h" | 81 #include "base/message_loop.h" |
82 #include "base/stl_util-inl.h" | 82 #include "base/stl_util-inl.h" |
83 #include "base/time.h" | 83 #include "base/time.h" |
84 #include "media/audio/audio_util.h" | 84 #include "media/audio/audio_util.h" |
85 #include "media/audio/linux/alsa_wrapper.h" | 85 #include "media/audio/linux/alsa_wrapper.h" |
86 #include "media/audio/linux/audio_manager_linux.h" | 86 #include "media/audio/linux/audio_manager_linux.h" |
| 87 #include "media/base/data_buffer.h" |
| 88 #include "media/base/seekable_buffer.h" |
87 | 89 |
88 // Amount of time to wait if we've exhausted the data source. This is to avoid | 90 // Amount of time to wait if we've exhausted the data source. This is to avoid |
89 // busy looping. | 91 // busy looping. |
90 static const uint32 kNoDataSleepMilliseconds = 10; | 92 static const uint32 kNoDataSleepMilliseconds = 10; |
91 | 93 |
92 // According to the linux nanosleep manpage, nanosleep on linux can miss the | 94 // According to the linux nanosleep manpage, nanosleep on linux can miss the |
93 // deadline by up to 10ms because the kernel timeslice is 10ms. Give a 2x | 95 // deadline by up to 10ms because the kernel timeslice is 10ms. Give a 2x |
94 // buffer to compensate for the timeslice, and any additional slowdowns. | 96 // buffer to compensate for the timeslice, and any additional slowdowns. |
95 static const uint32 kSleepErrorMilliseconds = 20; | 97 static const uint32 kSleepErrorMilliseconds = 20; |
96 | 98 |
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
369 void AlsaPcmOutputStream::GetVolume(double* volume) { | 371 void AlsaPcmOutputStream::GetVolume(double* volume) { |
370 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 372 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
371 | 373 |
372 *volume = shared_data_.volume(); | 374 *volume = shared_data_.volume(); |
373 } | 375 } |
374 | 376 |
375 void AlsaPcmOutputStream::OpenTask(uint32 packet_size) { | 377 void AlsaPcmOutputStream::OpenTask(uint32 packet_size) { |
376 DCHECK_EQ(MessageLoop::current(), message_loop_); | 378 DCHECK_EQ(MessageLoop::current(), message_loop_); |
377 | 379 |
378 // Initialize the configuration variables. | 380 // Initialize the configuration variables. |
379 frames_per_packet_ = packet_size / bytes_per_frame_; | 381 packet_size_ = packet_size; |
| 382 frames_per_packet_ = packet_size_ / bytes_per_frame_; |
380 | 383 |
381 // Try to open the device. | 384 // Try to open the device. |
382 micros_per_packet_ = | 385 micros_per_packet_ = |
383 FramesToMicros(packet_size / bytes_per_frame_, sample_rate_); | 386 FramesToMicros(packet_size / bytes_per_frame_, sample_rate_); |
384 latency_micros_ = std::max(AlsaPcmOutputStream::kMinLatencyMicros, | 387 latency_micros_ = std::max(AlsaPcmOutputStream::kMinLatencyMicros, |
385 micros_per_packet_ * 2); | 388 micros_per_packet_ * 2); |
386 if (requested_device_name_ == kAutoSelectDevice) { | 389 if (requested_device_name_ == kAutoSelectDevice) { |
387 playback_handle_ = AutoSelectDevice(latency_micros_); | 390 playback_handle_ = AutoSelectDevice(latency_micros_); |
388 if (playback_handle_) { | 391 if (playback_handle_) { |
389 LOG(INFO) << "Auto-selected device: " << device_name_; | 392 LOG(INFO) << "Auto-selected device: " << device_name_; |
390 } | 393 } |
391 } else { | 394 } else { |
392 device_name_ = requested_device_name_; | 395 device_name_ = requested_device_name_; |
393 playback_handle_ = OpenDevice(device_name_, channels_, latency_micros_); | 396 playback_handle_ = OpenDevice(device_name_, channels_, latency_micros_); |
394 } | 397 } |
395 | 398 |
396 // Finish initializing the stream if the device was opened successfully. | 399 // Finish initializing the stream if the device was opened successfully. |
397 if (playback_handle_ == NULL) { | 400 if (playback_handle_ == NULL) { |
398 stop_stream_ = true; | 401 stop_stream_ = true; |
399 } else { | 402 } else { |
400 packet_.reset(new Packet(packet_size)); | 403 bytes_per_output_frame_ = should_downmix_ ? 2 * bytes_per_sample_ : |
401 if (should_downmix_) { | 404 bytes_per_frame_; |
402 bytes_per_output_frame_ = 2 * bytes_per_sample_; | 405 uint32 output_packet_size = frames_per_packet_ * bytes_per_output_frame_; |
| 406 buffer_.reset(new media::SeekableBuffer(0, output_packet_size)); |
| 407 |
| 408 // Get alsa buffer size. |
| 409 snd_pcm_uframes_t buffer_size; |
| 410 snd_pcm_uframes_t period_size; |
| 411 int error = wrapper_->PcmGetParams(playback_handle_, &buffer_size, |
| 412 &period_size); |
| 413 if (error < 0) { |
| 414 LOG(ERROR) << "Failed to get playback buffer size from ALSA: " |
| 415 << wrapper_->StrError(error); |
| 416 alsa_buffer_frames_ = frames_per_packet_; |
| 417 } else { |
| 418 alsa_buffer_frames_ = buffer_size; |
403 } | 419 } |
404 } | 420 } |
405 } | 421 } |
406 | 422 |
407 void AlsaPcmOutputStream::StartTask() { | 423 void AlsaPcmOutputStream::StartTask() { |
408 DCHECK_EQ(MessageLoop::current(), message_loop_); | 424 DCHECK_EQ(MessageLoop::current(), message_loop_); |
409 | 425 |
410 if (stop_stream_) { | 426 if (stop_stream_) { |
411 return; | 427 return; |
412 } | 428 } |
(...skipping 11 matching lines...) Expand all Loading... |
424 | 440 |
425 error = wrapper_->PcmPrepare(playback_handle_); | 441 error = wrapper_->PcmPrepare(playback_handle_); |
426 if (error < 0 && error != -EAGAIN) { | 442 if (error < 0 && error != -EAGAIN) { |
427 LOG(ERROR) << "Failure preparing stream (" | 443 LOG(ERROR) << "Failure preparing stream (" |
428 << wrapper_->PcmName(playback_handle_) << "): " | 444 << wrapper_->PcmName(playback_handle_) << "): " |
429 << wrapper_->StrError(error); | 445 << wrapper_->StrError(error); |
430 stop_stream_ = true; | 446 stop_stream_ = true; |
431 return; | 447 return; |
432 } | 448 } |
433 | 449 |
434 // Do a best-effort pre-roll to fill the buffer. Use integer rounding to find | 450 ScheduleNextWrite(); |
435 // the maximum number of full packets that can fit into the buffer. | |
436 // | |
437 // TODO(ajwong): Handle EAGAIN. | |
438 const uint32 num_preroll = latency_micros_ / micros_per_packet_; | |
439 for (uint32 i = 0; i < num_preroll; ++i) { | |
440 BufferPacket(packet_.get()); | |
441 WritePacket(packet_.get()); | |
442 } | |
443 | |
444 ScheduleNextWrite(packet_.get()); | |
445 } | 451 } |
446 | 452 |
447 void AlsaPcmOutputStream::CloseTask() { | 453 void AlsaPcmOutputStream::CloseTask() { |
448 // NOTE: Keep this function idempotent to handle errors that might cause | 454 // NOTE: Keep this function idempotent to handle errors that might cause |
449 // multiple CloseTasks to be posted. | 455 // multiple CloseTasks to be posted. |
450 DCHECK_EQ(MessageLoop::current(), message_loop_); | 456 DCHECK_EQ(MessageLoop::current(), message_loop_); |
451 | 457 |
452 // Shutdown the audio device. | 458 // Shutdown the audio device. |
453 if (playback_handle_ && !CloseDevice(playback_handle_)) { | 459 if (playback_handle_ && !CloseDevice(playback_handle_)) { |
454 LOG(WARNING) << "Unable to close audio device. Leaking handle."; | 460 LOG(WARNING) << "Unable to close audio device. Leaking handle."; |
455 } | 461 } |
456 playback_handle_ = NULL; | 462 playback_handle_ = NULL; |
457 | 463 |
458 // Release the buffer. | 464 // Release the buffer. |
459 packet_.reset(); | 465 buffer_.reset(); |
460 | 466 |
461 // Signal anything that might already be scheduled to stop. | 467 // Signal anything that might already be scheduled to stop. |
462 stop_stream_ = true; | 468 stop_stream_ = true; |
463 } | 469 } |
464 | 470 |
465 void AlsaPcmOutputStream::BufferPacket(Packet* packet) { | 471 void AlsaPcmOutputStream::BufferPacket() { |
466 DCHECK_EQ(MessageLoop::current(), message_loop_); | 472 DCHECK_EQ(MessageLoop::current(), message_loop_); |
467 | 473 |
468 // If stopped, simulate a 0-lengthed packet. | 474 // If stopped, simulate a 0-lengthed packet. |
469 if (stop_stream_) { | 475 if (stop_stream_) { |
470 packet->used = packet->size = 0; | 476 buffer_->Clear(); |
471 return; | 477 return; |
472 } | 478 } |
473 | 479 |
474 // Request more data if we don't have any cached. | 480 // Request more data if we have capacity. |
475 if (packet->used >= packet->size) { | 481 if (buffer_->forward_capacity() > buffer_->forward_bytes()) { |
476 // Before making a request to source for data. We need to determine the | 482 // Before making a request to source for data. We need to determine the |
477 // delay (in bytes) for the requested data to be played. | 483 // delay (in bytes) for the requested data to be played. |
478 snd_pcm_sframes_t delay = 0; | 484 snd_pcm_sframes_t delay = buffer_->forward_bytes() * bytes_per_frame_ / |
| 485 bytes_per_output_frame_ + GetCurrentDelay() * bytes_per_output_frame_; |
479 | 486 |
480 // Don't query ALSA's delay if we have underrun since it'll be jammed at | 487 media::DataBuffer* packet = new media::DataBuffer(packet_size_); |
481 // some non-zero value and potentially even negative! | 488 size_t packet_size = |
482 if (wrapper_->PcmState(playback_handle_) != SND_PCM_STATE_XRUN) { | 489 shared_data_.OnMoreData(this, packet->GetWritableData(), |
483 int error = wrapper_->PcmDelay(playback_handle_, &delay); | 490 packet->GetBufferSize(), delay); |
484 if (error >= 0) { | 491 CHECK(packet_size <= packet->GetBufferSize()) << |
485 // Convert frames to bytes, but watch out for those negatives! | 492 "Data source overran buffer."; |
486 delay = (delay < 0 ? 0 : delay) * bytes_per_output_frame_; | |
487 } else { | |
488 // Assume a delay of zero and attempt to recover the device. | |
489 delay = 0; | |
490 error = wrapper_->PcmRecover(playback_handle_, | |
491 error, | |
492 kPcmRecoverIsSilent); | |
493 if (error < 0) { | |
494 LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error); | |
495 } | |
496 } | |
497 } | |
498 | |
499 packet->used = 0; | |
500 packet->size = shared_data_.OnMoreData(this, packet->buffer.get(), | |
501 packet->capacity, delay); | |
502 CHECK(packet->size <= packet->capacity) << "Data source overran buffer."; | |
503 | 493 |
504 // This should not happen, but incase it does, drop any trailing bytes | 494 // This should not happen, but incase it does, drop any trailing bytes |
505 // that aren't large enough to make a frame. Without this, packet writing | 495 // that aren't large enough to make a frame. Without this, packet writing |
506 // may stall because the last few bytes in the packet may never get used by | 496 // may stall because the last few bytes in the packet may never get used by |
507 // WritePacket. | 497 // WritePacket. |
508 DCHECK(packet->size % bytes_per_frame_ == 0); | 498 DCHECK(packet_size % bytes_per_frame_ == 0); |
509 packet->size = (packet->size / bytes_per_frame_) * bytes_per_frame_; | 499 packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_; |
510 | 500 |
511 if (should_downmix_) { | 501 if (should_downmix_) { |
512 if (media::FoldChannels(packet->buffer.get(), | 502 if (media::FoldChannels(packet->GetWritableData(), |
513 packet->size, | 503 packet_size, |
514 channels_, | 504 channels_, |
515 bytes_per_sample_, | 505 bytes_per_sample_, |
516 shared_data_.volume())) { | 506 shared_data_.volume())) { |
517 // Adjust packet size for downmix. | 507 // Adjust packet size for downmix. |
518 packet->size = | 508 packet_size = |
519 packet->size / bytes_per_frame_ * bytes_per_output_frame_; | 509 packet_size / bytes_per_frame_ * bytes_per_output_frame_; |
520 } else { | 510 } else { |
521 LOG(ERROR) << "Folding failed"; | 511 LOG(ERROR) << "Folding failed"; |
522 } | 512 } |
523 } else { | 513 } else { |
524 // TODO(ajwong): Handle other channel orderings. | 514 // TODO(ajwong): Handle other channel orderings. |
525 | 515 |
526 // Handle channel order for 5.0 audio. | 516 // Handle channel order for 5.0 audio. |
527 if (channels_ == 5) { | 517 if (channels_ == 5) { |
528 if (bytes_per_sample_ == 1) { | 518 if (bytes_per_sample_ == 1) { |
529 Swizzle50Layout(reinterpret_cast<uint8*>(packet->buffer.get()), | 519 Swizzle50Layout(packet->GetWritableData(), packet_size); |
530 packet->size); | |
531 } else if (bytes_per_sample_ == 2) { | 520 } else if (bytes_per_sample_ == 2) { |
532 Swizzle50Layout(reinterpret_cast<int16*>(packet->buffer.get()), | 521 Swizzle50Layout(packet->GetWritableData(), packet_size); |
533 packet->size); | |
534 } else if (bytes_per_sample_ == 4) { | 522 } else if (bytes_per_sample_ == 4) { |
535 Swizzle50Layout(reinterpret_cast<int32*>(packet->buffer.get()), | 523 Swizzle50Layout(packet->GetWritableData(), packet_size); |
536 packet->size); | |
537 } | 524 } |
538 } | 525 } |
539 | 526 |
540 // Handle channel order for 5.1 audio. | 527 // Handle channel order for 5.1 audio. |
541 if (channels_ == 6) { | 528 if (channels_ == 6) { |
542 if (bytes_per_sample_ == 1) { | 529 if (bytes_per_sample_ == 1) { |
543 Swizzle51Layout(reinterpret_cast<uint8*>(packet->buffer.get()), | 530 Swizzle51Layout(packet->GetWritableData(), packet_size); |
544 packet->size); | |
545 } else if (bytes_per_sample_ == 2) { | 531 } else if (bytes_per_sample_ == 2) { |
546 Swizzle51Layout(reinterpret_cast<int16*>(packet->buffer.get()), | 532 Swizzle51Layout(packet->GetWritableData(), packet_size); |
547 packet->size); | |
548 } else if (bytes_per_sample_ == 4) { | 533 } else if (bytes_per_sample_ == 4) { |
549 Swizzle51Layout(reinterpret_cast<int32*>(packet->buffer.get()), | 534 Swizzle51Layout(packet->GetWritableData(), packet_size); |
550 packet->size); | |
551 } | 535 } |
552 } | 536 } |
553 | 537 |
554 media::AdjustVolume(packet->buffer.get(), | 538 media::AdjustVolume(packet->GetWritableData(), |
555 packet->size, | 539 packet_size, |
556 channels_, | 540 channels_, |
557 bytes_per_sample_, | 541 bytes_per_sample_, |
558 shared_data_.volume()); | 542 shared_data_.volume()); |
| 543 |
| 544 packet->SetDataSize(packet_size); |
| 545 |
| 546 // Add the packet to the buffer. |
| 547 buffer_->Append(packet); |
559 } | 548 } |
560 } | 549 } |
561 } | 550 } |
562 | 551 |
563 void AlsaPcmOutputStream::WritePacket(Packet* packet) { | 552 void AlsaPcmOutputStream::WritePacket() { |
564 DCHECK_EQ(MessageLoop::current(), message_loop_); | 553 DCHECK_EQ(MessageLoop::current(), message_loop_); |
565 | 554 |
566 CHECK(packet->size % bytes_per_output_frame_ == 0); | |
567 | |
568 // If the device is in error, just eat the bytes. | 555 // If the device is in error, just eat the bytes. |
569 if (stop_stream_) { | 556 if (stop_stream_) { |
570 packet->used = packet->size; | 557 buffer_->Clear(); |
571 return; | 558 return; |
572 } | 559 } |
573 | 560 |
574 if (packet->used < packet->size) { | 561 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); |
575 char* buffer_pos = packet->buffer.get() + packet->used; | 562 |
576 snd_pcm_sframes_t frames = FramesInPacket(*packet, bytes_per_output_frame_); | 563 const uint8* buffer_data; |
| 564 size_t buffer_size; |
| 565 if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) { |
| 566 buffer_size = buffer_size - (buffer_size % bytes_per_output_frame_); |
| 567 snd_pcm_sframes_t frames = buffer_size / bytes_per_output_frame_; |
577 | 568 |
578 DCHECK_GT(frames, 0); | 569 DCHECK_GT(frames, 0); |
579 | 570 |
580 snd_pcm_sframes_t frames_written = | 571 snd_pcm_sframes_t frames_written = |
581 wrapper_->PcmWritei(playback_handle_, buffer_pos, frames); | 572 wrapper_->PcmWritei(playback_handle_, buffer_data, frames); |
582 if (frames_written < 0) { | 573 if (frames_written < 0) { |
583 // Attempt once to immediately recover from EINTR, | 574 // Attempt once to immediately recover from EINTR, |
584 // EPIPE (overrun/underrun), ESTRPIPE (stream suspended). WritePacket | 575 // EPIPE (overrun/underrun), ESTRPIPE (stream suspended). WritePacket |
585 // will eventually be called again, so eventual recovery will happen if | 576 // will eventually be called again, so eventual recovery will happen if |
586 // muliple retries are required. | 577 // muliple retries are required. |
587 frames_written = wrapper_->PcmRecover(playback_handle_, | 578 frames_written = wrapper_->PcmRecover(playback_handle_, |
588 frames_written, | 579 frames_written, |
589 kPcmRecoverIsSilent); | 580 kPcmRecoverIsSilent); |
590 } | 581 } |
591 | 582 |
592 if (frames_written < 0) { | 583 if (frames_written < 0) { |
593 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping | 584 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping |
594 // the pcm playback? | 585 // the pcm playback? |
595 if (frames_written != -EAGAIN) { | 586 if (frames_written != -EAGAIN) { |
596 LOG(ERROR) << "Failed to write to pcm device: " | 587 LOG(ERROR) << "Failed to write to pcm device: " |
597 << wrapper_->StrError(frames_written); | 588 << wrapper_->StrError(frames_written); |
598 shared_data_.OnError(this, frames_written); | 589 shared_data_.OnError(this, frames_written); |
599 stop_stream_ = true; | 590 stop_stream_ = true; |
600 } | 591 } |
601 } else { | 592 } else { |
602 packet->used += frames_written * bytes_per_output_frame_; | 593 if (frames_written > frames) { |
| 594 LOG(WARNING) |
| 595 << "snd_pcm_writei() has written more frame that we asked."; |
| 596 frames_written = frames; |
| 597 } |
| 598 |
| 599 // Seek forward in the buffer after we've written some data to ALSA. |
| 600 buffer_->Seek(frames_written * bytes_per_output_frame_); |
603 } | 601 } |
604 } | 602 } |
605 } | 603 } |
606 | 604 |
607 void AlsaPcmOutputStream::WriteTask() { | 605 void AlsaPcmOutputStream::WriteTask() { |
608 DCHECK_EQ(MessageLoop::current(), message_loop_); | 606 DCHECK_EQ(MessageLoop::current(), message_loop_); |
609 | 607 |
610 if (stop_stream_) { | 608 if (stop_stream_) { |
611 return; | 609 return; |
612 } | 610 } |
613 | 611 |
614 BufferPacket(packet_.get()); | 612 BufferPacket(); |
615 WritePacket(packet_.get()); | 613 WritePacket(); |
616 | 614 |
617 ScheduleNextWrite(packet_.get()); | 615 ScheduleNextWrite(); |
618 } | 616 } |
619 | 617 |
620 void AlsaPcmOutputStream::ScheduleNextWrite(Packet* current_packet) { | 618 void AlsaPcmOutputStream::ScheduleNextWrite() { |
621 DCHECK_EQ(MessageLoop::current(), message_loop_); | 619 DCHECK_EQ(MessageLoop::current(), message_loop_); |
622 | 620 |
623 if (stop_stream_) { | 621 if (stop_stream_) { |
624 return; | 622 return; |
625 } | 623 } |
626 | 624 |
627 // Calculate when we should have enough buffer for another packet of data. | 625 // Next write is scheduled for the moment when half of the buffer is |
628 // Make sure to take into consideration down-mixing. | 626 // available. |
629 uint32 frames_leftover = | 627 uint32 frames_avail_wanted = alsa_buffer_frames_ / 2; |
630 FramesInPacket(*current_packet, bytes_per_output_frame_); | |
631 uint32 frames_avail_wanted = | |
632 (frames_leftover > 0) ? frames_leftover : frames_per_packet_; | |
633 uint32 available_frames = GetAvailableFrames(); | 628 uint32 available_frames = GetAvailableFrames(); |
634 uint32 next_fill_time_ms = 0; | 629 uint32 next_fill_time_ms = 0; |
635 | 630 |
636 // It's possible to have more frames available than what we want, in which | 631 // It's possible to have more frames available than what we want, in which |
637 // case we'll leave our |next_fill_time_ms| at 0ms. | 632 // case we'll leave our |next_fill_time_ms| at 0ms. |
638 if (available_frames < frames_avail_wanted) { | 633 if (available_frames < frames_avail_wanted) { |
639 uint32 frames_until_empty_enough = frames_avail_wanted - available_frames; | 634 uint32 frames_until_empty_enough = frames_avail_wanted - available_frames; |
640 next_fill_time_ms = | 635 next_fill_time_ms = |
641 FramesToMillis(frames_until_empty_enough, sample_rate_); | 636 FramesToMillis(frames_until_empty_enough, sample_rate_); |
642 } | 637 } |
643 | 638 |
644 // Adjust for timer resolution issues. | 639 // Adjust for timer resolution issues. |
645 if (next_fill_time_ms < kSleepErrorMilliseconds) { | 640 if (next_fill_time_ms < kSleepErrorMilliseconds) { |
646 next_fill_time_ms = 0; | 641 next_fill_time_ms = 0; |
647 } else { | 642 } else { |
648 next_fill_time_ms -= kSleepErrorMilliseconds; | 643 next_fill_time_ms -= kSleepErrorMilliseconds; |
649 } | 644 } |
650 | 645 |
651 // Avoid busy looping if the data source is exhausted. | 646 // Avoid busy looping if the data source is exhausted. |
652 if (current_packet->size == 0) { | 647 if (buffer_->forward_bytes() == 0) { |
653 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); | 648 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); |
654 } | 649 } |
655 | 650 |
656 // Wake up sooner than should be necessary to avoid stutter. | |
657 next_fill_time_ms /= 2; // TODO(fbarchard): Remove this hack. | |
658 | |
659 // Only schedule more reads/writes if we are still in the playing state. | 651 // Only schedule more reads/writes if we are still in the playing state. |
660 if (shared_data_.state() == kIsPlaying) { | 652 if (shared_data_.state() == kIsPlaying) { |
661 if (next_fill_time_ms == 0) { | 653 if (next_fill_time_ms == 0) { |
662 message_loop_->PostTask( | 654 message_loop_->PostTask( |
663 FROM_HERE, | 655 FROM_HERE, |
664 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask)); | 656 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask)); |
665 } else { | 657 } else { |
666 // TODO(ajwong): Measure the reliability of the delay interval. Use | 658 // TODO(ajwong): Measure the reliability of the delay interval. Use |
667 // base/histogram.h. | 659 // base/histogram.h. |
668 message_loop_->PostDelayedTask( | 660 message_loop_->PostDelayedTask( |
669 FROM_HERE, | 661 FROM_HERE, |
670 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask), | 662 NewRunnableMethod(this, &AlsaPcmOutputStream::WriteTask), |
671 next_fill_time_ms); | 663 next_fill_time_ms); |
672 } | 664 } |
673 } | 665 } |
674 } | 666 } |
675 | 667 |
676 uint32 AlsaPcmOutputStream::FramesInPacket(const Packet& packet, | |
677 uint32 bytes_per_frame) { | |
678 return (packet.size - packet.used) / bytes_per_frame; | |
679 } | |
680 | |
681 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { | 668 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { |
682 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; | 669 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; |
683 } | 670 } |
684 | 671 |
685 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { | 672 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { |
686 return frames * base::Time::kMillisecondsPerSecond / sample_rate; | 673 return frames * base::Time::kMillisecondsPerSecond / sample_rate; |
687 } | 674 } |
688 | 675 |
689 std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels) { | 676 std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels) { |
690 // Constants specified by the ALSA API for device hints. | 677 // Constants specified by the ALSA API for device hints. |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
798 } | 785 } |
799 if (available_frames < 0) { | 786 if (available_frames < 0) { |
800 LOG(ERROR) << "Failed querying available frames. Assuming 0: " | 787 LOG(ERROR) << "Failed querying available frames. Assuming 0: " |
801 << wrapper_->StrError(available_frames); | 788 << wrapper_->StrError(available_frames); |
802 return 0; | 789 return 0; |
803 } | 790 } |
804 | 791 |
805 return available_frames; | 792 return available_frames; |
806 } | 793 } |
807 | 794 |
| 795 snd_pcm_sframes_t AlsaPcmOutputStream::GetCurrentDelay() { |
| 796 snd_pcm_sframes_t delay = 0; |
| 797 |
| 798 // Don't query ALSA's delay if we have underrun since it'll be jammed at |
| 799 // some non-zero value and potentially even negative! |
| 800 if (wrapper_->PcmState(playback_handle_) != SND_PCM_STATE_XRUN) { |
| 801 int error = wrapper_->PcmDelay(playback_handle_, &delay); |
| 802 if (error < 0) { |
| 803 // Assume a delay of zero and attempt to recover the device. |
| 804 delay = 0; |
| 805 error = wrapper_->PcmRecover(playback_handle_, |
| 806 error, |
| 807 kPcmRecoverIsSilent); |
| 808 if (error < 0) { |
| 809 LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error); |
| 810 } |
| 811 } |
| 812 if (delay < 0) |
| 813 delay = 0; |
| 814 } |
| 815 return delay; |
| 816 } |
| 817 |
808 snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) { | 818 snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) { |
809 // For auto-selection: | 819 // For auto-selection: |
810 // 1) Attempt to open a device that best matches the number of channels | 820 // 1) Attempt to open a device that best matches the number of channels |
811 // requested. | 821 // requested. |
812 // 2) If that fails, attempt the "plug:" version of it incase ALSA can | 822 // 2) If that fails, attempt the "plug:" version of it incase ALSA can |
813 // remap do some software conversion to make it work. | 823 // remap do some software conversion to make it work. |
814 // 3) Fallback to kDefaultDevice. | 824 // 3) Fallback to kDefaultDevice. |
815 // 4) If that fails too, try the "plug:" version of kDefaultDevice. | 825 // 4) If that fails too, try the "plug:" version of kDefaultDevice. |
816 // 5) Give up. | 826 // 5) Give up. |
817 snd_pcm_t* handle = NULL; | 827 snd_pcm_t* handle = NULL; |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
963 } | 973 } |
964 | 974 |
965 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 975 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
966 // release ownership of the currently registered callback. | 976 // release ownership of the currently registered callback. |
967 void AlsaPcmOutputStream::SharedData::set_source_callback( | 977 void AlsaPcmOutputStream::SharedData::set_source_callback( |
968 AudioSourceCallback* callback) { | 978 AudioSourceCallback* callback) { |
969 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | 979 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); |
970 AutoLock l(lock_); | 980 AutoLock l(lock_); |
971 source_callback_ = callback; | 981 source_callback_ = callback; |
972 } | 982 } |
OLD | NEW |