diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.cc b/webrtc/modules/video_coding/main/source/jitter_buffer.cc index d09fccd91..e3a4a8a71 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer.cc +++ b/webrtc/modules/video_coding/main/source/jitter_buffer.cc @@ -25,6 +25,7 @@ #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/metrics.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { @@ -143,6 +144,8 @@ VCMJitterBuffer::VCMJitterBuffer(Clock* clock, EventFactory* event_factory) drop_count_(0), num_consecutive_old_frames_(0), num_consecutive_old_packets_(0), + num_packets_(0), + num_duplicated_packets_(0), num_discarded_packets_(0), jitter_estimate_(clock), inter_frame_delay_(clock_->TimeInMilliseconds()), @@ -190,6 +193,8 @@ void VCMJitterBuffer::CopyFrom(const VCMJitterBuffer& rhs) { drop_count_ = rhs.drop_count_; num_consecutive_old_frames_ = rhs.num_consecutive_old_frames_; num_consecutive_old_packets_ = rhs.num_consecutive_old_packets_; + num_packets_ = rhs.num_packets_; + num_duplicated_packets_ = rhs.num_duplicated_packets_; num_discarded_packets_ = rhs.num_discarded_packets_; jitter_estimate_ = rhs.jitter_estimate_; inter_frame_delay_ = rhs.inter_frame_delay_; @@ -238,6 +243,15 @@ void VCMJitterBuffer::CopyFrames(FrameList* to_list, } } +void VCMJitterBuffer::UpdateHistograms() { + if (num_packets_ > 0) { + RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.DiscardedPacketsInPercent", + num_discarded_packets_ * 100 / num_packets_); + RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.DuplicatedPacketsInPercent", + num_duplicated_packets_ * 100 / num_packets_); + } +} + void VCMJitterBuffer::Start() { CriticalSectionScoped cs(crit_sect_); running_ = true; @@ -250,6 +264,8 @@ void VCMJitterBuffer::Start() { num_consecutive_old_frames_ = 0; num_consecutive_old_packets_ = 0; + num_packets_ = 0; + num_duplicated_packets_ = 0; num_discarded_packets_ = 0; // Start in a non-signaled state. @@ -265,6 +281,7 @@ void VCMJitterBuffer::Start() { void VCMJitterBuffer::Stop() { crit_sect_->Enter(); + UpdateHistograms(); running_ = false; last_decoded_state_.Reset(); free_frames_.clear(); @@ -313,6 +330,16 @@ std::map VCMJitterBuffer::FrameStatistics() const { return receive_statistics_; } +int VCMJitterBuffer::num_packets() const { + CriticalSectionScoped cs(crit_sect_); + return num_packets_; +} + +int VCMJitterBuffer::num_duplicated_packets() const { + CriticalSectionScoped cs(crit_sect_); + return num_duplicated_packets_; +} + int VCMJitterBuffer::num_discarded_packets() const { CriticalSectionScoped cs(crit_sect_); return num_discarded_packets_; @@ -543,6 +570,7 @@ void VCMJitterBuffer::ReleaseFrame(VCMEncodedFrame* frame) { // Gets frame to use for this timestamp. If no match, get empty frame. VCMFrameBufferEnum VCMJitterBuffer::GetFrame(const VCMPacket& packet, VCMFrameBuffer** frame) { + ++num_packets_; // Does this packet belong to an old frame? if (last_decoded_state_.IsOldPacket(&packet)) { // Account only for media packets. @@ -747,6 +775,7 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, case kNoError: case kOutOfBoundsPacket: case kDuplicatePacket: { + ++num_duplicated_packets_; break; } case kFlushIndicator: diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.h b/webrtc/modules/video_coding/main/source/jitter_buffer.h index 6ed9cfb85..182b80b4e 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer.h +++ b/webrtc/modules/video_coding/main/source/jitter_buffer.h @@ -103,6 +103,12 @@ class VCMJitterBuffer { // won't be able to decode them. int num_not_decodable_packets() const; + // Gets number of packets received. + int num_packets() const; + + // Gets number of duplicated packets received. + int num_duplicated_packets() const; + // Gets number of packets discarded by the jitter buffer. int num_discarded_packets() const; @@ -271,6 +277,8 @@ class VCMJitterBuffer { uint16_t EstimatedLowSequenceNumber(const VCMFrameBuffer& frame) const; + void UpdateHistograms(); + Clock* clock_; // If we are running (have started) or not. bool running_; @@ -303,6 +311,10 @@ class VCMJitterBuffer { int num_consecutive_old_frames_; // Number of packets in a row that have been too old. int num_consecutive_old_packets_; + // Number of packets received. + int num_packets_; + // Number of duplicated packets received. + int num_duplicated_packets_; // Number of packets discarded by the jitter buffer. int num_discarded_packets_; diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc index 0490658b4..899a3eeca 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc +++ b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc @@ -512,6 +512,8 @@ TEST_F(TestBasicJitterBuffer, DuplicatePackets) { packet_->markerBit = false; packet_->seqNum = seq_num_; packet_->timestamp = timestamp_; + EXPECT_EQ(0, jitter_buffer_->num_packets()); + EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets()); bool retransmitted = false; EXPECT_EQ(kIncomplete, jitter_buffer_->InsertPacket(*packet_, @@ -520,6 +522,8 @@ TEST_F(TestBasicJitterBuffer, DuplicatePackets) { VCMEncodedFrame* frame_out = DecodeCompleteFrame(); EXPECT_TRUE(frame_out == NULL); + EXPECT_EQ(1, jitter_buffer_->num_packets()); + EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets()); packet_->isFirstPacket = false; packet_->markerBit = true; @@ -527,6 +531,8 @@ TEST_F(TestBasicJitterBuffer, DuplicatePackets) { // Insert a packet into a frame. EXPECT_EQ(kDuplicatePacket, jitter_buffer_->InsertPacket(*packet_, &retransmitted)); + EXPECT_EQ(2, jitter_buffer_->num_packets()); + EXPECT_EQ(1, jitter_buffer_->num_duplicated_packets()); seq_num_++; packet_->seqNum = seq_num_; @@ -539,6 +545,8 @@ TEST_F(TestBasicJitterBuffer, DuplicatePackets) { CheckOutFrame(frame_out, 2 * size_, false); EXPECT_EQ(kVideoFrameKey, frame_out->FrameType()); + EXPECT_EQ(3, jitter_buffer_->num_packets()); + EXPECT_EQ(1, jitter_buffer_->num_duplicated_packets()); } TEST_F(TestBasicJitterBuffer, H264InsertStartCode) { diff --git a/webrtc/system_wrappers/interface/metrics.h b/webrtc/system_wrappers/interface/metrics.h index be9564a0c..0390140f5 100644 --- a/webrtc/system_wrappers/interface/metrics.h +++ b/webrtc/system_wrappers/interface/metrics.h @@ -69,12 +69,26 @@ // Also consider changing string to const char* when switching to atomics. // Histogram for counters. +#define RTC_HISTOGRAM_COUNTS_100(name, sample) RTC_HISTOGRAM_COUNTS( \ + name, sample, 1, 100, 50) + +#define RTC_HISTOGRAM_COUNTS_1000(name, sample) RTC_HISTOGRAM_COUNTS( \ + name, sample, 1, 1000, 50) + +#define RTC_HISTOGRAM_COUNTS_10000(name, sample) RTC_HISTOGRAM_COUNTS( \ + name, sample, 1, 10000, 50) + #define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \ RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ webrtc::metrics::HistogramFactoryGetCounts( \ name, min, max, bucket_count)) +// Histogram for percentage. +#define RTC_HISTOGRAM_PERCENTAGE(name, sample) \ + RTC_HISTOGRAM_ENUMERATION(name, sample, 101) + // Histogram for enumerators. +// |boundary| should be above the max enumerator sample. #define RTC_HISTOGRAM_ENUMERATION(name, sample, boundary) \ RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ webrtc::metrics::HistogramFactoryGetEnumeration(name, boundary)) diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc index bbcb602cc..ded21407c 100644 --- a/webrtc/video_engine/vie_channel.cc +++ b/webrtc/video_engine/vie_channel.cc @@ -25,6 +25,7 @@ #include "webrtc/modules/video_render/include/video_render_defines.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/metrics.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" #include "webrtc/video_engine/call_stats.h" #include "webrtc/video_engine/include/vie_codec.h" @@ -147,7 +148,8 @@ ViEChannel::ViEChannel(int32_t channel_id, sender_(sender), nack_history_size_sender_(kSendSidePacketHistorySize), max_nack_reordering_threshold_(kMaxPacketAgeToNack), - pre_render_callback_(NULL) { + pre_render_callback_(NULL), + start_ms_(Clock::GetRealTimeClock()->TimeInMilliseconds()) { RtpRtcp::Configuration configuration; configuration.id = ViEModuleId(engine_id, channel_id); configuration.audio = false; @@ -222,6 +224,7 @@ int32_t ViEChannel::Init() { } ViEChannel::~ViEChannel() { + UpdateHistograms(); // Make sure we don't get more callbacks from the RTP module. module_process_thread_.DeRegisterModule(vie_receiver_.GetReceiveStatistics()); module_process_thread_.DeRegisterModule(rtp_rtcp_.get()); @@ -246,6 +249,55 @@ ViEChannel::~ViEChannel() { VideoCodingModule::Destroy(vcm_); } +void ViEChannel::UpdateHistograms() { + const float kMinCallLengthInMinutes = 0.5f; + float elapsed_minutes = + (Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_) / 60000.0f; + if (elapsed_minutes < kMinCallLengthInMinutes) { + return; + } + RtcpPacketTypeCounter rtcp_sent; + RtcpPacketTypeCounter rtcp_received; + GetRtcpPacketTypeCounters(&rtcp_sent, &rtcp_received); + + if (sender_) { + if (rtcp_received.nack_requests > 0) { + RTC_HISTOGRAM_PERCENTAGE( + "WebRTC.Video.UniqueNackRequestsReceivedInPercent", + rtcp_received.UniqueNackRequestsInPercent()); + } + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsReceivedPerMinute", + rtcp_received.nack_packets / elapsed_minutes); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsReceivedPerMinute", + rtcp_received.fir_packets / elapsed_minutes); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsReceivedPerMinute", + rtcp_received.pli_packets / elapsed_minutes); + } else if (vie_receiver_.GetRemoteSsrc() > 0) { + // Get receive stats if we are receiving packets, i.e. there is a remote + // ssrc. + if (rtcp_sent.nack_requests > 0) { + RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent", + rtcp_sent.UniqueNackRequestsInPercent()); + } + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsSentPerMinute", + rtcp_sent.nack_packets / elapsed_minutes); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsSentPerMinute", + rtcp_sent.fir_packets / elapsed_minutes); + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute", + rtcp_sent.pli_packets / elapsed_minutes); + + webrtc::VCMFrameCount frames; + if (vcm_->ReceivedFrameCount(frames) == VCM_OK) { + uint32_t total_frames = frames.numKeyFrames + frames.numDeltaFrames; + if (total_frames > 0) { + RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.KeyFramesReceivedInPermille", + static_cast((frames.numKeyFrames * 1000.0f / total_frames) + + 0.5f)); + } + } + } +} + int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec, bool new_stream) { if (!sender_) { diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h index e55ade799..032790699 100644 --- a/webrtc/video_engine/vie_channel.h +++ b/webrtc/video_engine/vie_channel.h @@ -383,6 +383,8 @@ class ViEChannel int GetRequiredNackListSize(int target_delay_ms); void SetRtxSendStatus(bool enable); + void UpdateHistograms(); + // ViEChannel exposes methods that allow to modify observers and callbacks // to be modified. Such an API-style is cumbersome to implement and maintain // at all the levels when comparing to only setting them at construction. As @@ -499,6 +501,7 @@ class ViEChannel int nack_history_size_sender_; int max_nack_reordering_threshold_; I420FrameCallback* pre_render_callback_; + const int64_t start_ms_; std::map prev_report_blocks_; }; diff --git a/webrtc/video_engine/vie_encoder.cc b/webrtc/video_engine/vie_encoder.cc index 26ad8acef..9d6da977e 100644 --- a/webrtc/video_engine/vie_encoder.cc +++ b/webrtc/video_engine/vie_encoder.cc @@ -26,6 +26,7 @@ #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/metrics.h" #include "webrtc/system_wrappers/interface/tick_util.h" #include "webrtc/system_wrappers/interface/trace_event.h" #include "webrtc/video_engine/include/vie_codec.h" @@ -156,7 +157,8 @@ ViEEncoder::ViEEncoder(int32_t engine_id, picture_id_rpsi_(0), qm_callback_(NULL), video_suspended_(false), - pre_encode_callback_(NULL) { + pre_encode_callback_(NULL), + start_ms_(Clock::GetRealTimeClock()->TimeInMilliseconds()) { RtpRtcp::Configuration configuration; configuration.id = ViEModuleId(engine_id_, channel_id_); configuration.audio = false; // Video. @@ -225,6 +227,7 @@ bool ViEEncoder::Init() { } ViEEncoder::~ViEEncoder() { + UpdateHistograms(); if (bitrate_controller_) { bitrate_controller_->RemoveBitrateObserver(bitrate_observer_.get()); } @@ -237,6 +240,25 @@ ViEEncoder::~ViEEncoder() { delete qm_callback_; } +void ViEEncoder::UpdateHistograms() { + const float kMinCallLengthInMinutes = 0.5f; + float elapsed_minutes = + (Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_) / 60000.0f; + if (elapsed_minutes < kMinCallLengthInMinutes) { + return; + } + webrtc::VCMFrameCount frames; + if (vcm_.SentFrameCount(frames) != VCM_OK) { + return; + } + uint32_t total_frames = frames.numKeyFrames + frames.numDeltaFrames; + if (total_frames > 0) { + RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.KeyFramesSentInPermille", + static_cast( + (frames.numKeyFrames * 1000.0f / total_frames) + 0.5f)); + } +} + int ViEEncoder::Owner() const { return channel_id_; } diff --git a/webrtc/video_engine/vie_encoder.h b/webrtc/video_engine/vie_encoder.h index 51c1d0168..36f87faad 100644 --- a/webrtc/video_engine/vie_encoder.h +++ b/webrtc/video_engine/vie_encoder.h @@ -193,6 +193,8 @@ class ViEEncoder private: bool EncoderPaused() const EXCLUSIVE_LOCKS_REQUIRED(data_cs_); + void UpdateHistograms(); + int32_t engine_id_; const int channel_id_; const uint32_t number_of_cores_; @@ -235,6 +237,7 @@ class ViEEncoder QMVideoSettingsCallback* qm_callback_; bool video_suspended_ GUARDED_BY(data_cs_); I420FrameCallback* pre_encode_callback_ GUARDED_BY(callback_cs_); + const int64_t start_ms_; }; } // namespace webrtc