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

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: "Added unit tests for image comparison logic" 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 <cmath>
8
7 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
8 #include "base/logging.h" 11 #include "base/logging.h"
9 #include "base/synchronization/lock.h" 12 #include "base/synchronization/lock.h"
10 #include "base/thread_task_runner_handle.h" 13 #include "base/thread_task_runner_handle.h"
11 #include "base/threading/thread.h" 14 #include "base/threading/thread.h"
12 #include "remoting/codec/video_decoder.h" 15 #include "remoting/codec/video_decoder.h"
13 #include "remoting/codec/video_decoder_verbatim.h" 16 #include "remoting/codec/video_decoder_verbatim.h"
14 #include "remoting/codec/video_decoder_vpx.h" 17 #include "remoting/codec/video_decoder_vpx.h"
15 #include "remoting/proto/video.pb.h" 18 #include "remoting/proto/video.pb.h"
16 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 19 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
17 20
21 // RGB Color triplets, and it can be converted to and from RGBA32.
joedow 2015/07/10 23:39:16 This comment is a little confusing, I don't think
liaoyuke 2015/07/13 16:05:38 Acknowledged.
22 struct RGBTriplets {
joedow 2015/07/10 23:39:15 Would RgbValue be a better name here? If we care
liaoyuke 2015/07/13 16:05:38 Yes, and maybe I should move the convertion functi
23 RGBTriplets(int r, int g, int b) : red(r), green(g), blue(b) {}
24 int red;
25 int green;
26 int blue;
27 };
joedow 2015/07/10 23:39:16 This struct should be in the anonymous namespace b
liaoyuke 2015/07/13 16:05:38 Done.
28
29 namespace {
30 // Used to account for video frame resizing and lossy encoding, and the error is
31 // in percent.
joedow 2015/07/10 23:39:16 nit: Can you condense this comment so it fits on o
liaoyuke 2015/07/13 16:05:38 Done.
32 const double kMaxColorError = 0.02;
33 } // namespace
34
joedow 2015/07/10 23:39:15 no need for an end "// namespace" comment when the
liaoyuke 2015/07/13 16:05:38 I actually do get a lint error: "Anonymous namespa
18 namespace remoting { 35 namespace remoting {
19 namespace test { 36 namespace test {
20 37
21 // Implements video decoding functionality. 38 // Implements video decoding functionality.
22 class TestVideoRenderer::Core { 39 class TestVideoRenderer::Core {
23 public: 40 public:
24 Core(); 41 Core();
25 ~Core(); 42 ~Core();
26 43
27 // Initializes the internal structures of the class. 44 // Initializes the internal structures of the class.
28 void Initialize(); 45 void Initialize();
29 46
30 // Used to decode video packets. 47 // Used to decode video packets.
31 void ProcessVideoPacket(scoped_ptr<VideoPacket> packet, 48 void ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
32 const base::Closure& done); 49 const base::Closure& done);
33 50
34 // Initialize a decoder to decode video packets. 51 // Initialize a decoder to decode video packets.
35 void SetCodecForDecoding(const protocol::ChannelConfig::Codec codec); 52 void SetCodecForDecoding(const protocol::ChannelConfig::Codec codec);
36 53
37 // Returns a copy of the current buffer. 54 // Returns a copy of the current frame.
38 scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const; 55 scoped_ptr<webrtc::DesktopFrame> GetFrameForTest() const;
39 56
40 // Set expected image pattern for comparison and the callback will be called 57 // Set expected image pattern for comparison and the callback will be called
41 // when the pattern is matched. 58 // when the pattern is matched.
42 void SetImagePatternAndMatchedCallback( 59 void SetImagePatternAndMatchedCallback(
43 const webrtc::DesktopRect& expected_rect, 60 const webrtc::DesktopRect& expected_rect,
44 const RgbaColor& expected_color, 61 const RGBA32& expected_avg_color,
joedow 2015/07/10 23:39:16 Does this need to be a const ref since it is just
liaoyuke 2015/07/13 16:05:38 Done.
45 const base::Closure& image_pattern_matched_callback); 62 const base::Closure& image_pattern_matched_callback);
46 63
64 // Convert a RGB triplets to 32 bit RGBA.
65 static RGBA32 ConvertTripletsToRGBA32(const RGBTriplets& rgb_triplets);
66
67 // Convert a 32 bit RGBA to a RGB triplets.
68 static RGBTriplets ConvertRGBA32ToTriplets(RGBA32 color);
joedow 2015/07/10 23:39:15 Do these need to be static or can they live in the
liaoyuke 2015/07/13 16:05:38 Yes, I think so, since they are only used with RGB
69
47 private: 70 private:
71 // Returns average color of pixels fall within |rect| on the current frame.
72 RGBTriplets CalculateAverageColorTriplets(
73 const webrtc::DesktopRect& rect) const;
74
75 // Compares |candidate_avg_triplets| to |expected_avg_color_|.
76 // Returns true if the root mean square of the errors in the R, G and B
77 // components does not exceed a given limit.
78 bool ExpectedColorIsMatched(const RGBTriplets& candidate_avg_triplets,
79 double error_limit) 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> frame_;
65 98
66 // Protects access to |buffer_|. 99 // Protects access to |frame_|.
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 RGBA32 expected_avg_color_;
72
73 // Maintains accumulating image pattern.
74 webrtc::DesktopRect accumulating_rect_;
75 RgbaColor accumulating_color_;
76 105
77 // Used to store the callback when expected pattern is matched. 106 // Used to store the callback when expected pattern is matched.
78 base::Closure image_pattern_matched_callback_; 107 base::Closure image_pattern_matched_callback_;
79 108
80 DISALLOW_COPY_AND_ASSIGN(Core); 109 DISALLOW_COPY_AND_ASSIGN(Core);
81 }; 110 };
82 111
83 TestVideoRenderer::Core::Core() 112 TestVideoRenderer::Core::Core()
84 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) { 113 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
85 thread_checker_.DetachFromThread(); 114 thread_checker_.DetachFromThread();
(...skipping 30 matching lines...) Expand all
116 VLOG(1) << "Test Video Renderer will use VERBATIM decoder"; 145 VLOG(1) << "Test Video Renderer will use VERBATIM decoder";
117 decoder_.reset(new VideoDecoderVerbatim()); 146 decoder_.reset(new VideoDecoderVerbatim());
118 break; 147 break;
119 } 148 }
120 default: { 149 default: {
121 NOTREACHED() << "Unsupported codec: " << codec; 150 NOTREACHED() << "Unsupported codec: " << codec;
122 } 151 }
123 } 152 }
124 } 153 }
125 154
126 scoped_ptr<webrtc::DesktopFrame> 155 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::Core::GetFrameForTest()
127 TestVideoRenderer::Core::GetBufferForTest() const { 156 const {
128 base::AutoLock auto_lock(lock_); 157 base::AutoLock auto_lock(lock_);
129 DCHECK(buffer_); 158 DCHECK(frame_);
130 return make_scoped_ptr(webrtc::BasicDesktopFrame::CopyOf(*buffer_.get())); 159 return make_scoped_ptr(webrtc::BasicDesktopFrame::CopyOf(*frame_.get()));
131 } 160 }
132 161
133 void TestVideoRenderer::Core::ProcessVideoPacket( 162 void TestVideoRenderer::Core::ProcessVideoPacket(
134 scoped_ptr<VideoPacket> packet, const base::Closure& done) { 163 scoped_ptr<VideoPacket> packet, const base::Closure& done) {
135 DCHECK(thread_checker_.CalledOnValidThread()); 164 DCHECK(thread_checker_.CalledOnValidThread());
136 DCHECK(decoder_); 165 DCHECK(decoder_);
137 DCHECK(packet); 166 DCHECK(packet);
138 167
139 VLOG(2) << "TestVideoRenderer::Core::ProcessVideoPacket() Called"; 168 VLOG(2) << "TestVideoRenderer::Core::ProcessVideoPacket() Called";
140 169
141 // Screen size is attached on the first packet as well as when the 170 // Screen size is attached on the first packet as well as when the
142 // host screen is resized. 171 // host screen is resized.
143 if (packet->format().has_screen_width() && 172 if (packet->format().has_screen_width() &&
144 packet->format().has_screen_height()) { 173 packet->format().has_screen_height()) {
145 webrtc::DesktopSize source_size(packet->format().screen_width(), 174 webrtc::DesktopSize source_size(packet->format().screen_width(),
146 packet->format().screen_height()); 175 packet->format().screen_height());
147 if (!screen_size_.equals(source_size)) { 176 if (!screen_size_.equals(source_size)) {
148 screen_size_ = source_size; 177 screen_size_ = source_size;
149 decoder_->Initialize(screen_size_); 178 decoder_->Initialize(screen_size_);
150 buffer_.reset(new webrtc::BasicDesktopFrame(screen_size_)); 179 frame_.reset(new webrtc::BasicDesktopFrame(screen_size_));
151 } 180 }
152 } 181 }
153 182
154 // To make life easier, assume that the desktop shape is a single rectangle. 183 // To make life easier, assume that the desktop shape is a single rectangle.
155 packet->clear_use_desktop_shape(); 184 packet->clear_use_desktop_shape();
156 if (!decoder_->DecodePacket(*packet.get())) { 185 if (!decoder_->DecodePacket(*packet.get())) {
157 LOG(ERROR) << "Decoder::DecodePacket() failed."; 186 LOG(ERROR) << "Decoder::DecodePacket() failed.";
158 return; 187 return;
159 } 188 }
160 189
161 { 190 {
162 base::AutoLock auto_lock(lock_); 191 base::AutoLock auto_lock(lock_);
163 192
164 // Render the decoded packet and write results to the buffer. 193 // Render the decoded packet and write results to the buffer.
165 // Note that the |updated_region_| maintains the changed regions compared to 194 // Note that the |updated_region_| maintains the changed regions compared to
166 // previous video frame. 195 // previous video frame.
167 decoder_->RenderFrame(screen_size_, 196 decoder_->RenderFrame(screen_size_,
168 webrtc::DesktopRect::MakeWH(screen_size_.width(), 197 webrtc::DesktopRect::MakeWH(screen_size_.width(),
169 screen_size_.height()), buffer_->data(), 198 screen_size_.height()),
170 buffer_->stride(), &updated_region_); 199 frame_->data(), frame_->stride(), &updated_region_);
171 } 200 }
172 201
173 main_task_runner_->PostTask(FROM_HERE, done); 202 main_task_runner_->PostTask(FROM_HERE, done);
174 203
175 // TODO(liaoyuke): Update |accumulating_rect_| and |accumulating_color_|, then 204 // Check to see if a image pattern matched reply is passed in, and whether
176 // compare to the expected image pattern to check whether the pattern is 205 // the |expected_rect_| falls within the current frame.
177 // matched or not and update |image_pattern_matched| accordingly. 206 if (image_pattern_matched_callback_.is_null() ||
207 expected_rect_.right() > frame_->size().width() ||
208 expected_rect_.bottom() > frame_->size().height()) {
209 return;
210 }
211
212 // Compare the expected image pattern with the corresponding rectangle region
213 // on the current frame.
214 RGBTriplets accumulating_avg_triplets =
215 CalculateAverageColorTriplets(expected_rect_);
216 LOG(INFO) << accumulating_avg_triplets.red << " "
joedow 2015/07/10 23:39:16 LOG(INFO) is pretty chatty, can this be a VLOG(2)?
liaoyuke 2015/07/13 16:05:38 Done.
217 << accumulating_avg_triplets.green << " "
218 << accumulating_avg_triplets.blue;
219 if (ExpectedColorIsMatched(accumulating_avg_triplets, kMaxColorError)) {
joedow 2015/07/10 23:39:15 Does kMaxColorError need to be passed? If you alw
liaoyuke 2015/07/13 16:05:38 Done.
220 main_task_runner_->PostTask(
221 FROM_HERE, base::ResetAndReturn(&image_pattern_matched_callback_));
222 }
178 } 223 }
179 224
180 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback( 225 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback(
181 const webrtc::DesktopRect& expected_rect, 226 const webrtc::DesktopRect& expected_rect,
182 const RgbaColor& expected_color, 227 const RGBA32& expected_avg_color,
joedow 2015/07/10 23:39:16 Remove const?
liaoyuke 2015/07/13 16:05:38 Done.
183 const base::Closure& image_pattern_matched_callback) { 228 const base::Closure& image_pattern_matched_callback) {
184 DCHECK(thread_checker_.CalledOnValidThread()); 229 DCHECK(thread_checker_.CalledOnValidThread());
185 230
186 expected_rect_ = expected_rect; 231 expected_rect_ = expected_rect;
187 expected_color_ = expected_color; 232 expected_avg_color_ = expected_avg_color;
188 image_pattern_matched_callback_ = image_pattern_matched_callback; 233 image_pattern_matched_callback_ = image_pattern_matched_callback;
189 } 234 }
190 235
236 RGBA32 TestVideoRenderer::Core::ConvertTripletsToRGBA32(
237 const RGBTriplets& rgb_triplets) {
238 return 0xFF000000 | (rgb_triplets.red << 16) | (rgb_triplets.green << 8) |
239 rgb_triplets.blue;
240 }
241
242 RGBTriplets TestVideoRenderer::Core::ConvertRGBA32ToTriplets(RGBA32 color) {
243 RGBTriplets rgb_triplets((color >> 16) & 0xFF, (color >> 8) & 0xFF,
244 color & 0xFF);
245 return rgb_triplets;
246 }
247
248 RGBTriplets TestVideoRenderer::Core::CalculateAverageColorTriplets(
249 const webrtc::DesktopRect& rect) const {
250 int red_sum = 0;
251 int green_sum = 0;
252 int blue_sum = 0;
253
254 // Loop through pixels that fall within |accumulating_rect_| to obtain the
255 // average color triplets.
256 for (int y = rect.top(); y < rect.bottom(); ++y) {
257 uint8_t* frame_pos =
258 frame_->data() + (y * frame_->stride() +
259 rect.left() * webrtc::DesktopFrame::kBytesPerPixel);
260
261 // Pixels of decoded video frame are presented in ARGB format.
262 for (int x = 0; x < rect.width(); ++x) {
263 red_sum += frame_pos[2];
264 green_sum += frame_pos[1];
265 blue_sum += frame_pos[0];
266 frame_pos += 4;
267 }
268 }
269
270 int area = rect.width() * rect.height();
271 RGBTriplets rgb_triplets(red_sum / area, green_sum / area, blue_sum / area);
272 return rgb_triplets;
273 }
274
275 bool TestVideoRenderer::Core::ExpectedColorIsMatched(
276 const RGBTriplets& candidate_avg_triplets,
277 double error_limit) const {
278 RGBTriplets expected_avg_triplets =
279 ConvertRGBA32ToTriplets(expected_avg_color_);
280 double error_sum_squares = 0;
281 double red_error = expected_avg_triplets.red - candidate_avg_triplets.red;
282 double green_error =
283 expected_avg_triplets.green - candidate_avg_triplets.green;
284 double blue_error = expected_avg_triplets.blue - candidate_avg_triplets.blue;
285 error_sum_squares = red_error * red_error + green_error * green_error +
286 blue_error * blue_error;
287 error_sum_squares /= (255.0 * 255.0);
288
289 return sqrt(error_sum_squares / 3) < error_limit;
290 }
291
191 TestVideoRenderer::TestVideoRenderer() 292 TestVideoRenderer::TestVideoRenderer()
192 : video_decode_thread_( 293 : video_decode_thread_(
193 new base::Thread("TestVideoRendererVideoDecodingThread")), 294 new base::Thread("TestVideoRendererVideoDecodingThread")),
194 weak_factory_(this) { 295 weak_factory_(this) {
195 DCHECK(thread_checker_.CalledOnValidThread()); 296 DCHECK(thread_checker_.CalledOnValidThread());
196 297
197 core_.reset(new Core()); 298 core_.reset(new Core());
198 if (!video_decode_thread_->Start()) { 299 if (!video_decode_thread_->Start()) {
199 LOG(ERROR) << "Cannot start TestVideoRenderer"; 300 LOG(ERROR) << "Cannot start TestVideoRenderer";
200 } else { 301 } else {
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
260 const protocol::ChannelConfig::Codec codec) { 361 const protocol::ChannelConfig::Codec codec) {
261 DCHECK(thread_checker_.CalledOnValidThread()); 362 DCHECK(thread_checker_.CalledOnValidThread());
262 363
263 VLOG(2) << "TestVideoRenderer::SetDecoder() Called"; 364 VLOG(2) << "TestVideoRenderer::SetDecoder() Called";
264 video_decode_task_runner_->PostTask( 365 video_decode_task_runner_->PostTask(
265 FROM_HERE, base::Bind(&Core::SetCodecForDecoding, 366 FROM_HERE, base::Bind(&Core::SetCodecForDecoding,
266 base::Unretained(core_.get()), 367 base::Unretained(core_.get()),
267 codec)); 368 codec));
268 } 369 }
269 370
270 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const { 371 scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetFrameForTest() const {
271 DCHECK(thread_checker_.CalledOnValidThread()); 372 DCHECK(thread_checker_.CalledOnValidThread());
272 373
273 return core_->GetBufferForTest(); 374 return core_->GetFrameForTest();
274 } 375 }
275 376
276 void TestVideoRenderer::SetImagePatternAndMatchedCallback( 377 void TestVideoRenderer::SetImagePatternAndMatchedCallback(
277 const webrtc::DesktopRect& expected_rect, 378 const webrtc::DesktopRect& expected_rect,
278 const RgbaColor& expected_color, 379 const RGBA32& expected_avg_color,
joedow 2015/07/10 23:39:16 remove const?
liaoyuke 2015/07/13 16:05:38 Done.
279 const base::Closure& image_pattern_matched_callback) { 380 const base::Closure& image_pattern_matched_callback) {
280 DCHECK(thread_checker_.CalledOnValidThread()); 381 DCHECK(thread_checker_.CalledOnValidThread());
382 DCHECK(!expected_rect.is_empty()) << "Expected rect cannot be empty";
281 383
282 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called"; 384 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called";
283 video_decode_task_runner_->PostTask( 385 video_decode_task_runner_->PostTask(
284 FROM_HERE, base::Bind(&Core::SetImagePatternAndMatchedCallback, 386 FROM_HERE,
285 base::Unretained(core_.get()), expected_rect, 387 base::Bind(&Core::SetImagePatternAndMatchedCallback,
286 expected_color, image_pattern_matched_callback)); 388 base::Unretained(core_.get()), expected_rect,
389 expected_avg_color, image_pattern_matched_callback));
287 } 390 }
288 391
289 } // namespace test 392 } // namespace test
290 } // namespace remoting 393 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698