| 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 19 matching lines...) Expand all Loading... |
| 30 // When |stop_stream_| is set, no more commands will be made against the | 30 // When |stop_stream_| is set, no more commands will be made against the |
| 31 // ALSA device, and playback will effectively stop. From the client's point of | 31 // ALSA device, and playback will effectively stop. From the client's point of |
| 32 // view, it will seem that the device has just clogged and stopped requesting | 32 // view, it will seem that the device has just clogged and stopped requesting |
| 33 // data. | 33 // data. |
| 34 | 34 |
| 35 #include "media/audio/alsa/alsa_output.h" | 35 #include "media/audio/alsa/alsa_output.h" |
| 36 | 36 |
| 37 #include <stddef.h> | 37 #include <stddef.h> |
| 38 | 38 |
| 39 #include <algorithm> | 39 #include <algorithm> |
| 40 #include <utility> |
| 40 | 41 |
| 41 #include "base/bind.h" | 42 #include "base/bind.h" |
| 42 #include "base/logging.h" | 43 #include "base/logging.h" |
| 43 #include "base/memory/free_deleter.h" | 44 #include "base/memory/free_deleter.h" |
| 44 #include "base/stl_util.h" | 45 #include "base/stl_util.h" |
| 45 #include "base/threading/thread_task_runner_handle.h" | 46 #include "base/threading/thread_task_runner_handle.h" |
| 47 #include "base/time/default_tick_clock.h" |
| 46 #include "base/trace_event/trace_event.h" | 48 #include "base/trace_event/trace_event.h" |
| 47 #include "media/audio/alsa/alsa_util.h" | 49 #include "media/audio/alsa/alsa_util.h" |
| 48 #include "media/audio/alsa/alsa_wrapper.h" | 50 #include "media/audio/alsa/alsa_wrapper.h" |
| 49 #include "media/audio/alsa/audio_manager_alsa.h" | 51 #include "media/audio/alsa/audio_manager_alsa.h" |
| 52 #include "media/base/audio_timestamp_helper.h" |
| 50 #include "media/base/channel_mixer.h" | 53 #include "media/base/channel_mixer.h" |
| 51 #include "media/base/data_buffer.h" | 54 #include "media/base/data_buffer.h" |
| 52 #include "media/base/seekable_buffer.h" | 55 #include "media/base/seekable_buffer.h" |
| 53 | 56 |
| 54 namespace media { | 57 namespace media { |
| 55 | 58 |
| 56 // Set to 0 during debugging if you want error messages due to underrun | 59 // Set to 0 during debugging if you want error messages due to underrun |
| 57 // events or other recoverable errors. | 60 // events or other recoverable errors. |
| 58 #if defined(NDEBUG) | 61 #if defined(NDEBUG) |
| 59 static const int kPcmRecoverIsSilent = 1; | 62 static const int kPcmRecoverIsSilent = 1; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 break; | 121 break; |
| 119 case AlsaPcmOutputStream::kIsPlaying: | 122 case AlsaPcmOutputStream::kIsPlaying: |
| 120 os << "kIsPlaying"; | 123 os << "kIsPlaying"; |
| 121 break; | 124 break; |
| 122 case AlsaPcmOutputStream::kIsStopped: | 125 case AlsaPcmOutputStream::kIsStopped: |
| 123 os << "kIsStopped"; | 126 os << "kIsStopped"; |
| 124 break; | 127 break; |
| 125 case AlsaPcmOutputStream::kIsClosed: | 128 case AlsaPcmOutputStream::kIsClosed: |
| 126 os << "kIsClosed"; | 129 os << "kIsClosed"; |
| 127 break; | 130 break; |
| 128 }; | 131 } |
| 129 return os; | 132 return os; |
| 130 } | 133 } |
| 131 | 134 |
| 132 const char AlsaPcmOutputStream::kDefaultDevice[] = "default"; | 135 const char AlsaPcmOutputStream::kDefaultDevice[] = "default"; |
| 133 const char AlsaPcmOutputStream::kAutoSelectDevice[] = ""; | 136 const char AlsaPcmOutputStream::kAutoSelectDevice[] = ""; |
| 134 const char AlsaPcmOutputStream::kPlugPrefix[] = "plug:"; | 137 const char AlsaPcmOutputStream::kPlugPrefix[] = "plug:"; |
| 135 | 138 |
| 136 // We use 40ms as our minimum required latency. If it is needed, we may be able | 139 // We use 40ms as our minimum required latency. If it is needed, we may be able |
| 137 // to get it down to 20ms. | 140 // to get it down to 20ms. |
| 138 const uint32_t AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000; | 141 const uint32_t AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000; |
| 139 | 142 |
| 140 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, | 143 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, |
| 141 const AudioParameters& params, | 144 const AudioParameters& params, |
| 142 AlsaWrapper* wrapper, | 145 AlsaWrapper* wrapper, |
| 143 AudioManagerBase* manager) | 146 AudioManagerBase* manager) |
| 144 : requested_device_name_(device_name), | 147 : requested_device_name_(device_name), |
| 145 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())), | 148 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())), |
| 146 channels_(params.channels()), | 149 channels_(params.channels()), |
| 147 channel_layout_(params.channel_layout()), | 150 channel_layout_(params.channel_layout()), |
| 148 sample_rate_(params.sample_rate()), | 151 sample_rate_(params.sample_rate()), |
| 149 bytes_per_sample_(params.bits_per_sample() / 8), | 152 bytes_per_sample_(params.bits_per_sample() / 8), |
| 150 bytes_per_frame_(params.GetBytesPerFrame()), | 153 bytes_per_frame_(params.GetBytesPerFrame()), |
| 151 packet_size_(params.GetBytesPerBuffer()), | 154 packet_size_(params.GetBytesPerBuffer()), |
| 152 latency_(std::max( | 155 latency_(std::max( |
| 153 base::TimeDelta::FromMicroseconds(kMinLatencyMicros), | 156 base::TimeDelta::FromMicroseconds(kMinLatencyMicros), |
| 154 FramesToTimeDelta(params.frames_per_buffer() * 2, sample_rate_))), | 157 AudioTimestampHelper::FramesToTime(params.frames_per_buffer() * 2, |
| 158 sample_rate_))), |
| 155 bytes_per_output_frame_(bytes_per_frame_), | 159 bytes_per_output_frame_(bytes_per_frame_), |
| 156 alsa_buffer_frames_(0), | 160 alsa_buffer_frames_(0), |
| 157 stop_stream_(false), | 161 stop_stream_(false), |
| 158 wrapper_(wrapper), | 162 wrapper_(wrapper), |
| 159 manager_(manager), | 163 manager_(manager), |
| 160 task_runner_(base::ThreadTaskRunnerHandle::Get()), | 164 task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 161 playback_handle_(NULL), | 165 playback_handle_(NULL), |
| 162 frames_per_packet_(packet_size_ / bytes_per_frame_), | 166 frames_per_packet_(packet_size_ / bytes_per_frame_), |
| 163 state_(kCreated), | 167 state_(kCreated), |
| 164 volume_(1.0f), | 168 volume_(1.0f), |
| 165 source_callback_(NULL), | 169 source_callback_(NULL), |
| 166 audio_bus_(AudioBus::Create(params)), | 170 audio_bus_(AudioBus::Create(params)), |
| 171 tick_clock_(new base::DefaultTickClock()), |
| 167 weak_factory_(this) { | 172 weak_factory_(this) { |
| 168 DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); | 173 DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 169 DCHECK_EQ(audio_bus_->frames() * bytes_per_frame_, packet_size_); | 174 DCHECK_EQ(audio_bus_->frames() * bytes_per_frame_, packet_size_); |
| 170 | 175 |
| 171 // Sanity check input values. | 176 // Sanity check input values. |
| 172 if (!params.IsValid()) { | 177 if (!params.IsValid()) { |
| 173 LOG(WARNING) << "Unsupported audio parameters."; | 178 LOG(WARNING) << "Unsupported audio parameters."; |
| 174 TransitionTo(kInError); | 179 TransitionTo(kInError); |
| 175 } | 180 } |
| 176 | 181 |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 | 343 |
| 339 volume_ = static_cast<float>(volume); | 344 volume_ = static_cast<float>(volume); |
| 340 } | 345 } |
| 341 | 346 |
| 342 void AlsaPcmOutputStream::GetVolume(double* volume) { | 347 void AlsaPcmOutputStream::GetVolume(double* volume) { |
| 343 DCHECK(CalledOnValidThread()); | 348 DCHECK(CalledOnValidThread()); |
| 344 | 349 |
| 345 *volume = volume_; | 350 *volume = volume_; |
| 346 } | 351 } |
| 347 | 352 |
| 353 void AlsaPcmOutputStream::SetTickClockForTesting( |
| 354 std::unique_ptr<base::TickClock> tick_clock) { |
| 355 DCHECK(tick_clock); |
| 356 tick_clock_ = std::move(tick_clock); |
| 357 } |
| 358 |
| 348 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { | 359 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { |
| 349 DCHECK(CalledOnValidThread()); | 360 DCHECK(CalledOnValidThread()); |
| 350 | 361 |
| 351 // If stopped, simulate a 0-length packet. | 362 // If stopped, simulate a 0-length packet. |
| 352 if (stop_stream_) { | 363 if (stop_stream_) { |
| 353 buffer_->Clear(); | 364 buffer_->Clear(); |
| 354 *source_exhausted = true; | 365 *source_exhausted = true; |
| 355 return; | 366 return; |
| 356 } | 367 } |
| 357 | 368 |
| 358 *source_exhausted = false; | 369 *source_exhausted = false; |
| 359 | 370 |
| 360 // Request more data only when we run out of data in the buffer, because | 371 // Request more data only when we run out of data in the buffer, because |
| 361 // WritePacket() consumes only the current chunk of data. | 372 // WritePacket() consumes only the current chunk of data. |
| 362 if (!buffer_->forward_bytes()) { | 373 if (!buffer_->forward_bytes()) { |
| 363 // Before making a request to source for data we need to determine the | 374 // Before making a request to source for data we need to determine the |
| 364 // delay (in bytes) for the requested data to be played. | 375 // delay for the requested data to be played. |
| 365 const uint32_t hardware_delay = GetCurrentDelay() * bytes_per_frame_; | 376 const base::TimeDelta delay = |
| 377 AudioTimestampHelper::FramesToTime(GetCurrentDelay(), sample_rate_); |
| 366 | 378 |
| 367 scoped_refptr<media::DataBuffer> packet = | 379 scoped_refptr<media::DataBuffer> packet = |
| 368 new media::DataBuffer(packet_size_); | 380 new media::DataBuffer(packet_size_); |
| 369 int frames_filled = RunDataCallback( | 381 int frames_filled = |
| 370 audio_bus_.get(), hardware_delay); | 382 RunDataCallback(delay, tick_clock_->NowTicks(), audio_bus_.get()); |
| 371 | 383 |
| 372 size_t packet_size = frames_filled * bytes_per_frame_; | 384 size_t packet_size = frames_filled * bytes_per_frame_; |
| 373 DCHECK_LE(packet_size, packet_size_); | 385 DCHECK_LE(packet_size, packet_size_); |
| 374 | 386 |
| 375 // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer; | 387 // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer; |
| 376 // volume adjust should use SSE optimized vector_fmul() prior to interleave. | 388 // volume adjust should use SSE optimized vector_fmul() prior to interleave. |
| 377 AudioBus* output_bus = audio_bus_.get(); | 389 AudioBus* output_bus = audio_bus_.get(); |
| 378 ChannelLayout output_channel_layout = channel_layout_; | 390 ChannelLayout output_channel_layout = channel_layout_; |
| 379 if (channel_mixer_) { | 391 if (channel_mixer_) { |
| 380 output_bus = mixed_audio_bus_.get(); | 392 output_bus = mixed_audio_bus_.get(); |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 509 next_fill_time = base::TimeDelta(); | 521 next_fill_time = base::TimeDelta(); |
| 510 } else if (buffer_->forward_bytes()) { | 522 } else if (buffer_->forward_bytes()) { |
| 511 // If we've got data available and no room, poll until room is available. | 523 // If we've got data available and no room, poll until room is available. |
| 512 // Polling in this manner allows us to ensure a more consistent callback | 524 // Polling in this manner allows us to ensure a more consistent callback |
| 513 // schedule. In testing this yields a variance of +/- 5ms versus the non- | 525 // schedule. In testing this yields a variance of +/- 5ms versus the non- |
| 514 // polling strategy which is around +/- 30ms and bimodal. | 526 // polling strategy which is around +/- 30ms and bimodal. |
| 515 next_fill_time = base::TimeDelta::FromMilliseconds(5); | 527 next_fill_time = base::TimeDelta::FromMilliseconds(5); |
| 516 } else if (available_frames < kTargetFramesAvailable) { | 528 } else if (available_frames < kTargetFramesAvailable) { |
| 517 // Schedule the next write for the moment when the available buffer of the | 529 // Schedule the next write for the moment when the available buffer of the |
| 518 // sound card hits |kTargetFramesAvailable|. | 530 // sound card hits |kTargetFramesAvailable|. |
| 519 next_fill_time = FramesToTimeDelta( | 531 next_fill_time = AudioTimestampHelper::FramesToTime( |
| 520 kTargetFramesAvailable - available_frames, sample_rate_); | 532 kTargetFramesAvailable - available_frames, sample_rate_); |
| 521 } else if (!source_exhausted) { | 533 } else if (!source_exhausted) { |
| 522 // The sound card has |kTargetFramesAvailable| or more frames available. | 534 // The sound card has |kTargetFramesAvailable| or more frames available. |
| 523 // Invoke the next write immediately to avoid underrun. | 535 // Invoke the next write immediately to avoid underrun. |
| 524 next_fill_time = base::TimeDelta(); | 536 next_fill_time = base::TimeDelta(); |
| 525 } else { | 537 } else { |
| 526 // The sound card has frames available, but our source is exhausted, so | 538 // The sound card has frames available, but our source is exhausted, so |
| 527 // avoid busy looping by delaying a bit. | 539 // avoid busy looping by delaying a bit. |
| 528 next_fill_time = base::TimeDelta::FromMilliseconds(10); | 540 next_fill_time = base::TimeDelta::FromMilliseconds(10); |
| 529 } | 541 } |
| 530 | 542 |
| 531 task_runner_->PostDelayedTask( | 543 task_runner_->PostDelayedTask( |
| 532 FROM_HERE, | 544 FROM_HERE, |
| 533 base::Bind(&AlsaPcmOutputStream::WriteTask, weak_factory_.GetWeakPtr()), | 545 base::Bind(&AlsaPcmOutputStream::WriteTask, weak_factory_.GetWeakPtr()), |
| 534 next_fill_time); | 546 next_fill_time); |
| 535 } | 547 } |
| 536 | 548 |
| 537 // static | |
| 538 base::TimeDelta AlsaPcmOutputStream::FramesToTimeDelta(int frames, | |
| 539 double sample_rate) { | |
| 540 return base::TimeDelta::FromMicroseconds( | |
| 541 frames * base::Time::kMicrosecondsPerSecond / sample_rate); | |
| 542 } | |
| 543 | |
| 544 std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32_t channels) { | 549 std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32_t channels) { |
| 545 // Constants specified by the ALSA API for device hints. | 550 // Constants specified by the ALSA API for device hints. |
| 546 static const int kGetAllDevices = -1; | 551 static const int kGetAllDevices = -1; |
| 547 static const char kPcmInterfaceName[] = "pcm"; | 552 static const char kPcmInterfaceName[] = "pcm"; |
| 548 static const char kIoHintName[] = "IOID"; | 553 static const char kIoHintName[] = "IOID"; |
| 549 static const char kNameHintName[] = "NAME"; | 554 static const char kNameHintName[] = "NAME"; |
| 550 | 555 |
| 551 const char* wanted_device = GuessSpecificDeviceName(channels); | 556 const char* wanted_device = GuessSpecificDeviceName(channels); |
| 552 if (!wanted_device) | 557 if (!wanted_device) |
| 553 return std::string(); | 558 return std::string(); |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 770 } else { | 775 } else { |
| 771 state_ = to; | 776 state_ = to; |
| 772 } | 777 } |
| 773 return state_; | 778 return state_; |
| 774 } | 779 } |
| 775 | 780 |
| 776 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { | 781 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { |
| 777 return state_; | 782 return state_; |
| 778 } | 783 } |
| 779 | 784 |
| 780 int AlsaPcmOutputStream::RunDataCallback(AudioBus* audio_bus, | 785 int AlsaPcmOutputStream::RunDataCallback(base::TimeDelta delay, |
| 781 uint32_t total_bytes_delay) { | 786 base::TimeTicks delay_timestamp, |
| 787 AudioBus* audio_bus) { |
| 782 TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); | 788 TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); |
| 783 | 789 |
| 784 if (source_callback_) | 790 if (source_callback_) |
| 785 return source_callback_->OnMoreData(audio_bus, total_bytes_delay, 0); | 791 return source_callback_->OnMoreData(delay, delay_timestamp, 0, audio_bus); |
| 786 | 792 |
| 787 return 0; | 793 return 0; |
| 788 } | 794 } |
| 789 | 795 |
| 790 void AlsaPcmOutputStream::RunErrorCallback(int code) { | 796 void AlsaPcmOutputStream::RunErrorCallback(int code) { |
| 791 if (source_callback_) | 797 if (source_callback_) |
| 792 source_callback_->OnError(this); | 798 source_callback_->OnError(this); |
| 793 } | 799 } |
| 794 | 800 |
| 795 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 801 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 796 // release ownership of the currently registered callback. | 802 // release ownership of the currently registered callback. |
| 797 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { | 803 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
| 798 DCHECK(CalledOnValidThread()); | 804 DCHECK(CalledOnValidThread()); |
| 799 source_callback_ = callback; | 805 source_callback_ = callback; |
| 800 } | 806 } |
| 801 | 807 |
| 802 } // namespace media | 808 } // namespace media |
| OLD | NEW |