/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include #include #include #include #include #include #include "APITest.h" #include "thread_wrapper.h" #include "event_wrapper.h" #include "tick_util.h" #include "trace.h" #include "utility.h" #include "common_types.h" #include "engine_configurations.h" #define TEST_DURATION_SEC 600 #define NUMBER_OF_SENDER_TESTS 6 #define MAX_FILE_NAME_LENGTH_BYTE 500 #define CHECK_THREAD_NULLITY(myThread, S) if(myThread != NULL){unsigned int i; (myThread)->Start(i);}else{throw S; exit(1);} using namespace webrtc; void APITest::Wait(WebRtc_UWord32 waitLengthMs) { if(_randomTest) { return; } else { EventWrapper* myEvent = EventWrapper::Create(); myEvent->Wait(waitLengthMs); delete myEvent; return; } } APITest::APITest(): _acmA(NULL), _acmB(NULL), _channel_A2B(NULL), _channel_B2A(NULL), _writeToFile(true), _pullEventA(NULL), _pushEventA(NULL), _processEventA(NULL), _apiEventA(NULL), _pullEventB(NULL), _pushEventB(NULL), _processEventB(NULL), _apiEventB(NULL), _codecCntrA(0), _codecCntrB(0), _testCntrA(1), _testCntrB(1), _thereIsEncoderA(false), _thereIsEncoderB(false), _thereIsDecoderA(false), _thereIsDecoderB(false), _sendVADA(false), _sendDTXA(false), _sendVADModeA(VADNormal), _sendVADB(false), _sendDTXB(false), _sendVADModeB(VADNormal), _minDelayA(0), _minDelayB(0), _dotPositionA(0), _dotMoveDirectionA(1), _dotPositionB(39), _dotMoveDirectionB(-1), _dtmfCallback(NULL), _vadCallbackA(NULL), _vadCallbackB(NULL), _apiTestRWLock(*RWLockWrapper::CreateRWLock()), _randomTest(false), _testNumA(0), _testNumB(1) { int n; for( n = 0; n < 32; n++) { _payloadUsed[n] = false; } for(n = 0; n < 3; n++) { _receiveVADActivityA[n] = 0; _receiveVADActivityB[n] = 0; } _movingDot[40] = '\0'; for(int n = 0; n <40; n++) { _movingDot[n] = ' '; } } APITest::~APITest() { DESTROY_ACM(_acmA); DESTROY_ACM(_acmB); DELETE_POINTER(_channel_A2B); DELETE_POINTER(_channel_B2A); DELETE_POINTER(_pushEventA); DELETE_POINTER(_pullEventA); DELETE_POINTER(_processEventA); DELETE_POINTER(_apiEventA); DELETE_POINTER(_pushEventB); DELETE_POINTER(_pullEventB); DELETE_POINTER(_processEventB); DELETE_POINTER(_apiEventB); _inFileA.Close(); _outFileA.Close(); _inFileB.Close(); _outFileB.Close(); DELETE_POINTER(_dtmfCallback); DELETE_POINTER(_vadCallbackA); DELETE_POINTER(_vadCallbackB); delete &_apiTestRWLock; } //WebRtc_Word16 //APITest::SetInFile(char* fileName, WebRtc_UWord16 frequencyHz) //{ // return _inFile.Open(fileName, frequencyHz, "rb"); //} // //WebRtc_Word16 //APITest::SetOutFile(char* fileName, WebRtc_UWord16 frequencyHz) //{ // return _outFile.Open(fileName, frequencyHz, "wb"); //} WebRtc_Word16 APITest::SetUp() { _acmA = AudioCodingModule::Create(1); _acmB = AudioCodingModule::Create(2); CodecInst dummyCodec; int lastPayloadType = 0; WebRtc_Word16 numCodecs = _acmA->NumberOfCodecs(); for(WebRtc_UWord8 n = 0; n < numCodecs; n++) { AudioCodingModule::Codec(n, dummyCodec); if((STR_CASE_CMP(dummyCodec.plname, "CN") == 0) && (dummyCodec.plfreq == 32000)) { continue; } printf("Register Receive Codec %s ", dummyCodec.plname); if((n != 0) && !FixedPayloadTypeCodec(dummyCodec.plname)) { // Check registration with an already occupied payload type int currentPayloadType = dummyCodec.pltype; dummyCodec.pltype = 97; //lastPayloadType; CHECK_ERROR(_acmB->RegisterReceiveCodec(dummyCodec)); dummyCodec.pltype = currentPayloadType; } if((n < numCodecs - 1) && !FixedPayloadTypeCodec(dummyCodec.plname)) { // test if re-registration works; CodecInst nextCodec; int currentPayloadType = dummyCodec.pltype; AudioCodingModule::Codec(n + 1, nextCodec); dummyCodec.pltype = nextCodec.pltype; if(!FixedPayloadTypeCodec(nextCodec.plname)) { _acmB->RegisterReceiveCodec(dummyCodec); } dummyCodec.pltype = currentPayloadType; } if((n < numCodecs - 1) && !FixedPayloadTypeCodec(dummyCodec.plname)) { // test if un-registration works; CodecInst nextCodec; int currentPayloadType = dummyCodec.pltype; AudioCodingModule::Codec(n + 1, nextCodec); nextCodec.pltype = dummyCodec.pltype; if(!FixedPayloadTypeCodec(nextCodec.plname)) { CHECK_ERROR_MT(_acmA->RegisterReceiveCodec(nextCodec)); CHECK_ERROR_MT(_acmA->UnregisterReceiveCodec(nextCodec.pltype)); } } CHECK_ERROR_MT(_acmA->RegisterReceiveCodec(dummyCodec)); printf(" side A done!"); CHECK_ERROR_MT(_acmB->RegisterReceiveCodec(dummyCodec)); printf(" side B done!\n"); if(!strcmp(dummyCodec.plname, "CN")) { CHECK_ERROR_MT(_acmA->RegisterSendCodec(dummyCodec)); CHECK_ERROR_MT(_acmB->RegisterSendCodec(dummyCodec)); } lastPayloadType = dummyCodec.pltype; if((lastPayloadType >= 96) && (lastPayloadType <= 127)) { _payloadUsed[lastPayloadType - 96] = true; } } _thereIsDecoderA = true; _thereIsDecoderB = true; // Register Send Codec AudioCodingModule::Codec((WebRtc_UWord8)_codecCntrA, dummyCodec); CHECK_ERROR_MT(_acmA->RegisterSendCodec(dummyCodec)); _thereIsEncoderA = true; // AudioCodingModule::Codec((WebRtc_UWord8)_codecCntrB, dummyCodec); CHECK_ERROR_MT(_acmB->RegisterSendCodec(dummyCodec)); _thereIsEncoderB = true; char fileName[500]; WebRtc_UWord16 frequencyHz; printf("\n\nAPI Test\n"); printf("========\n"); printf("Hit enter to accept the default values indicated in []\n\n"); //--- Input A strcpy(fileName, "./modules/audio_coding/main/test/testfile32kHz.pcm"); frequencyHz = 32000; printf("Enter input file at side A [%s]: ", fileName); PCMFile::ChooseFile(fileName, 499, &frequencyHz); _inFileA.Open(fileName, frequencyHz, "rb", true); //--- Output A strcpy(fileName, "./modules/audio_coding/main/test/outA.pcm"); printf("Enter output file at side A [%s]: ", fileName); PCMFile::ChooseFile(fileName, 499, &frequencyHz); _outFileA.Open(fileName, frequencyHz, "wb"); //--- Input B strcpy(fileName, "./modules/audio_coding/main/test/testfile32kHz.pcm"); printf("\n\nEnter input file at side B [%s]: ", fileName); PCMFile::ChooseFile(fileName, 499, &frequencyHz); _inFileB.Open(fileName, frequencyHz, "rb", true); //--- Output B strcpy(fileName, "./modules/audio_coding/main/test/outB.pcm"); printf("Enter output file at side B [%s]: ", fileName); PCMFile::ChooseFile(fileName, 499, &frequencyHz); _outFileB.Open(fileName, frequencyHz, "wb"); //--- Set A-to-B channel _channel_A2B = new Channel(2); CHECK_ERROR_MT(_acmA->RegisterTransportCallback(_channel_A2B)); _channel_A2B->RegisterReceiverACM(_acmB); //--- Set B-to-A channel _channel_B2A = new Channel(1); CHECK_ERROR_MT(_acmB->RegisterTransportCallback(_channel_B2A)); _channel_B2A->RegisterReceiverACM(_acmA); //--- EVENT TIMERS // A _pullEventA = EventWrapper::Create(); _pushEventA = EventWrapper::Create(); _processEventA = EventWrapper::Create(); _apiEventA = EventWrapper::Create(); // B _pullEventB = EventWrapper::Create(); _pushEventB = EventWrapper::Create(); _processEventB = EventWrapper::Create(); _apiEventB = EventWrapper::Create(); //--- I/O params // A _outFreqHzA = _outFileA.SamplingFrequency(); // B _outFreqHzB = _outFileB.SamplingFrequency(); //Trace::SetEncryptedTraceFile("ACMAPITestEncrypted.txt"); char print[11]; printf("\nRandom Test (y/n)?"); fgets(print, 10, stdin); print[10] = '\0'; if(strstr(print, "y") != NULL) { _randomTest = true; _verbose = false; _writeToFile = false; Trace::CreateTrace(); Trace::SetTraceFile("ACMAPITest.txt"); //freopen("APITest_log.txt", "w", stdout); } else { Trace::CreateTrace(); Trace::SetTraceFile("ACMAPITest.txt", true); _randomTest = false; printf("\nPrint Tests (y/n)? "); fgets(print, 10, stdin); print[10] = '\0'; if(strstr(print, "y") == NULL) { freopen("APITest_log.txt", "w", stdout); _verbose = false; } } #ifdef WEBRTC_DTMF_DETECTION _dtmfCallback = new DTMFDetector; #endif _vadCallbackA = new VADCallback; _vadCallbackB = new VADCallback; return 0; } bool APITest::PushAudioThreadA(void* obj) { return static_cast(obj)->PushAudioRunA(); } bool APITest::PushAudioThreadB(void* obj) { return static_cast(obj)->PushAudioRunB(); } bool APITest::PullAudioThreadA(void* obj) { return static_cast(obj)->PullAudioRunA(); } bool APITest::PullAudioThreadB(void* obj) { return static_cast(obj)->PullAudioRunB(); } bool APITest::ProcessThreadA(void* obj) { return static_cast(obj)->ProcessRunA(); } bool APITest::ProcessThreadB(void* obj) { return static_cast(obj)->ProcessRunB(); } bool APITest::APIThreadA(void* obj) { return static_cast(obj)->APIRunA(); } bool APITest::APIThreadB(void* obj) { return static_cast(obj)->APIRunB(); } bool APITest::PullAudioRunA() { _pullEventA->Wait(100); AudioFrame audioFrame; if(_acmA->PlayoutData10Ms(_outFreqHzA, audioFrame) < 0) { bool thereIsDecoder; { ReadLockScoped rl(_apiTestRWLock); thereIsDecoder = _thereIsDecoderA; } if(thereIsDecoder) { fprintf(stderr, "\n>>>>>> cannot pull audio A <<<<<<<< \n"); } } else { if(_writeToFile) { _outFileA.Write10MsData(audioFrame); } _receiveVADActivityA[(int)audioFrame._vadActivity]++; } return true; } bool APITest::PullAudioRunB() { _pullEventB->Wait(100); AudioFrame audioFrame; if(_acmB->PlayoutData10Ms(_outFreqHzB, audioFrame) < 0) { bool thereIsDecoder; { ReadLockScoped rl(_apiTestRWLock); thereIsDecoder = _thereIsDecoderB; } if(thereIsDecoder) { fprintf(stderr, "\n>>>>>> cannot pull audio B <<<<<<<< \n"); fprintf(stderr, "%d %d\n", _testNumA, _testNumB); } } else { if(_writeToFile) { _outFileB.Write10MsData(audioFrame); } _receiveVADActivityB[(int)audioFrame._vadActivity]++; } return true; } bool APITest::PushAudioRunA() { _pushEventA->Wait(100); AudioFrame audioFrame; _inFileA.Read10MsData(audioFrame); if(_acmA->Add10MsData(audioFrame) < 0) { bool thereIsEncoder; { ReadLockScoped rl(_apiTestRWLock); thereIsEncoder = _thereIsEncoderA; } if(thereIsEncoder) { fprintf(stderr, "\n>>>> add10MsData at A failed <<<<\n"); } } return true; } bool APITest::PushAudioRunB() { _pushEventB->Wait(100); AudioFrame audioFrame; _inFileB.Read10MsData(audioFrame); if(_acmB->Add10MsData(audioFrame) < 0) { bool thereIsEncoder; { ReadLockScoped rl(_apiTestRWLock); thereIsEncoder = _thereIsEncoderB; } if(thereIsEncoder) { fprintf(stderr, "\n>>>> cannot add audio to B <<<<"); } } return true; } bool APITest::ProcessRunA() { _processEventA->Wait(100); if(_acmA->Process() < 0) { // do not print error message if there is no encoder bool thereIsEncoder; { ReadLockScoped rl(_apiTestRWLock); thereIsEncoder = _thereIsEncoderA; } if(thereIsEncoder) { fprintf(stderr, "\n>>>>> Process Failed at A <<<<<\n"); } } return true; } bool APITest::ProcessRunB() { _processEventB->Wait(100); if(_acmB->Process() < 0) { bool thereIsEncoder; { ReadLockScoped rl(_apiTestRWLock); thereIsEncoder = _thereIsEncoderB; } if(thereIsEncoder) { fprintf(stderr, "\n>>>>> Process Failed at B <<<<<\n"); } } return true; } /*/ * * In side A we test the APIs which are related to sender Side. * /*/ void APITest::RunTest(char thread) { int testNum; { WriteLockScoped cs(_apiTestRWLock); if(thread == 'A') { _testNumA = (_testNumB + 1 + (rand() % 6)) % 7; testNum = _testNumA; _movingDot[_dotPositionA] = ' '; if(_dotPositionA == 0) { _dotMoveDirectionA = 1; } if(_dotPositionA == 19) { _dotMoveDirectionA = -1; } _dotPositionA += _dotMoveDirectionA; _movingDot[_dotPositionA] = (_dotMoveDirectionA > 0)? '>':'<'; } else { _testNumB = (_testNumA + 1 + (rand() % 6)) % 7; testNum = _testNumB; _movingDot[_dotPositionB] = ' '; if(_dotPositionB == 20) { _dotMoveDirectionB = 1; } if(_dotPositionB == 39) { _dotMoveDirectionB = -1; } _dotPositionB += _dotMoveDirectionB; _movingDot[_dotPositionB] = (_dotMoveDirectionB > 0)? '>':'<'; } //fprintf(stderr, "%c: %d \n", thread, testNum); //fflush(stderr); } switch(testNum) { case 0: CurrentCodec('A'); ChangeCodec('A'); break; case 1: TestPlayout('B'); break; case 2: if(!_randomTest) { fprintf(stdout, "\nTesting Delay ...\n"); } TestDelay('A'); break; case 3: TestSendVAD('A'); break; case 4: TestRegisteration('A'); break; case 5: TestReceiverVAD('A'); break; case 6: #ifdef WEBRTC_DTMF_DETECTION LookForDTMF('A'); #endif break; default: fprintf(stderr, "Wrong Test Number\n"); getchar(); exit(1); } } bool APITest::APIRunA() { _apiEventA->Wait(50); bool randomTest; { ReadLockScoped rl(_apiTestRWLock); randomTest = _randomTest; } if(randomTest) { RunTest('A'); } else { CurrentCodec('A'); ChangeCodec('A'); TestPlayout('B'); if(_codecCntrA == 0) { fprintf(stdout, "\nTesting Delay ...\n"); TestDelay('A'); } // VAD TEST TestSendVAD('A'); TestRegisteration('A'); TestReceiverVAD('A'); #ifdef WEBRTC_DTMF_DETECTION LookForDTMF('A'); #endif } return true; } bool APITest::APIRunB() { _apiEventB->Wait(50); bool randomTest; { ReadLockScoped rl(_apiTestRWLock); randomTest = _randomTest; } //_apiEventB->Wait(2000); if(randomTest) { RunTest('B'); } return true; } void APITest::Perform() { SetUp(); //--- THREADS // A // PUSH ThreadWrapper* myPushAudioThreadA = ThreadWrapper::CreateThread(PushAudioThreadA, this, kNormalPriority, "PushAudioThreadA"); CHECK_THREAD_NULLITY(myPushAudioThreadA, "Unable to start A::PUSH thread"); // PULL ThreadWrapper* myPullAudioThreadA = ThreadWrapper::CreateThread(PullAudioThreadA, this, kNormalPriority, "PullAudioThreadA"); CHECK_THREAD_NULLITY(myPullAudioThreadA, "Unable to start A::PULL thread"); // Process ThreadWrapper* myProcessThreadA = ThreadWrapper::CreateThread(ProcessThreadA, this, kNormalPriority, "ProcessThreadA"); CHECK_THREAD_NULLITY(myProcessThreadA, "Unable to start A::Process thread"); // API ThreadWrapper* myAPIThreadA = ThreadWrapper::CreateThread(APIThreadA, this, kNormalPriority, "APIThreadA"); CHECK_THREAD_NULLITY(myAPIThreadA, "Unable to start A::API thread"); // B // PUSH ThreadWrapper* myPushAudioThreadB = ThreadWrapper::CreateThread(PushAudioThreadB, this, kNormalPriority, "PushAudioThreadB"); CHECK_THREAD_NULLITY(myPushAudioThreadB, "Unable to start B::PUSH thread"); // PULL ThreadWrapper* myPullAudioThreadB = ThreadWrapper::CreateThread(PullAudioThreadB, this, kNormalPriority, "PullAudioThreadB"); CHECK_THREAD_NULLITY(myPullAudioThreadB, "Unable to start B::PULL thread"); // Process ThreadWrapper* myProcessThreadB = ThreadWrapper::CreateThread(ProcessThreadB, this, kNormalPriority, "ProcessThreadB"); CHECK_THREAD_NULLITY(myProcessThreadB, "Unable to start B::Process thread"); // API ThreadWrapper* myAPIThreadB = ThreadWrapper::CreateThread(APIThreadB, this, kNormalPriority, "APIThreadB"); CHECK_THREAD_NULLITY(myAPIThreadB, "Unable to start B::API thread"); //_apiEventA->StartTimer(true, 5000); //_apiEventB->StartTimer(true, 5000); _processEventA->StartTimer(true, 10); _processEventB->StartTimer(true, 10); _pullEventA->StartTimer(true, 10); _pullEventB->StartTimer(true, 10); _pushEventA->StartTimer(true, 10); _pushEventB->StartTimer(true, 10); // Keep main thread waiting for sender/receiver // threads to complete EventWrapper* completeEvent = EventWrapper::Create(); WebRtc_UWord64 startTime = TickTime::MillisecondTimestamp(); WebRtc_UWord64 currentTime; do { { //ReadLockScoped rl(_apiTestRWLock); //fprintf(stderr, "\r%s", _movingDot); } //fflush(stderr); completeEvent->Wait(50); currentTime = TickTime::MillisecondTimestamp(); } while((currentTime - startTime) < 120000); // Run test in 2 minutes (120000 ms) //completeEvent->Wait(0xFFFFFFFF);//(unsigned long)((unsigned long)TEST_DURATION_SEC * (unsigned long)1000)); delete completeEvent; myPushAudioThreadA->Stop(); myPullAudioThreadA->Stop(); myProcessThreadA->Stop(); myAPIThreadA->Stop(); delete myPushAudioThreadA; delete myPullAudioThreadA; delete myProcessThreadA; delete myAPIThreadA; myPushAudioThreadB->Stop(); myPullAudioThreadB->Stop(); myProcessThreadB->Stop(); myAPIThreadB->Stop(); delete myPushAudioThreadB; delete myPullAudioThreadB; delete myProcessThreadB; delete myAPIThreadB; } void APITest::CheckVADStatus(char side) { bool dtxEnabled; bool vadEnabled; ACMVADMode vadMode; EventWrapper* myEvent = EventWrapper::Create(); if(side == 'A') { _acmA->VAD(dtxEnabled, vadEnabled, vadMode); _acmA->RegisterVADCallback(NULL); _vadCallbackA->Reset(); _acmA->RegisterVADCallback(_vadCallbackA); if(!_randomTest) { if(_verbose) { fprintf(stdout, "DTX %3s, VAD %3s, Mode %d", dtxEnabled? "ON":"OFF", vadEnabled? "ON":"OFF", (int)vadMode); Wait(5000); fprintf(stdout, " => bit-rate %3.0f kbps\n", _channel_A2B->BitRate()); } else { Wait(5000); fprintf(stdout, "DTX %3s, VAD %3s, Mode %d => bit-rate %3.0f kbps\n", dtxEnabled? "ON":"OFF", vadEnabled? "ON":"OFF", (int)vadMode, _channel_A2B->BitRate()); } _vadCallbackA->PrintFrameTypes(); } if(dtxEnabled != _sendDTXA) { fprintf(stderr, ">>> Error Enabling DTX <<<\n"); } if((vadEnabled != _sendVADA) && (!dtxEnabled)) { fprintf(stderr, ">>> Error Enabling VAD <<<\n"); } if((vadMode != _sendVADModeA) && vadEnabled) { fprintf(stderr, ">>> Error setting VAD-mode <<<\n"); } } else { _acmB->VAD(dtxEnabled, vadEnabled, vadMode); _acmB->RegisterVADCallback(NULL); _vadCallbackB->Reset(); _acmB->RegisterVADCallback(_vadCallbackB); if(!_randomTest) { if(_verbose) { fprintf(stdout, "DTX %3s, VAD %3s, Mode %d", dtxEnabled? "ON":"OFF", vadEnabled? "ON":"OFF", (int)vadMode); Wait(5000); fprintf(stdout, " => bit-rate %3.0f kbps\n", _channel_B2A->BitRate()); } else { Wait(5000); fprintf(stdout, "DTX %3s, VAD %3s, Mode %d => bit-rate %3.0f kbps\n", dtxEnabled? "ON":"OFF", vadEnabled? "ON":"OFF", (int)vadMode, _channel_B2A->BitRate()); } _vadCallbackB->PrintFrameTypes(); } if(dtxEnabled != _sendDTXB) { fprintf(stderr, ">>> Error Enabling DTX <<<\n"); } if((vadEnabled != _sendVADB) && (!dtxEnabled)) { fprintf(stderr, ">>> Error Enabling VAD <<<\n"); } if((vadMode != _sendVADModeB) && vadEnabled) { fprintf(stderr, ">>> Error setting VAD-mode <<<\n"); } } } // Set Min delay, get delay, playout timestamp void APITest::TestDelay(char side) { AudioCodingModule* myACM; Channel* myChannel; WebRtc_Word32* myMinDelay; EventWrapper* myEvent = EventWrapper::Create(); WebRtc_UWord32 inTimestamp = 0; WebRtc_UWord32 outTimestamp = 0; double estimDelay = 0; WebRtc_UWord16 delay = 0; double averageEstimDelay = 0; double averageDelay = 0; CircularBuffer estimDelayCB(100); CircularBuffer delayCB(100); estimDelayCB.SetArithMean(true); delayCB.SetArithMean(true); if(side == 'A') { myACM = _acmA; myChannel = _channel_B2A; myMinDelay = &_minDelayA; } else { myACM = _acmB; myChannel = _channel_A2B; myMinDelay = &_minDelayB; } CHECK_ERROR_MT(myACM->SetMinimumPlayoutDelay(*myMinDelay)); inTimestamp = myChannel->LastInTimestamp(); CHECK_ERROR_MT(myACM->PlayoutTimestamp(outTimestamp)); CHECK_ERROR_MT(myACM->Delay(delay)); if(!_randomTest) { myEvent->StartTimer(true, 30); int n = 0; int settlePoint = 5000; while(n < settlePoint + 400) { myEvent->Wait(1000); inTimestamp = myChannel->LastInTimestamp(); CHECK_ERROR_MT(myACM->PlayoutTimestamp(outTimestamp)); //std::cout << outTimestamp << std::endl << std::flush; estimDelay = (double)((WebRtc_UWord32)(inTimestamp - outTimestamp)) / ((double)myACM->ReceiveFrequency() / 1000.0); estimDelayCB.Update(estimDelay); estimDelayCB.ArithMean(averageEstimDelay); //printf("\n %6.1f \n", estimDelay); //std::cout << " " << std::flush; CHECK_ERROR_MT(myACM->Delay(delay)); delayCB.Update(delay); delayCB.ArithMean(averageDelay); if(_verbose) { fprintf(stdout, "\rExpected: %4d, retreived: %6.1f, measured: %6.1f", *myMinDelay, averageDelay, averageEstimDelay); std::cout << " " << std::flush; } if((averageDelay > *myMinDelay) && (n < settlePoint)) { settlePoint = n; } n++; } myEvent->StopTimer(); } if((!_verbose) && (!_randomTest)) { fprintf(stdout, "\nExpected: %4d, retreived: %6.1f, measured: %6.1f", *myMinDelay, averageDelay, averageEstimDelay); } *myMinDelay = (rand() % 1000) + 1; ACMJitterStatistics jitterStat; ACMNetworkStatistics networkStat; CHECK_ERROR_MT(myACM->JitterStatistics(jitterStat)); CHECK_ERROR_MT(myACM->NetworkStatistics(networkStat)); if(!_randomTest) { fprintf(stdout, "\n\nJitter Statistics at Side %c\n", side); fprintf(stdout, "--------------------------------------\n"); fprintf(stdout, "buffer-size............. %d\n", networkStat.currentBufferSize); fprintf(stdout, "Preferred buffer-size... %d\n", networkStat.preferredBufferSize); fprintf(stdout, "packet-size rate........ %d\n", networkStat.currentPacketLossRate); fprintf(stdout, "discard rate............ %d\n", networkStat.currentDiscardRate); fprintf(stdout, "expand rate............. %d\n", networkStat.currentExpandRate); fprintf(stdout, "Preemptive rate......... %d\n", networkStat.currentPreemptiveRate); fprintf(stdout, "Accelerate rate......... %d\n", networkStat.currentAccelerateRate); fprintf(stdout, "\n\nJitter Statistics at side %c\n", side); fprintf(stdout, "--------------------------------------\n"); fprintf(stdout, "Jitter buffer min size....... %d\n", jitterStat.jbMinSize); fprintf(stdout, "Jitter buffer Max size....... %d\n", jitterStat.jbMaxSize); fprintf(stdout, "Jitter buffer Average size... %d\n", jitterStat.jbAvgSize); fprintf(stdout, "Change Count................. %d ms\n", jitterStat.jbChangeCount); fprintf(stdout, "Late Loss.................... %d ms\n", jitterStat.lateLossMs); fprintf(stdout, "Accelerate................... %d ms\n", jitterStat.accelerateMs); fprintf(stdout, "Flushed...................... %d ms\n", jitterStat.flushedMs); fprintf(stdout, "Generated Silence............ %d ms\n", jitterStat.generatedSilentMs); fprintf(stdout, "Interpolated Voice........... %d ms\n", jitterStat.interpolatedVoiceMs); fprintf(stdout, "Interpolated Silence......... %d ms\n", jitterStat.interpolatedSilentMs); fprintf(stdout, "No tiny expand............... %d\n", jitterStat.numExpandTiny); fprintf(stdout, "No small expand.............. %d\n", jitterStat.numExpandSmall); fprintf(stdout, "No Medium expand............. %d\n", jitterStat.numExpandMedium); fprintf(stdout, "No long expand............... %d\n", jitterStat.numExpandLong); fprintf(stdout, "longest expand............... %d ms\n", jitterStat.longestExpandDurationMs); fprintf(stdout, "No IAT 500................... %d ms\n", jitterStat.countIAT500ms); fprintf(stdout, "No IAT 1000.................. %d ms\n", jitterStat.countIAT1000ms); fprintf(stdout, "No IAT 2000.................. %d ms\n", jitterStat.countIAT2000ms); fprintf(stdout, "longest IAT.................. %d ms\n", jitterStat.longestIATms); fprintf(stdout, "Min packet delay............. %d ms\n", jitterStat.minPacketDelayMs); fprintf(stdout, "Max packet delay............. %d ms\n", jitterStat.maxPacketDelayMs); fprintf(stdout, "Average packet delay......... %d ms\n", jitterStat.avgPacketDelayMs); } CHECK_ERROR_MT(myACM->SetMinimumPlayoutDelay(*myMinDelay)); if(!_randomTest) { myEvent->Wait(500); fprintf(stdout, "\n"); fprintf(stdout, "\n"); } delete myEvent; } // Unregister a codec & register again. void APITest::TestRegisteration(char sendSide) { AudioCodingModule* sendACM; AudioCodingModule* receiveACM; bool* thereIsDecoder; EventWrapper* myEvent = EventWrapper::Create(); if(!_randomTest) { fprintf(stdout, "\n\n"); fprintf(stdout, "---------------------------------------------------------\n"); fprintf(stdout, " Unregister/register Receive Codec\n"); fprintf(stdout, "---------------------------------------------------------\n"); } switch(sendSide) { case 'A': { sendACM = _acmA; receiveACM = _acmB; thereIsDecoder = &_thereIsDecoderB; break; } case 'B': { sendACM = _acmB; receiveACM = _acmA; thereIsDecoder = &_thereIsDecoderA; break; } default: fprintf(stderr, "Invalid sender-side in TestRegistration(%c)\n", sendSide); exit(-1); } CodecInst myCodec; if(sendACM->SendCodec(myCodec) < 0) { AudioCodingModule::Codec(_codecCntrA, myCodec); } if(!_randomTest) { fprintf(stdout, "Unregistering reveive codec, NO AUDIO.\n"); fflush(stdout); } { WriteLockScoped wl(_apiTestRWLock); *thereIsDecoder = false; } //myEvent->Wait(20); CHECK_ERROR_MT(receiveACM->UnregisterReceiveCodec(myCodec.pltype)); Wait(1000); int currentPayload = myCodec.pltype; if(!FixedPayloadTypeCodec(myCodec.plname)) { WebRtc_Word32 i; for(i = 0; i < 32; i++) { if(!_payloadUsed[i]) { if(!_randomTest) { fprintf(stdout, "Register receive codec with new Payload, AUDIO BACK.\n"); } //myCodec.pltype = i + 96; //CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); //CHECK_ERROR_MT(sendACM->RegisterSendCodec(myCodec)); //myEvent->Wait(20); //{ // WriteLockScoped wl(_apiTestRWLock); // *thereIsDecoder = true; //} Wait(1000); if(!_randomTest) { fprintf(stdout, "Unregistering reveive codec, NO AUDIO.\n"); } //{ // WriteLockScoped wl(_apiTestRWLock); // *thereIsDecoder = false; //} //myEvent->Wait(20); //CHECK_ERROR_MT(receiveACM->UnregisterReceiveCodec(myCodec.pltype)); Wait(1000); myCodec.pltype = currentPayload; if(!_randomTest) { fprintf(stdout, "Register receive codec with default Payload, AUDIO BACK.\n"); fflush(stdout); } CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); //CHECK_ERROR_MT(sendACM->RegisterSendCodec(myCodec)); myEvent->Wait(20); { WriteLockScoped wl(_apiTestRWLock); *thereIsDecoder = true; } Wait(1000); break; } } if(i == 32) { CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); { WriteLockScoped wl(_apiTestRWLock); *thereIsDecoder = true; } } } else { if(!_randomTest) { fprintf(stdout, "Register receive codec with fixed Payload, AUDIO BACK.\n"); fflush(stdout); } CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); //CHECK_ERROR_MT(receiveACM->UnregisterReceiveCodec(myCodec.pltype)); //CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); myEvent->Wait(20); { WriteLockScoped wl(_apiTestRWLock); *thereIsDecoder = true; } } delete myEvent; if(!_randomTest) { fprintf(stdout, "---------------------------------------------------------\n"); } } // Playout Mode, background noise mode. // Receiver Frequency, playout frequency. void APITest::TestPlayout(char receiveSide) { AudioCodingModule* receiveACM; AudioPlayoutMode* playoutMode; ACMBackgroundNoiseMode* bgnMode; switch(receiveSide) { case 'A': { receiveACM = _acmA; playoutMode = &_playoutModeA; bgnMode = &_bgnModeA; break; } case 'B': { receiveACM = _acmB; playoutMode = &_playoutModeB; bgnMode = &_bgnModeB; break; } default: receiveACM = _acmA; } WebRtc_Word32 receiveFreqHz = receiveACM->ReceiveFrequency(); WebRtc_Word32 playoutFreqHz = receiveACM->PlayoutFrequency(); CHECK_ERROR_MT(receiveFreqHz); CHECK_ERROR_MT(playoutFreqHz); char bgnString[25]; switch(*bgnMode) { case On: { *bgnMode = Fade; strncpy(bgnString, "Fade", 25); break; } case Fade: { *bgnMode = Off; strncpy(bgnString, "OFF", 25); break; } case Off: { *bgnMode = On; strncpy(bgnString, "ON", 25); break; } default: *bgnMode = On; strncpy(bgnString, "ON", 25); } CHECK_ERROR_MT(receiveACM->SetBackgroundNoiseMode(*bgnMode)); bgnString[24] = '\0'; char playoutString[25]; switch(*playoutMode) { case voice: { *playoutMode = fax; strncpy(playoutString, "FAX", 25); break; } case fax: { *playoutMode = streaming; strncpy(playoutString, "Streaming", 25); break; } case streaming: { *playoutMode = voice; strncpy(playoutString, "Voice", 25); break; } default: *playoutMode = voice; strncpy(playoutString, "Voice", 25); } CHECK_ERROR_MT(receiveACM->SetPlayoutMode(*playoutMode)); playoutString[24] = '\0'; if(!_randomTest) { fprintf(stdout, "\n"); fprintf(stdout, "In Side %c\n", receiveSide); fprintf(stdout, "---------------------------------\n"); fprintf(stdout, "Receive Frequency....... %d Hz\n", receiveFreqHz); fprintf(stdout, "Playout Frequency....... %d Hz\n", playoutFreqHz); fprintf(stdout, "Audio Playout Mode...... %s\n", playoutString); fprintf(stdout, "Background Noise Mode... %s\n", bgnString); } } // set/get receiver VAD status & mode. void APITest::TestReceiverVAD(char side) { AudioCodingModule* myACM; EventWrapper* myEvent = EventWrapper::Create(); WebRtc_UWord64* myReceiveVADActivity; if(side == 'A') { myACM = _acmA; myReceiveVADActivity = _receiveVADActivityA; } else { myACM = _acmB; myReceiveVADActivity = _receiveVADActivityB; } bool vadStatus = myACM->ReceiveVADStatus(); ACMVADMode mode = myACM->ReceiveVADMode(); CHECK_ERROR_MT(mode); if(!_randomTest) { fprintf(stdout, "\n\nCurrent Receive VAD at side %c\n", side); fprintf(stdout, "----------------------------------\n"); fprintf(stdout, "Status........ %s\n", vadStatus? "ON":"OFF"); fprintf(stdout, "mode.......... %d\n", (int)mode); fprintf(stdout, "VAD Active.... %llu\n", myReceiveVADActivity[0]); fprintf(stdout, "VAD Passive... %llu\n", myReceiveVADActivity[1]); fprintf(stdout, "VAD Unknown... %llu\n", myReceiveVADActivity[2]); } if(vadStatus) { if(!_randomTest) { fprintf(stdout, "\nChange Receive VAD at side %c\n\n", side); } switch(mode) { case VADNormal: mode = VADAggr; break; case VADLowBitrate: mode = VADVeryAggr; break; case VADAggr: mode = VADLowBitrate; break; case VADVeryAggr: vadStatus = false; mode = VADNormal; break; default: mode = VADNormal; } CHECK_ERROR_MT(myACM->SetReceiveVADMode(mode)); CHECK_ERROR_MT(myACM->SetReceiveVADStatus(vadStatus)); } else { if(!_randomTest) { fprintf(stdout, "\nTurn on Receive VAD at side %c\n\n", side); } CHECK_ERROR_MT(myACM->SetReceiveVADStatus(true)); CHECK_ERROR_MT(myACM->SetReceiveVADMode(VADNormal)); } for(int n = 0; n < 3; n++) { myReceiveVADActivity[n] = 0; } } void APITest::TestSendVAD(char side) { if(_randomTest) { return; } bool* vad; bool* dtx; ACMVADMode* mode; Channel* myChannel; AudioCodingModule* myACM; CodecInst myCodec; if(!_randomTest) { fprintf(stdout, "\n\n"); fprintf(stdout, "-----------------------------------------------\n"); fprintf(stdout, " Test VAD API\n"); fprintf(stdout, "-----------------------------------------------\n"); } if(side == 'A') { AudioCodingModule::Codec(_codecCntrA, myCodec); vad = &_sendVADA; dtx = &_sendDTXA; mode = &_sendVADModeA; myChannel = _channel_A2B; myACM = _acmA; } else { AudioCodingModule::Codec(_codecCntrB, myCodec); vad = &_sendVADB; dtx = &_sendDTXB; mode = &_sendVADModeB; myChannel = _channel_B2A; myACM = _acmB; } CheckVADStatus(side); if(!_randomTest) { fprintf(stdout, "\n\n"); } switch(*mode) { case VADNormal: *vad = true; *dtx = true; *mode = VADAggr; break; case VADLowBitrate: *vad = true; *dtx = true; *mode = VADVeryAggr; break; case VADAggr: *vad = true; *dtx = true; *mode = VADLowBitrate; break; case VADVeryAggr: *vad = false; *dtx = false; *mode = VADNormal; break; default: *mode = VADNormal; } *dtx = (myCodec.plfreq == 32000)? false:*dtx; CHECK_ERROR_MT(myACM->SetVAD(*dtx, *vad, *mode)); myChannel->ResetStats(); CheckVADStatus(side); if(!_randomTest) { fprintf(stdout, "\n"); fprintf(stdout, "-----------------------------------------------\n"); } // Fault Test CHECK_PROTECTED_MT(myACM->SetVAD(false, true, (ACMVADMode)-1)); CHECK_PROTECTED_MT(myACM->SetVAD(false, true, (ACMVADMode)4)); } void APITest::CurrentCodec(char side) { CodecInst myCodec; EventWrapper* myEvent = EventWrapper::Create(); if(side == 'A') { _acmA->SendCodec(myCodec); } else { _acmB->SendCodec(myCodec); } if(!_randomTest) { fprintf(stdout, "\n\n"); fprintf(stdout, "Send codec in Side A\n"); fprintf(stdout, "----------------------------\n"); fprintf(stdout, "Name................. %s\n", myCodec.plname); fprintf(stdout, "Sampling Frequency... %d\n", myCodec.plfreq); fprintf(stdout, "Rate................. %d\n", myCodec.rate); fprintf(stdout, "Payload-type......... %d\n", myCodec.pltype); fprintf(stdout, "Packet-size.......... %d\n", myCodec.pacsize); } Wait(100); } void APITest::ChangeCodec(char side) { CodecInst myCodec; AudioCodingModule* myACM; WebRtc_UWord8* codecCntr; bool* thereIsEncoder; bool* vad; bool* dtx; ACMVADMode* mode; Channel* myChannel; EventWrapper* myEvent = EventWrapper::Create(); // Reset and Wait if(!_randomTest) { fprintf(stdout, "Reset Encoder Side A \n"); } if(side == 'A') { myACM = _acmA; codecCntr = &_codecCntrA; { WriteLockScoped wl(_apiTestRWLock); thereIsEncoder = &_thereIsEncoderA; } vad = &_sendVADA; dtx = &_sendDTXA; mode = &_sendVADModeA; myChannel = _channel_A2B; } else { myACM = _acmB; codecCntr = &_codecCntrB; { WriteLockScoped wl(_apiTestRWLock); thereIsEncoder = &_thereIsEncoderB; } vad = &_sendVADB; dtx = &_sendDTXB; mode = &_sendVADModeB; myChannel = _channel_B2A; } myACM->ResetEncoder(); Wait(100); // Register the next codec do { *codecCntr = (*codecCntr < AudioCodingModule::NumberOfCodecs() - 1)? (*codecCntr + 1):0; if(*codecCntr == 0) { //printf("Initialize Sender Side A \n"); { WriteLockScoped wl(_apiTestRWLock); *thereIsEncoder = false; } CHECK_ERROR_MT(myACM->InitializeSender()); Wait(1000); // After Initialization CN is lost, re-register them if(AudioCodingModule::Codec("CN", myCodec, 8000) >= 0) { CHECK_ERROR_MT(myACM->RegisterSendCodec(myCodec)); } if(AudioCodingModule::Codec("CN", myCodec, 16000) >= 0) { CHECK_ERROR_MT(myACM->RegisterSendCodec(myCodec)); } // VAD & DTX are disabled after initialization *vad = false; *dtx = false; _writeToFile = false; } AudioCodingModule::Codec(*codecCntr, myCodec); } while(!STR_CASE_CMP(myCodec.plname, "CN") || !STR_CASE_CMP(myCodec.plname, "telephone-event") || !STR_CASE_CMP(myCodec.plname, "RED")); if(!_randomTest) { fprintf(stdout, "\n====================================================================\n"); fprintf(stdout, " Registering New Codec %s, %d kHz, %d kbps\n", myCodec.plname, myCodec.plfreq / 1000, myCodec.rate / 1000); } //std::cout<< std::flush; // NO DTX for supe-wideband codec at this point if(myCodec.plfreq == 32000) { *dtx = false; CHECK_ERROR_MT(myACM->SetVAD(*dtx, *vad, *mode)); } CHECK_ERROR_MT(myACM->RegisterSendCodec(myCodec)); myChannel->ResetStats(); { WriteLockScoped wl(_apiTestRWLock); *thereIsEncoder = true; } Wait(500); } void APITest::LookForDTMF(char side) { if(!_randomTest) { fprintf(stdout, "\n\nLooking for DTMF Signal in Side %c\n", side); fprintf(stdout, "----------------------------------------\n"); } if(side == 'A') { _acmB->RegisterIncomingMessagesCallback(NULL); _acmA->RegisterIncomingMessagesCallback(_dtmfCallback); Wait(1000); _acmA->RegisterIncomingMessagesCallback(NULL); } else { _acmA->RegisterIncomingMessagesCallback(NULL); _acmB->RegisterIncomingMessagesCallback(_dtmfCallback); Wait(1000); _acmB->RegisterIncomingMessagesCallback(NULL); } }