 Chromium Code Reviews
 Chromium Code Reviews Issue 420603004:
  Use the AudioProcessing float interface in MediaStreamAudioProcessor.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 420603004:
  Use the AudioProcessing float interface in MediaStreamAudioProcessor.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| 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 #include "content/renderer/media/media_stream_audio_processor.h" | 5 #include "content/renderer/media/media_stream_audio_processor.h" | 
| 6 | 6 | 
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" | 
| 8 #include "base/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" | 
| 9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" | 
| 10 #include "content/public/common/content_switches.h" | 10 #include "content/public/common/content_switches.h" | 
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 namespace { | 24 namespace { | 
| 25 | 25 | 
| 26 using webrtc::AudioProcessing; | 26 using webrtc::AudioProcessing; | 
| 27 | 27 | 
| 28 #if defined(OS_ANDROID) | 28 #if defined(OS_ANDROID) | 
| 29 const int kAudioProcessingSampleRate = 16000; | 29 const int kAudioProcessingSampleRate = 16000; | 
| 30 #else | 30 #else | 
| 31 const int kAudioProcessingSampleRate = 32000; | 31 const int kAudioProcessingSampleRate = 32000; | 
| 32 #endif | 32 #endif | 
| 33 const int kAudioProcessingNumberOfChannels = 1; | 33 const int kAudioProcessingNumberOfChannels = 1; | 
| 34 const AudioProcessing::ChannelLayout kAudioProcessingChannelLayout = | |
| 35 AudioProcessing::kMono; | |
| 36 | 34 | 
| 37 const int kMaxNumberOfBuffersInFifo = 2; | 35 AudioProcessing::ChannelLayout MapLayout(media::ChannelLayout media_layout) { | 
| 36 switch (media_layout) { | |
| 37 case media::CHANNEL_LAYOUT_MONO: | |
| 38 return AudioProcessing::kMono; | |
| 39 case media::CHANNEL_LAYOUT_STEREO: | |
| 40 return AudioProcessing::kStereo; | |
| 41 case media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC: | |
| 42 return AudioProcessing::kStereoAndKeyboard; | |
| 43 default: | |
| 44 NOTREACHED() << "Layout not supported: " << media_layout; | |
| 45 return AudioProcessing::kMono; | |
| 46 } | |
| 47 } | |
| 48 | |
| 49 AudioProcessing::ChannelLayout ChannelsToLayout(int num_channels) { | |
| 50 switch (num_channels) { | |
| 51 case 1: | |
| 52 return AudioProcessing::kMono; | |
| 53 case 2: | |
| 54 return AudioProcessing::kStereo; | |
| 55 default: | |
| 56 NOTREACHED() << "Channels not supported: " << num_channels; | |
| 57 return AudioProcessing::kMono; | |
| 58 } | |
| 59 } | |
| 38 | 60 | 
| 39 // Used by UMA histograms and entries shouldn't be re-ordered or removed. | 61 // Used by UMA histograms and entries shouldn't be re-ordered or removed. | 
| 40 enum AudioTrackProcessingStates { | 62 enum AudioTrackProcessingStates { | 
| 41 AUDIO_PROCESSING_ENABLED = 0, | 63 AUDIO_PROCESSING_ENABLED = 0, | 
| 42 AUDIO_PROCESSING_DISABLED, | 64 AUDIO_PROCESSING_DISABLED, | 
| 43 AUDIO_PROCESSING_IN_WEBRTC, | 65 AUDIO_PROCESSING_IN_WEBRTC, | 
| 44 AUDIO_PROCESSING_MAX | 66 AUDIO_PROCESSING_MAX | 
| 45 }; | 67 }; | 
| 46 | 68 | 
| 47 void RecordProcessingState(AudioTrackProcessingStates state) { | 69 void RecordProcessingState(AudioTrackProcessingStates state) { | 
| 48 UMA_HISTOGRAM_ENUMERATION("Media.AudioTrackProcessingStates", | 70 UMA_HISTOGRAM_ENUMERATION("Media.AudioTrackProcessingStates", | 
| 49 state, AUDIO_PROCESSING_MAX); | 71 state, AUDIO_PROCESSING_MAX); | 
| 50 } | 72 } | 
| 51 | 73 | 
| 52 } // namespace | 74 } // namespace | 
| 53 | 75 | 
| 54 class MediaStreamAudioProcessor::MediaStreamAudioConverter | 76 // Wraps AudioFifo to provide a cleaner interface to MediaStreamAudioProcessor. | 
| 55 : public media::AudioConverter::InputCallback { | 77 // It avoids the FIFO when the source and destination frames match. | 
| 78 class MediaStreamAudioFifo { | |
| 56 public: | 79 public: | 
| 57 MediaStreamAudioConverter(const media::AudioParameters& source_params, | 80 MediaStreamAudioFifo(int channels, int source_frames, | 
| 58 const media::AudioParameters& sink_params) | 81 int destination_frames) | 
| 59 : source_params_(source_params), | 82 : source_frames_(source_frames), | 
| 60 sink_params_(sink_params), | 83 destination_(media::AudioBus::Create(channels, destination_frames)), | 
| 61 audio_converter_(source_params, sink_params_, false) { | 84 data_available_(false) { | 
| 62 // An instance of MediaStreamAudioConverter may be created in the main | 85 if (source_frames != destination_frames) { | 
| 63 // render thread and used in the audio thread, for example, the | 86 // Since we require every Push to be followed by as many Consumes as | 
| 64 // |MediaStreamAudioProcessor::capture_converter_|. | 87 // possible, twice the larger of the two is a (probably) loose upper bound | 
| 88 // on the FIFO size. | |
| 89 const int fifo_frames = 2 * std::max(source_frames, destination_frames); | |
| 90 fifo_.reset(new media::AudioFifo(channels, fifo_frames)); | |
| 91 } | |
| 92 | |
| 93 // An instance of MediaStreamAudioFifo may be created in the main render | |
| 94 // thread and used in the audio thread. | |
| 65 thread_checker_.DetachFromThread(); | 95 thread_checker_.DetachFromThread(); | 
| 66 audio_converter_.AddInput(this); | |
| 67 | |
| 68 // Create and initialize audio fifo and audio bus wrapper. | |
| 69 // The size of the FIFO should be at least twice of the source buffer size | |
| 70 // or twice of the sink buffer size. Also, FIFO needs to have enough space | |
| 71 // to store pre-processed data before passing the data to | |
| 72 // webrtc::AudioProcessing, which requires 10ms as packet size. | |
| 73 int max_frame_size = std::max(source_params_.frames_per_buffer(), | |
| 74 sink_params_.frames_per_buffer()); | |
| 75 int buffer_size = std::max( | |
| 76 kMaxNumberOfBuffersInFifo * max_frame_size, | |
| 77 kMaxNumberOfBuffersInFifo * source_params_.sample_rate() / 100); | |
| 78 fifo_.reset(new media::AudioFifo(source_params_.channels(), buffer_size)); | |
| 79 | |
| 80 // TODO(xians): Use CreateWrapper to save one memcpy. | |
| 81 audio_wrapper_ = media::AudioBus::Create(sink_params_.channels(), | |
| 82 sink_params_.frames_per_buffer()); | |
| 83 } | 96 } | 
| 84 | 97 | 
| 85 virtual ~MediaStreamAudioConverter() { | 98 void Push(const media::AudioBus* source) { | 
| 86 audio_converter_.RemoveInput(this); | 99 // Called on one of the capture or render audio threads exclusively. | 
| 100 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 101 DCHECK_EQ(source->channels(), channels()); | |
| 102 DCHECK_EQ(source->frames(), source_frames_); | |
| 103 if (fifo_) { | |
| 104 fifo_->Push(source); | |
| 105 } else { | |
| 106 source->CopyTo(destination_.get()); | |
| 107 data_available_ = true; | |
| 108 } | |
| 87 } | 109 } | 
| 88 | 110 | 
| 89 void Push(const media::AudioBus* audio_source) { | 111 // Returns true if there are destination_frames() of data available to be | 
| 90 // Called on the audio thread, which is the capture audio thread for | 112 // consumed, and otherwise false. | 
| 91 // |MediaStreamAudioProcessor::capture_converter_|, and render audio thread | 113 bool Consume(media::AudioBus** destination) { | 
| 92 // for |MediaStreamAudioProcessor::render_converter_|. | 114 // Called on one of the capture or render audio threads exclusively. | 
| 93 // And it must be the same thread as calling Convert(). | |
| 94 DCHECK(thread_checker_.CalledOnValidThread()); | 115 DCHECK(thread_checker_.CalledOnValidThread()); | 
| 95 fifo_->Push(audio_source); | |
| 96 } | |
| 97 | 116 | 
| 98 bool Convert(webrtc::AudioFrame* out, bool audio_mirroring) { | 117 if (fifo_) { | 
| 99 // Called on the audio thread, which is the capture audio thread for | 118 if (fifo_->frames() < destination_->frames()) | 
| 100 // |MediaStreamAudioProcessor::capture_converter_|, and render audio thread | 119 return false; | 
| 101 // for |MediaStreamAudioProcessor::render_converter_|. | 120 | 
| 102 DCHECK(thread_checker_.CalledOnValidThread()); | 121 fifo_->Consume(destination_.get(), 0, destination_->frames()); | 
| 103 // Return false if there is not enough data in the FIFO, this happens when | 122 } else { | 
| 104 // fifo_->frames() / source_params_.sample_rate() is less than | 123 if (!data_available_) | 
| 105 // sink_params.frames_per_buffer() / sink_params.sample_rate(). | 124 return false; | 
| 106 if (fifo_->frames() * sink_params_.sample_rate() < | 125 | 
| 107 sink_params_.frames_per_buffer() * source_params_.sample_rate()) { | 126 // The data was already copied to |destination_| in this case. | 
| 108 return false; | 127 data_available_ = false; | 
| 109 } | 128 } | 
| 110 | 129 | 
| 111 // Convert data to the output format, this will trigger ProvideInput(). | 130 *destination = destination_.get(); | 
| 112 audio_converter_.Convert(audio_wrapper_.get()); | |
| 113 DCHECK_EQ(audio_wrapper_->frames(), sink_params_.frames_per_buffer()); | |
| 114 | |
| 115 // Swap channels before interleaving the data if |audio_mirroring| is | |
| 116 // set to true. | |
| 117 if (audio_mirroring && | |
| 118 sink_params_.channel_layout() == media::CHANNEL_LAYOUT_STEREO) { | |
| 119 // Swap the first and second channels. | |
| 120 audio_wrapper_->SwapChannels(0, 1); | |
| 121 } | |
| 122 | |
| 123 // TODO(xians): Figure out a better way to handle the interleaved and | |
| 124 // deinterleaved format switching. | |
| 125 audio_wrapper_->ToInterleaved(audio_wrapper_->frames(), | |
| 126 sink_params_.bits_per_sample() / 8, | |
| 127 out->data_); | |
| 128 | |
| 129 out->samples_per_channel_ = sink_params_.frames_per_buffer(); | |
| 130 out->sample_rate_hz_ = sink_params_.sample_rate(); | |
| 131 out->speech_type_ = webrtc::AudioFrame::kNormalSpeech; | |
| 132 out->vad_activity_ = webrtc::AudioFrame::kVadUnknown; | |
| 133 out->num_channels_ = sink_params_.channels(); | |
| 134 | |
| 135 return true; | 131 return true; | 
| 136 } | 132 } | 
| 137 | 133 | 
| 138 const media::AudioParameters& source_parameters() const { | 134 int channels() { return destination_->channels(); } | 
| 
tommi (sloooow) - chröme
2014/08/06 13:45:56
make these const?
actually - are they necessary?
 
ajm
2014/08/07 04:11:37
These fulfill the same function as the previous so
 | |
| 139 return source_params_; | 135 int source_frames() { return source_frames_; } | 
| 140 } | 136 int destination_frames() { return destination_->frames(); } | 
| 141 const media::AudioParameters& sink_parameters() const { | |
| 142 return sink_params_; | |
| 143 } | |
| 144 | 137 | 
| 145 private: | 138 private: | 
| 146 // AudioConverter::InputCallback implementation. | |
| 147 virtual double ProvideInput(media::AudioBus* audio_bus, | |
| 148 base::TimeDelta buffer_delay) OVERRIDE { | |
| 149 // Called on realtime audio thread. | |
| 150 // TODO(xians): Figure out why the first Convert() triggers ProvideInput | |
| 151 // two times. | |
| 152 if (fifo_->frames() < audio_bus->frames()) | |
| 153 return 0; | |
| 154 | |
| 155 fifo_->Consume(audio_bus, 0, audio_bus->frames()); | |
| 156 | |
| 157 // Return 1.0 to indicate no volume scaling on the data. | |
| 158 return 1.0; | |
| 159 } | |
| 160 | |
| 161 base::ThreadChecker thread_checker_; | 139 base::ThreadChecker thread_checker_; | 
| 162 const media::AudioParameters source_params_; | 140 int source_frames_; | 
| 163 const media::AudioParameters sink_params_; | 141 scoped_ptr<media::AudioBus> destination_; | 
| 164 | |
| 165 // TODO(xians): consider using SincResampler to save some memcpy. | |
| 166 // Handles mixing and resampling between input and output parameters. | |
| 167 media::AudioConverter audio_converter_; | |
| 168 scoped_ptr<media::AudioBus> audio_wrapper_; | |
| 169 scoped_ptr<media::AudioFifo> fifo_; | 142 scoped_ptr<media::AudioFifo> fifo_; | 
| 143 // Only used when the FIFO is disabled; | |
| 144 bool data_available_; | |
| 170 }; | 145 }; | 
| 171 | 146 | 
| 172 bool MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled() { | 147 bool MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled() { | 
| 173 return !CommandLine::ForCurrentProcess()->HasSwitch( | 148 return !CommandLine::ForCurrentProcess()->HasSwitch( | 
| 174 switches::kDisableAudioTrackProcessing); | 149 switches::kDisableAudioTrackProcessing); | 
| 175 } | 150 } | 
| 176 | 151 | 
| 177 MediaStreamAudioProcessor::MediaStreamAudioProcessor( | 152 MediaStreamAudioProcessor::MediaStreamAudioProcessor( | 
| 178 const blink::WebMediaConstraints& constraints, | 153 const blink::WebMediaConstraints& constraints, | 
| 179 int effects, | 154 int effects, | 
| (...skipping 15 matching lines...) Expand all Loading... | |
| 195 aec_dump_message_filter_->AddDelegate(this); | 170 aec_dump_message_filter_->AddDelegate(this); | 
| 196 } | 171 } | 
| 197 } | 172 } | 
| 198 | 173 | 
| 199 MediaStreamAudioProcessor::~MediaStreamAudioProcessor() { | 174 MediaStreamAudioProcessor::~MediaStreamAudioProcessor() { | 
| 200 DCHECK(main_thread_checker_.CalledOnValidThread()); | 175 DCHECK(main_thread_checker_.CalledOnValidThread()); | 
| 201 Stop(); | 176 Stop(); | 
| 202 } | 177 } | 
| 203 | 178 | 
| 204 void MediaStreamAudioProcessor::OnCaptureFormatChanged( | 179 void MediaStreamAudioProcessor::OnCaptureFormatChanged( | 
| 205 const media::AudioParameters& source_params) { | 180 const media::AudioParameters& input_format) { | 
| 206 DCHECK(main_thread_checker_.CalledOnValidThread()); | 181 DCHECK(main_thread_checker_.CalledOnValidThread()); | 
| 207 // There is no need to hold a lock here since the caller guarantees that | 182 // There is no need to hold a lock here since the caller guarantees that | 
| 208 // there is no more PushCaptureData() and ProcessAndConsumeData() callbacks | 183 // there is no more PushCaptureData() and ProcessAndConsumeData() callbacks | 
| 209 // on the capture thread. | 184 // on the capture thread. | 
| 210 InitializeCaptureConverter(source_params); | 185 InitializeCaptureFifo(input_format); | 
| 211 | 186 | 
| 212 // Reset the |capture_thread_checker_| since the capture data will come from | 187 // Reset the |capture_thread_checker_| since the capture data will come from | 
| 213 // a new capture thread. | 188 // a new capture thread. | 
| 214 capture_thread_checker_.DetachFromThread(); | 189 capture_thread_checker_.DetachFromThread(); | 
| 215 } | 190 } | 
| 216 | 191 | 
| 217 void MediaStreamAudioProcessor::PushCaptureData( | 192 void MediaStreamAudioProcessor::PushCaptureData( | 
| 218 const media::AudioBus* audio_source) { | 193 const media::AudioBus* audio_source) { | 
| 219 DCHECK(capture_thread_checker_.CalledOnValidThread()); | 194 DCHECK(capture_thread_checker_.CalledOnValidThread()); | 
| 220 DCHECK_EQ(audio_source->channels(), | |
| 221 capture_converter_->source_parameters().channels()); | |
| 222 DCHECK_EQ(audio_source->frames(), | |
| 223 capture_converter_->source_parameters().frames_per_buffer()); | |
| 224 | 195 | 
| 225 capture_converter_->Push(audio_source); | 196 capture_fifo_->Push(audio_source); | 
| 226 } | 197 } | 
| 227 | 198 | 
| 228 bool MediaStreamAudioProcessor::ProcessAndConsumeData( | 199 bool MediaStreamAudioProcessor::ProcessAndConsumeData( | 
| 229 base::TimeDelta capture_delay, int volume, bool key_pressed, | 200 base::TimeDelta capture_delay, int volume, bool key_pressed, | 
| 230 int* new_volume, int16** out) { | 201 int* new_volume, int16** out) { | 
| 231 DCHECK(capture_thread_checker_.CalledOnValidThread()); | 202 DCHECK(capture_thread_checker_.CalledOnValidThread()); | 
| 232 TRACE_EVENT0("audio", "MediaStreamAudioProcessor::ProcessAndConsumeData"); | 203 TRACE_EVENT0("audio", "MediaStreamAudioProcessor::ProcessAndConsumeData"); | 
| 233 | 204 | 
| 234 if (!capture_converter_->Convert(&capture_frame_, audio_mirroring_)) | 205 media::AudioBus* input_bus; | 
| 206 if (!capture_fifo_->Consume(&input_bus)) | |
| 235 return false; | 207 return false; | 
| 236 | 208 | 
| 237 *new_volume = ProcessData(&capture_frame_, capture_delay, volume, | 209 // Use the input bus directly if audio processing is disabled. | 
| 238 key_pressed); | 210 media::AudioBus* output_bus = input_bus; | 
| 239 *out = capture_frame_.data_; | 211 *new_volume = 0; | 
| 212 if (audio_processing_) { | |
| 213 output_bus = output_bus_.get(); | |
| 214 *new_volume = ProcessData(input_bus, capture_delay, volume, | |
| 215 key_pressed, output_bus); | |
| 216 } | |
| 217 | |
| 218 // Swap channels before interleaving the data. | |
| 219 if (audio_mirroring_ && | |
| 220 output_format_.channel_layout() == media::CHANNEL_LAYOUT_STEREO) { | |
| 221 // Swap the first and second channels. | |
| 222 output_bus->SwapChannels(0, 1); | |
| 223 } | |
| 224 | |
| 225 output_bus->ToInterleaved(output_bus->frames(), | |
| 226 sizeof(int16), | |
| 227 output_data_.get()); | |
| 228 *out = output_data_.get(); | |
| 240 | 229 | 
| 241 return true; | 230 return true; | 
| 242 } | 231 } | 
| 243 | 232 | 
| 244 void MediaStreamAudioProcessor::Stop() { | 233 void MediaStreamAudioProcessor::Stop() { | 
| 245 DCHECK(main_thread_checker_.CalledOnValidThread()); | 234 DCHECK(main_thread_checker_.CalledOnValidThread()); | 
| 246 if (stopped_) | 235 if (stopped_) | 
| 247 return; | 236 return; | 
| 248 | 237 | 
| 249 stopped_ = true; | 238 stopped_ = true; | 
| 250 | 239 | 
| 251 if (aec_dump_message_filter_) { | 240 if (aec_dump_message_filter_) { | 
| 252 aec_dump_message_filter_->RemoveDelegate(this); | 241 aec_dump_message_filter_->RemoveDelegate(this); | 
| 253 aec_dump_message_filter_ = NULL; | 242 aec_dump_message_filter_ = NULL; | 
| 254 } | 243 } | 
| 255 | 244 | 
| 256 if (!audio_processing_.get()) | 245 if (!audio_processing_.get()) | 
| 257 return; | 246 return; | 
| 258 | 247 | 
| 259 StopEchoCancellationDump(audio_processing_.get()); | 248 StopEchoCancellationDump(audio_processing_.get()); | 
| 260 | 249 | 
| 261 if (playout_data_source_) { | 250 if (playout_data_source_) { | 
| 262 playout_data_source_->RemovePlayoutSink(this); | 251 playout_data_source_->RemovePlayoutSink(this); | 
| 263 playout_data_source_ = NULL; | 252 playout_data_source_ = NULL; | 
| 264 } | 253 } | 
| 265 } | 254 } | 
| 266 | 255 | 
| 267 const media::AudioParameters& MediaStreamAudioProcessor::InputFormat() const { | 256 const media::AudioParameters& MediaStreamAudioProcessor::InputFormat() const { | 
| 268 return capture_converter_->source_parameters(); | 257 return input_format_; | 
| 
tommi (sloooow) - chröme
2014/08/06 13:45:56
can we add a thread check to these methods? (or ca
 
ajm
2014/08/07 04:11:37
Unfortunately we can't make them const, as they ca
 | |
| 269 } | 258 } | 
| 270 | 259 | 
| 271 const media::AudioParameters& MediaStreamAudioProcessor::OutputFormat() const { | 260 const media::AudioParameters& MediaStreamAudioProcessor::OutputFormat() const { | 
| 272 return capture_converter_->sink_parameters(); | 261 return output_format_; | 
| 273 } | 262 } | 
| 274 | 263 | 
| 275 void MediaStreamAudioProcessor::OnAecDumpFile( | 264 void MediaStreamAudioProcessor::OnAecDumpFile( | 
| 276 const IPC::PlatformFileForTransit& file_handle) { | 265 const IPC::PlatformFileForTransit& file_handle) { | 
| 277 DCHECK(main_thread_checker_.CalledOnValidThread()); | 266 DCHECK(main_thread_checker_.CalledOnValidThread()); | 
| 278 | 267 | 
| 279 base::File file = IPC::PlatformFileForTransitToFile(file_handle); | 268 base::File file = IPC::PlatformFileForTransitToFile(file_handle); | 
| 280 DCHECK(file.IsValid()); | 269 DCHECK(file.IsValid()); | 
| 281 | 270 | 
| 282 if (audio_processing_) | 271 if (audio_processing_) | 
| (...skipping 18 matching lines...) Expand all Loading... | |
| 301 int audio_delay_milliseconds) { | 290 int audio_delay_milliseconds) { | 
| 302 DCHECK(render_thread_checker_.CalledOnValidThread()); | 291 DCHECK(render_thread_checker_.CalledOnValidThread()); | 
| 303 DCHECK(audio_processing_->echo_control_mobile()->is_enabled() ^ | 292 DCHECK(audio_processing_->echo_control_mobile()->is_enabled() ^ | 
| 304 audio_processing_->echo_cancellation()->is_enabled()); | 293 audio_processing_->echo_cancellation()->is_enabled()); | 
| 305 | 294 | 
| 306 TRACE_EVENT0("audio", "MediaStreamAudioProcessor::OnPlayoutData"); | 295 TRACE_EVENT0("audio", "MediaStreamAudioProcessor::OnPlayoutData"); | 
| 307 DCHECK_LT(audio_delay_milliseconds, | 296 DCHECK_LT(audio_delay_milliseconds, | 
| 308 std::numeric_limits<base::subtle::Atomic32>::max()); | 297 std::numeric_limits<base::subtle::Atomic32>::max()); | 
| 309 base::subtle::Release_Store(&render_delay_ms_, audio_delay_milliseconds); | 298 base::subtle::Release_Store(&render_delay_ms_, audio_delay_milliseconds); | 
| 310 | 299 | 
| 311 InitializeRenderConverterIfNeeded(sample_rate, audio_bus->channels(), | 300 InitializeRenderFifoIfNeeded(sample_rate, audio_bus->channels(), | 
| 312 audio_bus->frames()); | 301 audio_bus->frames()); | 
| 313 | 302 | 
| 314 render_converter_->Push(audio_bus); | 303 render_fifo_->Push(audio_bus); | 
| 315 while (render_converter_->Convert(&render_frame_, false)) | 304 media::AudioBus* analysis_bus; | 
| 316 audio_processing_->AnalyzeReverseStream(&render_frame_); | 305 while (render_fifo_->Consume(&analysis_bus)) { | 
| 306 audio_processing_->AnalyzeReverseStream( | |
| 307 analysis_bus->channel_data(), | |
| 308 analysis_bus->frames(), | |
| 309 sample_rate, | |
| 310 ChannelsToLayout(audio_bus->channels())); | |
| 311 } | |
| 317 } | 312 } | 
| 318 | 313 | 
| 319 void MediaStreamAudioProcessor::OnPlayoutDataSourceChanged() { | 314 void MediaStreamAudioProcessor::OnPlayoutDataSourceChanged() { | 
| 320 DCHECK(main_thread_checker_.CalledOnValidThread()); | 315 DCHECK(main_thread_checker_.CalledOnValidThread()); | 
| 321 // There is no need to hold a lock here since the caller guarantees that | 316 // There is no need to hold a lock here since the caller guarantees that | 
| 322 // there is no more OnPlayoutData() callback on the render thread. | 317 // there is no more OnPlayoutData() callback on the render thread. | 
| 323 render_thread_checker_.DetachFromThread(); | 318 render_thread_checker_.DetachFromThread(); | 
| 324 render_converter_.reset(); | 319 render_fifo_.reset(); | 
| 325 } | 320 } | 
| 326 | 321 | 
| 327 void MediaStreamAudioProcessor::GetStats(AudioProcessorStats* stats) { | 322 void MediaStreamAudioProcessor::GetStats(AudioProcessorStats* stats) { | 
| 328 stats->typing_noise_detected = | 323 stats->typing_noise_detected = | 
| 329 (base::subtle::Acquire_Load(&typing_detected_) != false); | 324 (base::subtle::Acquire_Load(&typing_detected_) != false); | 
| 330 GetAecStats(audio_processing_.get(), stats); | 325 GetAecStats(audio_processing_.get(), stats); | 
| 331 } | 326 } | 
| 332 | 327 | 
| 333 void MediaStreamAudioProcessor::InitializeAudioProcessingModule( | 328 void MediaStreamAudioProcessor::InitializeAudioProcessingModule( | 
| 334 const blink::WebMediaConstraints& constraints, int effects) { | 329 const blink::WebMediaConstraints& constraints, int effects) { | 
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 377 // Return immediately if no goog constraint is enabled. | 372 // Return immediately if no goog constraint is enabled. | 
| 378 if (!echo_cancellation && !goog_experimental_aec && !goog_ns && | 373 if (!echo_cancellation && !goog_experimental_aec && !goog_ns && | 
| 379 !goog_high_pass_filter && !goog_typing_detection && | 374 !goog_high_pass_filter && !goog_typing_detection && | 
| 380 !goog_agc && !goog_experimental_ns) { | 375 !goog_agc && !goog_experimental_ns) { | 
| 381 RecordProcessingState(AUDIO_PROCESSING_DISABLED); | 376 RecordProcessingState(AUDIO_PROCESSING_DISABLED); | 
| 382 return; | 377 return; | 
| 383 } | 378 } | 
| 384 | 379 | 
| 385 // Create and configure the webrtc::AudioProcessing. | 380 // Create and configure the webrtc::AudioProcessing. | 
| 386 audio_processing_.reset(webrtc::AudioProcessing::Create()); | 381 audio_processing_.reset(webrtc::AudioProcessing::Create()); | 
| 387 CHECK_EQ(0, audio_processing_->Initialize(kAudioProcessingSampleRate, | |
| 388 kAudioProcessingSampleRate, | |
| 389 kAudioProcessingSampleRate, | |
| 390 kAudioProcessingChannelLayout, | |
| 391 kAudioProcessingChannelLayout, | |
| 392 kAudioProcessingChannelLayout)); | |
| 393 | 382 | 
| 394 // Enable the audio processing components. | 383 // Enable the audio processing components. | 
| 395 if (echo_cancellation) { | 384 if (echo_cancellation) { | 
| 396 EnableEchoCancellation(audio_processing_.get()); | 385 EnableEchoCancellation(audio_processing_.get()); | 
| 397 | 386 | 
| 398 if (goog_experimental_aec) | 387 if (goog_experimental_aec) | 
| 399 EnableExperimentalEchoCancellation(audio_processing_.get()); | 388 EnableExperimentalEchoCancellation(audio_processing_.get()); | 
| 400 | 389 | 
| 401 if (playout_data_source_) | 390 if (playout_data_source_) | 
| 402 playout_data_source_->AddPlayoutSink(this); | 391 playout_data_source_->AddPlayoutSink(this); | 
| (...skipping 14 matching lines...) Expand all Loading... | |
| 417 typing_detector_.reset(new webrtc::TypingDetection()); | 406 typing_detector_.reset(new webrtc::TypingDetection()); | 
| 418 EnableTypingDetection(audio_processing_.get(), typing_detector_.get()); | 407 EnableTypingDetection(audio_processing_.get(), typing_detector_.get()); | 
| 419 } | 408 } | 
| 420 | 409 | 
| 421 if (goog_agc) | 410 if (goog_agc) | 
| 422 EnableAutomaticGainControl(audio_processing_.get()); | 411 EnableAutomaticGainControl(audio_processing_.get()); | 
| 423 | 412 | 
| 424 RecordProcessingState(AUDIO_PROCESSING_ENABLED); | 413 RecordProcessingState(AUDIO_PROCESSING_ENABLED); | 
| 425 } | 414 } | 
| 426 | 415 | 
| 427 void MediaStreamAudioProcessor::InitializeCaptureConverter( | 416 void MediaStreamAudioProcessor::InitializeCaptureFifo( | 
| 428 const media::AudioParameters& source_params) { | 417 const media::AudioParameters& input_format) { | 
| 429 DCHECK(main_thread_checker_.CalledOnValidThread()); | 418 DCHECK(main_thread_checker_.CalledOnValidThread()); | 
| 430 DCHECK(source_params.IsValid()); | 419 DCHECK(input_format.IsValid()); | 
| 420 input_format_ = input_format; | |
| 431 | 421 | 
| 432 // Create and initialize audio converter for the source data. | 422 // TODO(ajm): For now, we assume fixed parameters for the output when audio | 
| 433 // When the webrtc AudioProcessing is enabled, the sink format of the | 423 // processing is enabled, to match the previous behavior. We should either | 
| 434 // converter will be the same as the post-processed data format, which is | 424 // use the input parameters (in which case, audio processing will convert | 
| 435 // 32k mono for desktops and 16k mono for Android. When the AudioProcessing | 425 // at output) or ideally, have a backchannel from the sink to know what | 
| 436 // is disabled, the sink format will be the same as the source format. | 426 // format it would prefer. | 
| 437 const int sink_sample_rate = audio_processing_ ? | 427 const int output_sample_rate = audio_processing_ ? | 
| 438 kAudioProcessingSampleRate : source_params.sample_rate(); | 428 kAudioProcessingSampleRate : input_format.sample_rate(); | 
| 439 const media::ChannelLayout sink_channel_layout = audio_processing_ ? | 429 const media::ChannelLayout output_channel_layout = audio_processing_ ? | 
| 440 media::GuessChannelLayout(kAudioProcessingNumberOfChannels) : | 430 media::GuessChannelLayout(kAudioProcessingNumberOfChannels) : | 
| 441 source_params.channel_layout(); | 431 input_format.channel_layout(); | 
| 442 | 432 | 
| 443 // WebRtc AudioProcessing requires 10ms as its packet size. We use this | 433 // webrtc::AudioProcessing requires a 10 ms chunk size. We use this native | 
| 444 // native size when processing is enabled. While processing is disabled, and | 434 // size when processing is enabled. When disabled we use the same size as | 
| 445 // the source is running with a buffer size smaller than 10ms buffer, we use | 435 // the source if less than 10 ms. | 
| 446 // same buffer size as the incoming format to avoid extra FIFO for WebAudio. | 436 // | 
| 447 int sink_buffer_size = sink_sample_rate / 100; | 437 // TODO(ajm): This conditional buffer size appears to be assuming knowledge of | 
| 448 if (!audio_processing_ && | 438 // the sink based on the source parameters. PeerConnection sinks seem to want | 
| 449 source_params.frames_per_buffer() < sink_buffer_size) { | 439 // 10 ms chunks regardless, while WebAudio sinks want less, and we're assuming | 
| 450 sink_buffer_size = source_params.frames_per_buffer(); | 440 // we can identify WebAudio sinks by the input chunk size. Less fragile would | 
| 441 // be to have the sink actually tell us how much it wants (as in the above | |
| 442 // TODO). | |
| 443 const int processing_frames = audio_processing_ ? | |
| 444 input_format.sample_rate() / 100 : input_format.frames_per_buffer(); | |
| 445 const int output_frames = audio_processing_ ? | |
| 446 output_sample_rate / 100 : input_format.frames_per_buffer(); | |
| 447 | |
| 448 output_format_ = media::AudioParameters( | |
| 449 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | |
| 450 output_channel_layout, | |
| 451 output_sample_rate, | |
| 452 16, | |
| 453 output_frames); | |
| 454 | |
| 455 capture_fifo_.reset( | |
| 456 new MediaStreamAudioFifo(input_format.channels(), | |
| 457 input_format.frames_per_buffer(), | |
| 458 processing_frames)); | |
| 459 | |
| 460 if (audio_processing_) { | |
| 461 output_bus_ = media::AudioBus::Create(output_format_.channels(), | |
| 462 output_frames); | |
| 451 } | 463 } | 
| 452 | 464 output_data_.reset(new int16[output_format_.GetBytesPerBuffer() / | 
| 453 media::AudioParameters sink_params( | 465 sizeof(int16)]); | 
| 454 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, sink_channel_layout, | |
| 455 sink_sample_rate, 16, sink_buffer_size); | |
| 456 capture_converter_.reset( | |
| 457 new MediaStreamAudioConverter(source_params, sink_params)); | |
| 458 } | 466 } | 
| 459 | 467 | 
| 460 void MediaStreamAudioProcessor::InitializeRenderConverterIfNeeded( | 468 void MediaStreamAudioProcessor::InitializeRenderFifoIfNeeded( | 
| 461 int sample_rate, int number_of_channels, int frames_per_buffer) { | 469 int sample_rate, int number_of_channels, int frames_per_buffer) { | 
| 462 DCHECK(render_thread_checker_.CalledOnValidThread()); | 470 DCHECK(render_thread_checker_.CalledOnValidThread()); | 
| 463 // TODO(xians): Figure out if we need to handle the buffer size change. | 471 const int analysis_frames = sample_rate / 100; // 10 ms chunks. | 
| 464 if (render_converter_.get() && | 472 if (render_fifo_.get() && | 
| 465 render_converter_->source_parameters().sample_rate() == sample_rate && | 473 render_fifo_->channels() == number_of_channels && | 
| 466 render_converter_->source_parameters().channels() == number_of_channels) { | 474 render_fifo_->source_frames() == frames_per_buffer && | 
| 467 // Do nothing if the |render_converter_| has been setup properly. | 475 render_fifo_->destination_frames() == analysis_frames) { | 
| 476 // Do nothing if the |render_fifo_| has been setup properly. | |
| 468 return; | 477 return; | 
| 469 } | 478 } | 
| 470 | 479 | 
| 471 // Create and initialize audio converter for the render data. | 480 render_fifo_.reset( | 
| 472 // webrtc::AudioProcessing accepts the same format as what it uses to process | 481 new MediaStreamAudioFifo(number_of_channels, | 
| 473 // capture data, which is 32k mono for desktops and 16k mono for Android. | 482 frames_per_buffer, | 
| 474 media::AudioParameters source_params( | 483 analysis_frames)); | 
| 475 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | |
| 476 media::GuessChannelLayout(number_of_channels), sample_rate, 16, | |
| 477 frames_per_buffer); | |
| 478 media::AudioParameters sink_params( | |
| 479 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | |
| 480 media::CHANNEL_LAYOUT_MONO, kAudioProcessingSampleRate, 16, | |
| 481 kAudioProcessingSampleRate / 100); | |
| 482 render_converter_.reset( | |
| 483 new MediaStreamAudioConverter(source_params, sink_params)); | |
| 484 render_data_bus_ = media::AudioBus::Create(number_of_channels, | |
| 485 frames_per_buffer); | |
| 486 } | 484 } | 
| 487 | 485 | 
| 488 int MediaStreamAudioProcessor::ProcessData(webrtc::AudioFrame* audio_frame, | 486 int MediaStreamAudioProcessor::ProcessData(const media::AudioBus* input, | 
| 489 base::TimeDelta capture_delay, | 487 base::TimeDelta capture_delay, | 
| 490 int volume, | 488 int volume, | 
| 491 bool key_pressed) { | 489 bool key_pressed, | 
| 490 media::AudioBus* output) { | |
| 491 DCHECK(audio_processing_); | |
| 492 DCHECK(capture_thread_checker_.CalledOnValidThread()); | 492 DCHECK(capture_thread_checker_.CalledOnValidThread()); | 
| 493 if (!audio_processing_) | |
| 494 return 0; | |
| 495 | 493 | 
| 496 TRACE_EVENT0("audio", "MediaStreamAudioProcessor::ProcessData"); | 494 TRACE_EVENT0("audio", "MediaStreamAudioProcessor::ProcessData"); | 
| 497 DCHECK_EQ(audio_processing_->input_sample_rate_hz(), | |
| 498 capture_converter_->sink_parameters().sample_rate()); | |
| 499 DCHECK_EQ(audio_processing_->num_input_channels(), | |
| 500 capture_converter_->sink_parameters().channels()); | |
| 501 DCHECK_EQ(audio_processing_->num_output_channels(), | |
| 502 capture_converter_->sink_parameters().channels()); | |
| 503 | 495 | 
| 504 base::subtle::Atomic32 render_delay_ms = | 496 base::subtle::Atomic32 render_delay_ms = | 
| 505 base::subtle::Acquire_Load(&render_delay_ms_); | 497 base::subtle::Acquire_Load(&render_delay_ms_); | 
| 506 int64 capture_delay_ms = capture_delay.InMilliseconds(); | 498 int64 capture_delay_ms = capture_delay.InMilliseconds(); | 
| 507 DCHECK_LT(capture_delay_ms, | 499 DCHECK_LT(capture_delay_ms, | 
| 508 std::numeric_limits<base::subtle::Atomic32>::max()); | 500 std::numeric_limits<base::subtle::Atomic32>::max()); | 
| 509 int total_delay_ms = capture_delay_ms + render_delay_ms; | 501 int total_delay_ms = capture_delay_ms + render_delay_ms; | 
| 510 if (total_delay_ms > 300) { | 502 if (total_delay_ms > 300) { | 
| 511 LOG(WARNING) << "Large audio delay, capture delay: " << capture_delay_ms | 503 LOG(WARNING) << "Large audio delay, capture delay: " << capture_delay_ms | 
| 512 << "ms; render delay: " << render_delay_ms << "ms"; | 504 << "ms; render delay: " << render_delay_ms << "ms"; | 
| 513 } | 505 } | 
| 514 | 506 | 
| 515 audio_processing_->set_stream_delay_ms(total_delay_ms); | 507 webrtc::AudioProcessing* ap = audio_processing_.get(); | 
| 508 ap->set_stream_delay_ms(total_delay_ms); | |
| 516 | 509 | 
| 517 DCHECK_LE(volume, WebRtcAudioDeviceImpl::kMaxVolumeLevel); | 510 DCHECK_LE(volume, WebRtcAudioDeviceImpl::kMaxVolumeLevel); | 
| 518 webrtc::GainControl* agc = audio_processing_->gain_control(); | 511 webrtc::GainControl* agc = ap->gain_control(); | 
| 519 int err = agc->set_stream_analog_level(volume); | 512 int err = agc->set_stream_analog_level(volume); | 
| 520 DCHECK_EQ(err, 0) << "set_stream_analog_level() error: " << err; | 513 DCHECK_EQ(err, 0) << "set_stream_analog_level() error: " << err; | 
| 521 | 514 | 
| 522 audio_processing_->set_stream_key_pressed(key_pressed); | 515 ap->set_stream_key_pressed(key_pressed); | 
| 523 | 516 | 
| 524 err = audio_processing_->ProcessStream(audio_frame); | 517 err = ap->ProcessStream(input->channel_data(), | 
| 518 input->frames(), | |
| 519 input_format_.sample_rate(), | |
| 520 MapLayout(input_format_.channel_layout()), | |
| 521 output_format_.sample_rate(), | |
| 522 MapLayout(output_format_.channel_layout()), | |
| 523 output->channel_data()); | |
| 525 DCHECK_EQ(err, 0) << "ProcessStream() error: " << err; | 524 DCHECK_EQ(err, 0) << "ProcessStream() error: " << err; | 
| 526 | 525 | 
| 527 if (typing_detector_ && | 526 if (typing_detector_) { | 
| 528 audio_frame->vad_activity_ != webrtc::AudioFrame::kVadUnknown) { | 527 webrtc::VoiceDetection* vad = ap->voice_detection(); | 
| 529 bool vad_active = | 528 DCHECK(vad->is_enabled()); | 
| 530 (audio_frame->vad_activity_ == webrtc::AudioFrame::kVadActive); | 529 bool detected = typing_detector_->Process(key_pressed, | 
| 531 bool typing_detected = typing_detector_->Process(key_pressed, vad_active); | 530 vad->stream_has_voice()); | 
| 532 base::subtle::Release_Store(&typing_detected_, typing_detected); | 531 base::subtle::Release_Store(&typing_detected_, detected); | 
| 533 } | 532 } | 
| 534 | 533 | 
| 535 // Return 0 if the volume has not been changed, otherwise return the new | 534 // Return 0 if the volume hasn't been changed, and otherwise the new volume. | 
| 536 // volume. | |
| 537 return (agc->stream_analog_level() == volume) ? | 535 return (agc->stream_analog_level() == volume) ? | 
| 538 0 : agc->stream_analog_level(); | 536 0 : agc->stream_analog_level(); | 
| 539 } | 537 } | 
| 540 | 538 | 
| 541 } // namespace content | 539 } // namespace content | 
| OLD | NEW |