| 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" |
| 50 #include "media/base/channel_mixer.h" | 52 #include "media/base/channel_mixer.h" |
| 51 #include "media/base/data_buffer.h" | 53 #include "media/base/data_buffer.h" |
| 52 #include "media/base/seekable_buffer.h" | 54 #include "media/base/seekable_buffer.h" |
| 53 | 55 |
| 54 namespace media { | 56 namespace media { |
| 55 | 57 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 break; | 120 break; |
| 119 case AlsaPcmOutputStream::kIsPlaying: | 121 case AlsaPcmOutputStream::kIsPlaying: |
| 120 os << "kIsPlaying"; | 122 os << "kIsPlaying"; |
| 121 break; | 123 break; |
| 122 case AlsaPcmOutputStream::kIsStopped: | 124 case AlsaPcmOutputStream::kIsStopped: |
| 123 os << "kIsStopped"; | 125 os << "kIsStopped"; |
| 124 break; | 126 break; |
| 125 case AlsaPcmOutputStream::kIsClosed: | 127 case AlsaPcmOutputStream::kIsClosed: |
| 126 os << "kIsClosed"; | 128 os << "kIsClosed"; |
| 127 break; | 129 break; |
| 128 }; | 130 } |
| 129 return os; | 131 return os; |
| 130 } | 132 } |
| 131 | 133 |
| 132 const char AlsaPcmOutputStream::kDefaultDevice[] = "default"; | 134 const char AlsaPcmOutputStream::kDefaultDevice[] = "default"; |
| 133 const char AlsaPcmOutputStream::kAutoSelectDevice[] = ""; | 135 const char AlsaPcmOutputStream::kAutoSelectDevice[] = ""; |
| 134 const char AlsaPcmOutputStream::kPlugPrefix[] = "plug:"; | 136 const char AlsaPcmOutputStream::kPlugPrefix[] = "plug:"; |
| 135 | 137 |
| 136 // We use 40ms as our minimum required latency. If it is needed, we may be able | 138 // We use 40ms as our minimum required latency. If it is needed, we may be able |
| 137 // to get it down to 20ms. | 139 // to get it down to 20ms. |
| 138 const uint32_t AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000; | 140 const uint32_t AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 157 stop_stream_(false), | 159 stop_stream_(false), |
| 158 wrapper_(wrapper), | 160 wrapper_(wrapper), |
| 159 manager_(manager), | 161 manager_(manager), |
| 160 task_runner_(base::ThreadTaskRunnerHandle::Get()), | 162 task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 161 playback_handle_(NULL), | 163 playback_handle_(NULL), |
| 162 frames_per_packet_(packet_size_ / bytes_per_frame_), | 164 frames_per_packet_(packet_size_ / bytes_per_frame_), |
| 163 state_(kCreated), | 165 state_(kCreated), |
| 164 volume_(1.0f), | 166 volume_(1.0f), |
| 165 source_callback_(NULL), | 167 source_callback_(NULL), |
| 166 audio_bus_(AudioBus::Create(params)), | 168 audio_bus_(AudioBus::Create(params)), |
| 169 tick_clock_(new base::DefaultTickClock()), |
| 167 weak_factory_(this) { | 170 weak_factory_(this) { |
| 168 DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); | 171 DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| 169 DCHECK_EQ(audio_bus_->frames() * bytes_per_frame_, packet_size_); | 172 DCHECK_EQ(audio_bus_->frames() * bytes_per_frame_, packet_size_); |
| 170 | 173 |
| 171 // Sanity check input values. | 174 // Sanity check input values. |
| 172 if (!params.IsValid()) { | 175 if (!params.IsValid()) { |
| 173 LOG(WARNING) << "Unsupported audio parameters."; | 176 LOG(WARNING) << "Unsupported audio parameters."; |
| 174 TransitionTo(kInError); | 177 TransitionTo(kInError); |
| 175 } | 178 } |
| 176 | 179 |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 | 341 |
| 339 volume_ = static_cast<float>(volume); | 342 volume_ = static_cast<float>(volume); |
| 340 } | 343 } |
| 341 | 344 |
| 342 void AlsaPcmOutputStream::GetVolume(double* volume) { | 345 void AlsaPcmOutputStream::GetVolume(double* volume) { |
| 343 DCHECK(CalledOnValidThread()); | 346 DCHECK(CalledOnValidThread()); |
| 344 | 347 |
| 345 *volume = volume_; | 348 *volume = volume_; |
| 346 } | 349 } |
| 347 | 350 |
| 351 void AlsaPcmOutputStream::SetTickClockForTesting( |
| 352 std::unique_ptr<base::TickClock> tick_clock) { |
| 353 DCHECK(tick_clock); |
| 354 tick_clock_ = std::move(tick_clock); |
| 355 } |
| 356 |
| 348 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { | 357 void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) { |
| 349 DCHECK(CalledOnValidThread()); | 358 DCHECK(CalledOnValidThread()); |
| 350 | 359 |
| 351 // If stopped, simulate a 0-length packet. | 360 // If stopped, simulate a 0-length packet. |
| 352 if (stop_stream_) { | 361 if (stop_stream_) { |
| 353 buffer_->Clear(); | 362 buffer_->Clear(); |
| 354 *source_exhausted = true; | 363 *source_exhausted = true; |
| 355 return; | 364 return; |
| 356 } | 365 } |
| 357 | 366 |
| 358 *source_exhausted = false; | 367 *source_exhausted = false; |
| 359 | 368 |
| 360 // Request more data only when we run out of data in the buffer, because | 369 // Request more data only when we run out of data in the buffer, because |
| 361 // WritePacket() consumes only the current chunk of data. | 370 // WritePacket() consumes only the current chunk of data. |
| 362 if (!buffer_->forward_bytes()) { | 371 if (!buffer_->forward_bytes()) { |
| 363 // Before making a request to source for data we need to determine the | 372 // Before making a request to source for data we need to determine the |
| 364 // delay (in bytes) for the requested data to be played. | 373 // delay for the requested data to be played. |
| 365 const uint32_t hardware_delay = GetCurrentDelay() * bytes_per_frame_; | 374 const base::TimeDelta delay = |
| 375 FramesToTimeDelta(GetCurrentDelay(), sample_rate_); |
| 366 | 376 |
| 367 scoped_refptr<media::DataBuffer> packet = | 377 scoped_refptr<media::DataBuffer> packet = |
| 368 new media::DataBuffer(packet_size_); | 378 new media::DataBuffer(packet_size_); |
| 369 int frames_filled = RunDataCallback( | 379 int frames_filled = |
| 370 audio_bus_.get(), hardware_delay); | 380 RunDataCallback(delay, tick_clock_->NowTicks(), audio_bus_.get()); |
| 371 | 381 |
| 372 size_t packet_size = frames_filled * bytes_per_frame_; | 382 size_t packet_size = frames_filled * bytes_per_frame_; |
| 373 DCHECK_LE(packet_size, packet_size_); | 383 DCHECK_LE(packet_size, packet_size_); |
| 374 | 384 |
| 375 // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer; | 385 // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer; |
| 376 // volume adjust should use SSE optimized vector_fmul() prior to interleave. | 386 // volume adjust should use SSE optimized vector_fmul() prior to interleave. |
| 377 AudioBus* output_bus = audio_bus_.get(); | 387 AudioBus* output_bus = audio_bus_.get(); |
| 378 ChannelLayout output_channel_layout = channel_layout_; | 388 ChannelLayout output_channel_layout = channel_layout_; |
| 379 if (channel_mixer_) { | 389 if (channel_mixer_) { |
| 380 output_bus = mixed_audio_bus_.get(); | 390 output_bus = mixed_audio_bus_.get(); |
| (...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 770 } else { | 780 } else { |
| 771 state_ = to; | 781 state_ = to; |
| 772 } | 782 } |
| 773 return state_; | 783 return state_; |
| 774 } | 784 } |
| 775 | 785 |
| 776 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { | 786 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { |
| 777 return state_; | 787 return state_; |
| 778 } | 788 } |
| 779 | 789 |
| 780 int AlsaPcmOutputStream::RunDataCallback(AudioBus* audio_bus, | 790 int AlsaPcmOutputStream::RunDataCallback(base::TimeDelta delay, |
| 781 uint32_t total_bytes_delay) { | 791 base::TimeTicks delay_timestamp, |
| 792 AudioBus* audio_bus) { |
| 782 TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); | 793 TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); |
| 783 | 794 |
| 784 if (source_callback_) | 795 if (source_callback_) |
| 785 return source_callback_->OnMoreData(audio_bus, total_bytes_delay, 0); | 796 return source_callback_->OnMoreData(delay, delay_timestamp, 0, audio_bus); |
| 786 | 797 |
| 787 return 0; | 798 return 0; |
| 788 } | 799 } |
| 789 | 800 |
| 790 void AlsaPcmOutputStream::RunErrorCallback(int code) { | 801 void AlsaPcmOutputStream::RunErrorCallback(int code) { |
| 791 if (source_callback_) | 802 if (source_callback_) |
| 792 source_callback_->OnError(this); | 803 source_callback_->OnError(this); |
| 793 } | 804 } |
| 794 | 805 |
| 795 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 806 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 796 // release ownership of the currently registered callback. | 807 // release ownership of the currently registered callback. |
| 797 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { | 808 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
| 798 DCHECK(CalledOnValidThread()); | 809 DCHECK(CalledOnValidThread()); |
| 799 source_callback_ = callback; | 810 source_callback_ = callback; |
| 800 } | 811 } |
| 801 | 812 |
| 802 } // namespace media | 813 } // namespace media |
| OLD | NEW |