summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-21 20:05:01 +0000
committerajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-21 20:05:01 +0000
commit66980d85fefe26261b310f35375a31bb91abd0c3 (patch)
tree43298b3d634c9e7cc0b887b12f4ba42957d10253 /remoting
parent9d0b0fbfc061a1889ae18fbefa92f7b73b065750 (diff)
downloadchromium_src-66980d85fefe26261b310f35375a31bb91abd0c3.zip
chromium_src-66980d85fefe26261b310f35375a31bb91abd0c3.tar.gz
chromium_src-66980d85fefe26261b310f35375a31bb91abd0c3.tar.bz2
Implement basic X11 screen capture on linux.
BUG= TEST= Review URL: http://codereview.chromium.org/3801012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63401 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/base/encoder_zlib.cc3
-rw-r--r--remoting/client/jingle_host_connection.cc2
-rw-r--r--remoting/host/capturer_linux.cc318
-rw-r--r--remoting/host/capturer_linux.h4
-rw-r--r--remoting/host/chromoting_host.cc2
-rw-r--r--remoting/remoting.gyp6
6 files changed, 332 insertions, 3 deletions
diff --git a/remoting/base/encoder_zlib.cc b/remoting/base/encoder_zlib.cc
index a36b8df..ddbe923 100644
--- a/remoting/base/encoder_zlib.cc
+++ b/remoting/base/encoder_zlib.cc
@@ -27,7 +27,8 @@ void EncoderZlib::Encode(scoped_refptr<CaptureData> capture_data,
bool key_frame,
DataAvailableCallback* data_available_callback) {
CHECK(capture_data->pixel_format() == PixelFormatRgb32)
- << "Zlib Encoder only works with RGB32";
+ << "Zlib Encoder only works with RGB32. Got "
+ << capture_data->pixel_format();
capture_data_ = capture_data;
callback_.reset(data_available_callback);
diff --git a/remoting/client/jingle_host_connection.cc b/remoting/client/jingle_host_connection.cc
index f40f720..c70e2c6 100644
--- a/remoting/client/jingle_host_connection.cc
+++ b/remoting/client/jingle_host_connection.cc
@@ -65,6 +65,8 @@ void JingleHostConnection::InitConnection() {
// Initialize |chromotocol_server_|.
JingleChromotingServer* chromotocol_server =
new JingleChromotingServer(message_loop());
+ // TODO(ajwong): Make this a command switch when we're more stable.
+ chromotocol_server->set_allow_local_ips(true);
chromotocol_server->Init(
jingle_client_->GetFullJid(),
jingle_client_->session_manager(),
diff --git a/remoting/host/capturer_linux.cc b/remoting/host/capturer_linux.cc
index e53c597..fcf7a01 100644
--- a/remoting/host/capturer_linux.cc
+++ b/remoting/host/capturer_linux.cc
@@ -4,23 +4,337 @@
#include "remoting/host/capturer_linux.h"
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xdamage.h>
+
+#include <set>
+
+#include "remoting/base/types.h"
+
namespace remoting {
-// TODO(dmaclach): Implement this class.
-CapturerLinux::CapturerLinux() {
+static int IndexOfLowestBit(unsigned int mask) {
+ int i = 0;
+
+ // Extra-special do-while premature optimization, just to make dmaclach@
+ // happy.
+ do {
+ if (mask & 1) {
+ return i;
+ }
+ mask >>= 1;
+ ++i;
+ } while (mask);
+
+ NOTREACHED() << "mask should never be 0.";
+ return 0;
+}
+
+static bool IsRgb32(XImage* image) {
+ return (IndexOfLowestBit(image->red_mask) == 16) &&
+ (IndexOfLowestBit(image->green_mask) == 8) &&
+ (IndexOfLowestBit(image->blue_mask) == 0);
+}
+
+static uint32_t* GetRowStart(uint8* buffer_start, int stride, int cur_row) {
+ return reinterpret_cast<uint32_t*>(buffer_start + (cur_row * stride));
+}
+
+// Private Implementation pattern to avoid leaking the X11 types into the header
+// file.
+class CapturerLinuxPimpl {
+ public:
+ explicit CapturerLinuxPimpl(CapturerLinux* capturer);
+ ~CapturerLinuxPimpl();
+
+ bool Init(); // TODO(ajwong): Do we really want this to be synchronous?
+ void CalculateInvalidRects();
+ void CaptureRects(const InvalidRects& rects,
+ Capturer::CaptureCompletedCallback* callback);
+
+ private:
+ void DeinitXlib();
+ // We expose two forms of blitting to handle variations in the pixel format.
+ // In FastBlit, the operation is effectively a memcpy.
+ void FastBlit(XImage* image, int dest_x, int dest_y,
+ CaptureData* capture_data);
+ void SlowBlit(XImage* image, int dest_x, int dest_y,
+ CaptureData* capture_data);
+
+ static const int kBytesPerPixel = 4;
+
+ // Reference to containing class so we can access friend functions.
+ CapturerLinux* capturer_;
+
+ // X11 graphics context.
+ Display* display_;
+ GC gc_;
+ Window root_window_;
+
+ // XDamage information.
+ Damage damage_handle_;
+ int damage_event_base_;
+ int damage_error_base_;
+
+ // Capture state.
+ uint8* buffers_[CapturerLinux::kNumBuffers];
+ int stride_;
+ bool capture_fullscreen_;
+};
+
+CapturerLinux::CapturerLinux()
+ : pimpl_(new CapturerLinuxPimpl(this)) {
+ // TODO(ajwong): This should be moved into an Init() method on Capturer
+ // itself. Then we can remove the CHECK.
+ CHECK(pimpl_->Init());
}
CapturerLinux::~CapturerLinux() {
}
void CapturerLinux::ScreenConfigurationChanged() {
+ // TODO(ajwong): Support resolution changes.
+ NOTIMPLEMENTED();
}
void CapturerLinux::CalculateInvalidRects() {
+ pimpl_->CalculateInvalidRects();
}
void CapturerLinux::CaptureRects(const InvalidRects& rects,
CaptureCompletedCallback* callback) {
+ pimpl_->CaptureRects(rects, callback);
+}
+
+CapturerLinuxPimpl::CapturerLinuxPimpl(CapturerLinux* capturer)
+ : capturer_(capturer),
+ display_(NULL),
+ gc_(NULL),
+ root_window_(BadValue),
+ damage_handle_(BadValue),
+ damage_event_base_(-1),
+ damage_error_base_(-1),
+ stride_(0),
+ capture_fullscreen_(true) {
+ for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
+ buffers_[i] = NULL;
+ }
+}
+
+CapturerLinuxPimpl::~CapturerLinuxPimpl() {
+ DeinitXlib();
+
+ for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
+ delete [] buffers_[i];
+ buffers_[i] = NULL;
+ }
+}
+
+bool CapturerLinuxPimpl::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;
+ }
+
+ 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;
+ }
+
+ // Setup XDamage to report changes in the damage window. Mark the whole
+ // window as invalid.
+ if (!XDamageQueryExtension(display_, &damage_event_base_,
+ &damage_error_base_)) {
+ LOG(ERROR) << "Server does not support XDamage.";
+ DeinitXlib();
+ return false;
+ }
+ damage_handle_ = XDamageCreate(display_, root_window_,
+ XDamageReportDeltaRectangles);
+ if (damage_handle_ == BadValue) {
+ LOG(ERROR) << "Unable to create damage handle.";
+ DeinitXlib();
+ return false;
+ }
+
+ // TODO(ajwong): We should be able to replace this with a XDamageAdd().
+ capture_fullscreen_ = true;
+
+ // Set up the dimensions of the catpure framebuffer.
+ XWindowAttributes root_attr;
+ XGetWindowAttributes(display_, root_window_, &root_attr);
+ capturer_->width_ = root_attr.width;
+ capturer_->height_ = root_attr.height;
+ stride_ = capturer_->width() * kBytesPerPixel;
+ VLOG(1) << "Initialized with Geometry: " << capturer_->width()
+ << "x" << capturer_->height();
+
+ // Allocate the screen buffers.
+ for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
+ buffers_[i] =
+ new uint8[capturer_->width() * capturer_->height() * kBytesPerPixel];
+ }
+
+ return true;
+}
+
+void CapturerLinuxPimpl::CalculateInvalidRects() {
+ // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor.
+
+ // 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;
+ InvalidRects invalid_rects;
+ for (int i = 0; i < events_to_process; i++) {
+ XNextEvent(display_, &e);
+ if (e.type == damage_event_base_ + XDamageNotify) {
+ // If we're doing a full screen capture, we should just drain the events.
+ if (!capture_fullscreen_) {
+ XDamageNotifyEvent *event = reinterpret_cast<XDamageNotifyEvent*>(&e);
+ gfx::Rect damage_rect(event->area.x, event->area.y, event->area.width,
+ event->area.height);
+ invalid_rects.insert(damage_rect);
+ VLOG(3) << "Damage receved for rect at ("
+ << damage_rect.x() << "," << damage_rect.y() << ") size ("
+ << damage_rect.width() << "," << damage_rect.height() << ")";
+ }
+ } else {
+ LOG(WARNING) << "Got unknown event type: " << e.type;
+ }
+ }
+
+ if (capture_fullscreen_) {
+ capturer_->InvalidateFullScreen();
+ capture_fullscreen_ = false;
+ } else {
+ capturer_->InvalidateRects(invalid_rects);
+ }
+}
+
+void CapturerLinuxPimpl::CaptureRects(
+ const InvalidRects& rects,
+ Capturer::CaptureCompletedCallback* callback) {
+ uint8* buffer = buffers_[capturer_->current_buffer_];
+ DataPlanes planes;
+ planes.data[0] = buffer;
+ planes.strides[0] = stride_;
+
+ scoped_refptr<CaptureData> capture_data(
+ new CaptureData(planes, capturer_->width(), capturer_->height(),
+ PixelFormatRgb32));
+
+ for (InvalidRects::const_iterator it = rects.begin();
+ it != rects.end();
+ ++it) {
+ XImage* image = XGetImage(display_, root_window_, it->x(), it->y(),
+ it->width(), it->height(), AllPlanes, ZPixmap);
+
+ // Check if we can fastpath the blit.
+ if ((image->depth == 24 || image->depth == 32) &&
+ image->bits_per_pixel == 32 &&
+ IsRgb32(image)) {
+ VLOG(3) << "Fast blitting";
+ FastBlit(image, it->x(), it->y(), capture_data);
+ } else {
+ VLOG(3) << "Slow blitting";
+ SlowBlit(image, it->x(), it->y(), capture_data);
+ }
+
+ XDestroyImage(image);
+ }
+
+ // TODO(ajwong): We should only repair the rects that were copied!
+ XDamageSubtract(display_, damage_handle_, None, None);
+
+ capture_data->mutable_dirty_rects() = rects;
+ // TODO(ajwong): These completion signals back to the upper class are very
+ // strange. Fix it.
+ capturer_->FinishCapture(capture_data, callback);
+}
+
+void CapturerLinuxPimpl::DeinitXlib() {
+ if (gc_) {
+ XFreeGC(display_, gc_);
+ gc_ = NULL;
+ }
+
+ if (display_) {
+ XCloseDisplay(display_);
+ display_ = NULL;
+ }
+}
+
+void CapturerLinuxPimpl::FastBlit(XImage* image, int dest_x, int dest_y,
+ CaptureData* capture_data) {
+ uint8* src_pos = reinterpret_cast<uint8*>(image->data);
+
+ DataPlanes planes = capture_data->data_planes();
+ uint8* dst_buffer = planes.data[0];
+
+ const int dst_stride = planes.strides[0];
+ const int src_stride = image->bytes_per_line;
+
+ // TODO(ajwong): I think this can never happen anyways due to the way we size
+ // the buffer. Check to be certain and possibly remove check.
+ CHECK((dst_stride - dest_x) >= src_stride);
+
+ // Flip the coordinate system to match the client.
+ for (int y = image->height - 1; y >= 0; y--) {
+ uint32_t* dst_pos = GetRowStart(dst_buffer, dst_stride, y + dest_y);
+ dst_pos += dest_x;
+
+ memcpy(dst_pos, src_pos, src_stride);
+
+ src_pos += src_stride;
+ }
+}
+
+void CapturerLinuxPimpl::SlowBlit(XImage* image, int dest_x, int dest_y,
+ CaptureData* capture_data) {
+ DataPlanes planes = capture_data->data_planes();
+ uint8* dst_buffer = planes.data[0];
+ const int dst_stride = planes.strides[0];
+
+ unsigned int red_shift = IndexOfLowestBit(image->red_mask);
+ unsigned int blue_shift = IndexOfLowestBit(image->blue_mask);
+ unsigned int green_shift = IndexOfLowestBit(image->green_mask);
+
+ unsigned int max_red = image->red_mask >> red_shift;
+ unsigned int max_blue = image->blue_mask >> blue_shift;
+ unsigned int max_green = image->green_mask >> green_shift;
+
+ for (int y = 0; y < image->height; y++) {
+ // Flip the coordinate system to match the client.
+ int dst_row = image->height - y - 1;
+ uint32_t* dst_pos = GetRowStart(dst_buffer, dst_stride, dst_row + dest_y);
+ dst_pos += dest_x;
+ for (int x = 0; x < image->width; x++) {
+ unsigned long pixel = XGetPixel(image, x, y);
+ uint32_t r = (((pixel & image->red_mask) >> red_shift) * max_red) / 255;
+ uint32_t g =
+ (((pixel & image->green_mask) >> green_shift) * max_blue) / 255;
+ uint32_t b =
+ (((pixel & image->blue_mask) >> blue_shift) * max_green) / 255;
+
+ // Write as 32-bit RGB.
+ *dst_pos = r << 16 | g << 8 | b;
+ dst_pos++;
+ }
+ }
}
} // namespace remoting
diff --git a/remoting/host/capturer_linux.h b/remoting/host/capturer_linux.h
index b7c2d93..8d7b32d 100644
--- a/remoting/host/capturer_linux.h
+++ b/remoting/host/capturer_linux.h
@@ -9,6 +9,8 @@
namespace remoting {
+class CapturerLinuxPimpl;
+
// A class to perform capturing for Linux.
class CapturerLinux : public Capturer {
public:
@@ -18,10 +20,12 @@ class CapturerLinux : public Capturer {
virtual void ScreenConfigurationChanged();
private:
+ friend class CapturerLinuxPimpl;
virtual void CalculateInvalidRects();
virtual void CaptureRects(const InvalidRects& rects,
CaptureCompletedCallback* callback);
+ scoped_ptr<CapturerLinuxPimpl> pimpl_;
DISALLOW_COPY_AND_ASSIGN(CapturerLinux);
};
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index d28ca79..7676eda 100644
--- a/remoting/host/chromoting_host.cc
+++ b/remoting/host/chromoting_host.cc
@@ -217,6 +217,8 @@ void ChromotingHost::OnStateChange(JingleClient* jingle_client,
// Create and start |chromotocol_server_|.
chromotocol_server_ =
new JingleChromotingServer(context_->jingle_thread()->message_loop());
+ // TODO(ajwong): Make this a command switch when we're more stable.
+ chromotocol_server_->set_allow_local_ips(true);
chromotocol_server_->Init(
jingle_client->GetFullJid(),
jingle_client->session_manager(),
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 0dc310d..72ed762f 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -219,6 +219,12 @@
'host/event_executor_linux.cc',
'host/event_executor_linux.h',
],
+ 'link_settings': {
+ 'libraries': [
+ '-lX11',
+ '-lXdamage',
+ ],
+ },
}],
['OS=="mac"', {
'sources': [