diff options
author | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-10 05:00:14 +0000 |
---|---|---|
committer | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-10 05:00:14 +0000 |
commit | 3c2a701bb59b792becab4496d6dfa8fb2e891085 (patch) | |
tree | 86acff92d453e09f9435355bb3787ff6d7f27e79 /remoting/host/video_frame_capturer_win.cc | |
parent | 293a0ee99b970d702c25279616ba9d7d9485bb36 (diff) | |
download | chromium_src-3c2a701bb59b792becab4496d6dfa8fb2e891085.zip chromium_src-3c2a701bb59b792becab4496d6dfa8fb2e891085.tar.gz chromium_src-3c2a701bb59b792becab4496d6dfa8fb2e891085.tar.bz2 |
Brushing up video capturers to make them more uniform.
- Video frame buffers are derived from common VideoFrameBuffer class (a data structure really).
- The video frame queue logic was moved to VideoFrameCapturerQueue class.
BUG=134694
Review URL: https://chromiumcodereview.appspot.com/11369120
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@167058 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/host/video_frame_capturer_win.cc')
-rw-r--r-- | remoting/host/video_frame_capturer_win.cc | 268 |
1 files changed, 124 insertions, 144 deletions
diff --git a/remoting/host/video_frame_capturer_win.cc b/remoting/host/video_frame_capturer_win.cc index 8d33620..3386c11 100644 --- a/remoting/host/video_frame_capturer_win.cc +++ b/remoting/host/video_frame_capturer_win.cc @@ -15,7 +15,9 @@ #include "base/win/scoped_hdc.h" #include "remoting/base/capture_data.h" #include "remoting/host/differ.h" +#include "remoting/host/video_frame.h" #include "remoting/host/video_frame_capturer_helper.h" +#include "remoting/host/video_frame_queue.h" #include "remoting/host/win/desktop.h" #include "remoting/host/win/scoped_thread_desktop.h" #include "remoting/proto/control.pb.h" @@ -38,6 +40,22 @@ const uint32 kPixelBgraBlack = 0xff000000; const uint32 kPixelBgraWhite = 0xffffffff; const uint32 kPixelBgraTransparent = 0x00000000; +// A class representing a full-frame pixel buffer. +class VideoFrameWin : public VideoFrame { + public: + VideoFrameWin(HDC desktop_dc, const SkISize& size); + virtual ~VideoFrameWin(); + + // Returns handle of the device independent bitmap representing this frame + // buffer to GDI. + HBITMAP GetBitmap(); + + private: + base::win::ScopedBitmap bitmap_; + + DISALLOW_COPY_AND_ASSIGN(VideoFrameWin); +}; + // VideoFrameCapturerWin captures 32bit RGB using GDI. // // VideoFrameCapturerWin is double-buffered as required by VideoFrameCapturer. @@ -56,33 +74,14 @@ class VideoFrameCapturerWin : public VideoFrameCapturer { virtual const SkISize& size_most_recent() const OVERRIDE; private: - struct VideoFrameBuffer { - VideoFrameBuffer(); - - void* data; - SkISize size; - int bytes_per_pixel; - int bytes_per_row; - int resource_generation; - }; - - // Make sure that the device contexts and the current buffer match the screen - // configuration. + // Make sure that the device contexts match the screen configuration. void PrepareCaptureResources(); - // Allocates the specified capture buffer using the current device contexts - // and desktop dimensions, releasing any pre-existing buffer. - void AllocateBuffer(int buffer_index, int resource_generation); - - // Compares the most recently captured screen contents with the previous - // contents reported to the caller, to determine the dirty region. - void CalculateInvalidRegion(); - // Creates a CaptureData instance wrapping the current framebuffer and // notifies |delegate_|. void CaptureRegion(const SkRegion& region); - // Captures the current screen contents into the next available framebuffer. + // Captures the current screen contents into the current buffer. void CaptureImage(); // Expand the cursor shape to add a white outline for visibility against @@ -104,24 +103,18 @@ class VideoFrameCapturerWin : public VideoFrameCapturer { scoped_array<uint8> last_cursor_; SkISize last_cursor_size_; - // There are two buffers for the screen images, as required by Capturer. - static const int kNumBuffers = 2; - VideoFrameBuffer buffers_[kNumBuffers]; + // Queue of the frames buffers. + VideoFrameQueue queue_; ScopedThreadDesktop desktop_; // GDI resources used for screen capture. scoped_ptr<base::win::ScopedGetDC> desktop_dc_; base::win::ScopedCreateDC memory_dc_; - base::win::ScopedBitmap target_bitmap_[kNumBuffers]; - int resource_generation_; // Rectangle describing the bounds of the desktop device context. SkIRect desktop_dc_rect_; - // The current buffer with valid data for reading. - int current_buffer_; - // Format of pixels returned in buffer. media::VideoFrame::Format pixel_format_; @@ -139,20 +132,46 @@ static const int kPixelsPerMeter = 3780; // 32 bit RGBA is 4 bytes per pixel. static const int kBytesPerPixel = 4; -VideoFrameCapturerWin::VideoFrameBuffer::VideoFrameBuffer() - : data(NULL), - size(SkISize::Make(0, 0)), - bytes_per_pixel(0), - bytes_per_row(0), - resource_generation(0) { +VideoFrameWin::VideoFrameWin(HDC desktop_dc, const SkISize& size) { + // Describe a device independent bitmap (DIB) that is the size of the desktop. + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biHeight = -size.height(); + bmi.bmiHeader.biWidth = size.width(); + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = kBytesPerPixel * 8; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + int bytes_per_row = size.width() * kBytesPerPixel; + bmi.bmiHeader.biSizeImage = bytes_per_row * size.height(); + bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter; + bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter; + + // Create the DIB, and store a pointer to its pixel buffer. + void* data = NULL; + bitmap_ = CreateDIBSection(desktop_dc, &bmi, DIB_RGB_COLORS, &data, NULL, 0); + + // TODO(wez): Cope gracefully with failure (crbug.com/157170). + CHECK(bitmap_ != NULL); + CHECK(data != NULL); + + set_pixels(reinterpret_cast<uint8*>(data)); + set_dimensions(SkISize::Make(bmi.bmiHeader.biWidth, + std::abs(bmi.bmiHeader.biHeight))); + set_bytes_per_row( + bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight)); +} + +VideoFrameWin::~VideoFrameWin() { +} + +HBITMAP VideoFrameWin::GetBitmap() { + return bitmap_; } VideoFrameCapturerWin::VideoFrameCapturerWin() : delegate_(NULL), last_cursor_size_(SkISize::Make(0, 0)), desktop_dc_rect_(SkIRect::MakeEmpty()), - resource_generation_(0), - current_buffer_(0), pixel_format_(media::VideoFrame::RGB32), composition_func_(NULL) { } @@ -172,8 +191,39 @@ void VideoFrameCapturerWin::CaptureInvalidRegion() { // Force the system to power-up display hardware, if it has been suspended. SetThreadExecutionState(ES_DISPLAY_REQUIRED); - // Perform the capture. - CalculateInvalidRegion(); + // Make sure the GDI capture resources are up-to-date. + PrepareCaptureResources(); + + // Copy screen bits to the current buffer. + CaptureImage(); + + const VideoFrame* current_buffer = queue_.current_frame(); + const VideoFrame* last_buffer = queue_.previous_frame(); + if (last_buffer) { + // Make sure the differencer is set up correctly for these previous and + // current screens. + if (!differ_.get() || + (differ_->width() != current_buffer->dimensions().width()) || + (differ_->height() != current_buffer->dimensions().height()) || + (differ_->bytes_per_row() != current_buffer->bytes_per_row())) { + differ_.reset(new Differ(current_buffer->dimensions().width(), + current_buffer->dimensions().height(), + kBytesPerPixel, + current_buffer->bytes_per_row())); + } + + // Calculate difference between the two last captured frames. + SkRegion region; + differ_->CalcDirtyRegion(last_buffer->pixels(), current_buffer->pixels(), + ®ion); + InvalidateRegion(region); + } else { + // No previous frame is available. Invalidate the whole screen. + helper_.InvalidateScreen(current_buffer->dimensions()); + } + + // Wrap the captured frame into CaptureData structure and invoke + // the completion callback. SkRegion invalid_region; helper_.SwapInvalidRegion(&invalid_region); CaptureRegion(invalid_region); @@ -246,137 +296,67 @@ void VideoFrameCapturerWin::PrepareCaptureResources() { desktop_dc_rect_.setEmpty(); } - // Create GDI device contexts to capture from the desktop into memory, and - // allocate buffers to capture into. if (desktop_dc_.get() == NULL) { DCHECK(memory_dc_.Get() == NULL); + // Create GDI device contexts to capture from the desktop into memory. desktop_dc_.reset(new base::win::ScopedGetDC(NULL)); memory_dc_.Set(CreateCompatibleDC(*desktop_dc_)); desktop_dc_rect_ = screen_rect; - ++resource_generation_; - } + // Make sure the frame buffers will be reallocated. + queue_.SetAllFramesNeedUpdate(); - // If the current buffer is from an older generation then allocate a new one. - // Note that we can't reallocate other buffers at this point, since the caller - // may still be reading from them. - if (resource_generation_ != buffers_[current_buffer_].resource_generation) { - AllocateBuffer(current_buffer_, resource_generation_); - - SkRegion region; - region.op(SkIRect::MakeSize(helper_.size_most_recent()), - SkRegion::kUnion_Op); - InvalidateRegion(region); - } -} - -void VideoFrameCapturerWin::AllocateBuffer(int buffer_index, - int resource_generation) { - DCHECK(desktop_dc_.get() != NULL); - DCHECK(memory_dc_.Get() != NULL); - // Windows requires DIB sections' rows to start DWORD-aligned, which is - // implicit when working with RGB32 pixels. - DCHECK_EQ(pixel_format_, media::VideoFrame::RGB32); - - // Describe a device independent bitmap (DIB) that is the size of the desktop. - BITMAPINFO bmi; - memset(&bmi, 0, sizeof(bmi)); - bmi.bmiHeader.biHeight = -desktop_dc_rect_.height(); - bmi.bmiHeader.biWidth = desktop_dc_rect_.width(); - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = kBytesPerPixel * 8; - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - int bytes_per_row = desktop_dc_rect_.width() * kBytesPerPixel; - bmi.bmiHeader.biSizeImage = bytes_per_row * desktop_dc_rect_.height(); - bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter; - bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter; - - // Create the DIB, and store a pointer to its pixel buffer. - target_bitmap_[buffer_index] = - CreateDIBSection(*desktop_dc_, &bmi, DIB_RGB_COLORS, - static_cast<void**>(&buffers_[buffer_index].data), - NULL, 0); - - // TODO(wez): Cope gracefully with failure (crbug.com/157170). - CHECK(target_bitmap_[buffer_index] != NULL); - CHECK(buffers_[buffer_index].data != NULL); - - buffers_[buffer_index].size = SkISize::Make(bmi.bmiHeader.biWidth, - std::abs(bmi.bmiHeader.biHeight)); - buffers_[buffer_index].bytes_per_pixel = bmi.bmiHeader.biBitCount / 8; - buffers_[buffer_index].bytes_per_row = - bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight); - buffers_[buffer_index].resource_generation = resource_generation; -} - -void VideoFrameCapturerWin::CalculateInvalidRegion() { - CaptureImage(); - - const VideoFrameBuffer& current = buffers_[current_buffer_]; - - // Find the previous and current screens. - int prev_buffer_id = current_buffer_ - 1; - if (prev_buffer_id < 0) { - prev_buffer_id = kNumBuffers - 1; - } - const VideoFrameBuffer& prev = buffers_[prev_buffer_id]; - - // Maybe the previous and current screens can't be differenced. - if ((current.size != prev.size) || - (current.bytes_per_pixel != prev.bytes_per_pixel) || - (current.bytes_per_row != prev.bytes_per_row)) { - helper_.InvalidateScreen(current.size); - return; - } - - // Make sure the differencer is set up correctly for these previous and - // current screens. - if (!differ_.get() || - (differ_->width() != current.size.width()) || - (differ_->height() != current.size.height()) || - (differ_->bytes_per_pixel() != current.bytes_per_pixel) || - (differ_->bytes_per_row() != current.bytes_per_row)) { - differ_.reset(new Differ(current.size.width(), current.size.height(), - current.bytes_per_pixel, current.bytes_per_row)); + helper_.ClearInvalidRegion(); } - - SkRegion region; - differ_->CalcDirtyRegion(prev.data, current.data, ®ion); - - InvalidateRegion(region); } void VideoFrameCapturerWin::CaptureRegion(const SkRegion& region) { - const VideoFrameBuffer& buffer = buffers_[current_buffer_]; - current_buffer_ = (current_buffer_ + 1) % kNumBuffers; + const VideoFrame* current_buffer = queue_.current_frame(); DataPlanes planes; - planes.data[0] = static_cast<uint8*>(buffer.data); - planes.strides[0] = buffer.bytes_per_row; + planes.data[0] = current_buffer->pixels(); + planes.strides[0] = current_buffer->bytes_per_row(); scoped_refptr<CaptureData> data(new CaptureData(planes, - buffer.size, + current_buffer->dimensions(), pixel_format_)); data->mutable_dirty_region() = region; helper_.set_size_most_recent(data->size()); + queue_.DoneWithCurrentFrame(); delegate_->OnCaptureCompleted(data); } void VideoFrameCapturerWin::CaptureImage() { - // Make sure the GDI capture resources are up-to-date. - PrepareCaptureResources(); - - // Select the target bitmap into the memory dc. - SelectObject(memory_dc_, target_bitmap_[current_buffer_]); + // If the current buffer is from an older generation then allocate a new one. + // Note that we can't reallocate other buffers at this point, since the caller + // may still be reading from them. + if (queue_.current_frame_needs_update()) { + DCHECK(desktop_dc_.get() != NULL); + DCHECK(memory_dc_.Get() != NULL); + // Windows requires DIB sections' rows to start DWORD-aligned, which is + // implicit when working with RGB32 pixels. + DCHECK_EQ(pixel_format_, media::VideoFrame::RGB32); + + SkISize size = SkISize::Make(desktop_dc_rect_.width(), + desktop_dc_rect_.height()); + scoped_ptr<VideoFrameWin> buffer(new VideoFrameWin(*desktop_dc_, size)); + queue_.ReplaceCurrentFrame(buffer.PassAs<VideoFrame>()); + } - // And then copy the rect from desktop to memory. - BitBlt(memory_dc_, 0, 0, buffers_[current_buffer_].size.width(), - buffers_[current_buffer_].size.height(), *desktop_dc_, - desktop_dc_rect_.x(), desktop_dc_rect_.y(), - SRCCOPY | CAPTUREBLT); + // Select the target bitmap into the memory dc and copy the rect from desktop + // to memory. + VideoFrameWin* current = + static_cast<VideoFrameWin*>(queue_.current_frame()); + if (SelectObject(memory_dc_, current->GetBitmap()) != NULL) { + BitBlt(memory_dc_, + 0, 0, desktop_dc_rect_.width(), desktop_dc_rect_.height(), + *desktop_dc_, + desktop_dc_rect_.x(), desktop_dc_rect_.y(), + SRCCOPY | CAPTUREBLT); + } } void VideoFrameCapturerWin::AddCursorOutline(int width, |