• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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