| Index: remoting/host/video_frame_capturer_linux.cc
|
| diff --git a/remoting/host/video_frame_capturer_linux.cc b/remoting/host/video_frame_capturer_linux.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0bcbb4c51cc850e2476a4e7fbac39c168d2dc288
|
| --- /dev/null
|
| +++ b/remoting/host/video_frame_capturer_linux.cc
|
| @@ -0,0 +1,638 @@
|
| +// 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_capturer.h"
|
| +
|
| +#include <X11/Xlib.h>
|
| +#include <X11/Xutil.h>
|
| +#include <X11/extensions/Xdamage.h>
|
| +#include <X11/extensions/Xfixes.h>
|
| +
|
| +#include <set>
|
| +
|
| +#include "base/basictypes.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "remoting/base/capture_data.h"
|
| +#include "remoting/host/differ.h"
|
| +#include "remoting/host/video_frame_capturer_helper.h"
|
| +#include "remoting/host/x_server_pixel_buffer.h"
|
| +#include "remoting/proto/control.pb.h"
|
| +
|
| +namespace remoting {
|
| +
|
| +namespace {
|
| +
|
| +static const int kBytesPerPixel = 4;
|
| +
|
| +// Default to false, since many systems have broken XDamage support - see
|
| +// http://crbug.com/73423.
|
| +static bool g_should_use_x_damage = false;
|
| +
|
| +static bool ShouldUseXDamage() {
|
| + return g_should_use_x_damage;
|
| +}
|
| +
|
| +// A class representing a full-frame pixel buffer
|
| +class VideoFrameBuffer {
|
| + public:
|
| + VideoFrameBuffer() : bytes_per_row_(0), needs_update_(true) {
|
| + size_.set(0, 0);
|
| + }
|
| +
|
| + 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; }
|
| +
|
| + private:
|
| + SkISize size_;
|
| + int bytes_per_row_;
|
| + scoped_array<uint8> ptr_;
|
| + bool needs_update_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(VideoFrameBuffer);
|
| +};
|
| +
|
| +// A class to perform video frame capturing for Linux.
|
| +class VideoFrameCapturerLinux : public VideoFrameCapturer {
|
| + public:
|
| + VideoFrameCapturerLinux();
|
| + virtual ~VideoFrameCapturerLinux();
|
| +
|
| + bool Init(); // TODO(ajwong): Do we really want this to be synchronous?
|
| +
|
| + // Capturer interface.
|
| + virtual void Start(const CursorShapeChangedCallback& callback) OVERRIDE;
|
| + virtual void Stop() OVERRIDE;
|
| + virtual void ScreenConfigurationChanged() OVERRIDE;
|
| + virtual media::VideoFrame::Format pixel_format() const OVERRIDE;
|
| + virtual void ClearInvalidRegion() OVERRIDE;
|
| + virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE;
|
| + virtual void InvalidateScreen(const SkISize& size) OVERRIDE;
|
| + virtual void InvalidateFullScreen() OVERRIDE;
|
| + virtual void CaptureInvalidRegion(
|
| + const CaptureCompletedCallback& callback) OVERRIDE;
|
| + virtual const SkISize& size_most_recent() const OVERRIDE;
|
| +
|
| + private:
|
| + void InitXDamage();
|
| +
|
| + // Read and handle all currently-pending XEvents.
|
| + // In the DAMAGE case, process the XDamage events and store the resulting
|
| + // damage rectangles in the VideoFrameCapturerHelper.
|
| + // In all cases, call ScreenConfigurationChanged() in response to any
|
| + // ConfigNotify events.
|
| + void ProcessPendingXEvents();
|
| +
|
| + // Capture screen pixels, and return the data in a new CaptureData object,
|
| + // to be freed by the caller.
|
| + // In the DAMAGE case, the VideoFrameCapturerHelper already holds the list of
|
| + // invalid rectangles from ProcessPendingXEvents().
|
| + // In the non-DAMAGE case, this captures the whole screen, then calculates
|
| + // some invalid rectangles that include any differences between this and the
|
| + // previous capture.
|
| + CaptureData* CaptureFrame();
|
| +
|
| + // Capture the cursor image and call the CursorShapeChangedCallback if it
|
| + // has been set (using SetCursorShapeChangedCallback).
|
| + void CaptureCursor();
|
| +
|
| + // Synchronize the current buffer with |last_buffer_|, by copying pixels from
|
| + // the area of |last_invalid_rects|.
|
| + // Note this only works on the assumption that kNumBuffers == 2, as
|
| + // |last_invalid_rects| holds the differences from the previous buffer and
|
| + // the one prior to that (which will then be the current buffer).
|
| + void SynchronizeFrame();
|
| +
|
| + void DeinitXlib();
|
| +
|
| + // Capture a rectangle from |x_server_pixel_buffer_|, and copy the data into
|
| + // |capture_data|.
|
| + void CaptureRect(const SkIRect& rect, CaptureData* capture_data);
|
| +
|
| + // We expose two forms of blitting to handle variations in the pixel format.
|
| + // In FastBlit, the operation is effectively a memcpy.
|
| + void FastBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data);
|
| + void SlowBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data);
|
| +
|
| + // X11 graphics context.
|
| + Display* display_;
|
| + GC gc_;
|
| + Window root_window_;
|
| +
|
| + // XFixes.
|
| + bool has_xfixes_;
|
| + int xfixes_event_base_;
|
| + int xfixes_error_base_;
|
| +
|
| + // XDamage information.
|
| + bool use_damage_;
|
| + Damage damage_handle_;
|
| + int damage_event_base_;
|
| + int damage_error_base_;
|
| + XserverRegion damage_region_;
|
| +
|
| + // Access to the X Server's pixel buffer.
|
| + XServerPixelBuffer x_server_pixel_buffer_;
|
| +
|
| + // A thread-safe list of invalid rectangles, and the size of the most
|
| + // recently captured screen.
|
| + VideoFrameCapturerHelper helper_;
|
| +
|
| + // Callback notified whenever the cursor shape is changed.
|
| + CursorShapeChangedCallback cursor_shape_changed_callback_;
|
| +
|
| + // Capture state.
|
| + static const int kNumBuffers = 2;
|
| + VideoFrameBuffer buffers_[kNumBuffers];
|
| + int current_buffer_;
|
| +
|
| + // Format of pixels returned in buffer.
|
| + media::VideoFrame::Format pixel_format_;
|
| +
|
| + // Invalid region from the previous capture. This is used to synchronize the
|
| + // current with the last buffer used.
|
| + SkRegion last_invalid_region_;
|
| +
|
| + // Last capture buffer used.
|
| + uint8* last_buffer_;
|
| +
|
| + // |Differ| for use when polling for changes.
|
| + scoped_ptr<Differ> differ_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerLinux);
|
| +};
|
| +
|
| +VideoFrameCapturerLinux::VideoFrameCapturerLinux()
|
| + : display_(NULL),
|
| + gc_(NULL),
|
| + root_window_(BadValue),
|
| + has_xfixes_(false),
|
| + xfixes_event_base_(-1),
|
| + xfixes_error_base_(-1),
|
| + use_damage_(false),
|
| + damage_handle_(0),
|
| + damage_event_base_(-1),
|
| + damage_error_base_(-1),
|
| + damage_region_(0),
|
| + current_buffer_(0),
|
| + pixel_format_(media::VideoFrame::RGB32),
|
| + last_buffer_(NULL) {
|
| + helper_.SetLogGridSize(4);
|
| +}
|
| +
|
| +VideoFrameCapturerLinux::~VideoFrameCapturerLinux() {
|
| + DeinitXlib();
|
| +}
|
| +
|
| +bool VideoFrameCapturerLinux::Init() {
|
| + // TODO(ajwong): We should specify the display string we are attaching to
|
| + // in the constructor.
|
| + display_ = XOpenDisplay(NULL);
|
| + if (!display_) {
|
| + LOG(ERROR) << "Unable to open display";
|
| + return false;
|
| + }
|
| +
|
| + x_server_pixel_buffer_.Init(display_);
|
| +
|
| + root_window_ = RootWindow(display_, DefaultScreen(display_));
|
| + if (root_window_ == BadValue) {
|
| + LOG(ERROR) << "Unable to get the root window";
|
| + DeinitXlib();
|
| + return false;
|
| + }
|
| +
|
| + gc_ = XCreateGC(display_, root_window_, 0, NULL);
|
| + if (gc_ == NULL) {
|
| + LOG(ERROR) << "Unable to get graphics context";
|
| + DeinitXlib();
|
| + return false;
|
| + }
|
| +
|
| + // Check for XFixes extension. This is required for cursor shape
|
| + // notifications, and for our use of XDamage.
|
| + if (XFixesQueryExtension(display_, &xfixes_event_base_,
|
| + &xfixes_error_base_)) {
|
| + has_xfixes_ = true;
|
| + } else {
|
| + LOG(INFO) << "X server does not support XFixes.";
|
| + }
|
| +
|
| + if (ShouldUseXDamage()) {
|
| + InitXDamage();
|
| + }
|
| +
|
| + // Register for changes to the dimensions of the root window.
|
| + XSelectInput(display_, root_window_, StructureNotifyMask);
|
| +
|
| + if (has_xfixes_) {
|
| + // Register for changes to the cursor shape.
|
| + XFixesSelectCursorInput(display_, root_window_,
|
| + XFixesDisplayCursorNotifyMask);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::InitXDamage() {
|
| + // Our use of XDamage requires XFixes.
|
| + if (!has_xfixes_) {
|
| + return;
|
| + }
|
| +
|
| + // Check for XDamage extension.
|
| + if (!XDamageQueryExtension(display_, &damage_event_base_,
|
| + &damage_error_base_)) {
|
| + LOG(INFO) << "X server does not support XDamage.";
|
| + return;
|
| + }
|
| +
|
| + // TODO(lambroslambrou): Disable DAMAGE in situations where it is known
|
| + // to fail, such as when Desktop Effects are enabled, with graphics
|
| + // drivers (nVidia, ATI) that fail to report DAMAGE notifications
|
| + // properly.
|
| +
|
| + // Request notifications every time the screen becomes damaged.
|
| + damage_handle_ = XDamageCreate(display_, root_window_,
|
| + XDamageReportNonEmpty);
|
| + if (!damage_handle_) {
|
| + LOG(ERROR) << "Unable to initialize XDamage.";
|
| + return;
|
| + }
|
| +
|
| + // Create an XFixes server-side region to collate damage into.
|
| + damage_region_ = XFixesCreateRegion(display_, 0, 0);
|
| + if (!damage_region_) {
|
| + XDamageDestroy(display_, damage_handle_);
|
| + LOG(ERROR) << "Unable to create XFixes region.";
|
| + return;
|
| + }
|
| +
|
| + use_damage_ = true;
|
| + LOG(INFO) << "Using XDamage extension.";
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::Start(
|
| + const CursorShapeChangedCallback& callback) {
|
| + cursor_shape_changed_callback_ = callback;
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::Stop() {
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::ScreenConfigurationChanged() {
|
| + last_buffer_ = NULL;
|
| + for (int i = 0; i < kNumBuffers; ++i) {
|
| + buffers_[i].set_needs_update();
|
| + }
|
| + helper_.ClearInvalidRegion();
|
| + x_server_pixel_buffer_.Init(display_);
|
| +}
|
| +
|
| +media::VideoFrame::Format VideoFrameCapturerLinux::pixel_format() const {
|
| + return pixel_format_;
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::ClearInvalidRegion() {
|
| + helper_.ClearInvalidRegion();
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::InvalidateRegion(const SkRegion& invalid_region) {
|
| + helper_.InvalidateRegion(invalid_region);
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::InvalidateScreen(const SkISize& size) {
|
| + helper_.InvalidateScreen(size);
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::InvalidateFullScreen() {
|
| + helper_.InvalidateFullScreen();
|
| + last_buffer_ = NULL;
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::CaptureInvalidRegion(
|
| + const CaptureCompletedCallback& callback) {
|
| + // 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_);
|
| + // Also refresh the Differ helper used by CaptureFrame(), if needed.
|
| + if (!use_damage_ && !last_buffer_) {
|
| + differ_.reset(new Differ(current.size().width(), current.size().height(),
|
| + kBytesPerPixel, current.bytes_per_row()));
|
| + }
|
| +
|
| + scoped_refptr<CaptureData> capture_data(CaptureFrame());
|
| +
|
| + current_buffer_ = (current_buffer_ + 1) % kNumBuffers;
|
| +
|
| + callback.Run(capture_data);
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::ProcessPendingXEvents() {
|
| + // Find the number of events that are outstanding "now." We don't just loop
|
| + // on XPending because we want to guarantee this terminates.
|
| + int events_to_process = XPending(display_);
|
| + XEvent e;
|
| +
|
| + for (int i = 0; i < events_to_process; i++) {
|
| + XNextEvent(display_, &e);
|
| + if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) {
|
| + XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
|
| + DCHECK(event->level == XDamageReportNonEmpty);
|
| + } else if (e.type == ConfigureNotify) {
|
| + ScreenConfigurationChanged();
|
| + } else if (has_xfixes_ &&
|
| + e.type == xfixes_event_base_ + XFixesCursorNotify) {
|
| + XFixesCursorNotifyEvent* cne;
|
| + cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e);
|
| + if (cne->subtype == XFixesDisplayCursorNotify) {
|
| + CaptureCursor();
|
| + }
|
| + } else {
|
| + LOG(WARNING) << "Got unknown event type: " << e.type;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::CaptureCursor() {
|
| + DCHECK(has_xfixes_);
|
| + if (cursor_shape_changed_callback_.is_null())
|
| + return;
|
| +
|
| + XFixesCursorImage* img = XFixesGetCursorImage(display_);
|
| + if (!img) {
|
| + return;
|
| + }
|
| +
|
| + int width = img->width;
|
| + int height = img->height;
|
| + int total_bytes = width * height * kBytesPerPixel;
|
| +
|
| + scoped_ptr<protocol::CursorShapeInfo> cursor_proto(
|
| + new protocol::CursorShapeInfo());
|
| + cursor_proto->set_width(width);
|
| + cursor_proto->set_height(height);
|
| + cursor_proto->set_hotspot_x(img->xhot);
|
| + cursor_proto->set_hotspot_y(img->yhot);
|
| +
|
| + cursor_proto->mutable_data()->resize(total_bytes);
|
| + uint8* proto_data = const_cast<uint8*>(reinterpret_cast<const uint8*>(
|
| + cursor_proto->mutable_data()->data()));
|
| +
|
| + // Xlib stores 32-bit data in longs, even if longs are 64-bits long.
|
| + unsigned long* src = img->pixels;
|
| + uint32* dst = reinterpret_cast<uint32*>(proto_data);
|
| + uint32* dst_end = dst + (width * height);
|
| + while (dst < dst_end) {
|
| + *dst++ = static_cast<uint32>(*src++);
|
| + }
|
| + XFree(img);
|
| +
|
| + cursor_shape_changed_callback_.Run(cursor_proto.Pass());
|
| +}
|
| +
|
| +CaptureData* VideoFrameCapturerLinux::CaptureFrame() {
|
| + VideoFrameBuffer& buffer = buffers_[current_buffer_];
|
| + DataPlanes planes;
|
| + planes.data[0] = buffer.ptr();
|
| + planes.strides[0] = buffer.bytes_per_row();
|
| +
|
| + CaptureData* capture_data = new CaptureData(planes, buffer.size(),
|
| + media::VideoFrame::RGB32);
|
| +
|
| + // Pass the screen size to the helper, so it can clip the invalid region if it
|
| + // expands that region to a grid.
|
| + helper_.set_size_most_recent(capture_data->size());
|
| +
|
| + // In the DAMAGE case, ensure the frame is up-to-date with the previous frame
|
| + // 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_ && last_buffer_)
|
| + SynchronizeFrame();
|
| +
|
| + SkRegion invalid_region;
|
| +
|
| + x_server_pixel_buffer_.Synchronize();
|
| + if (use_damage_ && last_buffer_) {
|
| + // Atomically fetch and clear the damage region.
|
| + XDamageSubtract(display_, damage_handle_, None, damage_region_);
|
| + int nRects = 0;
|
| + XRectangle bounds;
|
| + XRectangle* rects = XFixesFetchRegionAndBounds(display_, damage_region_,
|
| + &nRects, &bounds);
|
| + for (int i=0; i<nRects; ++i) {
|
| + invalid_region.op(SkIRect::MakeXYWH(rects[i].x, rects[i].y,
|
| + rects[i].width, rects[i].height),
|
| + SkRegion::kUnion_Op);
|
| + }
|
| + XFree(rects);
|
| + helper_.InvalidateRegion(invalid_region);
|
| +
|
| + // Capture the damaged portions of the desktop.
|
| + helper_.SwapInvalidRegion(&invalid_region);
|
| + for (SkRegion::Iterator it(invalid_region); !it.done(); it.next()) {
|
| + CaptureRect(it.rect(), capture_data);
|
| + }
|
| + } 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());
|
| + CaptureRect(screen_rect, capture_data);
|
| +
|
| + if (last_buffer_) {
|
| + // Full-screen polling, so calculate the invalid rects here, based on the
|
| + // changed pixels between current and previous buffers.
|
| + DCHECK(differ_ != NULL);
|
| + differ_->CalcDirtyRegion(last_buffer_, buffer.ptr(), &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
|
| + // full-screen notification after a screen-resolution change, so
|
| + // this is done here.
|
| + invalid_region.op(screen_rect, SkRegion::kUnion_Op);
|
| + }
|
| + }
|
| +
|
| + capture_data->mutable_dirty_region() = invalid_region;
|
| + last_invalid_region_ = invalid_region;
|
| + last_buffer_ = buffer.ptr();
|
| + return capture_data;
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::SynchronizeFrame() {
|
| + // Synchronize the current buffer with the previous one since we do not
|
| + // capture the entire desktop. Note that encoder may be reading from the
|
| + // previous buffer at this time so thread access complaints are false
|
| + // positives.
|
| +
|
| + // 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(last_buffer_);
|
| + VideoFrameBuffer& buffer = buffers_[current_buffer_];
|
| + 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;
|
| + for (int i = 0; i < r.height(); ++i) {
|
| + memcpy(buffer.ptr() + offset, last_buffer_ + offset,
|
| + r.width() * kBytesPerPixel);
|
| + offset += buffer.size().width() * kBytesPerPixel;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::DeinitXlib() {
|
| + if (gc_) {
|
| + XFreeGC(display_, gc_);
|
| + gc_ = NULL;
|
| + }
|
| +
|
| + x_server_pixel_buffer_.Release();
|
| +
|
| + if (display_) {
|
| + if (damage_handle_)
|
| + XDamageDestroy(display_, damage_handle_);
|
| + if (damage_region_)
|
| + XFixesDestroyRegion(display_, damage_region_);
|
| + XCloseDisplay(display_);
|
| + display_ = NULL;
|
| + damage_handle_ = 0;
|
| + damage_region_ = 0;
|
| + }
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::CaptureRect(const SkIRect& rect,
|
| + CaptureData* capture_data) {
|
| + uint8* image = x_server_pixel_buffer_.CaptureRect(rect);
|
| + int depth = x_server_pixel_buffer_.GetDepth();
|
| + int bpp = x_server_pixel_buffer_.GetBitsPerPixel();
|
| + bool is_rgb = x_server_pixel_buffer_.IsRgb();
|
| + if ((depth == 24 || depth == 32) && bpp == 32 && is_rgb) {
|
| + DVLOG(3) << "Fast blitting";
|
| + FastBlit(image, rect, capture_data);
|
| + } else {
|
| + DVLOG(3) << "Slow blitting";
|
| + SlowBlit(image, rect, capture_data);
|
| + }
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::FastBlit(uint8* image, const SkIRect& rect,
|
| + CaptureData* capture_data) {
|
| + uint8* src_pos = image;
|
| + int src_stride = x_server_pixel_buffer_.GetStride();
|
| + int dst_x = rect.fLeft, dst_y = rect.fTop;
|
| +
|
| + DataPlanes planes = capture_data->data_planes();
|
| + uint8* dst_buffer = planes.data[0];
|
| +
|
| + const int dst_stride = planes.strides[0];
|
| +
|
| + uint8* dst_pos = dst_buffer + dst_stride * dst_y;
|
| + dst_pos += dst_x * kBytesPerPixel;
|
| +
|
| + int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel;
|
| + for (int y = 0; y < height; ++y) {
|
| + memcpy(dst_pos, src_pos, row_bytes);
|
| + src_pos += src_stride;
|
| + dst_pos += dst_stride;
|
| + }
|
| +}
|
| +
|
| +void VideoFrameCapturerLinux::SlowBlit(uint8* image, const SkIRect& rect,
|
| + CaptureData* capture_data) {
|
| + DataPlanes planes = capture_data->data_planes();
|
| + uint8* dst_buffer = planes.data[0];
|
| + const int dst_stride = planes.strides[0];
|
| + int src_stride = x_server_pixel_buffer_.GetStride();
|
| + int dst_x = rect.fLeft, dst_y = rect.fTop;
|
| + int width = rect.width(), height = rect.height();
|
| +
|
| + unsigned int red_mask = x_server_pixel_buffer_.GetRedMask();
|
| + unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask();
|
| + unsigned int green_mask = x_server_pixel_buffer_.GetGreenMask();
|
| + unsigned int red_shift = x_server_pixel_buffer_.GetRedShift();
|
| + unsigned int blue_shift = x_server_pixel_buffer_.GetBlueShift();
|
| + unsigned int green_shift = x_server_pixel_buffer_.GetGreenShift();
|
| +
|
| + unsigned int max_red = red_mask >> red_shift;
|
| + unsigned int max_blue = blue_mask >> blue_shift;
|
| + unsigned int max_green = green_mask >> green_shift;
|
| +
|
| + unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel();
|
| +
|
| + uint8* dst_pos = dst_buffer + dst_stride * dst_y;
|
| + uint8* src_pos = image;
|
| + dst_pos += dst_x * kBytesPerPixel;
|
| + // TODO(hclam): Optimize, perhaps using MMX code or by converting to
|
| + // YUV directly
|
| + for (int y = 0; y < height; y++) {
|
| + uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
|
| + uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
|
| + uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
|
| + for (int x = 0; x < width; x++) {
|
| + // Dereference through an appropriately-aligned pointer.
|
| + uint32_t pixel;
|
| + if (bits_per_pixel == 32)
|
| + pixel = src_pos_32[x];
|
| + else if (bits_per_pixel == 16)
|
| + pixel = src_pos_16[x];
|
| + else
|
| + pixel = src_pos[x];
|
| + uint32_t r = (((pixel & red_mask) >> red_shift) * 255) / max_red;
|
| + uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue;
|
| + uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green;
|
| + // Write as 32-bit RGB.
|
| + dst_pos_32[x] = r << 16 | g << 8 | b;
|
| + }
|
| + dst_pos += dst_stride;
|
| + src_pos += src_stride;
|
| + }
|
| +}
|
| +
|
| +const SkISize& VideoFrameCapturerLinux::size_most_recent() const {
|
| + return helper_.size_most_recent();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// static
|
| +VideoFrameCapturer* VideoFrameCapturer::Create() {
|
| + VideoFrameCapturerLinux* capturer = new VideoFrameCapturerLinux();
|
| + if (!capturer->Init()) {
|
| + delete capturer;
|
| + capturer = NULL;
|
| + }
|
| + return capturer;
|
| +}
|
| +
|
| +// static
|
| +void VideoFrameCapturer::EnableXDamage(bool enable) {
|
| + g_should_use_x_damage = enable;
|
| +}
|
| +
|
| +} // namespace remoting
|
|
|