OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/speech_recognition_audio_sink.h" | 5 #include "content/renderer/media/speech_recognition_audio_sink.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/strings/utf_string_conversions.h" | 8 #include "base/strings/utf_string_conversions.h" |
9 #include "content/renderer/media/media_stream_audio_source.h" | 9 #include "content/renderer/media/media_stream_audio_source.h" |
10 #include "content/renderer/media/mock_media_constraint_factory.h" | 10 #include "content/renderer/media/mock_media_constraint_factory.h" |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
102 // Since buffer is used sequentially, we can reset the buffer indices here. | 102 // Since buffer is used sequentially, we can reset the buffer indices here. |
103 buffer_->start = buffer_->length = 0; | 103 buffer_->start = buffer_->length = 0; |
104 return length; | 104 return length; |
105 } | 105 } |
106 | 106 |
107 // This fake class is the consumer used to verify behaviour of the producer. | 107 // This fake class is the consumer used to verify behaviour of the producer. |
108 // The |Initialize()| method shows what the consumer should be responsible for | 108 // The |Initialize()| method shows what the consumer should be responsible for |
109 // in the production code (minus the mocks). | 109 // in the production code (minus the mocks). |
110 class FakeSpeechRecognizer { | 110 class FakeSpeechRecognizer { |
111 public: | 111 public: |
112 FakeSpeechRecognizer() : is_responsive_(true) { } | 112 FakeSpeechRecognizer() : is_responsive_(true) {} |
113 | 113 |
114 void Initialize( | 114 void Initialize( |
115 const blink::WebMediaStreamTrack& track, | 115 const blink::WebMediaStreamTrack& track, |
116 const media::AudioParameters& sink_params, | 116 const media::AudioParameters& sink_params, |
117 base::SharedMemoryHandle* foreign_memory_handle) { | 117 base::SharedMemoryHandle* foreign_memory_handle) { |
118 // Shared memory is allocated, mapped and shared. | 118 // Shared memory is allocated, mapped and shared. |
119 const uint32 kSharedMemorySize = | 119 const uint32 kSharedMemorySize = |
120 sizeof(media::AudioInputBufferParameters) + | 120 sizeof(media::AudioInputBufferParameters) + |
121 media::AudioBus::CalculateMemorySize(sink_params); | 121 media::AudioBus::CalculateMemorySize(sink_params); |
122 shared_memory_.reset(new base::SharedMemory()); | 122 shared_memory_.reset(new base::SharedMemory()); |
123 ASSERT_TRUE(shared_memory_->CreateAndMapAnonymous(kSharedMemorySize)); | 123 ASSERT_TRUE(shared_memory_->CreateAndMapAnonymous(kSharedMemorySize)); |
124 memset(shared_memory_->memory(), 0, kSharedMemorySize); | 124 memset(shared_memory_->memory(), 0, kSharedMemorySize); |
125 ASSERT_TRUE(shared_memory_->ShareToProcess(base::GetCurrentProcessHandle(), | 125 ASSERT_TRUE(shared_memory_->ShareToProcess(base::GetCurrentProcessHandle(), |
126 foreign_memory_handle)); | 126 foreign_memory_handle)); |
127 | 127 |
128 // Wrap the shared memory for the audio bus. | 128 // Wrap the shared memory for the audio bus. |
129 media::AudioInputBuffer* buffer = | 129 media::AudioInputBuffer* buffer = |
130 static_cast<media::AudioInputBuffer*>(shared_memory_->memory()); | 130 static_cast<media::AudioInputBuffer*>(shared_memory_->memory()); |
| 131 |
131 audio_track_bus_ = media::AudioBus::WrapMemory(sink_params, buffer->audio); | 132 audio_track_bus_ = media::AudioBus::WrapMemory(sink_params, buffer->audio); |
132 audio_track_bus_->Zero(); | 133 audio_track_bus_->Zero(); |
133 | 134 |
134 // Reference to the counter used to synchronize. | 135 // Reference to the counter used to synchronize. |
135 buffer_index_ = &(buffer->params.size); | 136 buffer->params.size = 0U; |
136 *buffer_index_ = 0U; | |
137 | 137 |
138 // Create a shared buffer for the |MockSyncSocket|s. | 138 // Create a shared buffer for the |MockSyncSocket|s. |
139 shared_buffer_.reset(new MockSyncSocket::SharedBuffer()); | 139 shared_buffer_.reset(new MockSyncSocket::SharedBuffer()); |
140 | 140 |
141 // Local socket will receive signals from the producer. | 141 // Local socket will receive signals from the producer. |
142 local_socket_.reset(new MockSyncSocket(shared_buffer_.get())); | 142 receiving_socket_.reset(new MockSyncSocket(shared_buffer_.get())); |
143 | 143 |
144 // We automatically trigger a Receive when data is sent over the socket. | 144 // We automatically trigger a Receive when data is sent over the socket. |
145 foreign_socket_ = new MockSyncSocket( | 145 sending_socket_ = new MockSyncSocket( |
146 shared_buffer_.get(), | 146 shared_buffer_.get(), |
147 base::Bind(&FakeSpeechRecognizer::EmulateReceiveThreadLoopIteration, | 147 base::Bind(&FakeSpeechRecognizer::EmulateReceiveThreadLoopIteration, |
148 base::Unretained(this))); | 148 base::Unretained(this))); |
149 | 149 |
150 // This is usually done to pair the sockets. Here it's not effective. | 150 // This is usually done to pair the sockets. Here it's not effective. |
151 base::SyncSocket::CreatePair(local_socket_.get(), foreign_socket_); | 151 base::SyncSocket::CreatePair(receiving_socket_.get(), sending_socket_); |
152 } | 152 } |
153 | 153 |
154 // Emulates a single iteraton of a thread receiving on the socket. | 154 // Emulates a single iteraton of a thread receiving on the socket. |
155 // This would normally be done on a receiving thread's task on the browser. | 155 // This would normally be done on a receiving thread's task on the browser. |
156 void EmulateReceiveThreadLoopIteration() { | 156 void EmulateReceiveThreadLoopIteration() { |
157 // When not responsive do nothing as if the process is busy. | |
158 if (!is_responsive_) | 157 if (!is_responsive_) |
159 return; | 158 return; |
160 | 159 |
161 local_socket_->Receive(buffer_index_, sizeof(*buffer_index_)); | 160 const int kSize = sizeof(media::AudioInputBufferParameters().size); |
| 161 receiving_socket_->Receive(&(GetAudioInputBuffer()->params.size), kSize); |
| 162 |
162 // Notify the producer that the audio buffer has been consumed. | 163 // Notify the producer that the audio buffer has been consumed. |
163 ++(*buffer_index_); | 164 GetAudioInputBuffer()->params.size++; |
164 } | 165 } |
165 | 166 |
166 // Used to simulate an unresponsive behaviour of the consumer. | 167 // Used to simulate an unresponsive behaviour of the consumer. |
167 void SimulateResponsiveness(bool is_responsive) { | 168 void SimulateResponsiveness(bool is_responsive) { |
168 is_responsive_ = is_responsive; | 169 is_responsive_ = is_responsive; |
169 } | 170 } |
170 | 171 |
171 MockSyncSocket* foreign_socket() { return foreign_socket_; } | 172 media::AudioInputBuffer * GetAudioInputBuffer() const { |
| 173 return static_cast<media::AudioInputBuffer*>(shared_memory_->memory()); |
| 174 } |
| 175 |
| 176 MockSyncSocket* sending_socket() { return sending_socket_; } |
172 media::AudioBus* audio_bus() const { return audio_track_bus_.get(); } | 177 media::AudioBus* audio_bus() const { return audio_track_bus_.get(); } |
173 uint32 buffer_index() { return *buffer_index_; } | 178 |
174 | 179 |
175 private: | 180 private: |
176 bool is_responsive_; | 181 bool is_responsive_; |
177 | 182 |
178 // Shared memory for the audio and synchronization. | 183 // Shared memory for the audio and synchronization. |
179 scoped_ptr<base::SharedMemory> shared_memory_; | 184 scoped_ptr<base::SharedMemory> shared_memory_; |
180 | 185 |
181 // Fake sockets and their shared buffer. | 186 // Fake sockets and their shared buffer. |
182 scoped_ptr<MockSyncSocket::SharedBuffer> shared_buffer_; | 187 scoped_ptr<MockSyncSocket::SharedBuffer> shared_buffer_; |
183 scoped_ptr<MockSyncSocket> local_socket_; | 188 scoped_ptr<MockSyncSocket> receiving_socket_; |
184 MockSyncSocket* foreign_socket_; | 189 MockSyncSocket* sending_socket_; |
185 | 190 |
186 // Audio bus wrapping the shared memory from the renderer. | 191 // Audio bus wrapping the shared memory from the renderer. |
187 scoped_ptr<media::AudioBus> audio_track_bus_; | 192 scoped_ptr<media::AudioBus> audio_track_bus_; |
188 | 193 |
189 // Used for synchronization of sent/received buffers. | |
190 uint32* buffer_index_; | |
191 | |
192 DISALLOW_COPY_AND_ASSIGN(FakeSpeechRecognizer); | 194 DISALLOW_COPY_AND_ASSIGN(FakeSpeechRecognizer); |
193 }; | 195 }; |
194 | 196 |
195 } // namespace | 197 } // namespace |
196 | 198 |
197 namespace content { | 199 namespace content { |
198 | 200 |
199 class SpeechRecognitionAudioSinkTest : public testing::Test { | 201 class SpeechRecognitionAudioSinkTest : public testing::Test { |
200 public: | 202 public: |
201 SpeechRecognitionAudioSinkTest() {} | 203 SpeechRecognitionAudioSinkTest() {} |
(...skipping 30 matching lines...) Expand all Loading... |
232 native_track_ = | 234 native_track_ = |
233 static_cast<WebRtcLocalAudioTrack*>(blink_track.extraData()); | 235 static_cast<WebRtcLocalAudioTrack*>(blink_track.extraData()); |
234 native_track_->OnSetFormat(source_params_); | 236 native_track_->OnSetFormat(source_params_); |
235 | 237 |
236 // Create and initialize the consumer. | 238 // Create and initialize the consumer. |
237 recognizer_.reset(new FakeSpeechRecognizer()); | 239 recognizer_.reset(new FakeSpeechRecognizer()); |
238 base::SharedMemoryHandle foreign_memory_handle; | 240 base::SharedMemoryHandle foreign_memory_handle; |
239 recognizer_->Initialize(blink_track, sink_params_, &foreign_memory_handle); | 241 recognizer_->Initialize(blink_track, sink_params_, &foreign_memory_handle); |
240 | 242 |
241 // Create the producer. | 243 // Create the producer. |
242 scoped_ptr<base::SyncSocket> foreign_socket(recognizer_->foreign_socket()); | 244 scoped_ptr<base::SyncSocket> sending_socket(recognizer_->sending_socket()); |
243 speech_audio_sink_.reset(new SpeechRecognitionAudioSink( | 245 speech_audio_sink_.reset(new SpeechRecognitionAudioSink( |
244 blink_track, sink_params_, foreign_memory_handle, | 246 blink_track, sink_params_, foreign_memory_handle, |
245 foreign_socket.Pass(), | 247 sending_socket.Pass(), |
246 base::Bind(&SpeechRecognitionAudioSinkTest::StoppedCallback, | 248 base::Bind(&SpeechRecognitionAudioSinkTest::StoppedCallback, |
247 base::Unretained(this)))); | 249 base::Unretained(this)))); |
248 | 250 |
249 // Return number of buffers needed to trigger resampling and consumption. | 251 // Return number of buffers needed to trigger resampling and consumption. |
250 return static_cast<uint32>(std::ceil( | 252 return static_cast<uint32>(std::ceil( |
251 static_cast<double>(output_frames_per_buffer * input_sample_rate) / | 253 static_cast<double>(output_frames_per_buffer * input_sample_rate) / |
252 (input_frames_per_buffer * output_sample_rate))); | 254 (input_frames_per_buffer * output_sample_rate))); |
253 } | 255 } |
254 | 256 |
255 // Mock callback expected to be called when the track is stopped. | 257 // Mock callback expected to be called when the track is stopped. |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
288 // Emulates an audio capture device capturing data from the source. | 290 // Emulates an audio capture device capturing data from the source. |
289 inline void CaptureAudio(const uint32 buffers) { | 291 inline void CaptureAudio(const uint32 buffers) { |
290 for (uint32 i = 0; i < buffers; ++i) | 292 for (uint32 i = 0; i < buffers; ++i) |
291 native_track()->Capture(source_data(), | 293 native_track()->Capture(source_data(), |
292 base::TimeDelta::FromMilliseconds(0), 1, false, | 294 base::TimeDelta::FromMilliseconds(0), 1, false, |
293 false); | 295 false); |
294 } | 296 } |
295 | 297 |
296 // Used to simulate a problem with sockets. | 298 // Used to simulate a problem with sockets. |
297 void SetFailureModeOnForeignSocket(bool in_failure_mode) { | 299 void SetFailureModeOnForeignSocket(bool in_failure_mode) { |
298 recognizer()->foreign_socket()->SetFailureMode(in_failure_mode); | 300 recognizer()->sending_socket()->SetFailureMode(in_failure_mode); |
299 } | 301 } |
300 | 302 |
301 // Helper method for verifying captured audio data has been consumed. | 303 // Helper method for verifying captured audio data has been consumed. |
302 inline void AssertConsumedBuffers(const uint32 buffer_index) { | 304 inline void AssertConsumedBuffers(const uint32 buffer_index) { |
303 ASSERT_EQ(buffer_index, recognizer_->buffer_index()); | 305 ASSERT_EQ(buffer_index, recognizer()->GetAudioInputBuffer()->params.size); |
304 } | 306 } |
305 | 307 |
306 // Helper method for providing audio data to producer and verifying it was | 308 // Helper method for providing audio data to producer and verifying it was |
307 // consumed on the recognizer. | 309 // consumed on the recognizer. |
308 inline void CaptureAudioAndAssertConsumedBuffers(const uint32 buffers, | 310 inline void CaptureAudioAndAssertConsumedBuffers(const uint32 buffers, |
309 const uint32 buffer_index) { | 311 const uint32 buffer_index) { |
310 CaptureAudio(buffers); | 312 CaptureAudio(buffers); |
311 AssertConsumedBuffers(buffer_index); | 313 AssertConsumedBuffers(buffer_index); |
312 } | 314 } |
313 | 315 |
314 // Helper method to capture and assert consumption at different sample rates | 316 // Helper method to capture and assert consumption at different sample rates |
315 // and audio buffer sizes. | 317 // and audio buffer sizes. |
316 inline void AssertConsumptionForAudioParameters( | 318 inline void AssertConsumptionForAudioParameters( |
317 const int input_sample_rate, | 319 const int input_sample_rate, |
318 const int input_frames_per_buffer, | 320 const int input_frames_per_buffer, |
319 const int output_sample_rate, | 321 const int output_sample_rate, |
320 const int output_frames_per_buffer, | 322 const int output_frames_per_buffer, |
321 const uint32 consumptions) { | 323 const uint32 consumptions) { |
322 const uint32 kBuffersPerNotification = Initialize(input_sample_rate, | 324 const uint32 kBuffersPerNotification = Initialize(input_sample_rate, |
323 input_frames_per_buffer, | 325 input_frames_per_buffer, |
324 output_sample_rate, | 326 output_sample_rate, |
325 output_frames_per_buffer); | 327 output_frames_per_buffer); |
326 AssertConsumedBuffers(0U); | 328 AssertConsumedBuffers(0U); |
327 | 329 |
328 for (uint32 i = 1U; i <= consumptions; ++i) { | 330 for (uint32 i = 1U; i <= consumptions; ++i) { |
329 CaptureAudio(kBuffersPerNotification); | 331 CaptureAudio(kBuffersPerNotification); |
330 ASSERT_EQ(i, recognizer_->buffer_index()) | 332 ASSERT_EQ(i, recognizer()->GetAudioInputBuffer()->params.size) |
331 << "Tested at rates: " | 333 << "Tested at rates: " |
332 << "In(" << input_sample_rate << ", " << input_frames_per_buffer | 334 << "In(" << input_sample_rate << ", " << input_frames_per_buffer |
333 << ") " | 335 << ") " |
334 << "Out(" << output_sample_rate << ", " << output_frames_per_buffer | 336 << "Out(" << output_sample_rate << ", " << output_frames_per_buffer |
335 << ")"; | 337 << ")"; |
336 } | 338 } |
337 } | 339 } |
338 | 340 |
339 int16* source_data() { return source_data_.get(); } | 341 int16* source_data() { return source_data_.get(); } |
340 | 342 |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
467 const uint32 kBuffersPerNotification = Initialize(44100, 441, 16000, 1600); | 469 const uint32 kBuffersPerNotification = Initialize(44100, 441, 16000, 1600); |
468 // Start with no problems on the socket. | 470 // Start with no problems on the socket. |
469 AssertConsumedBuffers(0U); | 471 AssertConsumedBuffers(0U); |
470 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); | 472 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); |
471 | 473 |
472 // A failure occurs (socket cannot send). | 474 // A failure occurs (socket cannot send). |
473 SetFailureModeOnForeignSocket(true); | 475 SetFailureModeOnForeignSocket(true); |
474 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); | 476 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); |
475 } | 477 } |
476 | 478 |
| 479 // A very unlikely scenario in which the peer is not synchronizing for a long |
| 480 // time (e.g. 300 ms) which results in dropping cached buffers and restarting. |
| 481 // We check that the FIFO overflow does not occur and that the producer is able |
| 482 // to resume. |
| 483 TEST_F(SpeechRecognitionAudioSinkTest, RepeatedSycnhronizationLag) { |
| 484 const uint32 kBuffersPerNotification = Initialize(44100, 441, 16000, 1600); |
| 485 |
| 486 // Start with no synchronization problems. |
| 487 AssertConsumedBuffers(0U); |
| 488 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); |
| 489 |
| 490 // Consumer gets out of sync. |
| 491 recognizer()->SimulateResponsiveness(false); |
| 492 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); |
| 493 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); |
| 494 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); |
| 495 |
| 496 // Consumer recovers. |
| 497 recognizer()->SimulateResponsiveness(true); |
| 498 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 2U); |
| 499 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 3U); |
| 500 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 4U); |
| 501 } |
| 502 |
477 // Checks that an OnStoppedCallback is issued when the track is stopped. | 503 // Checks that an OnStoppedCallback is issued when the track is stopped. |
478 TEST_F(SpeechRecognitionAudioSinkTest, OnReadyStateChangedOccured) { | 504 TEST_F(SpeechRecognitionAudioSinkTest, OnReadyStateChangedOccured) { |
479 const uint32 kBuffersPerNotification = Initialize(44100, 441, 16000, 1600); | 505 const uint32 kBuffersPerNotification = Initialize(44100, 441, 16000, 1600); |
480 AssertConsumedBuffers(0U); | 506 AssertConsumedBuffers(0U); |
481 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); | 507 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); |
482 EXPECT_CALL(*this, StoppedCallback()).Times(1); | 508 EXPECT_CALL(*this, StoppedCallback()).Times(1); |
483 | 509 |
484 native_track()->Stop(); | 510 native_track()->Stop(); |
485 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); | 511 CaptureAudioAndAssertConsumedBuffers(kBuffersPerNotification, 1U); |
486 } | 512 } |
487 | 513 |
488 } // namespace content | 514 } // namespace content |
OLD | NEW |