diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc index 5f60ea13ce..71f4725ae0 100644 --- a/webrtc/api/rtcstatscollector.cc +++ b/webrtc/api/rtcstatscollector.cc @@ -23,6 +23,8 @@ namespace webrtc { +namespace { + const char* CandidateTypeToRTCIceCandidateType(const std::string& type) { if (type == cricket::LOCAL_PORT_TYPE) return RTCIceCandidateType::kHost; @@ -36,6 +38,25 @@ const char* CandidateTypeToRTCIceCandidateType(const std::string& type) { return nullptr; } +const char* DataStateToRTCDataChannelState( + DataChannelInterface::DataState state) { + switch (state) { + case DataChannelInterface::kConnecting: + return RTCDataChannelState::kConnecting; + case DataChannelInterface::kOpen: + return RTCDataChannelState::kOpen; + case DataChannelInterface::kClosing: + return RTCDataChannelState::kClosing; + case DataChannelInterface::kClosed: + return RTCDataChannelState::kClosed; + default: + RTC_NOTREACHED(); + return nullptr; + } +} + +} // namespace + rtc::scoped_refptr RTCStatsCollector::Create( PeerConnection* pc, int64_t cache_lifetime_us) { return rtc::scoped_refptr( @@ -111,6 +132,7 @@ void RTCStatsCollector::ProducePartialResultsOnSignalingThread( ProduceIceCandidateAndPairStats_s(timestamp_us, session_stats, report.get()); } + ProduceDataChannelStats_s(timestamp_us, report.get()); ProducePeerConnectionStats_s(timestamp_us, report.get()); AddPartialResults(report); @@ -218,6 +240,28 @@ void RTCStatsCollector::ProduceCertificateStatsFromSSLCertificateAndChain_s( } } +void RTCStatsCollector::ProduceDataChannelStats_s( + int64_t timestamp_us, RTCStatsReport* report) const { + RTC_DCHECK(signaling_thread_->IsCurrent()); + for (const rtc::scoped_refptr& data_channel : + pc_->sctp_data_channels()) { + std::unique_ptr data_channel_stats( + new RTCDataChannelStats( + "RTCDataChannel_" + rtc::ToString<>(data_channel->id()), + timestamp_us)); + data_channel_stats->label = data_channel->label(); + data_channel_stats->protocol = data_channel->protocol(); + data_channel_stats->datachannelid = data_channel->id(); + data_channel_stats->state = + DataStateToRTCDataChannelState(data_channel->state()); + data_channel_stats->messages_sent = data_channel->messages_sent(); + data_channel_stats->bytes_sent = data_channel->bytes_sent(); + data_channel_stats->messages_received = data_channel->messages_received(); + data_channel_stats->bytes_received = data_channel->bytes_received(); + report->AddStats(std::move(data_channel_stats)); + } +} + void RTCStatsCollector::ProduceIceCandidateAndPairStats_s( int64_t timestamp_us, const SessionStats& session_stats, RTCStatsReport* report) const { @@ -337,4 +381,14 @@ void RTCStatsCollector::ProducePeerConnectionStats_s( report->AddStats(std::move(stats)); } +const char* CandidateTypeToRTCIceCandidateTypeForTesting( + const std::string& type) { + return CandidateTypeToRTCIceCandidateType(type); +} + +const char* DataStateToRTCDataChannelStateForTesting( + DataChannelInterface::DataState state) { + return DataStateToRTCDataChannelState(state); +} + } // namespace webrtc diff --git a/webrtc/api/rtcstatscollector.h b/webrtc/api/rtcstatscollector.h index 712f9e2949..ae04e61e56 100644 --- a/webrtc/api/rtcstatscollector.h +++ b/webrtc/api/rtcstatscollector.h @@ -14,6 +14,7 @@ #include #include +#include "webrtc/api/datachannelinterface.h" #include "webrtc/api/stats/rtcstats_objects.h" #include "webrtc/api/stats/rtcstatsreport.h" #include "webrtc/base/asyncinvoker.h" @@ -85,6 +86,9 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface { void ProduceCertificateStatsFromSSLCertificateAndChain_s( int64_t timestamp_us, const rtc::SSLCertificate& certificate, RTCStatsReport* report) const; + // Produces |RTCDataChannelStats|. + void ProduceDataChannelStats_s( + int64_t timestamp_us, RTCStatsReport* report) const; // Produces |RTCIceCandidatePairStats| and |RTCIceCandidateStats|. void ProduceIceCandidateAndPairStats_s( int64_t timestamp_us, const SessionStats& session_stats, @@ -116,8 +120,10 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface { rtc::scoped_refptr cached_report_; }; -// Helper function, exposed for unittests. -const char* CandidateTypeToRTCIceCandidateType(const std::string& type); +const char* CandidateTypeToRTCIceCandidateTypeForTesting( + const std::string& type); +const char* DataStateToRTCDataChannelStateForTesting( + DataChannelInterface::DataState state); } // namespace webrtc diff --git a/webrtc/api/rtcstatscollector_unittest.cc b/webrtc/api/rtcstatscollector_unittest.cc index c8eed9febb..fb07e0b29e 100644 --- a/webrtc/api/rtcstatscollector_unittest.cc +++ b/webrtc/api/rtcstatscollector_unittest.cc @@ -328,6 +328,10 @@ class RTCStatsCollectorTest : public testing::Test { rtc::scoped_refptr callback = StatsCallback::Create(); collector_->GetStatsReport(callback); EXPECT_TRUE_WAIT(callback->report(), kGetStatsReportTimeoutMs); + int64_t after = rtc::TimeUTCMicros(); + for (const RTCStats& stats : *callback->report()) { + EXPECT_LE(stats.timestamp_us(), after); + } return callback->report(); } @@ -347,7 +351,7 @@ class RTCStatsCollectorTest : public testing::Test { static_cast(candidate.address().port())); EXPECT_EQ(*candidate_stats->protocol, candidate.protocol()); EXPECT_EQ(*candidate_stats->candidate_type, - CandidateTypeToRTCIceCandidateType(candidate.type())); + CandidateTypeToRTCIceCandidateTypeForTesting(candidate.type())); EXPECT_EQ(*candidate_stats->priority, static_cast(candidate.priority())); // TODO(hbos): Define candidate_stats->url. crbug.com/632723 @@ -438,6 +442,27 @@ class RTCStatsCollectorTest : public testing::Test { } } + void ExpectReportContainsDataChannel( + const rtc::scoped_refptr& report, + const DataChannel& data_channel) { + const RTCStats* stats = report->Get("RTCDataChannel_" + + rtc::ToString<>(data_channel.id())); + EXPECT_TRUE(stats); + const RTCDataChannelStats& data_channel_stats = + stats->cast_to(); + EXPECT_EQ(*data_channel_stats.label, data_channel.label()); + EXPECT_EQ(*data_channel_stats.protocol, data_channel.protocol()); + EXPECT_EQ(*data_channel_stats.datachannelid, data_channel.id()); + EXPECT_EQ(*data_channel_stats.state, + DataStateToRTCDataChannelStateForTesting(data_channel.state())); + EXPECT_EQ(*data_channel_stats.messages_sent, data_channel.messages_sent()); + EXPECT_EQ(*data_channel_stats.bytes_sent, data_channel.bytes_sent()); + EXPECT_EQ(*data_channel_stats.messages_received, + data_channel.messages_received()); + EXPECT_EQ(*data_channel_stats.bytes_received, + data_channel.bytes_received()); + } + protected: rtc::scoped_refptr test_; rtc::scoped_refptr collector_; @@ -643,6 +668,44 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsChain) { ExpectReportContainsCertificateInfo(report, *remote_certinfo.get()); } +TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) { + test_->data_channels().push_back( + new MockDataChannel(0, DataChannelInterface::kConnecting)); + test_->data_channels().push_back( + new MockDataChannel(1, DataChannelInterface::kOpen)); + test_->data_channels().push_back( + new MockDataChannel(2, DataChannelInterface::kClosing)); + test_->data_channels().push_back( + new MockDataChannel(3, DataChannelInterface::kClosed)); + + rtc::scoped_refptr report = GetStatsReport(); + ExpectReportContainsDataChannel(report, *test_->data_channels()[0]); + ExpectReportContainsDataChannel(report, *test_->data_channels()[1]); + ExpectReportContainsDataChannel(report, *test_->data_channels()[2]); + ExpectReportContainsDataChannel(report, *test_->data_channels()[3]); + + test_->data_channels().clear(); + test_->data_channels().push_back( + new MockDataChannel(0, DataChannelInterface::kConnecting, + 1, 2, 3, 4)); + test_->data_channels().push_back( + new MockDataChannel(1, DataChannelInterface::kOpen, + 5, 6, 7, 8)); + test_->data_channels().push_back( + new MockDataChannel(2, DataChannelInterface::kClosing, + 9, 10, 11, 12)); + test_->data_channels().push_back( + new MockDataChannel(3, DataChannelInterface::kClosed, + 13, 14, 15, 16)); + + collector_->ClearCachedStatsReport(); + report = GetStatsReport(); + ExpectReportContainsDataChannel(report, *test_->data_channels()[0]); + ExpectReportContainsDataChannel(report, *test_->data_channels()[1]); + ExpectReportContainsDataChannel(report, *test_->data_channels()[2]); + ExpectReportContainsDataChannel(report, *test_->data_channels()[3]); +} + TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { // Candidates in the first transport stats. std::unique_ptr a_local_host = CreateFakeCandidate( @@ -759,15 +822,11 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { } TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) { - int64_t before = rtc::TimeUTCMicros(); rtc::scoped_refptr report = GetStatsReport(); - int64_t after = rtc::TimeUTCMicros(); EXPECT_EQ(report->GetStatsOfType().size(), static_cast(1)) << "Expecting 1 RTCPeerConnectionStats."; const RTCStats* stats = report->Get("RTCPeerConnection"); EXPECT_TRUE(stats); - EXPECT_LE(before, stats->timestamp_us()); - EXPECT_LE(stats->timestamp_us(), after); { // Expected stats with no data channels const RTCPeerConnectionStats& pcstats = @@ -777,13 +836,13 @@ TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) { } test_->data_channels().push_back( - new MockDataChannel(DataChannelInterface::kConnecting)); + new MockDataChannel(0, DataChannelInterface::kConnecting)); test_->data_channels().push_back( - new MockDataChannel(DataChannelInterface::kOpen)); + new MockDataChannel(1, DataChannelInterface::kOpen)); test_->data_channels().push_back( - new MockDataChannel(DataChannelInterface::kClosing)); + new MockDataChannel(2, DataChannelInterface::kClosing)); test_->data_channels().push_back( - new MockDataChannel(DataChannelInterface::kClosed)); + new MockDataChannel(3, DataChannelInterface::kClosed)); collector_->ClearCachedStatsReport(); report = GetStatsReport(); diff --git a/webrtc/api/stats/rtcstats_objects.h b/webrtc/api/stats/rtcstats_objects.h index 4738fe89a8..ad6a39cdfe 100644 --- a/webrtc/api/stats/rtcstats_objects.h +++ b/webrtc/api/stats/rtcstats_objects.h @@ -17,6 +17,14 @@ namespace webrtc { +// https://w3c.github.io/webrtc-pc/#idl-def-rtcdatachannelstate +struct RTCDataChannelState { + static const char* kConnecting; + static const char* kOpen; + static const char* kClosing; + static const char* kClosed; +}; + // https://w3c.github.io/webrtc-stats/#dom-rtcstatsicecandidatepairstate struct RTCStatsIceCandidatePairState { static const char* kFrozen; @@ -27,7 +35,7 @@ struct RTCStatsIceCandidatePairState { static const char* kCancelled; }; -// https://www.w3.org/TR/webrtc/#rtcicecandidatetype-enum +// https://w3c.github.io/webrtc-pc/#rtcicecandidatetype-enum struct RTCIceCandidateType { static const char* kHost; static const char* kSrflx; @@ -129,6 +137,27 @@ class RTCCertificateStats final : public RTCStats { RTCStatsMember issuer_certificate_id; }; +// https://w3c.github.io/webrtc-stats/#dcstats-dict* +class RTCDataChannelStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(); + + RTCDataChannelStats(const std::string& id, int64_t timestamp_us); + RTCDataChannelStats(std::string&& id, int64_t timestamp_us); + RTCDataChannelStats(const RTCDataChannelStats& other); + ~RTCDataChannelStats() override; + + RTCStatsMember label; + RTCStatsMember protocol; + RTCStatsMember datachannelid; + // TODO(hbos): Support enum types? "RTCStatsMember"? + RTCStatsMember state; + RTCStatsMember messages_sent; + RTCStatsMember bytes_sent; + RTCStatsMember messages_received; + RTCStatsMember bytes_received; +}; + // https://w3c.github.io/webrtc-stats/#pcstats-dict* // TODO(hbos): Tracking bug crbug.com/636818 class RTCPeerConnectionStats final : public RTCStats { diff --git a/webrtc/api/test/mock_datachannel.h b/webrtc/api/test/mock_datachannel.h index a09964e852..1bb3984257 100644 --- a/webrtc/api/test/mock_datachannel.h +++ b/webrtc/api/test/mock_datachannel.h @@ -18,12 +18,35 @@ namespace webrtc { class MockDataChannel : public rtc::RefCountedObject { public: - explicit MockDataChannel(DataState state) + MockDataChannel(int id, DataState state) + : MockDataChannel(id, state, 0, 0, 0, 0) { + } + MockDataChannel( + int id, + DataState state, + uint32_t messages_sent, + uint64_t bytes_sent, + uint32_t messages_received, + uint64_t bytes_received) : rtc::RefCountedObject( nullptr, cricket::DCT_NONE, "MockDataChannel") { + EXPECT_CALL(*this, id()).WillRepeatedly(testing::Return(id)); EXPECT_CALL(*this, state()).WillRepeatedly(testing::Return(state)); + EXPECT_CALL(*this, messages_sent()).WillRepeatedly( + testing::Return(messages_sent)); + EXPECT_CALL(*this, bytes_sent()).WillRepeatedly( + testing::Return(bytes_sent)); + EXPECT_CALL(*this, messages_received()).WillRepeatedly( + testing::Return(messages_received)); + EXPECT_CALL(*this, bytes_received()).WillRepeatedly( + testing::Return(bytes_received)); } + MOCK_CONST_METHOD0(id, int()); MOCK_CONST_METHOD0(state, DataState()); + MOCK_CONST_METHOD0(messages_sent, uint32_t()); + MOCK_CONST_METHOD0(bytes_sent, uint64_t()); + MOCK_CONST_METHOD0(messages_received, uint32_t()); + MOCK_CONST_METHOD0(bytes_received, uint64_t()); }; } // namespace webrtc diff --git a/webrtc/stats/rtcstats_objects.cc b/webrtc/stats/rtcstats_objects.cc index 947b5647d7..3d82d09e4a 100644 --- a/webrtc/stats/rtcstats_objects.cc +++ b/webrtc/stats/rtcstats_objects.cc @@ -12,6 +12,11 @@ namespace webrtc { +const char* RTCDataChannelState::kConnecting = "connecting"; +const char* RTCDataChannelState::kOpen = "open"; +const char* RTCDataChannelState::kClosing = "closing"; +const char* RTCDataChannelState::kClosed = "closed"; + const char* RTCStatsIceCandidatePairState::kFrozen = "frozen"; const char* RTCStatsIceCandidatePairState::kWaiting = "waiting"; const char* RTCStatsIceCandidatePairState::kInProgress = "inprogress"; @@ -218,6 +223,50 @@ RTCCertificateStats::RTCCertificateStats( RTCCertificateStats::~RTCCertificateStats() { } +WEBRTC_RTCSTATS_IMPL(RTCDataChannelStats, RTCStats, "data-channel", + &label, + &protocol, + &datachannelid, + &state, + &messages_sent, + &bytes_sent, + &messages_received, + &bytes_received); + +RTCDataChannelStats::RTCDataChannelStats( + const std::string& id, int64_t timestamp_us) + : RTCDataChannelStats(std::string(id), timestamp_us) { +} + +RTCDataChannelStats::RTCDataChannelStats( + std::string&& id, int64_t timestamp_us) + : RTCStats(std::move(id), timestamp_us), + label("label"), + protocol("protocol"), + datachannelid("datachannelid"), + state("state"), + messages_sent("messagesSent"), + bytes_sent("bytesSent"), + messages_received("messagesReceived"), + bytes_received("bytesReceived") { +} + +RTCDataChannelStats::RTCDataChannelStats( + const RTCDataChannelStats& other) + : RTCStats(other.id(), other.timestamp_us()), + label(other.label), + protocol(other.protocol), + datachannelid(other.datachannelid), + state(other.state), + messages_sent(other.messages_sent), + bytes_sent(other.bytes_sent), + messages_received(other.messages_received), + bytes_received(other.bytes_received) { +} + +RTCDataChannelStats::~RTCDataChannelStats() { +} + WEBRTC_RTCSTATS_IMPL(RTCPeerConnectionStats, RTCStats, "peer-connection", &data_channels_opened, &data_channels_closed);