summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--remoting/base/codec_test.cc390
-rw-r--r--remoting/base/codec_test.h37
-rw-r--r--remoting/base/decoder_verbatim.h2
-rw-r--r--remoting/base/decoder_verbatim_unittest.cc12
-rw-r--r--remoting/base/encoder_verbatim.cc10
-rw-r--r--remoting/base/encoder_verbatim_unittest.cc16
-rw-r--r--remoting/remoting.gyp3
7 files changed, 464 insertions, 6 deletions
diff --git a/remoting/base/codec_test.cc b/remoting/base/codec_test.cc
new file mode 100644
index 0000000..b97293f
--- /dev/null
+++ b/remoting/base/codec_test.cc
@@ -0,0 +1,390 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <deque>
+#include <stdlib.h>
+
+#include "gfx/rect.h"
+#include "media/base/video_frame.h"
+#include "remoting/base/codec_test.h"
+#include "remoting/base/encoder.h"
+#include "remoting/base/mock_objects.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+static const int kWidth = 320;
+static const int kHeight = 240;
+static const int kBytesPerPixel = 4;
+
+// Some sample rects for testing.
+static const gfx::Rect kTestRects[] = {
+ gfx::Rect(0, 0, kWidth, kHeight),
+ gfx::Rect(0, 0, kWidth / 2, kHeight / 2),
+ gfx::Rect(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
+ gfx::Rect(16, 16, 16, 16),
+ gfx::Rect(128, 64, 32, 32)
+};
+
+namespace remoting {
+
+// A class to test that the state transition of the Encoder is correct.
+class EncoderStateTester {
+ public:
+ EncoderStateTester()
+ : next_state_(Encoder::EncodingStarting) {
+ }
+
+ ~EncoderStateTester() {
+ EXPECT_EQ(Encoder::EncodingStarting, next_state_);
+ }
+
+ // Set the state output of the Encoder.
+ void ReceivedState(Encoder::EncodingState state) {
+ if (state & Encoder::EncodingStarting) {
+ EXPECT_EQ(Encoder::EncodingStarting, next_state_);
+ next_state_ = Encoder::EncodingInProgress | Encoder::EncodingEnded;
+ } else {
+ EXPECT_FALSE(next_state_ & Encoder::EncodingStarting);
+ }
+
+ if (state & Encoder::EncodingInProgress) {
+ EXPECT_TRUE(next_state_ & Encoder::EncodingInProgress);
+ }
+
+ if (state & Encoder::EncodingEnded) {
+ EXPECT_TRUE(next_state_ & Encoder::EncodingEnded);
+ next_state_ = Encoder::EncodingStarting;
+ }
+ }
+
+ private:
+ Encoder::EncodingState next_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(EncoderStateTester);
+};
+
+// A class to test the message output of the encoder.
+class EncoderMessageTester {
+ public:
+ EncoderMessageTester()
+ : begin_rect_(0),
+ rect_data_(0),
+ end_rect_(0),
+ state_(kWaitingForBeginRect),
+ strict_(false) {
+ }
+
+ ~EncoderMessageTester() {
+ EXPECT_EQ(begin_rect_, end_rect_);
+ EXPECT_EQ(kWaitingForBeginRect, state_);
+ }
+
+ // Test that we received the correct message.
+ void ReceivedMessage(HostMessage* message) {
+ EXPECT_TRUE(message->has_update_stream_packet());
+
+ if (state_ == kWaitingForBeginRect) {
+ EXPECT_TRUE(message->update_stream_packet().has_begin_rect());
+ state_ = kWaitingForRectData;
+ ++begin_rect_;
+
+ if (strict_) {
+ gfx::Rect rect = rects_.front();
+ rects_.pop_front();
+ EXPECT_EQ(rect.x(), message->update_stream_packet().begin_rect().x());
+ EXPECT_EQ(rect.y(), message->update_stream_packet().begin_rect().y());
+ EXPECT_EQ(rect.width(),
+ message->update_stream_packet().begin_rect().width());
+ EXPECT_EQ(rect.height(),
+ message->update_stream_packet().begin_rect().height());
+ }
+ } else {
+ EXPECT_FALSE(message->update_stream_packet().has_begin_rect());
+ }
+
+ if (state_ == kWaitingForRectData) {
+ if (message->update_stream_packet().has_rect_data()) {
+ ++rect_data_;
+ }
+
+ if (message->update_stream_packet().has_end_rect()) {
+ // Expect that we have received some data.
+ EXPECT_GT(rect_data_, 0);
+ rect_data_ = 0;
+ state_ = kWaitingForBeginRect;
+ ++end_rect_;
+ }
+ }
+ }
+
+ void set_strict(bool strict) {
+ strict_ = strict;
+ }
+
+ void AddRects(const gfx::Rect* rects, int count) {
+ rects_.insert(rects_.begin() + rects_.size(), rects, rects + count);
+ }
+
+ private:
+ enum State {
+ kWaitingForBeginRect,
+ kWaitingForRectData,
+ };
+
+ int begin_rect_;
+ int rect_data_;
+ int end_rect_;
+ State state_;
+ bool strict_;
+
+ std::deque<gfx::Rect> rects_;
+
+ DISALLOW_COPY_AND_ASSIGN(EncoderMessageTester);
+};
+
+class DecoderTester {
+ public:
+ DecoderTester(Decoder* decoder)
+ : strict_(false),
+ decoder_(decoder),
+ decode_done_(false) {
+ media::VideoFrame::CreateFrame(media::VideoFrame::RGB32,
+ kWidth, kHeight,
+ base::TimeDelta(),
+ base::TimeDelta(), &frame_);
+ EXPECT_TRUE(frame_.get());
+ }
+
+ void ReceivedMessage(HostMessage* message) {
+ if (message->has_update_stream_packet()) {
+ decoder_->PartialDecode(message);
+ return;
+ }
+
+ if (message->has_begin_update_stream()) {
+ decoder_->BeginDecode(
+ frame_, &update_rects_,
+ NewRunnableMethod(this, &DecoderTester::OnPartialDecodeDone),
+ NewRunnableMethod(this, &DecoderTester::OnDecodeDone));
+ }
+
+ if (message->has_end_update_stream()) {
+ decoder_->EndDecode();
+ }
+ delete message;
+ return;
+ }
+
+ void set_strict(bool strict) {
+ strict_ = strict;
+ }
+
+ void set_capture_data(scoped_refptr<CaptureData> data) {
+ capture_data_ = data;
+ }
+
+ void AddRects(const gfx::Rect* rects, int count) {
+ rects_.insert(rects_.begin() + rects_.size(), rects, rects + count);
+ }
+
+ bool decode_done() const { return decode_done_; }
+ void reset_decode_done() { decode_done_ = false; }
+
+ private:
+ void OnPartialDecodeDone() {
+ if (!strict_)
+ return;
+ for (size_t i = 0; i < update_rects_.size(); ++i) {
+ EXPECT_FALSE(rects_.empty());
+ gfx::Rect rect = rects_.front();
+ rects_.pop_front();
+ EXPECT_EQ(rect, update_rects_[i]);
+ }
+ }
+
+ void OnDecodeDone() {
+ decode_done_ = true;
+ if (!strict_)
+ return;
+
+ EXPECT_TRUE(capture_data_.get());
+ for (int i = 0; i < DataPlanes::kPlaneCount; ++i) {
+ if (!frame_->data(i) || !capture_data_->data_planes().data[i])
+ continue;
+ // TODO(hclam): HAndle YUV.
+ int size = capture_data_->data_planes().strides[i] * kHeight;
+ EXPECT_EQ(0, memcmp(capture_data_->data_planes().data[i],
+ frame_->data(i), size));
+ }
+ }
+
+ bool strict_;
+ std::deque<gfx::Rect> rects_;
+ UpdatedRects update_rects_;
+ Decoder* decoder_;
+ scoped_refptr<media::VideoFrame> frame_;
+ scoped_refptr<CaptureData> capture_data_;
+ bool decode_done_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecoderTester);
+};
+
+class EncoderTester {
+ public:
+ EncoderTester(EncoderMessageTester* message_tester,
+ EncoderStateTester* state_tester)
+ : message_tester_(message_tester),
+ state_tester_(state_tester),
+ decoder_tester_(NULL),
+ data_available_(0) {
+ }
+
+ ~EncoderTester() {
+ EXPECT_GT(data_available_, 0);
+ }
+
+ void DataAvailable(HostMessage* message,
+ Encoder::EncodingState state) {
+ ++data_available_;
+ message_tester_->ReceivedMessage(message);
+ state_tester_->ReceivedState(state);
+
+ // Send the message to the DecoderTester.
+ if (decoder_tester_) {
+ if (state & Encoder::EncodingStarting) {
+ HostMessage* begin_update = new HostMessage();
+ begin_update->mutable_begin_update_stream();
+ decoder_tester_->ReceivedMessage(begin_update);
+ }
+
+ if (state & Encoder::EncodingInProgress) {
+ decoder_tester_->ReceivedMessage(message);
+ }
+
+ if (state & Encoder::EncodingEnded) {
+ HostMessage* end_update = new HostMessage();
+ end_update->mutable_end_update_stream();
+ decoder_tester_->ReceivedMessage(end_update);
+ }
+ } else {
+ delete message;
+ }
+ }
+
+ void AddRects(const gfx::Rect* rects, int count) {
+ message_tester_->AddRects(rects, count);
+ }
+
+ void set_decoder_tester(DecoderTester* decoder_tester) {
+ decoder_tester_ = decoder_tester;
+ }
+
+ private:
+ EncoderMessageTester* message_tester_;
+ EncoderStateTester* state_tester_;
+ DecoderTester* decoder_tester_;
+ int data_available_;
+
+ DISALLOW_COPY_AND_ASSIGN(EncoderTester);
+};
+
+scoped_refptr<CaptureData> PrepareEncodeData(PixelFormat format,
+ uint8** memory) {
+ // TODO(hclam): Support also YUV format.
+ CHECK(format == PixelFormatRgb32);
+ int size = kWidth * kHeight * kBytesPerPixel;
+
+ *memory = new uint8[size];
+ srand(0);
+ for (int i = 0; i < size; ++i) {
+ (*memory)[i] = rand() % 256;
+ }
+
+ DataPlanes planes;
+ memset(planes.data, 0, sizeof(planes.data));
+ memset(planes.strides, 0, sizeof(planes.strides));
+ planes.data[0] = *memory;
+ planes.strides[0] = kWidth * kBytesPerPixel;
+
+ scoped_refptr<CaptureData> data =
+ new CaptureData(planes, kWidth, kHeight, format);
+ return data;
+}
+
+static void TestEncodingRects(Encoder* encoder,
+ EncoderTester* tester,
+ scoped_refptr<CaptureData> data,
+ const gfx::Rect* rects, int count) {
+ data->mutable_dirty_rects().clear();
+ data->mutable_dirty_rects().insert(
+ data->mutable_dirty_rects().begin(), rects, rects + count);
+ tester->AddRects(rects, count);
+
+ encoder->Encode(data, true,
+ NewCallback(tester, &EncoderTester::DataAvailable));
+}
+
+void TestEncoder(Encoder* encoder, bool strict) {
+ EncoderMessageTester message_tester;
+ message_tester.set_strict(strict);
+
+ EncoderStateTester state_tester;
+ EncoderTester tester(&message_tester, &state_tester);
+
+ uint8* memory;
+ scoped_refptr<CaptureData> data =
+ PrepareEncodeData(PixelFormatRgb32, &memory);
+
+ TestEncodingRects(encoder, &tester, data, kTestRects, 1);
+ TestEncodingRects(encoder, &tester, data, kTestRects + 1, 1);
+ TestEncodingRects(encoder, &tester, data, kTestRects + 2, 1);
+ TestEncodingRects(encoder, &tester, data, kTestRects + 3, 2);
+ delete memory;
+}
+
+static void TestEncodingRects(Encoder* encoder,
+ EncoderTester* encoder_tester,
+ DecoderTester* decoder_tester,
+ scoped_refptr<CaptureData> data,
+ const gfx::Rect* rects, int count) {
+ data->mutable_dirty_rects().clear();
+ data->mutable_dirty_rects().insert(
+ data->mutable_dirty_rects().begin(), rects, rects + count);
+ encoder_tester->AddRects(rects, count);
+ decoder_tester->AddRects(rects, count);
+ decoder_tester->reset_decode_done();
+
+ encoder->Encode(data, true,
+ NewCallback(encoder_tester, &EncoderTester::DataAvailable));
+ EXPECT_TRUE(decoder_tester->decode_done());
+}
+
+void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict) {
+ EncoderMessageTester message_tester;
+ message_tester.set_strict(strict);
+
+ EncoderStateTester state_tester;
+ EncoderTester encoder_tester(&message_tester, &state_tester);
+
+ uint8* memory;
+ scoped_refptr<CaptureData> data =
+ PrepareEncodeData(PixelFormatRgb32, &memory);
+ DecoderTester decoder_tester(decoder);
+ decoder_tester.set_strict(strict);
+ decoder_tester.set_capture_data(data);
+ encoder_tester.set_decoder_tester(&decoder_tester);
+
+ TestEncodingRects(encoder, &encoder_tester, &decoder_tester, data,
+ kTestRects, 1);
+ TestEncodingRects(encoder, &encoder_tester, &decoder_tester, data,
+ kTestRects + 1, 1);
+ TestEncodingRects(encoder, &encoder_tester, &decoder_tester, data,
+ kTestRects + 2, 1);
+ TestEncodingRects(encoder, &encoder_tester, &decoder_tester, data,
+ kTestRects + 3, 2);
+ delete memory;
+}
+
+} // namespace remoting
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::DecoderTester);
diff --git a/remoting/base/codec_test.h b/remoting/base/codec_test.h
new file mode 100644
index 0000000..d5702dd
--- /dev/null
+++ b/remoting/base/codec_test.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_BASE_CODEC_TEST_H_
+#define REMOTING_BASE_CODEC_TEST_H_
+
+#include "base/ref_counted.h"
+#include "media/base/video_frame.h"
+#include "remoting/base/capture_data.h"
+
+namespace remoting {
+
+class Decoder;
+class Encoder;
+
+// Prepare testing data for encoding. Memory created is written to |memory|.
+// Returns randomly generated data in CaptureData.
+scoped_refptr<CaptureData> PrepareEncodeData(PixelFormat format,
+ uint8** memory);
+
+// Generate test data and test the encoder for a regular encoding sequence.
+// This will test encoder test and the sequence of messages sent.
+//
+// If |strict| is set to true then this routine will make sure the updated
+// rects match dirty rects.
+void TestEncoder(Encoder* encoder, bool strict);
+
+// Generate test data and test the encoder and decoder pair.
+//
+// If |strict| is set to true, this routine will make sure the updated rects
+// are correct.
+void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict);
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_CODEC_TEST_H_
diff --git a/remoting/base/decoder_verbatim.h b/remoting/base/decoder_verbatim.h
index ba97b5e..22543f9 100644
--- a/remoting/base/decoder_verbatim.h
+++ b/remoting/base/decoder_verbatim.h
@@ -21,6 +21,8 @@ class DecoderVerbatim : public Decoder {
virtual bool PartialDecode(HostMessage* message);
virtual void EndDecode();
+ void set_reverse_rows(bool reverse) { reverse_rows_ = reverse; }
+
private:
bool HandleBeginRect(HostMessage* message);
bool HandleRectData(HostMessage* message);
diff --git a/remoting/base/decoder_verbatim_unittest.cc b/remoting/base/decoder_verbatim_unittest.cc
index f32b08c..947750a 100644
--- a/remoting/base/decoder_verbatim_unittest.cc
+++ b/remoting/base/decoder_verbatim_unittest.cc
@@ -3,7 +3,9 @@
// found in the LICENSE file.
#include "media/base/video_frame.h"
+#include "remoting/base/codec_test.h"
#include "remoting/base/decoder_verbatim.h"
+#include "remoting/base/encoder_verbatim.h"
#include "remoting/client/mock_objects.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -59,9 +61,6 @@ TEST(DecoderVerbatimTest, SimpleDecode) {
EXPECT_EQ(kWidth, frame->width());
EXPECT_EQ(kHeight, frame->height());
EXPECT_EQ(media::VideoFrame::ASCII, frame->format());
- // TODO(hclam): Enable this line.
- // EXPECT_EQ(0, memcmp(kData, frame->data(media::VideoFrame::kRGBPlane),
- // sizeof(kData)));
// Check the updated rects.
ASSERT_TRUE(rects.size() == 1);
@@ -69,4 +68,11 @@ TEST(DecoderVerbatimTest, SimpleDecode) {
EXPECT_EQ(kHeight, static_cast<size_t>(rects[0].height()));
}
+TEST(DecoderVerbatimTest, EncodeAndDecode) {
+ EncoderVerbatim encoder;
+ DecoderVerbatim decoder;
+ decoder.set_reverse_rows(false);
+ TestEncoderDecoder(&encoder, &decoder, true);
+}
+
} // namespace remoting
diff --git a/remoting/base/encoder_verbatim.cc b/remoting/base/encoder_verbatim.cc
index f6cc2ca..509daf9 100644
--- a/remoting/base/encoder_verbatim.cc
+++ b/remoting/base/encoder_verbatim.cc
@@ -71,14 +71,18 @@ bool EncoderVerbatim::EncodeRect(
&((*packet->mutable_rect_data()->mutable_data())[0]));
for (int i = 0; i < DataPlanes::kPlaneCount; ++i) {
- const uint8* in = capture_data->data_planes().data[i];
// Skip over planes that don't have data.
- if (!in) continue;
+ if (!capture_data->data_planes().data[i])
+ continue;
+
+ const uint8* in = capture_data->data_planes().data[i] +
+ y * capture_data->data_planes().strides[i] +
+ x * bytes_per_pixel;
// TODO(hclam): Handle YUV since the height would be different.
for (int j = 0; j < height; ++j) {
DCHECK_LE(row_size, capture_data->data_planes().strides[i]);
- memcpy(out, in, row_size);
+ memcpy(out, in, width * bytes_per_pixel);
in += capture_data->data_planes().strides[i];
out += row_size;
}
diff --git a/remoting/base/encoder_verbatim_unittest.cc b/remoting/base/encoder_verbatim_unittest.cc
new file mode 100644
index 0000000..1d4735c
--- /dev/null
+++ b/remoting/base/encoder_verbatim_unittest.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/base/codec_test.h"
+#include "remoting/base/encoder_verbatim.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+TEST(EncoderVerbatimTest, TestEncoder) {
+ EncoderVerbatim encoder;
+ TestEncoder(&encoder, true);
+}
+
+} // namespace remoting
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 3c601d3..c7edcea 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -342,9 +342,12 @@
'../testing/gmock/include',
],
'sources': [
+ 'base/codec_test.cc',
+ 'base/codec_test.h',
'base/compressor_zlib_unittest.cc',
'base/decoder_verbatim_unittest.cc',
'base/decompressor_zlib_unittest.cc',
+ 'base/encoder_verbatim_unittest.cc',
# TODO(hclam): Enable VP8 in the build.
#'base/encoder_vp8_unittest.cc',
'base/mock_objects.h',