Index: webrtc/api/rtcstatscollector.cc |
diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc |
index 1c87dacbb16beb037e13f9eecbcf86503b576de9..45c3c403aedacaa5799f4f221210cccebe099ffb 100644 |
--- a/webrtc/api/rtcstatscollector.cc |
+++ b/webrtc/api/rtcstatscollector.cc |
@@ -17,6 +17,8 @@ |
#include "webrtc/api/peerconnection.h" |
#include "webrtc/api/webrtcsession.h" |
#include "webrtc/base/checks.h" |
+#include "webrtc/base/timeutils.h" |
+#include "webrtc/media/base/mediachannel.h" |
#include "webrtc/p2p/base/candidate.h" |
#include "webrtc/p2p/base/p2pconstants.h" |
#include "webrtc/p2p/base/port.h" |
@@ -41,6 +43,21 @@ std::string RTCTransportStatsIDFromTransportChannel( |
rtc::ToString<>(channel_component); |
} |
+std::string RTCTransportStatsIDFromBaseChannel( |
+ const ProxyTransportMap& proxy_to_transport, |
+ const cricket::BaseChannel& base_channel) { |
+ auto proxy_it = proxy_to_transport.find(base_channel.content_name()); |
+ if (proxy_it == proxy_to_transport.cend()) |
+ return ""; |
+ return RTCTransportStatsIDFromTransportChannel( |
+ proxy_it->second, cricket::ICE_CANDIDATE_COMPONENT_RTP); |
+} |
+ |
+std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) { |
+ return audio ? "RTCOutboundRTPAudioStream_" + rtc::ToString<>(ssrc) |
+ : "RTCOutboundRTPVideoStream_" + rtc::ToString<>(ssrc); |
+} |
+ |
const char* CandidateTypeToRTCIceCandidateType(const std::string& type) { |
if (type == cricket::LOCAL_PORT_TYPE) |
return RTCIceCandidateType::kHost; |
@@ -71,6 +88,49 @@ const char* DataStateToRTCDataChannelState( |
} |
} |
+void SetOutboundRTPStreamStatsFromMediaSenderInfo( |
+ const cricket::MediaSenderInfo& media_sender_info, |
+ RTCOutboundRTPStreamStats* outbound_stats) { |
+ RTC_DCHECK(outbound_stats); |
+ outbound_stats->ssrc = rtc::ToString<>(media_sender_info.ssrc()); |
+ // TODO(hbos): Support the remote case. crbug.com/657856 |
+ outbound_stats->is_remote = false; |
+ // TODO(hbos): Set |codec_id| when we have |RTCCodecStats|. Maybe relevant: |
+ // |media_sender_info.codec_name|. crbug.com/657854, 657856, 659117 |
+ outbound_stats->packets_sent = |
+ static_cast<uint32_t>(media_sender_info.packets_sent); |
+ outbound_stats->bytes_sent = |
+ static_cast<uint64_t>(media_sender_info.bytes_sent); |
+ outbound_stats->round_trip_time = |
+ static_cast<double>(media_sender_info.rtt_ms) / rtc::kNumMillisecsPerSec; |
+} |
+ |
+void SetOutboundRTPStreamStatsFromVoiceSenderInfo( |
+ const cricket::VoiceSenderInfo& voice_sender_info, |
+ RTCOutboundRTPStreamStats* outbound_stats) { |
+ SetOutboundRTPStreamStatsFromMediaSenderInfo( |
+ voice_sender_info, outbound_stats); |
+ outbound_stats->media_type = "audio"; |
+ // |fir_count|, |pli_count|, |nack_count| and |sli_count| are only valid for |
+ // video and are purposefully left undefined for audio. |
hbos
2016/10/26 10:26:11
Is this true? Even for NACK?
FIR and SLI are docu
Taylor Brandstetter
2016/10/27 21:44:36
NACK seems relevant to audio, PLI doesn't. I'll ma
hbos
2016/10/28 09:42:37
Acknowledged.
|
+} |
+ |
+void SetOutboundRTPStreamStatsFromVideoSenderInfo( |
+ const cricket::VideoSenderInfo& video_sender_info, |
+ RTCOutboundRTPStreamStats* outbound_stats) { |
+ SetOutboundRTPStreamStatsFromMediaSenderInfo( |
+ video_sender_info, outbound_stats); |
+ outbound_stats->media_type = "video"; |
+ outbound_stats->fir_count = |
+ static_cast<uint32_t>(video_sender_info.firs_rcvd); |
+ outbound_stats->pli_count = |
+ static_cast<uint32_t>(video_sender_info.plis_rcvd); |
+ outbound_stats->nack_count = |
+ static_cast<uint32_t>(video_sender_info.nacks_rcvd); |
+ outbound_stats->target_bitrate = |
+ static_cast<double>(video_sender_info.preferred_bitrate); |
hbos
2016/10/26 10:26:11
Is this the correct value? There is also bandwidth
Taylor Brandstetter
2016/10/27 21:44:36
I came to the same conclusion; it should be target
hbos
2016/10/28 09:42:37
Done. Note that this means that all video-RTCOutbo
Taylor Brandstetter
2016/10/28 17:31:34
Wait, that doesn't seem right. I didn't realize ta
hbos
2016/10/31 09:21:01
That will be easy to surface but I'll do it in a s
|
+} |
+ |
} // namespace |
rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create( |
@@ -140,7 +200,8 @@ void RTCStatsCollector::ClearCachedStatsReport() { |
void RTCStatsCollector::ProducePartialResultsOnSignalingThread( |
int64_t timestamp_us) { |
RTC_DCHECK(signaling_thread_->IsCurrent()); |
- rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(); |
+ rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( |
+ timestamp_us); |
SessionStats session_stats; |
if (pc_->session()->GetTransportStats(&session_stats)) { |
@@ -151,6 +212,8 @@ void RTCStatsCollector::ProducePartialResultsOnSignalingThread( |
timestamp_us, transport_cert_stats, report.get()); |
ProduceIceCandidateAndPairStats_s( |
timestamp_us, session_stats, report.get()); |
+ ProduceRTPStreamStats_s( |
+ timestamp_us, session_stats, report.get()); |
ProduceTransportStats_s( |
timestamp_us, session_stats, transport_cert_stats, report.get()); |
} |
@@ -163,9 +226,16 @@ void RTCStatsCollector::ProducePartialResultsOnSignalingThread( |
void RTCStatsCollector::ProducePartialResultsOnWorkerThread( |
int64_t timestamp_us) { |
RTC_DCHECK(worker_thread_->IsCurrent()); |
- rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(); |
+ rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( |
+ timestamp_us); |
// TODO(hbos): Gather stats on worker thread. |
+ // pc_->session()'s channels are owned by the signaling thread but there are |
+ // some stats that are gathered on the worker thread. Instead of a synchronous |
+ // invoke on "s->w" we could to the "w" work here asynchronously if it wasn't |
+ // for the ownership issue. Synchronous invokes in other places makes it |
+ // difficult to introduce locks without introducing deadlocks and the channels |
+ // are not reference counted. |
AddPartialResults(report); |
} |
@@ -173,9 +243,16 @@ void RTCStatsCollector::ProducePartialResultsOnWorkerThread( |
void RTCStatsCollector::ProducePartialResultsOnNetworkThread( |
int64_t timestamp_us) { |
RTC_DCHECK(network_thread_->IsCurrent()); |
- rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(); |
+ rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( |
+ timestamp_us); |
// TODO(hbos): Gather stats on network thread. |
+ // pc_->session()'s channels are owned by the signaling thread but there are |
+ // some stats that are gathered on the network thread. Instead of a |
+ // synchronous invoke on "s->n" we could to the "n" work here asynchronously |
+ // if it wasn't for the ownership issue. Synchronous invokes in other places |
+ // makes it difficult to introduce locks without introducing deadlocks and the |
+ // channels are not reference counted. |
AddPartialResults(report); |
} |
@@ -312,7 +389,7 @@ void RTCStatsCollector::ProduceIceCandidateAndPairStats_s( |
// smoothed according to the spec. crbug.com/633550. See |
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentrtt |
candidate_pair_stats->current_rtt = |
- static_cast<double>(info.rtt) / 1000.0; |
+ static_cast<double>(info.rtt) / rtc::kNumMillisecsPerSec; |
candidate_pair_stats->requests_sent = |
static_cast<uint64_t>(info.sent_ping_requests_total); |
candidate_pair_stats->responses_received = |
@@ -376,6 +453,63 @@ void RTCStatsCollector::ProducePeerConnectionStats_s( |
report->AddStats(std::move(stats)); |
} |
+void RTCStatsCollector::ProduceRTPStreamStats_s( |
+ int64_t timestamp_us, const SessionStats& session_stats, |
+ RTCStatsReport* report) const { |
+ RTC_DCHECK(signaling_thread_->IsCurrent()); |
+ |
+ // Audio |
+ if (pc_->session()->voice_channel()) { |
+ cricket::VoiceMediaInfo voice_media_info; |
+ if (pc_->session()->voice_channel()->GetStats(&voice_media_info)) { |
+ std::string transport_id = RTCTransportStatsIDFromBaseChannel( |
+ session_stats.proxy_to_transport, *pc_->session()->voice_channel()); |
+ for (const cricket::VoiceSenderInfo& voice_sender_info : |
+ voice_media_info.senders) { |
+ // TODO(nisse): SSRC == 0 currently means none. Delete check when that |
+ // is fixed. |
+ if (voice_sender_info.ssrc() == 0) |
+ continue; |
+ std::unique_ptr<RTCOutboundRTPStreamStats> outbound_audio( |
+ new RTCOutboundRTPStreamStats( |
+ RTCOutboundRTPStreamStatsIDFromSSRC( |
+ true, voice_sender_info.ssrc()), |
+ timestamp_us)); |
+ SetOutboundRTPStreamStatsFromVoiceSenderInfo( |
+ voice_sender_info, outbound_audio.get()); |
+ if (!transport_id.empty()) |
+ outbound_audio->transport_id = transport_id; |
+ report->AddStats(std::move(outbound_audio)); |
+ } |
+ } |
+ } |
+ // Video |
+ if (pc_->session()->video_channel()) { |
+ cricket::VideoMediaInfo video_media_info; |
+ if (pc_->session()->video_channel()->GetStats(&video_media_info)) { |
+ std::string transport_id = RTCTransportStatsIDFromBaseChannel( |
+ session_stats.proxy_to_transport, *pc_->session()->video_channel()); |
+ for (const cricket::VideoSenderInfo& video_sender_info : |
+ video_media_info.senders) { |
+ // TODO(nisse): SSRC == 0 currently means none. Delete check when that |
+ // is fixed. |
+ if (video_sender_info.ssrc() == 0) |
+ continue; |
+ std::unique_ptr<RTCOutboundRTPStreamStats> outbound_video( |
+ new RTCOutboundRTPStreamStats( |
+ RTCOutboundRTPStreamStatsIDFromSSRC( |
+ false, video_sender_info.ssrc()), |
+ timestamp_us)); |
+ SetOutboundRTPStreamStatsFromVideoSenderInfo( |
+ video_sender_info, outbound_video.get()); |
+ if (!transport_id.empty()) |
+ outbound_video->transport_id = transport_id; |
+ report->AddStats(std::move(outbound_video)); |
+ } |
+ } |
+ } |
+} |
+ |
void RTCStatsCollector::ProduceTransportStats_s( |
int64_t timestamp_us, const SessionStats& session_stats, |
const std::map<std::string, CertificateStatsPair>& transport_cert_stats, |