summaryrefslogtreecommitdiffstats
path: root/remoting/client/plugin/client.cc
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/client/plugin/client.cc')
-rw-r--r--remoting/client/plugin/client.cc392
1 files changed, 392 insertions, 0 deletions
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