summaryrefslogtreecommitdiffstats
path: root/remoting/host/capturer_win.cc
diff options
context:
space:
mode:
authordmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-04 22:44:11 +0000
committerdmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-04 22:44:11 +0000
commit0b7e42805341699a240997b202d4d23aed4b458b (patch)
tree44b31edf61ace0f552a113dd56368768d9774598 /remoting/host/capturer_win.cc
parent167d52bbfbfc80f2232474eddfeb8bc587008d71 (diff)
downloadchromium_src-0b7e42805341699a240997b202d4d23aed4b458b.zip
chromium_src-0b7e42805341699a240997b202d4d23aed4b458b.tar.gz
chromium_src-0b7e42805341699a240997b202d4d23aed4b458b.tar.bz2
Clean up remoting project
Cleaned up some file names so it simplifies our project, and gets us more inline with chromium standards. Removed several unnecessary headers that were cluttering the remoting namespace. Simplified some of the double pimpl implementations that we had on Linux to hide X11 stuff. Got HostAuthentication working reasonably well as a mock. BUG=NONE TEST=BUILD Review URL: http://codereview.chromium.org/6780014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80385 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/host/capturer_win.cc')
-rw-r--r--remoting/host/capturer_win.cc337
1 files changed, 337 insertions, 0 deletions
diff --git a/remoting/host/capturer_win.cc b/remoting/host/capturer_win.cc
new file mode 100644
index 0000000..b5fe81e
--- /dev/null
+++ b/remoting/host/capturer_win.cc
@@ -0,0 +1,337 @@
+// Copyright (c) 2011 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/capturer.h"
+
+#include <windows.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "remoting/host/capturer_helper.h"
+#include "remoting/host/differ.h"
+#include "ui/gfx/rect.h"
+
+namespace remoting {
+
+namespace {
+
+// CapturerGdi captures 32bit RGB using GDI.
+//
+// CapturerGdi is double-buffered as required by Capturer. See
+// remoting/host/capturer.h.
+class CapturerGdi : public Capturer {
+ public:
+ CapturerGdi();
+ virtual ~CapturerGdi();
+
+ // Capturer interface.
+ virtual void ScreenConfigurationChanged() OVERRIDE;
+ virtual media::VideoFrame::Format pixel_format() const OVERRIDE;
+ virtual void ClearInvalidRects() OVERRIDE;
+ virtual void InvalidateRects(const InvalidRects& inval_rects) OVERRIDE;
+ virtual void InvalidateScreen(const gfx::Size& size) OVERRIDE;
+ virtual void InvalidateFullScreen() OVERRIDE;
+ virtual void CaptureInvalidRects(CaptureCompletedCallback* callback) OVERRIDE;
+ virtual const gfx::Size& size_most_recent() const OVERRIDE;
+
+ private:
+ struct VideoFrameBuffer {
+ VideoFrameBuffer(void* data, const gfx::Size& size, int bytes_per_pixel,
+ int bytes_per_row)
+ : data(data), size(size), bytes_per_pixel(bytes_per_pixel),
+ bytes_per_row(bytes_per_row) {
+ }
+ VideoFrameBuffer() {
+ data = 0;
+ size = gfx::Size(0, 0);
+ bytes_per_pixel = 0;
+ bytes_per_row = 0;
+ }
+ void* data;
+ gfx::Size size;
+ int bytes_per_pixel;
+ int bytes_per_row;
+ };
+
+ // Make sure that the current buffer has the same size as the screen.
+ void UpdateBufferCapture(const gfx::Size& size);
+
+ // Allocate memory for a buffer of a given size, freeing any memory previously
+ // allocated for that buffer.
+ void ReallocateBuffer(int buffer_index, const gfx::Size& size);
+
+ void CalculateInvalidRects();
+ void CaptureRects(const InvalidRects& rects,
+ CaptureCompletedCallback* callback);
+
+ void ReleaseBuffers();
+ // Generates an image in the current buffer.
+ void CaptureImage();
+
+ // Gets the current screen size and calls ScreenConfigurationChanged
+ // if the screen size has changed.
+ void MaybeChangeScreenConfiguration();
+
+ // Gets the screen size.
+ gfx::Size GetScreenSize();
+
+ // A thread-safe list of invalid rectangles, and the size of the most
+ // recently captured screen.
+ CapturerHelper helper;
+
+ // There are two buffers for the screen images, as required by Capturer.
+ static const int kNumBuffers = 2;
+ VideoFrameBuffer buffers_[kNumBuffers];
+
+ // Gdi specific information about screen.
+ HDC desktop_dc_;
+ HDC memory_dc_;
+ HBITMAP target_bitmap_[kNumBuffers];
+
+ // The screen size attached to the device contexts through which the screen
+ // is captured.
+ gfx::Size dc_size_;
+
+ // The current buffer with valid data for reading.
+ int current_buffer_;
+
+ // Format of pixels returned in buffer.
+ media::VideoFrame::Format pixel_format_;
+
+ // Class to calculate the difference between two screen bitmaps.
+ scoped_ptr<Differ> differ_;
+
+ // True if we should force a fullscreen capture.
+ bool capture_fullscreen_;
+
+ DISALLOW_COPY_AND_ASSIGN(CapturerGdi);
+};
+
+// 3780 pixels per meter is equivalent to 96 DPI, typical on desktop monitors.
+static const int kPixelsPerMeter = 3780;
+// 32 bit RGBA is 4 bytes per pixel.
+static const int kBytesPerPixel = 4;
+
+CapturerGdi::CapturerGdi()
+ : desktop_dc_(NULL),
+ memory_dc_(NULL),
+ dc_size_(0, 0),
+ current_buffer_(0),
+ pixel_format_(media::VideoFrame::RGB32),
+ capture_fullscreen_(true) {
+ memset(target_bitmap_, 0, sizeof(target_bitmap_));
+ memset(buffers_, 0, sizeof(buffers_));
+ ScreenConfigurationChanged();
+}
+
+CapturerGdi::~CapturerGdi() {
+ ReleaseBuffers();
+}
+
+media::VideoFrame::Format CapturerGdi::pixel_format() const {
+ return pixel_format_;
+}
+
+void CapturerGdi::ClearInvalidRects() {
+ helper.ClearInvalidRects();
+}
+
+void CapturerGdi::InvalidateRects(const InvalidRects& inval_rects) {
+ helper.InvalidateRects(inval_rects);
+}
+
+void CapturerGdi::InvalidateScreen(const gfx::Size& size) {
+ helper.InvalidateScreen(size);
+}
+
+void CapturerGdi::InvalidateFullScreen() {
+ helper.InvalidateFullScreen();
+}
+
+void CapturerGdi::CaptureInvalidRects(CaptureCompletedCallback* callback) {
+ CalculateInvalidRects();
+ InvalidRects inval_rects;
+ helper.SwapInvalidRects(inval_rects);
+ CaptureRects(inval_rects, callback);
+}
+
+const gfx::Size& CapturerGdi::size_most_recent() const {
+ return helper.size_most_recent();
+}
+
+void CapturerGdi::ReleaseBuffers() {
+ for (int i = kNumBuffers - 1; i >= 0; i--) {
+ if (target_bitmap_[i]) {
+ DeleteObject(target_bitmap_[i]);
+ target_bitmap_[i] = NULL;
+ }
+ if (buffers_[i].data) {
+ DeleteObject(buffers_[i].data);
+ buffers_[i].data = NULL;
+ }
+ }
+
+ desktop_dc_ = NULL;
+ if (memory_dc_) {
+ DeleteDC(memory_dc_);
+ memory_dc_ = NULL;
+ }
+}
+
+void CapturerGdi::ScreenConfigurationChanged() {
+ // We poll for screen configuration changes, so ignore notifications.
+}
+
+void CapturerGdi::UpdateBufferCapture(const gfx::Size& size) {
+ // Make sure the DCs have the correct dimensions.
+ if (size != dc_size_) {
+ // TODO(simonmorris): screen dimensions changing isn't equivalent to needing
+ // a new DC, but it's good enough for now.
+ desktop_dc_ = GetDC(GetDesktopWindow());
+ if (memory_dc_)
+ DeleteDC(memory_dc_);
+ memory_dc_ = CreateCompatibleDC(desktop_dc_);
+ dc_size_ = size;
+ }
+
+ // Make sure the current bitmap has the correct dimensions.
+ if (size != buffers_[current_buffer_].size) {
+ ReallocateBuffer(current_buffer_, size);
+ capture_fullscreen_ = true;
+ }
+}
+
+void CapturerGdi::ReallocateBuffer(int buffer_index, const gfx::Size& size) {
+ // Delete any previously constructed bitmap.
+ if (target_bitmap_[buffer_index]) {
+ DeleteObject(target_bitmap_[buffer_index]);
+ target_bitmap_[buffer_index] = NULL;
+ }
+ if (buffers_[buffer_index].data) {
+ DeleteObject(buffers_[buffer_index].data);
+ buffers_[buffer_index].data = NULL;
+ }
+
+ // Create a bitmap to keep the desktop image.
+ int rounded_width = (size.width() + 3) & (~3);
+
+ // Dimensions of screen.
+ pixel_format_ = media::VideoFrame::RGB32;
+ int bytes_per_row = rounded_width * kBytesPerPixel;
+
+ // Create a device independent bitmap (DIB) that is the same size.
+ 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);
+ bmi.bmiHeader.biSizeImage = bytes_per_row * size.height();
+ bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter;
+ bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter;
+
+ // Create memory for the buffers.
+ target_bitmap_[buffer_index] =
+ CreateDIBSection(desktop_dc_, &bmi, DIB_RGB_COLORS,
+ static_cast<void**>(&buffers_[buffer_index].data),
+ NULL, 0);
+ buffers_[buffer_index].size = gfx::Size(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);
+}
+
+void CapturerGdi::CalculateInvalidRects() {
+ CaptureImage();
+
+ const VideoFrameBuffer& current = buffers_[current_buffer_];
+ if (helper.IsCaptureFullScreen(current.size))
+ capture_fullscreen_ = true;
+
+ if (capture_fullscreen_) {
+ InvalidateScreen(current.size);
+ capture_fullscreen_ = false;
+ return;
+ }
+
+ // 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)) {
+ 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));
+ }
+
+ InvalidRects rects;
+ differ_->CalcDirtyRects(prev.data, current.data, &rects);
+
+ InvalidateRects(rects);
+}
+
+void CapturerGdi::CaptureRects(const InvalidRects& rects,
+ CaptureCompletedCallback* callback) {
+ scoped_ptr<CaptureCompletedCallback> callback_deleter(callback);
+
+ const VideoFrameBuffer& buffer = buffers_[current_buffer_];
+ current_buffer_ = (current_buffer_ + 1) % kNumBuffers;
+
+ DataPlanes planes;
+ planes.data[0] = static_cast<uint8*>(buffer.data);
+ planes.strides[0] = buffer.bytes_per_row;
+
+ scoped_refptr<CaptureData> data(new CaptureData(planes,
+ buffer.size,
+ pixel_format_));
+ data->mutable_dirty_rects() = rects;
+
+ helper.set_size_most_recent(data->size());
+
+ callback->Run(data);
+}
+
+void CapturerGdi::CaptureImage() {
+ // Make sure the structures we use to capture the image have the correct size.
+ UpdateBufferCapture(GetScreenSize());
+
+ // Select the target bitmap into the memory dc.
+ SelectObject(memory_dc_, target_bitmap_[current_buffer_]);
+
+ // 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_, 0, 0,
+ SRCCOPY | CAPTUREBLT);
+}
+
+gfx::Size CapturerGdi::GetScreenSize() {
+ return gfx::Size(GetSystemMetrics(SM_CXSCREEN),
+ GetSystemMetrics(SM_CYSCREEN));
+}
+
+} // namespace
+
+// static
+Capturer* Capturer::Create() {
+ return new CapturerGdi();
+}
+
+} // namespace remoting