summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/rtc_tools/frame_analyzer/video_quality_analysis.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/rtc_tools/frame_analyzer/video_quality_analysis.cc')
-rw-r--r--third_party/libwebrtc/rtc_tools/frame_analyzer/video_quality_analysis.cc160
1 files changed, 160 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_tools/frame_analyzer/video_quality_analysis.cc b/third_party/libwebrtc/rtc_tools/frame_analyzer/video_quality_analysis.cc
new file mode 100644
index 0000000000..1832438b75
--- /dev/null
+++ b/third_party/libwebrtc/rtc_tools/frame_analyzer/video_quality_analysis.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2012 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 "rtc_tools/frame_analyzer/video_quality_analysis.h"
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+
+#include "api/numerics/samples_stats_counter.h"
+#include "api/test/metrics/metric.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "third_party/libyuv/include/libyuv/compare.h"
+
+namespace webrtc {
+namespace test {
+
+ResultsContainer::ResultsContainer() {}
+ResultsContainer::~ResultsContainer() {}
+
+template <typename FrameMetricFunction>
+static double CalculateMetric(
+ const FrameMetricFunction& frame_metric_function,
+ const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
+ const rtc::scoped_refptr<I420BufferInterface>& test_buffer) {
+ RTC_CHECK_EQ(ref_buffer->width(), test_buffer->width());
+ RTC_CHECK_EQ(ref_buffer->height(), test_buffer->height());
+ return frame_metric_function(
+ ref_buffer->DataY(), ref_buffer->StrideY(), ref_buffer->DataU(),
+ ref_buffer->StrideU(), ref_buffer->DataV(), ref_buffer->StrideV(),
+ test_buffer->DataY(), test_buffer->StrideY(), test_buffer->DataU(),
+ test_buffer->StrideU(), test_buffer->DataV(), test_buffer->StrideV(),
+ test_buffer->width(), test_buffer->height());
+}
+
+double Psnr(const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
+ const rtc::scoped_refptr<I420BufferInterface>& test_buffer) {
+ // LibYuv sets the max psnr value to 128, we restrict it to 48.
+ // In case of 0 mse in one frame, 128 can skew the results significantly.
+ return std::min(48.0,
+ CalculateMetric(&libyuv::I420Psnr, ref_buffer, test_buffer));
+}
+
+double Ssim(const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
+ const rtc::scoped_refptr<I420BufferInterface>& test_buffer) {
+ return CalculateMetric(&libyuv::I420Ssim, ref_buffer, test_buffer);
+}
+
+std::vector<AnalysisResult> RunAnalysis(
+ const rtc::scoped_refptr<webrtc::test::Video>& reference_video,
+ const rtc::scoped_refptr<webrtc::test::Video>& test_video,
+ const std::vector<size_t>& test_frame_indices) {
+ std::vector<AnalysisResult> results;
+ for (size_t i = 0; i < test_video->number_of_frames(); ++i) {
+ const rtc::scoped_refptr<I420BufferInterface>& test_frame =
+ test_video->GetFrame(i);
+ const rtc::scoped_refptr<I420BufferInterface>& reference_frame =
+ reference_video->GetFrame(i);
+
+ // Fill in the result struct.
+ AnalysisResult result;
+ result.frame_number = test_frame_indices[i];
+ result.psnr_value = Psnr(reference_frame, test_frame);
+ result.ssim_value = Ssim(reference_frame, test_frame);
+ results.push_back(result);
+ }
+
+ return results;
+}
+
+std::vector<Cluster> CalculateFrameClusters(
+ const std::vector<size_t>& indices) {
+ std::vector<Cluster> clusters;
+
+ for (size_t index : indices) {
+ if (!clusters.empty() && clusters.back().index == index) {
+ // This frame belongs to the previous cluster.
+ ++clusters.back().number_of_repeated_frames;
+ } else {
+ // Start a new cluster.
+ clusters.push_back({index, /* number_of_repeated_frames= */ 1});
+ }
+ }
+
+ return clusters;
+}
+
+int GetMaxRepeatedFrames(const std::vector<Cluster>& clusters) {
+ int max_number_of_repeated_frames = 0;
+ for (const Cluster& cluster : clusters) {
+ max_number_of_repeated_frames = std::max(max_number_of_repeated_frames,
+ cluster.number_of_repeated_frames);
+ }
+ return max_number_of_repeated_frames;
+}
+
+int GetMaxSkippedFrames(const std::vector<Cluster>& clusters) {
+ size_t max_skipped_frames = 0;
+ for (size_t i = 1; i < clusters.size(); ++i) {
+ const size_t skipped_frames = clusters[i].index - clusters[i - 1].index - 1;
+ max_skipped_frames = std::max(max_skipped_frames, skipped_frames);
+ }
+ return static_cast<int>(max_skipped_frames);
+}
+
+int GetTotalNumberOfSkippedFrames(const std::vector<Cluster>& clusters) {
+ // The number of reference frames the test video spans.
+ const size_t number_ref_frames =
+ clusters.empty() ? 0 : 1 + clusters.back().index - clusters.front().index;
+ return static_cast<int>(number_ref_frames - clusters.size());
+}
+
+void PrintAnalysisResults(const std::string& label,
+ ResultsContainer& results,
+ MetricsLogger& logger) {
+ if (results.frames.size() > 0u) {
+ logger.LogSingleValueMetric("Unique_frames_count", label,
+ results.frames.size(), Unit::kUnitless,
+ ImprovementDirection::kNeitherIsBetter);
+
+ SamplesStatsCounter psnr_values;
+ SamplesStatsCounter ssim_values;
+ for (const auto& frame : results.frames) {
+ psnr_values.AddSample(frame.psnr_value);
+ ssim_values.AddSample(frame.ssim_value);
+ }
+
+ logger.LogMetric("PSNR_dB", label, psnr_values, Unit::kUnitless,
+ ImprovementDirection::kNeitherIsBetter);
+ logger.LogMetric("SSIM", label, ssim_values, Unit::kUnitless,
+ ImprovementDirection::kNeitherIsBetter);
+ }
+
+ logger.LogSingleValueMetric("Max_repeated", label,
+ results.max_repeated_frames, Unit::kUnitless,
+ ImprovementDirection::kNeitherIsBetter);
+ logger.LogSingleValueMetric("Max_skipped", label, results.max_skipped_frames,
+ Unit::kUnitless,
+ ImprovementDirection::kNeitherIsBetter);
+ logger.LogSingleValueMetric("Total_skipped", label,
+ results.total_skipped_frames, Unit::kUnitless,
+ ImprovementDirection::kNeitherIsBetter);
+ logger.LogSingleValueMetric("Decode_errors_reference", label,
+ results.decode_errors_ref, Unit::kUnitless,
+ ImprovementDirection::kNeitherIsBetter);
+ logger.LogSingleValueMetric("Decode_errors_test", label,
+ results.decode_errors_test, Unit::kUnitless,
+ ImprovementDirection::kNeitherIsBetter);
+}
+
+} // namespace test
+} // namespace webrtc