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

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: "Addressed feedback from Joe and Updated connection helper with base::ResetAndReturn()" 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
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 namespace {
23 const int kBytesPerPixel = 4;
Sergey Ulanov 2015/07/09 19:43:14 use webrtc::DesktopFrame::kBytesPerPixel.
liaoyuke 2015/07/09 23:21:01 Done.
24
25 // Used to account for video frame resizing and lossy encoding.
26 const double fuzzy_threshold_ = 0.05;
Sergey Ulanov 2015/07/09 19:43:14 Not a proper name for a const. What units is it m
liaoyuke 2015/07/09 23:21:01 I think so, have two separate values looks cleaner
27 } // namespace
28
18 namespace remoting { 29 namespace remoting {
19 namespace test { 30 namespace test {
20 31
21 // Implements video decoding functionality. 32 // Implements video decoding functionality.
22 class TestVideoRenderer::Core { 33 class TestVideoRenderer::Core {
23 public: 34 public:
24 Core(); 35 Core();
25 ~Core(); 36 ~Core();
26 37
27 // Initializes the internal structures of the class. 38 // Initializes the internal structures of the class.
28 void Initialize(); 39 void Initialize();
29 40
30 // Used to decode video packets. 41 // Used to decode video packets.
31 void ProcessVideoPacket(scoped_ptr<VideoPacket> packet, 42 void ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
32 const base::Closure& done); 43 const base::Closure& done);
33 44
34 // Initialize a decoder to decode video packets. 45 // Initialize a decoder to decode video packets.
35 void SetCodecForDecoding(const protocol::ChannelConfig::Codec codec); 46 void SetCodecForDecoding(const protocol::ChannelConfig::Codec codec);
36 47
37 // Returns a copy of the current buffer. 48 // Returns a copy of the current buffer.
38 scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const; 49 scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const;
39 50
40 // Set expected image pattern for comparison and the callback will be called 51 // Set expected image pattern for comparison and the callback will be called
41 // when the pattern is matched. 52 // when the pattern is matched.
42 void SetImagePatternAndMatchedCallback( 53 void SetImagePatternAndMatchedCallback(
43 const webrtc::DesktopRect& expected_rect, 54 const webrtc::DesktopRect& expected_rect,
44 const RgbaColor& expected_color, 55 const RGBA32& expected_color,
45 const base::Closure& image_pattern_matched_callback); 56 const base::Closure& image_pattern_matched_callback);
46 57
47 private: 58 private:
59 // Returns true if |rect2| falls within |rect1|, allowing for certain amount
liaoyuke 2015/07/09 23:21:01 Update comments in next patch.
60 // of error specified by a fuzzy threshold.
61 bool ContainsRect(const webrtc::DesktopRect& template_rect,
62 const webrtc::DesktopRect& candidate_rect) const;
63
64 // Expand the |accumulating_rect_| to a minimum rectangle that contains both
65 // |rect| and itself.
66 void MergeRectToAccumulatingRect(const webrtc::DesktopRect& rect);
67
68 // Update |accumulating_color_| to reflect the average color value of pixels
69 // fall within |accumulating_rect_|.
70 void UpdateAccumulatingColor();
71
72 // Compares |accumulating_color_| to |expected_color_|.
73 // Returns true if the root mean square of the errors in the R, G and B
74 // components does not exceed a fuzzy threshold.
75 bool ExpectedColorIsMatched() const;
76
77 // Returns true if |expected_rect_| and |accumulating_rect_| contains each
78 // other and expected color is matched.
79 bool ExpectedImagePatternIsMatched() const;
80
48 // Used to ensure Core methods are called on the same thread. 81 // Used to ensure Core methods are called on the same thread.
49 base::ThreadChecker thread_checker_; 82 base::ThreadChecker thread_checker_;
50 83
51 // Used to decode video packets. 84 // Used to decode video packets.
52 scoped_ptr<VideoDecoder> decoder_; 85 scoped_ptr<VideoDecoder> decoder_;
53 86
54 // Updated region of the current desktop frame compared to previous one. 87 // Updated region of the current desktop frame compared to previous one.
55 webrtc::DesktopRegion updated_region_; 88 webrtc::DesktopRegion updated_region_;
56 89
57 // Screen size of the remote host. 90 // Screen size of the remote host.
58 webrtc::DesktopSize screen_size_; 91 webrtc::DesktopSize screen_size_;
59 92
60 // Used to post tasks back to main thread. 93 // Used to post tasks back to main thread.
61 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; 94 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
62 95
63 // Used to store decoded video frame. 96 // Used to store decoded video frame.
64 scoped_ptr<webrtc::DesktopFrame> buffer_; 97 scoped_ptr<webrtc::DesktopFrame> buffer_;
Sergey Ulanov 2015/07/09 19:43:13 should this be called frame_?
65 98
66 // Protects access to |buffer_|. 99 // Protects access to |buffer_|.
67 mutable base::Lock lock_; 100 mutable base::Lock lock_;
68 101
69 // Used to store the expected image pattern. 102 // Used to store the expected image pattern.
70 webrtc::DesktopRect expected_rect_; 103 webrtc::DesktopRect expected_rect_;
71 RgbaColor expected_color_; 104 RGBColor expected_color_;
72 105
73 // Maintains accumulating image pattern. 106 // Maintains accumulating image pattern.
74 webrtc::DesktopRect accumulating_rect_; 107 webrtc::DesktopRect accumulating_rect_;
75 RgbaColor accumulating_color_; 108 RGBColor accumulating_color_;
Sergey Ulanov 2015/07/09 19:43:14 I'm not sure why it's called "accumulating" given
liaoyuke 2015/07/09 23:21:01 Done.
76 109
77 // Used to store the callback when expected pattern is matched. 110 // Used to store the callback when expected pattern is matched.
78 base::Closure image_pattern_matched_callback_; 111 base::Closure image_pattern_matched_callback_;
79 112
80 DISALLOW_COPY_AND_ASSIGN(Core); 113 DISALLOW_COPY_AND_ASSIGN(Core);
81 }; 114 };
82 115
83 TestVideoRenderer::Core::Core() 116 TestVideoRenderer::Core::Core()
84 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) { 117 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
85 thread_checker_.DetachFromThread(); 118 thread_checker_.DetachFromThread();
86 } 119 }
87 120
88 TestVideoRenderer::Core::~Core() { 121 TestVideoRenderer::Core::~Core() {
89 DCHECK(thread_checker_.CalledOnValidThread()); 122 DCHECK(thread_checker_.CalledOnValidThread());
90 } 123 }
91 124
92 void TestVideoRenderer::Core::Initialize() { 125 void TestVideoRenderer::Core::Initialize() {
93 DCHECK(thread_checker_.CalledOnValidThread()); 126 DCHECK(thread_checker_.CalledOnValidThread());
127
128 accumulating_rect_ = webrtc::DesktopRect::MakeLTRB(0, 0, 0, 0);
94 } 129 }
95 130
96 void TestVideoRenderer::Core::SetCodecForDecoding( 131 void TestVideoRenderer::Core::SetCodecForDecoding(
97 const protocol::ChannelConfig::Codec codec) { 132 const protocol::ChannelConfig::Codec codec) {
98 DCHECK(thread_checker_.CalledOnValidThread()); 133 DCHECK(thread_checker_.CalledOnValidThread());
99 134
100 if (decoder_) { 135 if (decoder_) {
101 LOG(WARNING) << "Decoder is set more than once"; 136 LOG(WARNING) << "Decoder is set more than once";
102 } 137 }
103 138
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 // Note that the |updated_region_| maintains the changed regions compared to 200 // Note that the |updated_region_| maintains the changed regions compared to
166 // previous video frame. 201 // previous video frame.
167 decoder_->RenderFrame(screen_size_, 202 decoder_->RenderFrame(screen_size_,
168 webrtc::DesktopRect::MakeWH(screen_size_.width(), 203 webrtc::DesktopRect::MakeWH(screen_size_.width(),
169 screen_size_.height()), buffer_->data(), 204 screen_size_.height()), buffer_->data(),
170 buffer_->stride(), &updated_region_); 205 buffer_->stride(), &updated_region_);
171 } 206 }
172 207
173 main_task_runner_->PostTask(FROM_HERE, done); 208 main_task_runner_->PostTask(FROM_HERE, done);
174 209
175 // TODO(liaoyuke): Update |accumulating_rect_| and |accumulating_color_|, then 210 // 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 211 if (image_pattern_matched_callback_.is_null()) {
177 // matched or not and update |image_pattern_matched| accordingly. 212 return;
213 }
214
215 for (webrtc::DesktopRegion::Iterator dirty_region(updated_region_);
216 !dirty_region.IsAtEnd(); dirty_region.Advance()) {
217 webrtc::DesktopRect dirty_rect = dirty_region.rect();
218
219 // Dirty rects not falling within |expected_rect_| doesn't affect the
220 // result.
221 if (ContainsRect(expected_rect_, dirty_rect)) {
Sergey Ulanov 2015/07/09 19:43:14 I don't get this part. What if dirty_rect is parti
liaoyuke 2015/07/09 23:21:01 Yes, it will be ignored in both case, and we only
Sergey Ulanov 2015/07/10 01:13:07 I still don't see how this can work correctly. Let
222 MergeRectToAccumulatingRect(dirty_rect);
223 }
224 }
225
226 UpdateAccumulatingColor();
Sergey Ulanov 2015/07/09 19:43:14 would it be better to return "accumulated_color" f
liaoyuke 2015/07/09 23:21:01 Yes, I think that's a good idea!
227 if (ExpectedImagePatternIsMatched()) {
Sergey Ulanov 2015/07/09 19:43:14 I don't get the logic here. UpdateAccumulatedColor
liaoyuke 2015/07/09 23:21:01 Actually, ExpectedImagePatternIsMatched() not only
Sergey Ulanov 2015/07/10 01:13:07 Missed that sorry. But I still think it isn't corr
228 base::ResetAndReturn(&image_pattern_matched_callback_).Run();
229 accumulating_rect_ = webrtc::DesktopRect::MakeLTRB(0, 0, 0, 0);
Sergey Ulanov 2015/07/09 19:43:14 Default constructor creates an empty rect, i.e. th
liaoyuke 2015/07/09 23:21:01 Done.
230 }
178 } 231 }
179 232
180 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback( 233 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback(
181 const webrtc::DesktopRect& expected_rect, 234 const webrtc::DesktopRect& expected_rect,
182 const RgbaColor& expected_color, 235 const RGBA32& expected_color,
183 const base::Closure& image_pattern_matched_callback) { 236 const base::Closure& image_pattern_matched_callback) {
184 DCHECK(thread_checker_.CalledOnValidThread()); 237 DCHECK(thread_checker_.CalledOnValidThread());
185 238
186 expected_rect_ = expected_rect; 239 expected_rect_ = expected_rect;
187 expected_color_ = expected_color; 240 expected_color_.SetRGB(expected_color);
188 image_pattern_matched_callback_ = image_pattern_matched_callback; 241 image_pattern_matched_callback_ = image_pattern_matched_callback;
189 } 242 }
190 243
244 bool TestVideoRenderer::Core::ContainsRect(
Sergey Ulanov 2015/07/09 19:43:14 This can be static function, doesn't need to be a
liaoyuke 2015/07/09 23:21:01 Done.
245 const webrtc::DesktopRect& template_rect,
246 const webrtc::DesktopRect& candidate_rect) const {
247 DCHECK(thread_checker_.CalledOnValidThread());
248
249 double fuzzy_limit_width = fuzzy_threshold_ * template_rect.width();
Sergey Ulanov 2015/07/09 19:43:14 Why does this need to be relative to the rect size
liaoyuke 2015/07/09 23:21:01 I think you are right, since the lossy encoding wi
250 double fuzzy_limit_height = fuzzy_threshold_ * template_rect.height();
251
252 return candidate_rect.left() >= template_rect.left() - fuzzy_limit_width &&
Sergey Ulanov 2015/07/09 19:43:14 I think you can use DesktopRect::ContainsRect(), j
liaoyuke 2015/07/09 23:21:01 Good point!
253 candidate_rect.top() >= template_rect.top() - fuzzy_limit_height &&
254 candidate_rect.right() <= template_rect.right() + fuzzy_limit_width &&
255 candidate_rect.bottom() <= template_rect.bottom() + fuzzy_limit_height;
256 }
257
258 void TestVideoRenderer::Core::MergeRectToAccumulatingRect(
259 const webrtc::DesktopRect& rect) {
260 // If |rect| is the first dirty rect that falls within |expected_rect_|,
261 // simply assign |rect| to |accumulating_rect_|.
262 if (!accumulating_rect_.width() && !accumulating_rect_.height()) {
Sergey Ulanov 2015/07/09 19:43:15 accumulating_rect_.is_empty()
liaoyuke 2015/07/09 23:21:01 Done.
263 accumulating_rect_ = rect;
264 } else {
265 // Merge |rect| to obtain a larger |accumulating_rect_|.
266 accumulating_rect_ = webrtc::DesktopRect::MakeLTRB(
Sergey Ulanov 2015/07/09 19:43:14 would it be better to store region instead of rect
liaoyuke 2015/07/09 23:21:01 Yes, I think so, using a single rectangle would lo
Sergey Ulanov 2015/07/10 01:13:07 See my comment above for my suggestions
267 std::min(accumulating_rect_.left(), rect.left()),
268 std::min(accumulating_rect_.top(), rect.top()),
269 std::max(accumulating_rect_.right(), rect.right()),
270 std::max(accumulating_rect_.bottom(), rect.bottom()));
271 }
272 }
273
274 void TestVideoRenderer::Core::UpdateAccumulatingColor() {
275 int red_sum = 0;
276 int green_sum = 0;
277 int blue_sum = 0;
278
279 // Loop through pixels that fall within |accumulating_rect_| to obtain the
280 // average color.
281 for (int y = accumulating_rect_.top(); y < accumulating_rect_.bottom(); ++y) {
282 for (int x = accumulating_rect_.left(); x < accumulating_rect_.right();
283 ++x) {
284 int offset =
285 (y * kBytesPerPixel * screen_size_.width()) + (x * kBytesPerPixel);
Sergey Ulanov 2015/07/09 19:43:14 You can make this code slightly faster by incremen
Sergey Ulanov 2015/07/09 19:43:14 use buffer_->stride() instead of screen_size_.widt
Sergey Ulanov 2015/07/09 19:43:15 nit: extra parentheses don't add much here
liaoyuke 2015/07/09 23:21:01 Done.
liaoyuke 2015/07/09 23:21:01 Done.
liaoyuke 2015/07/09 23:21:01 Done.
286
287 // Pixels of decoded video frame are presented in ARGB format.
288 red_sum += buffer_->data()[offset + 2];
289 green_sum += buffer_->data()[offset + 1];
290 blue_sum += buffer_->data()[offset + 0];
291 }
292 }
293
294 int area = screen_size_.width() * screen_size_.height();
295 accumulating_color_.SetRGB(red_sum / area, green_sum / area, blue_sum / area);
296 }
297
298 bool TestVideoRenderer::Core::ExpectedColorIsMatched() const {
299 double error_sum_squares = 0;
300 double red_error = static_cast<double>(expected_color_.GetRed() -
Sergey Ulanov 2015/07/09 19:43:14 I don't think you need explicit cast here
Sergey Ulanov 2015/07/09 19:43:14 Also I don't think that comparing averages is the
liaoyuke 2015/07/09 23:21:01 Yes, you are right, there are some potential probl
liaoyuke 2015/07/09 23:21:01 Done.
Sergey Ulanov 2015/07/10 01:13:07 I don't think you would need to change the interfa
301 accumulating_color_.GetRed());
302 double green_error = static_cast<double>(expected_color_.GetGreen() -
303 accumulating_color_.GetGreen());
304 double blue_error = static_cast<double>(expected_color_.GetBlue() -
305 accumulating_color_.GetBlue());
306 error_sum_squares = red_error * red_error + green_error * green_error +
307 blue_error * blue_error;
308 error_sum_squares /= (255.0 * 255.0);
309
310 return sqrt(error_sum_squares / 3) < fuzzy_threshold_;
311 }
312
313 bool TestVideoRenderer::Core::ExpectedImagePatternIsMatched() const {
314 // accumulating_rect_ must be valid in order to be matched.
Sergey Ulanov 2015/07/09 19:43:14 nit: |accumulating_rect_|
liaoyuke 2015/07/09 23:21:01 Done.
315 return accumulating_rect_.width() && accumulating_rect_.height() &&
316 ContainsRect(accumulating_rect_, expected_rect_) &&
317 ContainsRect(expected_rect_, accumulating_rect_) &&
318 ExpectedColorIsMatched();
319 }
320
191 TestVideoRenderer::TestVideoRenderer() 321 TestVideoRenderer::TestVideoRenderer()
192 : video_decode_thread_( 322 : video_decode_thread_(
193 new base::Thread("TestVideoRendererVideoDecodingThread")), 323 new base::Thread("TestVideoRendererVideoDecodingThread")),
194 weak_factory_(this) { 324 weak_factory_(this) {
195 DCHECK(thread_checker_.CalledOnValidThread()); 325 DCHECK(thread_checker_.CalledOnValidThread());
196 326
197 core_.reset(new Core()); 327 core_.reset(new Core());
198 if (!video_decode_thread_->Start()) { 328 if (!video_decode_thread_->Start()) {
199 LOG(ERROR) << "Cannot start TestVideoRenderer"; 329 LOG(ERROR) << "Cannot start TestVideoRenderer";
200 } else { 330 } else {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 } 398 }
269 399
270 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const { 400 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const {
271 DCHECK(thread_checker_.CalledOnValidThread()); 401 DCHECK(thread_checker_.CalledOnValidThread());
272 402
273 return core_->GetBufferForTest(); 403 return core_->GetBufferForTest();
274 } 404 }
275 405
276 void TestVideoRenderer::SetImagePatternAndMatchedCallback( 406 void TestVideoRenderer::SetImagePatternAndMatchedCallback(
277 const webrtc::DesktopRect& expected_rect, 407 const webrtc::DesktopRect& expected_rect,
278 const RgbaColor& expected_color, 408 const RGBA32& expected_color,
279 const base::Closure& image_pattern_matched_callback) { 409 const base::Closure& image_pattern_matched_callback) {
280 DCHECK(thread_checker_.CalledOnValidThread()); 410 DCHECK(thread_checker_.CalledOnValidThread());
281 411
282 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called"; 412 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called";
283 video_decode_task_runner_->PostTask( 413 video_decode_task_runner_->PostTask(
284 FROM_HERE, base::Bind(&Core::SetImagePatternAndMatchedCallback, 414 FROM_HERE, base::Bind(&Core::SetImagePatternAndMatchedCallback,
285 base::Unretained(core_.get()), expected_rect, 415 base::Unretained(core_.get()), expected_rect,
286 expected_color, image_pattern_matched_callback)); 416 expected_color, image_pattern_matched_callback));
287 } 417 }
288 418
419 RGBColor::RGBColor(int red, int green, int blue) {
420 SetRGB(red, green, blue);
421 }
422
423 int RGBColor::GetRed() const {
424 return (color_ >> 16) & 0xFF;
425 }
426
427 int RGBColor::GetGreen() const {
428 return (color_ >> 8) & 0xFF;
429 }
430
431 int RGBColor::GetBlue() const {
432 return color_ & 0xFF;
433 }
434
435 void RGBColor::SetRGB(int red, int green, int blue) {
436 // if any of the channels is out of range [0, 255], simply take it as 0 or 255
437 // accordingly.
438 color_ = 0xFF000000 | (std::max(0, std::min(red, 255)) << 16) |
439 (std::max(0, std::min(green, 255)) << 8) |
440 (std::max(0, std::min(blue, 255)));
441 }
442
289 } // namespace test 443 } // namespace test
290 } // namespace remoting 444 } // namespace remoting
OLDNEW
« remoting/test/test_video_renderer.h ('K') | « remoting/test/test_video_renderer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698