Add stats collection for the data channel.
BUG=1805
R=bemasc@chromium.org, hta@webrtc.org, pthatcher@webrtc.org, tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/34619004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8083 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/datachannelinterface.h b/talk/app/webrtc/datachannelinterface.h
index 5684cc2..eb8a0b7 100644
--- a/talk/app/webrtc/datachannelinterface.h
+++ b/talk/app/webrtc/datachannelinterface.h
@@ -35,6 +35,7 @@
#include "webrtc/base/basictypes.h"
#include "webrtc/base/buffer.h"
+#include "webrtc/base/checks.h"
#include "webrtc/base/refcount.h"
@@ -106,6 +107,21 @@
kClosed
};
+ static const char* DataStateString(DataState state) {
+ switch (state) {
+ case kConnecting:
+ return "connecting";
+ case kOpen:
+ return "open";
+ case kClosing:
+ return "closing";
+ case kClosed:
+ return "closed";
+ }
+ CHECK(false) << "Unknown DataChannel state: " << state;
+ return "";
+ }
+
virtual void RegisterObserver(DataChannelObserver* observer) = 0;
virtual void UnregisterObserver() = 0;
// The label attribute represents a label that can be used to distinguish this
diff --git a/talk/app/webrtc/mediastreamsignaling.h b/talk/app/webrtc/mediastreamsignaling.h
index d4b1be8..7b8cf97 100644
--- a/talk/app/webrtc/mediastreamsignaling.h
+++ b/talk/app/webrtc/mediastreamsignaling.h
@@ -162,6 +162,7 @@
public:
typedef std::map<std::string, rtc::scoped_refptr<DataChannel> >
RtpDataChannels;
+ typedef std::vector<rtc::scoped_refptr<DataChannel>> SctpDataChannels;
MediaStreamSignaling(rtc::Thread* signaling_thread,
MediaStreamSignalingObserver* stream_observer,
@@ -255,6 +256,10 @@
void OnDtlsRoleReadyForSctp(rtc::SSLRole role);
void OnRemoteSctpDataChannelClosed(uint32 sid);
+ const SctpDataChannels& sctp_data_channels() const {
+ return sctp_data_channels_;
+ }
+
private:
struct RemotePeerInfo {
RemotePeerInfo()
@@ -392,8 +397,6 @@
int last_allocated_sctp_even_sid_;
int last_allocated_sctp_odd_sid_;
- typedef std::vector<rtc::scoped_refptr<DataChannel> > SctpDataChannels;
-
RtpDataChannels rtp_data_channels_;
SctpDataChannels sctp_data_channels_;
};
diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc
index 1e3a1d9..05695c0 100644
--- a/talk/app/webrtc/statscollector.cc
+++ b/talk/app/webrtc/statscollector.cc
@@ -403,7 +403,8 @@
}
StatsCollector::StatsCollector(WebRtcSession* session)
- : session_(session), stats_gathering_started_(0) {
+ : session_(session),
+ stats_gathering_started_(0) {
ASSERT(session_);
}
@@ -516,6 +517,7 @@
ExtractSessionInfo();
ExtractVoiceInfo();
ExtractVideoInfo(level);
+ ExtractDataInfo();
}
}
@@ -756,14 +758,16 @@
channel_report->timestamp = stats_gathering_started_;
channel_report->AddValue(StatsReport::kStatsValueNameComponent,
channel_iter->component);
- if (!local_cert_report_id.empty())
+ if (!local_cert_report_id.empty()) {
channel_report->AddValue(
StatsReport::kStatsValueNameLocalCertificateId,
local_cert_report_id);
- if (!remote_cert_report_id.empty())
+ }
+ if (!remote_cert_report_id.empty()) {
channel_report->AddValue(
StatsReport::kStatsValueNameRemoteCertificateId,
remote_cert_report_id);
+ }
for (size_t i = 0;
i < channel_iter->connection_infos.size();
++i) {
@@ -880,6 +884,22 @@
}
}
+void StatsCollector::ExtractDataInfo() {
+ ASSERT(session_->signaling_thread()->IsCurrent());
+
+ for (const auto& dc :
+ session_->mediastream_signaling()->sctp_data_channels()) {
+ StatsReport* report = reports_.ReplaceOrAddNew(
+ StatsId(StatsReport::kStatsReportTypeDataChannel, dc->label()));
+ report->type = StatsReport::kStatsReportTypeDataChannel;
+ report->AddValue(StatsReport::kStatsValueNameLabel, dc->label());
+ report->AddValue(StatsReport::kStatsValueNameDataChannelId, dc->id());
+ report->AddValue(StatsReport::kStatsValueNameProtocol, dc->protocol());
+ report->AddValue(StatsReport::kStatsValueNameState,
+ DataChannelInterface::DataStateString(dc->state()));
+ }
+}
+
StatsReport* StatsCollector::GetReport(const std::string& type,
const std::string& id,
TrackDirection direction) {
diff --git a/talk/app/webrtc/statscollector.h b/talk/app/webrtc/statscollector.h
index f8d19c1..45626af 100644
--- a/talk/app/webrtc/statscollector.h
+++ b/talk/app/webrtc/statscollector.h
@@ -36,6 +36,7 @@
#include <vector>
#include "talk/app/webrtc/mediastreaminterface.h"
+#include "talk/app/webrtc/mediastreamsignaling.h"
#include "talk/app/webrtc/peerconnectioninterface.h"
#include "talk/app/webrtc/statstypes.h"
#include "talk/app/webrtc/webrtcsession.h"
@@ -60,7 +61,7 @@
// The caller is responsible for ensuring that the session outlives the
// StatsCollector instance.
- explicit StatsCollector(WebRtcSession* session);
+ StatsCollector(WebRtcSession* session);
virtual ~StatsCollector();
// Adds a MediaStream with tracks that can be used as a |selector| in a call
@@ -119,6 +120,7 @@
// returns the leaf certificate's report's ID.
std::string AddCertificateReports(const rtc::SSLCertificate* cert);
+ void ExtractDataInfo();
void ExtractSessionInfo();
void ExtractVoiceInfo();
void ExtractVideoInfo(PeerConnectionInterface::StatsOutputLevel level);
diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc
index 539bf23..cba4003 100644
--- a/talk/app/webrtc/statscollector_unittest.cc
+++ b/talk/app/webrtc/statscollector_unittest.cc
@@ -31,11 +31,13 @@
#include "talk/app/webrtc/mediastream.h"
#include "talk/app/webrtc/mediastreaminterface.h"
+#include "talk/app/webrtc/mediastreamsignaling.h"
#include "talk/app/webrtc/mediastreamtrack.h"
+#include "talk/app/webrtc/test/fakedatachannelprovider.h"
+#include "talk/app/webrtc/test/fakemediastreamsignaling.h"
#include "talk/app/webrtc/videotrack.h"
#include "talk/media/base/fakemediaengine.h"
#include "talk/media/devices/fakedevicemanager.h"
-#include "webrtc/p2p/base/fakesession.h"
#include "talk/session/media/channelmanager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -43,6 +45,7 @@
#include "webrtc/base/fakesslidentity.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/network.h"
+#include "webrtc/p2p/base/fakesession.h"
using cricket::StatsOptions;
using testing::_;
@@ -81,6 +84,7 @@
}
MOCK_METHOD0(voice_channel, cricket::VoiceChannel*());
MOCK_METHOD0(video_channel, cricket::VideoChannel*());
+ MOCK_CONST_METHOD0(mediastream_signaling, const MediaStreamSignaling*());
// Libjingle uses "local" for a outgoing track, and "remote" for a incoming
// track.
MOCK_METHOD2(GetLocalTrackIdBySsrc, bool(uint32, std::string*));
@@ -438,9 +442,12 @@
new cricket::ChannelManager(media_engine_,
new cricket::FakeDeviceManager(),
rtc::Thread::Current())),
+ signaling_(channel_manager_.get()),
session_(channel_manager_.get()) {
// By default, we ignore session GetStats calls.
EXPECT_CALL(session_, GetStats(_)).WillRepeatedly(Return(false));
+ EXPECT_CALL(session_, mediastream_signaling()).WillRepeatedly(
+ Return(&signaling_));
}
~StatsCollectorTest() {}
@@ -587,7 +594,8 @@
const std::vector<std::string>& local_ders,
const rtc::FakeSSLCertificate& remote_cert,
const std::vector<std::string>& remote_ders) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
StatsReports reports; // returned values.
// Fake stats to process.
@@ -665,15 +673,53 @@
cricket::FakeMediaEngine* media_engine_;
rtc::scoped_ptr<cricket::ChannelManager> channel_manager_;
MockWebRtcSession session_;
+ FakeMediaStreamSignaling signaling_;
+ FakeDataChannelProvider data_channel_provider_;
cricket::SessionStats session_stats_;
rtc::scoped_refptr<webrtc::MediaStream> stream_;
rtc::scoped_refptr<webrtc::VideoTrack> track_;
rtc::scoped_refptr<FakeAudioTrack> audio_track_;
};
+// Verify that ExtractDataInfo populates reports.
+TEST_F(StatsCollectorTest, ExtractDataInfo) {
+ const std::string label = "hacks";
+ const int id = 31337;
+ const std::string state = DataChannelInterface::DataStateString(
+ DataChannelInterface::DataState::kConnecting);
+
+ EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull());
+ EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull());
+
+ InternalDataChannelInit config;
+ config.id = id;
+ signaling_.AddDataChannel(DataChannel::Create(
+ &data_channel_provider_, cricket::DCT_SCTP, label, config));
+ webrtc::StatsCollector stats(&session_);
+
+ stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
+
+ StatsReports reports;
+ stats.GetStats(NULL, &reports);
+ EXPECT_EQ(label, ExtractStatsValue(StatsReport::kStatsReportTypeDataChannel,
+ reports,
+ StatsReport::kStatsValueNameLabel));
+ EXPECT_EQ(rtc::ToString<int64>(id),
+ ExtractStatsValue(StatsReport::kStatsReportTypeDataChannel,
+ reports,
+ StatsReport::kStatsValueNameDataChannelId));
+ EXPECT_EQ(state, ExtractStatsValue(StatsReport::kStatsReportTypeDataChannel,
+ reports,
+ StatsReport::kStatsValueNameState));
+ EXPECT_EQ("", ExtractStatsValue(StatsReport::kStatsReportTypeDataChannel,
+ reports,
+ StatsReport::kStatsValueNameProtocol));
+}
+
// This test verifies that 64-bit counters are passed successfully.
TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
cricket::VideoChannel video_channel(rtc::Thread::Current(),
media_engine_, media_channel, &session_, "", false, NULL);
@@ -706,7 +752,8 @@
// Test that BWE information is reported via stats.
TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
cricket::VideoChannel video_channel(rtc::Thread::Current(),
media_engine_, media_channel, &session_, "", false, NULL);
@@ -750,7 +797,8 @@
// This test verifies that an object of type "googSession" always
// exists in the returned stats.
TEST_F(StatsCollectorTest, SessionObjectExists) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
StatsReports reports; // returned values.
EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull());
EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull());
@@ -764,7 +812,8 @@
// This test verifies that only one object of type "googSession" exists
// in the returned stats.
TEST_F(StatsCollectorTest, OnlyOneSessionObjectExists) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
StatsReports reports; // returned values.
EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull());
EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull());
@@ -782,7 +831,8 @@
// This test verifies that the empty track report exists in the returned stats
// without calling StatsCollector::UpdateStats.
TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
cricket::VideoChannel video_channel(rtc::Thread::Current(),
media_engine_, media_channel, &session_, "", false, NULL);
@@ -806,7 +856,8 @@
// This test verifies that the empty track report exists in the returned stats
// when StatsCollector::UpdateStats is called with ssrc stats.
TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
cricket::VideoChannel video_channel(rtc::Thread::Current(),
media_engine_, media_channel, &session_, "", false, NULL);
@@ -861,7 +912,8 @@
// This test verifies that an SSRC object has the identifier of a Transport
// stats object, and that this transport stats object exists in stats.
TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
// Ignore unused callback (logspam).
EXPECT_CALL(session_, GetTransport(_))
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
@@ -910,7 +962,8 @@
// This test verifies that a remote stats object will not be created for
// an outgoing SSRC where remote stats are not returned.
TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
// The content_name known by the video channel.
const std::string kVcName("vcname");
@@ -933,7 +986,8 @@
// This test verifies that a remote stats object will be created for
// an outgoing SSRC where stats are returned.
TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
// Ignore unused callback (logspam).
EXPECT_CALL(session_, GetTransport(_))
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
@@ -981,7 +1035,8 @@
// This test verifies that the empty track report exists in the returned stats
// when StatsCollector::UpdateStats is called with ssrc stats.
TEST_F(StatsCollectorTest, ReportsFromRemoteTrack) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
cricket::VideoChannel video_channel(rtc::Thread::Current(),
media_engine_, media_channel, &session_, "", false, NULL);
@@ -1026,7 +1081,8 @@
// This test verifies the Ice Candidate report should contain the correct
// information from local/remote candidates.
TEST_F(StatsCollectorTest, IceCandidateReport) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
StatsReports reports; // returned values.
const int local_port = 2000;
@@ -1157,7 +1213,8 @@
// This test verifies that the stats are generated correctly when no
// transport is present.
TEST_F(StatsCollectorTest, NoTransport) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
StatsReports reports; // returned values.
// Fake stats to process.
@@ -1203,7 +1260,8 @@
// This test verifies that the stats are generated correctly when the transport
// does not have any certificates.
TEST_F(StatsCollectorTest, NoCertificates) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
StatsReports reports; // returned values.
// Fake stats to process.
@@ -1271,7 +1329,8 @@
// Verifies the correct optons are passed to the VideoMediaChannel when using
// verbose output level.
TEST_F(StatsCollectorTest, StatsOutputLevelVerbose) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
cricket::VideoChannel video_channel(rtc::Thread::Current(),
media_engine_, media_channel, &session_, "", false, NULL);
@@ -1316,7 +1375,8 @@
// This test verifies that a local stats object can get statistics via
// AudioTrackInterface::GetStats() method.
TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
// Ignore unused callback (logspam).
EXPECT_CALL(session_, GetTransport(_))
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
@@ -1349,7 +1409,8 @@
// This test verifies that audio receive streams populate stats reports
// correctly.
TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
// Ignore unused callback (logspam).
EXPECT_CALL(session_, GetTransport(_))
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
@@ -1375,7 +1436,8 @@
// This test verifies that a local stats object won't update its statistics
// after a RemoveLocalAudioTrack() call.
TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
// Ignore unused callback (logspam).
EXPECT_CALL(session_, GetTransport(_))
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
@@ -1432,7 +1494,8 @@
// This test verifies that when ongoing and incoming audio tracks are using
// the same ssrc, they populate stats reports correctly.
TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
// Ignore unused callback (logspam).
EXPECT_CALL(session_, GetTransport(_))
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
@@ -1514,7 +1577,8 @@
// TODO(xians): Figure out if it is possible to encapsulate the setup and
// avoid duplication of code in test cases.
TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) {
- webrtc::StatsCollector stats(&session_); // Implementation under test.
+ webrtc::StatsCollector stats(&session_);
+
// Ignore unused callback (logspam).
EXPECT_CALL(session_, GetTransport(_))
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
diff --git a/talk/app/webrtc/statstypes.cc b/talk/app/webrtc/statstypes.cc
index fd96b10..b15dba5 100644
--- a/talk/app/webrtc/statstypes.cc
+++ b/talk/app/webrtc/statstypes.cc
@@ -41,6 +41,7 @@
const char StatsReport::kStatsReportTypeComponent[] = "googComponent";
const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair";
const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate";
+const char StatsReport::kStatsReportTypeDataChannel[] = "datachannel";
const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
@@ -109,14 +110,22 @@
return "packetsSent";
case kStatsValueNameBytesReceived:
return "bytesReceived";
+ case kStatsValueNameLabel:
+ return "label";
case kStatsValueNamePacketsReceived:
return "packetsReceived";
case kStatsValueNamePacketsLost:
return "packetsLost";
+ case kStatsValueNameProtocol:
+ return "protocol";
case kStatsValueNameTransportId:
return "transportId";
case kStatsValueNameSsrc:
return "ssrc";
+ case kStatsValueNameState:
+ return "state";
+ case kStatsValueNameDataChannelId:
+ return "datachannelid";
// 'goog' prefixed constants.
case kStatsValueNameActiveConnection:
diff --git a/talk/app/webrtc/statstypes.h b/talk/app/webrtc/statstypes.h
index bca771c..15a12d8 100644
--- a/talk/app/webrtc/statstypes.h
+++ b/talk/app/webrtc/statstypes.h
@@ -79,11 +79,14 @@
kStatsValueNameAudioOutputLevel,
kStatsValueNameBytesReceived,
kStatsValueNameBytesSent,
+ kStatsValueNameDataChannelId,
kStatsValueNamePacketsLost,
kStatsValueNamePacketsReceived,
kStatsValueNamePacketsSent,
+ kStatsValueNameProtocol,
kStatsValueNameReadable,
kStatsValueNameSsrc,
+ kStatsValueNameState,
kStatsValueNameTransportId,
// Internal StatsValue names.
@@ -143,6 +146,7 @@
kStatsValueNameIssuerId,
kStatsValueNameJitterBufferMs,
kStatsValueNameJitterReceived,
+ kStatsValueNameLabel,
kStatsValueNameLocalAddress,
kStatsValueNameLocalCandidateId,
kStatsValueNameLocalCandidateType,
@@ -262,6 +266,10 @@
// The id of StatsReport of type VideoBWE.
static const char kStatsReportVideoBweId[];
+
+ // A StatsReport of |type| = "datachannel" with statistics for a
+ // particular DataChannel.
+ static const char kStatsReportTypeDataChannel[];
};
// This class is provided for the cases where we need to keep
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index 1d8281c..e3f71a5 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -137,6 +137,10 @@
return data_channel_.get();
}
+ virtual const MediaStreamSignaling* mediastream_signaling() const {
+ return mediastream_signaling_;
+ }
+
void SetSdesPolicy(cricket::SecurePolicy secure_policy);
cricket::SecurePolicy SdesPolicy() const;