OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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/host/video_frame_capturer.h" |
| 6 |
| 7 #include <X11/Xlib.h> |
| 8 #include <X11/Xutil.h> |
| 9 #include <X11/extensions/Xdamage.h> |
| 10 #include <X11/extensions/Xfixes.h> |
| 11 |
| 12 #include <set> |
| 13 |
| 14 #include "base/basictypes.h" |
| 15 #include "base/logging.h" |
| 16 #include "base/memory/scoped_ptr.h" |
| 17 #include "remoting/base/capture_data.h" |
| 18 #include "remoting/host/differ.h" |
| 19 #include "remoting/host/video_frame_capturer_helper.h" |
| 20 #include "remoting/host/x_server_pixel_buffer.h" |
| 21 #include "remoting/proto/control.pb.h" |
| 22 |
| 23 namespace remoting { |
| 24 |
| 25 namespace { |
| 26 |
| 27 static const int kBytesPerPixel = 4; |
| 28 |
| 29 // Default to false, since many systems have broken XDamage support - see |
| 30 // http://crbug.com/73423. |
| 31 static bool g_should_use_x_damage = false; |
| 32 |
| 33 static bool ShouldUseXDamage() { |
| 34 return g_should_use_x_damage; |
| 35 } |
| 36 |
| 37 // A class representing a full-frame pixel buffer |
| 38 class VideoFrameBuffer { |
| 39 public: |
| 40 VideoFrameBuffer() : bytes_per_row_(0), needs_update_(true) { |
| 41 size_.set(0, 0); |
| 42 } |
| 43 |
| 44 void Update(Display* display, Window root_window) { |
| 45 if (needs_update_) { |
| 46 needs_update_ = false; |
| 47 XWindowAttributes root_attr; |
| 48 XGetWindowAttributes(display, root_window, &root_attr); |
| 49 if (root_attr.width != size_.width() || |
| 50 root_attr.height != size_.height()) { |
| 51 size_.set(root_attr.width, root_attr.height); |
| 52 bytes_per_row_ = size_.width() * kBytesPerPixel; |
| 53 size_t buffer_size = size_.width() * size_.height() * kBytesPerPixel; |
| 54 ptr_.reset(new uint8[buffer_size]); |
| 55 } |
| 56 } |
| 57 } |
| 58 |
| 59 SkISize size() const { return size_; } |
| 60 int bytes_per_row() const { return bytes_per_row_; } |
| 61 uint8* ptr() const { return ptr_.get(); } |
| 62 |
| 63 void set_needs_update() { needs_update_ = true; } |
| 64 |
| 65 private: |
| 66 SkISize size_; |
| 67 int bytes_per_row_; |
| 68 scoped_array<uint8> ptr_; |
| 69 bool needs_update_; |
| 70 |
| 71 DISALLOW_COPY_AND_ASSIGN(VideoFrameBuffer); |
| 72 }; |
| 73 |
| 74 // A class to perform video frame capturing for Linux. |
| 75 class VideoFrameCapturerLinux : public VideoFrameCapturer { |
| 76 public: |
| 77 VideoFrameCapturerLinux(); |
| 78 virtual ~VideoFrameCapturerLinux(); |
| 79 |
| 80 bool Init(); // TODO(ajwong): Do we really want this to be synchronous? |
| 81 |
| 82 // Capturer interface. |
| 83 virtual void Start(const CursorShapeChangedCallback& callback) OVERRIDE; |
| 84 virtual void Stop() OVERRIDE; |
| 85 virtual void ScreenConfigurationChanged() OVERRIDE; |
| 86 virtual media::VideoFrame::Format pixel_format() const OVERRIDE; |
| 87 virtual void ClearInvalidRegion() OVERRIDE; |
| 88 virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE; |
| 89 virtual void InvalidateScreen(const SkISize& size) OVERRIDE; |
| 90 virtual void InvalidateFullScreen() OVERRIDE; |
| 91 virtual void CaptureInvalidRegion( |
| 92 const CaptureCompletedCallback& callback) OVERRIDE; |
| 93 virtual const SkISize& size_most_recent() const OVERRIDE; |
| 94 |
| 95 private: |
| 96 void InitXDamage(); |
| 97 |
| 98 // Read and handle all currently-pending XEvents. |
| 99 // In the DAMAGE case, process the XDamage events and store the resulting |
| 100 // damage rectangles in the VideoFrameCapturerHelper. |
| 101 // In all cases, call ScreenConfigurationChanged() in response to any |
| 102 // ConfigNotify events. |
| 103 void ProcessPendingXEvents(); |
| 104 |
| 105 // Capture screen pixels, and return the data in a new CaptureData object, |
| 106 // to be freed by the caller. |
| 107 // In the DAMAGE case, the VideoFrameCapturerHelper already holds the list of |
| 108 // invalid rectangles from ProcessPendingXEvents(). |
| 109 // In the non-DAMAGE case, this captures the whole screen, then calculates |
| 110 // some invalid rectangles that include any differences between this and the |
| 111 // previous capture. |
| 112 CaptureData* CaptureFrame(); |
| 113 |
| 114 // Capture the cursor image and call the CursorShapeChangedCallback if it |
| 115 // has been set (using SetCursorShapeChangedCallback). |
| 116 void CaptureCursor(); |
| 117 |
| 118 // Synchronize the current buffer with |last_buffer_|, by copying pixels from |
| 119 // the area of |last_invalid_rects|. |
| 120 // Note this only works on the assumption that kNumBuffers == 2, as |
| 121 // |last_invalid_rects| holds the differences from the previous buffer and |
| 122 // the one prior to that (which will then be the current buffer). |
| 123 void SynchronizeFrame(); |
| 124 |
| 125 void DeinitXlib(); |
| 126 |
| 127 // Capture a rectangle from |x_server_pixel_buffer_|, and copy the data into |
| 128 // |capture_data|. |
| 129 void CaptureRect(const SkIRect& rect, CaptureData* capture_data); |
| 130 |
| 131 // We expose two forms of blitting to handle variations in the pixel format. |
| 132 // In FastBlit, the operation is effectively a memcpy. |
| 133 void FastBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); |
| 134 void SlowBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); |
| 135 |
| 136 // X11 graphics context. |
| 137 Display* display_; |
| 138 GC gc_; |
| 139 Window root_window_; |
| 140 |
| 141 // XFixes. |
| 142 bool has_xfixes_; |
| 143 int xfixes_event_base_; |
| 144 int xfixes_error_base_; |
| 145 |
| 146 // XDamage information. |
| 147 bool use_damage_; |
| 148 Damage damage_handle_; |
| 149 int damage_event_base_; |
| 150 int damage_error_base_; |
| 151 XserverRegion damage_region_; |
| 152 |
| 153 // Access to the X Server's pixel buffer. |
| 154 XServerPixelBuffer x_server_pixel_buffer_; |
| 155 |
| 156 // A thread-safe list of invalid rectangles, and the size of the most |
| 157 // recently captured screen. |
| 158 VideoFrameCapturerHelper helper_; |
| 159 |
| 160 // Callback notified whenever the cursor shape is changed. |
| 161 CursorShapeChangedCallback cursor_shape_changed_callback_; |
| 162 |
| 163 // Capture state. |
| 164 static const int kNumBuffers = 2; |
| 165 VideoFrameBuffer buffers_[kNumBuffers]; |
| 166 int current_buffer_; |
| 167 |
| 168 // Format of pixels returned in buffer. |
| 169 media::VideoFrame::Format pixel_format_; |
| 170 |
| 171 // Invalid region from the previous capture. This is used to synchronize the |
| 172 // current with the last buffer used. |
| 173 SkRegion last_invalid_region_; |
| 174 |
| 175 // Last capture buffer used. |
| 176 uint8* last_buffer_; |
| 177 |
| 178 // |Differ| for use when polling for changes. |
| 179 scoped_ptr<Differ> differ_; |
| 180 |
| 181 DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerLinux); |
| 182 }; |
| 183 |
| 184 VideoFrameCapturerLinux::VideoFrameCapturerLinux() |
| 185 : display_(NULL), |
| 186 gc_(NULL), |
| 187 root_window_(BadValue), |
| 188 has_xfixes_(false), |
| 189 xfixes_event_base_(-1), |
| 190 xfixes_error_base_(-1), |
| 191 use_damage_(false), |
| 192 damage_handle_(0), |
| 193 damage_event_base_(-1), |
| 194 damage_error_base_(-1), |
| 195 damage_region_(0), |
| 196 current_buffer_(0), |
| 197 pixel_format_(media::VideoFrame::RGB32), |
| 198 last_buffer_(NULL) { |
| 199 helper_.SetLogGridSize(4); |
| 200 } |
| 201 |
| 202 VideoFrameCapturerLinux::~VideoFrameCapturerLinux() { |
| 203 DeinitXlib(); |
| 204 } |
| 205 |
| 206 bool VideoFrameCapturerLinux::Init() { |
| 207 // TODO(ajwong): We should specify the display string we are attaching to |
| 208 // in the constructor. |
| 209 display_ = XOpenDisplay(NULL); |
| 210 if (!display_) { |
| 211 LOG(ERROR) << "Unable to open display"; |
| 212 return false; |
| 213 } |
| 214 |
| 215 x_server_pixel_buffer_.Init(display_); |
| 216 |
| 217 root_window_ = RootWindow(display_, DefaultScreen(display_)); |
| 218 if (root_window_ == BadValue) { |
| 219 LOG(ERROR) << "Unable to get the root window"; |
| 220 DeinitXlib(); |
| 221 return false; |
| 222 } |
| 223 |
| 224 gc_ = XCreateGC(display_, root_window_, 0, NULL); |
| 225 if (gc_ == NULL) { |
| 226 LOG(ERROR) << "Unable to get graphics context"; |
| 227 DeinitXlib(); |
| 228 return false; |
| 229 } |
| 230 |
| 231 // Check for XFixes extension. This is required for cursor shape |
| 232 // notifications, and for our use of XDamage. |
| 233 if (XFixesQueryExtension(display_, &xfixes_event_base_, |
| 234 &xfixes_error_base_)) { |
| 235 has_xfixes_ = true; |
| 236 } else { |
| 237 LOG(INFO) << "X server does not support XFixes."; |
| 238 } |
| 239 |
| 240 if (ShouldUseXDamage()) { |
| 241 InitXDamage(); |
| 242 } |
| 243 |
| 244 // Register for changes to the dimensions of the root window. |
| 245 XSelectInput(display_, root_window_, StructureNotifyMask); |
| 246 |
| 247 if (has_xfixes_) { |
| 248 // Register for changes to the cursor shape. |
| 249 XFixesSelectCursorInput(display_, root_window_, |
| 250 XFixesDisplayCursorNotifyMask); |
| 251 } |
| 252 |
| 253 return true; |
| 254 } |
| 255 |
| 256 void VideoFrameCapturerLinux::InitXDamage() { |
| 257 // Our use of XDamage requires XFixes. |
| 258 if (!has_xfixes_) { |
| 259 return; |
| 260 } |
| 261 |
| 262 // Check for XDamage extension. |
| 263 if (!XDamageQueryExtension(display_, &damage_event_base_, |
| 264 &damage_error_base_)) { |
| 265 LOG(INFO) << "X server does not support XDamage."; |
| 266 return; |
| 267 } |
| 268 |
| 269 // TODO(lambroslambrou): Disable DAMAGE in situations where it is known |
| 270 // to fail, such as when Desktop Effects are enabled, with graphics |
| 271 // drivers (nVidia, ATI) that fail to report DAMAGE notifications |
| 272 // properly. |
| 273 |
| 274 // Request notifications every time the screen becomes damaged. |
| 275 damage_handle_ = XDamageCreate(display_, root_window_, |
| 276 XDamageReportNonEmpty); |
| 277 if (!damage_handle_) { |
| 278 LOG(ERROR) << "Unable to initialize XDamage."; |
| 279 return; |
| 280 } |
| 281 |
| 282 // Create an XFixes server-side region to collate damage into. |
| 283 damage_region_ = XFixesCreateRegion(display_, 0, 0); |
| 284 if (!damage_region_) { |
| 285 XDamageDestroy(display_, damage_handle_); |
| 286 LOG(ERROR) << "Unable to create XFixes region."; |
| 287 return; |
| 288 } |
| 289 |
| 290 use_damage_ = true; |
| 291 LOG(INFO) << "Using XDamage extension."; |
| 292 } |
| 293 |
| 294 void VideoFrameCapturerLinux::Start( |
| 295 const CursorShapeChangedCallback& callback) { |
| 296 cursor_shape_changed_callback_ = callback; |
| 297 } |
| 298 |
| 299 void VideoFrameCapturerLinux::Stop() { |
| 300 } |
| 301 |
| 302 void VideoFrameCapturerLinux::ScreenConfigurationChanged() { |
| 303 last_buffer_ = NULL; |
| 304 for (int i = 0; i < kNumBuffers; ++i) { |
| 305 buffers_[i].set_needs_update(); |
| 306 } |
| 307 helper_.ClearInvalidRegion(); |
| 308 x_server_pixel_buffer_.Init(display_); |
| 309 } |
| 310 |
| 311 media::VideoFrame::Format VideoFrameCapturerLinux::pixel_format() const { |
| 312 return pixel_format_; |
| 313 } |
| 314 |
| 315 void VideoFrameCapturerLinux::ClearInvalidRegion() { |
| 316 helper_.ClearInvalidRegion(); |
| 317 } |
| 318 |
| 319 void VideoFrameCapturerLinux::InvalidateRegion(const SkRegion& invalid_region) { |
| 320 helper_.InvalidateRegion(invalid_region); |
| 321 } |
| 322 |
| 323 void VideoFrameCapturerLinux::InvalidateScreen(const SkISize& size) { |
| 324 helper_.InvalidateScreen(size); |
| 325 } |
| 326 |
| 327 void VideoFrameCapturerLinux::InvalidateFullScreen() { |
| 328 helper_.InvalidateFullScreen(); |
| 329 last_buffer_ = NULL; |
| 330 } |
| 331 |
| 332 void VideoFrameCapturerLinux::CaptureInvalidRegion( |
| 333 const CaptureCompletedCallback& callback) { |
| 334 // Process XEvents for XDamage and cursor shape tracking. |
| 335 ProcessPendingXEvents(); |
| 336 |
| 337 // Resize the current buffer if there was a recent change of |
| 338 // screen-resolution. |
| 339 VideoFrameBuffer ¤t = buffers_[current_buffer_]; |
| 340 current.Update(display_, root_window_); |
| 341 // Also refresh the Differ helper used by CaptureFrame(), if needed. |
| 342 if (!use_damage_ && !last_buffer_) { |
| 343 differ_.reset(new Differ(current.size().width(), current.size().height(), |
| 344 kBytesPerPixel, current.bytes_per_row())); |
| 345 } |
| 346 |
| 347 scoped_refptr<CaptureData> capture_data(CaptureFrame()); |
| 348 |
| 349 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; |
| 350 |
| 351 callback.Run(capture_data); |
| 352 } |
| 353 |
| 354 void VideoFrameCapturerLinux::ProcessPendingXEvents() { |
| 355 // Find the number of events that are outstanding "now." We don't just loop |
| 356 // on XPending because we want to guarantee this terminates. |
| 357 int events_to_process = XPending(display_); |
| 358 XEvent e; |
| 359 |
| 360 for (int i = 0; i < events_to_process; i++) { |
| 361 XNextEvent(display_, &e); |
| 362 if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) { |
| 363 XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e); |
| 364 DCHECK(event->level == XDamageReportNonEmpty); |
| 365 } else if (e.type == ConfigureNotify) { |
| 366 ScreenConfigurationChanged(); |
| 367 } else if (has_xfixes_ && |
| 368 e.type == xfixes_event_base_ + XFixesCursorNotify) { |
| 369 XFixesCursorNotifyEvent* cne; |
| 370 cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e); |
| 371 if (cne->subtype == XFixesDisplayCursorNotify) { |
| 372 CaptureCursor(); |
| 373 } |
| 374 } else { |
| 375 LOG(WARNING) << "Got unknown event type: " << e.type; |
| 376 } |
| 377 } |
| 378 } |
| 379 |
| 380 void VideoFrameCapturerLinux::CaptureCursor() { |
| 381 DCHECK(has_xfixes_); |
| 382 if (cursor_shape_changed_callback_.is_null()) |
| 383 return; |
| 384 |
| 385 XFixesCursorImage* img = XFixesGetCursorImage(display_); |
| 386 if (!img) { |
| 387 return; |
| 388 } |
| 389 |
| 390 int width = img->width; |
| 391 int height = img->height; |
| 392 int total_bytes = width * height * kBytesPerPixel; |
| 393 |
| 394 scoped_ptr<protocol::CursorShapeInfo> cursor_proto( |
| 395 new protocol::CursorShapeInfo()); |
| 396 cursor_proto->set_width(width); |
| 397 cursor_proto->set_height(height); |
| 398 cursor_proto->set_hotspot_x(img->xhot); |
| 399 cursor_proto->set_hotspot_y(img->yhot); |
| 400 |
| 401 cursor_proto->mutable_data()->resize(total_bytes); |
| 402 uint8* proto_data = const_cast<uint8*>(reinterpret_cast<const uint8*>( |
| 403 cursor_proto->mutable_data()->data())); |
| 404 |
| 405 // Xlib stores 32-bit data in longs, even if longs are 64-bits long. |
| 406 unsigned long* src = img->pixels; |
| 407 uint32* dst = reinterpret_cast<uint32*>(proto_data); |
| 408 uint32* dst_end = dst + (width * height); |
| 409 while (dst < dst_end) { |
| 410 *dst++ = static_cast<uint32>(*src++); |
| 411 } |
| 412 XFree(img); |
| 413 |
| 414 cursor_shape_changed_callback_.Run(cursor_proto.Pass()); |
| 415 } |
| 416 |
| 417 CaptureData* VideoFrameCapturerLinux::CaptureFrame() { |
| 418 VideoFrameBuffer& buffer = buffers_[current_buffer_]; |
| 419 DataPlanes planes; |
| 420 planes.data[0] = buffer.ptr(); |
| 421 planes.strides[0] = buffer.bytes_per_row(); |
| 422 |
| 423 CaptureData* capture_data = new CaptureData(planes, buffer.size(), |
| 424 media::VideoFrame::RGB32); |
| 425 |
| 426 // Pass the screen size to the helper, so it can clip the invalid region if it |
| 427 // expands that region to a grid. |
| 428 helper_.set_size_most_recent(capture_data->size()); |
| 429 |
| 430 // In the DAMAGE case, ensure the frame is up-to-date with the previous frame |
| 431 // if any. If there isn't a previous frame, that means a screen-resolution |
| 432 // change occurred, and |invalid_rects| will be updated to include the whole |
| 433 // screen. |
| 434 if (use_damage_ && last_buffer_) |
| 435 SynchronizeFrame(); |
| 436 |
| 437 SkRegion invalid_region; |
| 438 |
| 439 x_server_pixel_buffer_.Synchronize(); |
| 440 if (use_damage_ && last_buffer_) { |
| 441 // Atomically fetch and clear the damage region. |
| 442 XDamageSubtract(display_, damage_handle_, None, damage_region_); |
| 443 int nRects = 0; |
| 444 XRectangle bounds; |
| 445 XRectangle* rects = XFixesFetchRegionAndBounds(display_, damage_region_, |
| 446 &nRects, &bounds); |
| 447 for (int i=0; i<nRects; ++i) { |
| 448 invalid_region.op(SkIRect::MakeXYWH(rects[i].x, rects[i].y, |
| 449 rects[i].width, rects[i].height), |
| 450 SkRegion::kUnion_Op); |
| 451 } |
| 452 XFree(rects); |
| 453 helper_.InvalidateRegion(invalid_region); |
| 454 |
| 455 // Capture the damaged portions of the desktop. |
| 456 helper_.SwapInvalidRegion(&invalid_region); |
| 457 for (SkRegion::Iterator it(invalid_region); !it.done(); it.next()) { |
| 458 CaptureRect(it.rect(), capture_data); |
| 459 } |
| 460 } else { |
| 461 // Doing full-screen polling, or this is the first capture after a |
| 462 // screen-resolution change. In either case, need a full-screen capture. |
| 463 SkIRect screen_rect = SkIRect::MakeWH(buffer.size().width(), |
| 464 buffer.size().height()); |
| 465 CaptureRect(screen_rect, capture_data); |
| 466 |
| 467 if (last_buffer_) { |
| 468 // Full-screen polling, so calculate the invalid rects here, based on the |
| 469 // changed pixels between current and previous buffers. |
| 470 DCHECK(differ_ != NULL); |
| 471 differ_->CalcDirtyRegion(last_buffer_, buffer.ptr(), &invalid_region); |
| 472 } else { |
| 473 // No previous buffer, so always invalidate the whole screen, whether |
| 474 // or not DAMAGE is being used. DAMAGE doesn't necessarily send a |
| 475 // full-screen notification after a screen-resolution change, so |
| 476 // this is done here. |
| 477 invalid_region.op(screen_rect, SkRegion::kUnion_Op); |
| 478 } |
| 479 } |
| 480 |
| 481 capture_data->mutable_dirty_region() = invalid_region; |
| 482 last_invalid_region_ = invalid_region; |
| 483 last_buffer_ = buffer.ptr(); |
| 484 return capture_data; |
| 485 } |
| 486 |
| 487 void VideoFrameCapturerLinux::SynchronizeFrame() { |
| 488 // Synchronize the current buffer with the previous one since we do not |
| 489 // capture the entire desktop. Note that encoder may be reading from the |
| 490 // previous buffer at this time so thread access complaints are false |
| 491 // positives. |
| 492 |
| 493 // TODO(hclam): We can reduce the amount of copying here by subtracting |
| 494 // |capturer_helper_|s region from |last_invalid_region_|. |
| 495 // http://crbug.com/92354 |
| 496 DCHECK(last_buffer_); |
| 497 VideoFrameBuffer& buffer = buffers_[current_buffer_]; |
| 498 for (SkRegion::Iterator it(last_invalid_region_); !it.done(); it.next()) { |
| 499 const SkIRect& r = it.rect(); |
| 500 int offset = r.fTop * buffer.bytes_per_row() + r.fLeft * kBytesPerPixel; |
| 501 for (int i = 0; i < r.height(); ++i) { |
| 502 memcpy(buffer.ptr() + offset, last_buffer_ + offset, |
| 503 r.width() * kBytesPerPixel); |
| 504 offset += buffer.size().width() * kBytesPerPixel; |
| 505 } |
| 506 } |
| 507 } |
| 508 |
| 509 void VideoFrameCapturerLinux::DeinitXlib() { |
| 510 if (gc_) { |
| 511 XFreeGC(display_, gc_); |
| 512 gc_ = NULL; |
| 513 } |
| 514 |
| 515 x_server_pixel_buffer_.Release(); |
| 516 |
| 517 if (display_) { |
| 518 if (damage_handle_) |
| 519 XDamageDestroy(display_, damage_handle_); |
| 520 if (damage_region_) |
| 521 XFixesDestroyRegion(display_, damage_region_); |
| 522 XCloseDisplay(display_); |
| 523 display_ = NULL; |
| 524 damage_handle_ = 0; |
| 525 damage_region_ = 0; |
| 526 } |
| 527 } |
| 528 |
| 529 void VideoFrameCapturerLinux::CaptureRect(const SkIRect& rect, |
| 530 CaptureData* capture_data) { |
| 531 uint8* image = x_server_pixel_buffer_.CaptureRect(rect); |
| 532 int depth = x_server_pixel_buffer_.GetDepth(); |
| 533 int bpp = x_server_pixel_buffer_.GetBitsPerPixel(); |
| 534 bool is_rgb = x_server_pixel_buffer_.IsRgb(); |
| 535 if ((depth == 24 || depth == 32) && bpp == 32 && is_rgb) { |
| 536 DVLOG(3) << "Fast blitting"; |
| 537 FastBlit(image, rect, capture_data); |
| 538 } else { |
| 539 DVLOG(3) << "Slow blitting"; |
| 540 SlowBlit(image, rect, capture_data); |
| 541 } |
| 542 } |
| 543 |
| 544 void VideoFrameCapturerLinux::FastBlit(uint8* image, const SkIRect& rect, |
| 545 CaptureData* capture_data) { |
| 546 uint8* src_pos = image; |
| 547 int src_stride = x_server_pixel_buffer_.GetStride(); |
| 548 int dst_x = rect.fLeft, dst_y = rect.fTop; |
| 549 |
| 550 DataPlanes planes = capture_data->data_planes(); |
| 551 uint8* dst_buffer = planes.data[0]; |
| 552 |
| 553 const int dst_stride = planes.strides[0]; |
| 554 |
| 555 uint8* dst_pos = dst_buffer + dst_stride * dst_y; |
| 556 dst_pos += dst_x * kBytesPerPixel; |
| 557 |
| 558 int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel; |
| 559 for (int y = 0; y < height; ++y) { |
| 560 memcpy(dst_pos, src_pos, row_bytes); |
| 561 src_pos += src_stride; |
| 562 dst_pos += dst_stride; |
| 563 } |
| 564 } |
| 565 |
| 566 void VideoFrameCapturerLinux::SlowBlit(uint8* image, const SkIRect& rect, |
| 567 CaptureData* capture_data) { |
| 568 DataPlanes planes = capture_data->data_planes(); |
| 569 uint8* dst_buffer = planes.data[0]; |
| 570 const int dst_stride = planes.strides[0]; |
| 571 int src_stride = x_server_pixel_buffer_.GetStride(); |
| 572 int dst_x = rect.fLeft, dst_y = rect.fTop; |
| 573 int width = rect.width(), height = rect.height(); |
| 574 |
| 575 unsigned int red_mask = x_server_pixel_buffer_.GetRedMask(); |
| 576 unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask(); |
| 577 unsigned int green_mask = x_server_pixel_buffer_.GetGreenMask(); |
| 578 unsigned int red_shift = x_server_pixel_buffer_.GetRedShift(); |
| 579 unsigned int blue_shift = x_server_pixel_buffer_.GetBlueShift(); |
| 580 unsigned int green_shift = x_server_pixel_buffer_.GetGreenShift(); |
| 581 |
| 582 unsigned int max_red = red_mask >> red_shift; |
| 583 unsigned int max_blue = blue_mask >> blue_shift; |
| 584 unsigned int max_green = green_mask >> green_shift; |
| 585 |
| 586 unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel(); |
| 587 |
| 588 uint8* dst_pos = dst_buffer + dst_stride * dst_y; |
| 589 uint8* src_pos = image; |
| 590 dst_pos += dst_x * kBytesPerPixel; |
| 591 // TODO(hclam): Optimize, perhaps using MMX code or by converting to |
| 592 // YUV directly |
| 593 for (int y = 0; y < height; y++) { |
| 594 uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos); |
| 595 uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos); |
| 596 uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos); |
| 597 for (int x = 0; x < width; x++) { |
| 598 // Dereference through an appropriately-aligned pointer. |
| 599 uint32_t pixel; |
| 600 if (bits_per_pixel == 32) |
| 601 pixel = src_pos_32[x]; |
| 602 else if (bits_per_pixel == 16) |
| 603 pixel = src_pos_16[x]; |
| 604 else |
| 605 pixel = src_pos[x]; |
| 606 uint32_t r = (((pixel & red_mask) >> red_shift) * 255) / max_red; |
| 607 uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue; |
| 608 uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green; |
| 609 // Write as 32-bit RGB. |
| 610 dst_pos_32[x] = r << 16 | g << 8 | b; |
| 611 } |
| 612 dst_pos += dst_stride; |
| 613 src_pos += src_stride; |
| 614 } |
| 615 } |
| 616 |
| 617 const SkISize& VideoFrameCapturerLinux::size_most_recent() const { |
| 618 return helper_.size_most_recent(); |
| 619 } |
| 620 |
| 621 } // namespace |
| 622 |
| 623 // static |
| 624 VideoFrameCapturer* VideoFrameCapturer::Create() { |
| 625 VideoFrameCapturerLinux* capturer = new VideoFrameCapturerLinux(); |
| 626 if (!capturer->Init()) { |
| 627 delete capturer; |
| 628 capturer = NULL; |
| 629 } |
| 630 return capturer; |
| 631 } |
| 632 |
| 633 // static |
| 634 void VideoFrameCapturer::EnableXDamage(bool enable) { |
| 635 g_should_use_x_damage = enable; |
| 636 } |
| 637 |
| 638 } // namespace remoting |
OLD | NEW |