OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 // AlsaPcmOutputStream object is *not* thread-safe and should only be used | 7 // AlsaPcmOutputStream object is *not* thread-safe and should only be used |
8 // from the audio thread. We DCHECK on this assumption whenever we can. | 8 // from the audio thread. We DCHECK on this assumption whenever we can. |
9 // | 9 // |
10 // SEMANTICS OF Close() | 10 // SEMANTICS OF Close() |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 // based on the number of channels requested. NULL is returned if no device | 71 // based on the number of channels requested. NULL is returned if no device |
72 // can be found to match the channel numbers. In this case, using | 72 // can be found to match the channel numbers. In this case, using |
73 // kDefaultDevice is probably the best bet. | 73 // kDefaultDevice is probably the best bet. |
74 // | 74 // |
75 // A five channel source is assumed to be surround50 instead of surround41 | 75 // A five channel source is assumed to be surround50 instead of surround41 |
76 // (which is also 5 channels). | 76 // (which is also 5 channels). |
77 // | 77 // |
78 // TODO(ajwong): The source data should have enough info to tell us if we want | 78 // TODO(ajwong): The source data should have enough info to tell us if we want |
79 // surround41 versus surround51, etc., instead of needing us to guess based on | 79 // surround41 versus surround51, etc., instead of needing us to guess based on |
80 // channel number. Fix API to pass that data down. | 80 // channel number. Fix API to pass that data down. |
81 static const char* GuessSpecificDeviceName(uint32 channels) { | 81 static const char* GuessSpecificDeviceName(uint32_t channels) { |
82 switch (channels) { | 82 switch (channels) { |
83 case 8: | 83 case 8: |
84 return "surround71"; | 84 return "surround71"; |
85 | 85 |
86 case 7: | 86 case 7: |
87 return "surround70"; | 87 return "surround70"; |
88 | 88 |
89 case 6: | 89 case 6: |
90 return "surround51"; | 90 return "surround51"; |
91 | 91 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 }; | 124 }; |
125 return os; | 125 return os; |
126 } | 126 } |
127 | 127 |
128 const char AlsaPcmOutputStream::kDefaultDevice[] = "default"; | 128 const char AlsaPcmOutputStream::kDefaultDevice[] = "default"; |
129 const char AlsaPcmOutputStream::kAutoSelectDevice[] = ""; | 129 const char AlsaPcmOutputStream::kAutoSelectDevice[] = ""; |
130 const char AlsaPcmOutputStream::kPlugPrefix[] = "plug:"; | 130 const char AlsaPcmOutputStream::kPlugPrefix[] = "plug:"; |
131 | 131 |
132 // We use 40ms as our minimum required latency. If it is needed, we may be able | 132 // We use 40ms as our minimum required latency. If it is needed, we may be able |
133 // to get it down to 20ms. | 133 // to get it down to 20ms. |
134 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000; | 134 const uint32_t AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000; |
135 | 135 |
136 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, | 136 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, |
137 const AudioParameters& params, | 137 const AudioParameters& params, |
138 AlsaWrapper* wrapper, | 138 AlsaWrapper* wrapper, |
139 AudioManagerBase* manager) | 139 AudioManagerBase* manager) |
140 : requested_device_name_(device_name), | 140 : requested_device_name_(device_name), |
141 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())), | 141 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())), |
142 channels_(params.channels()), | 142 channels_(params.channels()), |
143 channel_layout_(params.channel_layout()), | 143 channel_layout_(params.channel_layout()), |
144 sample_rate_(params.sample_rate()), | 144 sample_rate_(params.sample_rate()), |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 | 215 |
216 // Finish initializing the stream if the device was opened successfully. | 216 // Finish initializing the stream if the device was opened successfully. |
217 if (playback_handle_ == NULL) { | 217 if (playback_handle_ == NULL) { |
218 stop_stream_ = true; | 218 stop_stream_ = true; |
219 TransitionTo(kInError); | 219 TransitionTo(kInError); |
220 return false; | 220 return false; |
221 } | 221 } |
222 bytes_per_output_frame_ = | 222 bytes_per_output_frame_ = |
223 channel_mixer_ ? mixed_audio_bus_->channels() * bytes_per_sample_ | 223 channel_mixer_ ? mixed_audio_bus_->channels() * bytes_per_sample_ |
224 : bytes_per_frame_; | 224 : bytes_per_frame_; |
225 uint32 output_packet_size = frames_per_packet_ * bytes_per_output_frame_; | 225 uint32_t output_packet_size = frames_per_packet_ * bytes_per_output_frame_; |
226 buffer_.reset(new media::SeekableBuffer(0, output_packet_size)); | 226 buffer_.reset(new media::SeekableBuffer(0, output_packet_size)); |
227 | 227 |
228 // Get alsa buffer size. | 228 // Get alsa buffer size. |
229 snd_pcm_uframes_t buffer_size; | 229 snd_pcm_uframes_t buffer_size; |
230 snd_pcm_uframes_t period_size; | 230 snd_pcm_uframes_t period_size; |
231 int error = | 231 int error = |
232 wrapper_->PcmGetParams(playback_handle_, &buffer_size, &period_size); | 232 wrapper_->PcmGetParams(playback_handle_, &buffer_size, &period_size); |
233 if (error < 0) { | 233 if (error < 0) { |
234 LOG(ERROR) << "Failed to get playback buffer size from ALSA: " | 234 LOG(ERROR) << "Failed to get playback buffer size from ALSA: " |
235 << wrapper_->StrError(error); | 235 << wrapper_->StrError(error); |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
351 return; | 351 return; |
352 } | 352 } |
353 | 353 |
354 *source_exhausted = false; | 354 *source_exhausted = false; |
355 | 355 |
356 // Request more data only when we run out of data in the buffer, because | 356 // Request more data only when we run out of data in the buffer, because |
357 // WritePacket() consumes only the current chunk of data. | 357 // WritePacket() consumes only the current chunk of data. |
358 if (!buffer_->forward_bytes()) { | 358 if (!buffer_->forward_bytes()) { |
359 // Before making a request to source for data we need to determine the | 359 // Before making a request to source for data we need to determine the |
360 // delay (in bytes) for the requested data to be played. | 360 // delay (in bytes) for the requested data to be played. |
361 const uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; | 361 const uint32_t hardware_delay = GetCurrentDelay() * bytes_per_frame_; |
362 | 362 |
363 scoped_refptr<media::DataBuffer> packet = | 363 scoped_refptr<media::DataBuffer> packet = |
364 new media::DataBuffer(packet_size_); | 364 new media::DataBuffer(packet_size_); |
365 int frames_filled = RunDataCallback( | 365 int frames_filled = RunDataCallback( |
366 audio_bus_.get(), hardware_delay); | 366 audio_bus_.get(), hardware_delay); |
367 | 367 |
368 size_t packet_size = frames_filled * bytes_per_frame_; | 368 size_t packet_size = frames_filled * bytes_per_frame_; |
369 DCHECK_LE(packet_size, packet_size_); | 369 DCHECK_LE(packet_size, packet_size_); |
370 | 370 |
371 // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer; | 371 // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
422 if (stop_stream_) { | 422 if (stop_stream_) { |
423 buffer_->Clear(); | 423 buffer_->Clear(); |
424 return; | 424 return; |
425 } | 425 } |
426 | 426 |
427 if (state() != kIsPlaying) | 427 if (state() != kIsPlaying) |
428 return; | 428 return; |
429 | 429 |
430 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); | 430 CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u); |
431 | 431 |
432 const uint8* buffer_data; | 432 const uint8_t* buffer_data; |
433 int buffer_size; | 433 int buffer_size; |
434 if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) { | 434 if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) { |
435 snd_pcm_sframes_t frames = std::min( | 435 snd_pcm_sframes_t frames = std::min( |
436 static_cast<snd_pcm_sframes_t>(buffer_size / bytes_per_output_frame_), | 436 static_cast<snd_pcm_sframes_t>(buffer_size / bytes_per_output_frame_), |
437 GetAvailableFrames()); | 437 GetAvailableFrames()); |
438 | 438 |
439 if (!frames) | 439 if (!frames) |
440 return; | 440 return; |
441 | 441 |
442 snd_pcm_sframes_t frames_written = | 442 snd_pcm_sframes_t frames_written = |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
489 | 489 |
490 ScheduleNextWrite(source_exhausted); | 490 ScheduleNextWrite(source_exhausted); |
491 } | 491 } |
492 | 492 |
493 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { | 493 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { |
494 DCHECK(IsOnAudioThread()); | 494 DCHECK(IsOnAudioThread()); |
495 | 495 |
496 if (stop_stream_ || state() != kIsPlaying) | 496 if (stop_stream_ || state() != kIsPlaying) |
497 return; | 497 return; |
498 | 498 |
499 const uint32 kTargetFramesAvailable = alsa_buffer_frames_ / 2; | 499 const uint32_t kTargetFramesAvailable = alsa_buffer_frames_ / 2; |
500 uint32 available_frames = GetAvailableFrames(); | 500 uint32_t available_frames = GetAvailableFrames(); |
501 | 501 |
502 base::TimeDelta next_fill_time; | 502 base::TimeDelta next_fill_time; |
503 if (buffer_->forward_bytes() && available_frames) { | 503 if (buffer_->forward_bytes() && available_frames) { |
504 // If we've got data available and ALSA has room, deliver it immediately. | 504 // If we've got data available and ALSA has room, deliver it immediately. |
505 next_fill_time = base::TimeDelta(); | 505 next_fill_time = base::TimeDelta(); |
506 } else if (buffer_->forward_bytes()) { | 506 } else if (buffer_->forward_bytes()) { |
507 // If we've got data available and no room, poll until room is available. | 507 // If we've got data available and no room, poll until room is available. |
508 // Polling in this manner allows us to ensure a more consistent callback | 508 // Polling in this manner allows us to ensure a more consistent callback |
509 // schedule. In testing this yields a variance of +/- 5ms versus the non- | 509 // schedule. In testing this yields a variance of +/- 5ms versus the non- |
510 // polling strategy which is around +/- 30ms and bimodal. | 510 // polling strategy which is around +/- 30ms and bimodal. |
(...skipping 18 matching lines...) Expand all Loading... |
529 next_fill_time); | 529 next_fill_time); |
530 } | 530 } |
531 | 531 |
532 // static | 532 // static |
533 base::TimeDelta AlsaPcmOutputStream::FramesToTimeDelta(int frames, | 533 base::TimeDelta AlsaPcmOutputStream::FramesToTimeDelta(int frames, |
534 double sample_rate) { | 534 double sample_rate) { |
535 return base::TimeDelta::FromMicroseconds( | 535 return base::TimeDelta::FromMicroseconds( |
536 frames * base::Time::kMicrosecondsPerSecond / sample_rate); | 536 frames * base::Time::kMicrosecondsPerSecond / sample_rate); |
537 } | 537 } |
538 | 538 |
539 std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels) { | 539 std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32_t channels) { |
540 // Constants specified by the ALSA API for device hints. | 540 // Constants specified by the ALSA API for device hints. |
541 static const int kGetAllDevices = -1; | 541 static const int kGetAllDevices = -1; |
542 static const char kPcmInterfaceName[] = "pcm"; | 542 static const char kPcmInterfaceName[] = "pcm"; |
543 static const char kIoHintName[] = "IOID"; | 543 static const char kIoHintName[] = "IOID"; |
544 static const char kNameHintName[] = "NAME"; | 544 static const char kNameHintName[] = "NAME"; |
545 | 545 |
546 const char* wanted_device = GuessSpecificDeviceName(channels); | 546 const char* wanted_device = GuessSpecificDeviceName(channels); |
547 if (!wanted_device) | 547 if (!wanted_device) |
548 return std::string(); | 548 return std::string(); |
549 | 549 |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
635 if (available_frames < 0) { | 635 if (available_frames < 0) { |
636 available_frames = wrapper_->PcmRecover(playback_handle_, | 636 available_frames = wrapper_->PcmRecover(playback_handle_, |
637 available_frames, | 637 available_frames, |
638 kPcmRecoverIsSilent); | 638 kPcmRecoverIsSilent); |
639 } | 639 } |
640 if (available_frames < 0) { | 640 if (available_frames < 0) { |
641 LOG(ERROR) << "Failed querying available frames. Assuming 0: " | 641 LOG(ERROR) << "Failed querying available frames. Assuming 0: " |
642 << wrapper_->StrError(available_frames); | 642 << wrapper_->StrError(available_frames); |
643 return 0; | 643 return 0; |
644 } | 644 } |
645 if (static_cast<uint32>(available_frames) > alsa_buffer_frames_ * 2) { | 645 if (static_cast<uint32_t>(available_frames) > alsa_buffer_frames_ * 2) { |
646 LOG(ERROR) << "ALSA returned " << available_frames << " of " | 646 LOG(ERROR) << "ALSA returned " << available_frames << " of " |
647 << alsa_buffer_frames_ << " frames available."; | 647 << alsa_buffer_frames_ << " frames available."; |
648 return alsa_buffer_frames_; | 648 return alsa_buffer_frames_; |
649 } | 649 } |
650 | 650 |
651 return available_frames; | 651 return available_frames; |
652 } | 652 } |
653 | 653 |
654 snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) { | 654 snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) { |
655 // For auto-selection: | 655 // For auto-selection: |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
692 pcm_format_, latency)) != NULL) { | 692 pcm_format_, latency)) != NULL) { |
693 return handle; | 693 return handle; |
694 } | 694 } |
695 } | 695 } |
696 } | 696 } |
697 | 697 |
698 // For the kDefaultDevice device, we can only reliably depend on 2-channel | 698 // For the kDefaultDevice device, we can only reliably depend on 2-channel |
699 // output to have the correct ordering according to Lennart. For the channel | 699 // output to have the correct ordering according to Lennart. For the channel |
700 // formats that we know how to downmix from (3 channel to 8 channel), setup | 700 // formats that we know how to downmix from (3 channel to 8 channel), setup |
701 // downmixing. | 701 // downmixing. |
702 uint32 default_channels = channels_; | 702 uint32_t default_channels = channels_; |
703 if (default_channels > 2) { | 703 if (default_channels > 2) { |
704 channel_mixer_.reset( | 704 channel_mixer_.reset( |
705 new ChannelMixer(channel_layout_, kDefaultOutputChannelLayout)); | 705 new ChannelMixer(channel_layout_, kDefaultOutputChannelLayout)); |
706 default_channels = 2; | 706 default_channels = 2; |
707 mixed_audio_bus_ = AudioBus::Create( | 707 mixed_audio_bus_ = AudioBus::Create( |
708 default_channels, audio_bus_->frames()); | 708 default_channels, audio_bus_->frames()); |
709 } | 709 } |
710 | 710 |
711 // Step 4. | 711 // Step 4. |
712 device_name_ = kDefaultDevice; | 712 device_name_ = kDefaultDevice; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
770 | 770 |
771 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { | 771 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { |
772 return state_; | 772 return state_; |
773 } | 773 } |
774 | 774 |
775 bool AlsaPcmOutputStream::IsOnAudioThread() const { | 775 bool AlsaPcmOutputStream::IsOnAudioThread() const { |
776 return message_loop_ && message_loop_ == base::MessageLoop::current(); | 776 return message_loop_ && message_loop_ == base::MessageLoop::current(); |
777 } | 777 } |
778 | 778 |
779 int AlsaPcmOutputStream::RunDataCallback(AudioBus* audio_bus, | 779 int AlsaPcmOutputStream::RunDataCallback(AudioBus* audio_bus, |
780 uint32 total_bytes_delay) { | 780 uint32_t total_bytes_delay) { |
781 TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); | 781 TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); |
782 | 782 |
783 if (source_callback_) | 783 if (source_callback_) |
784 return source_callback_->OnMoreData(audio_bus, total_bytes_delay, 0); | 784 return source_callback_->OnMoreData(audio_bus, total_bytes_delay, 0); |
785 | 785 |
786 return 0; | 786 return 0; |
787 } | 787 } |
788 | 788 |
789 void AlsaPcmOutputStream::RunErrorCallback(int code) { | 789 void AlsaPcmOutputStream::RunErrorCallback(int code) { |
790 if (source_callback_) | 790 if (source_callback_) |
791 source_callback_->OnError(this); | 791 source_callback_->OnError(this); |
792 } | 792 } |
793 | 793 |
794 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 794 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
795 // release ownership of the currently registered callback. | 795 // release ownership of the currently registered callback. |
796 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { | 796 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
797 DCHECK(IsOnAudioThread()); | 797 DCHECK(IsOnAudioThread()); |
798 source_callback_ = callback; | 798 source_callback_ = callback; |
799 } | 799 } |
800 | 800 |
801 } // namespace media | 801 } // namespace media |
OLD | NEW |