[DVQA] Add support for removing peer from analyzer instrumentation
Add support for removing peer from EncodedImageDataExtractor and
from VideoQualityAnalyzerInjectionHelper.
Bug: b/231397778
Change-Id: Ic33da18b7a68149ef68e5e4ba0ee7eabfb634973
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/266364
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37285}
diff --git a/test/pc/e2e/analyzer/video/encoded_image_data_injector.h b/test/pc/e2e/analyzer/video/encoded_image_data_injector.h
index 20f7a43..384e901 100644
--- a/test/pc/e2e/analyzer/video/encoded_image_data_injector.h
+++ b/test/pc/e2e/analyzer/video/encoded_image_data_injector.h
@@ -56,6 +56,10 @@
// frames. Will be invoked before that receiver will start receive data.
virtual void AddParticipantInCall() = 0;
+ // Invoked by framework when it is required to remove receiver for frames.
+ // Will be invoked after that receiver will stop receiving data.
+ virtual void RemoveParticipantInCall() = 0;
+
// Returns encoded image id, extracted from payload and also encoded image
// with its original payload. For concatenated spatial layers it should be the
// same id.
diff --git a/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector.cc b/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector.cc
index d7ee0f4..ccd2f03 100644
--- a/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector.cc
+++ b/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector.cc
@@ -59,6 +59,25 @@
expected_receivers_count_++;
}
+void SingleProcessEncodedImageDataInjector::RemoveParticipantInCall() {
+ MutexLock crit(&lock_);
+ expected_receivers_count_--;
+ // Now we need go over `extraction_cache_` and removed frames which have been
+ // received by `expected_receivers_count_`.
+ for (auto& [frame_id, extraction_infos] : extraction_cache_) {
+ for (auto it = extraction_infos.infos.begin();
+ it != extraction_infos.infos.end();) {
+ // Frame is received if `received_count` equals to
+ // `expected_receivers_count_`.
+ if (it->second.received_count == expected_receivers_count_) {
+ it = extraction_infos.infos.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+}
+
EncodedImageExtractionResult SingleProcessEncodedImageDataInjector::ExtractData(
const EncodedImage& source) {
size_t size = source.size();
diff --git a/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector.h b/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector.h
index 81fc433..1082440 100644
--- a/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector.h
+++ b/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector.h
@@ -57,6 +57,7 @@
expected_receivers_count_ = expected_receivers_count;
}
void AddParticipantInCall() override;
+ void RemoveParticipantInCall() override;
EncodedImageExtractionResult ExtractData(const EncodedImage& source) override;
private:
diff --git a/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector_unittest.cc b/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector_unittest.cc
index cfeab23..f6fa404 100644
--- a/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector_unittest.cc
+++ b/test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector_unittest.cc
@@ -37,11 +37,18 @@
return image;
}
+EncodedImage DeepCopyEncodedImage(const EncodedImage& source) {
+ EncodedImage copy = source;
+ copy.SetEncodedData(EncodedImageBuffer::Create(source.data(), source.size()));
+ return copy;
+}
+
TEST(SingleProcessEncodedImageDataInjectorTest, InjectExtractDiscardFalse) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(1);
+ injector.Start(/*expected_receivers_count=*/1);
- EncodedImage source = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source.SetTimestamp(123456789);
EncodedImageExtractionResult out =
@@ -57,9 +64,10 @@
TEST(SingleProcessEncodedImageDataInjectorTest, InjectExtractDiscardTrue) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(1);
+ injector.Start(/*expected_receivers_count=*/1);
- EncodedImage source = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source.SetTimestamp(123456789);
EncodedImageExtractionResult out =
@@ -73,9 +81,10 @@
TEST(SingleProcessEncodedImageDataInjectorTest,
InjectWithUnsetSpatialLayerSizes) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(1);
+ injector.Start(/*expected_receivers_count=*/1);
- EncodedImage source = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source.SetTimestamp(123456789);
EncodedImage intermediate = injector.InjectData(512, false, source);
@@ -97,9 +106,10 @@
TEST(SingleProcessEncodedImageDataInjectorTest,
InjectWithZeroSpatialLayerSizes) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(1);
+ injector.Start(/*expected_receivers_count=*/1);
- EncodedImage source = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source.SetTimestamp(123456789);
EncodedImage intermediate = injector.InjectData(512, false, source);
@@ -123,16 +133,19 @@
TEST(SingleProcessEncodedImageDataInjectorTest, Inject3Extract3) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(1);
+ injector.Start(/*expected_receivers_count=*/1);
// 1st frame
- EncodedImage source1 = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source1 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source1.SetTimestamp(123456710);
// 2nd frame 1st spatial layer
- EncodedImage source2 = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 11);
+ EncodedImage source2 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/11);
source2.SetTimestamp(123456720);
// 2nd frame 2nd spatial layer
- EncodedImage source3 = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 21);
+ EncodedImage source3 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/21);
source3.SetTimestamp(123456720);
EncodedImage intermediate1 = injector.InjectData(510, false, source1);
@@ -166,13 +179,16 @@
TEST(SingleProcessEncodedImageDataInjectorTest, InjectExtractFromConcatenated) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(1);
+ injector.Start(/*expected_receivers_count=*/1);
- EncodedImage source1 = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source1 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source1.SetTimestamp(123456710);
- EncodedImage source2 = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 11);
+ EncodedImage source2 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/11);
source2.SetTimestamp(123456710);
- EncodedImage source3 = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 21);
+ EncodedImage source3 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/21);
source3.SetTimestamp(123456710);
// Inject id into 3 images with same frame id.
@@ -215,13 +231,16 @@
TEST(SingleProcessEncodedImageDataInjector,
InjectExtractFromConcatenatedAllDiscarded) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(1);
+ injector.Start(/*expected_receivers_count=*/1);
- EncodedImage source1 = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source1 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source1.SetTimestamp(123456710);
- EncodedImage source2 = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 11);
+ EncodedImage source2 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/11);
source2.SetTimestamp(123456710);
- EncodedImage source3 = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 21);
+ EncodedImage source3 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/21);
source3.SetTimestamp(123456710);
// Inject id into 3 images with same frame id.
@@ -259,9 +278,10 @@
TEST(SingleProcessEncodedImageDataInjectorTest, InjectOnceExtractTwice) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(2);
+ injector.Start(/*expected_receivers_count=*/2);
- EncodedImage source = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source.SetTimestamp(123456789);
EncodedImageExtractionResult out = injector.ExtractData(
@@ -286,9 +306,10 @@
TEST(SingleProcessEncodedImageDataInjectorTest, Add1stReceiverAfterStart) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(0);
+ injector.Start(/*expected_receivers_count=*/0);
- EncodedImage source = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source.SetTimestamp(123456789);
EncodedImage modified_image = injector.InjectData(
/*id=*/512, /*discard=*/false, source);
@@ -307,9 +328,10 @@
TEST(SingleProcessEncodedImageDataInjectorTest, Add3rdReceiverAfterStart) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(2);
+ injector.Start(/*expected_receivers_count=*/2);
- EncodedImage source = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source.SetTimestamp(123456789);
EncodedImage modified_image = injector.InjectData(
/*id=*/512, /*discard=*/false, source);
@@ -328,22 +350,55 @@
}
}
+TEST(SingleProcessEncodedImageDataInjectorTest,
+ RemoveReceiverRemovesOnlyFullyReceivedFrames) {
+ SingleProcessEncodedImageDataInjector injector;
+ injector.Start(/*expected_receivers_count=*/3);
+
+ EncodedImage source1 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
+ source1.SetTimestamp(10);
+ EncodedImage source2 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
+ source2.SetTimestamp(20);
+
+ EncodedImage modified_image1 = injector.InjectData(
+ /*id=*/512, /*discard=*/false, source1);
+ EncodedImage modified_image2 = injector.InjectData(
+ /*id=*/513, /*discard=*/false, source2);
+
+ // Out of 3 receivers 1st image received by 2 and 2nd image by 1
+ injector.ExtractData(DeepCopyEncodedImage(modified_image1));
+ injector.ExtractData(DeepCopyEncodedImage(modified_image1));
+ injector.ExtractData(DeepCopyEncodedImage(modified_image2));
+
+ // When we removed one receiver, 2nd image should still be available for
+ // extraction.
+ injector.RemoveParticipantInCall();
+
+ EncodedImageExtractionResult out =
+ injector.ExtractData(DeepCopyEncodedImage(modified_image2));
+
+ EXPECT_EQ(out.id, 513);
+ EXPECT_FALSE(out.discard);
+ EXPECT_EQ(out.image.size(), 10ul);
+ EXPECT_EQ(out.image.SpatialLayerFrameSize(0).value_or(0), 0ul);
+ for (int i = 0; i < 10; ++i) {
+ EXPECT_EQ(out.image.data()[i], i + 1);
+ }
+}
+
// Death tests.
// Disabled on Android because death tests misbehave on Android, see
// base/test/gtest_util.h.
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
-EncodedImage DeepCopyEncodedImage(const EncodedImage& source) {
- EncodedImage copy = source;
- copy.SetEncodedData(EncodedImageBuffer::Create(source.data(), source.size()));
- return copy;
-}
-
-TEST(SingleProcessEncodedImageDataInjectorTest,
+TEST(SingleProcessEncodedImageDataInjectorTestDeathTest,
InjectOnceExtractMoreThenExpected) {
SingleProcessEncodedImageDataInjector injector;
- injector.Start(2);
+ injector.Start(/*expected_receivers_count=*/2);
- EncodedImage source = CreateEncodedImageOfSizeNFilledWithValuesFromX(10, 1);
+ EncodedImage source =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
source.SetTimestamp(123456789);
EncodedImage modified =
@@ -354,6 +409,35 @@
EXPECT_DEATH(injector.ExtractData(DeepCopyEncodedImage(modified)),
"Unknown sub_id=0 for frame_id=512");
}
+
+TEST(SingleProcessEncodedImageDataInjectorTestDeathTest,
+ RemoveReceiverRemovesOnlyFullyReceivedFramesVerifyFrameIsRemoved) {
+ SingleProcessEncodedImageDataInjector injector;
+ injector.Start(/*expected_receivers_count=*/3);
+
+ EncodedImage source1 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
+ source1.SetTimestamp(10);
+ EncodedImage source2 =
+ CreateEncodedImageOfSizeNFilledWithValuesFromX(/*n=*/10, /*x=*/1);
+ source2.SetTimestamp(20);
+
+ EncodedImage modified_image1 = injector.InjectData(
+ /*id=*/512, /*discard=*/false, source1);
+ EncodedImage modified_image2 = injector.InjectData(
+ /*id=*/513, /*discard=*/false, source2);
+
+ // Out of 3 receivers 1st image received by 2 and 2nd image by 1
+ injector.ExtractData(DeepCopyEncodedImage(modified_image1));
+ injector.ExtractData(DeepCopyEncodedImage(modified_image1));
+ injector.ExtractData(DeepCopyEncodedImage(modified_image2));
+
+ // When we removed one receiver 1st image should be removed.
+ injector.RemoveParticipantInCall();
+
+ EXPECT_DEATH(injector.ExtractData(DeepCopyEncodedImage(modified_image1)),
+ "Unknown sub_id=0 for frame_id=512");
+}
#endif // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
} // namespace
diff --git a/test/pc/e2e/analyzer/video/video_frame_tracking_id_injector.h b/test/pc/e2e/analyzer/video/video_frame_tracking_id_injector.h
index d5ad613..ecc3cd3 100644
--- a/test/pc/e2e/analyzer/video/video_frame_tracking_id_injector.h
+++ b/test/pc/e2e/analyzer/video/video_frame_tracking_id_injector.h
@@ -37,6 +37,7 @@
void Start(int) override {}
void AddParticipantInCall() override {}
+ void RemoveParticipantInCall() override {}
};
} // namespace webrtc_pc_e2e
diff --git a/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.cc b/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.cc
index dcdf32b..81caeb3 100644
--- a/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.cc
+++ b/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.cc
@@ -167,6 +167,14 @@
peers_count_++;
}
+void VideoQualityAnalyzerInjectionHelper::UnregisterParticipantInCall(
+ absl::string_view peer_name) {
+ analyzer_->UnregisterParticipantInCall(peer_name);
+ extractor_->RemoveParticipantInCall();
+ MutexLock lock(&mutex_);
+ peers_count_--;
+}
+
void VideoQualityAnalyzerInjectionHelper::OnStatsReports(
absl::string_view pc_label,
const rtc::scoped_refptr<const RTCStatsReport>& report) {
@@ -242,6 +250,7 @@
absl::optional<std::string> output_dump_file_name =
config.output_dump_file_name;
if (output_dump_file_name.has_value() && peers_count_ > 2) {
+ // TODO(titovartem): make this default behavior for any amount of peers.
rtc::StringBuilder builder(*output_dump_file_name);
builder << "." << receiver_stream.peer_name;
output_dump_file_name = builder.str();
diff --git a/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h b/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h
index f130efc..102fa5d 100644
--- a/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h
+++ b/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h
@@ -76,10 +76,15 @@
void Start(std::string test_case_name,
rtc::ArrayView<const std::string> peer_names,
int max_threads_count = 1);
+
// Registers new call participant to the underlying video quality analyzer.
// The method should be called before the participant is actually added.
void RegisterParticipantInCall(absl::string_view peer_name);
+ // Will be called after test removed existing participant in the middle of the
+ // call.
+ void UnregisterParticipantInCall(absl::string_view peer_name);
+
// Forwards `stats_reports` for Peer Connection `pc_label` to
// `analyzer_`.
void OnStatsReports(