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