Refactor NetEq insert packet list.

Move some logic from PacketBuffer to NetEqImpl.

Bug: webrtc:13322
Change-Id: I88b1e55c0cd69700730d9ed41be04fcf1effa03f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/328861
Commit-Queue: Jakob Ivarsson‎ <jakobi@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41270}
This commit is contained in:
Jakob Ivarsson 2023-11-27 13:20:27 +01:00 committed by WebRTC LUCI CQ
parent 96e14c82b9
commit 526187708d
7 changed files with 184 additions and 359 deletions

View File

@ -27,13 +27,6 @@ class MockPacketBuffer : public PacketBuffer {
MOCK_METHOD(void, Flush, (), (override));
MOCK_METHOD(bool, Empty, (), (const, override));
MOCK_METHOD(int, InsertPacket, (Packet && packet), (override));
MOCK_METHOD(int,
InsertPacketList,
(PacketList * packet_list,
const DecoderDatabase& decoder_database,
absl::optional<uint8_t>* current_rtp_payload_type,
absl::optional<uint8_t>* current_cng_rtp_payload_type),
(override));
MOCK_METHOD(int,
NextTimestamp,
(uint32_t * next_timestamp),

View File

@ -70,6 +70,62 @@ std::unique_ptr<NetEqController> CreateNetEqController(
return controller_factory.CreateNetEqController(config);
}
void SetAudioFrameActivityAndType(bool vad_enabled,
NetEqImpl::OutputType type,
AudioFrame::VADActivity last_vad_activity,
AudioFrame* audio_frame) {
switch (type) {
case NetEqImpl::OutputType::kNormalSpeech: {
audio_frame->speech_type_ = AudioFrame::kNormalSpeech;
audio_frame->vad_activity_ = AudioFrame::kVadActive;
break;
}
case NetEqImpl::OutputType::kVadPassive: {
// This should only be reached if the VAD is enabled.
RTC_DCHECK(vad_enabled);
audio_frame->speech_type_ = AudioFrame::kNormalSpeech;
audio_frame->vad_activity_ = AudioFrame::kVadPassive;
break;
}
case NetEqImpl::OutputType::kCNG: {
audio_frame->speech_type_ = AudioFrame::kCNG;
audio_frame->vad_activity_ = AudioFrame::kVadPassive;
break;
}
case NetEqImpl::OutputType::kPLC: {
audio_frame->speech_type_ = AudioFrame::kPLC;
audio_frame->vad_activity_ = last_vad_activity;
break;
}
case NetEqImpl::OutputType::kPLCCNG: {
audio_frame->speech_type_ = AudioFrame::kPLCCNG;
audio_frame->vad_activity_ = AudioFrame::kVadPassive;
break;
}
case NetEqImpl::OutputType::kCodecPLC: {
audio_frame->speech_type_ = AudioFrame::kCodecPLC;
audio_frame->vad_activity_ = last_vad_activity;
break;
}
default:
RTC_DCHECK_NOTREACHED();
}
if (!vad_enabled) {
// Always set kVadUnknown when receive VAD is inactive.
audio_frame->vad_activity_ = AudioFrame::kVadUnknown;
}
}
// Returns true if both payload types are known to the decoder database, and
// have the same sample rate.
bool EqualSampleRates(uint8_t pt1,
uint8_t pt2,
const DecoderDatabase& decoder_database) {
auto* di1 = decoder_database.GetDecoderInfo(pt1);
auto* di2 = decoder_database.GetDecoderInfo(pt2);
return di1 && di2 && di1->SampleRateHz() == di2->SampleRateHz();
}
} // namespace
NetEqImpl::Dependencies::Dependencies(
@ -183,54 +239,6 @@ void NetEqImpl::InsertEmptyPacket(const RTPHeader& rtp_header) {
controller_->RegisterEmptyPacket();
}
namespace {
void SetAudioFrameActivityAndType(bool vad_enabled,
NetEqImpl::OutputType type,
AudioFrame::VADActivity last_vad_activity,
AudioFrame* audio_frame) {
switch (type) {
case NetEqImpl::OutputType::kNormalSpeech: {
audio_frame->speech_type_ = AudioFrame::kNormalSpeech;
audio_frame->vad_activity_ = AudioFrame::kVadActive;
break;
}
case NetEqImpl::OutputType::kVadPassive: {
// This should only be reached if the VAD is enabled.
RTC_DCHECK(vad_enabled);
audio_frame->speech_type_ = AudioFrame::kNormalSpeech;
audio_frame->vad_activity_ = AudioFrame::kVadPassive;
break;
}
case NetEqImpl::OutputType::kCNG: {
audio_frame->speech_type_ = AudioFrame::kCNG;
audio_frame->vad_activity_ = AudioFrame::kVadPassive;
break;
}
case NetEqImpl::OutputType::kPLC: {
audio_frame->speech_type_ = AudioFrame::kPLC;
audio_frame->vad_activity_ = last_vad_activity;
break;
}
case NetEqImpl::OutputType::kPLCCNG: {
audio_frame->speech_type_ = AudioFrame::kPLCCNG;
audio_frame->vad_activity_ = AudioFrame::kVadPassive;
break;
}
case NetEqImpl::OutputType::kCodecPLC: {
audio_frame->speech_type_ = AudioFrame::kCodecPLC;
audio_frame->vad_activity_ = last_vad_activity;
break;
}
default:
RTC_DCHECK_NOTREACHED();
}
if (!vad_enabled) {
// Always set kVadUnknown when receive VAD is inactive.
audio_frame->vad_activity_ = AudioFrame::kVadUnknown;
}
}
} // namespace
int NetEqImpl::GetAudio(AudioFrame* audio_frame,
bool* muted,
int* current_sample_rate_hz,
@ -681,18 +689,25 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header,
number_of_primary_packets);
}
// Insert packets in buffer.
const int ret = packet_buffer_->InsertPacketList(
&parsed_packet_list, *decoder_database_, &current_rtp_payload_type_,
&current_cng_rtp_payload_type_);
bool buffer_flush_occured = false;
if (ret == PacketBuffer::kFlushed) {
for (Packet& packet : parsed_packet_list) {
if (MaybeChangePayloadType(packet.payload_type)) {
packet_buffer_->Flush();
buffer_flush_occured = true;
}
int return_val = packet_buffer_->InsertPacket(std::move(packet));
if (return_val == PacketBuffer::kFlushed) {
buffer_flush_occured = true;
} else if (return_val != PacketBuffer::kOK) {
// An error occurred.
return kOtherError;
}
}
if (buffer_flush_occured) {
// Reset DSP timestamp etc. if packet buffer flushed.
new_codec_ = true;
update_sample_rate_and_channels = true;
buffer_flush_occured = true;
} else if (ret != PacketBuffer::kOK) {
return kOtherError;
}
if (first_packet_) {
@ -759,6 +774,31 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header,
return 0;
}
bool NetEqImpl::MaybeChangePayloadType(uint8_t payload_type) {
bool changed = false;
if (decoder_database_->IsComfortNoise(payload_type)) {
if (current_cng_rtp_payload_type_ &&
*current_cng_rtp_payload_type_ != payload_type) {
// New CNG payload type implies new codec type.
current_rtp_payload_type_ = absl::nullopt;
changed = true;
}
current_cng_rtp_payload_type_ = payload_type;
} else if (!decoder_database_->IsDtmf(payload_type)) {
// This must be speech.
if ((current_rtp_payload_type_ &&
*current_rtp_payload_type_ != payload_type) ||
(current_cng_rtp_payload_type_ &&
!EqualSampleRates(payload_type, *current_cng_rtp_payload_type_,
*decoder_database_))) {
current_cng_rtp_payload_type_ = absl::nullopt;
changed = true;
}
current_rtp_payload_type_ = payload_type;
}
return changed;
}
int NetEqImpl::GetAudioInternal(AudioFrame* audio_frame,
bool* muted,
absl::optional<Operation> action_override) {

View File

@ -27,6 +27,7 @@
#include "modules/audio_coding/neteq/audio_multi_vector.h"
#include "modules/audio_coding/neteq/expand_uma_logger.h"
#include "modules/audio_coding/neteq/packet.h"
#include "modules/audio_coding/neteq/packet_buffer.h"
#include "modules/audio_coding/neteq/random_vector.h"
#include "modules/audio_coding/neteq/statistics_calculator.h"
#include "rtc_base/synchronization/mutex.h"
@ -46,7 +47,6 @@ class Expand;
class Merge;
class NackTracker;
class Normal;
class PacketBuffer;
class RedPayloadSplitter;
class PostDecodeVad;
class PreemptiveExpand;
@ -215,6 +215,12 @@ class NetEqImpl : public webrtc::NetEq {
rtc::ArrayView<const uint8_t> payload)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if the payload type changed (this should be followed by
// resetting various state). Returns false if the current payload type is
// unknown or equal to `payload_type`.
bool MaybeChangePayloadType(uint8_t payload_type)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Delivers 10 ms of audio data. The data is written to `audio_frame`.
// Returns 0 on success, otherwise an error code.
int GetAudioInternal(AudioFrame* audio_frame,

View File

@ -329,14 +329,9 @@ TEST_F(NetEqImplTest, InsertPacket) {
EXPECT_CALL(*mock_packet_buffer_, Empty())
.WillOnce(Return(false)); // Called once after first packet is inserted.
EXPECT_CALL(*mock_packet_buffer_, Flush()).Times(1);
EXPECT_CALL(*mock_packet_buffer_, InsertPacketList(_, _, _, _))
EXPECT_CALL(*mock_packet_buffer_, InsertPacket(_))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<2>(kPayloadType),
WithArg<0>(Invoke(DeletePacketsAndReturnOk))));
// SetArgPointee<2>(kPayloadType) means that the third argument (zero-based
// index) is a pointer, and the variable pointed to is set to kPayloadType.
// Also invoke the function DeletePacketsAndReturnOk to properly delete all
// packets in the list (to avoid memory leaks in the test).
.WillRepeatedly(Return(PacketBuffer::kOK));
EXPECT_CALL(*mock_packet_buffer_, PeekNextPacket())
.Times(1)
.WillOnce(Return(&fake_packet));
@ -1642,6 +1637,74 @@ TEST_F(NetEqImplTest, NoCrashWith1000Channels) {
}
}
// The test first inserts a packet with narrow-band CNG, then a packet with
// wide-band speech. The expected behavior is to detect a change in sample rate,
// even though no speech packet has been inserted before, and flush out the CNG
// packet.
TEST_F(NetEqImplTest, CngFirstThenSpeechWithNewSampleRate) {
UseNoMocks();
CreateInstance();
constexpr int kCnPayloadType = 7;
neteq_->RegisterPayloadType(kCnPayloadType, SdpAudioFormat("cn", 8000, 1));
constexpr int kSpeechPayloadType = 8;
neteq_->RegisterPayloadType(kSpeechPayloadType,
SdpAudioFormat("l16", 16000, 1));
RTPHeader header;
header.payloadType = kCnPayloadType;
uint8_t payload[320] = {0};
EXPECT_EQ(neteq_->InsertPacket(header, payload), NetEq::kOK);
EXPECT_EQ(neteq_->GetLifetimeStatistics().packets_discarded, 0u);
header.payloadType = kSpeechPayloadType;
header.timestamp += 160;
EXPECT_EQ(neteq_->InsertPacket(header, payload), NetEq::kOK);
// CN packet should be discarded, since it does not match the
// new speech sample rate.
EXPECT_EQ(neteq_->GetLifetimeStatistics().packets_discarded, 1u);
// Next decoded packet should be speech.
AudioFrame audio_frame;
bool muted;
EXPECT_EQ(neteq_->GetAudio(&audio_frame, &muted), NetEq::kOK);
EXPECT_EQ(audio_frame.sample_rate_hz(), 16000);
EXPECT_EQ(audio_frame.speech_type_, AudioFrame::SpeechType::kNormalSpeech);
}
TEST_F(NetEqImplTest, InsertPacketChangePayloadType) {
UseNoMocks();
CreateInstance();
constexpr int kPcmuPayloadType = 7;
neteq_->RegisterPayloadType(kPcmuPayloadType,
SdpAudioFormat("pcmu", 8000, 1));
constexpr int kPcmaPayloadType = 8;
neteq_->RegisterPayloadType(kPcmaPayloadType,
SdpAudioFormat("pcma", 8000, 1));
RTPHeader header;
header.payloadType = kPcmuPayloadType;
header.timestamp = 1234;
uint8_t payload[160] = {0};
EXPECT_EQ(neteq_->InsertPacket(header, payload), NetEq::kOK);
EXPECT_EQ(neteq_->GetLifetimeStatistics().packets_discarded, 0u);
header.payloadType = kPcmaPayloadType;
header.timestamp += 80;
EXPECT_EQ(neteq_->InsertPacket(header, payload), NetEq::kOK);
// The previous packet should be discarded since the codec changed.
EXPECT_EQ(neteq_->GetLifetimeStatistics().packets_discarded, 1u);
// Next decoded packet should be speech.
AudioFrame audio_frame;
bool muted;
EXPECT_EQ(neteq_->GetAudio(&audio_frame, &muted), NetEq::kOK);
EXPECT_EQ(audio_frame.sample_rate_hz(), 8000);
EXPECT_EQ(audio_frame.speech_type_, AudioFrame::SpeechType::kNormalSpeech);
// TODO(jakobi): check active decoder.
}
class Decoder120ms : public AudioDecoder {
public:
Decoder120ms(int sample_rate_hz, SpeechType speech_type)

View File

@ -44,16 +44,6 @@ class NewTimestampIsLarger {
const Packet& new_packet_;
};
// Returns true if both payload types are known to the decoder database, and
// have the same sample rate.
bool EqualSampleRates(uint8_t pt1,
uint8_t pt2,
const DecoderDatabase& decoder_database) {
auto* di1 = decoder_database.GetDecoderInfo(pt1);
auto* di2 = decoder_database.GetDecoderInfo(pt2);
return di1 && di2 && di1->SampleRateHz() == di2->SampleRateHz();
}
} // namespace
PacketBuffer::PacketBuffer(size_t max_number_of_packets,
@ -128,50 +118,6 @@ int PacketBuffer::InsertPacket(Packet&& packet) {
return return_val;
}
int PacketBuffer::InsertPacketList(
PacketList* packet_list,
const DecoderDatabase& decoder_database,
absl::optional<uint8_t>* current_rtp_payload_type,
absl::optional<uint8_t>* current_cng_rtp_payload_type) {
bool flushed = false;
for (auto& packet : *packet_list) {
if (decoder_database.IsComfortNoise(packet.payload_type)) {
if (*current_cng_rtp_payload_type &&
**current_cng_rtp_payload_type != packet.payload_type) {
// New CNG payload type implies new codec type.
*current_rtp_payload_type = absl::nullopt;
Flush();
flushed = true;
}
*current_cng_rtp_payload_type = packet.payload_type;
} else if (!decoder_database.IsDtmf(packet.payload_type)) {
// This must be speech.
if ((*current_rtp_payload_type &&
**current_rtp_payload_type != packet.payload_type) ||
(*current_cng_rtp_payload_type &&
!EqualSampleRates(packet.payload_type,
**current_cng_rtp_payload_type,
decoder_database))) {
*current_cng_rtp_payload_type = absl::nullopt;
Flush();
flushed = true;
}
*current_rtp_payload_type = packet.payload_type;
}
int return_val = InsertPacket(std::move(packet));
if (return_val == kFlushed) {
// The buffer flushed, but this is not an error. We can still continue.
flushed = true;
} else if (return_val != kOK) {
// An error occurred. Delete remaining packets in list and return.
packet_list->clear();
return return_val;
}
}
packet_list->clear();
return flushed ? kFlushed : kOK;
}
int PacketBuffer::NextTimestamp(uint32_t* next_timestamp) const {
if (Empty()) {
return kBufferEmpty;

View File

@ -58,20 +58,6 @@ class PacketBuffer {
// was flushed due to overfilling.
virtual int InsertPacket(Packet&& packet);
// Inserts a list of packets into the buffer. The buffer will take over
// ownership of the packet objects.
// Returns PacketBuffer::kOK if all packets were inserted successfully.
// If the buffer was flushed due to overfilling, only a subset of the list is
// inserted, and PacketBuffer::kFlushed is returned.
// The last three parameters are included for legacy compatibility.
// TODO(hlundin): Redesign to not use current_*_payload_type and
// decoder_database.
virtual int InsertPacketList(
PacketList* packet_list,
const DecoderDatabase& decoder_database,
absl::optional<uint8_t>* current_rtp_payload_type,
absl::optional<uint8_t>* current_cng_rtp_payload_type);
// Gets the timestamp for the first packet in the buffer and writes it to the
// output variable `next_timestamp`.
// Returns PacketBuffer::kBufferEmpty if the buffer is empty,

View File

@ -196,92 +196,6 @@ TEST(PacketBuffer, OverfillBuffer) {
EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
}
// Test inserting a list of packets.
TEST(PacketBuffer, InsertPacketList) {
TickTimer tick_timer;
StrictMock<MockStatisticsCalculator> mock_stats;
PacketBuffer buffer(10, &tick_timer, &mock_stats); // 10 packets.
PacketGenerator gen(0, 0, 0, 10);
PacketList list;
const int payload_len = 10;
// Insert 10 small packets.
for (int i = 0; i < 10; ++i) {
list.push_back(gen.NextPacket(payload_len, nullptr));
}
MockDecoderDatabase decoder_database;
auto factory = CreateBuiltinAudioDecoderFactory();
const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1),
absl::nullopt, factory.get());
EXPECT_CALL(decoder_database, GetDecoderInfo(0))
.WillRepeatedly(Return(&info));
absl::optional<uint8_t> current_pt;
absl::optional<uint8_t> current_cng_pt;
EXPECT_EQ(PacketBuffer::kOK,
buffer.InsertPacketList(
/*packet_list=*/&list,
/*decoder_database=*/decoder_database,
/*current_rtp_payload_type=*/&current_pt,
/*current_cng_rtp_payload_type=*/&current_cng_pt));
EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list.
EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
EXPECT_EQ(0, current_pt); // Current payload type changed to 0.
EXPECT_EQ(absl::nullopt, current_cng_pt); // CNG payload type not changed.
EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
}
// Test inserting a list of packets. Last packet is of a different payload type.
// Expecting the buffer to flush.
// TODO(hlundin): Remove this test when legacy operation is no longer needed.
TEST(PacketBuffer, InsertPacketListChangePayloadType) {
TickTimer tick_timer;
StrictMock<MockStatisticsCalculator> mock_stats;
PacketBuffer buffer(10, &tick_timer, &mock_stats); // 10 packets.
PacketGenerator gen(0, 0, 0, 10);
PacketList list;
const int payload_len = 10;
// Insert 10 small packets.
for (int i = 0; i < 10; ++i) {
list.push_back(gen.NextPacket(payload_len, nullptr));
}
// Insert 11th packet of another payload type (not CNG).
{
Packet packet = gen.NextPacket(payload_len, nullptr);
packet.payload_type = 1;
list.push_back(std::move(packet));
}
MockDecoderDatabase decoder_database;
auto factory = CreateBuiltinAudioDecoderFactory();
const DecoderDatabase::DecoderInfo info0(SdpAudioFormat("pcmu", 8000, 1),
absl::nullopt, factory.get());
EXPECT_CALL(decoder_database, GetDecoderInfo(0))
.WillRepeatedly(Return(&info0));
const DecoderDatabase::DecoderInfo info1(SdpAudioFormat("pcma", 8000, 1),
absl::nullopt, factory.get());
EXPECT_CALL(decoder_database, GetDecoderInfo(1))
.WillRepeatedly(Return(&info1));
absl::optional<uint8_t> current_pt;
absl::optional<uint8_t> current_cng_pt;
EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(10);
EXPECT_EQ(PacketBuffer::kFlushed,
buffer.InsertPacketList(
/*packet_list=*/&list,
/*decoder_database=*/decoder_database,
/*current_rtp_payload_type=*/&current_pt,
/*current_cng_rtp_payload_type=*/&current_cng_pt));
EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list.
EXPECT_EQ(1u, buffer.NumPacketsInBuffer()); // Only the last packet.
EXPECT_EQ(1, current_pt); // Current payload type changed to 1.
EXPECT_EQ(absl::nullopt, current_cng_pt); // CNG payload type not changed.
EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
}
TEST(PacketBuffer, ExtractOrderRedundancy) {
TickTimer tick_timer;
@ -434,21 +348,9 @@ TEST(PacketBuffer, Reordering) {
}
}
MockDecoderDatabase decoder_database;
auto factory = CreateBuiltinAudioDecoderFactory();
const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1),
absl::nullopt, factory.get());
EXPECT_CALL(decoder_database, GetDecoderInfo(0))
.WillRepeatedly(Return(&info));
absl::optional<uint8_t> current_pt;
absl::optional<uint8_t> current_cng_pt;
EXPECT_EQ(PacketBuffer::kOK,
buffer.InsertPacketList(
/*packet_list=*/&list,
/*decoder_database=*/decoder_database,
/*current_rtp_payload_type=*/&current_pt,
/*current_cng_rtp_payload_type=*/&current_cng_pt));
for (Packet& packet : list) {
EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(std::move(packet)));
}
EXPECT_EQ(10u, buffer.NumPacketsInBuffer());
// Extract them and make sure that come out in the right order.
@ -460,77 +362,6 @@ TEST(PacketBuffer, Reordering) {
current_ts += ts_increment;
}
EXPECT_TRUE(buffer.Empty());
EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
}
// The test first inserts a packet with narrow-band CNG, then a packet with
// wide-band speech. The expected behavior of the packet buffer is to detect a
// change in sample rate, even though no speech packet has been inserted before,
// and flush out the CNG packet.
TEST(PacketBuffer, CngFirstThenSpeechWithNewSampleRate) {
TickTimer tick_timer;
StrictMock<MockStatisticsCalculator> mock_stats;
PacketBuffer buffer(10, &tick_timer, &mock_stats); // 10 packets.
const uint8_t kCngPt = 13;
const int kPayloadLen = 10;
const uint8_t kSpeechPt = 100;
MockDecoderDatabase decoder_database;
auto factory = CreateBuiltinAudioDecoderFactory();
const DecoderDatabase::DecoderInfo info_cng(SdpAudioFormat("cn", 8000, 1),
absl::nullopt, factory.get());
EXPECT_CALL(decoder_database, GetDecoderInfo(kCngPt))
.WillRepeatedly(Return(&info_cng));
const DecoderDatabase::DecoderInfo info_speech(
SdpAudioFormat("l16", 16000, 1), absl::nullopt, factory.get());
EXPECT_CALL(decoder_database, GetDecoderInfo(kSpeechPt))
.WillRepeatedly(Return(&info_speech));
// Insert first packet, which is narrow-band CNG.
PacketGenerator gen(0, 0, kCngPt, 10);
PacketList list;
list.push_back(gen.NextPacket(kPayloadLen, nullptr));
absl::optional<uint8_t> current_pt;
absl::optional<uint8_t> current_cng_pt;
EXPECT_EQ(PacketBuffer::kOK,
buffer.InsertPacketList(
/*packet_list=*/&list,
/*decoder_database=*/decoder_database,
/*current_rtp_payload_type=*/&current_pt,
/*current_cng_rtp_payload_type=*/&current_cng_pt));
EXPECT_TRUE(list.empty());
EXPECT_EQ(1u, buffer.NumPacketsInBuffer());
ASSERT_TRUE(buffer.PeekNextPacket());
EXPECT_EQ(kCngPt, buffer.PeekNextPacket()->payload_type);
EXPECT_EQ(current_pt, absl::nullopt); // Current payload type not set.
EXPECT_EQ(kCngPt, current_cng_pt); // CNG payload type set.
// Insert second packet, which is wide-band speech.
{
Packet packet = gen.NextPacket(kPayloadLen, nullptr);
packet.payload_type = kSpeechPt;
list.push_back(std::move(packet));
}
// Expect the buffer to flush out the CNG packet, since it does not match the
// new speech sample rate.
EXPECT_CALL(mock_stats, PacketsDiscarded(1));
EXPECT_EQ(PacketBuffer::kFlushed,
buffer.InsertPacketList(
/*packet_list=*/&list,
/*decoder_database=*/decoder_database,
/*current_rtp_payload_type=*/&current_pt,
/*current_cng_rtp_payload_type=*/&current_cng_pt));
EXPECT_TRUE(list.empty());
EXPECT_EQ(1u, buffer.NumPacketsInBuffer());
ASSERT_TRUE(buffer.PeekNextPacket());
EXPECT_EQ(kSpeechPt, buffer.PeekNextPacket()->payload_type);
EXPECT_EQ(kSpeechPt, current_pt); // Current payload type set.
EXPECT_EQ(absl::nullopt, current_cng_pt); // CNG payload type reset.
EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
}
TEST(PacketBuffer, Failures) {
@ -541,66 +372,26 @@ TEST(PacketBuffer, Failures) {
PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment);
TickTimer tick_timer;
StrictMock<MockStatisticsCalculator> mock_stats;
MockDecoderDatabase decoder_database;
PacketBuffer* buffer =
new PacketBuffer(100, &tick_timer, &mock_stats); // 100 packets.
PacketBuffer buffer(100, &tick_timer, &mock_stats); // 100 packets.
{
Packet packet = gen.NextPacket(payload_len, nullptr);
packet.payload.Clear();
EXPECT_EQ(PacketBuffer::kInvalidPacket,
buffer->InsertPacket(/*packet=*/std::move(packet)));
buffer.InsertPacket(/*packet=*/std::move(packet)));
}
// Buffer should still be empty. Test all empty-checks.
uint32_t temp_ts;
EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->NextTimestamp(&temp_ts));
EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer.NextTimestamp(&temp_ts));
EXPECT_EQ(PacketBuffer::kBufferEmpty,
buffer->NextHigherTimestamp(0, &temp_ts));
EXPECT_EQ(NULL, buffer->PeekNextPacket());
EXPECT_FALSE(buffer->GetNextPacket());
buffer.NextHigherTimestamp(0, &temp_ts));
EXPECT_EQ(NULL, buffer.PeekNextPacket());
EXPECT_FALSE(buffer.GetNextPacket());
// Discarding packets will not invoke mock_stats.PacketDiscarded() because the
// packet buffer is empty.
EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->DiscardNextPacket());
buffer->DiscardAllOldPackets(0);
// Insert one packet to make the buffer non-empty.
EXPECT_EQ(PacketBuffer::kOK, buffer->InsertPacket(/*packet=*/gen.NextPacket(
payload_len, nullptr)));
EXPECT_EQ(PacketBuffer::kInvalidPointer, buffer->NextTimestamp(NULL));
EXPECT_EQ(PacketBuffer::kInvalidPointer,
buffer->NextHigherTimestamp(0, NULL));
delete buffer;
// Insert packet list of three packets, where the second packet has an invalid
// payload. Expect first packet to be inserted, and the remaining two to be
// discarded.
buffer = new PacketBuffer(100, &tick_timer, &mock_stats); // 100 packets.
PacketList list;
list.push_back(gen.NextPacket(payload_len, nullptr)); // Valid packet.
{
Packet packet = gen.NextPacket(payload_len, nullptr);
packet.payload.Clear(); // Invalid.
list.push_back(std::move(packet));
}
list.push_back(gen.NextPacket(payload_len, nullptr)); // Valid packet.
auto factory = CreateBuiltinAudioDecoderFactory();
const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1),
absl::nullopt, factory.get());
EXPECT_CALL(decoder_database, GetDecoderInfo(0))
.WillRepeatedly(Return(&info));
absl::optional<uint8_t> current_pt;
absl::optional<uint8_t> current_cng_pt;
EXPECT_EQ(PacketBuffer::kInvalidPacket,
buffer->InsertPacketList(
/*packet_list=*/&list,
/*decoder_database=*/decoder_database,
/*current_rtp_payload_type=*/&current_pt,
/*current_cng_rtp_payload_type=*/&current_cng_pt));
EXPECT_TRUE(list.empty()); // The PacketBuffer should have depleted the list.
EXPECT_EQ(1u, buffer->NumPacketsInBuffer());
delete buffer;
EXPECT_CALL(decoder_database, Die()); // Called when object is deleted.
EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer.DiscardNextPacket());
buffer.DiscardAllOldPackets(0);
}
// Test packet comparison function.