1 /*
2 * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10 #include "test/pc/e2e/cross_media_metrics_reporter.h"
11
12 #include <utility>
13 #include <vector>
14
15 #include "api/stats/rtc_stats.h"
16 #include "api/stats/rtcstats_objects.h"
17 #include "api/test/metrics/metric.h"
18 #include "api/units/timestamp.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/event.h"
21 #include "system_wrappers/include/field_trial.h"
22 #include "test/pc/e2e/metric_metadata_keys.h"
23
24 namespace webrtc {
25 namespace webrtc_pc_e2e {
26
27 using ::webrtc::test::ImprovementDirection;
28 using ::webrtc::test::Unit;
29
CrossMediaMetricsReporter(test::MetricsLogger * metrics_logger)30 CrossMediaMetricsReporter::CrossMediaMetricsReporter(
31 test::MetricsLogger* metrics_logger)
32 : metrics_logger_(metrics_logger) {
33 RTC_CHECK(metrics_logger_);
34 }
35
Start(absl::string_view test_case_name,const TrackIdStreamInfoMap * reporter_helper)36 void CrossMediaMetricsReporter::Start(
37 absl::string_view test_case_name,
38 const TrackIdStreamInfoMap* reporter_helper) {
39 test_case_name_ = std::string(test_case_name);
40 reporter_helper_ = reporter_helper;
41 }
42
OnStatsReports(absl::string_view pc_label,const rtc::scoped_refptr<const RTCStatsReport> & report)43 void CrossMediaMetricsReporter::OnStatsReports(
44 absl::string_view pc_label,
45 const rtc::scoped_refptr<const RTCStatsReport>& report) {
46 auto inbound_stats = report->GetStatsOfType<RTCInboundRTPStreamStats>();
47 std::map<std::string, std::vector<const RTCInboundRTPStreamStats*>>
48 sync_group_stats;
49 for (const auto& stat : inbound_stats) {
50 auto media_source_stat =
51 report->GetAs<DEPRECATED_RTCMediaStreamTrackStats>(*stat->track_id);
52 if (stat->estimated_playout_timestamp.ValueOrDefault(0.) > 0 &&
53 media_source_stat->track_identifier.is_defined()) {
54 sync_group_stats[reporter_helper_
55 ->GetStreamInfoFromTrackId(
56 *media_source_stat->track_identifier)
57 .sync_group]
58 .push_back(stat);
59 }
60 }
61
62 MutexLock lock(&mutex_);
63 for (const auto& pair : sync_group_stats) {
64 // If there is less than two streams, it is not a sync group.
65 if (pair.second.size() < 2) {
66 continue;
67 }
68 auto sync_group = std::string(pair.first);
69 const RTCInboundRTPStreamStats* audio_stat = pair.second[0];
70 const RTCInboundRTPStreamStats* video_stat = pair.second[1];
71
72 RTC_CHECK(pair.second.size() == 2 && audio_stat->kind.is_defined() &&
73 video_stat->kind.is_defined() &&
74 *audio_stat->kind != *video_stat->kind)
75 << "Sync group should consist of one audio and one video stream.";
76
77 if (*audio_stat->kind == RTCMediaStreamTrackKind::kVideo) {
78 std::swap(audio_stat, video_stat);
79 }
80 // Stream labels of a sync group are same for all polls, so we need it add
81 // it only once.
82 if (stats_info_.find(sync_group) == stats_info_.end()) {
83 auto audio_source_stat =
84 report->GetAs<DEPRECATED_RTCMediaStreamTrackStats>(
85 *audio_stat->track_id);
86 auto video_source_stat =
87 report->GetAs<DEPRECATED_RTCMediaStreamTrackStats>(
88 *video_stat->track_id);
89 // *_source_stat->track_identifier is always defined here because we
90 // checked it while grouping stats.
91 stats_info_[sync_group].audio_stream_info =
92 reporter_helper_->GetStreamInfoFromTrackId(
93 *audio_source_stat->track_identifier);
94 stats_info_[sync_group].video_stream_info =
95 reporter_helper_->GetStreamInfoFromTrackId(
96 *video_source_stat->track_identifier);
97 }
98
99 double audio_video_playout_diff = *audio_stat->estimated_playout_timestamp -
100 *video_stat->estimated_playout_timestamp;
101 if (audio_video_playout_diff > 0) {
102 stats_info_[sync_group].audio_ahead_ms.AddSample(
103 audio_video_playout_diff);
104 stats_info_[sync_group].video_ahead_ms.AddSample(0);
105 } else {
106 stats_info_[sync_group].audio_ahead_ms.AddSample(0);
107 stats_info_[sync_group].video_ahead_ms.AddSample(
108 std::abs(audio_video_playout_diff));
109 }
110 }
111 }
112
StopAndReportResults()113 void CrossMediaMetricsReporter::StopAndReportResults() {
114 MutexLock lock(&mutex_);
115 for (const auto& pair : stats_info_) {
116 const std::string& sync_group = pair.first;
117 std::map<std::string, std::string> audio_metric_metadata{
118 {MetricMetadataKey::kPeerSyncGroupMetadataKey, sync_group},
119 {MetricMetadataKey::kAudioStreamMetadataKey,
120 pair.second.audio_stream_info.stream_label},
121 {MetricMetadataKey::kPeerMetadataKey,
122 pair.second.audio_stream_info.receiver_peer},
123 {MetricMetadataKey::kReceiverMetadataKey,
124 pair.second.audio_stream_info.receiver_peer}};
125 metrics_logger_->LogMetric(
126 "audio_ahead_ms",
127 GetTestCaseName(pair.second.audio_stream_info.stream_label, sync_group),
128 pair.second.audio_ahead_ms, Unit::kMilliseconds,
129 webrtc::test::ImprovementDirection::kSmallerIsBetter,
130 std::move(audio_metric_metadata));
131
132 std::map<std::string, std::string> video_metric_metadata{
133 {MetricMetadataKey::kPeerSyncGroupMetadataKey, sync_group},
134 {MetricMetadataKey::kAudioStreamMetadataKey,
135 pair.second.video_stream_info.stream_label},
136 {MetricMetadataKey::kPeerMetadataKey,
137 pair.second.video_stream_info.receiver_peer},
138 {MetricMetadataKey::kReceiverMetadataKey,
139 pair.second.video_stream_info.receiver_peer}};
140 metrics_logger_->LogMetric(
141 "video_ahead_ms",
142 GetTestCaseName(pair.second.video_stream_info.stream_label, sync_group),
143 pair.second.video_ahead_ms, Unit::kMilliseconds,
144 webrtc::test::ImprovementDirection::kSmallerIsBetter,
145 std::move(video_metric_metadata));
146 }
147 }
148
GetTestCaseName(const std::string & stream_label,const std::string & sync_group) const149 std::string CrossMediaMetricsReporter::GetTestCaseName(
150 const std::string& stream_label,
151 const std::string& sync_group) const {
152 return test_case_name_ + "/" + sync_group + "_" + stream_label;
153 }
154
155 } // namespace webrtc_pc_e2e
156 } // namespace webrtc
157