diff options
Diffstat (limited to 'remoting/client/plugin')
-rw-r--r-- | remoting/client/plugin/chromoting_main.cc | 24 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_plugin.cc | 114 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_plugin.h | 53 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_plugin_test.html | 9 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_plugin_unittest.cc | 93 | ||||
-rw-r--r-- | remoting/client/plugin/chromotocol.h | 79 | ||||
-rw-r--r-- | remoting/client/plugin/client.cc | 392 | ||||
-rw-r--r-- | remoting/client/plugin/client.h | 66 | ||||
-rw-r--r-- | remoting/client/plugin/compression.cc | 114 | ||||
-rw-r--r-- | remoting/client/plugin/compression.h | 90 | ||||
-rw-r--r-- | remoting/client/plugin/decoder.h | 39 | ||||
-rw-r--r-- | remoting/client/plugin/host_connection.cc | 89 | ||||
-rw-r--r-- | remoting/client/plugin/host_connection.h | 37 |
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_ |