Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(402)

Side by Side Diff: remoting/host/video_frame_capturer_linux.cc

Issue 11470028: Move screen capturers to remoting/capturer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698