Fix DesktopAndCursorComposer to restore frames to the original state.

Screen capturers may reuse frame buffers and they expect that the
frame content isn't changed by the frame consumer.
DesktopAndCursorComposer draws mouse cursor on generated frames and
it was releasing the frames with the mouse cursor on them. Fixed
it to restore frame content erasing mouse cursor before returning
desktop frames.

BUG=crbug.com/316297
R=wez@chromium.org

Review URL: https://webrtc-codereview.appspot.com/3899004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5133 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
sergeyu@chromium.org 2013-11-19 02:15:47 +00:00
parent 7a05ae5c69
commit 5d85819dd2
7 changed files with 138 additions and 27 deletions

View File

@ -51,6 +51,73 @@ void AlphaBlend(uint8_t* dest, int dest_stride,
}
}
// DesktopFrame wrapper that draws mouse on a frame and restores original
// content before releasing the underlying frame.
class DesktopFrameWithCursor : public DesktopFrame {
public:
// Takes ownership of |frame|.
DesktopFrameWithCursor(DesktopFrame* frame,
const MouseCursor& cursor,
const DesktopVector& position);
virtual ~DesktopFrameWithCursor();
private:
scoped_ptr<DesktopFrame> original_frame_;
DesktopVector restore_position_;
scoped_ptr<DesktopFrame> restore_frame_;
DISALLOW_COPY_AND_ASSIGN(DesktopFrameWithCursor);
};
DesktopFrameWithCursor::DesktopFrameWithCursor(DesktopFrame* frame,
const MouseCursor& cursor,
const DesktopVector& position)
: DesktopFrame(frame->size(), frame->stride(),
frame->data(), frame->shared_memory()),
original_frame_(frame) {
set_dpi(frame->dpi());
set_capture_time_ms(frame->capture_time_ms());
mutable_updated_region()->Swap(frame->mutable_updated_region());
DesktopVector image_pos = position.subtract(cursor.hotspot());
DesktopRect target_rect = DesktopRect::MakeSize(cursor.image().size());
target_rect.Translate(image_pos);
DesktopVector target_origin = target_rect.top_left();
target_rect.IntersectWith(DesktopRect::MakeSize(size()));
if (target_rect.is_empty())
return;
// Copy original screen content under cursor to |restore_frame_|.
restore_position_ = target_rect.top_left();
restore_frame_.reset(new BasicDesktopFrame(target_rect.size()));
restore_frame_->CopyPixelsFrom(*this, target_rect.top_left(),
DesktopRect::MakeSize(restore_frame_->size()));
// Blit the cursor.
uint8_t* target_rect_data = reinterpret_cast<uint8_t*>(data()) +
target_rect.top() * stride() +
target_rect.left() * DesktopFrame::kBytesPerPixel;
DesktopVector origin_shift = target_rect.top_left().subtract(target_origin);
AlphaBlend(target_rect_data, stride(),
cursor.image().data() +
origin_shift.y() * cursor.image().stride() +
origin_shift.x() * DesktopFrame::kBytesPerPixel,
cursor.image().stride(),
target_rect.size());
}
DesktopFrameWithCursor::~DesktopFrameWithCursor() {
// Restore original content of the frame.
if (restore_frame_.get()) {
DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size());
target_rect.Translate(restore_position_);
CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(),
target_rect);
}
}
} // namespace
DesktopAndCursorComposer::DesktopAndCursorComposer(
@ -81,22 +148,9 @@ SharedMemory* DesktopAndCursorComposer::CreateSharedMemory(size_t size) {
void DesktopAndCursorComposer::OnCaptureCompleted(DesktopFrame* frame) {
if (cursor_.get() && cursor_state_ == MouseCursorMonitor::INSIDE) {
DesktopVector image_pos = cursor_position_.subtract(cursor_->hotspot());
DesktopRect target_rect = DesktopRect::MakeSize(cursor_->image().size());
target_rect.Translate(image_pos);
DesktopVector target_origin = target_rect.top_left();
target_rect.IntersectWith(DesktopRect::MakeSize(frame->size()));
DesktopVector origin_shift = target_rect.top_left().subtract(target_origin);
int cursor_width = cursor_->image().size().width();
AlphaBlend(reinterpret_cast<uint8_t*>(frame->data()) +
target_rect.top() * frame->stride() +
target_rect.left() * DesktopFrame::kBytesPerPixel,
frame->stride(),
cursor_->image().data() +
(origin_shift.y() * cursor_width + origin_shift.x()) *
DesktopFrame::kBytesPerPixel,
cursor_width * DesktopFrame::kBytesPerPixel,
target_rect.size());
DesktopFrameWithCursor* frame_with_cursor =
new DesktopFrameWithCursor(frame, *cursor_, cursor_position_);
frame = frame_with_cursor;
}
callback_->OnCaptureCompleted(frame);

View File

@ -14,6 +14,7 @@
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
#include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
#include "webrtc/modules/desktop_capture/window_capturer.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
@ -74,11 +75,19 @@ class FakeScreenCapturer : public DesktopCapturer {
*(data++) = GetFakeFramePixelValue(DesktopVector(x, y));
}
}
callback_->OnCaptureCompleted(frame);
last_frame_.reset(SharedDesktopFrame::Wrap(frame));
callback_->OnCaptureCompleted(last_frame_->Share());
}
// Returns last fake captured frame.
SharedDesktopFrame* last_frame() { return last_frame_.get(); }
private:
Callback* callback_;
scoped_ptr<SharedDesktopFrame> last_frame_;
};
class FakeMouseMonitor : public MouseCursorMonitor {
@ -155,8 +164,9 @@ class DesktopAndCursorComposerTest : public testing::Test,
public DesktopCapturer::Callback {
public:
DesktopAndCursorComposerTest()
: fake_cursor_(new FakeMouseMonitor()),
blender_(new FakeScreenCapturer(), fake_cursor_) {
: fake_screen_(new FakeScreenCapturer()),
fake_cursor_(new FakeMouseMonitor()),
blender_(fake_screen_, fake_cursor_) {
}
// DesktopCapturer::Callback interface
@ -170,7 +180,9 @@ class DesktopAndCursorComposerTest : public testing::Test,
protected:
// Owned by |blender_|.
FakeScreenCapturer* fake_screen_;
FakeMouseMonitor* fake_cursor_;
DesktopAndCursorComposer blender_;
scoped_ptr<DesktopFrame> frame_;
};
@ -213,6 +225,13 @@ TEST_F(DesktopAndCursorComposerTest, Blend) {
blender_.Capture(DesktopRegion());
VerifyFrame(*frame_, state, pos);
// Verify that the cursor is erased before the frame buffer is returned to
// the screen capturer.
frame_.reset();
VerifyFrame(*fake_screen_->last_frame(),
MouseCursorMonitor::OUTSIDE,
DesktopVector());
}
}

View File

@ -10,6 +10,7 @@
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include <assert.h>
#include <string.h>
namespace webrtc {
@ -27,6 +28,30 @@ DesktopFrame::DesktopFrame(DesktopSize size,
DesktopFrame::~DesktopFrame() {}
void DesktopFrame::CopyPixelsFrom(uint8_t* src_buffer, int src_stride,
const DesktopRect& dest_rect) {
assert(DesktopRect::MakeSize(size()).ContainsRect(dest_rect));
uint8_t* dest = data() + stride() * dest_rect.top() +
DesktopFrame::kBytesPerPixel * dest_rect.left();
for (int y = 0; y < dest_rect.height(); ++y) {
memcpy(dest, src_buffer, DesktopFrame::kBytesPerPixel * dest_rect.width());
src_buffer += src_stride;
dest += stride();
}
}
void DesktopFrame::CopyPixelsFrom(const DesktopFrame& src_frame,
const DesktopVector& src_pos,
const DesktopRect& dest_rect) {
assert(DesktopRect::MakeSize(src_frame.size()).ContainsRect(
DesktopRect::MakeOriginSize(src_pos, dest_rect.size())));
CopyPixelsFrom(src_frame.data() + src_frame.stride() * src_pos.y() +
DesktopFrame::kBytesPerPixel * src_pos.x(),
src_frame.stride(), dest_rect);
}
BasicDesktopFrame::BasicDesktopFrame(DesktopSize size)
: DesktopFrame(size, kBytesPerPixel * size.width(),
new uint8_t[kBytesPerPixel * size.width() * size.height()],

View File

@ -53,6 +53,14 @@ class DesktopFrame {
int32_t capture_time_ms() const { return capture_time_ms_; }
void set_capture_time_ms(int32_t time_ms) { capture_time_ms_ = time_ms; }
// Copies pixels from a buffer or another frame. |dest_rect| rect must lay
// within bounds of this frame.
void CopyPixelsFrom(uint8_t* src_buffer, int src_stride,
const DesktopRect& dest_rect);
void CopyPixelsFrom(const DesktopFrame& src_frame,
const DesktopVector& src_pos,
const DesktopRect& dest_rect);
protected:
DesktopFrame(DesktopSize size,
int stride,

View File

@ -19,6 +19,11 @@ bool DesktopRect::Contains(const DesktopVector& point) const {
point.y() >= top() && point.y() < bottom();
}
bool DesktopRect::ContainsRect(const DesktopRect& rect) const {
return rect.left() >= left() && rect.right() <= right() &&
rect.top() >= top() && rect.bottom() <= bottom();
}
void DesktopRect::IntersectWith(const DesktopRect& rect) {
left_ = std::max(left(), rect.left());
top_ = std::max(top(), rect.top());

View File

@ -91,6 +91,10 @@ class DesktopRect {
int32_t right, int32_t bottom) {
return DesktopRect(left, top, right, bottom);
}
static DesktopRect MakeOriginSize(const DesktopVector& origin,
const DesktopSize& size) {
return MakeXYWH(origin.x(), origin.y(), size.width(), size.height());
}
DesktopRect() : left_(0), top_(0), right_(0), bottom_(0) {}
@ -114,6 +118,9 @@ class DesktopRect {
// Returns true if |point| lies within the rectangle boundaries.
bool Contains(const DesktopVector& point) const;
// Returns true if |rect| lies within the boundaries of this rectangle.
bool ContainsRect(const DesktopRect& rect) const;
// Finds intersection with |rect|.
void IntersectWith(const DesktopRect& rect);

View File

@ -462,14 +462,7 @@ void ScreenCapturerLinux::SynchronizeFrame() {
DCHECK(current != last);
for (DesktopRegion::Iterator it(last_invalid_region_);
!it.IsAtEnd(); it.Advance()) {
const DesktopRect& r = it.rect();
int offset = r.top() * current->stride() +
r.left() * DesktopFrame::kBytesPerPixel;
for (int i = 0; i < r.height(); ++i) {
memcpy(current->data() + offset, last->data() + offset,
r.width() * DesktopFrame::kBytesPerPixel);
offset += current->size().width() * DesktopFrame::kBytesPerPixel;
}
current->CopyPixelsFrom(*last, it.rect().top_left(), it.rect());
}
}