Adding two new network metrics to NetEQ
Clock-drift (in parts-per-million) and peaky-jitter mode status. Both metrics are propagated to the VoE API. Tests are added in the NetEQ unittests, and to some extent in ACM unittests and VoE tests. Introducing a proper translation between structs NetworkStatistics and ACMNetworkStatistics. Note: The file neteq_network_stats.dat in resources must be updated for the unittests to pass. Review URL: http://webrtc-codereview.appspot.com/337005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1328 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
80d28b22b9
commit
d439870473
2
DEPS
2
DEPS
@ -8,7 +8,7 @@ vars = {
|
||||
|
||||
# External resources like video and audio files used for testing purposes.
|
||||
# Downloaded on demand when needed.
|
||||
"webrtc_resources_revision": "5",
|
||||
"webrtc_resources_revision": "6",
|
||||
}
|
||||
|
||||
# NOTE: Prefer revision numbers to tags for svn deps.
|
||||
|
@ -251,6 +251,8 @@ struct NetworkStatistics // NETEQ statistics
|
||||
WebRtc_UWord16 currentBufferSize;
|
||||
// preferred (optimal) buffer size in ms
|
||||
WebRtc_UWord16 preferredBufferSize;
|
||||
// adding extra delay due to "peaky jitter"
|
||||
bool jitterPeaksFound;
|
||||
// loss rate (network + late) in percent (in Q14)
|
||||
WebRtc_UWord16 currentPacketLossRate;
|
||||
// late loss rate in percent (in Q14)
|
||||
@ -263,6 +265,8 @@ struct NetworkStatistics // NETEQ statistics
|
||||
WebRtc_UWord16 currentPreemptiveRate;
|
||||
// fraction of data removed through acceleration (in Q14)
|
||||
WebRtc_UWord16 currentAccelerateRate;
|
||||
// clock-drift in parts-per-million (negative or positive)
|
||||
int32_t clockDriftPPM;
|
||||
// average packet waiting time in the jitter buffer (ms)
|
||||
int meanWaitingTimeMs;
|
||||
// median packet waiting time in the jitter buffer (ms)
|
||||
|
@ -145,6 +145,8 @@ enum ACMAMRPackingFormat {
|
||||
//
|
||||
// -currentBufferSize : current jitter buffer size in ms
|
||||
// -preferredBufferSize : preferred (optimal) buffer size in ms
|
||||
// -jitterPeaksFound : indicate if peaky-jitter mode is engaged, that is,
|
||||
// if severe but sparse network delays have occurred.
|
||||
// -currentPacketLossRate : loss rate (network + late) (in Q14)
|
||||
// -currentDiscardRate : late loss rate (in Q14)
|
||||
// -currentExpandRate : fraction (of original stream) of synthesized
|
||||
@ -153,17 +155,22 @@ enum ACMAMRPackingFormat {
|
||||
// pre-emptive expansion (in Q14)
|
||||
// -currentAccelerateRate : fraction of data removed through acceleration
|
||||
// (in Q14)
|
||||
// -clockDriftPPM : clock-drift between sender and receiver in parts-
|
||||
// per-million. Positive means that receiver sample
|
||||
// rate is higher than sender sample rate.
|
||||
// -meanWaitingTimeMs : average packet waiting time in the buffer
|
||||
// -medianWaitingTimeMs : median packet waiting time in the buffer
|
||||
// -maxWaitingTimeMs : max packet waiting time in the buffer
|
||||
typedef struct {
|
||||
WebRtc_UWord16 currentBufferSize;
|
||||
WebRtc_UWord16 preferredBufferSize;
|
||||
bool jitterPeaksFound;
|
||||
WebRtc_UWord16 currentPacketLossRate;
|
||||
WebRtc_UWord16 currentDiscardRate;
|
||||
WebRtc_UWord16 currentExpandRate;
|
||||
WebRtc_UWord16 currentPreemptiveRate;
|
||||
WebRtc_UWord16 currentAccelerateRate;
|
||||
int32_t clockDriftPPM;
|
||||
int meanWaitingTimeMs;
|
||||
int medianWaitingTimeMs;
|
||||
int maxWaitingTimeMs;
|
||||
|
@ -458,11 +458,13 @@ ACMNetEQ::NetworkStatistics(
|
||||
{
|
||||
statistics->currentAccelerateRate = stats.currentAccelerateRate;
|
||||
statistics->currentBufferSize = stats.currentBufferSize;
|
||||
statistics->jitterPeaksFound = (stats.jitterPeaksFound > 0);
|
||||
statistics->currentDiscardRate = stats.currentDiscardRate;
|
||||
statistics->currentExpandRate = stats.currentExpandRate;
|
||||
statistics->currentPacketLossRate = stats.currentPacketLossRate;
|
||||
statistics->currentPreemptiveRate = stats.currentPreemptiveRate;
|
||||
statistics->preferredBufferSize = stats.preferredBufferSize;
|
||||
statistics->clockDriftPPM = stats.clockDriftPPM;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -103,6 +103,15 @@ TEST_F(AcmNetEqTest, NetworkStatistics) {
|
||||
|
||||
ACMNetworkStatistics stats;
|
||||
ASSERT_EQ(0, neteq_.NetworkStatistics(&stats));
|
||||
EXPECT_EQ(0, stats.currentBufferSize);
|
||||
EXPECT_EQ(0, stats.preferredBufferSize);
|
||||
EXPECT_FALSE(stats.jitterPeaksFound);
|
||||
EXPECT_EQ(0, stats.currentPacketLossRate);
|
||||
EXPECT_EQ(0, stats.currentDiscardRate);
|
||||
EXPECT_EQ(0, stats.currentExpandRate);
|
||||
EXPECT_EQ(0, stats.currentPreemptiveRate);
|
||||
EXPECT_EQ(0, stats.currentAccelerateRate);
|
||||
EXPECT_EQ(-916, stats.clockDriftPPM);
|
||||
EXPECT_EQ(300, stats.maxWaitingTimeMs);
|
||||
EXPECT_EQ(159, stats.meanWaitingTimeMs);
|
||||
EXPECT_EQ(160, stats.medianWaitingTimeMs);
|
||||
|
@ -977,12 +977,17 @@ APITest::TestDelay(char side)
|
||||
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, "Preferred buffer-size... %d\n", networkStat.preferredBufferSize);
|
||||
fprintf(stdout, "Peaky jitter mode........%d\n", networkStat.jitterPeaksFound);
|
||||
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, "Clock-drift............. %d\n", networkStat.clockDriftPPM);
|
||||
fprintf(stdout, "Mean waiting time....... %d\n", networkStat.meanWaitingTimeMs);
|
||||
fprintf(stdout, "Median waiting time..... %d\n", networkStat.medianWaitingTimeMs);
|
||||
fprintf(stdout, "Max waiting time........ %d\n", networkStat.maxWaitingTimeMs);
|
||||
}
|
||||
|
||||
CHECK_ERROR_MT(myACM->SetMinimumPlayoutDelay(*myMinDelay));
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "automode.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "signal_processing_library.h"
|
||||
|
||||
#include "neteq_defines.h"
|
||||
@ -496,11 +498,12 @@ WebRtc_Word16 WebRtcNetEQ_CalcOptimalBufLvl(AutomodeInst_t *inst, WebRtc_Word32
|
||||
* If not disabled (enough peaks have been observed) and
|
||||
* time since last peak is less than two peak periods.
|
||||
*/
|
||||
inst->peakFound = 0;
|
||||
if ((!inst->peakModeDisabled) && (inst->peakIatCountSamp
|
||||
<= WEBRTC_SPL_LSHIFT_W32(inst->curPeakPeriod , 1)))
|
||||
{
|
||||
/* Engage peak mode */
|
||||
|
||||
inst->peakFound = 1;
|
||||
/* Set optimal buffer level to curPeakHeight (if it's not already larger) */
|
||||
Bopt = WEBRTC_SPL_MAX(Bopt, inst->curPeakHeight);
|
||||
|
||||
@ -688,7 +691,7 @@ int WebRtcNetEQ_ResetAutomode(AutomodeInst_t *inst, int maxBufLenPackets)
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the optimal buffer level corresponing to the initial PDF.
|
||||
* Calculate the optimal buffer level corresponding to the initial PDF.
|
||||
* No need to call WebRtcNetEQ_CalcOptimalBufLvl() since we have just hard-coded
|
||||
* all the variables that the buffer level depends on => we know the result
|
||||
*/
|
||||
@ -715,3 +718,19 @@ int WebRtcNetEQ_ResetAutomode(AutomodeInst_t *inst, int maxBufLenPackets)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t WebRtcNetEQ_AverageIAT(const AutomodeInst_t *inst) {
|
||||
int i;
|
||||
int32_t sum_q24 = 0;
|
||||
assert(inst);
|
||||
for (i = 0; i <= MAX_IAT; ++i) {
|
||||
/* Shift 6 to fit worst case: 2^30 * 64. */
|
||||
sum_q24 += (inst->iatProb[i] >> 6) * i;
|
||||
}
|
||||
/* Subtract the nominal inter-arrival time 1 = 2^24 in Q24. */
|
||||
sum_q24 -= (1 << 24);
|
||||
/*
|
||||
* Multiply with 1000000 / 2^24 = 15625 / 2^18 to get the average.
|
||||
* Shift 7 to Q17 first, then multiply with 15625 and shift another 11.
|
||||
*/
|
||||
return ((sum_q24 >> 7) * 15625) >> 11;
|
||||
}
|
||||
|
@ -103,6 +103,8 @@ typedef struct
|
||||
WebRtc_Word16 curPeakHeight; /* derived from peakHeightPkt vector;
|
||||
used as optimal buffer level in peak mode */
|
||||
WebRtc_Word16 peakModeDisabled; /* ==0 if peak mode can be engaged; >0 if not */
|
||||
uint16_t peakFound; /* 1 if peaks are detected and extra delay is applied;
|
||||
* 0 otherwise. */
|
||||
|
||||
/* Post-call statistics */
|
||||
WebRtc_UWord32 countIAT500ms; /* number of times we got small network outage */
|
||||
@ -240,4 +242,23 @@ int WebRtcNetEQ_SetPacketSpeechLen(AutomodeInst_t *inst, WebRtc_Word16 newLenSam
|
||||
|
||||
int WebRtcNetEQ_ResetAutomode(AutomodeInst_t *inst, int maxBufLenPackets);
|
||||
|
||||
/****************************************************************************
|
||||
* WebRtcNetEQ_AverageIAT(...)
|
||||
*
|
||||
* Calculate the average inter-arrival time based on current statistics.
|
||||
* The average is expressed in parts per million relative the nominal. That is,
|
||||
* if the average inter-arrival time is equal to the nominal frame time,
|
||||
* the return value is zero. A positive value corresponds to packet spacing
|
||||
* being too large, while a negative value means that the packets arrive with
|
||||
* less spacing than expected.
|
||||
*
|
||||
*
|
||||
* Input:
|
||||
* - inst : Automode instance.
|
||||
*
|
||||
* Return value : Average relative inter-arrival time in samples.
|
||||
*/
|
||||
|
||||
int32_t WebRtcNetEQ_AverageIAT(const AutomodeInst_t *inst);
|
||||
|
||||
#endif /* AUTOMODE_H */
|
||||
|
@ -92,16 +92,21 @@ int WebRtcNetEQ_RecOutMasterSlave(void *inst, WebRtc_Word16 *pw16_outData,
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WebRtc_UWord16 currentBufferSize; /* current jitter buffer size in ms */
|
||||
WebRtc_UWord16 preferredBufferSize; /* preferred (optimal) buffer size in ms */
|
||||
WebRtc_UWord16 currentPacketLossRate; /* loss rate (network + late) (in Q14) */
|
||||
WebRtc_UWord16 currentDiscardRate; /* late loss rate (in Q14) */
|
||||
WebRtc_UWord16 currentExpandRate; /* fraction (of original stream) of synthesized speech
|
||||
* inserted through expansion (in Q14) */
|
||||
WebRtc_UWord16 currentPreemptiveRate; /* fraction of synthesized speech inserted through
|
||||
* pre-emptive expansion (in Q14) */
|
||||
WebRtc_UWord16 currentAccelerateRate; /* fraction of data removed through acceleration
|
||||
* (in Q14) */
|
||||
uint16_t currentBufferSize; /* Current jitter buffer size in ms. */
|
||||
uint16_t preferredBufferSize; /* Preferred buffer size in ms. */
|
||||
uint16_t jitterPeaksFound; /* 1 if adding extra delay due to peaky
|
||||
* jitter; 0 otherwise. */
|
||||
uint16_t currentPacketLossRate; /* Loss rate (network + late) (Q14). */
|
||||
uint16_t currentDiscardRate; /* Late loss rate (Q14). */
|
||||
uint16_t currentExpandRate; /* Fraction (of original stream) of
|
||||
* synthesized speech inserted through
|
||||
* expansion (in Q14). */
|
||||
uint16_t currentPreemptiveRate; /* Fraction of data inserted through
|
||||
* pre-emptive expansion (in Q14). */
|
||||
uint16_t currentAccelerateRate; /* Fraction of data removed through
|
||||
* acceleration (in Q14). */
|
||||
int32_t clockDriftPPM; /* Average clock-drift in parts-per-
|
||||
* million (positive or negative). */
|
||||
} WebRtcNetEQ_NetworkStatistics;
|
||||
|
||||
/*
|
||||
|
@ -1241,6 +1241,13 @@ int WebRtcNetEQ_GetNetworkStatistics(void *inst, WebRtcNetEQ_NetworkStatistics *
|
||||
stats->preferredBufferSize = 0;
|
||||
}
|
||||
|
||||
/***********************************/
|
||||
/* Check if jitter peaks are found */
|
||||
/***********************************/
|
||||
|
||||
stats->jitterPeaksFound =
|
||||
NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.peakFound;
|
||||
|
||||
/***********************/
|
||||
/* Calculate loss rate */
|
||||
/***********************/
|
||||
@ -1525,6 +1532,9 @@ int WebRtcNetEQ_GetNetworkStatistics(void *inst, WebRtcNetEQ_NetworkStatistics *
|
||||
stats->currentPreemptiveRate = 1 << 14; /* 1 in Q14 */
|
||||
}
|
||||
|
||||
stats->clockDriftPPM = WebRtcNetEQ_AverageIAT(
|
||||
&NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst);
|
||||
|
||||
/* reset counters */
|
||||
WebRtcNetEQ_ResetMcuInCallStats(&(NetEqMainInst->MCUinst));
|
||||
WebRtcNetEQ_ClearInCallStats(&(NetEqMainInst->DSPinst));
|
||||
|
@ -183,6 +183,9 @@ class NetEqDecodingTest : public ::testing::Test {
|
||||
void DecodeAndCheckStats(const std::string &rtp_file,
|
||||
const std::string &stat_ref_file,
|
||||
const std::string &rtcp_ref_file);
|
||||
static void PopulateRtpInfo(int frame_index,
|
||||
int samples_per_frame,
|
||||
WebRtcNetEQ_RTPInfo* rtp_info);
|
||||
|
||||
NETEQTEST_NetEQClass* neteq_inst_;
|
||||
std::vector<NETEQTEST_Decoder*> dec_;
|
||||
@ -333,6 +336,16 @@ void NetEqDecodingTest::DecodeAndCheckStats(const std::string &rtp_file,
|
||||
}
|
||||
}
|
||||
|
||||
void NetEqDecodingTest::PopulateRtpInfo(int frame_index,
|
||||
int samples_per_frame,
|
||||
WebRtcNetEQ_RTPInfo* rtp_info) {
|
||||
rtp_info->sequenceNumber = frame_index;
|
||||
rtp_info->timeStamp = frame_index * samples_per_frame;
|
||||
rtp_info->SSRC = 0x1234; // Just an arbitrary SSRC.
|
||||
rtp_info->payloadType = 94; // PCM16b WB codec.
|
||||
rtp_info->markerBit = 0;
|
||||
}
|
||||
|
||||
#if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_64_BITS)
|
||||
TEST_F(NetEqDecodingTest, TestBitExactness) {
|
||||
const std::string kInputRtpFile = webrtc::test::ProjectRootPath() +
|
||||
@ -419,4 +432,66 @@ TEST_F(NetEqDecodingTest, TestFrameWaitingTimeStatistics) {
|
||||
EXPECT_EQ(100, len);
|
||||
}
|
||||
|
||||
TEST_F(NetEqDecodingTest, TestAverageInterArrivalTimeNegative) {
|
||||
const int kNumFrames = 3000; // Needed for convergence.
|
||||
int frame_index = 0;
|
||||
const int kSamples = 10 * 16;
|
||||
const int kPayloadBytes = kSamples * 2;
|
||||
while (frame_index < kNumFrames) {
|
||||
// Insert one packet each time, except every 10th time where we insert two
|
||||
// packets at once. This will create a negative clock-drift of approx. 10%.
|
||||
int num_packets = (frame_index % 10 == 0 ? 2 : 1);
|
||||
for (int n = 0; n < num_packets; ++n) {
|
||||
uint8_t payload[kPayloadBytes] = {0};
|
||||
WebRtcNetEQ_RTPInfo rtp_info;
|
||||
PopulateRtpInfo(frame_index, kSamples, &rtp_info);
|
||||
ASSERT_EQ(0,
|
||||
WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(),
|
||||
&rtp_info,
|
||||
payload,
|
||||
kPayloadBytes, 0));
|
||||
++frame_index;
|
||||
}
|
||||
|
||||
// Pull out data once.
|
||||
ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_));
|
||||
}
|
||||
|
||||
WebRtcNetEQ_NetworkStatistics network_stats;
|
||||
ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(),
|
||||
&network_stats));
|
||||
EXPECT_EQ(-106911, network_stats.clockDriftPPM);
|
||||
}
|
||||
|
||||
TEST_F(NetEqDecodingTest, TestAverageInterArrivalTimePositive) {
|
||||
const int kNumFrames = 5000; // Needed for convergence.
|
||||
int frame_index = 0;
|
||||
const int kSamples = 10 * 16;
|
||||
const int kPayloadBytes = kSamples * 2;
|
||||
for (int i = 0; i < kNumFrames; ++i) {
|
||||
// Insert one packet each time, except every 10th time where we don't insert
|
||||
// any packet. This will create a positive clock-drift of approx. 11%.
|
||||
int num_packets = (i % 10 == 9 ? 0 : 1);
|
||||
for (int n = 0; n < num_packets; ++n) {
|
||||
uint8_t payload[kPayloadBytes] = {0};
|
||||
WebRtcNetEQ_RTPInfo rtp_info;
|
||||
PopulateRtpInfo(frame_index, kSamples, &rtp_info);
|
||||
ASSERT_EQ(0,
|
||||
WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(),
|
||||
&rtp_info,
|
||||
payload,
|
||||
kPayloadBytes, 0));
|
||||
++frame_index;
|
||||
}
|
||||
|
||||
// Pull out data once.
|
||||
ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_));
|
||||
}
|
||||
|
||||
WebRtcNetEQ_NetworkStatistics network_stats;
|
||||
ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(),
|
||||
&network_stats));
|
||||
EXPECT_EQ(108352, network_stats.clockDriftPPM);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -3081,6 +3081,10 @@ TEST_MUSTPASS(voe_codec_->SetSendCodec(0, ci));
|
||||
nStats.currentPreemptiveRate);
|
||||
TEST_LOG(" preferredBufferSize = %hu \n",
|
||||
nStats.preferredBufferSize);
|
||||
TEST_LOG(" jitterPeaksFound = %i \n",
|
||||
nStats.jitterPeaksFound);
|
||||
TEST_LOG(" clockDriftPPM = %i \n",
|
||||
nStats.clockDriftPPM);
|
||||
TEST_LOG(" meanWaitingTimeMs = %i \n",
|
||||
nStats.meanWaitingTimeMs);
|
||||
TEST_LOG(" medianWaitingTimeMs = %i \n",
|
||||
|
Loading…
x
Reference in New Issue
Block a user