summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-10 05:00:14 +0000
committeralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-10 05:00:14 +0000
commit3c2a701bb59b792becab4496d6dfa8fb2e891085 (patch)
tree86acff92d453e09f9435355bb3787ff6d7f27e79
parent293a0ee99b970d702c25279616ba9d7d9485bb36 (diff)
downloadchromium_src-3c2a701bb59b792becab4496d6dfa8fb2e891085.zip
chromium_src-3c2a701bb59b792becab4496d6dfa8fb2e891085.tar.gz
chromium_src-3c2a701bb59b792becab4496d6dfa8fb2e891085.tar.bz2
Brushing up video capturers to make them more uniform.
- Video frame buffers are derived from common VideoFrameBuffer class (a data structure really). - The video frame queue logic was moved to VideoFrameCapturerQueue class. BUG=134694 Review URL: https://chromiumcodereview.appspot.com/11369120 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@167058 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--remoting/host/video_frame.cc18
-rw-r--r--remoting/host/video_frame.h52
-rw-r--r--remoting/host/video_frame_capturer_linux.cc169
-rw-r--r--remoting/host/video_frame_capturer_mac.mm184
-rw-r--r--remoting/host/video_frame_capturer_win.cc268
-rw-r--r--remoting/host/video_frame_queue.cc38
-rw-r--r--remoting/host/video_frame_queue.h69
-rw-r--r--remoting/remoting.gyp4
8 files changed, 477 insertions, 325 deletions
diff --git a/remoting/host/video_frame.cc b/remoting/host/video_frame.cc
new file mode 100644
index 0000000..67859a0
--- /dev/null
+++ b/remoting/host/video_frame.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/video_frame.h"
+
+namespace remoting {
+
+VideoFrame::~VideoFrame() {
+}
+
+VideoFrame::VideoFrame()
+ : bytes_per_row_(0),
+ dimensions_(SkISize::Make(0, 0)),
+ pixels_(NULL) {
+}
+
+} // namespace remoting
diff --git a/remoting/host/video_frame.h b/remoting/host/video_frame.h
new file mode 100644
index 0000000..cd7c093
--- /dev/null
+++ b/remoting/host/video_frame.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_HOST_VIDEO_FRAME_H_
+#define REMOTING_HOST_VIDEO_FRAME_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "third_party/skia/include/core/SkTypes.h"
+#include "third_party/skia/include/core/SkSize.h"
+
+namespace remoting {
+
+// Represents a video frame.
+class VideoFrame {
+ public:
+ virtual ~VideoFrame();
+
+ int bytes_per_row() const { return bytes_per_row_; }
+ const SkISize& dimensions() const { return dimensions_; }
+ uint8* pixels() const { return pixels_; }
+
+ protected:
+ // Initializes an empty video frame. Derived classes are expected to allocate
+ // memory for the frame in a platform-specific way and set the properties of
+ // the allocated frame.
+ VideoFrame();
+
+ void set_bytes_per_row(int bytes_per_row) {
+ bytes_per_row_ = bytes_per_row;
+ }
+
+ void set_dimensions(const SkISize& dimensions) { dimensions_ = dimensions; }
+ void set_pixels(uint8* ptr) { pixels_ = ptr; }
+
+ private:
+ // Bytes per row of pixels including necessary padding.
+ int bytes_per_row_;
+
+ // Dimensions of the buffer in pixels.
+ SkISize dimensions_;
+
+ // Points to the pixel buffer.
+ uint8* pixels_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrame);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_VIDEO_FRAME_H_
diff --git a/remoting/host/video_frame_capturer_linux.cc b/remoting/host/video_frame_capturer_linux.cc
index 522eac7..945029f 100644
--- a/remoting/host/video_frame_capturer_linux.cc
+++ b/remoting/host/video_frame_capturer_linux.cc
@@ -17,7 +17,9 @@
#include "remoting/base/capture_data.h"
#include "remoting/host/differ.h"
#include "remoting/host/linux/x_server_pixel_buffer.h"
+#include "remoting/host/video_frame.h"
#include "remoting/host/video_frame_capturer_helper.h"
+#include "remoting/host/video_frame_queue.h"
#include "remoting/proto/control.pb.h"
namespace remoting {
@@ -34,44 +36,17 @@ static bool ShouldUseXDamage() {
return g_should_use_x_damage;
}
-// A class representing a full-frame pixel buffer
-class VideoFrameBuffer {
+// A class representing a full-frame pixel buffer.
+class VideoFrameLinux : public VideoFrame {
public:
- VideoFrameBuffer()
- : size_(SkISize::Make(0, 0)),
- bytes_per_row_(0),
- needs_update_(true) {
- }
-
- void Update(Display* display, Window root_window) {
- if (needs_update_) {
- needs_update_ = false;
- XWindowAttributes root_attr;
- XGetWindowAttributes(display, root_window, &root_attr);
- if (root_attr.width != size_.width() ||
- root_attr.height != size_.height()) {
- size_.set(root_attr.width, root_attr.height);
- bytes_per_row_ = size_.width() * kBytesPerPixel;
- size_t buffer_size = size_.width() * size_.height() * kBytesPerPixel;
- ptr_.reset(new uint8[buffer_size]);
- }
- }
- }
-
- SkISize size() const { return size_; }
- int bytes_per_row() const { return bytes_per_row_; }
- uint8* ptr() const { return ptr_.get(); }
-
- void set_needs_update() { needs_update_ = true; }
- bool needs_update() const { return needs_update_; }
+ explicit VideoFrameLinux(const SkISize& window_size);
+ virtual ~VideoFrameLinux();
private:
- SkISize size_;
- int bytes_per_row_;
- scoped_array<uint8> ptr_;
- bool needs_update_;
+ // Allocated pixel buffer.
+ scoped_array<uint8> data_;
- DISALLOW_COPY_AND_ASSIGN(VideoFrameBuffer);
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameLinux);
};
// A class to perform video frame capturing for Linux.
@@ -112,8 +87,9 @@ class VideoFrameCapturerLinux : public VideoFrameCapturer {
// Capture the cursor image and notify the delegate if it was captured.
void CaptureCursor();
- // Called when the screen configuration is changed.
- void ScreenConfigurationChanged();
+ // Called when the screen configuration is changed. |root_window_size|
+ // specifies size the most recent size of the root window.
+ void ScreenConfigurationChanged(const SkISize& root_window_size);
// Synchronize the current buffer with |last_buffer_|, by copying pixels from
// the area of |last_invalid_rects|.
@@ -140,6 +116,9 @@ class VideoFrameCapturerLinux : public VideoFrameCapturer {
GC gc_;
Window root_window_;
+ // Last known dimensions of the root window.
+ SkISize root_window_size_;
+
// XFixes.
bool has_xfixes_;
int xfixes_event_base_;
@@ -159,10 +138,8 @@ class VideoFrameCapturerLinux : public VideoFrameCapturer {
// recently captured screen.
VideoFrameCapturerHelper helper_;
- // Capture state.
- static const int kNumBuffers = 2;
- VideoFrameBuffer buffers_[kNumBuffers];
- int current_buffer_;
+ // Queue of the frames buffers.
+ VideoFrameQueue queue_;
// Format of pixels returned in buffer.
media::VideoFrame::Format pixel_format_;
@@ -171,20 +148,30 @@ class VideoFrameCapturerLinux : public VideoFrameCapturer {
// current with the last buffer used.
SkRegion last_invalid_region_;
- // Last capture buffer used.
- int last_buffer_;
-
// |Differ| for use when polling for changes.
scoped_ptr<Differ> differ_;
DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerLinux);
};
+VideoFrameLinux::VideoFrameLinux(const SkISize& window_size) {
+ set_bytes_per_row(window_size.width() * kBytesPerPixel);
+ set_dimensions(window_size);
+
+ size_t buffer_size = bytes_per_row() * window_size.height();
+ data_.reset(new uint8[buffer_size]);
+ set_pixels(data_.get());
+}
+
+VideoFrameLinux::~VideoFrameLinux() {
+}
+
VideoFrameCapturerLinux::VideoFrameCapturerLinux()
: delegate_(NULL),
display_(NULL),
gc_(NULL),
root_window_(BadValue),
+ root_window_size_(SkISize::Make(0, 0)),
has_xfixes_(false),
xfixes_event_base_(-1),
xfixes_error_base_(-1),
@@ -193,9 +180,7 @@ VideoFrameCapturerLinux::VideoFrameCapturerLinux()
damage_event_base_(-1),
damage_error_base_(-1),
damage_region_(0),
- current_buffer_(0),
- pixel_format_(media::VideoFrame::RGB32),
- last_buffer_(kNumBuffers - 1) {
+ pixel_format_(media::VideoFrame::RGB32) {
helper_.SetLogGridSize(4);
}
@@ -244,6 +229,11 @@ bool VideoFrameCapturerLinux::Init() {
// Register for changes to the dimensions of the root window.
XSelectInput(display_, root_window_, StructureNotifyMask);
+ // Update the root window size.
+ XWindowAttributes root_attributes;
+ XGetWindowAttributes(display_, root_window_, &root_attributes);
+ root_window_size_.set(root_attributes.width, root_attributes.height);
+
if (has_xfixes_) {
// Register for changes to the cursor shape.
XFixesSelectCursorInput(display_, root_window_,
@@ -312,29 +302,34 @@ void VideoFrameCapturerLinux::CaptureInvalidRegion() {
// Process XEvents for XDamage and cursor shape tracking.
ProcessPendingXEvents();
- // Resize the current buffer if there was a recent change of
- // screen-resolution.
- VideoFrameBuffer &current = buffers_[current_buffer_];
- current.Update(display_, root_window_);
-
- // Mark the previous frame for update if its dimensions no longer match.
- if (buffers_[last_buffer_].size() != current.size()) {
- buffers_[last_buffer_].set_needs_update();
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (queue_.current_frame_needs_update()) {
+ scoped_ptr<VideoFrameLinux> buffer(new VideoFrameLinux(
+ root_window_size_));
+ queue_.ReplaceCurrentFrame(buffer.PassAs<VideoFrame>());
+ }
- // Also refresh the Differ helper used by CaptureFrame(), if needed.
- if (!use_damage_) {
- differ_.reset(new Differ(current.size().width(), current.size().height(),
- kBytesPerPixel, current.bytes_per_row()));
- }
+ // Refresh the Differ helper used by CaptureFrame(), if needed.
+ const VideoFrame* current_buffer = queue_.current_frame();
+ if (!use_damage_ && (
+ !differ_.get() ||
+ (differ_->width() != current_buffer->dimensions().width()) ||
+ (differ_->height() != current_buffer->dimensions().height()) ||
+ (differ_->bytes_per_row() != current_buffer->bytes_per_row()))) {
+ differ_.reset(new Differ(current_buffer->dimensions().width(),
+ current_buffer->dimensions().height(),
+ kBytesPerPixel,
+ current_buffer->bytes_per_row()));
}
scoped_refptr<CaptureData> capture_data(CaptureFrame());
// Swap the current & previous buffers ready for the next capture.
last_invalid_region_ = capture_data->dirty_region();
- last_buffer_ = current_buffer_;
- current_buffer_ = (current_buffer_ + 1) % kNumBuffers;
+ queue_.DoneWithCurrentFrame();
delegate_->OnCaptureCompleted(capture_data);
}
@@ -350,7 +345,8 @@ void VideoFrameCapturerLinux::ProcessPendingXEvents() {
XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
DCHECK(event->level == XDamageReportNonEmpty);
} else if (e.type == ConfigureNotify) {
- ScreenConfigurationChanged();
+ const XConfigureEvent& event = e.xconfigure;
+ ScreenConfigurationChanged(SkISize::Make(event.width, event.height));
} else if (has_xfixes_ &&
e.type == xfixes_event_base_ + XFixesCursorNotify) {
XFixesCursorNotifyEvent* cne;
@@ -400,12 +396,12 @@ void VideoFrameCapturerLinux::CaptureCursor() {
}
CaptureData* VideoFrameCapturerLinux::CaptureFrame() {
- VideoFrameBuffer& buffer = buffers_[current_buffer_];
+ VideoFrame* current = queue_.current_frame();
DataPlanes planes;
- planes.data[0] = buffer.ptr();
- planes.strides[0] = buffer.bytes_per_row();
+ planes.data[0] = current->pixels();
+ planes.strides[0] = current->bytes_per_row();
- CaptureData* capture_data = new CaptureData(planes, buffer.size(),
+ CaptureData* capture_data = new CaptureData(planes, current->dimensions(),
media::VideoFrame::RGB32);
// Pass the screen size to the helper, so it can clip the invalid region if it
@@ -416,13 +412,13 @@ CaptureData* VideoFrameCapturerLinux::CaptureFrame() {
// if any. If there isn't a previous frame, that means a screen-resolution
// change occurred, and |invalid_rects| will be updated to include the whole
// screen.
- if (use_damage_ && !buffers_[last_buffer_].needs_update())
+ if (use_damage_ && queue_.previous_frame())
SynchronizeFrame();
SkRegion invalid_region;
x_server_pixel_buffer_.Synchronize();
- if (use_damage_ && !buffers_[last_buffer_].needs_update()) {
+ if (use_damage_ && queue_.previous_frame()) {
// Atomically fetch and clear the damage region.
XDamageSubtract(display_, damage_handle_, None, damage_region_);
int nRects = 0;
@@ -445,17 +441,16 @@ CaptureData* VideoFrameCapturerLinux::CaptureFrame() {
} else {
// Doing full-screen polling, or this is the first capture after a
// screen-resolution change. In either case, need a full-screen capture.
- SkIRect screen_rect = SkIRect::MakeWH(buffer.size().width(),
- buffer.size().height());
+ SkIRect screen_rect = SkIRect::MakeWH(current->dimensions().width(),
+ current->dimensions().height());
CaptureRect(screen_rect, capture_data);
- if (!buffers_[last_buffer_].needs_update()) {
+ if (queue_.previous_frame()) {
// Full-screen polling, so calculate the invalid rects here, based on the
// changed pixels between current and previous buffers.
DCHECK(differ_ != NULL);
- VideoFrameBuffer& last_buffer = buffers_[last_buffer_];
- differ_->CalcDirtyRegion(
- last_buffer.ptr(), buffer.ptr(), &invalid_region);
+ differ_->CalcDirtyRegion(queue_.previous_frame()->pixels(),
+ current->pixels(), &invalid_region);
} else {
// No previous buffer, so always invalidate the whole screen, whether
// or not DAMAGE is being used. DAMAGE doesn't necessarily send a
@@ -469,10 +464,13 @@ CaptureData* VideoFrameCapturerLinux::CaptureFrame() {
return capture_data;
}
-void VideoFrameCapturerLinux::ScreenConfigurationChanged() {
- for (int i = 0; i < kNumBuffers; ++i) {
- buffers_[i].set_needs_update();
- }
+void VideoFrameCapturerLinux::ScreenConfigurationChanged(
+ const SkISize& root_window_size) {
+ root_window_size_ = root_window_size;
+
+ // Make sure the frame buffers will be reallocated.
+ queue_.SetAllFramesNeedUpdate();
+
helper_.ClearInvalidRegion();
x_server_pixel_buffer_.Init(display_);
}
@@ -486,17 +484,18 @@ void VideoFrameCapturerLinux::SynchronizeFrame() {
// TODO(hclam): We can reduce the amount of copying here by subtracting
// |capturer_helper_|s region from |last_invalid_region_|.
// http://crbug.com/92354
- DCHECK(!buffers_[last_buffer_].needs_update());
- DCHECK_NE(last_buffer_, current_buffer_);
- VideoFrameBuffer& buffer = buffers_[current_buffer_];
- VideoFrameBuffer& last_buffer = buffers_[last_buffer_];
+ DCHECK(queue_.previous_frame());
+
+ VideoFrame* current = queue_.current_frame();
+ VideoFrame* last = queue_.previous_frame();
+ DCHECK_NE(current, last);
for (SkRegion::Iterator it(last_invalid_region_); !it.done(); it.next()) {
const SkIRect& r = it.rect();
- int offset = r.fTop * buffer.bytes_per_row() + r.fLeft * kBytesPerPixel;
+ int offset = r.fTop * current->bytes_per_row() + r.fLeft * kBytesPerPixel;
for (int i = 0; i < r.height(); ++i) {
- memcpy(buffer.ptr() + offset, last_buffer.ptr() + offset,
+ memcpy(current->pixels() + offset, last->pixels() + offset,
r.width() * kBytesPerPixel);
- offset += buffer.size().width() * kBytesPerPixel;
+ offset += current->dimensions().width() * kBytesPerPixel;
}
}
}
diff --git a/remoting/host/video_frame_capturer_mac.mm b/remoting/host/video_frame_capturer_mac.mm
index 456b01f..c03b443 100644
--- a/remoting/host/video_frame_capturer_mac.mm
+++ b/remoting/host/video_frame_capturer_mac.mm
@@ -23,7 +23,9 @@
#include "remoting/base/capture_data.h"
#include "remoting/base/util.h"
#include "remoting/host/mac/scoped_pixel_buffer_object.h"
+#include "remoting/host/video_frame.h"
#include "remoting/host/video_frame_capturer_helper.h"
+#include "remoting/host/video_frame_queue.h"
#include "remoting/proto/control.pb.h"
namespace remoting {
@@ -56,52 +58,21 @@ SkIRect CGRectToSkIRect(const CGRect& rect) {
const int64 kDisplayReconfigurationTimeoutInSeconds = 10;
// A class representing a full-frame pixel buffer.
-class VideoFrameBuffer {
+class VideoFrameMac : public VideoFrame {
public:
- VideoFrameBuffer() :
- size_(SkISize::Make(0, 0)),
- dpi_(SkIPoint::Make(0, 0)),
- bytes_per_row_(0),
- needs_update_(true) {
- }
-
- // If the buffer is marked as needing to be updated (for example after the
- // screen mode changes) and is the wrong size, then release the old buffer
- // and create a new one.
- void Update(const SkISize& size) {
- if (needs_update_) {
- needs_update_ = false;
- size_t buffer_size = size.width() * size.height() * sizeof(uint32_t);
- if (size != size_) {
- size_ = size;
- bytes_per_row_ = size.width() * sizeof(uint32_t);
- ptr_.reset(new uint8[buffer_size]);
- }
- memset(ptr(), 0, buffer_size);
-
- // TODO(wez): Move the ugly DPI code into a helper.
- NSScreen* screen = [NSScreen mainScreen];
- NSDictionary* attr = [screen deviceDescription];
- NSSize resolution = [[attr objectForKey: NSDeviceResolution] sizeValue];
- dpi_.set(resolution.width, resolution.height);
- }
- }
-
- SkISize size() const { return size_; }
- SkIPoint dpi() const { return dpi_; }
- int bytes_per_row() const { return bytes_per_row_; }
- uint8* ptr() const { return ptr_.get(); }
+ explicit VideoFrameMac(const SkISize& size);
+ virtual ~VideoFrameMac();
- void set_needs_update() { needs_update_ = true; }
+ const SkIPoint& dpi() const { return dpi_; }
private:
- SkISize size_;
+ // Allocated pixel buffer.
+ scoped_array<uint8> data_;
+
+ // DPI settings for this buffer.
SkIPoint dpi_;
- int bytes_per_row_;
- scoped_array<uint8> ptr_;
- bool needs_update_;
- DISALLOW_COPY_AND_ASSIGN(VideoFrameBuffer);
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameMac);
};
// A class to perform video frame capturing for mac.
@@ -123,10 +94,10 @@ class VideoFrameCapturerMac : public VideoFrameCapturer {
private:
void CaptureCursor();
- void GlBlitFast(const VideoFrameBuffer& buffer, const SkRegion& region);
- void GlBlitSlow(const VideoFrameBuffer& buffer);
- void CgBlitPreLion(const VideoFrameBuffer& buffer, const SkRegion& region);
- void CgBlitPostLion(const VideoFrameBuffer& buffer, const SkRegion& region);
+ void GlBlitFast(const VideoFrame& buffer, const SkRegion& region);
+ void GlBlitSlow(const VideoFrame& buffer);
+ void CgBlitPreLion(const VideoFrame& buffer, const SkRegion& region);
+ void CgBlitPostLion(const VideoFrame& buffer, const SkRegion& region);
// Called when the screen configuration is changed.
void ScreenConfigurationChanged();
@@ -153,9 +124,10 @@ class VideoFrameCapturerMac : public VideoFrameCapturer {
Delegate* delegate_;
CGLContextObj cgl_context_;
- static const int kNumBuffers = 2;
ScopedPixelBufferObject pixel_buffer_object_;
- VideoFrameBuffer buffers_[kNumBuffers];
+
+ // Queue of the frames buffers.
+ VideoFrameQueue queue_;
// Current display configuration.
std::vector<CGDirectDisplayID> display_ids_;
@@ -168,13 +140,6 @@ class VideoFrameCapturerMac : public VideoFrameCapturer {
// Image of the last cursor that we sent to the client.
base::mac::ScopedCFTypeRef<CGImageRef> current_cursor_;
- // The current buffer with valid data for reading.
- int current_buffer_;
-
- // The previous buffer into which we captured, or NULL for the first capture
- // for a particular screen resolution.
- uint8* last_buffer_;
-
// Contains an invalid region from the previous capture.
SkRegion last_invalid_region_;
@@ -202,11 +167,27 @@ class VideoFrameCapturerMac : public VideoFrameCapturer {
DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerMac);
};
+VideoFrameMac::VideoFrameMac(const SkISize& size) {
+ set_bytes_per_row(size.width() * sizeof(uint32_t));
+ set_dimensions(size);
+
+ size_t buffer_size = size.width() * size.height() * sizeof(uint32_t);
+ data_.reset(new uint8[buffer_size]);
+ set_pixels(data_.get());
+
+ // TODO(wez): Move the ugly DPI code into a helper.
+ NSScreen* screen = [NSScreen mainScreen];
+ NSDictionary* attr = [screen deviceDescription];
+ NSSize resolution = [[attr objectForKey: NSDeviceResolution] sizeValue];
+ dpi_.set(resolution.width, resolution.height);
+}
+
+VideoFrameMac::~VideoFrameMac() {
+}
+
VideoFrameCapturerMac::VideoFrameCapturerMac()
: delegate_(NULL),
cgl_context_(NULL),
- current_buffer_(0),
- last_buffer_(NULL),
pixel_format_(media::VideoFrame::RGB32),
display_configuration_capture_event_(false, true),
power_assertion_id_display_(kIOPMNullAssertionID),
@@ -265,9 +246,7 @@ void VideoFrameCapturerMac::ReleaseBuffers() {
// The buffers might be in use by the encoder, so don't delete them here.
// Instead, mark them as "needs update"; next time the buffers are used by
// the capturer, they will be recreated if necessary.
- for (int i = 0; i < kNumBuffers; ++i) {
- buffers_[i].set_needs_update();
- }
+ queue_.SetAllFramesNeedUpdate();
}
void VideoFrameCapturerMac::Start(Delegate* delegate) {
@@ -319,47 +298,55 @@ void VideoFrameCapturerMac::CaptureInvalidRegion() {
base::TimeDelta::FromSeconds(kDisplayReconfigurationTimeoutInSeconds)));
SkRegion region;
helper_.SwapInvalidRegion(&region);
- VideoFrameBuffer& current_buffer = buffers_[current_buffer_];
- current_buffer.Update(SkISize::Make(desktop_bounds_.width(),
- desktop_bounds_.height()));
+
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (queue_.current_frame_needs_update()) {
+ scoped_ptr<VideoFrameMac> buffer(new VideoFrameMac(
+ SkISize::Make(desktop_bounds_.width(), desktop_bounds_.height())));
+ queue_.ReplaceCurrentFrame(buffer.PassAs<VideoFrame>());
+ }
+
+ VideoFrame* current_buffer = queue_.current_frame();
bool flip = false; // GL capturers need flipping.
if (base::mac::IsOSLionOrLater()) {
// Lion requires us to use their new APIs for doing screen capture. These
// APIS currently crash on 10.6.8 if there is no monitor attached.
- CgBlitPostLion(current_buffer, region);
+ CgBlitPostLion(*current_buffer, region);
} else if (cgl_context_) {
flip = true;
if (pixel_buffer_object_.get() != 0) {
- GlBlitFast(current_buffer, region);
+ GlBlitFast(*current_buffer, region);
} else {
// See comment in ScopedPixelBufferObject::Init about why the slow
// path is always used on 10.5.
- GlBlitSlow(current_buffer);
+ GlBlitSlow(*current_buffer);
}
} else {
- CgBlitPreLion(current_buffer, region);
+ CgBlitPreLion(*current_buffer, region);
}
DataPlanes planes;
- planes.data[0] = current_buffer.ptr();
- planes.strides[0] = current_buffer.bytes_per_row();
+ planes.data[0] = current_buffer->pixels();
+ planes.strides[0] = current_buffer->bytes_per_row();
if (flip) {
planes.strides[0] = -planes.strides[0];
- planes.data[0] +=
- (current_buffer.size().height() - 1) * current_buffer.bytes_per_row();
+ planes.data[0] += (current_buffer->dimensions().height() - 1) *
+ current_buffer->bytes_per_row();
}
- data = new CaptureData(planes, current_buffer.size(), pixel_format());
- data->set_dpi(current_buffer.dpi());
+ data = new CaptureData(planes, current_buffer->dimensions(), pixel_format());
+ data->set_dpi(static_cast<VideoFrameMac*>(current_buffer)->dpi());
data->mutable_dirty_region() = region;
- current_buffer_ = (current_buffer_ + 1) % kNumBuffers;
helper_.set_size_most_recent(data->size());
display_configuration_capture_event_.Signal();
CaptureCursor();
+ queue_.DoneWithCurrentFrame();
delegate_->OnCaptureCompleted(data);
}
@@ -445,14 +432,14 @@ void VideoFrameCapturerMac::CaptureCursor() {
current_cursor_.reset(CGImageCreateCopy(image));
}
-void VideoFrameCapturerMac::GlBlitFast(const VideoFrameBuffer& buffer,
+void VideoFrameCapturerMac::GlBlitFast(const VideoFrame& buffer,
const SkRegion& region) {
- const int buffer_height = buffer.size().height();
- const int buffer_width = buffer.size().width();
+ const int buffer_height = buffer.dimensions().height();
+ const int buffer_width = buffer.dimensions().width();
// Clip to the size of our current screen.
SkIRect clip_rect = SkIRect::MakeWH(buffer_width, buffer_height);
- if (last_buffer_) {
+ if (queue_.previous_frame()) {
// We are doing double buffer for the capture data so we just need to copy
// the invalid region from the previous capture in the current buffer.
// TODO(hclam): We can reduce the amount of copying here by subtracting
@@ -465,16 +452,15 @@ void VideoFrameCapturerMac::GlBlitFast(const VideoFrameBuffer& buffer,
for(SkRegion::Iterator i(last_invalid_region_); !i.done(); i.next()) {
SkIRect copy_rect = i.rect();
if (copy_rect.intersect(clip_rect)) {
- CopyRect(last_buffer_ + y_offset,
+ CopyRect(queue_.previous_frame()->pixels() + y_offset,
-buffer.bytes_per_row(),
- buffer.ptr() + y_offset,
+ buffer.pixels() + y_offset,
-buffer.bytes_per_row(),
4, // Bytes for pixel for RGBA.
copy_rect);
}
}
}
- last_buffer_ = buffer.ptr();
last_invalid_region_ = region;
CGLContextObj CGL_MACRO_CONTEXT = cgl_context_;
@@ -495,7 +481,7 @@ void VideoFrameCapturerMac::GlBlitFast(const VideoFrameBuffer& buffer,
if (copy_rect.intersect(clip_rect)) {
CopyRect(ptr + y_offset,
-buffer.bytes_per_row(),
- buffer.ptr() + y_offset,
+ buffer.pixels() + y_offset,
-buffer.bytes_per_row(),
4, // Bytes for pixel for RGBA.
copy_rect);
@@ -513,7 +499,7 @@ void VideoFrameCapturerMac::GlBlitFast(const VideoFrameBuffer& buffer,
glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
}
-void VideoFrameCapturerMac::GlBlitSlow(const VideoFrameBuffer& buffer) {
+void VideoFrameCapturerMac::GlBlitSlow(const VideoFrame& buffer) {
CGLContextObj CGL_MACRO_CONTEXT = cgl_context_;
glReadBuffer(GL_FRONT);
glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
@@ -522,21 +508,23 @@ void VideoFrameCapturerMac::GlBlitSlow(const VideoFrameBuffer& buffer) {
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
// Read a block of pixels from the frame buffer.
- glReadPixels(0, 0, buffer.size().width(), buffer.size().height(),
- GL_BGRA, GL_UNSIGNED_BYTE, buffer.ptr());
+ glReadPixels(0, 0, buffer.dimensions().width(), buffer.dimensions().height(),
+ GL_BGRA, GL_UNSIGNED_BYTE, buffer.pixels());
glPopClientAttrib();
}
-void VideoFrameCapturerMac::CgBlitPreLion(const VideoFrameBuffer& buffer,
+void VideoFrameCapturerMac::CgBlitPreLion(const VideoFrame& buffer,
const SkRegion& region) {
- const int buffer_height = buffer.size().height();
+ const int buffer_height = buffer.dimensions().height();
// Copy the entire contents of the previous capture buffer, to capture over.
// TODO(wez): Get rid of this as per crbug.com/145064, or implement
// crbug.com/92354.
- if (last_buffer_)
- memcpy(buffer.ptr(), last_buffer_, buffer.bytes_per_row() * buffer_height);
- last_buffer_ = buffer.ptr();
+ if (queue_.previous_frame()) {
+ memcpy(buffer.pixels(),
+ queue_.previous_frame()->pixels(),
+ buffer.bytes_per_row() * buffer_height);
+ }
for (unsigned int d = 0; d < display_ids_.size(); ++d) {
// Use deprecated APIs to determine the display buffer layout.
@@ -562,7 +550,7 @@ void VideoFrameCapturerMac::CgBlitPreLion(const VideoFrameBuffer& buffer,
copy_region.translate(-display_bounds.left(), -display_bounds.top());
// Calculate where in the output buffer the display's origin is.
- uint8* out_ptr = buffer.ptr() +
+ uint8* out_ptr = buffer.pixels() +
(display_bounds.left() * src_bytes_per_pixel) +
(display_bounds.top() * buffer.bytes_per_row());
@@ -578,16 +566,18 @@ void VideoFrameCapturerMac::CgBlitPreLion(const VideoFrameBuffer& buffer,
}
}
-void VideoFrameCapturerMac::CgBlitPostLion(const VideoFrameBuffer& buffer,
+void VideoFrameCapturerMac::CgBlitPostLion(const VideoFrame& buffer,
const SkRegion& region) {
- const int buffer_height = buffer.size().height();
+ const int buffer_height = buffer.dimensions().height();
// Copy the entire contents of the previous capture buffer, to capture over.
// TODO(wez): Get rid of this as per crbug.com/145064, or implement
// crbug.com/92354.
- if (last_buffer_)
- memcpy(buffer.ptr(), last_buffer_, buffer.bytes_per_row() * buffer_height);
- last_buffer_ = buffer.ptr();
+ if (queue_.previous_frame()) {
+ memcpy(buffer.pixels(),
+ queue_.previous_frame()->pixels(),
+ buffer.bytes_per_row() * buffer_height);
+ }
for (unsigned int d = 0; d < display_ids_.size(); ++d) {
// Determine the position of the display in the buffer.
@@ -620,7 +610,7 @@ void VideoFrameCapturerMac::CgBlitPostLion(const VideoFrameBuffer& buffer,
int src_bytes_per_pixel = CGImageGetBitsPerPixel(image) / 8;
// Calculate where in the output buffer the display's origin is.
- uint8* out_ptr = buffer.ptr() +
+ uint8* out_ptr = buffer.pixels() +
(display_bounds.left() * src_bytes_per_pixel) +
(display_bounds.top() * buffer.bytes_per_row());
@@ -643,7 +633,6 @@ const SkISize& VideoFrameCapturerMac::size_most_recent() const {
void VideoFrameCapturerMac::ScreenConfigurationChanged() {
// Release existing buffers, which will be of the wrong size.
ReleaseBuffers();
- last_buffer_ = NULL;
// Clear the dirty region, in case the display is down-sizing.
helper_.ClearInvalidRegion();
@@ -669,6 +658,9 @@ void VideoFrameCapturerMac::ScreenConfigurationChanged() {
helper_.InvalidateScreen(SkISize::Make(desktop_bounds_.width(),
desktop_bounds_.height()));
+ // Make sure the frame buffers will be reallocated.
+ queue_.SetAllFramesNeedUpdate();
+
// CgBlitPostLion uses CGDisplayCreateImage() to snapshot each display's
// contents. Although the API exists in OS 10.6, it crashes the caller if
// the machine has no monitor connected, so we fall back to depcreated APIs
diff --git a/remoting/host/video_frame_capturer_win.cc b/remoting/host/video_frame_capturer_win.cc
index 8d33620..3386c11 100644
--- a/remoting/host/video_frame_capturer_win.cc
+++ b/remoting/host/video_frame_capturer_win.cc
@@ -15,7 +15,9 @@
#include "base/win/scoped_hdc.h"
#include "remoting/base/capture_data.h"
#include "remoting/host/differ.h"
+#include "remoting/host/video_frame.h"
#include "remoting/host/video_frame_capturer_helper.h"
+#include "remoting/host/video_frame_queue.h"
#include "remoting/host/win/desktop.h"
#include "remoting/host/win/scoped_thread_desktop.h"
#include "remoting/proto/control.pb.h"
@@ -38,6 +40,22 @@ const uint32 kPixelBgraBlack = 0xff000000;
const uint32 kPixelBgraWhite = 0xffffffff;
const uint32 kPixelBgraTransparent = 0x00000000;
+// A class representing a full-frame pixel buffer.
+class VideoFrameWin : public VideoFrame {
+ public:
+ VideoFrameWin(HDC desktop_dc, const SkISize& size);
+ virtual ~VideoFrameWin();
+
+ // Returns handle of the device independent bitmap representing this frame
+ // buffer to GDI.
+ HBITMAP GetBitmap();
+
+ private:
+ base::win::ScopedBitmap bitmap_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameWin);
+};
+
// VideoFrameCapturerWin captures 32bit RGB using GDI.
//
// VideoFrameCapturerWin is double-buffered as required by VideoFrameCapturer.
@@ -56,33 +74,14 @@ class VideoFrameCapturerWin : public VideoFrameCapturer {
virtual const SkISize& size_most_recent() const OVERRIDE;
private:
- struct VideoFrameBuffer {
- VideoFrameBuffer();
-
- void* data;
- SkISize size;
- int bytes_per_pixel;
- int bytes_per_row;
- int resource_generation;
- };
-
- // Make sure that the device contexts and the current buffer match the screen
- // configuration.
+ // Make sure that the device contexts match the screen configuration.
void PrepareCaptureResources();
- // Allocates the specified capture buffer using the current device contexts
- // and desktop dimensions, releasing any pre-existing buffer.
- void AllocateBuffer(int buffer_index, int resource_generation);
-
- // Compares the most recently captured screen contents with the previous
- // contents reported to the caller, to determine the dirty region.
- void CalculateInvalidRegion();
-
// Creates a CaptureData instance wrapping the current framebuffer and
// notifies |delegate_|.
void CaptureRegion(const SkRegion& region);
- // Captures the current screen contents into the next available framebuffer.
+ // Captures the current screen contents into the current buffer.
void CaptureImage();
// Expand the cursor shape to add a white outline for visibility against
@@ -104,24 +103,18 @@ class VideoFrameCapturerWin : public VideoFrameCapturer {
scoped_array<uint8> last_cursor_;
SkISize last_cursor_size_;
- // There are two buffers for the screen images, as required by Capturer.
- static const int kNumBuffers = 2;
- VideoFrameBuffer buffers_[kNumBuffers];
+ // Queue of the frames buffers.
+ VideoFrameQueue queue_;
ScopedThreadDesktop desktop_;
// GDI resources used for screen capture.
scoped_ptr<base::win::ScopedGetDC> desktop_dc_;
base::win::ScopedCreateDC memory_dc_;
- base::win::ScopedBitmap target_bitmap_[kNumBuffers];
- int resource_generation_;
// Rectangle describing the bounds of the desktop device context.
SkIRect desktop_dc_rect_;
- // The current buffer with valid data for reading.
- int current_buffer_;
-
// Format of pixels returned in buffer.
media::VideoFrame::Format pixel_format_;
@@ -139,20 +132,46 @@ static const int kPixelsPerMeter = 3780;
// 32 bit RGBA is 4 bytes per pixel.
static const int kBytesPerPixel = 4;
-VideoFrameCapturerWin::VideoFrameBuffer::VideoFrameBuffer()
- : data(NULL),
- size(SkISize::Make(0, 0)),
- bytes_per_pixel(0),
- bytes_per_row(0),
- resource_generation(0) {
+VideoFrameWin::VideoFrameWin(HDC desktop_dc, const SkISize& size) {
+ // Describe a device independent bitmap (DIB) that is the size of the desktop.
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biHeight = -size.height();
+ bmi.bmiHeader.biWidth = size.width();
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = kBytesPerPixel * 8;
+ bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
+ int bytes_per_row = size.width() * kBytesPerPixel;
+ bmi.bmiHeader.biSizeImage = bytes_per_row * size.height();
+ bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter;
+ bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter;
+
+ // Create the DIB, and store a pointer to its pixel buffer.
+ void* data = NULL;
+ bitmap_ = CreateDIBSection(desktop_dc, &bmi, DIB_RGB_COLORS, &data, NULL, 0);
+
+ // TODO(wez): Cope gracefully with failure (crbug.com/157170).
+ CHECK(bitmap_ != NULL);
+ CHECK(data != NULL);
+
+ set_pixels(reinterpret_cast<uint8*>(data));
+ set_dimensions(SkISize::Make(bmi.bmiHeader.biWidth,
+ std::abs(bmi.bmiHeader.biHeight)));
+ set_bytes_per_row(
+ bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight));
+}
+
+VideoFrameWin::~VideoFrameWin() {
+}
+
+HBITMAP VideoFrameWin::GetBitmap() {
+ return bitmap_;
}
VideoFrameCapturerWin::VideoFrameCapturerWin()
: delegate_(NULL),
last_cursor_size_(SkISize::Make(0, 0)),
desktop_dc_rect_(SkIRect::MakeEmpty()),
- resource_generation_(0),
- current_buffer_(0),
pixel_format_(media::VideoFrame::RGB32),
composition_func_(NULL) {
}
@@ -172,8 +191,39 @@ void VideoFrameCapturerWin::CaptureInvalidRegion() {
// Force the system to power-up display hardware, if it has been suspended.
SetThreadExecutionState(ES_DISPLAY_REQUIRED);
- // Perform the capture.
- CalculateInvalidRegion();
+ // Make sure the GDI capture resources are up-to-date.
+ PrepareCaptureResources();
+
+ // Copy screen bits to the current buffer.
+ CaptureImage();
+
+ const VideoFrame* current_buffer = queue_.current_frame();
+ const VideoFrame* last_buffer = queue_.previous_frame();
+ if (last_buffer) {
+ // Make sure the differencer is set up correctly for these previous and
+ // current screens.
+ if (!differ_.get() ||
+ (differ_->width() != current_buffer->dimensions().width()) ||
+ (differ_->height() != current_buffer->dimensions().height()) ||
+ (differ_->bytes_per_row() != current_buffer->bytes_per_row())) {
+ differ_.reset(new Differ(current_buffer->dimensions().width(),
+ current_buffer->dimensions().height(),
+ kBytesPerPixel,
+ current_buffer->bytes_per_row()));
+ }
+
+ // Calculate difference between the two last captured frames.
+ SkRegion region;
+ differ_->CalcDirtyRegion(last_buffer->pixels(), current_buffer->pixels(),
+ &region);
+ InvalidateRegion(region);
+ } else {
+ // No previous frame is available. Invalidate the whole screen.
+ helper_.InvalidateScreen(current_buffer->dimensions());
+ }
+
+ // Wrap the captured frame into CaptureData structure and invoke
+ // the completion callback.
SkRegion invalid_region;
helper_.SwapInvalidRegion(&invalid_region);
CaptureRegion(invalid_region);
@@ -246,137 +296,67 @@ void VideoFrameCapturerWin::PrepareCaptureResources() {
desktop_dc_rect_.setEmpty();
}
- // Create GDI device contexts to capture from the desktop into memory, and
- // allocate buffers to capture into.
if (desktop_dc_.get() == NULL) {
DCHECK(memory_dc_.Get() == NULL);
+ // Create GDI device contexts to capture from the desktop into memory.
desktop_dc_.reset(new base::win::ScopedGetDC(NULL));
memory_dc_.Set(CreateCompatibleDC(*desktop_dc_));
desktop_dc_rect_ = screen_rect;
- ++resource_generation_;
- }
+ // Make sure the frame buffers will be reallocated.
+ queue_.SetAllFramesNeedUpdate();
- // If the current buffer is from an older generation then allocate a new one.
- // Note that we can't reallocate other buffers at this point, since the caller
- // may still be reading from them.
- if (resource_generation_ != buffers_[current_buffer_].resource_generation) {
- AllocateBuffer(current_buffer_, resource_generation_);
-
- SkRegion region;
- region.op(SkIRect::MakeSize(helper_.size_most_recent()),
- SkRegion::kUnion_Op);
- InvalidateRegion(region);
- }
-}
-
-void VideoFrameCapturerWin::AllocateBuffer(int buffer_index,
- int resource_generation) {
- DCHECK(desktop_dc_.get() != NULL);
- DCHECK(memory_dc_.Get() != NULL);
- // Windows requires DIB sections' rows to start DWORD-aligned, which is
- // implicit when working with RGB32 pixels.
- DCHECK_EQ(pixel_format_, media::VideoFrame::RGB32);
-
- // Describe a device independent bitmap (DIB) that is the size of the desktop.
- BITMAPINFO bmi;
- memset(&bmi, 0, sizeof(bmi));
- bmi.bmiHeader.biHeight = -desktop_dc_rect_.height();
- bmi.bmiHeader.biWidth = desktop_dc_rect_.width();
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = kBytesPerPixel * 8;
- bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
- int bytes_per_row = desktop_dc_rect_.width() * kBytesPerPixel;
- bmi.bmiHeader.biSizeImage = bytes_per_row * desktop_dc_rect_.height();
- bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter;
- bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter;
-
- // Create the DIB, and store a pointer to its pixel buffer.
- target_bitmap_[buffer_index] =
- CreateDIBSection(*desktop_dc_, &bmi, DIB_RGB_COLORS,
- static_cast<void**>(&buffers_[buffer_index].data),
- NULL, 0);
-
- // TODO(wez): Cope gracefully with failure (crbug.com/157170).
- CHECK(target_bitmap_[buffer_index] != NULL);
- CHECK(buffers_[buffer_index].data != NULL);
-
- buffers_[buffer_index].size = SkISize::Make(bmi.bmiHeader.biWidth,
- std::abs(bmi.bmiHeader.biHeight));
- buffers_[buffer_index].bytes_per_pixel = bmi.bmiHeader.biBitCount / 8;
- buffers_[buffer_index].bytes_per_row =
- bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight);
- buffers_[buffer_index].resource_generation = resource_generation;
-}
-
-void VideoFrameCapturerWin::CalculateInvalidRegion() {
- CaptureImage();
-
- const VideoFrameBuffer& current = buffers_[current_buffer_];
-
- // Find the previous and current screens.
- int prev_buffer_id = current_buffer_ - 1;
- if (prev_buffer_id < 0) {
- prev_buffer_id = kNumBuffers - 1;
- }
- const VideoFrameBuffer& prev = buffers_[prev_buffer_id];
-
- // Maybe the previous and current screens can't be differenced.
- if ((current.size != prev.size) ||
- (current.bytes_per_pixel != prev.bytes_per_pixel) ||
- (current.bytes_per_row != prev.bytes_per_row)) {
- helper_.InvalidateScreen(current.size);
- return;
- }
-
- // Make sure the differencer is set up correctly for these previous and
- // current screens.
- if (!differ_.get() ||
- (differ_->width() != current.size.width()) ||
- (differ_->height() != current.size.height()) ||
- (differ_->bytes_per_pixel() != current.bytes_per_pixel) ||
- (differ_->bytes_per_row() != current.bytes_per_row)) {
- differ_.reset(new Differ(current.size.width(), current.size.height(),
- current.bytes_per_pixel, current.bytes_per_row));
+ helper_.ClearInvalidRegion();
}
-
- SkRegion region;
- differ_->CalcDirtyRegion(prev.data, current.data, &region);
-
- InvalidateRegion(region);
}
void VideoFrameCapturerWin::CaptureRegion(const SkRegion& region) {
- const VideoFrameBuffer& buffer = buffers_[current_buffer_];
- current_buffer_ = (current_buffer_ + 1) % kNumBuffers;
+ const VideoFrame* current_buffer = queue_.current_frame();
DataPlanes planes;
- planes.data[0] = static_cast<uint8*>(buffer.data);
- planes.strides[0] = buffer.bytes_per_row;
+ planes.data[0] = current_buffer->pixels();
+ planes.strides[0] = current_buffer->bytes_per_row();
scoped_refptr<CaptureData> data(new CaptureData(planes,
- buffer.size,
+ current_buffer->dimensions(),
pixel_format_));
data->mutable_dirty_region() = region;
helper_.set_size_most_recent(data->size());
+ queue_.DoneWithCurrentFrame();
delegate_->OnCaptureCompleted(data);
}
void VideoFrameCapturerWin::CaptureImage() {
- // Make sure the GDI capture resources are up-to-date.
- PrepareCaptureResources();
-
- // Select the target bitmap into the memory dc.
- SelectObject(memory_dc_, target_bitmap_[current_buffer_]);
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (queue_.current_frame_needs_update()) {
+ DCHECK(desktop_dc_.get() != NULL);
+ DCHECK(memory_dc_.Get() != NULL);
+ // Windows requires DIB sections' rows to start DWORD-aligned, which is
+ // implicit when working with RGB32 pixels.
+ DCHECK_EQ(pixel_format_, media::VideoFrame::RGB32);
+
+ SkISize size = SkISize::Make(desktop_dc_rect_.width(),
+ desktop_dc_rect_.height());
+ scoped_ptr<VideoFrameWin> buffer(new VideoFrameWin(*desktop_dc_, size));
+ queue_.ReplaceCurrentFrame(buffer.PassAs<VideoFrame>());
+ }
- // And then copy the rect from desktop to memory.
- BitBlt(memory_dc_, 0, 0, buffers_[current_buffer_].size.width(),
- buffers_[current_buffer_].size.height(), *desktop_dc_,
- desktop_dc_rect_.x(), desktop_dc_rect_.y(),
- SRCCOPY | CAPTUREBLT);
+ // Select the target bitmap into the memory dc and copy the rect from desktop
+ // to memory.
+ VideoFrameWin* current =
+ static_cast<VideoFrameWin*>(queue_.current_frame());
+ if (SelectObject(memory_dc_, current->GetBitmap()) != NULL) {
+ BitBlt(memory_dc_,
+ 0, 0, desktop_dc_rect_.width(), desktop_dc_rect_.height(),
+ *desktop_dc_,
+ desktop_dc_rect_.x(), desktop_dc_rect_.y(),
+ SRCCOPY | CAPTUREBLT);
+ }
}
void VideoFrameCapturerWin::AddCursorOutline(int width,
diff --git a/remoting/host/video_frame_queue.cc b/remoting/host/video_frame_queue.cc
new file mode 100644
index 0000000..3d1c300
--- /dev/null
+++ b/remoting/host/video_frame_queue.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/video_frame_queue.h"
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "remoting/host/video_frame.h"
+
+namespace remoting {
+
+VideoFrameQueue::VideoFrameQueue()
+ : current_(0),
+ previous_(NULL) {
+ SetAllFramesNeedUpdate();
+}
+
+VideoFrameQueue::~VideoFrameQueue() {
+}
+
+void VideoFrameQueue::DoneWithCurrentFrame() {
+ previous_ = current_frame();
+ current_ = (current_ + 1) % kQueueLength;
+}
+
+void VideoFrameQueue::ReplaceCurrentFrame(scoped_ptr<VideoFrame> frame) {
+ frames_[current_] = frame.Pass();
+ needs_update_[current_] = false;
+}
+
+void VideoFrameQueue::SetAllFramesNeedUpdate() {
+ std::fill(needs_update_, needs_update_ + arraysize(needs_update_), true);
+ previous_ = NULL;
+}
+
+} // namespace remoting
diff --git a/remoting/host/video_frame_queue.h b/remoting/host/video_frame_queue.h
new file mode 100644
index 0000000..5084435
--- /dev/null
+++ b/remoting/host/video_frame_queue.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_HOST_VIDEO_FRAME_QUEUE_H_
+#define REMOTING_HOST_VIDEO_FRAME_QUEUE_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace remoting {
+
+class VideoFrame;
+
+// Represents a queue of reusable video frames. Provides access to the 'current'
+// frame - the frame that the caller is working with at the moment, and to
+// the 'previous' frame - the predecessor of the current frame swapped by
+// DoneWithCurrentFrame() call, if any.
+//
+// The caller is expected to (re)allocate frames if current_frame_needs_update()
+// is set. The caller can mark all frames in the queue for reallocation (when,
+// say, frame dimensions change). The queue records which frames need updating
+// which the caller can query.
+class VideoFrameQueue {
+ public:
+ VideoFrameQueue();
+ ~VideoFrameQueue();
+
+ // Moves to the next frame in the queue, moving the 'current' frame to become
+ // the 'previous' one.
+ void DoneWithCurrentFrame();
+
+ // Replaces the current frame with a new one allocated by the caller.
+ // The existing frame (if any) is destroyed.
+ void ReplaceCurrentFrame(scoped_ptr<VideoFrame> frame);
+
+ // Marks all frames obsolete and resets the previous frame pointer. No
+ // frames are freed though as the caller can still access them.
+ void SetAllFramesNeedUpdate();
+
+ VideoFrame* current_frame() const {
+ return frames_[current_].get();
+ }
+
+ bool current_frame_needs_update() const {
+ return !current_frame() || needs_update_[current_];
+ }
+
+ VideoFrame* previous_frame() const { return previous_; }
+
+ private:
+ // Index of the current frame.
+ int current_;
+
+ static const int kQueueLength = 2;
+ scoped_ptr<VideoFrame> frames_[kQueueLength];
+
+ // True if the corresponding frame needs to be re-allocated.
+ bool needs_update_[kQueueLength];
+
+ // Points to the previous frame if any.
+ VideoFrame* previous_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameQueue);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_VIDEO_FRAME_QUEUE_H_
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index f7a40df..5c45bf5 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -1620,6 +1620,8 @@
'host/user_authenticator_linux.cc',
'host/user_authenticator_mac.cc',
'host/user_authenticator_win.cc',
+ 'host/video_frame.cc',
+ 'host/video_frame.h',
'host/video_frame_capturer.h',
'host/video_frame_capturer_fake.cc',
'host/video_frame_capturer_fake.h',
@@ -1628,6 +1630,8 @@
'host/video_frame_capturer_linux.cc',
'host/video_frame_capturer_mac.mm',
'host/video_frame_capturer_win.cc',
+ 'host/video_frame_queue.cc',
+ 'host/video_frame_queue.h',
'host/video_scheduler.cc',
'host/video_scheduler.h',
'host/vlog_net_log.cc',