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