blob: 2d13a9454a9e7708d40d1d050c5dee59c813be3a [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.
206// Failing when running on real iOS devices: bugs.webrtc.org/6889.
207- (void)DISABLED_testStartStopPlayout {
208 [self startPlayout];
209 [self stopPlayout];
210 [self startPlayout];
211 [self stopPlayout];
212}
213
214// Tests that recording can be initiated, started and stopped. No audio callback
215// is registered in this test.
216// Can sometimes fail when running on real devices: bugs.webrtc.org/7888.
217- (void)DISABLED_testStartStopRecording {
218 [self startRecording];
219 [self stopRecording];
220 [self startRecording];
221 [self stopRecording];
222}
223// Verify that calling StopPlayout() will leave us in an uninitialized state
224// which will require a new call to InitPlayout(). This test does not call
225// StartPlayout() while being uninitialized since doing so will hit a
226// RTC_DCHECK.
227- (void)testStopPlayoutRequiresInitToRestart {
228 XCTAssertEqual(0, audioDeviceModule->InitPlayout());
229 XCTAssertEqual(0, audioDeviceModule->StartPlayout());
230 XCTAssertEqual(0, audioDeviceModule->StopPlayout());
231 XCTAssertFalse(audioDeviceModule->PlayoutIsInitialized());
232}
233
234// Verify that we can create two ADMs and start playing on the second ADM.
235// Only the first active instance shall activate an audio session and the
236// last active instance shall deactivate the audio session. The test does not
237// explicitly verify correct audio session calls but instead focuses on
238// ensuring that audio starts for both ADMs.
239// Failing when running on real iOS devices: bugs.webrtc.org/6889.
240- (void)DISABLED_testStartPlayoutOnTwoInstances {
241 // Create and initialize a second/extra ADM instance. The default ADM is
242 // created by the test harness.
243 rtc::scoped_refptr<webrtc::AudioDeviceModule> secondAudioDeviceModule =
244 webrtc::CreateAudioDeviceModule();
245 XCTAssertNotEqual(secondAudioDeviceModule.get(), nullptr);
246 XCTAssertEqual(0, secondAudioDeviceModule->Init());
247
248 // Start playout for the default ADM but don't wait here. Instead use the
249 // upcoming second stream for that. We set the same expectation on number
250 // of callbacks as for the second stream.
251 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
252 const size_t nBytesPerSample,
253 const size_t nChannels,
254 const uint32_t samplesPerSec,
255 void *audioSamples,
256 size_t &nSamplesOut,
257 int64_t *elapsed_time_ms,
258 int64_t *ntp_time_ms) {
259 nSamplesOut = nSamples;
260 XCTAssertEqual(nSamples, playoutParameters.frames_per_10ms_buffer());
261 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
262 XCTAssertEqual(nChannels, playoutParameters.channels());
263 XCTAssertEqual((int) samplesPerSec, playoutParameters.sample_rate());
264 XCTAssertNotEqual((void*)NULL, audioSamples);
265
266 return 0;
267 });
268
269 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
270 [self startPlayout];
271
272 // Initialize playout for the second ADM. If all is OK, the second ADM shall
273 // reuse the audio session activated when the first ADM started playing.
274 // This call will also ensure that we avoid a problem related to initializing
275 // two different audio unit instances back to back (see webrtc:5166 for
276 // details).
277 XCTAssertEqual(0, secondAudioDeviceModule->InitPlayout());
278 XCTAssertTrue(secondAudioDeviceModule->PlayoutIsInitialized());
279
280 // Start playout for the second ADM and verify that it starts as intended.
281 // Passing this test ensures that initialization of the second audio unit
282 // has been done successfully and that there is no conflict with the already
283 // playing first ADM.
284 XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
285 playoutExpectation.expectedFulfillmentCount = kNumCallbacks;
286
287 MockAudioTransport mock2;
288 mock2.expectNeedMorePlayData(^int32_t(const size_t nSamples,
289 const size_t nBytesPerSample,
290 const size_t nChannels,
291 const uint32_t samplesPerSec,
292 void *audioSamples,
293 size_t &nSamplesOut,
294 int64_t *elapsed_time_ms,
295 int64_t *ntp_time_ms) {
296 nSamplesOut = nSamples;
297 XCTAssertEqual(nSamples, playoutParameters.frames_per_10ms_buffer());
298 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
299 XCTAssertEqual(nChannels, playoutParameters.channels());
300 XCTAssertEqual((int) samplesPerSec, playoutParameters.sample_rate());
301 XCTAssertNotEqual((void*)NULL, audioSamples);
302 [playoutExpectation fulfill];
303
304 return 0;
305 });
306
307 XCTAssertEqual(0, secondAudioDeviceModule->RegisterAudioCallback(&mock2));
308 XCTAssertEqual(0, secondAudioDeviceModule->StartPlayout());
309 XCTAssertTrue(secondAudioDeviceModule->Playing());
310 [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
311 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"];
323 playoutExpectation.expectedFulfillmentCount = kNumCallbacks;
324
325 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
326 const size_t nBytesPerSample,
327 const size_t nChannels,
328 const uint32_t samplesPerSec,
329 void *audioSamples,
330 size_t &nSamplesOut,
331 int64_t *elapsed_time_ms,
332 int64_t *ntp_time_ms) {
333 nSamplesOut = nSamples;
334 XCTAssertEqual(nSamples, playoutParameters.frames_per_10ms_buffer());
335 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
336 XCTAssertEqual(nChannels, playoutParameters.channels());
337 XCTAssertEqual((int) samplesPerSec, playoutParameters.sample_rate());
338 XCTAssertNotEqual((void*)NULL, audioSamples);
339 [playoutExpectation fulfill];
340
341 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"];
356 recordExpectation.expectedFulfillmentCount = kNumCallbacks;
357
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);
376 [recordExpectation fulfill];
377
378 return 0;
379 });
380
381 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
382 [self startRecording];
383 [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
384 [self stopRecording];
385}
386
387// Start playout and recording (full-duplex audio) and verify that audio is
388// active in both directions.
389- (void)testStartPlayoutAndRecordingVerifyCallbacks {
390 XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
391 __block NSUInteger callbackCount = 0;
392
393 XCTestExpectation *recordExpectation =
394 [self expectationWithDescription:@"RecordedDataIsAvailable"];
395 recordExpectation.expectedFulfillmentCount = kNumCallbacks;
396
397 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
398 const size_t nBytesPerSample,
399 const size_t nChannels,
400 const uint32_t samplesPerSec,
401 void *audioSamples,
402 size_t &nSamplesOut,
403 int64_t *elapsed_time_ms,
404 int64_t *ntp_time_ms) {
405 nSamplesOut = nSamples;
406 XCTAssertEqual(nSamples, playoutParameters.frames_per_10ms_buffer());
407 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
408 XCTAssertEqual(nChannels, playoutParameters.channels());
409 XCTAssertEqual((int) samplesPerSec, playoutParameters.sample_rate());
410 XCTAssertNotEqual((void*)NULL, audioSamples);
411 if (callbackCount++ >= kNumCallbacks) {
412 [playoutExpectation fulfill];
413 }
414
415 return 0;
416 });
417
418 mock.expectRecordedDataIsAvailable(^(const void* audioSamples,
419 const size_t nSamples,
420 const size_t nBytesPerSample,
421 const size_t nChannels,
422 const uint32_t samplesPerSec,
423 const uint32_t totalDelayMS,
424 const int32_t clockDrift,
425 const uint32_t currentMicLevel,
426 const bool keyPressed,
427 uint32_t& newMicLevel) {
428 XCTAssertNotEqual((void*)NULL, audioSamples);
429 XCTAssertEqual(nSamples, recordParameters.frames_per_10ms_buffer());
430 XCTAssertEqual(nBytesPerSample, kBytesPerSample);
431 XCTAssertEqual(nChannels, recordParameters.channels());
432 XCTAssertEqual((int) samplesPerSec, recordParameters.sample_rate());
433 XCTAssertEqual(0, clockDrift);
434 XCTAssertEqual(0u, currentMicLevel);
435 XCTAssertFalse(keyPressed);
436 [recordExpectation fulfill];
437
438 return 0;
439 });
440
441 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
442 [self startPlayout];
443 [self startRecording];
444 [self waitForExpectationsWithTimeout:kTestTimeOutInSec handler:nil];
445 [self stopRecording];
446 [self stopPlayout];
447}
448
449// Start playout and read audio from an external PCM file when the audio layer
450// asks for data to play out. Real audio is played out in this test but it does
451// not contain any explicit verification that the audio quality is perfect.
452- (void)testRunPlayoutWithFileAsSource {
453 XCTAssertEqual(1u, playoutParameters.channels());
454
455 // Using XCTestExpectation to count callbacks is very slow.
456 XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
457 const int expectedCallbackCount = kFilePlayTimeInSec * kNumCallbacksPerSecond;
458 __block int callbackCount = 0;
459
460 NSURL *fileURL = [self fileURLForSampleRate:playoutParameters.sample_rate()];
461 NSInputStream *inputStream = [[NSInputStream alloc] initWithURL:fileURL];
462
463 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
464 const size_t nBytesPerSample,
465 const size_t nChannels,
466 const uint32_t samplesPerSec,
467 void *audioSamples,
468 size_t &nSamplesOut,
469 int64_t *elapsed_time_ms,
470 int64_t *ntp_time_ms) {
471 [inputStream read:(uint8_t *)audioSamples maxLength:nSamples*nBytesPerSample*nChannels];
472 nSamplesOut = nSamples;
473 if (callbackCount++ == expectedCallbackCount) {
474 [playoutExpectation fulfill];
475 }
476
477 return 0;
478 });
479
480 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
481 [self startPlayout];
482 NSTimeInterval waitTimeout = kFilePlayTimeInSec * 2.0;
483 [self waitForExpectationsWithTimeout:waitTimeout handler:nil];
484 [self stopPlayout];
485}
486
487- (void)testDevices {
488 // Device enumeration is not supported. Verify fixed values only.
489 XCTAssertEqual(1, audioDeviceModule->PlayoutDevices());
490 XCTAssertEqual(1, audioDeviceModule->RecordingDevices());
491}
492
493// Start playout and recording and store recorded data in an intermediate FIFO
494// buffer from which the playout side then reads its samples in the same order
495// as they were stored. Under ideal circumstances, a callback sequence would
496// look like: ...+-+-+-+-+-+-+-..., where '+' means 'packet recorded' and '-'
497// means 'packet played'. Under such conditions, the FIFO would only contain
498// one packet on average. However, under more realistic conditions, the size
499// of the FIFO will vary more due to an unbalance between the two sides.
500// This test tries to verify that the device maintains a balanced callback-
501// sequence by running in loopback for ten seconds while measuring the size
502// (max and average) of the FIFO. The size of the FIFO is increased by the
503// recording side and decreased by the playout side.
504// TODO(henrika): tune the final test parameters after running tests on several
505// different devices.
506- (void)testRunPlayoutAndRecordingInFullDuplex {
507 XCTAssertEqual(recordParameters.channels(), playoutParameters.channels());
508 XCTAssertEqual(recordParameters.sample_rate(), playoutParameters.sample_rate());
509
510 XCTestExpectation *playoutExpectation = [self expectationWithDescription:@"NeedMorePlayoutData"];
511 __block NSUInteger playoutCallbacks = 0;
512 NSUInteger expectedPlayoutCallbacks = kFullDuplexTimeInSec * kNumCallbacksPerSecond;
513
514 // FIFO queue and measurements
515 NSMutableArray *fifoBuffer = [NSMutableArray arrayWithCapacity:20];
516 __block NSUInteger fifoMaxSize = 0;
517 __block NSUInteger fifoTotalWrittenElements = 0;
518 __block NSUInteger fifoWriteCount = 0;
519
520 mock.expectRecordedDataIsAvailable(^(const void* audioSamples,
521 const size_t nSamples,
522 const size_t nBytesPerSample,
523 const size_t nChannels,
524 const uint32_t samplesPerSec,
525 const uint32_t totalDelayMS,
526 const int32_t clockDrift,
527 const uint32_t currentMicLevel,
528 const bool keyPressed,
529 uint32_t& newMicLevel) {
530 if (fifoWriteCount++ < kNumIgnoreFirstCallbacks) {
531 return 0;
532 }
533
534 NSData *data = [NSData dataWithBytes:audioSamples length:nSamples*nBytesPerSample*nChannels];
535 @synchronized(fifoBuffer) {
536 [fifoBuffer addObject:data];
537 fifoMaxSize = MAX(fifoMaxSize, fifoBuffer.count);
538 fifoTotalWrittenElements += fifoBuffer.count;
539 }
540
541 return 0;
542 });
543
544 mock.expectNeedMorePlayData(^int32_t(const size_t nSamples,
545 const size_t nBytesPerSample,
546 const size_t nChannels,
547 const uint32_t samplesPerSec,
548 void *audioSamples,
549 size_t &nSamplesOut,
550 int64_t *elapsed_time_ms,
551 int64_t *ntp_time_ms) {
552 nSamplesOut = nSamples;
553 NSData *data;
554 @synchronized(fifoBuffer) {
555 data = fifoBuffer.firstObject;
556 if (data) {
557 [fifoBuffer removeObjectAtIndex:0];
558 }
559 }
560
561 if (data) {
562 memcpy(audioSamples, (char*) data.bytes, data.length);
563 } else {
564 memset(audioSamples, 0, nSamples*nBytesPerSample*nChannels);
565 }
566
567 if (playoutCallbacks++ == expectedPlayoutCallbacks) {
568 [playoutExpectation fulfill];
569 }
570 return 0;
571 });
572
573 XCTAssertEqual(0, audioDeviceModule->RegisterAudioCallback(&mock));
574 [self startRecording];
575 [self startPlayout];
576 NSTimeInterval waitTimeout = kFullDuplexTimeInSec * 2.0;
577 [self waitForExpectationsWithTimeout:waitTimeout handler:nil];
578
579 size_t fifoAverageSize =
580 (fifoTotalWrittenElements == 0)
581 ? 0.0
582 : 0.5 + (double)fifoTotalWrittenElements / (fifoWriteCount - kNumIgnoreFirstCallbacks);
583
584 [self stopPlayout];
585 [self stopRecording];
586 XCTAssertLessThan(fifoAverageSize, 10u);
587 XCTAssertLessThan(fifoMaxSize, 20u);
588}
589
590@end