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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: remoting/test/test_video_renderer.cc
diff --git a/remoting/test/test_video_renderer.cc b/remoting/test/test_video_renderer.cc
index 75dd2c9511ba614425c8230dcebcd5e82bbcb23f..d97442bbe883b6b9f9e9d9a5cd7918c864c6240e 100644
--- a/remoting/test/test_video_renderer.cc
+++ b/remoting/test/test_video_renderer.cc
@@ -4,7 +4,10 @@
#include "remoting/test/test_video_renderer.h"
+#include <cmath>
+
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/synchronization/lock.h"
#include "base/thread_task_runner_handle.h"
@@ -15,6 +18,20 @@
#include "remoting/proto/video.pb.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+// 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.
+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
+ RGBTriplets(int r, int g, int b) : red(r), green(g), blue(b) {}
+ int red;
+ int green;
+ int blue;
+};
joedow 2015/07/10 23:39:16 This struct should be in the anonymous namespace b
liaoyuke 2015/07/13 16:05:38 Done.
+
+namespace {
+// Used to account for video frame resizing and lossy encoding, and the error is
+// 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.
+const double kMaxColorError = 0.02;
+} // namespace
+
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
namespace remoting {
namespace test {
@@ -34,17 +51,33 @@ class TestVideoRenderer::Core {
// Initialize a decoder to decode video packets.
void SetCodecForDecoding(const protocol::ChannelConfig::Codec codec);
- // Returns a copy of the current buffer.
- scoped_ptr<webrtc::DesktopFrame> GetBufferForTest() const;
+ // Returns a copy of the current frame.
+ scoped_ptr<webrtc::DesktopFrame> GetFrameForTest() const;
// Set expected image pattern for comparison and the callback will be called
// when the pattern is matched.
void SetImagePatternAndMatchedCallback(
const webrtc::DesktopRect& expected_rect,
- const RgbaColor& expected_color,
+ 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.
const base::Closure& image_pattern_matched_callback);
+ // Convert a RGB triplets to 32 bit RGBA.
+ static RGBA32 ConvertTripletsToRGBA32(const RGBTriplets& rgb_triplets);
+
+ // Convert a 32 bit RGBA to a RGB triplets.
+ 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
+
private:
+ // Returns average color of pixels fall within |rect| on the current frame.
+ RGBTriplets CalculateAverageColorTriplets(
+ const webrtc::DesktopRect& rect) const;
+
+ // Compares |candidate_avg_triplets| to |expected_avg_color_|.
+ // Returns true if the root mean square of the errors in the R, G and B
+ // components does not exceed a given limit.
+ bool ExpectedColorIsMatched(const RGBTriplets& candidate_avg_triplets,
+ double error_limit) const;
+
// Used to ensure Core methods are called on the same thread.
base::ThreadChecker thread_checker_;
@@ -61,18 +94,14 @@ class TestVideoRenderer::Core {
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
// Used to store decoded video frame.
- scoped_ptr<webrtc::DesktopFrame> buffer_;
+ scoped_ptr<webrtc::DesktopFrame> frame_;
- // Protects access to |buffer_|.
+ // Protects access to |frame_|.
mutable base::Lock lock_;
// Used to store the expected image pattern.
webrtc::DesktopRect expected_rect_;
- RgbaColor expected_color_;
-
- // Maintains accumulating image pattern.
- webrtc::DesktopRect accumulating_rect_;
- RgbaColor accumulating_color_;
+ RGBA32 expected_avg_color_;
// Used to store the callback when expected pattern is matched.
base::Closure image_pattern_matched_callback_;
@@ -123,11 +152,11 @@ void TestVideoRenderer::Core::SetCodecForDecoding(
}
}
-scoped_ptr<webrtc::DesktopFrame>
- TestVideoRenderer::Core::GetBufferForTest() const {
+scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::Core::GetFrameForTest()
+ const {
base::AutoLock auto_lock(lock_);
- DCHECK(buffer_);
- return make_scoped_ptr(webrtc::BasicDesktopFrame::CopyOf(*buffer_.get()));
+ DCHECK(frame_);
+ return make_scoped_ptr(webrtc::BasicDesktopFrame::CopyOf(*frame_.get()));
}
void TestVideoRenderer::Core::ProcessVideoPacket(
@@ -147,7 +176,7 @@ void TestVideoRenderer::Core::ProcessVideoPacket(
if (!screen_size_.equals(source_size)) {
screen_size_ = source_size;
decoder_->Initialize(screen_size_);
- buffer_.reset(new webrtc::BasicDesktopFrame(screen_size_));
+ frame_.reset(new webrtc::BasicDesktopFrame(screen_size_));
}
}
@@ -166,28 +195,100 @@ void TestVideoRenderer::Core::ProcessVideoPacket(
// previous video frame.
decoder_->RenderFrame(screen_size_,
webrtc::DesktopRect::MakeWH(screen_size_.width(),
- screen_size_.height()), buffer_->data(),
- buffer_->stride(), &updated_region_);
+ screen_size_.height()),
+ frame_->data(), frame_->stride(), &updated_region_);
}
main_task_runner_->PostTask(FROM_HERE, done);
- // TODO(liaoyuke): Update |accumulating_rect_| and |accumulating_color_|, then
- // compare to the expected image pattern to check whether the pattern is
- // matched or not and update |image_pattern_matched| accordingly.
+ // Check to see if a image pattern matched reply is passed in, and whether
+ // the |expected_rect_| falls within the current frame.
+ if (image_pattern_matched_callback_.is_null() ||
+ expected_rect_.right() > frame_->size().width() ||
+ expected_rect_.bottom() > frame_->size().height()) {
+ return;
+ }
+
+ // Compare the expected image pattern with the corresponding rectangle region
+ // on the current frame.
+ RGBTriplets accumulating_avg_triplets =
+ CalculateAverageColorTriplets(expected_rect_);
+ 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.
+ << accumulating_avg_triplets.green << " "
+ << accumulating_avg_triplets.blue;
+ 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.
+ main_task_runner_->PostTask(
+ FROM_HERE, base::ResetAndReturn(&image_pattern_matched_callback_));
+ }
}
void TestVideoRenderer::Core::SetImagePatternAndMatchedCallback(
const webrtc::DesktopRect& expected_rect,
- const RgbaColor& expected_color,
+ const RGBA32& expected_avg_color,
joedow 2015/07/10 23:39:16 Remove const?
liaoyuke 2015/07/13 16:05:38 Done.
const base::Closure& image_pattern_matched_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
expected_rect_ = expected_rect;
- expected_color_ = expected_color;
+ expected_avg_color_ = expected_avg_color;
image_pattern_matched_callback_ = image_pattern_matched_callback;
}
+RGBA32 TestVideoRenderer::Core::ConvertTripletsToRGBA32(
+ const RGBTriplets& rgb_triplets) {
+ return 0xFF000000 | (rgb_triplets.red << 16) | (rgb_triplets.green << 8) |
+ rgb_triplets.blue;
+}
+
+RGBTriplets TestVideoRenderer::Core::ConvertRGBA32ToTriplets(RGBA32 color) {
+ RGBTriplets rgb_triplets((color >> 16) & 0xFF, (color >> 8) & 0xFF,
+ color & 0xFF);
+ return rgb_triplets;
+}
+
+RGBTriplets TestVideoRenderer::Core::CalculateAverageColorTriplets(
+ const webrtc::DesktopRect& rect) const {
+ int red_sum = 0;
+ int green_sum = 0;
+ int blue_sum = 0;
+
+ // Loop through pixels that fall within |accumulating_rect_| to obtain the
+ // average color triplets.
+ for (int y = rect.top(); y < rect.bottom(); ++y) {
+ uint8_t* frame_pos =
+ frame_->data() + (y * frame_->stride() +
+ rect.left() * webrtc::DesktopFrame::kBytesPerPixel);
+
+ // Pixels of decoded video frame are presented in ARGB format.
+ for (int x = 0; x < rect.width(); ++x) {
+ red_sum += frame_pos[2];
+ green_sum += frame_pos[1];
+ blue_sum += frame_pos[0];
+ frame_pos += 4;
+ }
+ }
+
+ int area = rect.width() * rect.height();
+ RGBTriplets rgb_triplets(red_sum / area, green_sum / area, blue_sum / area);
+ return rgb_triplets;
+}
+
+bool TestVideoRenderer::Core::ExpectedColorIsMatched(
+ const RGBTriplets& candidate_avg_triplets,
+ double error_limit) const {
+ RGBTriplets expected_avg_triplets =
+ ConvertRGBA32ToTriplets(expected_avg_color_);
+ double error_sum_squares = 0;
+ double red_error = expected_avg_triplets.red - candidate_avg_triplets.red;
+ double green_error =
+ expected_avg_triplets.green - candidate_avg_triplets.green;
+ double blue_error = expected_avg_triplets.blue - candidate_avg_triplets.blue;
+ error_sum_squares = red_error * red_error + green_error * green_error +
+ blue_error * blue_error;
+ error_sum_squares /= (255.0 * 255.0);
+
+ return sqrt(error_sum_squares / 3) < error_limit;
+}
+
TestVideoRenderer::TestVideoRenderer()
: video_decode_thread_(
new base::Thread("TestVideoRendererVideoDecodingThread")),
@@ -267,23 +368,25 @@ void TestVideoRenderer::SetCodecForDecoding(
codec));
}
-scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetBufferForTest() const {
+scoped_ptr<webrtc::DesktopFrame> TestVideoRenderer::GetFrameForTest() const {
DCHECK(thread_checker_.CalledOnValidThread());
- return core_->GetBufferForTest();
+ return core_->GetFrameForTest();
}
void TestVideoRenderer::SetImagePatternAndMatchedCallback(
const webrtc::DesktopRect& expected_rect,
- const RgbaColor& expected_color,
+ const RGBA32& expected_avg_color,
joedow 2015/07/10 23:39:16 remove const?
liaoyuke 2015/07/13 16:05:38 Done.
const base::Closure& image_pattern_matched_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!expected_rect.is_empty()) << "Expected rect cannot be empty";
DVLOG(2) << "TestVideoRenderer::SetImagePatternAndMatchedCallback() Called";
video_decode_task_runner_->PostTask(
- FROM_HERE, base::Bind(&Core::SetImagePatternAndMatchedCallback,
- base::Unretained(core_.get()), expected_rect,
- expected_color, image_pattern_matched_callback));
+ FROM_HERE,
+ base::Bind(&Core::SetImagePatternAndMatchedCallback,
+ base::Unretained(core_.get()), expected_rect,
+ expected_avg_color, image_pattern_matched_callback));
}
} // namespace test

Powered by Google App Engine
This is Rietveld 408576698