From 69e9cb08285f6cbcab547c7a5e6aa668fa6f2d29 Mon Sep 17 00:00:00 2001 From: hbos Date: Mon, 31 Oct 2016 14:48:26 -0700 Subject: [PATCH] RTCOutboundRTPStreamStats[1] added. This also adds RTCRTPStreamStats[2] which it derives from. Not all stats are supported in this CL, this must be addressed before closing the issue. RTCStatsReport also gets a timestamp and ToString. [1] https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict* [2] https://w3c.github.io/webrtc-stats/#streamstats-dict* BUG=chromium:627816, chromium:657856, chromium:657854 Review-Url: https://codereview.webrtc.org/2456463002 Cr-Commit-Position: refs/heads/master@{#14860} --- webrtc/api/rtcstatscollector.cc | 140 ++++++++++++++++++++- webrtc/api/rtcstatscollector.h | 6 + webrtc/api/rtcstatscollector_unittest.cc | 147 ++++++++++++++++++++++- webrtc/api/stats/rtcstats.h | 4 +- webrtc/api/stats/rtcstats_objects.h | 58 ++++++++- webrtc/api/stats/rtcstatsreport.h | 11 +- webrtc/stats/rtcstats.cc | 2 + webrtc/stats/rtcstats_objects.cc | 86 +++++++++++++ webrtc/stats/rtcstatsreport.cc | 26 +++- webrtc/stats/rtcstatsreport_unittest.cc | 12 +- 10 files changed, 472 insertions(+), 20 deletions(-) diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc index dc2b1896cc..543181ede4 100644 --- a/webrtc/api/rtcstatscollector.cc +++ b/webrtc/api/rtcstatscollector.cc @@ -17,6 +17,8 @@ #include "webrtc/api/peerconnection.h" #include "webrtc/api/webrtcsession.h" #include "webrtc/base/checks.h" +#include "webrtc/base/timeutils.h" +#include "webrtc/media/base/mediachannel.h" #include "webrtc/p2p/base/candidate.h" #include "webrtc/p2p/base/p2pconstants.h" #include "webrtc/p2p/base/port.h" @@ -41,6 +43,21 @@ std::string RTCTransportStatsIDFromTransportChannel( rtc::ToString<>(channel_component); } +std::string RTCTransportStatsIDFromBaseChannel( + const ProxyTransportMap& proxy_to_transport, + const cricket::BaseChannel& base_channel) { + auto proxy_it = proxy_to_transport.find(base_channel.content_name()); + if (proxy_it == proxy_to_transport.cend()) + return ""; + return RTCTransportStatsIDFromTransportChannel( + proxy_it->second, cricket::ICE_CANDIDATE_COMPONENT_RTP); +} + +std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) { + return audio ? "RTCOutboundRTPAudioStream_" + rtc::ToString<>(ssrc) + : "RTCOutboundRTPVideoStream_" + rtc::ToString<>(ssrc); +} + const char* CandidateTypeToRTCIceCandidateType(const std::string& type) { if (type == cricket::LOCAL_PORT_TYPE) return RTCIceCandidateType::kHost; @@ -71,6 +88,47 @@ const char* DataStateToRTCDataChannelState( } } +void SetOutboundRTPStreamStatsFromMediaSenderInfo( + const cricket::MediaSenderInfo& media_sender_info, + RTCOutboundRTPStreamStats* outbound_stats) { + RTC_DCHECK(outbound_stats); + outbound_stats->ssrc = rtc::ToString<>(media_sender_info.ssrc()); + // TODO(hbos): Support the remote case. crbug.com/657856 + outbound_stats->is_remote = false; + // TODO(hbos): Set |codec_id| when we have |RTCCodecStats|. Maybe relevant: + // |media_sender_info.codec_name|. crbug.com/657854, 657856, 659117 + outbound_stats->packets_sent = + static_cast(media_sender_info.packets_sent); + outbound_stats->bytes_sent = + static_cast(media_sender_info.bytes_sent); + outbound_stats->round_trip_time = + static_cast(media_sender_info.rtt_ms) / rtc::kNumMillisecsPerSec; +} + +void SetOutboundRTPStreamStatsFromVoiceSenderInfo( + const cricket::VoiceSenderInfo& voice_sender_info, + RTCOutboundRTPStreamStats* outbound_audio) { + SetOutboundRTPStreamStatsFromMediaSenderInfo( + voice_sender_info, outbound_audio); + outbound_audio->media_type = "audio"; + // |fir_count|, |pli_count| and |sli_count| are only valid for video and are + // purposefully left undefined for audio. +} + +void SetOutboundRTPStreamStatsFromVideoSenderInfo( + const cricket::VideoSenderInfo& video_sender_info, + RTCOutboundRTPStreamStats* outbound_video) { + SetOutboundRTPStreamStatsFromMediaSenderInfo( + video_sender_info, outbound_video); + outbound_video->media_type = "video"; + outbound_video->fir_count = + static_cast(video_sender_info.firs_rcvd); + outbound_video->pli_count = + static_cast(video_sender_info.plis_rcvd); + outbound_video->nack_count = + static_cast(video_sender_info.nacks_rcvd); +} + void ProduceCertificateStatsFromSSLCertificateStats( int64_t timestamp_us, const rtc::SSLCertificateStats& certificate_stats, RTCStatsReport* report) { @@ -184,7 +242,8 @@ void RTCStatsCollector::ClearCachedStatsReport() { void RTCStatsCollector::ProducePartialResultsOnSignalingThread( int64_t timestamp_us) { RTC_DCHECK(signaling_thread_->IsCurrent()); - rtc::scoped_refptr report = RTCStatsReport::Create(); + rtc::scoped_refptr report = RTCStatsReport::Create( + timestamp_us); SessionStats session_stats; if (pc_->session()->GetTransportStats(&session_stats)) { @@ -195,6 +254,8 @@ void RTCStatsCollector::ProducePartialResultsOnSignalingThread( timestamp_us, transport_cert_stats, report.get()); ProduceIceCandidateAndPairStats_s( timestamp_us, session_stats, report.get()); + ProduceRTPStreamStats_s( + timestamp_us, session_stats, report.get()); ProduceTransportStats_s( timestamp_us, session_stats, transport_cert_stats, report.get()); } @@ -207,9 +268,16 @@ void RTCStatsCollector::ProducePartialResultsOnSignalingThread( void RTCStatsCollector::ProducePartialResultsOnWorkerThread( int64_t timestamp_us) { RTC_DCHECK(worker_thread_->IsCurrent()); - rtc::scoped_refptr report = RTCStatsReport::Create(); + rtc::scoped_refptr report = RTCStatsReport::Create( + timestamp_us); // TODO(hbos): Gather stats on worker thread. + // pc_->session()'s channels are owned by the signaling thread but there are + // some stats that are gathered on the worker thread. Instead of a synchronous + // invoke on "s->w" we could to the "w" work here asynchronously if it wasn't + // for the ownership issue. Synchronous invokes in other places makes it + // difficult to introduce locks without introducing deadlocks and the channels + // are not reference counted. AddPartialResults(report); } @@ -217,9 +285,16 @@ void RTCStatsCollector::ProducePartialResultsOnWorkerThread( void RTCStatsCollector::ProducePartialResultsOnNetworkThread( int64_t timestamp_us) { RTC_DCHECK(network_thread_->IsCurrent()); - rtc::scoped_refptr report = RTCStatsReport::Create(); + rtc::scoped_refptr report = RTCStatsReport::Create( + timestamp_us); // TODO(hbos): Gather stats on network thread. + // pc_->session()'s channels are owned by the signaling thread but there are + // some stats that are gathered on the network thread. Instead of a + // synchronous invoke on "s->n" we could to the "n" work here asynchronously + // if it wasn't for the ownership issue. Synchronous invokes in other places + // makes it difficult to introduce locks without introducing deadlocks and the + // channels are not reference counted. AddPartialResults(report); } @@ -337,7 +412,7 @@ void RTCStatsCollector::ProduceIceCandidateAndPairStats_s( // smoothed according to the spec. crbug.com/633550. See // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentrtt candidate_pair_stats->current_rtt = - static_cast(info.rtt) / 1000.0; + static_cast(info.rtt) / rtc::kNumMillisecsPerSec; candidate_pair_stats->requests_sent = static_cast(info.sent_ping_requests_total); candidate_pair_stats->responses_received = @@ -374,6 +449,63 @@ void RTCStatsCollector::ProducePeerConnectionStats_s( report->AddStats(std::move(stats)); } +void RTCStatsCollector::ProduceRTPStreamStats_s( + int64_t timestamp_us, const SessionStats& session_stats, + RTCStatsReport* report) const { + RTC_DCHECK(signaling_thread_->IsCurrent()); + + // Audio + if (pc_->session()->voice_channel()) { + cricket::VoiceMediaInfo voice_media_info; + if (pc_->session()->voice_channel()->GetStats(&voice_media_info)) { + std::string transport_id = RTCTransportStatsIDFromBaseChannel( + session_stats.proxy_to_transport, *pc_->session()->voice_channel()); + for (const cricket::VoiceSenderInfo& voice_sender_info : + voice_media_info.senders) { + // TODO(nisse): SSRC == 0 currently means none. Delete check when that + // is fixed. + if (voice_sender_info.ssrc() == 0) + continue; + std::unique_ptr outbound_audio( + new RTCOutboundRTPStreamStats( + RTCOutboundRTPStreamStatsIDFromSSRC( + true, voice_sender_info.ssrc()), + timestamp_us)); + SetOutboundRTPStreamStatsFromVoiceSenderInfo( + voice_sender_info, outbound_audio.get()); + if (!transport_id.empty()) + outbound_audio->transport_id = transport_id; + report->AddStats(std::move(outbound_audio)); + } + } + } + // Video + if (pc_->session()->video_channel()) { + cricket::VideoMediaInfo video_media_info; + if (pc_->session()->video_channel()->GetStats(&video_media_info)) { + std::string transport_id = RTCTransportStatsIDFromBaseChannel( + session_stats.proxy_to_transport, *pc_->session()->video_channel()); + for (const cricket::VideoSenderInfo& video_sender_info : + video_media_info.senders) { + // TODO(nisse): SSRC == 0 currently means none. Delete check when that + // is fixed. + if (video_sender_info.ssrc() == 0) + continue; + std::unique_ptr outbound_video( + new RTCOutboundRTPStreamStats( + RTCOutboundRTPStreamStatsIDFromSSRC( + false, video_sender_info.ssrc()), + timestamp_us)); + SetOutboundRTPStreamStatsFromVideoSenderInfo( + video_sender_info, outbound_video.get()); + if (!transport_id.empty()) + outbound_video->transport_id = transport_id; + report->AddStats(std::move(outbound_video)); + } + } + } +} + void RTCStatsCollector::ProduceTransportStats_s( int64_t timestamp_us, const SessionStats& session_stats, const std::map& transport_cert_stats, diff --git a/webrtc/api/rtcstatscollector.h b/webrtc/api/rtcstatscollector.h index 51d2705c2c..fbef1187a5 100644 --- a/webrtc/api/rtcstatscollector.h +++ b/webrtc/api/rtcstatscollector.h @@ -101,6 +101,12 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface { // Produces |RTCPeerConnectionStats|. void ProducePeerConnectionStats_s( int64_t timestamp_us, RTCStatsReport* report) const; + // Produces |RTCOutboundRTPStreamStats|. TODO(hbos): Produce both types of + // |RTCRTPStreamStats|, the other one being |RTCInboundRTPStreamStats|. + // crbug.com/657855 + void ProduceRTPStreamStats_s( + int64_t timestamp_us, const SessionStats& session_stats, + RTCStatsReport* report) const; // Produces |RTCTransportStats|. void ProduceTransportStats_s( int64_t timestamp_us, const SessionStats& session_stats, diff --git a/webrtc/api/rtcstatscollector_unittest.cc b/webrtc/api/rtcstatscollector_unittest.cc index 97ede90207..312f0d2d98 100644 --- a/webrtc/api/rtcstatscollector_unittest.cc +++ b/webrtc/api/rtcstatscollector_unittest.cc @@ -32,13 +32,16 @@ #include "webrtc/base/timeutils.h" #include "webrtc/logging/rtc_event_log/rtc_event_log.h" #include "webrtc/media/base/fakemediaengine.h" +#include "webrtc/media/base/test/mock_mediachannel.h" #include "webrtc/p2p/base/p2pconstants.h" #include "webrtc/p2p/base/port.h" using testing::_; using testing::Invoke; using testing::Return; +using testing::ReturnNull; using testing::ReturnRef; +using testing::SetArgPointee; namespace webrtc { @@ -67,6 +70,10 @@ void PrintTo(const RTCPeerConnectionStats& stats, ::std::ostream* os) { *os << stats.ToString(); } +void PrintTo(const RTCOutboundRTPStreamStats& stats, ::std::ostream* os) { + *os << stats.ToString(); +} + void PrintTo(const RTCTransportStats& stats, ::std::ostream* os) { *os << stats.ToString(); } @@ -144,8 +151,9 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { RTCStatsCollectorTestHelper() : worker_thread_(rtc::Thread::Current()), network_thread_(rtc::Thread::Current()), + media_engine_(new cricket::FakeMediaEngine()), channel_manager_( - new cricket::ChannelManager(new cricket::FakeMediaEngine(), + new cricket::ChannelManager(media_engine_, worker_thread_, network_thread_)), media_controller_( @@ -159,6 +167,8 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { EXPECT_CALL(pc_, session()).WillRepeatedly(Return(&session_)); EXPECT_CALL(pc_, sctp_data_channels()).WillRepeatedly( ReturnRef(data_channels_)); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); EXPECT_CALL(session_, GetTransportStats(_)).WillRepeatedly(Return(false)); EXPECT_CALL(session_, GetLocalCertificate(_, _)).WillRepeatedly( Return(false)); @@ -167,6 +177,9 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { } rtc::ScopedFakeClock& fake_clock() { return fake_clock_; } + rtc::Thread* worker_thread() { return worker_thread_; } + rtc::Thread* network_thread() { return network_thread_; } + cricket::FakeMediaEngine* media_engine() { return media_engine_; } MockWebRtcSession& session() { return session_; } MockPeerConnection& pc() { return pc_; } std::vector>& data_channels() { @@ -184,6 +197,7 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { webrtc::RtcEventLogNullImpl event_log_; rtc::Thread* const worker_thread_; rtc::Thread* const network_thread_; + cricket::FakeMediaEngine* media_engine_; std::unique_ptr channel_manager_; std::unique_ptr media_controller_; MockWebRtcSession session_; @@ -271,7 +285,7 @@ class FakeRTCStatsCollector : public RTCStatsCollector, } rtc::scoped_refptr signaling_report = - RTCStatsReport::Create(); + RTCStatsReport::Create(0); signaling_report->AddStats(std::unique_ptr( new RTCTestStats("SignalingThreadStats", timestamp_us))); AddPartialResults(signaling_report); @@ -284,7 +298,8 @@ class FakeRTCStatsCollector : public RTCStatsCollector, ++produced_on_worker_thread_; } - rtc::scoped_refptr worker_report = RTCStatsReport::Create(); + rtc::scoped_refptr worker_report = + RTCStatsReport::Create(0); worker_report->AddStats(std::unique_ptr( new RTCTestStats("WorkerThreadStats", timestamp_us))); AddPartialResults(worker_report); @@ -298,7 +313,7 @@ class FakeRTCStatsCollector : public RTCStatsCollector, } rtc::scoped_refptr network_report = - RTCStatsReport::Create(); + RTCStatsReport::Create(0); network_report->AddStats(std::unique_ptr( new RTCTestStats("NetworkThreadStats", timestamp_us))); AddPartialResults(network_report); @@ -960,6 +975,130 @@ TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) { } } +TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { + MockVoiceMediaChannel* voice_media_channel = new MockVoiceMediaChannel(); + cricket::VoiceChannel voice_channel( + test_->worker_thread(), test_->network_thread(), test_->media_engine(), + voice_media_channel, nullptr, "VoiceContentName", false); + + cricket::VoiceMediaInfo voice_media_info; + voice_media_info.senders.push_back(cricket::VoiceSenderInfo()); + voice_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); + voice_media_info.senders[0].local_stats[0].ssrc = 1; + voice_media_info.senders[0].packets_sent = 2; + voice_media_info.senders[0].bytes_sent = 3; + voice_media_info.senders[0].rtt_ms = 4500.0; + EXPECT_CALL(*voice_media_channel, GetStats(_)) + .WillOnce(DoAll(SetArgPointee<0>(voice_media_info), Return(true))); + + SessionStats session_stats; + session_stats.proxy_to_transport["VoiceContentName"] = "TransportName"; + session_stats.transport_stats["TransportName"].transport_name = + "TransportName"; + + // Make sure the associated |RTCTransportStats| is created. + cricket::TransportChannelStats channel_stats; + channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP; + cricket::ConnectionInfo connection_info; + connection_info.local_candidate = *CreateFakeCandidate( + "42.42.42.42", 42, "protocol", cricket::LOCAL_PORT_TYPE, 42).get(); + connection_info.remote_candidate = *CreateFakeCandidate( + "42.42.42.42", 42, "protocol", cricket::LOCAL_PORT_TYPE, 42).get(); + channel_stats.connection_infos.push_back(connection_info); + session_stats.transport_stats["TransportName"].channel_stats.push_back( + channel_stats); + + EXPECT_CALL(test_->session(), GetTransportStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats), Return(true))); + EXPECT_CALL(test_->session(), voice_channel()) + .WillRepeatedly(Return(&voice_channel)); + + rtc::scoped_refptr report = GetStatsReport(); + + RTCOutboundRTPStreamStats expected_audio( + "RTCOutboundRTPAudioStream_1", report->timestamp_us()); + expected_audio.ssrc = "1"; + expected_audio.is_remote = false; + expected_audio.media_type = "audio"; + expected_audio.transport_id = "RTCTransport_TransportName_" + + rtc::ToString<>(cricket::ICE_CANDIDATE_COMPONENT_RTP); + expected_audio.packets_sent = 2; + expected_audio.bytes_sent = 3; + expected_audio.round_trip_time = 4.5; + + ASSERT(report->Get(expected_audio.id())); + const RTCOutboundRTPStreamStats& audio = report->Get( + expected_audio.id())->cast_to(); + EXPECT_EQ(audio, expected_audio); + + EXPECT_TRUE(report->Get(*expected_audio.transport_id)); +} + +TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { + MockVideoMediaChannel* video_media_channel = new MockVideoMediaChannel(); + cricket::VideoChannel video_channel( + test_->worker_thread(), test_->network_thread(), video_media_channel, + nullptr, "VideoContentName", false); + + cricket::VideoMediaInfo video_media_info; + video_media_info.senders.push_back(cricket::VideoSenderInfo()); + video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); + video_media_info.senders[0].local_stats[0].ssrc = 1; + video_media_info.senders[0].firs_rcvd = 2; + video_media_info.senders[0].plis_rcvd = 3; + video_media_info.senders[0].nacks_rcvd = 4; + video_media_info.senders[0].packets_sent = 5; + video_media_info.senders[0].bytes_sent = 6; + video_media_info.senders[0].rtt_ms = 7500.0; + EXPECT_CALL(*video_media_channel, GetStats(_)) + .WillOnce(DoAll(SetArgPointee<0>(video_media_info), Return(true))); + + SessionStats session_stats; + session_stats.proxy_to_transport["VideoContentName"] = "TransportName"; + session_stats.transport_stats["TransportName"].transport_name = + "TransportName"; + + // Make sure the associated |RTCTransportStats| is created. + cricket::TransportChannelStats channel_stats; + channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP; + cricket::ConnectionInfo connection_info; + connection_info.local_candidate = *CreateFakeCandidate( + "42.42.42.42", 42, "protocol", cricket::LOCAL_PORT_TYPE, 42).get(); + connection_info.remote_candidate = *CreateFakeCandidate( + "42.42.42.42", 42, "protocol", cricket::LOCAL_PORT_TYPE, 42).get(); + channel_stats.connection_infos.push_back(connection_info); + session_stats.transport_stats["TransportName"].channel_stats.push_back( + channel_stats); + + EXPECT_CALL(test_->session(), GetTransportStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats), Return(true))); + EXPECT_CALL(test_->session(), video_channel()) + .WillRepeatedly(Return(&video_channel)); + + rtc::scoped_refptr report = GetStatsReport(); + + RTCOutboundRTPStreamStats expected_video( + "RTCOutboundRTPVideoStream_1", report->timestamp_us()); + expected_video.ssrc = "1"; + expected_video.is_remote = false; + expected_video.media_type = "video"; + expected_video.transport_id = "RTCTransport_TransportName_" + + rtc::ToString<>(cricket::ICE_CANDIDATE_COMPONENT_RTP); + expected_video.fir_count = 2; + expected_video.pli_count = 3; + expected_video.nack_count = 4; + expected_video.packets_sent = 5; + expected_video.bytes_sent = 6; + expected_video.round_trip_time = 7.5; + + ASSERT(report->Get(expected_video.id())); + const RTCOutboundRTPStreamStats& video = report->Get( + expected_video.id())->cast_to(); + EXPECT_EQ(video, expected_video); + + EXPECT_TRUE(report->Get(*expected_video.transport_id)); +} + TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { std::unique_ptr rtp_local_candidate = CreateFakeCandidate( "42.42.42.42", 42, "protocol", cricket::LOCAL_PORT_TYPE, 42); diff --git a/webrtc/api/stats/rtcstats.h b/webrtc/api/stats/rtcstats.h index 0876080b00..d23e928fad 100644 --- a/webrtc/api/stats/rtcstats.h +++ b/webrtc/api/stats/rtcstats.h @@ -71,8 +71,8 @@ class RTCStats { bool operator==(const RTCStats& other) const; bool operator!=(const RTCStats& other) const; - // Creates a human readable string representation of the report, listing all - // of its members (names and values). + // Creates a human readable string representation of the stats object, listing + // all of its members (names and values). std::string ToString() const; // Downcasts the stats object to an |RTCStats| subclass |T|. DCHECKs that the diff --git a/webrtc/api/stats/rtcstats_objects.h b/webrtc/api/stats/rtcstats_objects.h index 3684e278c3..232e66b388 100644 --- a/webrtc/api/stats/rtcstats_objects.h +++ b/webrtc/api/stats/rtcstats_objects.h @@ -82,7 +82,7 @@ class RTCDataChannelStats final : public RTCStats { // https://w3c.github.io/webrtc-stats/#candidatepair-dict* // TODO(hbos): Finish implementation. Tracking bug crbug.com/633550 -class RTCIceCandidatePairStats : public RTCStats { +class RTCIceCandidatePairStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); @@ -202,6 +202,62 @@ class RTCPeerConnectionStats final : public RTCStats { RTCStatsMember data_channels_closed; }; +// https://w3c.github.io/webrtc-stats/#streamstats-dict* +// TODO(hbos): Finish implementation. Tracking bug crbug.com/657854 +class RTCRTPStreamStats : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(); + + RTCRTPStreamStats(const RTCRTPStreamStats& other); + ~RTCRTPStreamStats() override; + + RTCStatsMember ssrc; + // TODO(hbos): When the remote case is supported |RTCStatsCollector| needs to + // set this. crbug.com/657855, 657856 + RTCStatsMember associate_stats_id; + // TODO(hbos): Remote case not supported by |RTCStatsCollector|. + // crbug.com/657855, 657856 + RTCStatsMember is_remote; // = false + RTCStatsMember media_type; + // TODO(hbos): Not collected by |RTCStatsCollector|. crbug.com/657854, 659137 + RTCStatsMember media_track_id; + RTCStatsMember transport_id; + // TODO(hbos): Not collected by |RTCStatsCollector|. crbug.com/657854, 659117 + RTCStatsMember codec_id; + // FIR and PLI counts are only defined for |media_type == "video"|. + RTCStatsMember fir_count; + RTCStatsMember pli_count; + // TODO(hbos): NACK count should be collected by |RTCStatsCollector| for both + // audio and video but is only defined in the "video" case. crbug.com/657856 + RTCStatsMember nack_count; + // TODO(hbos): Not collected by |RTCStatsCollector|. crbug.com/657854 + // SLI count is only defined for |media_type == "video"|. + RTCStatsMember sli_count; + + protected: + RTCRTPStreamStats(const std::string& id, int64_t timestamp_us); + RTCRTPStreamStats(std::string&& id, int64_t timestamp_us); +}; + +// https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict* +// TODO(hbos): Finish implementation and support the remote case +// |is_remote = true|. Tracking bug crbug.com/657856 +class RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { + public: + WEBRTC_RTCSTATS_DECL(); + + RTCOutboundRTPStreamStats(const std::string& id, int64_t timestamp_us); + RTCOutboundRTPStreamStats(std::string&& id, int64_t timestamp_us); + RTCOutboundRTPStreamStats(const RTCOutboundRTPStreamStats& other); + ~RTCOutboundRTPStreamStats() override; + + RTCStatsMember packets_sent; + RTCStatsMember bytes_sent; + // TODO(hbos): Not collected by |RTCStatsCollector|. crbug.com/657856 + RTCStatsMember target_bitrate; + RTCStatsMember round_trip_time; +}; + // https://w3c.github.io/webrtc-stats/#transportstats-dict* class RTCTransportStats final : public RTCStats { public: diff --git a/webrtc/api/stats/rtcstatsreport.h b/webrtc/api/stats/rtcstatsreport.h index beb8650353..34f0d545ff 100644 --- a/webrtc/api/stats/rtcstatsreport.h +++ b/webrtc/api/stats/rtcstatsreport.h @@ -36,6 +36,7 @@ class RTCStatsReport : public rtc::RefCountInterface { ConstIterator& operator++(); ConstIterator& operator++(int); const RTCStats& operator*() const; + const RTCStats* operator->() const; bool operator==(const ConstIterator& other) const; bool operator!=(const ConstIterator& other) const; @@ -49,11 +50,12 @@ class RTCStatsReport : public rtc::RefCountInterface { StatsMap::const_iterator it_; }; - static rtc::scoped_refptr Create(); + static rtc::scoped_refptr Create(uint64_t timestamp_us); - RTCStatsReport(); + explicit RTCStatsReport(uint64_t timestamp_us); RTCStatsReport(const RTCStatsReport& other) = delete; + uint64_t timestamp_us() const { return timestamp_us_; } bool AddStats(std::unique_ptr stats); const RTCStats* Get(const std::string& id) const; size_t size() const { return stats_.size(); } @@ -77,11 +79,16 @@ class RTCStatsReport : public rtc::RefCountInterface { return stats_of_type; } + // Creates a human readable string representation of the report, listing all + // of its stats objects. + std::string ToString() const; + friend class rtc::RefCountedObject; private: ~RTCStatsReport() override; + uint64_t timestamp_us_; StatsMap stats_; }; diff --git a/webrtc/stats/rtcstats.cc b/webrtc/stats/rtcstats.cc index fb9740a12e..ef36666056 100644 --- a/webrtc/stats/rtcstats.cc +++ b/webrtc/stats/rtcstats.cc @@ -10,6 +10,8 @@ #include "webrtc/api/stats/rtcstats.h" +#include + #include "webrtc/base/stringencode.h" namespace webrtc { diff --git a/webrtc/stats/rtcstats_objects.cc b/webrtc/stats/rtcstats_objects.cc index 6a4203ed0e..3d1d369407 100644 --- a/webrtc/stats/rtcstats_objects.cc +++ b/webrtc/stats/rtcstats_objects.cc @@ -293,6 +293,92 @@ RTCPeerConnectionStats::RTCPeerConnectionStats( RTCPeerConnectionStats::~RTCPeerConnectionStats() { } +WEBRTC_RTCSTATS_IMPL(RTCRTPStreamStats, RTCStats, "rtp", + &ssrc, + &associate_stats_id, + &is_remote, + &media_type, + &media_track_id, + &transport_id, + &codec_id, + &fir_count, + &pli_count, + &nack_count, + &sli_count); + +RTCRTPStreamStats::RTCRTPStreamStats( + const std::string& id, int64_t timestamp_us) + : RTCRTPStreamStats(std::string(id), timestamp_us) { +} + +RTCRTPStreamStats::RTCRTPStreamStats( + std::string&& id, int64_t timestamp_us) + : RTCStats(std::move(id), timestamp_us), + ssrc("ssrc"), + associate_stats_id("associateStatsId"), + is_remote("isRemote", false), + media_type("mediaType"), + media_track_id("mediaTrackId"), + transport_id("transportId"), + codec_id("codecId"), + fir_count("firCount"), + pli_count("pliCount"), + nack_count("nackCount"), + sli_count("sliCount") { +} + +RTCRTPStreamStats::RTCRTPStreamStats( + const RTCRTPStreamStats& other) + : RTCStats(other.id(), other.timestamp_us()), + ssrc(other.ssrc), + associate_stats_id(other.associate_stats_id), + is_remote(other.is_remote), + media_type(other.media_type), + media_track_id(other.media_track_id), + transport_id(other.transport_id), + codec_id(other.codec_id), + fir_count(other.fir_count), + pli_count(other.pli_count), + nack_count(other.nack_count), + sli_count(other.sli_count) { +} + +RTCRTPStreamStats::~RTCRTPStreamStats() { +} + +WEBRTC_RTCSTATS_IMPL( + RTCOutboundRTPStreamStats, RTCRTPStreamStats, "outbound-rtp", + &packets_sent, + &bytes_sent, + &target_bitrate, + &round_trip_time); + +RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats( + const std::string& id, int64_t timestamp_us) + : RTCOutboundRTPStreamStats(std::string(id), timestamp_us) { +} + +RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats( + std::string&& id, int64_t timestamp_us) + : RTCRTPStreamStats(std::move(id), timestamp_us), + packets_sent("packetsSent"), + bytes_sent("bytesSent"), + target_bitrate("targetBitrate"), + round_trip_time("roundTripTime") { +} + +RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats( + const RTCOutboundRTPStreamStats& other) + : RTCRTPStreamStats(other), + packets_sent(other.packets_sent), + bytes_sent(other.bytes_sent), + target_bitrate(other.target_bitrate), + round_trip_time(other.round_trip_time) { +} + +RTCOutboundRTPStreamStats::~RTCOutboundRTPStreamStats() { +} + WEBRTC_RTCSTATS_IMPL(RTCTransportStats, RTCStats, "transport", &bytes_sent, &bytes_received, diff --git a/webrtc/stats/rtcstatsreport.cc b/webrtc/stats/rtcstatsreport.cc index 4be554daae..9e2244cecf 100644 --- a/webrtc/stats/rtcstatsreport.cc +++ b/webrtc/stats/rtcstatsreport.cc @@ -10,6 +10,8 @@ #include "webrtc/api/stats/rtcstatsreport.h" +#include + namespace webrtc { RTCStatsReport::ConstIterator::ConstIterator( @@ -40,6 +42,10 @@ const RTCStats& RTCStatsReport::ConstIterator::operator*() const { return *it_->second.get(); } +const RTCStats* RTCStatsReport::ConstIterator::operator->() const { + return it_->second.get(); +} + bool RTCStatsReport::ConstIterator::operator==( const RTCStatsReport::ConstIterator& other) const { return it_ == other.it_; @@ -50,12 +56,14 @@ bool RTCStatsReport::ConstIterator::operator!=( return !(*this == other); } -rtc::scoped_refptr RTCStatsReport::Create() { +rtc::scoped_refptr RTCStatsReport::Create( + uint64_t timestamp_us) { return rtc::scoped_refptr( - new rtc::RefCountedObject()); + new rtc::RefCountedObject(timestamp_us)); } -RTCStatsReport::RTCStatsReport() { +RTCStatsReport::RTCStatsReport(uint64_t timestamp_us) + : timestamp_us_(timestamp_us) { } RTCStatsReport::~RTCStatsReport() { @@ -92,4 +100,16 @@ RTCStatsReport::ConstIterator RTCStatsReport::end() const { stats_.cend()); } +std::string RTCStatsReport::ToString() const { + std::ostringstream oss; + ConstIterator it = begin(); + if (it != end()) { + oss << it->ToString(); + for (++it; it != end(); ++it) { + oss << '\n' << it->ToString(); + } + } + return oss.str(); +} + } // namespace webrtc diff --git a/webrtc/stats/rtcstatsreport_unittest.cc b/webrtc/stats/rtcstatsreport_unittest.cc index 3bfbd44cba..442adbe163 100644 --- a/webrtc/stats/rtcstatsreport_unittest.cc +++ b/webrtc/stats/rtcstatsreport_unittest.cc @@ -59,7 +59,8 @@ WEBRTC_RTCSTATS_IMPL(RTCTestStats3, RTCStats, "test-stats-3", &string); TEST(RTCStatsReport, AddAndGetStats) { - rtc::scoped_refptr report = RTCStatsReport::Create(); + rtc::scoped_refptr report = RTCStatsReport::Create(1337); + EXPECT_EQ(report->timestamp_us(), 1337u); EXPECT_EQ(report->size(), static_cast(0)); report->AddStats(std::unique_ptr(new RTCTestStats1("a0", 1))); report->AddStats(std::unique_ptr(new RTCTestStats1("a1", 2))); @@ -92,7 +93,8 @@ TEST(RTCStatsReport, AddAndGetStats) { } TEST(RTCStatsReport, StatsOrder) { - rtc::scoped_refptr report = RTCStatsReport::Create(); + rtc::scoped_refptr report = RTCStatsReport::Create(1337); + EXPECT_EQ(report->timestamp_us(), 1337u); report->AddStats(std::unique_ptr(new RTCTestStats1("C", 2))); report->AddStats(std::unique_ptr(new RTCTestStats1("D", 3))); report->AddStats(std::unique_ptr(new RTCTestStats2("B", 1))); @@ -109,11 +111,13 @@ TEST(RTCStatsReport, StatsOrder) { } TEST(RTCStatsReport, TakeMembersFrom) { - rtc::scoped_refptr a = RTCStatsReport::Create(); + rtc::scoped_refptr a = RTCStatsReport::Create(1337); + EXPECT_EQ(a->timestamp_us(), 1337u); a->AddStats(std::unique_ptr(new RTCTestStats1("B", 1))); a->AddStats(std::unique_ptr(new RTCTestStats1("C", 2))); a->AddStats(std::unique_ptr(new RTCTestStats1("E", 4))); - rtc::scoped_refptr b = RTCStatsReport::Create(); + rtc::scoped_refptr b = RTCStatsReport::Create(1338); + EXPECT_EQ(b->timestamp_us(), 1338u); b->AddStats(std::unique_ptr(new RTCTestStats1("A", 0))); b->AddStats(std::unique_ptr(new RTCTestStats1("D", 3))); b->AddStats(std::unique_ptr(new RTCTestStats1("F", 5)));