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 "remoting/host/video_frame_capturer.h" | 5 #include "remoting/host/video_frame_capturer.h" |
6 | 6 |
7 #include <X11/Xlib.h> | 7 #include <X11/Xlib.h> |
8 #include <X11/Xutil.h> | 8 #include <X11/Xutil.h> |
9 #include <X11/extensions/Xdamage.h> | 9 #include <X11/extensions/Xdamage.h> |
10 #include <X11/extensions/Xfixes.h> | 10 #include <X11/extensions/Xfixes.h> |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 ptr_.reset(new uint8[buffer_size]); | 56 ptr_.reset(new uint8[buffer_size]); |
57 } | 57 } |
58 } | 58 } |
59 } | 59 } |
60 | 60 |
61 SkISize size() const { return size_; } | 61 SkISize size() const { return size_; } |
62 int bytes_per_row() const { return bytes_per_row_; } | 62 int bytes_per_row() const { return bytes_per_row_; } |
63 uint8* ptr() const { return ptr_.get(); } | 63 uint8* ptr() const { return ptr_.get(); } |
64 | 64 |
65 void set_needs_update() { needs_update_ = true; } | 65 void set_needs_update() { needs_update_ = true; } |
| 66 bool needs_update() const { return needs_update_; } |
66 | 67 |
67 private: | 68 private: |
68 SkISize size_; | 69 SkISize size_; |
69 int bytes_per_row_; | 70 int bytes_per_row_; |
70 scoped_array<uint8> ptr_; | 71 scoped_array<uint8> ptr_; |
71 bool needs_update_; | 72 bool needs_update_; |
72 | 73 |
73 DISALLOW_COPY_AND_ASSIGN(VideoFrameBuffer); | 74 DISALLOW_COPY_AND_ASSIGN(VideoFrameBuffer); |
74 }; | 75 }; |
75 | 76 |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
167 int current_buffer_; | 168 int current_buffer_; |
168 | 169 |
169 // Format of pixels returned in buffer. | 170 // Format of pixels returned in buffer. |
170 media::VideoFrame::Format pixel_format_; | 171 media::VideoFrame::Format pixel_format_; |
171 | 172 |
172 // Invalid region from the previous capture. This is used to synchronize the | 173 // Invalid region from the previous capture. This is used to synchronize the |
173 // current with the last buffer used. | 174 // current with the last buffer used. |
174 SkRegion last_invalid_region_; | 175 SkRegion last_invalid_region_; |
175 | 176 |
176 // Last capture buffer used. | 177 // Last capture buffer used. |
177 uint8* last_buffer_; | 178 int last_buffer_; |
178 | 179 |
179 // |Differ| for use when polling for changes. | 180 // |Differ| for use when polling for changes. |
180 scoped_ptr<Differ> differ_; | 181 scoped_ptr<Differ> differ_; |
181 | 182 |
182 DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerLinux); | 183 DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerLinux); |
183 }; | 184 }; |
184 | 185 |
185 VideoFrameCapturerLinux::VideoFrameCapturerLinux() | 186 VideoFrameCapturerLinux::VideoFrameCapturerLinux() |
186 : display_(NULL), | 187 : display_(NULL), |
187 gc_(NULL), | 188 gc_(NULL), |
188 root_window_(BadValue), | 189 root_window_(BadValue), |
189 has_xfixes_(false), | 190 has_xfixes_(false), |
190 xfixes_event_base_(-1), | 191 xfixes_event_base_(-1), |
191 xfixes_error_base_(-1), | 192 xfixes_error_base_(-1), |
192 use_damage_(false), | 193 use_damage_(false), |
193 damage_handle_(0), | 194 damage_handle_(0), |
194 damage_event_base_(-1), | 195 damage_event_base_(-1), |
195 damage_error_base_(-1), | 196 damage_error_base_(-1), |
196 damage_region_(0), | 197 damage_region_(0), |
197 current_buffer_(0), | 198 current_buffer_(0), |
198 pixel_format_(media::VideoFrame::RGB32), | 199 pixel_format_(media::VideoFrame::RGB32), |
199 last_buffer_(NULL) { | 200 last_buffer_(kNumBuffers - 1) { |
200 helper_.SetLogGridSize(4); | 201 helper_.SetLogGridSize(4); |
201 } | 202 } |
202 | 203 |
203 VideoFrameCapturerLinux::~VideoFrameCapturerLinux() { | 204 VideoFrameCapturerLinux::~VideoFrameCapturerLinux() { |
204 DeinitXlib(); | 205 DeinitXlib(); |
205 } | 206 } |
206 | 207 |
207 bool VideoFrameCapturerLinux::Init() { | 208 bool VideoFrameCapturerLinux::Init() { |
208 // TODO(ajwong): We should specify the display string we are attaching to | 209 // TODO(ajwong): We should specify the display string we are attaching to |
209 // in the constructor. | 210 // in the constructor. |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 | 311 |
311 void VideoFrameCapturerLinux::CaptureInvalidRegion( | 312 void VideoFrameCapturerLinux::CaptureInvalidRegion( |
312 const CaptureCompletedCallback& callback) { | 313 const CaptureCompletedCallback& callback) { |
313 // Process XEvents for XDamage and cursor shape tracking. | 314 // Process XEvents for XDamage and cursor shape tracking. |
314 ProcessPendingXEvents(); | 315 ProcessPendingXEvents(); |
315 | 316 |
316 // Resize the current buffer if there was a recent change of | 317 // Resize the current buffer if there was a recent change of |
317 // screen-resolution. | 318 // screen-resolution. |
318 VideoFrameBuffer ¤t = buffers_[current_buffer_]; | 319 VideoFrameBuffer ¤t = buffers_[current_buffer_]; |
319 current.Update(display_, root_window_); | 320 current.Update(display_, root_window_); |
320 // Also refresh the Differ helper used by CaptureFrame(), if needed. | 321 |
321 if (!use_damage_ && !last_buffer_) { | 322 // Mark the previous frame for update if its dimensions no longer match. |
322 differ_.reset(new Differ(current.size().width(), current.size().height(), | 323 if (buffers_[last_buffer_].size() != current.size()) { |
323 kBytesPerPixel, current.bytes_per_row())); | 324 buffers_[last_buffer_].set_needs_update(); |
| 325 |
| 326 // Also refresh the Differ helper used by CaptureFrame(), if needed. |
| 327 if (!use_damage_) { |
| 328 differ_.reset(new Differ(current.size().width(), current.size().height(), |
| 329 kBytesPerPixel, current.bytes_per_row())); |
| 330 } |
324 } | 331 } |
325 | 332 |
326 scoped_refptr<CaptureData> capture_data(CaptureFrame()); | 333 scoped_refptr<CaptureData> capture_data(CaptureFrame()); |
327 | 334 |
| 335 // Swap the current & previous buffers ready for the next capture. |
| 336 last_invalid_region_ = capture_data->dirty_region(); |
| 337 last_buffer_ = current_buffer_; |
328 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; | 338 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; |
329 | 339 |
330 callback.Run(capture_data); | 340 callback.Run(capture_data); |
331 } | 341 } |
332 | 342 |
333 void VideoFrameCapturerLinux::ProcessPendingXEvents() { | 343 void VideoFrameCapturerLinux::ProcessPendingXEvents() { |
334 // Find the number of events that are outstanding "now." We don't just loop | 344 // Find the number of events that are outstanding "now." We don't just loop |
335 // on XPending because we want to guarantee this terminates. | 345 // on XPending because we want to guarantee this terminates. |
336 int events_to_process = XPending(display_); | 346 int events_to_process = XPending(display_); |
337 XEvent e; | 347 XEvent e; |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
403 media::VideoFrame::RGB32); | 413 media::VideoFrame::RGB32); |
404 | 414 |
405 // Pass the screen size to the helper, so it can clip the invalid region if it | 415 // Pass the screen size to the helper, so it can clip the invalid region if it |
406 // expands that region to a grid. | 416 // expands that region to a grid. |
407 helper_.set_size_most_recent(capture_data->size()); | 417 helper_.set_size_most_recent(capture_data->size()); |
408 | 418 |
409 // In the DAMAGE case, ensure the frame is up-to-date with the previous frame | 419 // In the DAMAGE case, ensure the frame is up-to-date with the previous frame |
410 // if any. If there isn't a previous frame, that means a screen-resolution | 420 // if any. If there isn't a previous frame, that means a screen-resolution |
411 // change occurred, and |invalid_rects| will be updated to include the whole | 421 // change occurred, and |invalid_rects| will be updated to include the whole |
412 // screen. | 422 // screen. |
413 if (use_damage_ && last_buffer_) | 423 if (use_damage_ && !buffers_[last_buffer_].needs_update()) |
414 SynchronizeFrame(); | 424 SynchronizeFrame(); |
415 | 425 |
416 SkRegion invalid_region; | 426 SkRegion invalid_region; |
417 | 427 |
418 x_server_pixel_buffer_.Synchronize(); | 428 x_server_pixel_buffer_.Synchronize(); |
419 if (use_damage_ && last_buffer_) { | 429 if (use_damage_ && !buffers_[last_buffer_].needs_update()) { |
420 // Atomically fetch and clear the damage region. | 430 // Atomically fetch and clear the damage region. |
421 XDamageSubtract(display_, damage_handle_, None, damage_region_); | 431 XDamageSubtract(display_, damage_handle_, None, damage_region_); |
422 int nRects = 0; | 432 int nRects = 0; |
423 XRectangle bounds; | 433 XRectangle bounds; |
424 XRectangle* rects = XFixesFetchRegionAndBounds(display_, damage_region_, | 434 XRectangle* rects = XFixesFetchRegionAndBounds(display_, damage_region_, |
425 &nRects, &bounds); | 435 &nRects, &bounds); |
426 for (int i=0; i<nRects; ++i) { | 436 for (int i=0; i<nRects; ++i) { |
427 invalid_region.op(SkIRect::MakeXYWH(rects[i].x, rects[i].y, | 437 invalid_region.op(SkIRect::MakeXYWH(rects[i].x, rects[i].y, |
428 rects[i].width, rects[i].height), | 438 rects[i].width, rects[i].height), |
429 SkRegion::kUnion_Op); | 439 SkRegion::kUnion_Op); |
430 } | 440 } |
431 XFree(rects); | 441 XFree(rects); |
432 helper_.InvalidateRegion(invalid_region); | 442 helper_.InvalidateRegion(invalid_region); |
433 | 443 |
434 // Capture the damaged portions of the desktop. | 444 // Capture the damaged portions of the desktop. |
435 helper_.SwapInvalidRegion(&invalid_region); | 445 helper_.SwapInvalidRegion(&invalid_region); |
436 for (SkRegion::Iterator it(invalid_region); !it.done(); it.next()) { | 446 for (SkRegion::Iterator it(invalid_region); !it.done(); it.next()) { |
437 CaptureRect(it.rect(), capture_data); | 447 CaptureRect(it.rect(), capture_data); |
438 } | 448 } |
439 } else { | 449 } else { |
440 // Doing full-screen polling, or this is the first capture after a | 450 // Doing full-screen polling, or this is the first capture after a |
441 // screen-resolution change. In either case, need a full-screen capture. | 451 // screen-resolution change. In either case, need a full-screen capture. |
442 SkIRect screen_rect = SkIRect::MakeWH(buffer.size().width(), | 452 SkIRect screen_rect = SkIRect::MakeWH(buffer.size().width(), |
443 buffer.size().height()); | 453 buffer.size().height()); |
444 CaptureRect(screen_rect, capture_data); | 454 CaptureRect(screen_rect, capture_data); |
445 | 455 |
446 if (last_buffer_) { | 456 if (!buffers_[last_buffer_].needs_update()) { |
447 // Full-screen polling, so calculate the invalid rects here, based on the | 457 // Full-screen polling, so calculate the invalid rects here, based on the |
448 // changed pixels between current and previous buffers. | 458 // changed pixels between current and previous buffers. |
449 DCHECK(differ_ != NULL); | 459 DCHECK(differ_ != NULL); |
450 differ_->CalcDirtyRegion(last_buffer_, buffer.ptr(), &invalid_region); | 460 VideoFrameBuffer& last_buffer = buffers_[last_buffer_]; |
| 461 differ_->CalcDirtyRegion( |
| 462 last_buffer.ptr(), buffer.ptr(), &invalid_region); |
451 } else { | 463 } else { |
452 // No previous buffer, so always invalidate the whole screen, whether | 464 // No previous buffer, so always invalidate the whole screen, whether |
453 // or not DAMAGE is being used. DAMAGE doesn't necessarily send a | 465 // or not DAMAGE is being used. DAMAGE doesn't necessarily send a |
454 // full-screen notification after a screen-resolution change, so | 466 // full-screen notification after a screen-resolution change, so |
455 // this is done here. | 467 // this is done here. |
456 invalid_region.op(screen_rect, SkRegion::kUnion_Op); | 468 invalid_region.op(screen_rect, SkRegion::kUnion_Op); |
457 } | 469 } |
458 } | 470 } |
459 | 471 |
460 capture_data->mutable_dirty_region() = invalid_region; | 472 capture_data->mutable_dirty_region() = invalid_region; |
461 last_invalid_region_ = invalid_region; | |
462 last_buffer_ = buffer.ptr(); | |
463 return capture_data; | 473 return capture_data; |
464 } | 474 } |
465 | 475 |
466 void VideoFrameCapturerLinux::ScreenConfigurationChanged() { | 476 void VideoFrameCapturerLinux::ScreenConfigurationChanged() { |
467 last_buffer_ = NULL; | |
468 for (int i = 0; i < kNumBuffers; ++i) { | 477 for (int i = 0; i < kNumBuffers; ++i) { |
469 buffers_[i].set_needs_update(); | 478 buffers_[i].set_needs_update(); |
470 } | 479 } |
471 helper_.ClearInvalidRegion(); | 480 helper_.ClearInvalidRegion(); |
472 x_server_pixel_buffer_.Init(display_); | 481 x_server_pixel_buffer_.Init(display_); |
473 } | 482 } |
474 | 483 |
475 void VideoFrameCapturerLinux::SynchronizeFrame() { | 484 void VideoFrameCapturerLinux::SynchronizeFrame() { |
476 // Synchronize the current buffer with the previous one since we do not | 485 // Synchronize the current buffer with the previous one since we do not |
477 // capture the entire desktop. Note that encoder may be reading from the | 486 // capture the entire desktop. Note that encoder may be reading from the |
478 // previous buffer at this time so thread access complaints are false | 487 // previous buffer at this time so thread access complaints are false |
479 // positives. | 488 // positives. |
480 | 489 |
481 // TODO(hclam): We can reduce the amount of copying here by subtracting | 490 // TODO(hclam): We can reduce the amount of copying here by subtracting |
482 // |capturer_helper_|s region from |last_invalid_region_|. | 491 // |capturer_helper_|s region from |last_invalid_region_|. |
483 // http://crbug.com/92354 | 492 // http://crbug.com/92354 |
484 DCHECK(last_buffer_); | 493 DCHECK(!buffers_[last_buffer_].needs_update()); |
| 494 DCHECK_NE(last_buffer_, current_buffer_); |
485 VideoFrameBuffer& buffer = buffers_[current_buffer_]; | 495 VideoFrameBuffer& buffer = buffers_[current_buffer_]; |
| 496 VideoFrameBuffer& last_buffer = buffers_[last_buffer_]; |
486 for (SkRegion::Iterator it(last_invalid_region_); !it.done(); it.next()) { | 497 for (SkRegion::Iterator it(last_invalid_region_); !it.done(); it.next()) { |
487 const SkIRect& r = it.rect(); | 498 const SkIRect& r = it.rect(); |
488 int offset = r.fTop * buffer.bytes_per_row() + r.fLeft * kBytesPerPixel; | 499 int offset = r.fTop * buffer.bytes_per_row() + r.fLeft * kBytesPerPixel; |
489 for (int i = 0; i < r.height(); ++i) { | 500 for (int i = 0; i < r.height(); ++i) { |
490 memcpy(buffer.ptr() + offset, last_buffer_ + offset, | 501 memcpy(buffer.ptr() + offset, last_buffer.ptr() + offset, |
491 r.width() * kBytesPerPixel); | 502 r.width() * kBytesPerPixel); |
492 offset += buffer.size().width() * kBytesPerPixel; | 503 offset += buffer.size().width() * kBytesPerPixel; |
493 } | 504 } |
494 } | 505 } |
495 } | 506 } |
496 | 507 |
497 void VideoFrameCapturerLinux::DeinitXlib() { | 508 void VideoFrameCapturerLinux::DeinitXlib() { |
498 if (gc_) { | 509 if (gc_) { |
499 XFreeGC(display_, gc_); | 510 XFreeGC(display_, gc_); |
500 gc_ = NULL; | 511 gc_ = NULL; |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
617 } | 628 } |
618 return capturer; | 629 return capturer; |
619 } | 630 } |
620 | 631 |
621 // static | 632 // static |
622 void VideoFrameCapturer::EnableXDamage(bool enable) { | 633 void VideoFrameCapturer::EnableXDamage(bool enable) { |
623 g_should_use_x_damage = enable; | 634 g_should_use_x_damage = enable; |
624 } | 635 } |
625 | 636 |
626 } // namespace remoting | 637 } // namespace remoting |
OLD | NEW |