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