diff options
Diffstat (limited to 'third_party/libwebrtc/modules/audio_coding/neteq')
16 files changed, 398 insertions, 851 deletions
diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.cc b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.cc index fd4f2f5a20..6648fd8709 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.cc @@ -95,10 +95,14 @@ DecisionLogic::DecisionLogic(NetEqController::Config config) DecisionLogic::DecisionLogic( NetEqController::Config config, std::unique_ptr<DelayManager> delay_manager, - std::unique_ptr<BufferLevelFilter> buffer_level_filter) + std::unique_ptr<BufferLevelFilter> buffer_level_filter, + std::unique_ptr<PacketArrivalHistory> packet_arrival_history) : delay_manager_(std::move(delay_manager)), buffer_level_filter_(std::move(buffer_level_filter)), - packet_arrival_history_(config_.packet_history_size_ms), + packet_arrival_history_(packet_arrival_history + ? std::move(packet_arrival_history) + : std::make_unique<PacketArrivalHistory>( + config_.packet_history_size_ms)), tick_timer_(config.tick_timer), disallow_time_stretching_(!config.allow_time_stretching), timescale_countdown_( @@ -115,7 +119,7 @@ void DecisionLogic::SoftReset() { time_stretched_cn_samples_ = 0; delay_manager_->Reset(); buffer_level_filter_->Reset(); - packet_arrival_history_.Reset(); + packet_arrival_history_->Reset(); } void DecisionLogic::SetSampleRate(int fs_hz, size_t output_size_samples) { @@ -124,7 +128,7 @@ void DecisionLogic::SetSampleRate(int fs_hz, size_t output_size_samples) { fs_hz == 48000); sample_rate_khz_ = fs_hz / 1000; output_size_samples_ = output_size_samples; - packet_arrival_history_.set_sample_rate(fs_hz); + packet_arrival_history_->set_sample_rate(fs_hz); } NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status, @@ -218,15 +222,15 @@ absl::optional<int> DecisionLogic::PacketArrived( delay_manager_->SetPacketAudioLength(packet_length_samples_ * 1000 / fs_hz); } int64_t time_now_ms = tick_timer_->ticks() * tick_timer_->ms_per_tick(); - packet_arrival_history_.Insert(info.main_timestamp, time_now_ms); - if (packet_arrival_history_.size() < 2) { + packet_arrival_history_->Insert(info.main_timestamp, time_now_ms); + if (packet_arrival_history_->size() < 2) { // No meaningful delay estimate unless at least 2 packets have arrived. return absl::nullopt; } int arrival_delay_ms = - packet_arrival_history_.GetDelayMs(info.main_timestamp, time_now_ms); + packet_arrival_history_->GetDelayMs(info.main_timestamp, time_now_ms); bool reordered = - !packet_arrival_history_.IsNewestRtpTimestamp(info.main_timestamp); + !packet_arrival_history_->IsNewestRtpTimestamp(info.main_timestamp); delay_manager_->Update(arrival_delay_ms, reordered); return arrival_delay_ms; } @@ -306,10 +310,10 @@ NetEq::Operation DecisionLogic::ExpectedPacketAvailable( !status.play_dtmf) { if (config_.enable_stable_delay_mode) { const int playout_delay_ms = GetPlayoutDelayMs(status); - const int low_limit = TargetLevelMs(); - const int high_limit = low_limit + - packet_arrival_history_.GetMaxDelayMs() + - kDelayAdjustmentGranularityMs; + const int64_t low_limit = TargetLevelMs(); + const int64_t high_limit = low_limit + + packet_arrival_history_->GetMaxDelayMs() + + kDelayAdjustmentGranularityMs; if (playout_delay_ms >= high_limit * 4) { return NetEq::Operation::kFastAccelerate; } @@ -460,7 +464,7 @@ int DecisionLogic::GetPlayoutDelayMs( NetEqController::NetEqStatus status) const { uint32_t playout_timestamp = status.target_timestamp - status.sync_buffer_samples; - return packet_arrival_history_.GetDelayMs( + return packet_arrival_history_->GetDelayMs( playout_timestamp, tick_timer_->ticks() * tick_timer_->ms_per_tick()); } diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.h b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.h index d96fbecd6a..a6b02c69cd 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.h +++ b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic.h @@ -27,9 +27,11 @@ namespace webrtc { class DecisionLogic : public NetEqController { public: DecisionLogic(NetEqController::Config config); - DecisionLogic(NetEqController::Config config, - std::unique_ptr<DelayManager> delay_manager, - std::unique_ptr<BufferLevelFilter> buffer_level_filter); + DecisionLogic( + NetEqController::Config config, + std::unique_ptr<DelayManager> delay_manager, + std::unique_ptr<BufferLevelFilter> buffer_level_filter, + std::unique_ptr<PacketArrivalHistory> packet_arrival_history = nullptr); ~DecisionLogic() override; @@ -154,16 +156,16 @@ class DecisionLogic : public NetEqController { struct Config { Config(); - bool enable_stable_delay_mode = false; - bool combine_concealment_decision = false; + bool enable_stable_delay_mode = true; + bool combine_concealment_decision = true; int deceleration_target_level_offset_ms = 85; int packet_history_size_ms = 2000; - absl::optional<int> cng_timeout_ms; + absl::optional<int> cng_timeout_ms = 1000; }; Config config_; std::unique_ptr<DelayManager> delay_manager_; std::unique_ptr<BufferLevelFilter> buffer_level_filter_; - PacketArrivalHistory packet_arrival_history_; + std::unique_ptr<PacketArrivalHistory> packet_arrival_history_; const TickTimer* tick_timer_; int sample_rate_khz_; size_t output_size_samples_; diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic_unittest.cc index 97e20dd883..9e9902af50 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic_unittest.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/decision_logic_unittest.cc @@ -18,6 +18,7 @@ #include "modules/audio_coding/neteq/delay_manager.h" #include "modules/audio_coding/neteq/mock/mock_buffer_level_filter.h" #include "modules/audio_coding/neteq/mock/mock_delay_manager.h" +#include "modules/audio_coding/neteq/mock/mock_packet_arrival_history.h" #include "test/field_trial.h" #include "test/gtest.h" @@ -47,6 +48,7 @@ NetEqController::NetEqStatus CreateNetEqStatus(NetEq::Mode last_mode, return status; } +using ::testing::_; using ::testing::Return; } // namespace @@ -54,8 +56,6 @@ using ::testing::Return; class DecisionLogicTest : public ::testing::Test { protected: DecisionLogicTest() { - test::ScopedFieldTrials trials( - "WebRTC-Audio-NetEqDecisionLogicConfig/cng_timeout_ms:1000/"); NetEqController::Config config; config.tick_timer = &tick_timer_; config.allow_time_stretching = true; @@ -64,8 +64,11 @@ class DecisionLogicTest : public ::testing::Test { mock_delay_manager_ = delay_manager.get(); auto buffer_level_filter = std::make_unique<MockBufferLevelFilter>(); mock_buffer_level_filter_ = buffer_level_filter.get(); + auto packet_arrival_history = std::make_unique<MockPacketArrivalHistory>(); + mock_packet_arrival_history_ = packet_arrival_history.get(); decision_logic_ = std::make_unique<DecisionLogic>( - config, std::move(delay_manager), std::move(buffer_level_filter)); + config, std::move(delay_manager), std::move(buffer_level_filter), + std::move(packet_arrival_history)); decision_logic_->SetSampleRate(kSampleRate, kOutputSizeSamples); } @@ -73,13 +76,16 @@ class DecisionLogicTest : public ::testing::Test { std::unique_ptr<DecisionLogic> decision_logic_; MockDelayManager* mock_delay_manager_; MockBufferLevelFilter* mock_buffer_level_filter_; + MockPacketArrivalHistory* mock_packet_arrival_history_; }; TEST_F(DecisionLogicTest, NormalOperation) { EXPECT_CALL(*mock_delay_manager_, TargetDelayMs()) .WillRepeatedly(Return(100)); - EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level()) - .WillRepeatedly(Return(90 * kSamplesPerMs)); + EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _)) + .WillRepeatedly(Return(100)); + EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs()) + .WillRepeatedly(Return(0)); bool reset_decoder = false; tick_timer_.Increment(kMinTimescaleInterval + 1); @@ -92,8 +98,10 @@ TEST_F(DecisionLogicTest, NormalOperation) { TEST_F(DecisionLogicTest, Accelerate) { EXPECT_CALL(*mock_delay_manager_, TargetDelayMs()) .WillRepeatedly(Return(100)); - EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level()) - .WillRepeatedly(Return(110 * kSamplesPerMs)); + EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _)) + .WillRepeatedly(Return(150)); + EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs()) + .WillRepeatedly(Return(0)); bool reset_decoder = false; tick_timer_.Increment(kMinTimescaleInterval + 1); @@ -106,8 +114,10 @@ TEST_F(DecisionLogicTest, Accelerate) { TEST_F(DecisionLogicTest, FastAccelerate) { EXPECT_CALL(*mock_delay_manager_, TargetDelayMs()) .WillRepeatedly(Return(100)); - EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level()) - .WillRepeatedly(Return(400 * kSamplesPerMs)); + EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _)) + .WillRepeatedly(Return(500)); + EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs()) + .WillRepeatedly(Return(0)); bool reset_decoder = false; tick_timer_.Increment(kMinTimescaleInterval + 1); @@ -120,8 +130,10 @@ TEST_F(DecisionLogicTest, FastAccelerate) { TEST_F(DecisionLogicTest, PreemptiveExpand) { EXPECT_CALL(*mock_delay_manager_, TargetDelayMs()) .WillRepeatedly(Return(100)); - EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level()) - .WillRepeatedly(Return(50 * kSamplesPerMs)); + EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _)) + .WillRepeatedly(Return(50)); + EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs()) + .WillRepeatedly(Return(0)); bool reset_decoder = false; tick_timer_.Increment(kMinTimescaleInterval + 1); @@ -131,20 +143,6 @@ TEST_F(DecisionLogicTest, PreemptiveExpand) { EXPECT_FALSE(reset_decoder); } -TEST_F(DecisionLogicTest, DecelerationTargetLevelOffset) { - EXPECT_CALL(*mock_delay_manager_, TargetDelayMs()) - .WillRepeatedly(Return(500)); - EXPECT_CALL(*mock_buffer_level_filter_, filtered_current_level()) - .WillRepeatedly(Return(400 * kSamplesPerMs)); - - bool reset_decoder = false; - tick_timer_.Increment(kMinTimescaleInterval + 1); - EXPECT_EQ(decision_logic_->GetDecision( - CreateNetEqStatus(NetEq::Mode::kNormal, 400), &reset_decoder), - NetEq::Operation::kPreemptiveExpand); - EXPECT_FALSE(reset_decoder); -} - TEST_F(DecisionLogicTest, PostponeDecodeAfterExpand) { EXPECT_CALL(*mock_delay_manager_, TargetDelayMs()) .WillRepeatedly(Return(500)); @@ -170,7 +168,7 @@ TEST_F(DecisionLogicTest, TimeStrechComfortNoise) { { bool reset_decoder = false; // Below target window. - auto status = CreateNetEqStatus(NetEq::Mode::kCodecInternalCng, 400); + auto status = CreateNetEqStatus(NetEq::Mode::kCodecInternalCng, 200); status.generated_noise_samples = 400 * kSamplesPerMs; status.next_packet->timestamp = status.target_timestamp + 400 * kSamplesPerMs; @@ -189,18 +187,6 @@ TEST_F(DecisionLogicTest, TimeStrechComfortNoise) { EXPECT_EQ(decision_logic_->GetDecision(status, &reset_decoder), NetEq::Operation::kNormal); EXPECT_FALSE(reset_decoder); - - // The buffer level filter should be adjusted with the number of samples - // that was skipped. - int timestamp_leap = status.next_packet->timestamp - - status.target_timestamp - - status.generated_noise_samples; - EXPECT_CALL(*mock_buffer_level_filter_, - Update(400 * kSamplesPerMs, timestamp_leap)); - EXPECT_EQ(decision_logic_->GetDecision( - CreateNetEqStatus(NetEq::Mode::kNormal, 400), &reset_decoder), - NetEq::Operation::kNormal); - EXPECT_FALSE(reset_decoder); } } diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_arrival_history.h b/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_arrival_history.h new file mode 100644 index 0000000000..1b2080cd94 --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_arrival_history.h @@ -0,0 +1,32 @@ +/* + * Copyright 2023 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 MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_ARRIVAL_HISTORY_H_ +#define MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_ARRIVAL_HISTORY_H_ + +#include "modules/audio_coding/neteq/packet_arrival_history.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockPacketArrivalHistory : public PacketArrivalHistory { + public: + MockPacketArrivalHistory() : PacketArrivalHistory(0) {} + + MOCK_METHOD(int, + GetDelayMs, + (uint32_t rtp_timestamp, int64_t time_ms), + (const override)); + MOCK_METHOD(int, GetMaxDelayMs, (), (const override)); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_ARRIVAL_HISTORY_H_ diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h b/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h index 48357ea466..fa44f606fc 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h +++ b/third_party/libwebrtc/modules/audio_coding/neteq/mock/mock_packet_buffer.h @@ -18,39 +18,15 @@ namespace webrtc { class MockPacketBuffer : public PacketBuffer { public: - MockPacketBuffer(size_t max_number_of_packets, const TickTimer* tick_timer) - : PacketBuffer(max_number_of_packets, tick_timer) {} + MockPacketBuffer(size_t max_number_of_packets, + const TickTimer* tick_timer, + StatisticsCalculator* stats) + : PacketBuffer(max_number_of_packets, tick_timer, stats) {} ~MockPacketBuffer() override { Die(); } MOCK_METHOD(void, Die, ()); - MOCK_METHOD(void, Flush, (StatisticsCalculator * stats), (override)); - MOCK_METHOD(void, - PartialFlush, - (int target_level_ms, - size_t sample_rate, - size_t last_decoded_length, - StatisticsCalculator* stats), - (override)); + MOCK_METHOD(void, Flush, (), (override)); MOCK_METHOD(bool, Empty, (), (const, override)); - MOCK_METHOD(int, - InsertPacket, - (Packet && packet, - StatisticsCalculator* stats, - size_t last_decoded_length, - size_t sample_rate, - int target_level_ms, - const DecoderDatabase& decoder_database), - (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, - StatisticsCalculator* stats, - size_t last_decoded_length, - size_t sample_rate, - int target_level_ms), - (override)); + MOCK_METHOD(int, InsertPacket, (Packet && packet), (override)); MOCK_METHOD(int, NextTimestamp, (uint32_t * next_timestamp), @@ -61,19 +37,14 @@ class MockPacketBuffer : public PacketBuffer { (const, override)); MOCK_METHOD(const Packet*, PeekNextPacket, (), (const, override)); MOCK_METHOD(absl::optional<Packet>, GetNextPacket, (), (override)); - MOCK_METHOD(int, - DiscardNextPacket, - (StatisticsCalculator * stats), - (override)); + MOCK_METHOD(int, DiscardNextPacket, (), (override)); MOCK_METHOD(void, DiscardOldPackets, - (uint32_t timestamp_limit, - uint32_t horizon_samples, - StatisticsCalculator* stats), + (uint32_t timestamp_limit, uint32_t horizon_samples), (override)); MOCK_METHOD(void, DiscardAllOldPackets, - (uint32_t timestamp_limit, StatisticsCalculator* stats), + (uint32_t timestamp_limit), (override)); MOCK_METHOD(size_t, NumPacketsInBuffer, (), (const, override)); }; diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.cc b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.cc index 52e8cbad3a..e5c8bf6c08 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.cc @@ -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( @@ -84,8 +140,9 @@ NetEqImpl::Dependencies::Dependencies( new DecoderDatabase(decoder_factory, config.codec_pair_id)), dtmf_buffer(new DtmfBuffer(config.sample_rate_hz)), dtmf_tone_generator(new DtmfToneGenerator), - packet_buffer( - new PacketBuffer(config.max_packets_in_buffer, tick_timer.get())), + packet_buffer(new PacketBuffer(config.max_packets_in_buffer, + tick_timer.get(), + stats.get())), neteq_controller( CreateNetEqController(controller_factory, config.min_delay_ms, @@ -182,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, @@ -265,7 +274,7 @@ void NetEqImpl::SetCodecs(const std::map<int, SdpAudioFormat>& codecs) { const std::vector<int> changed_payload_types = decoder_database_->SetCodecs(codecs); for (const int pt : changed_payload_types) { - packet_buffer_->DiscardPacketsWithPayloadType(pt, stats_.get()); + packet_buffer_->DiscardPacketsWithPayloadType(pt); } } @@ -283,8 +292,7 @@ int NetEqImpl::RemovePayloadType(uint8_t rtp_payload_type) { MutexLock lock(&mutex_); int ret = decoder_database_->Remove(rtp_payload_type); if (ret == DecoderDatabase::kOK || ret == DecoderDatabase::kDecoderNotFound) { - packet_buffer_->DiscardPacketsWithPayloadType(rtp_payload_type, - stats_.get()); + packet_buffer_->DiscardPacketsWithPayloadType(rtp_payload_type); return kOK; } return kFail; @@ -441,7 +449,7 @@ absl::optional<NetEq::DecoderFormat> NetEqImpl::GetDecoderFormat( void NetEqImpl::FlushBuffers() { MutexLock lock(&mutex_); RTC_LOG(LS_VERBOSE) << "FlushBuffers"; - packet_buffer_->Flush(stats_.get()); + packet_buffer_->Flush(); RTC_DCHECK(sync_buffer_.get()); RTC_DCHECK(expand_.get()); sync_buffer_->Flush(); @@ -542,7 +550,7 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header, // the packet has been successfully inserted into the packet buffer. // Flush the packet buffer and DTMF buffer. - packet_buffer_->Flush(stats_.get()); + packet_buffer_->Flush(); dtmf_buffer_->Flush(); // Update audio buffer timestamp. @@ -681,26 +689,25 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header, number_of_primary_packets); } - // Insert packets in buffer. - const int target_level_ms = controller_->TargetLevelMs(); - const int ret = packet_buffer_->InsertPacketList( - &parsed_packet_list, *decoder_database_, ¤t_rtp_payload_type_, - ¤t_cng_rtp_payload_type_, stats_.get(), decoder_frame_length_, - last_output_sample_rate_hz_, target_level_ms); 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::kPartialFlush) { - // Forward sync buffer timestamp - timestamp_ = packet_buffer_->PeekNextPacket()->timestamp; - sync_buffer_->IncreaseEndTimestamp(timestamp_ - - sync_buffer_->end_timestamp()); - buffer_flush_occured = true; - } else if (ret != PacketBuffer::kOK) { - return kOtherError; } if (first_packet_) { @@ -767,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) { @@ -1037,8 +1069,7 @@ int NetEqImpl::GetDecision(Operation* operation, uint32_t end_timestamp = sync_buffer_->end_timestamp(); if (!new_codec_) { const uint32_t five_seconds_samples = 5 * fs_hz_; - packet_buffer_->DiscardOldPackets(end_timestamp, five_seconds_samples, - stats_.get()); + packet_buffer_->DiscardOldPackets(end_timestamp, five_seconds_samples); } const Packet* packet = packet_buffer_->PeekNextPacket(); @@ -1058,14 +1089,12 @@ int NetEqImpl::GetDecision(Operation* operation, (end_timestamp >= packet->timestamp || end_timestamp + generated_noise_samples > packet->timestamp)) { // Don't use this packet, discard it. - if (packet_buffer_->DiscardNextPacket(stats_.get()) != - PacketBuffer::kOK) { + if (packet_buffer_->DiscardNextPacket() != PacketBuffer::kOK) { RTC_DCHECK_NOTREACHED(); // Must be ok by design. } // Check buffer again. if (!new_codec_) { - packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_, - stats_.get()); + packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_); } packet = packet_buffer_->PeekNextPacket(); } @@ -2024,7 +2053,7 @@ int NetEqImpl::ExtractPackets(size_t required_samples, // we could end up in the situation where we never decode anything, since // all incoming packets are considered too old but the buffer will also // never be flooded and flushed. - packet_buffer_->DiscardAllOldPackets(timestamp_, stats_.get()); + packet_buffer_->DiscardAllOldPackets(timestamp_); } return rtc::dchecked_cast<int>(extracted_samples); diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.h b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.h index f27738bcbf..f8f2b06410 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.h +++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl.h @@ -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, diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc index e61cd52502..8309dafb58 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc @@ -108,8 +108,8 @@ class NetEqImplTest : public ::testing::Test { dtmf_tone_generator_ = deps.dtmf_tone_generator.get(); if (use_mock_packet_buffer_) { - std::unique_ptr<MockPacketBuffer> mock( - new MockPacketBuffer(config_.max_packets_in_buffer, tick_timer_)); + std::unique_ptr<MockPacketBuffer> mock(new MockPacketBuffer( + config_.max_packets_in_buffer, tick_timer_, deps.stats.get())); mock_packet_buffer_ = mock.get(); deps.packet_buffer = std::move(mock); } @@ -120,7 +120,6 @@ class NetEqImplTest : public ::testing::Test { mock_neteq_controller_ = mock.get(); deps.neteq_controller = std::move(mock); } else { - deps.stats = std::make_unique<StatisticsCalculator>(); NetEqController::Config controller_config; controller_config.tick_timer = tick_timer_; controller_config.base_min_delay_ms = config_.min_delay_ms; @@ -329,15 +328,10 @@ TEST_F(NetEqImplTest, InsertPacket) { // Expectations for packet buffer. 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_, Flush()).Times(1); + 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)); @@ -1246,12 +1240,15 @@ TEST_F(NetEqImplTest, UnsupportedDecoder) { EXPECT_EQ(kChannels, output.num_channels_); EXPECT_THAT(output.packet_infos_, IsEmpty()); - // Second call to GetAudio will decode the packet that is ok. No errors are - // expected. - EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted)); - EXPECT_EQ(kExpectedOutputSize, output.samples_per_channel_ * kChannels); - EXPECT_EQ(kChannels, output.num_channels_); - EXPECT_THAT(output.packet_infos_, SizeIs(1)); + // Call GetAudio until the next packet is decoded. + int calls = 0; + int kTimeout = 10; + while (output.packet_infos_.empty() && calls < kTimeout) { + EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted)); + EXPECT_EQ(kExpectedOutputSize, output.samples_per_channel_ * kChannels); + EXPECT_EQ(kChannels, output.num_channels_); + } + EXPECT_LT(calls, kTimeout); // Die isn't called through NiceMock (since it's called by the // MockAudioDecoder constructor), so it needs to be mocked explicitly. @@ -1640,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) diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_network_stats_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_network_stats_unittest.cc index a669ad727e..da516982c7 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_network_stats_unittest.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_network_stats_unittest.cc @@ -273,15 +273,16 @@ class NetEqNetworkStatsTest { // Next we introduce packet losses. SetPacketLossRate(0.1); - expects.stats_ref.expand_rate = expects.stats_ref.speech_expand_rate = 898; + expects.expand_rate = expects.speech_expand_rate = kLargerThan; RunTest(50, expects); // Next we enable FEC. decoder_->set_fec_enabled(true); // If FEC fills in the lost packets, no packet loss will be counted. + expects.expand_rate = expects.speech_expand_rate = kEqual; expects.stats_ref.expand_rate = expects.stats_ref.speech_expand_rate = 0; - expects.stats_ref.secondary_decoded_rate = 2006; - expects.stats_ref.secondary_discarded_rate = 14336; + expects.secondary_decoded_rate = kLargerThan; + expects.secondary_discarded_rate = kLargerThan; RunTest(50, expects); } diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_unittest.cc index 77bd5b5035..aec7e580ec 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/neteq_unittest.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/neteq_unittest.cc @@ -31,7 +31,6 @@ #include "modules/include/module_common_types_public.h" #include "modules/rtp_rtcp/include/rtcp_statistics.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" -#include "rtc_base/ignore_wundef.h" #include "rtc_base/message_digest.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/strings/string_builder.h" @@ -77,11 +76,11 @@ TEST_F(NetEqDecodingTest, MAYBE_TestOpusBitExactness) { webrtc::test::ResourcePath("audio_coding/neteq_opus", "rtp"); const std::string output_checksum = - "fec6827bb9ee0b21770bbbb4a3a6f8823bf537dc|" - "3610cc7be4b3407b9c273b1299ab7f8f47cca96b"; + "2efdbea92c3fb2383c59f89d881efec9f94001d0|" + "a6831b946b59913852ae3e53f99fa8f209bb23cd"; const std::string network_stats_checksum = - "3d043e47e5f4bb81d37e7bce8c44bf802965c853|" + "dfaf4399fd60293405290476ccf1c05c807c71a0|" "076662525572dba753b11578330bd491923f7f5e"; DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum, @@ -99,11 +98,11 @@ TEST_F(NetEqDecodingTest, MAYBE_TestOpusDtxBitExactness) { webrtc::test::ResourcePath("audio_coding/neteq_opus_dtx", "rtp"); const std::string output_checksum = - "b3c4899eab5378ef5e54f2302948872149f6ad5e|" - "589e975ec31ea13f302457fea1425be9380ffb96"; + "7eddce841cbfa500964c91cdae78b01b9f448948|" + "5d13affec87bf4cc8c7667f0cd0d25e1ad09c7c3"; const std::string network_stats_checksum = - "dc8447b9fee1a21fd5d1f4045d62b982a3fb0215"; + "92b0fdcbf8bb9354d40140b7312f2fb76a078555"; DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum, absl::GetFlag(FLAGS_gen_ref)); @@ -165,7 +164,7 @@ TEST_F(NetEqDecodingTest, LongCngWithNegativeClockDrift) { const double kDriftFactor = 1000.0 / (1000.0 + 25.0); const double kNetworkFreezeTimeMs = 0.0; const bool kGetAudioDuringFreezeRecovery = false; - const int kDelayToleranceMs = 20; + const int kDelayToleranceMs = 60; const int kMaxTimeToSpeechMs = 100; LongCngWithClockDrift(kDriftFactor, kNetworkFreezeTimeMs, kGetAudioDuringFreezeRecovery, kDelayToleranceMs, @@ -495,7 +494,7 @@ TEST_F(NetEqDecodingTest, DiscardDuplicateCng) { timestamp += kCngPeriodSamples; uint32_t first_speech_timestamp = timestamp; // Insert speech again. - for (int i = 0; i < 3; ++i) { + for (int i = 0; i < 4; ++i) { PopulateRtpInfo(seq_no, timestamp, &rtp_info); ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload)); ++seq_no; @@ -700,8 +699,7 @@ TEST_F(NetEqDecodingTestWithMutedState, MutedStateOldPacket) { for (int i = 0; i < 5; ++i) { InsertPacket(kSamples * (i - 1000)); } - EXPECT_FALSE(GetAudioReturnMuted()); - EXPECT_EQ(AudioFrame::kNormalSpeech, out_frame_.speech_type_); + GetAudioUntilNormal(); } // Verifies that NetEq doesn't enter muted state when CNG mode is active and the diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/packet_arrival_history.h b/third_party/libwebrtc/modules/audio_coding/neteq/packet_arrival_history.h index cad362b469..722caf5688 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/packet_arrival_history.h +++ b/third_party/libwebrtc/modules/audio_coding/neteq/packet_arrival_history.h @@ -26,6 +26,7 @@ namespace webrtc { class PacketArrivalHistory { public: explicit PacketArrivalHistory(int window_size_ms); + virtual ~PacketArrivalHistory() = default; // Insert packet with `rtp_timestamp` and `arrival_time_ms` into the history. void Insert(uint32_t rtp_timestamp, int64_t arrival_time_ms); @@ -34,10 +35,10 @@ class PacketArrivalHistory { // `(time_ms - p.arrival_time_ms) - (rtp_timestamp - p.rtp_timestamp)` // where `p` is chosen as the packet arrival in the history that maximizes the // delay. - int GetDelayMs(uint32_t rtp_timestamp, int64_t time_ms) const; + virtual int GetDelayMs(uint32_t rtp_timestamp, int64_t time_ms) const; // Get the maximum packet arrival delay observed in the history. - int GetMaxDelayMs() const; + virtual int GetMaxDelayMs() const; bool IsNewestRtpTimestamp(uint32_t rtp_timestamp) const; diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.cc b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.cc index 9bfa908ab9..47c391a18f 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.cc @@ -44,53 +44,14 @@ 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(); -} - -void LogPacketDiscarded(int codec_level, StatisticsCalculator* stats) { - RTC_CHECK(stats); - if (codec_level > 0) { - stats->SecondaryPacketsDiscarded(1); - } else { - stats->PacketsDiscarded(1); - } -} - -absl::optional<SmartFlushingConfig> GetSmartflushingConfig() { - absl::optional<SmartFlushingConfig> result; - std::string field_trial_string = - field_trial::FindFullName("WebRTC-Audio-NetEqSmartFlushing"); - result = SmartFlushingConfig(); - bool enabled = false; - auto parser = StructParametersParser::Create( - "enabled", &enabled, "target_level_threshold_ms", - &result->target_level_threshold_ms, "target_level_multiplier", - &result->target_level_multiplier); - parser->Parse(field_trial_string); - if (!enabled) { - return absl::nullopt; - } - RTC_LOG(LS_INFO) << "Using smart flushing, target_level_threshold_ms: " - << result->target_level_threshold_ms - << ", target_level_multiplier: " - << result->target_level_multiplier; - return result; -} - } // namespace PacketBuffer::PacketBuffer(size_t max_number_of_packets, - const TickTimer* tick_timer) - : smart_flushing_config_(GetSmartflushingConfig()), - max_number_of_packets_(max_number_of_packets), - tick_timer_(tick_timer) {} + const TickTimer* tick_timer, + StatisticsCalculator* stats) + : max_number_of_packets_(max_number_of_packets), + tick_timer_(tick_timer), + stats_(stats) {} // Destructor. All packets in the buffer will be destroyed. PacketBuffer::~PacketBuffer() { @@ -98,45 +59,19 @@ PacketBuffer::~PacketBuffer() { } // Flush the buffer. All packets in the buffer will be destroyed. -void PacketBuffer::Flush(StatisticsCalculator* stats) { +void PacketBuffer::Flush() { for (auto& p : buffer_) { - LogPacketDiscarded(p.priority.codec_level, stats); + LogPacketDiscarded(p.priority.codec_level); } buffer_.clear(); - stats->FlushedPacketBuffer(); -} - -void PacketBuffer::PartialFlush(int target_level_ms, - size_t sample_rate, - size_t last_decoded_length, - StatisticsCalculator* stats) { - // Make sure that at least half the packet buffer capacity will be available - // after the flush. This is done to avoid getting stuck if the target level is - // very high. - int target_level_samples = - std::min(target_level_ms * sample_rate / 1000, - max_number_of_packets_ * last_decoded_length / 2); - // We should avoid flushing to very low levels. - target_level_samples = std::max( - target_level_samples, smart_flushing_config_->target_level_threshold_ms); - while (GetSpanSamples(last_decoded_length, sample_rate, false) > - static_cast<size_t>(target_level_samples) || - buffer_.size() > max_number_of_packets_ / 2) { - LogPacketDiscarded(PeekNextPacket()->priority.codec_level, stats); - buffer_.pop_front(); - } + stats_->FlushedPacketBuffer(); } bool PacketBuffer::Empty() const { return buffer_.empty(); } -int PacketBuffer::InsertPacket(Packet&& packet, - StatisticsCalculator* stats, - size_t last_decoded_length, - size_t sample_rate, - int target_level_ms, - const DecoderDatabase& decoder_database) { +int PacketBuffer::InsertPacket(Packet&& packet) { if (packet.empty()) { RTC_LOG(LS_WARNING) << "InsertPacket invalid packet"; return kInvalidPacket; @@ -149,32 +84,11 @@ int PacketBuffer::InsertPacket(Packet&& packet, packet.waiting_time = tick_timer_->GetNewStopwatch(); - // Perform a smart flush if the buffer size exceeds a multiple of the target - // level. - const size_t span_threshold = - smart_flushing_config_ - ? smart_flushing_config_->target_level_multiplier * - std::max(smart_flushing_config_->target_level_threshold_ms, - target_level_ms) * - sample_rate / 1000 - : 0; - const bool smart_flush = - smart_flushing_config_.has_value() && - GetSpanSamples(last_decoded_length, sample_rate, false) >= span_threshold; - if (buffer_.size() >= max_number_of_packets_ || smart_flush) { - size_t buffer_size_before_flush = buffer_.size(); - if (smart_flushing_config_.has_value()) { - // Flush down to the target level. - PartialFlush(target_level_ms, sample_rate, last_decoded_length, stats); - return_val = kPartialFlush; - } else { - // Buffer is full. - Flush(stats); - return_val = kFlushed; - } - RTC_LOG(LS_WARNING) << "Packet buffer flushed, " - << (buffer_size_before_flush - buffer_.size()) - << " packets discarded."; + if (buffer_.size() >= max_number_of_packets_) { + // Buffer is full. + Flush(); + return_val = kFlushed; + RTC_LOG(LS_WARNING) << "Packet buffer flushed."; } // Get an iterator pointing to the place in the buffer where the new packet @@ -187,7 +101,7 @@ int PacketBuffer::InsertPacket(Packet&& packet, // timestamp as `rit`, which has a higher priority, do not insert the new // packet to list. if (rit != buffer_.rend() && packet.timestamp == rit->timestamp) { - LogPacketDiscarded(packet.priority.codec_level, stats); + LogPacketDiscarded(packet.priority.codec_level); return return_val; } @@ -196,7 +110,7 @@ int PacketBuffer::InsertPacket(Packet&& packet, // packet. PacketList::iterator it = rit.base(); if (it != buffer_.end() && packet.timestamp == it->timestamp) { - LogPacketDiscarded(it->priority.codec_level, stats); + LogPacketDiscarded(it->priority.codec_level); it = buffer_.erase(it); } buffer_.insert(it, std::move(packet)); // Insert the packet at that position. @@ -204,57 +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, - StatisticsCalculator* stats, - size_t last_decoded_length, - size_t sample_rate, - int target_level_ms) { - RTC_DCHECK(stats); - 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(stats); - 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(stats); - flushed = true; - } - *current_rtp_payload_type = packet.payload_type; - } - int return_val = - InsertPacket(std::move(packet), stats, last_decoded_length, sample_rate, - target_level_ms, decoder_database); - 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; @@ -303,43 +166,40 @@ absl::optional<Packet> PacketBuffer::GetNextPacket() { return packet; } -int PacketBuffer::DiscardNextPacket(StatisticsCalculator* stats) { +int PacketBuffer::DiscardNextPacket() { if (Empty()) { return kBufferEmpty; } // Assert that the packet sanity checks in InsertPacket method works. const Packet& packet = buffer_.front(); RTC_DCHECK(!packet.empty()); - LogPacketDiscarded(packet.priority.codec_level, stats); + LogPacketDiscarded(packet.priority.codec_level); buffer_.pop_front(); return kOK; } void PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit, - uint32_t horizon_samples, - StatisticsCalculator* stats) { - buffer_.remove_if([timestamp_limit, horizon_samples, stats](const Packet& p) { + uint32_t horizon_samples) { + buffer_.remove_if([this, timestamp_limit, horizon_samples](const Packet& p) { if (timestamp_limit == p.timestamp || !IsObsoleteTimestamp(p.timestamp, timestamp_limit, horizon_samples)) { return false; } - LogPacketDiscarded(p.priority.codec_level, stats); + LogPacketDiscarded(p.priority.codec_level); return true; }); } -void PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit, - StatisticsCalculator* stats) { - DiscardOldPackets(timestamp_limit, 0, stats); +void PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit) { + DiscardOldPackets(timestamp_limit, 0); } -void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type, - StatisticsCalculator* stats) { - buffer_.remove_if([payload_type, stats](const Packet& p) { +void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type) { + buffer_.remove_if([this, payload_type](const Packet& p) { if (p.payload_type != payload_type) { return false; } - LogPacketDiscarded(p.priority.codec_level, stats); + LogPacketDiscarded(p.priority.codec_level); return true; }); } @@ -404,4 +264,12 @@ bool PacketBuffer::ContainsDtxOrCngPacket( return false; } +void PacketBuffer::LogPacketDiscarded(int codec_level) { + if (codec_level > 0) { + stats_->SecondaryPacketsDiscarded(1); + } else { + stats_->PacketsDiscarded(1); + } +} + } // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.h b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.h index 1eef64a02c..795dd4e812 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.h +++ b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer.h @@ -21,14 +21,6 @@ namespace webrtc { class DecoderDatabase; class StatisticsCalculator; class TickTimer; -struct SmartFlushingConfig { - // When calculating the flushing threshold, the maximum between the target - // level and this value is used. - int target_level_threshold_ms = 500; - // A smart flush is triggered when the packet buffer contains a multiple of - // the target level. - int target_level_multiplier = 3; -}; // This is the actual buffer holding the packets before decoding. class PacketBuffer { @@ -36,7 +28,6 @@ class PacketBuffer { enum BufferReturnCodes { kOK = 0, kFlushed, - kPartialFlush, kNotFound, kBufferEmpty, kInvalidPacket, @@ -45,7 +36,9 @@ class PacketBuffer { // Constructor creates a buffer which can hold a maximum of // `max_number_of_packets` packets. - PacketBuffer(size_t max_number_of_packets, const TickTimer* tick_timer); + PacketBuffer(size_t max_number_of_packets, + const TickTimer* tick_timer, + StatisticsCalculator* stats); // Deletes all packets in the buffer before destroying the buffer. virtual ~PacketBuffer(); @@ -54,13 +47,7 @@ class PacketBuffer { PacketBuffer& operator=(const PacketBuffer&) = delete; // Flushes the buffer and deletes all packets in it. - virtual void Flush(StatisticsCalculator* stats); - - // Partial flush. Flush packets but leave some packets behind. - virtual void PartialFlush(int target_level_ms, - size_t sample_rate, - size_t last_decoded_length, - StatisticsCalculator* stats); + virtual void Flush(); // Returns true for an empty buffer. virtual bool Empty() const; @@ -69,30 +56,7 @@ class PacketBuffer { // the packet object. // Returns PacketBuffer::kOK on success, PacketBuffer::kFlushed if the buffer // was flushed due to overfilling. - virtual int InsertPacket(Packet&& packet, - StatisticsCalculator* stats, - size_t last_decoded_length, - size_t sample_rate, - int target_level_ms, - const DecoderDatabase& decoder_database); - - // 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, - StatisticsCalculator* stats, - size_t last_decoded_length, - size_t sample_rate, - int target_level_ms); + virtual int InsertPacket(Packet&& packet); // Gets the timestamp for the first packet in the buffer and writes it to the // output variable `next_timestamp`. @@ -119,7 +83,7 @@ class PacketBuffer { // Discards the first packet in the buffer. The packet is deleted. // Returns PacketBuffer::kBufferEmpty if the buffer is empty, // PacketBuffer::kOK otherwise. - virtual int DiscardNextPacket(StatisticsCalculator* stats); + virtual int DiscardNextPacket(); // Discards all packets that are (strictly) older than timestamp_limit, // but newer than timestamp_limit - horizon_samples. Setting horizon_samples @@ -127,16 +91,13 @@ class PacketBuffer { // is, if a packet is more than 2^31 timestamps into the future compared with // timestamp_limit (including wrap-around), it is considered old. virtual void DiscardOldPackets(uint32_t timestamp_limit, - uint32_t horizon_samples, - StatisticsCalculator* stats); + uint32_t horizon_samples); // Discards all packets that are (strictly) older than timestamp_limit. - virtual void DiscardAllOldPackets(uint32_t timestamp_limit, - StatisticsCalculator* stats); + virtual void DiscardAllOldPackets(uint32_t timestamp_limit); // Removes all packets with a specific payload type from the buffer. - virtual void DiscardPacketsWithPayloadType(uint8_t payload_type, - StatisticsCalculator* stats); + virtual void DiscardPacketsWithPayloadType(uint8_t payload_type); // Returns the number of packets in the buffer, including duplicates and // redundant packets. @@ -171,10 +132,12 @@ class PacketBuffer { } private: - absl::optional<SmartFlushingConfig> smart_flushing_config_; + void LogPacketDiscarded(int codec_level); + size_t max_number_of_packets_; PacketList buffer_; const TickTimer* tick_timer_; + StatisticsCalculator* stats_; }; } // namespace webrtc diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc index b0079645ff..8f307a9eaf 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/packet_buffer_unittest.cc @@ -108,26 +108,23 @@ namespace webrtc { TEST(PacketBuffer, CreateAndDestroy) { TickTimer tick_timer; - PacketBuffer* buffer = new PacketBuffer(10, &tick_timer); // 10 packets. + StrictMock<MockStatisticsCalculator> mock_stats; + PacketBuffer* buffer = + new PacketBuffer(10, &tick_timer, &mock_stats); // 10 packets. EXPECT_TRUE(buffer->Empty()); delete buffer; } TEST(PacketBuffer, InsertPacket) { TickTimer tick_timer; - PacketBuffer buffer(10, &tick_timer); // 10 packets. - PacketGenerator gen(17u, 4711u, 0, 10); StrictMock<MockStatisticsCalculator> mock_stats; + PacketBuffer buffer(10, &tick_timer, &mock_stats); // 10 packets. + PacketGenerator gen(17u, 4711u, 0, 10); MockDecoderDatabase decoder_database; const int payload_len = 100; const Packet packet = gen.NextPacket(payload_len, nullptr); - EXPECT_EQ(0, buffer.InsertPacket(/*packet=*/packet.Clone(), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/10000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); + EXPECT_EQ(0, buffer.InsertPacket(/*packet=*/packet.Clone())); uint32_t next_ts; EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts)); EXPECT_EQ(4711u, next_ts); @@ -144,28 +141,22 @@ TEST(PacketBuffer, InsertPacket) { // Test to flush buffer. TEST(PacketBuffer, FlushBuffer) { TickTimer tick_timer; - PacketBuffer buffer(10, &tick_timer); // 10 packets. + StrictMock<MockStatisticsCalculator> mock_stats; + PacketBuffer buffer(10, &tick_timer, &mock_stats); // 10 packets. PacketGenerator gen(0, 0, 0, 10); const int payload_len = 10; - StrictMock<MockStatisticsCalculator> mock_stats; MockDecoderDatabase decoder_database; // Insert 10 small packets; should be ok. for (int i = 0; i < 10; ++i) { - EXPECT_EQ( - PacketBuffer::kOK, - buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); + EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(/*packet=*/gen.NextPacket( + payload_len, nullptr))); } EXPECT_EQ(10u, buffer.NumPacketsInBuffer()); EXPECT_FALSE(buffer.Empty()); EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(10); - buffer.Flush(&mock_stats); + buffer.Flush(); // Buffer should delete the payloads itself. EXPECT_EQ(0u, buffer.NumPacketsInBuffer()); EXPECT_TRUE(buffer.Empty()); @@ -175,23 +166,17 @@ TEST(PacketBuffer, FlushBuffer) { // Test to fill the buffer over the limits, and verify that it flushes. TEST(PacketBuffer, OverfillBuffer) { TickTimer tick_timer; - PacketBuffer buffer(10, &tick_timer); // 10 packets. - PacketGenerator gen(0, 0, 0, 10); StrictMock<MockStatisticsCalculator> mock_stats; + PacketBuffer buffer(10, &tick_timer, &mock_stats); // 10 packets. + PacketGenerator gen(0, 0, 0, 10); MockDecoderDatabase decoder_database; // Insert 10 small packets; should be ok. const int payload_len = 10; int i; for (i = 0; i < 10; ++i) { - EXPECT_EQ( - PacketBuffer::kOK, - buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); + EXPECT_EQ(PacketBuffer::kOK, buffer.InsertPacket(/*packet=*/gen.NextPacket( + payload_len, nullptr))); } EXPECT_EQ(10u, buffer.NumPacketsInBuffer()); uint32_t next_ts; @@ -202,12 +187,7 @@ TEST(PacketBuffer, OverfillBuffer) { const Packet packet = gen.NextPacket(payload_len, nullptr); // Insert 11th packet; should flush the buffer and insert it after flushing. EXPECT_EQ(PacketBuffer::kFlushed, - buffer.InsertPacket(/*packet=*/packet.Clone(), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); + buffer.InsertPacket(/*packet=*/packet.Clone())); EXPECT_EQ(1u, buffer.NumPacketsInBuffer()); EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts)); // Expect last inserted packet to be first in line. @@ -216,190 +196,11 @@ TEST(PacketBuffer, OverfillBuffer) { EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. } -// Test a partial buffer flush. -TEST(PacketBuffer, PartialFlush) { - // Use a field trial to configure smart flushing. - test::ScopedFieldTrials field_trials( - "WebRTC-Audio-NetEqSmartFlushing/enabled:true," - "target_level_threshold_ms:0,target_level_multiplier:2/"); - TickTimer tick_timer; - PacketBuffer buffer(10, &tick_timer); // 10 packets. - PacketGenerator gen(0, 0, 0, 10); - const int payload_len = 10; - StrictMock<MockStatisticsCalculator> mock_stats; - MockDecoderDatabase decoder_database; - - // Insert 10 small packets; should be ok. - for (int i = 0; i < 10; ++i) { - EXPECT_EQ( - PacketBuffer::kOK, - buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/100, - /*decoder_database=*/decoder_database)); - } - EXPECT_EQ(10u, buffer.NumPacketsInBuffer()); - EXPECT_FALSE(buffer.Empty()); - - EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(7); - buffer.PartialFlush(/*target_level_ms=*/30, - /*sample_rate=*/1000, - /*last_decoded_length=*/payload_len, - /*stats=*/&mock_stats); - // There should still be some packets left in the buffer. - EXPECT_EQ(3u, buffer.NumPacketsInBuffer()); - EXPECT_FALSE(buffer.Empty()); - EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. -} - -// Test to fill the buffer over the limits, and verify that the smart flush -// functionality works as expected. -TEST(PacketBuffer, SmartFlushOverfillBuffer) { - // Use a field trial to configure smart flushing. - test::ScopedFieldTrials field_trials( - "WebRTC-Audio-NetEqSmartFlushing/enabled:true," - "target_level_threshold_ms:0,target_level_multiplier:2/"); - TickTimer tick_timer; - PacketBuffer buffer(10, &tick_timer); // 10 packets. - PacketGenerator gen(0, 0, 0, 10); - StrictMock<MockStatisticsCalculator> mock_stats; - MockDecoderDatabase decoder_database; - - // Insert 10 small packets; should be ok. - const int payload_len = 10; - int i; - for (i = 0; i < 10; ++i) { - EXPECT_EQ( - PacketBuffer::kOK, - buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/100, - /*decoder_database=*/decoder_database)); - } - EXPECT_EQ(10u, buffer.NumPacketsInBuffer()); - uint32_t next_ts; - EXPECT_EQ(PacketBuffer::kOK, buffer.NextTimestamp(&next_ts)); - EXPECT_EQ(0u, next_ts); // Expect first inserted packet to be first in line. - - const Packet packet = gen.NextPacket(payload_len, nullptr); - EXPECT_CALL(mock_stats, PacketsDiscarded(1)).Times(6); - // Insert 11th packet; should cause a partial flush and insert the packet - // after flushing. - EXPECT_EQ(PacketBuffer::kPartialFlush, - buffer.InsertPacket(/*packet=*/packet.Clone(), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/40, - /*decoder_database=*/decoder_database)); - EXPECT_EQ(5u, buffer.NumPacketsInBuffer()); - EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. -} - -// Test inserting a list of packets. -TEST(PacketBuffer, InsertPacketList) { - TickTimer tick_timer; - PacketBuffer buffer(10, &tick_timer); // 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)); - - StrictMock<MockStatisticsCalculator> mock_stats; - - 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=*/¤t_pt, - /*current_cng_rtp_payload_type=*/¤t_cng_pt, - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/30)); - 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; - PacketBuffer buffer(10, &tick_timer); // 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)); - - StrictMock<MockStatisticsCalculator> mock_stats; - - 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=*/¤t_pt, - /*current_cng_rtp_payload_type=*/¤t_cng_pt, - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/30)); - 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; - PacketBuffer buffer(100, &tick_timer); // 100 packets. + StrictMock<MockStatisticsCalculator> mock_stats; + PacketBuffer buffer(100, &tick_timer, &mock_stats); // 100 packets. const int kPackets = 18; const int kFrameSize = 10; const int kPayloadLength = 10; @@ -423,8 +224,6 @@ TEST(PacketBuffer, ExtractOrderRedundancy) { PacketGenerator gen(0, 0, 0, kFrameSize); - StrictMock<MockStatisticsCalculator> mock_stats; - // Interleaving the EXPECT_CALL sequence with expectations on the MockFunction // check ensures that exactly one call to PacketsDiscarded happens in each // DiscardNextPacket call. @@ -444,12 +243,7 @@ TEST(PacketBuffer, ExtractOrderRedundancy) { } EXPECT_CALL(check, Call(i)); EXPECT_EQ(PacketBuffer::kOK, - buffer.InsertPacket(/*packet=*/packet.Clone(), - /*stats=*/&mock_stats, - /*last_decoded_length=*/kPayloadLength, - /*sample_rate=*/1000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); + buffer.InsertPacket(/*packet=*/packet.Clone())); if (packet_facts[i].extract_order >= 0) { expect_order[packet_facts[i].extract_order] = std::move(packet); } @@ -468,25 +262,20 @@ TEST(PacketBuffer, ExtractOrderRedundancy) { TEST(PacketBuffer, DiscardPackets) { TickTimer tick_timer; - PacketBuffer buffer(100, &tick_timer); // 100 packets. + StrictMock<MockStatisticsCalculator> mock_stats; + PacketBuffer buffer(100, &tick_timer, &mock_stats); // 100 packets. const uint16_t start_seq_no = 17; const uint32_t start_ts = 4711; const uint32_t ts_increment = 10; PacketGenerator gen(start_seq_no, start_ts, 0, ts_increment); PacketList list; const int payload_len = 10; - StrictMock<MockStatisticsCalculator> mock_stats; MockDecoderDatabase decoder_database; constexpr int kTotalPackets = 10; // Insert 10 small packets. for (int i = 0; i < kTotalPackets; ++i) { - buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database); + buffer.InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr)); } EXPECT_EQ(10u, buffer.NumPacketsInBuffer()); @@ -507,7 +296,7 @@ TEST(PacketBuffer, DiscardPackets) { EXPECT_EQ(current_ts, ts); EXPECT_CALL(mock_stats, PacketsDiscarded(1)); EXPECT_CALL(check, Call(i)); - EXPECT_EQ(PacketBuffer::kOK, buffer.DiscardNextPacket(&mock_stats)); + EXPECT_EQ(PacketBuffer::kOK, buffer.DiscardNextPacket()); current_ts += ts_increment; check.Call(i); } @@ -520,7 +309,7 @@ TEST(PacketBuffer, DiscardPackets) { .Times(kRemainingPackets - kSkipPackets); EXPECT_CALL(check, Call(17)); // Arbitrary id number. buffer.DiscardOldPackets(start_ts + kTotalPackets * ts_increment, - kRemainingPackets * ts_increment, &mock_stats); + kRemainingPackets * ts_increment); check.Call(17); // Same arbitrary id number. EXPECT_EQ(kSkipPackets, buffer.NumPacketsInBuffer()); @@ -530,8 +319,7 @@ TEST(PacketBuffer, DiscardPackets) { // Discard all remaining packets. EXPECT_CALL(mock_stats, PacketsDiscarded(kSkipPackets)); - buffer.DiscardAllOldPackets(start_ts + kTotalPackets * ts_increment, - &mock_stats); + buffer.DiscardAllOldPackets(start_ts + kTotalPackets * ts_increment); EXPECT_TRUE(buffer.Empty()); EXPECT_CALL(decoder_database, Die()); // Called when object is deleted. @@ -539,7 +327,8 @@ TEST(PacketBuffer, DiscardPackets) { TEST(PacketBuffer, Reordering) { TickTimer tick_timer; - PacketBuffer buffer(100, &tick_timer); // 100 packets. + StrictMock<MockStatisticsCalculator> mock_stats; + PacketBuffer buffer(100, &tick_timer, &mock_stats); // 100 packets. const uint16_t start_seq_no = 17; const uint32_t start_ts = 4711; const uint32_t ts_increment = 10; @@ -559,27 +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; - - StrictMock<MockStatisticsCalculator> mock_stats; - - EXPECT_EQ( - PacketBuffer::kOK, - buffer.InsertPacketList(/*packet_list=*/&list, - /*decoder_database=*/decoder_database, - /*current_rtp_payload_type=*/¤t_pt, - /*current_cng_rtp_payload_type=*/¤t_cng_pt, - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/30)); + 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. @@ -591,86 +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; - 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(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; - - StrictMock<MockStatisticsCalculator> mock_stats; - - EXPECT_EQ( - PacketBuffer::kOK, - buffer.InsertPacketList(/*packet_list=*/&list, - /*decoder_database=*/decoder_database, - /*current_rtp_payload_type=*/¤t_pt, - /*current_cng_rtp_payload_type=*/¤t_cng_pt, - /*stats=*/&mock_stats, - /*last_decoded_length=*/kPayloadLen, - /*sample_rate=*/1000, - /*target_level_ms=*/30)); - 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=*/¤t_pt, - /*current_cng_rtp_payload_type=*/¤t_cng_pt, - /*stats=*/&mock_stats, - /*last_decoded_length=*/kPayloadLen, - /*sample_rate=*/1000, - /*target_level_ms=*/30)); - 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) { @@ -681,80 +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); // 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), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); + 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(&mock_stats)); - buffer->DiscardAllOldPackets(0, &mock_stats); - - // Insert one packet to make the buffer non-empty. - EXPECT_EQ( - PacketBuffer::kOK, - buffer->InsertPacket(/*packet=*/gen.NextPacket(payload_len, nullptr), - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); - 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); // 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=*/¤t_pt, - /*current_cng_rtp_payload_type=*/¤t_cng_pt, - /*stats=*/&mock_stats, - /*last_decoded_length=*/payload_len, - /*sample_rate=*/1000, - /*target_level_ms=*/30)); - 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. @@ -873,9 +510,9 @@ TEST(PacketBuffer, GetSpanSamples) { constexpr int kSampleRateHz = 48000; constexpr bool kCountWaitingTime = false; TickTimer tick_timer; - PacketBuffer buffer(3, &tick_timer); - PacketGenerator gen(0, kStartTimeStamp, 0, kFrameSizeSamples); StrictMock<MockStatisticsCalculator> mock_stats; + PacketBuffer buffer(3, &tick_timer, &mock_stats); + PacketGenerator gen(0, kStartTimeStamp, 0, kFrameSizeSamples); MockDecoderDatabase decoder_database; Packet packet_1 = gen.NextPacket(kPayloadSizeBytes, nullptr); @@ -891,12 +528,7 @@ TEST(PacketBuffer, GetSpanSamples) { packet_2.timestamp); // Tmestamp wrapped around. EXPECT_EQ(PacketBuffer::kOK, - buffer.InsertPacket(/*packet=*/std::move(packet_1), - /*stats=*/&mock_stats, - /*last_decoded_length=*/kFrameSizeSamples, - /*sample_rate=*/1000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); + buffer.InsertPacket(/*packet=*/std::move(packet_1))); constexpr size_t kLastDecodedSizeSamples = 2; // packet_1 has no access to duration, and relies last decoded duration as @@ -906,12 +538,7 @@ TEST(PacketBuffer, GetSpanSamples) { kCountWaitingTime)); EXPECT_EQ(PacketBuffer::kOK, - buffer.InsertPacket(/*packet=*/std::move(packet_2), - /*stats=*/&mock_stats, - /*last_decoded_length=*/kFrameSizeSamples, - /*sample_rate=*/1000, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); + buffer.InsertPacket(/*packet=*/std::move(packet_2))); EXPECT_EQ(kFrameSizeSamples * 2, buffer.GetSpanSamples(0, kSampleRateHz, kCountWaitingTime)); @@ -931,20 +558,15 @@ TEST(PacketBuffer, GetSpanSamplesCountWaitingTime) { constexpr bool kCountWaitingTime = true; constexpr size_t kLastDecodedSizeSamples = 0; TickTimer tick_timer; - PacketBuffer buffer(3, &tick_timer); - PacketGenerator gen(0, kStartTimeStamp, 0, kFrameSizeSamples); StrictMock<MockStatisticsCalculator> mock_stats; + PacketBuffer buffer(3, &tick_timer, &mock_stats); + PacketGenerator gen(0, kStartTimeStamp, 0, kFrameSizeSamples); MockDecoderDatabase decoder_database; Packet packet = gen.NextPacket(kPayloadSizeBytes, nullptr); EXPECT_EQ(PacketBuffer::kOK, - buffer.InsertPacket(/*packet=*/std::move(packet), - /*stats=*/&mock_stats, - /*last_decoded_length=*/kFrameSizeSamples, - /*sample_rate=*/kSampleRateHz, - /*target_level_ms=*/60, - /*decoder_database=*/decoder_database)); + buffer.InsertPacket(/*packet=*/std::move(packet))); EXPECT_EQ(0u, buffer.GetSpanSamples(kLastDecodedSizeSamples, kSampleRateHz, kCountWaitingTime)); diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/test/neteq_decoding_test.cc b/third_party/libwebrtc/modules/audio_coding/neteq/test/neteq_decoding_test.cc index e6c1809fb6..e626d09c99 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/test/neteq_decoding_test.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/test/neteq_decoding_test.cc @@ -19,13 +19,13 @@ #include "test/testsupport/file_utils.h" #ifdef WEBRTC_NETEQ_UNITTEST_BITEXACT -RTC_PUSH_IGNORING_WUNDEF() + #ifdef WEBRTC_ANDROID_PLATFORM_BUILD #include "external/webrtc/webrtc/modules/audio_coding/neteq/neteq_unittest.pb.h" #else #include "modules/audio_coding/neteq/neteq_unittest.pb.h" #endif -RTC_POP_IGNORING_WUNDEF() + #endif namespace webrtc { diff --git a/third_party/libwebrtc/modules/audio_coding/neteq/test/result_sink.cc b/third_party/libwebrtc/modules/audio_coding/neteq/test/result_sink.cc index f5d50dc859..fee7b49eb3 100644 --- a/third_party/libwebrtc/modules/audio_coding/neteq/test/result_sink.cc +++ b/third_party/libwebrtc/modules/audio_coding/neteq/test/result_sink.cc @@ -13,19 +13,18 @@ #include <string> #include "absl/strings/string_view.h" -#include "rtc_base/ignore_wundef.h" #include "rtc_base/message_digest.h" #include "rtc_base/string_encode.h" #include "test/gtest.h" #ifdef WEBRTC_NETEQ_UNITTEST_BITEXACT -RTC_PUSH_IGNORING_WUNDEF() + #ifdef WEBRTC_ANDROID_PLATFORM_BUILD #include "external/webrtc/webrtc/modules/audio_coding/neteq/neteq_unittest.pb.h" #else #include "modules/audio_coding/neteq/neteq_unittest.pb.h" #endif -RTC_POP_IGNORING_WUNDEF() + #endif namespace webrtc { |