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 <algorithm> | |
| 8 #include <cmath> | |
|
liaoyuke
2015/07/08 18:58:15
Usage:
algorithm: std::min, std::max, cmath: sqrt
| |
| 9 | |
| 7 #include "base/bind.h" | 10 #include "base/bind.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 namespace { | |
| 22 const int kBytesPerPixel = 4; | |
| 23 } // namespace | |
| 24 | |
| 18 namespace remoting { | 25 namespace remoting { |
| 19 namespace test { | 26 namespace test { |
| 20 | 27 |
| 21 // Implements video decoding functionality. | 28 // Implements video decoding functionality. |
| 22 class TestVideoRenderer::Core { | 29 class TestVideoRenderer::Core { |
| 23 public: | 30 public: |
| 24 Core(); | 31 Core(); |
| 25 ~Core(); | 32 ~Core(); |
| 26 | 33 |
| 27 // Initializes the internal structures of the class. | 34 // Initializes the internal structures of the class. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 38 scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const; | 45 scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const; |
| 39 | 46 |
| 40 // Set expected image pattern for comparison and the callback will be called | 47 // Set expected image pattern for comparison and the callback will be called |
| 41 // when the pattern is matched. | 48 // when the pattern is matched. |
| 42 void SetImagePatternAndMatchedCallback( | 49 void SetImagePatternAndMatchedCallback( |
| 43 const webrtc::DesktopRect& expected_rect, | 50 const webrtc::DesktopRect& expected_rect, |
| 44 const RgbaColor& expected_color, | 51 const RgbaColor& expected_color, |
| 45 const base::Closure& image_pattern_matched_callback); | 52 const base::Closure& image_pattern_matched_callback); |
| 46 | 53 |
| 47 private: | 54 private: |
| 55 // Converts a set of RGB color to the RgbaColor format by adding the alpha | |
| 56 // values of 0x00. | |
| 57 RgbaColor ColorEncode(const int red, const int green, const int blue) const; | |
|
joedow
2015/07/09 03:03:43
Why are the int values const here? ints are passe
liaoyuke
2015/07/09 18:18:32
I think you are right, they shouldn't be const
| |
| 58 | |
| 59 // Converts a RgbaColor instance to an array of RGBA colors. | |
| 60 scoped_ptr<int> ColorDecode(const RgbaColor& rgba_color) const; | |
| 61 | |
| 62 // Returns true if |rect2| falls within |rect1|, allowing for certain amount | |
| 63 // of error specified by a fuzzy threshold. | |
| 64 bool ContainsRect(const webrtc::DesktopRect& rect1, | |
| 65 const webrtc::DesktopRect& rect2) const; | |
| 66 | |
| 67 // Expand the |accumulating_rect_| to a minimum rectangle that contains both | |
| 68 // |rect| and itself. | |
| 69 void MergeRectToAccumulatingRect(const webrtc::DesktopRect& rect); | |
| 70 | |
| 71 // Update |accumulating_color_| to reflect the average color value of pixels | |
| 72 // fall within |accumulating_rect_|. | |
| 73 void UpdateAccumulatingColor(); | |
| 74 | |
| 75 // Compares |accumulating_color_| to |expected_color_|. | |
| 76 // Returns true if the root mean square of the errors in the R, G and B | |
| 77 // components does not exceed a fuzzy threshold. | |
| 78 bool ExpectedColorIsMatched() const; | |
| 79 | |
| 80 // Returns true if |expected_rect_| and |accumulating_rect_| contains each | |
| 81 // other and expected color is matched. | |
| 82 bool ExpectedImagePatternIsMatched() const; | |
| 83 | |
| 48 // Used to ensure Core methods are called on the same thread. | 84 // Used to ensure Core methods are called on the same thread. |
| 49 base::ThreadChecker thread_checker_; | 85 base::ThreadChecker thread_checker_; |
| 50 | 86 |
| 51 // Used to decode video packets. | 87 // Used to decode video packets. |
| 52 scoped_ptr<VideoDecoder> decoder_; | 88 scoped_ptr<VideoDecoder> decoder_; |
| 53 | 89 |
| 54 // Updated region of the current desktop frame compared to previous one. | 90 // Updated region of the current desktop frame compared to previous one. |
| 55 webrtc::DesktopRegion updated_region_; | 91 webrtc::DesktopRegion updated_region_; |
| 56 | 92 |
| 57 // Screen size of the remote host. | 93 // Screen size of the remote host. |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 70 webrtc::DesktopRect expected_rect_; | 106 webrtc::DesktopRect expected_rect_; |
| 71 RgbaColor expected_color_; | 107 RgbaColor expected_color_; |
| 72 | 108 |
| 73 // Maintains accumulating image pattern. | 109 // Maintains accumulating image pattern. |
| 74 webrtc::DesktopRect accumulating_rect_; | 110 webrtc::DesktopRect accumulating_rect_; |
| 75 RgbaColor accumulating_color_; | 111 RgbaColor accumulating_color_; |
| 76 | 112 |
| 77 // Used to store the callback when expected pattern is matched. | 113 // Used to store the callback when expected pattern is matched. |
| 78 base::Closure image_pattern_matched_callback_; | 114 base::Closure image_pattern_matched_callback_; |
| 79 | 115 |
| 116 // Used to account for video frame resizing and lossy encoding. | |
| 117 const double fuzzy_threshold_ = 0.05; | |
|
joedow
2015/07/09 03:03:43
Does this need to be a class member or can it just
liaoyuke
2015/07/09 18:18:32
Done.
| |
| 118 | |
| 80 DISALLOW_COPY_AND_ASSIGN(Core); | 119 DISALLOW_COPY_AND_ASSIGN(Core); |
| 81 }; | 120 }; |
| 82 | 121 |
| 83 TestVideoRenderer::Core::Core() | 122 TestVideoRenderer::Core::Core() |
| 84 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) { | 123 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) { |
| 85 thread_checker_.DetachFromThread(); | 124 thread_checker_.DetachFromThread(); |
| 86 } | 125 } |
| 87 | 126 |
| 88 TestVideoRenderer::Core::~Core() { | 127 TestVideoRenderer::Core::~Core() { |
| 89 DCHECK(thread_checker_.CalledOnValidThread()); | 128 DCHECK(thread_checker_.CalledOnValidThread()); |
| 90 } | 129 } |
| 91 | 130 |
| 92 void TestVideoRenderer::Core::Initialize() { | 131 void TestVideoRenderer::Core::Initialize() { |
| 93 DCHECK(thread_checker_.CalledOnValidThread()); | 132 DCHECK(thread_checker_.CalledOnValidThread()); |
| 133 | |
| 134 accumulating_rect_ = webrtc::DesktopRect::MakeLTRB(0, 0, 0, 0); | |
| 94 } | 135 } |
| 95 | 136 |
| 96 void TestVideoRenderer::Core::SetCodecForDecoding( | 137 void TestVideoRenderer::Core::SetCodecForDecoding( |
| 97 const protocol::ChannelConfig::Codec codec) { | 138 const protocol::ChannelConfig::Codec codec) { |
| 98 DCHECK(thread_checker_.CalledOnValidThread()); | 139 DCHECK(thread_checker_.CalledOnValidThread()); |
| 99 | 140 |
| 100 if (decoder_) { | 141 if (decoder_) { |
| 101 LOG(WARNING) << "Decoder is set more than once"; | 142 LOG(WARNING) << "Decoder is set more than once"; |
| 102 } | 143 } |
| 103 | 144 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 // Note that the |updated_region_| maintains the changed regions compared to | 206 // Note that the |updated_region_| maintains the changed regions compared to |
| 166 // previous video frame. | 207 // previous video frame. |
| 167 decoder_->RenderFrame(screen_size_, | 208 decoder_->RenderFrame(screen_size_, |
| 168 webrtc::DesktopRect::MakeWH(screen_size_.width(), | 209 webrtc::DesktopRect::MakeWH(screen_size_.width(), |
| 169 screen_size_.height()), buffer_->data(), | 210 screen_size_.height()), buffer_->data(), |
| 170 buffer_->stride(), &updated_region_); | 211 buffer_->stride(), &updated_region_); |
| 171 } | 212 } |
| 172 | 213 |
| 173 main_task_runner_->PostTask(FROM_HERE, done); | 214 main_task_runner_->PostTask(FROM_HERE, done); |
| 174 | 215 |
| 175 // TODO(liaoyuke): Update |accumulating_rect_| and |accumulating_color_|, then | 216 // 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 | 217 if (!image_pattern_matched_callback_.is_null()) { |
|
joedow
2015/07/09 03:03:43
This be simpler if you return here if the callback
liaoyuke
2015/07/09 18:18:32
Done.
| |
| 177 // matched or not and update |image_pattern_matched| accordingly. | 218 for (webrtc::DesktopRegion::Iterator dirty_region(updated_region_); |
| 219 !dirty_region.IsAtEnd(); dirty_region.Advance()) { | |
| 220 webrtc::DesktopRect dirty_rect = dirty_region.rect(); | |
| 221 | |
| 222 // Dirty rects not falling within |expected_rect_| doesn't affect the | |
| 223 // result. | |
| 224 if (ContainsRect(expected_rect_, dirty_rect)) { | |
| 225 MergeRectToAccumulatingRect(dirty_rect); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 UpdateAccumulatingColor(); | |
| 230 if (ExpectedImagePatternIsMatched()) { | |
| 231 image_pattern_matched_callback_.Run(); | |
| 232 image_pattern_matched_callback_.Reset(); | |
|
joedow
2015/07/09 03:03:43
base::ResetAndReturn() can replace lines 231 and 2
liaoyuke
2015/07/09 18:18:32
Done.
| |
| 233 accumulating_rect_ = webrtc::DesktopRect::MakeLTRB(0, 0, 0, 0); | |
| 234 } | |
| 235 } | |
| 178 } | 236 } |
| 179 | 237 |
| 180 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback( | 238 void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback( |
| 181 const webrtc::DesktopRect& expected_rect, | 239 const webrtc::DesktopRect& expected_rect, |
| 182 const RgbaColor& expected_color, | 240 const RgbaColor& expected_color, |
| 183 const base::Closure& image_pattern_matched_callback) { | 241 const base::Closure& image_pattern_matched_callback) { |
| 184 DCHECK(thread_checker_.CalledOnValidThread()); | 242 DCHECK(thread_checker_.CalledOnValidThread()); |
| 185 | 243 |
| 186 expected_rect_ = expected_rect; | 244 expected_rect_ = expected_rect; |
| 187 expected_color_ = expected_color; | 245 expected_color_ = expected_color; |
| 188 image_pattern_matched_callback_ = image_pattern_matched_callback; | 246 image_pattern_matched_callback_ = image_pattern_matched_callback; |
| 189 } | 247 } |
| 190 | 248 |
| 249 RgbaColor TestVideoRenderer::Core::ColorEncode(const int red, | |
| 250 const int green, | |
| 251 const int blue) const { | |
| 252 RgbaColor rgba_color = red; | |
| 253 rgba_color = (rgba_color << 8) + green; | |
| 254 rgba_color = (rgba_color << 8) + blue; | |
| 255 rgba_color = (rgba_color << 8) + 0; | |
| 256 | |
| 257 return rgba_color; | |
| 258 } | |
| 259 | |
| 260 scoped_ptr<int> TestVideoRenderer::Core::ColorDecode( | |
| 261 const RgbaColor& rgba_color) const { | |
|
joedow
2015/07/09 03:03:43
If you are going to return an array, you want scop
| |
| 262 int* rgba_colors = new int[3]; | |
| 263 uint32 mask = (1 << 9) - 1; | |
| 264 rgba_colors[2] = ((rgba_color >> 8) & mask); | |
| 265 rgba_colors[1] = ((rgba_color >> 16) & mask); | |
| 266 rgba_colors[0] = ((rgba_color >> 24) & mask); | |
| 267 | |
| 268 return make_scoped_ptr(rgba_colors); | |
| 269 } | |
| 270 | |
| 271 bool TestVideoRenderer::Core::ContainsRect( | |
|
joedow
2015/07/09 03:03:43
rect1 and rect2 aren't quite descriptive enough, w
liaoyuke
2015/07/09 18:18:32
Done.
| |
| 272 const webrtc::DesktopRect& rect1, | |
| 273 const webrtc::DesktopRect& rect2) const { | |
| 274 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 275 DCHECK(!image_pattern_matched_callback_.is_null()); | |
|
joedow
2015/07/09 03:03:43
Does it matter if the callback is not null here?
liaoyuke
2015/07/09 18:18:32
At first, I thought this function will only be cal
| |
| 276 | |
| 277 return rect2.left() >= (1 - fuzzy_threshold_) * rect1.left() && | |
| 278 rect2.top() >= (1 - fuzzy_threshold_) * rect1.top() && | |
| 279 rect2.right() <= (1 + fuzzy_threshold_) * rect1.right() && | |
| 280 rect2.bottom() <= (1 + fuzzy_threshold_) * rect1.bottom(); | |
| 281 } | |
| 282 | |
| 283 void TestVideoRenderer::Core::MergeRectToAccumulatingRect( | |
| 284 const webrtc::DesktopRect& rect) { | |
| 285 if (accumulating_rect_.width() == 0 && accumulating_rect_.height() == 0) { | |
|
joedow
2015/07/09 03:03:43
if (!accumulating_rect_.width() && !accumulating_r
liaoyuke
2015/07/09 18:18:32
I think it's not possible, that's why I use it as
| |
| 286 accumulating_rect_ = rect; | |
| 287 } else { | |
| 288 accumulating_rect_ = webrtc::DesktopRect::MakeLTRB( | |
| 289 std::min(accumulating_rect_.left(), rect.left()), | |
| 290 std::min(accumulating_rect_.top(), rect.top()), | |
| 291 std::max(accumulating_rect_.right(), rect.right()), | |
| 292 std::max(accumulating_rect_.bottom(), rect.bottom())); | |
| 293 } | |
| 294 } | |
| 295 | |
| 296 void TestVideoRenderer::Core::UpdateAccumulatingColor() { | |
| 297 int red_sum = 0; | |
| 298 int green_sum = 0; | |
| 299 int blue_sum = 0; | |
| 300 for (int y = accumulating_rect_.top(); y < accumulating_rect_.bottom(); ++y) { | |
| 301 for (int x = accumulating_rect_.left(); x < accumulating_rect_.right(); | |
| 302 ++x) { | |
| 303 int offset = | |
| 304 y * kBytesPerPixel * screen_size_.width() + x * kBytesPerPixel; | |
|
joedow
2015/07/09 03:03:43
some parens would help with the readability here
liaoyuke
2015/07/09 18:18:32
Done.
| |
| 305 red_sum += buffer_->data()[offset + 2]; | |
| 306 green_sum += buffer_->data()[offset + 1]; | |
|
liaoyuke
2015/07/09 18:18:32
Here, I should mention that the decoded frame is i
| |
| 307 blue_sum += buffer_->data()[offset + 0]; | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 int area = screen_size_.width() * screen_size_.height(); | |
| 312 accumulating_color_ = | |
| 313 ColorEncode(red_sum / area, green_sum / area, blue_sum / area); | |
| 314 } | |
| 315 | |
| 316 bool TestVideoRenderer::Core::ExpectedColorIsMatched() const { | |
| 317 scoped_ptr<int> decoded_expected_color = ColorDecode(expected_color_); | |
| 318 scoped_ptr<int> decoded_accumulating_color = ColorDecode(accumulating_color_); | |
| 319 double error_sum_squares = 0; | |
| 320 for (int i = 0; i < 3; i++) { | |
| 321 double expected_value = | |
| 322 static_cast<double>(decoded_expected_color.get()[i]); | |
| 323 double accumulating_value = | |
| 324 static_cast<double>(decoded_accumulating_color.get()[i]); | |
| 325 double error = expected_value - accumulating_value; | |
| 326 error /= 255.0; | |
| 327 error_sum_squares += error * error; | |
| 328 } | |
| 329 | |
| 330 return sqrt(error_sum_squares / 3) < fuzzy_threshold_; | |
| 331 } | |
| 332 | |
| 333 bool TestVideoRenderer::Core::ExpectedImagePatternIsMatched() const { | |
| 334 return ContainsRect(accumulating_rect_, expected_rect_) && | |
| 335 ContainsRect(expected_rect_, accumulating_rect_) && | |
| 336 ExpectedColorIsMatched(); | |
| 337 } | |
| 338 | |
| 191 TestVideoRenderer::TestVideoRenderer() | 339 TestVideoRenderer::TestVideoRenderer() |
| 192 : video_decode_thread_( | 340 : video_decode_thread_( |
| 193 new base::Thread("TestVideoRendererVideoDecodingThread")), | 341 new base::Thread("TestVideoRendererVideoDecodingThread")), |
| 194 weak_factory_(this) { | 342 weak_factory_(this) { |
| 195 DCHECK(thread_checker_.CalledOnValidThread()); | 343 DCHECK(thread_checker_.CalledOnValidThread()); |
| 196 | 344 |
| 197 core_.reset(new Core()); | 345 core_.reset(new Core()); |
| 198 if (!video_decode_thread_->Start()) { | 346 if (!video_decode_thread_->Start()) { |
| 199 LOG(ERROR) << "Cannot start TestVideoRenderer"; | 347 LOG(ERROR) << "Cannot start TestVideoRenderer"; |
| 200 } else { | 348 } else { |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 281 | 429 |
| 282 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called"; | 430 DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called"; |
| 283 video_decode_task_runner_->PostTask( | 431 video_decode_task_runner_->PostTask( |
| 284 FROM_HERE, base::Bind(&Core::SetImagePatternAndMatchedCallback, | 432 FROM_HERE, base::Bind(&Core::SetImagePatternAndMatchedCallback, |
| 285 base::Unretained(core_.get()), expected_rect, | 433 base::Unretained(core_.get()), expected_rect, |
| 286 expected_color, image_pattern_matched_callback)); | 434 expected_color, image_pattern_matched_callback)); |
| 287 } | 435 } |
| 288 | 436 |
| 289 } // namespace test | 437 } // namespace test |
| 290 } // namespace remoting | 438 } // namespace remoting |
| OLD | NEW |