Index: content/renderer/media/webrtc_audio_device_impl.h |
diff --git a/content/renderer/media/webrtc_audio_device_impl.h b/content/renderer/media/webrtc_audio_device_impl.h |
index b3f3534b77b71979e2a6673703b935d7cd107f5e..c00e1291b444ae627c33c06c066c3e871a497b49 100644 |
--- a/content/renderer/media/webrtc_audio_device_impl.h |
+++ b/content/renderer/media/webrtc_audio_device_impl.h |
@@ -49,7 +49,7 @@ |
// base->StartPlayout(ch); |
// base->StartSending(ch); |
// ... |
-// <== full-duplex audio session ==> |
+// <== full-duplex audio session with AGC enabled ==> |
// ... |
// base->DeleteChannel(ch); |
// base->Terminate(); |
@@ -57,15 +57,30 @@ |
// VoiceEngine::Delete(voe); |
// } |
// |
-// Note that, WebRtcAudioDeviceImpl::RegisterAudioCallback() will |
-// be called by the webrtc::VoiceEngine::Init() call and the |
-// webrtc::VoiceEngine is an webrtc::AudioTransport implementation. |
-// Hence, when the underlying audio layer wants data samples to be played out, |
-// the AudioDevice::RenderCallback() will be called, which in turn uses the |
-// registered webrtc::AudioTransport callback and feeds the data to the |
-// webrtc::VoiceEngine. |
+// webrtc::VoiceEngine::Init() calls these ADM methods (in this order): |
// |
-// The picture below illustrates the media flow on the capture side: |
+// RegisterAudioCallback(this) |
+// webrtc::VoiceEngine is an webrtc::AudioTransport implementation and |
+// implements the RecordedDataIsAvailable() and NeedMorePlayData() callbacks. |
+// |
+// Init() |
+// Creates and initializes the AudioDevice and AudioInputDevice objects. |
+// |
+// SetAGC(true) |
+// Enables the adaptive analog mode of the AGC which ensures that a |
+// suitable microphone volume level will be set. This scheme will affect |
+// the actual microphone control slider. |
+// |
+// Media example: |
+// |
+// When the underlying audio layer wants data samples to be played out, the |
+// AudioDevice::RenderCallback() will be called, which in turn uses the |
+// registered webrtc::AudioTransport callback and gets the data to be played |
+// out from the webrtc::VoiceEngine. |
+// |
+// The picture below illustrates the media flow on the capture side where the |
+// AudioInputDevice client acts as link between the renderer and browser |
+// process: |
// |
// .------------------. .----------------------. |
// (Native audio) => | AudioInputStream |-> OnData ->| AudioInputController |-. |
@@ -87,11 +102,106 @@ |
// The actual data is transferred via SharedMemory. IPC is not involved |
// in the actual media transfer. |
// |
+// AGC overview: |
+// |
+// It aims to maintain a constant speech loudness level from the microphone. |
+// This is done by both controlling the analog microphone gain and applying |
+// digital gain. The microphone gain on the sound card is slowly |
+// increased/decreased during speech only. By observing the microphone control |
+// slider you can see it move when you speak. If you scream, the slider moves |
+// downwards and then upwards again when you return to normal. It is not |
+// uncommon that the slider hits the maximum. This means that the maximum |
+// analog gain is not large enough to give the desired loudness. Nevertheless, |
+// we can in general still attain the desired loudness. If the microphone |
+// control slider is moved manually, the gain adaptation restarts and returns |
+// to roughly the same position as before the change if the circumstances are |
+// still the same. When the input microphone signal causes saturation, the |
+// level is decreased dramatically and has to re-adapt towards the old level. |
+// The adaptation is a slowly varying process and at the beginning of capture |
+// this is noticed by a slow increase in volume. Smaller changes in microphone |
+// input level is leveled out by the built-in digital control. For larger |
+// differences we need to rely on the slow adaptation. |
+// See http://en.wikipedia.org/wiki/Automatic_gain_control for more details. |
+// |
+// AGC implementation details: |
+// |
+// The adaptive analog mode of the AGC is always enabled for desktop platforms |
+// in WebRTC. |
+// |
+// Before recording starts, the ADM sets an AGC state in the |
+// AudioInputDevice by calling AudioInputDevice::SetAutomaticGainControl(true). |
+// |
+// A capture session with AGC is started up as follows (simplified): |
+// |
+// [renderer] |
+// | |
+// ADM::StartRecording() |
+// AudioInputDevice::InitializeOnIOThread() |
+// AudioInputHostMsg_CreateStream(..., agc=true) [IPC] |
+// | |
+// [IPC to the browser] |
+// | |
+// AudioInputRendererHost::OnCreateStream() |
+// AudioInputController::CreateLowLatency() |
+// AudioInputController::DoSetAutomaticGainControl(true) |
+// AudioInputStream::SetAutomaticGainControl(true) |
+// | |
+// AGC is now enabled in the media layer and streaming starts (details omitted). |
+// The figure below illustrates the AGC scheme which is active in combination |
+// with the default media flow explained earlier. |
+// | |
+// [browser] |
+// | |
+// AudioInputStream::(Capture thread loop) |
+// AudioInputStreamImpl::QueryAgcVolume() => new volume once per second |
+// AudioInputData::OnData(..., volume) |
+// AudioInputController::OnData(..., volume) |
+// AudioInputSyncWriter::Write(..., volume) |
+// | |
+// [volume | size | data] is sent to the renderer [shared memory] |
+// | |
+// [renderer] |
+// | |
+// AudioInputDevice::AudioThreadCallback::Process() |
+// WebRtcAudioDeviceImpl::Capture(..., volume) |
+// AudioTransport::RecordedDataIsAvailable(...,volume, new_volume) |
+// | |
+// The AGC now uses the current volume input and computes a suitable new |
+// level given by the |new_level| output. This value is only non-zero if the |
+// AGC has take a decision that the microphone level should change. |
+// | |
+// if (new_volume != 0) |
+// AudioInputDevice::SetVolume(new_volume) |
+// AudioInputHostMsg_SetVolume(new_volume) [IPC] |
+// | |
+// [IPC to the browser] |
+// | |
+// AudioInputRendererHost::OnSetVolume() |
+// AudioInputController::SetVolume() |
+// AudioInputStream::SetVolume(scaled_volume) |
+// | |
+// Here we set the new microphone level in the media layer and at the same time |
+// read the new setting (we might not get exactly what is set). |
+// | |
+// AudioInputData::OnData(..., updated_volume) |
+// AudioInputController::OnData(..., updated_volume) |
+// | |
+// | |
+// This process repeats until we stop capturing data. Note that, a common |
+// steady state is that the volume control reaches its max and the new_volume |
+// value from the AGC is zero. A loud voice input is required to break this |
+// state and start lowering the level again. |
+// |
// Implementation notes: |
// |
// - This class must be created on the main render thread. |
// - The webrtc::AudioDeviceModule is reference counted. |
-// - Recording is currently not supported on Mac OS X. |
+// - AGC is only supported in combination with the WASAPI-based audio layer |
+// on Windows, i.e., it is not supported on Windows XP. |
+// - All volume levels required for the AGC scheme are transfered in a |
+// normalized range [0.0, 1.0]. Scaling takes place in both endpoints |
+// (WebRTC client a media layer). This approach ensures that we can avoid |
+// transferring maximum levels between the renderer and the browser. |
// |
class CONTENT_EXPORT WebRtcAudioDeviceImpl |
: NON_EXPORTED_BASE(public webrtc::AudioDeviceModule), |
@@ -120,7 +230,8 @@ class CONTENT_EXPORT WebRtcAudioDeviceImpl |
// AudioInputDevice::CaptureCallback implementation. |
virtual void Capture(const std::vector<float*>& audio_data, |
size_t number_of_frames, |
- size_t audio_delay_milliseconds) OVERRIDE; |
+ size_t audio_delay_milliseconds, |
+ double volume) OVERRIDE; |
virtual void OnCaptureError() OVERRIDE; |
// AudioInputDevice::CaptureEventHandler implementation. |
@@ -336,6 +447,9 @@ class CONTENT_EXPORT WebRtcAudioDeviceImpl |
bool playing_; |
bool recording_; |
+ // Local copy of the current Automatic Gain Control state. |
+ bool agc_is_enabled_; |
+ |
DISALLOW_COPY_AND_ASSIGN(WebRtcAudioDeviceImpl); |
}; |