diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/libwebrtc/rtc_tools/frame_analyzer/video_quality_analysis.cc | 160 |
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 |