Bug 1060738 - Add support for webrtc ThreadWindowsUI for use by webrtc desktop capture thread. r=jesup a=sylvestre
authorJim Mathies <jmathies@mozilla.com>
Fri, 12 Sep 2014 09:49:37 -0500
changeset 216719 2c6a2069023a
parent 216718 a128f3f1ce1f
child 216720 b6a5a3973477
push id3890
push userrjesup@wgate.com
push date2014-09-15 14:07 +0000
treeherdermozilla-beta@84daded3719c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, sylvestre
bugs1060738
milestone33.0
Bug 1060738 - Add support for webrtc ThreadWindowsUI for use by webrtc desktop capture thread. r=jesup a=sylvestre
media/webrtc/trunk/webrtc/system_wrappers/interface/thread_wrapper.h
media/webrtc/trunk/webrtc/system_wrappers/source/thread.cc
media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc
media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.h
media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
--- a/media/webrtc/trunk/webrtc/system_wrappers/interface/thread_wrapper.h
+++ b/media/webrtc/trunk/webrtc/system_wrappers/interface/thread_wrapper.h
@@ -52,16 +52,21 @@ class ThreadWrapper {
   // prio        Thread priority. May require root/admin rights.
   // thread_name  NULL terminated thread name, will be visable in the Windows
   //             debugger.
   static ThreadWrapper* CreateThread(ThreadRunFunction func,
                                      ThreadObj obj,
                                      ThreadPriority prio = kNormalPriority,
                                      const char* thread_name = 0);
 
+  static ThreadWrapper* CreateUIThread(ThreadRunFunction func,
+                                       ThreadObj obj,
+                                       ThreadPriority prio = kNormalPriority,
+                                       const char* thread_name = 0);
+
   // Get the current thread's kernel thread ID.
   static uint32_t GetThreadId();
 
   // Non blocking termination of the spawned thread. Note that it is not safe
   // to delete this class until the spawned thread has been reclaimed.
   virtual void SetNotAlive() = 0;
 
   // Tries to spawns a thread and returns true if that was successful.
@@ -80,13 +85,17 @@ class ThreadWrapper {
   virtual bool SetAffinity(const int* processor_numbers,
                            const unsigned int amount_of_processors);
 
   // Stops the spawned thread and waits for it to be reclaimed with a timeout
   // of two seconds. Will return false if the thread was not reclaimed.
   // Multiple tries to Stop are allowed (e.g. to wait longer than 2 seconds).
   // It's ok to call Stop() even if the spawned thread has been reclaimed.
   virtual bool Stop() = 0;
+
+  // Request a timed callback for ThreadRunFunction. Currently only
+  // implemented for a specific type of thread on Windows.
+  virtual bool RequestCallbackTimer(unsigned int milliseconds);
 };
 
 }  // namespace webrtc
 
 #endif  // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_THREAD_WRAPPER_H_
--- a/media/webrtc/trunk/webrtc/system_wrappers/source/thread.cc
+++ b/media/webrtc/trunk/webrtc/system_wrappers/source/thread.cc
@@ -23,14 +23,28 @@ ThreadWrapper* ThreadWrapper::CreateThre
                                            const char* thread_name) {
 #if defined(_WIN32)
   return new ThreadWindows(func, obj, prio, thread_name);
 #else
   return ThreadPosix::Create(func, obj, prio, thread_name);
 #endif
 }
 
+ThreadWrapper* ThreadWrapper::CreateUIThread(ThreadRunFunction func,
+                                             ThreadObj obj, ThreadPriority prio,
+                                             const char* thread_name) {
+#if defined(_WIN32)
+  return new ThreadWindowsUI(func, obj, prio, thread_name);
+#else
+  return ThreadPosix::Create(func, obj, prio, thread_name);
+#endif
+}
+
 bool ThreadWrapper::SetAffinity(const int* processor_numbers,
                                 const unsigned int amount_of_processors) {
   return false;
 }
 
+bool ThreadWrapper::RequestCallbackTimer(unsigned int milliseconds) {
+  return false;
+}
+
 }  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc
+++ b/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.cc
@@ -15,16 +15,23 @@
 #include <stdio.h>
 #include <windows.h>
 
 #include "webrtc/system_wrappers/interface/trace.h"
 #include "webrtc/system_wrappers/source/set_thread_name_win.h"
 
 namespace webrtc {
 
+// For use in ThreadWindowsUI callbacks
+static UINT static_reg_windows_msg = RegisterWindowMessageW(L"WebrtcWindowsUIThreadEvent");
+// timer id used in delayed callbacks
+static const UINT_PTR kTimerId = 1;
+static const wchar_t kThisProperty[] = L"ThreadWindowsUIPtr"; 
+static const wchar_t kThreadWindow[] = L"WebrtcWindowsUIThread";
+
 ThreadWindows::ThreadWindows(ThreadRunFunction func, ThreadObj obj,
                              ThreadPriority prio, const char* thread_name)
     : ThreadWrapper(),
       run_function_(func),
       obj_(obj),
       alive_(false),
       dead_(true),
       do_not_close_handle_(false),
@@ -145,54 +152,210 @@ bool ThreadWindows::Stop() {
 
   if (dead_ || signaled) {
     return true;
   } else {
     return false;
   }
 }
 
-void ThreadWindows::Run() {
-  alive_ = true;
-  dead_ = false;
-  event_->Set();
-
+void ThreadWindows::SetThreadNameHelper() {
   // All tracing must be after event_->Set to avoid deadlock in Trace.
   if (set_thread_name_) {
     WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
                  "Thread with name:%s started ", name_);
     SetThreadName(-1, name_); // -1, set thread name for the calling thread.
   } else {
     WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
                  "Thread without name started");
   }
+}
+
+void ThreadWindows::ThreadStoppedHelper() {
+  if (set_thread_name_) {
+    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
+                 "Thread with name:%s stopped", name_);
+  } else {
+    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
+                 "Thread without name stopped");
+  }
+}
+
+void ThreadWindows::Run() {
+  alive_ = true;
+  dead_ = false;
+  event_->Set();
+
+  SetThreadNameHelper();
 
   do {
     if (run_function_) {
       if (!run_function_(obj_)) {
         alive_ = false;
       }
     } else {
       alive_ = false;
     }
   } while (alive_);
 
-  if (set_thread_name_) {
-    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
-                 "Thread with name:%s stopped", name_);
-  } else {
-    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, id_,
-                 "Thread without name stopped");
-  }
+  ThreadStoppedHelper();
 
   critsect_stop_->Enter();
 
   if (thread_ && !do_not_close_handle_) {
     HANDLE thread = thread_;
     thread_ = NULL;
     CloseHandle(thread);
   }
   dead_ = true;
 
   critsect_stop_->Leave();
 };
 
+bool ThreadWindowsUI::Start(unsigned int& thread_id) {
+  return InternalInit() ? ThreadWindows::Start(thread_id) : false;
+}
+
+bool ThreadWindowsUI::Stop() {
+  critsect_stop_->Enter();
+
+  // Prevents the handle from being closed in ThreadWindows::Run()
+  do_not_close_handle_ = true;
+  alive_ = false;
+
+  // Shut down the dispatch loop and let the background thread exit.
+  if (timerid_) {
+    KillTimer(hwnd_, timerid_);
+    timerid_ = 0;
+  }
+
+  RemovePropW(hwnd_, kThisProperty);
+  PostMessage(hwnd_, WM_CLOSE, 0, 0);
+
+  bool signaled = false;
+  if (thread_ && !dead_) {
+    critsect_stop_->Leave();
+
+    // Wait up to 2 seconds for the thread to complete.
+    if (WAIT_OBJECT_0 == WaitForSingleObject(thread_, 2000)) {
+      signaled = true;
+    }
+    critsect_stop_->Enter();
+  }
+  if (thread_) {
+    CloseHandle(thread_);
+    thread_ = NULL;
+  }
+  critsect_stop_->Leave();
+
+  if (dead_ || signaled) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool ThreadWindowsUI::InternalInit() {
+  // Create an event window for use in generating callbacks to capture
+  // objects.
+  if (hwnd_ == nullptr) {
+    WNDCLASSW wc;
+    HMODULE hModule = GetModuleHandle(nullptr);
+    if (!GetClassInfoW(hModule, kThreadWindow, &wc)) {
+      ZeroMemory(&wc, sizeof(WNDCLASSW));
+      wc.hInstance = hModule;
+      wc.lpfnWndProc = EventWindowProc;
+      wc.lpszClassName = kThreadWindow;
+      RegisterClassW(&wc);
+    }
+    hwnd_ = CreateWindowW(kThreadWindow, L"",
+                          0, 0, 0, 0, 0,
+                          nullptr, nullptr, hModule, nullptr);
+    assert(hwnd_);
+    SetPropW(hwnd_, kThisProperty, this);
+  }
+  return !!hwnd_;
+}
+
+void ThreadWindowsUI::RequestCallback() {
+  assert(hwnd_);
+  assert(static_reg_windows_msg);
+  PostMessage(hwnd_, static_reg_windows_msg, 0, 0);
+}
+
+bool ThreadWindowsUI::RequestCallbackTimer(unsigned int milliseconds) {
+  assert(hwnd_);
+  if (timerid_) {
+    KillTimer(hwnd_, timerid_);
+  }
+  timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, nullptr);
+  return !!timerid_;
+}
+
+void ThreadWindowsUI::Run() {
+  assert(hwnd_);
+
+  alive_ = true;
+  dead_ = false;
+
+  event_->Set();
+  SetThreadNameHelper();
+
+  do {
+    if (!run_function_) {
+      alive_ = false;
+      break;
+    }
+
+    // blocks
+    MSG msg;
+    if (GetMessage(&msg, NULL, 0, 0)) {
+      if (msg.message == WM_CLOSE) {
+        alive_ = false;
+        break;
+      }
+      TranslateMessage(&msg);
+      DispatchMessage(&msg);
+    }
+  } while (alive_);
+
+  ThreadStoppedHelper();
+  DestroyWindow(hwnd_);
+
+  critsect_stop_->Enter();
+
+  if (thread_ && !do_not_close_handle_) {
+    HANDLE thread = thread_;
+    thread_ = NULL;
+    CloseHandle(thread);
+  }
+  dead_ = true;
+
+  critsect_stop_->Leave();
+};
+
+void
+ThreadWindowsUI::NativeEventCallback() {
+  if (!run_function_) {
+    alive_ = false;
+    return;
+  }
+  alive_ = run_function_(obj_);
+}
+
+/* static */
+LRESULT CALLBACK
+ThreadWindowsUI::EventWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+  ThreadWindowsUI *twui = static_cast<ThreadWindowsUI*>(GetPropW(hwnd, kThisProperty));
+  if (!twui) {
+    return DefWindowProc(hwnd, uMsg, wParam, lParam);
+  }
+
+  if ((uMsg == static_reg_windows_msg && uMsg != WM_NULL) ||
+      (uMsg == WM_TIMER && wParam == kTimerId)) {
+    twui->NativeEventCallback();
+    return 0;
+  }
+
+  return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
 }  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.h
+++ b/media/webrtc/trunk/webrtc/system_wrappers/source/thread_win.h
@@ -32,17 +32,19 @@ class ThreadWindows : public ThreadWrapp
   virtual bool Stop();
   virtual void SetNotAlive();
 
   static unsigned int WINAPI StartThread(LPVOID lp_parameter);
 
  protected:
   virtual void Run();
 
- private:
+  void SetThreadNameHelper();
+  void ThreadStoppedHelper();
+
   ThreadRunFunction    run_function_;
   ThreadObj            obj_;
 
   bool                    alive_;
   bool                    dead_;
 
   // TODO(hellner)
   // do_not_close_handle_ member seem pretty redundant. Should be able to remove
@@ -55,11 +57,46 @@ class ThreadWindows : public ThreadWrapp
 
   HANDLE                  thread_;
   unsigned int            id_;
   char                    name_[kThreadMaxNameLength];
   bool                    set_thread_name_;
 
 };
 
+class ThreadWindowsUI : public ThreadWindows {
+ public:
+  ThreadWindowsUI(ThreadRunFunction func, ThreadObj obj, ThreadPriority prio,
+                  const char* thread_name) :
+  ThreadWindows(func, obj, prio, thread_name),
+  hwnd_(nullptr),
+  timerid_(0) {
+ }
+
+ virtual bool Start(unsigned int& id);
+ virtual bool Stop();
+
+ /**
+  * Request an async callback soon.
+  */
+ void RequestCallback();
+
+ /**
+  * Request a recurring callback.
+  */
+ bool RequestCallbackTimer(unsigned int milliseconds);
+
+ protected:
+  virtual void Run();
+
+ private:
+  static LRESULT CALLBACK EventWindowProc(HWND, UINT, WPARAM, LPARAM);
+  void NativeEventCallback();
+  bool InternalInit();
+
+  HWND hwnd_;
+  UINT_PTR timerid_;
+};
+
+
 }  // namespace webrtc
 
 #endif  // WEBRTC_SYSTEM_WRAPPERS_SOURCE_THREAD_WIN_H_
--- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
@@ -478,17 +478,21 @@ DesktopCaptureImpl::DesktopCaptureImpl(c
     _captureCallBack(NULL),
   _lastProcessFrameCount(TickTime::Now()),
   _rotateFrame(kRotateNone),
   last_capture_time_(TickTime::MillisecondTimestamp()),
   delta_ntp_internal_ms_(
                          Clock::GetRealTimeClock()->CurrentNtpInMilliseconds() -
                          TickTime::MillisecondTimestamp()),
   time_event_(*EventWrapper::Create()),
+#if defined(_WIN32)
+  capturer_thread_(*ThreadWrapper::CreateUIThread(Run, this, kHighPriority, "ScreenCaptureThread")) {
+#else
   capturer_thread_(*ThreadWrapper::CreateThread(Run, this, kHighPriority, "ScreenCaptureThread")) {
+#endif
   _requestedCapability.width = kDefaultWidth;
   _requestedCapability.height = kDefaultHeight;
   _requestedCapability.maxFPS = 30;
   _requestedCapability.rawType = kVideoI420;
   _requestedCapability.codecType = kVideoCodecUnknown;
   memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes));
 }
 
@@ -763,16 +767,22 @@ uint32_t DesktopCaptureImpl::CalculateFr
   return nrOfFrames;
 }
 
 int32_t DesktopCaptureImpl::StartCapture(const VideoCaptureCapability& capability) {
   _requestedCapability = capability;
   desktop_capturer_cursor_composer_->Start(this);
   unsigned int t_id =0;
   capturer_thread_.Start(t_id);
+
+#if defined(_WIN32)
+  uint32_t maxFPSNeeded = 1000/_requestedCapability.maxFPS;
+  capturer_thread_.RequestCallbackTimer(maxFPSNeeded);
+#endif
+
   return 0;
 }
 
 int32_t DesktopCaptureImpl::StopCapture() {
   return -1;
 }
 
 bool DesktopCaptureImpl::CaptureStarted() {
@@ -802,25 +812,31 @@ void DesktopCaptureImpl::OnCaptureComple
 SharedMemory* DesktopCaptureImpl::CreateSharedMemory(size_t size) {
   return NULL;
 }
 
 void DesktopCaptureImpl::process() {
   DesktopRect desktop_rect;
   DesktopRegion desktop_region;
 
+#if !defined(_WIN32)
   TickTime startProcessTime = TickTime::Now();
+#endif
+
   desktop_capturer_cursor_composer_->Capture(DesktopRegion());
+
+#if !defined(_WIN32)
   const uint32_t processTime =
       (uint32_t)(TickTime::Now() - startProcessTime).Milliseconds();
   // Use at most x% CPU or limit framerate
   const uint32_t maxFPSNeeded = 1000/_requestedCapability.maxFPS;
   const float sleepTimeFactor = (100.0f / kMaxDesktopCaptureCpuUsage) - 1.0f;
   const uint32_t sleepTime = sleepTimeFactor * processTime;
   time_event_.Wait(std::max<uint32_t>(maxFPSNeeded, sleepTime));
+#endif
 }
 
 void DesktopCaptureImpl::OnCursorShapeChanged(MouseCursorShape* cursor_shape) {
   // do nothing, DesktopAndCursorComposer does it all
 }
 
 }  // namespace webrtc