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