1 /*
2  *  Copyright (c) 2012 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 "modules/video_coding/codecs/test/stats.h"
12 
13 #include <stdio.h>
14 
15 #include <algorithm>
16 
17 #include "rtc_base/checks.h"
18 #include "rtc_base/format_macros.h"
19 
20 namespace webrtc {
21 namespace test {
22 
23 namespace {
24 
LessForEncodeTime(const FrameStatistic & s1,const FrameStatistic & s2)25 bool LessForEncodeTime(const FrameStatistic& s1, const FrameStatistic& s2) {
26   RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
27   return s1.encode_time_us < s2.encode_time_us;
28 }
29 
LessForDecodeTime(const FrameStatistic & s1,const FrameStatistic & s2)30 bool LessForDecodeTime(const FrameStatistic& s1, const FrameStatistic& s2) {
31   RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
32   return s1.decode_time_us < s2.decode_time_us;
33 }
34 
LessForEncodedSize(const FrameStatistic & s1,const FrameStatistic & s2)35 bool LessForEncodedSize(const FrameStatistic& s1, const FrameStatistic& s2) {
36   RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
37   return s1.encoded_frame_size_bytes < s2.encoded_frame_size_bytes;
38 }
39 
LessForBitRate(const FrameStatistic & s1,const FrameStatistic & s2)40 bool LessForBitRate(const FrameStatistic& s1, const FrameStatistic& s2) {
41   RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
42   return s1.bitrate_kbps < s2.bitrate_kbps;
43 }
44 
LessForPsnr(const FrameStatistic & s1,const FrameStatistic & s2)45 bool LessForPsnr(const FrameStatistic& s1, const FrameStatistic& s2) {
46   RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
47   return s1.psnr < s2.psnr;
48 }
49 
LessForSsim(const FrameStatistic & s1,const FrameStatistic & s2)50 bool LessForSsim(const FrameStatistic& s1, const FrameStatistic& s2) {
51   RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
52   return s1.ssim < s2.ssim;
53 }
54 
55 }  // namespace
56 
AddFrame()57 FrameStatistic* Stats::AddFrame() {
58   // We don't expect more frames than what can be stored in an int.
59   stats_.emplace_back(static_cast<int>(stats_.size()));
60   return &stats_.back();
61 }
62 
GetFrame(int frame_number)63 FrameStatistic* Stats::GetFrame(int frame_number) {
64   RTC_CHECK_GE(frame_number, 0);
65   RTC_CHECK_LT(frame_number, stats_.size());
66   return &stats_[frame_number];
67 }
68 
size() const69 size_t Stats::size() const {
70   return stats_.size();
71 }
72 
PrintSummary() const73 void Stats::PrintSummary() const {
74   if (stats_.empty()) {
75     printf("No frame statistics have been logged yet.\n");
76     return;
77   }
78 
79   printf("Encode/decode statistics\n==\n");
80 
81   // Calculate min, max, average and total encoding time.
82   int total_encoding_time_us = 0;
83   int total_decoding_time_us = 0;
84   size_t total_encoded_frame_size_bytes = 0;
85   size_t total_encoded_key_frame_size_bytes = 0;
86   size_t total_encoded_delta_frame_size_bytes = 0;
87   size_t num_key_frames = 0;
88   size_t num_delta_frames = 0;
89   int num_encode_failures = 0;
90   double total_psnr = 0.0;
91   double total_ssim = 0.0;
92 
93   for (const FrameStatistic& stat : stats_) {
94     total_encoding_time_us += stat.encode_time_us;
95     total_decoding_time_us += stat.decode_time_us;
96     total_encoded_frame_size_bytes += stat.encoded_frame_size_bytes;
97     if (stat.frame_type == webrtc::kVideoFrameKey) {
98       total_encoded_key_frame_size_bytes += stat.encoded_frame_size_bytes;
99       ++num_key_frames;
100     } else {
101       total_encoded_delta_frame_size_bytes += stat.encoded_frame_size_bytes;
102       ++num_delta_frames;
103     }
104     if (stat.encode_return_code != 0) {
105       ++num_encode_failures;
106     }
107     if (stat.decoding_successful) {
108       total_psnr += stat.psnr;
109       total_ssim += stat.ssim;
110     }
111   }
112 
113   // Encoding stats.
114   printf("# Encoded frame failures: %d\n", num_encode_failures);
115   printf("Encoding time:\n");
116   auto frame_it =
117       std::min_element(stats_.begin(), stats_.end(), LessForEncodeTime);
118   printf("  Min     : %7d us (frame %d)\n", frame_it->encode_time_us,
119          frame_it->frame_number);
120   frame_it = std::max_element(stats_.begin(), stats_.end(), LessForEncodeTime);
121   printf("  Max     : %7d us (frame %d)\n", frame_it->encode_time_us,
122          frame_it->frame_number);
123   printf("  Average : %7d us\n",
124          static_cast<int>(total_encoding_time_us / stats_.size()));
125 
126   // Decoding stats.
127   printf("Decoding time:\n");
128   // Only consider successfully decoded frames (packet loss may cause failures).
129   std::vector<FrameStatistic> decoded_frames;
130   for (const FrameStatistic& stat : stats_) {
131     if (stat.decoding_successful) {
132       decoded_frames.push_back(stat);
133     }
134   }
135   if (decoded_frames.empty()) {
136     printf("No successfully decoded frames exist in this statistics.\n");
137   } else {
138     frame_it = std::min_element(decoded_frames.begin(), decoded_frames.end(),
139                                 LessForDecodeTime);
140     printf("  Min     : %7d us (frame %d)\n", frame_it->decode_time_us,
141            frame_it->frame_number);
142     frame_it = std::max_element(decoded_frames.begin(), decoded_frames.end(),
143                                 LessForDecodeTime);
144     printf("  Max     : %7d us (frame %d)\n", frame_it->decode_time_us,
145            frame_it->frame_number);
146     printf("  Average : %7d us\n",
147            static_cast<int>(total_decoding_time_us / decoded_frames.size()));
148     printf("  Failures: %d frames failed to decode.\n",
149            static_cast<int>(stats_.size() - decoded_frames.size()));
150   }
151 
152   // Frame size stats.
153   printf("Frame sizes:\n");
154   frame_it = std::min_element(stats_.begin(), stats_.end(), LessForEncodedSize);
155   printf("  Min     : %7" PRIuS " bytes (frame %d)\n",
156          frame_it->encoded_frame_size_bytes, frame_it->frame_number);
157   frame_it = std::max_element(stats_.begin(), stats_.end(), LessForEncodedSize);
158   printf("  Max     : %7" PRIuS " bytes (frame %d)\n",
159          frame_it->encoded_frame_size_bytes, frame_it->frame_number);
160   printf("  Average : %7" PRIuS " bytes\n",
161          total_encoded_frame_size_bytes / stats_.size());
162   if (num_key_frames > 0) {
163     printf("  Average key frame size    : %7" PRIuS " bytes (%" PRIuS
164            " keyframes)\n",
165            total_encoded_key_frame_size_bytes / num_key_frames, num_key_frames);
166   }
167   if (num_delta_frames > 0) {
168     printf("  Average non-key frame size: %7" PRIuS " bytes (%" PRIuS
169            " frames)\n",
170            total_encoded_delta_frame_size_bytes / num_delta_frames,
171            num_delta_frames);
172   }
173 
174   // Bitrate stats.
175   printf("Bitrates:\n");
176   frame_it = std::min_element(stats_.begin(), stats_.end(), LessForBitRate);
177   printf("  Min bitrate: %7d kbps (frame %d)\n", frame_it->bitrate_kbps,
178          frame_it->frame_number);
179   frame_it = std::max_element(stats_.begin(), stats_.end(), LessForBitRate);
180   printf("  Max bitrate: %7d kbps (frame %d)\n", frame_it->bitrate_kbps,
181          frame_it->frame_number);
182 
183   // Quality.
184   printf("Quality:\n");
185   if (decoded_frames.empty()) {
186     printf("No successfully decoded frames exist in this statistics.\n");
187   } else {
188     frame_it = std::min_element(decoded_frames.begin(), decoded_frames.end(),
189                                 LessForPsnr);
190     printf("  PSNR min: %f (frame %d)\n", frame_it->psnr,
191            frame_it->frame_number);
192     printf("  PSNR avg: %f\n", total_psnr / decoded_frames.size());
193 
194     frame_it = std::min_element(decoded_frames.begin(), decoded_frames.end(),
195                                 LessForSsim);
196     printf("  SSIM min: %f (frame %d)\n", frame_it->ssim,
197            frame_it->frame_number);
198     printf("  SSIM avg: %f\n", total_ssim / decoded_frames.size());
199   }
200 
201   printf("\n");
202   printf("Total encoding time  : %7d ms.\n", total_encoding_time_us / 1000);
203   printf("Total decoding time  : %7d ms.\n", total_decoding_time_us / 1000);
204   printf("Total processing time: %7d ms.\n",
205          (total_encoding_time_us + total_decoding_time_us) / 1000);
206 
207   // QP stats.
208   int total_qp = 0;
209   int total_qp_count = 0;
210   for (const FrameStatistic& stat : stats_) {
211     if (stat.qp >= 0) {
212       total_qp += stat.qp;
213       ++total_qp_count;
214     }
215   }
216   int avg_qp = (total_qp_count > 0) ? (total_qp / total_qp_count) : -1;
217   printf("Average QP: %d\n", avg_qp);
218   printf("\n");
219 }
220 
221 }  // namespace test
222 }  // namespace webrtc
223