From 445070818cec9d92cd15eae2288a4529b22c21c7 Mon Sep 17 00:00:00 2001 From: Jakob Ivarsson Date: Tue, 5 Mar 2019 16:59:03 +0100 Subject: [PATCH] Add relative_packet_arrival_delay and jitter_buffer_packets_received statistics. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: webrtc:10333 Change-Id: I415e2286b426cbca940fe3a187957531847272ec Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/124780 Commit-Queue: Jakob Ivarsson‎ Reviewed-by: Minyue Li Cr-Commit-Position: refs/heads/master@{#26976} --- .../neteq/decision_logic_unittest.cc | 6 +- modules/audio_coding/neteq/delay_manager.cc | 27 ++++--- modules/audio_coding/neteq/delay_manager.h | 6 +- .../neteq/delay_manager_unittest.cc | 20 ++++- modules/audio_coding/neteq/include/neteq.h | 11 ++- .../neteq/mock/mock_delay_manager.h | 3 + .../neteq/mock/mock_statistics_calculator.h | 1 + modules/audio_coding/neteq/neteq_impl.cc | 73 +++++++++++-------- modules/audio_coding/neteq/neteq_impl.h | 3 +- .../audio_coding/neteq/neteq_impl_unittest.cc | 3 +- .../neteq/statistics_calculator.cc | 8 ++ .../neteq/statistics_calculator.h | 8 +- .../neteq/statistics_calculator_unittest.cc | 24 ++++++ 13 files changed, 142 insertions(+), 51 deletions(-) diff --git a/modules/audio_coding/neteq/decision_logic_unittest.cc b/modules/audio_coding/neteq/decision_logic_unittest.cc index 17840d1a28..14b58425ca 100644 --- a/modules/audio_coding/neteq/decision_logic_unittest.cc +++ b/modules/audio_coding/neteq/decision_logic_unittest.cc @@ -16,6 +16,7 @@ #include "modules/audio_coding/neteq/delay_manager.h" #include "modules/audio_coding/neteq/delay_peak_detector.h" #include "modules/audio_coding/neteq/packet_buffer.h" +#include "modules/audio_coding/neteq/statistics_calculator.h" #include "modules/audio_coding/neteq/tick_timer.h" #include "test/field_trial.h" #include "test/gtest.h" @@ -29,10 +30,11 @@ TEST(DecisionLogic, CreateAndDestroy) { DecoderDatabase decoder_database( new rtc::RefCountedObject, absl::nullopt); TickTimer tick_timer; + StatisticsCalculator stats; PacketBuffer packet_buffer(10, &tick_timer); DelayPeakDetector delay_peak_detector(&tick_timer, false); - auto delay_manager = - DelayManager::Create(240, 0, false, &delay_peak_detector, &tick_timer); + auto delay_manager = DelayManager::Create(240, 0, false, &delay_peak_detector, + &tick_timer, &stats); BufferLevelFilter buffer_level_filter; DecisionLogic* logic = DecisionLogic::Create( fs_hz, output_size_samples, false, &decoder_database, packet_buffer, diff --git a/modules/audio_coding/neteq/delay_manager.cc b/modules/audio_coding/neteq/delay_manager.cc index 1c7ad19025..ab2d48dff2 100644 --- a/modules/audio_coding/neteq/delay_manager.cc +++ b/modules/audio_coding/neteq/delay_manager.cc @@ -20,6 +20,7 @@ #include "absl/memory/memory.h" #include "modules/audio_coding/neteq/delay_peak_detector.h" #include "modules/audio_coding/neteq/histogram.h" +#include "modules/audio_coding/neteq/statistics_calculator.h" #include "modules/include/module_common_types_public.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -114,6 +115,7 @@ DelayManager::DelayManager(size_t max_packets_in_buffer, bool enable_rtx_handling, DelayPeakDetector* peak_detector, const TickTimer* tick_timer, + StatisticsCalculator* statistics, std::unique_ptr histogram) : first_packet_received_(false), max_packets_in_buffer_(max_packets_in_buffer), @@ -121,6 +123,7 @@ DelayManager::DelayManager(size_t max_packets_in_buffer, histogram_quantile_(histogram_quantile), histogram_mode_(histogram_mode), tick_timer_(tick_timer), + statistics_(statistics), base_minimum_delay_ms_(base_minimum_delay_ms), effective_minimum_delay_ms_(base_minimum_delay_ms), base_target_level_(4), // In Q0 domain. @@ -150,7 +153,8 @@ std::unique_ptr DelayManager::Create( int base_minimum_delay_ms, bool enable_rtx_handling, DelayPeakDetector* peak_detector, - const TickTimer* tick_timer) { + const TickTimer* tick_timer, + StatisticsCalculator* statistics) { int quantile; std::unique_ptr histogram; HistogramMode mode; @@ -168,7 +172,8 @@ std::unique_ptr DelayManager::Create( } return absl::make_unique( max_packets_in_buffer, base_minimum_delay_ms, quantile, mode, - enable_rtx_handling, peak_detector, tick_timer, std::move(histogram)); + enable_rtx_handling, peak_detector, tick_timer, statistics, + std::move(histogram)); } DelayManager::~DelayManager() {} @@ -234,16 +239,18 @@ int DelayManager::Update(uint16_t sequence_number, reordered = true; } + int iat_delay = iat_ms - packet_len_ms; + int relative_delay; + if (reordered) { + relative_delay = std::max(iat_delay, 0); + } else { + UpdateDelayHistory(iat_delay); + relative_delay = CalculateRelativePacketArrivalDelay(); + } + statistics_->RelativePacketArrivalDelay(relative_delay); + switch (histogram_mode_) { case RELATIVE_ARRIVAL_DELAY: { - int iat_delay = iat_ms - packet_len_ms; - int relative_delay; - if (reordered) { - relative_delay = std::max(iat_delay, 0); - } else { - UpdateDelayHistory(iat_delay); - relative_delay = CalculateRelativePacketArrivalDelay(); - } const int index = relative_delay / kBucketSizeMs; if (index < histogram_->NumBuckets()) { // Maximum delay to register is 2000 ms. diff --git a/modules/audio_coding/neteq/delay_manager.h b/modules/audio_coding/neteq/delay_manager.h index 11dfeb9355..e54e950d82 100644 --- a/modules/audio_coding/neteq/delay_manager.h +++ b/modules/audio_coding/neteq/delay_manager.h @@ -18,6 +18,7 @@ #include "absl/types/optional.h" #include "modules/audio_coding/neteq/histogram.h" +#include "modules/audio_coding/neteq/statistics_calculator.h" #include "modules/audio_coding/neteq/tick_timer.h" #include "rtc_base/constructor_magic.h" @@ -40,6 +41,7 @@ class DelayManager { bool enable_rtx_handling, DelayPeakDetector* peak_detector, const TickTimer* tick_timer, + StatisticsCalculator* statistics, std::unique_ptr histogram); // Create a DelayManager object. Notify the delay manager that the packet @@ -51,7 +53,8 @@ class DelayManager { int base_minimum_delay_ms, bool enable_rtx_handling, DelayPeakDetector* peak_detector, - const TickTimer* tick_timer); + const TickTimer* tick_timer, + StatisticsCalculator* statistics); virtual ~DelayManager(); @@ -174,6 +177,7 @@ class DelayManager { const int histogram_quantile_; const HistogramMode histogram_mode_; const TickTimer* tick_timer_; + StatisticsCalculator* statistics_; int base_minimum_delay_ms_; // Provides delay which is used by LimitTargetLevel as lower bound on target // delay. diff --git a/modules/audio_coding/neteq/delay_manager_unittest.cc b/modules/audio_coding/neteq/delay_manager_unittest.cc index 7b573240d3..c57f074326 100644 --- a/modules/audio_coding/neteq/delay_manager_unittest.cc +++ b/modules/audio_coding/neteq/delay_manager_unittest.cc @@ -18,6 +18,7 @@ #include "modules/audio_coding/neteq/histogram.h" #include "modules/audio_coding/neteq/mock/mock_delay_peak_detector.h" #include "modules/audio_coding/neteq/mock/mock_histogram.h" +#include "modules/audio_coding/neteq/mock/mock_statistics_calculator.h" #include "rtc_base/checks.h" #include "test/field_trial.h" #include "test/gmock.h" @@ -53,6 +54,7 @@ class DelayManagerTest : public ::testing::Test { std::unique_ptr dm_; TickTimer tick_timer_; + MockStatisticsCalculator stats_; MockDelayPeakDetector detector_; MockHistogram* mock_histogram_; uint16_t seq_no_; @@ -81,10 +83,11 @@ void DelayManagerTest::RecreateDelayManager() { dm_ = absl::make_unique( kMaxNumberOfPackets, kMinDelayMs, kDefaultHistogramQuantile, histogram_mode_, enable_rtx_handling_, &detector_, &tick_timer_, - std::move(histogram)); + &stats_, std::move(histogram)); } else { dm_ = DelayManager::Create(kMaxNumberOfPackets, kMinDelayMs, - enable_rtx_handling_, &detector_, &tick_timer_); + enable_rtx_handling_, &detector_, &tick_timer_, + &stats_); } } @@ -709,4 +712,17 @@ TEST_F(DelayManagerTest, RelativeArrivalDelayMode) { EXPECT_EQ(0, dm_->Update(seq_no_, ts_, kFs)); } +TEST_F(DelayManagerTest, RelativeArrivalDelayStatistic) { + SetPacketAudioLength(kFrameSizeMs); + InsertNextPacket(); + + IncreaseTime(kFrameSizeMs); + EXPECT_CALL(stats_, RelativePacketArrivalDelay(0)); + InsertNextPacket(); + + IncreaseTime(2 * kFrameSizeMs); + EXPECT_CALL(stats_, RelativePacketArrivalDelay(20)); + InsertNextPacket(); +} + } // namespace webrtc diff --git a/modules/audio_coding/neteq/include/neteq.h b/modules/audio_coding/neteq/include/neteq.h index a1c0b529bf..57fd349fc5 100644 --- a/modules/audio_coding/neteq/include/neteq.h +++ b/modules/audio_coding/neteq/include/neteq.h @@ -71,9 +71,18 @@ struct NetEqLifetimeStatistics { uint64_t concealment_events = 0; uint64_t jitter_buffer_delay_ms = 0; uint64_t jitter_buffer_emitted_count = 0; - // Below stat is not part of the spec. + // Below stats are not part of the spec. uint64_t voice_concealed_samples = 0; uint64_t delayed_packet_outage_samples = 0; + // This is sum of relative packet arrival delays of received packets so far. + // Since end-to-end delay of a packet is difficult to measure and is not + // necessarily useful for measuring jitter buffer performance, we report a + // relative packet arrival delay. The relative packet arrival delay of a + // packet is defined as the arrival delay compared to the first packet + // received, given that it had zero delay. To avoid clock drift, the "first" + // packet can be made dynamic. + uint64_t relative_packet_arrival_delay_ms = 0; + uint64_t jitter_buffer_packets_received = 0; }; // Metrics that describe the operations performed in NetEq, and the internal diff --git a/modules/audio_coding/neteq/mock/mock_delay_manager.h b/modules/audio_coding/neteq/mock/mock_delay_manager.h index 63dd5751d1..3a128ceed5 100644 --- a/modules/audio_coding/neteq/mock/mock_delay_manager.h +++ b/modules/audio_coding/neteq/mock/mock_delay_manager.h @@ -15,6 +15,7 @@ #include "modules/audio_coding/neteq/delay_manager.h" #include "modules/audio_coding/neteq/histogram.h" +#include "modules/audio_coding/neteq/statistics_calculator.h" #include "test/gmock.h" namespace webrtc { @@ -28,6 +29,7 @@ class MockDelayManager : public DelayManager { bool enable_rtx_handling, DelayPeakDetector* peak_detector, const TickTimer* tick_timer, + StatisticsCalculator* stats, std::unique_ptr histogram) : DelayManager(max_packets_in_buffer, base_min_target_delay_ms, @@ -36,6 +38,7 @@ class MockDelayManager : public DelayManager { enable_rtx_handling, peak_detector, tick_timer, + stats, std::move(histogram)) {} virtual ~MockDelayManager() { Die(); } MOCK_METHOD0(Die, void()); diff --git a/modules/audio_coding/neteq/mock/mock_statistics_calculator.h b/modules/audio_coding/neteq/mock/mock_statistics_calculator.h index 85f26206c9..aedb1dfaeb 100644 --- a/modules/audio_coding/neteq/mock/mock_statistics_calculator.h +++ b/modules/audio_coding/neteq/mock/mock_statistics_calculator.h @@ -21,6 +21,7 @@ class MockStatisticsCalculator : public StatisticsCalculator { public: MOCK_METHOD1(PacketsDiscarded, void(size_t num_packets)); MOCK_METHOD1(SecondaryPacketsDiscarded, void(size_t num_packets)); + MOCK_METHOD1(RelativePacketArrivalDelay, void(size_t delay_ms)); }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc index 5e6b8bcc2a..f1e8527971 100644 --- a/modules/audio_coding/neteq/neteq_impl.cc +++ b/modules/audio_coding/neteq/neteq_impl.cc @@ -41,6 +41,7 @@ #include "modules/audio_coding/neteq/post_decode_vad.h" #include "modules/audio_coding/neteq/preemptive_expand.h" #include "modules/audio_coding/neteq/red_payload_splitter.h" +#include "modules/audio_coding/neteq/statistics_calculator.h" #include "modules/audio_coding/neteq/sync_buffer.h" #include "modules/audio_coding/neteq/tick_timer.h" #include "modules/audio_coding/neteq/time_stretch.h" @@ -58,6 +59,7 @@ NetEqImpl::Dependencies::Dependencies( const NetEq::Config& config, const rtc::scoped_refptr& decoder_factory) : tick_timer(new TickTimer), + stats(new StatisticsCalculator), buffer_level_filter(new BufferLevelFilter), decoder_database( new DecoderDatabase(decoder_factory, config.codec_pair_id)), @@ -67,7 +69,8 @@ NetEqImpl::Dependencies::Dependencies( config.min_delay_ms, config.enable_rtx_handling, delay_peak_detector.get(), - tick_timer.get())), + tick_timer.get(), + stats.get())), dtmf_buffer(new DtmfBuffer(config.sample_rate_hz)), dtmf_tone_generator(new DtmfToneGenerator), packet_buffer( @@ -97,6 +100,7 @@ NetEqImpl::NetEqImpl(const NetEq::Config& config, expand_factory_(std::move(deps.expand_factory)), accelerate_factory_(std::move(deps.accelerate_factory)), preemptive_expand_factory_(std::move(deps.preemptive_expand_factory)), + stats_(std::move(deps.stats)), last_mode_(kModeNormal), decoded_buffer_length_(kMaxFrameSize), decoded_buffer_(new int16_t[decoded_buffer_length_]), @@ -233,7 +237,7 @@ void NetEqImpl::SetCodecs(const std::map& codecs) { const std::vector changed_payload_types = decoder_database_->SetCodecs(codecs); for (const int pt : changed_payload_types) { - packet_buffer_->DiscardPacketsWithPayloadType(pt, &stats_); + packet_buffer_->DiscardPacketsWithPayloadType(pt, stats_.get()); } } @@ -251,7 +255,8 @@ int NetEqImpl::RemovePayloadType(uint8_t rtp_payload_type) { rtc::CritScope lock(&crit_sect_); int ret = decoder_database_->Remove(rtp_payload_type); if (ret == DecoderDatabase::kOK || ret == DecoderDatabase::kDecoderNotFound) { - packet_buffer_->DiscardPacketsWithPayloadType(rtp_payload_type, &stats_); + packet_buffer_->DiscardPacketsWithPayloadType(rtp_payload_type, + stats_.get()); return kOK; } return kFail; @@ -329,20 +334,21 @@ int NetEqImpl::NetworkStatistics(NetEqNetworkStatistics* stats) { assert(decision_logic_.get()); const int ms_per_packet = rtc::dchecked_cast( decision_logic_->packet_length_samples() / (fs_hz_ / 1000)); - stats_.PopulateDelayManagerStats(ms_per_packet, *delay_manager_.get(), stats); - stats_.GetNetworkStatistics(fs_hz_, total_samples_in_buffers, - decoder_frame_length_, stats); + stats_->PopulateDelayManagerStats(ms_per_packet, *delay_manager_.get(), + stats); + stats_->GetNetworkStatistics(fs_hz_, total_samples_in_buffers, + decoder_frame_length_, stats); return 0; } NetEqLifetimeStatistics NetEqImpl::GetLifetimeStatistics() const { rtc::CritScope lock(&crit_sect_); - return stats_.GetLifetimeStatistics(); + return stats_->GetLifetimeStatistics(); } NetEqOperationsAndState NetEqImpl::GetOperationsAndState() const { rtc::CritScope lock(&crit_sect_); - auto result = stats_.GetOperationsAndState(); + auto result = stats_->GetOperationsAndState(); result.current_buffer_size_ms = (packet_buffer_->NumSamplesInBuffer(decoder_frame_length_) + sync_buffer_->FutureLength()) * @@ -469,6 +475,7 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header, RTC_LOG_F(LS_ERROR) << "payload is empty"; return kInvalidPointer; } + stats_->ReceivedPacket(); PacketList packet_list; // Insert packet in a packet list. @@ -654,7 +661,7 @@ int NetEqImpl::InsertPacketInternal(const RTPHeader& rtp_header, // Insert packets in buffer. const int ret = packet_buffer_->InsertPacketList( &parsed_packet_list, *decoder_database_, ¤t_rtp_payload_type_, - ¤t_cng_rtp_payload_type_, &stats_); + ¤t_cng_rtp_payload_type_, stats_.get()); if (ret == PacketBuffer::kFlushed) { // Reset DSP timestamp etc. if packet buffer flushed. new_codec_ = true; @@ -751,8 +758,8 @@ int NetEqImpl::GetAudioInternal(AudioFrame* audio_frame, *muted = false; last_decoded_timestamps_.clear(); tick_timer_->Increment(); - stats_.IncreaseCounter(output_size_samples_, fs_hz_); - const auto lifetime_stats = stats_.GetLifetimeStatistics(); + stats_->IncreaseCounter(output_size_samples_, fs_hz_); + const auto lifetime_stats = stats_->GetLifetimeStatistics(); expand_uma_logger_.UpdateSampleCounter(lifetime_stats.concealed_samples, fs_hz_); speech_expand_uma_logger_.UpdateSampleCounter( @@ -772,7 +779,7 @@ int NetEqImpl::GetAudioInternal(AudioFrame* audio_frame, : timestamp_scaler_->ToExternal(playout_timestamp_) - static_cast(audio_frame->samples_per_channel_); audio_frame->num_channels_ = sync_buffer_->Channels(); - stats_.ExpandedNoiseSamples(output_size_samples_, false); + stats_->ExpandedNoiseSamples(output_size_samples_, false); *muted = true; return 0; } @@ -981,7 +988,7 @@ int NetEqImpl::GetDecision(Operations* operation, if (!new_codec_) { const uint32_t five_seconds_samples = 5 * fs_hz_; packet_buffer_->DiscardOldPackets(end_timestamp, five_seconds_samples, - &stats_); + stats_.get()); } const Packet* packet = packet_buffer_->PeekNextPacket(); @@ -1001,12 +1008,14 @@ int NetEqImpl::GetDecision(Operations* operation, (end_timestamp >= packet->timestamp || end_timestamp + generated_noise_samples > packet->timestamp)) { // Don't use this packet, discard it. - if (packet_buffer_->DiscardNextPacket(&stats_) != PacketBuffer::kOK) { + if (packet_buffer_->DiscardNextPacket(stats_.get()) != + PacketBuffer::kOK) { assert(false); // Must be ok by design. } // Check buffer again. if (!new_codec_) { - packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_, &stats_); + packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_, + stats_.get()); } packet = packet_buffer_->PeekNextPacket(); } @@ -1088,7 +1097,7 @@ int NetEqImpl::GetDecision(Operations* operation, decision_logic_->SoftReset(); buffer_level_filter_->Reset(); delay_manager_->Reset(); - stats_.ResetMcu(); + stats_->ResetMcu(); } size_t required_samples = output_size_samples_; @@ -1193,7 +1202,7 @@ int NetEqImpl::GetDecision(Operations* operation, // if comfort noise is not played. If comfort noise was just played, // this adjustment of timestamp is only done to get back in sync with the // stream timestamp; no loss to report. - stats_.LostSamples(packet->timestamp - end_timestamp); + stats_->LostSamples(packet->timestamp - end_timestamp); } if (*operation != kRfc3389Cng) { @@ -1460,10 +1469,10 @@ void NetEqImpl::DoMerge(int16_t* decoded_buffer, // Update in-call and post-call statistics. if (expand_->MuteFactor(0) == 0) { // Expand generates only noise. - stats_.ExpandedNoiseSamplesCorrection(expand_length_correction); + stats_->ExpandedNoiseSamplesCorrection(expand_length_correction); } else { // Expansion generates more than only noise. - stats_.ExpandedVoiceSamplesCorrection(expand_length_correction); + stats_->ExpandedVoiceSamplesCorrection(expand_length_correction); } last_mode_ = kModeMerge; @@ -1504,12 +1513,12 @@ bool NetEqImpl::DoCodecPlc() { if (std::all_of(concealment_audio_.cbegin(), concealment_audio_.cend(), [](int16_t i) { return i == 0; })) { // Expand operation generates only noise. - stats_.ExpandedNoiseSamples(concealed_samples_per_channel, - is_new_concealment_event); + stats_->ExpandedNoiseSamples(concealed_samples_per_channel, + is_new_concealment_event); } else { // Expand operation generates more than only noise. - stats_.ExpandedVoiceSamples(concealed_samples_per_channel, - is_new_concealment_event); + stats_->ExpandedVoiceSamples(concealed_samples_per_channel, + is_new_concealment_event); } last_mode_ = kModeCodecPlc; if (!generated_noise_stopwatch_) { @@ -1530,10 +1539,10 @@ int NetEqImpl::DoExpand(bool play_dtmf) { // Update in-call and post-call statistics. if (expand_->MuteFactor(0) == 0) { // Expand operation generates only noise. - stats_.ExpandedNoiseSamples(length, is_new_concealment_event); + stats_->ExpandedNoiseSamples(length, is_new_concealment_event); } else { // Expand operation generates more than only noise. - stats_.ExpandedVoiceSamples(length, is_new_concealment_event); + stats_->ExpandedVoiceSamples(length, is_new_concealment_event); } last_mode_ = kModeExpand; @@ -1582,7 +1591,7 @@ int NetEqImpl::DoAccelerate(int16_t* decoded_buffer, Accelerate::ReturnCodes return_code = accelerate_->Process(decoded_buffer, decoded_length, fast_accelerate, algorithm_buffer_.get(), &samples_removed); - stats_.AcceleratedSamples(samples_removed); + stats_->AcceleratedSamples(samples_removed); switch (return_code) { case Accelerate::kSuccess: last_mode_ = kModeAccelerateSuccess; @@ -1660,7 +1669,7 @@ int NetEqImpl::DoPreemptiveExpand(int16_t* decoded_buffer, PreemptiveExpand::ReturnCodes return_code = preemptive_expand_->Process( decoded_buffer, decoded_length, old_borrowed_samples_per_channel, algorithm_buffer_.get(), &samples_added); - stats_.PreemptiveExpandedSamples(samples_added); + stats_->PreemptiveExpandedSamples(samples_added); switch (return_code) { case PreemptiveExpand::kSuccess: last_mode_ = kModePreemptiveExpandSuccess; @@ -1875,7 +1884,7 @@ int NetEqImpl::ExtractPackets(size_t required_samples, return -1; } const uint64_t waiting_time_ms = packet->waiting_time->ElapsedMs(); - stats_.StoreWaitingTime(waiting_time_ms); + stats_->StoreWaitingTime(waiting_time_ms); RTC_DCHECK(!packet->empty()); if (first_packet) { @@ -1899,7 +1908,7 @@ int NetEqImpl::ExtractPackets(size_t required_samples, packet_duration = packet->frame->Duration(); // TODO(ossu): Is this the correct way to track Opus FEC packets? if (packet->priority.codec_level > 0) { - stats_.SecondaryDecodedSamples( + stats_->SecondaryDecodedSamples( rtc::dchecked_cast(packet_duration)); } } else if (!has_cng_packet) { @@ -1915,7 +1924,7 @@ int NetEqImpl::ExtractPackets(size_t required_samples, } extracted_samples = packet->timestamp - first_timestamp + packet_duration; - stats_.JitterBufferDelay(packet_duration, waiting_time_ms); + stats_->JitterBufferDelay(packet_duration, waiting_time_ms); packet_list->push_back(std::move(*packet)); // Store packet in list. packet = absl::nullopt; // Ensure it's never used after the move. @@ -1943,7 +1952,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_); + packet_buffer_->DiscardAllOldPackets(timestamp_, stats_.get()); } return rtc::dchecked_cast(extracted_samples); @@ -1953,7 +1962,7 @@ void NetEqImpl::UpdatePlcComponents(int fs_hz, size_t channels) { // Delete objects and create new ones. expand_.reset(expand_factory_->Create(background_noise_.get(), sync_buffer_.get(), &random_vector_, - &stats_, fs_hz, channels)); + stats_.get(), fs_hz, channels)); merge_.reset(new Merge(fs_hz, channels, expand_.get(), sync_buffer_.get())); } diff --git a/modules/audio_coding/neteq/neteq_impl.h b/modules/audio_coding/neteq/neteq_impl.h index 23b63eb943..34a5c71258 100644 --- a/modules/audio_coding/neteq/neteq_impl.h +++ b/modules/audio_coding/neteq/neteq_impl.h @@ -99,6 +99,7 @@ class NetEqImpl : public webrtc::NetEq { ~Dependencies(); std::unique_ptr tick_timer; + std::unique_ptr stats; std::unique_ptr buffer_level_filter; std::unique_ptr decoder_database; std::unique_ptr delay_peak_detector; @@ -361,6 +362,7 @@ class NetEqImpl : public webrtc::NetEq { RTC_GUARDED_BY(crit_sect_); const std::unique_ptr preemptive_expand_factory_ RTC_GUARDED_BY(crit_sect_); + const std::unique_ptr stats_ RTC_GUARDED_BY(crit_sect_); std::unique_ptr background_noise_ RTC_GUARDED_BY(crit_sect_); std::unique_ptr decision_logic_ RTC_GUARDED_BY(crit_sect_); @@ -375,7 +377,6 @@ class NetEqImpl : public webrtc::NetEq { RTC_GUARDED_BY(crit_sect_); RandomVector random_vector_ RTC_GUARDED_BY(crit_sect_); std::unique_ptr comfort_noise_ RTC_GUARDED_BY(crit_sect_); - StatisticsCalculator stats_ RTC_GUARDED_BY(crit_sect_); int fs_hz_ RTC_GUARDED_BY(crit_sect_); int fs_mult_ RTC_GUARDED_BY(crit_sect_); int last_output_sample_rate_hz_ RTC_GUARDED_BY(crit_sect_); diff --git a/modules/audio_coding/neteq/neteq_impl_unittest.cc b/modules/audio_coding/neteq/neteq_impl_unittest.cc index 86fbe9c75c..5875493b61 100644 --- a/modules/audio_coding/neteq/neteq_impl_unittest.cc +++ b/modules/audio_coding/neteq/neteq_impl_unittest.cc @@ -26,6 +26,7 @@ #include "modules/audio_coding/neteq/mock/mock_red_payload_splitter.h" #include "modules/audio_coding/neteq/neteq_impl.h" #include "modules/audio_coding/neteq/preemptive_expand.h" +#include "modules/audio_coding/neteq/statistics_calculator.h" #include "modules/audio_coding/neteq/sync_buffer.h" #include "modules/audio_coding/neteq/timestamp_scaler.h" #include "rtc_base/numerics/safe_conversions.h" @@ -100,7 +101,7 @@ class NetEqImplTest : public ::testing::Test { config_.max_packets_in_buffer, config_.min_delay_ms, 1020054733, DelayManager::HistogramMode::INTER_ARRIVAL_TIME, config_.enable_rtx_handling, delay_peak_detector_, tick_timer_, - absl::make_unique(50, 32745))); + deps.stats.get(), absl::make_unique(50, 32745))); mock_delay_manager_ = mock.get(); EXPECT_CALL(*mock_delay_manager_, set_streaming_mode(false)).Times(1); deps.delay_manager = std::move(mock); diff --git a/modules/audio_coding/neteq/statistics_calculator.cc b/modules/audio_coding/neteq/statistics_calculator.cc index 7ad1a28b69..a0e9bca4b1 100644 --- a/modules/audio_coding/neteq/statistics_calculator.cc +++ b/modules/audio_coding/neteq/statistics_calculator.cc @@ -258,6 +258,14 @@ void StatisticsCalculator::FlushedPacketBuffer() { buffer_full_counter_.RegisterSample(); } +void StatisticsCalculator::ReceivedPacket() { + ++lifetime_stats_.jitter_buffer_packets_received; +} + +void StatisticsCalculator::RelativePacketArrivalDelay(size_t delay_ms) { + lifetime_stats_.relative_packet_arrival_delay_ms += delay_ms; +} + void StatisticsCalculator::LogDelayedPacketOutageEvent(int num_samples, int fs_hz) { int outage_duration_ms = num_samples / (fs_hz / 1000); diff --git a/modules/audio_coding/neteq/statistics_calculator.h b/modules/audio_coding/neteq/statistics_calculator.h index 1dee6436a3..cb92f37f8f 100644 --- a/modules/audio_coding/neteq/statistics_calculator.h +++ b/modules/audio_coding/neteq/statistics_calculator.h @@ -83,9 +83,15 @@ class StatisticsCalculator { // Reports that |num_samples| samples were decoded from secondary packets. void SecondaryDecodedSamples(int num_samples); - // Rerport that the packet buffer was flushed. + // Reports that the packet buffer was flushed. void FlushedPacketBuffer(); + // Reports that the jitter buffer received a packet. + void ReceivedPacket(); + + // Reports that a received packet was delayed by |delay_ms| milliseconds. + virtual void RelativePacketArrivalDelay(size_t delay_ms); + // Logs a delayed packet outage event of |num_samples| expanded at a sample // rate of |fs_hz|. A delayed packet outage event is defined as an expand // period caused not by an actual packet loss, but by a delayed packet. diff --git a/modules/audio_coding/neteq/statistics_calculator_unittest.cc b/modules/audio_coding/neteq/statistics_calculator_unittest.cc index 0a4901d51b..1fb8e1c4d3 100644 --- a/modules/audio_coding/neteq/statistics_calculator_unittest.cc +++ b/modules/audio_coding/neteq/statistics_calculator_unittest.cc @@ -104,4 +104,28 @@ TEST(StatisticsCalculator, ExpandedSamplesCorrection) { EXPECT_EQ((50u << 14) / k10MsSamples, stats_output.speech_expand_rate); } +TEST(StatisticsCalculator, RelativePacketArrivalDelay) { + StatisticsCalculator stats; + + stats.RelativePacketArrivalDelay(50); + NetEqLifetimeStatistics stats_output = stats.GetLifetimeStatistics(); + EXPECT_EQ(50u, stats_output.relative_packet_arrival_delay_ms); + + stats.RelativePacketArrivalDelay(20); + stats_output = stats.GetLifetimeStatistics(); + EXPECT_EQ(70u, stats_output.relative_packet_arrival_delay_ms); +} + +TEST(StatisticsCalculator, ReceivedPacket) { + StatisticsCalculator stats; + + stats.ReceivedPacket(); + NetEqLifetimeStatistics stats_output = stats.GetLifetimeStatistics(); + EXPECT_EQ(1u, stats_output.jitter_buffer_packets_received); + + stats.ReceivedPacket(); + stats_output = stats.GetLifetimeStatistics(); + EXPECT_EQ(2u, stats_output.jitter_buffer_packets_received); +} + } // namespace webrtc