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 <utility>
14 #include <vector>
15
16 #include "absl/memory/memory.h"
17 #include "absl/strings/string_view.h"
18 #include "api/array_view.h"
19 #include "test/pc/e2e/analyzer/video/quality_analyzing_video_decoder.h"
20 #include "test/pc/e2e/analyzer/video/quality_analyzing_video_encoder.h"
21 #include "test/pc/e2e/analyzer/video/simulcast_dummy_buffer_helper.h"
22 #include "test/video_renderer.h"
23
24 namespace webrtc {
25 namespace webrtc_pc_e2e {
26
27 namespace {
28
29 class VideoWriter final : public rtc::VideoSinkInterface<VideoFrame> {
30 public:
VideoWriter(test::VideoFrameWriter * video_writer)31 VideoWriter(test::VideoFrameWriter* video_writer)
32 : video_writer_(video_writer) {}
33 ~VideoWriter() override = default;
34
OnFrame(const VideoFrame & frame)35 void OnFrame(const VideoFrame& frame) override {
36 bool result = video_writer_->WriteFrame(frame);
37 RTC_CHECK(result) << "Failed to write frame";
38 }
39
40 private:
41 test::VideoFrameWriter* video_writer_;
42 };
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(std::unique_ptr<VideoQualityAnalyzerInterface> analyzer,EncodedImageDataInjector * injector,EncodedImageDataExtractor * extractor)81 VideoQualityAnalyzerInjectionHelper::VideoQualityAnalyzerInjectionHelper(
82 std::unique_ptr<VideoQualityAnalyzerInterface> analyzer,
83 EncodedImageDataInjector* injector,
84 EncodedImageDataExtractor* extractor)
85 : analyzer_(std::move(analyzer)),
86 injector_(injector),
87 extractor_(extractor),
88 encoding_entities_id_generator_(std::make_unique<IntIdGenerator>(1)) {
89 RTC_DCHECK(injector_);
90 RTC_DCHECK(extractor_);
91 }
92 VideoQualityAnalyzerInjectionHelper::~VideoQualityAnalyzerInjectionHelper() =
93 default;
94
95 std::unique_ptr<VideoEncoderFactory>
WrapVideoEncoderFactory(absl::string_view peer_name,std::unique_ptr<VideoEncoderFactory> delegate,double bitrate_multiplier,std::map<std::string,absl::optional<int>> stream_required_spatial_index) const96 VideoQualityAnalyzerInjectionHelper::WrapVideoEncoderFactory(
97 absl::string_view peer_name,
98 std::unique_ptr<VideoEncoderFactory> delegate,
99 double bitrate_multiplier,
100 std::map<std::string, absl::optional<int>> stream_required_spatial_index)
101 const {
102 return std::make_unique<QualityAnalyzingVideoEncoderFactory>(
103 peer_name, std::move(delegate), bitrate_multiplier,
104 std::move(stream_required_spatial_index),
105 encoding_entities_id_generator_.get(), 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), encoding_entities_id_generator_.get(),
114 extractor_, analyzer_.get());
115 }
116
117 std::unique_ptr<test::TestVideoCapturer::FramePreprocessor>
CreateFramePreprocessor(absl::string_view peer_name,const VideoConfig & config)118 VideoQualityAnalyzerInjectionHelper::CreateFramePreprocessor(
119 absl::string_view peer_name,
120 const VideoConfig& config) {
121 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
122 test::VideoFrameWriter* writer =
123 MaybeCreateVideoWriter(config.input_dump_file_name, config);
124 if (writer) {
125 sinks.push_back(std::make_unique<VideoWriter>(writer));
126 }
127 if (config.show_on_screen) {
128 sinks.push_back(absl::WrapUnique(
129 test::VideoRenderer::Create((*config.stream_label + "-capture").c_str(),
130 config.width, config.height)));
131 }
132 {
133 MutexLock lock(&lock_);
134 known_video_configs_.insert({*config.stream_label, config});
135 }
136 return std::make_unique<AnalyzingFramePreprocessor>(
137 peer_name, std::move(*config.stream_label), analyzer_.get(),
138 std::move(sinks));
139 }
140
141 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>
CreateVideoSink(absl::string_view peer_name)142 VideoQualityAnalyzerInjectionHelper::CreateVideoSink(
143 absl::string_view peer_name) {
144 return std::make_unique<AnalyzingVideoSink>(peer_name, this);
145 }
146
Start(std::string test_case_name,rtc::ArrayView<const std::string> peer_names,int max_threads_count)147 void VideoQualityAnalyzerInjectionHelper::Start(
148 std::string test_case_name,
149 rtc::ArrayView<const std::string> peer_names,
150 int max_threads_count) {
151 analyzer_->Start(std::move(test_case_name), peer_names, max_threads_count);
152 }
153
OnStatsReports(absl::string_view pc_label,const rtc::scoped_refptr<const RTCStatsReport> & report)154 void VideoQualityAnalyzerInjectionHelper::OnStatsReports(
155 absl::string_view pc_label,
156 const rtc::scoped_refptr<const RTCStatsReport>& report) {
157 analyzer_->OnStatsReports(pc_label, report);
158 }
159
Stop()160 void VideoQualityAnalyzerInjectionHelper::Stop() {
161 analyzer_->Stop();
162 for (const auto& video_writer : video_writers_) {
163 video_writer->Close();
164 }
165 video_writers_.clear();
166 }
167
168 test::VideoFrameWriter*
MaybeCreateVideoWriter(absl::optional<std::string> file_name,const PeerConnectionE2EQualityTestFixture::VideoConfig & config)169 VideoQualityAnalyzerInjectionHelper::MaybeCreateVideoWriter(
170 absl::optional<std::string> file_name,
171 const PeerConnectionE2EQualityTestFixture::VideoConfig& config) {
172 if (!file_name.has_value()) {
173 return nullptr;
174 }
175 // TODO(titovartem) create only one file writer for simulcast video track.
176 // For now this code will be invoked for each simulcast stream separately, but
177 // only one file will be used.
178 auto video_writer = std::make_unique<test::Y4mVideoFrameWriterImpl>(
179 file_name.value(), config.width, config.height, config.fps);
180 test::VideoFrameWriter* out = video_writer.get();
181 video_writers_.push_back(std::move(video_writer));
182 return out;
183 }
184
OnFrame(absl::string_view peer_name,const VideoFrame & frame)185 void VideoQualityAnalyzerInjectionHelper::OnFrame(absl::string_view peer_name,
186 const VideoFrame& frame) {
187 rtc::scoped_refptr<I420BufferInterface> i420_buffer =
188 frame.video_frame_buffer()->ToI420();
189 if (IsDummyFrameBuffer(i420_buffer)) {
190 // This is dummy frame, so we don't need to process it further.
191 return;
192 }
193 // Copy entire video frame including video buffer to ensure that analyzer
194 // won't hold any WebRTC internal buffers.
195 VideoFrame frame_copy = frame;
196 frame_copy.set_video_frame_buffer(I420Buffer::Copy(*i420_buffer));
197 analyzer_->OnFrameRendered(peer_name, frame_copy);
198
199 std::string stream_label = analyzer_->GetStreamLabel(frame.id());
200 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>* sinks =
201 PopulateSinks(stream_label);
202 if (sinks == nullptr) {
203 return;
204 }
205 for (auto& sink : *sinks) {
206 sink->OnFrame(frame);
207 }
208 }
209
210 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>*
PopulateSinks(const std::string & stream_label)211 VideoQualityAnalyzerInjectionHelper::PopulateSinks(
212 const std::string& stream_label) {
213 MutexLock lock(&lock_);
214 auto sinks_it = sinks_.find(stream_label);
215 if (sinks_it != sinks_.end()) {
216 return &sinks_it->second;
217 }
218 auto it = known_video_configs_.find(stream_label);
219 RTC_DCHECK(it != known_video_configs_.end())
220 << "No video config for stream " << stream_label;
221 const VideoConfig& config = it->second;
222
223 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
224 test::VideoFrameWriter* writer =
225 MaybeCreateVideoWriter(config.output_dump_file_name, config);
226 if (writer) {
227 sinks.push_back(std::make_unique<VideoWriter>(writer));
228 }
229 if (config.show_on_screen) {
230 sinks.push_back(absl::WrapUnique(
231 test::VideoRenderer::Create((*config.stream_label + "-render").c_str(),
232 config.width, config.height)));
233 }
234 sinks_.insert({stream_label, std::move(sinks)});
235 return &(sinks_.find(stream_label)->second);
236 }
237
238 } // namespace webrtc_pc_e2e
239 } // namespace webrtc
240