Add histograms stats for sent/received fraction loss for a stream:

- "WebRTC.Video.SentPacketsLostInPercent"
- "WebRTC.Video.ReceivedPacketsLostInPercent"

BUG=crbug/419657
R=mflodman@webrtc.org, stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/37419004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@8020 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
asapersson@webrtc.org
2015-01-08 07:50:56 +00:00
parent d730b288c8
commit 823c9b8e36
7 changed files with 339 additions and 54 deletions

View File

@ -29,6 +29,8 @@ source_set("video_engine_core") {
"encoder_state_feedback.h",
"overuse_frame_detector.cc",
"overuse_frame_detector.h",
"report_block_stats.cc",
"report_block_stats.h",
"stream_synchronization.cc",
"stream_synchronization.h",
"vie_base_impl.cc",

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2014 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.
*/
#include "webrtc/video_engine/report_block_stats.h"
namespace webrtc {
namespace {
int FractionLost(uint32_t num_lost_sequence_numbers,
uint32_t num_sequence_numbers) {
if (num_sequence_numbers == 0) {
return 0;
}
return ((num_lost_sequence_numbers * 255) + (num_sequence_numbers / 2)) /
num_sequence_numbers;
}
} // namespace
// Helper class for rtcp statistics.
ReportBlockStats::ReportBlockStats()
: num_sequence_numbers_(0),
num_lost_sequence_numbers_(0) {
}
void ReportBlockStats::Store(const RtcpStatistics& rtcp_stats,
uint32_t remote_ssrc,
uint32_t source_ssrc) {
RTCPReportBlock block;
block.cumulativeLost = rtcp_stats.cumulative_lost;
block.fractionLost = rtcp_stats.fraction_lost;
block.extendedHighSeqNum = rtcp_stats.extended_max_sequence_number;
block.jitter = rtcp_stats.jitter;
block.remoteSSRC = remote_ssrc;
block.sourceSSRC = source_ssrc;
uint32_t num_sequence_numbers = 0;
uint32_t num_lost_sequence_numbers = 0;
StoreAndAddPacketIncrement(
block, &num_sequence_numbers, &num_lost_sequence_numbers);
}
RTCPReportBlock ReportBlockStats::AggregateAndStore(
const ReportBlockVector& report_blocks) {
RTCPReportBlock aggregate;
if (report_blocks.empty()) {
return aggregate;
}
uint32_t num_sequence_numbers = 0;
uint32_t num_lost_sequence_numbers = 0;
ReportBlockVector::const_iterator report_block = report_blocks.begin();
for (; report_block != report_blocks.end(); ++report_block) {
aggregate.cumulativeLost += report_block->cumulativeLost;
aggregate.jitter += report_block->jitter;
StoreAndAddPacketIncrement(*report_block,
&num_sequence_numbers,
&num_lost_sequence_numbers);
}
if (report_blocks.size() == 1) {
// No aggregation needed.
return report_blocks[0];
}
// Fraction lost since previous report block.
aggregate.fractionLost =
FractionLost(num_lost_sequence_numbers, num_sequence_numbers);
aggregate.jitter =
(aggregate.jitter + report_blocks.size() / 2) / report_blocks.size();
return aggregate;
}
void ReportBlockStats::StoreAndAddPacketIncrement(
const RTCPReportBlock& report_block,
uint32_t* num_sequence_numbers,
uint32_t* num_lost_sequence_numbers) {
// Get diff with previous report block.
ReportBlockMap::iterator prev_report_block = prev_report_blocks_.find(
report_block.sourceSSRC);
if (prev_report_block != prev_report_blocks_.end()) {
int seq_num_diff = report_block.extendedHighSeqNum -
prev_report_block->second.extendedHighSeqNum;
int cum_loss_diff = report_block.cumulativeLost -
prev_report_block->second.cumulativeLost;
if (seq_num_diff >= 0 && cum_loss_diff >= 0) {
*num_sequence_numbers += seq_num_diff;
*num_lost_sequence_numbers += cum_loss_diff;
// Update total number of packets/lost packets.
num_sequence_numbers_ += seq_num_diff;
num_lost_sequence_numbers_ += cum_loss_diff;
}
}
// Store current report block.
prev_report_blocks_[report_block.sourceSSRC] = report_block;
}
int ReportBlockStats::FractionLostInPercent() const {
return FractionLost(
num_lost_sequence_numbers_, num_sequence_numbers_) * 100 / 255;
}
} // namespace webrtc

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2014 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 WEBRTC_VIDEO_ENGINE_REPORT_BLOCK_STATS_H_
#define WEBRTC_VIDEO_ENGINE_REPORT_BLOCK_STATS_H_
#include <map>
#include <vector>
#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
namespace webrtc {
// Helper class for rtcp statistics.
class ReportBlockStats {
public:
typedef std::map<uint32_t, RTCPReportBlock> ReportBlockMap;
typedef std::vector<RTCPReportBlock> ReportBlockVector;
ReportBlockStats();
~ReportBlockStats() {}
// Updates stats and stores report blocks.
// Returns an aggregate of the |report_blocks|.
RTCPReportBlock AggregateAndStore(const ReportBlockVector& report_blocks);
// Updates stats and stores report block.
void Store(const RtcpStatistics& rtcp_stats,
uint32_t remote_ssrc,
uint32_t source_ssrc);
// Returns the total fraction of lost packets.
int FractionLostInPercent() const;
private:
// Updates the total number of packets/lost packets.
// Stores the report block.
// Returns the number of packets/lost packets since previous report block.
void StoreAndAddPacketIncrement(const RTCPReportBlock& report_block,
uint32_t* num_sequence_numbers,
uint32_t* num_lost_sequence_numbers);
// The total number of packets/lost packets.
uint32_t num_sequence_numbers_;
uint32_t num_lost_sequence_numbers_;
// Map holding the last stored report block (mapped by the source SSRC).
ReportBlockMap prev_report_blocks_;
};
} // namespace webrtc
#endif // WEBRTC_VIDEO_ENGINE_REPORT_BLOCK_STATS_H_

View File

@ -0,0 +1,146 @@
/*
* Copyright (c) 2014 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.
*/
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/video_engine/report_block_stats.h"
namespace webrtc {
class ReportBlockStatsTest : public ::testing::Test {
protected:
ReportBlockStatsTest() : kSsrc1(0x12345), kSsrc2(0x23456) {}
virtual void SetUp() OVERRIDE {
// kSsrc1: block 1-3.
block1_1_.cumulativeLost = 10;
block1_1_.fractionLost = 123;
block1_1_.extendedHighSeqNum = 24000;
block1_1_.jitter = 777;
block1_1_.sourceSSRC = kSsrc1;
block1_2_.cumulativeLost = 15;
block1_2_.fractionLost = 0;
block1_2_.extendedHighSeqNum = 24100;
block1_2_.jitter = 222;
block1_2_.sourceSSRC = kSsrc1;
block1_3_.cumulativeLost = 50;
block1_3_.fractionLost = 0;
block1_3_.extendedHighSeqNum = 24200;
block1_3_.jitter = 333;
block1_3_.sourceSSRC = kSsrc1;
// kSsrc2: block 1,2.
block2_1_.cumulativeLost = 111;
block2_1_.fractionLost = 222;
block2_1_.extendedHighSeqNum = 8500;
block2_1_.jitter = 555;
block2_1_.sourceSSRC = kSsrc2;
block2_2_.cumulativeLost = 136;
block2_2_.fractionLost = 0;
block2_2_.extendedHighSeqNum = 8800;
block2_2_.jitter = 888;
block2_2_.sourceSSRC = kSsrc2;
ssrc1block1_.push_back(block1_1_);
ssrc1block2_.push_back(block1_2_);
ssrc12block1_.push_back(block1_1_);
ssrc12block1_.push_back(block2_1_);
ssrc12block2_.push_back(block1_2_);
ssrc12block2_.push_back(block2_2_);
}
RtcpStatistics RtcpReportBlockToRtcpStatistics(
const RTCPReportBlock& stats) {
RtcpStatistics block;
block.cumulative_lost = stats.cumulativeLost;
block.fraction_lost = stats.fractionLost;
block.extended_max_sequence_number = stats.extendedHighSeqNum;
block.jitter = stats.jitter;
return block;
}
const uint32_t kSsrc1;
const uint32_t kSsrc2;
RTCPReportBlock block1_1_;
RTCPReportBlock block1_2_;
RTCPReportBlock block1_3_;
RTCPReportBlock block2_1_;
RTCPReportBlock block2_2_;
std::vector<RTCPReportBlock> ssrc1block1_;
std::vector<RTCPReportBlock> ssrc1block2_;
std::vector<RTCPReportBlock> ssrc12block1_;
std::vector<RTCPReportBlock> ssrc12block2_;
};
TEST_F(ReportBlockStatsTest, AggregateAndStore_NoSsrc) {
ReportBlockStats stats;
std::vector<RTCPReportBlock> empty;
RTCPReportBlock aggregated = stats.AggregateAndStore(empty);
EXPECT_EQ(0U, aggregated.fractionLost);
EXPECT_EQ(0U, aggregated.cumulativeLost);
EXPECT_EQ(0U, aggregated.jitter);
EXPECT_EQ(0U, aggregated.extendedHighSeqNum);
}
TEST_F(ReportBlockStatsTest, AggregateAndStore_OneSsrc) {
ReportBlockStats stats;
RTCPReportBlock aggregated = stats.AggregateAndStore(ssrc1block1_);
// One ssrc, no aggregation done.
EXPECT_EQ(123U, aggregated.fractionLost);
EXPECT_EQ(10U, aggregated.cumulativeLost);
EXPECT_EQ(777U, aggregated.jitter);
EXPECT_EQ(24000U, aggregated.extendedHighSeqNum);
aggregated = stats.AggregateAndStore(ssrc1block2_);
EXPECT_EQ(0U, aggregated.fractionLost);
EXPECT_EQ(15U, aggregated.cumulativeLost);
EXPECT_EQ(222U, aggregated.jitter);
EXPECT_EQ(24100U, aggregated.extendedHighSeqNum);
// fl: 100 * (15-10) / (24100-24000) = 5%
EXPECT_EQ(5, stats.FractionLostInPercent());
}
TEST_F(ReportBlockStatsTest, AggregateAndStore_TwoSsrcs) {
ReportBlockStats stats;
RTCPReportBlock aggregated = stats.AggregateAndStore(ssrc12block1_);
EXPECT_EQ(0U, aggregated.fractionLost);
EXPECT_EQ(10U + 111U, aggregated.cumulativeLost);
EXPECT_EQ((777U + 555U) / 2, aggregated.jitter);
EXPECT_EQ(0U, aggregated.extendedHighSeqNum);
aggregated = stats.AggregateAndStore(ssrc12block2_);
// fl: 255 * ((15-10) + (136-111)) / ((24100-24000) + (8800-8500)) = 19
EXPECT_EQ(19U, aggregated.fractionLost);
EXPECT_EQ(15U + 136U, aggregated.cumulativeLost);
EXPECT_EQ((222U + 888U) / 2, aggregated.jitter);
EXPECT_EQ(0U, aggregated.extendedHighSeqNum);
// fl: 100 * ((15-10) + (136-111)) / ((24100-24000) + (8800-8500)) = 7%
EXPECT_EQ(7, stats.FractionLostInPercent());
}
TEST_F(ReportBlockStatsTest, StoreAndGetFractionLost) {
const uint32_t kRemoteSsrc = 1;
ReportBlockStats stats;
EXPECT_EQ(0, stats.FractionLostInPercent());
// First block => 0%
stats.Store(RtcpReportBlockToRtcpStatistics(block1_1_), kRemoteSsrc, kSsrc1);
EXPECT_EQ(0, stats.FractionLostInPercent());
// fl: 100 * (15-10) / (24100-24000) = 5%
stats.Store(RtcpReportBlockToRtcpStatistics(block1_2_), kRemoteSsrc, kSsrc1);
EXPECT_EQ(5, stats.FractionLostInPercent());
// fl: 100 * (50-10) / (24200-24000) = 20%
stats.Store(RtcpReportBlockToRtcpStatistics(block1_3_), kRemoteSsrc, kSsrc1);
EXPECT_EQ(20, stats.FractionLostInPercent());
}
} // namespace webrtc

View File

@ -49,6 +49,7 @@
'call_stats.h',
'encoder_state_feedback.h',
'overuse_frame_detector.h',
'report_block_stats.h',
'stream_synchronization.h',
'vie_base_impl.h',
'vie_capture_impl.h',
@ -82,6 +83,7 @@
'call_stats.cc',
'encoder_state_feedback.cc',
'overuse_frame_detector.cc',
'report_block_stats.cc',
'stream_synchronization.cc',
'vie_base_impl.cc',
'vie_capture_impl.cc',
@ -132,6 +134,7 @@
'call_stats_unittest.cc',
'encoder_state_feedback_unittest.cc',
'overuse_frame_detector_unittest.cc',
'report_block_stats_unittest.cc',
'stream_synchronization_unittest.cc',
'vie_capturer_unittest.cc',
'vie_codec_unittest.cc',

View File

@ -34,6 +34,7 @@
#include "webrtc/video_engine/include/vie_errors.h"
#include "webrtc/video_engine/include/vie_image_process.h"
#include "webrtc/video_engine/include/vie_rtp_rtcp.h"
#include "webrtc/video_engine/report_block_stats.h"
#include "webrtc/video_engine/vie_defines.h"
namespace webrtc {
@ -43,52 +44,6 @@ const int kInvalidRtpExtensionId = 0;
static const int kMaxTargetDelayMs = 10000;
static const float kMaxIncompleteTimeMultiplier = 3.5f;
namespace {
RTCPReportBlock AggregateReportBlocks(
const std::vector<RTCPReportBlock>& report_blocks,
std::map<uint32_t, RTCPReportBlock>* prev_report_blocks) {
int num_sequence_numbers = 0;
int num_lost_sequence_numbers = 0;
int jitter_sum = 0;
int number_of_report_blocks = 0;
RTCPReportBlock aggregate;
std::vector<RTCPReportBlock>::const_iterator report_block =
report_blocks.begin();
for (; report_block != report_blocks.end(); ++report_block) {
aggregate.cumulativeLost += report_block->cumulativeLost;
std::map<uint32_t, RTCPReportBlock>::iterator prev_report_block =
prev_report_blocks->find(report_block->sourceSSRC);
if (prev_report_block != prev_report_blocks->end()) {
// Skip the first report block since we won't be able to get a correct
// weight for it.
int seq_num_diff = report_block->extendedHighSeqNum -
prev_report_block->second.extendedHighSeqNum;
int cum_loss_diff = report_block->cumulativeLost -
prev_report_block->second.cumulativeLost;
if (seq_num_diff >= 0 && cum_loss_diff >= 0) {
num_sequence_numbers += seq_num_diff;
num_lost_sequence_numbers += cum_loss_diff;
}
}
jitter_sum += report_block->jitter;
++number_of_report_blocks;
(*prev_report_blocks)[report_block->sourceSSRC] = *report_block;
}
if (num_sequence_numbers > 0) {
aggregate.fractionLost = ((num_lost_sequence_numbers * 255) +
(num_sequence_numbers / 2)) / num_sequence_numbers;
}
if (number_of_report_blocks > 0) {
aggregate.jitter =
(jitter_sum + number_of_report_blocks / 2) / number_of_report_blocks;
}
// Not well defined for aggregated report blocks.
aggregate.extendedHighSeqNum = 0;
return aggregate;
}
} // namespace
// Helper class receiving statistics callbacks.
class ChannelStatsObserver : public CallStatsObserver {
public:
@ -150,7 +105,9 @@ 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),
report_block_stats_sender_(new ReportBlockStats()),
report_block_stats_receiver_(new ReportBlockStats()) {
RtpRtcp::Configuration configuration = CreateRtpRtcpConfiguration();
configuration.remote_bitrate_estimator = remote_bitrate_estimator;
configuration.receive_statistics = vie_receiver_.GetReceiveStatistics();
@ -255,6 +212,8 @@ void ViEChannel::UpdateHistograms() {
"WebRTC.Video.UniqueNackRequestsReceivedInPercent",
rtcp_received.UniqueNackRequestsInPercent());
}
RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.SentPacketsLostInPercent",
report_block_stats_sender_->FractionLostInPercent());
}
} else if (vie_receiver_.GetRemoteSsrc() > 0) {
// Get receive stats if we are receiving packets, i.e. there is a remote
@ -271,6 +230,8 @@ void ViEChannel::UpdateHistograms() {
RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent",
rtcp_sent.UniqueNackRequestsInPercent());
}
RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.ReceivedPacketsLostInPercent",
report_block_stats_receiver_->FractionLostInPercent());
}
StreamDataCounters rtp;
@ -1076,12 +1037,10 @@ int32_t ViEChannel::GetSendRtcpStatistics(uint16_t* fraction_lost,
remote_ssrc = report_blocks[0].remoteSSRC;
}
RTCPReportBlock report;
if (report_blocks.size() > 1)
report = AggregateReportBlocks(report_blocks, &prev_report_blocks_);
else
report = report_blocks[0];
// TODO(asapersson): Change report_block_stats to not rely on
// GetSendRtcpStatistics to be called.
RTCPReportBlock report =
report_block_stats_sender_->AggregateAndStore(report_blocks);
*fraction_lost = report.fractionLost;
*cumulative_lost = report.cumulativeLost;
*extended_max = report.extendedHighSeqNum;
@ -1128,6 +1087,10 @@ int32_t ViEChannel::GetReceivedRtcpStatistics(uint16_t* fraction_lost,
*extended_max = receive_stats.extended_max_sequence_number;
*jitter_samples = receive_stats.jitter;
// TODO(asapersson): Change report_block_stats to not rely on
// GetReceivedRtcpStatistics to be called.
report_block_stats_receiver_->Store(receive_stats, remote_ssrc, 0);
uint16_t dummy = 0;
uint16_t rtt = 0;
rtp_rtcp_->RTT(remote_ssrc, &rtt, &dummy, &dummy, &dummy);

View File

@ -40,6 +40,7 @@ class I420FrameCallback;
class PacedSender;
class ProcessThread;
class ReceiveStatisticsProxy;
class ReportBlockStats;
class RtcpRttStats;
class ThreadWrapper;
class ViEDecoderObserver;
@ -509,7 +510,8 @@ class ViEChannel
int max_nack_reordering_threshold_;
I420FrameCallback* pre_render_callback_;
std::map<uint32_t, RTCPReportBlock> prev_report_blocks_;
scoped_ptr<ReportBlockStats> report_block_stats_sender_;
scoped_ptr<ReportBlockStats> report_block_stats_receiver_;
};
} // namespace webrtc