Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |