From f1fdec42a99195b2c9c1966201690772e88b458e Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Fri, 25 Sep 2015 14:23:01 -0400 Subject: [PATCH] Bug 1207824: Add Telemetry for WebRTC call type, simultaneous tracks, and renegotiations r=bwc --- media/webrtc/signaling/src/jsep/JsepSession.h | 30 +++++- .../signaling/src/jsep/JsepSessionImpl.cpp | 2 + .../src/peerconnection/PeerConnectionImpl.cpp | 71 ++++++++++++++ .../src/peerconnection/PeerConnectionImpl.h | 6 ++ .../signaling/src/sdp/SdpMediaSection.h | 2 + toolkit/components/telemetry/Histograms.json | 92 +++++++++++++++++++ 6 files changed, 201 insertions(+), 2 deletions(-) diff --git a/media/webrtc/signaling/src/jsep/JsepSession.h b/media/webrtc/signaling/src/jsep/JsepSession.h index 7bc95984623..0f4d1a911ed 100644 --- a/media/webrtc/signaling/src/jsep/JsepSession.h +++ b/media/webrtc/signaling/src/jsep/JsepSession.h @@ -15,13 +15,13 @@ #include "signaling/src/jsep/JsepTransport.h" #include "signaling/src/sdp/Sdp.h" +#include "JsepTrack.h" namespace mozilla { // Forward declarations class JsepCodecDescription; class JsepTrack; -struct JsepTrackPair; enum JsepSignalingState { kJsepStateStable, @@ -57,7 +57,7 @@ class JsepSession { public: explicit JsepSession(const std::string& name) - : mName(name), mState(kJsepStateStable) + : mName(name), mState(kJsepStateStable), mNegotiations(0) { } virtual ~JsepSession() {} @@ -75,6 +75,11 @@ public: { return mState; } + virtual uint32_t + GetNegotiations() const + { + return mNegotiations; + } // Set up the ICE And DTLS data. virtual nsresult SetIceCredentials(const std::string& ufrag, @@ -167,9 +172,30 @@ public: virtual bool AllLocalTracksAreAssigned() const = 0; + void + CountTracks(uint16_t (&receiving)[SdpMediaSection::kMediaTypes], + uint16_t (&sending)[SdpMediaSection::kMediaTypes]) const + { + auto trackPairs = GetNegotiatedTrackPairs(); + + memset(receiving, 0, sizeof(receiving)); + memset(sending, 0, sizeof(sending)); + + for (auto& pair : trackPairs) { + if (pair.mReceiving) { + receiving[pair.mReceiving->GetMediaType()]++; + } + + if (pair.mSending) { + sending[pair.mSending->GetMediaType()]++; + } + } + } + protected: const std::string mName; JsepSignalingState mState; + uint32_t mNegotiations; }; } // namespace mozilla diff --git a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp index f72382659bd..e204d8b4e3f 100644 --- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp +++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp @@ -1299,6 +1299,8 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr& local, mNegotiatedTrackPairs = trackPairs; mGeneratedLocalDescription.reset(); + + mNegotiations++; return NS_OK; } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index bbd785259ec..156b769c67e 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -410,6 +410,8 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal) mAllowIceLinkLocal = Preferences::GetBool( "media.peerconnection.ice.link_local", false); #endif + memset(mMaxReceiving, 0, sizeof(mMaxReceiving)); + memset(mMaxSending, 0, sizeof(mMaxSending)); } PeerConnectionImpl::~PeerConnectionImpl() @@ -2486,6 +2488,57 @@ PeerConnectionImpl::PluginCrash(uint32_t aPluginID, return true; } +void +PeerConnectionImpl::RecordEndOfCallTelemetry() const +{ +#if !defined(MOZILLA_EXTERNAL_LINKAGE) + // Bitmask used for WEBRTC/LOOP_CALL_TYPE telemetry reporting + static const uint32_t kAudioTypeMask = 1; + static const uint32_t kVideoTypeMask = 2; + static const uint32_t kDataChannelTypeMask = 4; + + // Report end-of-call Telemetry + if (mJsepSession->GetNegotiations() > 0) { + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_RENEGOTIATIONS : + Telemetry::WEBRTC_RENEGOTIATIONS, + mJsepSession->GetNegotiations()-1); + } + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_MAX_VIDEO_SEND_TRACK : + Telemetry::WEBRTC_MAX_VIDEO_SEND_TRACK, + mMaxSending[SdpMediaSection::MediaType::kVideo]); + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_MAX_VIDEO_RECEIVE_TRACK : + Telemetry::WEBRTC_MAX_VIDEO_RECEIVE_TRACK, + mMaxReceiving[SdpMediaSection::MediaType::kVideo]); + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_MAX_AUDIO_SEND_TRACK : + Telemetry::WEBRTC_MAX_AUDIO_SEND_TRACK, + mMaxSending[SdpMediaSection::MediaType::kAudio]); + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_MAX_AUDIO_RECEIVE_TRACK : + Telemetry::WEBRTC_MAX_AUDIO_RECEIVE_TRACK, + mMaxReceiving[SdpMediaSection::MediaType::kAudio]); + // DataChannels appear in both Sending and Receiving + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_DATACHANNEL_NEGOTIATED : + Telemetry::WEBRTC_DATACHANNEL_NEGOTIATED, + mMaxSending[SdpMediaSection::MediaType::kApplication]); + // Enumerated/bitmask: 1 = Audio, 2 = Video, 4 = DataChannel + // A/V = 3, A/V/D = 7, etc + uint32_t type = 0; + if (mMaxSending[SdpMediaSection::MediaType::kAudio] || + mMaxReceiving[SdpMediaSection::MediaType::kAudio]) { + type = kAudioTypeMask; + } + if (mMaxSending[SdpMediaSection::MediaType::kVideo] || + mMaxReceiving[SdpMediaSection::MediaType::kVideo]) { + type |= kVideoTypeMask; + } + if (mMaxSending[SdpMediaSection::MediaType::kApplication]) { + type |= kDataChannelTypeMask; + } + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_CALL_TYPE : + Telemetry::WEBRTC_CALL_TYPE, + type); +#endif +} + nsresult PeerConnectionImpl::CloseInt() { @@ -2496,6 +2549,7 @@ PeerConnectionImpl::CloseInt() // transitioned to connected. As a bonus, this allows us to detect race // conditions where a stats dispatch happens right as the PC closes. RecordLongtermICEStatistics(); + RecordEndOfCallTelemetry(); CSFLogInfo(logTag, "%s: Closing PeerConnectionImpl %s; " "ending call", __FUNCTION__, mHandle.c_str()); if (mJsepSession) { @@ -2585,6 +2639,23 @@ PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState, fireNegotiationNeeded = true; } } + + // Telemetry: record info on the current state of streams/renegotiations/etc + // Note: this code gets run on rollbacks as well! + + // Update the max channels used with each direction for each type + uint16_t receiving[SdpMediaSection::kMediaTypes]; + uint16_t sending[SdpMediaSection::kMediaTypes]; + mJsepSession->CountTracks(receiving, sending); + for (size_t i = 0; i < SdpMediaSection::kMediaTypes; i++) { + if (mMaxReceiving[i] < receiving[i]) { + mMaxReceiving[i] = receiving[i]; + } + if (mMaxSending[i] < sending[i]) { + mMaxSending[i] = sending[i]; + } + } + } else { mShouldSuppressNegotiationNeeded = true; } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index 64ba857a4c8..b562834fd2c 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -550,6 +550,8 @@ public: bool PluginCrash(uint32_t aPluginID, const nsAString& aPluginName); + void RecordEndOfCallTelemetry() const; + nsresult InitializeDataChannel(); NS_IMETHODIMP_TO_ERRORRESULT_RETREF(nsDOMDataChannel, @@ -796,6 +798,10 @@ private: bool mShouldSuppressNegotiationNeeded; + // storage for Telemetry data + uint16_t mMaxReceiving[SdpMediaSection::kMediaTypes]; + uint16_t mMaxSending[SdpMediaSection::kMediaTypes]; + public: //these are temporary until the DataChannel Listen/Connect API is removed unsigned short listenPort; diff --git a/media/webrtc/signaling/src/sdp/SdpMediaSection.h b/media/webrtc/signaling/src/sdp/SdpMediaSection.h index fd0452afc13..5353faeb63f 100644 --- a/media/webrtc/signaling/src/sdp/SdpMediaSection.h +++ b/media/webrtc/signaling/src/sdp/SdpMediaSection.h @@ -25,6 +25,8 @@ class SdpMediaSection { public: enum MediaType { kAudio, kVideo, kText, kApplication, kMessage }; + // don't add to enum to avoid warnings about unhandled enum values + static const size_t kMediaTypes = static_cast(kMessage) + 1; enum Protocol { kRtpAvp, // RTP/AVP [RFC4566] diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index c8b0da7774d..242aea25404 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -6541,6 +6541,52 @@ "n_buckets": "25", "description": "Percentage of time spent in the Stressed load state in calls 5-30 seconds." }, + "WEBRTC_RENEGOTIATIONS": { + "expires_in_version": "never", + "kind": "linear", + "high": "21", + "n_buckets": "20", + "description": "Number of Renegotiations during each call" + }, + "WEBRTC_MAX_VIDEO_SEND_TRACK": { + "expires_in_version": "never", + "kind": "linear", + "high": "10", + "n_buckets": "9", + "description": "Number of Video tracks sent simultaneously" + }, + "WEBRTC_MAX_VIDEO_RECEIVE_TRACK": { + "expires_in_version": "never", + "kind": "linear", + "high": "20", + "n_buckets": "19", + "description": "Number of Video tracks received simultaneously" + }, + "WEBRTC_MAX_AUDIO_SEND_TRACK": { + "expires_in_version": "never", + "kind": "linear", + "high": "20", + "n_buckets": "19", + "description": "Number of Audio tracks sent simultaneously" + }, + "WEBRTC_MAX_AUDIO_RECEIVE_TRACK": { + "expires_in_version": "never", + "kind": "linear", + "high": "30", + "n_buckets": "29", + "description": "Number of Audio tracks received simultaneously" + }, + "WEBRTC_DATACHANNEL_NEGOTIATED": { + "expires_in_version": "never", + "kind": "boolean", + "description": "Was DataChannels negotiated" + }, + "WEBRTC_CALL_TYPE": { + "expires_in_version": "never", + "kind": "enumerated", + "n_values": "8", + "description": "Type of call: (Bitmask) Audio = 1, Video = 2, DataChannels = 4" + }, "DEVTOOLS_DEBUGGER_RDP_LOCAL_TRACERDETACH_MS": { "expires_in_version": "never", "kind": "exponential", @@ -9387,6 +9433,52 @@ "n_values": 8, "description": "Type for media in getUserMedia calls (0=Camera, 1=Screen, 2=Application, 3=Window, 4=Browser, 5=Microphone, 6=AudioCapture, 7=Other)" }, + "LOOP_RENEGOTIATIONS": { + "expires_in_version": "never", + "kind": "linear", + "high": "21", + "n_buckets": "20", + "description": "Number of Renegotiations during each call" + }, + "LOOP_MAX_VIDEO_SEND_TRACK": { + "expires_in_version": "never", + "kind": "linear", + "high": "10", + "n_buckets": "9", + "description": "Number of Video tracks sent simultaneously" + }, + "LOOP_MAX_VIDEO_RECEIVE_TRACK": { + "expires_in_version": "never", + "kind": "linear", + "high": "20", + "n_buckets": "19", + "description": "Number of Video tracks received simultaneously" + }, + "LOOP_MAX_AUDIO_SEND_TRACK": { + "expires_in_version": "never", + "kind": "linear", + "high": "20", + "n_buckets": "19", + "description": "Number of Audio tracks sent simultaneously" + }, + "LOOP_MAX_AUDIO_RECEIVE_TRACK": { + "expires_in_version": "never", + "kind": "linear", + "high": "30", + "n_buckets": "29", + "description": "Number of Audio tracks received simultaneously" + }, + "LOOP_DATACHANNEL_NEGOTIATED": { + "expires_in_version": "never", + "kind": "boolean", + "description": "Was DataChannels negotiated" + }, + "LOOP_CALL_TYPE": { + "expires_in_version": "never", + "kind": "enumerated", + "n_values": "8", + "description": "Type of call: (Bitmask) Audio = 1, Video = 2, DataChannels = 4" + }, "PERF_MONITORING_TEST_CPU_RESCHEDULING_PROPORTION_MOVED": { "alert_emails": ["dteller@mozilla.com"], "expires_in_version": "45",