It was not clear how one could know if ACM is using DTX from WebRTC or codec internal DTX. This CL makes better use of IsInternalDTXReplacedWithWebRtc() which was designed for G.729 to export such information. Before IsInternalDTXReplacedWithWebRtc() gives true only if codec == G729 and G729's internal DTX is replaced with WebRTC DTX. Now IsInternalDTXReplacedWithWebRtc() gives true also when codec does not have internal DTX, i.e., must use WebRTC DTX, which is much more logical. BUG= R=henrik.lundin@webrtc.org Review URL: https://webrtc-codereview.appspot.com/35459004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7870 4adac7df-926f-26a2-2b94-8c16560cd09d
378 lines
9.9 KiB
C++
378 lines
9.9 KiB
C++
/*
|
|
* Copyright (c) 2012 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 "webrtc/modules/audio_coding/main/test/TestVADDTX.h"
|
|
|
|
#include <iostream>
|
|
|
|
#include "webrtc/common_types.h"
|
|
#include "webrtc/engine_configurations.h"
|
|
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
|
|
#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h"
|
|
#include "webrtc/modules/audio_coding/main/test/utility.h"
|
|
#include "webrtc/system_wrappers/interface/trace.h"
|
|
#include "webrtc/test/testsupport/fileutils.h"
|
|
|
|
namespace webrtc {
|
|
|
|
TestVADDTX::TestVADDTX()
|
|
: _acmA(AudioCodingModule::Create(0)),
|
|
_acmB(AudioCodingModule::Create(1)),
|
|
_channelA2B(NULL) {}
|
|
|
|
TestVADDTX::~TestVADDTX() {
|
|
if (_channelA2B != NULL) {
|
|
delete _channelA2B;
|
|
_channelA2B = NULL;
|
|
}
|
|
}
|
|
|
|
void TestVADDTX::Perform() {
|
|
const std::string file_name = webrtc::test::ResourcePath(
|
|
"audio_coding/testfile32kHz", "pcm");
|
|
_inFileA.Open(file_name, 32000, "rb");
|
|
|
|
EXPECT_EQ(0, _acmA->InitializeReceiver());
|
|
EXPECT_EQ(0, _acmB->InitializeReceiver());
|
|
|
|
uint8_t numEncoders = _acmA->NumberOfCodecs();
|
|
CodecInst myCodecParam;
|
|
for (uint8_t n = 0; n < numEncoders; n++) {
|
|
EXPECT_EQ(0, _acmB->Codec(n, &myCodecParam));
|
|
if (!strcmp(myCodecParam.plname, "opus")) {
|
|
// Register Opus as mono.
|
|
myCodecParam.channels = 1;
|
|
}
|
|
EXPECT_EQ(0, _acmB->RegisterReceiveCodec(myCodecParam));
|
|
}
|
|
|
|
// Create and connect the channel
|
|
_channelA2B = new Channel;
|
|
_acmA->RegisterTransportCallback(_channelA2B);
|
|
_channelA2B->RegisterReceiverACM(_acmB.get());
|
|
|
|
_acmA->RegisterVADCallback(&_monitor);
|
|
|
|
int16_t testCntr = 1;
|
|
|
|
#ifdef WEBRTC_CODEC_ISAC
|
|
// Open outputfile
|
|
OpenOutFile(testCntr++);
|
|
|
|
// Register iSAC WB as send codec
|
|
char nameISAC[] = "ISAC";
|
|
RegisterSendCodec('A', nameISAC, 16000);
|
|
|
|
// Run the five test cased
|
|
runTestCases();
|
|
|
|
// Close file
|
|
_outFileB.Close();
|
|
|
|
// Open outputfile
|
|
OpenOutFile(testCntr++);
|
|
|
|
// Register iSAC SWB as send codec
|
|
RegisterSendCodec('A', nameISAC, 32000);
|
|
|
|
// Run the five test cased
|
|
runTestCases();
|
|
|
|
// Close file
|
|
_outFileB.Close();
|
|
#endif
|
|
#ifdef WEBRTC_CODEC_ILBC
|
|
// Open outputfile
|
|
OpenOutFile(testCntr++);
|
|
|
|
// Register iLBC as send codec
|
|
char nameILBC[] = "ilbc";
|
|
RegisterSendCodec('A', nameILBC);
|
|
|
|
// Run the five test cased
|
|
runTestCases();
|
|
|
|
// Close file
|
|
_outFileB.Close();
|
|
|
|
#endif
|
|
#ifdef WEBRTC_CODEC_OPUS
|
|
// Open outputfile
|
|
OpenOutFile(testCntr++);
|
|
|
|
// Register Opus as send codec
|
|
char nameOPUS[] = "opus";
|
|
RegisterSendCodec('A', nameOPUS);
|
|
|
|
// Run the five test cased
|
|
runTestCases();
|
|
|
|
// Close file
|
|
_outFileB.Close();
|
|
|
|
#endif
|
|
}
|
|
|
|
void TestVADDTX::runTestCases() {
|
|
// #1 DTX = OFF, VAD = ON, VADNormal
|
|
SetVAD(false, true, VADNormal);
|
|
Run();
|
|
VerifyTest();
|
|
|
|
// #2 DTX = OFF, VAD = ON, VADAggr
|
|
SetVAD(false, true, VADAggr);
|
|
Run();
|
|
VerifyTest();
|
|
|
|
// #3 DTX = ON, VAD = ON, VADLowBitrate
|
|
SetVAD(true, true, VADLowBitrate);
|
|
Run();
|
|
VerifyTest();
|
|
|
|
// #4 DTX = ON, VAD = ON, VADVeryAggr
|
|
SetVAD(true, true, VADVeryAggr);
|
|
Run();
|
|
VerifyTest();
|
|
|
|
// #5 DTX = ON, VAD = OFF, VADNormal
|
|
SetVAD(true, false, VADNormal);
|
|
Run();
|
|
VerifyTest();
|
|
}
|
|
|
|
void TestVADDTX::runTestInternalDTX(int expected_result) {
|
|
// #6 DTX = ON, VAD = ON, VADNormal
|
|
SetVAD(true, true, VADNormal);
|
|
EXPECT_EQ(expected_result, _acmA->ReplaceInternalDTXWithWebRtc(true));
|
|
if (expected_result == 0) {
|
|
Run();
|
|
VerifyTest();
|
|
}
|
|
}
|
|
|
|
void TestVADDTX::SetVAD(bool statusDTX, bool statusVAD, int16_t vadMode) {
|
|
bool dtxEnabled, vadEnabled;
|
|
ACMVADMode vadModeSet;
|
|
|
|
EXPECT_EQ(0, _acmA->SetVAD(statusDTX, statusVAD, (ACMVADMode) vadMode));
|
|
EXPECT_EQ(0, _acmA->VAD(&dtxEnabled, &vadEnabled, &vadModeSet));
|
|
|
|
// Requested VAD/DTX settings
|
|
_setStruct.statusDTX = statusDTX;
|
|
_setStruct.statusVAD = statusVAD;
|
|
_setStruct.vadMode = (ACMVADMode) vadMode;
|
|
|
|
// VAD settings after setting VAD in ACM
|
|
_getStruct.statusDTX = dtxEnabled;
|
|
_getStruct.statusVAD = vadEnabled;
|
|
_getStruct.vadMode = vadModeSet;
|
|
}
|
|
|
|
VADDTXstruct TestVADDTX::GetVAD() {
|
|
VADDTXstruct retStruct;
|
|
bool dtxEnabled, vadEnabled;
|
|
ACMVADMode vadModeSet;
|
|
|
|
EXPECT_EQ(0, _acmA->VAD(&dtxEnabled, &vadEnabled, &vadModeSet));
|
|
|
|
retStruct.statusDTX = dtxEnabled;
|
|
retStruct.statusVAD = vadEnabled;
|
|
retStruct.vadMode = vadModeSet;
|
|
return retStruct;
|
|
}
|
|
|
|
int16_t TestVADDTX::RegisterSendCodec(char side, char* codecName,
|
|
int32_t samplingFreqHz,
|
|
int32_t rateKbps) {
|
|
std::cout << std::flush;
|
|
AudioCodingModule* myACM;
|
|
switch (side) {
|
|
case 'A': {
|
|
myACM = _acmA.get();
|
|
break;
|
|
}
|
|
case 'B': {
|
|
myACM = _acmB.get();
|
|
break;
|
|
}
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (myACM == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
CodecInst myCodecParam;
|
|
for (int16_t codecCntr = 0; codecCntr < myACM->NumberOfCodecs();
|
|
codecCntr++) {
|
|
EXPECT_EQ(0, myACM->Codec((uint8_t) codecCntr, &myCodecParam));
|
|
if (!STR_CASE_CMP(myCodecParam.plname, codecName)) {
|
|
if ((samplingFreqHz == -1) || (myCodecParam.plfreq == samplingFreqHz)) {
|
|
if ((rateKbps == -1) || (myCodecParam.rate == rateKbps)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We only allow VAD/DTX when sending mono.
|
|
myCodecParam.channels = 1;
|
|
EXPECT_EQ(0, myACM->RegisterSendCodec(myCodecParam));
|
|
|
|
// initialization was succesful
|
|
return 0;
|
|
}
|
|
|
|
void TestVADDTX::Run() {
|
|
AudioFrame audioFrame;
|
|
|
|
uint16_t SamplesIn10MsecA = _inFileA.PayloadLength10Ms();
|
|
uint32_t timestampA = 1;
|
|
int32_t outFreqHzB = _outFileB.SamplingFrequency();
|
|
|
|
while (!_inFileA.EndOfFile()) {
|
|
_inFileA.Read10MsData(audioFrame);
|
|
audioFrame.timestamp_ = timestampA;
|
|
timestampA += SamplesIn10MsecA;
|
|
EXPECT_EQ(0, _acmA->Add10MsData(audioFrame));
|
|
EXPECT_GT(_acmA->Process(), -1);
|
|
EXPECT_EQ(0, _acmB->PlayoutData10Ms(outFreqHzB, &audioFrame));
|
|
_outFileB.Write10MsData(audioFrame.data_, audioFrame.samples_per_channel_);
|
|
}
|
|
#ifdef PRINT_STAT
|
|
_monitor.PrintStatistics();
|
|
#endif
|
|
_inFileA.Rewind();
|
|
_monitor.GetStatistics(_statCounter);
|
|
_monitor.ResetStatistics();
|
|
}
|
|
|
|
void TestVADDTX::OpenOutFile(int16_t test_number) {
|
|
std::string file_name;
|
|
std::stringstream file_stream;
|
|
file_stream << webrtc::test::OutputPath();
|
|
file_stream << "testVADDTX_outFile_";
|
|
file_stream << test_number << ".pcm";
|
|
file_name = file_stream.str();
|
|
_outFileB.Open(file_name, 16000, "wb");
|
|
}
|
|
|
|
int16_t TestVADDTX::VerifyTest() {
|
|
// Verify empty frame result
|
|
uint8_t statusEF = 0;
|
|
uint8_t vadPattern = 0;
|
|
uint8_t emptyFramePattern[6];
|
|
CodecInst myCodecParam;
|
|
_acmA->SendCodec(&myCodecParam);
|
|
|
|
// TODO(minyue): Remove these treatment on Opus when DTX is properly handled
|
|
// by ACMOpus.
|
|
if (STR_CASE_CMP(myCodecParam.plname, "opus") == 0) {
|
|
_setStruct.statusDTX = false;
|
|
_setStruct.statusVAD = false;
|
|
}
|
|
|
|
bool isReplaced = false;
|
|
_acmA->IsInternalDTXReplacedWithWebRtc(&isReplaced);
|
|
bool webRtcDtxInUse = _getStruct.statusDTX && isReplaced;
|
|
bool codecDtxInUse = _getStruct.statusDTX && !isReplaced;
|
|
|
|
// Check for error in VAD/DTX settings
|
|
if (_getStruct.statusDTX != _setStruct.statusDTX) {
|
|
// DTX status doesn't match expected.
|
|
vadPattern |= 1;
|
|
}
|
|
if (!_getStruct.statusVAD && webRtcDtxInUse) {
|
|
// WebRTC DTX cannot run without WebRTC VAD.
|
|
vadPattern |= 2;
|
|
}
|
|
if ((!_getStruct.statusDTX || codecDtxInUse) &&
|
|
(_getStruct.statusVAD != _setStruct.statusVAD)) {
|
|
// Using no DTX or codec Internal DTX should not affect setting of VAD.
|
|
vadPattern |= 4;
|
|
}
|
|
if (_getStruct.vadMode != _setStruct.vadMode) {
|
|
// VAD Mode doesn't match expected.
|
|
vadPattern |= 8;
|
|
}
|
|
|
|
// Set expected empty frame pattern
|
|
int ii;
|
|
for (ii = 0; ii < 6; ii++) {
|
|
emptyFramePattern[ii] = 0;
|
|
}
|
|
// 0 - "kNoEncoding", not important to check.
|
|
// Codecs with packetsize != 80 samples will get this output.
|
|
// 1 - "kActiveNormalEncoded", expect to receive some frames with this label .
|
|
// 2 - "kPassiveNormalEncoded".
|
|
// 3 - "kPassiveDTXNB".
|
|
// 4 - "kPassiveDTXWB".
|
|
// 5 - "kPassiveDTXSWB".
|
|
emptyFramePattern[0] = 1;
|
|
emptyFramePattern[1] = 1;
|
|
emptyFramePattern[2] = _getStruct.statusVAD && !webRtcDtxInUse;
|
|
emptyFramePattern[3] = webRtcDtxInUse && (_acmA->SendFrequency() == 8000);
|
|
emptyFramePattern[4] = webRtcDtxInUse && (_acmA->SendFrequency() == 16000);
|
|
emptyFramePattern[5] = webRtcDtxInUse && (_acmA->SendFrequency() == 32000);
|
|
|
|
// Check pattern 1-5 (skip 0)
|
|
for (int ii = 1; ii < 6; ii++) {
|
|
if (emptyFramePattern[ii]) {
|
|
statusEF |= (_statCounter[ii] == 0);
|
|
} else {
|
|
statusEF |= (_statCounter[ii] > 0);
|
|
}
|
|
}
|
|
EXPECT_EQ(0, statusEF);
|
|
EXPECT_EQ(0, vadPattern);
|
|
|
|
return 0;
|
|
}
|
|
|
|
ActivityMonitor::ActivityMonitor() {
|
|
_counter[0] = _counter[1] = _counter[2] = _counter[3] = _counter[4] =
|
|
_counter[5] = 0;
|
|
}
|
|
|
|
ActivityMonitor::~ActivityMonitor() {
|
|
}
|
|
|
|
int32_t ActivityMonitor::InFrameType(int16_t frameType) {
|
|
_counter[frameType]++;
|
|
return 0;
|
|
}
|
|
|
|
void ActivityMonitor::PrintStatistics() {
|
|
printf("\n");
|
|
printf("kActiveNormalEncoded kPassiveNormalEncoded kPassiveDTXWB ");
|
|
printf("kPassiveDTXNB kPassiveDTXSWB kFrameEmpty\n");
|
|
printf("%19u", _counter[1]);
|
|
printf("%22u", _counter[2]);
|
|
printf("%14u", _counter[3]);
|
|
printf("%14u", _counter[4]);
|
|
printf("%14u", _counter[5]);
|
|
printf("%11u", _counter[0]);
|
|
printf("\n\n");
|
|
}
|
|
|
|
void ActivityMonitor::ResetStatistics() {
|
|
_counter[0] = _counter[1] = _counter[2] = _counter[3] = _counter[4] =
|
|
_counter[5] = 0;
|
|
}
|
|
|
|
void ActivityMonitor::GetStatistics(uint32_t* getCounter) {
|
|
for (int ii = 0; ii < 6; ii++) {
|
|
getCounter[ii] = _counter[ii];
|
|
}
|
|
}
|
|
|
|
} // namespace webrtc
|