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