blob: 689de581d04eaf58ae473ccdfe52183b0256437e [file] [log] [blame]
Peter Hanspers8d95e3b2018-05-15 10:22:36 +02001/*
2 * Copyright 2018 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#import <XCTest/XCTest.h>
12
13#if defined(WEBRTC_IOS)
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020014#import "sdk/objc/native/api/audio_device_module.h"
Peter Hanspers8d95e3b2018-05-15 10:22:36 +020015#endif
16
17#include "system_wrappers/include/event_wrapper.h"
18
19#include "rtc_base/scoped_ref_ptr.h"
20
21typedef int32_t(^NeedMorePlayDataBlock)(const size_t nSamples,
22 const size_t nBytesPerSample,
23 const size_t nChannels,
24 const uint32_t samplesPerSec,
25 void* audioSamples,
26 size_t& nSamplesOut,
27 int64_t* elapsed_time_ms,
28 int64_t* ntp_time_ms);
29
30typedef int32_t(^RecordedDataIsAvailableBlock)(const void* audioSamples,
31 const size_t nSamples,
32 const size_t nBytesPerSample,
33 const size_t nChannels,
34 const uint32_t samplesPerSec,
35 const uint32_t totalDelayMS,
36 const int32_t clockDrift,
37 const uint32_t currentMicLevel,
38 const bool keyPressed,
39 uint32_t& newMicLevel);
40
41
42// This class implements the AudioTransport API and forwards all methods to the appropriate blocks.
43class MockAudioTransport : public webrtc::AudioTransport {
44public:
45 MockAudioTransport() {}
Mirko Bonadei17aff352018-07-26 12:20:40 +020046 ~MockAudioTransport() override {}
Peter Hanspers8d95e3b2018-05-15 10:22:36 +020047
48 void expectNeedMorePlayData(NeedMorePlayDataBlock block) {
49 needMorePlayDataBlock = block;
50 }
51
52 void expectRecordedDataIsAvailable(RecordedDataIsAvailableBlock block) {
53 recordedDataIsAvailableBlock = block;
54 }
55
56 int32_t NeedMorePlayData(const size_t nSamples,
57 const size_t nBytesPerSample,
58 const size_t nChannels,
59 const uint32_t samplesPerSec,
60 void* audioSamples,
61 size_t& nSamplesOut,
62 int64_t* elapsed_time_ms,
Mirko Bonadei17aff352018-07-26 12:20:40 +020063 int64_t* ntp_time_ms) override {
Peter Hanspers8d95e3b2018-05-15 10:22:36 +020064 return needMorePlayDataBlock(nSamples,
65 nBytesPerSample,
66 nChannels,
67 samplesPerSec,
68 audioSamples,
69 nSamplesOut,
70 elapsed_time_ms,
71 ntp_time_ms);
72 }
73
74 int32_t RecordedDataIsAvailable(const void* audioSamples,
75 const size_t nSamples,
76 const size_t nBytesPerSample,
77 const size_t nChannels,
78 const uint32_t samplesPerSec,
79 const uint32_t totalDelayMS,
80 const int32_t clockDrift,
81 const uint32_t currentMicLevel,
82 const bool keyPressed,
Mirko Bonadei17aff352018-07-26 12:20:40 +020083 uint32_t& newMicLevel) override {
Peter Hanspers8d95e3b2018-05-15 10:22:36 +020084 return recordedDataIsAvailableBlock(audioSamples,
85 nSamples,
86 nBytesPerSample,
87 nChannels,
88 samplesPerSec,
89 totalDelayMS,
90 clockDrift,
91 currentMicLevel,
92 keyPressed,
93 newMicLevel);
94 }
95
96 void PullRenderData(int bits_per_sample,
97 int sample_rate,
98 size_t number_of_channels,
99 size_t number_of_frames,
100 void* audio_data,
101 int64_t* elapsed_time_ms,
Mirko Bonadei17aff352018-07-26 12:20:40 +0200102 int64_t* ntp_time_ms) override {}
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200103
Mirko Bonadei17aff352018-07-26 12:20:40 +0200104 private:
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200105 NeedMorePlayDataBlock needMorePlayDataBlock;
106 RecordedDataIsAvailableBlock recordedDataIsAvailableBlock;
107};
108
109// Number of callbacks (input or output) the tests waits for before we set
110// an event indicating that the test was OK.
111static const NSUInteger kNumCallbacks = 10;
112// Max amount of time we wait for an event to be set while counting callbacks.
113static const NSTimeInterval kTestTimeOutInSec = 20.0;
114// Number of bits per PCM audio sample.
115static const NSUInteger kBitsPerSample = 16;
116// Number of bytes per PCM audio sample.
117static const NSUInteger kBytesPerSample = kBitsPerSample / 8;
118// Average number of audio callbacks per second assuming 10ms packet size.
119static const NSUInteger kNumCallbacksPerSecond = 100;
120// Play out a test file during this time (unit is in seconds).
121static const NSUInteger kFilePlayTimeInSec = 15;
122// Run the full-duplex test during this time (unit is in seconds).
123// Note that first |kNumIgnoreFirstCallbacks| are ignored.
124static const NSUInteger kFullDuplexTimeInSec = 10;
125// Wait for the callback sequence to stabilize by ignoring this amount of the
126// initial callbacks (avoids initial FIFO access).
127// Only used in the RunPlayoutAndRecordingInFullDuplex test.
128static const NSUInteger kNumIgnoreFirstCallbacks = 50;
129
130@interface RTCAudioDeviceModuleTests : XCTestCase {
131
132 rtc::scoped_refptr<webrtc::AudioDeviceModule> audioDeviceModule;
133 webrtc::AudioParameters playoutParameters;
134 webrtc::AudioParameters recordParameters;
135 MockAudioTransport mock;
136}
137
138@end
139
140@implementation RTCAudioDeviceModuleTests
141
142- (void)setUp {
143 [super setUp];
144 audioDeviceModule = webrtc::CreateAudioDeviceModule();
145 XCTAssertEqual(0, audioDeviceModule->Init());
146 XCTAssertEqual(0, audioDeviceModule->GetPlayoutAudioParameters(&playoutParameters));
147 XCTAssertEqual(0, audioDeviceModule->GetRecordAudioParameters(&recordParameters));
148}
149
150- (void)tearDown {
151 XCTAssertEqual(0, audioDeviceModule->Terminate());
152 audioDeviceModule = nullptr;
153 [super tearDown];
154}
155
156- (void)startPlayout {
157 XCTAssertFalse(audioDeviceModule->Playing());
158 XCTAssertEqual(0, audioDeviceModule->InitPlayout());
159 XCTAssertTrue(audioDeviceModule->PlayoutIsInitialized());
160 XCTAssertEqual(0, audioDeviceModule->StartPlayout());
161 XCTAssertTrue(audioDeviceModule->Playing());
162}
163
164- (void)stopPlayout {
165 XCTAssertEqual(0, audioDeviceModule->StopPlayout());
166 XCTAssertFalse(audioDeviceModule->Playing());
167}
168
169- (void)startRecording{
170 XCTAssertFalse(audioDeviceModule->Recording());
171 XCTAssertEqual(0, audioDeviceModule->InitRecording());
172 XCTAssertTrue(audioDeviceModule->RecordingIsInitialized());
173 XCTAssertEqual(0, audioDeviceModule->StartRecording());
174 XCTAssertTrue(audioDeviceModule->Recording());
175}
176
177- (void)stopRecording{
178 XCTAssertEqual(0, audioDeviceModule->StopRecording());
179 XCTAssertFalse(audioDeviceModule->Recording());
180}
181
182- (NSURL*)fileURLForSampleRate:(int)sampleRate {
183 XCTAssertTrue(sampleRate == 48000 || sampleRate == 44100 || sampleRate == 16000);
184 NSString *filename = [NSString stringWithFormat:@"audio_short%d", sampleRate / 1000];
185 NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:@"pcm"];
186 XCTAssertNotNil(url);
187
188 return url;
189}
190
191#pragma mark - Tests
192
193- (void)testConstructDestruct {
194 // Using the test fixture to create and destruct the audio device module.
195}
196
197- (void)testInitTerminate {
198 // Initialization is part of the test fixture.
199 XCTAssertTrue(audioDeviceModule->Initialized());
200 XCTAssertEqual(0, audioDeviceModule->Terminate());
201 XCTAssertFalse(audioDeviceModule->Initialized());
202}
203
204// Tests that playout can be initiated, started and stopped. No audio callback
205// is registered in this test.
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200206- (void)testStartStopPlayout {
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200207 [self startPlayout];
208 [self stopPlayout];
209 [self startPlayout];
210 [self stopPlayout];
211}
212
213// Tests that recording can be initiated, started and stopped. No audio callback
214// is registered in this test.
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200215- (void)testStartStopRecording {
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200216 [self startRecording];
217 [self stopRecording];
218 [self startRecording];
219 [self stopRecording];
220}
221// Verify that calling StopPlayout() will leave us in an uninitialized state
222// which will require a new call to InitPlayout(). This test does not call
223// StartPlayout() while being uninitialized since doing so will hit a
224// RTC_DCHECK.
225- (void)testStopPlayoutRequiresInitToRestart {
226 XCTAssertEqual(0, audioDeviceModule->InitPlayout());
227 XCTAssertEqual(0, audioDeviceModule->StartPlayout());
228 XCTAssertEqual(0, audioDeviceModule->StopPlayout());
229 XCTAssertFalse(audioDeviceModule->PlayoutIsInitialized());
230}
231
232// Verify that we can create two ADMs and start playing on the second ADM.
233// Only the first active instance shall activate an audio session and the
234// last active instance shall deactivate the audio session. The test does not
235// explicitly verify correct audio session calls but instead focuses on
236// ensuring that audio starts for both ADMs.
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200237- (void)testStartPlayoutOnTwoInstances {
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200238 // Create and initialize a second/extra ADM instance. The default ADM is
239 // created by the test harness.
240 rtc::scoped_refptr<webrtc::AudioDeviceModule> secondAudioDeviceModule =
241 webrtc::CreateAudioDeviceModule();
242 XCTAssertNotEqual(secondAudioDeviceModule.get(), nullptr);
243 XCTAssertEqual(0, secondAudioDeviceModule->Init());
244
245 // Start playout for the default ADM but don't wait here. Instead use the
246 // upcoming second stream for that. We set the same expectation on number
247 // of callbacks as for the second stream.
248 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
249 const size_t nBytesPerSample,
250 const size_t nChannels,
251 const uint32_t samplesPerSec,
252 void *audioSamples,
253 size_t &nSamplesOut,
254 int64_t *elapsed_time_ms,
255 int64_t *ntp_time_ms) {
256 nSamplesOut = nSamples;
257 XCTAssertEqual(nSamples, playoutParameters.frames_per_10ms_buffer());
258 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
259 XCTAssertEqual(nChannels, playoutParameters.channels());
260 XCTAssertEqual((int) samplesPerSec, playoutParameters.sample_rate());
261 XCTAssertNotEqual((void*)NULL, audioSamples);
262
263 return 0;
264 });
265
266 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
267 [self startPlayout];
268
269 // Initialize playout for the second ADM. If all is OK, the second ADM shall
270 // reuse the audio session activated when the first ADM started playing.
271 // This call will also ensure that we avoid a problem related to initializing
272 // two different audio unit instances back to back (see webrtc:5166 for
273 // details).
274 XCTAssertEqual(0, secondAudioDeviceModule->InitPlayout());
275 XCTAssertTrue(secondAudioDeviceModule->PlayoutIsInitialized());
276
277 // Start playout for the second ADM and verify that it starts as intended.
278 // Passing this test ensures that initialization of the second audio unit
279 // has been done successfully and that there is no conflict with the already
280 // playing first ADM.
281 XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200282 __block int num_callbacks = 0;
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200283
284 MockAudioTransport mock2;
285 mock2.expectNeedMorePlayData(^int32_t(const size_t nSamples,
286 const size_t nBytesPerSample,
287 const size_t nChannels,
288 const uint32_t samplesPerSec,
289 void *audioSamples,
290 size_t &nSamplesOut,
291 int64_t *elapsed_time_ms,
292 int64_t *ntp_time_ms) {
293 nSamplesOut = nSamples;
294 XCTAssertEqual(nSamples, playoutParameters.frames_per_10ms_buffer());
295 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
296 XCTAssertEqual(nChannels, playoutParameters.channels());
297 XCTAssertEqual((int) samplesPerSec, playoutParameters.sample_rate());
298 XCTAssertNotEqual((void*)NULL, audioSamples);
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200299 if (++num_callbacks == kNumCallbacks) {
300 [playoutExpectation fulfill];
301 }
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200302
303 return 0;
304 });
305
306 XCTAssertEqual(0, secondAudioDeviceModule->RegisterAudioCallback(&mock2));
307 XCTAssertEqual(0, secondAudioDeviceModule->StartPlayout());
308 XCTAssertTrue(secondAudioDeviceModule->Playing());
309 [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200310 [self stopPlayout];
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200311 XCTAssertEqual(0, secondAudioDeviceModule->StopPlayout());
312 XCTAssertFalse(secondAudioDeviceModule->Playing());
313 XCTAssertFalse(secondAudioDeviceModule->PlayoutIsInitialized());
314
315 XCTAssertEqual(0, secondAudioDeviceModule->Terminate());
316}
317
318// Start playout and verify that the native audio layer starts asking for real
319// audio samples to play out using the NeedMorePlayData callback.
320- (void)testStartPlayoutVerifyCallbacks {
321
322 XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200323 __block int num_callbacks = 0;
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200324 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
325 const size_t nBytesPerSample,
326 const size_t nChannels,
327 const uint32_t samplesPerSec,
328 void *audioSamples,
329 size_t &nSamplesOut,
330 int64_t *elapsed_time_ms,
331 int64_t *ntp_time_ms) {
332 nSamplesOut = nSamples;
333 XCTAssertEqual(nSamples, playoutParameters.frames_per_10ms_buffer());
334 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
335 XCTAssertEqual(nChannels, playoutParameters.channels());
336 XCTAssertEqual((int) samplesPerSec, playoutParameters.sample_rate());
337 XCTAssertNotEqual((void*)NULL, audioSamples);
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200338 if (++num_callbacks == kNumCallbacks) {
339 [playoutExpectation fulfill];
340 }
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200341 return 0;
342 });
343
344 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
345
346 [self startPlayout];
347 [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
348 [self stopPlayout];
349}
350
351// Start recording and verify that the native audio layer starts feeding real
352// audio samples via the RecordedDataIsAvailable callback.
353- (void)testStartRecordingVerifyCallbacks {
354 XCTestExpectation *recordExpectation =
355 [self expectationWithDescription:@"RecordedDataIsAvailable"];
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200356 __block int num_callbacks = 0;
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200357
358 mock.expectRecordedDataIsAvailable(^(const void* audioSamples,
359 const size_t nSamples,
360 const size_t nBytesPerSample,
361 const size_t nChannels,
362 const uint32_t samplesPerSec,
363 const uint32_t totalDelayMS,
364 const int32_t clockDrift,
365 const uint32_t currentMicLevel,
366 const bool keyPressed,
367 uint32_t& newMicLevel) {
368 XCTAssertNotEqual((void*)NULL, audioSamples);
369 XCTAssertEqual(nSamples, recordParameters.frames_per_10ms_buffer());
370 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
371 XCTAssertEqual(nChannels, recordParameters.channels());
372 XCTAssertEqual((int) samplesPerSec, recordParameters.sample_rate());
373 XCTAssertEqual(0, clockDrift);
374 XCTAssertEqual(0u, currentMicLevel);
375 XCTAssertFalse(keyPressed);
Kári Tristan Helgasondb543c92018-09-06 13:49:37 +0200376 if (++num_callbacks == kNumCallbacks) {
377 [recordExpectation fulfill];
378 }
Peter Hanspers8d95e3b2018-05-15 10:22:36 +0200379
380 return 0;
381 });
382
383 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
384 [self startRecording];
385 [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
386 [self stopRecording];
387}
388
389// Start playout and recording (full-duplex audio) and verify that audio is
390// active in both directions.
391- (void)testStartPlayoutAndRecordingVerifyCallbacks {
392 XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
393 __block NSUInteger callbackCount = 0;
394
395 XCTestExpectation *recordExpectation =
396 [self expectationWithDescription:@"RecordedDataIsAvailable"];
397 recordExpectation.expectedFulfillmentCount = kNumCallbacks;
398
399 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
400 const size_t nBytesPerSample,
401 const size_t nChannels,
402 const uint32_t samplesPerSec,
403 void *audioSamples,
404 size_t &nSamplesOut,
405 int64_t *elapsed_time_ms,
406 int64_t *ntp_time_ms) {
407 nSamplesOut = nSamples;
408 XCTAssertEqual(nSamples, playoutParameters.frames_per_10ms_buffer());
409 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
410 XCTAssertEqual(nChannels, playoutParameters.channels());
411 XCTAssertEqual((int) samplesPerSec, playoutParameters.sample_rate());
412 XCTAssertNotEqual((void*)NULL, audioSamples);
413 if (callbackCount++ >= kNumCallbacks) {
414 [playoutExpectation fulfill];
415 }
416
417 return 0;
418 });
419
420 mock.expectRecordedDataIsAvailable(^(const void* audioSamples,
421 const size_t nSamples,
422 const size_t nBytesPerSample,
423 const size_t nChannels,
424 const uint32_t samplesPerSec,
425 const uint32_t totalDelayMS,
426 const int32_t clockDrift,
427 const uint32_t currentMicLevel,
428 const bool keyPressed,
429 uint32_t& newMicLevel) {
430 XCTAssertNotEqual((void*)NULL, audioSamples);
431 XCTAssertEqual(nSamples, recordParameters.frames_per_10ms_buffer());
432 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
433 XCTAssertEqual(nChannels, recordParameters.channels());
434 XCTAssertEqual((int) samplesPerSec, recordParameters.sample_rate());
435 XCTAssertEqual(0, clockDrift);
436 XCTAssertEqual(0u, currentMicLevel);
437 XCTAssertFalse(keyPressed);
438 [recordExpectation fulfill];
439
440 return 0;
441 });
442
443 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
444 [self startPlayout];
445 [self startRecording];
446 [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
447 [self stopRecording];
448 [self stopPlayout];
449}
450
451// Start playout and read audio from an external PCM file when the audio layer
452// asks for data to play out. Real audio is played out in this test but it does
453// not contain any explicit verification that the audio quality is perfect.
454- (void)testRunPlayoutWithFileAsSource {
455 XCTAssertEqual(1u, playoutParameters.channels());
456
457 // Using XCTestExpectation to count callbacks is very slow.
458 XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
459 const int expectedCallbackCount = kFilePlayTimeInSec * kNumCallbacksPerSecond;
460 __block int callbackCount = 0;
461
462 NSURL *fileURL = [self fileURLForSampleRate:playoutParameters.sample_rate()];
463 NSInputStream *inputStream = [[NSInputStream alloc] initWithURL:fileURL];
464
465 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
466 const size_t nBytesPerSample,
467 const size_t nChannels,
468 const uint32_t samplesPerSec,
469 void *audioSamples,
470 size_t &nSamplesOut,
471 int64_t *elapsed_time_ms,
472 int64_t *ntp_time_ms) {
473 [inputStream read:(uint8_t *)audioSamples maxLength:nSamples*nBytesPerSample*nChannels];
474 nSamplesOut = nSamples;
475 if (callbackCount++ == expectedCallbackCount) {
476 [playoutExpectation fulfill];
477 }
478
479 return 0;
480 });
481
482 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
483 [self startPlayout];
484 NSTimeInterval waitTimeout = kFilePlayTimeInSec * 2.0;
485 [self waitForExpectationsWithTimeout:waitTimeout handler:nil];
486 [self stopPlayout];
487}
488
489- (void)testDevices {
490 // Device enumeration is not supported. Verify fixed values only.
491 XCTAssertEqual(1, audioDeviceModule->PlayoutDevices());
492 XCTAssertEqual(1, audioDeviceModule->RecordingDevices());
493}
494
495// Start playout and recording and store recorded data in an intermediate FIFO
496// buffer from which the playout side then reads its samples in the same order
497// as they were stored. Under ideal circumstances, a callback sequence would
498// look like: ...+-+-+-+-+-+-+-..., where '+' means 'packet recorded' and '-'
499// means 'packet played'. Under such conditions, the FIFO would only contain
500// one packet on average. However, under more realistic conditions, the size
501// of the FIFO will vary more due to an unbalance between the two sides.
502// This test tries to verify that the device maintains a balanced callback-
503// sequence by running in loopback for ten seconds while measuring the size
504// (max and average) of the FIFO. The size of the FIFO is increased by the
505// recording side and decreased by the playout side.
506// TODO(henrika): tune the final test parameters after running tests on several
507// different devices.
508- (void)testRunPlayoutAndRecordingInFullDuplex {
509 XCTAssertEqual(recordParameters.channels(), playoutParameters.channels());
510 XCTAssertEqual(recordParameters.sample_rate(), playoutParameters.sample_rate());
511
512 XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
513 __block NSUInteger playoutCallbacks = 0;
514 NSUInteger expectedPlayoutCallbacks = kFullDuplexTimeInSec * kNumCallbacksPerSecond;
515
516 // FIFO queue and measurements
517 NSMutableArray *fifoBuffer = [NSMutableArray arrayWithCapacity:20];
518 __block NSUInteger fifoMaxSize = 0;
519 __block NSUInteger fifoTotalWrittenElements = 0;
520 __block NSUInteger fifoWriteCount = 0;
521
522 mock.expectRecordedDataIsAvailable(^(const void* audioSamples,
523 const size_t nSamples,
524 const size_t nBytesPerSample,
525 const size_t nChannels,
526 const uint32_t samplesPerSec,
527 const uint32_t totalDelayMS,
528 const int32_t clockDrift,
529 const uint32_t currentMicLevel,
530 const bool keyPressed,
531 uint32_t& newMicLevel) {
532 if (fifoWriteCount++ < kNumIgnoreFirstCallbacks) {
533 return 0;
534 }
535
536 NSData *data = [NSData dataWithBytes:audioSamples length:nSamples*nBytesPerSample*nChannels];
537 @synchronized(fifoBuffer) {
538 [fifoBuffer addObject:data];
539 fifoMaxSize = MAX(fifoMaxSize, fifoBuffer.count);
540 fifoTotalWrittenElements += fifoBuffer.count;
541 }
542
543 return 0;
544 });
545
546 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
547 const size_t nBytesPerSample,
548 const size_t nChannels,
549 const uint32_t samplesPerSec,
550 void *audioSamples,
551 size_t &nSamplesOut,
552 int64_t *elapsed_time_ms,
553 int64_t *ntp_time_ms) {
554 nSamplesOut = nSamples;
555 NSData *data;
556 @synchronized(fifoBuffer) {
557 data = fifoBuffer.firstObject;
558 if (data) {
559 [fifoBuffer removeObjectAtIndex:0];
560 }
561 }
562
563 if (data) {
564 memcpy(audioSamples, (char*) data.bytes, data.length);
565 } else {
566 memset(audioSamples, 0, nSamples*nBytesPerSample*nChannels);
567 }
568
569 if (playoutCallbacks++ == expectedPlayoutCallbacks) {
570 [playoutExpectation fulfill];
571 }
572 return 0;
573 });
574
575 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
576 [self startRecording];
577 [self startPlayout];
578 NSTimeInterval waitTimeout = kFullDuplexTimeInSec * 2.0;
579 [self waitForExpectationsWithTimeout:waitTimeout handler:nil];
580
581 size_t fifoAverageSize =
582 (fifoTotalWrittenElements == 0)
583 ? 0.0
584 : 0.5 + (double)fifoTotalWrittenElements / (fifoWriteCount - kNumIgnoreFirstCallbacks);
585
586 [self stopPlayout];
587 [self stopRecording];
588 XCTAssertLessThan(fifoAverageSize, 10u);
589 XCTAssertLessThan(fifoMaxSize, 20u);
590}
591
592@end