Bug 1060738 - Add support for webrtc ThreadWindowsUI for use by webrtc desktop capture thread. r=jesup

This commit is contained in:
Jim Mathies 2014-09-12 09:49:37 -05:00
parent 79011b2bc0
commit ed51fe72f6
5 changed files with 252 additions and 13 deletions

View File

@ -57,6 +57,11 @@ class ThreadWrapper {
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();
@ -85,6 +90,10 @@ class ThreadWrapper {
// 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

View File

@ -28,9 +28,23 @@ ThreadWrapper* ThreadWrapper::CreateThread(ThreadRunFunction func,
#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

View File

@ -20,6 +20,13 @@
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(),
@ -150,11 +157,7 @@ bool ThreadWindows::Stop() {
}
}
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_,
@ -164,6 +167,24 @@ void ThreadWindows::Run() {
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_) {
@ -175,13 +196,7 @@ void ThreadWindows::Run() {
}
} 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();
@ -195,4 +210,152 @@ void ThreadWindows::Run() {
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

View File

@ -37,7 +37,9 @@ class ThreadWindows : public ThreadWrapper {
protected:
virtual void Run();
private:
void SetThreadNameHelper();
void ThreadStoppedHelper();
ThreadRunFunction run_function_;
ThreadObj obj_;
@ -60,6 +62,41 @@ class ThreadWindows : public ThreadWrapper {
};
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_

View File

@ -475,7 +475,11 @@ DesktopCaptureImpl::DesktopCaptureImpl(const int32_t id)
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;
@ -758,6 +762,12 @@ int32_t DesktopCaptureImpl::StartCapture(const VideoCaptureCapability& capabilit
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;
}
@ -797,8 +807,13 @@ 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
@ -806,6 +821,7 @@ void DesktopCaptureImpl::process() {
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) {