Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/media/webrtc_audio_device_impl.h" | 5 #include "content/renderer/media/webrtc_audio_device_impl.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/string_util.h" | 8 #include "base/string_util.h" |
| 9 #include "base/win/windows_version.h" | 9 #include "base/win/windows_version.h" |
| 10 #include "content/renderer/media/audio_hardware.h" | 10 #include "content/renderer/media/audio_hardware.h" |
| 11 #include "content/renderer/render_thread_impl.h" | 11 #include "content/renderer/render_thread_impl.h" |
| 12 #include "media/audio/audio_util.h" | 12 #include "media/audio/audio_util.h" |
| 13 | 13 |
| 14 static const int64 kMillisecondsBetweenProcessCalls = 5000; | 14 static const int64 kMillisecondsBetweenProcessCalls = 5000; |
| 15 static const double kMaxVolumeLevel = 255.0; | |
| 15 | 16 |
| 16 // Supported hardware sample rates for input and output sides. | 17 // Supported hardware sample rates for input and output sides. |
| 17 #if defined(OS_WIN) || defined(OS_MACOSX) | 18 #if defined(OS_WIN) || defined(OS_MACOSX) |
| 18 // media::GetAudioInput[Output]HardwareSampleRate() asks the audio layer | 19 // media::GetAudioInput[Output]HardwareSampleRate() asks the audio layer |
| 19 // for its current sample rate (set by the user) on Windows and Mac OS X. | 20 // for its current sample rate (set by the user) on Windows and Mac OS X. |
| 20 // The listed rates below adds restrictions and WebRtcAudioDeviceImpl::Init() | 21 // The listed rates below adds restrictions and WebRtcAudioDeviceImpl::Init() |
| 21 // will fail if the user selects any rate outside these ranges. | 22 // will fail if the user selects any rate outside these ranges. |
| 22 static int kValidInputRates[] = {96000, 48000, 44100, 32000, 16000}; | 23 static int kValidInputRates[] = {96000, 48000, 44100, 32000, 16000}; |
| 23 static int kValidOutputRates[] = {96000, 48000, 44100}; | 24 static int kValidOutputRates[] = {96000, 48000, 44100}; |
| 24 #elif defined(OS_LINUX) || defined(OS_OPENBSD) | 25 #elif defined(OS_LINUX) || defined(OS_OPENBSD) |
| 25 // media::GetAudioInput[Output]HardwareSampleRate() is hardcoded to return | 26 // media::GetAudioInput[Output]HardwareSampleRate() is hardcoded to return |
| 26 // 48000 in both directions on Linux. | 27 // 48000 in both directions on Linux. |
| 27 static int kValidInputRates[] = {48000}; | 28 static int kValidInputRates[] = {48000}; |
| 28 static int kValidOutputRates[] = {48000}; | 29 static int kValidOutputRates[] = {48000}; |
| 29 #endif | 30 #endif |
| 30 | 31 |
| 31 WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl() | 32 WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl() |
| 32 : ref_count_(0), | 33 : ref_count_(0), |
| 33 render_loop_(base::MessageLoopProxy::current()), | 34 render_loop_(base::MessageLoopProxy::current()), |
| 34 audio_transport_callback_(NULL), | 35 audio_transport_callback_(NULL), |
| 35 input_delay_ms_(0), | 36 input_delay_ms_(0), |
| 36 output_delay_ms_(0), | 37 output_delay_ms_(0), |
| 37 last_error_(AudioDeviceModule::kAdmErrNone), | 38 last_error_(AudioDeviceModule::kAdmErrNone), |
| 38 last_process_time_(base::TimeTicks::Now()), | 39 last_process_time_(base::TimeTicks::Now()), |
| 39 session_id_(0), | 40 session_id_(0), |
| 40 bytes_per_sample_(0), | 41 bytes_per_sample_(0), |
| 41 initialized_(false), | 42 initialized_(false), |
| 42 playing_(false), | 43 playing_(false), |
| 43 recording_(false) { | 44 recording_(false), |
| 45 agc_is_enabled_(false) { | |
| 44 DVLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()"; | 46 DVLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()"; |
| 45 DCHECK(RenderThreadImpl::current()) << | 47 DCHECK(RenderThreadImpl::current()) << |
| 46 "WebRtcAudioDeviceImpl must be constructed on the render thread"; | 48 "WebRtcAudioDeviceImpl must be constructed on the render thread"; |
| 47 } | 49 } |
| 48 | 50 |
| 49 WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() { | 51 WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() { |
| 50 DVLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()"; | 52 DVLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()"; |
| 51 if (playing_) | 53 if (playing_) |
| 52 StopPlayout(); | 54 StopPlayout(); |
| 53 if (recording_) | 55 if (recording_) |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 } | 127 } |
| 126 return number_of_frames; | 128 return number_of_frames; |
| 127 } | 129 } |
| 128 | 130 |
| 129 void WebRtcAudioDeviceImpl::OnRenderError() { | 131 void WebRtcAudioDeviceImpl::OnRenderError() { |
| 130 DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); | 132 DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); |
| 131 // TODO(henrika): Implement error handling. | 133 // TODO(henrika): Implement error handling. |
| 132 LOG(ERROR) << "OnRenderError()"; | 134 LOG(ERROR) << "OnRenderError()"; |
| 133 } | 135 } |
| 134 | 136 |
| 135 void WebRtcAudioDeviceImpl::Capture( | 137 void WebRtcAudioDeviceImpl::Capture(const std::vector<float*>& audio_data, |
| 136 const std::vector<float*>& audio_data, | 138 size_t number_of_frames, |
| 137 size_t number_of_frames, | 139 size_t audio_delay_milliseconds, |
| 138 size_t audio_delay_milliseconds) { | 140 double volume) { |
| 139 DCHECK_LE(number_of_frames, input_buffer_size()); | 141 DCHECK_LE(number_of_frames, input_buffer_size()); |
| 142 #if defined(OS_WIN) || defined(OS_MACOSX) | |
| 143 DCHECK_LE(volume, 1.0); | |
| 144 #elif defined(OS_LINUX) || defined(OS_OPENBSD) | |
| 145 // We have a special situation on Linux where the microphone volume can be | |
| 146 // "higher than maximum". The input volume slider in the sound preference | |
| 147 // allows the user to set a scaling that is higher than 100%. It means that | |
| 148 // even if the reported maximum levels is N, the actual microphone level can | |
| 149 // go up to 1.5*N and that corresponds to a normalized |volume| of 1.5. | |
| 150 DCHECK_LE(volume, 1.5); | |
| 151 #endif | |
| 140 | 152 |
| 141 int output_delay_ms = 0; | 153 int output_delay_ms = 0; |
| 142 { | 154 { |
| 143 base::AutoLock auto_lock(lock_); | 155 base::AutoLock auto_lock(lock_); |
| 144 // Store the reported audio delay locally. | 156 // Store the reported audio delay locally. |
| 145 input_delay_ms_ = audio_delay_milliseconds; | 157 input_delay_ms_ = audio_delay_milliseconds; |
| 146 output_delay_ms = output_delay_ms_; | 158 output_delay_ms = output_delay_ms_; |
| 147 } | 159 } |
| 148 | 160 |
| 149 const int channels = audio_data.size(); | 161 const int channels = audio_data.size(); |
| 150 DCHECK_LE(channels, input_channels()); | 162 DCHECK_LE(channels, input_channels()); |
| 151 uint32_t new_mic_level = 0; | 163 uint32_t new_mic_level = 0; |
| 152 | 164 |
| 153 // Interleave, scale, and clip input to int16 and store result in | 165 // Interleave, scale, and clip input to int16 and store result in |
| 154 // a local byte buffer. | 166 // a local byte buffer. |
| 155 media::InterleaveFloatToInt16(audio_data, | 167 media::InterleaveFloatToInt16(audio_data, |
| 156 input_buffer_.get(), | 168 input_buffer_.get(), |
| 157 number_of_frames); | 169 number_of_frames); |
| 158 | 170 |
| 159 int samples_per_sec = input_sample_rate(); | 171 int samples_per_sec = input_sample_rate(); |
| 160 if (samples_per_sec == 44100) { | 172 if (samples_per_sec == 44100) { |
| 161 // Even if the hardware runs at 44.1kHz, we use 44.0 internally. | 173 // Even if the hardware runs at 44.1kHz, we use 44.0 internally. |
| 162 samples_per_sec = 44000; | 174 samples_per_sec = 44000; |
| 163 } | 175 } |
| 164 const int samples_per_10_msec = (samples_per_sec / 100); | 176 const int samples_per_10_msec = (samples_per_sec / 100); |
| 165 const int bytes_per_10_msec = | 177 const int bytes_per_10_msec = |
| 166 channels * samples_per_10_msec * bytes_per_sample_; | 178 channels * samples_per_10_msec * bytes_per_sample_; |
| 167 size_t accumulated_audio_samples = 0; | 179 size_t accumulated_audio_samples = 0; |
| 180 char* audio_byte_buffer = reinterpret_cast<char*>(input_buffer_.get()); | |
| 168 | 181 |
| 169 char* audio_byte_buffer = reinterpret_cast<char*>(input_buffer_.get()); | 182 // Map internal volume range of [0.0, 1.0] into [0, 255] used by the |
| 183 // webrtc::VoiceEngine. | |
| 184 uint32_t current_mic_level = static_cast<uint32_t>(volume * kMaxVolumeLevel); | |
| 170 | 185 |
| 171 // Write audio samples in blocks of 10 milliseconds to the registered | 186 // Write audio samples in blocks of 10 milliseconds to the registered |
| 172 // webrtc::AudioTransport sink. Keep writing until our internal byte | 187 // webrtc::AudioTransport sink. Keep writing until our internal byte |
| 173 // buffer is empty. | 188 // buffer is empty. |
| 174 while (accumulated_audio_samples < number_of_frames) { | 189 while (accumulated_audio_samples < number_of_frames) { |
| 175 // Deliver 10ms of recorded PCM audio. | 190 // Deliver 10ms of recorded 16-bit linear PCM audio. |
| 176 // TODO(henrika): add support for analog AGC? | |
| 177 audio_transport_callback_->RecordedDataIsAvailable( | 191 audio_transport_callback_->RecordedDataIsAvailable( |
| 178 audio_byte_buffer, | 192 audio_byte_buffer, |
| 179 samples_per_10_msec, | 193 samples_per_10_msec, |
| 180 bytes_per_sample_, | 194 bytes_per_sample_, |
| 181 channels, | 195 channels, |
| 182 samples_per_sec, | 196 samples_per_sec, |
| 183 input_delay_ms_ + output_delay_ms, | 197 input_delay_ms_ + output_delay_ms, |
| 184 0, // clock_drift | 198 0, // TODO(henrika): |clock_drift| parameter is not utilized today. |
| 185 0, // current_mic_level | 199 current_mic_level, |
| 186 new_mic_level); // not used | 200 new_mic_level); |
| 201 | |
| 187 accumulated_audio_samples += samples_per_10_msec; | 202 accumulated_audio_samples += samples_per_10_msec; |
| 188 audio_byte_buffer += bytes_per_10_msec; | 203 audio_byte_buffer += bytes_per_10_msec; |
| 189 } | 204 } |
| 205 | |
| 206 // The AGC returns a non-zero microphone level if it has been decided | |
| 207 // that a new level should be set. | |
| 208 if (new_mic_level != 0) { | |
| 209 // Use IPC and set the new level. Note that, it will take some time | |
| 210 // before the new level is effective due to the IPC scheme. | |
| 211 // During this time, |current_mic_level| will contain "non-valid" values | |
| 212 // and it might reduce the AGC performance. Measurements on Windows 7 have | |
| 213 // shown that we might receive old volume levels for one or two callbacks. | |
| 214 SetMicrophoneVolume(new_mic_level); | |
| 215 } | |
| 190 } | 216 } |
| 191 | 217 |
| 192 void WebRtcAudioDeviceImpl::OnCaptureError() { | 218 void WebRtcAudioDeviceImpl::OnCaptureError() { |
| 193 DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); | 219 DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); |
| 194 // TODO(henrika): Implement error handling. | 220 // TODO(henrika): Implement error handling. |
| 195 LOG(ERROR) << "OnCaptureError()"; | 221 LOG(ERROR) << "OnCaptureError()"; |
| 196 } | 222 } |
| 197 | 223 |
| 198 void WebRtcAudioDeviceImpl::OnDeviceStarted(const std::string& device_id) { | 224 void WebRtcAudioDeviceImpl::OnDeviceStarted(const std::string& device_id) { |
| 199 DVLOG(1) << "OnDeviceStarted (device_id=" << device_id << ")"; | 225 DVLOG(1) << "OnDeviceStarted (device_id=" << device_id << ")"; |
| (...skipping 468 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 668 base::AutoLock auto_lock(lock_); | 694 base::AutoLock auto_lock(lock_); |
| 669 recording_ = false; | 695 recording_ = false; |
| 670 return 0; | 696 return 0; |
| 671 } | 697 } |
| 672 | 698 |
| 673 bool WebRtcAudioDeviceImpl::Recording() const { | 699 bool WebRtcAudioDeviceImpl::Recording() const { |
| 674 return recording_; | 700 return recording_; |
| 675 } | 701 } |
| 676 | 702 |
| 677 int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable) { | 703 int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable) { |
| 678 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetAGC() " << "NOT IMPLEMENTED"; | 704 // The current implementation does not support changing the AGC state while |
| 679 return -1; | 705 // recording. Using this approach simplifies the design and it is also |
| 706 // inline with the latest WebRTC standard. | |
| 707 DCHECK(!recording_); | |
| 708 if (recording_) { | |
| 709 DLOG(ERROR) << "Failed to modify the AGC state since recording is active."; | |
| 710 return -1; | |
| 711 } | |
| 712 | |
| 713 audio_input_device_->SetAutomaticGainControl(enable); | |
|
scherkus (not reviewing)
2012/03/26 22:41:04
nit: instead of a setter you may consider passing
henrika (OOO until Aug 14)
2012/03/27 09:20:38
Thanks for the proposal. I'd like to keep the curr
| |
| 714 agc_is_enabled_ = enable; | |
| 715 return 0; | |
| 680 } | 716 } |
| 681 | 717 |
| 682 bool WebRtcAudioDeviceImpl::AGC() const { | 718 bool WebRtcAudioDeviceImpl::AGC() const { |
| 683 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::AGC() " << "NOT IMPLEMENTED"; | 719 // To reduce the usage of IPC messages, an internal AGC state is used. |
| 684 return false; | 720 // TODO(henrika): investigate if there is a need for a "deeper" getter. |
| 721 return agc_is_enabled_; | |
| 685 } | 722 } |
| 686 | 723 |
| 687 int32_t WebRtcAudioDeviceImpl::SetWaveOutVolume(uint16_t volume_left, | 724 int32_t WebRtcAudioDeviceImpl::SetWaveOutVolume(uint16_t volume_left, |
| 688 uint16_t volume_right) { | 725 uint16_t volume_right) { |
| 689 NOTIMPLEMENTED(); | 726 NOTIMPLEMENTED(); |
| 690 return -1; | 727 return -1; |
| 691 } | 728 } |
| 692 int32_t WebRtcAudioDeviceImpl::WaveOutVolume( | 729 int32_t WebRtcAudioDeviceImpl::WaveOutVolume( |
| 693 uint16_t* volume_left, | 730 uint16_t* volume_left, |
| 694 uint16_t* volume_right) const { | 731 uint16_t* volume_right) const { |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 747 int32_t WebRtcAudioDeviceImpl::SpeakerVolume(uint32_t* volume) const { | 784 int32_t WebRtcAudioDeviceImpl::SpeakerVolume(uint32_t* volume) const { |
| 748 NOTIMPLEMENTED(); | 785 NOTIMPLEMENTED(); |
| 749 return -1; | 786 return -1; |
| 750 } | 787 } |
| 751 | 788 |
| 752 int32_t WebRtcAudioDeviceImpl::MaxSpeakerVolume(uint32_t* max_volume) const { | 789 int32_t WebRtcAudioDeviceImpl::MaxSpeakerVolume(uint32_t* max_volume) const { |
| 753 NOTIMPLEMENTED(); | 790 NOTIMPLEMENTED(); |
| 754 return -1; | 791 return -1; |
| 755 } | 792 } |
| 756 | 793 |
| 757 int32_t WebRtcAudioDeviceImpl::MinSpeakerVolume( | 794 int32_t WebRtcAudioDeviceImpl::MinSpeakerVolume(uint32_t* min_volume) const { |
| 758 uint32_t* min_volume) const { | |
| 759 NOTIMPLEMENTED(); | 795 NOTIMPLEMENTED(); |
| 760 return -1; | 796 return -1; |
| 761 } | 797 } |
| 762 | 798 |
| 763 int32_t WebRtcAudioDeviceImpl::SpeakerVolumeStepSize( | 799 int32_t WebRtcAudioDeviceImpl::SpeakerVolumeStepSize( |
| 764 uint16_t* step_size) const { | 800 uint16_t* step_size) const { |
| 765 NOTIMPLEMENTED(); | 801 NOTIMPLEMENTED(); |
| 766 return -1; | 802 return -1; |
| 767 } | 803 } |
| 768 | 804 |
| 769 int32_t WebRtcAudioDeviceImpl::MicrophoneVolumeIsAvailable(bool* available) { | 805 int32_t WebRtcAudioDeviceImpl::MicrophoneVolumeIsAvailable(bool* available) { |
| 770 NOTIMPLEMENTED(); | 806 NOTIMPLEMENTED(); |
| 771 return -1; | 807 return -1; |
| 772 } | 808 } |
| 773 | 809 |
| 774 int32_t WebRtcAudioDeviceImpl::SetMicrophoneVolume(uint32_t volume) { | 810 int32_t WebRtcAudioDeviceImpl::SetMicrophoneVolume(uint32_t volume) { |
| 775 NOTIMPLEMENTED(); | 811 DVLOG(1) << "SetMicrophoneVolume(" << volume << ")"; |
| 812 if (volume > kMaxVolumeLevel) | |
| 813 return -1; | |
| 814 | |
| 815 // WebRTC uses a range of [0, 255] to represent the level of the microphone | |
| 816 // volume. The IPC channel between the renderer and browser process works | |
| 817 // with doubles in the [0.0, 1.0] range and we have to compensate for that. | |
| 818 double normalized_volume = static_cast<double>(volume / kMaxVolumeLevel); | |
| 819 audio_input_device_->SetVolume(normalized_volume); | |
| 820 return 0; | |
| 821 } | |
| 822 | |
| 823 int32_t WebRtcAudioDeviceImpl::MicrophoneVolume(uint32_t* volume) const { | |
| 824 // The microphone level is fed to this class using the Capture() callback | |
| 825 // and this external API should not be used. Additional IPC messages are | |
| 826 // required if support for this API is ever needed. | |
| 827 NOTREACHED(); | |
| 776 return -1; | 828 return -1; |
| 777 } | 829 } |
| 778 | 830 |
| 779 int32_t WebRtcAudioDeviceImpl::MicrophoneVolume(uint32_t* volume) const { | 831 int32_t WebRtcAudioDeviceImpl::MaxMicrophoneVolume(uint32_t* max_volume) const { |
| 780 NOTIMPLEMENTED(); | 832 *max_volume = kMaxVolumeLevel; |
| 781 return -1; | 833 return 0; |
| 782 } | 834 } |
| 783 | 835 |
| 784 int32_t WebRtcAudioDeviceImpl::MaxMicrophoneVolume( | 836 int32_t WebRtcAudioDeviceImpl::MinMicrophoneVolume(uint32_t* min_volume) const { |
| 785 uint32_t* max_volume) const { | 837 *min_volume = 0; |
| 786 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::MaxMicrophoneVolume() " | 838 return 0; |
| 787 << "NOT IMPLEMENTED"; | |
| 788 return -1; | |
| 789 } | |
| 790 | |
| 791 int32_t WebRtcAudioDeviceImpl::MinMicrophoneVolume( | |
| 792 uint32_t* min_volume) const { | |
| 793 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::MinMicrophoneVolume() " | |
| 794 << "NOT IMPLEMENTED"; | |
| 795 return -1; | |
| 796 } | 839 } |
| 797 | 840 |
| 798 int32_t WebRtcAudioDeviceImpl::MicrophoneVolumeStepSize( | 841 int32_t WebRtcAudioDeviceImpl::MicrophoneVolumeStepSize( |
| 799 uint16_t* step_size) const { | 842 uint16_t* step_size) const { |
| 800 NOTIMPLEMENTED(); | 843 NOTREACHED(); |
| 801 return -1; | 844 return -1; |
| 802 } | 845 } |
| 803 | 846 |
| 804 int32_t WebRtcAudioDeviceImpl::SpeakerMuteIsAvailable(bool* available) { | 847 int32_t WebRtcAudioDeviceImpl::SpeakerMuteIsAvailable(bool* available) { |
| 805 NOTIMPLEMENTED(); | 848 NOTIMPLEMENTED(); |
| 806 return -1; | 849 return -1; |
| 807 } | 850 } |
| 808 | 851 |
| 809 int32_t WebRtcAudioDeviceImpl::SetSpeakerMute(bool enable) { | 852 int32_t WebRtcAudioDeviceImpl::SetSpeakerMute(bool enable) { |
| 810 NOTIMPLEMENTED(); | 853 NOTIMPLEMENTED(); |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 988 } | 1031 } |
| 989 | 1032 |
| 990 int32_t WebRtcAudioDeviceImpl::GetLoudspeakerStatus(bool* enabled) const { | 1033 int32_t WebRtcAudioDeviceImpl::GetLoudspeakerStatus(bool* enabled) const { |
| 991 NOTIMPLEMENTED(); | 1034 NOTIMPLEMENTED(); |
| 992 return -1; | 1035 return -1; |
| 993 } | 1036 } |
| 994 | 1037 |
| 995 void WebRtcAudioDeviceImpl::SetSessionId(int session_id) { | 1038 void WebRtcAudioDeviceImpl::SetSessionId(int session_id) { |
| 996 session_id_ = session_id; | 1039 session_id_ = session_id; |
| 997 } | 1040 } |
| OLD | NEW |