Add support for DataChannel and MediaStreamTrack stats
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Mar 2017 16:32:53 +0000 (16:32 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 24 Mar 2017 16:32:53 +0000 (16:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=170031

Patch by Youenn Fablet <youenn@apple.com> on 2017-03-24
Reviewed by Eric Carlson.

Source/WebCore:

Tests: webrtc/datachannel/datachannel-stats.html
       webrtc/video-mediastreamtrack-stats.html

Exposing libwebrtc stats through WebRTC stats API, gathered for data channel and media stream tracks.

* Modules/mediastream/RTCStatsReport.h:
(WebCore::RTCStatsReport::MediaStreamTrackStats::MediaStreamTrackStats):
(WebCore::RTCStatsReport::DataChannelStats::DataChannelStats):
* Modules/mediastream/RTCStatsReport.idl:
* Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp:
(WebCore::fillRTCMediaStreamTrackStats):
(WebCore::fillRTCDataChannelStats):
(WebCore::LibWebRTCMediaEndpoint::StatsCollector::OnStatsDelivered):

LayoutTests:

* webrtc/datachannel/datachannel-stats-expected.txt: Added.
* webrtc/datachannel/datachannel-stats.html: Added.
* webrtc/video-mediastreamtrack-stats-expected.txt: Added.
* webrtc/video-mediastreamtrack-stats.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@214350 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/webrtc/datachannel/datachannel-stats-expected.txt [new file with mode: 0644]
LayoutTests/webrtc/datachannel/datachannel-stats.html [new file with mode: 0644]
LayoutTests/webrtc/video-mediastreamtrack-stats-expected.txt [new file with mode: 0644]
LayoutTests/webrtc/video-mediastreamtrack-stats.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediastream/RTCStatsReport.h
Source/WebCore/Modules/mediastream/RTCStatsReport.idl
Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp

index 3cb3d17..a2cfeef 100644 (file)
@@ -1,3 +1,15 @@
+2017-03-24  Youenn Fablet  <youenn@apple.com>
+
+        Add support for DataChannel and MediaStreamTrack stats
+        https://bugs.webkit.org/show_bug.cgi?id=170031
+
+        Reviewed by Eric Carlson.
+
+        * webrtc/datachannel/datachannel-stats-expected.txt: Added.
+        * webrtc/datachannel/datachannel-stats.html: Added.
+        * webrtc/video-mediastreamtrack-stats-expected.txt: Added.
+        * webrtc/video-mediastreamtrack-stats.html: Added.
+
 2017-03-24  Chris Dumez  <cdumez@apple.com>
 
         Extend svg/animations/animations-paused-disconnected-iframe.html
diff --git a/LayoutTests/webrtc/datachannel/datachannel-stats-expected.txt b/LayoutTests/webrtc/datachannel/datachannel-stats-expected.txt
new file mode 100644 (file)
index 0000000..afbaf25
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Basic data channel exchange with stats 
+
diff --git a/LayoutTests/webrtc/datachannel/datachannel-stats.html b/LayoutTests/webrtc/datachannel/datachannel-stats.html
new file mode 100644 (file)
index 0000000..a84dc17
--- /dev/null
@@ -0,0 +1,87 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Testing basic data channel from offerer to receiver</title>
+    <script src="../../resources/testharness.js"></script>
+    <script src="../../resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script src ="../routines.js"></script>
+    <script>
+var localChannel;
+var remoteChannel;
+
+function receiveMessages(event) {
+    if (++counter === 1)
+        assert_equals(event.data, "one");
+    else if (counter === 2)
+        assert_equals(event.data, "two");
+    else if (counter === 3)
+        assert_equals(event.data, "three");
+    else if (counter === 4) {
+        assert_equals(event.data, "four");
+        finishTest();
+    } else
+        assert_unreached();
+}
+
+function sendMessages(channel)
+{
+    channel.send("one");
+    channel.send("two");
+    channel.send("three");
+    channel.send("four");
+}
+
+function getDataChannelStats(connection)
+{
+    return connection.getStats().then((report) => {
+        var stats;
+        report.forEach((statItem) => {
+            if (statItem.type === "data-channel") {
+                stats = statItem;
+            }
+        });
+        return stats;
+    });
+}
+
+var finishTest;
+promise_test((test) => {
+    counter = 0;
+    return new Promise((resolve, reject) => {
+        if (window.internals)
+            internals.useMockRTCPeerConnectionFactory("TwoRealPeerConnections");
+
+        var localConnection, remoteConnection;
+        finishTest = () => {
+            getDataChannelStats(localConnection).then((stats) => {
+                stats.id = "id";
+                stats.timestamp = 1;
+                assert_equals(JSON.stringify(stats), '{"id":"id","timestamp":1,"type":"data-channel","bytesReceived":0,"bytesSent":15,"datachannelid":1,"label":"sendDataChannel","messagesReceived":0,"messagesSent":4,"protocol":"","state":"open"}');
+                return getDataChannelStats(remoteConnection);
+            }).then((stats) => {
+                stats.id = "id";
+                stats.timestamp = 1;
+                assert_equals(JSON.stringify(stats), '{"id":"id","timestamp":1,"type":"data-channel","bytesReceived":15,"bytesSent":0,"datachannelid":1,"label":"sendDataChannel","messagesReceived":4,"messagesSent":0,"protocol":"","state":"open"}');
+                 resolve();
+            });
+        };
+
+        createConnections((connection) => {
+            localConnection = connection;
+            localChannel = localConnection.createDataChannel('sendDataChannel');
+            localChannel.onopen = () => { sendMessages(localChannel) };
+        }, (connection) => {
+            remoteConnection = connection;
+            remoteConnection.ondatachannel = (event) => {
+                remoteChannel = event.channel;
+                remoteChannel.onmessage = receiveMessages;
+            };
+        });
+    });
+}, "Basic data channel exchange with stats");
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/webrtc/video-mediastreamtrack-stats-expected.txt b/LayoutTests/webrtc/video-mediastreamtrack-stats-expected.txt
new file mode 100644 (file)
index 0000000..efac2e6
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Basic video media stream track stats 
+
diff --git a/LayoutTests/webrtc/video-mediastreamtrack-stats.html b/LayoutTests/webrtc/video-mediastreamtrack-stats.html
new file mode 100644 (file)
index 0000000..bb75627
--- /dev/null
@@ -0,0 +1,69 @@
+<!doctype html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Testing basic video exchange from offerer to receiver</title>
+        <script src="../resources/testharness.js"></script>
+        <script src="../resources/testharnessreport.js"></script>
+    </head>
+    <body>
+        <script src ="routines.js"></script>
+        <script>
+function getTrackStats(connection)
+{
+    return connection.getStats().then((report) => {
+        var stats;
+        report.forEach((statItem) => {
+            if (statItem.type === "track") {
+                stats = statItem;
+            }
+        });
+        return stats;
+    });
+}
+
+var firstConnection, secondConnection;
+promise_test((test) => {
+    if (window.testRunner)
+        testRunner.setUserMediaPermission(true);
+
+    var localStream, remoteStream;
+    return navigator.mediaDevices.getUserMedia({ video: true}).then((stream) => {
+        localStream = stream;
+        return new Promise((resolve, reject) => {
+            if (window.internals)
+                internals.useMockRTCPeerConnectionFactory("TwoRealPeerConnections");
+
+            createConnections((connection) => {
+                firstConnection = connection;
+                firstConnection.addTrack(stream.getVideoTracks()[0], stream);
+            }, (connection) => {
+                secondConnection = connection;
+                secondConnection.ontrack = (trackEvent) => {
+                    remoteStream = trackEvent.streams[0];
+                    resolve();
+                };
+            });
+            setTimeout(() => reject("Test timed out"), 5000);
+        });
+    }).then(() => {
+        return getTrackStats(secondConnection);
+    }).then((stats) => {
+        assert_true(!!stats, "tracks stats should not be null or undefined");
+        stats.id = "id";
+        stats.timestamp = 1;
+        stats.trackIdentifier = "trackid";
+        assert_equals(JSON.stringify(stats), '{"id":"id","timestamp":1,"type":"track","audioLevel":0,"detached":false,"echoReturnLoss":0,"echoReturnLossEnhancement":0,"ended":false,"frameHeight":0,"frameWidth":0,"framesCorrupted":0,"framesDecoded":0,"framesDropped":0,"framesPerSecond":0,"framesReceived":0,"framesSent":0,"fullFramesLost":0,"partialFramesLost":0,"remoteSource":true,"trackIdentifier":"trackid"}');
+        return waitFor(1000);
+    }).then(() => {
+        return getTrackStats(secondConnection);
+    }).then((stats) => {
+        assert_equals(stats.frameHeight, 480);
+        assert_equals(stats.frameWidth, 640);
+        assert_true(stats.framesDecoded > 0);
+        assert_true(stats.framesReceived > 0);
+    });
+}, "Basic video media stream track stats");
+        </script>
+    </body>
+</html>
index 4e1ae19..c848136 100644 (file)
@@ -1,5 +1,26 @@
 2017-03-24  Youenn Fablet  <youenn@apple.com>
 
+        Add support for DataChannel and MediaStreamTrack stats
+        https://bugs.webkit.org/show_bug.cgi?id=170031
+
+        Reviewed by Eric Carlson.
+
+        Tests: webrtc/datachannel/datachannel-stats.html
+               webrtc/video-mediastreamtrack-stats.html
+
+        Exposing libwebrtc stats through WebRTC stats API, gathered for data channel and media stream tracks.
+
+        * Modules/mediastream/RTCStatsReport.h:
+        (WebCore::RTCStatsReport::MediaStreamTrackStats::MediaStreamTrackStats):
+        (WebCore::RTCStatsReport::DataChannelStats::DataChannelStats):
+        * Modules/mediastream/RTCStatsReport.idl:
+        * Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp:
+        (WebCore::fillRTCMediaStreamTrackStats):
+        (WebCore::fillRTCDataChannelStats):
+        (WebCore::LibWebRTCMediaEndpoint::StatsCollector::OnStatsDelivered):
+
+2017-03-24  Youenn Fablet  <youenn@apple.com>
+
         Fix framesEncoded/framesDecoded RTC stats
         https://bugs.webkit.org/show_bug.cgi?id=170024
 
index 68f36e7..976caca 100644 (file)
@@ -116,6 +116,41 @@ public:
         unsigned long framesEncoded { 0 };
     };
 
+    struct MediaStreamTrackStats : Stats {
+        MediaStreamTrackStats() { type = RTCStatsReport::Type::Track; }
+
+        String trackIdentifier;
+        bool remoteSource { false };
+        bool ended { false };
+        bool detached { false };
+        unsigned long frameWidth { 0 };
+        unsigned long frameHeight { 0};
+        double framesPerSecond { 0 };
+        unsigned long framesSent { 0 };
+        unsigned long framesReceived { 0 };
+        unsigned long framesDecoded { 0 };
+        unsigned long framesDropped { 0 };
+        unsigned long framesCorrupted { 0 };
+        unsigned long partialFramesLost { 0 };
+        unsigned long fullFramesLost { 0 };
+        double audioLevel { 0 };
+        double echoReturnLoss { 0 };
+        double echoReturnLossEnhancement { 0 };
+    };
+
+    struct DataChannelStats : Stats {
+        DataChannelStats() { type = RTCStatsReport::Type::DataChannel; }
+        
+        String label;
+        String protocol;
+        long datachannelid { 0 };
+        String state;
+        unsigned long messagesSent { 0 };
+        unsigned long long bytesSent { 0 };
+        unsigned long messagesReceived { 0 };
+        unsigned long long bytesReceived { 0 };
+    };
+
 private:
     RTCStatsReport() = default;
 
index 0ced670..c5659bd 100644 (file)
@@ -70,8 +70,6 @@ dictionary RTCRTPStreamStats : RTCStats {
     unsigned long long qpSum;
 };
 
-// FIXME 169662: missing RTCCodecStats
-
 [ JSGenerateToJSObject ]
 dictionary RTCInboundRTPStreamStats : RTCRTPStreamStats {
     unsigned long packetsReceived;
@@ -101,10 +99,44 @@ dictionary RTCOutboundRTPStreamStats : RTCRTPStreamStats {
     unsigned long framesEncoded;
 };
 
+[ JSGenerateToJSObject ]
+dictionary RTCMediaStreamTrackStats : RTCStats {
+    DOMString trackIdentifier;
+    boolean remoteSource;
+    boolean ended;
+    boolean detached;
+    // FIXME: Add sequence<DOMString> ssrcIds;
+    unsigned long frameWidth;
+    unsigned long frameHeight;
+    double framesPerSecond;
+    unsigned long framesSent;
+    unsigned long framesReceived;
+    unsigned long framesDecoded;
+    unsigned long framesDropped;
+    unsigned long framesCorrupted;
+    unsigned long partialFramesLost;
+    unsigned long fullFramesLost;
+    double audioLevel;
+    double echoReturnLoss;
+    double echoReturnLossEnhancement;
+};
+
+[ JSGenerateToJSObject ]
+dictionary RTCDataChannelStats : RTCStats {
+    DOMString label;
+    DOMString protocol;
+    long datachannelid;
+    // FIXME: Switch state to RTCDataChannelState
+    DOMString state;
+    unsigned long messagesSent;
+    unsigned long long bytesSent;
+    unsigned long messagesReceived;
+    unsigned long long bytesReceived;
+};
+
+// FIXME 169662: missing RTCCodecStats
 // FIXME 169662: missing RTCPeerConnectionStats
 // FIXME 169662: missing RTCMediaStreamStats
-// FIXME 169662: missing RTCMediaStreamTrackStats
-// FIXME 169662: missing RTCDataChannelStats
 // FIXME 169662: missing RTCTransportStats
 // FIXME 169662: missing RTCIceCandidateStats
 // FIXME 169662: missing RTCIceCandidatePairStats
index b08733e..04c602e 100644 (file)
@@ -323,6 +323,65 @@ static inline void fillOutboundRTPStreamStats(RTCStatsReport::OutboundRTPStreamS
         stats.framesEncoded = *rtcStats.frames_encoded;
 }
 
+static inline void fillRTCMediaStreamTrackStats(RTCStatsReport::MediaStreamTrackStats& stats, const webrtc::RTCMediaStreamTrackStats& rtcStats)
+{
+    fillRTCStats(stats, rtcStats);
+    if (rtcStats.track_identifier.is_defined())
+        stats.trackIdentifier = fromStdString(*rtcStats.track_identifier);
+    if (rtcStats.remote_source.is_defined())
+        stats.remoteSource = *rtcStats.remote_source;
+    if (rtcStats.ended.is_defined())
+        stats.ended = *rtcStats.ended;
+    if (rtcStats.detached.is_defined())
+        stats.detached = *rtcStats.detached;
+    if (rtcStats.frame_width.is_defined())
+        stats.frameWidth = *rtcStats.frame_width;
+    if (rtcStats.frame_height.is_defined())
+        stats.frameHeight = *rtcStats.frame_height;
+    if (rtcStats.frames_per_second.is_defined())
+        stats.framesPerSecond = *rtcStats.frames_per_second;
+    if (rtcStats.frames_sent.is_defined())
+        stats.framesSent = *rtcStats.frames_sent;
+    if (rtcStats.frames_received.is_defined())
+        stats.framesReceived = *rtcStats.frames_received;
+    if (rtcStats.frames_decoded.is_defined())
+        stats.framesDecoded = *rtcStats.frames_decoded;
+    if (rtcStats.frames_dropped.is_defined())
+        stats.framesDropped = *rtcStats.frames_dropped;
+    if (rtcStats.partial_frames_lost.is_defined())
+        stats.partialFramesLost = *rtcStats.partial_frames_lost;
+    if (rtcStats.full_frames_lost.is_defined())
+        stats.fullFramesLost = *rtcStats.full_frames_lost;
+    if (rtcStats.audio_level.is_defined())
+        stats.audioLevel = *rtcStats.audio_level;
+    if (rtcStats.echo_return_loss.is_defined())
+        stats.echoReturnLoss = *rtcStats.echo_return_loss;
+    if (rtcStats.echo_return_loss_enhancement.is_defined())
+        stats.echoReturnLossEnhancement = *rtcStats.echo_return_loss_enhancement;
+}
+
+static inline void fillRTCDataChannelStats(RTCStatsReport::DataChannelStats& stats, const webrtc::RTCDataChannelStats& rtcStats)
+{
+    fillRTCStats(stats, rtcStats);
+    
+    if (rtcStats.label.is_defined())
+        stats.label = fromStdString(*rtcStats.label);
+    if (rtcStats.protocol.is_defined())
+        stats.protocol = fromStdString(*rtcStats.protocol);
+    if (rtcStats.datachannelid.is_defined())
+        stats.datachannelid = *rtcStats.datachannelid;
+    if (rtcStats.state.is_defined())
+        stats.state = fromStdString(*rtcStats.state);
+    if (rtcStats.messages_sent.is_defined())
+        stats.messagesSent = *rtcStats.messages_sent;
+    if (rtcStats.bytes_sent.is_defined())
+        stats.bytesSent = *rtcStats.bytes_sent;
+    if (rtcStats.messages_received.is_defined())
+        stats.messagesReceived = *rtcStats.messages_received;
+    if (rtcStats.bytes_received.is_defined())
+        stats.bytesReceived = *rtcStats.bytes_received;
+}
+
 void LibWebRTCMediaEndpoint::StatsCollector::OnStatsDelivered(const rtc::scoped_refptr<const webrtc::RTCStatsReport>& rtcReport)
 {
     callOnMainThread([protectedThis = rtc::scoped_refptr<LibWebRTCMediaEndpoint::StatsCollector>(this), rtcReport] {
@@ -338,13 +397,18 @@ void LibWebRTCMediaEndpoint::StatsCollector::OnStatsDelivered(const rtc::scoped_
                 RTCStatsReport::InboundRTPStreamStats stats;
                 fillInboundRTPStreamStats(stats, static_cast<const webrtc::RTCInboundRTPStreamStats&>(rtcStats));
                 report->addStats<IDLDictionary<RTCStatsReport::InboundRTPStreamStats>>(WTFMove(stats));
-                return;
-            }
-            if (rtcStats.type() == webrtc::RTCOutboundRTPStreamStats::kType) {
+            } else if (rtcStats.type() == webrtc::RTCOutboundRTPStreamStats::kType) {
                 RTCStatsReport::OutboundRTPStreamStats stats;
                 fillOutboundRTPStreamStats(stats, static_cast<const webrtc::RTCOutboundRTPStreamStats&>(rtcStats));
                 report->addStats<IDLDictionary<RTCStatsReport::OutboundRTPStreamStats>>(WTFMove(stats));
-                return;
+            } else if (rtcStats.type() == webrtc::RTCMediaStreamTrackStats::kType) {
+                RTCStatsReport::MediaStreamTrackStats stats;
+                fillRTCMediaStreamTrackStats(stats, static_cast<const webrtc::RTCMediaStreamTrackStats&>(rtcStats));
+                report->addStats<IDLDictionary<RTCStatsReport::MediaStreamTrackStats>>(WTFMove(stats));
+            } else if (rtcStats.type() == webrtc::RTCDataChannelStats::kType) {
+                RTCStatsReport::DataChannelStats stats;
+                fillRTCDataChannelStats(stats, static_cast<const webrtc::RTCDataChannelStats&>(rtcStats));
+                report->addStats<IDLDictionary<RTCStatsReport::DataChannelStats>>(WTFMove(stats));
             }
         }
     });