diff --git a/webrtc/common_types.h b/webrtc/common_types.h index fe736cdf2..f347ec3da 100644 --- a/webrtc/common_types.h +++ b/webrtc/common_types.h @@ -286,16 +286,16 @@ struct StreamDataCounters { } // Returns the number of bytes corresponding to the actual media payload (i.e. - // RTP headers, padding and retransmissions are excluded). Note this function - // does not have meaning for an RTX stream. + // RTP headers, padding, retransmissions and fec packets are excluded). + // Note this function does not have meaning for an RTX stream. size_t MediaPayloadBytes() const { - return transmitted.payload_bytes - retransmitted.payload_bytes; + return transmitted.payload_bytes - retransmitted.payload_bytes - + fec.payload_bytes; } int64_t first_packet_time_ms; // Time when first packet is sent/received. RtpPacketCounter transmitted; // Number of transmitted packets/bytes. RtpPacketCounter retransmitted; // Number of retransmitted packets/bytes. - // TODO(asapersson): add FEC bytes. RtpPacketCounter fec; // Number of redundancy packets/bytes. }; diff --git a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h index 701d8dd3a..3bd953f0d 100644 --- a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h +++ b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h @@ -61,7 +61,8 @@ class ReceiveStatistics : public Module { bool retransmitted) = 0; // Increment counter for number of FEC packets received. - virtual void FecPacketReceived(uint32_t ssrc) = 0; + virtual void FecPacketReceived(const RTPHeader& header, + size_t packet_length) = 0; // Returns a map of all statisticians which have seen an incoming packet // during the last two seconds. @@ -87,7 +88,8 @@ class NullReceiveStatistics : public ReceiveStatistics { virtual void IncomingPacket(const RTPHeader& rtp_header, size_t packet_length, bool retransmitted) OVERRIDE; - virtual void FecPacketReceived(uint32_t ssrc) OVERRIDE; + virtual void FecPacketReceived(const RTPHeader& header, + size_t packet_length) OVERRIDE; virtual StatisticianMap GetActiveStatisticians() const OVERRIDE; virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE; virtual int64_t TimeUntilNextProcess() OVERRIDE; diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc index aefab15f3..916ef54de 100644 --- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc @@ -200,10 +200,15 @@ void StreamStatisticianImpl::NotifyRtcpCallback() { rtcp_callback_->StatisticsUpdated(data, ssrc); } -void StreamStatisticianImpl::FecPacketReceived() { +void StreamStatisticianImpl::FecPacketReceived(const RTPHeader& header, + size_t packet_length) { { CriticalSectionScoped cs(stream_lock_.get()); ++receive_counters_.fec.packets; + receive_counters_.fec.payload_bytes += + packet_length - (header.headerLength + header.paddingLength); + receive_counters_.fec.header_bytes += header.headerLength; + receive_counters_.fec.padding_bytes += header.paddingLength; } NotifyRtpCallback(); } @@ -441,12 +446,13 @@ void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header, impl->IncomingPacket(header, packet_length, retransmitted); } -void ReceiveStatisticsImpl::FecPacketReceived(uint32_t ssrc) { +void ReceiveStatisticsImpl::FecPacketReceived(const RTPHeader& header, + size_t packet_length) { CriticalSectionScoped cs(receive_statistics_lock_.get()); - StatisticianImplMap::iterator it = statisticians_.find(ssrc); + StatisticianImplMap::iterator it = statisticians_.find(header.ssrc); // Ignore FEC if it is the first packet. if (it != statisticians_.end()) { - it->second->FecPacketReceived(); + it->second->FecPacketReceived(header, packet_length); } } @@ -543,7 +549,8 @@ void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header, size_t packet_length, bool retransmitted) {} -void NullReceiveStatistics::FecPacketReceived(uint32_t ssrc) {} +void NullReceiveStatistics::FecPacketReceived(const RTPHeader& header, + size_t packet_length) {} StatisticianMap NullReceiveStatistics::GetActiveStatisticians() const { return StatisticianMap(); diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h index 4b02f3638..ab80a74bd 100644 --- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h +++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h @@ -44,7 +44,7 @@ class StreamStatisticianImpl : public StreamStatistician { void IncomingPacket(const RTPHeader& rtp_header, size_t packet_length, bool retransmitted); - void FecPacketReceived(); + void FecPacketReceived(const RTPHeader& header, size_t packet_length); void SetMaxReorderingThreshold(int max_reordering_threshold); void ProcessBitrate(); virtual void LastReceiveTimeNtp(uint32_t* secs, uint32_t* frac) const; @@ -110,7 +110,8 @@ class ReceiveStatisticsImpl : public ReceiveStatistics, virtual void IncomingPacket(const RTPHeader& header, size_t packet_length, bool retransmitted) OVERRIDE; - virtual void FecPacketReceived(uint32_t ssrc) OVERRIDE; + virtual void FecPacketReceived(const RTPHeader& header, + size_t packet_length) OVERRIDE; virtual StatisticianMap GetActiveStatisticians() const OVERRIDE; virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE; virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE; diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc b/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc index c90e62eaa..7f3f564b7 100644 --- a/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc @@ -276,6 +276,9 @@ class RtpTestCallback : public StreamDataCountersCallback { EXPECT_EQ(expected.retransmitted.padding_bytes, stats_.retransmitted.padding_bytes); EXPECT_EQ(expected.retransmitted.packets, stats_.retransmitted.packets); + EXPECT_EQ(expected.fec.payload_bytes, stats_.fec.payload_bytes); + EXPECT_EQ(expected.fec.header_bytes, stats_.fec.header_bytes); + EXPECT_EQ(expected.fec.padding_bytes, stats_.fec.padding_bytes); EXPECT_EQ(expected.fec.packets, stats_.fec.packets); } @@ -336,13 +339,16 @@ TEST_F(ReceiveStatisticsTest, RtpCallbacks) { header1_.paddingLength = 0; ++header1_.sequenceNumber; clock_.AdvanceTimeMilliseconds(5); - // One recovered packet. + // One FEC packet. receive_statistics_->IncomingPacket( header1_, kPacketSize1 + kHeaderLength, false); - receive_statistics_->FecPacketReceived(kSsrc1); + receive_statistics_->FecPacketReceived(header1_, + kPacketSize1 + kHeaderLength); expected.transmitted.payload_bytes = kPacketSize1 * 4; expected.transmitted.header_bytes = kHeaderLength * 4; expected.transmitted.packets = 4; + expected.fec.payload_bytes = kPacketSize1; + expected.fec.header_bytes = kHeaderLength; expected.fec.packets = 1; callback.Matches(5, kSsrc1, expected); @@ -361,12 +367,13 @@ TEST_F(ReceiveStatisticsTest, RtpCallbacksFecFirst) { receive_statistics_->RegisterRtpStatisticsCallback(&callback); const uint32_t kHeaderLength = 20; + header1_.headerLength = kHeaderLength; // If first packet is FEC, ignore it. - receive_statistics_->FecPacketReceived(kSsrc1); + receive_statistics_->FecPacketReceived(header1_, + kPacketSize1 + kHeaderLength); EXPECT_EQ(0u, callback.num_calls_); - header1_.headerLength = kHeaderLength; receive_statistics_->IncomingPacket( header1_, kPacketSize1 + kHeaderLength, false); StreamDataCounters expected; @@ -377,7 +384,10 @@ TEST_F(ReceiveStatisticsTest, RtpCallbacksFecFirst) { expected.fec.packets = 0; callback.Matches(1, kSsrc1, expected); - receive_statistics_->FecPacketReceived(kSsrc1); + receive_statistics_->FecPacketReceived(header1_, + kPacketSize1 + kHeaderLength); + expected.fec.payload_bytes = kPacketSize1; + expected.fec.header_bytes = kHeaderLength; expected.fec.packets = 1; callback.Matches(2, kSsrc1, expected); } diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc index 952e770c0..d7cc0bc84 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc @@ -900,6 +900,10 @@ void RTPSender::UpdateRtpStats(const uint8_t* buffer, } if (IsFecPacket(buffer, header)) { ++counters->fec.packets; + counters->fec.payload_bytes += + packet_length - (header.headerLength + header.paddingLength); + counters->fec.header_bytes += header.headerLength; + counters->fec.padding_bytes += header.paddingLength; } if (is_retransmit) { diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc index 1e0a056e2..1ec8a104d 100644 --- a/webrtc/video_engine/vie_channel.cc +++ b/webrtc/video_engine/vie_channel.cc @@ -202,11 +202,11 @@ void ViEChannel::UpdateHistograms() { int64_t elapsed_sec = rtcp_received.TimeSinceFirstPacketInMs(now) / 1000; if (elapsed_sec > metrics::kMinRunTimeInSeconds) { RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsReceivedPerMinute", - rtcp_received.nack_packets / elapsed_sec / 60); + rtcp_received.nack_packets * 60 / elapsed_sec); RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsReceivedPerMinute", - rtcp_received.fir_packets / elapsed_sec / 60); + rtcp_received.fir_packets * 60 / elapsed_sec); RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsReceivedPerMinute", - rtcp_received.pli_packets / elapsed_sec / 60); + rtcp_received.pli_packets * 60 / elapsed_sec); if (rtcp_received.nack_requests > 0) { RTC_HISTOGRAM_PERCENTAGE( "WebRTC.Video.UniqueNackRequestsReceivedInPercent", @@ -224,11 +224,11 @@ void ViEChannel::UpdateHistograms() { int64_t elapsed_sec = rtcp_sent.TimeSinceFirstPacketInMs(now) / 1000; if (elapsed_sec > metrics::kMinRunTimeInSeconds) { RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsSentPerMinute", - rtcp_sent.nack_packets / elapsed_sec / 60); + rtcp_sent.nack_packets * 60 / elapsed_sec); RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsSentPerMinute", - rtcp_sent.fir_packets / elapsed_sec / 60); + rtcp_sent.fir_packets * 60 / elapsed_sec); RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute", - rtcp_sent.pli_packets / elapsed_sec / 60); + rtcp_sent.pli_packets * 60 / elapsed_sec); if (rtcp_sent.nack_requests > 0) { RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent", rtcp_sent.UniqueNackRequestsInPercent()); @@ -261,6 +261,10 @@ void ViEChannel::UpdateHistograms() { RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.RtxBitrateReceivedInKbps", rtx.transmitted.TotalBytes() * 8 / elapsed_sec / 1000); } + if (vie_receiver_.IsFecEnabled()) { + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FecBitrateReceivedInKbps", + rtp_rtx.fec.TotalBytes() * 8 / elapsed_sec / 1000); + } } } } @@ -290,6 +294,14 @@ void ViEChannel::UpdateHistogramsAtStopSend() { RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.RtxBitrateSentInKbps", rtx.transmitted.TotalBytes() * 8 / elapsed_sec / 1000); } + bool fec_enabled = false; + uint8_t pltype_red; + uint8_t pltype_fec; + rtp_rtcp_->GenericFECStatus(fec_enabled, pltype_red, pltype_fec); + if (fec_enabled) { + RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FecBitrateSentInKbps", + rtp_rtx.fec.TotalBytes() * 8 / elapsed_sec / 1000); + } } int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec, diff --git a/webrtc/video_engine/vie_receiver.cc b/webrtc/video_engine/vie_receiver.cc index 7e1034f39..cdff8607c 100644 --- a/webrtc/video_engine/vie_receiver.cc +++ b/webrtc/video_engine/vie_receiver.cc @@ -129,6 +129,10 @@ bool ViEReceiver::GetRtxSsrc(uint32_t* ssrc) const { return rtp_payload_registry_->GetRtxSsrc(ssrc); } +bool ViEReceiver::IsFecEnabled() const { + return rtp_payload_registry_->ulpfec_payload_type() > -1; +} + uint32_t ViEReceiver::GetRemoteSsrc() const { return rtp_receiver_->SSRC(); } @@ -319,7 +323,7 @@ bool ViEReceiver::ParseAndHandleEncapsulatingHeader(const uint8_t* packet, if (rtp_payload_registry_->IsRed(header)) { int8_t ulpfec_pt = rtp_payload_registry_->ulpfec_payload_type(); if (packet[header.headerLength] == ulpfec_pt) - rtp_receive_statistics_->FecPacketReceived(header.ssrc); + rtp_receive_statistics_->FecPacketReceived(header, packet_length); if (fec_receiver_->AddReceivedRedPacket( header, packet, packet_length, ulpfec_pt) != 0) { return false; diff --git a/webrtc/video_engine/vie_receiver.h b/webrtc/video_engine/vie_receiver.h index 6354d6872..b8e6569b4 100644 --- a/webrtc/video_engine/vie_receiver.h +++ b/webrtc/video_engine/vie_receiver.h @@ -51,6 +51,8 @@ class ViEReceiver : public RtpData { void SetRtxSsrc(uint32_t ssrc); bool GetRtxSsrc(uint32_t* ssrc) const; + bool IsFecEnabled() const; + uint32_t GetRemoteSsrc() const; int GetCsrcs(uint32_t* csrcs) const;