OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 // | 84 // |
85 // This function makes a best guess at the specific > 2 channel device name | 85 // This function makes a best guess at the specific > 2 channel device name |
86 // based on the number of channels requested. NULL is returned if no device | 86 // based on the number of channels requested. NULL is returned if no device |
87 // can be found to match the channel numbers. In this case, using | 87 // can be found to match the channel numbers. In this case, using |
88 // kDefaultDevice is probably the best bet. | 88 // kDefaultDevice is probably the best bet. |
89 // | 89 // |
90 // A five channel source is assumed to be surround50 instead of surround41 | 90 // A five channel source is assumed to be surround50 instead of surround41 |
91 // (which is also 5 channels). | 91 // (which is also 5 channels). |
92 // | 92 // |
93 // TODO(ajwong): The source data should have enough info to tell us if we want | 93 // TODO(ajwong): The source data should have enough info to tell us if we want |
94 // surround41 versus surround51, etc., instead of needing us to guess base don | 94 // surround41 versus surround51, etc., instead of needing us to guess based on |
95 // channel number. Fix API to pass that data down. | 95 // channel number. Fix API to pass that data down. |
96 static const char* GuessSpecificDeviceName(uint32 channels) { | 96 static const char* GuessSpecificDeviceName(uint32 channels) { |
97 switch (channels) { | 97 switch (channels) { |
98 case 8: | 98 case 8: |
99 return "surround71"; | 99 return "surround71"; |
100 | 100 |
101 case 7: | 101 case 7: |
102 return "surround70"; | 102 return "surround70"; |
103 | 103 |
104 case 6: | 104 case 6: |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
168 alsa_buffer_frames_(0), | 168 alsa_buffer_frames_(0), |
169 stop_stream_(false), | 169 stop_stream_(false), |
170 wrapper_(wrapper), | 170 wrapper_(wrapper), |
171 manager_(manager), | 171 manager_(manager), |
172 message_loop_(MessageLoop::current()), | 172 message_loop_(MessageLoop::current()), |
173 playback_handle_(NULL), | 173 playback_handle_(NULL), |
174 frames_per_packet_(packet_size_ / bytes_per_frame_), | 174 frames_per_packet_(packet_size_ / bytes_per_frame_), |
175 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | 175 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
176 state_(kCreated), | 176 state_(kCreated), |
177 volume_(1.0f), | 177 volume_(1.0f), |
178 source_callback_(NULL) { | 178 source_callback_(NULL), |
| 179 audio_bus_(AudioBus::Create(params)) { |
179 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); | 180 DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread()); |
| 181 DCHECK_EQ(audio_bus_->frames() * bytes_per_frame_, packet_size_); |
180 | 182 |
181 // Sanity check input values. | 183 // Sanity check input values. |
182 if (params.sample_rate() > kAlsaMaxSampleRate || | 184 if (params.sample_rate() > kAlsaMaxSampleRate || |
183 params.sample_rate() <= 0) { | 185 params.sample_rate() <= 0) { |
184 LOG(WARNING) << "Unsupported audio frequency."; | 186 LOG(WARNING) << "Unsupported audio frequency."; |
185 TransitionTo(kInError); | 187 TransitionTo(kInError); |
186 } | 188 } |
187 | 189 |
188 if (AudioParameters::AUDIO_PCM_LINEAR != params.format() && | 190 if (AudioParameters::AUDIO_PCM_LINEAR != params.format() && |
189 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format()) { | 191 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format()) { |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
369 // Before making a request to source for data we need to determine the | 371 // Before making a request to source for data we need to determine the |
370 // delay (in bytes) for the requested data to be played. | 372 // delay (in bytes) for the requested data to be played. |
371 | 373 |
372 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / | 374 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / |
373 bytes_per_output_frame_; | 375 bytes_per_output_frame_; |
374 | 376 |
375 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; | 377 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; |
376 | 378 |
377 scoped_refptr<media::DataBuffer> packet = | 379 scoped_refptr<media::DataBuffer> packet = |
378 new media::DataBuffer(packet_size_); | 380 new media::DataBuffer(packet_size_); |
379 int packet_size = RunDataCallback(packet->GetWritableData(), | 381 int frames_filled = RunDataCallback( |
380 packet->GetBufferSize(), | 382 audio_bus_.get(), AudioBuffersState(buffer_delay, hardware_delay)); |
381 AudioBuffersState(buffer_delay, | 383 size_t packet_size = frames_filled * bytes_per_frame_; |
382 hardware_delay)); | 384 DCHECK_LE(packet_size, packet_size_); |
383 CHECK_LE(packet_size, packet->GetBufferSize()); | 385 audio_bus_->ToInterleaved( |
| 386 frames_filled, bytes_per_sample_, packet->GetWritableData()); |
384 | 387 |
385 // Reset the |last_fill_time| to avoid back to back RunDataCallback(). | 388 // Reset the |last_fill_time| to avoid back to back RunDataCallback(). |
386 last_fill_time_ = base::Time::Now(); | 389 last_fill_time_ = base::Time::Now(); |
387 | 390 |
388 // This should not happen, but in case it does, drop any trailing bytes | 391 // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer; |
389 // that aren't large enough to make a frame. Without this, packet writing | 392 // volume adjust should use SSE optimized vector_fmul() prior to interleave. |
390 // may stall because the last few bytes in the packet may never get used by | |
391 // WritePacket. | |
392 DCHECK_EQ(0u, packet_size % bytes_per_frame_); | |
393 packet_size = (packet_size / bytes_per_frame_) * bytes_per_frame_; | |
394 | |
395 if (should_downmix_) { | 393 if (should_downmix_) { |
396 if (media::FoldChannels(packet->GetWritableData(), | 394 if (media::FoldChannels(packet->GetWritableData(), |
397 packet_size, | 395 packet_size, |
398 channels_, | 396 channels_, |
399 bytes_per_sample_, | 397 bytes_per_sample_, |
400 volume_)) { | 398 volume_)) { |
401 // Adjust packet size for downmix. | 399 // Adjust packet size for downmix. |
402 packet_size = packet_size / bytes_per_frame_ * bytes_per_output_frame_; | 400 packet_size = packet_size / bytes_per_frame_ * bytes_per_output_frame_; |
403 } else { | 401 } else { |
404 LOG(ERROR) << "Folding failed"; | 402 LOG(ERROR) << "Folding failed"; |
(...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
777 } | 775 } |
778 | 776 |
779 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { | 777 AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() { |
780 return state_; | 778 return state_; |
781 } | 779 } |
782 | 780 |
783 bool AlsaPcmOutputStream::IsOnAudioThread() const { | 781 bool AlsaPcmOutputStream::IsOnAudioThread() const { |
784 return message_loop_ && message_loop_ == MessageLoop::current(); | 782 return message_loop_ && message_loop_ == MessageLoop::current(); |
785 } | 783 } |
786 | 784 |
787 uint32 AlsaPcmOutputStream::RunDataCallback(uint8* dest, | 785 int AlsaPcmOutputStream::RunDataCallback(AudioBus* audio_bus, |
788 uint32 max_size, | 786 AudioBuffersState buffers_state) { |
789 AudioBuffersState buffers_state) { | |
790 TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); | 787 TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback"); |
791 | 788 |
792 if (source_callback_) | 789 if (source_callback_) |
793 return source_callback_->OnMoreData(dest, max_size, buffers_state); | 790 return source_callback_->OnMoreData(audio_bus, buffers_state); |
794 | 791 |
795 return 0; | 792 return 0; |
796 } | 793 } |
797 | 794 |
798 void AlsaPcmOutputStream::RunErrorCallback(int code) { | 795 void AlsaPcmOutputStream::RunErrorCallback(int code) { |
799 if (source_callback_) | 796 if (source_callback_) |
800 source_callback_->OnError(this, code); | 797 source_callback_->OnError(this, code); |
801 } | 798 } |
802 | 799 |
803 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 800 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
804 // release ownership of the currently registered callback. | 801 // release ownership of the currently registered callback. |
805 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { | 802 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
806 DCHECK(IsOnAudioThread()); | 803 DCHECK(IsOnAudioThread()); |
807 source_callback_ = callback; | 804 source_callback_ = callback; |
808 } | 805 } |
809 | 806 |
810 } // namespace media | 807 } // namespace media |
OLD | NEW |