diff --git a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h index d034b4957..6f2ea4fb3 100644 --- a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h +++ b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h @@ -51,9 +51,13 @@ class ReceiveStatistics : public Module { 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 @@ class ReceiveStatistics : public Module { // 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 @@ class NullReceiveStatistics : public ReceiveStatistics { 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 af0108be4..cb0055685 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 @@ StreamStatistician::~StreamStatistician() {} 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 @@ StreamStatisticianImpl::StreamStatisticianImpl( 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 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header, 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 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header, 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 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header, // 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 StreamStatisticianImpl::GetStatistics(RtcpStatistics* statistics, 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 @@ bool StreamStatisticianImpl::GetStatistics(RtcpStatistics* statistics, } rtcp_callback_->StatisticsUpdated(*statistics, ssrc_); + rtp_callback_->DataCountersUpdated(receive_counters_, ssrc_); + return true; } @@ -219,7 +225,8 @@ RtcpStatistics StreamStatisticianImpl::CalculateStatistics() { // 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 @@ RtcpStatistics StreamStatisticianImpl::CalculateStatistics() { // 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 @@ RtcpStatistics StreamStatisticianImpl::CalculateStatistics() { 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 @@ void StreamStatisticianImpl::GetDataCounters( 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 @@ ReceiveStatisticsImpl::ReceiveStatisticsImpl(Clock* clock) : 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 @@ ReceiveStatisticsImpl::~ReceiveStatisticsImpl() { } 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 @@ void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header, if (it == statisticians_.end()) { std::pair 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::StatisticsUpdated(const RtcpStatistics& statistics, } } +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 @@ int32_t NullReceiveStatistics::Process() { return 0; } 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 0e24ca243..34bd46c00 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 CriticalSectionWrapper; 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 @@ class StreamStatisticianImpl : public StreamStatistician { 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 @@ class StreamStatisticianImpl : public StreamStatistician { 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 stream_lock_; @@ -66,13 +73,10 @@ class StreamStatisticianImpl : public StreamStatistician { 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 @@ class StreamStatisticianImpl : public StreamStatistician { 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 @@ class ReceiveStatisticsImpl : public ReceiveStatistics, 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 StatisticianImplMap; Clock* clock_; @@ -118,6 +131,7 @@ class ReceiveStatisticsImpl : public ReceiveStatistics, 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 6969bbc82..f0b9dedde 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 @@ class ReceiveStatisticsTest : public ::testing::Test { 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 @@ TEST_F(ReceiveStatisticsTest, ActiveStatisticians) { EXPECT_EQ(2u, packets_received); } -TEST_F(ReceiveStatisticsTest, Callbacks) { +TEST_F(ReceiveStatisticsTest, RtcpCallbacks) { class TestCallback : public RtcpStatisticsCallback { public: TestCallback() @@ -186,6 +186,10 @@ TEST_F(ReceiveStatisticsTest, Callbacks) { 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 @@ TEST_F(ReceiveStatisticsTest, Callbacks) { // 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 c0439e724..13c0a5cd4 100644 --- a/webrtc/video_engine/vie_channel.cc +++ b/webrtc/video_engine/vie_channel.cc @@ -1372,6 +1372,16 @@ void ViEChannel::RegisterSendChannelRtpStatisticsCallback( } } +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 9313fdc5a..be961d576 100644 --- a/webrtc/video_engine/vie_channel.h +++ b/webrtc/video_engine/vie_channel.h @@ -199,6 +199,10 @@ class ViEChannel 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 2946c4a08..0d8801433 100644 --- a/webrtc/video_engine/vie_receiver.cc +++ b/webrtc/video_engine/vie_receiver.cc @@ -269,11 +269,13 @@ int ViEReceiver::InsertRTPPacket(const int8_t* rtp_packet, 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 @@ bool ViEReceiver::ParseAndHandleEncapsulatingHeader(const uint8_t* packet, 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 6f480cbb4..5cccb510b 100644 --- a/webrtc/video_engine/vie_receiver.h +++ b/webrtc/video_engine/vie_receiver.h @@ -90,8 +90,10 @@ class ViEReceiver : public RtpData { 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 d9f2211db..b655349d4 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::DeregisterSendChannelRtpStatisticsCallback( } 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.