xref: /aosp_15_r20/external/webrtc/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2019 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 "test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h"
12 
13 #include <stdio.h>
14 
15 #include <memory>
16 #include <utility>
17 #include <vector>
18 
19 #include "absl/memory/memory.h"
20 #include "absl/strings/string_view.h"
21 #include "api/array_view.h"
22 #include "api/test/pclf/media_configuration.h"
23 #include "api/video/i420_buffer.h"
24 #include "rtc_base/checks.h"
25 #include "rtc_base/logging.h"
26 #include "rtc_base/strings/string_builder.h"
27 #include "system_wrappers/include/clock.h"
28 #include "test/pc/e2e/analyzer/video/analyzing_video_sink.h"
29 #include "test/pc/e2e/analyzer/video/quality_analyzing_video_decoder.h"
30 #include "test/pc/e2e/analyzer/video/quality_analyzing_video_encoder.h"
31 #include "test/pc/e2e/analyzer/video/simulcast_dummy_buffer_helper.h"
32 #include "test/pc/e2e/analyzer/video/video_dumping.h"
33 #include "test/testsupport/fixed_fps_video_frame_writer_adapter.h"
34 #include "test/video_renderer.h"
35 
36 namespace webrtc {
37 namespace webrtc_pc_e2e {
38 namespace {
39 
40 using webrtc::webrtc_pc_e2e::VideoConfig;
41 using EmulatedSFUConfigMap =
42     ::webrtc::webrtc_pc_e2e::QualityAnalyzingVideoEncoder::EmulatedSFUConfigMap;
43 
44 class AnalyzingFramePreprocessor
45     : public test::TestVideoCapturer::FramePreprocessor {
46  public:
AnalyzingFramePreprocessor(absl::string_view peer_name,absl::string_view stream_label,VideoQualityAnalyzerInterface * analyzer,std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks)47   AnalyzingFramePreprocessor(
48       absl::string_view peer_name,
49       absl::string_view stream_label,
50       VideoQualityAnalyzerInterface* analyzer,
51       std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks)
52       : peer_name_(peer_name),
53         stream_label_(stream_label),
54         analyzer_(analyzer),
55         sinks_(std::move(sinks)) {}
56   ~AnalyzingFramePreprocessor() override = default;
57 
Preprocess(const VideoFrame & source_frame)58   VideoFrame Preprocess(const VideoFrame& source_frame) override {
59     // Copy VideoFrame to be able to set id on it.
60     VideoFrame frame = source_frame;
61     uint16_t frame_id =
62         analyzer_->OnFrameCaptured(peer_name_, stream_label_, frame);
63     frame.set_id(frame_id);
64 
65     for (auto& sink : sinks_) {
66       sink->OnFrame(frame);
67     }
68     return frame;
69   }
70 
71  private:
72   const std::string peer_name_;
73   const std::string stream_label_;
74   VideoQualityAnalyzerInterface* const analyzer_;
75   const std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>
76       sinks_;
77 };
78 
79 }  // namespace
80 
VideoQualityAnalyzerInjectionHelper(Clock * clock,std::unique_ptr<VideoQualityAnalyzerInterface> analyzer,EncodedImageDataInjector * injector,EncodedImageDataExtractor * extractor)81 VideoQualityAnalyzerInjectionHelper::VideoQualityAnalyzerInjectionHelper(
82     Clock* clock,
83     std::unique_ptr<VideoQualityAnalyzerInterface> analyzer,
84     EncodedImageDataInjector* injector,
85     EncodedImageDataExtractor* extractor)
86     : clock_(clock),
87       analyzer_(std::move(analyzer)),
88       injector_(injector),
89       extractor_(extractor) {
90   RTC_DCHECK(clock_);
91   RTC_DCHECK(injector_);
92   RTC_DCHECK(extractor_);
93 }
94 VideoQualityAnalyzerInjectionHelper::~VideoQualityAnalyzerInjectionHelper() =
95     default;
96 
97 std::unique_ptr<VideoEncoderFactory>
WrapVideoEncoderFactory(absl::string_view peer_name,std::unique_ptr<VideoEncoderFactory> delegate,double bitrate_multiplier,EmulatedSFUConfigMap stream_to_sfu_config) const98 VideoQualityAnalyzerInjectionHelper::WrapVideoEncoderFactory(
99     absl::string_view peer_name,
100     std::unique_ptr<VideoEncoderFactory> delegate,
101     double bitrate_multiplier,
102     EmulatedSFUConfigMap stream_to_sfu_config) const {
103   return std::make_unique<QualityAnalyzingVideoEncoderFactory>(
104       peer_name, std::move(delegate), bitrate_multiplier,
105       std::move(stream_to_sfu_config), injector_, analyzer_.get());
106 }
107 
108 std::unique_ptr<VideoDecoderFactory>
WrapVideoDecoderFactory(absl::string_view peer_name,std::unique_ptr<VideoDecoderFactory> delegate) const109 VideoQualityAnalyzerInjectionHelper::WrapVideoDecoderFactory(
110     absl::string_view peer_name,
111     std::unique_ptr<VideoDecoderFactory> delegate) const {
112   return std::make_unique<QualityAnalyzingVideoDecoderFactory>(
113       peer_name, std::move(delegate), extractor_, analyzer_.get());
114 }
115 
116 std::unique_ptr<test::TestVideoCapturer::FramePreprocessor>
CreateFramePreprocessor(absl::string_view peer_name,const VideoConfig & config)117 VideoQualityAnalyzerInjectionHelper::CreateFramePreprocessor(
118     absl::string_view peer_name,
119     const VideoConfig& config) {
120   std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
121   if (config.input_dump_options.has_value()) {
122     std::unique_ptr<test::VideoFrameWriter> writer =
123         config.input_dump_options->CreateInputDumpVideoFrameWriter(
124             *config.stream_label, config.GetResolution());
125     sinks.push_back(std::make_unique<VideoWriter>(
126         writer.get(), config.input_dump_options->sampling_modulo()));
127     video_writers_.push_back(std::move(writer));
128   }
129   if (config.show_on_screen) {
130     sinks.push_back(absl::WrapUnique(
131         test::VideoRenderer::Create((*config.stream_label + "-capture").c_str(),
132                                     config.width, config.height)));
133   }
134   sinks_helper_.AddConfig(peer_name, config);
135   {
136     MutexLock lock(&mutex_);
137     known_video_configs_.insert({*config.stream_label, config});
138   }
139   return std::make_unique<AnalyzingFramePreprocessor>(
140       peer_name, std::move(*config.stream_label), analyzer_.get(),
141       std::move(sinks));
142 }
143 
144 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>
CreateVideoSink(absl::string_view peer_name)145 VideoQualityAnalyzerInjectionHelper::CreateVideoSink(
146     absl::string_view peer_name) {
147   return std::make_unique<AnalyzingVideoSink2>(peer_name, this);
148 }
149 
150 std::unique_ptr<AnalyzingVideoSink>
CreateVideoSink(absl::string_view peer_name,const VideoSubscription & subscription,bool report_infra_metrics)151 VideoQualityAnalyzerInjectionHelper::CreateVideoSink(
152     absl::string_view peer_name,
153     const VideoSubscription& subscription,
154     bool report_infra_metrics) {
155   return std::make_unique<AnalyzingVideoSink>(peer_name, clock_, *analyzer_,
156                                               sinks_helper_, subscription,
157                                               report_infra_metrics);
158 }
159 
Start(std::string test_case_name,rtc::ArrayView<const std::string> peer_names,int max_threads_count)160 void VideoQualityAnalyzerInjectionHelper::Start(
161     std::string test_case_name,
162     rtc::ArrayView<const std::string> peer_names,
163     int max_threads_count) {
164   analyzer_->Start(std::move(test_case_name), peer_names, max_threads_count);
165   extractor_->Start(peer_names.size());
166   MutexLock lock(&mutex_);
167   peers_count_ = peer_names.size();
168 }
169 
RegisterParticipantInCall(absl::string_view peer_name)170 void VideoQualityAnalyzerInjectionHelper::RegisterParticipantInCall(
171     absl::string_view peer_name) {
172   analyzer_->RegisterParticipantInCall(peer_name);
173   extractor_->AddParticipantInCall();
174   MutexLock lock(&mutex_);
175   peers_count_++;
176 }
177 
UnregisterParticipantInCall(absl::string_view peer_name)178 void VideoQualityAnalyzerInjectionHelper::UnregisterParticipantInCall(
179     absl::string_view peer_name) {
180   analyzer_->UnregisterParticipantInCall(peer_name);
181   extractor_->RemoveParticipantInCall();
182   MutexLock lock(&mutex_);
183   peers_count_--;
184 }
185 
OnStatsReports(absl::string_view pc_label,const rtc::scoped_refptr<const RTCStatsReport> & report)186 void VideoQualityAnalyzerInjectionHelper::OnStatsReports(
187     absl::string_view pc_label,
188     const rtc::scoped_refptr<const RTCStatsReport>& report) {
189   analyzer_->OnStatsReports(pc_label, report);
190 }
191 
Stop()192 void VideoQualityAnalyzerInjectionHelper::Stop() {
193   analyzer_->Stop();
194   for (const auto& video_writer : video_writers_) {
195     video_writer->Close();
196   }
197   video_writers_.clear();
198   sinks_helper_.Clear();
199 }
200 
OnFrame(absl::string_view peer_name,const VideoFrame & frame)201 void VideoQualityAnalyzerInjectionHelper::OnFrame(absl::string_view peer_name,
202                                                   const VideoFrame& frame) {
203   if (IsDummyFrame(frame)) {
204     // This is dummy frame, so we  don't need to process it further.
205     return;
206   }
207   // Copy entire video frame including video buffer to ensure that analyzer
208   // won't hold any WebRTC internal buffers.
209   VideoFrame frame_copy = frame;
210   frame_copy.set_video_frame_buffer(
211       I420Buffer::Copy(*frame.video_frame_buffer()->ToI420()));
212   analyzer_->OnFrameRendered(peer_name, frame_copy);
213 
214   if (frame.id() != VideoFrame::kNotSetId) {
215     std::string stream_label = analyzer_->GetStreamLabel(frame.id());
216     std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>* sinks =
217         PopulateSinks(ReceiverStream(peer_name, stream_label));
218     if (sinks == nullptr) {
219       return;
220     }
221     for (auto& sink : *sinks) {
222       sink->OnFrame(frame);
223     }
224   }
225 }
226 
227 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>*
PopulateSinks(const ReceiverStream & receiver_stream)228 VideoQualityAnalyzerInjectionHelper::PopulateSinks(
229     const ReceiverStream& receiver_stream) {
230   MutexLock lock(&mutex_);
231   auto sinks_it = sinks_.find(receiver_stream);
232   if (sinks_it != sinks_.end()) {
233     return &sinks_it->second;
234   }
235   auto it = known_video_configs_.find(receiver_stream.stream_label);
236   RTC_DCHECK(it != known_video_configs_.end())
237       << "No video config for stream " << receiver_stream.stream_label;
238   const VideoConfig& config = it->second;
239 
240   std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
241   if (config.output_dump_options.has_value()) {
242     std::unique_ptr<test::VideoFrameWriter> writer =
243         config.output_dump_options->CreateOutputDumpVideoFrameWriter(
244             receiver_stream.stream_label, receiver_stream.peer_name,
245             config.GetResolution());
246     if (config.output_dump_use_fixed_framerate) {
247       writer = std::make_unique<test::FixedFpsVideoFrameWriterAdapter>(
248           config.fps, clock_, std::move(writer));
249     }
250     sinks.push_back(std::make_unique<VideoWriter>(
251         writer.get(), config.output_dump_options->sampling_modulo()));
252     video_writers_.push_back(std::move(writer));
253   }
254   if (config.show_on_screen) {
255     sinks.push_back(absl::WrapUnique(
256         test::VideoRenderer::Create((*config.stream_label + "-render").c_str(),
257                                     config.width, config.height)));
258   }
259   sinks_.insert({receiver_stream, std::move(sinks)});
260   return &(sinks_.find(receiver_stream)->second);
261 }
262 
263 }  // namespace webrtc_pc_e2e
264 }  // namespace webrtc
265