summaryrefslogtreecommitdiffstats
path: root/remoting/base
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-23 00:54:47 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-23 00:54:47 +0000
commitbe2da4de5227f4f1cbb8455a58045c5e51076474 (patch)
treed69b7cb3b045d01adb953b007086fbe9c864af40 /remoting/base
parent452390cc7dc08a33a2c9b5729372b830f6966095 (diff)
downloadchromium_src-be2da4de5227f4f1cbb8455a58045c5e51076474.zip
chromium_src-be2da4de5227f4f1cbb8455a58045c5e51076474.tar.gz
chromium_src-be2da4de5227f4f1cbb8455a58045c5e51076474.tar.bz2
Moving Encoder and Decoder to remoting/base
Putting Encder and Decoder together so we can have test that tests both of them. TEST=remoting_unittests Review URL: http://codereview.chromium.org/2840036 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53427 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/base')
-rw-r--r--remoting/base/capture_data.h75
-rw-r--r--remoting/base/decoder.h114
-rw-r--r--remoting/base/decoder_verbatim.cc127
-rw-r--r--remoting/base/decoder_verbatim.h56
-rw-r--r--remoting/base/decoder_verbatim_unittest.cc72
-rw-r--r--remoting/base/encoder.h61
-rw-r--r--remoting/base/encoder_verbatim.cc89
-rw-r--r--remoting/base/encoder_verbatim.h36
-rw-r--r--remoting/base/encoder_vp8.cc133
-rw-r--r--remoting/base/encoder_vp8.h61
-rw-r--r--remoting/base/encoder_vp8_unittest.cc68
-rw-r--r--remoting/base/mock_objects.h16
12 files changed, 908 insertions, 0 deletions
diff --git a/remoting/base/capture_data.h b/remoting/base/capture_data.h
new file mode 100644
index 0000000..2ac8a12
--- /dev/null
+++ b/remoting/base/capture_data.h
@@ -0,0 +1,75 @@
+// 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_CAPTURE_DATA_H_
+#define REMOTING_BASE_CAPTURE_DATA_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "gfx/rect.h"
+#include "remoting/base/protocol/chromotocol.pb.h"
+
+namespace remoting {
+
+typedef std::vector<gfx::Rect> RectVector;
+
+struct DataPlanes {
+ static const int kPlaneCount = 3;
+ uint8* data[kPlaneCount];
+ int strides[kPlaneCount];
+
+ DataPlanes() {
+ for (int i = 0; i < kPlaneCount; ++i) {
+ data[i] = NULL;
+ strides[i] = 0;
+ }
+ }
+};
+
+// Stores the data and information of a capture to pass off to the
+// encoding thread.
+class CaptureData : public base::RefCountedThreadSafe<CaptureData> {
+ public:
+ CaptureData(const DataPlanes &data_planes,
+ int width,
+ int height,
+ PixelFormat format) :
+ data_planes_(data_planes), dirty_rects_(),
+ width_(width), height_(height), pixel_format_(format) { }
+
+ // Get the data_planes data of the last capture.
+ const DataPlanes& data_planes() const { return data_planes_; }
+
+ // Get the list of updated rectangles in the last capture. The result is
+ // written into |rects|.
+ const RectVector& dirty_rects() const { return dirty_rects_; }
+
+ // Get the width of the image captured.
+ int width() const { return width_; }
+
+ // Get the height of the image captured.
+ int height() const { return height_; }
+
+ // Get the pixel format of the image captured.
+ PixelFormat pixel_format() const { return pixel_format_; }
+
+ // Mutating methods.
+ RectVector& mutable_dirty_rects() { return dirty_rects_; }
+
+ private:
+ const DataPlanes data_planes_;
+ RectVector dirty_rects_;
+ int width_;
+ int height_;
+ PixelFormat pixel_format_;
+
+ friend class base::RefCountedThreadSafe<CaptureData>;
+ ~CaptureData() {}
+};
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_CAPTURE_DATA_H_
diff --git a/remoting/base/decoder.h b/remoting/base/decoder.h
new file mode 100644
index 0000000..1df477d
--- /dev/null
+++ b/remoting/base/decoder.h
@@ -0,0 +1,114 @@
+// 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_DECODER_H_
+#define REMOTING_BASE_DECODER_H_
+
+#include <vector>
+
+#include "base/task.h"
+#include "base/scoped_ptr.h"
+#include "gfx/rect.h"
+#include "media/base/video_frame.h"
+#include "remoting/base/protocol/chromotocol.pb.h"
+
+namespace remoting {
+
+// TODO(hclam): Merge this with the one in remoting/host/encoder.h.
+typedef std::vector<gfx::Rect> UpdatedRects;
+
+// Defines the behavior of a decoder for decoding images received from the
+// host.
+//
+// Sequence of actions with a decoder is as follows:
+//
+// 1. BeginDecode(PartialDecodeDone, DecodeDone, VideoFrame)
+// 2. PartialDecode(HostMessage)
+// ...
+// 3. EndDecode()
+//
+// The decoder will reply with:
+// 1. PartialDecodeDone(VideoFrame, UpdatedRects)
+// ...
+// 2. DecodeDone(VideoFrame)
+//
+// The format of VideoFrame is a contract between the object that creates the
+// decoder (most likely the renderer) and the decoder.
+class Decoder {
+ public:
+
+ virtual ~Decoder() {
+ }
+
+ // Tell the decoder to use |frame| as a target to write the decoded image
+ // for the coming update stream.
+ // If decode is partially done and |frame| can be read, |partial_decode_done|
+ // is called and |update_rects| contains the updated regions.
+ // If decode is completed |decode_done| is called.
+ // Return true if the decoder can writes output to |frame| and accept
+ // the codec format.
+ // TODO(hclam): Provide more information when calling this function.
+ virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame,
+ UpdatedRects* updated_rects,
+ Task* partial_decode_done,
+ Task* decode_done) = 0;
+
+ // Give a HostMessage that contains the update stream packet that contains
+ // the encoded data to the decoder.
+ // The decoder will own |message| and is responsible for deleting it.
+ //
+ // If the decoder has written something into |frame|,
+ // |partial_decode_done_| is called with |frame| and updated regions.
+ // Return true if the decoder can accept |message| and decode it.
+ //
+ // HostMessage returned by this method will contain a
+ // UpdateStreamPacketMessage.
+ // This message will contain either:
+ // 1. UpdateStreamBeginRect
+ // 2. UpdateStreamRectData
+ // 3. UpdateStreamEndRect
+ //
+ // See remoting/base/protocol/chromotocol.proto for more information about
+ // these messages.
+ virtual bool PartialDecode(HostMessage* message) = 0;
+
+ // Notify the decoder that we have received the last update stream packet.
+ // If the decoding of the update stream has completed |decode_done_| is
+ // called with |frame|.
+ // If the update stream is not received fully and this method is called the
+ // decoder should also call |decode_done_| as soon as possible.
+ virtual void EndDecode() = 0;
+
+ protected:
+ // Every decoder will have two internal states because there are three
+ // kinds of messages send to PartialDecode().
+ //
+ // Here's a state diagram:
+ //
+ // UpdateStreamBeginRect UpdateStreamRectData
+ // .............. ............
+ // . . . .
+ // . v . .
+ // kWaitingForBeginRect kWaitingForRectData .
+ // ^ . ^ .
+ // . . . .
+ // .............. ............
+ // UpdateStreaEndRect
+ enum State {
+ // In this state the decoder is waiting for UpdateStreamBeginRect.
+ // After receiving UpdateStreaBeginRect, the encoder will transit to
+ // to kWaitingForRectData state.
+ kWaitingForBeginRect,
+
+ // In this state the decoder is waiting for UpdateStreamRectData.
+ // The decode remains in this state if UpdateStreamRectData is received.
+ // The decoder will transit to kWaitingForBeginRect if UpdateStreamEndRect
+ // is received.
+ kWaitingForRectData,
+ };
+};
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_DECODER_H_
diff --git a/remoting/base/decoder_verbatim.cc b/remoting/base/decoder_verbatim.cc
new file mode 100644
index 0000000..e17310d
--- /dev/null
+++ b/remoting/base/decoder_verbatim.cc
@@ -0,0 +1,127 @@
+// 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/decoder_verbatim.h"
+
+#include "remoting/base/protocol_util.h"
+
+namespace remoting {
+
+DecoderVerbatim::DecoderVerbatim()
+ : state_(kWaitingForBeginRect),
+ rect_x_(0),
+ rect_y_(0),
+ rect_width_(0),
+ rect_height_(0),
+ bytes_per_pixel_(0),
+ updated_rects_(NULL),
+ reverse_rows_(true) {
+}
+
+bool DecoderVerbatim::BeginDecode(scoped_refptr<media::VideoFrame> frame,
+ UpdatedRects* updated_rects,
+ Task* partial_decode_done,
+ Task* decode_done) {
+ DCHECK(!partial_decode_done_.get());
+ DCHECK(!decode_done_.get());
+ DCHECK(!updated_rects_);
+ DCHECK_EQ(kWaitingForBeginRect, state_);
+
+ partial_decode_done_.reset(partial_decode_done);
+ decode_done_.reset(decode_done);
+ updated_rects_ = updated_rects;
+
+ // TODO(hclam): Check if we can accept the color format of the video frame
+ // and the codec.
+ frame_ = frame;
+ return true;
+}
+
+bool DecoderVerbatim::PartialDecode(HostMessage* message) {
+ scoped_ptr<HostMessage> msg_deleter(message);
+ DCHECK(message->has_update_stream_packet());
+
+ bool ret = true;
+ if (message->update_stream_packet().has_begin_rect())
+ ret = HandleBeginRect(message);
+ if (ret && message->update_stream_packet().has_rect_data())
+ ret = HandleRectData(message);
+ if (ret && message->update_stream_packet().has_end_rect())
+ ret = HandleEndRect(message);
+ return ret;
+}
+
+void DecoderVerbatim::EndDecode() {
+ DCHECK_EQ(kWaitingForBeginRect, state_);
+ decode_done_->Run();
+
+ partial_decode_done_.reset();
+ decode_done_.reset();
+ frame_ = NULL;
+ updated_rects_ = NULL;
+}
+
+bool DecoderVerbatim::HandleBeginRect(HostMessage* message) {
+ DCHECK_EQ(kWaitingForBeginRect, state_);
+ state_ = kWaitingForRectData;
+
+ rect_width_ = message->update_stream_packet().begin_rect().width();
+ rect_height_ = message->update_stream_packet().begin_rect().height();
+ rect_x_ = message->update_stream_packet().begin_rect().x();
+ rect_y_ = message->update_stream_packet().begin_rect().y();
+
+ PixelFormat pixel_format =
+ message->update_stream_packet().begin_rect().pixel_format();
+
+ if (static_cast<PixelFormat>(frame_->format()) != pixel_format) {
+ NOTREACHED() << "Pixel format of message doesn't match the video frame. "
+ "Expected vs received = "
+ << frame_->format() << " vs " << pixel_format
+ << " Color space conversion required.";
+ return false;
+ }
+
+ bytes_per_pixel_ = GetBytesPerPixel(pixel_format);
+ return true;
+}
+
+bool DecoderVerbatim::HandleRectData(HostMessage* message) {
+ DCHECK_EQ(kWaitingForRectData, state_);
+ DCHECK_EQ(0,
+ message->update_stream_packet().rect_data().sequence_number());
+
+ // Copy the data line by line.
+ const int src_stride = bytes_per_pixel_ * rect_width_;
+ const char* src =
+ message->update_stream_packet().rect_data().data().c_str();
+ int src_stride_dir = src_stride;
+ if (reverse_rows_) {
+ // Copy rows from bottom to top to flip the image vertically.
+ src += (rect_height_ - 1) * src_stride;
+ // Change the direction of the stride to work bottom to top.
+ src_stride_dir *= -1;
+ }
+ const int dest_stride = frame_->stride(media::VideoFrame::kRGBPlane);
+ uint8* dest = frame_->data(media::VideoFrame::kRGBPlane) +
+ dest_stride * rect_y_ + bytes_per_pixel_ * rect_x_;
+ for (int i = 0; i < rect_height_; ++i) {
+ memcpy(dest, src, src_stride);
+ dest += dest_stride;
+ src += src_stride_dir;
+ }
+
+ updated_rects_->clear();
+ updated_rects_->push_back(gfx::Rect(rect_x_, rect_y_,
+ rect_width_, rect_height_));
+ partial_decode_done_->Run();
+ return true;
+}
+
+bool DecoderVerbatim::HandleEndRect(HostMessage* message) {
+ DCHECK_EQ(kWaitingForRectData, state_);
+ state_ = kWaitingForBeginRect;
+ return true;
+}
+
+} // namespace remoting
diff --git a/remoting/base/decoder_verbatim.h b/remoting/base/decoder_verbatim.h
new file mode 100644
index 0000000..ba97b5e
--- /dev/null
+++ b/remoting/base/decoder_verbatim.h
@@ -0,0 +1,56 @@
+// 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_DECODER_VERBATIM_H_
+#define REMOTING_BASE_DECODER_VERBATIM_H_
+
+#include "remoting/base/decoder.h"
+
+namespace remoting {
+
+class DecoderVerbatim : public Decoder {
+ public:
+ DecoderVerbatim();
+
+ // Decoder implementations.
+ virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame,
+ UpdatedRects* update_rects,
+ Task* partial_decode_done,
+ Task* decode_done);
+ virtual bool PartialDecode(HostMessage* message);
+ virtual void EndDecode();
+
+ private:
+ bool HandleBeginRect(HostMessage* message);
+ bool HandleRectData(HostMessage* message);
+ bool HandleEndRect(HostMessage* message);
+
+ // The internal state of the decoder.
+ State state_;
+
+ // Keeps track of the updating rect.
+ int rect_x_;
+ int rect_y_;
+ int rect_width_;
+ int rect_height_;
+ int bytes_per_pixel_;
+
+ // Tasks to call when decode is done.
+ scoped_ptr<Task> partial_decode_done_;
+ scoped_ptr<Task> decode_done_;
+
+ // The video frame to write to.
+ scoped_refptr<media::VideoFrame> frame_;
+ UpdatedRects* updated_rects_;
+
+ // True if we should reverse the rows when copying data into the target
+ // frame buffer.
+ bool reverse_rows_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecoderVerbatim);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_DECODER_VERBATIM_H_
diff --git a/remoting/base/decoder_verbatim_unittest.cc b/remoting/base/decoder_verbatim_unittest.cc
new file mode 100644
index 0000000..f32b08c
--- /dev/null
+++ b/remoting/base/decoder_verbatim_unittest.cc
@@ -0,0 +1,72 @@
+// 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 "media/base/video_frame.h"
+#include "remoting/base/decoder_verbatim.h"
+#include "remoting/client/mock_objects.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::InSequence;
+
+namespace remoting {
+
+TEST(DecoderVerbatimTest, SimpleDecode) {
+ DecoderVerbatim decoder;
+ scoped_refptr<MockDecodeDoneHandler> handler = new MockDecodeDoneHandler();
+
+ const size_t kWidth = 10;
+ const size_t kHeight = 1;
+ const char kData[] = "ABCDEFGHIJ";
+ scoped_ptr<HostMessage> msg(new HostMessage());
+
+ // Prepare the begin rect message.
+ UpdateStreamBeginRect* begin_rect =
+ msg->mutable_update_stream_packet()->mutable_begin_rect();
+ begin_rect->set_width(kWidth);
+ begin_rect->set_height(kHeight);
+ begin_rect->set_x(0);
+ begin_rect->set_y(0);
+ begin_rect->set_pixel_format(PixelFormatAscii);
+
+ // Prepare the rect data.
+ msg->mutable_update_stream_packet()->mutable_rect_data()->set_data(
+ kData, sizeof(kData));
+
+ // Prepare the end rect.
+ msg->mutable_update_stream_packet()->mutable_end_rect();
+
+ scoped_refptr<media::VideoFrame> frame;
+ media::VideoFrame::CreateFrame(media::VideoFrame::ASCII, kWidth, kHeight,
+ base::TimeDelta(), base::TimeDelta(), &frame);
+ ASSERT_TRUE(frame);
+
+ InSequence s;
+ EXPECT_CALL(*handler, PartialDecodeDone());
+ EXPECT_CALL(*handler, DecodeDone());
+
+ UpdatedRects rects;
+ decoder.BeginDecode(
+ frame, &rects,
+ NewRunnableMethod(handler.get(),
+ &MockDecodeDoneHandler::PartialDecodeDone),
+ NewRunnableMethod(handler.get(), &MockDecodeDoneHandler::DecodeDone));
+ decoder.PartialDecode(msg.release());
+ decoder.EndDecode();
+
+ // Make sure we get the same data back.
+ 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);
+ EXPECT_EQ(kWidth, static_cast<size_t>(rects[0].width()));
+ EXPECT_EQ(kHeight, static_cast<size_t>(rects[0].height()));
+}
+
+} // namespace remoting
diff --git a/remoting/base/encoder.h b/remoting/base/encoder.h
new file mode 100644
index 0000000..0c1fd50f
--- /dev/null
+++ b/remoting/base/encoder.h
@@ -0,0 +1,61 @@
+// 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_ENCODER_H_
+#define REMOTING_BASE_ENCODER_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "media/base/data_buffer.h"
+#include "remoting/base/protocol/chromotocol.pb.h"
+
+namespace media {
+ class DataBuffer;
+}
+
+namespace remoting {
+
+class CaptureData;
+class HostMessage;
+
+// A class to perform the task of encoding a continous stream of
+// images.
+// This class operates asynchronously to enable maximum throughput.
+class Encoder {
+ public:
+
+ // EncodingState is a bitfield that tracks the state of the encoding.
+ // An encoding that consists of a single block could concievably be starting
+ // inprogress and ended at the same time.
+ enum {
+ EncodingStarting = 1 << 0,
+ EncodingInProgress = 1 << 1,
+ EncodingEnded = 1 << 2
+ };
+ typedef int EncodingState;
+
+ // DataAvailableCallback is called as blocks of data are made available
+ // from the encoder. Data made available by the encoder is in the form
+ // of HostMessage to reduce the amount of memory copies.
+ // The callback takes ownership of the HostMessage and is responsible for
+ // deleting it.
+ typedef Callback2<HostMessage*, EncodingState>::Type DataAvailableCallback;
+
+ virtual ~Encoder() {}
+
+ // Encode an image stored in |capture_data|.
+ //
+ // If |key_frame| is true, the encoder should not reference
+ // previous encode and encode the full frame.
+ //
+ // When encoded data is available, partial or full |data_available_callback|
+ // is called.
+ virtual void Encode(scoped_refptr<CaptureData> capture_data,
+ bool key_frame,
+ DataAvailableCallback* data_available_callback) = 0;
+};
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_ENCODER_H_
diff --git a/remoting/base/encoder_verbatim.cc b/remoting/base/encoder_verbatim.cc
new file mode 100644
index 0000000..f6cc2ca
--- /dev/null
+++ b/remoting/base/encoder_verbatim.cc
@@ -0,0 +1,89 @@
+// 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/encoder_verbatim.h"
+
+#include "gfx/rect.h"
+#include "media/base/data_buffer.h"
+#include "remoting/base/capture_data.h"
+#include "remoting/base/protocol_util.h"
+#include "remoting/base/protocol/chromotocol.pb.h"
+
+namespace remoting {
+
+using media::DataBuffer;
+
+void EncoderVerbatim::Encode(scoped_refptr<CaptureData> capture_data,
+ bool key_frame,
+ DataAvailableCallback* data_available_callback) {
+ int num_rects = capture_data->dirty_rects().size();
+ for (int i = 0; i < num_rects; i++) {
+ const gfx::Rect& dirty_rect = capture_data->dirty_rects()[i];
+ HostMessage* msg = new HostMessage();
+ UpdateStreamPacketMessage* packet = msg->mutable_update_stream_packet();
+
+ if (EncodeRect(dirty_rect.x(), dirty_rect.y(), dirty_rect.width(),
+ dirty_rect.height(), capture_data, packet)) {
+ // Prepare the end rect content.
+ packet->mutable_end_rect();
+
+ EncodingState state = EncodingInProgress;
+ if (i == 0) {
+ state |= EncodingStarting;
+ }
+ if (i == num_rects - 1) {
+ state |= EncodingEnded;
+ }
+ data_available_callback->Run(msg, state);
+ }
+ }
+
+ delete data_available_callback;
+}
+
+bool EncoderVerbatim::EncodeRect(
+ int x, int y, int width, int height,
+ const scoped_refptr<CaptureData>& capture_data,
+ UpdateStreamPacketMessage* packet) {
+ // Prepare the begin rect content.
+ packet->mutable_begin_rect()->set_x(x);
+ packet->mutable_begin_rect()->set_y(y);
+ packet->mutable_begin_rect()->set_width(width);
+ packet->mutable_begin_rect()->set_height(height);
+ packet->mutable_begin_rect()->set_encoding(EncodingNone);
+ packet->mutable_begin_rect()->set_pixel_format(capture_data->pixel_format());
+
+ // Calculate the size of output.
+ int bytes_per_pixel = GetBytesPerPixel(capture_data->pixel_format());
+ int row_size = bytes_per_pixel * width;
+ int output_size = 0;
+ for (int i = 0; i < DataPlanes::kPlaneCount; ++i) {
+ // TODO(hclam): Handle YUV since the height would be different.
+ const uint8* in = capture_data->data_planes().data[i];
+ if (!in) continue;
+ output_size += row_size * height;
+ }
+
+ // Resize the output data buffer.
+ packet->mutable_rect_data()->mutable_data()->resize(output_size);
+ uint8* out = reinterpret_cast<uint8*>(
+ &((*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;
+
+ // 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);
+ in += capture_data->data_planes().strides[i];
+ out += row_size;
+ }
+ }
+ return true;
+}
+
+} // namespace remoting
diff --git a/remoting/base/encoder_verbatim.h b/remoting/base/encoder_verbatim.h
new file mode 100644
index 0000000..dd019fc
--- /dev/null
+++ b/remoting/base/encoder_verbatim.h
@@ -0,0 +1,36 @@
+// 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_ENCODER_VERBATIM_H_
+#define REMOTING_BASE_ENCODER_VERBATIM_H_
+
+#include "remoting/base/encoder.h"
+
+namespace remoting {
+
+class UpdateStreamPacket;
+
+// EncoderVerbatim implements Encoder and simply copies input to the output
+// buffer verbatim.
+class EncoderVerbatim : public Encoder {
+ public:
+ EncoderVerbatim() {}
+ virtual ~EncoderVerbatim() {}
+
+ virtual void Encode(scoped_refptr<CaptureData> capture_data,
+ bool key_frame,
+ DataAvailableCallback* data_available_callback);
+
+ private:
+ // Encode a single dirty rect. Called by Encode(). Output is written
+ // to |msg|.
+ // Returns false if there is an error.
+ bool EncodeRect(int x, int y, int width, int height,
+ const scoped_refptr<CaptureData>& capture_data,
+ UpdateStreamPacketMessage* msg);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_ENCODER_VERBATIM_H_
diff --git a/remoting/base/encoder_vp8.cc b/remoting/base/encoder_vp8.cc
new file mode 100644
index 0000000..231acd5
--- /dev/null
+++ b/remoting/base/encoder_vp8.cc
@@ -0,0 +1,133 @@
+// 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 "base/logging.h"
+#include "media/base/callback.h"
+#include "media/base/data_buffer.h"
+#include "remoting/host/encoder_vp8.h"
+
+extern "C" {
+// TODO(garykac): Rix with correct path to vp8 header.
+#include "remoting/third_party/on2/include/vp8cx.h"
+}
+
+namespace remoting {
+
+EncoderVp8::EncoderVp8()
+ : initialized_(false),
+ last_timestamp_(0) {
+}
+
+EncoderVp8::~EncoderVp8() {
+}
+
+bool EncoderVp8::Init() {
+ // TODO(hclam): Now always assume we receive YV12. May need to extend this
+ // so we can do color space conversion manually.
+ image_.fmt = IMG_FMT_YV12;
+ image_.w = width_;
+ image_.h = height_;
+
+ on2_codec_enc_cfg_t config;
+ on2_codec_err_t result = on2_codec_enc_config_default(&on2_codec_vp8_cx_algo,
+ &config, 0);
+
+ // TODO(hclam): Adjust the parameters.
+ config.g_w = width_;
+ config.g_h = height_;
+ config.g_pass = ON2_RC_ONE_PASS;
+ config.g_profile = 1;
+ config.g_threads = 2;
+ config.rc_target_bitrate = 1000000;
+ config.rc_min_quantizer = 0;
+ config.rc_max_quantizer = 15;
+ config.g_timebase.num = 1;
+ config.g_timebase.den = 30;
+
+ if (on2_codec_enc_init(&codec_, &on2_codec_vp8_cx_algo, &config, 0))
+ return false;
+
+ on2_codec_control_(&codec_, VP8E_SET_CPUUSED, -15);
+ return true;
+}
+
+void EncoderVp8::Encode(const DirtyRects& dirty_rects,
+ const uint8** input_data,
+ const int* strides,
+ bool key_frame,
+ UpdateStreamPacketHeader* header,
+ scoped_refptr<media::DataBuffer>* output_data,
+ bool* encode_done,
+ Task* data_available_task) {
+ // This will allow the task be called when this method exits.
+ media::AutoTaskRunner task(data_available_task);
+ *encode_done = false;
+
+ // TODO(hclam): We only initialize the encoder once. We may have to
+ // allow encoder be initialized with difference sizes.
+ if (!initialized_) {
+ if (!Init()) {
+ LOG(ERROR) << "Can't initialize VP8 encoder";
+ return;
+ }
+ initialized_ = true;
+ }
+
+ // Assume the capturer has done the color space conversion.
+ if (!input_data || !strides)
+ return;
+
+ image_.planes[0] = (unsigned char*)input_data[0];
+ image_.planes[1] = (unsigned char*)input_data[1];
+ image_.planes[2] = (unsigned char*)input_data[2];
+ image_.stride[0] = strides[0];
+ image_.stride[1] = strides[1];
+ image_.stride[2] = strides[2];
+
+ // Do the actual encoding.
+ if (on2_codec_encode(&codec_, &image_,
+ last_timestamp_, 1, 0, ON2_DL_REALTIME)) {
+ return;
+ }
+
+ // TODO(hclam): fix this.
+ last_timestamp_ += 100;
+
+ // Read the encoded data.
+ on2_codec_iter_t iter = NULL;
+ bool got_data = false;
+
+ // TODO(hclam: We assume one frame of input will get exactly one frame of
+ // output. This assumption may not be valid.
+ while (!got_data) {
+ on2_codec_cx_pkt_t* packet = on2_codec_get_cx_data(&codec_, &iter);
+ if (!packet)
+ continue;
+
+ switch (packet->kind) {
+ case ON2_CODEC_CX_FRAME_PKT:
+ got_data = true;
+ *encode_done = true;
+ *output_data = new media::DataBuffer(packet->data.frame.sz);
+ memcpy((*output_data)->GetWritableData(),
+ packet->data.frame.buf,
+ packet->data.frame.sz);
+ break;
+ default:
+ break;
+ }
+ }
+ return;
+}
+
+void EncoderVp8::SetSize(int width, int height) {
+ width_ = width;
+ height_ = height;
+}
+
+void EncoderVp8::SetPixelFormat(PixelFormat pixel_format) {
+ pixel_format_ = pixel_format;
+}
+
+} // namespace remoting
diff --git a/remoting/base/encoder_vp8.h b/remoting/base/encoder_vp8.h
new file mode 100644
index 0000000..4b8b539
--- /dev/null
+++ b/remoting/base/encoder_vp8.h
@@ -0,0 +1,61 @@
+// 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_ENCODER_VP8_H_
+#define REMOTING_BASE_ENCODER_VP8_H_
+
+#include "remoting/host/encoder.h"
+
+#include "remoting/base/protocol/chromotocol.pb.h"
+
+extern "C" {
+// TODO(garykac): fix this link with the correct path to on2
+#include "remoting/third_party/on2/include/on2_encoder.h"
+} // extern "C"
+
+namespace media {
+
+class DataBuffer;
+
+} // namespace media
+
+namespace remoting {
+
+// A class that uses VP8 to perform encoding.
+class EncoderVp8 : public Encoder {
+ public:
+ EncoderVp8();
+ virtual ~EncoderVp8();
+
+ virtual void Encode(const DirtyRects& dirty_rects,
+ const uint8** input_data,
+ const int* strides,
+ bool key_frame,
+ UpdateStreamPacketHeader* header,
+ scoped_refptr<media::DataBuffer>* output_data,
+ bool* encode_done,
+ Task* data_available_task);
+ virtual void SetSize(int width, int height);
+ virtual void SetPixelFormat(PixelFormat pixel_format);
+
+ private:
+ // Setup the VP8 encoder.
+ bool Init();
+
+ // True if the encoder is initialized.
+ bool initialized_;
+
+ int width_;
+ int height_;
+ PixelFormat pixel_format_;
+ on2_codec_ctx_t codec_;
+ on2_image_t image_;
+ int last_timestamp_;
+
+ DISALLOW_COPY_AND_ASSIGN(EncoderVp8);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_ENCODER_VP8_H_
diff --git a/remoting/base/encoder_vp8_unittest.cc b/remoting/base/encoder_vp8_unittest.cc
new file mode 100644
index 0000000..0b29830
--- /dev/null
+++ b/remoting/base/encoder_vp8_unittest.cc
@@ -0,0 +1,68 @@
+// 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 "media/base/data_buffer.h"
+#include "remoting/base/pixel_format.h"
+#include "remoting/host/encoder_vp8.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace remoting {
+
+static const int kWidth = 1024;
+static const int kHeight = 768;
+static const PixelFormat kPixelFormat = kPixelFormat_YV12;
+
+static void GenerateData(uint8* data, int size) {
+ for (int i = 0; i < size; ++i) {
+ data[i] = i;
+ }
+}
+
+class EncodeDoneHandler
+ : public base::RefCountedThreadSafe<EncodeDoneHandler> {
+ public:
+ MOCK_METHOD0(EncodeDone, void());
+};
+
+TEST(EncoderVp8Test, SimpleEncode) {
+ EncoderVp8 encoder;
+ encoder.SetSize(kWidth, kHeight);
+ encoder.SetPixelFormat(kPixelFormat);
+
+ DirtyRects rects;
+ rects.push_back(gfx::Rect(kWidth, kHeight));
+
+ // Prepare memory for encoding.
+ int strides[3];
+ strides[0] = kWidth;
+ strides[1] = strides[2] = kWidth / 2;
+
+ uint8* planes[3];
+ planes[0] = new uint8[kWidth * kHeight];
+ planes[1] = new uint8[kWidth * kHeight / 4];
+ planes[2] = new uint8[kWidth * kHeight / 4];
+ GenerateData(planes[0], kWidth * kHeight);
+ GenerateData(planes[1], kWidth * kHeight / 4);
+ GenerateData(planes[2], kWidth * kHeight / 4);
+
+ scoped_refptr<EncodeDoneHandler> handler = new EncodeDoneHandler();
+ UpdateStreamPacketHeader* header = new UpdateStreamPacketHeader();
+ scoped_refptr<media::DataBuffer> encoded_data;
+ bool encode_done = false;
+ EXPECT_CALL(*handler, EncodeDone());
+ encoder.Encode(rects, const_cast<const uint8**>(planes),
+ strides, true, header, &encoded_data, &encode_done,
+ NewRunnableMethod(handler.get(),
+ &EncodeDoneHandler::EncodeDone));
+
+ EXPECT_TRUE(encode_done);
+ ASSERT_TRUE(encoded_data.get());
+ EXPECT_NE(0u, encoded_data->GetBufferSize());
+
+ delete [] planes[0];
+ delete [] planes[1];
+ delete [] planes[2];
+}
+
+} // namespace remoting
diff --git a/remoting/base/mock_objects.h b/remoting/base/mock_objects.h
index a61830e..7565f22 100644
--- a/remoting/base/mock_objects.h
+++ b/remoting/base/mock_objects.h
@@ -5,6 +5,9 @@
#ifndef REMOTING_BASE_MOCK_OBJECTS_H_
#define REMOTING_BASE_MOCK_OBJECTS_H_
+#include "remoting/base/capture_data.h"
+#include "remoting/base/decoder.h"
+#include "remoting/base/encoder.h"
#include "remoting/base/protocol_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -25,6 +28,19 @@ class MockProtocolDecoder : public ProtocolDecoder {
DISALLOW_COPY_AND_ASSIGN(MockProtocolDecoder);
};
+class MockEncoder : public Encoder {
+ public:
+ MockEncoder() {}
+
+ MOCK_METHOD3(Encode, void(
+ scoped_refptr<CaptureData> capture_data,
+ bool key_frame,
+ DataAvailableCallback* data_available_callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockEncoder);
+};
+
} // namespace remoting
#endif // REMOTING_BASE_MOCK_OBJECTS_H_