diff options
Diffstat (limited to 'remoting/base')
-rw-r--r-- | remoting/base/capture_data.h | 75 | ||||
-rw-r--r-- | remoting/base/decoder.h | 114 | ||||
-rw-r--r-- | remoting/base/decoder_verbatim.cc | 127 | ||||
-rw-r--r-- | remoting/base/decoder_verbatim.h | 56 | ||||
-rw-r--r-- | remoting/base/decoder_verbatim_unittest.cc | 72 | ||||
-rw-r--r-- | remoting/base/encoder.h | 61 | ||||
-rw-r--r-- | remoting/base/encoder_verbatim.cc | 89 | ||||
-rw-r--r-- | remoting/base/encoder_verbatim.h | 36 | ||||
-rw-r--r-- | remoting/base/encoder_vp8.cc | 133 | ||||
-rw-r--r-- | remoting/base/encoder_vp8.h | 61 | ||||
-rw-r--r-- | remoting/base/encoder_vp8_unittest.cc | 68 | ||||
-rw-r--r-- | remoting/base/mock_objects.h | 16 |
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_ |