webrtc_m130/talk/app/webrtc/statscollector.cc

922 lines
37 KiB
C++
Raw Normal View History

/*
* libjingle
* Copyright 2012 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "talk/app/webrtc/statscollector.h"
#include <utility>
#include <vector>
#include "talk/session/media/channel.h"
#include "webrtc/base/base64.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/timing.h"
using rtc::scoped_ptr;
namespace webrtc {
namespace {
// The following is the enum RTCStatsIceCandidateType from
// http://w3c.github.io/webrtc-stats/#rtcstatsicecandidatetype-enum such that
// our stats report for ice candidate type could conform to that.
const char STATSREPORT_LOCAL_PORT_TYPE[] = "host";
const char STATSREPORT_STUN_PORT_TYPE[] = "serverreflexive";
const char STATSREPORT_PRFLX_PORT_TYPE[] = "peerreflexive";
const char STATSREPORT_RELAY_PORT_TYPE[] = "relayed";
// Strings used by the stats collector to report adapter types. This fits the
// general stype of http://w3c.github.io/webrtc-stats than what
// AdapterTypeToString does.
const char* STATSREPORT_ADAPTER_TYPE_ETHERNET = "lan";
const char* STATSREPORT_ADAPTER_TYPE_WIFI = "wlan";
const char* STATSREPORT_ADAPTER_TYPE_WWAN = "wwan";
const char* STATSREPORT_ADAPTER_TYPE_VPN = "vpn";
Makes libjingle_peerconnection_android_unittest run on networkless devices. PeerConnectionTest.java currently works, but only on a device with network interfaces up. This is not a problem for desktop, but it is a problem when running on Android devices since the devices in the lab generally don't have network (due to the chaotic radio environment in the device labs, devices are simply kept in flight mode). The test does work if one modifies this line in the file webrtc/base/network.cc: bool ignored = ((cursor->ifa_flags & IFF_LOOPBACK) || IsIgnoredNetwork(*network)); If we remove the IFF_LOOPBACK clause, the test starts working on an Android device in flight mode. This is nice - we're running the call and packets interact with the OS network stack, which is good for this end-to-end test. We can't just remove the clause though since having loopback is undesirable for everyone except the test (right)? so we need to make this behavior configurable. This CL takes a stab at a complete solution where we pass a boolean all the way through the Java PeerConnectionFactory down to the BasicNetworkManager. This comes as a heavy price in interface changes though. It's pretty out of proportion, but fundamentally we need some way of telling the network manager that it is on Android and in test mode. Passing the boolean all the way through is one way. Another way might be to put the loopback filter behind an ifdef and link a custom libjingle_peerconnection.so with the test. That is hacky but doesn't pollute the interfaces. Not sure how to solve that in GYP but it could mean some duplication between the production and test .so files. It would have been perfect to use flags here, but then we need to hook up gflags parsing to some main() somewhere to make sure the flag gets parsed, and make sure to pass that flag in our tests. I'm not sure how that can be done. Making the loopback filtering conditional is exactly how we solved the equivalent problem in content_browsertests in Chrome, and it worked great. That's all I could think of. BUG=4181 R=perkj@webrtc.org, pthatcher@webrtc.org Review URL: https://webrtc-codereview.appspot.com/36769004 Cr-Commit-Position: refs/heads/master@{#8344} git-svn-id: http://webrtc.googlecode.com/svn/trunk@8344 4adac7df-926f-26a2-2b94-8c16560cd09d
2015-02-12 09:23:59 +00:00
const char* STATSREPORT_ADAPTER_TYPE_LOOPBACK = "loopback";
template<typename ValueType>
struct TypeForAdd {
const StatsReport::StatsValueName name;
const ValueType& value;
};
typedef TypeForAdd<bool> BoolForAdd;
typedef TypeForAdd<float> FloatForAdd;
typedef TypeForAdd<int64> Int64ForAdd;
typedef TypeForAdd<int> IntForAdd;
StatsReport::Id GetTransportIdFromProxy(const cricket::ProxyTransportMap& map,
const std::string& proxy) {
DCHECK(!proxy.empty());
cricket::ProxyTransportMap::const_iterator found = map.find(proxy);
if (found == map.end())
return StatsReport::Id();
return StatsReport::NewComponentId(
found->second, cricket::ICE_CANDIDATE_COMPONENT_RTP);
}
void AddTrackReport(StatsCollection* reports, const std::string& track_id) {
// Adds an empty track report.
StatsReport::Id id(
StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack, track_id));
StatsReport* report = reports->ReplaceOrAddNew(id);
report->AddString(StatsReport::kStatsValueNameTrackId, track_id);
}
template <class TrackVector>
void CreateTrackReports(const TrackVector& tracks, StatsCollection* reports) {
for (const auto& track : tracks)
AddTrackReport(reports, track->id());
}
void ExtractCommonSendProperties(const cricket::MediaSenderInfo& info,
StatsReport* report) {
report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name);
report->AddInt64(StatsReport::kStatsValueNameBytesSent, info.bytes_sent);
report->AddInt64(StatsReport::kStatsValueNameRtt, info.rtt_ms);
}
void SetAudioProcessingStats(StatsReport* report, int signal_level,
bool typing_noise_detected, int echo_return_loss,
int echo_return_loss_enhancement, int echo_delay_median_ms,
float aec_quality_min, int echo_delay_std_ms) {
report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
typing_noise_detected);
report->AddFloat(StatsReport::kStatsValueNameEchoCancellationQualityMin,
aec_quality_min);
// Don't overwrite the previous signal level if it's not available now.
if (signal_level >= 0)
report->AddInt(StatsReport::kStatsValueNameAudioInputLevel, signal_level);
const IntForAdd ints[] = {
{ StatsReport::kStatsValueNameEchoReturnLoss, echo_return_loss },
{ StatsReport::kStatsValueNameEchoReturnLossEnhancement,
echo_return_loss_enhancement },
{ StatsReport::kStatsValueNameEchoDelayMedian, echo_delay_median_ms },
{ StatsReport::kStatsValueNameEchoDelayStdDev, echo_delay_std_ms },
};
for (const auto& i : ints)
report->AddInt(i.name, i.value);
}
void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
const FloatForAdd floats[] = {
{ StatsReport::kStatsValueNameExpandRate, info.expand_rate },
{ StatsReport::kStatsValueNameSecondaryDecodedRate,
info.secondary_decoded_rate },
{ StatsReport::kStatsValueNameSpeechExpandRate, info.speech_expand_rate },
};
const IntForAdd ints[] = {
{ StatsReport::kStatsValueNameAudioOutputLevel, info.audio_level },
{ StatsReport::kStatsValueNameCurrentDelayMs, info.delay_estimate_ms },
{ StatsReport::kStatsValueNameDecodingCNG, info.decoding_cng },
{ StatsReport::kStatsValueNameDecodingCTN, info.decoding_calls_to_neteq },
{ StatsReport::kStatsValueNameDecodingCTSG,
info.decoding_calls_to_silence_generator },
{ StatsReport::kStatsValueNameDecodingNormal, info.decoding_normal },
{ StatsReport::kStatsValueNameDecodingPLC, info.decoding_plc },
{ StatsReport::kStatsValueNameDecodingPLCCNG, info.decoding_plc_cng },
{ StatsReport::kStatsValueNameJitterBufferMs, info.jitter_buffer_ms },
{ StatsReport::kStatsValueNameJitterReceived, info.jitter_ms },
{ StatsReport::kStatsValueNamePacketsLost, info.packets_lost },
{ StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd },
{ StatsReport::kStatsValueNamePreferredJitterBufferMs,
info.jitter_buffer_preferred_ms },
};
for (const auto& f : floats)
report->AddFloat(f.name, f.value);
for (const auto& i : ints)
report->AddInt(i.name, i.value);
report->AddInt64(StatsReport::kStatsValueNameBytesReceived,
info.bytes_rcvd);
report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
info.capture_start_ntp_time_ms);
report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name);
}
void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
ExtractCommonSendProperties(info, report);
SetAudioProcessingStats(report, info.audio_level, info.typing_noise_detected,
info.echo_return_loss, info.echo_return_loss_enhancement,
info.echo_delay_median_ms, info.aec_quality_min, info.echo_delay_std_ms);
const IntForAdd ints[] = {
{ StatsReport::kStatsValueNameJitterReceived, info.jitter_ms },
{ StatsReport::kStatsValueNamePacketsLost, info.packets_lost },
{ StatsReport::kStatsValueNamePacketsSent, info.packets_sent },
};
for (const auto& i : ints)
report->AddInt(i.name, i.value);
}
void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
report->AddInt64(StatsReport::kStatsValueNameBytesReceived,
info.bytes_rcvd);
report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
info.capture_start_ntp_time_ms);
const IntForAdd ints[] = {
{ StatsReport::kStatsValueNameCurrentDelayMs, info.current_delay_ms },
{ StatsReport::kStatsValueNameDecodeMs, info.decode_ms },
{ StatsReport::kStatsValueNameFirsSent, info.firs_sent },
{ StatsReport::kStatsValueNameFrameHeightReceived, info.frame_height },
{ StatsReport::kStatsValueNameFrameRateDecoded, info.framerate_decoded },
{ StatsReport::kStatsValueNameFrameRateOutput, info.framerate_output },
{ StatsReport::kStatsValueNameFrameRateReceived, info.framerate_rcvd },
{ StatsReport::kStatsValueNameFrameWidthReceived, info.frame_width },
{ StatsReport::kStatsValueNameJitterBufferMs, info.jitter_buffer_ms },
{ StatsReport::kStatsValueNameMaxDecodeMs, info.max_decode_ms },
{ StatsReport::kStatsValueNameMinPlayoutDelayMs,
info.min_playout_delay_ms },
{ StatsReport::kStatsValueNameNacksSent, info.nacks_sent },
{ StatsReport::kStatsValueNamePacketsLost, info.packets_lost },
{ StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd },
{ StatsReport::kStatsValueNamePlisSent, info.plis_sent },
{ StatsReport::kStatsValueNameRenderDelayMs, info.render_delay_ms },
{ StatsReport::kStatsValueNameTargetDelayMs, info.target_delay_ms },
};
for (const auto& i : ints)
report->AddInt(i.name, i.value);
}
void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
ExtractCommonSendProperties(info, report);
report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
(info.adapt_reason & 0x2) > 0);
report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
(info.adapt_reason & 0x1) > 0);
report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution,
(info.adapt_reason & 0x4) > 0);
const IntForAdd ints[] = {
{ StatsReport::kStatsValueNameAdaptationChanges, info.adapt_changes },
{ StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms },
{ StatsReport::kStatsValueNameCaptureJitterMs, info.capture_jitter_ms },
{ StatsReport::kStatsValueNameCaptureQueueDelayMsPerS,
info.capture_queue_delay_ms_per_s },
{ StatsReport::kStatsValueNameEncodeUsagePercent,
info.encode_usage_percent },
{ StatsReport::kStatsValueNameFirsReceived, info.firs_rcvd },
{ StatsReport::kStatsValueNameFrameHeightInput, info.input_frame_height },
{ StatsReport::kStatsValueNameFrameHeightSent, info.send_frame_height },
{ StatsReport::kStatsValueNameFrameRateInput, info.framerate_input },
{ StatsReport::kStatsValueNameFrameRateSent, info.framerate_sent },
{ StatsReport::kStatsValueNameFrameWidthInput, info.input_frame_width },
{ StatsReport::kStatsValueNameFrameWidthSent, info.send_frame_width },
{ StatsReport::kStatsValueNameNacksReceived, info.nacks_rcvd },
{ StatsReport::kStatsValueNamePacketsLost, info.packets_lost },
{ StatsReport::kStatsValueNamePacketsSent, info.packets_sent },
{ StatsReport::kStatsValueNamePlisReceived, info.plis_rcvd },
};
for (const auto& i : ints)
report->AddInt(i.name, i.value);
}
void ExtractStats(const cricket::BandwidthEstimationInfo& info,
double stats_gathering_started,
PeerConnectionInterface::StatsOutputLevel level,
StatsReport* report) {
DCHECK(report->type() == StatsReport::kStatsReportTypeBwe);
report->set_timestamp(stats_gathering_started);
const IntForAdd ints[] = {
{ StatsReport::kStatsValueNameAvailableSendBandwidth,
info.available_send_bandwidth },
{ StatsReport::kStatsValueNameAvailableReceiveBandwidth,
info.available_recv_bandwidth },
{ StatsReport::kStatsValueNameTargetEncBitrate, info.target_enc_bitrate },
{ StatsReport::kStatsValueNameActualEncBitrate, info.actual_enc_bitrate },
{ StatsReport::kStatsValueNameRetransmitBitrate, info.retransmit_bitrate },
{ StatsReport::kStatsValueNameTransmitBitrate, info.transmit_bitrate },
};
for (const auto& i : ints)
report->AddInt(i.name, i.value);
report->AddInt64(StatsReport::kStatsValueNameBucketDelay, info.bucket_delay);
}
void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
StatsReport* report) {
report->set_timestamp(info.remote_stats[0].timestamp);
// TODO(hta): Extract some stats here.
}
void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
StatsReport* report) {
report->set_timestamp(info.remote_stats[0].timestamp);
// TODO(hta): Extract some stats here.
}
// Template to extract stats from a data vector.
// In order to use the template, the functions that are called from it,
// ExtractStats and ExtractRemoteStats, must be defined and overloaded
// for each type.
template<typename T>
void ExtractStatsFromList(const std::vector<T>& data,
const StatsReport::Id& transport_id,
StatsCollector* collector,
StatsReport::Direction direction) {
for (const auto& d : data) {
uint32 ssrc = d.ssrc();
// Each track can have stats for both local and remote objects.
// TODO(hta): Handle the case of multiple SSRCs per object.
StatsReport* report = collector->PrepareReport(true, ssrc, transport_id,
direction);
if (report)
ExtractStats(d, report);
if (!d.remote_stats.empty()) {
report = collector->PrepareReport(false, ssrc, transport_id, direction);
if (report)
ExtractRemoteStats(d, report);
}
}
}
} // namespace
const char* IceCandidateTypeToStatsType(const std::string& candidate_type) {
if (candidate_type == cricket::LOCAL_PORT_TYPE) {
return STATSREPORT_LOCAL_PORT_TYPE;
}
if (candidate_type == cricket::STUN_PORT_TYPE) {
return STATSREPORT_STUN_PORT_TYPE;
}
if (candidate_type == cricket::PRFLX_PORT_TYPE) {
return STATSREPORT_PRFLX_PORT_TYPE;
}
if (candidate_type == cricket::RELAY_PORT_TYPE) {
return STATSREPORT_RELAY_PORT_TYPE;
}
DCHECK(false);
return "unknown";
}
const char* AdapterTypeToStatsType(rtc::AdapterType type) {
switch (type) {
case rtc::ADAPTER_TYPE_UNKNOWN:
return "unknown";
case rtc::ADAPTER_TYPE_ETHERNET:
return STATSREPORT_ADAPTER_TYPE_ETHERNET;
case rtc::ADAPTER_TYPE_WIFI:
return STATSREPORT_ADAPTER_TYPE_WIFI;
case rtc::ADAPTER_TYPE_CELLULAR:
return STATSREPORT_ADAPTER_TYPE_WWAN;
case rtc::ADAPTER_TYPE_VPN:
return STATSREPORT_ADAPTER_TYPE_VPN;
Makes libjingle_peerconnection_android_unittest run on networkless devices. PeerConnectionTest.java currently works, but only on a device with network interfaces up. This is not a problem for desktop, but it is a problem when running on Android devices since the devices in the lab generally don't have network (due to the chaotic radio environment in the device labs, devices are simply kept in flight mode). The test does work if one modifies this line in the file webrtc/base/network.cc: bool ignored = ((cursor->ifa_flags & IFF_LOOPBACK) || IsIgnoredNetwork(*network)); If we remove the IFF_LOOPBACK clause, the test starts working on an Android device in flight mode. This is nice - we're running the call and packets interact with the OS network stack, which is good for this end-to-end test. We can't just remove the clause though since having loopback is undesirable for everyone except the test (right)? so we need to make this behavior configurable. This CL takes a stab at a complete solution where we pass a boolean all the way through the Java PeerConnectionFactory down to the BasicNetworkManager. This comes as a heavy price in interface changes though. It's pretty out of proportion, but fundamentally we need some way of telling the network manager that it is on Android and in test mode. Passing the boolean all the way through is one way. Another way might be to put the loopback filter behind an ifdef and link a custom libjingle_peerconnection.so with the test. That is hacky but doesn't pollute the interfaces. Not sure how to solve that in GYP but it could mean some duplication between the production and test .so files. It would have been perfect to use flags here, but then we need to hook up gflags parsing to some main() somewhere to make sure the flag gets parsed, and make sure to pass that flag in our tests. I'm not sure how that can be done. Making the loopback filtering conditional is exactly how we solved the equivalent problem in content_browsertests in Chrome, and it worked great. That's all I could think of. BUG=4181 R=perkj@webrtc.org, pthatcher@webrtc.org Review URL: https://webrtc-codereview.appspot.com/36769004 Cr-Commit-Position: refs/heads/master@{#8344} git-svn-id: http://webrtc.googlecode.com/svn/trunk@8344 4adac7df-926f-26a2-2b94-8c16560cd09d
2015-02-12 09:23:59 +00:00
case rtc::ADAPTER_TYPE_LOOPBACK:
return STATSREPORT_ADAPTER_TYPE_LOOPBACK;
default:
DCHECK(false);
return "";
}
}
StatsCollector::StatsCollector(WebRtcSession* session)
: session_(session),
stats_gathering_started_(0) {
DCHECK(session_);
}
StatsCollector::~StatsCollector() {
DCHECK(session_->signaling_thread()->IsCurrent());
}
double StatsCollector::GetTimeNow() {
return rtc::Timing::WallTimeNow() * rtc::kNumMillisecsPerSec;
}
// Adds a MediaStream with tracks that can be used as a |selector| in a call
// to GetStats.
void StatsCollector::AddStream(MediaStreamInterface* stream) {
DCHECK(session_->signaling_thread()->IsCurrent());
DCHECK(stream != NULL);
CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
&reports_);
CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
&reports_);
}
void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track,
uint32 ssrc) {
DCHECK(session_->signaling_thread()->IsCurrent());
DCHECK(audio_track != NULL);
#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
for (const auto& track : local_audio_tracks_)
DCHECK(track.first != audio_track || track.second != ssrc);
#endif
local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc));
// Create the kStatsReportTypeTrack report for the new track if there is no
// report yet.
StatsReport::Id id(StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack,
audio_track->id()));
StatsReport* report = reports_.Find(id);
if (!report) {
report = reports_.InsertNew(id);
report->AddString(StatsReport::kStatsValueNameTrackId, audio_track->id());
}
}
void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track,
uint32 ssrc) {
DCHECK(audio_track != NULL);
local_audio_tracks_.erase(std::remove_if(local_audio_tracks_.begin(),
local_audio_tracks_.end(),
[audio_track, ssrc](const LocalAudioTrackVector::value_type& track) {
return track.first == audio_track && track.second == ssrc;
}));
}
Revert 6897 (i.e. Reland 6863) - "Revert 6863 "Refactor StatsCollector and associated..." The bot that had the problem was using an old version of STL, so relanding. > Revert 6863 "Refactor StatsCollector and associated types." > > Breaks chrome compilation on Mac: > > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/vector.tcc:252:8: > error: no matching constructor for initialization of > 'webrtc::StatsReport' > _Tp __x_copy = __x; > ^ ~~~ > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:608:4: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::_M_insert_aux' requested here > _M_insert_aux(end(), __x); > ^ > ../../content/renderer/media/mock_peer_connection_impl.cc:282:11: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::push_back' requested here > reports.push_back(report1); > ^ > ../../third_party/libjingle/source/talk/app/webrtc/statstypes.h:49:3: > note: candidate constructor not viable: requires 0 arguments, but 1 > was provided > StatsReport() : timestamp(0) {} > > > > > Refactor StatsCollector and associated types. > > * Due to the type changes, I'm going to update the OnCompleted event in two phases to sync with Chrome. This is the first phase. > > * Reports are now managed in a set, not a map, since it's enough to store the id in one place. > > * Report ids are now const. > > * Copying of data has been greatly reduced. > > * This change includes preparation work for making GetStats fully async. > > > > This is a reland of r6778 which was reverted due to fyi bots failing. > > I found and fixed the issue which was that in a couple of places I needed to replace a report instead of finding+updating an existing one. > > > > R=xians@webrtc.org > > > > Review URL: https://webrtc-codereview.appspot.com/15119004 > > TBR=tommi@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/21169004 TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/22099004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6908 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-15 08:38:30 +00:00
void StatsCollector::GetStats(MediaStreamTrackInterface* track,
StatsReports* reports) {
DCHECK(session_->signaling_thread()->IsCurrent());
DCHECK(reports != NULL);
DCHECK(reports->empty());
rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
if (!track) {
reports->reserve(reports_.size());
for (auto* r : reports_)
reports->push_back(r);
Revert 6897 (i.e. Reland 6863) - "Revert 6863 "Refactor StatsCollector and associated..." The bot that had the problem was using an old version of STL, so relanding. > Revert 6863 "Refactor StatsCollector and associated types." > > Breaks chrome compilation on Mac: > > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/vector.tcc:252:8: > error: no matching constructor for initialization of > 'webrtc::StatsReport' > _Tp __x_copy = __x; > ^ ~~~ > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:608:4: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::_M_insert_aux' requested here > _M_insert_aux(end(), __x); > ^ > ../../content/renderer/media/mock_peer_connection_impl.cc:282:11: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::push_back' requested here > reports.push_back(report1); > ^ > ../../third_party/libjingle/source/talk/app/webrtc/statstypes.h:49:3: > note: candidate constructor not viable: requires 0 arguments, but 1 > was provided > StatsReport() : timestamp(0) {} > > > > > Refactor StatsCollector and associated types. > > * Due to the type changes, I'm going to update the OnCompleted event in two phases to sync with Chrome. This is the first phase. > > * Reports are now managed in a set, not a map, since it's enough to store the id in one place. > > * Report ids are now const. > > * Copying of data has been greatly reduced. > > * This change includes preparation work for making GetStats fully async. > > > > This is a reland of r6778 which was reverted due to fyi bots failing. > > I found and fixed the issue which was that in a couple of places I needed to replace a report instead of finding+updating an existing one. > > > > R=xians@webrtc.org > > > > Review URL: https://webrtc-codereview.appspot.com/15119004 > > TBR=tommi@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/21169004 TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/22099004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6908 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-15 08:38:30 +00:00
return;
}
StatsReport* report = reports_.Find(StatsReport::NewTypedId(
StatsReport::kStatsReportTypeSession, session_->id()));
Revert 6897 (i.e. Reland 6863) - "Revert 6863 "Refactor StatsCollector and associated..." The bot that had the problem was using an old version of STL, so relanding. > Revert 6863 "Refactor StatsCollector and associated types." > > Breaks chrome compilation on Mac: > > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/vector.tcc:252:8: > error: no matching constructor for initialization of > 'webrtc::StatsReport' > _Tp __x_copy = __x; > ^ ~~~ > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:608:4: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::_M_insert_aux' requested here > _M_insert_aux(end(), __x); > ^ > ../../content/renderer/media/mock_peer_connection_impl.cc:282:11: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::push_back' requested here > reports.push_back(report1); > ^ > ../../third_party/libjingle/source/talk/app/webrtc/statstypes.h:49:3: > note: candidate constructor not viable: requires 0 arguments, but 1 > was provided > StatsReport() : timestamp(0) {} > > > > > Refactor StatsCollector and associated types. > > * Due to the type changes, I'm going to update the OnCompleted event in two phases to sync with Chrome. This is the first phase. > > * Reports are now managed in a set, not a map, since it's enough to store the id in one place. > > * Report ids are now const. > > * Copying of data has been greatly reduced. > > * This change includes preparation work for making GetStats fully async. > > > > This is a reland of r6778 which was reverted due to fyi bots failing. > > I found and fixed the issue which was that in a couple of places I needed to replace a report instead of finding+updating an existing one. > > > > R=xians@webrtc.org > > > > Review URL: https://webrtc-codereview.appspot.com/15119004 > > TBR=tommi@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/21169004 TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/22099004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6908 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-15 08:38:30 +00:00
if (report)
reports->push_back(report);
report = reports_.Find(StatsReport::NewTypedId(
StatsReport::kStatsReportTypeTrack, track->id()));
Revert 6897 (i.e. Reland 6863) - "Revert 6863 "Refactor StatsCollector and associated..." The bot that had the problem was using an old version of STL, so relanding. > Revert 6863 "Refactor StatsCollector and associated types." > > Breaks chrome compilation on Mac: > > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/vector.tcc:252:8: > error: no matching constructor for initialization of > 'webrtc::StatsReport' > _Tp __x_copy = __x; > ^ ~~~ > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:608:4: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::_M_insert_aux' requested here > _M_insert_aux(end(), __x); > ^ > ../../content/renderer/media/mock_peer_connection_impl.cc:282:11: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::push_back' requested here > reports.push_back(report1); > ^ > ../../third_party/libjingle/source/talk/app/webrtc/statstypes.h:49:3: > note: candidate constructor not viable: requires 0 arguments, but 1 > was provided > StatsReport() : timestamp(0) {} > > > > > Refactor StatsCollector and associated types. > > * Due to the type changes, I'm going to update the OnCompleted event in two phases to sync with Chrome. This is the first phase. > > * Reports are now managed in a set, not a map, since it's enough to store the id in one place. > > * Report ids are now const. > > * Copying of data has been greatly reduced. > > * This change includes preparation work for making GetStats fully async. > > > > This is a reland of r6778 which was reverted due to fyi bots failing. > > I found and fixed the issue which was that in a couple of places I needed to replace a report instead of finding+updating an existing one. > > > > R=xians@webrtc.org > > > > Review URL: https://webrtc-codereview.appspot.com/15119004 > > TBR=tommi@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/21169004 TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/22099004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6908 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-15 08:38:30 +00:00
if (!report)
return;
Revert 6897 (i.e. Reland 6863) - "Revert 6863 "Refactor StatsCollector and associated..." The bot that had the problem was using an old version of STL, so relanding. > Revert 6863 "Refactor StatsCollector and associated types." > > Breaks chrome compilation on Mac: > > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/vector.tcc:252:8: > error: no matching constructor for initialization of > 'webrtc::StatsReport' > _Tp __x_copy = __x; > ^ ~~~ > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:608:4: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::_M_insert_aux' requested here > _M_insert_aux(end(), __x); > ^ > ../../content/renderer/media/mock_peer_connection_impl.cc:282:11: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::push_back' requested here > reports.push_back(report1); > ^ > ../../third_party/libjingle/source/talk/app/webrtc/statstypes.h:49:3: > note: candidate constructor not viable: requires 0 arguments, but 1 > was provided > StatsReport() : timestamp(0) {} > > > > > Refactor StatsCollector and associated types. > > * Due to the type changes, I'm going to update the OnCompleted event in two phases to sync with Chrome. This is the first phase. > > * Reports are now managed in a set, not a map, since it's enough to store the id in one place. > > * Report ids are now const. > > * Copying of data has been greatly reduced. > > * This change includes preparation work for making GetStats fully async. > > > > This is a reland of r6778 which was reverted due to fyi bots failing. > > I found and fixed the issue which was that in a couple of places I needed to replace a report instead of finding+updating an existing one. > > > > R=xians@webrtc.org > > > > Review URL: https://webrtc-codereview.appspot.com/15119004 > > TBR=tommi@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/21169004 TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/22099004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6908 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-15 08:38:30 +00:00
reports->push_back(report);
std::string track_id;
for (const auto* r : reports_) {
if (r->type() != StatsReport::kStatsReportTypeSsrc)
continue;
Revert 6897 (i.e. Reland 6863) - "Revert 6863 "Refactor StatsCollector and associated..." The bot that had the problem was using an old version of STL, so relanding. > Revert 6863 "Refactor StatsCollector and associated types." > > Breaks chrome compilation on Mac: > > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/vector.tcc:252:8: > error: no matching constructor for initialization of > 'webrtc::StatsReport' > _Tp __x_copy = __x; > ^ ~~~ > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:608:4: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::_M_insert_aux' requested here > _M_insert_aux(end(), __x); > ^ > ../../content/renderer/media/mock_peer_connection_impl.cc:282:11: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::push_back' requested here > reports.push_back(report1); > ^ > ../../third_party/libjingle/source/talk/app/webrtc/statstypes.h:49:3: > note: candidate constructor not viable: requires 0 arguments, but 1 > was provided > StatsReport() : timestamp(0) {} > > > > > Refactor StatsCollector and associated types. > > * Due to the type changes, I'm going to update the OnCompleted event in two phases to sync with Chrome. This is the first phase. > > * Reports are now managed in a set, not a map, since it's enough to store the id in one place. > > * Report ids are now const. > > * Copying of data has been greatly reduced. > > * This change includes preparation work for making GetStats fully async. > > > > This is a reland of r6778 which was reverted due to fyi bots failing. > > I found and fixed the issue which was that in a couple of places I needed to replace a report instead of finding+updating an existing one. > > > > R=xians@webrtc.org > > > > Review URL: https://webrtc-codereview.appspot.com/15119004 > > TBR=tommi@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/21169004 TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/22099004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6908 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-15 08:38:30 +00:00
const StatsReport::Value* v =
r->FindValue(StatsReport::kStatsValueNameTrackId);
if (v && v->string_val() == track->id())
reports->push_back(r);
}
}
void
StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
DCHECK(session_->signaling_thread()->IsCurrent());
double time_now = GetTimeNow();
// Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
// ms apart will be ignored.
const double kMinGatherStatsPeriod = 50;
if (stats_gathering_started_ != 0 &&
stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
return;
}
stats_gathering_started_ = time_now;
if (session_) {
// TODO(tommi): All of these hop over to the worker thread to fetch
// information. We could use an AsyncInvoker to run all of these and post
// the information back to the signaling thread where we can create and
// update stats reports. That would also clean up the threading story a bit
// since we'd be creating/updating the stats report objects consistently on
// the same thread (this class has no locks right now).
ExtractSessionInfo();
ExtractVoiceInfo();
ExtractVideoInfo(level);
ExtractDataInfo();
}
}
StatsReport* StatsCollector::PrepareReport(
bool local,
uint32 ssrc,
const StatsReport::Id& transport_id,
StatsReport::Direction direction) {
DCHECK(session_->signaling_thread()->IsCurrent());
StatsReport::Id id(StatsReport::NewIdWithDirection(
local ? StatsReport::kStatsReportTypeSsrc :
StatsReport::kStatsReportTypeRemoteSsrc,
rtc::ToString<uint32>(ssrc), direction));
StatsReport* report = reports_.Find(id);
// Use the ID of the track that is currently mapped to the SSRC, if any.
std::string track_id;
if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
Revert 6897 (i.e. Reland 6863) - "Revert 6863 "Refactor StatsCollector and associated..." The bot that had the problem was using an old version of STL, so relanding. > Revert 6863 "Refactor StatsCollector and associated types." > > Breaks chrome compilation on Mac: > > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/vector.tcc:252:8: > error: no matching constructor for initialization of > 'webrtc::StatsReport' > _Tp __x_copy = __x; > ^ ~~~ > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:608:4: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::_M_insert_aux' requested here > _M_insert_aux(end(), __x); > ^ > ../../content/renderer/media/mock_peer_connection_impl.cc:282:11: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::push_back' requested here > reports.push_back(report1); > ^ > ../../third_party/libjingle/source/talk/app/webrtc/statstypes.h:49:3: > note: candidate constructor not viable: requires 0 arguments, but 1 > was provided > StatsReport() : timestamp(0) {} > > > > > Refactor StatsCollector and associated types. > > * Due to the type changes, I'm going to update the OnCompleted event in two phases to sync with Chrome. This is the first phase. > > * Reports are now managed in a set, not a map, since it's enough to store the id in one place. > > * Report ids are now const. > > * Copying of data has been greatly reduced. > > * This change includes preparation work for making GetStats fully async. > > > > This is a reland of r6778 which was reverted due to fyi bots failing. > > I found and fixed the issue which was that in a couple of places I needed to replace a report instead of finding+updating an existing one. > > > > R=xians@webrtc.org > > > > Review URL: https://webrtc-codereview.appspot.com/15119004 > > TBR=tommi@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/21169004 TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/22099004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6908 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-15 08:38:30 +00:00
if (!report) {
// The ssrc is not used by any track or existing report, return NULL
// in such case to indicate no report is prepared for the ssrc.
return NULL;
}
// The ssrc is not used by any existing track. Keeps the old track id
// since we want to report the stats for inactive ssrc.
const StatsReport::Value* v =
report->FindValue(StatsReport::kStatsValueNameTrackId);
if (v)
track_id = v->string_val();
}
if (!report)
report = reports_.InsertNew(id);
// FYI - for remote reports, the timestamp will be overwritten later.
report->set_timestamp(stats_gathering_started_);
report->AddInt64(StatsReport::kStatsValueNameSsrc, ssrc);
report->AddString(StatsReport::kStatsValueNameTrackId, track_id);
// Add the mapping of SSRC to transport.
report->AddId(StatsReport::kStatsValueNameTransportId, transport_id);
return report;
}
StatsReport* StatsCollector::AddOneCertificateReport(
const rtc::SSLCertificate* cert, const StatsReport* issuer) {
DCHECK(session_->signaling_thread()->IsCurrent());
// TODO(bemasc): Move this computation to a helper class that caches these
// values to reduce CPU use in GetStats. This will require adding a fast
// SSLCertificate::Equals() method to detect certificate changes.
std::string digest_algorithm;
if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
return nullptr;
rtc::scoped_ptr<rtc::SSLFingerprint> ssl_fingerprint(
rtc::SSLFingerprint::Create(digest_algorithm, cert));
// SSLFingerprint::Create can fail if the algorithm returned by
// SSLCertificate::GetSignatureDigestAlgorithm is not supported by the
// implementation of SSLCertificate::ComputeDigest. This currently happens
// with MD5- and SHA-224-signed certificates when linked to libNSS.
if (!ssl_fingerprint)
return nullptr;
std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
rtc::Buffer der_buffer;
cert->ToDER(&der_buffer);
std::string der_base64;
rtc::Base64::EncodeFromArray(
der_buffer.data(), der_buffer.length(), &der_base64);
StatsReport::Id id(StatsReport::NewTypedId(
StatsReport::kStatsReportTypeCertificate, fingerprint));
StatsReport* report = reports_.ReplaceOrAddNew(id);
report->set_timestamp(stats_gathering_started_);
report->AddString(StatsReport::kStatsValueNameFingerprint, fingerprint);
report->AddString(StatsReport::kStatsValueNameFingerprintAlgorithm,
digest_algorithm);
report->AddString(StatsReport::kStatsValueNameDer, der_base64);
if (issuer)
report->AddId(StatsReport::kStatsValueNameIssuerId, issuer->id());
return report;
}
StatsReport* StatsCollector::AddCertificateReports(
const rtc::SSLCertificate* cert) {
DCHECK(session_->signaling_thread()->IsCurrent());
// Produces a chain of StatsReports representing this certificate and the rest
// of its chain, and adds those reports to |reports_|. The return value is
// the id of the leaf report. The provided cert must be non-null, so at least
// one report will always be provided and the returned string will never be
// empty.
DCHECK(cert != NULL);
StatsReport* issuer = nullptr;
rtc::scoped_ptr<rtc::SSLCertChain> chain;
if (cert->GetChain(chain.accept())) {
// This loop runs in reverse, i.e. from root to leaf, so that each
// certificate's issuer's report ID is known before the child certificate's
// report is generated. The root certificate does not have an issuer ID
// value.
for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
const rtc::SSLCertificate& cert_i = chain->Get(i);
issuer = AddOneCertificateReport(&cert_i, issuer);
}
}
// Add the leaf certificate.
return AddOneCertificateReport(cert, issuer);
}
StatsReport* StatsCollector::AddConnectionInfoReport(
const std::string& content_name, int component, int connection_id,
const StatsReport::Id& channel_report_id,
const cricket::ConnectionInfo& info) {
StatsReport::Id id(StatsReport::NewCandidatePairId(content_name, component,
connection_id));
StatsReport* report = reports_.ReplaceOrAddNew(id);
report->set_timestamp(stats_gathering_started_);
const BoolForAdd bools[] = {
{ StatsReport::kStatsValueNameActiveConnection, info.best_connection },
{ StatsReport::kStatsValueNameReadable, info.readable },
{ StatsReport::kStatsValueNameWritable, info.writable },
};
for (const auto& b : bools)
report->AddBoolean(b.name, b.value);
report->AddId(StatsReport::kStatsValueNameChannelId, channel_report_id);
report->AddId(StatsReport::kStatsValueNameLocalCandidateId,
AddCandidateReport(info.local_candidate, true)->id());
report->AddId(StatsReport::kStatsValueNameRemoteCandidateId,
AddCandidateReport(info.remote_candidate, false)->id());
const Int64ForAdd int64s[] = {
{ StatsReport::kStatsValueNameBytesReceived, info.recv_total_bytes },
{ StatsReport::kStatsValueNameBytesSent, info.sent_total_bytes },
{ StatsReport::kStatsValueNamePacketsSent, info.sent_total_packets },
{ StatsReport::kStatsValueNameRtt, info.rtt },
{ StatsReport::kStatsValueNameSendPacketsDiscarded,
info.sent_discarded_packets },
};
for (const auto& i : int64s)
report->AddInt64(i.name, i.value);
report->AddString(StatsReport::kStatsValueNameLocalAddress,
info.local_candidate.address().ToString());
report->AddString(StatsReport::kStatsValueNameLocalCandidateType,
info.local_candidate.type());
report->AddString(StatsReport::kStatsValueNameRemoteAddress,
info.remote_candidate.address().ToString());
report->AddString(StatsReport::kStatsValueNameRemoteCandidateType,
info.remote_candidate.type());
report->AddString(StatsReport::kStatsValueNameTransportType,
info.local_candidate.protocol());
return report;
}
StatsReport* StatsCollector::AddCandidateReport(
const cricket::Candidate& candidate,
bool local) {
StatsReport::Id id(StatsReport::NewCandidateId(local, candidate.id()));
StatsReport* report = reports_.Find(id);
if (!report) {
report = reports_.InsertNew(id);
report->set_timestamp(stats_gathering_started_);
if (local) {
report->AddString(StatsReport::kStatsValueNameCandidateNetworkType,
AdapterTypeToStatsType(candidate.network_type()));
}
report->AddString(StatsReport::kStatsValueNameCandidateIPAddress,
candidate.address().ipaddr().ToString());
report->AddString(StatsReport::kStatsValueNameCandidatePortNumber,
candidate.address().PortAsString());
report->AddInt(StatsReport::kStatsValueNameCandidatePriority,
candidate.priority());
report->AddString(StatsReport::kStatsValueNameCandidateType,
IceCandidateTypeToStatsType(candidate.type()));
report->AddString(StatsReport::kStatsValueNameCandidateTransportType,
candidate.protocol());
}
return report;
}
void StatsCollector::ExtractSessionInfo() {
DCHECK(session_->signaling_thread()->IsCurrent());
// Extract information from the base session.
StatsReport::Id id(StatsReport::NewTypedId(
StatsReport::kStatsReportTypeSession, session_->id()));
StatsReport* report = reports_.ReplaceOrAddNew(id);
report->set_timestamp(stats_gathering_started_);
Revert 6897 (i.e. Reland 6863) - "Revert 6863 "Refactor StatsCollector and associated..." The bot that had the problem was using an old version of STL, so relanding. > Revert 6863 "Refactor StatsCollector and associated types." > > Breaks chrome compilation on Mac: > > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/vector.tcc:252:8: > error: no matching constructor for initialization of > 'webrtc::StatsReport' > _Tp __x_copy = __x; > ^ ~~~ > /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:608:4: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::_M_insert_aux' requested here > _M_insert_aux(end(), __x); > ^ > ../../content/renderer/media/mock_peer_connection_impl.cc:282:11: > note: in instantiation of member function > 'std::vector<webrtc::StatsReport, std::allocator<webrtc::StatsReport> > >::push_back' requested here > reports.push_back(report1); > ^ > ../../third_party/libjingle/source/talk/app/webrtc/statstypes.h:49:3: > note: candidate constructor not viable: requires 0 arguments, but 1 > was provided > StatsReport() : timestamp(0) {} > > > > > Refactor StatsCollector and associated types. > > * Due to the type changes, I'm going to update the OnCompleted event in two phases to sync with Chrome. This is the first phase. > > * Reports are now managed in a set, not a map, since it's enough to store the id in one place. > > * Report ids are now const. > > * Copying of data has been greatly reduced. > > * This change includes preparation work for making GetStats fully async. > > > > This is a reland of r6778 which was reverted due to fyi bots failing. > > I found and fixed the issue which was that in a couple of places I needed to replace a report instead of finding+updating an existing one. > > > > R=xians@webrtc.org > > > > Review URL: https://webrtc-codereview.appspot.com/15119004 > > TBR=tommi@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/21169004 TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/22099004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6908 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-08-15 08:38:30 +00:00
report->AddBoolean(StatsReport::kStatsValueNameInitiator,
session_->initiator());
cricket::SessionStats stats;
if (session_->GetStats(&stats)) {
// Store the proxy map away for use in SSRC reporting.
// TODO(tommi): This shouldn't be necessary if we post the stats back to the
// signaling thread after fetching them on the worker thread, then just use
// the proxy map directly from the session stats.
// As is, if GetStats() failed, we could be using old (incorrect?) proxy
// data.
proxy_to_transport_ = stats.proxy_to_transport;
for (const auto& transport_iter : stats.transport_stats) {
// Attempt to get a copy of the certificates from the transport and
// expose them in stats reports. All channels in a transport share the
// same local and remote certificates.
//
// Note that Transport::GetIdentity and Transport::GetRemoteCertificate
// invoke method calls on the worker thread and block this thread, but
// messages are still processed on this thread, which may blow way the
// existing transports. So we cannot reuse |transport| after these calls.
StatsReport::Id local_cert_report_id, remote_cert_report_id;
cricket::Transport* transport =
session_->GetTransport(transport_iter.second.content_name);
rtc::scoped_ptr<rtc::SSLIdentity> identity;
if (transport && transport->GetIdentity(identity.accept())) {
StatsReport* r = AddCertificateReports(&(identity->certificate()));
if (r)
local_cert_report_id = r->id();
}
transport = session_->GetTransport(transport_iter.second.content_name);
rtc::scoped_ptr<rtc::SSLCertificate> cert;
if (transport && transport->GetRemoteCertificate(cert.accept())) {
StatsReport* r = AddCertificateReports(cert.get());
if (r)
remote_cert_report_id = r->id();
}
for (const auto& channel_iter : transport_iter.second.channel_stats) {
StatsReport::Id id(StatsReport::NewComponentId(
transport_iter.second.content_name, channel_iter.component));
StatsReport* channel_report = reports_.ReplaceOrAddNew(id);
channel_report->set_timestamp(stats_gathering_started_);
channel_report->AddInt(StatsReport::kStatsValueNameComponent,
channel_iter.component);
if (local_cert_report_id.get()) {
channel_report->AddId(StatsReport::kStatsValueNameLocalCertificateId,
local_cert_report_id);
}
if (remote_cert_report_id.get()) {
channel_report->AddId(StatsReport::kStatsValueNameRemoteCertificateId,
remote_cert_report_id);
}
const std::string& srtp_cipher = channel_iter.srtp_cipher;
if (!srtp_cipher.empty()) {
channel_report->AddString(StatsReport::kStatsValueNameSrtpCipher,
srtp_cipher);
}
const std::string& ssl_cipher = channel_iter.ssl_cipher;
if (!ssl_cipher.empty()) {
channel_report->AddString(StatsReport::kStatsValueNameDtlsCipher,
ssl_cipher);
}
int connection_id = 0;
for (const cricket::ConnectionInfo& info :
channel_iter.connection_infos) {
StatsReport* connection_report = AddConnectionInfoReport(
transport_iter.first, channel_iter.component, connection_id++,
channel_report->id(), info);
if (info.best_connection) {
channel_report->AddId(
StatsReport::kStatsValueNameSelectedCandidatePairId,
connection_report->id());
}
}
}
}
}
}
void StatsCollector::ExtractVoiceInfo() {
DCHECK(session_->signaling_thread()->IsCurrent());
if (!session_->voice_channel()) {
return;
}
cricket::VoiceMediaInfo voice_info;
if (!session_->voice_channel()->GetStats(&voice_info)) {
LOG(LS_ERROR) << "Failed to get voice channel stats.";
return;
}
// TODO(tommi): The above code should run on the worker thread and post the
// results back to the signaling thread, where we can add data to the reports.
rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
StatsReport::Id transport_id(GetTransportIdFromProxy(proxy_to_transport_,
session_->voice_channel()->content_name()));
if (!transport_id.get()) {
LOG(LS_ERROR) << "Failed to get transport name for proxy "
<< session_->voice_channel()->content_name();
return;
}
ExtractStatsFromList(voice_info.receivers, transport_id, this,
StatsReport::kReceive);
ExtractStatsFromList(voice_info.senders, transport_id, this,
StatsReport::kSend);
UpdateStatsFromExistingLocalAudioTracks();
}
void StatsCollector::ExtractVideoInfo(
PeerConnectionInterface::StatsOutputLevel level) {
DCHECK(session_->signaling_thread()->IsCurrent());
if (!session_->video_channel())
return;
cricket::VideoMediaInfo video_info;
if (!session_->video_channel()->GetStats(&video_info)) {
LOG(LS_ERROR) << "Failed to get video channel stats.";
return;
}
// TODO(tommi): The above code should run on the worker thread and post the
// results back to the signaling thread, where we can add data to the reports.
rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
StatsReport::Id transport_id(GetTransportIdFromProxy(proxy_to_transport_,
session_->video_channel()->content_name()));
if (!transport_id.get()) {
LOG(LS_ERROR) << "Failed to get transport name for proxy "
<< session_->video_channel()->content_name();
return;
}
ExtractStatsFromList(video_info.receivers, transport_id, this,
StatsReport::kReceive);
ExtractStatsFromList(video_info.senders, transport_id, this,
StatsReport::kSend);
if (video_info.bw_estimations.size() != 1) {
LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
} else {
StatsReport::Id report_id(StatsReport::NewBandwidthEstimationId());
StatsReport* report = reports_.FindOrAddNew(report_id);
ExtractStats(
video_info.bw_estimations[0], stats_gathering_started_, level, report);
}
}
void StatsCollector::ExtractDataInfo() {
DCHECK(session_->signaling_thread()->IsCurrent());
rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
for (const auto& dc :
session_->mediastream_signaling()->sctp_data_channels()) {
StatsReport::Id id(StatsReport::NewTypedIntId(
StatsReport::kStatsReportTypeDataChannel, dc->id()));
StatsReport* report = reports_.ReplaceOrAddNew(id);
report->set_timestamp(stats_gathering_started_);
report->AddString(StatsReport::kStatsValueNameLabel, dc->label());
report->AddInt(StatsReport::kStatsValueNameDataChannelId, dc->id());
report->AddString(StatsReport::kStatsValueNameProtocol, dc->protocol());
report->AddString(StatsReport::kStatsValueNameState,
DataChannelInterface::DataStateString(dc->state()));
}
}
StatsReport* StatsCollector::GetReport(const StatsReport::StatsType& type,
const std::string& id,
StatsReport::Direction direction) {
DCHECK(session_->signaling_thread()->IsCurrent());
DCHECK(type == StatsReport::kStatsReportTypeSsrc ||
type == StatsReport::kStatsReportTypeRemoteSsrc);
return reports_.Find(StatsReport::NewIdWithDirection(type, id, direction));
}
void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() {
DCHECK(session_->signaling_thread()->IsCurrent());
// Loop through the existing local audio tracks.
for (const auto& it : local_audio_tracks_) {
AudioTrackInterface* track = it.first;
uint32 ssrc = it.second;
StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc,
rtc::ToString<uint32>(ssrc),
StatsReport::kSend);
if (report == NULL) {
// This can happen if a local audio track is added to a stream on the
// fly and the report has not been set up yet. Do nothing in this case.
LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc;
continue;
}
// The same ssrc can be used by both local and remote audio tracks.
const StatsReport::Value* v =
report->FindValue(StatsReport::kStatsValueNameTrackId);
if (!v || v->string_val() != track->id())
continue;
UpdateReportFromAudioTrack(track, report);
}
}
void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track,
StatsReport* report) {
DCHECK(session_->signaling_thread()->IsCurrent());
DCHECK(track != NULL);
int signal_level = 0;
if (!track->GetSignalLevel(&signal_level))
signal_level = -1;
rtc::scoped_refptr<AudioProcessorInterface> audio_processor(
track->GetAudioProcessor());
AudioProcessorInterface::AudioProcessorStats stats;
if (audio_processor.get())
audio_processor->GetStats(&stats);
SetAudioProcessingStats(report, signal_level, stats.typing_noise_detected,
stats.echo_return_loss, stats.echo_return_loss_enhancement,
stats.echo_delay_median_ms, stats.aec_quality_min,
stats.echo_delay_std_ms);
}
bool StatsCollector::GetTrackIdBySsrc(uint32 ssrc, std::string* track_id,
StatsReport::Direction direction) {
DCHECK(session_->signaling_thread()->IsCurrent());
if (direction == StatsReport::kSend) {
if (!session_->GetLocalTrackIdBySsrc(ssrc, track_id)) {
LOG(LS_WARNING) << "The SSRC " << ssrc
<< " is not associated with a sending track";
return false;
}
} else {
DCHECK(direction == StatsReport::kReceive);
if (!session_->GetRemoteTrackIdBySsrc(ssrc, track_id)) {
LOG(LS_WARNING) << "The SSRC " << ssrc
<< " is not associated with a receiving track";
return false;
}
}
return true;
}
void StatsCollector::ClearUpdateStatsCacheForTest() {
stats_gathering_started_ = 0;
}
} // namespace webrtc