Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Side by Side Diff: content/browser/speech/speech_recognition_audio_receiver_unittest.cc

Issue 549373003: Binding media stream audio track to speech recognition [browser] Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Tommi's comments Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "base/bind.h"
6 #include "base/memory/shared_memory.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/process/process_handle.h"
9 #include "base/time/time.h"
10 #include "content/browser/browser_thread_impl.h"
11 #include "content/browser/speech/speech_recognition_audio_receiver.h"
12 #include "media/audio/audio_parameters.h"
13 #include "media/base/audio_bus.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 using testing::_;
18
19 namespace {
20
21 // Output audio format.
22 const media::AudioParameters::Format kOutputFormat =
23 media::AudioParameters::AUDIO_PCM_LOW_LATENCY;
24 const media::ChannelLayout kOutputChannelLayout = media::CHANNEL_LAYOUT_STEREO;
25 const int kOutputChannels = 2;
26 const int kOutputSampleRate = 16000;
27 const int kOutputBitsPerSample = 16;
28 const int kOutputFramesPerBuffer = 1600;
29
30 // Timeout for socket.
31 const uint32 kMessages = 5;
32
33 // Used for simulating behavior of the peer after the last socket receive was
34 // triggered. The test scenario consists of a decision whether to close the
35 // the socket, destroy the receiver and if there should be an error event
36 // triggered in that scenario.
37 enum TestScenario {
38 // Used for negating in a bitwise and operation. E.g.: NOT & CLOSE_SOCKET.
39 // Useful for readability.
40 NOT = 0,
41
42 // The socket is closed after last message is received.
43 // The |Close()| occurs before the receiver gets destroyed.
44 CLOSE_SOCKET = 1,
45
46 // The receiver is destroyed right after last message is received
47 // but not before the socket was closed if that's part of the scenario.
48 DESTROY_RECEIVER = 2,
49
50 // Indicate the chosen test scenario should expect an error callback or not.
51 EXPECT_ERROR = 4
52 };
53
54 } // namespace
55
56 namespace content {
57
58 class SpeechRecognitionAudioReceiverTestBase
59 : public testing::Test,
60 public media::AudioInputController::EventHandler {
61 public:
62 SpeechRecognitionAudioReceiverTestBase() :
63 io_thread_(BrowserThread::IO, &message_loop_),
64 num_messages_(0),
65 buffer_index_(0) {}
66
67 virtual ~SpeechRecognitionAudioReceiverTestBase() {}
68
69 void Initialize() {
70 media::AudioParameters audio_params;
71 audio_params.Reset(kOutputFormat,
72 kOutputChannelLayout,
73 kOutputChannels,
74 kOutputSampleRate,
75 kOutputBitsPerSample,
76 kOutputFramesPerBuffer);
77
78 base::SharedMemoryHandle foreign_memory_handle;
79 base::SyncSocket::TransitDescriptor foreign_socket_descriptor;
80 receiver_ = SpeechRecognitionAudioReceiver::Create(
81 audio_params,
82 base::GetCurrentProcessHandle(),
83 this,
84 base::MessageLoop::current()->task_runner().get(),
85 &foreign_memory_handle,
86 &foreign_socket_descriptor);
87 ASSERT_TRUE(receiver_.get() != NULL);
88
89 // Get the producer socket.
90 producer_socket_.reset(new base::SyncSocket(
91 base::SyncSocket::UnwrapHandle(foreign_socket_descriptor)));
92
93 shared_memory_.reset(new base::SharedMemory(foreign_memory_handle, false));
94
95 // Get the shared memory.
96 const size_t memory_length =
97 media::AudioBus::CalculateMemorySize(audio_params) +
98 sizeof(media::AudioInputBufferParameters);
99 ASSERT_TRUE(shared_memory_->Map(memory_length));
100
101 // Peer's buffer index is accessed via |params.size| in shared memory.
102 uint8* ptr = static_cast<uint8*>(shared_memory_->memory());
103 media::AudioInputBuffer* buffer =
104 reinterpret_cast<media::AudioInputBuffer*>(ptr);
105 peer_buffer_index_ = &(buffer->params.size);
106
107 // Local counter of sent messages.
108 buffer_index_ = 0;
109
110 // The peer must manage their own counter and reset it.
111 ASSERT_EQ(0U, *peer_buffer_index_);
112 output_bus_ = media::AudioBus::WrapMemory(audio_params, buffer->audio);
113 }
114
115 protected:
116 // Mock methods triggered by events and used for counting callbacks.
117 MOCK_METHOD0(SendCalled, void());
118 MOCK_METHOD0(OnDataCalled, void());
119 MOCK_METHOD0(OnErrorCalled, void());
120
121 // Create the receiver and starts posting tasks to send |num_messages|.
122 void SendMessages(const uint32 num_messages) {
123 buffer_index_ = 0;
124 num_messages_ = num_messages;
125
126 Initialize();
127
128 // Start the sending soon on this thread.
129 PostSendTask();
130
131 // We start listening after the send task was posted to catch the first
132 // message in the same thread message loop.
133 receiver_->Listen();
134 }
135
136 base::SyncSocket* producer_socket() { return producer_socket_.get(); }
137 uint32 peer_buffer_index() { return *peer_buffer_index_; }
138 base::MessageLoopForIO * message_loop() { return &message_loop_; }
139
140 void set_scenario(const TestScenario scenario) { scenario_ = scenario; }
141 const TestScenario& scenario() const { return scenario_; }
142
143 base::MessageLoopForIO message_loop_;
144 BrowserThreadImpl io_thread_;
145
146 private:
147 // AudioInputController::EventHandler methods.
148 void OnCreated(media::AudioInputController* controller) override {}
149 void OnRecording(media::AudioInputController* controller) override {}
150 void OnLog(media::AudioInputController* controller,
151 const std::string& message) override {}
152
153 void OnError(
154 media::AudioInputController* controller,
155 media::AudioInputController::ErrorCode error_code) override {
156 // Trigger the mock callback for counting.
157 OnErrorCalled();
158 }
159
160 void OnData(media::AudioInputController* controller,
161 const media::AudioBus* data) {
162 // Check if we're getting the same data as sent.
163 CheckAudioBus(data);
164
165 if (buffer_index_ < num_messages_) {
166 // Schedule another send once this receive completes.
167 // The Send task should be scheduled before the Receive task.
168 PostSendTask();
169 } else {
170 // This path indicates we have received the last message.
171 // Here we apply the current test scenario.
172 if (scenario_ & CLOSE_SOCKET) {
173 producer_socket()->Close();
174 }
175
176 if (scenario_ & DESTROY_RECEIVER) {
177 PostDestroyReceiverTask();
178 }
179 }
180
181 // Trigger the mock callback for counting.
182 OnDataCalled();
183 }
184
185 // Posts a task to trigger a send on the socket.
186 void PostSendTask() {
187 message_loop()->task_runner()->PostTask(
188 FROM_HERE,
189 base::Bind(&SpeechRecognitionAudioReceiverTestBase::Send,
190 base::Unretained(this)));
191 }
192
193 // Sends a buffer to the receiver over the socket and check synchronization.
194 void Send() {
195 // Check buffer index synchronization is done.
196 EXPECT_EQ(buffer_index_, peer_buffer_index());
197
198 // Render different sample data for each send.
199 RenderSampleAudioToOutputBus(1 + static_cast<int16>(buffer_index_));
200
201 size_t sent_bytes =
202 producer_socket()->Send(&buffer_index_, sizeof(buffer_index_));
203 EXPECT_EQ(sizeof(buffer_index_), sent_bytes);
204
205 ++buffer_index_;
206
207 // Trigger the mock callback for counting.
208 SendCalled();
209 }
210
211 void DestroyReceiver() {
212 receiver_.reset();
213 }
214
215 void PostDestroyReceiverTask() {
216 message_loop()->task_runner()->PostTask(
217 FROM_HERE,
218 base::Bind(&SpeechRecognitionAudioReceiverTestBase::DestroyReceiver,
219 base::Unretained(this)));
220 }
221
222 void RenderSampleAudioToOutputBus(int16 value) {
223 const int kFrames = kOutputFramesPerBuffer * kOutputChannels;
224 int16 audio_packet[kFrames];
225 for (int i = 0; i < kFrames; ++i) {
226 audio_packet[i] = value;
227 }
228
229 output_bus_->FromInterleaved(audio_packet,
230 output_bus_->frames(),
231 sizeof(audio_packet[0]));
232 }
233
234 // Checks that target_bus has same data as the source |output_bus_|.
235 void CheckAudioBus(const media::AudioBus* target_bus) {
236 const int kDataLength = kOutputFramesPerBuffer * kOutputChannels;
237 int16 source_data[kDataLength] = {0};
238 output_bus_->ToInterleaved(output_bus_->frames(),
239 kOutputBitsPerSample / 8,
240 source_data);
241 int16 target_data[kDataLength] = {0};
242 target_bus->ToInterleaved(target_bus->frames(),
243 kOutputBitsPerSample / 8,
244 target_data);
245
246 for (int i = 0 ; i < kDataLength; ++i) {
247 EXPECT_EQ(source_data[i], target_data[i]);
248 }
249 }
250
251 // How many sends before applying the test scenario.
252 uint32 num_messages_;
253
254 // Affects behavior of the socket and receiver after the |num_messages_|
255 // messages were receieved.
256 TestScenario scenario_;
257
258 uint32 buffer_index_;
259
260 scoped_ptr<SpeechRecognitionAudioReceiver> receiver_;
261
262 scoped_ptr<base::SyncSocket> producer_socket_;
263
264 scoped_ptr<base::SharedMemory> shared_memory_;
265
266 scoped_ptr<media::AudioBus> output_bus_;
267
268 uint32* peer_buffer_index_;
269
270 DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionAudioReceiverTestBase);
271 };
272
273 class SpeechRecognitionAudioReceiverTest
274 : public SpeechRecognitionAudioReceiverTestBase,
275 public testing::WithParamInterface<TestScenario> {
276 };
277
278 TEST_P(SpeechRecognitionAudioReceiverTest, CheckScenario) {
279 set_scenario(GetParam());
280 SendMessages(kMessages);
281
282 EXPECT_CALL(*this, SendCalled())
283 .Times(kMessages);
284 EXPECT_CALL(*this, OnDataCalled())
285 .Times(kMessages);
286 EXPECT_CALL(*this, OnErrorCalled())
287 .Times((scenario() & EXPECT_ERROR) ? 1 : 0);
288
289 message_loop()->RunUntilIdle();
290 };
291
292 // We don't destroy the receiver after all sends were issued which will cause
293 // timeouts on the socket receive and an |OnError()| callback is issued to
294 // indicate there is a problem with receiving data. The session is then aborted
295 // by the speech recognizer in production.
296 INSTANTIATE_TEST_CASE_P(
297 SendAndFreezeTriggersError,
298 SpeechRecognitionAudioReceiverTest,
299 testing::Values(TestScenario(
300 NOT & CLOSE_SOCKET | NOT & DESTROY_RECEIVER | EXPECT_ERROR)));
301
302 // Here we destroy the receiver after sending the messages which should stop the
303 // receiving thread cleanly - without an error callback. This is a desired
304 // behavior since the speech recognizer should destroy the receiver when the
305 // session is to be stopped.
306 INSTANTIATE_TEST_CASE_P(
307 SendAndDestroyTriggersNoError,
308 SpeechRecognitionAudioReceiverTest,
309 testing::Values(TestScenario(
310 NOT & CLOSE_SOCKET | DESTROY_RECEIVER | NOT & EXPECT_ERROR)));
311
312 // We close the socket after sending |kMessages| messages expecting same
313 // behavior as when a timeout occurs.
314 INSTANTIATE_TEST_CASE_P(
315 SendAndCloseSocketTriggersError,
316 SpeechRecognitionAudioReceiverTest,
317 testing::Values(TestScenario(
318 CLOSE_SOCKET | NOT & DESTROY_RECEIVER | EXPECT_ERROR)));
319
320 // We close the socket and destroy the receiver expecting same behavior as when
321 // a timeout occurs.
322 INSTANTIATE_TEST_CASE_P(
323 SendCloseDestroyTriggersNoError,
324 SpeechRecognitionAudioReceiverTest,
325 testing::Values(TestScenario(
326 CLOSE_SOCKET | DESTROY_RECEIVER | NOT & EXPECT_ERROR)));
327
328 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698