From 067d855291f85a83ee914f4432cca311aaa816e2 Mon Sep 17 00:00:00 2001 From: "henrik.lundin" Date: Thu, 1 Sep 2016 23:19:05 -0700 Subject: [PATCH] NetEq: Flush and reset if the speech and cng sample rates mismatch If a CNG packet is received first, followed by a speech packet with another sample rate, NetEq should treat this as a change of codec, flush out the CNG packet and reset the sample rate to that of the speech packet. BUG=webrtc:5447 NOTRY=True Review-Url: https://codereview.webrtc.org/2307493002 Cr-Commit-Position: refs/heads/master@{#14032} --- .../audio_coding/neteq/packet_buffer.cc | 21 ++++++- .../neteq/packet_buffer_unittest.cc | 61 +++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/webrtc/modules/audio_coding/neteq/packet_buffer.cc b/webrtc/modules/audio_coding/neteq/packet_buffer.cc index 0e512b64d1..ba469a6cd6 100644 --- a/webrtc/modules/audio_coding/neteq/packet_buffer.cc +++ b/webrtc/modules/audio_coding/neteq/packet_buffer.cc @@ -22,7 +22,7 @@ #include "webrtc/modules/audio_coding/neteq/tick_timer.h" namespace webrtc { - +namespace { // Predicate used when inserting packets in the buffer list. // Operator() returns true when |packet| goes before |new_packet|. class NewTimestampIsLarger { @@ -38,6 +38,17 @@ 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, const TickTimer* tick_timer) : max_number_of_packets_(max_number_of_packets), tick_timer_(tick_timer) {} @@ -126,8 +137,12 @@ int PacketBuffer::InsertPacketList( rtc::Optional(packet->header.payloadType); } else if (!decoder_database.IsDtmf(packet->header.payloadType)) { // This must be speech. - if (*current_rtp_payload_type && - **current_rtp_payload_type != packet->header.payloadType) { + if ((*current_rtp_payload_type && + **current_rtp_payload_type != packet->header.payloadType) || + (*current_cng_rtp_payload_type && + !EqualSampleRates(packet->header.payloadType, + **current_cng_rtp_payload_type, + decoder_database))) { *current_cng_rtp_payload_type = rtc::Optional(); Flush(); flushed = true; diff --git a/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc b/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc index 45669430f7..1b86d8326b 100644 --- a/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc +++ b/webrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc @@ -384,6 +384,67 @@ TEST(PacketBuffer, Reordering) { 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; + PacketBuffer buffer(10, &tick_timer); // 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(NetEqDecoder::kDecoderCNGnb, "", + factory); + EXPECT_CALL(decoder_database, GetDecoderInfo(kCngPt)) + .WillRepeatedly(Return(&info_cng)); + const DecoderDatabase::DecoderInfo info_speech(NetEqDecoder::kDecoderPCM16Bwb, + "", factory); + 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)); + rtc::Optional current_pt; + rtc::Optional current_cng_pt; + EXPECT_EQ(PacketBuffer::kOK, + buffer.InsertPacketList(&list, decoder_database, ¤t_pt, + ¤t_cng_pt)); + EXPECT_TRUE(list.empty()); + EXPECT_EQ(1u, buffer.NumPacketsInBuffer()); + ASSERT_TRUE(buffer.NextRtpHeader()); + EXPECT_EQ(kCngPt, buffer.NextRtpHeader()->payloadType); + EXPECT_FALSE(current_pt); // Current payload type not set. + EXPECT_EQ(rtc::Optional(kCngPt), + current_cng_pt); // CNG payload type set. + + // Insert second packet, which is wide-band speech. + Packet* packet = gen.NextPacket(kPayloadLen); + packet->header.payloadType = kSpeechPt; + list.push_back(packet); + // Expect the buffer to flush out the CNG packet, since it does not match the + // new speech sample rate. + EXPECT_EQ(PacketBuffer::kFlushed, + buffer.InsertPacketList(&list, decoder_database, ¤t_pt, + ¤t_cng_pt)); + EXPECT_TRUE(list.empty()); + EXPECT_EQ(1u, buffer.NumPacketsInBuffer()); + ASSERT_TRUE(buffer.NextRtpHeader()); + EXPECT_EQ(kSpeechPt, buffer.NextRtpHeader()->payloadType); + + EXPECT_EQ(rtc::Optional(kSpeechPt), + current_pt); // Current payload type set. + EXPECT_FALSE(current_cng_pt); // CNG payload type reset. + + buffer.Flush(); // Clean up. + EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. +} + TEST(PacketBuffer, Failures) { const uint16_t start_seq_no = 17; const uint32_t start_ts = 4711;