aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2013-08-23 18:22:12 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2013-08-23 18:22:12 +0000
commit9f282403f258162ca53eb2f16b8e9a26e7970096 (patch)
tree0b4736d97d68c3401a38cd0a7e19236f24c1558f
parent563910bde3efd612a6080dba4ceaa122d9f6e9f9 (diff)
downloadwebrtc-9f282403f258162ca53eb2f16b8e9a26e7970096.tar.gz
WindowCapturer implementation for Linux.
Window enumeration is based on the code used by hangouts plugin (see libjingle/talk/base/linuxwindowpicker.cc). XServerPixelBuffer is used to capture windows. It had to be refactored to support window capturing (previously it worked only for the whole screen). R=wez@chromium.org Review URL: https://webrtc-codereview.appspot.com/1741004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4605 4adac7df-926f-26a2-2b94-8c16560cd09d
-rw-r--r--webrtc/modules/desktop_capture/desktop_capture.gypi7
-rw-r--r--webrtc/modules/desktop_capture/screen_capturer_x11.cc176
-rwxr-xr-xwebrtc/modules/desktop_capture/window_capturer_null.cc (renamed from webrtc/modules/desktop_capture/window_capturer_linux.cc)22
-rw-r--r--webrtc/modules/desktop_capture/window_capturer_unittest.cc6
-rwxr-xr-xwebrtc/modules/desktop_capture/window_capturer_x11.cc354
-rw-r--r--webrtc/modules/desktop_capture/x11/x_error_trap.cc69
-rw-r--r--webrtc/modules/desktop_capture/x11/x_error_trap.h39
-rw-r--r--webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc292
-rw-r--r--webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h53
9 files changed, 708 insertions, 310 deletions
diff --git a/webrtc/modules/desktop_capture/desktop_capture.gypi b/webrtc/modules/desktop_capture/desktop_capture.gypi
index 269c583780..0bc3839ef4 100644
--- a/webrtc/modules/desktop_capture/desktop_capture.gypi
+++ b/webrtc/modules/desktop_capture/desktop_capture.gypi
@@ -53,9 +53,11 @@
"win/scoped_thread_desktop.cc",
"win/scoped_thread_desktop.h",
"window_capturer.h",
- "window_capturer_linux.cc",
"window_capturer_mac.cc",
"window_capturer_win.cc",
+ "window_capturer_x11.cc",
+ "x11/x_error_trap.cc",
+ "x11/x_error_trap.h",
"x11/x_server_pixel_buffer.cc",
"x11/x_server_pixel_buffer.h",
],
@@ -69,15 +71,18 @@
'link_settings': {
'libraries': [
'-lX11',
+ '-lXcomposite',
'-lXdamage',
'-lXext',
'-lXfixes',
+ '-lXrender',
],
},
}],
['OS!="win" and OS!="mac" and use_x11==0', {
'sources': [
"screen_capturer_null.cc",
+ "window_capturer_null.cc",
],
}],
['OS=="mac"', {
diff --git a/webrtc/modules/desktop_capture/screen_capturer_x11.cc b/webrtc/modules/desktop_capture/screen_capturer_x11.cc
index e730b71a84..5cca65b133 100644
--- a/webrtc/modules/desktop_capture/screen_capturer_x11.cc
+++ b/webrtc/modules/desktop_capture/screen_capturer_x11.cc
@@ -76,9 +76,8 @@ class ScreenCapturerLinux : public ScreenCapturer {
// differences between this and the previous capture.
DesktopFrame* CaptureScreen();
- // Called when the screen configuration is changed. |root_window_size|
- // specifies the most recent size of the root window.
- void ScreenConfigurationChanged(const DesktopSize& root_window_size);
+ // Called when the screen configuration is changed.
+ void ScreenConfigurationChanged();
// Synchronize the current buffer with |last_buffer_|, by copying pixels from
// the area of |last_invalid_rects|.
@@ -89,25 +88,6 @@ class ScreenCapturerLinux : public ScreenCapturer {
void DeinitXlib();
- // Capture a rectangle from |x_server_pixel_buffer_|, and copy the data into
- // |frame|.
- void CaptureRect(const DesktopRect& rect,
- DesktopFrame* frame);
-
- // We expose two forms of blitting to handle variations in the pixel format.
- // In FastBlit, the operation is effectively a memcpy.
- void FastBlit(uint8_t* image,
- const DesktopRect& rect,
- DesktopFrame* frame);
- void SlowBlit(uint8_t* image,
- const DesktopRect& rect,
- DesktopFrame* frame);
-
- // Returns the number of bits |mask| has to be shifted left so its last
- // (most-significant) bit set becomes the most-significant bit of the word.
- // When |mask| is 0 the function returns 31.
- static uint32_t GetRgbShift(uint32_t mask);
-
Callback* callback_;
MouseShapeObserver* mouse_shape_observer_;
@@ -116,9 +96,6 @@ class ScreenCapturerLinux : public ScreenCapturer {
GC gc_;
Window root_window_;
- // Last known dimensions of the root window.
- DesktopSize root_window_size_;
-
// XFixes.
bool has_xfixes_;
int xfixes_event_base_;
@@ -207,8 +184,10 @@ bool ScreenCapturerLinux::Init(bool use_x_damage) {
// Register for changes to the dimensions of the root window.
XSelectInput(display_, root_window_, StructureNotifyMask);
- root_window_size_ = XServerPixelBuffer::GetRootWindowSize(display_);
- x_server_pixel_buffer_.Init(display_, root_window_size_);
+ if (!x_server_pixel_buffer_.Init(display_, DefaultRootWindow(display_))) {
+ LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
+ return false;
+ }
if (has_xfixes_) {
// Register for changes to the cursor shape.
@@ -276,12 +255,21 @@ void ScreenCapturerLinux::Capture(const DesktopRegion& region) {
// Process XEvents for XDamage and cursor shape tracking.
ProcessPendingXEvents();
+ // ProcessPendingXEvents() may call ScreenConfigurationChanged() which
+ // reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still
+ // in a good shape.
+ if (!x_server_pixel_buffer_.is_initialized()) {
+ // We failed to initialize pixel buffer.
+ callback_->OnCaptureCompleted(NULL);
+ return;
+ }
+
// If the current frame 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()) {
scoped_ptr<DesktopFrame> frame(
- new BasicDesktopFrame(root_window_size_));
+ new BasicDesktopFrame(x_server_pixel_buffer_.window_size()));
queue_.ReplaceCurrentFrame(frame.release());
}
@@ -324,9 +312,7 @@ void ScreenCapturerLinux::ProcessPendingXEvents() {
XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
DCHECK(event->level == XDamageReportNonEmpty);
} else if (e.type == ConfigureNotify) {
- const XConfigureEvent& event = e.xconfigure;
- ScreenConfigurationChanged(
- DesktopSize(event.width, event.height));
+ ScreenConfigurationChanged();
} else if (has_xfixes_ &&
e.type == xfixes_event_base_ + XFixesCursorNotify) {
XFixesCursorNotifyEvent* cne;
@@ -371,6 +357,7 @@ void ScreenCapturerLinux::CaptureCursor() {
DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
DesktopFrame* frame = queue_.current_frame()->Share();
+ assert(x_server_pixel_buffer_.window_size().equals(frame->size()));
// Pass the screen size to the helper, so it can clip the invalid region if it
// expands that region to a grid.
@@ -407,18 +394,17 @@ DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
// spurious XDamage notifications were received for a previous (larger)
// screen size.
updated_region->IntersectWith(
- DesktopRect::MakeSize(root_window_size_));
+ DesktopRect::MakeSize(x_server_pixel_buffer_.window_size()));
for (DesktopRegion::Iterator it(*updated_region);
!it.IsAtEnd(); it.Advance()) {
- CaptureRect(it.rect(), frame);
+ x_server_pixel_buffer_.CaptureRect(it.rect(), frame);
}
} else {
// Doing full-screen polling, or this is the first capture after a
// screen-resolution change. In either case, need a full-screen capture.
- DesktopRect screen_rect =
- DesktopRect::MakeSize(frame->size());
- CaptureRect(screen_rect, frame);
+ DesktopRect screen_rect = DesktopRect::MakeSize(frame->size());
+ x_server_pixel_buffer_.CaptureRect(screen_rect, frame);
if (queue_.previous_frame()) {
// Full-screen polling, so calculate the invalid rects here, based on the
@@ -439,15 +425,15 @@ DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
return frame;
}
-void ScreenCapturerLinux::ScreenConfigurationChanged(
- const DesktopSize& root_window_size) {
- root_window_size_ = root_window_size;
-
+void ScreenCapturerLinux::ScreenConfigurationChanged() {
// Make sure the frame buffers will be reallocated.
queue_.Reset();
helper_.ClearInvalidRegion();
- x_server_pixel_buffer_.Init(display_, root_window_size_);
+ if (!x_server_pixel_buffer_.Init(display_, DefaultRootWindow(display_))) {
+ LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen "
+ "configuration change.";
+ }
}
void ScreenCapturerLinux::SynchronizeFrame() {
@@ -497,114 +483,6 @@ void ScreenCapturerLinux::DeinitXlib() {
}
}
-void ScreenCapturerLinux::CaptureRect(const DesktopRect& rect,
- DesktopFrame* frame) {
- uint8_t* image = x_server_pixel_buffer_.CaptureRect(rect);
- int depth = x_server_pixel_buffer_.GetDepth();
- if ((depth == 24 || depth == 32) &&
- x_server_pixel_buffer_.GetBitsPerPixel() == 32 &&
- x_server_pixel_buffer_.GetRedMask() == 0xff0000 &&
- x_server_pixel_buffer_.GetGreenMask() == 0xff00 &&
- x_server_pixel_buffer_.GetBlueMask() == 0xff) {
- FastBlit(image, rect, frame);
- } else {
- SlowBlit(image, rect, frame);
- }
-}
-
-void ScreenCapturerLinux::FastBlit(uint8_t* image,
- const DesktopRect& rect,
- DesktopFrame* frame) {
- uint8_t* src_pos = image;
- int src_stride = x_server_pixel_buffer_.GetStride();
- int dst_x = rect.left(), dst_y = rect.top();
-
- uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
- dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
-
- int height = rect.height();
- int row_bytes = rect.width() * DesktopFrame::kBytesPerPixel;
- for (int y = 0; y < height; ++y) {
- memcpy(dst_pos, src_pos, row_bytes);
- src_pos += src_stride;
- dst_pos += frame->stride();
- }
-}
-
-void ScreenCapturerLinux::SlowBlit(uint8_t* image,
- const DesktopRect& rect,
- DesktopFrame* frame) {
- int src_stride = x_server_pixel_buffer_.GetStride();
- int dst_x = rect.left(), dst_y = rect.top();
- int width = rect.width(), height = rect.height();
-
- uint32_t red_mask = x_server_pixel_buffer_.GetRedMask();
- uint32_t green_mask = x_server_pixel_buffer_.GetGreenMask();
- uint32_t blue_mask = x_server_pixel_buffer_.GetBlueMask();
-
- uint32_t red_shift = GetRgbShift(red_mask);
- uint32_t green_shift = GetRgbShift(green_mask);
- uint32_t blue_shift = GetRgbShift(blue_mask);
-
- unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel();
-
- uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
- uint8_t* src_pos = image;
- dst_pos += dst_x * DesktopFrame::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;
- uint32_t g = (pixel & green_mask) << green_shift;
- uint32_t b = (pixel & blue_mask) << blue_shift;
-
- // Write as 32-bit RGB.
- dst_pos_32[x] = ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) |
- ((b >> 24) & 0xff);
- }
- dst_pos += frame->stride();
- src_pos += src_stride;
- }
-}
-
-// static
-uint32_t ScreenCapturerLinux::GetRgbShift(uint32_t mask) {
- int shift = 0;
- if ((mask & 0xffff0000u) == 0) {
- mask <<= 16;
- shift += 16;
- }
- if ((mask & 0xff000000u) == 0) {
- mask <<= 8;
- shift += 8;
- }
- if ((mask & 0xf0000000u) == 0) {
- mask <<= 4;
- shift += 4;
- }
- if ((mask & 0xc0000000u) == 0) {
- mask <<= 2;
- shift += 2;
- }
- if ((mask & 0x80000000u) == 0)
- shift += 1;
-
- return shift;
-}
-
} // namespace
// static
diff --git a/webrtc/modules/desktop_capture/window_capturer_linux.cc b/webrtc/modules/desktop_capture/window_capturer_null.cc
index e95cad8e61..8ea723aaa6 100755
--- a/webrtc/modules/desktop_capture/window_capturer_linux.cc
+++ b/webrtc/modules/desktop_capture/window_capturer_null.cc
@@ -18,10 +18,10 @@ namespace webrtc {
namespace {
-class WindowCapturerLinux : public WindowCapturer {
+class WindowCapturerNull : public WindowCapturer {
public:
- WindowCapturerLinux();
- virtual ~WindowCapturerLinux();
+ WindowCapturerNull();
+ virtual ~WindowCapturerNull();
// WindowCapturer interface.
virtual bool GetWindowList(WindowList* windows) OVERRIDE;
@@ -34,34 +34,34 @@ class WindowCapturerLinux : public WindowCapturer {
private:
Callback* callback_;
- DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
+ DISALLOW_COPY_AND_ASSIGN(WindowCapturerNull);
};
-WindowCapturerLinux::WindowCapturerLinux()
+WindowCapturerNull::WindowCapturerNull()
: callback_(NULL) {
}
-WindowCapturerLinux::~WindowCapturerLinux() {
+WindowCapturerNull::~WindowCapturerNull() {
}
-bool WindowCapturerLinux::GetWindowList(WindowList* windows) {
+bool WindowCapturerNull::GetWindowList(WindowList* windows) {
// Not implemented yet.
return false;
}
-bool WindowCapturerLinux::SelectWindow(WindowId id) {
+bool WindowCapturerNull::SelectWindow(WindowId id) {
// Not implemented yet.
return false;
}
-void WindowCapturerLinux::Start(Callback* callback) {
+void WindowCapturerNull::Start(Callback* callback) {
assert(!callback_);
assert(callback);
callback_ = callback;
}
-void WindowCapturerLinux::Capture(const DesktopRegion& region) {
+void WindowCapturerNull::Capture(const DesktopRegion& region) {
// Not implemented yet.
callback_->OnCaptureCompleted(NULL);
}
@@ -70,7 +70,7 @@ void WindowCapturerLinux::Capture(const DesktopRegion& region) {
// static
WindowCapturer* WindowCapturer::Create() {
- return new WindowCapturerLinux();
+ return new WindowCapturerNull();
}
} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/window_capturer_unittest.cc b/webrtc/modules/desktop_capture/window_capturer_unittest.cc
index 2c11d849d9..c6de16ac42 100644
--- a/webrtc/modules/desktop_capture/window_capturer_unittest.cc
+++ b/webrtc/modules/desktop_capture/window_capturer_unittest.cc
@@ -10,6 +10,8 @@
#include "webrtc/modules/desktop_capture/window_capturer.h"
+#include <iostream>
+
#include "gtest/gtest.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/desktop_region.h"
@@ -42,8 +44,6 @@ class WindowCapturerTest : public testing::Test,
scoped_ptr<DesktopFrame> frame_;
};
-#if defined(WEBRTC_WIN) || defined(WEBRTC_MAC)
-
// Verify that we can enumerate windows.
TEST_F(WindowCapturerTest, Enumerate) {
WindowCapturer::WindowList windows;
@@ -92,6 +92,4 @@ TEST_F(WindowCapturerTest, Capture) {
}
}
-#endif // defined(WEBRTC_WIN) || defined(WEBRTC_MAC)
-
} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/window_capturer_x11.cc b/webrtc/modules/desktop_capture/window_capturer_x11.cc
new file mode 100755
index 0000000000..adafa9450d
--- /dev/null
+++ b/webrtc/modules/desktop_capture/window_capturer_x11.cc
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/desktop_capture/window_capturer.h"
+
+#include <string.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/Xutil.h>
+#include <algorithm>
+#include <cassert>
+
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
+#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
+#include "webrtc/system_wrappers/interface/logging.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+namespace {
+
+// Convenience wrapper for XGetWindowProperty() results.
+template <class PropertyType>
+class XWindowProperty {
+ public:
+ XWindowProperty(Display* display, Window window, Atom property)
+ : is_valid_(false),
+ size_(0),
+ data_(NULL) {
+ const int kBitsPerByte = 8;
+ Atom actual_type;
+ int actual_format;
+ unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty
+ int status = XGetWindowProperty(display, window, property, 0L, ~0L, False,
+ AnyPropertyType, &actual_type,
+ &actual_format, &size_,
+ &bytes_after, &data_);
+ if (status != Success) {
+ data_ = NULL;
+ return;
+ }
+ if (sizeof(PropertyType) * kBitsPerByte != actual_format) {
+ size_ = 0;
+ return;
+ }
+
+ is_valid_ = true;
+ }
+
+ ~XWindowProperty() {
+ if (data_)
+ XFree(data_);
+ }
+
+ // True if we got properly value successfully.
+ bool is_valid() const { return is_valid_; }
+
+ // Size and value of the property.
+ size_t size() const { return size_; }
+ const PropertyType* data() const {
+ return reinterpret_cast<PropertyType*>(data_);
+ }
+ PropertyType* data() {
+ return reinterpret_cast<PropertyType*>(data_);
+ }
+
+ private:
+ bool is_valid_;
+ unsigned long size_; // NOLINT: type required by XGetWindowProperty
+ unsigned char* data_;
+
+ DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
+};
+
+class WindowCapturerLinux : public WindowCapturer {
+ public:
+ WindowCapturerLinux();
+ virtual ~WindowCapturerLinux();
+
+ // WindowCapturer interface.
+ virtual bool GetWindowList(WindowList* windows) OVERRIDE;
+ virtual bool SelectWindow(WindowId id) OVERRIDE;
+
+ // DesktopCapturer interface.
+ virtual void Start(Callback* callback) OVERRIDE;
+ virtual void Capture(const DesktopRegion& region) OVERRIDE;
+
+ private:
+ // Iterates through |window| hierarchy to find first visible window, i.e. one
+ // that has WM_STATE property set to NormalState.
+ // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
+ ::Window GetApplicationWindow(::Window window);
+
+ // Returns true if the |window| is a desktop element.
+ bool IsDesktopElement(::Window window);
+
+ // Returns window title for the specified X |window|.
+ bool GetWindowTitle(::Window window, std::string* title);
+
+ Callback* callback_;
+
+ Display* display_;
+
+ Atom wm_state_atom_;
+ Atom window_type_atom_;
+ Atom normal_window_type_atom_;
+ bool has_composite_extension_;
+
+ ::Window selected_window_;
+ XServerPixelBuffer x_server_pixel_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
+};
+
+WindowCapturerLinux::WindowCapturerLinux()
+ : callback_(NULL),
+ display_(NULL),
+ has_composite_extension_(false),
+ selected_window_(0) {
+ display_ = XOpenDisplay(NULL);
+ if (!display_) {
+ LOG(LS_ERROR) << "Failed to open display.";
+ return;
+ }
+
+ // Create Atoms so we don't need to do it every time they are used.
+ wm_state_atom_ = XInternAtom(display_, "WM_STATE", True);
+ window_type_atom_ = XInternAtom(display_, "_NET_WM_WINDOW_TYPE", True);
+ normal_window_type_atom_ = XInternAtom(
+ display_, "_NET_WM_WINDOW_TYPE_NORMAL", True);
+
+ int event_base, error_base, major_version, minor_version;
+ if (XCompositeQueryExtension(display_, &event_base, &error_base) &&
+ XCompositeQueryVersion(display_, &major_version, &minor_version) &&
+ // XCompositeNameWindowPixmap() requires version 0.2
+ (major_version > 0 || minor_version >= 2)) {
+ has_composite_extension_ = true;
+ } else {
+ LOG(LS_INFO) << "Xcomposite extension not available or too old.";
+ }
+}
+
+WindowCapturerLinux::~WindowCapturerLinux() {
+ if (display_)
+ XCloseDisplay(display_);
+}
+
+bool WindowCapturerLinux::GetWindowList(WindowList* windows) {
+ if (!display_)
+ return false;
+
+ WindowList result;
+
+ XErrorTrap error_trap(display_);
+
+ int num_screens = XScreenCount(display_);
+ for (int screen = 0; screen < num_screens; ++screen) {
+ ::Window root_window = XRootWindow(display_, screen);
+ ::Window parent;
+ ::Window *children;
+ unsigned int num_children;
+ int status = XQueryTree(display_, root_window, &root_window, &parent,
+ &children, &num_children);
+ if (status == 0) {
+ LOG(LS_ERROR) << "Failed to query for child windows for screen "
+ << screen;
+ continue;
+ }
+
+ for (unsigned int i = 0; i < num_children; ++i) {
+ // Iterate in reverse order to return windows from front to back.
+ ::Window app_window =
+ GetApplicationWindow(children[num_children - 1 - i]);
+ if (app_window && !IsDesktopElement(app_window)) {
+ Window w;
+ w.id = app_window;
+ if (GetWindowTitle(app_window, &w.title))
+ result.push_back(w);
+ }
+ }
+
+ if (children)
+ XFree(children);
+ }
+
+ windows->swap(result);
+
+ return true;
+}
+
+bool WindowCapturerLinux::SelectWindow(WindowId id) {
+ if (!x_server_pixel_buffer_.Init(display_, id))
+ return false;
+
+ selected_window_ = id;
+
+ // In addition to needing X11 server-side support for Xcomposite, it actually
+ // needs to be turned on for the window. If the user has modern
+ // hardware/drivers but isn't using a compositing window manager, that won't
+ // be the case. Here we automatically turn it on.
+
+ // Redirect drawing to an offscreen buffer (ie, turn on compositing). X11
+ // remembers who has requested this and will turn it off for us when we exit.
+ XCompositeRedirectWindow(display_, id, CompositeRedirectAutomatic);
+
+ return true;
+}
+
+void WindowCapturerLinux::Start(Callback* callback) {
+ assert(!callback_);
+ assert(callback);
+
+ callback_ = callback;
+}
+
+void WindowCapturerLinux::Capture(const DesktopRegion& region) {
+ if (!has_composite_extension_) {
+ // Without the Xcomposite extension we capture when the whole window is
+ // visible on screen and not covered by any other window. This is not
+ // something we want so instead, just bail out.
+ LOG(LS_INFO) << "No Xcomposite extension detected.";
+ callback_->OnCaptureCompleted(NULL);
+ return;
+ }
+
+ DesktopFrame* frame =
+ new BasicDesktopFrame(x_server_pixel_buffer_.window_size());
+
+ x_server_pixel_buffer_.Synchronize();
+ x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()),
+ frame);
+
+ callback_->OnCaptureCompleted(frame);
+}
+
+::Window WindowCapturerLinux::GetApplicationWindow(::Window window) {
+ // Get WM_STATE property of the window.
+ XWindowProperty<uint32_t> window_state(display_, window, wm_state_atom_);
+
+ // WM_STATE is considered to be set to WithdrawnState when it missing.
+ int32_t state = window_state.is_valid() ?
+ *window_state.data() : WithdrawnState;
+
+ if (state == NormalState) {
+ // Window has WM_STATE==NormalState. Return it.
+ return window;
+ } else if (state == IconicState) {
+ // Window is in minimized. Skip it.
+ return 0;
+ }
+
+ // If the window is in WithdrawnState then look at all of its children.
+ ::Window root, parent;
+ ::Window *children;
+ unsigned int num_children;
+ if (!XQueryTree(display_, window, &root, &parent, &children,
+ &num_children)) {
+ LOG(LS_ERROR) << "Failed to query for child windows although window"
+ << "does not have a valid WM_STATE.";
+ return 0;
+ }
+ ::Window app_window = 0;
+ for (unsigned int i = 0; i < num_children; ++i) {
+ app_window = GetApplicationWindow(children[i]);
+ if (app_window)
+ break;
+ }
+
+ if (children)
+ XFree(children);
+ return app_window;
+}
+
+bool WindowCapturerLinux::IsDesktopElement(::Window window) {
+ if (window == 0)
+ return false;
+
+ // First look for _NET_WM_WINDOW_TYPE. The standard
+ // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
+ // says this hint *should* be present on all windows, and we use the existence
+ // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
+ // a desktop element (that is, only "normal" windows should be shareable).
+ XWindowProperty<uint32_t> window_type(display_, window, window_type_atom_);
+ if (window_type.is_valid() && window_type.size() > 0) {
+ uint32_t* end = window_type.data() + window_type.size();
+ bool is_normal = (end != std::find(
+ window_type.data(), end, normal_window_type_atom_));
+ return !is_normal;
+ }
+
+ // Fall back on using the hint.
+ XClassHint class_hint;
+ Status status = XGetClassHint(display_, window, &class_hint);
+ bool result = false;
+ if (status == 0) {
+ // No hints, assume this is a normal application window.
+ return result;
+ }
+
+ if (strcmp("gnome-panel", class_hint.res_name) == 0 ||
+ strcmp("desktop_window", class_hint.res_name) == 0) {
+ result = true;
+ }
+ XFree(class_hint.res_name);
+ XFree(class_hint.res_class);
+ return result;
+}
+
+bool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) {
+ int status;
+ bool result = false;
+ XTextProperty window_name;
+ window_name.value = NULL;
+ if (window) {
+ status = XGetWMName(display_, window, &window_name);
+ if (status && window_name.value && window_name.nitems) {
+ int cnt;
+ char **list = NULL;
+ status = Xutf8TextPropertyToTextList(display_, &window_name, &list,
+ &cnt);
+ if (status >= Success && cnt && *list) {
+ if (cnt > 1) {
+ LOG(LS_INFO) << "Window has " << cnt
+ << " text properties, only using the first one.";
+ }
+ *title = *list;
+ result = true;
+ }
+ if (list)
+ XFreeStringList(list);
+ }
+ if (window_name.value)
+ XFree(window_name.value);
+ }
+ return result;
+}
+
+} // namespace
+
+// static
+WindowCapturer* WindowCapturer::Create() {
+ return new WindowCapturerLinux();
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/x11/x_error_trap.cc b/webrtc/modules/desktop_capture/x11/x_error_trap.cc
new file mode 100644
index 0000000000..458fbe61a0
--- /dev/null
+++ b/webrtc/modules/desktop_capture/x11/x_error_trap.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
+
+#include <assert.h>
+
+#if defined(TOOLKIT_GTK)
+#include <gdk/gdk.h>
+#endif // !defined(TOOLKIT_GTK)
+
+namespace webrtc {
+
+namespace {
+
+#if !defined(TOOLKIT_GTK)
+
+// TODO(sergeyu): This code is not thread safe. Fix it. Bug 2202.
+static bool g_xserver_error_trap_enabled = false;
+static int g_last_xserver_error_code = 0;
+
+int XServerErrorHandler(Display* display, XErrorEvent* error_event) {
+ assert(g_xserver_error_trap_enabled);
+ g_last_xserver_error_code = error_event->error_code;
+ return 0;
+}
+
+#endif // !defined(TOOLKIT_GTK)
+
+} // namespace
+
+XErrorTrap::XErrorTrap(Display* display)
+ : original_error_handler_(NULL),
+ enabled_(true) {
+#if defined(TOOLKIT_GTK)
+ gdk_error_trap_push();
+#else // !defined(TOOLKIT_GTK)
+ assert(!g_xserver_error_trap_enabled);
+ original_error_handler_ = XSetErrorHandler(&XServerErrorHandler);
+ g_xserver_error_trap_enabled = true;
+ g_last_xserver_error_code = 0;
+#endif // !defined(TOOLKIT_GTK)
+}
+
+int XErrorTrap::GetLastErrorAndDisable() {
+ enabled_ = false;
+#if defined(TOOLKIT_GTK)
+ return gdk_error_trap_push();
+#else // !defined(TOOLKIT_GTK)
+ assert(g_xserver_error_trap_enabled);
+ XSetErrorHandler(original_error_handler_);
+ g_xserver_error_trap_enabled = false;
+ return g_last_xserver_error_code;
+#endif // !defined(TOOLKIT_GTK)
+}
+
+XErrorTrap::~XErrorTrap() {
+ if (enabled_)
+ GetLastErrorAndDisable();
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/x11/x_error_trap.h b/webrtc/modules/desktop_capture/x11/x_error_trap.h
new file mode 100644
index 0000000000..fd8346928c
--- /dev/null
+++ b/webrtc/modules/desktop_capture/x11/x_error_trap.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_
+
+#include <X11/Xlib.h>
+
+#include "webrtc/system_wrappers/interface/constructor_magic.h"
+
+namespace webrtc {
+
+// Helper class that registers X Window error handler. Caller can use
+// GetLastErrorAndDisable() to get the last error that was caught, if any.
+class XErrorTrap {
+ public:
+ explicit XErrorTrap(Display* display);
+ ~XErrorTrap();
+
+ // Returns last error and removes unregisters the error handler.
+ int GetLastErrorAndDisable();
+
+ private:
+ XErrorHandler original_error_handler_;
+ bool enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(XErrorTrap);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_
diff --git a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc
index 88f7738dcf..6983a6dcce 100644
--- a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc
+++ b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc
@@ -11,62 +11,56 @@
#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
#include <assert.h>
+#include <string.h>
#include <sys/shm.h>
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
#include "webrtc/system_wrappers/interface/logging.h"
-#if defined(TOOLKIT_GTK)
-#include <gdk/gdk.h>
-#else // !defined(TOOLKIT_GTK)
-#include <X11/Xlib.h>
-#endif // !defined(TOOLKIT_GTK)
-
namespace {
-#if defined(TOOLKIT_GTK)
-// GDK sets error handler for Xlib errors, so we need to use it to
-// trap X errors when this code is compiled with GTK.
-void EnableXServerErrorTrap() {
- gdk_error_trap_push();
-}
-
-int GetLastXServerError() {
- return gdk_error_trap_pop();
-}
-
-#else // !defined(TOOLKIT_GTK)
-
-static bool g_xserver_error_trap_enabled = false;
-static int g_last_xserver_error_code = 0;
-
-int XServerErrorHandler(Display* display, XErrorEvent* error_event) {
- assert(g_xserver_error_trap_enabled);
- g_last_xserver_error_code = error_event->error_code;
- return 0;
-}
+// Returns the number of bits |mask| has to be shifted left so its last
+// (most-significant) bit set becomes the most-significant bit of the word.
+// When |mask| is 0 the function returns 31.
+uint32_t MaskToShift(uint32_t mask) {
+ int shift = 0;
+ if ((mask & 0xffff0000u) == 0) {
+ mask <<= 16;
+ shift += 16;
+ }
+ if ((mask & 0xff000000u) == 0) {
+ mask <<= 8;
+ shift += 8;
+ }
+ if ((mask & 0xf0000000u) == 0) {
+ mask <<= 4;
+ shift += 4;
+ }
+ if ((mask & 0xc0000000u) == 0) {
+ mask <<= 2;
+ shift += 2;
+ }
+ if ((mask & 0x80000000u) == 0)
+ shift += 1;
-void EnableXServerErrorTrap() {
- assert(!g_xserver_error_trap_enabled);
- XSetErrorHandler(&XServerErrorHandler);
- g_xserver_error_trap_enabled = true;
- g_last_xserver_error_code = 0;
+ return shift;
}
-int GetLastXServerError() {
- assert(g_xserver_error_trap_enabled);
- XSetErrorHandler(NULL);
- g_xserver_error_trap_enabled = false;
- return g_last_xserver_error_code;
+// Returns true if |image| is in RGB format.
+bool IsXImageRGBFormat(XImage* image) {
+ return image->bits_per_pixel == 32 &&
+ image->red_mask == 0xff0000 &&
+ image->green_mask == 0xff00 &&
+ image->blue_mask == 0xff;
}
-#endif // !defined(TOOLKIT_GTK)
-
} // namespace
namespace webrtc {
XServerPixelBuffer::XServerPixelBuffer()
- : display_(NULL), root_window_(0),
+ : display_(NULL), window_(0),
x_image_(NULL),
shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) {
}
@@ -96,34 +90,39 @@ void XServerPixelBuffer::Release() {
delete shm_segment_info_;
shm_segment_info_ = NULL;
}
+ window_ = 0;
}
-void XServerPixelBuffer::Init(Display* display,
- const DesktopSize& screen_size) {
+bool XServerPixelBuffer::Init(Display* display, Window window) {
Release();
display_ = display;
- root_window_size_ = screen_size;
- int default_screen = DefaultScreen(display_);
- root_window_ = RootWindow(display_, default_screen);
- InitShm(default_screen);
-}
-// static
-DesktopSize XServerPixelBuffer::GetRootWindowSize(Display* display) {
- XWindowAttributes root_attr;
- XGetWindowAttributes(display, DefaultRootWindow(display), &root_attr);
- return DesktopSize(root_attr.width, root_attr.height);
+ XWindowAttributes attributes;
+ {
+ XErrorTrap error_trap(display_);
+ if (!XGetWindowAttributes(display_, window, &attributes) ||
+ error_trap.GetLastErrorAndDisable() != 0) {
+ return false;
+ }
+ }
+
+ window_size_ = DesktopSize(attributes.width, attributes.height);
+ window_ = window;
+ InitShm(attributes);
+
+ return true;
}
-void XServerPixelBuffer::InitShm(int screen) {
- Visual* default_visual = DefaultVisual(display_, screen);
- int default_depth = DefaultDepth(display_, screen);
+void XServerPixelBuffer::InitShm(const XWindowAttributes& attributes) {
+ Visual* default_visual = attributes.visual;
+ int default_depth = attributes.depth;
int major, minor;
- Bool havePixmaps;
- if (!XShmQueryVersion(display_, &major, &minor, &havePixmaps))
+ Bool have_pixmaps;
+ if (!XShmQueryVersion(display_, &major, &minor, &have_pixmaps)) {
// Shared memory not supported. CaptureRect will use the XImage API instead.
return;
+ }
bool using_shm = false;
shm_segment_info_ = new XShmSegmentInfo;
@@ -131,8 +130,8 @@ void XServerPixelBuffer::InitShm(int screen) {
shm_segment_info_->shmaddr = reinterpret_cast<char*>(-1);
shm_segment_info_->readOnly = False;
x_image_ = XShmCreateImage(display_, default_visual, default_depth, ZPixmap,
- 0, shm_segment_info_, root_window_size_.width(),
- root_window_size_.height());
+ 0, shm_segment_info_, window_size_.width(),
+ window_size_.height());
if (x_image_) {
shm_segment_info_->shmid = shmget(
IPC_PRIVATE, x_image_->bytes_per_line * x_image_->height,
@@ -141,10 +140,10 @@ void XServerPixelBuffer::InitShm(int screen) {
shm_segment_info_->shmaddr = x_image_->data =
reinterpret_cast<char*>(shmat(shm_segment_info_->shmid, 0, 0));
if (x_image_->data != reinterpret_cast<char*>(-1)) {
- EnableXServerErrorTrap();
+ XErrorTrap error_trap(display_);
using_shm = XShmAttach(display_, shm_segment_info_);
XSync(display_, False);
- if (GetLastXServerError() != 0)
+ if (error_trap.GetLastErrorAndDisable() != 0)
using_shm = false;
if (using_shm) {
LOG(LS_VERBOSE) << "Using X shared memory segment "
@@ -163,48 +162,52 @@ void XServerPixelBuffer::InitShm(int screen) {
return;
}
- if (havePixmaps)
- havePixmaps = InitPixmaps(default_depth);
+ if (have_pixmaps)
+ have_pixmaps = InitPixmaps(default_depth);
shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
shm_segment_info_->shmid = -1;
LOG(LS_VERBOSE) << "Using X shared memory extension v"
<< major << "." << minor
- << " with" << (havePixmaps ? "" : "out") << " pixmaps.";
+ << " with" << (have_pixmaps ? "" : "out") << " pixmaps.";
}
bool XServerPixelBuffer::InitPixmaps(int depth) {
if (XShmPixmapFormat(display_) != ZPixmap)
return false;
- EnableXServerErrorTrap();
- shm_pixmap_ = XShmCreatePixmap(display_, root_window_,
- shm_segment_info_->shmaddr,
- shm_segment_info_,
- root_window_size_.width(),
- root_window_size_.height(), depth);
- XSync(display_, False);
- if (GetLastXServerError() != 0) {
- // |shm_pixmap_| is not not valid because the request was not processed
- // by the X Server, so zero it.
- shm_pixmap_ = 0;
- return false;
+ {
+ XErrorTrap error_trap(display_);
+ shm_pixmap_ = XShmCreatePixmap(display_, window_,
+ shm_segment_info_->shmaddr,
+ shm_segment_info_,
+ window_size_.width(),
+ window_size_.height(), depth);
+ XSync(display_, False);
+ if (error_trap.GetLastErrorAndDisable() != 0) {
+ // |shm_pixmap_| is not not valid because the request was not processed
+ // by the X Server, so zero it.
+ shm_pixmap_ = 0;
+ return false;
+ }
}
- EnableXServerErrorTrap();
- XGCValues shm_gc_values;
- shm_gc_values.subwindow_mode = IncludeInferiors;
- shm_gc_values.graphics_exposures = False;
- shm_gc_ = XCreateGC(display_, root_window_,
- GCSubwindowMode | GCGraphicsExposures,
- &shm_gc_values);
- XSync(display_, False);
- if (GetLastXServerError() != 0) {
- XFreePixmap(display_, shm_pixmap_);
- shm_pixmap_ = 0;
- shm_gc_ = 0; // See shm_pixmap_ comment above.
- return false;
+ {
+ XErrorTrap error_trap(display_);
+ XGCValues shm_gc_values;
+ shm_gc_values.subwindow_mode = IncludeInferiors;
+ shm_gc_values.graphics_exposures = False;
+ shm_gc_ = XCreateGC(display_, window_,
+ GCSubwindowMode | GCGraphicsExposures,
+ &shm_gc_values);
+ XSync(display_, False);
+ if (error_trap.GetLastErrorAndDisable() != 0) {
+ XFreePixmap(display_, shm_pixmap_);
+ shm_pixmap_ = 0;
+ shm_gc_ = 0; // See shm_pixmap_ comment above.
+ return false;
+ }
}
return true;
@@ -213,57 +216,110 @@ bool XServerPixelBuffer::InitPixmaps(int depth) {
void XServerPixelBuffer::Synchronize() {
if (shm_segment_info_ && !shm_pixmap_) {
// XShmGetImage can fail if the display is being reconfigured.
- EnableXServerErrorTrap();
- XShmGetImage(display_, root_window_, x_image_, 0, 0, AllPlanes);
- GetLastXServerError();
+ XErrorTrap error_trap(display_);
+ XShmGetImage(display_, window_, x_image_, 0, 0, AllPlanes);
}
}
-uint8_t* XServerPixelBuffer::CaptureRect(const DesktopRect& rect) {
- assert(rect.right() <= root_window_size_.width());
- assert(rect.bottom() <= root_window_size_.height());
+void XServerPixelBuffer::CaptureRect(const DesktopRect& rect,
+ DesktopFrame* frame) {
+ assert(rect.right() <= window_size_.width());
+ assert(rect.bottom() <= window_size_.height());
+
+ uint8_t* data;
if (shm_segment_info_) {
if (shm_pixmap_) {
- XCopyArea(display_, root_window_, shm_pixmap_, shm_gc_,
+ XCopyArea(display_, window_, shm_pixmap_, shm_gc_,
rect.left(), rect.top(), rect.width(), rect.height(),
rect.left(), rect.top());
XSync(display_, False);
}
- return reinterpret_cast<uint8_t*>(x_image_->data) +
+ data = reinterpret_cast<uint8_t*>(x_image_->data) +
rect.top() * x_image_->bytes_per_line +
rect.left() * x_image_->bits_per_pixel / 8;
} else {
if (x_image_)
XDestroyImage(x_image_);
- x_image_ = XGetImage(display_, root_window_, rect.left(), rect.top(),
+ x_image_ = XGetImage(display_, window_, rect.left(), rect.top(),
rect.width(), rect.height(), AllPlanes, ZPixmap);
- return reinterpret_cast<uint8_t*>(x_image_->data);
+ data = reinterpret_cast<uint8_t*>(x_image_->data);
}
-}
-
-int XServerPixelBuffer::GetStride() const {
- return x_image_->bytes_per_line;
-}
-int XServerPixelBuffer::GetDepth() const {
- return x_image_->depth;
-}
-
-int XServerPixelBuffer::GetBitsPerPixel() const {
- return x_image_->bits_per_pixel;
-}
-
-int XServerPixelBuffer::GetRedMask() const {
- return x_image_->red_mask;
+ if (IsXImageRGBFormat(x_image_)) {
+ FastBlit(data, rect, frame);
+ } else {
+ SlowBlit(data, rect, frame);
+ }
}
-int XServerPixelBuffer::GetBlueMask() const {
- return x_image_->blue_mask;
+void XServerPixelBuffer::FastBlit(uint8_t* image,
+ const DesktopRect& rect,
+ DesktopFrame* frame) {
+ uint8_t* src_pos = image;
+ int src_stride = x_image_->bytes_per_line;
+ int dst_x = rect.left(), dst_y = rect.top();
+
+ uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
+ dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
+
+ int height = rect.height();
+ int row_bytes = rect.width() * DesktopFrame::kBytesPerPixel;
+ for (int y = 0; y < height; ++y) {
+ memcpy(dst_pos, src_pos, row_bytes);
+ src_pos += src_stride;
+ dst_pos += frame->stride();
+ }
}
-int XServerPixelBuffer::GetGreenMask() const {
- return x_image_->green_mask;
+void XServerPixelBuffer::SlowBlit(uint8_t* image,
+ const DesktopRect& rect,
+ DesktopFrame* frame) {
+ int src_stride = x_image_->bytes_per_line;
+ int dst_x = rect.left(), dst_y = rect.top();
+ int width = rect.width(), height = rect.height();
+
+ uint32_t red_mask = x_image_->red_mask;
+ uint32_t green_mask = x_image_->red_mask;
+ uint32_t blue_mask = x_image_->blue_mask;
+
+ uint32_t red_shift = MaskToShift(red_mask);
+ uint32_t green_shift = MaskToShift(green_mask);
+ uint32_t blue_shift = MaskToShift(blue_mask);
+
+ int bits_per_pixel = x_image_->bits_per_pixel;
+
+ uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
+ uint8_t* src_pos = image;
+ dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
+ // TODO(hclam): Optimize, perhaps using MMX code or by converting to
+ // YUV directly.
+ // TODO(sergeyu): This code doesn't handle XImage byte order properly and
+ // won't work with 24bpp images. Fix it.
+ 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;
+ uint32_t g = (pixel & green_mask) << green_shift;
+ uint32_t b = (pixel & blue_mask) << blue_shift;
+ // Write as 32-bit RGB.
+ dst_pos_32[x] = ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) |
+ ((b >> 24) & 0xff);
+ }
+ dst_pos += frame->stride();
+ src_pos += src_stride;
+ }
}
} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h
index 8759be329b..b81096c811 100644
--- a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h
+++ b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h
@@ -20,6 +20,8 @@
namespace webrtc {
+class DesktopFrame;
+
// A class to allow the X server's pixel buffer to be accessed as efficiently
// as possible.
class XServerPixelBuffer {
@@ -29,14 +31,14 @@ class XServerPixelBuffer {
void Release();
- // Allocate (or reallocate) the pixel buffer with the given size, which is
- // assumed to be the current size of the root window.
- // |screen_size| should either come from GetRootWindowSize(), or
- // from a recent ConfigureNotify event on the root window.
- void Init(Display* display, const DesktopSize& screen_size);
+ // Allocate (or reallocate) the pixel buffer for |window|. Returns false in
+ // case of an error (e.g. window doesn't exist).
+ bool Init(Display* display, Window window);
+
+ bool is_initialized() { return window_ != 0; }
- // Request the current size of the root window from the X Server.
- static DesktopSize GetRootWindowSize(Display* display);
+ // Returns the size of the window the buffer was initialized for.
+ const DesktopSize& window_size() { return window_size_; }
// If shared memory is being used without pixmaps, synchronize this pixel
// buffer with the root window contents (otherwise, this is a no-op).
@@ -45,31 +47,28 @@ class XServerPixelBuffer {
// beginning.
void Synchronize();
- // Capture the specified rectangle and return a pointer to its top-left pixel
- // or NULL if capture fails. The returned pointer remains valid until the next
- // call to CaptureRect.
- // In the case where the full-screen data is captured by Synchronize(), this
- // simply returns the pointer without doing any more work.
- // The caller must ensure that |rect| is no larger than the screen size
- // supplied to Init().
- uint8_t* CaptureRect(const DesktopRect& rect);
-
- // Return information about the most recent capture. This is only guaranteed
- // to be valid between CaptureRect calls.
- int GetStride() const;
- int GetDepth() const;
- int GetBitsPerPixel() const;
- int GetRedMask() const;
- int GetBlueMask() const;
- int GetGreenMask() const;
+ // Capture the specified rectangle and stores it in the |frame|. In the case
+ // where the full-screen data is captured by Synchronize(), this simply
+ // returns the pointer without doing any more work. The caller must ensure
+ // that |rect| is not larger than window_size().
+ void CaptureRect(const DesktopRect& rect, DesktopFrame* frame);
private:
- void InitShm(int screen);
+ void InitShm(const XWindowAttributes& attributes);
bool InitPixmaps(int depth);
+ // We expose two forms of blitting to handle variations in the pixel format.
+ // In FastBlit(), the operation is effectively a memcpy.
+ void FastBlit(uint8_t* image,
+ const DesktopRect& rect,
+ DesktopFrame* frame);
+ void SlowBlit(uint8_t* image,
+ const DesktopRect& rect,
+ DesktopFrame* frame);
+
Display* display_;
- Window root_window_;
- DesktopSize root_window_size_;
+ Window window_;
+ DesktopSize window_size_;
XImage* x_image_;
XShmSegmentInfo* shm_segment_info_;
Pixmap shm_pixmap_;