summaryrefslogtreecommitdiffstats
path: root/remoting/client/plugin
diff options
context:
space:
mode:
authorgarykac@google.com <garykac@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-07 19:58:23 +0000
committergarykac@google.com <garykac@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-07 19:58:23 +0000
commitcb3b1f93130040a150e7fcc57cd4d5a75569685a (patch)
treeff93665e3c1478c61663d1107cd42dc25b31448d /remoting/client/plugin
parentb0110e822ac2b2db56d1b1542aad06da573cd544 (diff)
downloadchromium_src-cb3b1f93130040a150e7fcc57cd4d5a75569685a.zip
chromium_src-cb3b1f93130040a150e7fcc57cd4d5a75569685a.tar.gz
chromium_src-cb3b1f93130040a150e7fcc57cd4d5a75569685a.tar.bz2
Copy the (early prototype of) remoting in Chrome into the public tree.
At the moment, this is a semi-functional demo. BUG=none TEST=build/run all unittests on linux Review URL: http://codereview.chromium.org/2690003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49087 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/client/plugin')
-rw-r--r--remoting/client/plugin/chromoting_main.cc24
-rw-r--r--remoting/client/plugin/chromoting_plugin.cc114
-rw-r--r--remoting/client/plugin/chromoting_plugin.h53
-rw-r--r--remoting/client/plugin/chromoting_plugin_test.html9
-rw-r--r--remoting/client/plugin/chromoting_plugin_unittest.cc93
-rw-r--r--remoting/client/plugin/chromotocol.h79
-rw-r--r--remoting/client/plugin/client.cc392
-rw-r--r--remoting/client/plugin/client.h66
-rw-r--r--remoting/client/plugin/compression.cc114
-rw-r--r--remoting/client/plugin/compression.h90
-rw-r--r--remoting/client/plugin/decoder.h39
-rw-r--r--remoting/client/plugin/host_connection.cc89
-rw-r--r--remoting/client/plugin/host_connection.h37
13 files changed, 1199 insertions, 0 deletions
diff --git a/remoting/client/plugin/chromoting_main.cc b/remoting/client/plugin/chromoting_main.cc
new file mode 100644
index 0000000..e2974bd
--- /dev/null
+++ b/remoting/client/plugin/chromoting_main.cc
@@ -0,0 +1,24 @@
+// 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/client/plugin/chromoting_plugin.h"
+
+// Initialize general plugin info like name and description.
+// This information needs to live outside of the PepperPlugin since it can
+// be requested by the browser before the PepperPlugin has been instantiated.
+void InitializePluginInfo(pepper::PepperPlugin::Info* plugin_info) {
+ plugin_info->mime_description = remoting::kMimeType;
+ plugin_info->plugin_name = "Chromoting";
+ plugin_info->plugin_description = "Remote access for Chrome";
+}
+
+// Create the Pepper plugin instance.
+//
+// This is called in response to the NPP_New call.
+// This instantiates a PepperPlugin subclass that implements the plugin
+// specific functionality.
+pepper::PepperPlugin* CreatePlugin(NPNetscapeFuncs* browser_funcs,
+ NPP instance) {
+ return new remoting::ChromotingPlugin(browser_funcs, instance);
+}
diff --git a/remoting/client/plugin/chromoting_plugin.cc b/remoting/client/plugin/chromoting_plugin.cc
new file mode 100644
index 0000000..42b975b
--- /dev/null
+++ b/remoting/client/plugin/chromoting_plugin.cc
@@ -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.
+
+#include "remoting/client/plugin/chromoting_plugin.h"
+
+#include "remoting/client/plugin/client.h"
+
+namespace remoting {
+
+ChromotingPlugin::ChromotingPlugin(NPNetscapeFuncs* browser_funcs,
+ NPP instance)
+ : PepperPlugin(browser_funcs, instance) {
+ device_ = extensions()->acquireDevice(instance, NPPepper2DDevice);
+ client_ = new ChromotingClient(this);
+}
+
+ChromotingPlugin::~ChromotingPlugin() {
+}
+
+NPError ChromotingPlugin::New(NPMIMEType pluginType,
+ int16 argc, char* argn[], char* argv[]) {
+ // Verify the mime type and subtype
+ std::string mime(kMimeType);
+ std::string::size_type type_end = mime.find("/");
+ std::string::size_type subtype_end = mime.find(":", type_end);
+ if (strncmp(pluginType, kMimeType, subtype_end)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // Extract the URL from the arguments.
+ char* url = NULL;
+ for (int i = 0; i < argc; ++i) {
+ if (strcmp(argn[i], "src") == 0) {
+ url = argv[i];
+ break;
+ }
+ }
+
+ if (!url) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+NPError ChromotingPlugin::Destroy(NPSavedData** save) {
+ return NPERR_NO_ERROR;
+}
+
+void ChromotingPlugin::draw() {
+ NPDeviceContext2D context;
+ NPDeviceContext2DConfig config;
+ device_->initializeContext(instance(), &config, &context);
+
+ client_->draw(width_, height_, &context);
+
+ device_->flushContext(instance(), &context, NULL, NULL);
+}
+
+NPError ChromotingPlugin::SetWindow(NPWindow* window) {
+ width_ = window->width;
+ height_ = window->height;
+
+ client_->set_window();
+
+ draw();
+
+ return NPERR_NO_ERROR;
+}
+
+int16 ChromotingPlugin::HandleEvent(void* event) {
+ NPPepperEvent* npevent = static_cast<NPPepperEvent*>(event);
+ char ch;
+
+ switch (npevent->type) {
+ case NPEventType_MouseDown:
+ // Fall through
+ case NPEventType_MouseUp:
+ // Fall through
+ case NPEventType_MouseMove:
+ // Fall through
+ case NPEventType_MouseEnter:
+ // Fall through
+ case NPEventType_MouseLeave:
+ client_->handle_mouse_event(npevent);
+ break;
+ case NPEventType_MouseWheel:
+ case NPEventType_RawKeyDown:
+ break;
+ case NPEventType_KeyDown:
+ case NPEventType_KeyUp:
+ break;
+ case NPEventType_Char:
+ client_->handle_char_event(npevent);
+ break;
+ case NPEventType_Minimize:
+ case NPEventType_Focus:
+ case NPEventType_Device:
+ break;
+ }
+
+ return false;
+}
+
+NPError ChromotingPlugin::GetValue(NPPVariable variable, void* value) {
+ return NPERR_NO_ERROR;
+}
+
+NPError ChromotingPlugin::SetValue(NPNVariable variable, void* value) {
+ return NPERR_NO_ERROR;
+}
+
+} // namespace remoting
diff --git a/remoting/client/plugin/chromoting_plugin.h b/remoting/client/plugin/chromoting_plugin.h
new file mode 100644
index 0000000..e2bc78f
--- /dev/null
+++ b/remoting/client/plugin/chromoting_plugin.h
@@ -0,0 +1,53 @@
+// 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_CLIENT_PLUGIN_CHROMOTING_PLUGIN_H_
+#define REMOTING_CLIENT_PLUGIN_CHROMOTING_PLUGIN_H_
+
+#include <string>
+
+#include "remoting/client/pepper/pepper_plugin.h"
+
+namespace remoting {
+
+static const char kMimeType[]
+ = "pepper-application/x-chromoting-plugin::Chromoting";
+
+class ChromotingClient;
+
+class ChromotingPlugin : public pepper::PepperPlugin {
+ public:
+ ChromotingPlugin(NPNetscapeFuncs* browser_funcs, NPP instance);
+ virtual ~ChromotingPlugin();
+
+ int width() { return width_; }
+ int height() { return height_; }
+ NPDevice* device() { return device_; }
+
+ NPError New(NPMIMEType pluginType, int16 argc, char* argn[], char* argv[]);
+ NPError Destroy(NPSavedData** save);
+ NPError SetWindow(NPWindow* window);
+ int16 HandleEvent(void* event);
+ NPError GetValue(NPPVariable variable, void* value);
+ NPError SetValue(NPNVariable variable, void* value);
+
+ // Set up drawing context and update display.
+ void draw();
+
+ private:
+ // Size of the plugin window.
+ int width_, height_;
+
+ // Rendering device provided by browser.
+ NPDevice* device_;
+
+ // Chromoting client manager.
+ ChromotingClient* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromotingPlugin);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_CLIENT_PLUGIN_CHROMOTING_PLUGIN_H_
diff --git a/remoting/client/plugin/chromoting_plugin_test.html b/remoting/client/plugin/chromoting_plugin_test.html
new file mode 100644
index 0000000..ddc53c2
--- /dev/null
+++ b/remoting/client/plugin/chromoting_plugin_test.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>Sample Pepper Plugin</title>
+</head>
+<body>
+<object type="pepper-application/x-chromoting-plugin" width="1366" height="768"
+ src="chromotocol:garykac@chromoting.org" />
+</body>
+</html>
diff --git a/remoting/client/plugin/chromoting_plugin_unittest.cc b/remoting/client/plugin/chromoting_plugin_unittest.cc
new file mode 100644
index 0000000..68baa9c
--- /dev/null
+++ b/remoting/client/plugin/chromoting_plugin_unittest.cc
@@ -0,0 +1,93 @@
+// 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 "base/scoped_ptr.h"
+#include "remoting/client/plugin/chromoting_plugin.h"
+#include "remoting/client/pepper/fake_browser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Routine to create the PepperPlugin subclass that implements all of the
+// plugin-specific functionality.
+pepper::PepperPlugin* CreatePlugin(NPNetscapeFuncs* browser_funcs,
+ NPP instance);
+
+
+class ChromotingPluginTest : public testing::Test {
+ protected:
+
+ virtual void SetUp() {
+ // Set up the fake browser callback routines.
+ fake_browser_ = Singleton<FakeBrowser>::get();
+ NPNetscapeFuncs* browser_funcs_ = fake_browser_->GetBrowserFuncs();
+ instance_.reset(new NPP_t());
+
+ // Create the ChromotingPlugin for testing.
+ pepper::PepperPlugin* pepper_plugin;
+ pepper_plugin = CreatePlugin(browser_funcs_, instance_.get());
+ plugin_.reset(
+ reinterpret_cast<remoting::ChromotingPlugin*>(pepper_plugin));
+ }
+
+ virtual void TearDown() {
+ }
+
+ FakeBrowser* fake_browser_;
+ scoped_ptr<NPP_t> instance_;
+ scoped_ptr<remoting::ChromotingPlugin> plugin_;
+};
+
+TEST_F(ChromotingPluginTest, TestSetup) {
+ ASSERT_TRUE(plugin_->browser() != NULL);
+ ASSERT_TRUE(plugin_->extensions() != NULL);
+ ASSERT_TRUE(plugin_->instance() != NULL);
+
+ ASSERT_TRUE(plugin_->device() != NULL);
+}
+
+TEST_F(ChromotingPluginTest, TestNew) {
+ NPMIMEType mimetype =
+ const_cast<NPMIMEType>("pepper-application/x-chromoting-plugin");
+ int16 argc;
+ char* argn[4];
+ char* argv[4];
+ NPError result;
+
+ // Test 0 arguments (NULL arrays).
+ argc = 0;
+ result = plugin_->New(mimetype, argc, NULL, NULL);
+ ASSERT_EQ(NPERR_GENERIC_ERROR, result);
+
+ // Test 0 arguments.
+ argc = 0;
+ result = plugin_->New(mimetype, argc, argn, argv);
+ ASSERT_EQ(NPERR_GENERIC_ERROR, result);
+
+ // Test 1 argument (missing "src").
+ argc = 1;
+ argn[0] = const_cast<char*>("noturl");
+ argv[0] = const_cast<char*>("random.value");
+ result = plugin_->New(mimetype, argc, argn, argv);
+ ASSERT_EQ(NPERR_GENERIC_ERROR, result);
+
+ // Test "src" argument.
+ argc = 1;
+ argn[0] = const_cast<char*>("src");
+ argv[0] = const_cast<char*>("chromotocol:name@chromoting.org");
+ result = plugin_->New(mimetype, argc, argn, argv);
+ ASSERT_EQ(NPERR_NO_ERROR, result);
+}
+
+
+static uint32 get_pixel(uint32* pixels, int stride, int x, int y) {
+ return pixels[((x) + ((y) * (stride >> 2)))];
+}
+
+TEST_F(ChromotingPluginTest, TestSetWindow) {
+ NPWindow* window = fake_browser_->GetWindow();
+ NPError result;
+
+ result = plugin_->SetWindow(window);
+ ASSERT_EQ(NPERR_NO_ERROR, result);
+}
diff --git a/remoting/client/plugin/chromotocol.h b/remoting/client/plugin/chromotocol.h
new file mode 100644
index 0000000..9a35351
--- /dev/null
+++ b/remoting/client/plugin/chromotocol.h
@@ -0,0 +1,79 @@
+// 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_CLIENT_PLUGIN_CHROMOTOCOL_H_
+#define REMOTING_CLIENT_PLUGIN_CHROMOTOCOL_H_
+
+#include "base/scoped_ptr.h"
+
+namespace remoting {
+
+class HostConnection;
+
+enum ControlMessage {
+ MessageInit,
+ MessageUpdate,
+ MessageMouse,
+};
+
+struct InitMessage {
+ int message;
+ int compression;
+ int width;
+ int height;
+};
+
+struct MouseMessage {
+ int message;
+ int x, y;
+ int flags;
+};
+
+enum MouseFlag {
+ LeftDown = 1 << 1,
+ LeftUp = 1 << 2,
+ RightDown = 1 << 3,
+ RightUp = 1 << 4
+};
+
+struct UpdateMessage {
+ int message;
+ int num_diffs;
+ int compression;
+ int compressed_size;
+};
+
+enum ImageFormat {
+ FormatRaw,
+ FormatJpeg, // Not used
+ FormatPng, // Not used
+ FormatZlib, // Not used
+ FormatVp8,
+};
+
+enum Compression {
+ CompressionNone,
+ CompressionZlib,
+};
+
+struct BinaryImageHeader {
+ BinaryImageHeader()
+ : format(FormatRaw), x(0), y(0), width(0), height(0), size(0) {}
+
+ ImageFormat format;
+ int x;
+ int y;
+ int width;
+ int height;
+ int size;
+};
+
+struct BinaryImage {
+ BinaryImageHeader header;
+ scoped_ptr<char> data;
+};
+
+} // namespace remoting
+
+#endif // REMOTING_CLIENT_PLUGIN_CHROMOTOCOL_H_
diff --git a/remoting/client/plugin/client.cc b/remoting/client/plugin/client.cc
new file mode 100644
index 0000000..5de4b5f
--- /dev/null
+++ b/remoting/client/plugin/client.cc
@@ -0,0 +1,392 @@
+// 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/client/plugin/client.h"
+
+#include <string>
+#include <iostream> // TODO(garykac): Remove this or replace with debug log.
+
+#include "base/logging.h"
+#include "media/base/yuv_convert.h"
+#include "remoting/client/plugin/chromoting_plugin.h"
+#include "remoting/client/plugin/chromotocol.h"
+#include "remoting/client/plugin/compression.h"
+#include "remoting/client/plugin/decoder.h"
+#include "remoting/client/plugin/host_connection.h"
+
+namespace remoting {
+
+ChromotingClient::ChromotingClient(ChromotingPlugin* plugin) {
+ plugin_ = plugin;
+ host_ = new HostConnection();
+ verbose_ = true;
+}
+
+ChromotingClient::~ChromotingClient() {
+}
+
+void ChromotingClient::hexdump(void* ptr, int buflen) {
+ unsigned char* buf = static_cast<unsigned char*>(ptr);
+ int i, j;
+ for (int i = 0; i < buflen; i += 16) {
+ printf("%06x: ", i);
+ for (int j = 0; j < 16; j ++)
+ if ((i + j) < buflen)
+ printf("%02x ", buf[i + j]);
+ else
+ printf(" ");
+ printf(" ");
+ for (int j = 0; j < 16; j++)
+ if ((i + j) < buflen)
+ printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
+ printf("\n");
+ }
+}
+
+void ChromotingClient::merge_image(BinaryImageHeader* header, char* data) {
+ // Merge this image into the current image.
+ // Src bitmap starts at lower left.
+ int bytes_per_pixel = 3;
+ uint8* src_bitmap = reinterpret_cast<uint8*>(data);
+ int src_bytes_per_row = header->width * bytes_per_pixel;
+
+ // Dst bitmap starts at lower left.
+ uint8* dst_bitmap = reinterpret_cast<uint8*>(screen_->data.get());
+ dst_bitmap += ((header->y * screen_->header.width)
+ + header->x) * bytes_per_pixel;
+ int dst_bytes_per_row = screen_->header.width * bytes_per_pixel;
+
+ for (int y = 0; y < header->height; ++y) {
+ memcpy(dst_bitmap, src_bitmap, src_bytes_per_row);
+
+ src_bitmap += src_bytes_per_row;
+ dst_bitmap += dst_bytes_per_row;
+ }
+}
+
+// Draw the current image into the given device context.
+void ChromotingClient::draw(int width, int height, NPDeviceContext2D* context) {
+ if (screen_ != NULL) {
+ int max_width = width;
+ if (max_width > screen_->header.width) {
+ max_width = screen_->header.width;
+ }
+ int max_height = height;
+ if (max_height > screen_->header.height) {
+ max_height = screen_->header.height;
+ }
+
+ // Src bitmap starts at lower left.
+ int bytes_per_pixel = 3;
+ int src_bytes_per_row = screen_->header.width * bytes_per_pixel;
+ uint8* src_bitmap = reinterpret_cast<uint8*>(screen_->data.get());
+ src_bitmap += ((max_height - 1) * src_bytes_per_row);
+ uint8* src_row_start = reinterpret_cast<uint8*>(src_bitmap);
+
+ // Dst bitmap (window) starts at upper left.
+ uint32* dst_bitmap = static_cast<uint32*>(context->region);
+ uint8* dst_row_start = reinterpret_cast<uint8*>(dst_bitmap);
+ int dst_bytes_per_row = context->stride;
+
+ for (int y = 0; y < max_height; ++y) {
+ for (int x = 0; x < max_width; ++x) {
+ uint8 b = (*src_bitmap++ & 0xff);
+ uint8 g = (*src_bitmap++ & 0xff);
+ uint8 r = (*src_bitmap++ & 0xff);
+ uint8 alpha = 0xff;
+ *dst_bitmap++ = ((alpha << 24) | (r << 16) | (g << 8) | b);
+ }
+ src_row_start -= src_bytes_per_row;
+ src_bitmap = src_row_start;
+ dst_row_start += dst_bytes_per_row;
+ dst_bitmap = reinterpret_cast<uint32*>(dst_row_start);
+ }
+ }
+}
+
+bool ChromotingClient::connect_to_host(const std::string ip) {
+ if (!host_->connect(ip.c_str())) {
+ return false;
+ }
+
+ // Process init command.
+ InitMessage init_message;
+ host_->read_data(reinterpret_cast<char*>(&init_message),
+ sizeof(init_message));
+ if (verbose_) {
+ std::cout << "Received message " << init_message.message << std::endl;
+ }
+ if (init_message.message != MessageInit) {
+ std::cout << "Expected MessageInit" << std::endl;
+ return false;
+ }
+ if (verbose_) {
+ std::cout << "Compression: " << init_message.compression << std::endl;
+ std::cout << "Width x height: " << init_message.width << " x "
+ << init_message.height << std::endl;
+ }
+
+ screen_.reset(new BinaryImage());
+ if (!read_image(screen_.get())) {
+ return false;
+ }
+
+ plugin_->draw();
+ return true;
+}
+
+void ChromotingClient::print_host_ip_prompt() {
+ std::cout << "IP address of host machine: ";
+}
+
+// This is called whenever the window changes geometry.
+// Currently, we only worry about the first call so we can display our
+// login prompt.
+void ChromotingClient::set_window() {
+ if (!host_->connected()) {
+ print_host_ip_prompt();
+ std::cout.flush();
+ }
+}
+
+// Process char input.
+void ChromotingClient::handle_char_event(NPPepperEvent* npevent) {
+ if (!host_->connected()) {
+ handle_login_char(static_cast<char>(npevent->u.character.text[0]));
+ }
+}
+
+// Process char input before the connection to the host has been made.
+// This currently reads the IP address of the host but will eventually
+// be changed to read GAIA login credentials.
+// Later this will be removed once we have an appropriate web interface for
+// discovering hosts.
+void ChromotingClient::handle_login_char(char ch) {
+ if (ch == 0x0d) {
+ std::cout << std::endl;
+ if (host_ip_address_.length() == 0) {
+ host_ip_address_ = "172.31.11.205";
+ }
+ if (!connect_to_host(host_ip_address_)) {
+ host_ip_address_ = "";
+ std::cout << "Unable to connect to host!" << std::endl;
+ print_host_ip_prompt();
+ } else {
+ std::cout << "Connected to " << host_ip_address_ << std::endl;
+ }
+ } else {
+ host_ip_address_ += ch;
+ std::cout << ch;
+ }
+ std::cout.flush();
+}
+
+// Process the Pepper mouse event.
+void ChromotingClient::handle_mouse_event(NPPepperEvent* npevent) {
+ if (host_->connected()) {
+ send_mouse_message(npevent);
+ if (handle_update_message()) {
+ plugin_->draw();
+ }
+ }
+}
+
+// Pass the given Pepper mouse event along to the host.
+void ChromotingClient::send_mouse_message(NPPepperEvent* event) {
+ NPMouseEvent* mouse_event = &event->u.mouse;
+ MouseMessage mouse_msg;
+
+ mouse_msg.message = MessageMouse;
+ mouse_msg.x = mouse_event->x;
+ mouse_msg.y = mouse_event->y;
+
+ mouse_msg.flags = 0;
+ int32 type = event->type;
+ int32 button = mouse_event->button;
+ if (type == NPEventType_MouseDown) {
+ if (button == NPMouseButton_Left) {
+ mouse_msg.flags |= LeftDown;
+ } else if (button == NPMouseButton_Right) {
+ mouse_msg.flags |= RightDown;
+ }
+ } else if (type == NPEventType_MouseUp) {
+ if (button == NPMouseButton_Left) {
+ mouse_msg.flags |= LeftUp;
+ } else if (button == NPMouseButton_Right) {
+ mouse_msg.flags |= RightUp;
+ }
+ }
+ host_->write_data((const char*)&mouse_msg, sizeof(mouse_msg));
+}
+
+// Process the pending update command from the host.
+// Return true if the screen image has been updated.
+bool ChromotingClient::handle_update_message() {
+ UpdateMessage update_message;
+ int result = host_->read_data(reinterpret_cast<char*>(&update_message),
+ sizeof(update_message));
+ if (!result) {
+ std::cout << "Failed to get update command" << std::endl;
+ return false;
+ }
+
+ if (update_message.message != MessageUpdate) {
+ std::cout << "Expected MessageUpdate" << std::endl;
+ return false;
+ }
+
+ if (verbose_) {
+ std::cout << "message: " << update_message.message << std::endl;
+ }
+
+ if (update_message.compression == CompressionZlib) {
+ // Read all data.
+ ZDecompressor decomp;
+ char buffer[4096];
+ int size = update_message.compressed_size;
+ while (size > 0) {
+ // Determine how much we should read from network.
+ int read = std::min(static_cast<int>(sizeof(buffer)), size);
+ result = host_->read_data(buffer, read);
+ decomp.Write(buffer, read);
+ size -= read;
+ }
+ decomp.Flush();
+
+ // Decompress raw image data and break into individual images.
+ char* raw_buffer = decomp.GetRawData();
+ int raw_size = decomp.GetRawSize();
+ int read = 0;
+ BinaryImageHeader header;
+ while (read < raw_size) {
+ memcpy(&header, raw_buffer, sizeof(BinaryImageHeader));
+ if (!check_image_header(&header)) {
+ return false;
+ }
+ read += sizeof(BinaryImageHeader);
+ raw_buffer += sizeof(BinaryImageHeader);
+
+ // Merge this image fragment into the screen bitmap.
+ merge_image(&header, raw_buffer);
+
+ read += header.size;
+ raw_buffer += header.size;
+ }
+ } else if (update_message.compression == CompressionNone) {
+ printf("compressionNone\n");
+ for (int i = 0; i < update_message.num_diffs; i++) {
+ BinaryImage* image = new BinaryImage();
+ read_image(image);
+
+ // Merge this image update into the screen image.
+ merge_image(&image->header, image->data.get());
+
+ delete image;
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+// Check the validity of the image header.
+bool ChromotingClient::check_image_header(BinaryImageHeader* header) {
+ if (header == NULL) {
+ std::cout << "Invalid image" << std::endl;
+ return false;
+ }
+
+ if (header->format != FormatRaw && header->format != FormatVp8) {
+ std::cout << "Wrong image format : " << header->format << std::endl;
+ return false;
+ }
+
+ if (verbose_) {
+ std::cout << "Image:" << std::endl;
+ std::cout << " Format " << header->format << std::endl;
+ std::cout << " X,Y " << header->x << ", "
+ << header->y << std::endl;
+ std::cout << " WxH " << header->width << " x "
+ << header->height << std::endl;
+ std::cout << " Size " << header->size << std::endl;
+ }
+
+ return true;
+}
+
+// Read an image from the host and store it in the given BinaryImage.
+bool ChromotingClient::read_image(BinaryImage* image) {
+ int result = host_->read_data(reinterpret_cast<char*>(&image->header),
+ sizeof(image->header));
+ if (!result) {
+ std::cout << "Failed to receive image header" << std::endl;
+ return false;
+ }
+
+ if (!check_image_header(&image->header)) {
+ return false;
+ }
+
+ char* raw_data = new char[image->header.size];
+ result = host_->read_data(raw_data, image->header.size);
+ if (!result) {
+ std::cout << "Failed to receive image data" << std::endl;
+ return false;
+ }
+
+ if (image->header.format == FormatRaw) {
+ // Raw image - all we need to do is load the data, so we're done.
+ image->data.reset(raw_data);
+ return true;
+ } else if (image->header.format == FormatVp8) {
+ return false;
+ // TODO(hclam): Enable this block of code when we have VP8.
+#if 0
+ // Vp8 encoded - need to convert YUV image data to RGB.
+ static VP8VideoDecoder decoder;
+ uint8* planes[3];
+ int strides[3];
+ printf("decoder.DecodeFrame\n");
+ if (!decoder.DecodeFrame(raw_data, image->header.size)) {
+ std::cout << "Unable to decode frame" << std::endl;
+ return false;
+ }
+ printf("decoder.GetDecodedFrame\n");
+ if (!decoder.GetDecodedFrame(reinterpret_cast<char**>(planes), strides)) {
+ std::cout << "Unable to get decoded frame" << std::endl;
+ return false;
+ }
+ printf("width = %d\n", image->header.width);
+ for (int i=0; i<3; i++) {
+ printf("stride[%d] = %d\n", i, strides[0]);
+ }
+
+ // Convert YUV to RGB.
+ int width = image->header.width;
+ int height = image->header.height;
+ char* rgb_data = new char[width * height * sizeof(int32)];
+ printf("ConvertYUVToRGB32\n");
+ ConvertYUVToRGB32(planes[0], planes[1], planes[2],
+ reinterpret_cast<uint8*>(rgb_data),
+ width,
+ image->header.height,
+ width, // y stride,
+ width / 2, // uv stride,
+ width * sizeof(int32), // rgb stride
+ media::YV12);
+ printf("conversion done\n");
+
+ image->data.reset(rgb_data);
+
+ // Raw YUV data is no longer needed.
+ delete raw_data;
+ return true;
+#endif
+ }
+
+ return false;
+}
+
+} // namespace remoting
diff --git a/remoting/client/plugin/client.h b/remoting/client/plugin/client.h
new file mode 100644
index 0000000..8772a6e
--- /dev/null
+++ b/remoting/client/plugin/client.h
@@ -0,0 +1,66 @@
+// 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_CLIENT_PLUGIN_CLIENT_H_
+#define REMOTING_CLIENT_PLUGIN_CLIENT_H_
+
+#include <string>
+
+#include "base/scoped_ptr.h"
+#include "remoting/client/plugin/chromoting_plugin.h"
+
+namespace remoting {
+
+class BinaryImage;
+class BinaryImageHeader;
+class HostConnection;
+
+class ChromotingClient {
+ public:
+ explicit ChromotingClient(ChromotingPlugin* plugin);
+ virtual ~ChromotingClient();
+
+ void hexdump(void *ptr, int buflen);
+
+ void merge_image(BinaryImageHeader* header, char* data);
+ void draw(int width, int height, NPDeviceContext2D* context);
+
+ bool connect_to_host(const std::string ip);
+ void print_host_ip_prompt();
+
+ void set_window();
+
+ void handle_char_event(NPPepperEvent* npevent);
+ void handle_login_char(char ch);
+
+ void handle_mouse_event(NPPepperEvent* npevent);
+ void send_mouse_message(NPPepperEvent* event);
+
+ bool handle_update_message();
+
+ bool check_image_header(BinaryImageHeader* header);
+ bool read_image(BinaryImage* image);
+
+ private:
+ // Pepper plugin (communicate with browser).
+ ChromotingPlugin* plugin_;
+
+ // Network connection (communicate with remote host machine).
+ HostConnection* host_;
+
+ // IP address of remote host machine.
+ std::string host_ip_address_;
+
+ // Screen bitmap image.
+ scoped_ptr<BinaryImage> screen_;
+
+ // Display extended output messages (for debugging).
+ bool verbose_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromotingClient);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_CLIENT_PLUGIN_CLIENT_H_
diff --git a/remoting/client/plugin/compression.cc b/remoting/client/plugin/compression.cc
new file mode 100644
index 0000000..4e89727
--- /dev/null
+++ b/remoting/client/plugin/compression.cc
@@ -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.
+
+#include "remoting/client/plugin/compression.h"
+
+#include <assert.h>
+
+namespace remoting {
+
+static const int kZLIB_CHUNK = 256 * 1024;
+
+ZCompressor::ZCompressor() {
+ stream_.zalloc = Z_NULL;
+ stream_.zfree = Z_NULL;
+ stream_.opaque = Z_NULL;
+
+ deflateInit(&stream_, Z_BEST_SPEED);
+}
+
+void ZCompressor::WriteInternal(char* buffer, int size, int flush) {
+ stream_.avail_in = size;
+ stream_.next_in = reinterpret_cast<Bytef*>(buffer);
+
+ // Expand the internal buffer.
+ while (true) {
+ int last_size = buffer_.size();
+ buffer_.resize(last_size + kZLIB_CHUNK);
+ stream_.avail_out = kZLIB_CHUNK;
+ stream_.next_out = reinterpret_cast<Bytef*>(&buffer_[last_size]);
+ int ret = deflate(&stream_, flush);
+ assert(ret != Z_STREAM_ERROR);
+
+ // Shrink the size of the vector. It doesn't alter the capacity.
+ int compressed = kZLIB_CHUNK - stream_.avail_out;
+ buffer_.resize(last_size + compressed);
+ if (!compressed)
+ break;
+ }
+}
+
+void ZCompressor::Write(char* buffer, int size) {
+ WriteInternal(buffer, size, Z_NO_FLUSH);
+}
+
+void ZCompressor::Flush() {
+ WriteInternal(NULL, 0, Z_FINISH);
+ deflateEnd(&stream_);
+}
+
+int ZCompressor::GetCompressedSize() {
+ return buffer_.size();
+}
+
+char* ZCompressor::GetCompressedData() {
+ return &buffer_[0];
+}
+
+int ZCompressor::GetRawSize() {
+ // I don't care about this.
+ return 0;
+}
+
+ZDecompressor::ZDecompressor() {
+ stream_.zalloc = Z_NULL;
+ stream_.zfree = Z_NULL;
+ stream_.opaque = Z_NULL;
+ stream_.avail_in = 0;
+ stream_.next_in = Z_NULL;
+
+ inflateInit(&stream_);
+}
+
+void ZDecompressor::WriteInternal(char* buffer, int size, int flush) {
+ stream_.avail_in = size;
+ stream_.next_in = reinterpret_cast<Bytef*>(buffer);
+
+ while (true) {
+ int last_size = buffer_.size();
+ buffer_.resize(last_size + kZLIB_CHUNK);
+ stream_.avail_out = kZLIB_CHUNK;
+ stream_.next_out = reinterpret_cast<Bytef*>(&buffer_[last_size]);
+ int ret = inflate(&stream_, flush);
+ assert(ret != Z_STREAM_ERROR);
+
+ // Shrink the size of the vector. It doesn't alter the capacity.
+ int decompressed = kZLIB_CHUNK - stream_.avail_out;
+ buffer_.resize(last_size + decompressed);
+ if (!decompressed)
+ break;
+ }
+}
+
+void ZDecompressor::Write(char* buffer, int size) {
+ WriteInternal(buffer, size, Z_NO_FLUSH);
+}
+
+void ZDecompressor::Flush() {
+ inflateEnd(&stream_);
+}
+
+char* ZDecompressor::GetRawData() {
+ return &buffer_[0];
+}
+
+int ZDecompressor::GetRawSize() {
+ return buffer_.size();
+}
+
+int ZDecompressor::GetCompressedSize() {
+ // I don't care.
+}
+
+} // namespace remoting
diff --git a/remoting/client/plugin/compression.h b/remoting/client/plugin/compression.h
new file mode 100644
index 0000000..8d4ee49
--- /dev/null
+++ b/remoting/client/plugin/compression.h
@@ -0,0 +1,90 @@
+// 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_CLIENT_PLUGIN_COMPRESSION_H_
+#define REMOTING_CLIENT_PLUGIN_COMPRESSION_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+#if defined(USE_SYSTEM_ZLIB)
+#include <zlib.h>
+#else
+#include "third_party/zlib/zlib.h"
+#endif
+
+namespace remoting {
+
+class Compressor {
+ public:
+ Compressor() {}
+ virtual ~Compressor() {}
+
+ virtual void Write(char* buffer, int size) = 0;
+ virtual void Flush() = 0;
+ virtual int GetCompressedSize() = 0;
+ virtual char* GetCompressedData() = 0;
+ virtual int GetRawSize() = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(Compressor);
+};
+
+class Decompressor {
+ public:
+ Decompressor() {}
+ virtual ~Decompressor() {}
+
+ virtual void Write(char* buffer, int size) = 0;
+ virtual void Flush() = 0;
+ virtual char* GetRawData() = 0;
+ virtual int GetRawSize() = 0;
+ virtual int GetCompressedSize() = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(Decompressor);
+};
+
+class ZCompressor : public Compressor {
+ public:
+ ZCompressor();
+ virtual ~ZCompressor() {}
+
+ virtual void Write(char* buffer, int size);
+ virtual void Flush();
+ virtual int GetCompressedSize();
+ virtual char* GetCompressedData();
+ virtual int GetRawSize();
+
+ private:
+ void WriteInternal(char* buffer, int size, int flush);
+
+ std::vector<char> buffer_;
+ z_stream stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(ZCompressor);
+};
+
+class ZDecompressor : public Decompressor {
+ public:
+ ZDecompressor();
+ virtual ~ZDecompressor() {}
+
+ virtual void Write(char* buffer, int size);
+ virtual void Flush();
+ virtual char* GetRawData();
+ virtual int GetRawSize();
+ virtual int GetCompressedSize();
+
+ private:
+ void WriteInternal(char* buffer, int size, int flush);
+
+ std::vector<char> buffer_;
+ z_stream stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(ZDecompressor);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_CLIENT_PLUGIN_COMPRESSION_H_
diff --git a/remoting/client/plugin/decoder.h b/remoting/client/plugin/decoder.h
new file mode 100644
index 0000000..7c82723
--- /dev/null
+++ b/remoting/client/plugin/decoder.h
@@ -0,0 +1,39 @@
+// 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.
+
+// TODO(hclam): Enable this when we have VP8.
+// extern "C" {
+// #include "remoting/demo/third_party/on2/include/on2_decoder.h"
+// #include "remoting/demo/third_party/on2/include/vp8dx.h"
+// }
+
+class Stream;
+
+class VideoDecoder {
+ public:
+ virtual ~VideoDecoder() {}
+ virtual bool DecodeFrame(char* buffer, int size) = 0;
+ virtual bool GetDecodedFrame(char** planes, int* strides) = 0;
+ virtual bool IsInitialized() = 0;
+ virtual int GetWidth() = 0;
+ virtual int GetHeight() = 0;
+ virtual int GetFormat() = 0;
+};
+
+// TODO(hclam): Enable this when we have VP8.
+// class VP8VideoDecoder {
+// public:
+// VP8VideoDecoder();
+// virtual bool DecodeFrame(char* buffer, int size);
+// virtual bool GetDecodedFrame(char** planes, int* strides);
+// virtual bool IsInitialized();
+// virtual int GetWidth();
+// virtual int GetHeight();
+// virtual int GetFormat();
+
+// private:
+// on2_codec_ctx_t codec_;
+// on2_codec_iter_t iter_;
+// bool first_frame_;
+// };
diff --git a/remoting/client/plugin/host_connection.cc b/remoting/client/plugin/host_connection.cc
new file mode 100644
index 0000000..fff9526
--- /dev/null
+++ b/remoting/client/plugin/host_connection.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/client/plugin/host_connection.h"
+
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include <iostream> // TODO(garykac): Replace or remove debug output.
+
+#include "remoting/client/plugin/chromoting_plugin.h"
+
+namespace remoting {
+
+HostConnection::HostConnection() {
+ connected_ = false;
+}
+
+HostConnection::~HostConnection() {
+}
+
+bool HostConnection::connect(const char* ip_address) {
+ std::cout << "Attempting to connect to " << ip_address << std::endl;
+
+ sock_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock_ < 0) {
+ std::cout << "Can't open socket" << std::endl;
+ return false;
+ }
+
+ hostent* server = gethostbyname(ip_address);
+ if (!server) {
+ std::cout << "Can't resolve address" << std::endl;
+ return false;
+ }
+
+ sockaddr_in server_address;
+ memset(&server_address, 0, sizeof(server_address));
+ server_address.sin_family = AF_INET;
+ memcpy(&server_address.sin_addr.s_addr, server->h_addr, server->h_length);
+ server_address.sin_port = htons(4000);
+
+ if (::connect(sock_, reinterpret_cast<sockaddr*>(&server_address),
+ sizeof(server_address)) < 0) {
+ std::cout << "Cannot connect to server" << std::endl;
+ return false;
+ }
+
+ connected_ = true;
+ return true;
+}
+
+// Read data from a socket to a buffer.
+bool HostConnection::read_data(char* buffer, int num_bytes) {
+ if (!connected_) {
+ return false;
+ }
+
+ while (num_bytes > 0) {
+ int num_bytes_read = read(sock_, buffer, num_bytes);
+ if (num_bytes_read <= 0) {
+ return false;
+ }
+ buffer += num_bytes_read;
+ num_bytes -= num_bytes_read;
+ }
+ return true;
+}
+
+// Write data from a buffer to a socket.
+bool HostConnection::write_data(const char* buffer, int num_bytes) {
+ if (!connected_) {
+ return false;
+ }
+
+ while (num_bytes > 0) {
+ int num_bytes_written = write(sock_, buffer, num_bytes);
+ if (num_bytes_written <= 0) {
+ return false;
+ }
+ num_bytes -= num_bytes_written;
+ buffer += num_bytes_written;
+ }
+ return true;
+}
+
+} // namespace remoting
diff --git a/remoting/client/plugin/host_connection.h b/remoting/client/plugin/host_connection.h
new file mode 100644
index 0000000..7dc4004
--- /dev/null
+++ b/remoting/client/plugin/host_connection.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_CLIENT_PLUGIN_HOST_CONNECTION_H_
+#define REMOTING_CLIENT_PLUGIN_HOST_CONNECTION_H_
+
+#include "base/basictypes.h"
+
+namespace remoting {
+
+class HostConnection {
+ public:
+ HostConnection();
+ virtual ~HostConnection();
+
+ bool connect(const char* ip_address);
+ bool connected() {
+ return connected_;
+ }
+
+ bool read_data(char *buffer, int num_bytes);
+ bool write_data(const char* buffer, int num_bytes);
+
+ private:
+ // True if we have a valid connection.
+ bool connected_;
+
+ // The socket for the connection.
+ int sock_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostConnection);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_CLIENT_PLUGIN_HOST_CONNECTION_H_