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