| OLD | NEW |
| 1 /* | 1 /* |
| 2 * libjingle | 2 * libjingle |
| 3 * Copyright 2012 Google Inc. | 3 * Copyright 2012 Google Inc. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
| 9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 */ | 26 */ |
| 27 | 27 |
| 28 #include "talk/app/webrtc/statscollector.h" | 28 #include "talk/app/webrtc/statscollector.h" |
| 29 | 29 |
| 30 #include <utility> | 30 #include <utility> |
| 31 #include <vector> | 31 #include <vector> |
| 32 | 32 |
| 33 #include "talk/app/webrtc/peerconnection.h" | |
| 34 #include "talk/session/media/channel.h" | 33 #include "talk/session/media/channel.h" |
| 35 #include "webrtc/base/base64.h" | 34 #include "webrtc/base/base64.h" |
| 36 #include "webrtc/base/checks.h" | 35 #include "webrtc/base/checks.h" |
| 37 #include "webrtc/base/scoped_ptr.h" | 36 #include "webrtc/base/scoped_ptr.h" |
| 38 #include "webrtc/base/timing.h" | 37 #include "webrtc/base/timing.h" |
| 39 | 38 |
| 40 using rtc::scoped_ptr; | 39 using rtc::scoped_ptr; |
| 41 | 40 |
| 42 namespace webrtc { | 41 namespace webrtc { |
| 43 namespace { | 42 namespace { |
| (...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 350 case rtc::ADAPTER_TYPE_VPN: | 349 case rtc::ADAPTER_TYPE_VPN: |
| 351 return STATSREPORT_ADAPTER_TYPE_VPN; | 350 return STATSREPORT_ADAPTER_TYPE_VPN; |
| 352 case rtc::ADAPTER_TYPE_LOOPBACK: | 351 case rtc::ADAPTER_TYPE_LOOPBACK: |
| 353 return STATSREPORT_ADAPTER_TYPE_LOOPBACK; | 352 return STATSREPORT_ADAPTER_TYPE_LOOPBACK; |
| 354 default: | 353 default: |
| 355 RTC_DCHECK(false); | 354 RTC_DCHECK(false); |
| 356 return ""; | 355 return ""; |
| 357 } | 356 } |
| 358 } | 357 } |
| 359 | 358 |
| 360 StatsCollector::StatsCollector(PeerConnection* pc) | 359 StatsCollector::StatsCollector(WebRtcSession* session) |
| 361 : pc_(pc), stats_gathering_started_(0) { | 360 : session_(session), |
| 362 RTC_DCHECK(pc_); | 361 stats_gathering_started_(0) { |
| 362 RTC_DCHECK(session_); |
| 363 } | 363 } |
| 364 | 364 |
| 365 StatsCollector::~StatsCollector() { | 365 StatsCollector::~StatsCollector() { |
| 366 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 366 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 367 } | 367 } |
| 368 | 368 |
| 369 double StatsCollector::GetTimeNow() { | 369 double StatsCollector::GetTimeNow() { |
| 370 return rtc::Timing::WallTimeNow() * rtc::kNumMillisecsPerSec; | 370 return rtc::Timing::WallTimeNow() * rtc::kNumMillisecsPerSec; |
| 371 } | 371 } |
| 372 | 372 |
| 373 // Adds a MediaStream with tracks that can be used as a |selector| in a call | 373 // Adds a MediaStream with tracks that can be used as a |selector| in a call |
| 374 // to GetStats. | 374 // to GetStats. |
| 375 void StatsCollector::AddStream(MediaStreamInterface* stream) { | 375 void StatsCollector::AddStream(MediaStreamInterface* stream) { |
| 376 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 376 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 377 RTC_DCHECK(stream != NULL); | 377 RTC_DCHECK(stream != NULL); |
| 378 | 378 |
| 379 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(), | 379 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(), |
| 380 &reports_, track_ids_); | 380 &reports_, track_ids_); |
| 381 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(), | 381 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(), |
| 382 &reports_, track_ids_); | 382 &reports_, track_ids_); |
| 383 } | 383 } |
| 384 | 384 |
| 385 void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track, | 385 void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track, |
| 386 uint32_t ssrc) { | 386 uint32_t ssrc) { |
| 387 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 387 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 388 RTC_DCHECK(audio_track != NULL); | 388 RTC_DCHECK(audio_track != NULL); |
| 389 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) | 389 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) |
| 390 for (const auto& track : local_audio_tracks_) | 390 for (const auto& track : local_audio_tracks_) |
| 391 RTC_DCHECK(track.first != audio_track || track.second != ssrc); | 391 RTC_DCHECK(track.first != audio_track || track.second != ssrc); |
| 392 #endif | 392 #endif |
| 393 | 393 |
| 394 local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc)); | 394 local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc)); |
| 395 | 395 |
| 396 // Create the kStatsReportTypeTrack report for the new track if there is no | 396 // Create the kStatsReportTypeTrack report for the new track if there is no |
| 397 // report yet. | 397 // report yet. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 409 RTC_DCHECK(audio_track != NULL); | 409 RTC_DCHECK(audio_track != NULL); |
| 410 local_audio_tracks_.erase(std::remove_if(local_audio_tracks_.begin(), | 410 local_audio_tracks_.erase(std::remove_if(local_audio_tracks_.begin(), |
| 411 local_audio_tracks_.end(), | 411 local_audio_tracks_.end(), |
| 412 [audio_track, ssrc](const LocalAudioTrackVector::value_type& track) { | 412 [audio_track, ssrc](const LocalAudioTrackVector::value_type& track) { |
| 413 return track.first == audio_track && track.second == ssrc; | 413 return track.first == audio_track && track.second == ssrc; |
| 414 })); | 414 })); |
| 415 } | 415 } |
| 416 | 416 |
| 417 void StatsCollector::GetStats(MediaStreamTrackInterface* track, | 417 void StatsCollector::GetStats(MediaStreamTrackInterface* track, |
| 418 StatsReports* reports) { | 418 StatsReports* reports) { |
| 419 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 419 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 420 RTC_DCHECK(reports != NULL); | 420 RTC_DCHECK(reports != NULL); |
| 421 RTC_DCHECK(reports->empty()); | 421 RTC_DCHECK(reports->empty()); |
| 422 | 422 |
| 423 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; | 423 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |
| 424 | 424 |
| 425 if (!track) { | 425 if (!track) { |
| 426 reports->reserve(reports_.size()); | 426 reports->reserve(reports_.size()); |
| 427 for (auto* r : reports_) | 427 for (auto* r : reports_) |
| 428 reports->push_back(r); | 428 reports->push_back(r); |
| 429 return; | 429 return; |
| 430 } | 430 } |
| 431 | 431 |
| 432 StatsReport* report = reports_.Find(StatsReport::NewTypedId( | 432 StatsReport* report = reports_.Find(StatsReport::NewTypedId( |
| 433 StatsReport::kStatsReportTypeSession, pc_->session()->id())); | 433 StatsReport::kStatsReportTypeSession, session_->id())); |
| 434 if (report) | 434 if (report) |
| 435 reports->push_back(report); | 435 reports->push_back(report); |
| 436 | 436 |
| 437 report = reports_.Find(StatsReport::NewTypedId( | 437 report = reports_.Find(StatsReport::NewTypedId( |
| 438 StatsReport::kStatsReportTypeTrack, track->id())); | 438 StatsReport::kStatsReportTypeTrack, track->id())); |
| 439 | 439 |
| 440 if (!report) | 440 if (!report) |
| 441 return; | 441 return; |
| 442 | 442 |
| 443 reports->push_back(report); | 443 reports->push_back(report); |
| 444 | 444 |
| 445 std::string track_id; | 445 std::string track_id; |
| 446 for (const auto* r : reports_) { | 446 for (const auto* r : reports_) { |
| 447 if (r->type() != StatsReport::kStatsReportTypeSsrc) | 447 if (r->type() != StatsReport::kStatsReportTypeSsrc) |
| 448 continue; | 448 continue; |
| 449 | 449 |
| 450 const StatsReport::Value* v = | 450 const StatsReport::Value* v = |
| 451 r->FindValue(StatsReport::kStatsValueNameTrackId); | 451 r->FindValue(StatsReport::kStatsValueNameTrackId); |
| 452 if (v && v->string_val() == track->id()) | 452 if (v && v->string_val() == track->id()) |
| 453 reports->push_back(r); | 453 reports->push_back(r); |
| 454 } | 454 } |
| 455 } | 455 } |
| 456 | 456 |
| 457 void | 457 void |
| 458 StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) { | 458 StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) { |
| 459 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 459 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 460 double time_now = GetTimeNow(); | 460 double time_now = GetTimeNow(); |
| 461 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of | 461 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of |
| 462 // ms apart will be ignored. | 462 // ms apart will be ignored. |
| 463 const double kMinGatherStatsPeriod = 50; | 463 const double kMinGatherStatsPeriod = 50; |
| 464 if (stats_gathering_started_ != 0 && | 464 if (stats_gathering_started_ != 0 && |
| 465 stats_gathering_started_ + kMinGatherStatsPeriod > time_now) { | 465 stats_gathering_started_ + kMinGatherStatsPeriod > time_now) { |
| 466 return; | 466 return; |
| 467 } | 467 } |
| 468 stats_gathering_started_ = time_now; | 468 stats_gathering_started_ = time_now; |
| 469 | 469 |
| 470 if (pc_->session()) { | 470 if (session_) { |
| 471 // TODO(tommi): All of these hop over to the worker thread to fetch | 471 // TODO(tommi): All of these hop over to the worker thread to fetch |
| 472 // information. We could use an AsyncInvoker to run all of these and post | 472 // information. We could use an AsyncInvoker to run all of these and post |
| 473 // the information back to the signaling thread where we can create and | 473 // the information back to the signaling thread where we can create and |
| 474 // update stats reports. That would also clean up the threading story a bit | 474 // update stats reports. That would also clean up the threading story a bit |
| 475 // since we'd be creating/updating the stats report objects consistently on | 475 // since we'd be creating/updating the stats report objects consistently on |
| 476 // the same thread (this class has no locks right now). | 476 // the same thread (this class has no locks right now). |
| 477 ExtractSessionInfo(); | 477 ExtractSessionInfo(); |
| 478 ExtractVoiceInfo(); | 478 ExtractVoiceInfo(); |
| 479 ExtractVideoInfo(level); | 479 ExtractVideoInfo(level); |
| 480 ExtractDataInfo(); | 480 ExtractDataInfo(); |
| 481 UpdateTrackReports(); | 481 UpdateTrackReports(); |
| 482 } | 482 } |
| 483 } | 483 } |
| 484 | 484 |
| 485 StatsReport* StatsCollector::PrepareReport( | 485 StatsReport* StatsCollector::PrepareReport(bool local, |
| 486 bool local, | 486 uint32_t ssrc, |
| 487 uint32_t ssrc, | 487 const StatsReport::Id& transport_id, |
| 488 const StatsReport::Id& transport_id, | 488 StatsReport::Direction direction) { |
| 489 StatsReport::Direction direction) { | 489 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 490 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | |
| 491 StatsReport::Id id(StatsReport::NewIdWithDirection( | 490 StatsReport::Id id(StatsReport::NewIdWithDirection( |
| 492 local ? StatsReport::kStatsReportTypeSsrc | 491 local ? StatsReport::kStatsReportTypeSsrc |
| 493 : StatsReport::kStatsReportTypeRemoteSsrc, | 492 : StatsReport::kStatsReportTypeRemoteSsrc, |
| 494 rtc::ToString<uint32_t>(ssrc), direction)); | 493 rtc::ToString<uint32_t>(ssrc), direction)); |
| 495 StatsReport* report = reports_.Find(id); | 494 StatsReport* report = reports_.Find(id); |
| 496 | 495 |
| 497 // Use the ID of the track that is currently mapped to the SSRC, if any. | 496 // Use the ID of the track that is currently mapped to the SSRC, if any. |
| 498 std::string track_id; | 497 std::string track_id; |
| 499 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) { | 498 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) { |
| 500 if (!report) { | 499 if (!report) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 519 | 518 |
| 520 report->AddInt64(StatsReport::kStatsValueNameSsrc, ssrc); | 519 report->AddInt64(StatsReport::kStatsValueNameSsrc, ssrc); |
| 521 report->AddString(StatsReport::kStatsValueNameTrackId, track_id); | 520 report->AddString(StatsReport::kStatsValueNameTrackId, track_id); |
| 522 // Add the mapping of SSRC to transport. | 521 // Add the mapping of SSRC to transport. |
| 523 report->AddId(StatsReport::kStatsValueNameTransportId, transport_id); | 522 report->AddId(StatsReport::kStatsValueNameTransportId, transport_id); |
| 524 return report; | 523 return report; |
| 525 } | 524 } |
| 526 | 525 |
| 527 StatsReport* StatsCollector::AddOneCertificateReport( | 526 StatsReport* StatsCollector::AddOneCertificateReport( |
| 528 const rtc::SSLCertificate* cert, const StatsReport* issuer) { | 527 const rtc::SSLCertificate* cert, const StatsReport* issuer) { |
| 529 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 528 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 530 | 529 |
| 531 // TODO(bemasc): Move this computation to a helper class that caches these | 530 // TODO(bemasc): Move this computation to a helper class that caches these |
| 532 // values to reduce CPU use in GetStats. This will require adding a fast | 531 // values to reduce CPU use in GetStats. This will require adding a fast |
| 533 // SSLCertificate::Equals() method to detect certificate changes. | 532 // SSLCertificate::Equals() method to detect certificate changes. |
| 534 | 533 |
| 535 std::string digest_algorithm; | 534 std::string digest_algorithm; |
| 536 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm)) | 535 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm)) |
| 537 return nullptr; | 536 return nullptr; |
| 538 | 537 |
| 539 rtc::scoped_ptr<rtc::SSLFingerprint> ssl_fingerprint( | 538 rtc::scoped_ptr<rtc::SSLFingerprint> ssl_fingerprint( |
| (...skipping 22 matching lines...) Expand all Loading... |
| 562 report->AddString(StatsReport::kStatsValueNameFingerprintAlgorithm, | 561 report->AddString(StatsReport::kStatsValueNameFingerprintAlgorithm, |
| 563 digest_algorithm); | 562 digest_algorithm); |
| 564 report->AddString(StatsReport::kStatsValueNameDer, der_base64); | 563 report->AddString(StatsReport::kStatsValueNameDer, der_base64); |
| 565 if (issuer) | 564 if (issuer) |
| 566 report->AddId(StatsReport::kStatsValueNameIssuerId, issuer->id()); | 565 report->AddId(StatsReport::kStatsValueNameIssuerId, issuer->id()); |
| 567 return report; | 566 return report; |
| 568 } | 567 } |
| 569 | 568 |
| 570 StatsReport* StatsCollector::AddCertificateReports( | 569 StatsReport* StatsCollector::AddCertificateReports( |
| 571 const rtc::SSLCertificate* cert) { | 570 const rtc::SSLCertificate* cert) { |
| 572 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 571 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 573 // Produces a chain of StatsReports representing this certificate and the rest | 572 // Produces a chain of StatsReports representing this certificate and the rest |
| 574 // of its chain, and adds those reports to |reports_|. The return value is | 573 // of its chain, and adds those reports to |reports_|. The return value is |
| 575 // the id of the leaf report. The provided cert must be non-null, so at least | 574 // the id of the leaf report. The provided cert must be non-null, so at least |
| 576 // one report will always be provided and the returned string will never be | 575 // one report will always be provided and the returned string will never be |
| 577 // empty. | 576 // empty. |
| 578 RTC_DCHECK(cert != NULL); | 577 RTC_DCHECK(cert != NULL); |
| 579 | 578 |
| 580 StatsReport* issuer = nullptr; | 579 StatsReport* issuer = nullptr; |
| 581 rtc::scoped_ptr<rtc::SSLCertChain> chain; | 580 rtc::scoped_ptr<rtc::SSLCertChain> chain; |
| 582 if (cert->GetChain(chain.accept())) { | 581 if (cert->GetChain(chain.accept())) { |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 662 report->AddString(StatsReport::kStatsValueNameCandidateType, | 661 report->AddString(StatsReport::kStatsValueNameCandidateType, |
| 663 IceCandidateTypeToStatsType(candidate.type())); | 662 IceCandidateTypeToStatsType(candidate.type())); |
| 664 report->AddString(StatsReport::kStatsValueNameCandidateTransportType, | 663 report->AddString(StatsReport::kStatsValueNameCandidateTransportType, |
| 665 candidate.protocol()); | 664 candidate.protocol()); |
| 666 } | 665 } |
| 667 | 666 |
| 668 return report; | 667 return report; |
| 669 } | 668 } |
| 670 | 669 |
| 671 void StatsCollector::ExtractSessionInfo() { | 670 void StatsCollector::ExtractSessionInfo() { |
| 672 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 671 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 673 | 672 |
| 674 // Extract information from the base session. | 673 // Extract information from the base session. |
| 675 StatsReport::Id id(StatsReport::NewTypedId( | 674 StatsReport::Id id(StatsReport::NewTypedId( |
| 676 StatsReport::kStatsReportTypeSession, pc_->session()->id())); | 675 StatsReport::kStatsReportTypeSession, session_->id())); |
| 677 StatsReport* report = reports_.ReplaceOrAddNew(id); | 676 StatsReport* report = reports_.ReplaceOrAddNew(id); |
| 678 report->set_timestamp(stats_gathering_started_); | 677 report->set_timestamp(stats_gathering_started_); |
| 679 report->AddBoolean(StatsReport::kStatsValueNameInitiator, | 678 report->AddBoolean(StatsReport::kStatsValueNameInitiator, |
| 680 pc_->session()->initiator()); | 679 session_->initiator()); |
| 681 | 680 |
| 682 cricket::SessionStats stats; | 681 cricket::SessionStats stats; |
| 683 if (!pc_->session()->GetTransportStats(&stats)) { | 682 if (!session_->GetTransportStats(&stats)) { |
| 684 return; | 683 return; |
| 685 } | 684 } |
| 686 | 685 |
| 687 // Store the proxy map away for use in SSRC reporting. | 686 // Store the proxy map away for use in SSRC reporting. |
| 688 // TODO(tommi): This shouldn't be necessary if we post the stats back to the | 687 // TODO(tommi): This shouldn't be necessary if we post the stats back to the |
| 689 // signaling thread after fetching them on the worker thread, then just use | 688 // signaling thread after fetching them on the worker thread, then just use |
| 690 // the proxy map directly from the session stats. | 689 // the proxy map directly from the session stats. |
| 691 // As is, if GetStats() failed, we could be using old (incorrect?) proxy | 690 // As is, if GetStats() failed, we could be using old (incorrect?) proxy |
| 692 // data. | 691 // data. |
| 693 proxy_to_transport_ = stats.proxy_to_transport; | 692 proxy_to_transport_ = stats.proxy_to_transport; |
| 694 | 693 |
| 695 for (const auto& transport_iter : stats.transport_stats) { | 694 for (const auto& transport_iter : stats.transport_stats) { |
| 696 // Attempt to get a copy of the certificates from the transport and | 695 // Attempt to get a copy of the certificates from the transport and |
| 697 // expose them in stats reports. All channels in a transport share the | 696 // expose them in stats reports. All channels in a transport share the |
| 698 // same local and remote certificates. | 697 // same local and remote certificates. |
| 699 // | 698 // |
| 700 StatsReport::Id local_cert_report_id, remote_cert_report_id; | 699 StatsReport::Id local_cert_report_id, remote_cert_report_id; |
| 701 rtc::scoped_refptr<rtc::RTCCertificate> certificate; | 700 rtc::scoped_refptr<rtc::RTCCertificate> certificate; |
| 702 if (pc_->session()->GetLocalCertificate( | 701 if (session_->GetLocalCertificate(transport_iter.second.transport_name, |
| 703 transport_iter.second.transport_name, &certificate)) { | 702 &certificate)) { |
| 704 StatsReport* r = AddCertificateReports(&(certificate->ssl_certificate())); | 703 StatsReport* r = AddCertificateReports(&(certificate->ssl_certificate())); |
| 705 if (r) | 704 if (r) |
| 706 local_cert_report_id = r->id(); | 705 local_cert_report_id = r->id(); |
| 707 } | 706 } |
| 708 | 707 |
| 709 rtc::scoped_ptr<rtc::SSLCertificate> cert; | 708 rtc::scoped_ptr<rtc::SSLCertificate> cert; |
| 710 if (pc_->session()->GetRemoteSSLCertificate( | 709 if (session_->GetRemoteSSLCertificate(transport_iter.second.transport_name, |
| 711 transport_iter.second.transport_name, cert.accept())) { | 710 cert.accept())) { |
| 712 StatsReport* r = AddCertificateReports(cert.get()); | 711 StatsReport* r = AddCertificateReports(cert.get()); |
| 713 if (r) | 712 if (r) |
| 714 remote_cert_report_id = r->id(); | 713 remote_cert_report_id = r->id(); |
| 715 } | 714 } |
| 716 | 715 |
| 717 for (const auto& channel_iter : transport_iter.second.channel_stats) { | 716 for (const auto& channel_iter : transport_iter.second.channel_stats) { |
| 718 StatsReport::Id id(StatsReport::NewComponentId( | 717 StatsReport::Id id(StatsReport::NewComponentId( |
| 719 transport_iter.second.transport_name, channel_iter.component)); | 718 transport_iter.second.transport_name, channel_iter.component)); |
| 720 StatsReport* channel_report = reports_.ReplaceOrAddNew(id); | 719 StatsReport* channel_report = reports_.ReplaceOrAddNew(id); |
| 721 channel_report->set_timestamp(stats_gathering_started_); | 720 channel_report->set_timestamp(stats_gathering_started_); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 752 channel_report->AddId( | 751 channel_report->AddId( |
| 753 StatsReport::kStatsValueNameSelectedCandidatePairId, | 752 StatsReport::kStatsValueNameSelectedCandidatePairId, |
| 754 connection_report->id()); | 753 connection_report->id()); |
| 755 } | 754 } |
| 756 } | 755 } |
| 757 } | 756 } |
| 758 } | 757 } |
| 759 } | 758 } |
| 760 | 759 |
| 761 void StatsCollector::ExtractVoiceInfo() { | 760 void StatsCollector::ExtractVoiceInfo() { |
| 762 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 761 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 763 | 762 |
| 764 if (!pc_->session()->voice_channel()) { | 763 if (!session_->voice_channel()) { |
| 765 return; | 764 return; |
| 766 } | 765 } |
| 767 cricket::VoiceMediaInfo voice_info; | 766 cricket::VoiceMediaInfo voice_info; |
| 768 if (!pc_->session()->voice_channel()->GetStats(&voice_info)) { | 767 if (!session_->voice_channel()->GetStats(&voice_info)) { |
| 769 LOG(LS_ERROR) << "Failed to get voice channel stats."; | 768 LOG(LS_ERROR) << "Failed to get voice channel stats."; |
| 770 return; | 769 return; |
| 771 } | 770 } |
| 772 | 771 |
| 773 // TODO(tommi): The above code should run on the worker thread and post the | 772 // TODO(tommi): The above code should run on the worker thread and post the |
| 774 // results back to the signaling thread, where we can add data to the reports. | 773 // results back to the signaling thread, where we can add data to the reports. |
| 775 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; | 774 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |
| 776 | 775 |
| 777 StatsReport::Id transport_id(GetTransportIdFromProxy( | 776 StatsReport::Id transport_id(GetTransportIdFromProxy(proxy_to_transport_, |
| 778 proxy_to_transport_, pc_->session()->voice_channel()->content_name())); | 777 session_->voice_channel()->content_name())); |
| 779 if (!transport_id.get()) { | 778 if (!transport_id.get()) { |
| 780 LOG(LS_ERROR) << "Failed to get transport name for proxy " | 779 LOG(LS_ERROR) << "Failed to get transport name for proxy " |
| 781 << pc_->session()->voice_channel()->content_name(); | 780 << session_->voice_channel()->content_name(); |
| 782 return; | 781 return; |
| 783 } | 782 } |
| 784 | 783 |
| 785 ExtractStatsFromList(voice_info.receivers, transport_id, this, | 784 ExtractStatsFromList(voice_info.receivers, transport_id, this, |
| 786 StatsReport::kReceive); | 785 StatsReport::kReceive); |
| 787 ExtractStatsFromList(voice_info.senders, transport_id, this, | 786 ExtractStatsFromList(voice_info.senders, transport_id, this, |
| 788 StatsReport::kSend); | 787 StatsReport::kSend); |
| 789 | 788 |
| 790 UpdateStatsFromExistingLocalAudioTracks(); | 789 UpdateStatsFromExistingLocalAudioTracks(); |
| 791 } | 790 } |
| 792 | 791 |
| 793 void StatsCollector::ExtractVideoInfo( | 792 void StatsCollector::ExtractVideoInfo( |
| 794 PeerConnectionInterface::StatsOutputLevel level) { | 793 PeerConnectionInterface::StatsOutputLevel level) { |
| 795 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 794 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 796 | 795 |
| 797 if (!pc_->session()->video_channel()) | 796 if (!session_->video_channel()) |
| 798 return; | 797 return; |
| 799 | 798 |
| 800 cricket::VideoMediaInfo video_info; | 799 cricket::VideoMediaInfo video_info; |
| 801 if (!pc_->session()->video_channel()->GetStats(&video_info)) { | 800 if (!session_->video_channel()->GetStats(&video_info)) { |
| 802 LOG(LS_ERROR) << "Failed to get video channel stats."; | 801 LOG(LS_ERROR) << "Failed to get video channel stats."; |
| 803 return; | 802 return; |
| 804 } | 803 } |
| 805 | 804 |
| 806 // TODO(tommi): The above code should run on the worker thread and post the | 805 // TODO(tommi): The above code should run on the worker thread and post the |
| 807 // results back to the signaling thread, where we can add data to the reports. | 806 // results back to the signaling thread, where we can add data to the reports. |
| 808 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; | 807 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |
| 809 | 808 |
| 810 StatsReport::Id transport_id(GetTransportIdFromProxy( | 809 StatsReport::Id transport_id(GetTransportIdFromProxy(proxy_to_transport_, |
| 811 proxy_to_transport_, pc_->session()->video_channel()->content_name())); | 810 session_->video_channel()->content_name())); |
| 812 if (!transport_id.get()) { | 811 if (!transport_id.get()) { |
| 813 LOG(LS_ERROR) << "Failed to get transport name for proxy " | 812 LOG(LS_ERROR) << "Failed to get transport name for proxy " |
| 814 << pc_->session()->video_channel()->content_name(); | 813 << session_->video_channel()->content_name(); |
| 815 return; | 814 return; |
| 816 } | 815 } |
| 817 ExtractStatsFromList(video_info.receivers, transport_id, this, | 816 ExtractStatsFromList(video_info.receivers, transport_id, this, |
| 818 StatsReport::kReceive); | 817 StatsReport::kReceive); |
| 819 ExtractStatsFromList(video_info.senders, transport_id, this, | 818 ExtractStatsFromList(video_info.senders, transport_id, this, |
| 820 StatsReport::kSend); | 819 StatsReport::kSend); |
| 821 if (video_info.bw_estimations.size() != 1) { | 820 if (video_info.bw_estimations.size() != 1) { |
| 822 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size(); | 821 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size(); |
| 823 } else { | 822 } else { |
| 824 StatsReport::Id report_id(StatsReport::NewBandwidthEstimationId()); | 823 StatsReport::Id report_id(StatsReport::NewBandwidthEstimationId()); |
| 825 StatsReport* report = reports_.FindOrAddNew(report_id); | 824 StatsReport* report = reports_.FindOrAddNew(report_id); |
| 826 ExtractStats( | 825 ExtractStats( |
| 827 video_info.bw_estimations[0], stats_gathering_started_, level, report); | 826 video_info.bw_estimations[0], stats_gathering_started_, level, report); |
| 828 } | 827 } |
| 829 } | 828 } |
| 830 | 829 |
| 831 void StatsCollector::ExtractDataInfo() { | 830 void StatsCollector::ExtractDataInfo() { |
| 832 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 831 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 833 | 832 |
| 834 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; | 833 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |
| 835 | 834 |
| 836 for (const auto& dc : pc_->sctp_data_channels()) { | 835 for (const auto& dc : |
| 836 session_->mediastream_signaling()->sctp_data_channels()) { |
| 837 StatsReport::Id id(StatsReport::NewTypedIntId( | 837 StatsReport::Id id(StatsReport::NewTypedIntId( |
| 838 StatsReport::kStatsReportTypeDataChannel, dc->id())); | 838 StatsReport::kStatsReportTypeDataChannel, dc->id())); |
| 839 StatsReport* report = reports_.ReplaceOrAddNew(id); | 839 StatsReport* report = reports_.ReplaceOrAddNew(id); |
| 840 report->set_timestamp(stats_gathering_started_); | 840 report->set_timestamp(stats_gathering_started_); |
| 841 report->AddString(StatsReport::kStatsValueNameLabel, dc->label()); | 841 report->AddString(StatsReport::kStatsValueNameLabel, dc->label()); |
| 842 report->AddInt(StatsReport::kStatsValueNameDataChannelId, dc->id()); | 842 report->AddInt(StatsReport::kStatsValueNameDataChannelId, dc->id()); |
| 843 report->AddString(StatsReport::kStatsValueNameProtocol, dc->protocol()); | 843 report->AddString(StatsReport::kStatsValueNameProtocol, dc->protocol()); |
| 844 report->AddString(StatsReport::kStatsValueNameState, | 844 report->AddString(StatsReport::kStatsValueNameState, |
| 845 DataChannelInterface::DataStateString(dc->state())); | 845 DataChannelInterface::DataStateString(dc->state())); |
| 846 } | 846 } |
| 847 } | 847 } |
| 848 | 848 |
| 849 StatsReport* StatsCollector::GetReport(const StatsReport::StatsType& type, | 849 StatsReport* StatsCollector::GetReport(const StatsReport::StatsType& type, |
| 850 const std::string& id, | 850 const std::string& id, |
| 851 StatsReport::Direction direction) { | 851 StatsReport::Direction direction) { |
| 852 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 852 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 853 RTC_DCHECK(type == StatsReport::kStatsReportTypeSsrc || | 853 RTC_DCHECK(type == StatsReport::kStatsReportTypeSsrc || |
| 854 type == StatsReport::kStatsReportTypeRemoteSsrc); | 854 type == StatsReport::kStatsReportTypeRemoteSsrc); |
| 855 return reports_.Find(StatsReport::NewIdWithDirection(type, id, direction)); | 855 return reports_.Find(StatsReport::NewIdWithDirection(type, id, direction)); |
| 856 } | 856 } |
| 857 | 857 |
| 858 void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() { | 858 void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() { |
| 859 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 859 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 860 // Loop through the existing local audio tracks. | 860 // Loop through the existing local audio tracks. |
| 861 for (const auto& it : local_audio_tracks_) { | 861 for (const auto& it : local_audio_tracks_) { |
| 862 AudioTrackInterface* track = it.first; | 862 AudioTrackInterface* track = it.first; |
| 863 uint32_t ssrc = it.second; | 863 uint32_t ssrc = it.second; |
| 864 StatsReport* report = | 864 StatsReport* report = |
| 865 GetReport(StatsReport::kStatsReportTypeSsrc, | 865 GetReport(StatsReport::kStatsReportTypeSsrc, |
| 866 rtc::ToString<uint32_t>(ssrc), StatsReport::kSend); | 866 rtc::ToString<uint32_t>(ssrc), StatsReport::kSend); |
| 867 if (report == NULL) { | 867 if (report == NULL) { |
| 868 // This can happen if a local audio track is added to a stream on the | 868 // This can happen if a local audio track is added to a stream on the |
| 869 // fly and the report has not been set up yet. Do nothing in this case. | 869 // fly and the report has not been set up yet. Do nothing in this case. |
| 870 LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc; | 870 LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc; |
| 871 continue; | 871 continue; |
| 872 } | 872 } |
| 873 | 873 |
| 874 // The same ssrc can be used by both local and remote audio tracks. | 874 // The same ssrc can be used by both local and remote audio tracks. |
| 875 const StatsReport::Value* v = | 875 const StatsReport::Value* v = |
| 876 report->FindValue(StatsReport::kStatsValueNameTrackId); | 876 report->FindValue(StatsReport::kStatsValueNameTrackId); |
| 877 if (!v || v->string_val() != track->id()) | 877 if (!v || v->string_val() != track->id()) |
| 878 continue; | 878 continue; |
| 879 | 879 |
| 880 report->set_timestamp(stats_gathering_started_); | 880 report->set_timestamp(stats_gathering_started_); |
| 881 UpdateReportFromAudioTrack(track, report); | 881 UpdateReportFromAudioTrack(track, report); |
| 882 } | 882 } |
| 883 } | 883 } |
| 884 | 884 |
| 885 void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track, | 885 void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track, |
| 886 StatsReport* report) { | 886 StatsReport* report) { |
| 887 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 887 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 888 RTC_DCHECK(track != NULL); | 888 RTC_DCHECK(track != NULL); |
| 889 | 889 |
| 890 int signal_level = 0; | 890 int signal_level = 0; |
| 891 if (!track->GetSignalLevel(&signal_level)) | 891 if (!track->GetSignalLevel(&signal_level)) |
| 892 signal_level = -1; | 892 signal_level = -1; |
| 893 | 893 |
| 894 rtc::scoped_refptr<AudioProcessorInterface> audio_processor( | 894 rtc::scoped_refptr<AudioProcessorInterface> audio_processor( |
| 895 track->GetAudioProcessor()); | 895 track->GetAudioProcessor()); |
| 896 | 896 |
| 897 AudioProcessorInterface::AudioProcessorStats stats; | 897 AudioProcessorInterface::AudioProcessorStats stats; |
| 898 if (audio_processor.get()) | 898 if (audio_processor.get()) |
| 899 audio_processor->GetStats(&stats); | 899 audio_processor->GetStats(&stats); |
| 900 | 900 |
| 901 SetAudioProcessingStats(report, signal_level, stats.typing_noise_detected, | 901 SetAudioProcessingStats(report, signal_level, stats.typing_noise_detected, |
| 902 stats.echo_return_loss, stats.echo_return_loss_enhancement, | 902 stats.echo_return_loss, stats.echo_return_loss_enhancement, |
| 903 stats.echo_delay_median_ms, stats.aec_quality_min, | 903 stats.echo_delay_median_ms, stats.aec_quality_min, |
| 904 stats.echo_delay_std_ms); | 904 stats.echo_delay_std_ms); |
| 905 } | 905 } |
| 906 | 906 |
| 907 bool StatsCollector::GetTrackIdBySsrc(uint32_t ssrc, | 907 bool StatsCollector::GetTrackIdBySsrc(uint32_t ssrc, |
| 908 std::string* track_id, | 908 std::string* track_id, |
| 909 StatsReport::Direction direction) { | 909 StatsReport::Direction direction) { |
| 910 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 910 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 911 if (direction == StatsReport::kSend) { | 911 if (direction == StatsReport::kSend) { |
| 912 if (!pc_->session()->GetLocalTrackIdBySsrc(ssrc, track_id)) { | 912 if (!session_->GetLocalTrackIdBySsrc(ssrc, track_id)) { |
| 913 LOG(LS_WARNING) << "The SSRC " << ssrc | 913 LOG(LS_WARNING) << "The SSRC " << ssrc |
| 914 << " is not associated with a sending track"; | 914 << " is not associated with a sending track"; |
| 915 return false; | 915 return false; |
| 916 } | 916 } |
| 917 } else { | 917 } else { |
| 918 RTC_DCHECK(direction == StatsReport::kReceive); | 918 RTC_DCHECK(direction == StatsReport::kReceive); |
| 919 if (!pc_->session()->GetRemoteTrackIdBySsrc(ssrc, track_id)) { | 919 if (!session_->GetRemoteTrackIdBySsrc(ssrc, track_id)) { |
| 920 LOG(LS_WARNING) << "The SSRC " << ssrc | 920 LOG(LS_WARNING) << "The SSRC " << ssrc |
| 921 << " is not associated with a receiving track"; | 921 << " is not associated with a receiving track"; |
| 922 return false; | 922 return false; |
| 923 } | 923 } |
| 924 } | 924 } |
| 925 | 925 |
| 926 return true; | 926 return true; |
| 927 } | 927 } |
| 928 | 928 |
| 929 void StatsCollector::UpdateTrackReports() { | 929 void StatsCollector::UpdateTrackReports() { |
| 930 RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); | 930 RTC_DCHECK(session_->signaling_thread()->IsCurrent()); |
| 931 | 931 |
| 932 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; | 932 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |
| 933 | 933 |
| 934 for (const auto& entry : track_ids_) { | 934 for (const auto& entry : track_ids_) { |
| 935 StatsReport* report = entry.second; | 935 StatsReport* report = entry.second; |
| 936 report->set_timestamp(stats_gathering_started_); | 936 report->set_timestamp(stats_gathering_started_); |
| 937 } | 937 } |
| 938 } | 938 } |
| 939 | 939 |
| 940 void StatsCollector::ClearUpdateStatsCacheForTest() { | 940 void StatsCollector::ClearUpdateStatsCacheForTest() { |
| 941 stats_gathering_started_ = 0; | 941 stats_gathering_started_ = 0; |
| 942 } | 942 } |
| 943 | 943 |
| 944 } // namespace webrtc | 944 } // namespace webrtc |
| OLD | NEW |