summaryrefslogtreecommitdiffstats
path: root/remoting/client
diff options
context:
space:
mode:
authorajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-04 19:48:42 +0000
committerajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-04 19:48:42 +0000
commit8ea7a167522a24be192e958af46a41d49e78504c (patch)
tree571cf839290beed90019a744c3a096be89cc67d1 /remoting/client
parentb552f1787ca864e458e4c14e6012c20b423161a5 (diff)
downloadchromium_src-8ea7a167522a24be192e958af46a41d49e78504c.zip
chromium_src-8ea7a167522a24be192e958af46a41d49e78504c.tar.gz
chromium_src-8ea7a167522a24be192e958af46a41d49e78504c.tar.bz2
This is a monster CL.
It started as an attempt to put the decoder onto another thread. However, this became complicated due to multiple object ownership transfers and coupling between the decode layer and the network layer; the decoder's states were highly coupled with how the network packets were processed. This could probably be broken up slightly, but at this point, it's easier to just commit as a whole The refactor includes: 1) Making the decoder interface unaware of "network packet" types. 2) Making the network layer process packets in order. 3) Threading through asynchronous APIs all over the place. 4) Simplifying the rectangle update protocol. 5) Cleaning up object lifetime and ownership semantics between the decode layer and the renderer. As of right now, the Verbatim format is still broken on the encode side because it uses the old protocol. BUG=52883, 57351 TEST=still connects to chromoting_simple_host Review URL: http://codereview.chromium.org/3305001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61402 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/client')
-rw-r--r--remoting/client/chromoting_client.cc119
-rw-r--r--remoting/client/chromoting_client.h26
-rw-r--r--remoting/client/chromoting_view.cc92
-rw-r--r--remoting/client/chromoting_view.h69
-rw-r--r--remoting/client/chromoting_view_unittest.cc1
-rw-r--r--remoting/client/frame_consumer.h8
-rw-r--r--remoting/client/plugin/chromoting_instance.cc6
-rw-r--r--remoting/client/plugin/chromoting_instance.h2
-rw-r--r--remoting/client/plugin/pepper_view.cc215
-rw-r--r--remoting/client/plugin/pepper_view.h36
-rw-r--r--remoting/client/rectangle_update_decoder.cc11
-rw-r--r--remoting/client/rectangle_update_decoder.h3
-rw-r--r--remoting/client/x11_client.cc8
-rw-r--r--remoting/client/x11_view.cc107
-rw-r--r--remoting/client/x11_view.h26
15 files changed, 334 insertions, 395 deletions
diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc
index a9cf5ba..e4f93ab 100644
--- a/remoting/client/chromoting_client.cc
+++ b/remoting/client/chromoting_client.cc
@@ -5,10 +5,12 @@
#include "remoting/client/chromoting_client.h"
#include "base/message_loop.h"
+#include "remoting/base/tracer.h"
#include "remoting/client/chromoting_view.h"
#include "remoting/client/client_context.h"
#include "remoting/client/host_connection.h"
#include "remoting/client/input_handler.h"
+#include "remoting/client/rectangle_update_decoder.h"
static const uint32 kCreatedColor = 0xffccccff;
static const uint32 kDisconnectedColor = 0xff00ccff;
@@ -20,15 +22,18 @@ ChromotingClient::ChromotingClient(const ClientConfig& config,
ClientContext* context,
HostConnection* connection,
ChromotingView* view,
+ RectangleUpdateDecoder* rectangle_decoder,
InputHandler* input_handler,
CancelableTask* client_done)
: config_(config),
context_(context),
connection_(connection),
view_(view),
+ rectangle_decoder_(rectangle_decoder),
input_handler_(input_handler),
client_done_(client_done),
- state_(CREATED) {
+ state_(CREATED),
+ message_being_processed_(false) {
}
ChromotingClient::~ChromotingClient() {
@@ -101,24 +106,51 @@ void ChromotingClient::HandleMessages(HostConnection* conn,
return;
}
- for (size_t i = 0; i < messages->size(); ++i) {
- ChromotingHostMessage* msg = (*messages)[i];
- // TODO(ajwong): Consider creating a macro similar to the IPC message
- // mappings. Also reconsider the lifetime of the message object.
- if (msg->has_init_client()) {
- InitClient(msg);
- } else if (msg->has_begin_update_stream()) {
- BeginUpdate(msg);
- } else if (msg->has_update_stream_packet()) {
- HandleUpdate(msg);
- } else if (msg->has_end_update_stream()) {
- EndUpdate(msg);
- } else {
- NOTREACHED() << "Unknown message received";
- }
+ // Put all messages in the queue.
+ received_messages_.splice(received_messages_.end(), *messages);
+
+ if (!message_being_processed_) {
+ DispatchMessage();
+ }
+}
+
+void ChromotingClient::DispatchMessage() {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ CHECK(!message_being_processed_);
+
+ if (received_messages_.empty()) {
+ // Nothing to do!
+ return;
+ }
+
+ ChromotingHostMessage* msg = received_messages_.front();
+ received_messages_.pop_front();
+ message_being_processed_ = true;
+
+ // TODO(ajwong): Consider creating a macro similar to the IPC message
+ // mappings. Also reconsider the lifetime of the message object.
+ if (msg->has_init_client()) {
+ ScopedTracer tracer("Handle Init Client");
+ // TODO(ajwong): Change this to use a done callback.
+ InitClient(msg->init_client(),
+ NewTracedMethod(this, &ChromotingClient::OnMessageDone, msg));
+ } else if (msg->has_rectangle_update()) {
+ ScopedTracer tracer("Handle Rectangle Update");
+ rectangle_decoder_->DecodePacket(
+ msg->rectangle_update(),
+ NewTracedMethod(this, &ChromotingClient::OnMessageDone, msg));
+ } else {
+ NOTREACHED() << "Unknown message received";
+
+ // We have an unknown message. Drop it, and schedule another dispatch.
+ // Call DispatchMessage as a continuation to avoid growing the stack.
+ delete msg;
+ message_being_processed_ = false;
+ message_loop()->PostTask(
+ FROM_HERE,
+ NewTracedMethod(this, &ChromotingClient::DispatchMessage));
+ return;
}
- // Assume we have processed all the messages.
- messages->clear();
}
void ChromotingClient::OnConnectionOpened(HostConnection* conn) {
@@ -172,41 +204,40 @@ void ChromotingClient::SetState(State s) {
Repaint();
}
-void ChromotingClient::InitClient(ChromotingHostMessage* msg) {
- DCHECK_EQ(message_loop(), MessageLoop::current());
- DCHECK(msg->has_init_client());
- scoped_ptr<ChromotingHostMessage> deleter(msg);
-
- // Resize the window.
- int width = msg->init_client().width();
- int height = msg->init_client().height();
- LOG(INFO) << "Init client received geometry: " << width << "x" << height;
+void ChromotingClient::OnMessageDone(ChromotingHostMessage* msg) {
+ if (message_loop() != MessageLoop::current()) {
+ message_loop()->PostTask(
+ FROM_HERE,
+ NewTracedMethod(this, &ChromotingClient::OnMessageDone, msg));
+ return;
+ }
- view_->SetHostScreenSize(width, height);
+ TraceContext::tracer()->PrintString("Message done");
- // Schedule the input handler to process the event queue.
- input_handler_->Initialize();
+ message_being_processed_ = false;
+ delete msg;
+ DispatchMessage();
}
-void ChromotingClient::BeginUpdate(ChromotingHostMessage* msg) {
+void ChromotingClient::InitClient(const InitClientMessage& init_client,
+ Task* done) {
DCHECK_EQ(message_loop(), MessageLoop::current());
- DCHECK(msg->has_begin_update_stream());
-
- view_->HandleBeginUpdateStream(msg);
-}
+ TraceContext::tracer()->PrintString("Init received");
-void ChromotingClient::HandleUpdate(ChromotingHostMessage* msg) {
- DCHECK_EQ(message_loop(), MessageLoop::current());
- DCHECK(msg->has_update_stream_packet());
+ // Resize the window.
+ int width = init_client.width();
+ int height = init_client.height();
+ LOG(INFO) << "Init client received geometry: " << width << "x" << height;
- view_->HandleUpdateStreamPacket(msg);
-}
+// TODO(ajwong): What to do here? Does the decoder actually need to request
+// the right frame size? This is mainly an optimization right?
+// rectangle_decoder_->SetOutputFrameSize(width, height);
-void ChromotingClient::EndUpdate(ChromotingHostMessage* msg) {
- DCHECK_EQ(message_loop(), MessageLoop::current());
- DCHECK(msg->has_end_update_stream());
+ // Schedule the input handler to process the event queue.
+ input_handler_->Initialize();
- view_->HandleEndUpdateStream(msg);
+ done->Run();
+ delete done;
}
} // namespace remoting
diff --git a/remoting/client/chromoting_client.h b/remoting/client/chromoting_client.h
index c7026e3..31870ed 100644
--- a/remoting/client/chromoting_client.h
+++ b/remoting/client/chromoting_client.h
@@ -18,6 +18,9 @@ namespace remoting {
class ChromotingView;
class ClientContext;
class InputHandler;
+class ChromotingHostMessage;
+class InitClientMessage;
+class RectangleUpdateDecoder;
class ChromotingClient : public HostConnection::HostEventCallback {
public:
@@ -26,6 +29,7 @@ class ChromotingClient : public HostConnection::HostEventCallback {
ClientContext* context,
HostConnection* connection,
ChromotingView* view,
+ RectangleUpdateDecoder* rectangle_decoder,
InputHandler* input_handler,
CancelableTask* client_done);
virtual ~ChromotingClient();
@@ -64,17 +68,21 @@ class ChromotingClient : public HostConnection::HostEventCallback {
// Convenience method for modifying the state on this object's message loop.
void SetState(State s);
+ // If a message is not being processed, dispatches a single message from the
+ // |received_messages_| queue.
+ void DispatchMessage();
+
+ void OnMessageDone(ChromotingHostMessage* msg);
+
// Handles for chromotocol messages.
- void InitClient(ChromotingHostMessage* msg);
- void BeginUpdate(ChromotingHostMessage* msg);
- void HandleUpdate(ChromotingHostMessage* msg);
- void EndUpdate(ChromotingHostMessage* msg);
+ void InitClient(const InitClientMessage& msg, Task* done);
// The following are not owned by this class.
ClientConfig config_;
ClientContext* context_;
HostConnection* connection_;
ChromotingView* view_;
+ RectangleUpdateDecoder* rectangle_decoder_;
InputHandler* input_handler_;
// If non-NULL, this is called when the client is done.
@@ -82,6 +90,16 @@ class ChromotingClient : public HostConnection::HostEventCallback {
State state_;
+ // Contains all messages that have been received, but have not yet been
+ // processed.
+ //
+ // Used to serialize sending of messages to the client.
+ HostMessageList received_messages_;
+
+ // True if a message is being processed. Can be used to determine if it is
+ // safe to dispatch another message.
+ bool message_being_processed_;
+
DISALLOW_COPY_AND_ASSIGN(ChromotingClient);
};
diff --git a/remoting/client/chromoting_view.cc b/remoting/client/chromoting_view.cc
index 95d2dfa..ba34a5d 100644
--- a/remoting/client/chromoting_view.cc
+++ b/remoting/client/chromoting_view.cc
@@ -4,8 +4,9 @@
#include "remoting/client/chromoting_view.h"
-#include "remoting/base/decoder_verbatim.h"
-#include "remoting/base/decoder_zlib.h"
+#include "base/message_loop.h"
+#include "base/waitable_event.h"
+#include "remoting/base/tracer.h"
namespace remoting {
@@ -14,7 +15,6 @@ ChromotingView::ChromotingView()
frame_height_(0) {
}
-
// TODO(garykac): This assumes a single screen. This will need to be adjusted
// to add support for mulitple monitors.
void ChromotingView::GetScreenSize(int* width, int* height) {
@@ -22,90 +22,4 @@ void ChromotingView::GetScreenSize(int* width, int* height) {
*height = frame_height_;
}
-bool ChromotingView::SetupDecoder(UpdateStreamEncoding encoding) {
- if (encoding == EncodingInvalid) {
- LOG(ERROR) << "Cannot create encoder for EncodingInvalid";
- return false;
- }
-
- // If we're in the middle of decoding a stream, then we need to make sure
- // that that all packets in that stream match the encoding of the first
- // packet.
- //
- // If we decide to relax this constraint in the future, we'll need to
- // update this to keep a set of decoders around.
- if (decoder_.get() && decoder_->IsStarted()) {
- // Verify that the encoding matches the decoder. Once we've started
- // decoding, we can't switch to another decoder.
- if (decoder_->Encoding() != encoding) {
- LOG(ERROR) << "Encoding mismatch: Set up to handle "
- << "UpdateStreamEncoding=" << decoder_->Encoding()
- << " but received request for "
- << encoding;
- return false;
- }
- return true;
- }
-
- // Lazily initialize a new decoder.
- // We create a new decoder if we don't currently have a decoder or if the
- // decoder doesn't match the desired encoding.
- if (!decoder_.get() || decoder_->Encoding() != encoding) {
- // Initialize a new decoder based on this message encoding.
- if (encoding == EncodingNone) {
- decoder_.reset(new DecoderVerbatim());
- } else if (encoding == EncodingZlib) {
- decoder_.reset(new DecoderZlib());
- }
- // Make sure we successfully allocated a decoder of the correct type.
- DCHECK(decoder_.get());
- DCHECK(decoder_->Encoding() == encoding);
- }
-
- return true;
-}
-
-bool ChromotingView::BeginDecoding(Task* partial_decode_done,
- Task* decode_done) {
- if (decoder_->IsStarted()) {
- LOG(ERROR) << "BeginDecoding called without ending previous decode.";
- return false;
- }
-
- decoder_->BeginDecode(frame_, &update_rects_,
- partial_decode_done, decode_done);
-
- if (!decoder_->IsStarted()) {
- LOG(ERROR) << "Unable to start decoding.";
- return false;
- }
-
- return true;
-}
-
-bool ChromotingView::Decode(ChromotingHostMessage* msg) {
- if (!decoder_->IsStarted()) {
- LOG(ERROR) << "Attempt to decode payload before calling BeginDecode.";
- return false;
- }
-
- return decoder_->PartialDecode(msg);
-}
-
-bool ChromotingView::EndDecoding() {
- if (!decoder_->IsStarted()) {
- LOG(ERROR) << "Attempt to end decode when none has been started.";
- return false;
- }
-
- decoder_->EndDecode();
-
- if (decoder_->IsStarted()) {
- LOG(ERROR) << "Unable to properly end decoding.\n";
- return false;
- }
-
- return true;
-}
-
} // namespace remoting
diff --git a/remoting/client/chromoting_view.h b/remoting/client/chromoting_view.h
index d785b29..d1c978b 100644
--- a/remoting/client/chromoting_view.h
+++ b/remoting/client/chromoting_view.h
@@ -6,13 +6,19 @@
#define REMOTING_CLIENT_CHROMOTING_VIEW_H_
#include "base/ref_counted.h"
-#include "remoting/base/decoder.h"
+#include "media/base/video_frame.h"
+
+class MessageLoop;
+
+namespace base {
+class WaitableEvent;
+} // namespace base
namespace remoting {
// ChromotingView defines the behavior of an object that draws a view of the
-// remote desktop. Its main function is to choose the right decoder and render
-// the update stream onto the screen.
+// remote desktop. Its main function is to render the update stream onto the
+// screen.
class ChromotingView {
public:
ChromotingView();
@@ -44,70 +50,13 @@ class ChromotingView {
// extends past the end of the backing store, it is filled with black.
virtual void SetViewport(int x, int y, int width, int height) = 0;
- // Resize the underlying image that contains the host screen buffer.
- // This should match the size of the output from the decoder.
- //
- // TODO(garykac): This handles only 1 screen. We need multi-screen support.
- virtual void SetHostScreenSize(int width, int height) = 0;
-
- // Handle the BeginUpdateStream message.
- // This method should perform the following tasks:
- // (1) Perform any platform-specific tasks for start of update stream.
- // (2) Make sure the |frame_| has been initialized.
- // (3) Delete the HostMessage.
- virtual void HandleBeginUpdateStream(ChromotingHostMessage* msg) = 0;
-
- // Handle the UpdateStreamPacket message.
- // This method should perform the following tasks:
- // (1) Extract the decoding from the update packet message.
- // (2) Call SetupDecoder with the encoding to lazily initialize the decoder.
- // We don't do this in BeginUpdateStream because the begin message
- // doesn't contain the encoding.
- // (3) Call BeginDecoding if this is the first packet of the stream.
- // (4) Call the decoder's PartialDecode() method to decode the packet.
- // This call will delete the HostMessage.
- // Note:
- // * For a given begin/end update stream, the encodings specified in the
- // update packets must all match. We may revisit this constraint at a
- // later date.
- virtual void HandleUpdateStreamPacket(ChromotingHostMessage* msg) = 0;
-
- // Handle the EndUpdateStream message.
- // This method should perform the following tasks:
- // (1) Call EndDecoding().
- // (2) Perform any platform-specific tasks for end of update stream.
- // (3) Delete the HostMessage.
- virtual void HandleEndUpdateStream(ChromotingHostMessage* msg) = 0;
-
protected:
- // Setup the decoder based on the given encoding.
- // Returns true if a new decoder has already been started (with a call to
- // BeginDecoding).
- bool SetupDecoder(UpdateStreamEncoding encoding);
-
- // Prepare the decoder to start decoding a chunk of data.
- // This needs to be called if SetupDecoder() returns false.
- bool BeginDecoding(Task* partial_decode_done, Task* decode_done);
-
- // Decode the given message.
- // BeginDecoding() must be called before any calls to Decode().
- bool Decode(ChromotingHostMessage* msg);
-
- // Finish decoding and send notifications to update the view.
- bool EndDecoding();
-
- // Decoder used to decode the video frames (or frame fragements).
- scoped_ptr<Decoder> decoder_;
-
// Framebuffer for the decoder.
scoped_refptr<media::VideoFrame> frame_;
// Dimensions of |frame_| bitmap.
int frame_width_;
int frame_height_;
-
- UpdatedRects update_rects_;
- UpdatedRects all_update_rects_;
};
} // namespace remoting
diff --git a/remoting/client/chromoting_view_unittest.cc b/remoting/client/chromoting_view_unittest.cc
index 4301e5a..6dcb60c 100644
--- a/remoting/client/chromoting_view_unittest.cc
+++ b/remoting/client/chromoting_view_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/scoped_ptr.h"
+#include "remoting/base/decoder.h"
#include "remoting/base/protocol/chromotocol.pb.h"
#include "remoting/client/chromoting_view.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/remoting/client/frame_consumer.h b/remoting/client/frame_consumer.h
index 05c4448..90f5e55 100644
--- a/remoting/client/frame_consumer.h
+++ b/remoting/client/frame_consumer.h
@@ -5,6 +5,10 @@
#ifndef REMOTING_CLIENT_FRAME_CONSUMER_H_
#define REMOTING_CLIENT_FRAME_CONSUMER_H_
+#include "remoting/base/decoder.h" // For UpdatedRects
+
+class Task;
+
namespace remoting {
class FrameConsumer {
@@ -41,8 +45,8 @@ class FrameConsumer {
virtual void ReleaseFrame(media::VideoFrame* frame) = 0;
// OnPartialFrameOutput() is called every time at least one rectangle of
- // output is produced. The |frame| is guaranteed to have valid data for
- // every region included in the |rects| list.
+ // output is produced. The |frame| is guaranteed to have valid data for every
+ // region included in the |rects| list.
//
// Both |frame| and |rects| are guaranteed to be valid until the |done|
// callback is invoked.
diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc
index 14fad8e..c03f287 100644
--- a/remoting/client/plugin/chromoting_instance.cc
+++ b/remoting/client/plugin/chromoting_instance.cc
@@ -15,6 +15,7 @@
#include "remoting/client/chromoting_client.h"
#include "remoting/client/host_connection.h"
#include "remoting/client/jingle_host_connection.h"
+#include "remoting/client/rectangle_update_decoder.h"
#include "remoting/client/plugin/chromoting_scriptable_object.h"
#include "remoting/client/plugin/pepper_input_handler.h"
#include "remoting/client/plugin/pepper_view.h"
@@ -67,7 +68,9 @@ bool ChromotingInstance::Init(uint32_t argc,
// Create the chromoting objects.
host_connection_.reset(new JingleHostConnection(&context_));
- view_.reset(new PepperView(this));
+ view_.reset(new PepperView(this, &context_));
+ rectangle_decoder_.reset(
+ new RectangleUpdateDecoder(context_.decode_message_loop(), view_.get()));
input_handler_.reset(new PepperInputHandler(&context_, host_connection_.get(),
view_.get()));
@@ -84,6 +87,7 @@ void ChromotingInstance::Connect(const ClientConfig& config) {
&context_,
host_connection_.get(),
view_.get(),
+ rectangle_decoder_.get(),
input_handler_.get(),
NULL));
diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h
index ee9b1df..6c9d7c0 100644
--- a/remoting/client/plugin/chromoting_instance.h
+++ b/remoting/client/plugin/chromoting_instance.h
@@ -39,6 +39,7 @@ class HostConnection;
class InputHandler;
class JingleThread;
class PepperView;
+class RectangleUpdateDecoder;
class ChromotingInstance : public pp::Instance {
public:
@@ -70,6 +71,7 @@ class ChromotingInstance : public pp::Instance {
ClientContext context_;
scoped_ptr<HostConnection> host_connection_;
scoped_ptr<PepperView> view_;
+ scoped_ptr<RectangleUpdateDecoder> rectangle_decoder_;
scoped_ptr<InputHandler> input_handler_;
scoped_ptr<ChromotingClient> client_;
pp::Var instance_object_; // JavaScript interface to control this instance.
diff --git a/remoting/client/plugin/pepper_view.cc b/remoting/client/plugin/pepper_view.cc
index cc72c43..eacb37d 100644
--- a/remoting/client/plugin/pepper_view.cc
+++ b/remoting/client/plugin/pepper_view.cc
@@ -5,6 +5,8 @@
#include "remoting/client/plugin/pepper_view.h"
#include "base/message_loop.h"
+#include "remoting/base/tracer.h"
+#include "remoting/client/client_context.h"
#include "remoting/client/plugin/chromoting_instance.h"
#include "remoting/client/plugin/pepper_util.h"
#include "third_party/ppapi/cpp/graphics_2d.h"
@@ -14,14 +16,15 @@
namespace remoting {
-PepperView::PepperView(ChromotingInstance* instance)
- : instance_(instance),
- viewport_x_(0),
- viewport_y_(0),
- viewport_width_(0),
- viewport_height_(0),
- is_static_fill_(false),
- static_fill_color_(0) {
+PepperView::PepperView(ChromotingInstance* instance, ClientContext* context)
+ : instance_(instance),
+ context_(context),
+ viewport_x_(0),
+ viewport_y_(0),
+ viewport_width_(0),
+ viewport_height_(0),
+ is_static_fill_(false),
+ static_fill_color_(0) {
}
PepperView::~PepperView() {
@@ -36,10 +39,47 @@ void PepperView::TearDown() {
void PepperView::Paint() {
if (!instance_->CurrentlyOnPluginThread()) {
- RunTaskOnPluginThread(NewRunnableMethod(this, &PepperView::Paint));
+ RunTaskOnPluginThread(NewTracedMethod(this, &PepperView::Paint));
return;
}
+ TraceContext::tracer()->PrintString("Start Paint.");
+ // TODO(ajwong): We're assuming the native format is BGRA_PREMUL below. This
+ // is wrong.
+ if (is_static_fill_) {
+ LOG(ERROR) << "Static filling " << static_fill_color_;
+ pp::ImageData image(pp::ImageData::GetNativeImageDataFormat(),
+ pp::Size(viewport_width_, viewport_height_),
+ false);
+ if (image.is_null()) {
+ LOG(ERROR) << "Unable to allocate image of size: "
+ << viewport_width_ << "x" << viewport_height_;
+ return;
+ }
+
+ for (int y = 0; y < image.size().height(); y++) {
+ for (int x = 0; x < image.size().width(); x++) {
+ *image.GetAddr32(pp::Point(x, y)) = static_fill_color_;
+ }
+ }
+
+ // For ReplaceContents, make sure the image size matches the device context
+ // size! Otherwise, this will just silently do nothing.
+ graphics2d_.ReplaceContents(&image);
+ graphics2d_.Flush(TaskToCompletionCallback(
+ NewTracedMethod(this, &PepperView::OnPaintDone)));
+ } else {
+ // TODO(ajwong): We need to keep a backing store image of the viewport that
+ // has the data here which can be redrawn.
+ return;
+ }
+ TraceContext::tracer()->PrintString("End Paint.");
+}
+
+void PepperView::PaintFrame(media::VideoFrame* frame, UpdatedRects* rects) {
+ DCHECK(instance_->CurrentlyOnPluginThread());
+
+ TraceContext::tracer()->PrintString("Start Paint Frame.");
// TODO(ajwong): We're assuming the native format is BGRA_PREMUL below. This
// is wrong.
pp::ImageData image(pp::ImageData::GetNativeImageDataFormat(),
@@ -47,43 +87,37 @@ void PepperView::Paint() {
false);
if (image.is_null()) {
LOG(ERROR) << "Unable to allocate image of size: "
- << viewport_width_ << "x" << viewport_height_;
+ << frame->width() << "x" << frame->height();
return;
}
- if (is_static_fill_) {
- for (int y = 0; y < image.size().height(); y++) {
- for (int x = 0; x < image.size().width(); x++) {
- *image.GetAddr32(pp::Point(x, y)) = static_fill_color_;
- }
- }
- } else if (frame_) {
- uint32_t* frame_data =
- reinterpret_cast<uint32_t*>(frame_->data(media::VideoFrame::kRGBPlane));
- int max_height = std::min(frame_height_, image.size().height());
- int max_width = std::min(frame_width_, image.size().width());
- for (int y = 0; y < max_height; y++) {
- for (int x = 0; x < max_width; x++) {
- // Force alpha to be set to 255.
- *image.GetAddr32(pp::Point(x, y)) =
- frame_data[y*frame_width_ + x] | 0xFF000000;
- }
+ uint32_t* frame_data =
+ reinterpret_cast<uint32_t*>(frame->data(media::VideoFrame::kRGBPlane));
+ int frame_width = static_cast<int>(frame->width());
+ int frame_height = static_cast<int>(frame->height());
+ int max_height = std::min(frame_height, image.size().height());
+ int max_width = std::min(frame_width, image.size().width());
+ for (int y = 0; y < max_height; y++) {
+ for (int x = 0; x < max_width; x++) {
+ // Force alpha to be set to 255.
+ *image.GetAddr32(pp::Point(x, y)) =
+ frame_data[y*frame_width + x] | 0xFF000000;
}
- } else {
- // Nothing to paint. escape!
- //
- // TODO(ajwong): This is an ugly control flow. fix.
- return;
}
- device_context_.ReplaceContents(&image);
- device_context_.Flush(TaskToCompletionCallback(
- NewRunnableMethod(this, &PepperView::OnPaintDone)));
+
+ // For ReplaceContents, make sure the image size matches the device context
+ // size! Otherwise, this will just silently do nothing.
+ graphics2d_.ReplaceContents(&image);
+ graphics2d_.Flush(TaskToCompletionCallback(
+ NewTracedMethod(this, &PepperView::OnPaintDone)));
+
+ TraceContext::tracer()->PrintString("End Paint Frame.");
}
void PepperView::SetSolidFill(uint32 color) {
if (!instance_->CurrentlyOnPluginThread()) {
RunTaskOnPluginThread(
- NewRunnableMethod(this, &PepperView::SetSolidFill, color));
+ NewTracedMethod(this, &PepperView::SetSolidFill, color));
return;
}
@@ -94,7 +128,7 @@ void PepperView::SetSolidFill(uint32 color) {
void PepperView::UnsetSolidFill() {
if (!instance_->CurrentlyOnPluginThread()) {
RunTaskOnPluginThread(
- NewRunnableMethod(this, &PepperView::UnsetSolidFill));
+ NewTracedMethod(this, &PepperView::UnsetSolidFill));
return;
}
@@ -103,7 +137,7 @@ void PepperView::UnsetSolidFill() {
void PepperView::SetViewport(int x, int y, int width, int height) {
if (!instance_->CurrentlyOnPluginThread()) {
- RunTaskOnPluginThread(NewRunnableMethod(this, &PepperView::SetViewport,
+ RunTaskOnPluginThread(NewTracedMethod(this, &PepperView::SetViewport,
x, y, width, height));
return;
}
@@ -116,95 +150,64 @@ void PepperView::SetViewport(int x, int y, int width, int height) {
viewport_width_ = width;
viewport_height_ = height;
- device_context_ =
- pp::Graphics2D(pp::Size(viewport_width_, viewport_height_), false);
- if (!instance_->BindGraphics(device_context_)) {
+ graphics2d_ = pp::Graphics2D(pp::Size(viewport_width_, viewport_height_),
+ false);
+ if (!instance_->BindGraphics(graphics2d_)) {
LOG(ERROR) << "Couldn't bind the device context.";
return;
}
}
-void PepperView::SetHostScreenSize(int width, int height) {
- if (!instance_->CurrentlyOnPluginThread()) {
- RunTaskOnPluginThread(NewRunnableMethod(this,
- &PepperView::SetHostScreenSize,
- width, height));
- return;
- }
-
- frame_width_ = width;
- frame_height_ = height;
-
- // Reset |frame_| - it will be recreated by the next update stream.
- frame_ = NULL;
-}
-
-void PepperView::HandleBeginUpdateStream(ChromotingHostMessage* msg) {
- if (!instance_->CurrentlyOnPluginThread()) {
- RunTaskOnPluginThread(
- NewRunnableMethod(this, &PepperView::HandleBeginUpdateStream,
- msg));
- return;
- }
-
- scoped_ptr<ChromotingHostMessage> deleter(msg);
-
- // Make sure the |frame_| is initialized.
- if (!frame_) {
- media::VideoFrame::CreateFrame(media::VideoFrame::RGB32,
- frame_width_, frame_height_,
- base::TimeDelta(), base::TimeDelta(),
- &frame_);
- CHECK(frame_);
+void PepperView::AllocateFrame(media::VideoFrame::Format format,
+ size_t width,
+ size_t height,
+ base::TimeDelta timestamp,
+ base::TimeDelta duration,
+ scoped_refptr<media::VideoFrame>* frame_out,
+ Task* done) {
+ // TODO(ajwong): Implement this to be backed by an pp::ImageData rather than
+ // generic memory.
+ media::VideoFrame::CreateFrame(media::VideoFrame::RGB32,
+ width, height,
+ base::TimeDelta(), base::TimeDelta(),
+ frame_out);
+ if (*frame_out) {
+ (*frame_out)->AddRef();
}
+ done->Run();
+ delete done;
}
-void PepperView::HandleUpdateStreamPacket(ChromotingHostMessage* msg) {
- if (!instance_->CurrentlyOnPluginThread()) {
- RunTaskOnPluginThread(
- NewRunnableMethod(this, &PepperView::HandleUpdateStreamPacket,
- msg));
- return;
+void PepperView::ReleaseFrame(media::VideoFrame* frame) {
+ if (frame) {
+ LOG(WARNING) << "Frame released.";
+ frame->Release();
}
-
- // Lazily initialize the decoder.
- SetupDecoder(msg->update_stream_packet().begin_rect().encoding());
- if (!decoder_->IsStarted()) {
- BeginDecoding(NewRunnableMethod(this, &PepperView::OnPartialDecodeDone),
- NewRunnableMethod(this, &PepperView::OnDecodeDone));
- }
-
- Decode(msg);
}
-void PepperView::HandleEndUpdateStream(ChromotingHostMessage* msg) {
+void PepperView::OnPartialFrameOutput(media::VideoFrame* frame,
+ UpdatedRects* rects,
+ Task* done) {
if (!instance_->CurrentlyOnPluginThread()) {
- RunTaskOnPluginThread(
- NewRunnableMethod(this, &PepperView::HandleEndUpdateStream,
- msg));
+ RunTaskOnPluginThread(NewTracedMethod(this,
+ &PepperView::OnPartialFrameOutput,
+ frame, rects, done));
return;
}
- scoped_ptr<ChromotingHostMessage> deleter(msg);
- EndDecoding();
+ TraceContext::tracer()->PrintString("Calling PaintFrame");
+ // TODO(ajwong): Clean up this API to be async so we don't need to use a
+ // member variable as a hack.
+ PaintFrame(frame, rects);
+ done->Run();
+ delete done;
}
void PepperView::OnPaintDone() {
// TODO(ajwong):Probably should set some variable to allow repaints to
// actually paint.
+ TraceContext::tracer()->PrintString("Paint flushed");
return;
}
-void PepperView::OnPartialDecodeDone() {
- all_update_rects_.insert(all_update_rects_.begin() +
- all_update_rects_.size(),
- update_rects_.begin(), update_rects_.end());
- Paint();
- // TODO(ajwong): Need to block here to be synchronous.
-}
-
-
-void PepperView::OnDecodeDone() {
-}
-
} // namespace remoting
diff --git a/remoting/client/plugin/pepper_view.h b/remoting/client/plugin/pepper_view.h
index 4ce932ec..95c180d 100644
--- a/remoting/client/plugin/pepper_view.h
+++ b/remoting/client/plugin/pepper_view.h
@@ -18,20 +18,21 @@
#include "base/task.h"
#include "media/base/video_frame.h"
#include "remoting/client/chromoting_view.h"
+#include "remoting/client/frame_consumer.h"
+#include "remoting/client/rectangle_update_decoder.h"
#include "third_party/ppapi/cpp/graphics_2d.h"
namespace remoting {
class ChromotingInstance;
+class ClientContext;
-class PepperView : public ChromotingView {
+class PepperView : public ChromotingView,
+ public FrameConsumer {
public:
// Constructs a PepperView that draws to the |rendering_device|. The
// |rendering_device| instance must outlive this class.
- //
- // TODO(ajwong): This probably needs to synchronize with the pepper thread
- // to be safe.
- explicit PepperView(ChromotingInstance* instance);
+ PepperView(ChromotingInstance* instance, ClientContext* context);
virtual ~PepperView();
// ChromotingView implementation.
@@ -41,22 +42,33 @@ class PepperView : public ChromotingView {
virtual void SetSolidFill(uint32 color);
virtual void UnsetSolidFill();
virtual void SetViewport(int x, int y, int width, int height);
- virtual void SetHostScreenSize(int width, int height);
- virtual void HandleBeginUpdateStream(ChromotingHostMessage* msg);
- virtual void HandleUpdateStreamPacket(ChromotingHostMessage* msg);
- virtual void HandleEndUpdateStream(ChromotingHostMessage* msg);
+
+ // FrameConsumer implementation.
+ virtual void AllocateFrame(media::VideoFrame::Format format,
+ size_t width,
+ size_t height,
+ base::TimeDelta timestamp,
+ base::TimeDelta duration,
+ scoped_refptr<media::VideoFrame>* frame_out,
+ Task* done);
+ virtual void ReleaseFrame(media::VideoFrame* frame);
+ virtual void OnPartialFrameOutput(media::VideoFrame* frame,
+ UpdatedRects* rects,
+ Task* done);
private:
void OnPaintDone();
- void OnPartialDecodeDone();
- void OnDecodeDone();
+ void PaintFrame(media::VideoFrame* frame, UpdatedRects* rects);
// Reference to the creating plugin instance. Needed for interacting with
// pepper. Marking explciitly as const since it must be initialized at
// object creation, and never change.
ChromotingInstance* const instance_;
- pp::Graphics2D device_context_;
+ // Context should be constant for the lifetime of the plugin.
+ ClientContext* const context_;
+
+ pp::Graphics2D graphics2d_;
int viewport_x_;
int viewport_y_;
diff --git a/remoting/client/rectangle_update_decoder.cc b/remoting/client/rectangle_update_decoder.cc
index 9d34e2a..44bac08 100644
--- a/remoting/client/rectangle_update_decoder.cc
+++ b/remoting/client/rectangle_update_decoder.cc
@@ -8,9 +8,9 @@
#include "base/message_loop.h"
#include "media/base/callback.h"
#include "remoting/base/decoder.h"
-#include "remoting/base/decoder_verbatim.h"
-#include "remoting/base/decoder_zlib.h"
+#include "remoting/base/decoder_row_based.h"
#include "remoting/base/protocol/chromotocol.pb.h"
+#include "remoting/base/protocol_util.h"
#include "remoting/base/tracer.h"
#include "remoting/client/frame_consumer.h"
@@ -195,10 +195,10 @@ void RectangleUpdateDecoder::InitializeDecoder(const RectangleFormat& format,
// Initialize a new decoder based on this message encoding.
if (format.encoding() == EncodingNone) {
TraceContext::tracer()->PrintString("Creating Verbatim decoder.");
- decoder_.reset(new DecoderVerbatim());
+ decoder_.reset(DecoderRowBased::CreateVerbatimDecoder());
} else if (format.encoding() == EncodingZlib) {
TraceContext::tracer()->PrintString("Creating Zlib decoder");
- decoder_.reset(new DecoderZlib());
+ decoder_.reset(DecoderRowBased::CreateZlibDecoder());
} else {
NOTREACHED() << "Invalid Encoding found: " << format.encoding();
}
@@ -211,7 +211,8 @@ void RectangleUpdateDecoder::InitializeDecoder(const RectangleFormat& format,
gfx::Rect rectangle_size(format.x(), format.y(),
format.width(), format.height());
updated_rects_.push_back(rectangle_size);
- decoder_->Initialize(frame_, rectangle_size);
+ decoder_->Initialize(frame_, rectangle_size,
+ GetBytesPerPixel(format.pixel_format()));
TraceContext::tracer()->PrintString("Decoder is Initialized");
}
diff --git a/remoting/client/rectangle_update_decoder.h b/remoting/client/rectangle_update_decoder.h
index 5f18e12..b383c20 100644
--- a/remoting/client/rectangle_update_decoder.h
+++ b/remoting/client/rectangle_update_decoder.h
@@ -8,12 +8,13 @@
#include "base/scoped_ptr.h"
#include "base/task.h"
#include "media/base/video_frame.h"
-#include "remoting/base/decoder.h" // For UpdatedRects.
+#include "remoting/base/decoder.h" // For UpdatedRects
class MessageLoop;
namespace remoting {
+class Decoder;
class FrameConsumer;
class RectangleFormat;
class RectangleUpdatePacket;
diff --git a/remoting/client/x11_client.cc b/remoting/client/x11_client.cc
index fd3f410..258f20f 100644
--- a/remoting/client/x11_client.cc
+++ b/remoting/client/x11_client.cc
@@ -11,6 +11,7 @@
#include "remoting/client/client_config.h"
#include "remoting/client/client_util.h"
#include "remoting/client/jingle_host_connection.h"
+#include "remoting/client/rectangle_update_decoder.h"
#include "remoting/client/x11_view.h"
#include "remoting/client/x11_input_handler.h"
@@ -31,9 +32,12 @@ int main(int argc, char** argv) {
remoting::ClientContext context;
remoting::JingleHostConnection connection(&context);
remoting::X11View view;
+ remoting::RectangleUpdateDecoder rectangle_decoder(
+ context.decode_message_loop(), &view);
remoting::X11InputHandler input_handler(&context, &connection, &view);
- remoting::ChromotingClient client(config, &context, &connection, &view,
- &input_handler, NewRunnableFunction(&ClientQuit, &ui_loop));
+ remoting::ChromotingClient client(
+ config, &context, &connection, &view, &rectangle_decoder, &input_handler,
+ NewRunnableFunction(&ClientQuit, &ui_loop));
// Run the client on a new MessageLoop until
context.Start();
diff --git a/remoting/client/x11_view.cc b/remoting/client/x11_view.cc
index 43ecd65..9ccdb88 100644
--- a/remoting/client/x11_view.cc
+++ b/remoting/client/x11_view.cc
@@ -10,7 +10,7 @@
#include <X11/extensions/Xcomposite.h>
#include "base/logging.h"
-#include "remoting/base/decoder_zlib.h"
+#include "base/task.h"
namespace remoting {
@@ -65,14 +65,15 @@ void X11View::TearDown() {
}
void X11View::Paint() {
+ NOTIMPLEMENTED() << "Not sure if we need this call anymore.";
+}
+
+void X11View::PaintRect(media::VideoFrame* frame, const gfx::Rect& clip) {
// Don't bother attempting to paint if the display hasn't been set up.
- if (!display_ || !window_ || !frame_height_ || !frame_width_ || !frame_) {
+ if (!display_ || !window_ || !frame) {
return;
}
- // TODO(hclam): Paint only the updated regions.
- all_update_rects_.clear();
-
// If we have not initialized the render target then do it now.
if (!picture_)
InitPaintTarget();
@@ -83,8 +84,8 @@ void X11View::Paint() {
// Creates a XImage.
XImage image;
memset(&image, 0, sizeof(image));
- image.width = frame_width_;
- image.height = frame_height_;
+ image.width = frame->width();
+ image.height = frame->height();
image.depth = 32;
image.bits_per_pixel = 32;
image.format = ZPixmap;
@@ -100,11 +101,11 @@ void X11View::Paint() {
// Creates a pixmap and uploads from the XImage.
unsigned long pixmap = XCreatePixmap(display_, window_,
- frame_width_, frame_height_, 32);
+ frame->width(), frame->height(), 32);
GC gc = XCreateGC(display_, pixmap, 0, NULL);
- XPutImage(display_, pixmap, gc, &image, 0, 0, 0, 0,
- frame_width_, frame_height_);
+ XPutImage(display_, pixmap, gc, &image, clip.x(), clip.y(),
+ clip.x(), clip.y(), clip.width(), clip.height());
XFreeGC(display_, gc);
// Creates the picture representing the pixmap.
@@ -115,8 +116,8 @@ void X11View::Paint() {
// Composite the picture over the picture representing the window.
XRenderComposite(display_, PictOpSrc, picture, 0,
- picture_, 0, 0, 0, 0, 0, 0,
- frame_width_, frame_height_);
+ picture_, 0, 0, 0, 0, clip.x(), clip.y(),
+ clip.width(), clip.height());
XRenderFreePicture(display_, picture);
XFreePixmap(display_, pixmap);
@@ -137,14 +138,6 @@ void X11View::SetViewport(int x, int y, int width, int height) {
// NOTIMPLEMENTED();
}
-void X11View::SetHostScreenSize(int width, int height) {
- frame_width_ = width;
- frame_height_ = height;
- frame_ = NULL;
-
- XResizeWindow(display_, window_, frame_width_, frame_height_);
-}
-
void X11View::InitPaintTarget() {
// Testing XRender support.
int dummy;
@@ -163,60 +156,52 @@ void X11View::InitPaintTarget() {
CHECK(picture_) << "Backing picture not created";
}
-void X11View::HandleBeginUpdateStream(ChromotingHostMessage* msg) {
- scoped_ptr<ChromotingHostMessage> deleter(msg);
-
- // Make sure the |frame_| is initialized.
- if (!frame_) {
- media::VideoFrame::CreateFrame(media::VideoFrame::RGB32,
- frame_width_, frame_height_,
- base::TimeDelta(), base::TimeDelta(),
- &frame_);
- CHECK(frame_);
+void X11View::AllocateFrame(media::VideoFrame::Format format,
+ size_t width,
+ size_t height,
+ base::TimeDelta timestamp,
+ base::TimeDelta duration,
+ scoped_refptr<media::VideoFrame>* frame_out,
+ Task* done) {
+ // TODO(ajwong): Implement this to use the native X window rather than
+ // just a generic frame buffer.
+ media::VideoFrame::CreateFrame(media::VideoFrame::RGB32,
+ width, height,
+ base::TimeDelta(), base::TimeDelta(),
+ frame_out);
+ if (*frame_out) {
+ (*frame_out)->AddRef();
}
+ done->Run();
+ delete done;
}
-void X11View::HandleUpdateStreamPacket(ChromotingHostMessage* msg) {
- // Lazily initialize the decoder.
- SetupDecoder(msg->update_stream_packet().begin_rect().encoding());
- if (!decoder_->IsStarted()) {
- BeginDecoding(NewRunnableMethod(this, &X11View::OnPartialDecodeDone),
- NewRunnableMethod(this, &X11View::OnDecodeDone));
+void X11View::ReleaseFrame(media::VideoFrame* frame) {
+ if (frame) {
+ LOG(WARNING) << "Frame released.";
+ frame->Release();
}
-
- Decode(msg);
}
-void X11View::HandleEndUpdateStream(ChromotingHostMessage* msg) {
- scoped_ptr<ChromotingHostMessage> deleter(msg);
- EndDecoding();
-}
-
-void X11View::OnPartialDecodeDone() {
- // Decoder has produced some output so schedule a paint. We'll get a Paint()
- // call in the near future. Note that we can get UpdateStreamPacket during
- // this short period of time and we will perform decode again and the
- // information in updated rects will be lost.
- // There are several ways to solve this problem.
- // 1. Merge the updated rects and perform one paint.
- // 2. Queue the updated rects and perform two paints.
- // 3. Ignore the updated rects and always paint the full image. Since we
- // use one frame as output this will always be correct.
- // We will take (1) and simply concat the list of rectangles.
- all_update_rects_.insert(all_update_rects_.begin() +
- all_update_rects_.size(),
- update_rects_.begin(), update_rects_.end());
-
+void X11View::OnPartialFrameOutput(media::VideoFrame* frame,
+ UpdatedRects* rects,
+ Task* done) {
// TODO(hclam): Make sure we call this method on the right thread. Since
// decoder is single-threaded we don't have a problem but we better post
// a task to do the right thing.
+
+ for (UpdatedRects::iterator it = rects->begin(); it != rects->end(); ++it) {
+ PaintRect(frame, *it);
+ }
+
+ // TODO(ajwong): Shouldn't we only expose the part of the window that was
+ // damanged?
XEvent event;
event.type = Expose;
XSendEvent(display_, static_cast<int>(window_), true, ExposureMask, &event);
-}
-void X11View::OnDecodeDone() {
- // Since we do synchronous decoding here there's nothing in this method.
+ done->Run();
+ delete done;
}
} // namespace remoting
diff --git a/remoting/client/x11_view.h b/remoting/client/x11_view.h
index 3ae7f2a..5da6220 100644
--- a/remoting/client/x11_view.h
+++ b/remoting/client/x11_view.h
@@ -5,9 +5,11 @@
#ifndef REMOTING_CLIENT_X11_VIEW_H_
#define REMOTING_CLIENT_X11_VIEW_H_
-#include "base/basictypes.h"
#include "base/scoped_ptr.h"
+#include "base/task.h"
#include "media/base/video_frame.h"
+#include "remoting/base/decoder.h" // For UpdatedRects
+#include "remoting/client/frame_consumer.h"
#include "remoting/client/chromoting_view.h"
typedef unsigned long XID;
@@ -16,7 +18,7 @@ typedef struct _XDisplay Display;
namespace remoting {
// A ChromotingView implemented using X11 and XRender.
-class X11View : public ChromotingView {
+class X11View : public ChromotingView, public FrameConsumer {
public:
X11View();
virtual ~X11View();
@@ -28,17 +30,25 @@ class X11View : public ChromotingView {
virtual void SetSolidFill(uint32 color);
virtual void UnsetSolidFill();
virtual void SetViewport(int x, int y, int width, int height);
- virtual void SetHostScreenSize(int width, int height);
- virtual void HandleBeginUpdateStream(ChromotingHostMessage* msg);
- virtual void HandleUpdateStreamPacket(ChromotingHostMessage* msg);
- virtual void HandleEndUpdateStream(ChromotingHostMessage* msg);
+
+ // FrameConsumer implementation.
+ virtual void AllocateFrame(media::VideoFrame::Format format,
+ size_t width,
+ size_t height,
+ base::TimeDelta timestamp,
+ base::TimeDelta duration,
+ scoped_refptr<media::VideoFrame>* frame_out,
+ Task* done);
+ virtual void ReleaseFrame(media::VideoFrame* frame);
+ virtual void OnPartialFrameOutput(media::VideoFrame* frame,
+ UpdatedRects* rects,
+ Task* done);
Display* display() { return display_; }
private:
void InitPaintTarget();
- void OnPartialDecodeDone();
- void OnDecodeDone();
+ void PaintRect(media::VideoFrame* frame, const gfx::Rect& clip);
Display* display_;
XID window_;