Add callbacks for receive channel RTP statistics
This allows a listener to receive new statistics (byte/packet counts, etc) as it
is generated - avoiding the need to poll. This also makes handling stats from
multiple RTP streams more tractable. The change is primarily targeted at the new
video engine API.
TEST=Unit test in ReceiveStatisticsTest.
Integration tests to follow as call tests when fully wired up.
BUG=2235
R=mflodman@webrtc.org, pbos@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/6259004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@5416 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h
index d034b49..6f2ea4f 100644
--- a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h
+++ b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h
@@ -51,9 +51,13 @@
static ReceiveStatistics* Create(Clock* clock);
// Updates the receive statistics with this packet.
- virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
+ virtual void IncomingPacket(const RTPHeader& rtp_header,
+ size_t bytes,
bool retransmitted) = 0;
+ // Increment counter for number of FEC packets received.
+ virtual void FecPacketReceived(uint32_t ssrc) = 0;
+
// Returns a map of all statisticians which have seen an incoming packet
// during the last two seconds.
virtual StatisticianMap GetActiveStatisticians() const = 0;
@@ -67,12 +71,18 @@
// Called on new RTCP stats creation.
virtual void RegisterRtcpStatisticsCallback(
RtcpStatisticsCallback* callback) = 0;
+
+ // Called on new RTP stats creation.
+ virtual void RegisterRtpStatisticsCallback(
+ StreamDataCountersCallback* callback) = 0;
};
class NullReceiveStatistics : public ReceiveStatistics {
public:
- virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
+ virtual void IncomingPacket(const RTPHeader& rtp_header,
+ size_t bytes,
bool retransmitted) OVERRIDE;
+ virtual void FecPacketReceived(uint32_t ssrc) OVERRIDE;
virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE;
virtual int32_t TimeUntilNextProcess() OVERRIDE;
@@ -80,6 +90,8 @@
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
virtual void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback)
OVERRIDE;
+ virtual void RegisterRtpStatisticsCallback(
+ StreamDataCountersCallback* callback) OVERRIDE;
};
} // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
index af0108b..cb00556 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
@@ -26,7 +26,8 @@
StreamStatisticianImpl::StreamStatisticianImpl(
Clock* clock,
- RtcpStatisticsCallback* rtcp_callback)
+ RtcpStatisticsCallback* rtcp_callback,
+ StreamDataCountersCallback* rtp_callback)
: clock_(clock),
stream_lock_(CriticalSectionWrapper::CreateCriticalSection()),
incoming_bitrate_(clock, NULL),
@@ -43,33 +44,26 @@
received_seq_first_(0),
received_seq_max_(0),
received_seq_wraps_(0),
- first_packet_(true),
received_packet_overhead_(12),
- received_byte_count_(0),
- received_retransmitted_packets_(0),
- received_inorder_packet_count_(0),
last_report_inorder_packets_(0),
last_report_old_packets_(0),
last_report_seq_max_(0),
- last_reported_statistics_(),
- rtcp_callback_(rtcp_callback) {}
+ rtcp_callback_(rtcp_callback),
+ rtp_callback_(rtp_callback) {}
void StreamStatisticianImpl::ResetStatistics() {
CriticalSectionScoped cs(stream_lock_.get());
last_report_inorder_packets_ = 0;
last_report_old_packets_ = 0;
last_report_seq_max_ = 0;
- memset(&last_reported_statistics_, 0, sizeof(last_reported_statistics_));
+ last_reported_statistics_ = RtcpStatistics();
jitter_q4_ = 0;
cumulative_loss_ = 0;
jitter_q4_transmission_time_offset_ = 0;
received_seq_wraps_ = 0;
received_seq_max_ = 0;
received_seq_first_ = 0;
- received_byte_count_ = 0;
- received_retransmitted_packets_ = 0;
- received_inorder_packet_count_ = 0;
- first_packet_ = true;
+ receive_counters_ = StreamDataCounters();
}
void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header,
@@ -79,17 +73,17 @@
bool in_order = InOrderPacketInternal(header.sequenceNumber);
ssrc_ = header.ssrc;
incoming_bitrate_.Update(bytes);
- received_byte_count_ += bytes;
+ receive_counters_.bytes +=
+ bytes - (header.paddingLength + header.headerLength);
+ receive_counters_.header_bytes += header.headerLength;
+ receive_counters_.padding_bytes += header.paddingLength;
+ ++receive_counters_.packets;
+ if (!in_order && retransmitted) {
+ ++receive_counters_.retransmitted_packets;
+ }
- if (first_packet_) {
- first_packet_ = false;
- // This is the first received report.
+ if (receive_counters_.packets == 1) {
received_seq_first_ = header.sequenceNumber;
- received_seq_max_ = header.sequenceNumber;
- received_inorder_packet_count_ = 1;
- clock_->CurrentNtp(last_receive_time_secs_, last_receive_time_frac_);
- last_receive_time_ms_ = clock_->TimeInMilliseconds();
- return;
}
// Count only the new packets received. That is, if packets 1, 2, 3, 5, 4, 6
@@ -99,66 +93,27 @@
uint32_t receive_time_secs;
uint32_t receive_time_frac;
clock_->CurrentNtp(receive_time_secs, receive_time_frac);
- received_inorder_packet_count_++;
// Wrong if we use RetransmitOfOldPacket.
- int32_t seq_diff = header.sequenceNumber - received_seq_max_;
- if (seq_diff < 0) {
+ if (receive_counters_.packets > 1 &&
+ received_seq_max_ > header.sequenceNumber) {
// Wrap around detected.
received_seq_wraps_++;
}
// New max.
received_seq_max_ = header.sequenceNumber;
+ // If new time stamp and more than one in-order packet received, calculate
+ // new jitter statistics.
if (header.timestamp != last_received_timestamp_ &&
- received_inorder_packet_count_ > 1) {
- uint32_t receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP(
- receive_time_secs, receive_time_frac, header.payload_type_frequency);
- uint32_t last_receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP(
- last_receive_time_secs_, last_receive_time_frac_,
- header.payload_type_frequency);
- int32_t time_diff_samples = (receive_time_rtp - last_receive_time_rtp) -
- (header.timestamp - last_received_timestamp_);
-
- time_diff_samples = abs(time_diff_samples);
-
- // lib_jingle sometimes deliver crazy jumps in TS for the same stream.
- // If this happens, don't update jitter value. Use 5 secs video frequency
- // as the threshold.
- if (time_diff_samples < 450000) {
- // Note we calculate in Q4 to avoid using float.
- int32_t jitter_diff_q4 = (time_diff_samples << 4) - jitter_q4_;
- jitter_q4_ += ((jitter_diff_q4 + 8) >> 4);
- }
-
- // Extended jitter report, RFC 5450.
- // Actual network jitter, excluding the source-introduced jitter.
- int32_t time_diff_samples_ext =
- (receive_time_rtp - last_receive_time_rtp) -
- ((header.timestamp +
- header.extension.transmissionTimeOffset) -
- (last_received_timestamp_ +
- last_received_transmission_time_offset_));
-
- time_diff_samples_ext = abs(time_diff_samples_ext);
-
- if (time_diff_samples_ext < 450000) {
- int32_t jitter_diffQ4TransmissionTimeOffset =
- (time_diff_samples_ext << 4) - jitter_q4_transmission_time_offset_;
- jitter_q4_transmission_time_offset_ +=
- ((jitter_diffQ4TransmissionTimeOffset + 8) >> 4);
- }
+ (receive_counters_.packets - receive_counters_.retransmitted_packets) >
+ 1) {
+ UpdateJitter(header, receive_time_secs, receive_time_frac);
}
last_received_timestamp_ = header.timestamp;
last_receive_time_secs_ = receive_time_secs;
last_receive_time_frac_ = receive_time_frac;
last_receive_time_ms_ = clock_->TimeInMilliseconds();
- } else {
- if (retransmitted) {
- received_retransmitted_packets_++;
- } else {
- received_inorder_packet_count_++;
- }
}
uint16_t packet_oh = header.headerLength + header.paddingLength;
@@ -166,6 +121,55 @@
// Our measured overhead. Filter from RFC 5104 4.2.1.2:
// avg_OH (new) = 15/16*avg_OH (old) + 1/16*pckt_OH,
received_packet_overhead_ = (15 * received_packet_overhead_ + packet_oh) >> 4;
+
+ rtp_callback_->DataCountersUpdated(receive_counters_, ssrc_);
+}
+
+void StreamStatisticianImpl::UpdateJitter(const RTPHeader& header,
+ uint32_t receive_time_secs,
+ uint32_t receive_time_frac) {
+ uint32_t receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP(
+ receive_time_secs, receive_time_frac, header.payload_type_frequency);
+ uint32_t last_receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP(
+ last_receive_time_secs_, last_receive_time_frac_,
+ header.payload_type_frequency);
+ int32_t time_diff_samples = (receive_time_rtp - last_receive_time_rtp) -
+ (header.timestamp - last_received_timestamp_);
+
+ time_diff_samples = abs(time_diff_samples);
+
+ // lib_jingle sometimes deliver crazy jumps in TS for the same stream.
+ // If this happens, don't update jitter value. Use 5 secs video frequency
+ // as the threshold.
+ if (time_diff_samples < 450000) {
+ // Note we calculate in Q4 to avoid using float.
+ int32_t jitter_diff_q4 = (time_diff_samples << 4) - jitter_q4_;
+ jitter_q4_ += ((jitter_diff_q4 + 8) >> 4);
+ }
+
+ // Extended jitter report, RFC 5450.
+ // Actual network jitter, excluding the source-introduced jitter.
+ int32_t time_diff_samples_ext =
+ (receive_time_rtp - last_receive_time_rtp) -
+ ((header.timestamp +
+ header.extension.transmissionTimeOffset) -
+ (last_received_timestamp_ +
+ last_received_transmission_time_offset_));
+
+ time_diff_samples_ext = abs(time_diff_samples_ext);
+
+ if (time_diff_samples_ext < 450000) {
+ int32_t jitter_diffQ4TransmissionTimeOffset =
+ (time_diff_samples_ext << 4) - jitter_q4_transmission_time_offset_;
+ jitter_q4_transmission_time_offset_ +=
+ ((jitter_diffQ4TransmissionTimeOffset + 8) >> 4);
+ }
+}
+
+void StreamStatisticianImpl::FecPacketReceived() {
+ CriticalSectionScoped cs(stream_lock_.get());
+ ++receive_counters_.fec_packets;
+ rtp_callback_->DataCountersUpdated(receive_counters_, ssrc_);
}
void StreamStatisticianImpl::SetMaxReorderingThreshold(
@@ -178,7 +182,7 @@
bool reset) {
{
CriticalSectionScoped cs(stream_lock_.get());
- if (received_seq_first_ == 0 && received_byte_count_ == 0) {
+ if (received_seq_first_ == 0 && receive_counters_.bytes == 0) {
// We have not received anything.
return false;
}
@@ -197,6 +201,8 @@
}
rtcp_callback_->StatisticsUpdated(*statistics, ssrc_);
+ rtp_callback_->DataCountersUpdated(receive_counters_, ssrc_);
+
return true;
}
@@ -219,7 +225,8 @@
// Number of received RTP packets since last report, counts all packets but
// not re-transmissions.
uint32_t rec_since_last =
- received_inorder_packet_count_ - last_report_inorder_packets_;
+ (receive_counters_.packets - receive_counters_.retransmitted_packets) -
+ last_report_inorder_packets_;
// With NACK we don't know the expected retransmissions during the last
// second. We know how many "old" packets we have received. We just count
@@ -231,7 +238,7 @@
// re-transmitted. We use RTT to decide if a packet is re-ordered or
// re-transmitted.
uint32_t retransmitted_packets =
- received_retransmitted_packets_ - last_report_old_packets_;
+ receive_counters_.retransmitted_packets - last_report_old_packets_;
rec_since_last += retransmitted_packets;
int32_t missing = 0;
@@ -258,8 +265,9 @@
last_reported_statistics_ = stats;
// Only for report blocks in RTCP SR and RR.
- last_report_inorder_packets_ = received_inorder_packet_count_;
- last_report_old_packets_ = received_retransmitted_packets_;
+ last_report_inorder_packets_ =
+ receive_counters_.packets - receive_counters_.retransmitted_packets;
+ last_report_old_packets_ = receive_counters_.retransmitted_packets;
last_report_seq_max_ = received_seq_max_;
return stats;
@@ -269,11 +277,11 @@
uint32_t* bytes_received, uint32_t* packets_received) const {
CriticalSectionScoped cs(stream_lock_.get());
if (bytes_received) {
- *bytes_received = received_byte_count_;
+ *bytes_received = receive_counters_.bytes + receive_counters_.header_bytes +
+ receive_counters_.padding_bytes;
}
if (packets_received) {
- *packets_received =
- received_retransmitted_packets_ + received_inorder_packet_count_;
+ *packets_received = receive_counters_.packets;
}
}
@@ -358,7 +366,8 @@
: clock_(clock),
receive_statistics_lock_(CriticalSectionWrapper::CreateCriticalSection()),
last_rate_update_ms_(0),
- rtcp_stats_callback_(NULL) {}
+ rtcp_stats_callback_(NULL),
+ rtp_stats_callback_(NULL) {}
ReceiveStatisticsImpl::~ReceiveStatisticsImpl() {
while (!statisticians_.empty()) {
@@ -368,7 +377,8 @@
}
void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header,
- size_t bytes, bool old_packet) {
+ size_t bytes,
+ bool retransmitted) {
StatisticianImplMap::iterator it;
{
CriticalSectionScoped cs(receive_statistics_lock_.get());
@@ -376,11 +386,18 @@
if (it == statisticians_.end()) {
std::pair<StatisticianImplMap::iterator, uint32_t> insert_result =
statisticians_.insert(std::make_pair(
- header.ssrc, new StreamStatisticianImpl(clock_, this)));
+ header.ssrc, new StreamStatisticianImpl(clock_, this, this)));
it = insert_result.first;
}
}
- it->second->IncomingPacket(header, bytes, old_packet);
+ it->second->IncomingPacket(header, bytes, retransmitted);
+}
+
+void ReceiveStatisticsImpl::FecPacketReceived(uint32_t ssrc) {
+ CriticalSectionScoped cs(receive_statistics_lock_.get());
+ StatisticianImplMap::iterator it = statisticians_.find(ssrc);
+ assert(it != statisticians_.end());
+ it->second->FecPacketReceived();
}
void ReceiveStatisticsImpl::ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc) {
@@ -461,10 +478,28 @@
}
}
+void ReceiveStatisticsImpl::RegisterRtpStatisticsCallback(
+ StreamDataCountersCallback* callback) {
+ CriticalSectionScoped cs(receive_statistics_lock_.get());
+ if (callback != NULL)
+ assert(rtp_stats_callback_ == NULL);
+ rtp_stats_callback_ = callback;
+}
+
+void ReceiveStatisticsImpl::DataCountersUpdated(const StreamDataCounters& stats,
+ uint32_t ssrc) {
+ CriticalSectionScoped cs(receive_statistics_lock_.get());
+ if (rtp_stats_callback_) {
+ rtp_stats_callback_->DataCountersUpdated(stats, ssrc);
+ }
+}
+
void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header,
size_t bytes,
bool retransmitted) {}
+void NullReceiveStatistics::FecPacketReceived(uint32_t ssrc) {}
+
StatisticianMap NullReceiveStatistics::GetActiveStatisticians() const {
return StatisticianMap();
}
@@ -484,4 +519,7 @@
void NullReceiveStatistics::RegisterRtcpStatisticsCallback(
RtcpStatisticsCallback* callback) {}
+void NullReceiveStatistics::RegisterRtpStatisticsCallback(
+ StreamDataCountersCallback* callback) {}
+
} // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h
index 0e24ca2..34bd46c 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h
@@ -25,7 +25,9 @@
class StreamStatisticianImpl : public StreamStatistician {
public:
- StreamStatisticianImpl(Clock* clock, RtcpStatisticsCallback* rtcp_callback);
+ StreamStatisticianImpl(Clock* clock,
+ RtcpStatisticsCallback* rtcp_callback,
+ StreamDataCountersCallback* rtp_callback);
virtual ~StreamStatisticianImpl() {}
virtual bool GetStatistics(RtcpStatistics* statistics, bool reset) OVERRIDE;
@@ -34,11 +36,13 @@
virtual uint32_t BitrateReceived() const OVERRIDE;
virtual void ResetStatistics() OVERRIDE;
virtual bool IsRetransmitOfOldPacket(const RTPHeader& header,
- int min_rtt) const OVERRIDE;
+ int min_rtt) const OVERRIDE;
virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE;
- void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
+ void IncomingPacket(const RTPHeader& rtp_header,
+ size_t bytes,
bool retransmitted);
+ void FecPacketReceived();
void SetMaxReorderingThreshold(int max_reordering_threshold);
void ProcessBitrate();
virtual void LastReceiveTimeNtp(uint32_t* secs, uint32_t* frac) const;
@@ -46,6 +50,9 @@
private:
bool InOrderPacketInternal(uint16_t sequence_number) const;
RtcpStatistics CalculateStatistics();
+ void UpdateJitter(const RTPHeader& header,
+ uint32_t receive_time_secs,
+ uint32_t receive_time_frac);
Clock* clock_;
scoped_ptr<CriticalSectionWrapper> stream_lock_;
@@ -66,13 +73,10 @@
uint16_t received_seq_first_;
uint16_t received_seq_max_;
uint16_t received_seq_wraps_;
- bool first_packet_;
// Current counter values.
uint16_t received_packet_overhead_;
- uint32_t received_byte_count_;
- uint32_t received_retransmitted_packets_;
- uint32_t received_inorder_packet_count_;
+ StreamDataCounters receive_counters_;
// Counter values when we sent the last report.
uint32_t last_report_inorder_packets_;
@@ -81,18 +85,22 @@
RtcpStatistics last_reported_statistics_;
RtcpStatisticsCallback* const rtcp_callback_;
+ StreamDataCountersCallback* const rtp_callback_;
};
class ReceiveStatisticsImpl : public ReceiveStatistics,
- public RtcpStatisticsCallback {
+ public RtcpStatisticsCallback,
+ public StreamDataCountersCallback {
public:
explicit ReceiveStatisticsImpl(Clock* clock);
~ReceiveStatisticsImpl();
// Implement ReceiveStatistics.
- virtual void IncomingPacket(const RTPHeader& header, size_t bytes,
- bool old_packet) OVERRIDE;
+ virtual void IncomingPacket(const RTPHeader& header,
+ size_t bytes,
+ bool retransmitted) OVERRIDE;
+ virtual void FecPacketReceived(uint32_t ssrc) OVERRIDE;
virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE;
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
@@ -106,10 +114,15 @@
virtual void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback)
OVERRIDE;
- virtual void StatisticsUpdated(const RtcpStatistics& statistics,
- uint32_t ssrc) OVERRIDE;
+ virtual void RegisterRtpStatisticsCallback(
+ StreamDataCountersCallback* callback) OVERRIDE;
private:
+ virtual void StatisticsUpdated(const RtcpStatistics& statistics,
+ uint32_t ssrc) OVERRIDE;
+ virtual void DataCountersUpdated(const StreamDataCounters& counters,
+ uint32_t ssrc) OVERRIDE;
+
typedef std::map<uint32_t, StreamStatisticianImpl*> StatisticianImplMap;
Clock* clock_;
@@ -118,6 +131,7 @@
StatisticianImplMap statisticians_;
RtcpStatisticsCallback* rtcp_stats_callback_;
+ StreamDataCountersCallback* rtp_stats_callback_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVE_STATISTICS_IMPL_H_
diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc b/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc
index 6969bbc..f0b9ded 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc
@@ -28,10 +28,10 @@
receive_statistics_(ReceiveStatistics::Create(&clock_)) {
memset(&header1_, 0, sizeof(header1_));
header1_.ssrc = kSsrc1;
- header1_.sequenceNumber = 0;
+ header1_.sequenceNumber = 100;
memset(&header2_, 0, sizeof(header2_));
header2_.ssrc = kSsrc2;
- header2_.sequenceNumber = 0;
+ header2_.sequenceNumber = 100;
}
protected:
@@ -132,7 +132,7 @@
EXPECT_EQ(2u, packets_received);
}
-TEST_F(ReceiveStatisticsTest, Callbacks) {
+TEST_F(ReceiveStatisticsTest, RtcpCallbacks) {
class TestCallback : public RtcpStatisticsCallback {
public:
TestCallback()
@@ -186,6 +186,10 @@
callback.stats_.extended_max_sequence_number);
EXPECT_EQ(statistics.fraction_lost, callback.stats_.fraction_lost);
EXPECT_EQ(statistics.jitter, callback.stats_.jitter);
+ EXPECT_EQ(51, statistics.fraction_lost);
+ EXPECT_EQ(1u, statistics.cumulative_lost);
+ EXPECT_EQ(5u, statistics.extended_max_sequence_number);
+ EXPECT_EQ(4u, statistics.jitter);
receive_statistics_->RegisterRtcpStatisticsCallback(NULL);
@@ -214,4 +218,86 @@
// Should not have been called after deregister.
EXPECT_EQ(1u, callback.num_calls_);
}
+
+TEST_F(ReceiveStatisticsTest, RtpCallbacks) {
+ class TestCallback : public StreamDataCountersCallback {
+ public:
+ TestCallback()
+ : StreamDataCountersCallback(), num_calls_(0), ssrc_(0), stats_() {}
+ virtual ~TestCallback() {}
+
+ virtual void DataCountersUpdated(const StreamDataCounters& counters,
+ uint32_t ssrc) {
+ ssrc_ = ssrc;
+ stats_ = counters;
+ ++num_calls_;
+ }
+
+ void ExpectMatches(uint32_t num_calls,
+ uint32_t ssrc,
+ uint32_t bytes,
+ uint32_t padding,
+ uint32_t packets,
+ uint32_t retransmits,
+ uint32_t fec) {
+ EXPECT_EQ(num_calls, num_calls_);
+ EXPECT_EQ(ssrc, ssrc_);
+ EXPECT_EQ(bytes, stats_.bytes);
+ EXPECT_EQ(padding, stats_.padding_bytes);
+ EXPECT_EQ(packets, stats_.packets);
+ EXPECT_EQ(retransmits, stats_.retransmitted_packets);
+ EXPECT_EQ(fec, stats_.fec_packets);
+ }
+
+ uint32_t num_calls_;
+ uint32_t ssrc_;
+ StreamDataCounters stats_;
+ } callback;
+
+ receive_statistics_->RegisterRtpStatisticsCallback(&callback);
+
+ const uint32_t kHeaderLength = 20;
+ const uint32_t kPaddingLength = 9;
+
+ // One packet of size kPacketSize1.
+ header1_.headerLength = kHeaderLength;
+ receive_statistics_->IncomingPacket(
+ header1_, kPacketSize1 + kHeaderLength, false);
+ callback.ExpectMatches(1, kSsrc1, kPacketSize1, 0, 1, 0, 0);
+
+ ++header1_.sequenceNumber;
+ clock_.AdvanceTimeMilliseconds(5);
+ header1_.paddingLength = 9;
+ // Another packet of size kPacketSize1 with 9 bytes padding.
+ receive_statistics_->IncomingPacket(
+ header1_, kPacketSize1 + kHeaderLength + kPaddingLength, false);
+ callback.ExpectMatches(2, kSsrc1, 2 * kPacketSize1, kPaddingLength, 2, 0, 0);
+
+ clock_.AdvanceTimeMilliseconds(5);
+ // Retransmit last packet.
+ receive_statistics_->IncomingPacket(
+ header1_, kPacketSize1 + kHeaderLength + kPaddingLength, true);
+ callback.ExpectMatches(
+ 3, kSsrc1, 3 * kPacketSize1, kPaddingLength * 2, 3, 1, 0);
+
+ header1_.paddingLength = 0;
+ ++header1_.sequenceNumber;
+ clock_.AdvanceTimeMilliseconds(5);
+ // One recovered packet.
+ receive_statistics_->IncomingPacket(
+ header1_, kPacketSize1 + kHeaderLength, false);
+ receive_statistics_->FecPacketReceived(kSsrc1);
+ callback.ExpectMatches(
+ 5, kSsrc1, 4 * kPacketSize1, kPaddingLength * 2, 4, 1, 1);
+
+ receive_statistics_->RegisterRtpStatisticsCallback(NULL);
+
+ // New stats, but callback should not be called.
+ ++header1_.sequenceNumber;
+ clock_.AdvanceTimeMilliseconds(5);
+ receive_statistics_->IncomingPacket(
+ header1_, kPacketSize1 + kHeaderLength, true);
+ callback.ExpectMatches(
+ 5, kSsrc1, 4 * kPacketSize1, kPaddingLength * 2, 4, 1, 1);
+}
} // namespace webrtc
diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc
index c0439e7..13c0a5c 100644
--- a/webrtc/video_engine/vie_channel.cc
+++ b/webrtc/video_engine/vie_channel.cc
@@ -1372,6 +1372,16 @@
}
}
+void ViEChannel::RegisterReceiveChannelRtpStatisticsCallback(
+ StreamDataCountersCallback* callback) {
+ WEBRTC_TRACE(kTraceInfo,
+ kTraceVideo,
+ ViEId(engine_id_, channel_id_),
+ "%s",
+ __FUNCTION__);
+ vie_receiver_.GetReceiveStatistics()->RegisterRtpStatisticsCallback(callback);
+}
+
void ViEChannel::GetBandwidthUsage(uint32_t* total_bitrate_sent,
uint32_t* video_bitrate_sent,
uint32_t* fec_bitrate_sent,
diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h
index 9313fdc..be961d5 100644
--- a/webrtc/video_engine/vie_channel.h
+++ b/webrtc/video_engine/vie_channel.h
@@ -199,6 +199,10 @@
void RegisterSendChannelRtpStatisticsCallback(
StreamDataCountersCallback* callback);
+ // Called on update of RTP statistics.
+ void RegisterReceiveChannelRtpStatisticsCallback(
+ StreamDataCountersCallback* callback);
+
void GetBandwidthUsage(uint32_t* total_bitrate_sent,
uint32_t* video_bitrate_sent,
uint32_t* fec_bitrate_sent,
diff --git a/webrtc/video_engine/vie_receiver.cc b/webrtc/video_engine/vie_receiver.cc
index 2946c4a..0d88014 100644
--- a/webrtc/video_engine/vie_receiver.cc
+++ b/webrtc/video_engine/vie_receiver.cc
@@ -269,11 +269,13 @@
header.payload_type_frequency = kVideoPayloadTypeFrequency;
bool in_order = IsPacketInOrder(header);
- rtp_receive_statistics_->IncomingPacket(header, received_packet_length,
- IsPacketRetransmitted(header, in_order));
+ rtp_receive_statistics_->IncomingPacket(
+ header, received_packet_length, IsPacketRetransmitted(header, in_order));
rtp_payload_registry_->SetIncomingPayloadType(header);
- return ReceivePacket(received_packet, received_packet_length, header,
- in_order) ? 0 : -1;
+ return ReceivePacket(
+ received_packet, received_packet_length, header, in_order)
+ ? 0
+ : -1;
}
bool ViEReceiver::ReceivePacket(const uint8_t* packet,
@@ -299,9 +301,11 @@
int packet_length,
const RTPHeader& header) {
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);
if (fec_receiver_->AddReceivedRedPacket(
- header, packet, packet_length,
- rtp_payload_registry_->ulpfec_payload_type()) != 0) {
+ header, packet, packet_length, ulpfec_pt) != 0) {
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
"Incoming RED packet error");
return false;
diff --git a/webrtc/video_engine/vie_receiver.h b/webrtc/video_engine/vie_receiver.h
index 6f480cb..5cccb51 100644
--- a/webrtc/video_engine/vie_receiver.h
+++ b/webrtc/video_engine/vie_receiver.h
@@ -90,8 +90,10 @@
private:
int InsertRTPPacket(const int8_t* rtp_packet, int rtp_packet_length,
const PacketTime& packet_time);
- bool ReceivePacket(const uint8_t* packet, int packet_length,
- const RTPHeader& header, bool in_order);
+ bool ReceivePacket(const uint8_t* packet,
+ int packet_length,
+ const RTPHeader& header,
+ bool in_order);
// Parses and handles for instance RTX and RED headers.
// This function assumes that it's being called from only one thread.
bool ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
diff --git a/webrtc/video_engine/vie_rtp_rtcp_impl.cc b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
index d9f2211..b655349 100644
--- a/webrtc/video_engine/vie_rtp_rtcp_impl.cc
+++ b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
@@ -1224,15 +1224,35 @@
}
int ViERTP_RTCPImpl::RegisterReceiveChannelRtpStatisticsCallback(
- int channel, StreamDataCountersCallback* callback) {
- // TODO(sprang): Implement
- return -1;
+ const int video_channel,
+ StreamDataCountersCallback* callback) {
+ WEBRTC_TRACE(kTraceApiCall,
+ kTraceVideo,
+ ViEId(shared_data_->instance_id(), video_channel),
+ "%s(channel: %d)",
+ __FUNCTION__,
+ video_channel);
+ ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+ ViEChannel* vie_channel = cs.Channel(video_channel);
+ assert(vie_channel != NULL);
+ vie_channel->RegisterReceiveChannelRtpStatisticsCallback(callback);
+ return 0;
}
int ViERTP_RTCPImpl::DeregisterReceiveChannelRtpStatisticsCallback(
- int channel, StreamDataCountersCallback* callback) {
- // TODO(sprang): Implement
- return -1;
+ const int video_channel,
+ StreamDataCountersCallback* callback) {
+ WEBRTC_TRACE(kTraceApiCall,
+ kTraceVideo,
+ ViEId(shared_data_->instance_id(), video_channel),
+ "%s(channel: %d)",
+ __FUNCTION__,
+ video_channel);
+ ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+ ViEChannel* vie_channel = cs.Channel(video_channel);
+ assert(vie_channel != NULL);
+ vie_channel->RegisterReceiveChannelRtpStatisticsCallback(NULL);
+ return 0;
}
// Called whenever the send bitrate is updated.