Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 5 | #include <vector> |
| 6 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 7 | #include "base/environment.h" |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 8 | #include "base/file_util.h" |
| 9 | #include "base/files/file_path.h" |
| 10 | #include "base/path_service.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 11 | #include "base/strings/stringprintf.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 12 | #include "base/test/test_timeouts.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 13 | #include "content/renderer/media/webrtc_audio_capturer.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 14 | #include "content/renderer/media/webrtc_audio_device_impl.h" |
| 15 | #include "content/renderer/media/webrtc_audio_renderer.h" |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 16 | #include "content/renderer/media/webrtc_local_audio_track.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 17 | #include "content/renderer/render_thread_impl.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 18 | #include "content/test/webrtc_audio_device_test.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 19 | #include "media/audio/audio_manager_base.h" |
| 20 | #include "media/base/audio_hardware_config.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 21 | #include "testing/gmock/include/gmock/gmock.h" |
| 22 | #include "third_party/webrtc/voice_engine/include/voe_audio_processing.h" |
| 23 | #include "third_party/webrtc/voice_engine/include/voe_base.h" |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 24 | #include "third_party/webrtc/voice_engine/include/voe_codec.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 25 | #include "third_party/webrtc/voice_engine/include/voe_external_media.h" |
| 26 | #include "third_party/webrtc/voice_engine/include/voe_file.h" |
| 27 | #include "third_party/webrtc/voice_engine/include/voe_network.h" |
| 28 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 29 | #if defined(OS_WIN) |
| 30 | #include "base/win/windows_version.h" |
| 31 | #endif |
| 32 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 33 | using media::AudioParameters; |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 34 | using media::CHANNEL_LAYOUT_STEREO; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 35 | using testing::_; |
| 36 | using testing::AnyNumber; |
| 37 | using testing::InvokeWithoutArgs; |
| 38 | using testing::Return; |
| 39 | using testing::StrEq; |
| 40 | |
| 41 | namespace content { |
| 42 | |
| 43 | namespace { |
| 44 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 45 | const int kRenderViewId = 1; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 46 | |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 47 | // The number of packers that RunWebRtcLoopbackTimeTest() uses for measurement. |
| 48 | const int kNumberOfPacketsForLoopbackTest = 100; |
| 49 | |
| 50 | // The hardware latency we feed to WebRtc. |
| 51 | const int kHardwareLatencyInMs = 50; |
| 52 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 53 | scoped_ptr<media::AudioHardwareConfig> CreateRealHardwareConfig( |
| 54 | media::AudioManager* manager) { |
| 55 | const AudioParameters output_parameters = |
| 56 | manager->GetDefaultOutputStreamParameters(); |
| 57 | const AudioParameters input_parameters = |
| 58 | manager->GetInputStreamParameters( |
| 59 | media::AudioManagerBase::kDefaultDeviceId); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 60 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 61 | return make_scoped_ptr(new media::AudioHardwareConfig( |
| 62 | input_parameters, output_parameters)); |
| 63 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 64 | |
| 65 | // Return true if at least one element in the array matches |value|. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 66 | bool FindElementInArray(const int* array, int size, int value) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 67 | return (std::find(&array[0], &array[0] + size, value) != &array[size]); |
| 68 | } |
| 69 | |
| 70 | // This method returns false if a non-supported rate is detected on the |
| 71 | // input or output side. |
| 72 | // TODO(henrika): add support for automatic fallback to Windows Wave audio |
| 73 | // if a non-supported rate is detected. It is probably better to detect |
| 74 | // invalid audio settings by actually trying to open the audio streams instead |
| 75 | // of relying on hard coded conditions. |
| 76 | bool HardwareSampleRatesAreValid() { |
| 77 | // These are the currently supported hardware sample rates in both directions. |
| 78 | // The actual WebRTC client can limit these ranges further depending on |
| 79 | // platform but this is the maximum range we support today. |
| 80 | int valid_input_rates[] = {16000, 32000, 44100, 48000, 96000}; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 81 | int valid_output_rates[] = {16000, 32000, 44100, 48000, 96000}; |
| 82 | |
| 83 | media::AudioHardwareConfig* hardware_config = |
| 84 | RenderThreadImpl::current()->GetAudioHardwareConfig(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 85 | |
| 86 | // Verify the input sample rate. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 87 | int input_sample_rate = hardware_config->GetInputSampleRate(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 88 | |
| 89 | if (!FindElementInArray(valid_input_rates, arraysize(valid_input_rates), |
| 90 | input_sample_rate)) { |
| 91 | LOG(WARNING) << "Non-supported input sample rate detected."; |
| 92 | return false; |
| 93 | } |
| 94 | |
| 95 | // Given that the input rate was OK, verify the output rate as well. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 96 | int output_sample_rate = hardware_config->GetOutputSampleRate(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 97 | if (!FindElementInArray(valid_output_rates, arraysize(valid_output_rates), |
| 98 | output_sample_rate)) { |
| 99 | LOG(WARNING) << "Non-supported output sample rate detected."; |
| 100 | return false; |
| 101 | } |
| 102 | |
| 103 | return true; |
| 104 | } |
| 105 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 106 | // Utility method which creates and initializes the audio capturer and adds it |
| 107 | // to WebRTC audio device. This method should be used in tests where |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 108 | // HardwareSampleRatesAreValid() has been called and returned true. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 109 | bool CreateAndInitializeCapturer(WebRtcAudioDeviceImpl* webrtc_audio_device) { |
| 110 | DCHECK(webrtc_audio_device); |
| 111 | scoped_refptr<WebRtcAudioCapturer> capturer( |
| 112 | WebRtcAudioCapturer::CreateCapturer()); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 113 | |
| 114 | media::AudioHardwareConfig* hardware_config = |
| 115 | RenderThreadImpl::current()->GetAudioHardwareConfig(); |
| 116 | |
| 117 | // Use native capture sample rate and channel configuration to get some |
| 118 | // action in this test. |
| 119 | int sample_rate = hardware_config->GetInputSampleRate(); |
| 120 | media::ChannelLayout channel_layout = |
| 121 | hardware_config->GetInputChannelLayout(); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 122 | if (!capturer->Initialize(kRenderViewId, channel_layout, sample_rate, 1, |
| 123 | media::AudioManagerBase::kDefaultDeviceId)) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 124 | return false; |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 125 | } |
| 126 | |
| 127 | // Add the capturer to the WebRtcAudioDeviceImpl. |
| 128 | webrtc_audio_device->AddAudioCapturer(capturer); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 129 | |
| 130 | return true; |
| 131 | } |
| 132 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 133 | // Create and start a local audio track. Starting the audio track will connect |
| 134 | // the audio track to the capturer and also start the source of the capturer. |
| 135 | // Also, connect the sink to the audio track. |
| 136 | scoped_refptr<WebRtcLocalAudioTrack> |
| 137 | CreateAndStartLocalAudioTrack(WebRtcAudioCapturer* capturer, |
| 138 | WebRtcAudioCapturerSink* sink) { |
| 139 | scoped_refptr<WebRtcLocalAudioTrack> local_audio_track( |
| 140 | WebRtcLocalAudioTrack::Create(std::string(), capturer, NULL)); |
| 141 | local_audio_track->AddSink(sink); |
| 142 | local_audio_track->Start(); |
| 143 | return local_audio_track; |
| 144 | } |
| 145 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 146 | class WebRTCMediaProcessImpl : public webrtc::VoEMediaProcess { |
| 147 | public: |
| 148 | explicit WebRTCMediaProcessImpl(base::WaitableEvent* event) |
| 149 | : event_(event), |
| 150 | channel_id_(-1), |
| 151 | type_(webrtc::kPlaybackPerChannel), |
| 152 | packet_size_(0), |
| 153 | sample_rate_(0), |
| 154 | channels_(0) { |
| 155 | } |
| 156 | virtual ~WebRTCMediaProcessImpl() {} |
| 157 | |
| 158 | // TODO(henrika): Refactor in WebRTC and convert to Chrome coding style. |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 159 | virtual void Process(int channel, |
| 160 | webrtc::ProcessingTypes type, |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 161 | int16_t audio_10ms[], |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 162 | int length, |
| 163 | int sampling_freq, |
| 164 | bool is_stereo) OVERRIDE { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 165 | base::AutoLock auto_lock(lock_); |
| 166 | channel_id_ = channel; |
| 167 | type_ = type; |
| 168 | packet_size_ = length; |
| 169 | sample_rate_ = sampling_freq; |
| 170 | channels_ = (is_stereo ? 2 : 1); |
| 171 | if (event_) { |
| 172 | // Signal that a new callback has been received. |
| 173 | event_->Signal(); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | int channel_id() const { |
| 178 | base::AutoLock auto_lock(lock_); |
| 179 | return channel_id_; |
| 180 | } |
| 181 | |
| 182 | int type() const { |
| 183 | base::AutoLock auto_lock(lock_); |
| 184 | return type_; |
| 185 | } |
| 186 | |
| 187 | int packet_size() const { |
| 188 | base::AutoLock auto_lock(lock_); |
| 189 | return packet_size_; |
| 190 | } |
| 191 | |
| 192 | int sample_rate() const { |
| 193 | base::AutoLock auto_lock(lock_); |
| 194 | return sample_rate_; |
| 195 | } |
| 196 | |
| 197 | private: |
| 198 | base::WaitableEvent* event_; |
| 199 | int channel_id_; |
| 200 | webrtc::ProcessingTypes type_; |
| 201 | int packet_size_; |
| 202 | int sample_rate_; |
| 203 | int channels_; |
| 204 | mutable base::Lock lock_; |
| 205 | DISALLOW_COPY_AND_ASSIGN(WebRTCMediaProcessImpl); |
| 206 | }; |
| 207 | |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 208 | class MockWebRtcAudioCapturerSink : public WebRtcAudioCapturerSink { |
| 209 | public: |
| 210 | explicit MockWebRtcAudioCapturerSink(base::WaitableEvent* event) |
| 211 | : event_(event) { |
| 212 | DCHECK(event_); |
| 213 | } |
| 214 | virtual ~MockWebRtcAudioCapturerSink() {} |
| 215 | |
| 216 | // WebRtcAudioCapturerSink implementation. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 217 | virtual int CaptureData(const std::vector<int>& channels, |
| 218 | const int16* audio_data, |
| 219 | int sample_rate, |
| 220 | int number_of_channels, |
| 221 | int number_of_frames, |
| 222 | int audio_delay_milliseconds, |
| 223 | int current_volume, |
| 224 | bool need_audio_processing) OVERRIDE { |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 225 | // Signal that a callback has been received. |
| 226 | event_->Signal(); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 227 | return 0; |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 228 | } |
| 229 | |
| 230 | // Set the format for the capture audio parameters. |
| 231 | virtual void SetCaptureFormat( |
| 232 | const media::AudioParameters& params) OVERRIDE {} |
| 233 | |
| 234 | private: |
| 235 | base::WaitableEvent* event_; |
| 236 | |
| 237 | DISALLOW_COPY_AND_ASSIGN(MockWebRtcAudioCapturerSink); |
| 238 | }; |
| 239 | |
| 240 | class MockWebRtcAudioRendererSource : public WebRtcAudioRendererSource { |
| 241 | public: |
| 242 | explicit MockWebRtcAudioRendererSource(base::WaitableEvent* event) |
| 243 | : event_(event) { |
| 244 | DCHECK(event_); |
| 245 | } |
| 246 | virtual ~MockWebRtcAudioRendererSource() {} |
| 247 | |
| 248 | // WebRtcAudioRendererSource implementation. |
| 249 | virtual void RenderData(uint8* audio_data, |
| 250 | int number_of_channels, |
| 251 | int number_of_frames, |
| 252 | int audio_delay_milliseconds) OVERRIDE { |
| 253 | // Signal that a callback has been received. |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 254 | // Initialize the memory to zero to avoid uninitialized warning from |
| 255 | // Valgrind. |
| 256 | memset(audio_data, 0, |
| 257 | sizeof(int16) * number_of_channels * number_of_frames); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 258 | event_->Signal(); |
| 259 | } |
| 260 | |
| 261 | virtual void SetRenderFormat(const media::AudioParameters& params) OVERRIDE { |
| 262 | } |
| 263 | |
| 264 | virtual void RemoveAudioRenderer(WebRtcAudioRenderer* renderer) OVERRIDE {}; |
| 265 | |
| 266 | private: |
| 267 | base::WaitableEvent* event_; |
| 268 | |
| 269 | DISALLOW_COPY_AND_ASSIGN(MockWebRtcAudioRendererSource); |
| 270 | }; |
| 271 | |
| 272 | // Prints numerical information to stdout in a controlled format so we can plot |
| 273 | // the result. |
| 274 | void PrintPerfResultMs(const char* graph, const char* trace, float time_ms) { |
| 275 | std::string times; |
| 276 | base::StringAppendF(×, "%.2f,", time_ms); |
| 277 | std::string result = base::StringPrintf( |
| 278 | "%sRESULT %s%s: %s= %s%s%s %s\n", "*", graph, "", |
| 279 | trace, "[", times.c_str(), "]", "ms"); |
| 280 | |
| 281 | fflush(stdout); |
| 282 | printf("%s", result.c_str()); |
| 283 | fflush(stdout); |
| 284 | } |
| 285 | |
| 286 | void ReadDataFromSpeechFile(char* data, int length) { |
| 287 | base::FilePath data_file; |
| 288 | CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_file)); |
| 289 | data_file = |
| 290 | data_file.Append(FILE_PATH_LITERAL("media")) |
| 291 | .Append(FILE_PATH_LITERAL("test")) |
| 292 | .Append(FILE_PATH_LITERAL("data")) |
| 293 | .Append(FILE_PATH_LITERAL("speech_16b_stereo_48kHz.raw")); |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 294 | DCHECK(base::PathExists(data_file)); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 295 | int64 data_file_size64 = 0; |
| 296 | DCHECK(file_util::GetFileSize(data_file, &data_file_size64)); |
| 297 | EXPECT_EQ(length, file_util::ReadFile(data_file, data, length)); |
| 298 | DCHECK(data_file_size64 > length); |
| 299 | } |
| 300 | |
| 301 | void SetChannelCodec(webrtc::VoiceEngine* engine, int channel) { |
| 302 | // TODO(xians): move the codec as an input param to this function, and add |
| 303 | // tests for different codecs, also add support to Android and IOS. |
| 304 | #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| 305 | webrtc::CodecInst isac; |
| 306 | strcpy(isac.plname, "ISAC"); |
| 307 | isac.pltype = 104; |
| 308 | isac.pacsize = 960; |
| 309 | isac.plfreq = 32000; |
| 310 | isac.channels = 1; |
| 311 | isac.rate = -1; |
| 312 | ScopedWebRTCPtr<webrtc::VoECodec> codec(engine); |
| 313 | EXPECT_EQ(0, codec->SetRecPayloadType(channel, isac)); |
| 314 | EXPECT_EQ(0, codec->SetSendCodec(channel, isac)); |
| 315 | #endif |
| 316 | } |
| 317 | |
| 318 | // Returns the time in millisecond for sending packets to WebRtc for encoding, |
| 319 | // signal processing, decoding and receiving them back. |
| 320 | int RunWebRtcLoopbackTimeTest(media::AudioManager* manager, |
| 321 | bool enable_apm) { |
| 322 | scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device( |
| 323 | new WebRtcAudioDeviceImpl()); |
| 324 | WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create()); |
| 325 | EXPECT_TRUE(engine.valid()); |
| 326 | ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get()); |
| 327 | EXPECT_TRUE(base.valid()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 328 | int err = base->Init(webrtc_audio_device.get()); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 329 | EXPECT_EQ(0, err); |
| 330 | |
| 331 | // We use SetCaptureFormat() and SetRenderFormat() to configure the audio |
| 332 | // parameters so that this test can run on machine without hardware device. |
| 333 | const media::AudioParameters params = media::AudioParameters( |
| 334 | media::AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, |
| 335 | 48000, 2, 480); |
| 336 | WebRtcAudioCapturerSink* capturer_sink = |
| 337 | static_cast<WebRtcAudioCapturerSink*>(webrtc_audio_device.get()); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 338 | WebRtcAudioRendererSource* renderer_source = |
| 339 | static_cast<WebRtcAudioRendererSource*>(webrtc_audio_device.get()); |
| 340 | renderer_source->SetRenderFormat(params); |
| 341 | |
| 342 | // Turn on/off all the signal processing components like AGC, AEC and NS. |
| 343 | ScopedWebRTCPtr<webrtc::VoEAudioProcessing> audio_processing(engine.get()); |
| 344 | EXPECT_TRUE(audio_processing.valid()); |
| 345 | audio_processing->SetAgcStatus(enable_apm); |
| 346 | audio_processing->SetNsStatus(enable_apm); |
| 347 | audio_processing->SetEcStatus(enable_apm); |
| 348 | |
| 349 | // Create a voice channel for the WebRtc. |
| 350 | int channel = base->CreateChannel(); |
| 351 | EXPECT_NE(-1, channel); |
| 352 | SetChannelCodec(engine.get(), channel); |
| 353 | |
| 354 | // Use our fake network transmission and start playout and recording. |
| 355 | ScopedWebRTCPtr<webrtc::VoENetwork> network(engine.get()); |
| 356 | EXPECT_TRUE(network.valid()); |
| 357 | scoped_ptr<WebRTCTransportImpl> transport( |
| 358 | new WebRTCTransportImpl(network.get())); |
| 359 | EXPECT_EQ(0, network->RegisterExternalTransport(channel, *transport.get())); |
| 360 | EXPECT_EQ(0, base->StartPlayout(channel)); |
| 361 | EXPECT_EQ(0, base->StartSend(channel)); |
| 362 | |
| 363 | // Read speech data from a speech test file. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 364 | const int input_packet_size = |
| 365 | params.frames_per_buffer() * 2 * params.channels(); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 366 | const int num_output_channels = webrtc_audio_device->output_channels(); |
| 367 | const int output_packet_size = webrtc_audio_device->output_buffer_size() * 2 * |
| 368 | num_output_channels; |
| 369 | const size_t length = input_packet_size * kNumberOfPacketsForLoopbackTest; |
| 370 | scoped_ptr<char[]> capture_data(new char[length]); |
| 371 | ReadDataFromSpeechFile(capture_data.get(), length); |
| 372 | |
| 373 | // Start the timer. |
| 374 | scoped_ptr<uint8[]> buffer(new uint8[output_packet_size]); |
| 375 | base::Time start_time = base::Time::Now(); |
| 376 | int delay = 0; |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 377 | std::vector<int> voe_channels; |
| 378 | voe_channels.push_back(channel); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 379 | for (int j = 0; j < kNumberOfPacketsForLoopbackTest; ++j) { |
| 380 | // Sending fake capture data to WebRtc. |
| 381 | capturer_sink->CaptureData( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 382 | voe_channels, |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 383 | reinterpret_cast<int16*>(capture_data.get() + input_packet_size * j), |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 384 | params.sample_rate(), params.channels(), params.frames_per_buffer(), |
| 385 | kHardwareLatencyInMs, 1.0, enable_apm); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 386 | |
| 387 | // Receiving data from WebRtc. |
| 388 | renderer_source->RenderData( |
| 389 | reinterpret_cast<uint8*>(buffer.get()), |
| 390 | num_output_channels, webrtc_audio_device->output_buffer_size(), |
| 391 | kHardwareLatencyInMs + delay); |
| 392 | delay = (base::Time::Now() - start_time).InMilliseconds(); |
| 393 | } |
| 394 | |
| 395 | int latency = (base::Time::Now() - start_time).InMilliseconds(); |
| 396 | |
| 397 | EXPECT_EQ(0, base->StopSend(channel)); |
| 398 | EXPECT_EQ(0, base->StopPlayout(channel)); |
| 399 | EXPECT_EQ(0, base->DeleteChannel(channel)); |
| 400 | EXPECT_EQ(0, base->Terminate()); |
| 401 | |
| 402 | return latency; |
| 403 | } |
| 404 | |
| 405 | } // namespace |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 406 | |
| 407 | // Trivial test which verifies that one part of the test harness |
| 408 | // (HardwareSampleRatesAreValid()) works as intended for all supported |
| 409 | // hardware input sample rates. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 410 | TEST_F(MAYBE_WebRTCAudioDeviceTest, TestValidInputRates) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 411 | int valid_rates[] = {16000, 32000, 44100, 48000, 96000}; |
| 412 | |
| 413 | // Verify that we will approve all rates listed in |valid_rates|. |
| 414 | for (size_t i = 0; i < arraysize(valid_rates); ++i) { |
| 415 | EXPECT_TRUE(FindElementInArray(valid_rates, arraysize(valid_rates), |
| 416 | valid_rates[i])); |
| 417 | } |
| 418 | |
| 419 | // Verify that any value outside the valid range results in negative |
| 420 | // find results. |
| 421 | int invalid_rates[] = {-1, 0, 8000, 11025, 22050, 192000}; |
| 422 | for (size_t i = 0; i < arraysize(invalid_rates); ++i) { |
| 423 | EXPECT_FALSE(FindElementInArray(valid_rates, arraysize(valid_rates), |
| 424 | invalid_rates[i])); |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | // Trivial test which verifies that one part of the test harness |
| 429 | // (HardwareSampleRatesAreValid()) works as intended for all supported |
| 430 | // hardware output sample rates. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 431 | TEST_F(MAYBE_WebRTCAudioDeviceTest, TestValidOutputRates) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 432 | int valid_rates[] = {44100, 48000, 96000}; |
| 433 | |
| 434 | // Verify that we will approve all rates listed in |valid_rates|. |
| 435 | for (size_t i = 0; i < arraysize(valid_rates); ++i) { |
| 436 | EXPECT_TRUE(FindElementInArray(valid_rates, arraysize(valid_rates), |
| 437 | valid_rates[i])); |
| 438 | } |
| 439 | |
| 440 | // Verify that any value outside the valid range results in negative |
| 441 | // find results. |
| 442 | int invalid_rates[] = {-1, 0, 8000, 11025, 22050, 32000, 192000}; |
| 443 | for (size_t i = 0; i < arraysize(invalid_rates); ++i) { |
| 444 | EXPECT_FALSE(FindElementInArray(valid_rates, arraysize(valid_rates), |
| 445 | invalid_rates[i])); |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | // Basic test that instantiates and initializes an instance of |
| 450 | // WebRtcAudioDeviceImpl. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 451 | TEST_F(MAYBE_WebRTCAudioDeviceTest, Construct) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 452 | #if defined(OS_WIN) |
| 453 | // This test crashes on Win XP bots. |
| 454 | if (base::win::GetVersion() <= base::win::VERSION_XP) |
| 455 | return; |
| 456 | #endif |
| 457 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 458 | AudioParameters input_params( |
| 459 | AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| 460 | media::CHANNEL_LAYOUT_MONO, |
| 461 | 48000, |
| 462 | 16, |
| 463 | 480); |
| 464 | |
| 465 | AudioParameters output_params( |
| 466 | AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| 467 | media::CHANNEL_LAYOUT_STEREO, |
| 468 | 48000, |
| 469 | 16, |
| 470 | 480); |
| 471 | |
| 472 | media::AudioHardwareConfig audio_config(input_params, output_params); |
| 473 | SetAudioHardwareConfig(&audio_config); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 474 | |
| 475 | scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device( |
| 476 | new WebRtcAudioDeviceImpl()); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 477 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 478 | WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create()); |
| 479 | ASSERT_TRUE(engine.valid()); |
| 480 | |
| 481 | ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 482 | int err = base->Init(webrtc_audio_device.get()); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 483 | EXPECT_TRUE(CreateAndInitializeCapturer(webrtc_audio_device.get())); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 484 | EXPECT_EQ(0, err); |
| 485 | EXPECT_EQ(0, base->Terminate()); |
| 486 | } |
| 487 | |
| 488 | // Verify that a call to webrtc::VoEBase::StartPlayout() starts audio output |
| 489 | // with the correct set of parameters. A WebRtcAudioDeviceImpl instance will |
| 490 | // be utilized to implement the actual audio path. The test registers a |
| 491 | // webrtc::VoEExternalMedia implementation to hijack the output audio and |
| 492 | // verify that streaming starts correctly. |
| 493 | // Disabled when running headless since the bots don't have the required config. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 494 | // Flaky, http://crbug.com/167299 . |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 495 | TEST_F(MAYBE_WebRTCAudioDeviceTest, DISABLED_StartPlayout) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 496 | if (!has_output_devices_) { |
| 497 | LOG(WARNING) << "No output device detected."; |
| 498 | return; |
| 499 | } |
| 500 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 501 | scoped_ptr<media::AudioHardwareConfig> config = |
| 502 | CreateRealHardwareConfig(audio_manager_.get()); |
| 503 | SetAudioHardwareConfig(config.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 504 | |
| 505 | if (!HardwareSampleRatesAreValid()) |
| 506 | return; |
| 507 | |
| 508 | EXPECT_CALL(media_observer(), |
| 509 | OnSetAudioStreamStatus(_, 1, StrEq("created"))).Times(1); |
| 510 | EXPECT_CALL(media_observer(), |
| 511 | OnSetAudioStreamPlaying(_, 1, true)).Times(1); |
| 512 | EXPECT_CALL(media_observer(), |
| 513 | OnSetAudioStreamStatus(_, 1, StrEq("closed"))).Times(1); |
| 514 | EXPECT_CALL(media_observer(), |
| 515 | OnDeleteAudioStream(_, 1)).Times(AnyNumber()); |
| 516 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 517 | scoped_refptr<WebRtcAudioRenderer> renderer = |
| 518 | new WebRtcAudioRenderer(kRenderViewId); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 519 | scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device( |
| 520 | new WebRtcAudioDeviceImpl()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 521 | EXPECT_TRUE(webrtc_audio_device->SetAudioRenderer(renderer.get())); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 522 | |
| 523 | WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create()); |
| 524 | ASSERT_TRUE(engine.valid()); |
| 525 | |
| 526 | ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get()); |
| 527 | ASSERT_TRUE(base.valid()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 528 | int err = base->Init(webrtc_audio_device.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 529 | ASSERT_EQ(0, err); |
| 530 | |
| 531 | int ch = base->CreateChannel(); |
| 532 | EXPECT_NE(-1, ch); |
| 533 | |
| 534 | ScopedWebRTCPtr<webrtc::VoEExternalMedia> external_media(engine.get()); |
| 535 | ASSERT_TRUE(external_media.valid()); |
| 536 | |
| 537 | base::WaitableEvent event(false, false); |
| 538 | scoped_ptr<WebRTCMediaProcessImpl> media_process( |
| 539 | new WebRTCMediaProcessImpl(&event)); |
| 540 | EXPECT_EQ(0, external_media->RegisterExternalMediaProcessing( |
| 541 | ch, webrtc::kPlaybackPerChannel, *media_process.get())); |
| 542 | |
| 543 | EXPECT_EQ(0, base->StartPlayout(ch)); |
| 544 | renderer->Play(); |
| 545 | |
| 546 | EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout())); |
| 547 | WaitForIOThreadCompletion(); |
| 548 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 549 | EXPECT_TRUE(webrtc_audio_device->Playing()); |
| 550 | EXPECT_FALSE(webrtc_audio_device->Recording()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 551 | EXPECT_EQ(ch, media_process->channel_id()); |
| 552 | EXPECT_EQ(webrtc::kPlaybackPerChannel, media_process->type()); |
| 553 | EXPECT_EQ(80, media_process->packet_size()); |
| 554 | EXPECT_EQ(8000, media_process->sample_rate()); |
| 555 | |
| 556 | EXPECT_EQ(0, external_media->DeRegisterExternalMediaProcessing( |
| 557 | ch, webrtc::kPlaybackPerChannel)); |
| 558 | EXPECT_EQ(0, base->StopPlayout(ch)); |
| 559 | renderer->Stop(); |
| 560 | |
| 561 | EXPECT_EQ(0, base->DeleteChannel(ch)); |
| 562 | EXPECT_EQ(0, base->Terminate()); |
| 563 | } |
| 564 | |
| 565 | // Verify that a call to webrtc::VoEBase::StartRecording() starts audio input |
| 566 | // with the correct set of parameters. A WebRtcAudioDeviceImpl instance will |
| 567 | // be utilized to implement the actual audio path. The test registers a |
| 568 | // webrtc::VoEExternalMedia implementation to hijack the input audio and |
| 569 | // verify that streaming starts correctly. An external transport implementation |
| 570 | // is also required to ensure that "sending" can start without actually trying |
| 571 | // to send encoded packets to the network. Our main interest here is to ensure |
| 572 | // that the audio capturing starts as it should. |
| 573 | // Disabled when running headless since the bots don't have the required config. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 574 | |
| 575 | // TODO(leozwang): Because ExternalMediaProcessing is disabled in webrtc, |
| 576 | // disable this unit test on Android for now. |
| 577 | #if defined(OS_ANDROID) |
| 578 | #define MAYBE_StartRecording DISABLED_StartRecording |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 579 | #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY) |
| 580 | // This test is failing on ARM linux: http://crbug.com/238490 |
| 581 | #define MAYBE_StartRecording DISABLED_StartRecording |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 582 | #else |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 583 | // Flakily hangs on all other platforms as well: crbug.com/268376. |
| 584 | // When the flakiness has been fixed, you probably want to leave it disabled |
| 585 | // on the above platforms. |
| 586 | #define MAYBE_StartRecording DISABLED_StartRecording |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 587 | #endif |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 588 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 589 | TEST_F(MAYBE_WebRTCAudioDeviceTest, MAYBE_StartRecording) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 590 | if (!has_input_devices_ || !has_output_devices_) { |
| 591 | LOG(WARNING) << "Missing audio devices."; |
| 592 | return; |
| 593 | } |
| 594 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 595 | scoped_ptr<media::AudioHardwareConfig> config = |
| 596 | CreateRealHardwareConfig(audio_manager_.get()); |
| 597 | SetAudioHardwareConfig(config.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 598 | |
| 599 | if (!HardwareSampleRatesAreValid()) |
| 600 | return; |
| 601 | |
| 602 | // TODO(tommi): extend MediaObserver and MockMediaObserver with support |
| 603 | // for new interfaces, like OnSetAudioStreamRecording(). When done, add |
| 604 | // EXPECT_CALL() macros here. |
| 605 | scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device( |
| 606 | new WebRtcAudioDeviceImpl()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 607 | |
| 608 | WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create()); |
| 609 | ASSERT_TRUE(engine.valid()); |
| 610 | |
| 611 | ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get()); |
| 612 | ASSERT_TRUE(base.valid()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 613 | int err = base->Init(webrtc_audio_device.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 614 | ASSERT_EQ(0, err); |
| 615 | |
| 616 | int ch = base->CreateChannel(); |
| 617 | EXPECT_NE(-1, ch); |
| 618 | |
| 619 | ScopedWebRTCPtr<webrtc::VoEExternalMedia> external_media(engine.get()); |
| 620 | ASSERT_TRUE(external_media.valid()); |
| 621 | |
| 622 | base::WaitableEvent event(false, false); |
| 623 | scoped_ptr<WebRTCMediaProcessImpl> media_process( |
| 624 | new WebRTCMediaProcessImpl(&event)); |
| 625 | EXPECT_EQ(0, external_media->RegisterExternalMediaProcessing( |
| 626 | ch, webrtc::kRecordingPerChannel, *media_process.get())); |
| 627 | |
| 628 | // We must add an external transport implementation to be able to start |
| 629 | // recording without actually sending encoded packets to the network. All |
| 630 | // we want to do here is to verify that audio capturing starts as it should. |
| 631 | ScopedWebRTCPtr<webrtc::VoENetwork> network(engine.get()); |
| 632 | scoped_ptr<WebRTCTransportImpl> transport( |
| 633 | new WebRTCTransportImpl(network.get())); |
| 634 | EXPECT_EQ(0, network->RegisterExternalTransport(ch, *transport.get())); |
| 635 | EXPECT_EQ(0, base->StartSend(ch)); |
| 636 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 637 | // Create and initialize the capturer which starts the source of the data |
| 638 | // flow. |
| 639 | EXPECT_TRUE(CreateAndInitializeCapturer(webrtc_audio_device.get())); |
| 640 | |
| 641 | // Create and start a local audio track which is bridging the data flow |
| 642 | // between the capturer and WebRtcAudioDeviceImpl. |
| 643 | scoped_refptr<WebRtcLocalAudioTrack> local_audio_track( |
| 644 | CreateAndStartLocalAudioTrack(webrtc_audio_device->GetDefaultCapturer(), |
| 645 | webrtc_audio_device)); |
| 646 | // connect the VoE voice channel to the audio track |
| 647 | static_cast<webrtc::AudioTrackInterface*>(local_audio_track.get())-> |
| 648 | GetRenderer()->AddChannel(ch); |
| 649 | |
| 650 | // Verify we get the data flow. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 651 | EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout())); |
| 652 | WaitForIOThreadCompletion(); |
| 653 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 654 | EXPECT_FALSE(webrtc_audio_device->Playing()); |
| 655 | EXPECT_TRUE(webrtc_audio_device->Recording()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 656 | EXPECT_EQ(ch, media_process->channel_id()); |
| 657 | EXPECT_EQ(webrtc::kRecordingPerChannel, media_process->type()); |
| 658 | EXPECT_EQ(80, media_process->packet_size()); |
| 659 | EXPECT_EQ(8000, media_process->sample_rate()); |
| 660 | |
| 661 | EXPECT_EQ(0, external_media->DeRegisterExternalMediaProcessing( |
| 662 | ch, webrtc::kRecordingPerChannel)); |
| 663 | EXPECT_EQ(0, base->StopSend(ch)); |
| 664 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 665 | local_audio_track->Stop(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 666 | EXPECT_EQ(0, base->DeleteChannel(ch)); |
| 667 | EXPECT_EQ(0, base->Terminate()); |
| 668 | } |
| 669 | |
| 670 | // Uses WebRtcAudioDeviceImpl to play a local wave file. |
| 671 | // Disabled when running headless since the bots don't have the required config. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 672 | // Flaky, http://crbug.com/167298 . |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 673 | TEST_F(MAYBE_WebRTCAudioDeviceTest, DISABLED_PlayLocalFile) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 674 | if (!has_output_devices_) { |
| 675 | LOG(WARNING) << "No output device detected."; |
| 676 | return; |
| 677 | } |
| 678 | |
| 679 | std::string file_path( |
| 680 | GetTestDataPath(FILE_PATH_LITERAL("speechmusic_mono_16kHz.pcm"))); |
| 681 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 682 | scoped_ptr<media::AudioHardwareConfig> config = |
| 683 | CreateRealHardwareConfig(audio_manager_.get()); |
| 684 | SetAudioHardwareConfig(config.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 685 | |
| 686 | if (!HardwareSampleRatesAreValid()) |
| 687 | return; |
| 688 | |
| 689 | EXPECT_CALL(media_observer(), |
| 690 | OnSetAudioStreamStatus(_, 1, StrEq("created"))).Times(1); |
| 691 | EXPECT_CALL(media_observer(), |
| 692 | OnSetAudioStreamPlaying(_, 1, true)).Times(1); |
| 693 | EXPECT_CALL(media_observer(), |
| 694 | OnSetAudioStreamStatus(_, 1, StrEq("closed"))).Times(1); |
| 695 | EXPECT_CALL(media_observer(), |
| 696 | OnDeleteAudioStream(_, 1)).Times(AnyNumber()); |
| 697 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 698 | scoped_refptr<WebRtcAudioRenderer> renderer = |
| 699 | new WebRtcAudioRenderer(kRenderViewId); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 700 | scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device( |
| 701 | new WebRtcAudioDeviceImpl()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 702 | EXPECT_TRUE(webrtc_audio_device->SetAudioRenderer(renderer.get())); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 703 | |
| 704 | WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create()); |
| 705 | ASSERT_TRUE(engine.valid()); |
| 706 | |
| 707 | ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get()); |
| 708 | ASSERT_TRUE(base.valid()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 709 | int err = base->Init(webrtc_audio_device.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 710 | ASSERT_EQ(0, err); |
| 711 | |
| 712 | int ch = base->CreateChannel(); |
| 713 | EXPECT_NE(-1, ch); |
| 714 | EXPECT_EQ(0, base->StartPlayout(ch)); |
| 715 | renderer->Play(); |
| 716 | |
| 717 | ScopedWebRTCPtr<webrtc::VoEFile> file(engine.get()); |
| 718 | ASSERT_TRUE(file.valid()); |
| 719 | int duration = 0; |
| 720 | EXPECT_EQ(0, file->GetFileDuration(file_path.c_str(), duration, |
| 721 | webrtc::kFileFormatPcm16kHzFile)); |
| 722 | EXPECT_NE(0, duration); |
| 723 | |
| 724 | EXPECT_EQ(0, file->StartPlayingFileLocally(ch, file_path.c_str(), false, |
| 725 | webrtc::kFileFormatPcm16kHzFile)); |
| 726 | |
| 727 | // Play 2 seconds worth of audio and then quit. |
| 728 | message_loop_.PostDelayedTask(FROM_HERE, |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 729 | base::MessageLoop::QuitClosure(), |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 730 | base::TimeDelta::FromSeconds(6)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 731 | message_loop_.Run(); |
| 732 | |
| 733 | renderer->Stop(); |
| 734 | EXPECT_EQ(0, base->StopSend(ch)); |
| 735 | EXPECT_EQ(0, base->StopPlayout(ch)); |
| 736 | EXPECT_EQ(0, base->DeleteChannel(ch)); |
| 737 | EXPECT_EQ(0, base->Terminate()); |
| 738 | } |
| 739 | |
| 740 | // Uses WebRtcAudioDeviceImpl to play out recorded audio in loopback. |
| 741 | // An external transport implementation is utilized to feed back RTP packets |
| 742 | // which are recorded, encoded, packetized into RTP packets and finally |
| 743 | // "transmitted". The RTP packets are then fed back into the VoiceEngine |
| 744 | // where they are decoded and played out on the default audio output device. |
| 745 | // Disabled when running headless since the bots don't have the required config. |
| 746 | // TODO(henrika): improve quality by using a wideband codec, enabling noise- |
| 747 | // suppressions etc. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 748 | // FullDuplexAudioWithAGC is flaky on Android, disable it for now. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 749 | // Also flakily hangs on Windows: crbug.com/269348. |
| 750 | #if defined(OS_ANDROID) || defined(OS_WIN) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 751 | #define MAYBE_FullDuplexAudioWithAGC DISABLED_FullDuplexAudioWithAGC |
| 752 | #else |
| 753 | #define MAYBE_FullDuplexAudioWithAGC FullDuplexAudioWithAGC |
| 754 | #endif |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 755 | TEST_F(MAYBE_WebRTCAudioDeviceTest, MAYBE_FullDuplexAudioWithAGC) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 756 | if (!has_output_devices_ || !has_input_devices_) { |
| 757 | LOG(WARNING) << "Missing audio devices."; |
| 758 | return; |
| 759 | } |
| 760 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 761 | scoped_ptr<media::AudioHardwareConfig> config = |
| 762 | CreateRealHardwareConfig(audio_manager_.get()); |
| 763 | SetAudioHardwareConfig(config.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 764 | |
| 765 | if (!HardwareSampleRatesAreValid()) |
| 766 | return; |
| 767 | |
| 768 | EXPECT_CALL(media_observer(), |
| 769 | OnSetAudioStreamStatus(_, 1, StrEq("created"))); |
| 770 | EXPECT_CALL(media_observer(), |
| 771 | OnSetAudioStreamPlaying(_, 1, true)); |
| 772 | EXPECT_CALL(media_observer(), |
| 773 | OnSetAudioStreamStatus(_, 1, StrEq("closed"))); |
| 774 | EXPECT_CALL(media_observer(), |
| 775 | OnDeleteAudioStream(_, 1)).Times(AnyNumber()); |
| 776 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 777 | scoped_refptr<WebRtcAudioRenderer> renderer = |
| 778 | new WebRtcAudioRenderer(kRenderViewId); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 779 | scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device( |
| 780 | new WebRtcAudioDeviceImpl()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 781 | EXPECT_TRUE(webrtc_audio_device->SetAudioRenderer(renderer.get())); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 782 | |
| 783 | WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create()); |
| 784 | ASSERT_TRUE(engine.valid()); |
| 785 | |
| 786 | ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get()); |
| 787 | ASSERT_TRUE(base.valid()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 788 | int err = base->Init(webrtc_audio_device.get()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 789 | ASSERT_EQ(0, err); |
| 790 | |
| 791 | ScopedWebRTCPtr<webrtc::VoEAudioProcessing> audio_processing(engine.get()); |
| 792 | ASSERT_TRUE(audio_processing.valid()); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 793 | #if defined(OS_ANDROID) |
| 794 | // On Android, by default AGC is off. |
| 795 | bool enabled = true; |
| 796 | webrtc::AgcModes agc_mode = webrtc::kAgcDefault; |
| 797 | EXPECT_EQ(0, audio_processing->GetAgcStatus(enabled, agc_mode)); |
| 798 | EXPECT_FALSE(enabled); |
| 799 | #else |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 800 | bool enabled = false; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 801 | webrtc::AgcModes agc_mode = webrtc::kAgcDefault; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 802 | EXPECT_EQ(0, audio_processing->GetAgcStatus(enabled, agc_mode)); |
| 803 | EXPECT_TRUE(enabled); |
| 804 | EXPECT_EQ(agc_mode, webrtc::kAgcAdaptiveAnalog); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 805 | #endif |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 806 | |
| 807 | int ch = base->CreateChannel(); |
| 808 | EXPECT_NE(-1, ch); |
| 809 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 810 | EXPECT_TRUE(CreateAndInitializeCapturer(webrtc_audio_device.get())); |
| 811 | scoped_refptr<WebRtcLocalAudioTrack> local_audio_track( |
| 812 | CreateAndStartLocalAudioTrack(webrtc_audio_device->GetDefaultCapturer(), |
| 813 | webrtc_audio_device)); |
| 814 | // connect the VoE voice channel to the audio track |
| 815 | static_cast<webrtc::AudioTrackInterface*>(local_audio_track.get())-> |
| 816 | GetRenderer()->AddChannel(ch); |
| 817 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 818 | ScopedWebRTCPtr<webrtc::VoENetwork> network(engine.get()); |
| 819 | ASSERT_TRUE(network.valid()); |
| 820 | scoped_ptr<WebRTCTransportImpl> transport( |
| 821 | new WebRTCTransportImpl(network.get())); |
| 822 | EXPECT_EQ(0, network->RegisterExternalTransport(ch, *transport.get())); |
| 823 | EXPECT_EQ(0, base->StartPlayout(ch)); |
| 824 | EXPECT_EQ(0, base->StartSend(ch)); |
| 825 | renderer->Play(); |
| 826 | |
| 827 | LOG(INFO) << ">> You should now be able to hear yourself in loopback..."; |
| 828 | message_loop_.PostDelayedTask(FROM_HERE, |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 829 | base::MessageLoop::QuitClosure(), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 830 | base::TimeDelta::FromSeconds(2)); |
| 831 | message_loop_.Run(); |
| 832 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 833 | local_audio_track->Stop(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 834 | renderer->Stop(); |
| 835 | EXPECT_EQ(0, base->StopSend(ch)); |
| 836 | EXPECT_EQ(0, base->StopPlayout(ch)); |
| 837 | |
| 838 | EXPECT_EQ(0, base->DeleteChannel(ch)); |
| 839 | EXPECT_EQ(0, base->Terminate()); |
| 840 | } |
| 841 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 842 | // Test times out on bots, see http://crbug.com/247447 |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 843 | TEST_F(MAYBE_WebRTCAudioDeviceTest, DISABLED_WebRtcRecordingSetupTime) { |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 844 | if (!has_input_devices_) { |
| 845 | LOG(WARNING) << "Missing audio capture devices."; |
| 846 | return; |
| 847 | } |
| 848 | |
| 849 | scoped_ptr<media::AudioHardwareConfig> config = |
| 850 | CreateRealHardwareConfig(audio_manager_.get()); |
| 851 | SetAudioHardwareConfig(config.get()); |
| 852 | |
| 853 | if (!HardwareSampleRatesAreValid()) |
| 854 | return; |
| 855 | |
| 856 | scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device( |
| 857 | new WebRtcAudioDeviceImpl()); |
| 858 | |
| 859 | WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create()); |
| 860 | ASSERT_TRUE(engine.valid()); |
| 861 | |
| 862 | ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get()); |
| 863 | ASSERT_TRUE(base.valid()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 864 | int err = base->Init(webrtc_audio_device.get()); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 865 | ASSERT_EQ(0, err); |
| 866 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 867 | int ch = base->CreateChannel(); |
| 868 | EXPECT_NE(-1, ch); |
| 869 | |
| 870 | EXPECT_TRUE(CreateAndInitializeCapturer(webrtc_audio_device.get())); |
| 871 | base::WaitableEvent event(false, false); |
| 872 | scoped_ptr<MockWebRtcAudioCapturerSink> sink( |
| 873 | new MockWebRtcAudioCapturerSink(&event)); |
| 874 | |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 875 | // Create and start a local audio track. Starting the audio track will connect |
| 876 | // the audio track to the capturer and also start the source of the capturer. |
| 877 | scoped_refptr<WebRtcLocalAudioTrack> local_audio_track( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 878 | CreateAndStartLocalAudioTrack( |
| 879 | webrtc_audio_device->GetDefaultCapturer().get(), sink.get())); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 880 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 881 | // connect the VoE voice channel to the audio track. |
| 882 | static_cast<webrtc::AudioTrackInterface*>(local_audio_track.get())-> |
| 883 | GetRenderer()->AddChannel(ch); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 884 | |
| 885 | base::Time start_time = base::Time::Now(); |
| 886 | EXPECT_EQ(0, base->StartSend(ch)); |
| 887 | |
| 888 | EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout())); |
| 889 | int delay = (base::Time::Now() - start_time).InMilliseconds(); |
| 890 | PrintPerfResultMs("webrtc_recording_setup_c", "t", delay); |
| 891 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 892 | local_audio_track->RemoveSink(sink.get()); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 893 | local_audio_track->Stop(); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 894 | EXPECT_EQ(0, base->StopSend(ch)); |
| 895 | EXPECT_EQ(0, base->DeleteChannel(ch)); |
| 896 | EXPECT_EQ(0, base->Terminate()); |
| 897 | } |
| 898 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 899 | TEST_F(MAYBE_WebRTCAudioDeviceTest, WebRtcPlayoutSetupTime) { |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 900 | if (!has_output_devices_) { |
| 901 | LOG(WARNING) << "No output device detected."; |
| 902 | return; |
| 903 | } |
| 904 | |
| 905 | scoped_ptr<media::AudioHardwareConfig> config = |
| 906 | CreateRealHardwareConfig(audio_manager_.get()); |
| 907 | SetAudioHardwareConfig(config.get()); |
| 908 | |
| 909 | if (!HardwareSampleRatesAreValid()) |
| 910 | return; |
| 911 | |
| 912 | EXPECT_CALL(media_observer(), |
| 913 | OnSetAudioStreamStatus(_, 1, _)).Times(AnyNumber()); |
| 914 | EXPECT_CALL(media_observer(), |
| 915 | OnSetAudioStreamPlaying(_, 1, true)); |
| 916 | EXPECT_CALL(media_observer(), |
| 917 | OnDeleteAudioStream(_, 1)).Times(AnyNumber()); |
| 918 | |
| 919 | base::WaitableEvent event(false, false); |
| 920 | scoped_ptr<MockWebRtcAudioRendererSource> renderer_source( |
| 921 | new MockWebRtcAudioRendererSource(&event)); |
| 922 | scoped_refptr<WebRtcAudioRenderer> renderer = |
| 923 | new WebRtcAudioRenderer(kRenderViewId); |
| 924 | renderer->Initialize(renderer_source.get()); |
| 925 | |
| 926 | // Start the timer and playout. |
| 927 | base::Time start_time = base::Time::Now(); |
| 928 | renderer->Play(); |
| 929 | EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout())); |
| 930 | int delay = (base::Time::Now() - start_time).InMilliseconds(); |
| 931 | PrintPerfResultMs("webrtc_playout_setup_c", "t", delay); |
| 932 | |
| 933 | renderer->Stop(); |
| 934 | } |
| 935 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 936 | #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY) |
| 937 | // Timing out on ARM linux bot: http://crbug.com/238490 |
| 938 | #define MAYBE_WebRtcLoopbackTimeWithoutSignalProcessing \ |
| 939 | DISABLED_WebRtcLoopbackTimeWithoutSignalProcessing |
| 940 | #else |
| 941 | #define MAYBE_WebRtcLoopbackTimeWithoutSignalProcessing \ |
| 942 | WebRtcLoopbackTimeWithoutSignalProcessing |
| 943 | #endif |
| 944 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 945 | TEST_F(MAYBE_WebRTCAudioDeviceTest, |
| 946 | MAYBE_WebRtcLoopbackTimeWithoutSignalProcessing) { |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 947 | int latency = RunWebRtcLoopbackTimeTest(audio_manager_.get(), false); |
| 948 | PrintPerfResultMs("webrtc_loopback_without_sigal_processing (100 packets)", |
| 949 | "t", latency); |
| 950 | } |
| 951 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 952 | #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY) |
| 953 | // Timing out on ARM linux bot: http://crbug.com/238490 |
| 954 | #define MAYBE_WebRtcLoopbackTimeWithSignalProcessing \ |
| 955 | DISABLED_WebRtcLoopbackTimeWithSignalProcessing |
| 956 | #else |
| 957 | #define MAYBE_WebRtcLoopbackTimeWithSignalProcessing \ |
| 958 | WebRtcLoopbackTimeWithSignalProcessing |
| 959 | #endif |
| 960 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 961 | TEST_F(MAYBE_WebRTCAudioDeviceTest, |
| 962 | MAYBE_WebRtcLoopbackTimeWithSignalProcessing) { |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 963 | int latency = RunWebRtcLoopbackTimeTest(audio_manager_.get(), true); |
| 964 | PrintPerfResultMs("webrtc_loopback_with_signal_processing (100 packets)", |
| 965 | "t", latency); |
| 966 | } |
| 967 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 968 | } // namespace content |