Adding decoded_fec_rate to NetEQ Network Statistics.
A statistic is introduced to reflect the actual benefits of Opus FEC. It shows what percentage of samples in the rendered audio come from FEC data. BUG=3867 R=henrik.lundin@webrtc.org Review URL: https://webrtc-codereview.appspot.com/34969004 Cr-Commit-Position: refs/heads/master@{#8384} git-svn-id: http://webrtc.googlecode.com/svn/trunk@8384 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
290cb56dca
commit
2c1bcf2cb4
@ -38,6 +38,8 @@ struct NetEqNetworkStatistics {
|
||||
// expansion (in Q14).
|
||||
uint16_t accelerate_rate; // Fraction of data removed through acceleration
|
||||
// (in Q14).
|
||||
uint16_t secondary_decoded_rate; // Fraction of data coming from secondary
|
||||
// decoding (in Q14).
|
||||
int32_t clockdrift_ppm; // Average clock-drift in parts-per-million
|
||||
// (positive or negative).
|
||||
int added_zero_samples; // Number of zero samples added in "off" mode.
|
||||
|
@ -80,8 +80,6 @@ class MockExternalPcm16B : public ExternalPcm16B {
|
||||
uint32_t arrival_timestamp));
|
||||
MOCK_METHOD0(ErrorCode,
|
||||
int());
|
||||
MOCK_CONST_METHOD0(codec_type,
|
||||
NetEqDecoder());
|
||||
|
||||
private:
|
||||
ExternalPcm16B real_;
|
||||
|
@ -10,221 +10,237 @@
|
||||
|
||||
// Test to verify correct operation for externally created decoders.
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "webrtc/modules/audio_coding/neteq/interface/neteq.h"
|
||||
#include "webrtc/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h"
|
||||
#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h"
|
||||
#include "webrtc/modules/audio_coding/neteq/tools/neteq_external_decoder_test.h"
|
||||
#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
#include "webrtc/test/testsupport/fileutils.h"
|
||||
#include "webrtc/test/testsupport/gtest_disable.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
|
||||
// This test encodes a few packets of PCM16b 32 kHz data and inserts it into two
|
||||
// different NetEq instances. The first instance uses the internal version of
|
||||
// the decoder object, while the second one uses an externally created decoder
|
||||
// object (ExternalPcm16B wrapped in MockExternalPcm16B, both defined above).
|
||||
// The test verifies that the output from both instances match.
|
||||
class NetEqExternalDecoderTest : public ::testing::Test {
|
||||
class NetEqExternalDecoderUnitTest : public test::NetEqExternalDecoderTest {
|
||||
protected:
|
||||
static const int kTimeStepMs = 10;
|
||||
static const int kMaxBlockSize = 480; // 10 ms @ 48 kHz.
|
||||
static const uint8_t kPayloadType = 95;
|
||||
static const int kSampleRateHz = 32000;
|
||||
static const int kFrameSizeMs = 10; // Frame size of Pcm16B.
|
||||
|
||||
NetEqExternalDecoderTest()
|
||||
: sample_rate_hz_(kSampleRateHz),
|
||||
samples_per_ms_(sample_rate_hz_ / 1000),
|
||||
frame_size_ms_(10),
|
||||
frame_size_samples_(frame_size_ms_ * samples_per_ms_),
|
||||
output_size_samples_(frame_size_ms_ * samples_per_ms_),
|
||||
external_decoder_(new MockExternalPcm16B),
|
||||
NetEqExternalDecoderUnitTest(NetEqDecoder codec,
|
||||
MockExternalPcm16B* decoder)
|
||||
: NetEqExternalDecoderTest(codec, decoder),
|
||||
external_decoder_(decoder),
|
||||
samples_per_ms_(CodecSampleRateHz(codec) / 1000),
|
||||
frame_size_samples_(kFrameSizeMs * samples_per_ms_),
|
||||
rtp_generator_(new test::RtpGenerator(samples_per_ms_)),
|
||||
input_(new int16_t[frame_size_samples_]),
|
||||
// Payload should be no larger than input.
|
||||
encoded_(new uint8_t[2 * frame_size_samples_]),
|
||||
payload_size_bytes_(0),
|
||||
last_send_time_(0),
|
||||
last_arrival_time_(0) {
|
||||
config_.sample_rate_hz = sample_rate_hz_;
|
||||
neteq_external_ = NetEq::Create(config_);
|
||||
neteq_ = NetEq::Create(config_);
|
||||
input_ = new int16_t[frame_size_samples_];
|
||||
encoded_ = new uint8_t[2 * frame_size_samples_];
|
||||
}
|
||||
|
||||
~NetEqExternalDecoderTest() {
|
||||
delete neteq_external_;
|
||||
delete neteq_;
|
||||
// We will now delete the decoder ourselves, so expecting Die to be called.
|
||||
EXPECT_CALL(*external_decoder_, Die()).Times(1);
|
||||
delete [] input_;
|
||||
delete [] encoded_;
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
const std::string file_name =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
||||
input_file_.reset(new test::InputAudioFile(file_name));
|
||||
assert(sample_rate_hz_ == 32000);
|
||||
NetEqDecoder decoder = kDecoderPCM16Bswb32kHz;
|
||||
// Init() will trigger external_decoder_->Init().
|
||||
EXPECT_CALL(*external_decoder_, Init());
|
||||
// NetEq is not allowed to delete the external decoder (hence Times(0)).
|
||||
EXPECT_CALL(*external_decoder_, Die()).Times(0);
|
||||
ASSERT_EQ(NetEq::kOK,
|
||||
neteq_external_->RegisterExternalDecoder(
|
||||
external_decoder_.get(), decoder, kPayloadType));
|
||||
ASSERT_EQ(NetEq::kOK,
|
||||
neteq_->RegisterPayloadType(decoder, kPayloadType));
|
||||
Init();
|
||||
|
||||
const std::string file_name =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
||||
input_file_.reset(new test::InputAudioFile(file_name));
|
||||
}
|
||||
|
||||
virtual void TearDown() {}
|
||||
virtual ~NetEqExternalDecoderUnitTest() {
|
||||
delete [] input_;
|
||||
delete [] encoded_;
|
||||
// ~NetEqExternalDecoderTest() will delete |external_decoder_|, so expecting
|
||||
// Die() to be called.
|
||||
EXPECT_CALL(*external_decoder_, Die()).Times(1);
|
||||
}
|
||||
|
||||
int GetNewPackets() {
|
||||
// Method to draw kFrameSizeMs audio and verify the output.
|
||||
// Use gTest methods. e.g. ASSERT_EQ() inside to trigger errors.
|
||||
virtual void GetAndVerifyOutput() = 0;
|
||||
|
||||
// Method to get the number of calls to the Decode() method of the external
|
||||
// decoder.
|
||||
virtual int NumExpectedDecodeCalls(int num_loops) = 0;
|
||||
|
||||
// Method to generate packets and return the send time of the packet.
|
||||
int GetNewPacket() {
|
||||
if (!input_file_->Read(frame_size_samples_, input_)) {
|
||||
return -1;
|
||||
}
|
||||
payload_size_bytes_ = WebRtcPcm16b_Encode(input_, frame_size_samples_,
|
||||
encoded_);
|
||||
if (frame_size_samples_ * 2 != payload_size_bytes_) {
|
||||
return -1;
|
||||
}
|
||||
encoded_);;
|
||||
|
||||
int next_send_time = rtp_generator_->GetRtpHeader(
|
||||
kPayloadType, frame_size_samples_, &rtp_header_);
|
||||
return next_send_time;
|
||||
}
|
||||
|
||||
virtual void VerifyOutput(size_t num_samples) const {
|
||||
for (size_t i = 0; i < num_samples; ++i) {
|
||||
ASSERT_EQ(output_[i], output_external_[i]) <<
|
||||
"Diff in sample " << i << ".";
|
||||
}
|
||||
}
|
||||
// Method to decide packet losses.
|
||||
virtual bool Lost() { return false; }
|
||||
|
||||
virtual int GetArrivalTime(int send_time) {
|
||||
// Method to calculate packet arrival time.
|
||||
int GetArrivalTime(int send_time) {
|
||||
int arrival_time = last_arrival_time_ + (send_time - last_send_time_);
|
||||
last_send_time_ = send_time;
|
||||
last_arrival_time_ = arrival_time;
|
||||
return arrival_time;
|
||||
}
|
||||
|
||||
virtual bool Lost() { return false; }
|
||||
|
||||
virtual void InsertPackets(int next_arrival_time) {
|
||||
// Insert packet in regular instance.
|
||||
ASSERT_EQ(
|
||||
NetEq::kOK,
|
||||
neteq_->InsertPacket(
|
||||
rtp_header_, encoded_, payload_size_bytes_, next_arrival_time));
|
||||
// Insert packet in external decoder instance.
|
||||
EXPECT_CALL(*external_decoder_,
|
||||
IncomingPacket(_,
|
||||
payload_size_bytes_,
|
||||
rtp_header_.header.sequenceNumber,
|
||||
rtp_header_.header.timestamp,
|
||||
next_arrival_time));
|
||||
ASSERT_EQ(
|
||||
NetEq::kOK,
|
||||
neteq_external_->InsertPacket(
|
||||
rtp_header_, encoded_, payload_size_bytes_, next_arrival_time));
|
||||
}
|
||||
|
||||
virtual void GetOutputAudio() {
|
||||
NetEqOutputType output_type;
|
||||
// Get audio from regular instance.
|
||||
int samples_per_channel;
|
||||
int num_channels;
|
||||
EXPECT_EQ(NetEq::kOK,
|
||||
neteq_->GetAudio(kMaxBlockSize,
|
||||
output_,
|
||||
&samples_per_channel,
|
||||
&num_channels,
|
||||
&output_type));
|
||||
EXPECT_EQ(1, num_channels);
|
||||
EXPECT_EQ(output_size_samples_, samples_per_channel);
|
||||
// Get audio from external decoder instance.
|
||||
ASSERT_EQ(NetEq::kOK,
|
||||
neteq_external_->GetAudio(kMaxBlockSize,
|
||||
output_external_,
|
||||
&samples_per_channel,
|
||||
&num_channels,
|
||||
&output_type));
|
||||
EXPECT_EQ(1, num_channels);
|
||||
EXPECT_EQ(output_size_samples_, samples_per_channel);
|
||||
}
|
||||
|
||||
virtual int NumExpectedDecodeCalls(int num_loops) const { return num_loops; }
|
||||
|
||||
void RunTest(int num_loops) {
|
||||
// Get next input packets (mono and multi-channel).
|
||||
int next_send_time;
|
||||
int next_arrival_time;
|
||||
uint32_t next_send_time;
|
||||
uint32_t next_arrival_time;
|
||||
do {
|
||||
next_send_time = GetNewPackets();
|
||||
ASSERT_NE(-1, next_send_time);
|
||||
next_send_time = GetNewPacket();
|
||||
next_arrival_time = GetArrivalTime(next_send_time);
|
||||
} while (Lost()); // If lost, immediately read the next packet.
|
||||
|
||||
EXPECT_CALL(*external_decoder_, Decode(_, payload_size_bytes_, _, _))
|
||||
.Times(NumExpectedDecodeCalls(num_loops));
|
||||
|
||||
int time_now = 0;
|
||||
uint32_t time_now = 0;
|
||||
for (int k = 0; k < num_loops; ++k) {
|
||||
while (time_now >= next_arrival_time) {
|
||||
InsertPackets(next_arrival_time);
|
||||
|
||||
InsertPacket(rtp_header_, encoded_, payload_size_bytes_,
|
||||
next_arrival_time);
|
||||
// Get next input packet.
|
||||
do {
|
||||
next_send_time = GetNewPackets();
|
||||
ASSERT_NE(-1, next_send_time);
|
||||
next_send_time = GetNewPacket();
|
||||
next_arrival_time = GetArrivalTime(next_send_time);
|
||||
} while (Lost()); // If lost, immediately read the next packet.
|
||||
}
|
||||
|
||||
GetOutputAudio();
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << "Lap number " << k << ".";
|
||||
SCOPED_TRACE(ss.str()); // Print out the parameter values on failure.
|
||||
// Compare mono and multi-channel.
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyOutput(output_size_samples_));
|
||||
ASSERT_NO_FATAL_FAILURE(GetAndVerifyOutput());
|
||||
|
||||
time_now += kTimeStepMs;
|
||||
time_now += kOutputLengthMs;
|
||||
}
|
||||
}
|
||||
|
||||
NetEq::Config config_;
|
||||
int sample_rate_hz_;
|
||||
int samples_per_ms_;
|
||||
const int frame_size_ms_;
|
||||
size_t frame_size_samples_;
|
||||
int output_size_samples_;
|
||||
NetEq* neteq_external_;
|
||||
NetEq* neteq_;
|
||||
void InsertPacket(WebRtcRTPHeader rtp_header, const uint8_t* payload,
|
||||
size_t payload_size_bytes,
|
||||
uint32_t receive_timestamp) override {
|
||||
EXPECT_CALL(*external_decoder_,
|
||||
IncomingPacket(_,
|
||||
payload_size_bytes,
|
||||
rtp_header.header.sequenceNumber,
|
||||
rtp_header.header.timestamp,
|
||||
receive_timestamp));
|
||||
NetEqExternalDecoderTest::InsertPacket(rtp_header, payload,
|
||||
payload_size_bytes,
|
||||
receive_timestamp);
|
||||
}
|
||||
|
||||
MockExternalPcm16B* external_decoder() { return external_decoder_.get(); }
|
||||
|
||||
void ResetRtpGenerator(test::RtpGenerator* rtp_generator) {
|
||||
rtp_generator_.reset(rtp_generator);
|
||||
}
|
||||
|
||||
int samples_per_ms() const { return samples_per_ms_; }
|
||||
private:
|
||||
scoped_ptr<MockExternalPcm16B> external_decoder_;
|
||||
int samples_per_ms_;
|
||||
size_t frame_size_samples_;
|
||||
scoped_ptr<test::RtpGenerator> rtp_generator_;
|
||||
int16_t* input_;
|
||||
uint8_t* encoded_;
|
||||
int16_t output_[kMaxBlockSize];
|
||||
int16_t output_external_[kMaxBlockSize];
|
||||
WebRtcRTPHeader rtp_header_;
|
||||
size_t payload_size_bytes_;
|
||||
int last_send_time_;
|
||||
int last_arrival_time_;
|
||||
uint32_t last_send_time_;
|
||||
uint32_t last_arrival_time_;
|
||||
scoped_ptr<test::InputAudioFile> input_file_;
|
||||
WebRtcRTPHeader rtp_header_;
|
||||
};
|
||||
|
||||
TEST_F(NetEqExternalDecoderTest, RunTest) {
|
||||
// This test encodes a few packets of PCM16b 32 kHz data and inserts it into two
|
||||
// different NetEq instances. The first instance uses the internal version of
|
||||
// the decoder object, while the second one uses an externally created decoder
|
||||
// object (ExternalPcm16B wrapped in MockExternalPcm16B, both defined above).
|
||||
// The test verifies that the output from both instances match.
|
||||
class NetEqExternalVsInternalDecoderTest : public NetEqExternalDecoderUnitTest,
|
||||
public ::testing::Test {
|
||||
protected:
|
||||
static const int kMaxBlockSize = 480; // 10 ms @ 48 kHz.
|
||||
|
||||
NetEqExternalVsInternalDecoderTest()
|
||||
: NetEqExternalDecoderUnitTest(kDecoderPCM16Bswb32kHz,
|
||||
new MockExternalPcm16B),
|
||||
sample_rate_hz_(CodecSampleRateHz(kDecoderPCM16Bswb32kHz)) {
|
||||
NetEq::Config config;
|
||||
config.sample_rate_hz = CodecSampleRateHz(kDecoderPCM16Bswb32kHz);
|
||||
neteq_internal_.reset(NetEq::Create(config));
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_EQ(NetEq::kOK,
|
||||
neteq_internal_->RegisterPayloadType(kDecoderPCM16Bswb32kHz,
|
||||
kPayloadType));
|
||||
}
|
||||
|
||||
void GetAndVerifyOutput() override {
|
||||
NetEqOutputType output_type;
|
||||
int samples_per_channel;
|
||||
int num_channels;
|
||||
// Get audio from internal decoder instance.
|
||||
EXPECT_EQ(NetEq::kOK,
|
||||
neteq_internal_->GetAudio(kMaxBlockSize,
|
||||
output_internal_,
|
||||
&samples_per_channel,
|
||||
&num_channels,
|
||||
&output_type));
|
||||
EXPECT_EQ(1, num_channels);
|
||||
EXPECT_EQ(kOutputLengthMs * sample_rate_hz_ / 1000, samples_per_channel);
|
||||
|
||||
// Get audio from external decoder instance.
|
||||
samples_per_channel = GetOutputAudio(kMaxBlockSize, output_, &output_type);
|
||||
|
||||
for (int i = 0; i < samples_per_channel; ++i) {
|
||||
ASSERT_EQ(output_[i], output_internal_[i]) <<
|
||||
"Diff in sample " << i << ".";
|
||||
}
|
||||
}
|
||||
|
||||
void InsertPacket(WebRtcRTPHeader rtp_header, const uint8_t* payload,
|
||||
size_t payload_size_bytes,
|
||||
uint32_t receive_timestamp) override {
|
||||
// Insert packet in internal decoder.
|
||||
ASSERT_EQ(
|
||||
NetEq::kOK,
|
||||
neteq_internal_->InsertPacket(
|
||||
rtp_header, payload, payload_size_bytes, receive_timestamp));
|
||||
|
||||
// Insert packet in external decoder instance.
|
||||
NetEqExternalDecoderUnitTest::InsertPacket(rtp_header, payload,
|
||||
payload_size_bytes,
|
||||
receive_timestamp);
|
||||
}
|
||||
|
||||
int NumExpectedDecodeCalls(int num_loops) override { return num_loops; }
|
||||
|
||||
private:
|
||||
int sample_rate_hz_;
|
||||
scoped_ptr<NetEq> neteq_internal_;
|
||||
int16_t output_internal_[kMaxBlockSize];
|
||||
int16_t output_[kMaxBlockSize];
|
||||
};
|
||||
|
||||
TEST_F(NetEqExternalVsInternalDecoderTest, RunTest) {
|
||||
RunTest(100); // Run 100 laps @ 10 ms each in the test loop.
|
||||
}
|
||||
|
||||
class LargeTimestampJumpTest : public NetEqExternalDecoderTest {
|
||||
class LargeTimestampJumpTest : public NetEqExternalDecoderUnitTest,
|
||||
public ::testing::Test {
|
||||
protected:
|
||||
static const int kMaxBlockSize = 480; // 10 ms @ 48 kHz.
|
||||
|
||||
enum TestStates {
|
||||
kInitialPhase,
|
||||
kNormalPhase,
|
||||
@ -234,60 +250,11 @@ class LargeTimestampJumpTest : public NetEqExternalDecoderTest {
|
||||
};
|
||||
|
||||
LargeTimestampJumpTest()
|
||||
: NetEqExternalDecoderTest(), test_state_(kInitialPhase) {
|
||||
sample_rate_hz_ = 8000;
|
||||
samples_per_ms_ = sample_rate_hz_ / 1000;
|
||||
frame_size_samples_ = frame_size_ms_ * samples_per_ms_;
|
||||
output_size_samples_ = frame_size_ms_ * samples_per_ms_;
|
||||
EXPECT_CALL(*external_decoder_, Die()).Times(1);
|
||||
external_decoder_.reset(new MockExternalPcm16B);
|
||||
}
|
||||
|
||||
void SetUp() OVERRIDE {
|
||||
const std::string file_name =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
||||
input_file_.reset(new test::InputAudioFile(file_name));
|
||||
assert(sample_rate_hz_ == 8000);
|
||||
NetEqDecoder decoder = kDecoderPCM16B;
|
||||
EXPECT_CALL(*external_decoder_, Init());
|
||||
EXPECT_CALL(*external_decoder_, HasDecodePlc())
|
||||
: NetEqExternalDecoderUnitTest(kDecoderPCM16B,
|
||||
new MockExternalPcm16B),
|
||||
test_state_(kInitialPhase) {
|
||||
EXPECT_CALL(*external_decoder(), HasDecodePlc())
|
||||
.WillRepeatedly(Return(false));
|
||||
// NetEq is not allowed to delete the external decoder (hence Times(0)).
|
||||
EXPECT_CALL(*external_decoder_, Die()).Times(0);
|
||||
ASSERT_EQ(NetEq::kOK,
|
||||
neteq_external_->RegisterExternalDecoder(
|
||||
external_decoder_.get(), decoder, kPayloadType));
|
||||
ASSERT_EQ(NetEq::kOK, neteq_->RegisterPayloadType(decoder, kPayloadType));
|
||||
}
|
||||
|
||||
void InsertPackets(int next_arrival_time) OVERRIDE {
|
||||
// Insert packet in external decoder instance.
|
||||
EXPECT_CALL(*external_decoder_,
|
||||
IncomingPacket(_,
|
||||
payload_size_bytes_,
|
||||
rtp_header_.header.sequenceNumber,
|
||||
rtp_header_.header.timestamp,
|
||||
next_arrival_time));
|
||||
ASSERT_EQ(
|
||||
NetEq::kOK,
|
||||
neteq_external_->InsertPacket(
|
||||
rtp_header_, encoded_, payload_size_bytes_, next_arrival_time));
|
||||
}
|
||||
|
||||
void GetOutputAudio() OVERRIDE {
|
||||
NetEqOutputType output_type;
|
||||
int samples_per_channel;
|
||||
int num_channels;
|
||||
// Get audio from external decoder instance.
|
||||
ASSERT_EQ(NetEq::kOK,
|
||||
neteq_external_->GetAudio(kMaxBlockSize,
|
||||
output_external_,
|
||||
&samples_per_channel,
|
||||
&num_channels,
|
||||
&output_type));
|
||||
EXPECT_EQ(1, num_channels);
|
||||
EXPECT_EQ(output_size_samples_, samples_per_channel);
|
||||
UpdateState(output_type);
|
||||
}
|
||||
|
||||
virtual void UpdateState(NetEqOutputType output_type) {
|
||||
@ -324,20 +291,26 @@ class LargeTimestampJumpTest : public NetEqExternalDecoderTest {
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyOutput(size_t num_samples) const OVERRIDE {
|
||||
void GetAndVerifyOutput() override {
|
||||
int num_samples;
|
||||
NetEqOutputType output_type;
|
||||
num_samples = GetOutputAudio(kMaxBlockSize, output_, &output_type);
|
||||
UpdateState(output_type);
|
||||
|
||||
if (test_state_ == kExpandPhase || test_state_ == kFadedExpandPhase) {
|
||||
// Don't verify the output in this phase of the test.
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < num_samples; ++i) {
|
||||
if (output_external_[i] != 0)
|
||||
|
||||
for (int i = 0; i < num_samples; ++i) {
|
||||
if (output_[i] != 0)
|
||||
return;
|
||||
}
|
||||
EXPECT_TRUE(false)
|
||||
<< "Expected at least one non-zero sample in each output block.";
|
||||
}
|
||||
|
||||
int NumExpectedDecodeCalls(int num_loops) const OVERRIDE {
|
||||
int NumExpectedDecodeCalls(int num_loops) override {
|
||||
// Some packets at the end of the stream won't be decoded. When the jump in
|
||||
// timestamp happens, NetEq will do Expand during one GetAudio call. In the
|
||||
// next call it will decode the packet after the jump, but the net result is
|
||||
@ -349,6 +322,9 @@ class LargeTimestampJumpTest : public NetEqExternalDecoderTest {
|
||||
}
|
||||
|
||||
TestStates test_state_;
|
||||
|
||||
private:
|
||||
int16_t output_[kMaxBlockSize];
|
||||
};
|
||||
|
||||
TEST_F(LargeTimestampJumpTest, JumpLongerThanHalfRange) {
|
||||
@ -365,11 +341,11 @@ TEST_F(LargeTimestampJumpTest, JumpLongerThanHalfRange) {
|
||||
static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) > 0x7FFFFFFF,
|
||||
"jump should be larger than half range");
|
||||
// Replace the default RTP generator with one that jumps in timestamp.
|
||||
rtp_generator_.reset(new test::TimestampJumpRtpGenerator(samples_per_ms_,
|
||||
kStartSeqeunceNumber,
|
||||
kStartTimestamp,
|
||||
kJumpFromTimestamp,
|
||||
kJumpToTimestamp));
|
||||
ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(),
|
||||
kStartSeqeunceNumber,
|
||||
kStartTimestamp,
|
||||
kJumpFromTimestamp,
|
||||
kJumpToTimestamp));
|
||||
|
||||
RunTest(130); // Run 130 laps @ 10 ms each in the test loop.
|
||||
EXPECT_EQ(kRecovered, test_state_);
|
||||
@ -389,11 +365,11 @@ TEST_F(LargeTimestampJumpTest, JumpLongerThanHalfRangeAndWrap) {
|
||||
static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) > 0x7FFFFFFF,
|
||||
"jump should be larger than half range");
|
||||
// Replace the default RTP generator with one that jumps in timestamp.
|
||||
rtp_generator_.reset(new test::TimestampJumpRtpGenerator(samples_per_ms_,
|
||||
kStartSeqeunceNumber,
|
||||
kStartTimestamp,
|
||||
kJumpFromTimestamp,
|
||||
kJumpToTimestamp));
|
||||
ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(),
|
||||
kStartSeqeunceNumber,
|
||||
kStartTimestamp,
|
||||
kJumpFromTimestamp,
|
||||
kJumpToTimestamp));
|
||||
|
||||
RunTest(130); // Run 130 laps @ 10 ms each in the test loop.
|
||||
EXPECT_EQ(kRecovered, test_state_);
|
||||
@ -428,7 +404,7 @@ class ShortTimestampJumpTest : public LargeTimestampJumpTest {
|
||||
}
|
||||
}
|
||||
|
||||
int NumExpectedDecodeCalls(int num_loops) const OVERRIDE {
|
||||
int NumExpectedDecodeCalls(int num_loops) override {
|
||||
// Some packets won't be decoded because of the timestamp jump.
|
||||
return num_loops - 2;
|
||||
}
|
||||
@ -448,11 +424,11 @@ TEST_F(ShortTimestampJumpTest, JumpShorterThanHalfRange) {
|
||||
static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) < 0x7FFFFFFF,
|
||||
"jump should be smaller than half range");
|
||||
// Replace the default RTP generator with one that jumps in timestamp.
|
||||
rtp_generator_.reset(new test::TimestampJumpRtpGenerator(samples_per_ms_,
|
||||
kStartSeqeunceNumber,
|
||||
kStartTimestamp,
|
||||
kJumpFromTimestamp,
|
||||
kJumpToTimestamp));
|
||||
ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(),
|
||||
kStartSeqeunceNumber,
|
||||
kStartTimestamp,
|
||||
kJumpFromTimestamp,
|
||||
kJumpToTimestamp));
|
||||
|
||||
RunTest(130); // Run 130 laps @ 10 ms each in the test loop.
|
||||
EXPECT_EQ(kRecovered, test_state_);
|
||||
@ -472,11 +448,11 @@ TEST_F(ShortTimestampJumpTest, JumpShorterThanHalfRangeAndWrap) {
|
||||
static_cast<uint32_t>(kJumpToTimestamp - kJumpFromTimestamp) < 0x7FFFFFFF,
|
||||
"jump should be smaller than half range");
|
||||
// Replace the default RTP generator with one that jumps in timestamp.
|
||||
rtp_generator_.reset(new test::TimestampJumpRtpGenerator(samples_per_ms_,
|
||||
kStartSeqeunceNumber,
|
||||
kStartTimestamp,
|
||||
kJumpFromTimestamp,
|
||||
kJumpToTimestamp));
|
||||
ResetRtpGenerator(new test::TimestampJumpRtpGenerator(samples_per_ms(),
|
||||
kStartSeqeunceNumber,
|
||||
kStartTimestamp,
|
||||
kJumpFromTimestamp,
|
||||
kJumpToTimestamp));
|
||||
|
||||
RunTest(130); // Run 130 laps @ 10 ms each in the test loop.
|
||||
EXPECT_EQ(kRecovered, test_state_);
|
||||
|
@ -1799,10 +1799,14 @@ int NetEqImpl::ExtractPackets(int required_samples, PacketList* packet_list) {
|
||||
if (packet->sync_packet) {
|
||||
packet_duration = decoder_frame_length_;
|
||||
} else {
|
||||
packet_duration = packet->primary ?
|
||||
decoder->PacketDuration(packet->payload, packet->payload_length) :
|
||||
decoder->PacketDurationRedundant(packet->payload,
|
||||
packet->payload_length);
|
||||
if (packet->primary) {
|
||||
packet_duration = decoder->PacketDuration(packet->payload,
|
||||
packet->payload_length);
|
||||
} else {
|
||||
packet_duration = decoder->
|
||||
PacketDurationRedundant(packet->payload, packet->payload_length);
|
||||
stats_.SecondaryDecodedSamples(packet_duration);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_FERR1(LS_WARNING, GetDecoder,
|
||||
|
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (c) 2015 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 "testing/gmock/include/gmock/gmock.h"
|
||||
#include "webrtc/modules/audio_coding/neteq/audio_decoder_impl.h"
|
||||
#include "webrtc/modules/audio_coding/neteq/tools/neteq_external_decoder_test.h"
|
||||
#include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::Return;
|
||||
|
||||
|
||||
class MockAudioDecoderOpus : public AudioDecoderOpus {
|
||||
public:
|
||||
static const int kPacketDuration = 960; // 48 kHz * 20 ms
|
||||
|
||||
explicit MockAudioDecoderOpus(int num_channels)
|
||||
: AudioDecoderOpus(num_channels),
|
||||
fec_enabled_(false) {
|
||||
}
|
||||
virtual ~MockAudioDecoderOpus() { Die(); }
|
||||
MOCK_METHOD0(Die, void());
|
||||
|
||||
MOCK_METHOD0(Init, int());
|
||||
|
||||
// Override the following methods such that no actual payload is needed.
|
||||
int Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded,
|
||||
SpeechType* speech_type) override {
|
||||
*speech_type = kSpeech;
|
||||
memset(decoded, 0, sizeof(int16_t) * kPacketDuration * channels_);
|
||||
return kPacketDuration * channels_;
|
||||
}
|
||||
|
||||
int DecodeRedundant(const uint8_t* encoded, size_t encoded_len,
|
||||
int16_t* decoded, SpeechType* speech_type) override {
|
||||
return Decode(encoded, encoded_len, decoded, speech_type);
|
||||
}
|
||||
|
||||
int PacketDuration(const uint8_t* encoded,
|
||||
size_t encoded_len) const override {
|
||||
return kPacketDuration;
|
||||
}
|
||||
|
||||
int PacketDurationRedundant(const uint8_t* encoded,
|
||||
size_t encoded_len) const override {
|
||||
return kPacketDuration;
|
||||
}
|
||||
|
||||
bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const override {
|
||||
return fec_enabled_;
|
||||
}
|
||||
|
||||
void set_fec_enabled(bool enable_fec) { fec_enabled_ = enable_fec; }
|
||||
|
||||
bool fec_enabled() const { return fec_enabled_; }
|
||||
|
||||
private:
|
||||
bool fec_enabled_;
|
||||
};
|
||||
|
||||
class NetEqNetworkStatsTest : public NetEqExternalDecoderTest {
|
||||
public:
|
||||
static const int kPayloadSizeByte = 30;
|
||||
static const int kFrameSizeMs = 20; // frame size of Opus
|
||||
static const int kMaxOutputSize = 960; // 10 ms * 48 kHz * 2 channels.
|
||||
|
||||
enum logic {
|
||||
IGNORE,
|
||||
EQUAL,
|
||||
SMALLER_THAN,
|
||||
LARGER_THAN,
|
||||
};
|
||||
|
||||
struct NetEqNetworkStatsCheck {
|
||||
logic current_buffer_size_ms;
|
||||
logic preferred_buffer_size_ms;
|
||||
logic jitter_peaks_found;
|
||||
logic packet_loss_rate;
|
||||
logic packet_discard_rate;
|
||||
logic expand_rate;
|
||||
logic preemptive_rate;
|
||||
logic accelerate_rate;
|
||||
logic secondary_decoded_rate;
|
||||
logic clockdrift_ppm;
|
||||
logic added_zero_samples;
|
||||
NetEqNetworkStatistics stats_ref;
|
||||
};
|
||||
|
||||
NetEqNetworkStatsTest(NetEqDecoder codec,
|
||||
MockAudioDecoderOpus* decoder)
|
||||
: NetEqExternalDecoderTest(codec, decoder),
|
||||
external_decoder_(decoder),
|
||||
samples_per_ms_(CodecSampleRateHz(codec) / 1000),
|
||||
frame_size_samples_(kFrameSizeMs * samples_per_ms_),
|
||||
rtp_generator_(new test::RtpGenerator(samples_per_ms_)),
|
||||
last_lost_time_(0),
|
||||
packet_loss_interval_(0xffffffff) {
|
||||
Init();
|
||||
}
|
||||
|
||||
bool Lost(uint32_t send_time) {
|
||||
if (send_time - last_lost_time_ >= packet_loss_interval_) {
|
||||
last_lost_time_ = send_time;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetPacketLossRate(double loss_rate) {
|
||||
packet_loss_interval_ = (loss_rate >= 1e-3 ?
|
||||
static_cast<double>(kFrameSizeMs) / loss_rate : 0xffffffff);
|
||||
}
|
||||
|
||||
// |stats_ref|
|
||||
// expects.x = -1, do not care
|
||||
// expects.x = 0, 'x' in current stats should equal 'x' in |stats_ref|
|
||||
// expects.x = 1, 'x' in current stats should < 'x' in |stats_ref|
|
||||
// expects.x = 2, 'x' in current stats should > 'x' in |stats_ref|
|
||||
void CheckNetworkStatistics(NetEqNetworkStatsCheck expects) {
|
||||
NetEqNetworkStatistics stats;
|
||||
neteq()->NetworkStatistics(&stats);
|
||||
|
||||
#define CHECK_NETEQ_NETWORK_STATS(x)\
|
||||
switch (expects.x) {\
|
||||
case EQUAL:\
|
||||
EXPECT_EQ(stats.x, expects.stats_ref.x);\
|
||||
break;\
|
||||
case SMALLER_THAN:\
|
||||
EXPECT_LT(stats.x, expects.stats_ref.x);\
|
||||
break;\
|
||||
case LARGER_THAN:\
|
||||
EXPECT_GT(stats.x, expects.stats_ref.x);\
|
||||
break;\
|
||||
default:\
|
||||
break;\
|
||||
}
|
||||
|
||||
CHECK_NETEQ_NETWORK_STATS(current_buffer_size_ms);
|
||||
CHECK_NETEQ_NETWORK_STATS(preferred_buffer_size_ms);
|
||||
CHECK_NETEQ_NETWORK_STATS(jitter_peaks_found);
|
||||
CHECK_NETEQ_NETWORK_STATS(packet_loss_rate);
|
||||
CHECK_NETEQ_NETWORK_STATS(packet_discard_rate);
|
||||
CHECK_NETEQ_NETWORK_STATS(expand_rate);
|
||||
CHECK_NETEQ_NETWORK_STATS(preemptive_rate);
|
||||
CHECK_NETEQ_NETWORK_STATS(accelerate_rate);
|
||||
CHECK_NETEQ_NETWORK_STATS(secondary_decoded_rate);
|
||||
CHECK_NETEQ_NETWORK_STATS(clockdrift_ppm);
|
||||
CHECK_NETEQ_NETWORK_STATS(added_zero_samples);
|
||||
|
||||
#undef CHECK_NETEQ_NETWORK_STATS
|
||||
}
|
||||
|
||||
void RunTest(int num_loops, NetEqNetworkStatsCheck expects) {
|
||||
NetEqOutputType output_type;
|
||||
uint32_t time_now;
|
||||
uint32_t next_send_time;
|
||||
|
||||
// Initiate |last_lost_time_|.
|
||||
time_now = next_send_time = last_lost_time_ =
|
||||
rtp_generator_->GetRtpHeader(kPayloadType, frame_size_samples_,
|
||||
&rtp_header_);
|
||||
for (int k = 0; k < num_loops; ++k) {
|
||||
// Delay by one frame such that the FEC can come in.
|
||||
while (time_now + kFrameSizeMs >= next_send_time) {
|
||||
next_send_time = rtp_generator_->GetRtpHeader(kPayloadType,
|
||||
frame_size_samples_,
|
||||
&rtp_header_);
|
||||
if (!Lost(next_send_time)) {
|
||||
InsertPacket(rtp_header_, payload_, kPayloadSizeByte,
|
||||
next_send_time);
|
||||
}
|
||||
}
|
||||
GetOutputAudio(kMaxOutputSize, output_, &output_type);
|
||||
time_now += kOutputLengthMs;
|
||||
}
|
||||
CheckNetworkStatistics(expects);
|
||||
neteq()->FlushBuffers();
|
||||
}
|
||||
|
||||
void DecodeFecTest() {
|
||||
external_decoder_->set_fec_enabled(false);
|
||||
NetEqNetworkStatsCheck expects = {
|
||||
IGNORE, // current_buffer_size_ms
|
||||
IGNORE, // preferred_buffer_size_ms
|
||||
IGNORE, // jitter_peaks_found
|
||||
EQUAL, // packet_loss_rate
|
||||
EQUAL, // packet_discard_rate
|
||||
EQUAL, // expand_rate
|
||||
IGNORE, // preemptive_rate
|
||||
EQUAL, // accelerate_rate
|
||||
EQUAL, // decoded_fec_rate
|
||||
IGNORE, // clockdrift_ppm
|
||||
EQUAL, // added_zero_samples
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
RunTest(50, expects);
|
||||
|
||||
// Next we introduce packet losses.
|
||||
SetPacketLossRate(0.1);
|
||||
expects.stats_ref.packet_loss_rate = 1337;
|
||||
expects.stats_ref.expand_rate = 1065;
|
||||
RunTest(50, expects);
|
||||
|
||||
// Next we enable Opus FEC.
|
||||
external_decoder_->set_fec_enabled(true);
|
||||
// If FEC fills in the lost packets, no packet loss will be counted.
|
||||
expects.stats_ref.packet_loss_rate = 0;
|
||||
expects.stats_ref.expand_rate = 0;
|
||||
expects.stats_ref.secondary_decoded_rate = 2006;
|
||||
RunTest(50, expects);
|
||||
}
|
||||
|
||||
private:
|
||||
MockAudioDecoderOpus* external_decoder_;
|
||||
const int samples_per_ms_;
|
||||
const size_t frame_size_samples_;
|
||||
scoped_ptr<test::RtpGenerator> rtp_generator_;
|
||||
WebRtcRTPHeader rtp_header_;
|
||||
uint32_t last_lost_time_;
|
||||
uint32_t packet_loss_interval_;
|
||||
uint8_t payload_[kPayloadSizeByte];
|
||||
int16_t output_[kMaxOutputSize];
|
||||
};
|
||||
|
||||
TEST(NetEqNetworkStatsTest, OpusDecodeFec) {
|
||||
MockAudioDecoderOpus decoder(1);
|
||||
EXPECT_CALL(decoder, Init());
|
||||
NetEqNetworkStatsTest test(kDecoderOpus, &decoder);
|
||||
test.DecodeFecTest();
|
||||
EXPECT_CALL(decoder, Die()).Times(1);
|
||||
}
|
||||
|
||||
TEST(NetEqNetworkStatsTest, StereoOpusDecodeFec) {
|
||||
MockAudioDecoderOpus decoder(2);
|
||||
EXPECT_CALL(decoder, Init());
|
||||
NetEqNetworkStatsTest test(kDecoderOpus, &decoder);
|
||||
test.DecodeFecTest();
|
||||
EXPECT_CALL(decoder, Die()).Times(1);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
|
||||
|
||||
|
@ -164,6 +164,8 @@
|
||||
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
|
||||
],
|
||||
'sources': [
|
||||
'tools/neteq_external_decoder_test.cc',
|
||||
'tools/neteq_external_decoder_test.h',
|
||||
'tools/neteq_performance_test.cc',
|
||||
'tools/neteq_performance_test.h',
|
||||
'tools/neteq_quality_test.cc',
|
||||
|
@ -137,13 +137,42 @@ void RefFiles::WriteToFile(const NetEqNetworkStatistics& stats) {
|
||||
|
||||
void RefFiles::ReadFromFileAndCompare(
|
||||
const NetEqNetworkStatistics& stats) {
|
||||
// TODO(minyue): Update resource/audio_coding/neteq_network_stats.dat and
|
||||
// resource/audio_coding/neteq_network_stats_win32.dat.
|
||||
struct NetEqNetworkStatisticsOld {
|
||||
uint16_t current_buffer_size_ms; // Current jitter buffer size in ms.
|
||||
uint16_t preferred_buffer_size_ms; // Target buffer size in ms.
|
||||
uint16_t jitter_peaks_found; // 1 if adding extra delay due to peaky
|
||||
// jitter; 0 otherwise.
|
||||
uint16_t packet_loss_rate; // Loss rate (network + late) in Q14.
|
||||
uint16_t packet_discard_rate; // Late loss rate in Q14.
|
||||
uint16_t expand_rate; // Fraction (of original stream) of synthesized
|
||||
// speech inserted through expansion (in Q14).
|
||||
uint16_t preemptive_rate; // Fraction of data inserted through pre-emptive
|
||||
// expansion (in Q14).
|
||||
uint16_t accelerate_rate; // Fraction of data removed through acceleration
|
||||
// (in Q14).
|
||||
int32_t clockdrift_ppm; // Average clock-drift in parts-per-million
|
||||
// (positive or negative).
|
||||
int added_zero_samples; // Number of zero samples added in "off" mode.
|
||||
};
|
||||
if (input_fp_) {
|
||||
// Read from ref file.
|
||||
size_t stat_size = sizeof(NetEqNetworkStatistics);
|
||||
NetEqNetworkStatistics ref_stats;
|
||||
size_t stat_size = sizeof(NetEqNetworkStatisticsOld);
|
||||
NetEqNetworkStatisticsOld ref_stats;
|
||||
ASSERT_EQ(1u, fread(&ref_stats, stat_size, 1, input_fp_));
|
||||
// Compare
|
||||
ASSERT_EQ(0, memcmp(&stats, &ref_stats, stat_size));
|
||||
ASSERT_EQ(stats.current_buffer_size_ms, ref_stats.current_buffer_size_ms);
|
||||
ASSERT_EQ(stats.preferred_buffer_size_ms,
|
||||
ref_stats.preferred_buffer_size_ms);
|
||||
ASSERT_EQ(stats.jitter_peaks_found, ref_stats.jitter_peaks_found);
|
||||
ASSERT_EQ(stats.packet_loss_rate, ref_stats.packet_loss_rate);
|
||||
ASSERT_EQ(stats.packet_discard_rate, ref_stats.packet_discard_rate);
|
||||
ASSERT_EQ(stats.preemptive_rate, ref_stats.preemptive_rate);
|
||||
ASSERT_EQ(stats.accelerate_rate, ref_stats.accelerate_rate);
|
||||
ASSERT_EQ(stats.clockdrift_ppm, ref_stats.clockdrift_ppm);
|
||||
ASSERT_EQ(stats.added_zero_samples, ref_stats.added_zero_samples);
|
||||
ASSERT_EQ(stats.secondary_decoded_rate, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,8 @@ StatisticsCalculator::StatisticsCalculator()
|
||||
lost_timestamps_(0),
|
||||
timestamps_since_last_report_(0),
|
||||
len_waiting_times_(0),
|
||||
next_waiting_time_index_(0) {
|
||||
next_waiting_time_index_(0),
|
||||
secondary_decoded_samples_(0) {
|
||||
memset(waiting_times_, 0, kLenWaitingTimes * sizeof(waiting_times_[0]));
|
||||
}
|
||||
|
||||
@ -38,6 +39,7 @@ void StatisticsCalculator::Reset() {
|
||||
added_zero_samples_ = 0;
|
||||
expanded_voice_samples_ = 0;
|
||||
expanded_noise_samples_ = 0;
|
||||
secondary_decoded_samples_ = 0;
|
||||
}
|
||||
|
||||
void StatisticsCalculator::ResetMcu() {
|
||||
@ -90,6 +92,10 @@ void StatisticsCalculator::IncreaseCounter(int num_samples, int fs_hz) {
|
||||
}
|
||||
}
|
||||
|
||||
void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {
|
||||
secondary_decoded_samples_ += num_samples;
|
||||
}
|
||||
|
||||
void StatisticsCalculator::StoreWaitingTime(int waiting_time_ms) {
|
||||
assert(next_waiting_time_index_ < kLenWaitingTimes);
|
||||
waiting_times_[next_waiting_time_index_] = waiting_time_ms;
|
||||
@ -140,6 +146,10 @@ void StatisticsCalculator::GetNetworkStatistics(
|
||||
CalculateQ14Ratio(expanded_voice_samples_ + expanded_noise_samples_,
|
||||
timestamps_since_last_report_);
|
||||
|
||||
stats->secondary_decoded_rate =
|
||||
CalculateQ14Ratio(secondary_decoded_samples_,
|
||||
timestamps_since_last_report_);
|
||||
|
||||
// Reset counters.
|
||||
ResetMcu();
|
||||
Reset();
|
||||
|
@ -70,6 +70,9 @@ class StatisticsCalculator {
|
||||
// Stores new packet waiting time in waiting time statistics.
|
||||
void StoreWaitingTime(int waiting_time_ms);
|
||||
|
||||
// Reports that |num_samples| samples were decoded from secondary packets.
|
||||
void SecondaryDecodedSamples(int num_samples);
|
||||
|
||||
// Returns the current network statistics in |stats|. The current sample rate
|
||||
// is |fs_hz|, the total number of samples in packet buffer and sync buffer
|
||||
// yet to play out is |num_samples_in_buffers|, and the number of samples per
|
||||
@ -101,6 +104,7 @@ class StatisticsCalculator {
|
||||
int waiting_times_[kLenWaitingTimes]; // Used as a circular buffer.
|
||||
int len_waiting_times_;
|
||||
int next_waiting_time_index_;
|
||||
uint32_t secondary_decoded_samples_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(StatisticsCalculator);
|
||||
};
|
||||
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2015 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/neteq/tools/neteq_external_decoder_test.h"
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
NetEqExternalDecoderTest::NetEqExternalDecoderTest(NetEqDecoder codec,
|
||||
AudioDecoder* decoder)
|
||||
: codec_(codec),
|
||||
decoder_(decoder),
|
||||
sample_rate_hz_(CodecSampleRateHz(codec_)),
|
||||
channels_(static_cast<int>(decoder_->channels())) {
|
||||
NetEq::Config config;
|
||||
config.sample_rate_hz = sample_rate_hz_;
|
||||
neteq_.reset(NetEq::Create(config));
|
||||
printf("%d\n", channels_);
|
||||
}
|
||||
|
||||
void NetEqExternalDecoderTest::Init() {
|
||||
ASSERT_EQ(NetEq::kOK,
|
||||
neteq_->RegisterExternalDecoder(decoder_, codec_, kPayloadType));
|
||||
}
|
||||
|
||||
void NetEqExternalDecoderTest::InsertPacket(WebRtcRTPHeader rtp_header,
|
||||
const uint8_t* payload,
|
||||
size_t payload_size_bytes,
|
||||
uint32_t receive_timestamp) {
|
||||
ASSERT_EQ(
|
||||
NetEq::kOK,
|
||||
neteq_->InsertPacket(
|
||||
rtp_header, payload, payload_size_bytes, receive_timestamp));
|
||||
}
|
||||
|
||||
int NetEqExternalDecoderTest::GetOutputAudio(size_t max_length,
|
||||
int16_t* output,
|
||||
NetEqOutputType* output_type) {
|
||||
// Get audio from regular instance.
|
||||
int samples_per_channel;
|
||||
int num_channels;
|
||||
EXPECT_EQ(NetEq::kOK,
|
||||
neteq_->GetAudio(max_length,
|
||||
output,
|
||||
&samples_per_channel,
|
||||
&num_channels,
|
||||
output_type));
|
||||
EXPECT_EQ(channels_, num_channels);
|
||||
EXPECT_EQ(kOutputLengthMs * sample_rate_hz_ / 1000, samples_per_channel);
|
||||
return samples_per_channel;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_EXTERNAL_DECODER_TEST_H_
|
||||
#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_EXTERNAL_DECODER_TEST_H_
|
||||
|
||||
#include "webrtc/modules/audio_coding/codecs/audio_decoder.h"
|
||||
#include "webrtc/modules/audio_coding/neteq/interface/neteq.h"
|
||||
#include "webrtc/modules/interface/module_common_types.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
// This test class provides a way run NetEQ with an external decoder.
|
||||
class NetEqExternalDecoderTest {
|
||||
protected:
|
||||
static const uint8_t kPayloadType = 95;
|
||||
static const int kOutputLengthMs = 10;
|
||||
|
||||
// The external decoder |decoder| is suppose to be of type |codec|.
|
||||
NetEqExternalDecoderTest(NetEqDecoder codec, AudioDecoder* decoder);
|
||||
|
||||
virtual ~NetEqExternalDecoderTest() { }
|
||||
|
||||
// In Init(), we register the external decoder.
|
||||
void Init();
|
||||
|
||||
// Inserts a new packet with |rtp_header| and |payload| of
|
||||
// |payload_size_bytes| bytes. The |receive_timestamp| is an indication
|
||||
// of the time when the packet was received, and should be measured with
|
||||
// the same tick rate as the RTP timestamp of the current payload.
|
||||
virtual void InsertPacket(WebRtcRTPHeader rtp_header, const uint8_t* payload,
|
||||
size_t payload_size_bytes,
|
||||
uint32_t receive_timestamp);
|
||||
|
||||
// Get 10 ms of audio data. The data is written to |output|, which can hold
|
||||
// (at least) |max_length| elements. Returns number of samples.
|
||||
int GetOutputAudio(size_t max_length, int16_t* output,
|
||||
NetEqOutputType* output_type);
|
||||
|
||||
NetEq* neteq() { return neteq_.get(); }
|
||||
|
||||
private:
|
||||
NetEqDecoder codec_;
|
||||
AudioDecoder* decoder_;
|
||||
int sample_rate_hz_;
|
||||
int channels_;
|
||||
scoped_ptr<NetEq> neteq_;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_EXTERNAL_DECODER_TEST_H_
|
@ -64,6 +64,7 @@
|
||||
'iSACFix',
|
||||
'media_file',
|
||||
'neteq',
|
||||
'neteq_test_support',
|
||||
'neteq_unittest_tools',
|
||||
'paced_sender',
|
||||
'PCM16B', # Needed by NetEq tests.
|
||||
@ -125,6 +126,7 @@
|
||||
'audio_coding/neteq/merge_unittest.cc',
|
||||
'audio_coding/neteq/neteq_external_decoder_unittest.cc',
|
||||
'audio_coding/neteq/neteq_impl_unittest.cc',
|
||||
'audio_coding/neteq/neteq_network_stats_unittest.cc',
|
||||
'audio_coding/neteq/neteq_stereo_unittest.cc',
|
||||
'audio_coding/neteq/neteq_unittest.cc',
|
||||
'audio_coding/neteq/normal_unittest.cc',
|
||||
|
Loading…
Reference in New Issue
Block a user