blob: 56bed86ec5fcedebf4a6e7e1ef7133b333049cbf [file] [log] [blame]
/**
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <AudioConfig.h>
#include <ImsMediaAudioUtil.h>
#include <MediaQualityAnalyzer.h>
#include <ImsMediaCondition.h>
#include <MockBaseSessionCallback.h>
#include <ImsMediaTimer.h>
using namespace android::telephony::imsmedia;
using ::testing::_;
// RtpConfig
const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_INACTIVE;
const android::String8 kRemoteAddress("127.0.0.1");
const int32_t kRemotePort = 10000;
const int8_t kDscp = 0;
const int8_t kRxPayload = 96;
const int8_t kTxPayload = 96;
const int8_t kSamplingRate = 16;
// RtcpConfig
const android::String8 kCanonicalName("name");
const int32_t kTransmitPort = 1001;
const int32_t kIntervalSec = 3;
const int32_t kRtcpXrBlockTypes = RtcpConfig::FLAG_RTCPXR_STATISTICS_SUMMARY_REPORT_BLOCK |
RtcpConfig::FLAG_RTCPXR_VOIP_METRICS_REPORT_BLOCK;
// AudioConfig
const int8_t kPTimeMillis = 20;
const int32_t kMaxPtimeMillis = 100;
const int8_t kcodecModeRequest = 15;
const bool kDtxEnabled = true;
const int32_t kCodecType = AudioConfig::CODEC_AMR_WB;
const int8_t kDtmfPayloadTypeNumber = 100;
const int8_t kDtmfsamplingRateKHz = 16;
// AmrParam
const int32_t kAmrMode = 8;
const bool kOctetAligned = false;
const int32_t kMaxRedundancyMillis = 240;
// EvsParam
const int32_t kEvsBandwidth = EvsParams::EVS_BAND_NONE;
const int32_t kEvsMode = 8;
const int8_t kChannelAwareMode = 3;
const bool kUseHeaderFullOnly = false;
// MediaQualityThreshold
const std::vector<int32_t> kRtpInactivityTimerMillis = {2000, 4000};
const int32_t kRtcpInactivityTimerMillis = 2000;
const int32_t kRtpHysteresisTimeInMillis = 2000;
const int32_t kRtpPacketLossDurationMillis = 3000;
const std::vector<int32_t> kRtpPacketLossRate = {3, 5};
const std::vector<int32_t> kRtpJitterMillis = {10, 20};
class FakeMediaQualityCallback : public BaseSessionCallback
{
public:
FakeMediaQualityCallback() {}
virtual ~FakeMediaQualityCallback() {}
virtual void SendEvent(int32_t type, uint64_t param1, uint64_t /*param2*/)
{
if (type == kAudioCallQualityChangedInd)
{
CallQuality* quality = reinterpret_cast<CallQuality*>(param1);
if (quality != nullptr)
{
mCallQuality = *quality;
delete quality;
}
}
else if (type == kImsMediaEventMediaQualityStatus)
{
MediaQualityStatus* status = reinterpret_cast<MediaQualityStatus*>(param1);
if (status != nullptr)
{
mStatus = *status;
delete status;
}
}
}
virtual void onEvent(int32_t type, uint64_t param1, uint64_t param2)
{
(void)type;
(void)param1;
(void)param2;
}
CallQuality getCallQuality() { return mCallQuality; }
MediaQualityStatus getMediaQualityStatus() { return mStatus; }
private:
CallQuality mCallQuality;
MediaQualityStatus mStatus;
};
class MediaQualityAnalyzerTest : public ::testing::Test
{
public:
MediaQualityAnalyzerTest() { mAnalyzer = nullptr; }
virtual ~MediaQualityAnalyzerTest() {}
protected:
MediaQualityAnalyzer* mAnalyzer;
AudioConfig mConfig;
RtcpConfig mRtcpConfig;
AmrParams mAmrParam;
EvsParams mEvsParam;
FakeMediaQualityCallback mFakeCallback;
MockBaseSessionCallback mCallback;
ImsMediaCondition mCondition;
virtual void SetUp() override
{
mCallback.SetDelegate(&mFakeCallback);
mCallback.DelegateToFake();
mAnalyzer = new MediaQualityAnalyzer();
mRtcpConfig.setCanonicalName(kCanonicalName);
mRtcpConfig.setTransmitPort(kTransmitPort);
mRtcpConfig.setIntervalSec(kIntervalSec);
mRtcpConfig.setRtcpXrBlockTypes(kRtcpXrBlockTypes);
mAmrParam.setAmrMode(kAmrMode);
mAmrParam.setOctetAligned(kOctetAligned);
mAmrParam.setMaxRedundancyMillis(kMaxRedundancyMillis);
mEvsParam.setEvsBandwidth(kEvsBandwidth);
mEvsParam.setEvsMode(kEvsMode);
mEvsParam.setChannelAwareMode(kChannelAwareMode);
mEvsParam.setUseHeaderFullOnly(kUseHeaderFullOnly);
mEvsParam.setCodecModeRequest(kcodecModeRequest);
mConfig.setMediaDirection(kMediaDirection);
mConfig.setRemoteAddress(kRemoteAddress);
mConfig.setRemotePort(kRemotePort);
mConfig.setRtcpConfig(mRtcpConfig);
mConfig.setDscp(kDscp);
mConfig.setRxPayloadTypeNumber(kRxPayload);
mConfig.setTxPayloadTypeNumber(kTxPayload);
mConfig.setSamplingRateKHz(kSamplingRate);
mConfig.setPtimeMillis(kPTimeMillis);
mConfig.setMaxPtimeMillis(kMaxPtimeMillis);
mConfig.setDtxEnabled(kDtxEnabled);
mConfig.setCodecType(kCodecType);
mConfig.setTxDtmfPayloadTypeNumber(kDtmfPayloadTypeNumber);
mConfig.setRxDtmfPayloadTypeNumber(kDtmfPayloadTypeNumber);
mConfig.setDtmfsamplingRateKHz(kDtmfsamplingRateKHz);
mConfig.setAmrParams(mAmrParam);
mConfig.setEvsParams(mEvsParam);
mAnalyzer->setCallback(&mCallback);
mAnalyzer->setConfig(&mConfig);
mCondition.reset();
}
virtual void TearDown() override
{
if (mAnalyzer != nullptr)
{
delete mAnalyzer;
}
}
};
TEST_F(MediaQualityAnalyzerTest, TestStartStop)
{
EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(1);
mAnalyzer->start();
CallQuality quality = mAnalyzer->getCallQuality();
mAnalyzer->stop();
EXPECT_EQ(mFakeCallback.getCallQuality(), quality);
}
TEST_F(MediaQualityAnalyzerTest, TestCollectTxPackets)
{
EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(1);
mAnalyzer->start();
const int32_t numPackets = 10;
for (int32_t i = 0; i < numPackets; i++)
{
RtpPacket* packet = new RtpPacket();
mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpTx, reinterpret_cast<uint64_t>(packet));
}
mCondition.wait_timeout(1100); // 1.1 sec
EXPECT_EQ(mAnalyzer->getTxPacketSize(), numPackets);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0);
CallQuality quality = mAnalyzer->getCallQuality();
mAnalyzer->stop();
EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0);
// Check CallQuality value
CallQuality quality2 = mFakeCallback.getCallQuality();
EXPECT_EQ(quality2, quality);
EXPECT_EQ(quality2.getNumRtpPacketsTransmitted(), numPackets);
}
TEST_F(MediaQualityAnalyzerTest, TestRtpInactivity)
{
EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2);
EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(3);
MediaQualityThreshold threshold;
threshold.setRtpInactivityTimerMillis(kRtpInactivityTimerMillis);
mAnalyzer->setMediaQualityThreshold(threshold);
mAnalyzer->start();
mCondition.wait_timeout(2100); // 2.1 sec
// Check MediaQualityStatus value
MediaQualityStatus quality1 = mFakeCallback.getMediaQualityStatus();
EXPECT_EQ(quality1.getRtpInactivityTimeMillis(), 2000);
mCondition.wait_timeout(2100); // 2.1 sec
// Check MediaQualityStatus value
MediaQualityStatus quality2 = mFakeCallback.getMediaQualityStatus();
EXPECT_EQ(quality2.getRtpInactivityTimeMillis(), 4000);
RtpPacket* packet = new RtpPacket();
packet->seqNum = 0;
mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet));
mCondition.wait_timeout(3100); // 3.1 sec
MediaQualityStatus quality3 = mFakeCallback.getMediaQualityStatus();
EXPECT_EQ(quality3.getRtpInactivityTimeMillis(), 2000);
mAnalyzer->stop();
}
TEST_F(MediaQualityAnalyzerTest, TestRtcpInactivity)
{
EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2);
EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(3);
MediaQualityThreshold threshold;
threshold.setRtcpInactivityTimerMillis(kRtcpInactivityTimerMillis);
mAnalyzer->setMediaQualityThreshold(threshold);
mAnalyzer->start();
mCondition.wait_timeout(2100); // 2.1 sec
// Check MediaQualityStatus value
MediaQualityStatus quality1 = mFakeCallback.getMediaQualityStatus();
EXPECT_EQ(quality1.getRtcpInactivityTimeMillis(), 2000);
mCondition.wait_timeout(2100); // 2.1 sec
// Check MediaQualityStatus value
MediaQualityStatus quality2 = mFakeCallback.getMediaQualityStatus();
EXPECT_EQ(quality2.getRtcpInactivityTimeMillis(), 2000);
mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtcp);
mCondition.wait_timeout(2100); // 2.1 sec
MediaQualityStatus quality3 = mFakeCallback.getMediaQualityStatus();
EXPECT_EQ(quality3.getRtcpInactivityTimeMillis(), 2000);
mAnalyzer->stop();
}
TEST_F(MediaQualityAnalyzerTest, TestCallQualityInactivity)
{
EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2);
mAnalyzer->start();
mCondition.wait_timeout(4100); // 4.1 sec
CallQuality quality = mAnalyzer->getCallQuality();
mAnalyzer->stop();
// Check CallQuality value
CallQuality quality2 = mFakeCallback.getCallQuality();
EXPECT_EQ(quality2, quality);
EXPECT_TRUE(quality2.getRtpInactivityDetected());
}
TEST_F(MediaQualityAnalyzerTest, TestCallQualityLevelChanged)
{
EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2);
mAnalyzer->start();
const int32_t numPackets = 10;
const int32_t jitter = 10;
for (int32_t i = 0; i < numPackets; i++)
{
RtpPacket* packet = new RtpPacket();
if (i == 5) // make 10% loss rate
{
continue;
}
packet->seqNum = i;
packet->jitter = jitter;
mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet));
SessionCallbackParameter* param = new SessionCallbackParameter(
i, kRtpStatusNormal, ImsMediaTimer::GetTimeInMilliSeconds());
mAnalyzer->SendEvent(kCollectRxRtpStatus, reinterpret_cast<uint64_t>(param));
}
SessionCallbackParameter* param = new SessionCallbackParameter(kReportPacketLossGap, 5, 1);
mAnalyzer->SendEvent(kCollectOptionalInfo, reinterpret_cast<uint64_t>(param), 0);
mCondition.wait_timeout(5100); // 5.1 sec
EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), numPackets - 1);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 1);
CallQuality quality = mAnalyzer->getCallQuality();
mAnalyzer->stop();
EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0);
// Check CallQuality value
CallQuality quality2 = mFakeCallback.getCallQuality();
EXPECT_EQ(quality2, quality);
EXPECT_EQ(quality2.getNumRtpPacketsReceived(), numPackets - 1);
EXPECT_EQ(quality2.getDownlinkCallQualityLevel(), CallQuality::kCallQualityBad);
}
TEST_F(MediaQualityAnalyzerTest, TestJitterInd)
{
EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(1);
EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(1);
MediaQualityThreshold threshold;
threshold.setRtpHysteresisTimeInMillis(kRtpHysteresisTimeInMillis);
threshold.setRtpJitterMillis(kRtpJitterMillis);
mAnalyzer->setMediaQualityThreshold(threshold);
mAnalyzer->start();
const int32_t numPackets = 20;
const int32_t jitter = 20;
const uint32_t ssrc = 10000;
for (int32_t i = 0; i < numPackets; i++)
{
RtpPacket* packet = new RtpPacket();
packet->seqNum = i;
packet->jitter = jitter;
packet->ssrc = ssrc;
mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet));
}
mCondition.wait_timeout(1100); // 1.1 sec
EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), numPackets);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0);
CallQuality quality = mAnalyzer->getCallQuality();
mAnalyzer->stop();
EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0);
CallQuality quality2 = mFakeCallback.getCallQuality();
EXPECT_EQ(quality2, quality);
EXPECT_EQ(quality2.getNumRtpPacketsReceived(), numPackets);
EXPECT_EQ(quality2.getAverageRelativeJitter(), jitter);
MediaQualityStatus status = mFakeCallback.getMediaQualityStatus();
EXPECT_EQ(status.getRtpJitterMillis(), jitter);
}
TEST_F(MediaQualityAnalyzerTest, TestSsrcChange)
{
mAnalyzer->start();
const int32_t numPackets = 20;
const int32_t jitter = 20;
const uint32_t ssrc1 = 10000;
const uint32_t ssrc2 = 20000;
for (int32_t i = 0; i < numPackets; i++)
{
RtpPacket* packet = new RtpPacket();
packet->seqNum = i;
packet->jitter = jitter;
packet->ssrc = ssrc1;
if (i >= 5)
{
packet->ssrc = ssrc2;
}
mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet));
}
mCondition.wait_timeout(1100); // 1.1 sec
EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), numPackets);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0);
mAnalyzer->stop();
EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0);
}
TEST_F(MediaQualityAnalyzerTest, TestPacketLossInd)
{
EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(1);
EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(1);
MediaQualityThreshold threshold;
threshold.setRtpHysteresisTimeInMillis(kRtpHysteresisTimeInMillis);
threshold.setRtpPacketLossDurationMillis(kRtpPacketLossDurationMillis);
threshold.setRtpPacketLossRate(kRtpPacketLossRate);
mAnalyzer->setMediaQualityThreshold(threshold);
mAnalyzer->start();
const int32_t numPackets = 10;
for (int32_t i = 0; i < numPackets; i++)
{
RtpPacket* packet = new RtpPacket();
if (i == 5) // make 10% loss rate
{
continue;
}
packet->seqNum = i;
packet->jitter = 10;
packet->arrival = ImsMediaTimer::GetTimeInMilliSeconds();
mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet));
}
SessionCallbackParameter* param = new SessionCallbackParameter(kReportPacketLossGap, 5, 1);
mAnalyzer->SendEvent(kCollectOptionalInfo, reinterpret_cast<uint64_t>(param), 0);
mCondition.wait_timeout(1100); // 1.1 sec
EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), numPackets - 1);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 1);
CallQuality quality = mAnalyzer->getCallQuality();
mAnalyzer->stop();
EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getRxPacketSize(), 0);
EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0);
CallQuality quality2 = mFakeCallback.getCallQuality();
EXPECT_EQ(quality2, quality);
EXPECT_EQ(quality2.getNumRtpPacketsNotReceived(), 1);
MediaQualityStatus status = mFakeCallback.getMediaQualityStatus();
EXPECT_EQ(status.getRtpPacketLossRate(), 10);
}
TEST_F(MediaQualityAnalyzerTest, TestNotifyMediaQualityStatus)
{
EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(1);
EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(1);
MediaQualityThreshold threshold;
threshold.setNotifyCurrentStatus(true);
mAnalyzer->setMediaQualityThreshold(threshold);
mAnalyzer->start();
mCondition.wait_timeout(2100); // 2.1 sec
mAnalyzer->stop();
}