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 |