Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2019 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 | |
| 11 | #include "test/scenario/stats_collection.h" |
Jonas Olsson | a4d8737 | 2019-07-05 17:08:33 | [diff] [blame] | 12 | |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 13 | #include "common_video/libyuv/include/webrtc_libyuv.h" |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 14 | #include "rtc_base/memory_usage.h" |
Niels Möller | a837030 | 2019-09-02 13:16:49 | [diff] [blame] | 15 | #include "rtc_base/thread.h" |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 16 | |
| 17 | namespace webrtc { |
| 18 | namespace test { |
| 19 | |
| 20 | VideoQualityAnalyzer::VideoQualityAnalyzer( |
| 21 | VideoQualityAnalyzerConfig config, |
| 22 | std::unique_ptr<RtcEventLogOutput> writer) |
| 23 | : config_(config), writer_(std::move(writer)) { |
| 24 | if (writer_) { |
| 25 | PrintHeaders(); |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | VideoQualityAnalyzer::~VideoQualityAnalyzer() = default; |
| 30 | |
| 31 | void VideoQualityAnalyzer::PrintHeaders() { |
Ali Tofigh | b7821ce | 2022-07-12 14:08:13 | [diff] [blame] | 32 | writer_->Write( |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 33 | "capture_time render_time capture_width capture_height render_width " |
Ali Tofigh | b7821ce | 2022-07-12 14:08:13 | [diff] [blame] | 34 | "render_height psnr\n"); |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 35 | } |
| 36 | |
| 37 | std::function<void(const VideoFramePair&)> VideoQualityAnalyzer::Handler() { |
| 38 | return [this](VideoFramePair pair) { HandleFramePair(pair); }; |
| 39 | } |
| 40 | |
Sebastian Jansson | 7cbee84 | 2019-08-06 15:19:38 | [diff] [blame] | 41 | void VideoQualityAnalyzer::HandleFramePair(VideoFramePair sample, double psnr) { |
| 42 | layer_analyzers_[sample.layer_id].HandleFramePair(sample, psnr, |
| 43 | writer_.get()); |
Sebastian Jansson | 3d351c6 | 2019-08-05 10:46:12 | [diff] [blame] | 44 | cached_.reset(); |
Sebastian Jansson | ad5c4ac | 2019-08-05 09:11:58 | [diff] [blame] | 45 | } |
| 46 | |
Sebastian Jansson | 7cbee84 | 2019-08-06 15:19:38 | [diff] [blame] | 47 | void VideoQualityAnalyzer::HandleFramePair(VideoFramePair sample) { |
| 48 | double psnr = NAN; |
| 49 | if (sample.decoded) |
| 50 | psnr = I420PSNR(*sample.captured->ToI420(), *sample.decoded->ToI420()); |
| 51 | |
| 52 | if (config_.thread) { |
Henrik Boström | 2deee4b | 2022-01-20 10:58:05 | [diff] [blame] | 53 | config_.thread->PostTask( |
| 54 | [this, sample, psnr] { HandleFramePair(std::move(sample), psnr); }); |
Sebastian Jansson | 7cbee84 | 2019-08-06 15:19:38 | [diff] [blame] | 55 | } else { |
| 56 | HandleFramePair(std::move(sample), psnr); |
| 57 | } |
| 58 | } |
| 59 | |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 60 | std::vector<VideoQualityStats> VideoQualityAnalyzer::layer_stats() const { |
| 61 | std::vector<VideoQualityStats> res; |
| 62 | for (auto& layer : layer_analyzers_) |
| 63 | res.push_back(layer.second.stats_); |
| 64 | return res; |
| 65 | } |
| 66 | |
| 67 | VideoQualityStats& VideoQualityAnalyzer::stats() { |
| 68 | if (!cached_) { |
| 69 | cached_ = VideoQualityStats(); |
| 70 | for (auto& layer : layer_analyzers_) |
| 71 | cached_->AddStats(layer.second.stats_); |
| 72 | } |
| 73 | return *cached_; |
| 74 | } |
| 75 | |
| 76 | void VideoLayerAnalyzer::HandleFramePair(VideoFramePair sample, |
Sebastian Jansson | 7cbee84 | 2019-08-06 15:19:38 | [diff] [blame] | 77 | double psnr, |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 78 | RtcEventLogOutput* writer) { |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 79 | RTC_CHECK(sample.captured); |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 80 | HandleCapturedFrame(sample); |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 81 | if (!sample.decoded) { |
Sebastian Jansson | e9cac4f | 2019-06-24 15:10:55 | [diff] [blame] | 82 | // Can only happen in the beginning of a call or if the resolution is |
| 83 | // reduced. Otherwise we will detect a freeze. |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 84 | ++stats_.lost_count; |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 85 | ++skip_count_; |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 86 | } else { |
Sebastian Jansson | e9cac4f | 2019-06-24 15:10:55 | [diff] [blame] | 87 | stats_.psnr_with_freeze.AddSample(psnr); |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 88 | if (sample.repeated) { |
| 89 | ++stats_.freeze_count; |
| 90 | ++skip_count_; |
| 91 | } else { |
Sebastian Jansson | e9cac4f | 2019-06-24 15:10:55 | [diff] [blame] | 92 | stats_.psnr.AddSample(psnr); |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 93 | HandleRenderedFrame(sample); |
| 94 | } |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 95 | } |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 96 | if (writer) { |
| 97 | LogWriteFormat(writer, "%.3f %.3f %.3f %i %i %i %i %.3f\n", |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 98 | sample.capture_time.seconds<double>(), |
| 99 | sample.render_time.seconds<double>(), |
| 100 | sample.captured->width(), sample.captured->height(), |
Sebastian Jansson | 6e07cde | 2020-01-30 17:14:01 | [diff] [blame] | 101 | sample.decoded ? sample.decoded->width() : 0, |
| 102 | sample.decoded ? sample.decoded->height() : 0, psnr); |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 103 | } |
| 104 | } |
| 105 | |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 106 | void VideoLayerAnalyzer::HandleCapturedFrame(const VideoFramePair& sample) { |
| 107 | stats_.capture.AddFrameInfo(*sample.captured, sample.capture_time); |
| 108 | if (last_freeze_time_.IsInfinite()) |
| 109 | last_freeze_time_ = sample.capture_time; |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 110 | } |
| 111 | |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 112 | void VideoLayerAnalyzer::HandleRenderedFrame(const VideoFramePair& sample) { |
Sebastian Jansson | e9cac4f | 2019-06-24 15:10:55 | [diff] [blame] | 113 | stats_.capture_to_decoded_delay.AddSample(sample.decoded_time - |
| 114 | sample.capture_time); |
| 115 | stats_.end_to_end_delay.AddSample(sample.render_time - sample.capture_time); |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 116 | stats_.render.AddFrameInfo(*sample.decoded, sample.render_time); |
| 117 | stats_.skipped_between_rendered.AddSample(skip_count_); |
| 118 | skip_count_ = 0; |
| 119 | |
| 120 | if (last_render_time_.IsFinite()) { |
| 121 | RTC_DCHECK(sample.render_time.IsFinite()); |
| 122 | TimeDelta render_interval = sample.render_time - last_render_time_; |
| 123 | TimeDelta mean_interval = stats_.render.frames.interval().Mean(); |
Danil Chapovalov | 0c626af | 2020-02-10 10:16:00 | [diff] [blame] | 124 | if (render_interval > TimeDelta::Millis(150) + mean_interval || |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 125 | render_interval > 3 * mean_interval) { |
| 126 | stats_.freeze_duration.AddSample(render_interval); |
| 127 | stats_.time_between_freezes.AddSample(last_render_time_ - |
| 128 | last_freeze_time_); |
| 129 | last_freeze_time_ = sample.render_time; |
| 130 | } |
| 131 | } |
| 132 | last_render_time_ = sample.render_time; |
| 133 | } |
| 134 | |
| 135 | void CallStatsCollector::AddStats(Call::Stats sample) { |
Sebastian Jansson | 72b7524 | 2019-04-15 13:10:18 | [diff] [blame] | 136 | if (sample.send_bandwidth_bps > 0) |
| 137 | stats_.target_rate.AddSampleBps(sample.send_bandwidth_bps); |
| 138 | if (sample.pacer_delay_ms > 0) |
Danil Chapovalov | 0c626af | 2020-02-10 10:16:00 | [diff] [blame] | 139 | stats_.pacer_delay.AddSample(TimeDelta::Millis(sample.pacer_delay_ms)); |
Sebastian Jansson | 72b7524 | 2019-04-15 13:10:18 | [diff] [blame] | 140 | if (sample.rtt_ms > 0) |
Danil Chapovalov | 0c626af | 2020-02-10 10:16:00 | [diff] [blame] | 141 | stats_.round_trip_time.AddSample(TimeDelta::Millis(sample.rtt_ms)); |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 142 | stats_.memory_usage.AddSample(rtc::GetProcessResidentSizeBytes()); |
| 143 | } |
| 144 | |
Tommi | 3176ef7 | 2022-05-22 18:47:28 | [diff] [blame] | 145 | void AudioReceiveStatsCollector::AddStats( |
| 146 | AudioReceiveStreamInterface::Stats sample) { |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 147 | stats_.expand_rate.AddSample(sample.expand_rate); |
| 148 | stats_.accelerate_rate.AddSample(sample.accelerate_rate); |
| 149 | stats_.jitter_buffer.AddSampleMs(sample.jitter_buffer_ms); |
| 150 | } |
| 151 | |
| 152 | void VideoSendStatsCollector::AddStats(VideoSendStream::Stats sample, |
| 153 | Timestamp at_time) { |
| 154 | // It's not certain that we yet have estimates for any of these stats. |
| 155 | // Check that they are positive before mixing them in. |
| 156 | if (sample.encode_frame_rate <= 0) |
| 157 | return; |
| 158 | |
| 159 | stats_.encode_frame_rate.AddSample(sample.encode_frame_rate); |
| 160 | stats_.encode_time.AddSampleMs(sample.avg_encode_time_ms); |
| 161 | stats_.encode_usage.AddSample(sample.encode_usage_percent / 100.0); |
| 162 | stats_.media_bitrate.AddSampleBps(sample.media_bitrate_bps); |
| 163 | |
| 164 | size_t fec_bytes = 0; |
| 165 | for (const auto& kv : sample.substreams) { |
| 166 | fec_bytes += kv.second.rtp_stats.fec.payload_bytes + |
| 167 | kv.second.rtp_stats.fec.padding_bytes; |
| 168 | } |
| 169 | if (last_update_.IsFinite()) { |
Danil Chapovalov | cad3e0e | 2020-02-17 17:46:07 | [diff] [blame] | 170 | auto fec_delta = DataSize::Bytes(fec_bytes - last_fec_bytes_); |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 171 | auto time_delta = at_time - last_update_; |
| 172 | stats_.fec_bitrate.AddSample(fec_delta / time_delta); |
| 173 | } |
| 174 | last_fec_bytes_ = fec_bytes; |
| 175 | last_update_ = at_time; |
| 176 | } |
| 177 | |
Tommi | f6f4543 | 2022-05-20 13:21:20 | [diff] [blame] | 178 | void VideoReceiveStatsCollector::AddStats( |
| 179 | VideoReceiveStreamInterface::Stats sample) { |
Sebastian Jansson | 9a2ca0a | 2019-04-15 11:18:19 | [diff] [blame] | 180 | if (sample.decode_ms > 0) |
| 181 | stats_.decode_time.AddSampleMs(sample.decode_ms); |
| 182 | if (sample.max_decode_ms > 0) |
| 183 | stats_.decode_time_max.AddSampleMs(sample.max_decode_ms); |
| 184 | if (sample.width > 0 && sample.height > 0) { |
| 185 | stats_.decode_pixels.AddSample(sample.width * sample.height); |
| 186 | stats_.resolution.AddSample(sample.height); |
| 187 | } |
| 188 | } |
Sebastian Jansson | 7150d8c | 2019-04-09 12:18:09 | [diff] [blame] | 189 | } // namespace test |
| 190 | } // namespace webrtc |