Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(278)

Side by Side Diff: remoting/test/test_video_renderer.cc

Issue 1219923011: Added image pattern comparison logic for test interface and fixture. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: "Minor changes on comments" Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/test/test_video_renderer.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/test/test_video_renderer.h" 5 #include "remoting/test/test_video_renderer.h"
6 6
7 #include <algorithm>
8 #include <cmath>
9
7 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
8 #include "base/logging.h" 12 #include "base/logging.h"
9 #include "base/synchronization/lock.h" 13 #include "base/synchronization/lock.h"
10 #include "base/thread_task_runner_handle.h" 14 #include "base/thread_task_runner_handle.h"
11 #include "base/threading/thread.h" 15 #include "base/threading/thread.h"
12 #include "remoting/codec/video_decoder.h" 16 #include "remoting/codec/video_decoder.h"
13 #include "remoting/codec/video_decoder_verbatim.h" 17 #include "remoting/codec/video_decoder_verbatim.h"
14 #include "remoting/codec/video_decoder_vpx.h" 18 #include "remoting/codec/video_decoder_vpx.h"
15 #include "remoting/proto/video.pb.h" 19 #include "remoting/proto/video.pb.h"
16 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 20 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
17 21
22 // RGB Color triplets, and it can be converted to and from RGBA32.
23 struct RGBTriplets {
24 RGBTriplets(int red, int green, int blue)
25 : red_(red), green_(green), blue_(blue) {}
26 int red_;
Sergey Ulanov 2015/07/10 01:21:56 don't need _ suffix for struct fields. It's only f
liaoyuke 2015/07/10 16:22:30 Done.
27 int green_;
28 int blue_;
29 };
30
31 namespace {
32 // Used to account for video frame resizing and lossy encoding.
33 const int kRectFuzzyThreshold = 2;
Sergey Ulanov 2015/07/10 01:21:56 Maybe call it something like kMaxPositionError? Do
liaoyuke 2015/07/10 16:22:30 Done.
34 const double kColorFuzzyThreshold = 0.05;
Sergey Ulanov 2015/07/10 01:21:55 kMaxColorError? Also document units.
liaoyuke 2015/07/10 16:22:30 Done.
35 } // namespace
36
18 namespace remoting { 37 namespace remoting {
19 namespace test { 38 namespace test {
20 39
liaoyuke 2015/07/10 16:22:30 Per discussion with Sergey, the dirty rects are no
21 // Implements video decoding functionality. 40 // Implements video decoding functionality.
22 class TestVideoRenderer::Core { 41 class TestVideoRenderer::Core {
23 public: 42 public:
24 Core(); 43 Core();
25 ~Core(); 44 ~Core();
26 45
27 // Initializes the internal structures of the class. 46 // Initializes the internal structures of the class.
28 void Initialize(); 47 void Initialize();
29 48
30 // Used to decode video packets. 49 // Used to decode video packets.
31 void ProcessVideoPacket(scoped_ptr<VideoPacket> packet, 50 void ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
32 const base::Closure& done); 51 const base::Closure& done);
33 52
34 // Initialize a decoder to decode video packets. 53 // Initialize a decoder to decode video packets.
35 void SetCodecForDecoding(const protocol::ChannelConfig::Codec codec); 54 void SetCodecForDecoding(const protocol::ChannelConfig::Codec codec);
36 55
37 // Returns a copy of the current buffer. 56 // Returns a copy of the current buffer.
38 scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const; 57 scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const;
39 58
40 // Set expected image pattern for comparison and the callback will be called 59 // Set expected image pattern for comparison and the callback will be called
41 // when the pattern is matched. 60 // when the pattern is matched.
42 void SetImagePatternAndMatchedCallback( 61 void SetImagePatternAndMatchedCallback(
43 const webrtc::DesktopRect& expected_rect, 62 const webrtc::DesktopRect& expected_rect,
44 const RgbaColor& expected_color, 63 const RGBA32& expected_avg_color,
45 const base::Closure& image_pattern_matched_callback); 64 const base::Closure& image_pattern_matched_callback);
46 65
66 // Convert a RGB triplets to 32 bit RGBA.
67 static RGBA32 ConvertTripletsToRGBA32(const RGBTriplets& rgb_triplets);
68
69 // Convert a 32 bit RGBA to a RGB triplets.
70 static RGBTriplets ConvertRGBA32ToTriplets(RGBA32 color);
71
72 // Returns true if |candidate_rect| falls within |template_rect|, allowing for
73 // certain amount of error specified by a fuzzy threshold.
74 static bool ContainsRectWithErrorMargin(
75 const webrtc::DesktopRect& template_rect,
76 const webrtc::DesktopRect& candidate_rect,
77 int error_margin);
78
47 private: 79 private:
80 // Expand the |accumulating_rect_| to a minimum rectangle that contains both
81 // |rect| and itself.
82 void MergeRectToAccumulatingRect(const webrtc::DesktopRect& rect);
83
84 // Returns the average color value of pixels fall within |rect|.
85 RGBTriplets CalculateAverageColorTriplets(
86 const webrtc::DesktopRect& rect) const;
87
88 // Compares |candidate_avg_triplets| to |expected_avg_color_|.
89 // Returns true if the root mean square of the errors in the R, G and B
90 // components does not exceed a fuzzy threshold.
91 bool ExpectedColorIsMatched(const RGBTriplets& candidate_avg_triplets) const;
92
93 // Returns true if |expected_rect_| and |accumulating_rect_| contains each
94 // other and expected color is matched.
95 bool ExpectedImagePatternIsMatched(
96 const RGBTriplets& candidate_avg_triplets) const;
97
48 // Used to ensure Core methods are called on the same thread. 98 // Used to ensure Core methods are called on the same thread.
49 base::ThreadChecker thread_checker_; 99 base::ThreadChecker thread_checker_;
50 100
51 // Used to decode video packets. 101 // Used to decode video packets.
52 scoped_ptr<VideoDecoder> decoder_; 102 scoped_ptr<VideoDecoder> decoder_;
53 103
54 // Updated region of the current desktop frame compared to previous one. 104 // Updated region of the current desktop frame compared to previous one.
55 webrtc::DesktopRegion updated_region_; 105 webrtc::DesktopRegion updated_region_;
56 106
57 // Screen size of the remote host. 107 // Screen size of the remote host.
58 webrtc::DesktopSize screen_size_; 108 webrtc::DesktopSize screen_size_;
59 109
60 // Used to post tasks back to main thread. 110 // Used to post tasks back to main thread.
61 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; 111 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
62 112
63 // Used to store decoded video frame. 113 // Used to store decoded video frame.
64 scoped_ptr<webrtc::DesktopFrame> buffer_; 114 scoped_ptr<webrtc::DesktopFrame> buffer_;
65 115
66 // Protects access to |buffer_|. 116 // Protects access to |buffer_|.
67 mutable base::Lock lock_; 117 mutable base::Lock lock_;
68 118
69 // Used to store the expected image pattern. 119 // Used to store the expected image pattern.
70 webrtc::DesktopRect expected_rect_; 120 webrtc::DesktopRect expected_rect_;
71 RgbaColor expected_color_; 121 RGBA32 expected_avg_color_;
72 122
73 // Maintains accumulating image pattern. 123 // Maintains accumulating image pattern.
74 webrtc::DesktopRect accumulating_rect_; 124 webrtc::DesktopRect accumulating_rect_;
75 RgbaColor accumulating_color_;
76 125
77 // Used to store the callback when expected pattern is matched. 126 // Used to store the callback when expected pattern is matched.
78 base::Closure image_pattern_matched_callback_; 127 base::Closure image_pattern_matched_callback_;
79 128
80 DISALLOW_COPY_AND_ASSIGN(Core); 129 DISALLOW_COPY_AND_ASSIGN(Core);
81 }; 130 };
82 131
83 TestVideoRenderer::Core::Core() 132 TestVideoRenderer::Core::Core()
84 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) { 133 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
85 thread_checker_.DetachFromThread(); 134 thread_checker_.DetachFromThread();
86 } 135 }
87 136
88 TestVideoRenderer::Core::~Core() { 137 TestVideoRenderer::Core::~Core() {
89 DCHECK(thread_checker_.CalledOnValidThread()); 138 DCHECK(thread_checker_.CalledOnValidThread());
90 } 139 }
91 140
92 void TestVideoRenderer::Core::Initialize() { 141 void TestVideoRenderer::Core::Initialize() {
93 DCHECK(thread_checker_.CalledOnValidThread()); 142 DCHECK(thread_checker_.CalledOnValidThread());
143
144 accumulating_rect_ = webrtc::DesktopRect();
94 } 145 }
95 146
96 void TestVideoRenderer::Core::SetCodecForDecoding( 147 void TestVideoRenderer::Core::SetCodecForDecoding(
97 const protocol::ChannelConfig::Codec codec) { 148 const protocol::ChannelConfig::Codec codec) {
98 DCHECK(thread_checker_.CalledOnValidThread()); 149 DCHECK(thread_checker_.CalledOnValidThread());
99 150
100 if (decoder_) { 151 if (decoder_) {
101 LOG(WARNING) << "Decoder is set more than once"; 152 LOG(WARNING) << "Decoder is set more than once";
102 } 153 }
103 154
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 // Note that the |updated_region_| maintains the changed regions compared to 216 // Note that the |updated_region_| maintains the changed regions compared to
166 // previous video frame. 217 // previous video frame.
167 decoder_->RenderFrame(screen_size_, 218 decoder_->RenderFrame(screen_size_,
168 webrtc::DesktopRect::MakeWH(screen_size_.width(), 219 webrtc::DesktopRect::MakeWH(screen_size_.width(),
169 screen_size_.height()), buffer_->data(), 220 screen_size_.height()), buffer_->data(),
170 buffer_->stride(), &updated_region_); 221 buffer_->stride(), &updated_region_);
171 } 222 }
172 223
173 main_task_runner_->PostTask(FROM_HERE, done); 224 main_task_runner_->PostTask(FROM_HERE, done);
174 225
175 // TODO(liaoyuke): Update |accumulating_rect_| and |accumulating_color_|, then 226 // Check to see if a image pattern matched reply is passed in.
176 // compare to the expected image pattern to check whether the pattern is 227 if (image_pattern_matched_callback_.is_null()) {
177 // matched or not and update |image_pattern_matched| accordingly. 228 return;
229 }
230
231 for (webrtc::DesktopRegion::Iterator dirty_region(updated_region_);
232 !dirty_region.IsAtEnd(); dirty_region.Advance()) {
233 webrtc::DesktopRect dirty_rect = dirty_region.rect();
234
235 // Dirty rects not falling within |expected_rect_| doesn't affect the
236 // result.
237 if (ContainsRectWithErrorMargin(expected_rect_, dirty_rect,
238 kRectFuzzyThreshold)) {
239 MergeRectToAccumulatingRect(dirty_rect);
240 }
241 }
242
243 if (accumulating_rect_.is_empty()) {
244 return;
245 }
246
247 RGBTriplets accumulating_avg_triplets =
248 CalculateAverageColorTriplets(accumulating_rect_);
249 if (ExpectedImagePatternIsMatched(accumulating_avg_triplets)) {
250 base::ResetAndReturn(&image_pattern_matched_callback_).Run();
251 accumulating_rect_ = webrtc::DesktopRect();
252 }
178 } 253 }
179 254
180 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback( 255 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback(
181 const webrtc::DesktopRect& expected_rect, 256 const webrtc::DesktopRect& expected_rect,
182 const RgbaColor& expected_color, 257 const RGBA32& expected_avg_color,
183 const base::Closure& image_pattern_matched_callback) { 258 const base::Closure& image_pattern_matched_callback) {
184 DCHECK(thread_checker_.CalledOnValidThread()); 259 DCHECK(thread_checker_.CalledOnValidThread());
185 260
186 expected_rect_ = expected_rect; 261 expected_rect_ = expected_rect;
187 expected_color_ = expected_color; 262 expected_avg_color_ = expected_avg_color;
188 image_pattern_matched_callback_ = image_pattern_matched_callback; 263 image_pattern_matched_callback_ = image_pattern_matched_callback;
189 } 264 }
190 265
266 RGBA32 TestVideoRenderer::Core::ConvertTripletsToRGBA32(
267 const RGBTriplets& rgb_triplets) {
268 return 0xFF000000 | (rgb_triplets.red_ << 16) | (rgb_triplets.green_ << 8) |
269 rgb_triplets.blue_;
270 }
271
272 RGBTriplets TestVideoRenderer::Core::ConvertRGBA32ToTriplets(RGBA32 color) {
273 RGBTriplets rgb_triplets((color >> 16) & 0xFF, (color >> 8) & 0xFF,
274 color & 0xFF);
275 return rgb_triplets;
276 }
277
278 bool TestVideoRenderer::Core::ContainsRectWithErrorMargin(
279 const webrtc::DesktopRect& template_rect,
280 const webrtc::DesktopRect& candidate_rect,
281 int error_margin) {
282 return template_rect.ContainsRect(webrtc::DesktopRect::MakeLTRB(
283 candidate_rect.left() + error_margin, candidate_rect.top() + error_margin,
284 candidate_rect.right() - error_margin,
285 candidate_rect.bottom() - error_margin));
286 }
287
288 void TestVideoRenderer::Core::MergeRectToAccumulatingRect(
289 const webrtc::DesktopRect& rect) {
290 // If |rect| is the first dirty rect that falls within |expected_rect_|,
291 // simply assign |rect| to |accumulating_rect_|.
292 if (accumulating_rect_.is_empty()) {
293 accumulating_rect_ = rect;
294 } else {
295 // Merge |rect| to obtain a larger |accumulating_rect_|.
296 accumulating_rect_ = webrtc::DesktopRect::MakeLTRB(
297 std::min(accumulating_rect_.left(), rect.left()),
298 std::min(accumulating_rect_.top(), rect.top()),
299 std::max(accumulating_rect_.right(), rect.right()),
300 std::max(accumulating_rect_.bottom(), rect.bottom()));
301 }
302 }
303
304 RGBTriplets TestVideoRenderer::Core::CalculateAverageColorTriplets(
305 const webrtc::DesktopRect& rect) const {
306 int red_sum = 0;
307 int green_sum = 0;
308 int blue_sum = 0;
309
310 // Loop through pixels that fall within |accumulating_rect_| to obtain the
311 // average color triplets.
312 for (int y = rect.top(); y < rect.bottom(); ++y) {
313 uint8_t* ptr =
Sergey Ulanov 2015/07/10 01:21:56 |ptr| is not the best name. https://google-stylegu
liaoyuke 2015/07/10 16:22:30 Done.
314 buffer_->data() + (y * buffer_->stride() +
315 rect.left() * webrtc::DesktopFrame::kBytesPerPixel);
316
317 // Pixels of decoded video frame are presented in ARGB format.
318 for (int x = 0; x < rect.width(); ++x) {
319 red_sum += *(ptr + 2);
Sergey Ulanov 2015/07/10 01:21:55 These can be ptr[2], ptr[1] and ptr[0]
liaoyuke 2015/07/10 16:22:30 Done.
320 green_sum += *(ptr + 1);
321 blue_sum += *(ptr + 0);
322 ptr += 4;
323 }
324 }
325
326 int area = screen_size_.width() * screen_size_.height();
327 RGBTriplets rgb_triplets(red_sum / area, green_sum / area, blue_sum / area);
328 return rgb_triplets;
329 }
330
331 bool TestVideoRenderer::Core::ExpectedColorIsMatched(
332 const RGBTriplets& candidate_avg_triplets) const {
333 RGBTriplets expected_avg_triplets =
334 ConvertRGBA32ToTriplets(expected_avg_color_);
335 double error_sum_squares = 0;
336 double red_error = expected_avg_triplets.red_ - candidate_avg_triplets.red_;
337 double green_error =
338 expected_avg_triplets.green_ - candidate_avg_triplets.green_;
339 double blue_error =
340 expected_avg_triplets.blue_ - candidate_avg_triplets.blue_;
341 error_sum_squares = red_error * red_error + green_error * green_error +
342 blue_error * blue_error;
343 error_sum_squares /= (255.0 * 255.0);
344
345 return sqrt(error_sum_squares / 3) < kColorFuzzyThreshold;
346 }
347
348 bool TestVideoRenderer::Core::ExpectedImagePatternIsMatched(
349 const RGBTriplets& candidate_avg_triplets) const {
350 // |accumulating_rect_| must be valid in order to be matched.
351 return accumulating_rect_.width() && accumulating_rect_.height() &&
352 ContainsRectWithErrorMargin(accumulating_rect_, expected_rect_,
353 kRectFuzzyThreshold) &&
354 ContainsRectWithErrorMargin(expected_rect_, accumulating_rect_,
355 kRectFuzzyThreshold) &&
356 ExpectedColorIsMatched(candidate_avg_triplets);
357 }
358
191 TestVideoRenderer::TestVideoRenderer() 359 TestVideoRenderer::TestVideoRenderer()
192 : video_decode_thread_( 360 : video_decode_thread_(
193 new base::Thread("TestVideoRendererVideoDecodingThread")), 361 new base::Thread("TestVideoRendererVideoDecodingThread")),
194 weak_factory_(this) { 362 weak_factory_(this) {
195 DCHECK(thread_checker_.CalledOnValidThread()); 363 DCHECK(thread_checker_.CalledOnValidThread());
196 364
197 core_.reset(new Core()); 365 core_.reset(new Core());
198 if (!video_decode_thread_->Start()) { 366 if (!video_decode_thread_->Start()) {
199 LOG(ERROR) << "Cannot start TestVideoRenderer"; 367 LOG(ERROR) << "Cannot start TestVideoRenderer";
200 } else { 368 } else {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 } 436 }
269 437
270 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const { 438 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const {
271 DCHECK(thread_checker_.CalledOnValidThread()); 439 DCHECK(thread_checker_.CalledOnValidThread());
272 440
273 return core_->GetBufferForTest(); 441 return core_->GetBufferForTest();
274 } 442 }
275 443
276 void TestVideoRenderer::SetImagePatternAndMatchedCallback( 444 void TestVideoRenderer::SetImagePatternAndMatchedCallback(
277 const webrtc::DesktopRect& expected_rect, 445 const webrtc::DesktopRect& expected_rect,
278 const RgbaColor& expected_color, 446 const RGBA32& expected_avg_color,
279 const base::Closure& image_pattern_matched_callback) { 447 const base::Closure& image_pattern_matched_callback) {
280 DCHECK(thread_checker_.CalledOnValidThread()); 448 DCHECK(thread_checker_.CalledOnValidThread());
281 449
282 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called"; 450 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called";
283 video_decode_task_runner_->PostTask( 451 video_decode_task_runner_->PostTask(
284 FROM_HERE, base::Bind(&Core::SetImagePatternAndMatchedCallback, 452 FROM_HERE,
285 base::Unretained(core_.get()), expected_rect, 453 base::Bind(&Core::SetImagePatternAndMatchedCallback,
286 expected_color, image_pattern_matched_callback)); 454 base::Unretained(core_.get()), expected_rect,
455 expected_avg_color, image_pattern_matched_callback));
287 } 456 }
288 457
289 } // namespace test 458 } // namespace test
290 } // namespace remoting 459 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/test/test_video_renderer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698