Packet waiting-time statistics
Adding new statistics API to NetEQ, reporting the waiting time for each frame. The output is raw waiting time for the frames that have been decoded since the last statistics report (or maximum 100 frames). The statistics are reset on each query. Implemented functionality in ACM to query NetEQ for the raw waiting times, and process it to produce max, average and median. Updating common_types.h and VoiceEngine tests to include the new metrics. Unit tests are also added for NetEQ and AcmNetEq. Review URL: http://webrtc-codereview.appspot.com/328011 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1251 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
219acc6cec
commit
dbba1f969f
@ -263,6 +263,12 @@ struct NetworkStatistics // NETEQ statistics
|
||||
WebRtc_UWord16 currentPreemptiveRate;
|
||||
// fraction of data removed through acceleration (in Q14)
|
||||
WebRtc_UWord16 currentAccelerateRate;
|
||||
// average packet waiting time in the jitter buffer (ms)
|
||||
int meanWaitingTimeMs;
|
||||
// median packet waiting time in the jitter buffer (ms)
|
||||
int medianWaitingTimeMs;
|
||||
// max packet waiting time in the jitter buffer (ms)
|
||||
int maxWaitingTimeMs;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
|
@ -153,6 +153,9 @@ enum ACMAMRPackingFormat {
|
||||
// pre-emptive expansion (in Q14)
|
||||
// -currentAccelerateRate : fraction of data removed through acceleration
|
||||
// (in Q14)
|
||||
// -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;
|
||||
@ -161,6 +164,9 @@ typedef struct {
|
||||
WebRtc_UWord16 currentExpandRate;
|
||||
WebRtc_UWord16 currentPreemptiveRate;
|
||||
WebRtc_UWord16 currentAccelerateRate;
|
||||
int meanWaitingTimeMs;
|
||||
int medianWaitingTimeMs;
|
||||
int maxWaitingTimeMs;
|
||||
} ACMNetworkStatistics;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -9,7 +9,9 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <algorithm> // sort
|
||||
#include <stdlib.h> // malloc
|
||||
#include <vector>
|
||||
|
||||
#include "acm_neteq.h"
|
||||
#include "common_types.h"
|
||||
@ -461,13 +463,46 @@ ACMNetEQ::NetworkStatistics(
|
||||
statistics->currentPacketLossRate = stats.currentPacketLossRate;
|
||||
statistics->currentPreemptiveRate = stats.currentPreemptiveRate;
|
||||
statistics->preferredBufferSize = stats.preferredBufferSize;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError("getNetworkStatistics", 0);
|
||||
return -1;
|
||||
}
|
||||
const int kArrayLen = 100;
|
||||
int waiting_times[kArrayLen];
|
||||
int waiting_times_len = WebRtcNetEQ_GetRawFrameWaitingTimes(
|
||||
_inst[0], kArrayLen, waiting_times);
|
||||
if (waiting_times_len >= 0)
|
||||
{
|
||||
std::vector<int> waiting_times_vec(waiting_times,
|
||||
waiting_times + waiting_times_len);
|
||||
sort(waiting_times_vec.begin(), waiting_times_vec.end());
|
||||
size_t size = waiting_times_vec.size();
|
||||
assert(size == static_cast<size_t>(waiting_times_len));
|
||||
if (size % 2 == 0)
|
||||
{
|
||||
statistics->medianWaitingTimeMs =
|
||||
(waiting_times_vec[size / 2 - 1] +
|
||||
waiting_times_vec[size / 2]) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
statistics->medianWaitingTimeMs = waiting_times_vec[size / 2];
|
||||
}
|
||||
statistics->maxWaitingTimeMs = waiting_times_vec.back();
|
||||
double sum = 0;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
sum += waiting_times_vec[i];
|
||||
}
|
||||
statistics->meanWaitingTimeMs = static_cast<int>(sum / size);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError("getRawFrameWaitingTimes", 0);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
|
111
src/modules/audio_coding/main/source/acm_neteq_unittest.cc
Normal file
111
src/modules/audio_coding/main/source/acm_neteq_unittest.cc
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// This file contains unit tests for ACM's NetEQ wrapper (class ACMNetEQ).
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "modules/audio_coding/codecs/pcm16b/include/pcm16b.h"
|
||||
#include "modules/audio_coding/main/interface/audio_coding_module_typedefs.h"
|
||||
#include "modules/audio_coding/main/source/acm_codec_database.h"
|
||||
#include "modules/audio_coding/main/source/acm_neteq.h"
|
||||
#include "modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h"
|
||||
#include "modules/interface/module_common_types.h"
|
||||
#include "typedefs.h" // NOLINT(build/include)
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class AcmNetEqTest : public ::testing::Test {
|
||||
protected:
|
||||
static const size_t kMaxPayloadLen = 5760; // 60 ms, 48 kHz, 16 bit samples.
|
||||
static const int kPcm16WbPayloadType = 94;
|
||||
AcmNetEqTest() {}
|
||||
virtual void SetUp();
|
||||
virtual void TearDown() {}
|
||||
|
||||
void InsertZeroPacket(uint16_t sequence_number,
|
||||
uint32_t timestamp,
|
||||
uint8_t payload_type,
|
||||
uint32_t ssrc,
|
||||
bool marker_bit,
|
||||
size_t len_payload_bytes);
|
||||
void PullData(int expected_num_samples);
|
||||
|
||||
ACMNetEQ neteq_;
|
||||
};
|
||||
|
||||
void AcmNetEqTest::SetUp() {
|
||||
ASSERT_EQ(0, neteq_.Init());
|
||||
ASSERT_EQ(0, neteq_.AllocatePacketBuffer(ACMCodecDB::NetEQDecoders(),
|
||||
ACMCodecDB::kNumCodecs));
|
||||
WebRtcNetEQ_CodecDef codec_def;
|
||||
SET_CODEC_PAR(codec_def, kDecoderPCM16Bwb, kPcm16WbPayloadType, NULL, 16000);
|
||||
SET_PCM16B_WB_FUNCTIONS(codec_def);
|
||||
ASSERT_EQ(0, neteq_.AddCodec(&codec_def, true));
|
||||
}
|
||||
|
||||
void AcmNetEqTest::InsertZeroPacket(uint16_t sequence_number,
|
||||
uint32_t timestamp,
|
||||
uint8_t payload_type,
|
||||
uint32_t ssrc,
|
||||
bool marker_bit,
|
||||
size_t len_payload_bytes) {
|
||||
ASSERT_TRUE(len_payload_bytes <= kMaxPayloadLen);
|
||||
uint16_t payload[kMaxPayloadLen] = {0};
|
||||
WebRtcRTPHeader rtp_header;
|
||||
rtp_header.header.sequenceNumber = sequence_number;
|
||||
rtp_header.header.timestamp = timestamp;
|
||||
rtp_header.header.ssrc = ssrc;
|
||||
rtp_header.header.payloadType = payload_type;
|
||||
rtp_header.header.markerBit = marker_bit;
|
||||
rtp_header.type.Audio.channel = 1;
|
||||
ASSERT_EQ(0, neteq_.RecIn(reinterpret_cast<WebRtc_Word8*>(payload),
|
||||
len_payload_bytes, rtp_header));
|
||||
}
|
||||
|
||||
void AcmNetEqTest::PullData(int expected_num_samples) {
|
||||
AudioFrame out_frame;
|
||||
ASSERT_EQ(0, neteq_.RecOut(out_frame));
|
||||
ASSERT_EQ(expected_num_samples, out_frame._payloadDataLengthInSamples);
|
||||
}
|
||||
|
||||
TEST_F(AcmNetEqTest, NetworkStatistics) {
|
||||
// Use fax mode to avoid time-scaling. This is to simplify the testing of
|
||||
// packet waiting times in the packet buffer.
|
||||
neteq_.SetPlayoutMode(fax);
|
||||
// Insert 31 dummy packets at once. Each packet contains 10 ms 16 kHz audio.
|
||||
int num_frames = 30;
|
||||
const int kSamples = 10 * 16;
|
||||
const int kPayloadBytes = kSamples * 2;
|
||||
int i, j;
|
||||
for (i = 0; i < num_frames; ++i) {
|
||||
InsertZeroPacket(i, i * kSamples, kPcm16WbPayloadType, 0x1234, false,
|
||||
kPayloadBytes);
|
||||
}
|
||||
// Pull out data once.
|
||||
PullData(kSamples);
|
||||
// Insert one more packet (to produce different mean and median).
|
||||
i = num_frames;
|
||||
InsertZeroPacket(i, i * kSamples, kPcm16WbPayloadType, 0x1234, false,
|
||||
kPayloadBytes);
|
||||
// Pull out all data.
|
||||
for (j = 1; j < num_frames + 1; ++j) {
|
||||
PullData(kSamples);
|
||||
}
|
||||
|
||||
ACMNetworkStatistics stats;
|
||||
ASSERT_EQ(0, neteq_.NetworkStatistics(&stats));
|
||||
EXPECT_EQ(300, stats.maxWaitingTimeMs);
|
||||
EXPECT_EQ(159, stats.meanWaitingTimeMs);
|
||||
EXPECT_EQ(160, stats.medianWaitingTimeMs);
|
||||
}
|
||||
|
||||
} // namespace
|
@ -134,6 +134,21 @@
|
||||
}],
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'audio_coding_unittests',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'audio_coding_module',
|
||||
'NetEq',
|
||||
'<(webrtc_root)/common_audio/common_audio.gyp:vad',
|
||||
'<(webrtc_root)/../testing/gtest.gyp:gtest',
|
||||
'<(webrtc_root)/../test/test.gyp:test_support_main',
|
||||
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
],
|
||||
'sources': [
|
||||
'acm_neteq_unittest.cc',
|
||||
],
|
||||
}, # audio_coding_unittests
|
||||
],
|
||||
}],
|
||||
],
|
||||
|
@ -110,6 +110,17 @@ typedef struct
|
||||
*/
|
||||
int WebRtcNetEQ_GetNetworkStatistics(void *inst, WebRtcNetEQ_NetworkStatistics *stats);
|
||||
|
||||
/*
|
||||
* Get the raw waiting times for decoded frames. The function writes the last
|
||||
* recorded waiting times (from frame arrival to frame decoding) to the memory
|
||||
* pointed to by waitingTimeMs. The number of elements written is in the return
|
||||
* value. No more than maxLength elements are written. Statistics are reset on
|
||||
* each query.
|
||||
*/
|
||||
int WebRtcNetEQ_GetRawFrameWaitingTimes(void *inst,
|
||||
int max_length,
|
||||
int* waiting_times_ms);
|
||||
|
||||
/***********************************************/
|
||||
/* Functions for post-decode VAD functionality */
|
||||
/***********************************************/
|
||||
|
@ -38,6 +38,8 @@ enum TsScaling
|
||||
kTSscalingFourThirds
|
||||
};
|
||||
|
||||
enum { kLenWaitingTimes = 100 };
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
@ -77,6 +79,10 @@ typedef struct
|
||||
WebRtc_UWord32 lostTS; /* Number of timestamps lost */
|
||||
WebRtc_UWord32 lastReportTS; /* Timestamp elapsed since last report was given */
|
||||
|
||||
int waiting_times[kLenWaitingTimes]; /* Waiting time statistics storage. */
|
||||
int len_waiting_times;
|
||||
int next_waiting_time_index;
|
||||
|
||||
WebRtc_UWord32 externalTS;
|
||||
WebRtc_UWord32 internalTS;
|
||||
WebRtc_Word16 TSscalingInitialized;
|
||||
@ -114,6 +120,31 @@ int WebRtcNetEQ_McuReset(MCUInst_t *inst);
|
||||
*/
|
||||
int WebRtcNetEQ_ResetMcuInCallStats(MCUInst_t *inst);
|
||||
|
||||
/****************************************************************************
|
||||
* WebRtcNetEQ_ResetWaitingTimeStats(...)
|
||||
*
|
||||
* Reset waiting-time statistics.
|
||||
*
|
||||
* Input:
|
||||
* - inst : MCU instance.
|
||||
*
|
||||
* Return value : n/a
|
||||
*/
|
||||
void WebRtcNetEQ_ResetWaitingTimeStats(MCUInst_t *inst);
|
||||
|
||||
/****************************************************************************
|
||||
* WebRtcNetEQ_LogWaitingTime(...)
|
||||
*
|
||||
* Log waiting-time to the statistics.
|
||||
*
|
||||
* Input:
|
||||
* - inst : MCU instance.
|
||||
* - waiting_time : Waiting time in "RecOut calls" (i.e., 1 call = 10 ms).
|
||||
*
|
||||
* Return value : n/a
|
||||
*/
|
||||
void WebRtcNetEQ_StoreWaitingTime(MCUInst_t *inst, int waiting_time);
|
||||
|
||||
/****************************************************************************
|
||||
* WebRtcNetEQ_ResetMcuJitterStat(...)
|
||||
*
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "mcu.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "automode.h"
|
||||
@ -61,6 +62,8 @@ int WebRtcNetEQ_McuReset(MCUInst_t *inst)
|
||||
|
||||
WebRtcNetEQ_ResetMcuInCallStats(inst);
|
||||
|
||||
WebRtcNetEQ_ResetWaitingTimeStats(inst);
|
||||
|
||||
WebRtcNetEQ_ResetMcuJitterStat(inst);
|
||||
|
||||
WebRtcNetEQ_ResetAutomode(&(inst->BufferStat_inst.Automode_inst),
|
||||
@ -82,6 +85,33 @@ int WebRtcNetEQ_ResetMcuInCallStats(MCUInst_t *inst)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset waiting-time statistics.
|
||||
*/
|
||||
|
||||
void WebRtcNetEQ_ResetWaitingTimeStats(MCUInst_t *inst) {
|
||||
memset(inst->waiting_times, 0,
|
||||
kLenWaitingTimes * sizeof(inst->waiting_times[0]));
|
||||
inst->len_waiting_times = 0;
|
||||
inst->next_waiting_time_index = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store waiting-time in the statistics.
|
||||
*/
|
||||
|
||||
void WebRtcNetEQ_StoreWaitingTime(MCUInst_t *inst, int waiting_time) {
|
||||
assert(inst->next_waiting_time_index < kLenWaitingTimes);
|
||||
inst->waiting_times[inst->next_waiting_time_index] = waiting_time;
|
||||
inst->next_waiting_time_index++;
|
||||
if (inst->next_waiting_time_index >= kLenWaitingTimes) {
|
||||
inst->next_waiting_time_index = 0;
|
||||
}
|
||||
if (inst->len_waiting_times < kLenWaitingTimes) {
|
||||
inst->len_waiting_times++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset all MCU-side statistics variables for the post-call statistics.
|
||||
*/
|
||||
|
@ -78,6 +78,11 @@ int WebRtcNetEQ_PacketBufferInit(PacketBuf_t *bufferInst, int maxNoOfPackets,
|
||||
bufferInst->rcuPlCntr = &pw16_memory[pos];
|
||||
pos += maxNoOfPackets; /* advance maxNoOfPackets * WebRtc_Word16 */
|
||||
|
||||
bufferInst->waitingTime = (int*) (&pw16_memory[pos]);
|
||||
/* Advance maxNoOfPackets * sizeof(waitingTime element). */
|
||||
pos += maxNoOfPackets *
|
||||
sizeof(*bufferInst->waitingTime) / sizeof(*pw16_memory);
|
||||
|
||||
/* The payload memory starts after the slot arrays */
|
||||
bufferInst->startPayloadMemory = &pw16_memory[pos];
|
||||
bufferInst->currentMemoryPos = bufferInst->startPayloadMemory;
|
||||
@ -301,6 +306,8 @@ int WebRtcNetEQ_PacketBufferInsert(PacketBuf_t *bufferInst, const RTPPacket_t *R
|
||||
bufferInst->seqNumber[bufferInst->insertPosition] = RTPpacket->seqNumber;
|
||||
bufferInst->timeStamp[bufferInst->insertPosition] = RTPpacket->timeStamp;
|
||||
bufferInst->rcuPlCntr[bufferInst->insertPosition] = RTPpacket->rcuPlCntr;
|
||||
bufferInst->rcuPlCntr[bufferInst->insertPosition] = 0;
|
||||
bufferInst->waitingTime[bufferInst->insertPosition] = 0;
|
||||
/* Update buffer parameters */
|
||||
bufferInst->numPacketsInBuffer++;
|
||||
bufferInst->currentMemoryPos += (RTPpacket->payloadLen + 1) >> 1;
|
||||
@ -326,7 +333,7 @@ int WebRtcNetEQ_PacketBufferInsert(PacketBuf_t *bufferInst, const RTPPacket_t *R
|
||||
|
||||
|
||||
int WebRtcNetEQ_PacketBufferExtract(PacketBuf_t *bufferInst, RTPPacket_t *RTPpacket,
|
||||
int bufferPosition)
|
||||
int bufferPosition, int *waitingTime)
|
||||
{
|
||||
|
||||
/* Sanity check */
|
||||
@ -364,6 +371,7 @@ int WebRtcNetEQ_PacketBufferExtract(PacketBuf_t *bufferInst, RTPPacket_t *RTPpac
|
||||
RTPpacket->seqNumber = bufferInst->seqNumber[bufferPosition];
|
||||
RTPpacket->timeStamp = bufferInst->timeStamp[bufferPosition];
|
||||
RTPpacket->rcuPlCntr = bufferInst->rcuPlCntr[bufferPosition];
|
||||
*waitingTime = bufferInst->waitingTime[bufferPosition];
|
||||
RTPpacket->starts_byte1 = 0; /* payload is 16-bit aligned */
|
||||
|
||||
/* Clear the position in the packet buffer */
|
||||
@ -371,6 +379,7 @@ int WebRtcNetEQ_PacketBufferExtract(PacketBuf_t *bufferInst, RTPPacket_t *RTPpac
|
||||
bufferInst->payloadLengthBytes[bufferPosition] = 0;
|
||||
bufferInst->seqNumber[bufferPosition] = 0;
|
||||
bufferInst->timeStamp[bufferPosition] = 0;
|
||||
bufferInst->waitingTime[bufferPosition] = 0;
|
||||
bufferInst->payloadLocation[bufferPosition] = bufferInst->startPayloadMemory;
|
||||
|
||||
/* Reduce packet counter with one */
|
||||
@ -495,6 +504,16 @@ WebRtc_Word32 WebRtcNetEQ_PacketBufferGetSize(const PacketBuf_t *bufferInst)
|
||||
return sizeSamples;
|
||||
}
|
||||
|
||||
void WebRtcNetEQ_IncrementWaitingTimes(PacketBuf_t *buffer_inst) {
|
||||
int i;
|
||||
/* Loop through all slots in the buffer. */
|
||||
for (i = 0; i < buffer_inst->maxInsertPositions; ++i) {
|
||||
/* Only increment waiting time for the packets with non-zero size. */
|
||||
if (buffer_inst->payloadLengthBytes[i] != 0) {
|
||||
buffer_inst->waitingTime[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int WebRtcNetEQ_GetDefaultCodecSettings(const enum WebRtcNetEQDecoder *codecID,
|
||||
int noOfCodecs, int *maxBytes, int *maxSlots)
|
||||
@ -692,7 +711,8 @@ int WebRtcNetEQ_GetDefaultCodecSettings(const enum WebRtcNetEQDecoder *codecID,
|
||||
+ sizeof(WebRtc_UWord16) /* seqNumber */
|
||||
+ sizeof(WebRtc_Word16) /* payloadType */
|
||||
+ sizeof(WebRtc_Word16) /* payloadLengthBytes */
|
||||
+ sizeof(WebRtc_Word16)); /* rcuPlCntr */
|
||||
+ sizeof(WebRtc_Word16) /* rcuPlCntr */
|
||||
+ sizeof(int)); /* waitingTime */
|
||||
/* Add the extra size per slot to the memory count */
|
||||
*maxBytes += w16_tmp * (*maxSlots);
|
||||
|
||||
|
@ -49,6 +49,8 @@ typedef struct
|
||||
WebRtc_Word16 *payloadLengthBytes; /* Payload length of packet in slot n */
|
||||
WebRtc_Word16 *rcuPlCntr; /* zero for non-RCU payload, 1 for main payload
|
||||
2 for redundant payload */
|
||||
int *waitingTime;
|
||||
|
||||
|
||||
/* Statistics counter */
|
||||
WebRtc_UWord16 discardedPackets; /* Number of discarded packets */
|
||||
@ -136,7 +138,7 @@ int WebRtcNetEQ_PacketBufferInsert(PacketBuf_t *bufferInst, const RTPPacket_t *R
|
||||
*/
|
||||
|
||||
int WebRtcNetEQ_PacketBufferExtract(PacketBuf_t *bufferInst, RTPPacket_t *RTPpacket,
|
||||
int bufferPosition);
|
||||
int bufferPosition, int *waitingTime);
|
||||
|
||||
/****************************************************************************
|
||||
* WebRtcNetEQ_PacketBufferFindLowestTimestamp(...)
|
||||
@ -181,6 +183,19 @@ int WebRtcNetEQ_PacketBufferFindLowestTimestamp(PacketBuf_t *bufferInst,
|
||||
|
||||
WebRtc_Word32 WebRtcNetEQ_PacketBufferGetSize(const PacketBuf_t *bufferInst);
|
||||
|
||||
/****************************************************************************
|
||||
* WebRtcNetEQ_IncrementWaitingTimes(...)
|
||||
*
|
||||
* Increment the waiting time for all packets in the buffer by one.
|
||||
*
|
||||
* Input:
|
||||
* - bufferInst : Buffer instance
|
||||
*
|
||||
* Return value : n/a
|
||||
*/
|
||||
|
||||
void WebRtcNetEQ_IncrementWaitingTimes(PacketBuf_t *buffer_inst);
|
||||
|
||||
/****************************************************************************
|
||||
* WebRtcNetEQ_GetDefaultCodecSettings(...)
|
||||
*
|
||||
|
@ -63,6 +63,9 @@ int WebRtcNetEQ_SignalMcu(MCUInst_t *inst)
|
||||
/* Increment counter since last statistics report */
|
||||
inst->lastReportTS += inst->timestampsPerCall;
|
||||
|
||||
/* Increment waiting time for all packets. */
|
||||
WebRtcNetEQ_IncrementWaitingTimes(&inst->PacketBuffer_inst);
|
||||
|
||||
/* Read info from DSP so we now current status */
|
||||
|
||||
WEBRTC_SPL_MEMCPY_W8(&dspInfo,inst->pw16_readAddress,sizeof(DSP2MCU_info_t));
|
||||
@ -159,13 +162,15 @@ int WebRtcNetEQ_SignalMcu(MCUInst_t *inst)
|
||||
&& (WebRtcNetEQ_DbGetPayload(&(inst->codec_DB_inst),
|
||||
(enum WebRtcNetEQDecoder) inst->current_Codec) == payloadType))
|
||||
{
|
||||
int waitingTime;
|
||||
temp_pkt.payload = blockPtr + 1;
|
||||
i_res = WebRtcNetEQ_PacketBufferExtract(&inst->PacketBuffer_inst, &temp_pkt,
|
||||
i_bufferpos);
|
||||
i_bufferpos, &waitingTime);
|
||||
if (i_res < 0)
|
||||
{ /* error returned */
|
||||
return i_res;
|
||||
}
|
||||
WebRtcNetEQ_StoreWaitingTime(inst, waitingTime);
|
||||
*blockPtr = temp_pkt.payloadLen;
|
||||
/* set the flag if this is a redundant payload */
|
||||
if (temp_pkt.rcuPlCntr > 0)
|
||||
@ -626,17 +631,19 @@ int WebRtcNetEQ_SignalMcu(MCUInst_t *inst)
|
||||
inst->pw16_writeAddress[0] = inst->pw16_writeAddress[0] & 0xFF3F;
|
||||
do
|
||||
{
|
||||
int waitingTime;
|
||||
inst->timeStamp = uw32_availableTS;
|
||||
/* Write directly to shared memory */
|
||||
temp_pkt.payload = blockPtr + 1;
|
||||
i_res = WebRtcNetEQ_PacketBufferExtract(&inst->PacketBuffer_inst, &temp_pkt,
|
||||
i_bufferpos);
|
||||
i_bufferpos, &waitingTime);
|
||||
|
||||
if (i_res < 0)
|
||||
{
|
||||
/* error returned */
|
||||
return i_res;
|
||||
}
|
||||
WebRtcNetEQ_StoreWaitingTime(inst, waitingTime);
|
||||
|
||||
#ifdef NETEQ_DELAY_LOGGING
|
||||
temp_var = NETEQ_DELAY_LOGGING_SIGNAL_DECODE;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "webrtc_neteq.h"
|
||||
#include "webrtc_neteq_internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "typedefs.h"
|
||||
@ -1531,6 +1532,23 @@ int WebRtcNetEQ_GetNetworkStatistics(void *inst, WebRtcNetEQ_NetworkStatistics *
|
||||
return (0);
|
||||
}
|
||||
|
||||
int WebRtcNetEQ_GetRawFrameWaitingTimes(void *inst,
|
||||
int max_length,
|
||||
int* waiting_times_ms) {
|
||||
int i = 0;
|
||||
MainInst_t *main_inst = (MainInst_t*) inst;
|
||||
if (main_inst == NULL) return -1;
|
||||
|
||||
while ((i < max_length) && (i < main_inst->MCUinst.len_waiting_times)) {
|
||||
waiting_times_ms[i] = main_inst->MCUinst.waiting_times[i] *
|
||||
main_inst->DSPinst.millisecondsPerCall;
|
||||
++i;
|
||||
}
|
||||
assert(i <= kLenWaitingTimes);
|
||||
WebRtcNetEQ_ResetWaitingTimeStats(&main_inst->MCUinst);
|
||||
return i;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* WebRtcNetEQ_SetVADInstance(...)
|
||||
*
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "modules/audio_coding/neteq/interface/webrtc_neteq.h"
|
||||
#include "modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h"
|
||||
#include "modules/audio_coding/neteq/interface/webrtc_neteq_internal.h"
|
||||
#include "modules/audio_coding/neteq/test/NETEQTEST_CodecClass.h"
|
||||
#include "modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.h"
|
||||
#include "modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h"
|
||||
@ -355,4 +356,67 @@ TEST_F(NetEqDecodingTest, TestNetworkStatistics) {
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
TEST_F(NetEqDecodingTest, TestFrameWaitingTimeStatistics) {
|
||||
// Use fax mode to avoid time-scaling. This is to simplify the testing of
|
||||
// packet waiting times in the packet buffer.
|
||||
ASSERT_EQ(0,
|
||||
WebRtcNetEQ_SetPlayoutMode(neteq_inst_->instance(), kPlayoutFax));
|
||||
// Insert 30 dummy packets at once. Each packet contains 10 ms 16 kHz audio.
|
||||
int num_frames = 30;
|
||||
const int kSamples = 10 * 16;
|
||||
const int kPayloadBytes = kSamples * 2;
|
||||
for (int i = 0; i < num_frames; ++i) {
|
||||
uint16_t payload[kSamples] = {0};
|
||||
WebRtcNetEQ_RTPInfo rtp_info;
|
||||
rtp_info.sequenceNumber = i;
|
||||
rtp_info.timeStamp = i * kSamples;
|
||||
rtp_info.SSRC = 0x1234; // Just an arbitrary SSRC.
|
||||
rtp_info.payloadType = 94; // PCM16b WB codec.
|
||||
rtp_info.markerBit = 0;
|
||||
ASSERT_EQ(0, WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), &rtp_info,
|
||||
reinterpret_cast<uint8_t*>(payload),
|
||||
kPayloadBytes, 0));
|
||||
}
|
||||
// Pull out all data.
|
||||
for (int i = 0; i < num_frames; ++i) {
|
||||
ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_));
|
||||
}
|
||||
const int kVecLen = 110; // More than kLenWaitingTimes in mcu.h.
|
||||
int waiting_times[kVecLen];
|
||||
int len = WebRtcNetEQ_GetRawFrameWaitingTimes(neteq_inst_->instance(),
|
||||
kVecLen, waiting_times);
|
||||
EXPECT_EQ(num_frames, len);
|
||||
// Since all frames are dumped into NetEQ at once, but pulled out with 10 ms
|
||||
// spacing (per definition), we expect the delay to increase with 10 ms for
|
||||
// each packet.
|
||||
for (int i = 0; i < len; ++i) {
|
||||
EXPECT_EQ((i + 1) * 10, waiting_times[i]);
|
||||
}
|
||||
|
||||
// Check statistics again and make sure it's been reset.
|
||||
EXPECT_EQ(0, WebRtcNetEQ_GetRawFrameWaitingTimes(neteq_inst_->instance(),
|
||||
kVecLen, waiting_times));
|
||||
|
||||
// Process > 100 frames, and make sure that that we get statistics
|
||||
// only for 100 frames. Note the new SSRC, causing NetEQ to reset.
|
||||
num_frames = 110;
|
||||
for (int i = 0; i < num_frames; ++i) {
|
||||
uint16_t payload[kSamples] = {0};
|
||||
WebRtcNetEQ_RTPInfo rtp_info;
|
||||
rtp_info.sequenceNumber = i;
|
||||
rtp_info.timeStamp = i * kSamples;
|
||||
rtp_info.SSRC = 0x1235; // Just an arbitrary SSRC.
|
||||
rtp_info.payloadType = 94; // PCM16b WB codec.
|
||||
rtp_info.markerBit = 0;
|
||||
ASSERT_EQ(0, WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(), &rtp_info,
|
||||
reinterpret_cast<uint8_t*>(payload),
|
||||
kPayloadBytes, 0));
|
||||
ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_));
|
||||
}
|
||||
|
||||
len = WebRtcNetEQ_GetRawFrameWaitingTimes(neteq_inst_->instance(),
|
||||
kVecLen, waiting_times);
|
||||
EXPECT_EQ(100, len);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -3081,6 +3081,12 @@ TEST_MUSTPASS(voe_codec_->SetSendCodec(0, ci));
|
||||
nStats.currentPreemptiveRate);
|
||||
TEST_LOG(" preferredBufferSize = %hu \n",
|
||||
nStats.preferredBufferSize);
|
||||
TEST_LOG(" meanWaitingTimeMs = %i \n",
|
||||
nStats.meanWaitingTimeMs);
|
||||
TEST_LOG(" medianWaitingTimeMs = %i \n",
|
||||
nStats.medianWaitingTimeMs);
|
||||
TEST_LOG(" maxWaitingTimeMs = %i \n",
|
||||
nStats.maxWaitingTimeMs);
|
||||
#else
|
||||
TEST_LOG("Skipping NetEQ statistics tests - "
|
||||
"WEBRTC_VOICE_ENGINE_NETEQ_STATS_API not defined \n");
|
||||
|
Loading…
Reference in New Issue
Block a user