diff --git a/api/umametrics.h b/api/umametrics.h index f885416f37..081b515c7c 100644 --- a/api/umametrics.h +++ b/api/umametrics.h @@ -45,6 +45,7 @@ enum PeerConnectionEnumCounterType { // The next 2 counters log the value of srtp_err_status_t defined in libsrtp. kEnumCounterSrtpUnprotectError, kEnumCounterSrtcpUnprotectError, + kEnumCounterUsagePattern, kPeerConnectionEnumCounterMax }; diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index 6557fca23b..58a35c460a 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -11,6 +11,7 @@ #include "pc/peerconnection.h" #include +#include #include #include #include @@ -1034,7 +1035,6 @@ bool PeerConnection::Initialize( RtpTransceiverProxyWithInternal::Create( signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_VIDEO))); } - return true; } @@ -1417,6 +1417,7 @@ PeerConnection::CreateSender( new AudioRtpSender(worker_thread(), static_cast(track.get()), stream_ids, stats_.get())); + NoteUsageEvent(UsageEvent::AUDIO_ADDED); } else { RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); RTC_DCHECK(!track || @@ -1426,6 +1427,7 @@ PeerConnection::CreateSender( new VideoRtpSender(worker_thread(), static_cast(track.get()), stream_ids)); + NoteUsageEvent(UsageEvent::VIDEO_ADDED); } sender->internal()->set_stream_ids(stream_ids); return sender; @@ -1440,11 +1442,13 @@ PeerConnection::CreateReceiver(cricket::MediaType media_type, receiver = RtpReceiverProxyWithInternal::Create( signaling_thread(), new AudioRtpReceiver(worker_thread(), receiver_id, {})); + NoteUsageEvent(UsageEvent::AUDIO_ADDED); } else { RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); receiver = RtpReceiverProxyWithInternal::Create( signaling_thread(), new VideoRtpReceiver(worker_thread(), receiver_id, {})); + NoteUsageEvent(UsageEvent::VIDEO_ADDED); } return receiver; } @@ -1711,7 +1715,7 @@ PeerConnection::CreateDataChannel( if (data_channel_type() == cricket::DCT_RTP || first_datachannel) { observer_->OnRenegotiationNeeded(); } - + NoteUsageEvent(UsageEvent::DATA_ADDED); return DataChannelProxy::Create(signaling_thread(), channel.get()); } @@ -1991,6 +1995,7 @@ void PeerConnection::SetLocalDescription( // Make UMA notes about what was agreed to. ReportNegotiatedSdpSemantics(*local_description()); } + NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_CALLED); } RTCError PeerConnection::ApplyLocalDescription( @@ -2233,6 +2238,7 @@ void PeerConnection::SetRemoteDescription( } observer->OnSetRemoteDescriptionComplete(RTCError::OK()); + NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_CALLED); } RTCError PeerConnection::ApplyRemoteDescription( @@ -2899,6 +2905,13 @@ bool PeerConnection::SetConfiguration(const RTCConfiguration& configuration, if (parse_error != RTCErrorType::NONE) { return SafeSetError(parse_error, error); } + // Note if STUN or TURN servers were supplied. + if (!stun_servers.empty()) { + NoteUsageEvent(UsageEvent::STUN_SERVER_ADDED); + } + if (!turn_servers.empty()) { + NoteUsageEvent(UsageEvent::TURN_SERVER_ADDED); + } // In theory this shouldn't fail. if (!network_thread()->Invoke( @@ -2958,6 +2971,7 @@ bool PeerConnection::AddIceCandidate( RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used."; return false; } + NoteUsageEvent(UsageEvent::REMOTE_CANDIDATE_ADDED); if (ready) { return UseCandidate(ice_candidate); @@ -3224,6 +3238,7 @@ void PeerConnection::Close() { stats_->UpdateStats(kStatsOutputLevelStandard); ChangeSignalingState(PeerConnectionInterface::kClosed); + NoteUsageEvent(UsageEvent::CLOSE_CALLED); for (auto transceiver : transceivers_) { transceiver->Stop(); @@ -3257,6 +3272,7 @@ void PeerConnection::Close() { // The event log must outlive call (and any other object that uses it). event_log_.reset(); }); + ReportUsagePattern(); } void PeerConnection::OnMessage(rtc::Message* msg) { @@ -3335,6 +3351,7 @@ void PeerConnection::CreateAudioReceiver( signaling_thread(), audio_receiver); GetAudioTransceiver()->internal()->AddReceiver(receiver); observer_->OnAddTrack(receiver, std::move(streams)); + NoteUsageEvent(UsageEvent::AUDIO_ADDED); } void PeerConnection::CreateVideoReceiver( @@ -3350,6 +3367,7 @@ void PeerConnection::CreateVideoReceiver( signaling_thread(), video_receiver); GetVideoTransceiver()->internal()->AddReceiver(receiver); observer_->OnAddTrack(receiver, std::move(streams)); + NoteUsageEvent(UsageEvent::VIDEO_ADDED); } // TODO(deadbeef): Keep RtpReceivers around even if track goes away in remote @@ -3485,6 +3503,7 @@ void PeerConnection::OnIceCandidate( if (IsClosed()) { return; } + NoteUsageEvent(UsageEvent::CANDIDATE_COLLECTED); observer_->OnIceCandidate(candidate.get()); } @@ -5223,6 +5242,7 @@ void PeerConnection::OnTransportControllerConnectionState( RTC_LOG(LS_INFO) << "Changing to ICE connected state because " "all transports are writable."; SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); + NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); break; case cricket::kIceConnectionCompleted: RTC_LOG(LS_INFO) << "Changing to ICE completed state because " @@ -5234,6 +5254,7 @@ void PeerConnection::OnTransportControllerConnectionState( SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); } SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted); + NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); if (metrics_observer()) { ReportTransportStats(); } @@ -5874,6 +5895,19 @@ void PeerConnection::ReportSdpFormatReceived( kSdpFormatReceivedMax); } +void PeerConnection::NoteUsageEvent(UsageEvent event) { + RTC_DCHECK_RUN_ON(signaling_thread()); + usage_event_accumulator_ |= static_cast(event); +} + +void PeerConnection::ReportUsagePattern() const { + RTC_DLOG(LS_INFO) << "Usage signature is " << usage_event_accumulator_; + if (uma_observer_) { + uma_observer_->IncrementSparseEnumCounter(kEnumCounterUsagePattern, + usage_event_accumulator_); + } +} + void PeerConnection::ReportNegotiatedSdpSemantics( const SessionDescriptionInterface& answer) { if (!uma_observer_) { diff --git a/pc/peerconnection.h b/pc/peerconnection.h index 754913152d..8bcee7a0e1 100644 --- a/pc/peerconnection.h +++ b/pc/peerconnection.h @@ -55,6 +55,20 @@ class PeerConnection : public PeerConnectionInternal, public rtc::MessageHandler, public sigslot::has_slots<> { public: + enum class UsageEvent : int { + TURN_SERVER_ADDED = 0x01, + STUN_SERVER_ADDED = 0x02, + DATA_ADDED = 0x04, + AUDIO_ADDED = 0x08, + VIDEO_ADDED = 0x10, + SET_LOCAL_DESCRIPTION_CALLED = 0x20, + SET_REMOTE_DESCRIPTION_CALLED = 0x40, + CANDIDATE_COLLECTED = 0x80, + REMOTE_CANDIDATE_ADDED = 0x100, + ICE_STATE_CONNECTED = 0x200, + CLOSE_CALLED = 0x400 + }; + explicit PeerConnection(PeerConnectionFactory* factory, std::unique_ptr event_log, std::unique_ptr call); @@ -857,6 +871,9 @@ class PeerConnection : public PeerConnectionInternal, void ReportNegotiatedCiphers(const cricket::TransportStats& stats, const std::set& media_types); + void NoteUsageEvent(UsageEvent event); + void ReportUsagePattern() const; + void OnSentPacket_w(const rtc::SentPacket& sent_packet); const std::string GetTransportName(const std::string& content_name); @@ -999,6 +1016,8 @@ class PeerConnection : public PeerConnectionInternal, // Member variables for caching global options. cricket::AudioOptions audio_options_; cricket::VideoOptions video_options_; + + int usage_event_accumulator_ = 0; }; } // namespace webrtc diff --git a/pc/peerconnection_integrationtest.cc b/pc/peerconnection_integrationtest.cc index 8e8edda1ea..ec7f5c1b94 100644 --- a/pc/peerconnection_integrationtest.cc +++ b/pc/peerconnection_integrationtest.cc @@ -173,6 +173,14 @@ int FindFirstMediaStatsIndexByKind( return -1; } +int MakeUsageFingerprint(std::set events) { + int signature = 0; + for (const auto it : events) { + signature |= static_cast(it); + } + return signature; +} + class SignalingMessageReceiver { public: virtual void ReceiveSdpMessage(SdpType type, const std::string& msg) = 0; @@ -4467,6 +4475,38 @@ TEST_P(PeerConnectionIntegrationInteropTest, ASSERT_TRUE(ExpectNewFrames(media_expectations)); } +// Test getting the usage fingerprint for a simple test case. +TEST_P(PeerConnectionIntegrationTest, UsageFingerprintHistogram) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + // Register UMA observer before signaling begins. + rtc::scoped_refptr caller_observer = + new rtc::RefCountedObject(); + caller()->pc()->RegisterUMAObserver(caller_observer); + rtc::scoped_refptr callee_observer = + new rtc::RefCountedObject(); + callee()->pc()->RegisterUMAObserver(callee_observer); + caller()->AddAudioTrack(); + caller()->AddVideoTrack(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(DtlsConnected(), kDefaultTimeout); + caller()->pc()->Close(); + callee()->pc()->Close(); + int expected_fingerprint = MakeUsageFingerprint( + {PeerConnection::UsageEvent::AUDIO_ADDED, + PeerConnection::UsageEvent::VIDEO_ADDED, + PeerConnection::UsageEvent::SET_LOCAL_DESCRIPTION_CALLED, + PeerConnection::UsageEvent::SET_REMOTE_DESCRIPTION_CALLED, + PeerConnection::UsageEvent::CANDIDATE_COLLECTED, + PeerConnection::UsageEvent::REMOTE_CANDIDATE_ADDED, + PeerConnection::UsageEvent::ICE_STATE_CONNECTED, + PeerConnection::UsageEvent::CLOSE_CALLED}); + EXPECT_TRUE(caller_observer->ExpectOnlySingleEnumCount( + webrtc::kEnumCounterUsagePattern, expected_fingerprint)); + EXPECT_TRUE(callee_observer->ExpectOnlySingleEnumCount( + webrtc::kEnumCounterUsagePattern, expected_fingerprint)); +} + INSTANTIATE_TEST_CASE_P( PeerConnectionIntegrationTest, PeerConnectionIntegrationInteropTest,