diff options
author | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-01 15:02:53 +0000 |
---|---|---|
committer | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-01 15:02:53 +0000 |
commit | 804c99625cc375da683a6c1a681178b0014e02fe (patch) | |
tree | 444c6deffd05f7744f0d2b50077aeabde0cc7170 | |
parent | 9da1cedf45ee5bb54cc5558ca044dc24b999b8d9 (diff) | |
download | chromium_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.cc | 40 | ||||
-rw-r--r-- | remoting/host/capturer.h | 131 | ||||
-rw-r--r-- | remoting/host/capturer_fake.cc | 65 | ||||
-rw-r--r-- | remoting/host/capturer_fake.h | 8 | ||||
-rw-r--r-- | remoting/host/capturer_fake_ascii.cc | 68 | ||||
-rw-r--r-- | remoting/host/capturer_fake_ascii.h | 8 | ||||
-rw-r--r-- | remoting/host/capturer_gdi.cc | 101 | ||||
-rw-r--r-- | remoting/host/capturer_gdi.h | 16 | ||||
-rw-r--r-- | remoting/host/capturer_linux.cc | 37 | ||||
-rw-r--r-- | remoting/host/capturer_linux.h | 11 | ||||
-rw-r--r-- | remoting/host/capturer_mac.cc | 154 | ||||
-rw-r--r-- | remoting/host/capturer_mac.h | 33 | ||||
-rw-r--r-- | remoting/host/capturer_mac_unittest.cc | 67 | ||||
-rw-r--r-- | remoting/host/chromoting_host.cc | 3 | ||||
-rw-r--r-- | remoting/host/chromoting_host.h | 4 | ||||
-rw-r--r-- | remoting/host/encoder.h | 32 | ||||
-rw-r--r-- | remoting/host/encoder_verbatim.cc | 56 | ||||
-rw-r--r-- | remoting/host/encoder_verbatim.h | 21 | ||||
-rw-r--r-- | remoting/host/mock_objects.h | 29 | ||||
-rw-r--r-- | remoting/host/session_manager.cc | 65 | ||||
-rw-r--r-- | remoting/host/session_manager.h | 18 | ||||
-rw-r--r-- | remoting/host/session_manager_unittest.cc | 57 |
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()); |