Bug 983504 - Desktop capture code changes/updates for screen sharing. r=gcp
authorMatthew A. Miller <linuxwolf@outer-planes.net>
Tue, 08 Jul 2014 10:00:00 +0200
changeset 215206 ffb95db2dc7e00b2a761f2a333ac7b0a04298495
parent 215205 f0d2710d4ea58fd8651c5317fcffb821eb1d4f61
child 215207 e8491346dfbd193078598872646883fd197a34b4
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgcp
bugs983504
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 983504 - Desktop capture code changes/updates for screen sharing. r=gcp
media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_types.h
media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc
media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer.h
media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc
media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc
media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_types.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_types.h
@@ -32,12 +32,16 @@ const WindowId kNullWindowId = 0;
 //   - On Linux (with X11): TBD.
 typedef intptr_t ScreenId;
 
 // The screen id corresponds to all screen combined together.
 const ScreenId kFullDesktopScreenId = -1;
 
 const ScreenId kInvalidScreenId = -2;
 
+
+typedef intptr_t ProcessId;
+const ProcessId DesktopProcessId = 0;
+
 }  // namespace webrtc
 
 #endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_TYPES_H_
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc
@@ -183,17 +183,18 @@ void ScreenCapturerWin::Capture(const De
     }
 
     // Calculate difference between the two last captured frames.
     DesktopRegion region;
     differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(),
                              &region);
     helper_.InvalidateRegion(region);
   } else {
-    // No previous frame is available. Invalidate the whole screen.
+    // No previous frame is available, or the screen is resized. Invalidate the
+    // whole screen.
     helper_.InvalidateScreen(current_frame->size());
   }
 
   helper_.set_size_most_recent(current_frame->size());
 
   // Emit the current frame.
   DesktopFrame* frame = queue_.current_frame()->Share();
   frame->set_dpi(DesktopVector(
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer.h
@@ -44,14 +44,22 @@ class WindowCapturer : public DesktopCap
   virtual ~WindowCapturer() {}
 
   // Get list of windows. Returns false in case of a failure.
   virtual bool GetWindowList(WindowList* windows) = 0;
 
   // Select window to be captured. Returns false in case of a failure (e.g. if
   // there is no window with the specified id).
   virtual bool SelectWindow(WindowId id) = 0;
+
+  // Bring the selected window to the front. Returns false in case of a
+  // failure or no window selected.
+  // TODO(jiayl): remove the default impl when FakeWindowCapturer is updated in
+  // Chromium.
+  virtual bool BringSelectedWindowToFront() {
+    return true;
+  }
 };
 
 }  // namespace webrtc
 
 #endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_H_
 
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc
@@ -21,16 +21,17 @@ namespace {
 class WindowCapturerNull : public WindowCapturer {
  public:
   WindowCapturerNull();
   virtual ~WindowCapturerNull();
 
   // WindowCapturer interface.
   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
   virtual bool SelectWindow(WindowId id) OVERRIDE;
+  virtual bool BringSelectedWindowToFront() OVERRIDE;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) OVERRIDE;
   virtual void Capture(const DesktopRegion& region) OVERRIDE;
 
  private:
   Callback* callback_;
 
@@ -49,16 +50,21 @@ bool WindowCapturerNull::GetWindowList(W
   return false;
 }
 
 bool WindowCapturerNull::SelectWindow(WindowId id) {
   // Not implemented yet.
   return false;
 }
 
+bool WindowCapturerNull::BringSelectedWindowToFront() {
+  // Not implemented yet.
+  return false;
+}
+
 void WindowCapturerNull::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
 void WindowCapturerNull::Capture(const DesktopRegion& region) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc
@@ -84,16 +84,17 @@ BOOL CALLBACK WindowsEnumerationHandler(
 class WindowCapturerWin : public WindowCapturer {
  public:
   WindowCapturerWin();
   virtual ~WindowCapturerWin();
 
   // WindowCapturer interface.
   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
   virtual bool SelectWindow(WindowId id) OVERRIDE;
+  virtual bool BringSelectedWindowToFront() OVERRIDE;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) OVERRIDE;
   virtual void Capture(const DesktopRegion& region) OVERRIDE;
 
  private:
   bool IsAeroEnabled();
 
@@ -152,16 +153,26 @@ bool WindowCapturerWin::SelectWindow(Win
   HWND window = reinterpret_cast<HWND>(id);
   if (!IsWindow(window) || !IsWindowVisible(window) || IsIconic(window))
     return false;
   window_ = window;
   previous_size_.set(0, 0);
   return true;
 }
 
+bool WindowCapturerWin::BringSelectedWindowToFront() {
+  if (!window_)
+    return false;
+
+  if (!IsWindow(window_) || !IsWindowVisible(window_) || IsIconic(window_))
+    return false;
+
+  return SetForegroundWindow(window_) != 0;
+}
+
 void WindowCapturerWin::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
 void WindowCapturerWin::Capture(const DesktopRegion& region) {
@@ -212,21 +223,21 @@ void WindowCapturerWin::Capture(const De
   //
   // If Aero is enabled, we prefer BitBlt() because it's faster and avoids
   // window flickering. Otherwise, we prefer PrintWindow() because BitBlt() may
   // render occluding windows on top of the desired window.
   //
   // When composition is enabled the DC returned by GetWindowDC() doesn't always
   // have window frame rendered correctly. Windows renders it only once and then
   // caches the result between captures. We hack it around by calling
-  // PrintWindow() whenever window size changes - it somehow affects what we
-  // get from BitBlt() on the subsequent captures.
+  // PrintWindow() whenever window size changes, including the first time of
+  // capturing - it somehow affects what we get from BitBlt() on the subsequent
+  // captures.
 
-  if (!IsAeroEnabled() ||
-      (!previous_size_.is_empty() && !previous_size_.equals(frame->size()))) {
+  if (!IsAeroEnabled() || !previous_size_.equals(frame->size())) {
     result = PrintWindow(window_, mem_dc, 0);
   }
 
   // Aero is enabled or PrintWindow() failed, use BitBlt.
   if (!result) {
     result = BitBlt(mem_dc, 0, 0, frame->size().width(), frame->size().height(),
                     window_dc, 0, 0, SRCCOPY);
   }
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc
@@ -10,16 +10,17 @@
 
 #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_capture_options.h"
 #include "webrtc/modules/desktop_capture/desktop_frame.h"
 #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
 #include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
@@ -79,29 +80,34 @@ class XWindowProperty {
  private:
   bool is_valid_;
   unsigned long size_;  // NOLINT: type required by XGetWindowProperty
   unsigned char* data_;
 
   DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
 };
 
-class WindowCapturerLinux : public WindowCapturer {
+class WindowCapturerLinux : public WindowCapturer,
+                            public SharedXDisplay::XEventHandler {
  public:
   WindowCapturerLinux(const DesktopCaptureOptions& options);
   virtual ~WindowCapturerLinux();
 
   // WindowCapturer interface.
   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
   virtual bool SelectWindow(WindowId id) OVERRIDE;
+  virtual bool BringSelectedWindowToFront() OVERRIDE;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) OVERRIDE;
   virtual void Capture(const DesktopRegion& region) OVERRIDE;
 
+  // SharedXDisplay::XEventHandler interface.
+  virtual bool HandleXEvent(const XEvent& event) OVERRIDE;
+
  private:
   Display* display() { return x_display_->display(); }
 
   // 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);
 
@@ -141,19 +147,23 @@ WindowCapturerLinux::WindowCapturerLinux
   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.";
   }
+
+  x_display_->AddEventHandler(ConfigureNotify, this);
 }
 
-WindowCapturerLinux::~WindowCapturerLinux() {}
+WindowCapturerLinux::~WindowCapturerLinux() {
+  x_display_->RemoveEventHandler(ConfigureNotify, this);
+}
 
 bool WindowCapturerLinux::GetWindowList(WindowList* windows) {
   WindowList result;
 
   XErrorTrap error_trap(display());
 
   int num_screens = XScreenCount(display());
   for (int screen = 0; screen < num_screens; ++screen) {
@@ -189,38 +199,92 @@ bool WindowCapturerLinux::GetWindowList(
 
   return true;
 }
 
 bool WindowCapturerLinux::SelectWindow(WindowId id) {
   if (!x_server_pixel_buffer_.Init(display(), id))
     return false;
 
+  // Tell the X server to send us window resizing events.
+  XSelectInput(display(), id, StructureNotifyMask);
+
   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;
 }
 
+bool WindowCapturerLinux::BringSelectedWindowToFront() {
+  if (!selected_window_)
+    return false;
+
+  unsigned int num_children;
+  ::Window* children;
+  ::Window parent;
+  ::Window root;
+  // Find the root window to pass event to.
+  int status = XQueryTree(
+      display(), selected_window_, &root, &parent, &children, &num_children);
+  if (status == 0) {
+    LOG(LS_ERROR) << "Failed to query for the root window.";
+    return false;
+  }
+
+  if (children)
+    XFree(children);
+
+  XRaiseWindow(display(), selected_window_);
+
+  // Some window managers (e.g., metacity in GNOME) consider it illegal to
+  // raise a window without also giving it input focus with
+  // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough.
+  Atom atom = XInternAtom(display(), "_NET_ACTIVE_WINDOW", True);
+  if (atom != None) {
+    XEvent xev;
+    xev.xclient.type = ClientMessage;
+    xev.xclient.serial = 0;
+    xev.xclient.send_event = True;
+    xev.xclient.window = selected_window_;
+    xev.xclient.message_type = atom;
+
+    // The format member is set to 8, 16, or 32 and specifies whether the
+    // data should be viewed as a list of bytes, shorts, or longs.
+    xev.xclient.format = 32;
+
+    memset(xev.xclient.data.l, 0, sizeof(xev.xclient.data.l));
+
+    XSendEvent(display(),
+               root,
+               False,
+               SubstructureRedirectMask | SubstructureNotifyMask,
+               &xev);
+  }
+  XFlush(display());
+  return true;
+}
+
 void WindowCapturerLinux::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
 void WindowCapturerLinux::Capture(const DesktopRegion& region) {
+  x_display_->ProcessPendingXEvents();
+
   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;
   }
@@ -230,16 +294,30 @@ void WindowCapturerLinux::Capture(const 
 
   x_server_pixel_buffer_.Synchronize();
   x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()),
                                      frame);
 
   callback_->OnCaptureCompleted(frame);
 }
 
+bool WindowCapturerLinux::HandleXEvent(const XEvent& event) {
+  if (event.type == ConfigureNotify) {
+    XConfigureEvent xce = event.xconfigure;
+    if (!DesktopSize(xce.width, xce.height).equals(
+            x_server_pixel_buffer_.window_size())) {
+      if (!x_server_pixel_buffer_.Init(display(), selected_window_)) {
+        LOG(LS_ERROR) << "Failed to initialize pixel buffer after resizing.";
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
 ::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;