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