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