| Index: remoting/client/plugin/client.cc
|
| ===================================================================
|
| --- remoting/client/plugin/client.cc (revision 0)
|
| +++ remoting/client/plugin/client.cc (revision 0)
|
| @@ -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
|
|
|
| Property changes on: remoting/client/plugin/client.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|