OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "remoting/client/plugin/client.h" |
| 6 |
| 7 #include <string> |
| 8 #include <iostream> // TODO(garykac): Remove this or replace with debug log. |
| 9 |
| 10 #include "base/logging.h" |
| 11 #include "media/base/yuv_convert.h" |
| 12 #include "remoting/client/plugin/chromoting_plugin.h" |
| 13 #include "remoting/client/plugin/chromotocol.h" |
| 14 #include "remoting/client/plugin/compression.h" |
| 15 #include "remoting/client/plugin/decoder.h" |
| 16 #include "remoting/client/plugin/host_connection.h" |
| 17 |
| 18 namespace remoting { |
| 19 |
| 20 ChromotingClient::ChromotingClient(ChromotingPlugin* plugin) { |
| 21 plugin_ = plugin; |
| 22 host_ = new HostConnection(); |
| 23 verbose_ = true; |
| 24 } |
| 25 |
| 26 ChromotingClient::~ChromotingClient() { |
| 27 } |
| 28 |
| 29 void ChromotingClient::hexdump(void* ptr, int buflen) { |
| 30 unsigned char* buf = static_cast<unsigned char*>(ptr); |
| 31 int i, j; |
| 32 for (int i = 0; i < buflen; i += 16) { |
| 33 printf("%06x: ", i); |
| 34 for (int j = 0; j < 16; j ++) |
| 35 if ((i + j) < buflen) |
| 36 printf("%02x ", buf[i + j]); |
| 37 else |
| 38 printf(" "); |
| 39 printf(" "); |
| 40 for (int j = 0; j < 16; j++) |
| 41 if ((i + j) < buflen) |
| 42 printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.'); |
| 43 printf("\n"); |
| 44 } |
| 45 } |
| 46 |
| 47 void ChromotingClient::merge_image(BinaryImageHeader* header, char* data) { |
| 48 // Merge this image into the current image. |
| 49 // Src bitmap starts at lower left. |
| 50 int bytes_per_pixel = 3; |
| 51 uint8* src_bitmap = reinterpret_cast<uint8*>(data); |
| 52 int src_bytes_per_row = header->width * bytes_per_pixel; |
| 53 |
| 54 // Dst bitmap starts at lower left. |
| 55 uint8* dst_bitmap = reinterpret_cast<uint8*>(screen_->data.get()); |
| 56 dst_bitmap += ((header->y * screen_->header.width) |
| 57 + header->x) * bytes_per_pixel; |
| 58 int dst_bytes_per_row = screen_->header.width * bytes_per_pixel; |
| 59 |
| 60 for (int y = 0; y < header->height; ++y) { |
| 61 memcpy(dst_bitmap, src_bitmap, src_bytes_per_row); |
| 62 |
| 63 src_bitmap += src_bytes_per_row; |
| 64 dst_bitmap += dst_bytes_per_row; |
| 65 } |
| 66 } |
| 67 |
| 68 // Draw the current image into the given device context. |
| 69 void ChromotingClient::draw(int width, int height, NPDeviceContext2D* context) { |
| 70 if (screen_ != NULL) { |
| 71 int max_width = width; |
| 72 if (max_width > screen_->header.width) { |
| 73 max_width = screen_->header.width; |
| 74 } |
| 75 int max_height = height; |
| 76 if (max_height > screen_->header.height) { |
| 77 max_height = screen_->header.height; |
| 78 } |
| 79 |
| 80 // Src bitmap starts at lower left. |
| 81 int bytes_per_pixel = 3; |
| 82 int src_bytes_per_row = screen_->header.width * bytes_per_pixel; |
| 83 uint8* src_bitmap = reinterpret_cast<uint8*>(screen_->data.get()); |
| 84 src_bitmap += ((max_height - 1) * src_bytes_per_row); |
| 85 uint8* src_row_start = reinterpret_cast<uint8*>(src_bitmap); |
| 86 |
| 87 // Dst bitmap (window) starts at upper left. |
| 88 uint32* dst_bitmap = static_cast<uint32*>(context->region); |
| 89 uint8* dst_row_start = reinterpret_cast<uint8*>(dst_bitmap); |
| 90 int dst_bytes_per_row = context->stride; |
| 91 |
| 92 for (int y = 0; y < max_height; ++y) { |
| 93 for (int x = 0; x < max_width; ++x) { |
| 94 uint8 b = (*src_bitmap++ & 0xff); |
| 95 uint8 g = (*src_bitmap++ & 0xff); |
| 96 uint8 r = (*src_bitmap++ & 0xff); |
| 97 uint8 alpha = 0xff; |
| 98 *dst_bitmap++ = ((alpha << 24) | (r << 16) | (g << 8) | b); |
| 99 } |
| 100 src_row_start -= src_bytes_per_row; |
| 101 src_bitmap = src_row_start; |
| 102 dst_row_start += dst_bytes_per_row; |
| 103 dst_bitmap = reinterpret_cast<uint32*>(dst_row_start); |
| 104 } |
| 105 } |
| 106 } |
| 107 |
| 108 bool ChromotingClient::connect_to_host(const std::string ip) { |
| 109 if (!host_->connect(ip.c_str())) { |
| 110 return false; |
| 111 } |
| 112 |
| 113 // Process init command. |
| 114 InitMessage init_message; |
| 115 host_->read_data(reinterpret_cast<char*>(&init_message), |
| 116 sizeof(init_message)); |
| 117 if (verbose_) { |
| 118 std::cout << "Received message " << init_message.message << std::endl; |
| 119 } |
| 120 if (init_message.message != MessageInit) { |
| 121 std::cout << "Expected MessageInit" << std::endl; |
| 122 return false; |
| 123 } |
| 124 if (verbose_) { |
| 125 std::cout << "Compression: " << init_message.compression << std::endl; |
| 126 std::cout << "Width x height: " << init_message.width << " x " |
| 127 << init_message.height << std::endl; |
| 128 } |
| 129 |
| 130 screen_.reset(new BinaryImage()); |
| 131 if (!read_image(screen_.get())) { |
| 132 return false; |
| 133 } |
| 134 |
| 135 plugin_->draw(); |
| 136 return true; |
| 137 } |
| 138 |
| 139 void ChromotingClient::print_host_ip_prompt() { |
| 140 std::cout << "IP address of host machine: "; |
| 141 } |
| 142 |
| 143 // This is called whenever the window changes geometry. |
| 144 // Currently, we only worry about the first call so we can display our |
| 145 // login prompt. |
| 146 void ChromotingClient::set_window() { |
| 147 if (!host_->connected()) { |
| 148 print_host_ip_prompt(); |
| 149 std::cout.flush(); |
| 150 } |
| 151 } |
| 152 |
| 153 // Process char input. |
| 154 void ChromotingClient::handle_char_event(NPPepperEvent* npevent) { |
| 155 if (!host_->connected()) { |
| 156 handle_login_char(static_cast<char>(npevent->u.character.text[0])); |
| 157 } |
| 158 } |
| 159 |
| 160 // Process char input before the connection to the host has been made. |
| 161 // This currently reads the IP address of the host but will eventually |
| 162 // be changed to read GAIA login credentials. |
| 163 // Later this will be removed once we have an appropriate web interface for |
| 164 // discovering hosts. |
| 165 void ChromotingClient::handle_login_char(char ch) { |
| 166 if (ch == 0x0d) { |
| 167 std::cout << std::endl; |
| 168 if (host_ip_address_.length() == 0) { |
| 169 host_ip_address_ = "172.31.11.205"; |
| 170 } |
| 171 if (!connect_to_host(host_ip_address_)) { |
| 172 host_ip_address_ = ""; |
| 173 std::cout << "Unable to connect to host!" << std::endl; |
| 174 print_host_ip_prompt(); |
| 175 } else { |
| 176 std::cout << "Connected to " << host_ip_address_ << std::endl; |
| 177 } |
| 178 } else { |
| 179 host_ip_address_ += ch; |
| 180 std::cout << ch; |
| 181 } |
| 182 std::cout.flush(); |
| 183 } |
| 184 |
| 185 // Process the Pepper mouse event. |
| 186 void ChromotingClient::handle_mouse_event(NPPepperEvent* npevent) { |
| 187 if (host_->connected()) { |
| 188 send_mouse_message(npevent); |
| 189 if (handle_update_message()) { |
| 190 plugin_->draw(); |
| 191 } |
| 192 } |
| 193 } |
| 194 |
| 195 // Pass the given Pepper mouse event along to the host. |
| 196 void ChromotingClient::send_mouse_message(NPPepperEvent* event) { |
| 197 NPMouseEvent* mouse_event = &event->u.mouse; |
| 198 MouseMessage mouse_msg; |
| 199 |
| 200 mouse_msg.message = MessageMouse; |
| 201 mouse_msg.x = mouse_event->x; |
| 202 mouse_msg.y = mouse_event->y; |
| 203 |
| 204 mouse_msg.flags = 0; |
| 205 int32 type = event->type; |
| 206 int32 button = mouse_event->button; |
| 207 if (type == NPEventType_MouseDown) { |
| 208 if (button == NPMouseButton_Left) { |
| 209 mouse_msg.flags |= LeftDown; |
| 210 } else if (button == NPMouseButton_Right) { |
| 211 mouse_msg.flags |= RightDown; |
| 212 } |
| 213 } else if (type == NPEventType_MouseUp) { |
| 214 if (button == NPMouseButton_Left) { |
| 215 mouse_msg.flags |= LeftUp; |
| 216 } else if (button == NPMouseButton_Right) { |
| 217 mouse_msg.flags |= RightUp; |
| 218 } |
| 219 } |
| 220 host_->write_data((const char*)&mouse_msg, sizeof(mouse_msg)); |
| 221 } |
| 222 |
| 223 // Process the pending update command from the host. |
| 224 // Return true if the screen image has been updated. |
| 225 bool ChromotingClient::handle_update_message() { |
| 226 UpdateMessage update_message; |
| 227 int result = host_->read_data(reinterpret_cast<char*>(&update_message), |
| 228 sizeof(update_message)); |
| 229 if (!result) { |
| 230 std::cout << "Failed to get update command" << std::endl; |
| 231 return false; |
| 232 } |
| 233 |
| 234 if (update_message.message != MessageUpdate) { |
| 235 std::cout << "Expected MessageUpdate" << std::endl; |
| 236 return false; |
| 237 } |
| 238 |
| 239 if (verbose_) { |
| 240 std::cout << "message: " << update_message.message << std::endl; |
| 241 } |
| 242 |
| 243 if (update_message.compression == CompressionZlib) { |
| 244 // Read all data. |
| 245 ZDecompressor decomp; |
| 246 char buffer[4096]; |
| 247 int size = update_message.compressed_size; |
| 248 while (size > 0) { |
| 249 // Determine how much we should read from network. |
| 250 int read = std::min(static_cast<int>(sizeof(buffer)), size); |
| 251 result = host_->read_data(buffer, read); |
| 252 decomp.Write(buffer, read); |
| 253 size -= read; |
| 254 } |
| 255 decomp.Flush(); |
| 256 |
| 257 // Decompress raw image data and break into individual images. |
| 258 char* raw_buffer = decomp.GetRawData(); |
| 259 int raw_size = decomp.GetRawSize(); |
| 260 int read = 0; |
| 261 BinaryImageHeader header; |
| 262 while (read < raw_size) { |
| 263 memcpy(&header, raw_buffer, sizeof(BinaryImageHeader)); |
| 264 if (!check_image_header(&header)) { |
| 265 return false; |
| 266 } |
| 267 read += sizeof(BinaryImageHeader); |
| 268 raw_buffer += sizeof(BinaryImageHeader); |
| 269 |
| 270 // Merge this image fragment into the screen bitmap. |
| 271 merge_image(&header, raw_buffer); |
| 272 |
| 273 read += header.size; |
| 274 raw_buffer += header.size; |
| 275 } |
| 276 } else if (update_message.compression == CompressionNone) { |
| 277 printf("compressionNone\n"); |
| 278 for (int i = 0; i < update_message.num_diffs; i++) { |
| 279 BinaryImage* image = new BinaryImage(); |
| 280 read_image(image); |
| 281 |
| 282 // Merge this image update into the screen image. |
| 283 merge_image(&image->header, image->data.get()); |
| 284 |
| 285 delete image; |
| 286 } |
| 287 } else { |
| 288 return false; |
| 289 } |
| 290 |
| 291 return true; |
| 292 } |
| 293 |
| 294 // Check the validity of the image header. |
| 295 bool ChromotingClient::check_image_header(BinaryImageHeader* header) { |
| 296 if (header == NULL) { |
| 297 std::cout << "Invalid image" << std::endl; |
| 298 return false; |
| 299 } |
| 300 |
| 301 if (header->format != FormatRaw && header->format != FormatVp8) { |
| 302 std::cout << "Wrong image format : " << header->format << std::endl; |
| 303 return false; |
| 304 } |
| 305 |
| 306 if (verbose_) { |
| 307 std::cout << "Image:" << std::endl; |
| 308 std::cout << " Format " << header->format << std::endl; |
| 309 std::cout << " X,Y " << header->x << ", " |
| 310 << header->y << std::endl; |
| 311 std::cout << " WxH " << header->width << " x " |
| 312 << header->height << std::endl; |
| 313 std::cout << " Size " << header->size << std::endl; |
| 314 } |
| 315 |
| 316 return true; |
| 317 } |
| 318 |
| 319 // Read an image from the host and store it in the given BinaryImage. |
| 320 bool ChromotingClient::read_image(BinaryImage* image) { |
| 321 int result = host_->read_data(reinterpret_cast<char*>(&image->header), |
| 322 sizeof(image->header)); |
| 323 if (!result) { |
| 324 std::cout << "Failed to receive image header" << std::endl; |
| 325 return false; |
| 326 } |
| 327 |
| 328 if (!check_image_header(&image->header)) { |
| 329 return false; |
| 330 } |
| 331 |
| 332 char* raw_data = new char[image->header.size]; |
| 333 result = host_->read_data(raw_data, image->header.size); |
| 334 if (!result) { |
| 335 std::cout << "Failed to receive image data" << std::endl; |
| 336 return false; |
| 337 } |
| 338 |
| 339 if (image->header.format == FormatRaw) { |
| 340 // Raw image - all we need to do is load the data, so we're done. |
| 341 image->data.reset(raw_data); |
| 342 return true; |
| 343 } else if (image->header.format == FormatVp8) { |
| 344 return false; |
| 345 // TODO(hclam): Enable this block of code when we have VP8. |
| 346 #if 0 |
| 347 // Vp8 encoded - need to convert YUV image data to RGB. |
| 348 static VP8VideoDecoder decoder; |
| 349 uint8* planes[3]; |
| 350 int strides[3]; |
| 351 printf("decoder.DecodeFrame\n"); |
| 352 if (!decoder.DecodeFrame(raw_data, image->header.size)) { |
| 353 std::cout << "Unable to decode frame" << std::endl; |
| 354 return false; |
| 355 } |
| 356 printf("decoder.GetDecodedFrame\n"); |
| 357 if (!decoder.GetDecodedFrame(reinterpret_cast<char**>(planes), strides)) { |
| 358 std::cout << "Unable to get decoded frame" << std::endl; |
| 359 return false; |
| 360 } |
| 361 printf("width = %d\n", image->header.width); |
| 362 for (int i=0; i<3; i++) { |
| 363 printf("stride[%d] = %d\n", i, strides[0]); |
| 364 } |
| 365 |
| 366 // Convert YUV to RGB. |
| 367 int width = image->header.width; |
| 368 int height = image->header.height; |
| 369 char* rgb_data = new char[width * height * sizeof(int32)]; |
| 370 printf("ConvertYUVToRGB32\n"); |
| 371 ConvertYUVToRGB32(planes[0], planes[1], planes[2], |
| 372 reinterpret_cast<uint8*>(rgb_data), |
| 373 width, |
| 374 image->header.height, |
| 375 width, // y stride, |
| 376 width / 2, // uv stride, |
| 377 width * sizeof(int32), // rgb stride |
| 378 media::YV12); |
| 379 printf("conversion done\n"); |
| 380 |
| 381 image->data.reset(rgb_data); |
| 382 |
| 383 // Raw YUV data is no longer needed. |
| 384 delete raw_data; |
| 385 return true; |
| 386 #endif |
| 387 } |
| 388 |
| 389 return false; |
| 390 } |
| 391 |
| 392 } // namespace remoting |
OLD | NEW |