diff --git a/webrtc/common_types.h b/webrtc/common_types.h index a45cf069d2..28099df78c 100644 --- a/webrtc/common_types.h +++ b/webrtc/common_types.h @@ -240,14 +240,12 @@ struct RtcpStatistics { : fraction_lost(0), cumulative_lost(0), extended_max_sequence_number(0), - jitter(0), - max_jitter(0) {} + jitter(0) {} uint8_t fraction_lost; uint32_t cumulative_lost; uint32_t extended_max_sequence_number; uint32_t jitter; - uint32_t max_jitter; }; // Callback, called whenever a new rtcp report block is transmitted. diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h index 9fb8199ade..51471ba3d0 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h @@ -590,6 +590,12 @@ class RtpRtcp : public Module { // Returns true if the module is configured to store packets. virtual bool StorePackets() const = 0; + // Called on receipt of RTCP report block from remote side. + virtual void RegisterSendChannelRtcpStatisticsCallback( + RtcpStatisticsCallback* callback) = 0; + virtual RtcpStatisticsCallback* + GetSendChannelRtcpStatisticsCallback() = 0; + /************************************************************************** * * Audio diff --git a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 93230da101..9ca48975a3 100644 --- a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -201,6 +201,10 @@ class MockRtpRtcp : public RtpRtcp { MOCK_METHOD2(SetStorePacketsStatus, int32_t(const bool enable, const uint16_t numberToStore)); MOCK_CONST_METHOD0(StorePackets, bool()); + MOCK_METHOD1(RegisterSendChannelRtcpStatisticsCallback, + void(RtcpStatisticsCallback*)); + MOCK_METHOD0(GetSendChannelRtcpStatisticsCallback, + RtcpStatisticsCallback*()); MOCK_METHOD1(RegisterAudioCallback, int32_t(RtpAudioFeedback* messagesCallback)); MOCK_METHOD1(SetAudioPacketSize, diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc index fb7adf6b34..a95fddede2 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.cc @@ -54,7 +54,8 @@ RTCPReceiver::RTCPReceiver(const int32_t id, Clock* clock, _receivedInfoMap(), _packetTimeOutMS(0), _lastReceivedRrMs(0), - _lastIncreasedSequenceNumberMs(0) { + _lastIncreasedSequenceNumberMs(0), + stats_callback_(NULL) { memset(&_remoteSenderInfo, 0, sizeof(_remoteSenderInfo)); WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); } @@ -1359,6 +1360,19 @@ int32_t RTCPReceiver::UpdateTMMBR() { return 0; } +void RTCPReceiver::RegisterRtcpStatisticsCallback( + RtcpStatisticsCallback* callback) { + CriticalSectionScoped cs(_criticalSectionFeedbacks); + if (callback != NULL) + assert(stats_callback_ == NULL); + stats_callback_ = callback; +} + +RtcpStatisticsCallback* RTCPReceiver::GetRtcpStatisticsCallback() { + CriticalSectionScoped cs(_criticalSectionFeedbacks); + return stats_callback_; +} + // Holding no Critical section void RTCPReceiver::TriggerCallbacksFromRTCPPacket( RTCPPacketInformation& rtcpPacketInformation) { @@ -1453,6 +1467,24 @@ void RTCPReceiver::TriggerCallbacksFromRTCPPacket( } } } + + { + CriticalSectionScoped cs(_criticalSectionFeedbacks); + if (stats_callback_) { + for (ReportBlockList::const_iterator it = + rtcpPacketInformation.report_blocks.begin(); + it != rtcpPacketInformation.report_blocks.end(); + ++it) { + RtcpStatistics stats; + stats.cumulative_lost = it->cumulativeLost; + stats.extended_max_sequence_number = it->extendedHighSeqNum; + stats.fraction_lost = it->fractionLost; + stats.jitter = it->jitter; + + stats_callback_->StatisticsUpdated(stats, local_ssrc); + } + } + } } int32_t RTCPReceiver::CNAME(const uint32_t remoteSSRC, diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h index 5c6f732195..637773dc74 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_receiver.h @@ -109,6 +109,9 @@ public: int32_t UpdateTMMBR(); + void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback); + RtcpStatisticsCallback* GetRtcpStatisticsCallback(); + protected: RTCPHelp::RTCPReportBlockInformation* CreateReportBlockInformation(const uint32_t remoteSSRC); RTCPHelp::RTCPReportBlockInformation* GetReportBlockInformation(const uint32_t remoteSSRC) const; @@ -262,6 +265,7 @@ protected: // delivered RTP packet to the remote side. int64_t _lastIncreasedSequenceNumberMs; + RtcpStatisticsCallback* stats_callback_; }; } // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc index 25670f54d8..47befea301 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc @@ -35,16 +35,18 @@ class PacketBuilder { struct ReportBlock { ReportBlock(uint32_t ssrc, uint32_t extended_max, uint8_t fraction_loss, - uint32_t cumulative_loss) + uint32_t cumulative_loss, uint32_t jitter) : ssrc(ssrc), extended_max(extended_max), fraction_loss(fraction_loss), - cumulative_loss(cumulative_loss) {} + cumulative_loss(cumulative_loss), + jitter(jitter) {} uint32_t ssrc; uint32_t extended_max; uint8_t fraction_loss; uint32_t cumulative_loss; + uint32_t jitter; }; PacketBuilder() @@ -108,9 +110,9 @@ class PacketBuilder { void AddRrPacket(uint32_t sender_ssrc, uint32_t rtp_ssrc, uint32_t extended_max, uint8_t fraction_loss, - uint32_t cumulative_loss) { + uint32_t cumulative_loss, uint32_t jitter) { ReportBlock report_block(rtp_ssrc, extended_max, fraction_loss, - cumulative_loss); + cumulative_loss, jitter); std::list report_block_vector(&report_block, &report_block + 1); AddRrPacketMultipleReportBlocks(sender_ssrc, report_block_vector); @@ -123,16 +125,17 @@ class PacketBuilder { for (std::list::const_iterator it = report_blocks.begin(); it != report_blocks.end(); ++it) { AddReportBlock(it->ssrc, it->extended_max, it->fraction_loss, - it->cumulative_loss); + it->cumulative_loss, it->jitter); } } void AddReportBlock(uint32_t rtp_ssrc, uint32_t extended_max, - uint8_t fraction_loss, uint32_t cumulative_loss) { + uint8_t fraction_loss, uint32_t cumulative_loss, + uint32_t jitter) { Add32(rtp_ssrc); Add32((fraction_loss << 24) + cumulative_loss); Add32(extended_max); - Add32(0); // Jitter. + Add32(jitter); Add32(0); // Last SR. Add32(0); // Delay since last SR. } @@ -283,8 +286,9 @@ class RtcpReceiverTest : public ::testing::Test { true); // Allow non-compound RTCP RTCPHelp::RTCPPacketInformation rtcpPacketInformation; - int result = rtcp_receiver_->IncomingRTCPPacket(rtcpPacketInformation, - &rtcpParser); + EXPECT_EQ(0, rtcp_receiver_->IncomingRTCPPacket(rtcpPacketInformation, + &rtcpParser)); + rtcp_receiver_->TriggerCallbacksFromRTCPPacket(rtcpPacketInformation); // The NACK list is on purpose not copied below as it isn't needed by the // test. rtcp_packet_info_.rtcpPacketTypeFlags = @@ -308,7 +312,7 @@ class RtcpReceiverTest : public ::testing::Test { if (rtcpPacketInformation.VoIPMetric) { rtcp_packet_info_.AddVoIPMetric(rtcpPacketInformation.VoIPMetric); } - return result; + return 0; } OverUseDetectorOptions over_use_detector_options_; @@ -543,7 +547,7 @@ TEST_F(RtcpReceiverTest, ReceiveReportTimeout) { // Add a RR and advance the clock just enough to not trigger a timeout. PacketBuilder p1; - p1.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0); + p1.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0, 0); EXPECT_EQ(0, InjectRtcpPacket(p1.packet(), p1.length())); system_clock_.AdvanceTimeMilliseconds(3 * kRtcpIntervalMs - 1); EXPECT_FALSE(rtcp_receiver_->RtcpRrTimeout(kRtcpIntervalMs)); @@ -552,7 +556,7 @@ TEST_F(RtcpReceiverTest, ReceiveReportTimeout) { // Add a RR with the same extended max as the previous RR to trigger a // sequence number timeout, but not a RR timeout. PacketBuilder p2; - p2.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0); + p2.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0, 0); EXPECT_EQ(0, InjectRtcpPacket(p2.packet(), p2.length())); system_clock_.AdvanceTimeMilliseconds(2); EXPECT_FALSE(rtcp_receiver_->RtcpRrTimeout(kRtcpIntervalMs)); @@ -570,7 +574,7 @@ TEST_F(RtcpReceiverTest, ReceiveReportTimeout) { // Add a new RR with increase sequence number to reset timers. PacketBuilder p3; sequence_number++; - p2.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0); + p2.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0, 0); EXPECT_EQ(0, InjectRtcpPacket(p2.packet(), p2.length())); EXPECT_FALSE(rtcp_receiver_->RtcpRrTimeout(kRtcpIntervalMs)); EXPECT_FALSE(rtcp_receiver_->RtcpRrSequenceNumberTimeout(kRtcpIntervalMs)); @@ -578,7 +582,7 @@ TEST_F(RtcpReceiverTest, ReceiveReportTimeout) { // Verify we can get a timeout again once we've received new RR. system_clock_.AdvanceTimeMilliseconds(2 * kRtcpIntervalMs); PacketBuilder p4; - p4.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0); + p4.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, 0, 0, 0); EXPECT_EQ(0, InjectRtcpPacket(p4.packet(), p4.length())); system_clock_.AdvanceTimeMilliseconds(kRtcpIntervalMs + 1); EXPECT_FALSE(rtcp_receiver_->RtcpRrTimeout(kRtcpIntervalMs)); @@ -604,9 +608,9 @@ TEST_F(RtcpReceiverTest, TwoReportBlocks) { PacketBuilder packet; std::list report_blocks; report_blocks.push_back(PacketBuilder::ReportBlock( - kSourceSsrcs[0], sequence_numbers[0], 10, 5)); + kSourceSsrcs[0], sequence_numbers[0], 10, 5, 0)); report_blocks.push_back(PacketBuilder::ReportBlock( - kSourceSsrcs[1], sequence_numbers[1], 0, 0)); + kSourceSsrcs[1], sequence_numbers[1], 0, 0, 0)); packet.AddRrPacketMultipleReportBlocks(kSenderSsrc, report_blocks); EXPECT_EQ(0, InjectRtcpPacket(packet.packet(), packet.length())); ASSERT_EQ(2u, rtcp_packet_info_.report_blocks.size()); @@ -616,9 +620,9 @@ TEST_F(RtcpReceiverTest, TwoReportBlocks) { PacketBuilder packet2; report_blocks.clear(); report_blocks.push_back(PacketBuilder::ReportBlock( - kSourceSsrcs[0], sequence_numbers[0], 0, 0)); + kSourceSsrcs[0], sequence_numbers[0], 0, 0, 0)); report_blocks.push_back(PacketBuilder::ReportBlock( - kSourceSsrcs[1], sequence_numbers[1], 20, 10)); + kSourceSsrcs[1], sequence_numbers[1], 20, 10, 0)); packet2.AddRrPacketMultipleReportBlocks(kSenderSsrc, report_blocks); EXPECT_EQ(0, InjectRtcpPacket(packet2.packet(), packet2.length())); ASSERT_EQ(2u, rtcp_packet_info_.report_blocks.size()); @@ -736,6 +740,61 @@ TEST_F(RtcpReceiverTest, TmmbrThreeConstraintsTimeOut) { EXPECT_EQ(kMediaRecipientSsrc + 2, candidate_set.Ssrc(0)); } +TEST_F(RtcpReceiverTest, Callbacks) { + class RtcpCallbackImpl : public RtcpStatisticsCallback { + public: + RtcpCallbackImpl() : RtcpStatisticsCallback(), ssrc_(0) {} + virtual ~RtcpCallbackImpl() {} + + virtual void StatisticsUpdated(const RtcpStatistics& statistics, + uint32_t ssrc) { + stats_ = statistics; + ssrc_ = ssrc; + } + + bool Matches(uint32_t ssrc, uint32_t extended_max, uint8_t fraction_loss, + uint32_t cumulative_loss, uint32_t jitter) { + return ssrc_ == ssrc && + stats_.fraction_lost == fraction_loss && + stats_.cumulative_lost == cumulative_loss && + stats_.extended_max_sequence_number == extended_max && + stats_.jitter == jitter; + } + + RtcpStatistics stats_; + uint32_t ssrc_; + } callback; + + rtcp_receiver_->RegisterRtcpStatisticsCallback(&callback); + + const uint32_t kSenderSsrc = 0x10203; + const uint32_t kSourceSsrc = 0x123456; + const uint8_t fraction_loss = 3; + const uint32_t cumulative_loss = 7; + const uint32_t jitter = 9; + uint32_t sequence_number = 1234; + + std::set ssrcs; + ssrcs.insert(kSourceSsrc); + rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs); + + // First packet, all numbers should just propagate + PacketBuilder p1; + p1.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number, + fraction_loss, cumulative_loss, jitter); + EXPECT_EQ(0, InjectRtcpPacket(p1.packet(), p1.length())); + EXPECT_TRUE(callback.Matches(kSourceSsrc, sequence_number, fraction_loss, + cumulative_loss, jitter)); + + rtcp_receiver_->RegisterRtcpStatisticsCallback(NULL); + + // Add arbitrary numbers, callback should not be called (retain old values) + PacketBuilder p2; + p2.AddRrPacket(kSenderSsrc, kSourceSsrc, sequence_number + 1, 42, 137, 4711); + EXPECT_EQ(0, InjectRtcpPacket(p2.packet(), p2.length())); + EXPECT_TRUE(callback.Matches(kSourceSsrc, sequence_number, fraction_loss, + cumulative_loss, jitter)); +} } // Anonymous namespace diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 027d17d28d..4af8cde5f5 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -1189,6 +1189,16 @@ bool ModuleRtpRtcpImpl::StorePackets() const { return rtp_sender_.StorePackets(); } +void ModuleRtpRtcpImpl::RegisterSendChannelRtcpStatisticsCallback( + RtcpStatisticsCallback* callback) { + rtcp_receiver_.RegisterRtcpStatisticsCallback(callback); +} + +RtcpStatisticsCallback* ModuleRtpRtcpImpl:: + GetSendChannelRtcpStatisticsCallback() { + return rtcp_receiver_.GetRtcpStatisticsCallback(); +} + // Send a TelephoneEvent tone using RFC 2833 (4733). int32_t ModuleRtpRtcpImpl::SendTelephoneEventOutband( const uint8_t key, diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h index c8ab063e06..0dd25492d5 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -246,6 +246,12 @@ class ModuleRtpRtcpImpl : public RtpRtcp { virtual bool StorePackets() const OVERRIDE; + // Called on receipt of RTCP report block from remote side. + virtual void RegisterSendChannelRtcpStatisticsCallback( + RtcpStatisticsCallback* callback) OVERRIDE; + virtual RtcpStatisticsCallback* + GetSendChannelRtcpStatisticsCallback() OVERRIDE; + // (APP) Application specific data. virtual int32_t SetRTCPApplicationSpecificData( const uint8_t sub_type, diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc index 59c628edcf..0c595fc82c 100644 --- a/webrtc/video_engine/vie_channel.cc +++ b/webrtc/video_engine/vie_channel.cc @@ -357,6 +357,7 @@ int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec, rtp_rtcp->SetSendingStatus(false); rtp_rtcp->SetSendingMediaStatus(false); rtp_rtcp->RegisterSendFrameCountObserver(NULL); + rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(NULL); simulcast_rtp_rtcp_.pop_back(); removed_rtp_rtcp_.push_front(rtp_rtcp); } @@ -413,6 +414,8 @@ int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec, rtp_rtcp->SetRtcpXrRrtrStatus(rtp_rtcp_->RtcpXrRrtrStatus()); rtp_rtcp->RegisterSendFrameCountObserver( rtp_rtcp_->GetSendFrameCountObserver()); + rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback( + rtp_rtcp_->GetSendChannelRtcpStatisticsCallback()); } // |RegisterSimulcastRtpRtcpModules| resets all old weak pointers and old // modules can be deleted after this step. @@ -424,6 +427,7 @@ int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec, rtp_rtcp->SetSendingStatus(false); rtp_rtcp->SetSendingMediaStatus(false); rtp_rtcp->RegisterSendFrameCountObserver(NULL); + rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(NULL); simulcast_rtp_rtcp_.pop_back(); removed_rtp_rtcp_.push_front(rtp_rtcp); } @@ -1264,6 +1268,19 @@ int32_t ViEChannel::GetSendRtcpStatistics(uint16_t* fraction_lost, return 0; } +void ViEChannel::RegisterSendChannelRtcpStatisticsCallback( + RtcpStatisticsCallback* callback) { + WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s", + __FUNCTION__); + rtp_rtcp_->RegisterSendChannelRtcpStatisticsCallback(callback); + CriticalSectionScoped cs(rtp_rtcp_cs_.get()); + for (std::list::const_iterator it = simulcast_rtp_rtcp_.begin(); + it != simulcast_rtp_rtcp_.end(); + ++it) { + (*it)->RegisterSendChannelRtcpStatisticsCallback(callback); + } +} + // TODO(holmer): This is a bad function name as it implies that it returns the // received RTCP, while it actually returns the statistics which will be sent // in the RTCP. diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h index a16a65d513..a17bb2b928 100644 --- a/webrtc/video_engine/vie_channel.h +++ b/webrtc/video_engine/vie_channel.h @@ -174,6 +174,10 @@ class ViEChannel uint32_t* jitter_samples, int32_t* rtt_ms); + // Called on receipt of RTCP report block from remote side. + void RegisterSendChannelRtcpStatisticsCallback( + RtcpStatisticsCallback* callback); + // Returns our localy created statistics of the received RTP stream. int32_t GetReceivedRtcpStatistics(uint16_t* fraction_lost, uint32_t* cumulative_lost, diff --git a/webrtc/video_engine/vie_rtp_rtcp_impl.cc b/webrtc/video_engine/vie_rtp_rtcp_impl.cc index edad270be5..9822b9d618 100644 --- a/webrtc/video_engine/vie_rtp_rtcp_impl.cc +++ b/webrtc/video_engine/vie_rtp_rtcp_impl.cc @@ -1119,15 +1119,39 @@ int ViERTP_RTCPImpl::DeregisterRTCPObserver(const int video_channel) { } int ViERTP_RTCPImpl::RegisterSendChannelRtcpStatisticsCallback( - int channel, RtcpStatisticsCallback* callback) { - // TODO(sprang): Implement - return -1; + int video_channel, RtcpStatisticsCallback* 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); + if (!vie_channel) { + WEBRTC_TRACE(kTraceError, kTraceVideo, + ViEId(shared_data_->instance_id(), video_channel), + "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); + shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); + return -1; + } + vie_channel->RegisterSendChannelRtcpStatisticsCallback(callback); + return 0; } int ViERTP_RTCPImpl::DeregisterSendChannelRtcpStatisticsCallback( - int channel, RtcpStatisticsCallback* callback) { - // TODO(sprang): Implement - return -1; + int video_channel, RtcpStatisticsCallback* 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); + if (!vie_channel) { + WEBRTC_TRACE(kTraceError, kTraceVideo, + ViEId(shared_data_->instance_id(), video_channel), + "%s: Channel %d doesn't exist", __FUNCTION__, video_channel); + shared_data_->SetLastError(kViERtpRtcpInvalidChannelId); + return -1; + } + vie_channel->RegisterSendChannelRtcpStatisticsCallback(NULL); + return 0; } int ViERTP_RTCPImpl::RegisterReceiveChannelRtcpStatisticsCallback(