Add helper for comparing FrameInstrumentationData with a VideoFrame
Bug: webrtc:358039777 Change-Id: Ibe597160658dbc66aba427f4e30dade4d6fe56e2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/363701 Auto-Submit: Fanny Linderborg <linderborg@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Commit-Queue: Erik Språng <sprang@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43134}
This commit is contained in:
parent
9e1d0564b0
commit
55398a7612
@ -21,6 +21,23 @@ rtc_library("corruption_classifier") {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc_library("frame_instrumentation_evaluation") {
|
||||||
|
sources = [
|
||||||
|
"frame_instrumentation_evaluation.cc",
|
||||||
|
"frame_instrumentation_evaluation.h",
|
||||||
|
]
|
||||||
|
deps = [
|
||||||
|
":corruption_classifier",
|
||||||
|
":halton_frame_sampler",
|
||||||
|
"../../api:array_view",
|
||||||
|
"../../api:scoped_refptr",
|
||||||
|
"../../api/video:video_frame",
|
||||||
|
"../../common_video:frame_instrumentation_data",
|
||||||
|
"../../rtc_base:checks",
|
||||||
|
"../../rtc_base:logging",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
rtc_library("frame_instrumentation_generator") {
|
rtc_library("frame_instrumentation_generator") {
|
||||||
sources = [
|
sources = [
|
||||||
"frame_instrumentation_generator.cc",
|
"frame_instrumentation_generator.cc",
|
||||||
@ -89,6 +106,18 @@ if (rtc_include_tests) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc_library("frame_instrumentation_evaluation_unittest") {
|
||||||
|
testonly = true
|
||||||
|
sources = [ "frame_instrumentation_evaluation_unittest.cc" ]
|
||||||
|
deps = [
|
||||||
|
":frame_instrumentation_evaluation",
|
||||||
|
"../../api:scoped_refptr",
|
||||||
|
"../../api/video:video_frame",
|
||||||
|
"../../common_video:frame_instrumentation_data",
|
||||||
|
"../../test:test_support",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
rtc_library("frame_instrumentation_generator_unittest") {
|
rtc_library("frame_instrumentation_generator_unittest") {
|
||||||
testonly = true
|
testonly = true
|
||||||
sources = [ "frame_instrumentation_generator_unittest.cc" ]
|
sources = [ "frame_instrumentation_generator_unittest.cc" ]
|
||||||
@ -139,6 +168,7 @@ if (rtc_include_tests) {
|
|||||||
sources = []
|
sources = []
|
||||||
deps = [
|
deps = [
|
||||||
":corruption_classifier_unittest",
|
":corruption_classifier_unittest",
|
||||||
|
":frame_instrumentation_evaluation_unittest",
|
||||||
":frame_instrumentation_generator_unittest",
|
":frame_instrumentation_generator_unittest",
|
||||||
":generic_mapping_functions_unittest",
|
":generic_mapping_functions_unittest",
|
||||||
":halton_frame_sampler_unittest",
|
":halton_frame_sampler_unittest",
|
||||||
|
|||||||
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 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 "video/corruption_detection/frame_instrumentation_evaluation.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/array_view.h"
|
||||||
|
#include "api/scoped_refptr.h"
|
||||||
|
#include "api/video/video_frame.h"
|
||||||
|
#include "api/video/video_frame_buffer.h"
|
||||||
|
#include "common_video/frame_instrumentation_data.h"
|
||||||
|
#include "rtc_base/checks.h"
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
|
#include "video/corruption_detection/corruption_classifier.h"
|
||||||
|
#include "video/corruption_detection/halton_frame_sampler.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::vector<FilteredSample> ConvertSampleValuesToFilteredSamples(
|
||||||
|
rtc::ArrayView<const double> values,
|
||||||
|
rtc::ArrayView<const FilteredSample> samples) {
|
||||||
|
RTC_CHECK_EQ(values.size(), samples.size())
|
||||||
|
<< "values and samples must have the same size";
|
||||||
|
std::vector<FilteredSample> filtered_samples;
|
||||||
|
filtered_samples.reserve(values.size());
|
||||||
|
for (size_t i = 0; i < values.size(); ++i) {
|
||||||
|
filtered_samples.push_back({.value = values[i], .plane = samples[i].plane});
|
||||||
|
}
|
||||||
|
return filtered_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::optional<double> GetCorruptionScore(const FrameInstrumentationData& data,
|
||||||
|
const VideoFrame& frame) {
|
||||||
|
if (data.sample_values.empty()) {
|
||||||
|
RTC_LOG(LS_WARNING)
|
||||||
|
<< "Samples are needed to calculate a corruption score.";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
scoped_refptr<I420BufferInterface> frame_buffer_as_i420 =
|
||||||
|
frame.video_frame_buffer()->ToI420();
|
||||||
|
if (!frame_buffer_as_i420) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Failed to convert "
|
||||||
|
<< VideoFrameBufferTypeToString(
|
||||||
|
frame.video_frame_buffer()->type())
|
||||||
|
<< " image to I420";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
HaltonFrameSampler frame_sampler;
|
||||||
|
frame_sampler.SetCurrentIndex(data.sequence_index);
|
||||||
|
std::vector<HaltonFrameSampler::Coordinates> sample_coordinates =
|
||||||
|
frame_sampler.GetSampleCoordinatesForFrame(data.sample_values.size());
|
||||||
|
if (sample_coordinates.empty()) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Failed to get sample coordinates for frame.";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<FilteredSample> samples =
|
||||||
|
GetSampleValuesForFrame(frame_buffer_as_i420, sample_coordinates,
|
||||||
|
frame.width(), frame.height(), data.std_dev);
|
||||||
|
if (samples.empty()) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Failed to get sample values for frame";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<FilteredSample> data_samples =
|
||||||
|
ConvertSampleValuesToFilteredSamples(data.sample_values, samples);
|
||||||
|
if (data_samples.empty()) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Failed to convert sample values to filtered samples";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: bugs.webrtc.org/358039777 - Update before rollout. Which variant of
|
||||||
|
// classifier should we use? What input parameters should it have?
|
||||||
|
CorruptionClassifier classifier(2.5);
|
||||||
|
|
||||||
|
return classifier.CalculateCorruptionProbablility(
|
||||||
|
data_samples, samples, data.luma_error_threshold,
|
||||||
|
data.chroma_error_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VIDEO_CORRUPTION_DETECTION_FRAME_INSTRUMENTATION_EVALUATION_H_
|
||||||
|
#define VIDEO_CORRUPTION_DETECTION_FRAME_INSTRUMENTATION_EVALUATION_H_
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "api/video/video_frame.h"
|
||||||
|
#include "common_video/frame_instrumentation_data.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
std::optional<double> GetCorruptionScore(const FrameInstrumentationData& data,
|
||||||
|
const VideoFrame& frame);
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // VIDEO_CORRUPTION_DETECTION_FRAME_INSTRUMENTATION_EVALUATION_H_
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 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 "video/corruption_detection/frame_instrumentation_evaluation.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/scoped_refptr.h"
|
||||||
|
#include "api/video/i420_buffer.h"
|
||||||
|
#include "api/video/video_frame.h"
|
||||||
|
#include "common_video/frame_instrumentation_data.h"
|
||||||
|
#include "test/gtest.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
scoped_refptr<I420Buffer> MakeI420FrameBufferWithDifferentPixelValues() {
|
||||||
|
// Create an I420 frame of size 4x4.
|
||||||
|
const int kDefaultLumaWidth = 4;
|
||||||
|
const int kDefaultLumaHeight = 4;
|
||||||
|
const int kDefaultChromaWidth = 2;
|
||||||
|
std::vector<uint8_t> kDefaultYContent = {1, 2, 3, 4, 5, 6, 7, 8,
|
||||||
|
9, 10, 11, 12, 13, 14, 15, 16};
|
||||||
|
std::vector<uint8_t> kDefaultUContent = {17, 18, 19, 20};
|
||||||
|
std::vector<uint8_t> kDefaultVContent = {21, 22, 23, 24};
|
||||||
|
|
||||||
|
return I420Buffer::Copy(kDefaultLumaWidth, kDefaultLumaHeight,
|
||||||
|
kDefaultYContent.data(), kDefaultLumaWidth,
|
||||||
|
kDefaultUContent.data(), kDefaultChromaWidth,
|
||||||
|
kDefaultVContent.data(), kDefaultChromaWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FrameInstrumentationEvaluationTest,
|
||||||
|
HaveNoCorruptionScoreWhenNoSampleValuesAreProvided) {
|
||||||
|
FrameInstrumentationData data = {.sequence_index = 0,
|
||||||
|
.communicate_upper_bits = false,
|
||||||
|
.std_dev = 0.0,
|
||||||
|
.luma_error_threshold = 0,
|
||||||
|
.chroma_error_threshold = 0,
|
||||||
|
.sample_values = {}};
|
||||||
|
VideoFrame frame =
|
||||||
|
VideoFrame::Builder()
|
||||||
|
.set_video_frame_buffer(MakeI420FrameBufferWithDifferentPixelValues())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
std::optional<double> corruption_score = GetCorruptionScore(data, frame);
|
||||||
|
|
||||||
|
EXPECT_FALSE(corruption_score.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FrameInstrumentationEvaluationTest,
|
||||||
|
HaveACorruptionScoreWhenSampleValuesAreProvided) {
|
||||||
|
FrameInstrumentationData data = {
|
||||||
|
.sequence_index = 0,
|
||||||
|
.communicate_upper_bits = false,
|
||||||
|
.std_dev = 0.0,
|
||||||
|
.luma_error_threshold = 0,
|
||||||
|
.chroma_error_threshold = 0,
|
||||||
|
.sample_values = {12, 12, 12, 12, 12, 12, 12, 12}};
|
||||||
|
VideoFrame frame =
|
||||||
|
VideoFrame::Builder()
|
||||||
|
.set_video_frame_buffer(MakeI420FrameBufferWithDifferentPixelValues())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
std::optional<double> corruption_score = GetCorruptionScore(data, frame);
|
||||||
|
|
||||||
|
ASSERT_TRUE(corruption_score.has_value());
|
||||||
|
EXPECT_DOUBLE_EQ(*corruption_score, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FrameInstrumentationEvaluationTest,
|
||||||
|
ApplyThresholdsWhenNonNegativeThresholdsAreProvided) {
|
||||||
|
FrameInstrumentationData data = {
|
||||||
|
.sequence_index = 0,
|
||||||
|
.communicate_upper_bits = false,
|
||||||
|
.std_dev = 0.0,
|
||||||
|
.luma_error_threshold = 8,
|
||||||
|
.chroma_error_threshold = 8,
|
||||||
|
.sample_values = {12, 12, 12, 12, 12, 12, 12, 12}};
|
||||||
|
VideoFrame frame =
|
||||||
|
VideoFrame::Builder()
|
||||||
|
.set_video_frame_buffer(MakeI420FrameBufferWithDifferentPixelValues())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
std::optional<double> corruption_score = GetCorruptionScore(data, frame);
|
||||||
|
|
||||||
|
ASSERT_TRUE(corruption_score.has_value());
|
||||||
|
EXPECT_DOUBLE_EQ(*corruption_score, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FrameInstrumentationEvaluationTest,
|
||||||
|
ApplyStdDevWhenNonNegativeStdDevIsProvided) {
|
||||||
|
FrameInstrumentationData data = {
|
||||||
|
.sequence_index = 0,
|
||||||
|
.communicate_upper_bits = false,
|
||||||
|
.std_dev = 0.6,
|
||||||
|
.luma_error_threshold = 8,
|
||||||
|
.chroma_error_threshold = 8,
|
||||||
|
.sample_values = {12, 12, 12, 12, 12, 12, 12, 12}};
|
||||||
|
|
||||||
|
std::vector<double> sample_values = {12, 12, 12, 12, 12, 12, 12, 12};
|
||||||
|
VideoFrame frame =
|
||||||
|
VideoFrame::Builder()
|
||||||
|
.set_video_frame_buffer(MakeI420FrameBufferWithDifferentPixelValues())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
std::optional<double> corruption_score = GetCorruptionScore(data, frame);
|
||||||
|
|
||||||
|
ASSERT_TRUE(corruption_score.has_value());
|
||||||
|
EXPECT_DOUBLE_EQ(*corruption_score, 0.3302493109581533);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FrameInstrumentationEvaluationTest, ApplySequenceIndexWhenProvided) {
|
||||||
|
FrameInstrumentationData data = {
|
||||||
|
.sequence_index = 1,
|
||||||
|
.communicate_upper_bits = false,
|
||||||
|
.std_dev = 0.6,
|
||||||
|
.luma_error_threshold = 8,
|
||||||
|
.chroma_error_threshold = 8,
|
||||||
|
.sample_values = {12, 12, 12, 12, 12, 12, 12, 12}};
|
||||||
|
|
||||||
|
std::vector<double> sample_values = {12, 12, 12, 12, 12, 12, 12, 12};
|
||||||
|
VideoFrame frame =
|
||||||
|
VideoFrame::Builder()
|
||||||
|
.set_video_frame_buffer(MakeI420FrameBufferWithDifferentPixelValues())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
std::optional<double> corruption_score = GetCorruptionScore(data, frame);
|
||||||
|
|
||||||
|
ASSERT_TRUE(corruption_score.has_value());
|
||||||
|
EXPECT_DOUBLE_EQ(*corruption_score, 0.12983429453668965);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace webrtc
|
||||||
@ -142,7 +142,7 @@ FrameInstrumentationGenerator::OnEncodedImage(
|
|||||||
contexts_[layer_id]
|
contexts_[layer_id]
|
||||||
.frame_sampler.GetSampleCoordinatesForFrameIfFrameShouldBeSampled(
|
.frame_sampler.GetSampleCoordinatesForFrameIfFrameShouldBeSampled(
|
||||||
is_key_frame, captured_frame.rtp_timestamp(),
|
is_key_frame, captured_frame.rtp_timestamp(),
|
||||||
/*sample_size=*/13);
|
/*num_samples=*/13);
|
||||||
if (sample_coordinates.empty()) {
|
if (sample_coordinates.empty()) {
|
||||||
if (!is_key_frame) {
|
if (!is_key_frame) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user