summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-01 15:02:53 +0000
committerdmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-01 15:02:53 +0000
commit804c99625cc375da683a6c1a681178b0014e02fe (patch)
tree444c6deffd05f7744f0d2b50077aeabde0cc7170
parent9da1cedf45ee5bb54cc5558ca044dc24b999b8d9 (diff)
downloadchromium_src-804c99625cc375da683a6c1a681178b0014e02fe.zip
chromium_src-804c99625cc375da683a6c1a681178b0014e02fe.tar.gz
chromium_src-804c99625cc375da683a6c1a681178b0014e02fe.tar.bz2
Revamp capturer to clean up the interface, and to keep data as atomic as possible when making calls across threads.
TEST=build remoting BUG=none Review URL: http://codereview.chromium.org/2805025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51363 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--remoting/host/capturer.cc40
-rw-r--r--remoting/host/capturer.h131
-rw-r--r--remoting/host/capturer_fake.cc65
-rw-r--r--remoting/host/capturer_fake.h8
-rw-r--r--remoting/host/capturer_fake_ascii.cc68
-rw-r--r--remoting/host/capturer_fake_ascii.h8
-rw-r--r--remoting/host/capturer_gdi.cc101
-rw-r--r--remoting/host/capturer_gdi.h16
-rw-r--r--remoting/host/capturer_linux.cc37
-rw-r--r--remoting/host/capturer_linux.h11
-rw-r--r--remoting/host/capturer_mac.cc154
-rw-r--r--remoting/host/capturer_mac.h33
-rw-r--r--remoting/host/capturer_mac_unittest.cc67
-rw-r--r--remoting/host/chromoting_host.cc3
-rw-r--r--remoting/host/chromoting_host.h4
-rw-r--r--remoting/host/encoder.h32
-rw-r--r--remoting/host/encoder_verbatim.cc56
-rw-r--r--remoting/host/encoder_verbatim.h21
-rw-r--r--remoting/host/mock_objects.h29
-rw-r--r--remoting/host/session_manager.cc65
-rw-r--r--remoting/host/session_manager.h18
-rw-r--r--remoting/host/session_manager_unittest.cc57
22 files changed, 534 insertions, 490 deletions
diff --git a/remoting/host/capturer.cc b/remoting/host/capturer.cc
index 331f9c7..917a960 100644
--- a/remoting/host/capturer.cc
+++ b/remoting/host/capturer.cc
@@ -10,7 +10,6 @@ Capturer::Capturer()
: width_(0),
height_(0),
pixel_format_(PixelFormatInvalid),
- bytes_per_pixel_(0),
bytes_per_row_(0),
current_buffer_(0) {
}
@@ -18,32 +17,39 @@ Capturer::Capturer()
Capturer::~Capturer() {
}
-void Capturer::GetDirtyRects(DirtyRects* rects) const {
- *rects = dirty_rects_;
-}
+void Capturer::CaptureInvalidRects(CaptureCompletedCallback* callback) {
+ // Braced to scope the lock.
+ RectVector local_rects;
+ {
+ AutoLock auto_inval_rects_lock(inval_rects_lock_);
+ local_rects = inval_rects_;
+ inval_rects_.clear();
+ }
-int Capturer::GetWidth() const {
- return width_;
+ CaptureRects(local_rects, callback);
}
-int Capturer::GetHeight() const {
- return height_;
+void Capturer::InvalidateRects(const RectVector& inval_rects) {
+ AutoLock auto_inval_rects_lock(inval_rects_lock_);
+ inval_rects_.insert(inval_rects_.end(),
+ inval_rects.begin(),
+ inval_rects.end());
}
-PixelFormat Capturer::GetPixelFormat() const {
- return pixel_format_;
-}
+void Capturer::InvalidateFullScreen() {
+ RectVector rects;
+ rects.push_back(gfx::Rect(0, 0, width_, height_));
-void Capturer::InvalidateRect(gfx::Rect dirty_rect) {
- inval_rects_.push_back(dirty_rect);
+ InvalidateRects(rects);
}
-void Capturer::FinishCapture(Task* done_task) {
- done_task->Run();
- delete done_task;
-
+void Capturer::FinishCapture(scoped_refptr<CaptureData> data,
+ CaptureCompletedCallback* callback) {
// Select the next buffer to be the current buffer.
current_buffer_ = (current_buffer_ + 1) % kNumBuffers;
+
+ callback->Run(data);
+ delete callback;
}
} // namespace remoting
diff --git a/remoting/host/capturer.h b/remoting/host/capturer.h
index 77a8aad..6e16098 100644
--- a/remoting/host/capturer.h
+++ b/remoting/host/capturer.h
@@ -8,13 +8,15 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/lock.h"
#include "base/task.h"
#include "gfx/rect.h"
#include "remoting/base/protocol/chromotocol.pb.h"
namespace remoting {
-typedef std::vector<gfx::Rect> DirtyRects;
+typedef std::vector<gfx::Rect> RectVector;
// A class to perform the task of capturing the image of a window.
// The capture action is asynchronous to allow maximum throughput.
@@ -25,72 +27,118 @@ typedef std::vector<gfx::Rect> DirtyRects;
// happening.
class Capturer {
public:
+
+ struct DataPlanes {
+ static const int kPlaneCount = 3;
+ uint8* data[kPlaneCount];
+ int strides[kPlaneCount];
+
+ DataPlanes() {
+ for (int i = 0; i < kPlaneCount; ++i) {
+ data[i] = NULL;
+ strides[i] = 0;
+ }
+ }
+ };
+
+ // Stores the data and information of a capture to pass off to the
+ // encoding thread.
+ class CaptureData : public base::RefCountedThreadSafe<CaptureData> {
+ public:
+ CaptureData(const DataPlanes &data_planes,
+ int width,
+ int height,
+ PixelFormat format) :
+ data_planes_(data_planes), dirty_rects_(),
+ width_(width), height_(height), pixel_format_(format) { }
+
+ // Get the data_planes data of the last capture.
+ const DataPlanes& data_planes() const { return data_planes_; }
+
+ // Get the list of updated rectangles in the last capture. The result is
+ // written into |rects|.
+ const RectVector& dirty_rects() const { return dirty_rects_; }
+
+ // Get the width of the image captured.
+ int width() const { return width_; }
+
+ // Get the height of the image captured.
+ int height() const { return height_; }
+
+ // Get the pixel format of the image captured.
+ PixelFormat pixel_format() const { return pixel_format_; }
+
+ // Mutating methods.
+ RectVector& mutable_dirty_rects() { return dirty_rects_; }
+
+ private:
+ const DataPlanes data_planes_;
+ RectVector dirty_rects_;
+ int width_;
+ int height_;
+ PixelFormat pixel_format_;
+
+ friend class base::RefCountedThreadSafe<CaptureData>;
+ ~CaptureData() {}
+ };
+
+ // CaptureCompletedCallback is called when the capturer has completed.
+ typedef Callback1<scoped_refptr<CaptureData> >::Type CaptureCompletedCallback;
+
Capturer();
virtual ~Capturer();
- // Capture the full screen. When the action is completed |done_task|
- // is called.
- //
- // It is OK to call this method while another thread is reading
- // data of the last capture.
- // There can be at most one concurrent read going on when this
- // method is called.
- virtual void CaptureFullScreen(Task* done_task) = 0;
// Capture the updated regions since last capture. If the last
// capture doesn't exist, the full window is captured.
//
- // When complete |done_task| is called.
+ // When complete |callback| is called.
//
// It is OK to call this method while another thread is reading
// data of the last capture.
// There can be at most one concurrent read going on when this
// method is called.
- virtual void CaptureDirtyRects(Task* done_task) = 0;
+ virtual void CaptureInvalidRects(CaptureCompletedCallback* callback);
- // Capture the specified screen rect and call |done_task| when complete.
- // Dirty or invalid regions are ignored and only the given |rect| area is
+ // Capture the specified screen rects and call |callback| when complete.
+ // Dirty or invalid regions are ignored and only the given |rects| areas are
// captured.
//
// It is OK to call this method while another thread is reading
// data of the last capture.
// There can be at most one concurrent read going on when this
// method is called.
- virtual void CaptureRect(const gfx::Rect& rect, Task* done_task) = 0;
-
- // Get the image data of the last capture. The pointers to data is
- // written to |planes|. |planes| should be an array of 3 elements.
- virtual void GetData(const uint8* planes[]) const = 0;
+ virtual void CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback) = 0;
- // Get the image data stride of the last capture. The size of strides
- // is written to |strides|. |strides| should be array of 3 elements.
- virtual void GetDataStride(int strides[]) const = 0;
+ // Invalidate the specified screen rects.
+ virtual void InvalidateRects(const RectVector& inval_rects);
- // Get the list of updated rectangles in the last capture. The result is
- // written into |rects|.
- virtual void GetDirtyRects(DirtyRects* rects) const;
+ // Invalidate the entire screen.
+ virtual void InvalidateFullScreen();
- // Get the width of the image captured.
- virtual int GetWidth() const;
+ // Called when the screen configuration is changed.
+ virtual void ScreenConfigurationChanged() = 0;
- // Get the height of the image captured.
- virtual int GetHeight() const;
+ // Return the width of the screen.
+ virtual int width() const { return width_; }
- // Get the pixel format of the image captured.
- virtual PixelFormat GetPixelFormat() const;
+ // Return the height of the screen
+ virtual int height() const { return height_; }
- // Invalidate the specified screen rect.
- virtual void InvalidateRect(gfx::Rect dirty);
+ // Return the pixel format of the screen
+ virtual PixelFormat pixel_format() const { return pixel_format_; }
protected:
// Finish/cleanup capture task.
// This should be called at the end of each of the CaptureXxx() routines.
// This routine should (at least):
- // (1) Call the |done_task| routine.
+ // (1) Call the |callback| routine.
// (2) Select the next screen buffer.
// Note that capturers are required to be double-buffered so that we can
// read from one which capturing into another.
- virtual void FinishCapture(Task* done_task);
+ virtual void FinishCapture(scoped_refptr<CaptureData> data,
+ CaptureCompletedCallback* callback);
// Number of screen buffers.
static const int kNumBuffers = 2;
@@ -101,21 +149,20 @@ class Capturer {
// Format of pixels returned in buffer.
PixelFormat pixel_format_;
-
- // Information about screen.
- int bytes_per_pixel_;
int bytes_per_row_;
// The current buffer with valid data for reading.
int current_buffer_;
- // List of dirty rects.
- // These are the rects that we send to the client to update.
- DirtyRects dirty_rects_;
+ private:
// Rects that have been manually invalidated (through InvalidateRect).
- // These will be merged into |dirty_rects_| during the next capture.
- DirtyRects inval_rects_;
+ // These will be returned as dirty_rects in the capture data during the next
+ // capture.
+ RectVector inval_rects_;
+
+ // A lock protecting |inval_rects_| across threads.
+ Lock inval_rects_lock_;
};
} // namespace remoting
diff --git a/remoting/host/capturer_fake.cc b/remoting/host/capturer_fake.cc
index f1a8135..32121d2 100644
--- a/remoting/host/capturer_fake.cc
+++ b/remoting/host/capturer_fake.cc
@@ -15,61 +15,36 @@ static const int kMaxColorChannelValue = 255;
CapturerFake::CapturerFake()
: seed_(0) {
- // Dimensions of screen.
- width_ = kWidth;
- height_ = kHeight;
- pixel_format_ = PixelFormatRgb32;
- bytes_per_pixel_ = kBytesPerPixel;
- bytes_per_row_ = width_ * bytes_per_pixel_;
-
- // Create memory for the buffers.
- int buffer_size = height_ * bytes_per_row_;
- for (int i = 0; i < kNumBuffers; i++) {
- buffers_[i].reset(new uint8[buffer_size]);
- }
}
CapturerFake::~CapturerFake() {
}
-void CapturerFake::CaptureFullScreen(Task* done_task) {
- dirty_rects_.clear();
-
- GenerateImage();
- dirty_rects_.push_back(gfx::Rect(width_, height_));
-
- FinishCapture(done_task);
-}
-
-void CapturerFake::CaptureDirtyRects(Task* done_task) {
- dirty_rects_.clear();
-
+void CapturerFake::CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback) {
GenerateImage();
- // TODO(garykac): Diff old/new images and generate |dirty_rects_|.
- // Currently, this just marks the entire screen as dirty.
- dirty_rects_.push_back(gfx::Rect(width_, height_));
+ Capturer::DataPlanes planes;
+ planes.data[0] = buffers_[current_buffer_].get();
+ planes.strides[0] = bytes_per_row_;
- FinishCapture(done_task);
+ scoped_refptr<CaptureData> capture_data(new CaptureData(planes,
+ width_,
+ height_,
+ pixel_format_));
+ FinishCapture(capture_data, callback);
}
-void CapturerFake::CaptureRect(const gfx::Rect& rect, Task* done_task) {
- dirty_rects_.clear();
-
- GenerateImage();
- dirty_rects_.push_back(rect);
-
- FinishCapture(done_task);
-}
-
-void CapturerFake::GetData(const uint8* planes[]) const {
- planes[0] = buffers_[current_buffer_].get();
- planes[1] = planes[2] = NULL;
-}
+void CapturerFake::ScreenConfigurationChanged() {
+ width_ = kWidth;
+ height_ = kHeight;
+ pixel_format_ = PixelFormatRgb32;
+ bytes_per_row_ = width_ * kBytesPerPixel;
-void CapturerFake::GetDataStride(int strides[]) const {
- // Only the first plane has data.
- strides[0] = bytes_per_row_;
- strides[1] = strides[2] = 0;
+ // Create memory for the buffers.
+ int buffer_size = height_ * bytes_per_row_;
+ for (int i = 0; i < kNumBuffers; i++) {
+ buffers_[i].reset(new uint8[buffer_size]);
+ }
}
void CapturerFake::GenerateImage() {
diff --git a/remoting/host/capturer_fake.h b/remoting/host/capturer_fake.h
index e2b5282..79de376 100644
--- a/remoting/host/capturer_fake.h
+++ b/remoting/host/capturer_fake.h
@@ -20,11 +20,9 @@ class CapturerFake : public Capturer {
CapturerFake();
virtual ~CapturerFake();
- virtual void CaptureFullScreen(Task* done_task);
- virtual void CaptureDirtyRects(Task* done_task);
- virtual void CaptureRect(const gfx::Rect& rect, Task* done_task);
- virtual void GetData(const uint8* planes[]) const;
- virtual void GetDataStride(int strides[]) const;
+ virtual void CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback);
+ virtual void ScreenConfigurationChanged();
private:
// Generates an image in the front buffer.
diff --git a/remoting/host/capturer_fake_ascii.cc b/remoting/host/capturer_fake_ascii.cc
index 00f8529..d0d7479 100644
--- a/remoting/host/capturer_fake_ascii.cc
+++ b/remoting/host/capturer_fake_ascii.cc
@@ -13,63 +13,35 @@ static const int kHeight = 20;
static const int kBytesPerPixel = 1;
CapturerFakeAscii::CapturerFakeAscii() {
- // Dimensions of screen.
- width_ = kWidth;
- height_ = kHeight;
- pixel_format_ = PixelFormatAscii;
- bytes_per_pixel_ = kBytesPerPixel;
- bytes_per_row_ = width_ * bytes_per_pixel_;
-
- // Create memory for the buffers.
- int buffer_size = height_ * bytes_per_row_;
- for (int i = 0; i < kNumBuffers; i++) {
- buffers_[i].reset(new uint8[buffer_size]);
- }
}
CapturerFakeAscii::~CapturerFakeAscii() {
}
-void CapturerFakeAscii::CaptureFullScreen(Task* done_task) {
- dirty_rects_.clear();
-
+void CapturerFakeAscii::CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback) {
GenerateImage();
-
- // Return a single dirty rect that includes the entire screen.
- dirty_rects_.push_back(gfx::Rect(width_, height_));
-
- FinishCapture(done_task);
+ Capturer::DataPlanes planes;
+ planes.data[0] = buffers_[current_buffer_].get();
+ planes.strides[0] = bytes_per_row_;
+ scoped_refptr<CaptureData> capture_data(new CaptureData(planes,
+ width_,
+ height_,
+ pixel_format_));
+ FinishCapture(capture_data, callback);
}
-void CapturerFakeAscii::CaptureDirtyRects(Task* done_task) {
- dirty_rects_.clear();
-
- GenerateImage();
- // TODO(garykac): Diff old/new screen.
- // Currently, this just marks the entire screen as dirty.
- dirty_rects_.push_back(gfx::Rect(width_, height_));
-
- FinishCapture(done_task);
-}
-
-void CapturerFakeAscii::CaptureRect(const gfx::Rect& rect, Task* done_task) {
- dirty_rects_.clear();
-
- GenerateImage();
- dirty_rects_.push_back(rect);
-
- FinishCapture(done_task);
-}
-
-void CapturerFakeAscii::GetData(const uint8* planes[]) const {
- planes[0] = buffers_[current_buffer_].get();
- planes[1] = planes[2] = NULL;
-}
+void CapturerFakeAscii::ScreenConfigurationChanged() {
+ width_ = kWidth;
+ height_ = kHeight;
+ pixel_format_ = PixelFormatAscii;
+ bytes_per_row_ = width_ * kBytesPerPixel;
-void CapturerFakeAscii::GetDataStride(int strides[]) const {
- // Only the first plane has data.
- strides[0] = bytes_per_row_;
- strides[1] = strides[2] = 0;
+ // Create memory for the buffers.
+ int buffer_size = height_ * bytes_per_row_;
+ for (int i = 0; i < kNumBuffers; i++) {
+ buffers_[i].reset(new uint8[buffer_size]);
+ }
}
void CapturerFakeAscii::GenerateImage() {
diff --git a/remoting/host/capturer_fake_ascii.h b/remoting/host/capturer_fake_ascii.h
index ceb406b..5457c41 100644
--- a/remoting/host/capturer_fake_ascii.h
+++ b/remoting/host/capturer_fake_ascii.h
@@ -20,11 +20,9 @@ class CapturerFakeAscii : public Capturer {
CapturerFakeAscii();
virtual ~CapturerFakeAscii();
- virtual void CaptureFullScreen(Task* done_task);
- virtual void CaptureDirtyRects(Task* done_task);
- virtual void CaptureRect(const gfx::Rect& rect, Task* done_task);
- virtual void GetData(const uint8* planes[]) const;
- virtual void GetDataStride(int strides[]) const;
+ virtual void CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback);
+ virtual void ScreenConfigurationChanged();
private:
// Generates an image in the front buffer.
diff --git a/remoting/host/capturer_gdi.cc b/remoting/host/capturer_gdi.cc
index 35a8c43..7f54aa9 100644
--- a/remoting/host/capturer_gdi.cc
+++ b/remoting/host/capturer_gdi.cc
@@ -10,71 +10,41 @@ namespace remoting {
// 3780 pixels per meter is equivalent to 96 DPI, typical on desktop monitors.
static const int kPixelsPerMeter = 3780;
-// 24 bit RGB is 3 bytes per pixel.
+// 32 bit RGBA is 4 bytes per pixel.
static const int kBytesPerPixel = 4;
-CapturerGdi::CapturerGdi()
- : initialized_(false) {
+CapturerGdi::CapturerGdi() : desktop_dc_(NULL),
+ memory_dc_(NULL) {
+ memset(target_bitmap_, 0, sizeof(target_bitmap_));
+ memset(buffers_, 0, sizeof(buffers_));
}
CapturerGdi::~CapturerGdi() {
- if (initialized_) {
- for (int i = kNumBuffers - 1; i >= 0; i--) {
+ ReleaseBuffers();
+}
+
+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]) {
+ DeleteObject(buffers_[i]);
+ buffers_[i] = NULL;
}
}
-}
-
-void CapturerGdi::CaptureFullScreen(Task* done_task) {
- dirty_rects_.clear();
-
- CaptureImage();
- dirty_rects_.push_back(gfx::Rect(width_, height_));
-
- FinishCapture(done_task);
-}
-
-void CapturerGdi::CaptureDirtyRects(Task* done_task) {
- dirty_rects_.clear();
-
- CaptureImage();
- // TODO(garykac): Diff old/new images and generate |dirty_rects_|.
- // Currently, this just marks the entire screen as dirty.
- dirty_rects_.push_back(gfx::Rect(width_, height_));
- FinishCapture(done_task);
-}
-
-void CapturerGdi::CaptureRect(const gfx::Rect& rect, Task* done_task) {
- dirty_rects_.clear();
-
- CaptureImage();
- dirty_rects_.push_back(rect);
-
- FinishCapture(done_task);
-}
-
-void CapturerGdi::GetData(const uint8* planes[]) const {
- planes[0] = static_cast<const uint8*>(buffers_[current_buffer_]);
- planes[1] = planes[2] = NULL;
-}
-
-void CapturerGdi::GetDataStride(int strides[]) const {
- // Only the first plane has data.
- strides[0] = bytes_per_row_;
- strides[1] = strides[2] = 0;
+ desktop_dc_ = NULL;
+ if (memory_dc_) {
+ DeleteDC(memory_dc_);
+ memory_dc_ = NULL;
+ }
}
-int CapturerGdi::GetWidth() const {
- return GetSystemMetrics(SM_CXSCREEN);
-}
+void CapturerGdi::ScreenConfigurationChanged() {
+ ReleaseBuffers();
-int CapturerGdi::GetHeight() const {
- return GetSystemMetrics(SM_CYSCREEN);
-}
-
-// TODO(fbarchard): handle error cases.
-void CapturerGdi::InitializeBuffers() {
desktop_dc_ = GetDC(GetDesktopWindow());
memory_dc_ = CreateCompatibleDC(desktop_dc_);
@@ -85,8 +55,7 @@ void CapturerGdi::InitializeBuffers() {
// Dimensions of screen.
pixel_format_ = PixelFormatRgb32;
- bytes_per_pixel_ = kBytesPerPixel;
- bytes_per_row_ = rounded_width * bytes_per_pixel_;
+ bytes_per_row_ = rounded_width * kBytesPerPixel;
// Create a device independant bitmap (DIB) that is the same size.
BITMAPINFO bmi;
@@ -94,7 +63,7 @@ void CapturerGdi::InitializeBuffers() {
bmi.bmiHeader.biHeight = height_;
bmi.bmiHeader.biWidth = width_;
bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = bytes_per_pixel_ * 8;
+ bmi.bmiHeader.biBitCount = kBytesPerPixel * 8;
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biSizeImage = bytes_per_row_ * height_;
bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter;
@@ -106,13 +75,25 @@ void CapturerGdi::InitializeBuffers() {
static_cast<void**>(&buffers_[i]),
NULL, 0);
}
- initialized_ = true;
+}
+
+void CapturerGdi::CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback) {
+ Capturer::DataPlanes planes;
+ planes.data[0] = static_cast<uint8*>(buffers_[current_buffer_]);
+ planes.strides[0] = bytes_per_row_;
+
+ CaptureImage();
+ scoped_refptr<CaptureData> data(new CaptureData(planes,
+ width(),
+ height(),
+ pixel_format()));
+ data->mutable_dirty_rects() = rects;
+
+ FinishCapture(data, callback);
}
void CapturerGdi::CaptureImage() {
- if (initialized_ == false) {
- InitializeBuffers();
- }
// Selection the target bitmap into the memory dc.
SelectObject(memory_dc_, target_bitmap_[current_buffer_]);
diff --git a/remoting/host/capturer_gdi.h b/remoting/host/capturer_gdi.h
index a132923..4a1949e 100644
--- a/remoting/host/capturer_gdi.h
+++ b/remoting/host/capturer_gdi.h
@@ -12,7 +12,7 @@ typedef HBITMAP BitmapRef;
namespace remoting {
-// CapturerGdi captures 24bit RGB using GDI.
+// CapturerGdi captures 32bit RGB using GDI.
//
// CapturerGdi is doubled buffered as required by Capturer. See
// remoting/host/capturer.h.
@@ -21,17 +21,12 @@ class CapturerGdi : public Capturer {
CapturerGdi();
virtual ~CapturerGdi();
- virtual void CaptureFullScreen(Task* done_task);
- virtual void CaptureDirtyRects(Task* done_task);
- virtual void CaptureRect(const gfx::Rect& rect, Task* done_task);
- virtual void GetData(const uint8* planes[]) const;
- virtual void GetDataStride(int strides[]) const;
- virtual int GetWidth() const;
- virtual int GetHeight() const;
+ virtual void CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback);
+ virtual void ScreenConfigurationChanged();
private:
- // Initialize GDI structures.
- void InitializeBuffers();
+ void ReleaseBuffers();
// Generates an image in the current buffer.
void CaptureImage();
@@ -42,7 +37,6 @@ class CapturerGdi : public Capturer {
// We have two buffers for the screen images as required by Capturer.
void* buffers_[kNumBuffers];
- bool initialized_; // Set to 'true' if buffers are initialized.
DISALLOW_COPY_AND_ASSIGN(CapturerGdi);
};
diff --git a/remoting/host/capturer_linux.cc b/remoting/host/capturer_linux.cc
index f5c89ee..e61d530 100644
--- a/remoting/host/capturer_linux.cc
+++ b/remoting/host/capturer_linux.cc
@@ -13,42 +13,11 @@ CapturerLinux::CapturerLinux() {
CapturerLinux::~CapturerLinux() {
}
-void CapturerLinux::CaptureFullScreen(Task* done_task) {
- dirty_rects_.clear();
-
- CaptureImage();
- dirty_rects_.push_back(gfx::Rect(width_, height_));
-
- FinishCapture(done_task);
-}
-
-void CapturerLinux::CaptureDirtyRects(Task* done_task) {
- dirty_rects_.clear();
-
- CaptureImage();
- // TODO(garykac): Diff old/new images and generate |dirty_rects_|.
- // Currently, this just marks the entire screen as dirty.
- dirty_rects_.push_back(gfx::Rect(width_, height_));
-
- FinishCapture(done_task);
-}
-
-void CapturerLinux::CaptureRect(const gfx::Rect& rect, Task* done_task) {
- dirty_rects_.clear();
-
- CaptureImage();
- dirty_rects_.push_back(rect);
-
- FinishCapture(done_task);
-}
-
-void CapturerLinux::GetData(const uint8* planes[]) const {
-}
-
-void CapturerLinux::GetDataStride(int strides[]) const {
+void CapturerLinux::ScreenConfigurationChanged() {
}
-void CapturerLinux::CaptureImage() {
+void CapturerLinux::CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback) {
}
} // namespace remoting
diff --git a/remoting/host/capturer_linux.h b/remoting/host/capturer_linux.h
index 1b9acb4..db47679 100644
--- a/remoting/host/capturer_linux.h
+++ b/remoting/host/capturer_linux.h
@@ -15,15 +15,12 @@ class CapturerLinux : public Capturer {
CapturerLinux();
virtual ~CapturerLinux();
- virtual void CaptureFullScreen(Task* done_task);
- virtual void CaptureDirtyRects(Task* done_task);
- virtual void CaptureRect(const gfx::Rect& rect, Task* done_task);
- virtual void GetData(const uint8* planes[]) const;
- virtual void GetDataStride(int strides[]) const;
+ virtual void CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback);
+
+ virtual void ScreenConfigurationChanged();
private:
- // Generates an image in the current buffer.
- void CaptureImage();
DISALLOW_COPY_AND_ASSIGN(CapturerLinux);
};
diff --git a/remoting/host/capturer_mac.cc b/remoting/host/capturer_mac.cc
index 08f39c7..52d58c3 100644
--- a/remoting/host/capturer_mac.cc
+++ b/remoting/host/capturer_mac.cc
@@ -4,51 +4,157 @@
#include "remoting/host/capturer_mac.h"
+#include <stddef.h>
+
+#include <OpenGL/CGLMacro.h>
+
namespace remoting {
-// TODO(dmaclach): Implement this class.
-CapturerMac::CapturerMac() {
+CapturerMac::CapturerMac() : cgl_context_(NULL) {
+ // TODO(dmaclach): move this initialization out into session_manager,
+ // or at least have session_manager call into here to initialize it.
+ CGError err
+ = CGRegisterScreenRefreshCallback(CapturerMac::ScreenRefreshCallback,
+ this);
+ DCHECK_EQ(err, kCGErrorSuccess);
+ err = CGScreenRegisterMoveCallback(CapturerMac::ScreenUpdateMoveCallback,
+ this);
+ DCHECK_EQ(err, kCGErrorSuccess);
+ err = CGDisplayRegisterReconfigurationCallback(
+ CapturerMac::DisplaysReconfiguredCallback, this);
+ DCHECK_EQ(err, kCGErrorSuccess);
}
CapturerMac::~CapturerMac() {
+ ReleaseBuffers();
+ CGUnregisterScreenRefreshCallback(CapturerMac::ScreenRefreshCallback, this);
+ CGScreenUnregisterMoveCallback(CapturerMac::ScreenUpdateMoveCallback, this);
+ CGDisplayRemoveReconfigurationCallback(
+ CapturerMac::DisplaysReconfiguredCallback, this);
}
-void CapturerMac::CaptureFullScreen(Task* done_task) {
- dirty_rects_.clear();
+void CapturerMac::ReleaseBuffers() {
+ if (cgl_context_) {
+ CGLDestroyContext(cgl_context_);
+ cgl_context_ = NULL;
+ }
+}
- CaptureImage();
- dirty_rects_.push_back(gfx::Rect(width_, height_));
+void CapturerMac::ScreenConfigurationChanged() {
+ ReleaseBuffers();
+ CGDirectDisplayID mainDevice = CGMainDisplayID();
+
+ width_ = CGDisplayPixelsWide(mainDevice);
+ height_ = CGDisplayPixelsHigh(mainDevice);
+ bytes_per_row_ = width_ * sizeof(uint32_t);
+ pixel_format_ = PixelFormatRgb32;
+ size_t buffer_size = height() * bytes_per_row_;
+ for (int i = 0; i < kNumBuffers; ++i) {
+ buffers_[i].reset(new uint8[buffer_size]);
+ }
+ CGLPixelFormatAttribute attributes[] = {
+ kCGLPFAFullScreen,
+ kCGLPFADisplayMask,
+ (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(mainDevice),
+ (CGLPixelFormatAttribute)0
+ };
+ CGLPixelFormatObj pixel_format = NULL;
+ GLint matching_pixel_format_count = 0;
+ CGLError err = CGLChoosePixelFormat(attributes,
+ &pixel_format,
+ &matching_pixel_format_count);
+ DCHECK_EQ(err, kCGLNoError);
+ err = CGLCreateContext(pixel_format, NULL, &cgl_context_);
+ DCHECK_EQ(err, kCGLNoError);
+ CGLDestroyPixelFormat(pixel_format);
+ CGLSetFullScreen(cgl_context_);
+ CGLSetCurrentContext(cgl_context_);
+}
- FinishCapture(done_task);
+void CapturerMac::CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback) {
+ // TODO(dmaclach): something smarter here in the future.
+ gfx::Rect dirtyRect;
+ for (RectVector::const_iterator i = rects.begin(); i < rects.end(); ++i) {
+ dirtyRect = dirtyRect.Union(*i);
+ }
+
+ CGLContextObj CGL_MACRO_CONTEXT = cgl_context_;
+ glReadBuffer(GL_FRONT);
+ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 4); // Force 4-byte alignment.
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_PACK_SKIP_ROWS, 0);
+ glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+
+ // Read a block of pixels from the frame buffer.
+ glReadPixels(0, 0, width(), height(), GL_BGRA, GL_UNSIGNED_BYTE,
+ buffers_[current_buffer_].get());
+ glPopClientAttrib();
+
+ Capturer::DataPlanes planes;
+ planes.data[0] = buffers_[current_buffer_].get();
+ planes.strides[0] = bytes_per_row_;
+
+ scoped_refptr<CaptureData> data(new CaptureData(planes,
+ width(),
+ height(),
+ pixel_format()));
+ data->mutable_dirty_rects().assign(1, dirtyRect);
+ FinishCapture(data, callback);
}
-void CapturerMac::CaptureDirtyRects(Task* done_task) {
- dirty_rects_.clear();
- CaptureImage();
- // TODO(garykac): Diff old/new images and generate |dirty_rects_|.
- // Currently, this just marks the entire screen as dirty.
- dirty_rects_.push_back(gfx::Rect(width_, height_));
+void CapturerMac::ScreenRefresh(CGRectCount count, const CGRect *rect_array) {
+ RectVector rects;
+ for (CGRectCount i = 0; i < count; ++i) {
+ CGRect rect = rect_array[i];
+ rect.origin.y = height() - rect.size.height;
+ rects.push_back(gfx::Rect(rect));
+ }
+ InvalidateRects(rects);
- FinishCapture(done_task);
}
-void CapturerMac::CaptureRect(const gfx::Rect& rect, Task* done_task) {
- dirty_rects_.clear();
-
- CaptureImage();
- dirty_rects_.push_back(rect);
-
- FinishCapture(done_task);
+void CapturerMac::ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
+ size_t count,
+ const CGRect *rect_array) {
+ RectVector rects;
+ for (CGRectCount i = 0; i < count; ++i) {
+ CGRect rect = rect_array[i];
+ rect.origin.y = height() - rect.size.height;
+ rects.push_back(gfx::Rect(rect));
+ rect = CGRectOffset(rect, delta.dX, delta.dY);
+ rects.push_back(gfx::Rect(rect));
+ }
+ InvalidateRects(rects);
}
-void CapturerMac::GetData(const uint8* planes[]) const {
+void CapturerMac::ScreenRefreshCallback(CGRectCount count,
+ const CGRect *rect_array,
+ void *user_parameter) {
+ CapturerMac *capturer = reinterpret_cast<CapturerMac *>(user_parameter);
+ capturer->ScreenRefresh(count, rect_array);
}
-void CapturerMac::GetDataStride(int strides[]) const {
+void CapturerMac::ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta,
+ size_t count,
+ const CGRect *rect_array,
+ void *user_parameter) {
+ CapturerMac *capturer = reinterpret_cast<CapturerMac *>(user_parameter);
+ capturer->ScreenUpdateMove(delta, count, rect_array);
}
-void CapturerMac::CaptureImage() {
+void CapturerMac::DisplaysReconfiguredCallback(
+ CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void *user_parameter) {
+ if ((display == CGMainDisplayID()) &&
+ !(flags & kCGDisplayBeginConfigurationFlag)) {
+ CapturerMac *capturer = reinterpret_cast<CapturerMac *>(user_parameter);
+ capturer->ScreenConfigurationChanged();
+ }
}
} // namespace remoting
diff --git a/remoting/host/capturer_mac.h b/remoting/host/capturer_mac.h
index da4073e..1ca88ae1 100644
--- a/remoting/host/capturer_mac.h
+++ b/remoting/host/capturer_mac.h
@@ -6,6 +6,9 @@
#define REMOTING_HOST_CAPTURER_MAC_H_
#include "remoting/host/capturer.h"
+#include <ApplicationServices/ApplicationServices.h>
+#include <OpenGL/OpenGL.h>
+#include "base/scoped_ptr.h"
namespace remoting {
@@ -15,16 +18,30 @@ class CapturerMac : public Capturer {
CapturerMac();
virtual ~CapturerMac();
- virtual void CaptureFullScreen(Task* done_task);
- virtual void CaptureDirtyRects(Task* done_task);
- virtual void CaptureRect(const gfx::Rect& rect, Task* done_task);
- virtual void GetData(const uint8* planes[]) const;
- virtual void GetDataStride(int strides[]) const;
+ virtual void CaptureRects(const RectVector& rects,
+ CaptureCompletedCallback* callback);
- private:
- // Generates an image in the current buffer.
- void CaptureImage();
+ virtual void ScreenConfigurationChanged();
+ private:
+ void ScreenRefresh(CGRectCount count, const CGRect *rect_array);
+ void ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
+ size_t count,
+ const CGRect *rect_array);
+ static void ScreenRefreshCallback(CGRectCount count,
+ const CGRect *rect_array,
+ void *user_parameter);
+ static void ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta,
+ size_t count,
+ const CGRect *rect_array,
+ void *user_parameter);
+ static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void *user_parameter);
+
+ void ReleaseBuffers();
+ CGLContextObj cgl_context_;
+ scoped_array<uint8> buffers_[kNumBuffers];
DISALLOW_COPY_AND_ASSIGN(CapturerMac);
};
diff --git a/remoting/host/capturer_mac_unittest.cc b/remoting/host/capturer_mac_unittest.cc
index 1202861..e59a0bb 100644
--- a/remoting/host/capturer_mac_unittest.cc
+++ b/remoting/host/capturer_mac_unittest.cc
@@ -2,12 +2,75 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <iostream>
+
+#include "base/callback.h"
+#include "base/scoped_ptr.h"
+#include "remoting/host/capturer_mac.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace remoting {
-TEST(CapturerMacTest, Capture) {
- // TODO(dmaclach): implement this.
+class CapturerMacTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ capturer_.reset(new CapturerMac());
+ capturer_->ScreenConfigurationChanged();
+ rects_.push_back(gfx::Rect(0, 0, 10, 10));
+ }
+
+ scoped_ptr<CapturerMac> capturer_;
+ RectVector rects_;
+};
+
+class CapturerCallback {
+ public:
+ explicit CapturerCallback(const RectVector& rects) : rects_(rects) { }
+ void CaptureDoneCallback(scoped_refptr<Capturer::CaptureData> capture_data);
+
+ protected:
+ RectVector rects_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CapturerCallback);
+};
+
+void CapturerCallback::CaptureDoneCallback(
+ scoped_refptr<Capturer::CaptureData> capture_data) {
+ CGDirectDisplayID mainDevice = CGMainDisplayID();
+ int width = CGDisplayPixelsWide(mainDevice);
+ int height = CGDisplayPixelsHigh(mainDevice);
+
+ EXPECT_EQ(rects_, capture_data->dirty_rects());
+ EXPECT_EQ(width, capture_data->width());
+ EXPECT_EQ(height, capture_data->height());
+ const Capturer::DataPlanes &planes = capture_data->data_planes();
+ EXPECT_TRUE(planes.data[0] != NULL);
+ EXPECT_TRUE(planes.data[1] == NULL);
+ EXPECT_TRUE(planes.data[2] == NULL);
+ EXPECT_EQ(static_cast<int>(sizeof(uint32_t) * width),
+ planes.strides[0]);
+ EXPECT_EQ(0, planes.strides[1]);
+ EXPECT_EQ(0, planes.strides[2]);
+}
+
+TEST_F(CapturerMacTest, Capture) {
+ SCOPED_TRACE("");
+ CapturerCallback capturer(rects_);
+ capturer_->InvalidateRects(rects_);
+ capturer_->CaptureInvalidRects(
+ NewCallback(&capturer, &CapturerCallback::CaptureDoneCallback));
}
} // namespace remoting
+
+std::ostream& operator<<(std::ostream& out, const remoting::RectVector& rects) {
+ for (remoting::RectVector::const_iterator i = rects.begin();
+ i < rects.end();
+ ++i) {
+ out << *i << std::endl;
+ }
+ return out;
+}
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index 8510678..3c349fa 100644
--- a/remoting/host/chromoting_host.cc
+++ b/remoting/host/chromoting_host.cc
@@ -33,6 +33,9 @@ ChromotingHost::~ChromotingHost() {
}
void ChromotingHost::Start(Task* shutdown_task) {
+ // Get capturer to set up it's initial configuration.
+ capturer_->ScreenConfigurationChanged();
+
// Submit a task to perform host registration. We'll also start
// listening to connection if registration is done.
context_->main_message_loop()->PostTask(
diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h
index fd07791..ee15b47a 100644
--- a/remoting/host/chromoting_host.h
+++ b/remoting/host/chromoting_host.h
@@ -57,7 +57,7 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
Capturer* capturer, Encoder* encoder, EventExecutor* executor);
virtual ~ChromotingHost();
- // Start the host porcess. This methods starts the chromoting host
+ // Start the host process. This method starts the chromoting host
// asynchronously.
//
// |shutdown_task| is called if Start() has failed ot Shutdown() is called
@@ -66,7 +66,7 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
// This method can only be called once during the lifetime of this object.
void Start(Task* shutdown_task);
- // This method is called when we need to the host process.
+ // This method is called when we need to destroy the host process.
void Shutdown();
// This method is called if a client is connected to this object.
diff --git a/remoting/host/encoder.h b/remoting/host/encoder.h
index 808b34f..09cbdde 100644
--- a/remoting/host/encoder.h
+++ b/remoting/host/encoder.h
@@ -12,10 +12,8 @@
#include "remoting/host/capturer.h"
namespace media {
-
-class DataBuffer;
-
-} // namespace media
+ class DataBuffer;
+}
namespace remoting {
@@ -44,34 +42,16 @@ class Encoder {
virtual ~Encoder() {}
- // Encode an image stored in |input_data|. |dirty_rects| contains
- // regions of update since last encode.
+ // Encode an image stored in |capture_data|.
//
// If |key_frame| is true, the encoder should not reference
// previous encode and encode the full frame.
//
- // When encoded data is available, partial or full |data_available_task|
- // is called, data can be read from |data| and size is |data_size|.
- // After the last data available event and encode has completed,
- // |encode_done| is set to true and |data_available_task| is deleted.
- //
- // Note that |input_data| and |stride| are arrays of 3 elements.
- //
- // Implementation has to ensure that when |data_available_task| is called
- // output parameters are stable.
- virtual void Encode(const DirtyRects& dirty_rects,
- const uint8* const* input_data,
- const int* strides,
+ // When encoded data is available, partial or full |data_available_callback|
+ // is called.
+ virtual void Encode(scoped_refptr<Capturer::CaptureData> capture_data,
bool key_frame,
DataAvailableCallback* data_available_callback) = 0;
-
- // Set the dimension of the incoming images. Need to call this before
- // calling Encode().
- virtual void SetSize(int width, int height) = 0;
-
- // Set the pixel format of the incoming images. Need to call this before
- // calling Encode().
- virtual void SetPixelFormat(PixelFormat pixel_format) = 0;
};
} // namespace remoting
diff --git a/remoting/host/encoder_verbatim.cc b/remoting/host/encoder_verbatim.cc
index ce0d405..f1e2b46 100644
--- a/remoting/host/encoder_verbatim.cc
+++ b/remoting/host/encoder_verbatim.cc
@@ -12,19 +12,16 @@ namespace remoting {
using media::DataBuffer;
-void EncoderVerbatim::Encode(const DirtyRects& dirty_rects,
- const uint8* const* input_data,
- const int* strides,
+void EncoderVerbatim::Encode(scoped_refptr<Capturer::CaptureData> capture_data,
bool key_frame,
DataAvailableCallback* data_available_callback) {
- int num_rects = dirty_rects.size();
+ int num_rects = capture_data->dirty_rects().size();
for (int i = 0; i < num_rects; i++) {
scoped_refptr<DataBuffer> data;
- gfx::Rect dirty_rect = dirty_rects[i];
+ const gfx::Rect& dirty_rect = capture_data->dirty_rects()[i];
scoped_ptr<UpdateStreamPacketHeader> header(new UpdateStreamPacketHeader);
if (EncodeRect(dirty_rect,
- input_data,
- strides,
+ capture_data,
header.get(),
&data)) {
EncodingState state = EncodingInProgress;
@@ -43,28 +40,21 @@ void EncoderVerbatim::Encode(const DirtyRects& dirty_rects,
delete data_available_callback;
}
-void EncoderVerbatim::SetSize(int width, int height) {
- width_ = width;
- height_ = height;
-}
-
-void EncoderVerbatim::SetPixelFormat(PixelFormat pixel_format) {
- bytes_per_pixel_ = GetBytesPerPixel(pixel_format);
- pixel_format_ = pixel_format;
-}
-
-bool EncoderVerbatim::EncodeRect(const gfx::Rect& dirty,
- const uint8* const* input_data,
- const int* strides,
- UpdateStreamPacketHeader *header,
- scoped_refptr<DataBuffer>* output_data) {
- const int kPlanes = 3;
+bool EncoderVerbatim::EncodeRect(
+ const gfx::Rect& dirty,
+ const scoped_refptr<Capturer::CaptureData>& capture_data,
+ UpdateStreamPacketHeader* header,
+ scoped_refptr<DataBuffer>* output_data) {
+ int bytes_per_pixel = GetBytesPerPixel(capture_data->pixel_format());
+ int row_size = bytes_per_pixel * dirty.width();
// Calculate the size of output.
int output_size = 0;
- for (int i = 0; i < kPlanes; ++i) {
+ for (int i = 0; i < Capturer::DataPlanes::kPlaneCount; ++i) {
// TODO(hclam): Handle YUV since the height would be different.
- output_size += strides[i] * height_;
+ const uint8* in = capture_data->data_planes().data[i];
+ if (!in) continue;
+ output_size += row_size * dirty.height();
}
header->set_x(dirty.x());
@@ -72,23 +62,21 @@ bool EncoderVerbatim::EncodeRect(const gfx::Rect& dirty,
header->set_width(dirty.width());
header->set_height(dirty.height());
header->set_encoding(EncodingNone);
- header->set_pixel_format(pixel_format_);
+ header->set_pixel_format(capture_data->pixel_format());
*output_data = new DataBuffer(new uint8[output_size], output_size);
uint8* out = (*output_data)->GetWritableData();
- for (int i = 0; i < kPlanes; ++i) {
- const uint8* in = input_data[i];
+ for (int i = 0; i < Capturer::DataPlanes::kPlaneCount; ++i) {
+ const uint8* in = capture_data->data_planes().data[i];
// Skip over planes that don't have data.
- if (!in)
- continue;
+ if (!in) continue;
// TODO(hclam): Handle YUV since the height would be different.
- for (int j = 0; j < height_; ++j) {
- int row_size = width_ * bytes_per_pixel_;
- DCHECK_LE(row_size, strides[i]);
+ for (int j = 0; j < dirty.height(); ++j) {
+ DCHECK_LE(row_size, capture_data->data_planes().strides[i]);
memcpy(out, in, row_size);
- in += strides[i];
+ in += capture_data->data_planes().strides[i];
out += row_size;
}
}
diff --git a/remoting/host/encoder_verbatim.h b/remoting/host/encoder_verbatim.h
index 6d371ea..745d544 100644
--- a/remoting/host/encoder_verbatim.h
+++ b/remoting/host/encoder_verbatim.h
@@ -13,31 +13,20 @@ namespace remoting {
// buffer verbatim.
class EncoderVerbatim : public Encoder {
public:
- EncoderVerbatim()
- : width_(0), height_(0), bytes_per_pixel_(0) {}
+ EncoderVerbatim() {}
virtual ~EncoderVerbatim() {}
- virtual void Encode(const DirtyRects& dirty_rects,
- const uint8* const* input_data,
- const int* strides,
+ virtual void Encode(scoped_refptr<Capturer::CaptureData> capture_data,
bool key_frame,
DataAvailableCallback* data_available_callback);
- virtual void SetSize(int width, int height);
- virtual void SetPixelFormat(PixelFormat pixel_format);
private:
// Encode a single dirty rect. Called by Encode().
// Returns false if there is an error.
bool EncodeRect(const gfx::Rect& dirty,
- const uint8* const* input_data,
- const int* strides,
- UpdateStreamPacketHeader *header,
- scoped_refptr<media::DataBuffer> *output_data);
-
- int width_;
- int height_;
- int bytes_per_pixel_;
- PixelFormat pixel_format_;
+ const scoped_refptr<Capturer::CaptureData>& capture_data,
+ UpdateStreamPacketHeader* header,
+ scoped_refptr<media::DataBuffer>* output_data);
};
} // namespace remoting
diff --git a/remoting/host/mock_objects.h b/remoting/host/mock_objects.h
index 597c25e..0585864 100644
--- a/remoting/host/mock_objects.h
+++ b/remoting/host/mock_objects.h
@@ -19,15 +19,14 @@ class MockCapturer : public Capturer {
public:
MockCapturer() {}
- MOCK_METHOD1(CaptureFullScreen, void(Task* done_task));
- MOCK_METHOD1(CaptureDirtyRects, void(Task* done_task));
- MOCK_METHOD2(CaptureRect, void(const gfx::Rect& rect, Task* done_task));
- MOCK_CONST_METHOD1(GetData, void(const uint8* planes[]));
- MOCK_CONST_METHOD1(GetDataStride, void(int strides[]));
- MOCK_CONST_METHOD1(GetDirtyRects, void(DirtyRects* rects));
- MOCK_CONST_METHOD0(GetWidth, int());
- MOCK_CONST_METHOD0(GetHeight, int());
- MOCK_CONST_METHOD0(GetPixelFormat, PixelFormat());
+ MOCK_METHOD1(CaptureInvalidRects, void(CaptureCompletedCallback* callback));
+ MOCK_METHOD2(CaptureRects, void(const RectVector& rects,
+ CaptureCompletedCallback* callback));
+ MOCK_METHOD1(InvalidateRects, void(const RectVector& inval_rects));
+ MOCK_METHOD0(InvalidateFullScreen, void());
+ MOCK_METHOD0(ScreenConfigurationChanged, void());
+ MOCK_CONST_METHOD0(width, int());
+ MOCK_CONST_METHOD0(height, int());
private:
DISALLOW_COPY_AND_ASSIGN(MockCapturer);
@@ -37,14 +36,10 @@ class MockEncoder : public Encoder {
public:
MockEncoder() {}
- MOCK_METHOD5(Encode, void(
- const DirtyRects& dirty_rects,
- const uint8* const* input_data,
- const int* strides,
- bool key_frame,
- DataAvailableCallback* data_available_callback));
- MOCK_METHOD2(SetSize, void(int width, int height));
- MOCK_METHOD1(SetPixelFormat, void(PixelFormat pixel_format));
+ MOCK_METHOD3(Encode, void(
+ scoped_refptr<Capturer::CaptureData> capture_data,
+ bool key_frame,
+ DataAvailableCallback* data_available_callback));
private:
DISALLOW_COPY_AND_ASSIGN(MockEncoder);
diff --git a/remoting/host/session_manager.cc b/remoting/host/session_manager.cc
index 1e04482..29dfb8b 100644
--- a/remoting/host/session_manager.cc
+++ b/remoting/host/session_manager.cc
@@ -147,22 +147,23 @@ void SessionManager::RemoveAllClients() {
void SessionManager::DoCapture() {
DCHECK_EQ(capture_loop_, MessageLoop::current());
-
// Make sure we have at most two oustanding recordings. We can simply return
// if we can't make a capture now, the next capture will be started by the
// end of an encode operation.
- if (recordings_ >= 2 || !started_)
+ if (recordings_ >= 2 || !started_) {
return;
+ }
base::Time now = base::Time::Now();
base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
static_cast<int>(base::Time::kMillisecondsPerSecond / rate_));
base::TimeDelta elapsed = now - last_capture_time_;
- // If this method is called sonner than the required interval we return
+ // If this method is called sooner than the required interval we return
// immediately
- if (elapsed < interval)
+ if (elapsed < interval) {
return;
+ }
// At this point we are going to perform one capture so save the current time.
last_capture_time_ = now;
@@ -172,8 +173,14 @@ void SessionManager::DoCapture() {
ScheduleNextCapture();
// And finally perform one capture.
- capturer()->CaptureDirtyRects(
- NewRunnableMethod(this, &SessionManager::CaptureDoneTask));
+ DCHECK(capturer_.get());
+
+ // TODO(dmaclach): make this not required on the Mac eventually.
+ // Will require getting the X11client to work properly. Right now X11 expects
+ // full screens each pass.
+ capturer_->InvalidateFullScreen();
+ capturer_->CaptureInvalidRects(
+ NewCallback(this, &SessionManager::CaptureDoneCallback));
}
void SessionManager::DoFinishEncode() {
@@ -189,22 +196,14 @@ void SessionManager::DoFinishEncode() {
DoCapture();
}
-void SessionManager::DoEncode(const CaptureData *capture_data) {
- // Take ownership of capture_data.
- scoped_ptr<const CaptureData> capture_data_owner(capture_data);
-
+void SessionManager::DoEncode(
+ scoped_refptr<Capturer::CaptureData> capture_data) {
DCHECK_EQ(encode_loop_, MessageLoop::current());
// TODO(hclam): Enable |force_refresh| if a new client was
// added.
- encoder()->SetSize(capture_data->width_, capture_data->height_);
- encoder()->SetPixelFormat(capture_data->pixel_format_);
- encoder()->Encode(
- capture_data->dirty_rects_,
- capture_data->data_,
- capture_data->data_strides_,
- false,
- NewCallback(this, &SessionManager::EncodeDataAvailableTask));
+ encoder_->Encode(capture_data, false,
+ NewCallback(this, &SessionManager::EncodeDataAvailableTask));
}
void SessionManager::DoSendUpdate(const UpdateStreamPacketHeader* header,
@@ -214,15 +213,17 @@ void SessionManager::DoSendUpdate(const UpdateStreamPacketHeader* header,
scoped_ptr<const UpdateStreamPacketHeader> header_owner(header);
DCHECK_EQ(network_loop_, MessageLoop::current());
- for (size_t i = 0; i < clients_.size(); ++i) {
+ for (ClientConnectionList::const_iterator i = clients_.begin();
+ i < clients_.end();
+ ++i) {
if (state & Encoder::EncodingStarting) {
- clients_[i]->SendBeginUpdateStreamMessage();
+ (*i)->SendBeginUpdateStreamMessage();
}
- clients_[i]->SendUpdateStreamPacketMessage(header, data);
+ (*i)->SendUpdateStreamPacketMessage(header, data);
if (state & Encoder::EncodingEnded) {
- clients_[i]->SendEndUpdateStreamMessage();
+ (*i)->SendEndUpdateStreamMessage();
}
}
}
@@ -238,11 +239,11 @@ void SessionManager::DoSendInit(scoped_refptr<ClientConnection> client,
void SessionManager::DoGetInitInfo(scoped_refptr<ClientConnection> client) {
DCHECK_EQ(capture_loop_, MessageLoop::current());
- // Sends the init message to the cleint.
+ // Sends the init message to the client.
network_loop_->PostTask(
FROM_HERE,
NewRunnableMethod(this, &SessionManager::DoSendInit, client,
- capturer()->GetWidth(), capturer()->GetHeight()));
+ capturer()->width(), capturer()->height()));
// And then add the client to the list so it can receive update stream.
// It is important we do so in such order or the client will receive
@@ -359,22 +360,12 @@ void SessionManager::ScheduleNextRateControl() {
kRateControlInterval.InMilliseconds());
}
-void SessionManager::CaptureDoneTask() {
+void SessionManager::CaptureDoneCallback(
+ scoped_refptr<Capturer::CaptureData> capture_data) {
DCHECK_EQ(capture_loop_, MessageLoop::current());
-
- scoped_ptr<CaptureData> data(new CaptureData);
-
- // Save results of the capture.
- capturer()->GetData(data->data_);
- capturer()->GetDataStride(data->data_strides_);
- capturer()->GetDirtyRects(&data->dirty_rects_);
- data->pixel_format_ = capturer()->GetPixelFormat();
- data->width_ = capturer()->GetWidth();
- data->height_ = capturer()->GetHeight();
-
encode_loop_->PostTask(
FROM_HERE,
- NewRunnableMethod(this, &SessionManager::DoEncode, data.release()));
+ NewRunnableMethod(this, &SessionManager::DoEncode, capture_data));
}
void SessionManager::EncodeDataAvailableTask(
diff --git a/remoting/host/session_manager.h b/remoting/host/session_manager.h
index d624a35..8f2cbc1 100644
--- a/remoting/host/session_manager.h
+++ b/remoting/host/session_manager.h
@@ -99,18 +99,6 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
void RemoveAllClients();
private:
-
- // Stores the data and information of a capture to pass off to the
- // encoding thread.
- struct CaptureData {
- DirtyRects dirty_rects_;
- const uint8* data_[3];
- int data_strides_[3];
- int width_;
- int height_;
- PixelFormat pixel_format_;
- };
-
void DoStart();
void DoPause();
void DoStartRateControl();
@@ -119,9 +107,7 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
void DoCapture();
void DoFinishEncode();
- // DoEncode takes ownership of capture_data and is responsible for deleting
- // it.
- void DoEncode(const CaptureData *capture_data);
+ void DoEncode(scoped_refptr<Capturer::CaptureData> capture_data);
// DoSendUpdate takes ownership of header and is responsible for deleting it.
void DoSendUpdate(const UpdateStreamPacketHeader* header,
const scoped_refptr<media::DataBuffer> data,
@@ -142,7 +128,7 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
// Helper method to schedule next rate regulation task.
void ScheduleNextRateControl();
- void CaptureDoneTask();
+ void CaptureDoneCallback(scoped_refptr<Capturer::CaptureData> capture_data);
// EncodeDataAvailableTask takes ownership of header and is responsible for
// deleting it.
void EncodeDataAvailableTask(const UpdateStreamPacketHeader *header,
diff --git a/remoting/host/session_manager_unittest.cc b/remoting/host/session_manager_unittest.cc
index a4134a8..5a4e4eb 100644
--- a/remoting/host/session_manager_unittest.cc
+++ b/remoting/host/session_manager_unittest.cc
@@ -18,16 +18,6 @@ namespace remoting {
static const int kWidth = 640;
static const int kHeight = 480;
-static int kStride[3] = {
- kWidth * 4,
- kWidth * 4,
- kWidth * 4,
-};
-static uint8* kData[3] = {
- reinterpret_cast<uint8*>(0x01),
- reinterpret_cast<uint8*>(0x02),
- reinterpret_cast<uint8*>(0x03),
-};
static const PixelFormat kFormat = PixelFormatRgb32;
static const UpdateStreamEncoding kEncoding = EncodingNone;
@@ -53,7 +43,6 @@ class SessionManagerTest : public testing::Test {
MockCapturer* capturer_;
MockEncoder* encoder_;
MessageLoop message_loop_;
-
private:
DISALLOW_COPY_AND_ASSIGN(SessionManagerTest);
};
@@ -62,8 +51,10 @@ TEST_F(SessionManagerTest, Init) {
Init();
}
-ACTION(RunSimpleTask) {
- arg0->Run();
+ACTION_P2(RunCallback, rects, data) {
+ RectVector& dirty_rects = data->mutable_dirty_rects();
+ dirty_rects.insert(dirty_rects.end(), rects.begin(), rects.end());
+ arg0->Run(data);
delete arg0;
}
@@ -78,7 +69,7 @@ ACTION_P3(FinishDecode, rects, buffer, header) {
header->set_height(rect.height());
header->set_encoding(kEncoding);
header->set_pixel_format(kFormat);
- arg4->Run(header, *buffer, state);
+ arg2->Run(header, *buffer, state);
}
ACTION_P(AssignCaptureData, data) {
@@ -94,38 +85,36 @@ ACTION_P(AssignDirtyRect, rects) {
TEST_F(SessionManagerTest, OneRecordCycle) {
Init();
+ RectVector update_rects;
+ update_rects.push_back(gfx::Rect(0, 0, 10, 10));
+ Capturer::DataPlanes planes;
+ for (int i = 0; i < Capturer::DataPlanes::kPlaneCount; ++i) {
+ planes.data[i] = reinterpret_cast<uint8*>(i);
+ planes.strides[i] = kWidth * 4;
+ }
+ scoped_refptr<Capturer::CaptureData> data(new Capturer::CaptureData(planes,
+ kWidth,
+ kHeight,
+ kFormat));
// Set the recording rate to very low to avoid capture twice.
record_->SetMaxRate(0.01);
// Add the mock client connection to the session.
- EXPECT_CALL(*capturer_, GetWidth()).WillRepeatedly(Return(kWidth));
- EXPECT_CALL(*capturer_, GetHeight()).WillRepeatedly(Return(kHeight));
+ EXPECT_CALL(*capturer_, width()).WillRepeatedly(Return(kWidth));
+ EXPECT_CALL(*capturer_, height()).WillRepeatedly(Return(kHeight));
EXPECT_CALL(*client_, SendInitClientMessage(kWidth, kHeight));
record_->AddClient(client_);
// First the capturer is called.
- EXPECT_CALL(*capturer_, CaptureDirtyRects(NotNull()))
- .WillOnce(RunSimpleTask());
-
- // Create a dirty rect that can be verified.
- DirtyRects rects;
- rects.push_back(gfx::Rect(0, 0, 10, 10));
- EXPECT_CALL(*capturer_, GetDirtyRects(NotNull()))
- .WillOnce(AssignDirtyRect(&rects));
- EXPECT_CALL(*capturer_, GetData(NotNull()))
- .WillOnce(AssignCaptureData(kData));
- EXPECT_CALL(*capturer_, GetDataStride(NotNull()))
- .WillOnce(AssignCaptureData(kStride));
- EXPECT_CALL(*capturer_, GetPixelFormat())
- .WillOnce(Return(kFormat));
+ EXPECT_CALL(*capturer_, InvalidateFullScreen());
+ EXPECT_CALL(*capturer_, CaptureInvalidRects(NotNull()))
+ .WillOnce(RunCallback(update_rects, data));
// Expect the encoder be called.
scoped_refptr<media::DataBuffer> buffer = new media::DataBuffer(0);
- EXPECT_CALL(*encoder_, SetSize(kWidth, kHeight));
- EXPECT_CALL(*encoder_, SetPixelFormat(kFormat));
UpdateStreamPacketHeader *header = new UpdateStreamPacketHeader;
- EXPECT_CALL(*encoder_, Encode(rects, NotNull(), NotNull(), false, NotNull()))
- .WillOnce(FinishDecode(&rects, &buffer, header));
+ EXPECT_CALL(*encoder_, Encode(data, false, NotNull()))
+ .WillOnce(FinishDecode(&update_rects, &buffer, header));
// Expect the client be notified.
EXPECT_CALL(*client_, SendBeginUpdateStreamMessage());