1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
/*
* Copyright (c) 2022 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/default_video_quality_analyzer_frame_in_flight.h"
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "api/video/video_frame.h"
#include "api/video/video_frame_type.h"
#include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_internal_shared_objects.h"
namespace webrtc {
namespace {
template <typename T>
absl::optional<T> MaybeGetValue(const std::map<size_t, T>& map, size_t key) {
auto it = map.find(key);
if (it == map.end()) {
return absl::nullopt;
}
return it->second;
}
} // namespace
FrameInFlight::FrameInFlight(size_t stream,
VideoFrame frame,
Timestamp captured_time,
std::set<size_t> expected_receivers)
: stream_(stream),
expected_receivers_(std::move(expected_receivers)),
frame_(std::move(frame)),
captured_time_(captured_time) {}
bool FrameInFlight::RemoveFrame() {
if (!frame_) {
return false;
}
frame_ = absl::nullopt;
return true;
}
void FrameInFlight::SetFrameId(uint16_t id) {
if (frame_) {
frame_->set_id(id);
}
frame_id_ = id;
}
std::vector<size_t> FrameInFlight::GetPeersWhichDidntReceive() const {
std::vector<size_t> out;
for (size_t peer : expected_receivers_) {
auto it = receiver_stats_.find(peer);
if (it == receiver_stats_.end() ||
(!it->second.dropped && it->second.rendered_time.IsInfinite())) {
out.push_back(peer);
}
}
return out;
}
bool FrameInFlight::HaveAllPeersReceived() const {
for (size_t peer : expected_receivers_) {
auto it = receiver_stats_.find(peer);
if (it == receiver_stats_.end()) {
return false;
}
if (!it->second.dropped && it->second.rendered_time.IsInfinite()) {
return false;
}
}
return true;
}
void FrameInFlight::OnFrameEncoded(webrtc::Timestamp time,
VideoFrameType frame_type,
DataSize encoded_image_size,
uint32_t target_encode_bitrate,
int spatial_layer,
int qp,
StreamCodecInfo used_encoder) {
encoded_time_ = time;
frame_type_ = frame_type;
encoded_image_size_ = encoded_image_size;
target_encode_bitrate_ += target_encode_bitrate;
spatial_layers_qp_[spatial_layer].AddSample(SamplesStatsCounter::StatsSample{
.value = static_cast<double>(qp), .time = time});
// Update used encoder info. If simulcast/SVC is used, this method can
// be called multiple times, in such case we should preserve the value
// of `used_encoder_.switched_on_at` from the first invocation as the
// smallest one.
Timestamp encoder_switched_on_at = used_encoder_.has_value()
? used_encoder_->switched_on_at
: Timestamp::PlusInfinity();
RTC_DCHECK(used_encoder.switched_on_at.IsFinite());
RTC_DCHECK(used_encoder.switched_from_at.IsFinite());
used_encoder_ = used_encoder;
if (encoder_switched_on_at < used_encoder_->switched_on_at) {
used_encoder_->switched_on_at = encoder_switched_on_at;
}
}
void FrameInFlight::OnFramePreDecode(size_t peer,
webrtc::Timestamp received_time,
webrtc::Timestamp decode_start_time,
VideoFrameType frame_type,
DataSize encoded_image_size) {
receiver_stats_[peer].received_time = received_time;
receiver_stats_[peer].decode_start_time = decode_start_time;
receiver_stats_[peer].frame_type = frame_type;
receiver_stats_[peer].encoded_image_size = encoded_image_size;
}
bool FrameInFlight::HasReceivedTime(size_t peer) const {
auto it = receiver_stats_.find(peer);
if (it == receiver_stats_.end()) {
return false;
}
return it->second.received_time.IsFinite();
}
void FrameInFlight::OnFrameDecoded(size_t peer,
webrtc::Timestamp time,
int width,
int height,
const StreamCodecInfo& used_decoder) {
receiver_stats_[peer].decode_end_time = time;
receiver_stats_[peer].used_decoder = used_decoder;
receiver_stats_[peer].decoded_frame_width = width;
receiver_stats_[peer].decoded_frame_height = height;
}
void FrameInFlight::OnDecoderError(size_t peer,
const StreamCodecInfo& used_decoder) {
receiver_stats_[peer].decoder_failed = true;
receiver_stats_[peer].used_decoder = used_decoder;
}
bool FrameInFlight::HasDecodeEndTime(size_t peer) const {
auto it = receiver_stats_.find(peer);
if (it == receiver_stats_.end()) {
return false;
}
return it->second.decode_end_time.IsFinite();
}
void FrameInFlight::OnFrameRendered(size_t peer, webrtc::Timestamp time) {
receiver_stats_[peer].rendered_time = time;
}
bool FrameInFlight::HasRenderedTime(size_t peer) const {
auto it = receiver_stats_.find(peer);
if (it == receiver_stats_.end()) {
return false;
}
return it->second.rendered_time.IsFinite();
}
bool FrameInFlight::IsDropped(size_t peer) const {
auto it = receiver_stats_.find(peer);
if (it == receiver_stats_.end()) {
return false;
}
return it->second.dropped;
}
FrameStats FrameInFlight::GetStatsForPeer(size_t peer) const {
RTC_DCHECK_NE(frame_id_, VideoFrame::kNotSetId)
<< "Frame id isn't initialized";
FrameStats stats(frame_id_, captured_time_);
stats.pre_encode_time = pre_encode_time_;
stats.encoded_time = encoded_time_;
stats.target_encode_bitrate = target_encode_bitrate_;
stats.encoded_frame_type = frame_type_;
stats.encoded_image_size = encoded_image_size_;
stats.used_encoder = used_encoder_;
stats.spatial_layers_qp = spatial_layers_qp_;
absl::optional<ReceiverFrameStats> receiver_stats =
MaybeGetValue<ReceiverFrameStats>(receiver_stats_, peer);
if (receiver_stats.has_value()) {
stats.received_time = receiver_stats->received_time;
stats.decode_start_time = receiver_stats->decode_start_time;
stats.decode_end_time = receiver_stats->decode_end_time;
stats.rendered_time = receiver_stats->rendered_time;
stats.prev_frame_rendered_time = receiver_stats->prev_frame_rendered_time;
stats.decoded_frame_width = receiver_stats->decoded_frame_width;
stats.decoded_frame_height = receiver_stats->decoded_frame_height;
stats.used_decoder = receiver_stats->used_decoder;
stats.pre_decoded_frame_type = receiver_stats->frame_type;
stats.pre_decoded_image_size = receiver_stats->encoded_image_size;
stats.decoder_failed = receiver_stats->decoder_failed;
}
return stats;
}
} // namespace webrtc
|