[Chromoting] Add unit tests for up- and down-scaling.


Review URL: https://chromiumcodereview.appspot.com/10828058

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148888 0039d316-1c4b-4281-b951-d872f2087c98
parent aacecdcf
...@@ -16,18 +16,31 @@ ...@@ -16,18 +16,31 @@
#include "remoting/base/util.h" #include "remoting/base/util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
static const int kWidth = 320; namespace {
static const int kHeight = 240;
static const int kBytesPerPixel = 4; const int kBytesPerPixel = 4;
// Some sample rects for testing. // Some sample rects for testing.
static const SkIRect kTestRects[] = { std::vector<std::vector<SkIRect> > MakeTestRectLists(const SkISize& size) {
SkIRect::MakeXYWH(0, 0, kWidth, kHeight), std::vector<std::vector<SkIRect> > rect_lists;
SkIRect::MakeXYWH(0, 0, kWidth / 2, kHeight / 2), std::vector<SkIRect> rects;
SkIRect::MakeXYWH(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2), rects.push_back(SkIRect::MakeXYWH(0, 0, size.width(), size.height()));
SkIRect::MakeXYWH(16, 16, 16, 16), rect_lists.push_back(rects);
SkIRect::MakeXYWH(128, 64, 32, 32), rects.clear();
}; rects.push_back(SkIRect::MakeXYWH(0, 0, size.width() / 2, size.height() / 2));
rect_lists.push_back(rects);
rects.clear();
rects.push_back(SkIRect::MakeXYWH(size.width() / 2, size.height() / 2,
size.width() / 2, size.height() / 2));
rect_lists.push_back(rects);
rects.clear();
rects.push_back(SkIRect::MakeXYWH(16, 16, 16, 16));
rects.push_back(SkIRect::MakeXYWH(128, 64, 32, 32));
rect_lists.push_back(rects);
return rect_lists;
}
} // namespace
namespace remoting { namespace remoting {
...@@ -120,12 +133,16 @@ class EncoderMessageTester { ...@@ -120,12 +133,16 @@ class EncoderMessageTester {
class DecoderTester { class DecoderTester {
public: public:
DecoderTester(Decoder* decoder) DecoderTester(Decoder* decoder, const SkISize& screen_size,
: strict_(false), const SkISize& view_size)
: screen_size_(screen_size),
view_size_(view_size),
strict_(false),
decoder_(decoder) { decoder_(decoder) {
image_data_.reset(new uint8[kWidth * kHeight * kBytesPerPixel]); image_data_.reset(new uint8[
view_size_.width() * view_size_.height() * kBytesPerPixel]);
EXPECT_TRUE(image_data_.get()); EXPECT_TRUE(image_data_.get());
decoder_->Initialize(SkISize::Make(kWidth, kHeight)); decoder_->Initialize(screen_size_);
} }
void Reset() { void Reset() {
...@@ -133,19 +150,28 @@ class DecoderTester { ...@@ -133,19 +150,28 @@ class DecoderTester {
update_region_.setEmpty(); update_region_.setEmpty();
} }
void ResetRenderedData() {
memset(image_data_.get(), 0,
view_size_.width() * view_size_.height() * kBytesPerPixel);
}
void ReceivedPacket(VideoPacket* packet) { void ReceivedPacket(VideoPacket* packet) {
Decoder::DecodeResult result = decoder_->DecodePacket(packet); Decoder::DecodeResult result = decoder_->DecodePacket(packet);
ASSERT_NE(Decoder::DECODE_ERROR, result); ASSERT_NE(Decoder::DECODE_ERROR, result);
if (result == Decoder::DECODE_DONE) { if (result == Decoder::DECODE_DONE) {
decoder_->RenderFrame(SkISize::Make(kWidth, kHeight), RenderFrame();
SkIRect::MakeXYWH(0, 0, kWidth, kHeight), }
}
void RenderFrame() {
decoder_->RenderFrame(view_size_,
SkIRect::MakeSize(view_size_),
image_data_.get(), image_data_.get(),
kWidth * kBytesPerPixel, view_size_.width() * kBytesPerPixel,
&update_region_); &update_region_);
} }
}
void ReceivedScopedPacket(scoped_ptr<VideoPacket> packet) { void ReceivedScopedPacket(scoped_ptr<VideoPacket> packet) {
ReceivedPacket(packet.get()); ReceivedPacket(packet.get());
...@@ -178,7 +204,7 @@ class DecoderTester { ...@@ -178,7 +204,7 @@ class DecoderTester {
// Test the content of the update region. // Test the content of the update region.
EXPECT_EQ(expected_region_, update_region_); EXPECT_EQ(expected_region_, update_region_);
for (SkRegion::Iterator i(update_region_); !i.done(); i.next()) { for (SkRegion::Iterator i(update_region_); !i.done(); i.next()) {
const int stride = kWidth * kBytesPerPixel; const int stride = view_size_.width() * kBytesPerPixel;
EXPECT_EQ(stride, capture_data_->data_planes().strides[0]); EXPECT_EQ(stride, capture_data_->data_planes().strides[0]);
const int offset = stride * i.rect().top() + const int offset = stride * i.rect().top() +
kBytesPerPixel * i.rect().left(); kBytesPerPixel * i.rect().left();
...@@ -197,29 +223,25 @@ class DecoderTester { ...@@ -197,29 +223,25 @@ class DecoderTester {
// The error at each pixel is the root mean square of the errors in // The error at each pixel is the root mean square of the errors in
// the R, G, and B components, each normalized to [0, 1]. This routine // the R, G, and B components, each normalized to [0, 1]. This routine
// checks that the maximum and mean pixel errors do not exceed given limits. // checks that the maximum and mean pixel errors do not exceed given limits.
void VerifyResultsApprox(double max_error_limit, double mean_error_limit) { void VerifyResultsApprox(const uint8* expected_view_data,
ASSERT_TRUE(capture_data_.get()); double max_error_limit, double mean_error_limit) {
// Test the content of the update region.
EXPECT_EQ(expected_region_, update_region_);
double max_error = 0.0; double max_error = 0.0;
double sum_error = 0.0; double sum_error = 0.0;
int error_num = 0; int error_num = 0;
for (SkRegion::Iterator i(update_region_); !i.done(); i.next()) { for (SkRegion::Iterator i(update_region_); !i.done(); i.next()) {
const int stride = kWidth * kBytesPerPixel; const int stride = view_size_.width() * kBytesPerPixel;
EXPECT_EQ(stride, capture_data_->data_planes().strides[0]);
const int offset = stride * i.rect().top() + const int offset = stride * i.rect().top() +
kBytesPerPixel * i.rect().left(); kBytesPerPixel * i.rect().left();
const uint8* original = capture_data_->data_planes().data[0] + offset; const uint8* expected = expected_view_data + offset;
const uint8* decoded = image_data_.get() + offset; const uint8* actual = image_data_.get() + offset;
for (int y = 0; y < i.rect().height(); ++y) { for (int y = 0; y < i.rect().height(); ++y) {
for (int x = 0; x < i.rect().width(); ++x) { for (int x = 0; x < i.rect().width(); ++x) {
double error = CalculateError(original, decoded); double error = CalculateError(expected, actual);
max_error = std::max(max_error, error); max_error = std::max(max_error, error);
sum_error += error; sum_error += error;
++error_num; ++error_num;
original += 4; expected += 4;
decoded += 4; actual += 4;
} }
} }
} }
...@@ -244,6 +266,8 @@ class DecoderTester { ...@@ -244,6 +266,8 @@ class DecoderTester {
} }
private: private:
SkISize screen_size_;
SkISize view_size_;
bool strict_; bool strict_;
SkRegion expected_region_; SkRegion expected_region_;
SkRegion update_region_; SkRegion update_region_;
...@@ -294,15 +318,16 @@ class EncoderTester { ...@@ -294,15 +318,16 @@ class EncoderTester {
DISALLOW_COPY_AND_ASSIGN(EncoderTester); DISALLOW_COPY_AND_ASSIGN(EncoderTester);
}; };
scoped_refptr<CaptureData> PrepareEncodeData(media::VideoFrame::Format format, scoped_refptr<CaptureData> PrepareEncodeData(const SkISize& size,
media::VideoFrame::Format format,
uint8** memory) { uint8** memory) {
// TODO(hclam): Support also YUV format. // TODO(hclam): Support also YUV format.
CHECK_EQ(format, media::VideoFrame::RGB32); CHECK_EQ(format, media::VideoFrame::RGB32);
int size = kWidth * kHeight * kBytesPerPixel; int memory_size = size.width() * size.height() * kBytesPerPixel;
*memory = new uint8[size]; *memory = new uint8[memory_size];
srand(0); srand(0);
for (int i = 0; i < size; ++i) { for (int i = 0; i < memory_size; ++i) {
(*memory)[i] = rand() % 256; (*memory)[i] = rand() % 256;
} }
...@@ -310,10 +335,10 @@ scoped_refptr<CaptureData> PrepareEncodeData(media::VideoFrame::Format format, ...@@ -310,10 +335,10 @@ scoped_refptr<CaptureData> PrepareEncodeData(media::VideoFrame::Format format,
memset(planes.data, 0, sizeof(planes.data)); memset(planes.data, 0, sizeof(planes.data));
memset(planes.strides, 0, sizeof(planes.strides)); memset(planes.strides, 0, sizeof(planes.strides));
planes.data[0] = *memory; planes.data[0] = *memory;
planes.strides[0] = kWidth * kBytesPerPixel; planes.strides[0] = size.width() * kBytesPerPixel;
scoped_refptr<CaptureData> data = scoped_refptr<CaptureData> data =
new CaptureData(planes, SkISize::Make(kWidth, kHeight), format); new CaptureData(planes, size, format);
return data; return data;
} }
...@@ -332,6 +357,8 @@ static void TestEncodingRects(Encoder* encoder, ...@@ -332,6 +357,8 @@ static void TestEncodingRects(Encoder* encoder,
} }
void TestEncoder(Encoder* encoder, bool strict) { void TestEncoder(Encoder* encoder, bool strict) {
SkISize kSize = SkISize::Make(320, 240);
EncoderMessageTester message_tester; EncoderMessageTester message_tester;
message_tester.set_strict(strict); message_tester.set_strict(strict);
...@@ -339,13 +366,15 @@ void TestEncoder(Encoder* encoder, bool strict) { ...@@ -339,13 +366,15 @@ void TestEncoder(Encoder* encoder, bool strict) {
uint8* memory; uint8* memory;
scoped_refptr<CaptureData> data = scoped_refptr<CaptureData> data =
PrepareEncodeData(media::VideoFrame::RGB32, &memory); PrepareEncodeData(kSize, media::VideoFrame::RGB32, &memory);
scoped_array<uint8> memory_wrapper(memory); scoped_array<uint8> memory_wrapper(memory);
TestEncodingRects(encoder, &tester, data, kTestRects, 1); std::vector<std::vector<SkIRect> > test_rect_lists = MakeTestRectLists(kSize);
TestEncodingRects(encoder, &tester, data, kTestRects + 1, 1); for (size_t i = 0; i < test_rect_lists.size(); ++i) {
TestEncodingRects(encoder, &tester, data, kTestRects + 2, 1); const std::vector<SkIRect>& test_rects = test_rect_lists[i];
TestEncodingRects(encoder, &tester, data, kTestRects + 3, 2); TestEncodingRects(encoder, &tester, data,
&test_rects[0], test_rects.size());
}
} }
static void TestEncodeDecodeRects(Encoder* encoder, static void TestEncodeDecodeRects(Encoder* encoder,
...@@ -380,6 +409,8 @@ static void TestEncodeDecodeRects(Encoder* encoder, ...@@ -380,6 +409,8 @@ static void TestEncodeDecodeRects(Encoder* encoder,
} }
void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict) { void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict) {
SkISize kSize = SkISize::Make(320, 240);
EncoderMessageTester message_tester; EncoderMessageTester message_tester;
message_tester.set_strict(strict); message_tester.set_strict(strict);
...@@ -387,22 +418,20 @@ void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict) { ...@@ -387,22 +418,20 @@ void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict) {
uint8* memory; uint8* memory;
scoped_refptr<CaptureData> data = scoped_refptr<CaptureData> data =
PrepareEncodeData(media::VideoFrame::RGB32, &memory); PrepareEncodeData(kSize, media::VideoFrame::RGB32, &memory);
scoped_array<uint8> memory_wrapper(memory); scoped_array<uint8> memory_wrapper(memory);
DecoderTester decoder_tester(decoder); DecoderTester decoder_tester(decoder, kSize, kSize);
decoder_tester.set_strict(strict); decoder_tester.set_strict(strict);
decoder_tester.set_capture_data(data); decoder_tester.set_capture_data(data);
encoder_tester.set_decoder_tester(&decoder_tester); encoder_tester.set_decoder_tester(&decoder_tester);
std::vector<std::vector<SkIRect> > test_rect_lists = MakeTestRectLists(kSize);
for (size_t i = 0; i < test_rect_lists.size(); ++i) {
const std::vector<SkIRect> test_rects = test_rect_lists[i];
TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester, data, TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester, data,
kTestRects, 1); &test_rects[0], test_rects.size());
TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester, data, }
kTestRects + 1, 1);
TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester, data,
kTestRects + 2, 1);
TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester, data,
kTestRects + 3, 2);
} }
static void FillWithGradient(uint8* memory, const SkISize& frame_size, static void FillWithGradient(uint8* memory, const SkISize& frame_size,
...@@ -421,25 +450,31 @@ static void FillWithGradient(uint8* memory, const SkISize& frame_size, ...@@ -421,25 +450,31 @@ static void FillWithGradient(uint8* memory, const SkISize& frame_size,
void TestEncoderDecoderGradient(Encoder* encoder, void TestEncoderDecoderGradient(Encoder* encoder,
Decoder* decoder, Decoder* decoder,
const SkISize& screen_size,
const SkISize& view_size,
double max_error_limit, double max_error_limit,
double mean_error_limit) { double mean_error_limit) {
SkIRect full_frame = SkIRect::MakeWH(kWidth, kHeight); SkIRect screen_rect = SkIRect::MakeSize(screen_size);
scoped_array<uint8> frame_data(new uint8[kWidth * kHeight * kBytesPerPixel]); scoped_array<uint8> screen_data(new uint8[
FillWithGradient(frame_data.get(), SkISize::Make(kWidth, kHeight), screen_size.width() * screen_size.height() * kBytesPerPixel]);
full_frame); FillWithGradient(screen_data.get(), screen_size, screen_rect);
SkIRect view_rect = SkIRect::MakeSize(view_size);
scoped_array<uint8> expected_view_data(new uint8[
view_size.width() * view_size.height() * kBytesPerPixel]);
FillWithGradient(expected_view_data.get(), view_size, view_rect);
DataPlanes planes; DataPlanes planes;
memset(planes.data, 0, sizeof(planes.data)); memset(planes.data, 0, sizeof(planes.data));
memset(planes.strides, 0, sizeof(planes.strides)); memset(planes.strides, 0, sizeof(planes.strides));
planes.data[0] = frame_data.get(); planes.data[0] = screen_data.get();
planes.strides[0] = kWidth * kBytesPerPixel; planes.strides[0] = screen_size.width() * kBytesPerPixel;
scoped_refptr<CaptureData> capture_data = scoped_refptr<CaptureData> capture_data =
new CaptureData(planes, SkISize::Make(kWidth, kHeight), new CaptureData(planes, screen_size, media::VideoFrame::RGB32);
media::VideoFrame::RGB32); capture_data->mutable_dirty_region().op(screen_rect, SkRegion::kUnion_Op);
capture_data->mutable_dirty_region().op(full_frame, SkRegion::kUnion_Op);
DecoderTester decoder_tester(decoder); DecoderTester decoder_tester(decoder, screen_size, view_size);
decoder_tester.set_capture_data(capture_data); decoder_tester.set_capture_data(capture_data);
decoder_tester.AddRegion(capture_data->dirty_region()); decoder_tester.AddRegion(capture_data->dirty_region());
...@@ -447,7 +482,16 @@ void TestEncoderDecoderGradient(Encoder* encoder, ...@@ -447,7 +482,16 @@ void TestEncoderDecoderGradient(Encoder* encoder,
base::Bind(&DecoderTester::ReceivedScopedPacket, base::Bind(&DecoderTester::ReceivedScopedPacket,
base::Unretained(&decoder_tester))); base::Unretained(&decoder_tester)));
decoder_tester.VerifyResultsApprox(max_error_limit, mean_error_limit); decoder_tester.VerifyResultsApprox(expected_view_data.get(),
max_error_limit, mean_error_limit);
// Check that the decoder correctly re-renders the frame if its client
// invalidates the frame.
decoder_tester.ResetRenderedData();
decoder->Invalidate(view_size, SkRegion(view_rect));
decoder_tester.RenderFrame();
decoder_tester.VerifyResultsApprox(expected_view_data.get(),
max_error_limit, mean_error_limit);
} }
} // namespace remoting } // namespace remoting
...@@ -14,11 +14,6 @@ namespace remoting { ...@@ -14,11 +14,6 @@ namespace remoting {
class Decoder; class Decoder;
class Encoder; class Encoder;
// Prepare testing data for encoding. Memory created is written to |memory|.
// Returns randomly generated data in CaptureData.
scoped_refptr<CaptureData> PrepareEncodeData(media::VideoFrame::Format format,
uint8** memory);
// Generate test data and test the encoder for a regular encoding sequence. // Generate test data and test the encoder for a regular encoding sequence.
// This will test encoder test and the sequence of messages sent. // This will test encoder test and the sequence of messages sent.
// //
...@@ -35,6 +30,8 @@ void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict); ...@@ -35,6 +30,8 @@ void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict);
// Generate a frame containing a gradient, and test the encoder and decoder // Generate a frame containing a gradient, and test the encoder and decoder
// pair. // pair.
void TestEncoderDecoderGradient(Encoder* encoder, Decoder* decoder, void TestEncoderDecoderGradient(Encoder* encoder, Decoder* decoder,
const SkISize& screen_size,
const SkISize& view_size,
double max_error_limit, double max_error_limit,
double mean_error_limit); double mean_error_limit);
......
...@@ -10,19 +10,65 @@ ...@@ -10,19 +10,65 @@
namespace remoting { namespace remoting {
TEST(DecoderVp8Test, EncodeAndDecode) { class DecoderVp8Test : public testing::Test {
EncoderVp8 encoder; protected:
DecoderVp8 decoder; EncoderVp8 encoder_;
TestEncoderDecoder(&encoder, &decoder, false); DecoderVp8 decoder_;
void TestGradient(int screen_width, int screen_height,
int view_width, int view_height,
double max_error_limit, double mean_error_limit) {
TestEncoderDecoderGradient(&encoder_, &decoder_,
SkISize::Make(screen_width, screen_height),
SkISize::Make(view_width, view_height),
max_error_limit, mean_error_limit);
}
};
TEST_F(DecoderVp8Test, EncodeAndDecode) {
TestEncoderDecoder(&encoder_, &decoder_, false);
} }
// Check that encoding and decoding a particular frame doesn't change the // Check that encoding and decoding a particular frame doesn't change the
// frame too much. The frame used is a gradient, which does not contain sharp // frame too much. The frame used is a gradient, which does not contain sharp
// transitions, so encoding lossiness should not be too high. // transitions, so encoding lossiness should not be too high.
TEST(DecoderVp8Test, Gradient) { TEST_F(DecoderVp8Test, Gradient) {
EncoderVp8 encoder; TestGradient(320, 240, 320, 240, 0.03, 0.01);
DecoderVp8 decoder; }
TestEncoderDecoderGradient(&encoder, &decoder, 0.03, 0.01);
TEST_F(DecoderVp8Test, GradientScaleUpEvenToEven) {
TestGradient(320, 240, 640, 480, 0.04, 0.02);
}
TEST_F(DecoderVp8Test, GradientScaleUpEvenToOdd) {
TestGradient(320, 240, 641, 481, 0.04, 0.02);
}
TEST_F(DecoderVp8Test, GradientScaleUpOddToEven) {
TestGradient(321, 241, 640, 480, 0.04, 0.02);
}
TEST_F(DecoderVp8Test, GradientScaleUpOddToOdd) {
TestGradient(321, 241, 641, 481, 0.04, 0.02);
}
TEST_F(DecoderVp8Test, GradientScaleDownEvenToEven) {
TestGradient(320, 240, 160, 120, 0.04, 0.02);
}
TEST_F(DecoderVp8Test, GradientScaleDownEvenToOdd) {
// TODO(simonmorris): The maximum error is non-deterministic.
// The mean error is not too high, which suggests that the problem is
// restricted to a small area of the output image. See crbug.com/139437.
TestGradient(320, 240, 161, 121, 1.0, 0.02);
}
TEST_F(DecoderVp8Test, GradientScaleDownOddToEven) {
TestGradient(321, 241, 160, 120, 0.04, 0.02);
}
TEST_F(DecoderVp8Test, GradientScaleDownOddToOdd) {
TestGradient(321, 241, 161, 121, 0.04, 0.02);
} }
} // namespace remoting } // namespace remoting
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment