| 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 "base/time.h" | |
| 18 #include "remoting/base/capture_data.h" | |
| 19 #include "remoting/host/differ.h" | |
| 20 #include "remoting/host/linux/x_server_pixel_buffer.h" | |
| 21 #include "remoting/host/video_frame.h" | |
| 22 #include "remoting/host/video_frame_capturer_helper.h" | |
| 23 #include "remoting/host/video_frame_queue.h" | |
| 24 #include "remoting/proto/control.pb.h" | |
| 25 | |
| 26 namespace remoting { | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 static const int kBytesPerPixel = 4; | |
| 31 | |
| 32 // Default to false, since many systems have broken XDamage support - see | |
| 33 // http://crbug.com/73423. | |
| 34 static bool g_should_use_x_damage = false; | |
| 35 | |
| 36 static bool ShouldUseXDamage() { | |
| 37 return g_should_use_x_damage; | |
| 38 } | |
| 39 | |
| 40 // A class representing a full-frame pixel buffer. | |
| 41 class VideoFrameLinux : public VideoFrame { | |
| 42 public: | |
| 43 explicit VideoFrameLinux(const SkISize& window_size); | |
| 44 virtual ~VideoFrameLinux(); | |
| 45 | |
| 46 private: | |
| 47 // Allocated pixel buffer. | |
| 48 scoped_array<uint8> data_; | |
| 49 | |
| 50 DISALLOW_COPY_AND_ASSIGN(VideoFrameLinux); | |
| 51 }; | |
| 52 | |
| 53 // A class to perform video frame capturing for Linux. | |
| 54 class VideoFrameCapturerLinux : public VideoFrameCapturer { | |
| 55 public: | |
| 56 VideoFrameCapturerLinux(); | |
| 57 virtual ~VideoFrameCapturerLinux(); | |
| 58 | |
| 59 bool Init(); // TODO(ajwong): Do we really want this to be synchronous? | |
| 60 | |
| 61 // Capturer interface. | |
| 62 virtual void Start(Delegate* delegate) OVERRIDE; | |
| 63 virtual void Stop() OVERRIDE; | |
| 64 virtual media::VideoFrame::Format pixel_format() const OVERRIDE; | |
| 65 virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE; | |
| 66 virtual void CaptureFrame() OVERRIDE; | |
| 67 virtual const SkISize& size_most_recent() const OVERRIDE; | |
| 68 | |
| 69 private: | |
| 70 void InitXDamage(); | |
| 71 | |
| 72 // Read and handle all currently-pending XEvents. | |
| 73 // In the DAMAGE case, process the XDamage events and store the resulting | |
| 74 // damage rectangles in the VideoFrameCapturerHelper. | |
| 75 // In all cases, call ScreenConfigurationChanged() in response to any | |
| 76 // ConfigNotify events. | |
| 77 void ProcessPendingXEvents(); | |
| 78 | |
| 79 // Capture the cursor image and notify the delegate if it was captured. | |
| 80 void CaptureCursor(); | |
| 81 | |
| 82 // Capture screen pixels, and return the data in a new CaptureData object, | |
| 83 // to be freed by the caller. | |
| 84 // In the DAMAGE case, the VideoFrameCapturerHelper already holds the list of | |
| 85 // invalid rectangles from ProcessPendingXEvents(). | |
| 86 // In the non-DAMAGE case, this captures the whole screen, then calculates | |
| 87 // some invalid rectangles that include any differences between this and the | |
| 88 // previous capture. | |
| 89 CaptureData* CaptureScreen(); | |
| 90 | |
| 91 // Called when the screen configuration is changed. |root_window_size| | |
| 92 // specifies size the most recent size of the root window. | |
| 93 void ScreenConfigurationChanged(const SkISize& root_window_size); | |
| 94 | |
| 95 // Synchronize the current buffer with |last_buffer_|, by copying pixels from | |
| 96 // the area of |last_invalid_rects|. | |
| 97 // Note this only works on the assumption that kNumBuffers == 2, as | |
| 98 // |last_invalid_rects| holds the differences from the previous buffer and | |
| 99 // the one prior to that (which will then be the current buffer). | |
| 100 void SynchronizeFrame(); | |
| 101 | |
| 102 void DeinitXlib(); | |
| 103 | |
| 104 // Capture a rectangle from |x_server_pixel_buffer_|, and copy the data into | |
| 105 // |capture_data|. | |
| 106 void CaptureRect(const SkIRect& rect, CaptureData* capture_data); | |
| 107 | |
| 108 // We expose two forms of blitting to handle variations in the pixel format. | |
| 109 // In FastBlit, the operation is effectively a memcpy. | |
| 110 void FastBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); | |
| 111 void SlowBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); | |
| 112 | |
| 113 Delegate* delegate_; | |
| 114 | |
| 115 // X11 graphics context. | |
| 116 Display* display_; | |
| 117 GC gc_; | |
| 118 Window root_window_; | |
| 119 | |
| 120 // Last known dimensions of the root window. | |
| 121 SkISize root_window_size_; | |
| 122 | |
| 123 // XFixes. | |
| 124 bool has_xfixes_; | |
| 125 int xfixes_event_base_; | |
| 126 int xfixes_error_base_; | |
| 127 | |
| 128 // XDamage information. | |
| 129 bool use_damage_; | |
| 130 Damage damage_handle_; | |
| 131 int damage_event_base_; | |
| 132 int damage_error_base_; | |
| 133 XserverRegion damage_region_; | |
| 134 | |
| 135 // Access to the X Server's pixel buffer. | |
| 136 XServerPixelBuffer x_server_pixel_buffer_; | |
| 137 | |
| 138 // A thread-safe list of invalid rectangles, and the size of the most | |
| 139 // recently captured screen. | |
| 140 VideoFrameCapturerHelper helper_; | |
| 141 | |
| 142 // Queue of the frames buffers. | |
| 143 VideoFrameQueue queue_; | |
| 144 | |
| 145 // Format of pixels returned in buffer. | |
| 146 media::VideoFrame::Format pixel_format_; | |
| 147 | |
| 148 // Invalid region from the previous capture. This is used to synchronize the | |
| 149 // current with the last buffer used. | |
| 150 SkRegion last_invalid_region_; | |
| 151 | |
| 152 // |Differ| for use when polling for changes. | |
| 153 scoped_ptr<Differ> differ_; | |
| 154 | |
| 155 DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerLinux); | |
| 156 }; | |
| 157 | |
| 158 VideoFrameLinux::VideoFrameLinux(const SkISize& window_size) { | |
| 159 set_bytes_per_row(window_size.width() * kBytesPerPixel); | |
| 160 set_dimensions(window_size); | |
| 161 | |
| 162 size_t buffer_size = bytes_per_row() * window_size.height(); | |
| 163 data_.reset(new uint8[buffer_size]); | |
| 164 set_pixels(data_.get()); | |
| 165 } | |
| 166 | |
| 167 VideoFrameLinux::~VideoFrameLinux() { | |
| 168 } | |
| 169 | |
| 170 VideoFrameCapturerLinux::VideoFrameCapturerLinux() | |
| 171 : delegate_(NULL), | |
| 172 display_(NULL), | |
| 173 gc_(NULL), | |
| 174 root_window_(BadValue), | |
| 175 root_window_size_(SkISize::Make(0, 0)), | |
| 176 has_xfixes_(false), | |
| 177 xfixes_event_base_(-1), | |
| 178 xfixes_error_base_(-1), | |
| 179 use_damage_(false), | |
| 180 damage_handle_(0), | |
| 181 damage_event_base_(-1), | |
| 182 damage_error_base_(-1), | |
| 183 damage_region_(0), | |
| 184 pixel_format_(media::VideoFrame::RGB32) { | |
| 185 helper_.SetLogGridSize(4); | |
| 186 } | |
| 187 | |
| 188 VideoFrameCapturerLinux::~VideoFrameCapturerLinux() { | |
| 189 DeinitXlib(); | |
| 190 } | |
| 191 | |
| 192 bool VideoFrameCapturerLinux::Init() { | |
| 193 // TODO(ajwong): We should specify the display string we are attaching to | |
| 194 // in the constructor. | |
| 195 display_ = XOpenDisplay(NULL); | |
| 196 if (!display_) { | |
| 197 LOG(ERROR) << "Unable to open display"; | |
| 198 return false; | |
| 199 } | |
| 200 | |
| 201 x_server_pixel_buffer_.Init(display_); | |
| 202 | |
| 203 root_window_ = RootWindow(display_, DefaultScreen(display_)); | |
| 204 if (root_window_ == BadValue) { | |
| 205 LOG(ERROR) << "Unable to get the root window"; | |
| 206 DeinitXlib(); | |
| 207 return false; | |
| 208 } | |
| 209 | |
| 210 gc_ = XCreateGC(display_, root_window_, 0, NULL); | |
| 211 if (gc_ == NULL) { | |
| 212 LOG(ERROR) << "Unable to get graphics context"; | |
| 213 DeinitXlib(); | |
| 214 return false; | |
| 215 } | |
| 216 | |
| 217 // Check for XFixes extension. This is required for cursor shape | |
| 218 // notifications, and for our use of XDamage. | |
| 219 if (XFixesQueryExtension(display_, &xfixes_event_base_, | |
| 220 &xfixes_error_base_)) { | |
| 221 has_xfixes_ = true; | |
| 222 } else { | |
| 223 LOG(INFO) << "X server does not support XFixes."; | |
| 224 } | |
| 225 | |
| 226 if (ShouldUseXDamage()) { | |
| 227 InitXDamage(); | |
| 228 } | |
| 229 | |
| 230 // Register for changes to the dimensions of the root window. | |
| 231 XSelectInput(display_, root_window_, StructureNotifyMask); | |
| 232 | |
| 233 // Update the root window size. | |
| 234 XWindowAttributes root_attributes; | |
| 235 XGetWindowAttributes(display_, root_window_, &root_attributes); | |
| 236 root_window_size_.set(root_attributes.width, root_attributes.height); | |
| 237 | |
| 238 if (has_xfixes_) { | |
| 239 // Register for changes to the cursor shape. | |
| 240 XFixesSelectCursorInput(display_, root_window_, | |
| 241 XFixesDisplayCursorNotifyMask); | |
| 242 } | |
| 243 | |
| 244 return true; | |
| 245 } | |
| 246 | |
| 247 void VideoFrameCapturerLinux::InitXDamage() { | |
| 248 // Our use of XDamage requires XFixes. | |
| 249 if (!has_xfixes_) { | |
| 250 return; | |
| 251 } | |
| 252 | |
| 253 // Check for XDamage extension. | |
| 254 if (!XDamageQueryExtension(display_, &damage_event_base_, | |
| 255 &damage_error_base_)) { | |
| 256 LOG(INFO) << "X server does not support XDamage."; | |
| 257 return; | |
| 258 } | |
| 259 | |
| 260 // TODO(lambroslambrou): Disable DAMAGE in situations where it is known | |
| 261 // to fail, such as when Desktop Effects are enabled, with graphics | |
| 262 // drivers (nVidia, ATI) that fail to report DAMAGE notifications | |
| 263 // properly. | |
| 264 | |
| 265 // Request notifications every time the screen becomes damaged. | |
| 266 damage_handle_ = XDamageCreate(display_, root_window_, | |
| 267 XDamageReportNonEmpty); | |
| 268 if (!damage_handle_) { | |
| 269 LOG(ERROR) << "Unable to initialize XDamage."; | |
| 270 return; | |
| 271 } | |
| 272 | |
| 273 // Create an XFixes server-side region to collate damage into. | |
| 274 damage_region_ = XFixesCreateRegion(display_, 0, 0); | |
| 275 if (!damage_region_) { | |
| 276 XDamageDestroy(display_, damage_handle_); | |
| 277 LOG(ERROR) << "Unable to create XFixes region."; | |
| 278 return; | |
| 279 } | |
| 280 | |
| 281 use_damage_ = true; | |
| 282 LOG(INFO) << "Using XDamage extension."; | |
| 283 } | |
| 284 | |
| 285 void VideoFrameCapturerLinux::Start(Delegate* delegate) { | |
| 286 DCHECK(delegate_ == NULL); | |
| 287 | |
| 288 delegate_ = delegate; | |
| 289 } | |
| 290 | |
| 291 void VideoFrameCapturerLinux::Stop() { | |
| 292 } | |
| 293 | |
| 294 media::VideoFrame::Format VideoFrameCapturerLinux::pixel_format() const { | |
| 295 return pixel_format_; | |
| 296 } | |
| 297 | |
| 298 void VideoFrameCapturerLinux::InvalidateRegion(const SkRegion& invalid_region) { | |
| 299 helper_.InvalidateRegion(invalid_region); | |
| 300 } | |
| 301 | |
| 302 void VideoFrameCapturerLinux::CaptureFrame() { | |
| 303 base::Time capture_start_time = base::Time::Now(); | |
| 304 | |
| 305 // Process XEvents for XDamage and cursor shape tracking. | |
| 306 ProcessPendingXEvents(); | |
| 307 | |
| 308 // If the current buffer is from an older generation then allocate a new one. | |
| 309 // Note that we can't reallocate other buffers at this point, since the caller | |
| 310 // may still be reading from them. | |
| 311 if (queue_.current_frame_needs_update()) { | |
| 312 scoped_ptr<VideoFrameLinux> buffer(new VideoFrameLinux( | |
| 313 root_window_size_)); | |
| 314 queue_.ReplaceCurrentFrame(buffer.PassAs<VideoFrame>()); | |
| 315 } | |
| 316 | |
| 317 // Refresh the Differ helper used by CaptureFrame(), if needed. | |
| 318 const VideoFrame* current_buffer = queue_.current_frame(); | |
| 319 if (!use_damage_ && ( | |
| 320 !differ_.get() || | |
| 321 (differ_->width() != current_buffer->dimensions().width()) || | |
| 322 (differ_->height() != current_buffer->dimensions().height()) || | |
| 323 (differ_->bytes_per_row() != current_buffer->bytes_per_row()))) { | |
| 324 differ_.reset(new Differ(current_buffer->dimensions().width(), | |
| 325 current_buffer->dimensions().height(), | |
| 326 kBytesPerPixel, | |
| 327 current_buffer->bytes_per_row())); | |
| 328 } | |
| 329 | |
| 330 scoped_refptr<CaptureData> capture_data(CaptureScreen()); | |
| 331 | |
| 332 // Swap the current & previous buffers ready for the next capture. | |
| 333 last_invalid_region_ = capture_data->dirty_region(); | |
| 334 | |
| 335 queue_.DoneWithCurrentFrame(); | |
| 336 | |
| 337 capture_data->set_capture_time_ms( | |
| 338 (base::Time::Now() - capture_start_time).InMillisecondsRoundedUp()); | |
| 339 delegate_->OnCaptureCompleted(capture_data); | |
| 340 } | |
| 341 | |
| 342 void VideoFrameCapturerLinux::ProcessPendingXEvents() { | |
| 343 // Find the number of events that are outstanding "now." We don't just loop | |
| 344 // on XPending because we want to guarantee this terminates. | |
| 345 int events_to_process = XPending(display_); | |
| 346 XEvent e; | |
| 347 | |
| 348 for (int i = 0; i < events_to_process; i++) { | |
| 349 XNextEvent(display_, &e); | |
| 350 if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) { | |
| 351 XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e); | |
| 352 DCHECK(event->level == XDamageReportNonEmpty); | |
| 353 } else if (e.type == ConfigureNotify) { | |
| 354 const XConfigureEvent& event = e.xconfigure; | |
| 355 ScreenConfigurationChanged(SkISize::Make(event.width, event.height)); | |
| 356 } else if (has_xfixes_ && | |
| 357 e.type == xfixes_event_base_ + XFixesCursorNotify) { | |
| 358 XFixesCursorNotifyEvent* cne; | |
| 359 cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e); | |
| 360 if (cne->subtype == XFixesDisplayCursorNotify) { | |
| 361 CaptureCursor(); | |
| 362 } | |
| 363 } else { | |
| 364 LOG(WARNING) << "Got unknown event type: " << e.type; | |
| 365 } | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 void VideoFrameCapturerLinux::CaptureCursor() { | |
| 370 DCHECK(has_xfixes_); | |
| 371 | |
| 372 XFixesCursorImage* img = XFixesGetCursorImage(display_); | |
| 373 if (!img) { | |
| 374 return; | |
| 375 } | |
| 376 | |
| 377 int width = img->width; | |
| 378 int height = img->height; | |
| 379 int total_bytes = width * height * kBytesPerPixel; | |
| 380 | |
| 381 scoped_ptr<protocol::CursorShapeInfo> cursor_proto( | |
| 382 new protocol::CursorShapeInfo()); | |
| 383 cursor_proto->set_width(width); | |
| 384 cursor_proto->set_height(height); | |
| 385 cursor_proto->set_hotspot_x(img->xhot); | |
| 386 cursor_proto->set_hotspot_y(img->yhot); | |
| 387 | |
| 388 cursor_proto->mutable_data()->resize(total_bytes); | |
| 389 uint8* proto_data = const_cast<uint8*>(reinterpret_cast<const uint8*>( | |
| 390 cursor_proto->mutable_data()->data())); | |
| 391 | |
| 392 // Xlib stores 32-bit data in longs, even if longs are 64-bits long. | |
| 393 unsigned long* src = img->pixels; | |
| 394 uint32* dst = reinterpret_cast<uint32*>(proto_data); | |
| 395 uint32* dst_end = dst + (width * height); | |
| 396 while (dst < dst_end) { | |
| 397 *dst++ = static_cast<uint32>(*src++); | |
| 398 } | |
| 399 XFree(img); | |
| 400 | |
| 401 delegate_->OnCursorShapeChanged(cursor_proto.Pass()); | |
| 402 } | |
| 403 | |
| 404 CaptureData* VideoFrameCapturerLinux::CaptureScreen() { | |
| 405 VideoFrame* current = queue_.current_frame(); | |
| 406 DataPlanes planes; | |
| 407 planes.data[0] = current->pixels(); | |
| 408 planes.strides[0] = current->bytes_per_row(); | |
| 409 | |
| 410 CaptureData* capture_data = new CaptureData(planes, current->dimensions(), | |
| 411 media::VideoFrame::RGB32); | |
| 412 | |
| 413 // Pass the screen size to the helper, so it can clip the invalid region if it | |
| 414 // expands that region to a grid. | |
| 415 helper_.set_size_most_recent(capture_data->size()); | |
| 416 | |
| 417 // In the DAMAGE case, ensure the frame is up-to-date with the previous frame | |
| 418 // if any. If there isn't a previous frame, that means a screen-resolution | |
| 419 // change occurred, and |invalid_rects| will be updated to include the whole | |
| 420 // screen. | |
| 421 if (use_damage_ && queue_.previous_frame()) | |
| 422 SynchronizeFrame(); | |
| 423 | |
| 424 SkRegion invalid_region; | |
| 425 | |
| 426 x_server_pixel_buffer_.Synchronize(); | |
| 427 if (use_damage_ && queue_.previous_frame()) { | |
| 428 // Atomically fetch and clear the damage region. | |
| 429 XDamageSubtract(display_, damage_handle_, None, damage_region_); | |
| 430 int nRects = 0; | |
| 431 XRectangle bounds; | |
| 432 XRectangle* rects = XFixesFetchRegionAndBounds(display_, damage_region_, | |
| 433 &nRects, &bounds); | |
| 434 for (int i=0; i<nRects; ++i) { | |
| 435 invalid_region.op(SkIRect::MakeXYWH(rects[i].x, rects[i].y, | |
| 436 rects[i].width, rects[i].height), | |
| 437 SkRegion::kUnion_Op); | |
| 438 } | |
| 439 XFree(rects); | |
| 440 helper_.InvalidateRegion(invalid_region); | |
| 441 | |
| 442 // Capture the damaged portions of the desktop. | |
| 443 helper_.SwapInvalidRegion(&invalid_region); | |
| 444 for (SkRegion::Iterator it(invalid_region); !it.done(); it.next()) { | |
| 445 CaptureRect(it.rect(), capture_data); | |
| 446 } | |
| 447 } else { | |
| 448 // Doing full-screen polling, or this is the first capture after a | |
| 449 // screen-resolution change. In either case, need a full-screen capture. | |
| 450 SkIRect screen_rect = SkIRect::MakeWH(current->dimensions().width(), | |
| 451 current->dimensions().height()); | |
| 452 CaptureRect(screen_rect, capture_data); | |
| 453 | |
| 454 if (queue_.previous_frame()) { | |
| 455 // Full-screen polling, so calculate the invalid rects here, based on the | |
| 456 // changed pixels between current and previous buffers. | |
| 457 DCHECK(differ_ != NULL); | |
| 458 differ_->CalcDirtyRegion(queue_.previous_frame()->pixels(), | |
| 459 current->pixels(), &invalid_region); | |
| 460 } else { | |
| 461 // No previous buffer, so always invalidate the whole screen, whether | |
| 462 // or not DAMAGE is being used. DAMAGE doesn't necessarily send a | |
| 463 // full-screen notification after a screen-resolution change, so | |
| 464 // this is done here. | |
| 465 invalid_region.op(screen_rect, SkRegion::kUnion_Op); | |
| 466 } | |
| 467 } | |
| 468 | |
| 469 capture_data->mutable_dirty_region() = invalid_region; | |
| 470 return capture_data; | |
| 471 } | |
| 472 | |
| 473 void VideoFrameCapturerLinux::ScreenConfigurationChanged( | |
| 474 const SkISize& root_window_size) { | |
| 475 root_window_size_ = root_window_size; | |
| 476 | |
| 477 // Make sure the frame buffers will be reallocated. | |
| 478 queue_.SetAllFramesNeedUpdate(); | |
| 479 | |
| 480 helper_.ClearInvalidRegion(); | |
| 481 x_server_pixel_buffer_.Init(display_); | |
| 482 } | |
| 483 | |
| 484 void VideoFrameCapturerLinux::SynchronizeFrame() { | |
| 485 // Synchronize the current buffer with the previous one since we do not | |
| 486 // capture the entire desktop. Note that encoder may be reading from the | |
| 487 // previous buffer at this time so thread access complaints are false | |
| 488 // positives. | |
| 489 | |
| 490 // TODO(hclam): We can reduce the amount of copying here by subtracting | |
| 491 // |capturer_helper_|s region from |last_invalid_region_|. | |
| 492 // http://crbug.com/92354 | |
| 493 DCHECK(queue_.previous_frame()); | |
| 494 | |
| 495 VideoFrame* current = queue_.current_frame(); | |
| 496 VideoFrame* last = queue_.previous_frame(); | |
| 497 DCHECK_NE(current, last); | |
| 498 for (SkRegion::Iterator it(last_invalid_region_); !it.done(); it.next()) { | |
| 499 const SkIRect& r = it.rect(); | |
| 500 int offset = r.fTop * current->bytes_per_row() + r.fLeft * kBytesPerPixel; | |
| 501 for (int i = 0; i < r.height(); ++i) { | |
| 502 memcpy(current->pixels() + offset, last->pixels() + offset, | |
| 503 r.width() * kBytesPerPixel); | |
| 504 offset += current->dimensions().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 scoped_ptr<VideoFrameCapturer> VideoFrameCapturer::Create() { | |
| 625 scoped_ptr<VideoFrameCapturerLinux> capturer(new VideoFrameCapturerLinux()); | |
| 626 if (!capturer->Init()) | |
| 627 capturer.reset(); | |
| 628 return capturer.PassAs<VideoFrameCapturer>(); | |
| 629 } | |
| 630 | |
| 631 // static | |
| 632 scoped_ptr<VideoFrameCapturer> VideoFrameCapturer::CreateWithFactory( | |
| 633 SharedBufferFactory* shared_buffer_factory) { | |
| 634 NOTIMPLEMENTED(); | |
| 635 return scoped_ptr<VideoFrameCapturer>(); | |
| 636 } | |
| 637 | |
| 638 // static | |
| 639 void VideoFrameCapturer::EnableXDamage(bool enable) { | |
| 640 g_should_use_x_damage = enable; | |
| 641 } | |
| 642 | |
| 643 } // namespace remoting | |
| OLD | NEW |