blob: 87f1590cb0ce1a42ff6a1e578c70c6f9063ebadd [file] [log] [blame]
/*
* Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "test/pc/e2e/analyzer/video/video_quality_metrics_reporter.h"
#include <map>
#include <string>
#include "api/stats/rtc_stats.h"
#include "api/stats/rtcstats_objects.h"
#include "api/test/metrics/metric.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/checks.h"
#include "test/pc/e2e/metric_metadata_keys.h"
namespace webrtc {
namespace webrtc_pc_e2e {
namespace {
using ::webrtc::test::ImprovementDirection;
using ::webrtc::test::Unit;
using ::webrtc::webrtc_pc_e2e::MetricMetadataKey;
SamplesStatsCounter BytesPerSecondToKbps(const SamplesStatsCounter& counter) {
return counter * 0.008;
}
} // namespace
VideoQualityMetricsReporter::VideoQualityMetricsReporter(
Clock* const clock,
test::MetricsLogger* const metrics_logger)
: clock_(clock), metrics_logger_(metrics_logger) {
RTC_CHECK(metrics_logger_);
}
void VideoQualityMetricsReporter::Start(
absl::string_view test_case_name,
const TrackIdStreamInfoMap* /*reporter_helper*/) {
test_case_name_ = std::string(test_case_name);
start_time_ = Now();
}
void VideoQualityMetricsReporter::OnStatsReports(
absl::string_view pc_label,
const rtc::scoped_refptr<const RTCStatsReport>& report) {
RTC_CHECK(start_time_)
<< "Please invoke Start(...) method before calling OnStatsReports(...)";
auto transport_stats = report->GetStatsOfType<RTCTransportStats>();
if (transport_stats.size() == 0u ||
!transport_stats[0]->selected_candidate_pair_id.has_value()) {
return;
}
RTC_DCHECK_EQ(transport_stats.size(), 1);
std::string selected_ice_id =
transport_stats[0]
->GetAttribute(transport_stats[0]->selected_candidate_pair_id)
.ToString();
// Use the selected ICE candidate pair ID to get the appropriate ICE stats.
const RTCIceCandidatePairStats ice_candidate_pair_stats =
report->Get(selected_ice_id)->cast_to<const RTCIceCandidatePairStats>();
auto outbound_rtp_stats = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
StatsSample sample;
for (auto& s : outbound_rtp_stats) {
if (!s->kind.has_value()) {
continue;
}
if (!(*s->kind == "video")) {
continue;
}
if (s->timestamp() > sample.sample_time) {
sample.sample_time = s->timestamp();
}
sample.retransmitted_bytes_sent +=
DataSize::Bytes(s->retransmitted_bytes_sent.value_or(0ul));
sample.bytes_sent += DataSize::Bytes(s->bytes_sent.value_or(0ul));
sample.header_bytes_sent +=
DataSize::Bytes(s->header_bytes_sent.value_or(0ul));
}
MutexLock lock(&video_bwe_stats_lock_);
VideoBweStats& video_bwe_stats = video_bwe_stats_[std::string(pc_label)];
if (ice_candidate_pair_stats.available_outgoing_bitrate.has_value()) {
video_bwe_stats.available_send_bandwidth.AddSample(
DataRate::BitsPerSec(
*ice_candidate_pair_stats.available_outgoing_bitrate)
.bytes_per_sec());
}
StatsSample prev_sample = last_stats_sample_[std::string(pc_label)];
if (prev_sample.sample_time.IsZero()) {
prev_sample.sample_time = start_time_.value();
}
last_stats_sample_[std::string(pc_label)] = sample;
TimeDelta time_between_samples = sample.sample_time - prev_sample.sample_time;
if (time_between_samples.IsZero()) {
return;
}
DataRate retransmission_bitrate =
(sample.retransmitted_bytes_sent - prev_sample.retransmitted_bytes_sent) /
time_between_samples;
video_bwe_stats.retransmission_bitrate.AddSample(
retransmission_bitrate.bytes_per_sec());
DataRate transmission_bitrate =
(sample.bytes_sent + sample.header_bytes_sent - prev_sample.bytes_sent -
prev_sample.header_bytes_sent) /
time_between_samples;
video_bwe_stats.transmission_bitrate.AddSample(
transmission_bitrate.bytes_per_sec());
}
void VideoQualityMetricsReporter::StopAndReportResults() {
MutexLock video_bwemutex_(&video_bwe_stats_lock_);
for (const auto& item : video_bwe_stats_) {
ReportVideoBweResults(item.first, item.second);
}
}
std::string VideoQualityMetricsReporter::GetTestCaseName(
const std::string& peer_name) const {
return test_case_name_ + "/" + peer_name;
}
void VideoQualityMetricsReporter::ReportVideoBweResults(
const std::string& peer_name,
const VideoBweStats& video_bwe_stats) {
std::string test_case_name = GetTestCaseName(peer_name);
// TODO(bugs.webrtc.org/14757): Remove kExperimentalTestNameMetadataKey.
std::map<std::string, std::string> metric_metadata{
{MetricMetadataKey::kPeerMetadataKey, peer_name},
{MetricMetadataKey::kExperimentalTestNameMetadataKey, test_case_name_}};
metrics_logger_->LogMetric(
"available_send_bandwidth", test_case_name,
BytesPerSecondToKbps(video_bwe_stats.available_send_bandwidth),
Unit::kKilobitsPerSecond, ImprovementDirection::kNeitherIsBetter,
metric_metadata);
metrics_logger_->LogMetric(
"transmission_bitrate", test_case_name,
BytesPerSecondToKbps(video_bwe_stats.transmission_bitrate),
Unit::kKilobitsPerSecond, ImprovementDirection::kNeitherIsBetter,
metric_metadata);
metrics_logger_->LogMetric(
"retransmission_bitrate", test_case_name,
BytesPerSecondToKbps(video_bwe_stats.retransmission_bitrate),
Unit::kKilobitsPerSecond, ImprovementDirection::kNeitherIsBetter,
metric_metadata);
}
} // namespace webrtc_pc_e2e
} // namespace webrtc