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

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 Sergey" 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_;
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;
34 const double kColorFuzzyThreshold = 0.05;
35 } // namespace
36
18 namespace remoting { 37 namespace remoting {
19 namespace test { 38 namespace test {
20 39
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
liaoyuke 2015/07/10 16:22:30 Add comments in next patch.
66 static RGBA32 ConvertTripletsToRGBA32(const RGBTriplets& rgb_triplets);
67
68 static RGBTriplets ConvertRGBA32ToTriplets(RGBA32 color);
69
70 // Returns true if |candidate_rect| falls within |template_rect|, allowing for
71 // certain amount of error specified by a fuzzy threshold.
72 static bool ContainsRectWithErrorMargin(
73 const webrtc::DesktopRect& template_rect,
74 const webrtc::DesktopRect& candidate_rect,
75 int error_margin);
76
47 private: 77 private:
78 // Expand the |accumulating_rect_| to a minimum rectangle that contains both
79 // |rect| and itself.
80 void MergeRectToAccumulatingRect(const webrtc::DesktopRect& rect);
81
82 // Returns the average color value of pixels fall within |rect|.
83 RGBTriplets CalculateAverageColorTriplets(
84 const webrtc::DesktopRect& rect) const;
85
86 // Compares |candidate_avg_triplets| to |expected_avg_color_|.
87 // Returns true if the root mean square of the errors in the R, G and B
88 // components does not exceed a fuzzy threshold.
89 bool ExpectedColorIsMatched(const RGBTriplets& candidate_avg_triplets) const;
90
91 // Returns true if |expected_rect_| and |accumulating_rect_| contains each
92 // other and expected color is matched.
93 bool ExpectedImagePatternIsMatched(
94 const RGBTriplets& candidate_avg_triplets) const;
95
48 // Used to ensure Core methods are called on the same thread. 96 // Used to ensure Core methods are called on the same thread.
49 base::ThreadChecker thread_checker_; 97 base::ThreadChecker thread_checker_;
50 98
51 // Used to decode video packets. 99 // Used to decode video packets.
52 scoped_ptr<VideoDecoder> decoder_; 100 scoped_ptr<VideoDecoder> decoder_;
53 101
54 // Updated region of the current desktop frame compared to previous one. 102 // Updated region of the current desktop frame compared to previous one.
55 webrtc::DesktopRegion updated_region_; 103 webrtc::DesktopRegion updated_region_;
56 104
57 // Screen size of the remote host. 105 // Screen size of the remote host.
58 webrtc::DesktopSize screen_size_; 106 webrtc::DesktopSize screen_size_;
59 107
60 // Used to post tasks back to main thread. 108 // Used to post tasks back to main thread.
61 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; 109 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
62 110
63 // Used to store decoded video frame. 111 // Used to store decoded video frame.
64 scoped_ptr<webrtc::DesktopFrame> buffer_; 112 scoped_ptr<webrtc::DesktopFrame> buffer_;
65 113
66 // Protects access to |buffer_|. 114 // Protects access to |buffer_|.
67 mutable base::Lock lock_; 115 mutable base::Lock lock_;
68 116
69 // Used to store the expected image pattern. 117 // Used to store the expected image pattern.
70 webrtc::DesktopRect expected_rect_; 118 webrtc::DesktopRect expected_rect_;
71 RgbaColor expected_color_; 119 RGBA32 expected_avg_color_;
72 120
73 // Maintains accumulating image pattern. 121 // Maintains accumulating image pattern.
74 webrtc::DesktopRect accumulating_rect_; 122 webrtc::DesktopRect accumulating_rect_;
75 RgbaColor accumulating_color_;
76 123
77 // Used to store the callback when expected pattern is matched. 124 // Used to store the callback when expected pattern is matched.
78 base::Closure image_pattern_matched_callback_; 125 base::Closure image_pattern_matched_callback_;
79 126
80 DISALLOW_COPY_AND_ASSIGN(Core); 127 DISALLOW_COPY_AND_ASSIGN(Core);
81 }; 128 };
82 129
83 TestVideoRenderer::Core::Core() 130 TestVideoRenderer::Core::Core()
84 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) { 131 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
85 thread_checker_.DetachFromThread(); 132 thread_checker_.DetachFromThread();
86 } 133 }
87 134
88 TestVideoRenderer::Core::~Core() { 135 TestVideoRenderer::Core::~Core() {
89 DCHECK(thread_checker_.CalledOnValidThread()); 136 DCHECK(thread_checker_.CalledOnValidThread());
90 } 137 }
91 138
92 void TestVideoRenderer::Core::Initialize() { 139 void TestVideoRenderer::Core::Initialize() {
93 DCHECK(thread_checker_.CalledOnValidThread()); 140 DCHECK(thread_checker_.CalledOnValidThread());
141
142 accumulating_rect_ = webrtc::DesktopRect::MakeLTRB(0, 0, 0, 0);
liaoyuke 2015/07/10 16:22:30 Change to DesktopRect() in next patch.
94 } 143 }
95 144
96 void TestVideoRenderer::Core::SetCodecForDecoding( 145 void TestVideoRenderer::Core::SetCodecForDecoding(
97 const protocol::ChannelConfig::Codec codec) { 146 const protocol::ChannelConfig::Codec codec) {
98 DCHECK(thread_checker_.CalledOnValidThread()); 147 DCHECK(thread_checker_.CalledOnValidThread());
99 148
100 if (decoder_) { 149 if (decoder_) {
101 LOG(WARNING) << "Decoder is set more than once"; 150 LOG(WARNING) << "Decoder is set more than once";
102 } 151 }
103 152
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 // Note that the |updated_region_| maintains the changed regions compared to 214 // Note that the |updated_region_| maintains the changed regions compared to
166 // previous video frame. 215 // previous video frame.
167 decoder_->RenderFrame(screen_size_, 216 decoder_->RenderFrame(screen_size_,
168 webrtc::DesktopRect::MakeWH(screen_size_.width(), 217 webrtc::DesktopRect::MakeWH(screen_size_.width(),
169 screen_size_.height()), buffer_->data(), 218 screen_size_.height()), buffer_->data(),
170 buffer_->stride(), &updated_region_); 219 buffer_->stride(), &updated_region_);
171 } 220 }
172 221
173 main_task_runner_->PostTask(FROM_HERE, done); 222 main_task_runner_->PostTask(FROM_HERE, done);
174 223
175 // TODO(liaoyuke): Update |accumulating_rect_| and |accumulating_color_|, then 224 // 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 225 if (image_pattern_matched_callback_.is_null()) {
177 // matched or not and update |image_pattern_matched| accordingly. 226 return;
227 }
228
229 for (webrtc::DesktopRegion::Iterator dirty_region(updated_region_);
230 !dirty_region.IsAtEnd(); dirty_region.Advance()) {
231 webrtc::DesktopRect dirty_rect = dirty_region.rect();
232
233 // Dirty rects not falling within |expected_rect_| doesn't affect the
234 // result.
235 if (ContainsRectWithErrorMargin(expected_rect_, dirty_rect,
236 kRectFuzzyThreshold)) {
237 MergeRectToAccumulatingRect(dirty_rect);
238 }
239 }
240
241 if (accumulating_rect_.is_empty()) {
242 return;
243 }
244
245 RGBTriplets accumulating_avg_triplets =
246 CalculateAverageColorTriplets(accumulating_rect_);
247 if (ExpectedImagePatternIsMatched(accumulating_avg_triplets)) {
248 base::ResetAndReturn(&image_pattern_matched_callback_).Run();
249 accumulating_rect_ = webrtc::DesktopRect();
250 }
178 } 251 }
179 252
180 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback( 253 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback(
181 const webrtc::DesktopRect& expected_rect, 254 const webrtc::DesktopRect& expected_rect,
182 const RgbaColor& expected_color, 255 const RGBA32& expected_avg_color,
183 const base::Closure& image_pattern_matched_callback) { 256 const base::Closure& image_pattern_matched_callback) {
184 DCHECK(thread_checker_.CalledOnValidThread()); 257 DCHECK(thread_checker_.CalledOnValidThread());
185 258
186 expected_rect_ = expected_rect; 259 expected_rect_ = expected_rect;
187 expected_color_ = expected_color; 260 expected_avg_color_ = expected_avg_color;
188 image_pattern_matched_callback_ = image_pattern_matched_callback; 261 image_pattern_matched_callback_ = image_pattern_matched_callback;
189 } 262 }
190 263
264 RGBA32 TestVideoRenderer::Core::ConvertTripletsToRGBA32(
265 const RGBTriplets& rgb_triplets) {
266 return 0xFF000000 | (rgb_triplets.red_ << 16) | (rgb_triplets.green_ << 8) |
267 rgb_triplets.blue_;
268 }
269
270 RGBTriplets TestVideoRenderer::Core::ConvertRGBA32ToTriplets(RGBA32 color) {
271 RGBTriplets rgb_triplets((color >> 16) & 0xFF, (color >> 8) & 0xFF,
272 color & 0xFF);
273 return rgb_triplets;
274 }
275
276 bool TestVideoRenderer::Core::ContainsRectWithErrorMargin(
277 const webrtc::DesktopRect& template_rect,
278 const webrtc::DesktopRect& candidate_rect,
279 int error_margin) {
280 return template_rect.ContainsRect(webrtc::DesktopRect::MakeLTRB(
281 candidate_rect.left() + error_margin, candidate_rect.top() + error_margin,
282 candidate_rect.right() - error_margin,
283 candidate_rect.bottom() - error_margin));
284 }
285
286 void TestVideoRenderer::Core::MergeRectToAccumulatingRect(
287 const webrtc::DesktopRect& rect) {
288 // If |rect| is the first dirty rect that falls within |expected_rect_|,
289 // simply assign |rect| to |accumulating_rect_|.
290 if (accumulating_rect_.is_empty()) {
291 accumulating_rect_ = rect;
292 } else {
293 // Merge |rect| to obtain a larger |accumulating_rect_|.
294 accumulating_rect_ = webrtc::DesktopRect::MakeLTRB(
295 std::min(accumulating_rect_.left(), rect.left()),
296 std::min(accumulating_rect_.top(), rect.top()),
297 std::max(accumulating_rect_.right(), rect.right()),
298 std::max(accumulating_rect_.bottom(), rect.bottom()));
299 }
300 }
301
302 RGBTriplets TestVideoRenderer::Core::CalculateAverageColorTriplets(
303 const webrtc::DesktopRect& rect) const {
304 int red_sum = 0;
305 int green_sum = 0;
306 int blue_sum = 0;
307
308 // Loop through pixels that fall within |accumulating_rect_| to obtain the
309 // average color triplets.
310 for (int y = accumulating_rect_.top(); y < accumulating_rect_.bottom(); ++y) {
311 uint8_t* ptr = buffer_->data() + (y * buffer_->stride() +
312 accumulating_rect_.left() *
313 webrtc::DesktopFrame::kBytesPerPixel);
314
315 // Pixels of decoded video frame are presented in ARGB format.
316 for (int x = 0; x < accumulating_rect_.width(); ++x) {
317 red_sum += *(ptr + 2);
318 green_sum += *(ptr + 1);
319 blue_sum += *(ptr + 0);
320 ptr += 4;
321 }
322 }
323
324 int area = screen_size_.width() * screen_size_.height();
325 RGBTriplets rgb_triplets(red_sum / area, green_sum / area, blue_sum / area);
326 return rgb_triplets;
327 }
328
329 bool TestVideoRenderer::Core::ExpectedColorIsMatched(
330 const RGBTriplets& candidate_avg_triplets) const {
331 RGBTriplets expected_avg_triplets =
332 ConvertRGBA32ToTriplets(expected_avg_color_);
333 double error_sum_squares = 0;
334 double red_error = expected_avg_triplets.red_ - candidate_avg_triplets.red_;
335 double green_error =
336 expected_avg_triplets.green_ - candidate_avg_triplets.green_;
337 double blue_error =
338 expected_avg_triplets.blue_ - candidate_avg_triplets.blue_;
339 error_sum_squares = red_error * red_error + green_error * green_error +
340 blue_error * blue_error;
341 error_sum_squares /= (255.0 * 255.0);
342
343 return sqrt(error_sum_squares / 3) < kColorFuzzyThreshold;
344 }
345
346 bool TestVideoRenderer::Core::ExpectedImagePatternIsMatched(
347 const RGBTriplets& candidate_avg_triplets) const {
348 // |accumulating_rect_| must be valid in order to be matched.
349 return accumulating_rect_.width() && accumulating_rect_.height() &&
350 ContainsRectWithErrorMargin(accumulating_rect_, expected_rect_,
351 kRectFuzzyThreshold) &&
352 ContainsRectWithErrorMargin(expected_rect_, accumulating_rect_,
353 kRectFuzzyThreshold) &&
354 ExpectedColorIsMatched(candidate_avg_triplets);
355 }
356
191 TestVideoRenderer::TestVideoRenderer() 357 TestVideoRenderer::TestVideoRenderer()
192 : video_decode_thread_( 358 : video_decode_thread_(
193 new base::Thread("TestVideoRendererVideoDecodingThread")), 359 new base::Thread("TestVideoRendererVideoDecodingThread")),
194 weak_factory_(this) { 360 weak_factory_(this) {
195 DCHECK(thread_checker_.CalledOnValidThread()); 361 DCHECK(thread_checker_.CalledOnValidThread());
196 362
197 core_.reset(new Core()); 363 core_.reset(new Core());
198 if (!video_decode_thread_->Start()) { 364 if (!video_decode_thread_->Start()) {
199 LOG(ERROR) << "Cannot start TestVideoRenderer"; 365 LOG(ERROR) << "Cannot start TestVideoRenderer";
200 } else { 366 } else {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 } 434 }
269 435
270 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const { 436 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const {
271 DCHECK(thread_checker_.CalledOnValidThread()); 437 DCHECK(thread_checker_.CalledOnValidThread());
272 438
273 return core_->GetBufferForTest(); 439 return core_->GetBufferForTest();
274 } 440 }
275 441
276 void TestVideoRenderer::SetImagePatternAndMatchedCallback( 442 void TestVideoRenderer::SetImagePatternAndMatchedCallback(
277 const webrtc::DesktopRect& expected_rect, 443 const webrtc::DesktopRect& expected_rect,
278 const RgbaColor& expected_color, 444 const RGBA32& expected_avg_color,
279 const base::Closure& image_pattern_matched_callback) { 445 const base::Closure& image_pattern_matched_callback) {
280 DCHECK(thread_checker_.CalledOnValidThread()); 446 DCHECK(thread_checker_.CalledOnValidThread());
281 447
282 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called"; 448 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called";
283 video_decode_task_runner_->PostTask( 449 video_decode_task_runner_->PostTask(
284 FROM_HERE, base::Bind(&Core::SetImagePatternAndMatchedCallback, 450 FROM_HERE,
285 base::Unretained(core_.get()), expected_rect, 451 base::Bind(&Core::SetImagePatternAndMatchedCallback,
286 expected_color, image_pattern_matched_callback)); 452 base::Unretained(core_.get()), expected_rect,
453 expected_avg_color, image_pattern_matched_callback));
287 } 454 }
288 455
289 } // namespace test 456 } // namespace test
290 } // namespace remoting 457 } // 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