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 3 // found in the LICENSE file. 4 5 #include "media/audio/virtual_audio_input_stream.h" 6 7 #include <algorithm> 8 #include <utility> 9 10 #include "base/bind.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/message_loop/message_loop_proxy.h" 13 #include "media/audio/virtual_audio_output_stream.h" 14 15 namespace media { 16 17 // LoopbackAudioConverter works similar to AudioConverter and converts input 18 // streams to different audio parameters. Then, the LoopbackAudioConverter can 19 // be used as an input to another AudioConverter. This allows us to 20 // use converted audio from AudioOutputStreams as input to an AudioConverter. 21 // For example, this allows converting multiple streams into a common format and 22 // using the converted audio as input to another AudioConverter (i.e. a mixer). 23 class LoopbackAudioConverter : public AudioConverter::InputCallback { 24 public: 25 LoopbackAudioConverter(const AudioParameters& input_params, 26 const AudioParameters& output_params) 27 : audio_converter_(input_params, output_params, false) {} 28 29 virtual ~LoopbackAudioConverter() {} 30 31 void AddInput(AudioConverter::InputCallback* input) { 32 audio_converter_.AddInput(input); 33 } 34 35 void RemoveInput(AudioConverter::InputCallback* input) { 36 audio_converter_.RemoveInput(input); 37 } 38 39 private: 40 virtual double ProvideInput(AudioBus* audio_bus, 41 base::TimeDelta buffer_delay) OVERRIDE { 42 audio_converter_.Convert(audio_bus); 43 return 1.0; 44 } 45 46 AudioConverter audio_converter_; 47 48 DISALLOW_COPY_AND_ASSIGN(LoopbackAudioConverter); 49 }; 50 51 VirtualAudioInputStream::VirtualAudioInputStream( 52 const AudioParameters& params, 53 const scoped_refptr<base::MessageLoopProxy>& worker_loop, 54 const AfterCloseCallback& after_close_cb) 55 : worker_loop_(worker_loop), 56 after_close_cb_(after_close_cb), 57 callback_(NULL), 58 buffer_(new uint8[params.GetBytesPerBuffer()]), 59 params_(params), 60 mixer_(params_, params_, false), 61 num_attached_output_streams_(0), 62 fake_consumer_(worker_loop_, params_) { 63 DCHECK(params_.IsValid()); 64 DCHECK(worker_loop_.get()); 65 66 // VAIS can be constructed on any thread, but will DCHECK that all 67 // AudioInputStream methods are called from the same thread. 68 thread_checker_.DetachFromThread(); 69 } 70 71 VirtualAudioInputStream::~VirtualAudioInputStream() { 72 DCHECK(!callback_); 73 74 // Sanity-check: Contract for Add/RemoveOutputStream() requires that all 75 // output streams be removed before VirtualAudioInputStream is destroyed. 76 DCHECK_EQ(0, num_attached_output_streams_); 77 78 for (AudioConvertersMap::iterator it = converters_.begin(); 79 it != converters_.end(); ++it) { 80 delete it->second; 81 } 82 } 83 84 bool VirtualAudioInputStream::Open() { 85 DCHECK(thread_checker_.CalledOnValidThread()); 86 memset(buffer_.get(), 0, params_.GetBytesPerBuffer()); 87 return true; 88 } 89 90 void VirtualAudioInputStream::Start(AudioInputCallback* callback) { 91 DCHECK(thread_checker_.CalledOnValidThread()); 92 callback_ = callback; 93 fake_consumer_.Start(base::Bind( 94 &VirtualAudioInputStream::PumpAudio, base::Unretained(this))); 95 } 96 97 void VirtualAudioInputStream::Stop() { 98 DCHECK(thread_checker_.CalledOnValidThread()); 99 fake_consumer_.Stop(); 100 } 101 102 void VirtualAudioInputStream::AddOutputStream( 103 VirtualAudioOutputStream* stream, const AudioParameters& output_params) { 104 DCHECK(thread_checker_.CalledOnValidThread()); 105 106 base::AutoLock scoped_lock(converter_network_lock_); 107 108 AudioConvertersMap::iterator converter = converters_.find(output_params); 109 if (converter == converters_.end()) { 110 std::pair<AudioConvertersMap::iterator, bool> result = converters_.insert( 111 std::make_pair(output_params, 112 new LoopbackAudioConverter(output_params, params_))); 113 converter = result.first; 114 115 // Add to main mixer if we just added a new AudioTransform. 116 mixer_.AddInput(converter->second); 117 } 118 converter->second->AddInput(stream); 119 ++num_attached_output_streams_; 120 } 121 122 void VirtualAudioInputStream::RemoveOutputStream( 123 VirtualAudioOutputStream* stream, const AudioParameters& output_params) { 124 DCHECK(thread_checker_.CalledOnValidThread()); 125 126 base::AutoLock scoped_lock(converter_network_lock_); 127 128 DCHECK(converters_.find(output_params) != converters_.end()); 129 converters_[output_params]->RemoveInput(stream); 130 131 --num_attached_output_streams_; 132 DCHECK_LE(0, num_attached_output_streams_); 133 } 134 135 void VirtualAudioInputStream::PumpAudio(AudioBus* audio_bus) { 136 DCHECK(worker_loop_->BelongsToCurrentThread()); 137 DCHECK(callback_); 138 139 { 140 base::AutoLock scoped_lock(converter_network_lock_); 141 mixer_.Convert(audio_bus); 142 } 143 audio_bus->ToInterleaved(params_.frames_per_buffer(), 144 params_.bits_per_sample() / 8, 145 buffer_.get()); 146 callback_->OnData(this, 147 buffer_.get(), 148 params_.GetBytesPerBuffer(), 149 params_.GetBytesPerBuffer(), 150 1.0); 151 } 152 153 void VirtualAudioInputStream::Close() { 154 DCHECK(thread_checker_.CalledOnValidThread()); 155 156 Stop(); // Make sure callback_ is no longer being used. 157 if (callback_) { 158 callback_->OnClose(this); 159 callback_ = NULL; 160 } 161 162 // If a non-null AfterCloseCallback was provided to the constructor, invoke it 163 // here. The callback is moved to a stack-local first since |this| could be 164 // destroyed during Run(). 165 if (!after_close_cb_.is_null()) { 166 const AfterCloseCallback cb = after_close_cb_; 167 after_close_cb_.Reset(); 168 cb.Run(this); 169 } 170 } 171 172 double VirtualAudioInputStream::GetMaxVolume() { 173 return 1.0; 174 } 175 176 void VirtualAudioInputStream::SetVolume(double volume) {} 177 178 double VirtualAudioInputStream::GetVolume() { 179 return 1.0; 180 } 181 182 void VirtualAudioInputStream::SetAutomaticGainControl(bool enabled) {} 183 184 bool VirtualAudioInputStream::GetAutomaticGainControl() { 185 return false; 186 } 187 188 } // namespace media 189