Implement a test for an old corner-case in NetEq
This CL implements a unit test to cover an case where comfort noise packets should be discarded. The situation arises when NetEq gets a duplicate comfort noise packet. Without this check, the duplicate would be decoded, and a the timing would shift. As it turned out, the corner-case funcionality was not completely accurate in NetEq4. This is because decision_logic_::cng_state_ is set after the corner-case check. In the old NetEq3, the corresponding state was changed before the check. This is now fixed. R=turaj@webrtc.org Review URL: https://webrtc-codereview.appspot.com/9639005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5685 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@ -870,7 +870,7 @@ int NetEqImpl::GetDecision(Operations* operation,
|
|||||||
}
|
}
|
||||||
const RTPHeader* header = packet_buffer_->NextRtpHeader();
|
const RTPHeader* header = packet_buffer_->NextRtpHeader();
|
||||||
|
|
||||||
if (decision_logic_->CngRfc3389On()) {
|
if (decision_logic_->CngRfc3389On() || last_mode_ == kModeRfc3389Cng) {
|
||||||
// Because of timestamp peculiarities, we have to "manually" disallow using
|
// Because of timestamp peculiarities, we have to "manually" disallow using
|
||||||
// a CNG packet with the same timestamp as the one that was last played.
|
// a CNG packet with the same timestamp as the one that was last played.
|
||||||
// This can happen when using redundancy and will cause the timing to shift.
|
// This can happen when using redundancy and will cause the timing to shift.
|
||||||
@ -878,7 +878,6 @@ int NetEqImpl::GetDecision(Operations* operation,
|
|||||||
decoder_database_->IsComfortNoise(header->payloadType) &&
|
decoder_database_->IsComfortNoise(header->payloadType) &&
|
||||||
end_timestamp >= header->timestamp) {
|
end_timestamp >= header->timestamp) {
|
||||||
// Don't use this packet, discard it.
|
// Don't use this packet, discard it.
|
||||||
// TODO(hlundin): Write test for this case.
|
|
||||||
if (packet_buffer_->DiscardNextPacket() != PacketBuffer::kOK) {
|
if (packet_buffer_->DiscardNextPacket() != PacketBuffer::kOK) {
|
||||||
assert(false); // Must be ok by design.
|
assert(false); // Must be ok by design.
|
||||||
}
|
}
|
||||||
|
@ -220,6 +220,7 @@ class NetEqDecodingTest : public ::testing::Test {
|
|||||||
bool expect_seq_no_wrap, bool expect_timestamp_wrap);
|
bool expect_seq_no_wrap, bool expect_timestamp_wrap);
|
||||||
|
|
||||||
void LongCngWithClockDrift(double drift_factor);
|
void LongCngWithClockDrift(double drift_factor);
|
||||||
|
void DuplicateCng();
|
||||||
|
|
||||||
NetEq* neteq_;
|
NetEq* neteq_;
|
||||||
FILE* rtp_fp_;
|
FILE* rtp_fp_;
|
||||||
@ -1143,7 +1144,7 @@ void NetEqDecodingTest::WrapTest(uint16_t start_seq_no,
|
|||||||
NetEqOutputType output_type;
|
NetEqOutputType output_type;
|
||||||
uint32_t receive_timestamp = 0;
|
uint32_t receive_timestamp = 0;
|
||||||
|
|
||||||
// Insert speech for 1 second.
|
// Insert speech for 2 seconds.
|
||||||
const int kSpeechDurationMs = 2000;
|
const int kSpeechDurationMs = 2000;
|
||||||
int packets_inserted = 0;
|
int packets_inserted = 0;
|
||||||
uint16_t last_seq_no;
|
uint16_t last_seq_no;
|
||||||
@ -1229,4 +1230,81 @@ TEST_F(NetEqDecodingTest, TimestampAndSequenceNumberWrap) {
|
|||||||
WrapTest(0xFFFF - 10, 0xFFFFFFFF - 5000, drop_seq_numbers, true, true);
|
WrapTest(0xFFFF - 10, 0xFFFFFFFF - 5000, drop_seq_numbers, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetEqDecodingTest::DuplicateCng() {
|
||||||
|
uint16_t seq_no = 0;
|
||||||
|
uint32_t timestamp = 0;
|
||||||
|
const int kFrameSizeMs = 10;
|
||||||
|
const int kSampleRateKhz = 16;
|
||||||
|
const int kSamples = kFrameSizeMs * kSampleRateKhz;
|
||||||
|
const int kPayloadBytes = kSamples * 2;
|
||||||
|
|
||||||
|
// Insert three speech packet. Three are needed to get the frame length
|
||||||
|
// correct.
|
||||||
|
int out_len;
|
||||||
|
int num_channels;
|
||||||
|
NetEqOutputType type;
|
||||||
|
uint8_t payload[kPayloadBytes] = {0};
|
||||||
|
WebRtcRTPHeader rtp_info;
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
PopulateRtpInfo(seq_no, timestamp, &rtp_info);
|
||||||
|
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0));
|
||||||
|
++seq_no;
|
||||||
|
timestamp += kSamples;
|
||||||
|
|
||||||
|
// Pull audio once.
|
||||||
|
ASSERT_EQ(0,
|
||||||
|
neteq_->GetAudio(
|
||||||
|
kMaxBlockSize, out_data_, &out_len, &num_channels, &type));
|
||||||
|
ASSERT_EQ(kBlockSize16kHz, out_len);
|
||||||
|
}
|
||||||
|
// Verify speech output.
|
||||||
|
EXPECT_EQ(kOutputNormal, type);
|
||||||
|
|
||||||
|
// Insert same CNG packet twice.
|
||||||
|
const int kCngPeriodMs = 100;
|
||||||
|
const int kCngPeriodSamples = kCngPeriodMs * kSampleRateKhz;
|
||||||
|
int payload_len;
|
||||||
|
PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len);
|
||||||
|
// This is the first time this CNG packet is inserted.
|
||||||
|
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, payload_len, 0));
|
||||||
|
|
||||||
|
// Pull audio once and make sure CNG is played.
|
||||||
|
ASSERT_EQ(0,
|
||||||
|
neteq_->GetAudio(
|
||||||
|
kMaxBlockSize, out_data_, &out_len, &num_channels, &type));
|
||||||
|
ASSERT_EQ(kBlockSize16kHz, out_len);
|
||||||
|
EXPECT_EQ(kOutputCNG, type);
|
||||||
|
EXPECT_EQ(timestamp - 10, neteq_->PlayoutTimestamp());
|
||||||
|
|
||||||
|
// Insert the same CNG packet again. Note that at this point it is old, since
|
||||||
|
// we have already decoded the first copy of it.
|
||||||
|
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, payload_len, 0));
|
||||||
|
|
||||||
|
// Pull audio until we have played |kCngPeriodMs| of CNG. Start at 10 ms since
|
||||||
|
// we have already pulled out CNG once.
|
||||||
|
for (int cng_time_ms = 10; cng_time_ms < kCngPeriodMs; cng_time_ms += 10) {
|
||||||
|
ASSERT_EQ(0,
|
||||||
|
neteq_->GetAudio(
|
||||||
|
kMaxBlockSize, out_data_, &out_len, &num_channels, &type));
|
||||||
|
ASSERT_EQ(kBlockSize16kHz, out_len);
|
||||||
|
EXPECT_EQ(kOutputCNG, type);
|
||||||
|
EXPECT_EQ(timestamp - 10, neteq_->PlayoutTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert speech again.
|
||||||
|
++seq_no;
|
||||||
|
timestamp += kCngPeriodSamples;
|
||||||
|
PopulateRtpInfo(seq_no, timestamp, &rtp_info);
|
||||||
|
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes, 0));
|
||||||
|
|
||||||
|
// Pull audio once and verify that the output is speech again.
|
||||||
|
ASSERT_EQ(0,
|
||||||
|
neteq_->GetAudio(
|
||||||
|
kMaxBlockSize, out_data_, &out_len, &num_channels, &type));
|
||||||
|
ASSERT_EQ(kBlockSize16kHz, out_len);
|
||||||
|
EXPECT_EQ(kOutputNormal, type);
|
||||||
|
EXPECT_EQ(timestamp + kSamples - 10, neteq_->PlayoutTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(NetEqDecodingTest, DiscardDuplicateCng) { DuplicateCng(); }
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
Reference in New Issue
Block a user