1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// found in the LICENSE file. 4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/renderer/media/media_stream_audio_processor.h" 6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/command_line.h" 8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/debug/trace_event.h" 903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#if defined(OS_MACOSX) 1003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "base/metrics/field_trial.h" 1103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#endif 12effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/metrics/histogram.h" 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/public/common/content_switches.h" 14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/renderer/media/media_stream_audio_processor_options.h" 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/renderer/media/rtc_media_constraints.h" 16a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch#include "content/renderer/media/webrtc_audio_device_impl.h" 17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/audio/audio_parameters.h" 18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/base/audio_converter.h" 19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/base/audio_fifo.h" 20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/base/channel_layout.h" 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "third_party/WebKit/public/platform/WebMediaConstraints.h" 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.h" 23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "third_party/webrtc/modules/audio_processing/typing_detection.h" 24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace content { 26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace { 28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using webrtc::AudioProcessing; 30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_ANDROID) 32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kAudioProcessingSampleRate = 16000; 33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#else 34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kAudioProcessingSampleRate = 32000; 35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#endif 36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst int kAudioProcessingNumberOfChannels = 1; 37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)AudioProcessing::ChannelLayout MapLayout(media::ChannelLayout media_layout) { 396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) switch (media_layout) { 406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) case media::CHANNEL_LAYOUT_MONO: 416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return AudioProcessing::kMono; 426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) case media::CHANNEL_LAYOUT_STEREO: 436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return AudioProcessing::kStereo; 446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) case media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC: 456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return AudioProcessing::kStereoAndKeyboard; 466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) default: 476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) NOTREACHED() << "Layout not supported: " << media_layout; 486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return AudioProcessing::kMono; 496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)AudioProcessing::ChannelLayout ChannelsToLayout(int num_channels) { 536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) switch (num_channels) { 546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) case 1: 556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return AudioProcessing::kMono; 566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) case 2: 576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return AudioProcessing::kStereo; 586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) default: 596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) NOTREACHED() << "Channels not supported: " << num_channels; 606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return AudioProcessing::kMono; 616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)} 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Used by UMA histograms and entries shouldn't be re-ordered or removed. 65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochenum AudioTrackProcessingStates { 66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch AUDIO_PROCESSING_ENABLED = 0, 67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch AUDIO_PROCESSING_DISABLED, 68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch AUDIO_PROCESSING_IN_WEBRTC, 69effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch AUDIO_PROCESSING_MAX 70effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}; 71effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 72effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordProcessingState(AudioTrackProcessingStates state) { 73effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch UMA_HISTOGRAM_ENUMERATION("Media.AudioTrackProcessingStates", 74effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch state, AUDIO_PROCESSING_MAX); 75effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 76effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} // namespace 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Wraps AudioBus to provide access to the array of channel pointers, since this 806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// is the type webrtc::AudioProcessing deals in. The array is refreshed on every 816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// channel_ptrs() call, and will be valid until the underlying AudioBus pointers 826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// are changed, e.g. through calls to SetChannelData() or SwapChannels(). 836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// 846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// All methods are called on one of the capture or render audio threads 856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// exclusively. 866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)class MediaStreamAudioBus { 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public: 886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) MediaStreamAudioBus(int channels, int frames) 896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) : bus_(media::AudioBus::Create(channels, frames)), 906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) channel_ptrs_(new float*[channels]) { 916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // May be created in the main render thread and used in the audio threads. 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) thread_checker_.DetachFromThread(); 93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) media::AudioBus* bus() { 966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DCHECK(thread_checker_.CalledOnValidThread()); 976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return bus_.get(); 98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) float* const* channel_ptrs() { 101f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK(thread_checker_.CalledOnValidThread()); 1026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (int i = 0; i < bus_->channels(); ++i) { 1036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) channel_ptrs_[i] = bus_->channel(i); 1046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return channel_ptrs_.get(); 106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private: 1096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) base::ThreadChecker thread_checker_; 1106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) scoped_ptr<media::AudioBus> bus_; 1116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) scoped_ptr<float*[]> channel_ptrs_; 1126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}; 1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Wraps AudioFifo to provide a cleaner interface to MediaStreamAudioProcessor. 1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// It avoids the FIFO when the source and destination frames match. All methods 1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// are called on one of the capture or render audio threads exclusively. 1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)class MediaStreamAudioFifo { 1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public: 1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) MediaStreamAudioFifo(int channels, int source_frames, 1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) int destination_frames) 1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) : source_frames_(source_frames), 1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) destination_(new MediaStreamAudioBus(channels, destination_frames)), 1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) data_available_(false) { 1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (source_frames != destination_frames) { 1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Since we require every Push to be followed by as many Consumes as 1266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // possible, twice the larger of the two is a (probably) loose upper bound 1276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // on the FIFO size. 1286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const int fifo_frames = 2 * std::max(source_frames, destination_frames); 1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) fifo_.reset(new media::AudioFifo(channels, fifo_frames)); 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // May be created in the main render thread and used in the audio threads. 1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) thread_checker_.DetachFromThread(); 1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 135116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) void Push(const media::AudioBus* source) { 1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DCHECK(thread_checker_.CalledOnValidThread()); 1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DCHECK_EQ(source->channels(), destination_->bus()->channels()); 1396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DCHECK_EQ(source->frames(), source_frames_); 1406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (fifo_) { 1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) fifo_->Push(source); 1436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } else { 1446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) source->CopyTo(destination_->bus()); 1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) data_available_ = true; 146116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Returns true if there are destination_frames() of data available to be 1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // consumed, and otherwise false. 1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) bool Consume(MediaStreamAudioBus** destination) { 1526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DCHECK(thread_checker_.CalledOnValidThread()); 153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (fifo_) { 1556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (fifo_->frames() < destination_->bus()->frames()) 1566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return false; 157f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) fifo_->Consume(destination_->bus(), 0, destination_->bus()->frames()); 1596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } else { 1606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!data_available_) 1616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return false; 162f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // The data was already copied to |destination_| in this case. 1646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) data_available_ = false; 1656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *destination = destination_.get(); 1686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return true; 169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private: 172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::ThreadChecker thread_checker_; 1736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const int source_frames_; // For a DCHECK. 1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) scoped_ptr<MediaStreamAudioBus> destination_; 175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) scoped_ptr<media::AudioFifo> fifo_; 1766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Only used when the FIFO is disabled; 1776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) bool data_available_; 178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}; 179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochbool MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled() { 181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return !CommandLine::ForCurrentProcess()->HasSwitch( 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) switches::kDisableAudioTrackProcessing); 1830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch} 1840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)MediaStreamAudioProcessor::MediaStreamAudioProcessor( 1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const blink::WebMediaConstraints& constraints, 187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int effects, 188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) WebRtcPlayoutDataSource* playout_data_source) 1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : render_delay_ms_(0), 190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) playout_data_source_(playout_data_source), 191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) audio_mirroring_(false), 192116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch typing_detected_(false), 193116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch stopped_(false) { 194f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) capture_thread_checker_.DetachFromThread(); 195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) render_thread_checker_.DetachFromThread(); 196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) InitializeAudioProcessingModule(constraints, effects); 197f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if (IsAudioTrackProcessingEnabled()) { 198f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) aec_dump_message_filter_ = AecDumpMessageFilter::Get(); 199f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // In unit tests not creating a message filter, |aec_dump_message_filter_| 200f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // will be NULL. We can just ignore that. Other unit tests and browser tests 201f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // ensure that we do get the filter when we should. 2021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (aec_dump_message_filter_.get()) 203f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) aec_dump_message_filter_->AddDelegate(this); 204f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) } 205f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 206f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 207f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)MediaStreamAudioProcessor::~MediaStreamAudioProcessor() { 208f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK(main_thread_checker_.CalledOnValidThread()); 209116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Stop(); 210f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 211f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void MediaStreamAudioProcessor::OnCaptureFormatChanged( 2136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const media::AudioParameters& input_format) { 214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK(main_thread_checker_.CalledOnValidThread()); 215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // There is no need to hold a lock here since the caller guarantees that 216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // there is no more PushCaptureData() and ProcessAndConsumeData() callbacks 217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // on the capture thread. 2186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) InitializeCaptureFifo(input_format); 219f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Reset the |capture_thread_checker_| since the capture data will come from 221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // a new capture thread. 222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) capture_thread_checker_.DetachFromThread(); 223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 224f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 225116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid MediaStreamAudioProcessor::PushCaptureData( 226116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const media::AudioBus* audio_source) { 227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK(capture_thread_checker_.CalledOnValidThread()); 228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 2296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) capture_fifo_->Push(audio_source); 230f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 231f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool MediaStreamAudioProcessor::ProcessAndConsumeData( 233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::TimeDelta capture_delay, int volume, bool key_pressed, 2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int* new_volume, int16** out) { 235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK(capture_thread_checker_.CalledOnValidThread()); 236a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) TRACE_EVENT0("audio", "MediaStreamAudioProcessor::ProcessAndConsumeData"); 237f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 2386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) MediaStreamAudioBus* process_bus; 2396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!capture_fifo_->Consume(&process_bus)) 240f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 2426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Use the process bus directly if audio processing is disabled. 2436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) MediaStreamAudioBus* output_bus = process_bus; 2446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *new_volume = 0; 2456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (audio_processing_) { 2466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_bus = output_bus_.get(); 2476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *new_volume = ProcessData(process_bus->channel_ptrs(), 2486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) process_bus->bus()->frames(), capture_delay, 2496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) volume, key_pressed, output_bus->channel_ptrs()); 2506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 2516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Swap channels before interleaving the data. 2536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (audio_mirroring_ && 2546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_format_.channel_layout() == media::CHANNEL_LAYOUT_STEREO) { 2556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Swap the first and second channels. 2566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_bus->bus()->SwapChannels(0, 1); 2576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 2586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 2596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_bus->bus()->ToInterleaved(output_bus->bus()->frames(), 2606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sizeof(int16), 2616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_data_.get()); 2626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *out = output_data_.get(); 263f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 264f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return true; 265f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 266f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 267116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid MediaStreamAudioProcessor::Stop() { 268116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK(main_thread_checker_.CalledOnValidThread()); 269116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (stopped_) 270116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return; 271116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 272116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch stopped_ = true; 273116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 2741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (aec_dump_message_filter_.get()) { 275116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch aec_dump_message_filter_->RemoveDelegate(this); 276116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch aec_dump_message_filter_ = NULL; 277116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 278116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 279116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!audio_processing_.get()) 280116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return; 281116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 282116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch StopEchoCancellationDump(audio_processing_.get()); 283116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 284116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (playout_data_source_) { 285116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch playout_data_source_->RemovePlayoutSink(this); 286116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch playout_data_source_ = NULL; 287116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 288116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch} 289116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const media::AudioParameters& MediaStreamAudioProcessor::InputFormat() const { 2916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return input_format_; 292f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 293f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 294f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const media::AudioParameters& MediaStreamAudioProcessor::OutputFormat() const { 2956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return output_format_; 296f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 297f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 298f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void MediaStreamAudioProcessor::OnAecDumpFile( 299f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) const IPC::PlatformFileForTransit& file_handle) { 300f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) DCHECK(main_thread_checker_.CalledOnValidThread()); 301f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 302f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) base::File file = IPC::PlatformFileForTransitToFile(file_handle); 303f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) DCHECK(file.IsValid()); 304f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 305a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (audio_processing_) 306f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) StartEchoCancellationDump(audio_processing_.get(), file.Pass()); 307f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) else 308f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) file.Close(); 309a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 310a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 311f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void MediaStreamAudioProcessor::OnDisableAecDump() { 312f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) DCHECK(main_thread_checker_.CalledOnValidThread()); 313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (audio_processing_) 314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) StopEchoCancellationDump(audio_processing_.get()); 315a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 316a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 317f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void MediaStreamAudioProcessor::OnIpcClosing() { 318f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) DCHECK(main_thread_checker_.CalledOnValidThread()); 319f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) aec_dump_message_filter_ = NULL; 320f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)} 321f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 322a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void MediaStreamAudioProcessor::OnPlayoutData(media::AudioBus* audio_bus, 323a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int sample_rate, 324a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int audio_delay_milliseconds) { 325a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK(render_thread_checker_.CalledOnValidThread()); 326010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) DCHECK(audio_processing_->echo_control_mobile()->is_enabled() ^ 327010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) audio_processing_->echo_cancellation()->is_enabled()); 328a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 329a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) TRACE_EVENT0("audio", "MediaStreamAudioProcessor::OnPlayoutData"); 330a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK_LT(audio_delay_milliseconds, 331a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) std::numeric_limits<base::subtle::Atomic32>::max()); 332a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::subtle::Release_Store(&render_delay_ms_, audio_delay_milliseconds); 333a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 3346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) InitializeRenderFifoIfNeeded(sample_rate, audio_bus->channels(), 3356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) audio_bus->frames()); 3366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 3376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) render_fifo_->Push(audio_bus); 3386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) MediaStreamAudioBus* analysis_bus; 3396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) while (render_fifo_->Consume(&analysis_bus)) { 3406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) audio_processing_->AnalyzeReverseStream( 3416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) analysis_bus->channel_ptrs(), 3426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) analysis_bus->bus()->frames(), 3436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sample_rate, 3446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ChannelsToLayout(audio_bus->channels())); 3456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 346a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 347a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 348a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void MediaStreamAudioProcessor::OnPlayoutDataSourceChanged() { 349a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK(main_thread_checker_.CalledOnValidThread()); 350a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // There is no need to hold a lock here since the caller guarantees that 351a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // there is no more OnPlayoutData() callback on the render thread. 352a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) render_thread_checker_.DetachFromThread(); 3536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) render_fifo_.reset(); 354a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 355a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 356a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void MediaStreamAudioProcessor::GetStats(AudioProcessorStats* stats) { 357a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) stats->typing_noise_detected = 358a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) (base::subtle::Acquire_Load(&typing_detected_) != false); 359a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) GetAecStats(audio_processing_.get(), stats); 360a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 361a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 362f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MediaStreamAudioProcessor::InitializeAudioProcessingModule( 363cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const blink::WebMediaConstraints& constraints, int effects) { 364f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK(!audio_processing_); 365f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 366cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MediaAudioConstraints audio_constraints(constraints, effects); 367a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 368effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // Audio mirroring can be enabled even though audio processing is otherwise 369effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // disabled. 370cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) audio_mirroring_ = audio_constraints.GetProperty( 371cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MediaAudioConstraints::kGoogAudioMirroring); 372effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 373effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!IsAudioTrackProcessingEnabled()) { 374effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordProcessingState(AUDIO_PROCESSING_IN_WEBRTC); 375effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return; 376effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 377effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 3785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_IOS) 379cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // On iOS, VPIO provides built-in AGC and AEC. 380cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool echo_cancellation = false; 381cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool goog_agc = false; 3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#else 383cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool echo_cancellation = 384cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) audio_constraints.GetEchoCancellationProperty(); 385cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool goog_agc = audio_constraints.GetProperty( 386cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MediaAudioConstraints::kGoogAutoGainControl); 3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#endif 3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_IOS) || defined(OS_ANDROID) 390cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool goog_experimental_aec = false; 391cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool goog_typing_detection = false; 392f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#else 393cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool goog_experimental_aec = audio_constraints.GetProperty( 394cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MediaAudioConstraints::kGoogExperimentalEchoCancellation); 395cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool goog_typing_detection = audio_constraints.GetProperty( 396cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MediaAudioConstraints::kGoogTypingNoiseDetection); 397f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#endif 398f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 399cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool goog_ns = audio_constraints.GetProperty( 400cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MediaAudioConstraints::kGoogNoiseSuppression); 401cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool goog_experimental_ns = audio_constraints.GetProperty( 402cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MediaAudioConstraints::kGoogExperimentalNoiseSuppression); 403cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const bool goog_high_pass_filter = audio_constraints.GetProperty( 404cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MediaAudioConstraints::kGoogHighpassFilter); 405cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 406cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Return immediately if no goog constraint is enabled. 407cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!echo_cancellation && !goog_experimental_aec && !goog_ns && 408cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) !goog_high_pass_filter && !goog_typing_detection && 409cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) !goog_agc && !goog_experimental_ns) { 410effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordProcessingState(AUDIO_PROCESSING_DISABLED); 411f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return; 412f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 413f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 4146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Experimental options provided at creation. 4156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) webrtc::Config config; 4166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (goog_experimental_aec) 4176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) config.Set<webrtc::DelayCorrection>(new webrtc::DelayCorrection(true)); 4186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (goog_experimental_ns) 4196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(true)); 42003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#if defined(OS_MACOSX) 42103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (base::FieldTrialList::FindFullName("NoReportedDelayOnMac") == "Enabled") 42203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) config.Set<webrtc::ReportedDelay>(new webrtc::ReportedDelay(false)); 42303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#endif 4246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 425f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Create and configure the webrtc::AudioProcessing. 426e4256316f8b5e8d1ec0df1f7762771622a53fa63Ben Murdoch audio_processing_.reset(webrtc::AudioProcessing::Create(config)); 427f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 428f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Enable the audio processing components. 429cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (echo_cancellation) { 430f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EnableEchoCancellation(audio_processing_.get()); 431cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 432a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (playout_data_source_) 433a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) playout_data_source_->AddPlayoutSink(this); 434f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 435f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 436cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog_ns) 437f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EnableNoiseSuppression(audio_processing_.get()); 438f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 439cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog_high_pass_filter) 440f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EnableHighPassFilter(audio_processing_.get()); 441f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 442cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog_typing_detection) { 443a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // TODO(xians): Remove this |typing_detector_| after the typing suppression 444a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // is enabled by default. 445a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) typing_detector_.reset(new webrtc::TypingDetection()); 446a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EnableTypingDetection(audio_processing_.get(), typing_detector_.get()); 447a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 448f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 449cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog_agc) 4505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) EnableAutomaticGainControl(audio_processing_.get()); 451f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 452effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordProcessingState(AUDIO_PROCESSING_ENABLED); 453f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 454f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 4556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void MediaStreamAudioProcessor::InitializeCaptureFifo( 4566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const media::AudioParameters& input_format) { 457a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DCHECK(main_thread_checker_.CalledOnValidThread()); 4586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DCHECK(input_format.IsValid()); 4596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) input_format_ = input_format; 4606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // TODO(ajm): For now, we assume fixed parameters for the output when audio 4626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // processing is enabled, to match the previous behavior. We should either 4636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // use the input parameters (in which case, audio processing will convert 4646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // at output) or ideally, have a backchannel from the sink to know what 4656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // format it would prefer. 4666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const int output_sample_rate = audio_processing_ ? 4676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) kAudioProcessingSampleRate : input_format.sample_rate(); 4686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const media::ChannelLayout output_channel_layout = audio_processing_ ? 469effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch media::GuessChannelLayout(kAudioProcessingNumberOfChannels) : 4706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) input_format.channel_layout(); 4716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // webrtc::AudioProcessing requires a 10 ms chunk size. We use this native 4736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // size when processing is enabled. When disabled we use the same size as 4746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // the source if less than 10 ms. 4756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 4766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // TODO(ajm): This conditional buffer size appears to be assuming knowledge of 4776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // the sink based on the source parameters. PeerConnection sinks seem to want 4786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // 10 ms chunks regardless, while WebAudio sinks want less, and we're assuming 4796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // we can identify WebAudio sinks by the input chunk size. Less fragile would 4806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // be to have the sink actually tell us how much it wants (as in the above 4816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // TODO). 4826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) int processing_frames = input_format.sample_rate() / 100; 4836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) int output_frames = output_sample_rate / 100; 4846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!audio_processing_ && input_format.frames_per_buffer() < output_frames) { 4856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) processing_frames = input_format.frames_per_buffer(); 4866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_frames = processing_frames; 4875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 4885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 4896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_format_ = media::AudioParameters( 4906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 4916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_channel_layout, 4926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_sample_rate, 4936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 16, 4946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_frames); 4956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) capture_fifo_.reset( 4976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) new MediaStreamAudioFifo(input_format.channels(), 4986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) input_format.frames_per_buffer(), 4996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) processing_frames)); 5006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (audio_processing_) { 5026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_bus_.reset(new MediaStreamAudioBus(output_format_.channels(), 5036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_frames)); 5046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 5056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_data_.reset(new int16[output_format_.GetBytesPerBuffer() / 5066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sizeof(int16)]); 5075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 5096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void MediaStreamAudioProcessor::InitializeRenderFifoIfNeeded( 510f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int sample_rate, int number_of_channels, int frames_per_buffer) { 511f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK(render_thread_checker_.CalledOnValidThread()); 5126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (render_fifo_.get() && 5136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) render_format_.sample_rate() == sample_rate && 5146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) render_format_.channels() == number_of_channels && 5156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) render_format_.frames_per_buffer() == frames_per_buffer) { 5166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Do nothing if the |render_fifo_| has been setup properly. 517f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return; 518f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 519f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) render_format_ = media::AudioParameters( 521f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 5226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) media::GuessChannelLayout(number_of_channels), 5236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) sample_rate, 5246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 16, 525f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) frames_per_buffer); 5266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const int analysis_frames = sample_rate / 100; // 10 ms chunks. 5286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) render_fifo_.reset( 5296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) new MediaStreamAudioFifo(number_of_channels, 5306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) frames_per_buffer, 5316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) analysis_frames)); 532f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 533f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)int MediaStreamAudioProcessor::ProcessData(const float* const* process_ptrs, 5356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) int process_frames, 5365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::TimeDelta capture_delay, 5375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int volume, 5386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) bool key_pressed, 5396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) float* const* output_ptrs) { 5406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DCHECK(audio_processing_); 541f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK(capture_thread_checker_.CalledOnValidThread()); 542f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) TRACE_EVENT0("audio", "MediaStreamAudioProcessor::ProcessData"); 544f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 545f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::subtle::Atomic32 render_delay_ms = 546f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::subtle::Acquire_Load(&render_delay_ms_); 547f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int64 capture_delay_ms = capture_delay.InMilliseconds(); 548f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK_LT(capture_delay_ms, 549f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) std::numeric_limits<base::subtle::Atomic32>::max()); 550f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int total_delay_ms = capture_delay_ms + render_delay_ms; 5515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (total_delay_ms > 300) { 552f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) LOG(WARNING) << "Large audio delay, capture delay: " << capture_delay_ms 553f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) << "ms; render delay: " << render_delay_ms << "ms"; 554f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 555f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) webrtc::AudioProcessing* ap = audio_processing_.get(); 5576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ap->set_stream_delay_ms(total_delay_ms); 558a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 559a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch DCHECK_LE(volume, WebRtcAudioDeviceImpl::kMaxVolumeLevel); 5606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) webrtc::GainControl* agc = ap->gain_control(); 561f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int err = agc->set_stream_analog_level(volume); 562f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK_EQ(err, 0) << "set_stream_analog_level() error: " << err; 563a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 5646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) ap->set_stream_key_pressed(key_pressed); 565a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 5666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) err = ap->ProcessStream(process_ptrs, 5676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) process_frames, 5686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) input_format_.sample_rate(), 5696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) MapLayout(input_format_.channel_layout()), 5706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_format_.sample_rate(), 5716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) MapLayout(output_format_.channel_layout()), 5726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) output_ptrs); 573f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK_EQ(err, 0) << "ProcessStream() error: " << err; 574f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (typing_detector_) { 5766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) webrtc::VoiceDetection* vad = ap->voice_detection(); 5776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DCHECK(vad->is_enabled()); 5786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) bool detected = typing_detector_->Process(key_pressed, 5796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) vad->stream_has_voice()); 5806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) base::subtle::Release_Store(&typing_detected_, detected); 5815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 5825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 5836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // Return 0 if the volume hasn't been changed, and otherwise the new volume. 5845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return (agc->stream_analog_level() == volume) ? 5855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 0 : agc->stream_analog_level(); 586f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 587f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 588f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} // namespace content 589