diff options
Diffstat (limited to 'plugins/pluginWinDD/plugin_win_dd_producer.cxx')
-rwxr-xr-x | plugins/pluginWinDD/plugin_win_dd_producer.cxx | 1600 |
1 files changed, 746 insertions, 854 deletions
diff --git a/plugins/pluginWinDD/plugin_win_dd_producer.cxx b/plugins/pluginWinDD/plugin_win_dd_producer.cxx index 7c13767..01d857d 100755 --- a/plugins/pluginWinDD/plugin_win_dd_producer.cxx +++ b/plugins/pluginWinDD/plugin_win_dd_producer.cxx @@ -55,28 +55,27 @@ // // plugin_win_dd_producer_t // -typedef struct plugin_win_dd_producer_s -{ - TMEDIA_DECLARE_PRODUCER; - - bool bStarted, bPrepared, bMuted, bWindowHooked, bThreadTerminationDelayed; - tsk_thread_handle_t* ppTread[1]; - - OUTPUTMANAGER *pOutMgr; - THREADMANAGER *pThreadMgr; - - // Window handles - HWND hwndPreview; - WNDPROC wndPreviewProc; - HWND hwndSrc; - - // Synchronization - HANDLE hlUnexpectedErrorEvent; - HANDLE hlExpectedErrorEvent; - HANDLE hlOcclutionEvent; - HANDLE hlTerminateThreadsEvent; - - HCURSOR hcCursor; +typedef struct plugin_win_dd_producer_s { + TMEDIA_DECLARE_PRODUCER; + + bool bStarted, bPrepared, bMuted, bWindowHooked, bThreadTerminationDelayed; + tsk_thread_handle_t* ppTread[1]; + + OUTPUTMANAGER *pOutMgr; + THREADMANAGER *pThreadMgr; + + // Window handles + HWND hwndPreview; + WNDPROC wndPreviewProc; + HWND hwndSrc; + + // Synchronization + HANDLE hlUnexpectedErrorEvent; + HANDLE hlExpectedErrorEvent; + HANDLE hlOcclutionEvent; + HANDLE hlTerminateThreadsEvent; + + HCURSOR hcCursor; } plugin_win_dd_producer_t; @@ -97,11 +96,10 @@ static void* TSK_STDCALL DDThread(void *pArg); // // Class for progressive waits // -typedef struct -{ - UINT WaitTime; - UINT WaitCount; -}WAIT_BAND; +typedef struct { + UINT WaitTime; + UINT WaitCount; +} WAIT_BAND; #define WAIT_BAND_COUNT 3 #define WAIT_BAND_STOP 0 @@ -109,360 +107,316 @@ typedef struct class DYNAMIC_WAIT { public: - DYNAMIC_WAIT(); - ~DYNAMIC_WAIT(); + DYNAMIC_WAIT(); + ~DYNAMIC_WAIT(); - void Wait(); + void Wait(); private: - static const WAIT_BAND m_WaitBands[WAIT_BAND_COUNT]; + static const WAIT_BAND m_WaitBands[WAIT_BAND_COUNT]; - // Period in seconds that a new wait call is considered part of the same wait sequence - static const UINT m_WaitSequenceTimeInSeconds = 2; + // Period in seconds that a new wait call is considered part of the same wait sequence + static const UINT m_WaitSequenceTimeInSeconds = 2; - UINT m_CurrentWaitBandIdx; - UINT m_WaitCountInCurrentBand; - LARGE_INTEGER m_QPCFrequency; - LARGE_INTEGER m_LastWakeUpTime; - BOOL m_QPCValid; + UINT m_CurrentWaitBandIdx; + UINT m_WaitCountInCurrentBand; + LARGE_INTEGER m_QPCFrequency; + LARGE_INTEGER m_LastWakeUpTime; + BOOL m_QPCValid; }; const WAIT_BAND DYNAMIC_WAIT::m_WaitBands[WAIT_BAND_COUNT] = { - { 250, 20 }, - { 2000, 60 }, - { 5000, WAIT_BAND_STOP } // Never move past this band + { 250, 20 }, + { 2000, 60 }, + { 5000, WAIT_BAND_STOP } // Never move past this band }; /* ============ Video DD Producer Interface ================= */ static int plugin_win_dd_producer_set(tmedia_producer_t *p_self, const tmedia_param_t* pc_param) { - int ret = -1; - plugin_win_dd_producer_t* p_dd = (plugin_win_dd_producer_t*)p_self; - - if (!p_dd || !pc_param) - { - DD_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if (pc_param->value_type == tmedia_pvt_int64) - { - if (tsk_striequals(pc_param->key, "local-hwnd") || tsk_striequals(pc_param->key, "preview-hwnd")) - { - HWND hwnd = (HWND)*((int64_t*)pc_param->value); - ret = SUCCEEDED(HookWindow(p_dd, hwnd)) ? 0 : -1; - } - else if (tsk_striequals(pc_param->key, "src-hwnd")) - { - p_dd->hwndSrc = (HWND)*((int64_t*)pc_param->value); - ret = 0; - } - } - else if (pc_param->value_type == tmedia_pvt_int32) - { - if (tsk_striequals(pc_param->key, "mute")) - { - p_dd->bMuted = (TSK_TO_INT32((uint8_t*)pc_param->value) != 0); - ret = 0; - } - } - - return ret; + int ret = -1; + plugin_win_dd_producer_t* p_dd = (plugin_win_dd_producer_t*)p_self; + + if (!p_dd || !pc_param) { + DD_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (pc_param->value_type == tmedia_pvt_int64) { + if (tsk_striequals(pc_param->key, "local-hwnd") || tsk_striequals(pc_param->key, "preview-hwnd")) { + HWND hwnd = (HWND)*((int64_t*)pc_param->value); + ret = SUCCEEDED(HookWindow(p_dd, hwnd)) ? 0 : -1; + } + else if (tsk_striequals(pc_param->key, "src-hwnd")) { + p_dd->hwndSrc = (HWND)*((int64_t*)pc_param->value); + ret = 0; + } + } + else if (pc_param->value_type == tmedia_pvt_int32) { + if (tsk_striequals(pc_param->key, "mute")) { + p_dd->bMuted = (TSK_TO_INT32((uint8_t*)pc_param->value) != 0); + ret = 0; + } + } + + return ret; } static int plugin_win_dd_producer_prepare(tmedia_producer_t* self, const tmedia_codec_t* codec) { - plugin_win_dd_producer_t* pSelf = (plugin_win_dd_producer_t*)self; - HRESULT hr = S_OK; - - if (!pSelf || !codec && codec->plugin) - { - DD_DEBUG_ERROR("Invalid parameter"); - DD_CHECK_HR(hr = E_UNEXPECTED); - } - - if (pSelf->bPrepared) - { - DD_DEBUG_WARN("DD video producer already prepared"); - DD_CHECK_HR(hr = E_UNEXPECTED); - } - - if (pSelf->bThreadTerminationDelayed) - { - DD_DEBUG_INFO("Thread termination was delayed ...cleanup now"); - if (_plugin_win_dd_producer_unprepare(pSelf, true/*cleanup?*/) != 0) - { - DD_CHECK_HR(hr = E_UNEXPECTED); - } - } - - TMEDIA_PRODUCER(pSelf)->video.fps = TMEDIA_CODEC_VIDEO(codec)->out.fps; - TMEDIA_PRODUCER(pSelf)->video.width = TMEDIA_CODEC_VIDEO(codec)->out.width; - TMEDIA_PRODUCER(pSelf)->video.height = TMEDIA_CODEC_VIDEO(codec)->out.height; - TMEDIA_PRODUCER(pSelf)->encoder.codec_id = tmedia_codec_id_none; // means RAW frames as input - - DD_DEBUG_INFO("DD video producer: fps=%d, width=%d, height=%d", - TMEDIA_PRODUCER(pSelf)->video.fps, - TMEDIA_PRODUCER(pSelf)->video.width, - TMEDIA_PRODUCER(pSelf)->video.height); - - // Event used by the threads to signal an unexpected error and we want to quit the app - if (!pSelf->hlUnexpectedErrorEvent && !(pSelf->hlUnexpectedErrorEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) - { - ProcessFailure(nullptr, L"UnexpectedErrorEvent creation failed", L"Error", E_UNEXPECTED); - DD_CHECK_HR(hr = E_UNEXPECTED); - } - - // Event for when a thread encounters an expected error - if (!pSelf->hlExpectedErrorEvent && !(pSelf->hlExpectedErrorEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) - { - ProcessFailure(nullptr, L"ExpectedErrorEvent creation failed", L"Error", E_UNEXPECTED); - DD_CHECK_HR(hr = E_UNEXPECTED); - } - - // Event for Occlution - if (!pSelf->hlOcclutionEvent && !(pSelf->hlOcclutionEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) - { - ProcessFailure(nullptr, L"OcclutionEvent creation failed", L"Error", E_UNEXPECTED); - DD_CHECK_HR(hr = E_UNEXPECTED); - } - - // Event to tell spawned threads to quit - if (!pSelf->hlTerminateThreadsEvent && !(pSelf->hlTerminateThreadsEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) - { - ProcessFailure(nullptr, L"TerminateThreadsEvent creation failed", L"Error", E_UNEXPECTED); - DD_CHECK_HR(hr = E_UNEXPECTED); - } - - // Load simple cursor - if (!pSelf->hcCursor && !(pSelf->hcCursor = LoadCursor(nullptr, IDC_ARROW))) - { - ProcessFailure(nullptr, L"Cursor load failed", L"Error", E_UNEXPECTED); - DD_CHECK_HR(hr = E_UNEXPECTED); - } - - if (!pSelf->pOutMgr && !(pSelf->pOutMgr = new OUTPUTMANAGER())) - { - ProcessFailure(nullptr, L"Out manager allocation failed", L"Error", E_OUTOFMEMORY); - DD_CHECK_HR(hr = E_OUTOFMEMORY); - } - - if (!pSelf->pThreadMgr && !(pSelf->pThreadMgr = new THREADMANAGER())) - { - ProcessFailure(nullptr, L"Thread managed allocation failed", L"Error", E_OUTOFMEMORY); - DD_CHECK_HR(hr = E_OUTOFMEMORY); - } + plugin_win_dd_producer_t* pSelf = (plugin_win_dd_producer_t*)self; + HRESULT hr = S_OK; + + if (!pSelf || !codec && codec->plugin) { + DD_DEBUG_ERROR("Invalid parameter"); + DD_CHECK_HR(hr = E_UNEXPECTED); + } + + if (pSelf->bPrepared) { + DD_DEBUG_WARN("DD video producer already prepared"); + DD_CHECK_HR(hr = E_UNEXPECTED); + } + + if (pSelf->bThreadTerminationDelayed) { + DD_DEBUG_INFO("Thread termination was delayed ...cleanup now"); + if (_plugin_win_dd_producer_unprepare(pSelf, true/*cleanup?*/) != 0) { + DD_CHECK_HR(hr = E_UNEXPECTED); + } + } + + TMEDIA_PRODUCER(pSelf)->video.fps = TMEDIA_CODEC_VIDEO(codec)->out.fps; + TMEDIA_PRODUCER(pSelf)->video.width = TMEDIA_CODEC_VIDEO(codec)->out.width; + TMEDIA_PRODUCER(pSelf)->video.height = TMEDIA_CODEC_VIDEO(codec)->out.height; + TMEDIA_PRODUCER(pSelf)->encoder.codec_id = tmedia_codec_id_none; // means RAW frames as input + + DD_DEBUG_INFO("DD video producer: fps=%d, width=%d, height=%d", + TMEDIA_PRODUCER(pSelf)->video.fps, + TMEDIA_PRODUCER(pSelf)->video.width, + TMEDIA_PRODUCER(pSelf)->video.height); + + // Event used by the threads to signal an unexpected error and we want to quit the app + if (!pSelf->hlUnexpectedErrorEvent && !(pSelf->hlUnexpectedErrorEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) { + ProcessFailure(nullptr, L"UnexpectedErrorEvent creation failed", L"Error", E_UNEXPECTED); + DD_CHECK_HR(hr = E_UNEXPECTED); + } + + // Event for when a thread encounters an expected error + if (!pSelf->hlExpectedErrorEvent && !(pSelf->hlExpectedErrorEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) { + ProcessFailure(nullptr, L"ExpectedErrorEvent creation failed", L"Error", E_UNEXPECTED); + DD_CHECK_HR(hr = E_UNEXPECTED); + } + + // Event for Occlution + if (!pSelf->hlOcclutionEvent && !(pSelf->hlOcclutionEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) { + ProcessFailure(nullptr, L"OcclutionEvent creation failed", L"Error", E_UNEXPECTED); + DD_CHECK_HR(hr = E_UNEXPECTED); + } + + // Event to tell spawned threads to quit + if (!pSelf->hlTerminateThreadsEvent && !(pSelf->hlTerminateThreadsEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr))) { + ProcessFailure(nullptr, L"TerminateThreadsEvent creation failed", L"Error", E_UNEXPECTED); + DD_CHECK_HR(hr = E_UNEXPECTED); + } + + // Load simple cursor + if (!pSelf->hcCursor && !(pSelf->hcCursor = LoadCursor(nullptr, IDC_ARROW))) { + ProcessFailure(nullptr, L"Cursor load failed", L"Error", E_UNEXPECTED); + DD_CHECK_HR(hr = E_UNEXPECTED); + } + + if (!pSelf->pOutMgr && !(pSelf->pOutMgr = new OUTPUTMANAGER())) { + ProcessFailure(nullptr, L"Out manager allocation failed", L"Error", E_OUTOFMEMORY); + DD_CHECK_HR(hr = E_OUTOFMEMORY); + } + + if (!pSelf->pThreadMgr && !(pSelf->pThreadMgr = new THREADMANAGER())) { + ProcessFailure(nullptr, L"Thread managed allocation failed", L"Error", E_OUTOFMEMORY); + DD_CHECK_HR(hr = E_OUTOFMEMORY); + } bail: - pSelf->bPrepared = SUCCEEDED(hr); - return SUCCEEDED(hr) ? 0 : -1; + pSelf->bPrepared = SUCCEEDED(hr); + return SUCCEEDED(hr) ? 0 : -1; } static int plugin_win_dd_producer_start(tmedia_producer_t* self) { - plugin_win_dd_producer_t* pSelf = (plugin_win_dd_producer_t*)self; - HRESULT hr = S_OK; - - if (!pSelf) - { - DD_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if (pSelf->bStarted) - { - DD_DEBUG_INFO("Producer already started"); - goto bail; - } - if (!pSelf->bPrepared) - { - DD_DEBUG_ERROR("Producer not prepared"); - DD_CHECK_HR(hr = E_UNEXPECTED); - } - - DD_CHECK_HR(hr = HookWindow(pSelf, pSelf->hwndPreview)); - - // Start asynchronous watcher thread - pSelf->bStarted = true; - int ret = tsk_thread_create(&pSelf->ppTread[0], DDThread, pSelf); - if (ret != 0) - { - TSK_DEBUG_ERROR("Failed to create thread"); - pSelf->bStarted = false; - if (pSelf->ppTread[0]) - { - tsk_thread_join(&pSelf->ppTread[0]); - } - DD_CHECK_HR(hr = E_UNEXPECTED); - } + plugin_win_dd_producer_t* pSelf = (plugin_win_dd_producer_t*)self; + HRESULT hr = S_OK; + + if (!pSelf) { + DD_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (pSelf->bStarted) { + DD_DEBUG_INFO("Producer already started"); + goto bail; + } + if (!pSelf->bPrepared) { + DD_DEBUG_ERROR("Producer not prepared"); + DD_CHECK_HR(hr = E_UNEXPECTED); + } + + DD_CHECK_HR(hr = HookWindow(pSelf, pSelf->hwndPreview)); + + // Start asynchronous watcher thread + pSelf->bStarted = true; + int ret = tsk_thread_create(&pSelf->ppTread[0], DDThread, pSelf); + if (ret != 0) { + TSK_DEBUG_ERROR("Failed to create thread"); + pSelf->bStarted = false; + if (pSelf->ppTread[0]) { + tsk_thread_join(&pSelf->ppTread[0]); + } + DD_CHECK_HR(hr = E_UNEXPECTED); + } bail: - if (FAILED(hr)) - { - UnhookWindow(pSelf); - return -1; - } - pSelf->bStarted = true; - return 0; + if (FAILED(hr)) { + UnhookWindow(pSelf); + return -1; + } + pSelf->bStarted = true; + return 0; } static int plugin_win_dd_producer_pause(tmedia_producer_t* self) { - plugin_win_dd_producer_t* pSelf = (plugin_win_dd_producer_t*)self; - - if (!pSelf) - { - DD_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if (!pSelf->bStarted) - { - DD_DEBUG_INFO("MF video producer not started"); - } - - return 0; + plugin_win_dd_producer_t* pSelf = (plugin_win_dd_producer_t*)self; + + if (!pSelf) { + DD_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (!pSelf->bStarted) { + DD_DEBUG_INFO("MF video producer not started"); + } + + return 0; } static int plugin_win_dd_producer_stop(tmedia_producer_t* self) { - plugin_win_dd_producer_t* pSelf = (plugin_win_dd_producer_t*)self; - - if (!pSelf) - { - DD_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - pSelf->bStarted = false; - - UnhookWindow(pSelf); - - if (pSelf->hlTerminateThreadsEvent) - { - SetEvent(pSelf->hlTerminateThreadsEvent); - } - if (pSelf->ppTread[0]) - { - tsk_thread_join(&pSelf->ppTread[0]); - } - - // next start() will be called after prepare() - int ret = _plugin_win_dd_producer_unprepare(pSelf); - - return ret; + plugin_win_dd_producer_t* pSelf = (plugin_win_dd_producer_t*)self; + + if (!pSelf) { + DD_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + pSelf->bStarted = false; + + UnhookWindow(pSelf); + + if (pSelf->hlTerminateThreadsEvent) { + SetEvent(pSelf->hlTerminateThreadsEvent); + } + if (pSelf->ppTread[0]) { + tsk_thread_join(&pSelf->ppTread[0]); + } + + // next start() will be called after prepare() + int ret = _plugin_win_dd_producer_unprepare(pSelf); + + return ret; } static int _plugin_win_dd_producer_unprepare(plugin_win_dd_producer_t* pSelf, bool bCleanup /*= false*/) { - HRESULT hr = S_OK; - - if (!pSelf) - { - DD_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if (pSelf->bStarted) - { - DD_CHECK_HR(hr = E_UNEXPECTED); - } - - pSelf->bThreadTerminationDelayed = false; - - // Thread manager must be destroyed before the events and output manager - if (pSelf->pThreadMgr) - { - // if we are cleaning the producer then all threads must exit only when all threads are destroyed - // https://code.google.com/p/sincity/issues/detail?id=7 - if (pSelf->pThreadMgr->WaitForThreadTermination(bCleanup ? INFINITE : DD_DDPROC_THREAD_TIMEOUT) == true) - { - delete pSelf->pThreadMgr; - pSelf->pThreadMgr = nullptr; - } - else - { - // Thread wait timedout - DD_DEBUG_WARN("DDProc thread termination delayed"); - pSelf->bThreadTerminationDelayed = true; - } - } - - if (!pSelf->bThreadTerminationDelayed) - { - if (pSelf->hlUnexpectedErrorEvent) - { - CloseHandle(pSelf->hlUnexpectedErrorEvent); - pSelf->hlUnexpectedErrorEvent = nullptr; - } - if (pSelf->hlExpectedErrorEvent) - { - CloseHandle(pSelf->hlExpectedErrorEvent); - pSelf->hlExpectedErrorEvent = nullptr; - } - if (pSelf->hlOcclutionEvent) - { - CloseHandle(pSelf->hlOcclutionEvent); - pSelf->hlOcclutionEvent = nullptr; - } - if (pSelf->hlTerminateThreadsEvent) - { - CloseHandle(pSelf->hlTerminateThreadsEvent); - pSelf->hlTerminateThreadsEvent = nullptr; - } - - if (pSelf->hcCursor) - { - DestroyCursor(pSelf->hcCursor); - pSelf->hcCursor = nullptr; - } - - if (pSelf->pOutMgr) - { - delete pSelf->pOutMgr; - pSelf->pOutMgr = nullptr; - } - } - - pSelf->bPrepared = false; + HRESULT hr = S_OK; + + if (!pSelf) { + DD_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (pSelf->bStarted) { + DD_CHECK_HR(hr = E_UNEXPECTED); + } + + pSelf->bThreadTerminationDelayed = false; + + // Thread manager must be destroyed before the events and output manager + if (pSelf->pThreadMgr) { + // if we are cleaning the producer then all threads must exit only when all threads are destroyed + // https://code.google.com/p/sincity/issues/detail?id=7 + if (pSelf->pThreadMgr->WaitForThreadTermination(bCleanup ? INFINITE : DD_DDPROC_THREAD_TIMEOUT) == true) { + delete pSelf->pThreadMgr; + pSelf->pThreadMgr = nullptr; + } + else { + // Thread wait timedout + DD_DEBUG_WARN("DDProc thread termination delayed"); + pSelf->bThreadTerminationDelayed = true; + } + } + + if (!pSelf->bThreadTerminationDelayed) { + if (pSelf->hlUnexpectedErrorEvent) { + CloseHandle(pSelf->hlUnexpectedErrorEvent); + pSelf->hlUnexpectedErrorEvent = nullptr; + } + if (pSelf->hlExpectedErrorEvent) { + CloseHandle(pSelf->hlExpectedErrorEvent); + pSelf->hlExpectedErrorEvent = nullptr; + } + if (pSelf->hlOcclutionEvent) { + CloseHandle(pSelf->hlOcclutionEvent); + pSelf->hlOcclutionEvent = nullptr; + } + if (pSelf->hlTerminateThreadsEvent) { + CloseHandle(pSelf->hlTerminateThreadsEvent); + pSelf->hlTerminateThreadsEvent = nullptr; + } + + if (pSelf->hcCursor) { + DestroyCursor(pSelf->hcCursor); + pSelf->hcCursor = nullptr; + } + + if (pSelf->pOutMgr) { + delete pSelf->pOutMgr; + pSelf->pOutMgr = nullptr; + } + } + + pSelf->bPrepared = false; bail: - return 0; + return 0; } static HRESULT HookWindow(struct plugin_win_dd_producer_s *pSelf, HWND hWnd) { - HRESULT hr = S_OK; - - DD_CHECK_HR(hr = UnhookWindow(pSelf)); - - if ((pSelf->hwndPreview = hWnd)) - { - pSelf->wndPreviewProc = (WNDPROC)SetWindowLongPtr(pSelf->hwndPreview, GWLP_WNDPROC, (LONG_PTR)WndProc); - if (!pSelf->wndPreviewProc) - { - DD_DEBUG_ERROR("HookWindowLongPtr() failed with errcode=%d", GetLastError()); - DD_CHECK_HR(hr = E_FAIL); - } - SetProp(pSelf->hwndPreview, L"Self", pSelf); - pSelf->bWindowHooked = true; - } + HRESULT hr = S_OK; + + DD_CHECK_HR(hr = UnhookWindow(pSelf)); + + if ((pSelf->hwndPreview = hWnd)) { + pSelf->wndPreviewProc = (WNDPROC)SetWindowLongPtr(pSelf->hwndPreview, GWLP_WNDPROC, (LONG_PTR)WndProc); + if (!pSelf->wndPreviewProc) { + DD_DEBUG_ERROR("HookWindowLongPtr() failed with errcode=%d", GetLastError()); + DD_CHECK_HR(hr = E_FAIL); + } + SetProp(pSelf->hwndPreview, L"Self", pSelf); + pSelf->bWindowHooked = true; + } bail: - return S_OK; + return S_OK; } static HRESULT UnhookWindow(struct plugin_win_dd_producer_s *pSelf) { - if (pSelf->hwndPreview && pSelf->wndPreviewProc) - { - SetWindowLongPtr(pSelf->hwndPreview, GWLP_WNDPROC, (LONG_PTR)pSelf->wndPreviewProc); - pSelf->wndPreviewProc = NULL; - } - if (pSelf->hwndPreview) - { - ::InvalidateRect(pSelf->hwndPreview, NULL, FALSE); - } - pSelf->bWindowHooked = false; - return S_OK; + if (pSelf->hwndPreview && pSelf->wndPreviewProc) { + SetWindowLongPtr(pSelf->hwndPreview, GWLP_WNDPROC, (LONG_PTR)pSelf->wndPreviewProc); + pSelf->wndPreviewProc = NULL; + } + if (pSelf->hwndPreview) { + ::InvalidateRect(pSelf->hwndPreview, NULL, FALSE); + } + pSelf->bWindowHooked = false; + return S_OK; } // @@ -471,66 +425,61 @@ static HRESULT UnhookWindow(struct plugin_win_dd_producer_s *pSelf) /* constructor */ static tsk_object_t* plugin_win_dd_producer_ctor(tsk_object_t * self, va_list * app) { - plugin_win_dd_producer_t *pSelf = (plugin_win_dd_producer_t *)self; - if (pSelf) - { - /* init base */ - tmedia_producer_init(TMEDIA_PRODUCER(pSelf)); - - /* init self with default values*/ - TMEDIA_PRODUCER(pSelf)->encoder.codec_id = tmedia_codec_id_none; // means RAW frames as input - TMEDIA_PRODUCER(pSelf)->video.chroma = tmedia_chroma_rgb32; - TMEDIA_PRODUCER(pSelf)->video.fps = 15; - TMEDIA_PRODUCER(pSelf)->video.width = 352; - TMEDIA_PRODUCER(pSelf)->video.height = 288; - - DD_DEBUG_INFO("Create Microsoft Desktop Duplication producer"); - } - return self; + plugin_win_dd_producer_t *pSelf = (plugin_win_dd_producer_t *)self; + if (pSelf) { + /* init base */ + tmedia_producer_init(TMEDIA_PRODUCER(pSelf)); + + /* init self with default values*/ + TMEDIA_PRODUCER(pSelf)->encoder.codec_id = tmedia_codec_id_none; // means RAW frames as input + TMEDIA_PRODUCER(pSelf)->video.chroma = tmedia_chroma_rgb32; + TMEDIA_PRODUCER(pSelf)->video.fps = 15; + TMEDIA_PRODUCER(pSelf)->video.width = 352; + TMEDIA_PRODUCER(pSelf)->video.height = 288; + + DD_DEBUG_INFO("Create Microsoft Desktop Duplication producer"); + } + return self; } /* destructor */ static tsk_object_t* plugin_win_dd_producer_dtor(tsk_object_t * self) { - plugin_win_dd_producer_t *pSelf = (plugin_win_dd_producer_t *)self; - if (pSelf) - { - /* stop */ - if (pSelf->bStarted) - { - plugin_win_dd_producer_stop(TMEDIA_PRODUCER(pSelf)); - } - - /* deinit base */ - tmedia_producer_deinit(TMEDIA_PRODUCER(pSelf)); - /* deinit self */ - _plugin_win_dd_producer_unprepare(pSelf, true/*cleanup*/); - - DD_DEBUG_INFO("*** WinDD producer destroyed ***"); - } - - return self; + plugin_win_dd_producer_t *pSelf = (plugin_win_dd_producer_t *)self; + if (pSelf) { + /* stop */ + if (pSelf->bStarted) { + plugin_win_dd_producer_stop(TMEDIA_PRODUCER(pSelf)); + } + + /* deinit base */ + tmedia_producer_deinit(TMEDIA_PRODUCER(pSelf)); + /* deinit self */ + _plugin_win_dd_producer_unprepare(pSelf, true/*cleanup*/); + + DD_DEBUG_INFO("*** WinDD producer destroyed ***"); + } + + return self; } /* object definition */ -static const tsk_object_def_t plugin_win_dd_producer_def_s = -{ - sizeof(plugin_win_dd_producer_t), - plugin_win_dd_producer_ctor, - plugin_win_dd_producer_dtor, - tsk_null, +static const tsk_object_def_t plugin_win_dd_producer_def_s = { + sizeof(plugin_win_dd_producer_t), + plugin_win_dd_producer_ctor, + plugin_win_dd_producer_dtor, + tsk_null, }; /* plugin definition*/ -static const tmedia_producer_plugin_def_t plugin_win_dd_producer_plugin_def_s = -{ - &plugin_win_dd_producer_def_s, +static const tmedia_producer_plugin_def_t plugin_win_dd_producer_plugin_def_s = { + &plugin_win_dd_producer_def_s, - tmedia_bfcp_video, - "Microsoft Windows Desktop Duplication producer (Video)", + tmedia_bfcp_video, + "Microsoft Windows Desktop Duplication producer (Video)", - plugin_win_dd_producer_set, - plugin_win_dd_producer_prepare, - plugin_win_dd_producer_start, - plugin_win_dd_producer_pause, - plugin_win_dd_producer_stop + plugin_win_dd_producer_set, + plugin_win_dd_producer_prepare, + plugin_win_dd_producer_start, + plugin_win_dd_producer_pause, + plugin_win_dd_producer_stop }; const tmedia_producer_plugin_def_t *plugin_win_dd_producer_plugin_def_t = &plugin_win_dd_producer_plugin_def_s; @@ -545,130 +494,115 @@ const tmedia_producer_plugin_def_t *plugin_win_dd_producer_plugin_def_t = &plugi // These are the errors we expect from general Dxgi API due to a transition HRESULT SystemTransitionsExpectedErrors[] = { - DXGI_ERROR_DEVICE_REMOVED, - DXGI_ERROR_ACCESS_LOST, - static_cast<HRESULT>(WAIT_ABANDONED), - S_OK // Terminate list with zero valued HRESULT + DXGI_ERROR_DEVICE_REMOVED, + DXGI_ERROR_ACCESS_LOST, + static_cast<HRESULT>(WAIT_ABANDONED), + S_OK // Terminate list with zero valued HRESULT }; // These are the errors we expect from IDXGIOutput1::DuplicateOutput due to a transition HRESULT CreateDuplicationExpectedErrors[] = { - DXGI_ERROR_DEVICE_REMOVED, - static_cast<HRESULT>(E_ACCESSDENIED), - DXGI_ERROR_UNSUPPORTED, - DXGI_ERROR_SESSION_DISCONNECTED, - S_OK // Terminate list with zero valued HRESULT + DXGI_ERROR_DEVICE_REMOVED, + static_cast<HRESULT>(E_ACCESSDENIED), + DXGI_ERROR_UNSUPPORTED, + DXGI_ERROR_SESSION_DISCONNECTED, + S_OK // Terminate list with zero valued HRESULT }; // These are the errors we expect from IDXGIOutputDuplication methods due to a transition HRESULT FrameInfoExpectedErrors[] = { - DXGI_ERROR_DEVICE_REMOVED, - DXGI_ERROR_ACCESS_LOST, - S_OK // Terminate list with zero valued HRESULT + DXGI_ERROR_DEVICE_REMOVED, + DXGI_ERROR_ACCESS_LOST, + S_OK // Terminate list with zero valued HRESULT }; // These are the errors we expect from IDXGIAdapter::EnumOutputs methods due to outputs becoming stale during a transition HRESULT EnumOutputsExpectedErrors[] = { - DXGI_ERROR_NOT_FOUND, - S_OK // Terminate list with zero valued HRESULT + DXGI_ERROR_NOT_FOUND, + S_OK // Terminate list with zero valued HRESULT }; _Post_satisfies_(return != DUPL_RETURN_SUCCESS) - DUPL_RETURN ProcessFailure(_In_opt_ ID3D11Device* Device, _In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr, _In_opt_z_ HRESULT* ExpectedErrors) +DUPL_RETURN ProcessFailure(_In_opt_ ID3D11Device* Device, _In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr, _In_opt_z_ HRESULT* ExpectedErrors) { - HRESULT TranslatedHr; - - // On an error check if the DX device is lost - if (Device) - { - HRESULT DeviceRemovedReason = Device->GetDeviceRemovedReason(); - - switch (DeviceRemovedReason) - { - case DXGI_ERROR_DEVICE_REMOVED: - case DXGI_ERROR_DEVICE_RESET: - case static_cast<HRESULT>(E_OUTOFMEMORY) : - { - // Our device has been stopped due to an external event on the GPU so map them all to - // device removed and continue processing the condition - TranslatedHr = DXGI_ERROR_DEVICE_REMOVED; - break; - } - - case S_OK: - { - // Device is not removed so use original error - TranslatedHr = hr; - break; - } - - default: - { - // Device is removed but not a error we want to remap - TranslatedHr = DeviceRemovedReason; - } - } - } - else - { - TranslatedHr = hr; - } - - // Check if this error was expected or not - if (ExpectedErrors) - { - HRESULT* CurrentResult = ExpectedErrors; - - while (*CurrentResult != S_OK) - { - if (*(CurrentResult++) == TranslatedHr) - { - return DUPL_RETURN_ERROR_EXPECTED; - } - } - } - - // Error was not expected so display the message box - DisplayMsg(Str, Title, TranslatedHr); - - return DUPL_RETURN_ERROR_UNEXPECTED; + HRESULT TranslatedHr; + + // On an error check if the DX device is lost + if (Device) { + HRESULT DeviceRemovedReason = Device->GetDeviceRemovedReason(); + + switch (DeviceRemovedReason) { + case DXGI_ERROR_DEVICE_REMOVED: + case DXGI_ERROR_DEVICE_RESET: + case static_cast<HRESULT>(E_OUTOFMEMORY) : { + // Our device has been stopped due to an external event on the GPU so map them all to + // device removed and continue processing the condition + TranslatedHr = DXGI_ERROR_DEVICE_REMOVED; + break; + } + + case S_OK: { + // Device is not removed so use original error + TranslatedHr = hr; + break; + } + + default: { + // Device is removed but not a error we want to remap + TranslatedHr = DeviceRemovedReason; + } + } + } + else { + TranslatedHr = hr; + } + + // Check if this error was expected or not + if (ExpectedErrors) { + HRESULT* CurrentResult = ExpectedErrors; + + while (*CurrentResult != S_OK) { + if (*(CurrentResult++) == TranslatedHr) { + return DUPL_RETURN_ERROR_EXPECTED; + } + } + } + + // Error was not expected so display the message box + DisplayMsg(Str, Title, TranslatedHr); + + return DUPL_RETURN_ERROR_UNEXPECTED; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - switch (message) - { - case WM_DESTROY: - { - PostQuitMessage(0); - break; - } - case WM_SIZE: - { - // Tell output manager that window size has changed - plugin_win_dd_producer_t* pSelf = static_cast<plugin_win_dd_producer_t*>(GetProp(hWnd, L"Self")); - if (pSelf && pSelf->pOutMgr) - { - pSelf->pOutMgr->WindowResize(); - } - break; - } - case OCCLUSION_STATUS_MSG: - { - plugin_win_dd_producer_t* pSelf = static_cast<plugin_win_dd_producer_t*>(GetProp(hWnd, L"Self")); - if (pSelf && pSelf->hlOcclutionEvent) - { - SetEvent(pSelf->hlOcclutionEvent); - } - break; - } - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - - return 0; + switch (message) { + case WM_DESTROY: { + PostQuitMessage(0); + break; + } + case WM_SIZE: { + // Tell output manager that window size has changed + plugin_win_dd_producer_t* pSelf = static_cast<plugin_win_dd_producer_t*>(GetProp(hWnd, L"Self")); + if (pSelf && pSelf->pOutMgr) { + pSelf->pOutMgr->WindowResize(); + } + break; + } + case OCCLUSION_STATUS_MSG: { + plugin_win_dd_producer_t* pSelf = static_cast<plugin_win_dd_producer_t*>(GetProp(hWnd, L"Self")); + if (pSelf && pSelf->hlOcclutionEvent) { + SetEvent(pSelf->hlOcclutionEvent); + } + break; + } + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return 0; } // @@ -676,399 +610,357 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // DWORD WINAPI DDProc(_In_ void* Param) { - DD_DEBUG_INFO("DDProc (producer) - ENTER"); - - // Classes - DISPLAYMANAGER DispMgr; - DUPLICATIONMANAGER DuplMgr; - - // D3D objects - ID3D11Texture2D* SharedSurf = nullptr; - IDXGIKeyedMutex* KeyMutex = nullptr; - - // Data passed in from thread creation - THREAD_DATA* TData = reinterpret_cast<THREAD_DATA*>(Param); - - // Get desktop - DUPL_RETURN Ret; - HDESK CurrentDesktop = nullptr; - CurrentDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL); - if (!CurrentDesktop) - { - // We do not have access to the desktop so request a retry - SetEvent(TData->ExpectedErrorEvent); - Ret = DUPL_RETURN_ERROR_EXPECTED; - goto Exit; - } - - // Attach desktop to this thread - bool DesktopAttached = SetThreadDesktop(CurrentDesktop) != 0; - CloseDesktop(CurrentDesktop); - CurrentDesktop = nullptr; - if (!DesktopAttached) - { - // We do not have access to the desktop so request a retry - Ret = DUPL_RETURN_ERROR_EXPECTED; - goto Exit; - } - - // New display manager - DispMgr.InitD3D(&TData->DxRes); - - // FPS manager - uint64_t TimeNow, TimeLastFrame = 0; - const uint64_t TimeFrameDuration = 1000 / TData->Producer->video.fps; - - // Obtain handle to sync shared Surface - HRESULT hr = TData->DxRes.Device->OpenSharedResource(TData->TexSharedHandle, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&SharedSurf)); - if (FAILED(hr)) - { - Ret = ProcessFailure(TData->DxRes.Device, L"Opening shared texture failed", L"Error", hr, SystemTransitionsExpectedErrors); - goto Exit; - } - - hr = SharedSurf->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&KeyMutex)); - if (FAILED(hr)) - { - Ret = ProcessFailure(nullptr, L"Failed to get keyed mutex interface in spawned thread", L"Error", hr); - goto Exit; - } - - // Make duplication manager - Ret = DuplMgr.InitDupl(TData->DxRes.Device, TData->DxRes.Context ,TData->Output); - if (Ret != DUPL_RETURN_SUCCESS) - { - goto Exit; - } - - // Get output description - DXGI_OUTPUT_DESC DesktopDesc; - RtlZeroMemory(&DesktopDesc, sizeof(DXGI_OUTPUT_DESC)); - DuplMgr.GetOutputDesc(&DesktopDesc); - - // Main duplication loop - bool WaitToProcessCurrentFrame = false; - FRAME_DATA CurrentData; - - while (TData->Producer->is_started && (WaitForSingleObjectEx(TData->TerminateThreadsEvent, 0, FALSE) == WAIT_TIMEOUT)) - { - if (!WaitToProcessCurrentFrame) - { - // Get new frame from desktop duplication - bool TimeOut; - Ret = DuplMgr.GetFrame(&CurrentData, &TimeOut); - if (Ret != DUPL_RETURN_SUCCESS) - { - // An error occurred getting the next frame drop out of loop which - // will check if it was expected or not - break; - } - - // Check for timeout - if (TimeOut) - { - // No new frame at the moment - continue; - } - } - - // We have a new frame so try and process it - // Try to acquire keyed mutex in order to access shared surface - hr = KeyMutex->AcquireSync(0, 1000); - if (hr == static_cast<HRESULT>(WAIT_TIMEOUT)) - { - // Can't use shared surface right now, try again later - WaitToProcessCurrentFrame = true; - continue; - } - else if (FAILED(hr)) - { - // Generic unknown failure - Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error acquiring KeyMutex", L"Error", hr, SystemTransitionsExpectedErrors); - DuplMgr.DoneWithFrame(); - break; - } - - // We can now process the current frame - WaitToProcessCurrentFrame = false; - - // Get mouse info - Ret = DuplMgr.GetMouse(TData->PtrInfo, &(CurrentData.FrameInfo), TData->OffsetX, TData->OffsetY); - if (Ret != DUPL_RETURN_SUCCESS) - { - DuplMgr.DoneWithFrame(); - KeyMutex->ReleaseSync(1); - break; - } - - // Process new frame - Ret = DispMgr.ProcessFrame(&CurrentData, SharedSurf, TData->OffsetX, TData->OffsetY, &DesktopDesc); - if (Ret != DUPL_RETURN_SUCCESS) - { - DuplMgr.DoneWithFrame(); - KeyMutex->ReleaseSync(1); - break; - } - - // Release acquired keyed mutex - hr = KeyMutex->ReleaseSync(1); - if (FAILED(hr)) - { - Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error releasing the keyed mutex", L"Error", hr, SystemTransitionsExpectedErrors); - DuplMgr.DoneWithFrame(); - break; - } - - // Send Frame Over the Network - TimeNow = tsk_time_now(); - if ((TimeNow - TimeLastFrame) > TimeFrameDuration) - { - if (!((const plugin_win_dd_producer_t*)TData->Producer)->bMuted) - { - hr = DuplMgr.SendData(const_cast<struct tmedia_producer_s*>(TData->Producer), &CurrentData); - } - if (SUCCEEDED(hr)) - { - TimeLastFrame = TimeNow; - } - } + DD_DEBUG_INFO("DDProc (producer) - ENTER"); + + // Classes + DISPLAYMANAGER DispMgr; + DUPLICATIONMANAGER DuplMgr; + + // D3D objects + ID3D11Texture2D* SharedSurf = nullptr; + IDXGIKeyedMutex* KeyMutex = nullptr; + + // Data passed in from thread creation + THREAD_DATA* TData = reinterpret_cast<THREAD_DATA*>(Param); + + // Get desktop + DUPL_RETURN Ret; + HDESK CurrentDesktop = nullptr; + CurrentDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL); + if (!CurrentDesktop) { + // We do not have access to the desktop so request a retry + SetEvent(TData->ExpectedErrorEvent); + Ret = DUPL_RETURN_ERROR_EXPECTED; + goto Exit; + } + + // Attach desktop to this thread + bool DesktopAttached = SetThreadDesktop(CurrentDesktop) != 0; + CloseDesktop(CurrentDesktop); + CurrentDesktop = nullptr; + if (!DesktopAttached) { + // We do not have access to the desktop so request a retry + Ret = DUPL_RETURN_ERROR_EXPECTED; + goto Exit; + } + + // New display manager + DispMgr.InitD3D(&TData->DxRes); + + // FPS manager + uint64_t TimeNow, TimeLastFrame = 0; + const uint64_t TimeFrameDuration = 1000 / TData->Producer->video.fps; + + // Obtain handle to sync shared Surface + HRESULT hr = TData->DxRes.Device->OpenSharedResource(TData->TexSharedHandle, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&SharedSurf)); + if (FAILED(hr)) { + Ret = ProcessFailure(TData->DxRes.Device, L"Opening shared texture failed", L"Error", hr, SystemTransitionsExpectedErrors); + goto Exit; + } + + hr = SharedSurf->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&KeyMutex)); + if (FAILED(hr)) { + Ret = ProcessFailure(nullptr, L"Failed to get keyed mutex interface in spawned thread", L"Error", hr); + goto Exit; + } + + // Make duplication manager + Ret = DuplMgr.InitDupl(TData->DxRes.Device, TData->DxRes.Context ,TData->Output); + if (Ret != DUPL_RETURN_SUCCESS) { + goto Exit; + } + + // Get output description + DXGI_OUTPUT_DESC DesktopDesc; + RtlZeroMemory(&DesktopDesc, sizeof(DXGI_OUTPUT_DESC)); + DuplMgr.GetOutputDesc(&DesktopDesc); + + // Main duplication loop + bool WaitToProcessCurrentFrame = false; + FRAME_DATA CurrentData; + + while (TData->Producer->is_started && (WaitForSingleObjectEx(TData->TerminateThreadsEvent, 0, FALSE) == WAIT_TIMEOUT)) { + if (!WaitToProcessCurrentFrame) { + // Get new frame from desktop duplication + bool TimeOut; + Ret = DuplMgr.GetFrame(&CurrentData, &TimeOut); + if (Ret != DUPL_RETURN_SUCCESS) { + // An error occurred getting the next frame drop out of loop which + // will check if it was expected or not + break; + } + + // Check for timeout + if (TimeOut) { + // No new frame at the moment + continue; + } + } + + // We have a new frame so try and process it + // Try to acquire keyed mutex in order to access shared surface + hr = KeyMutex->AcquireSync(0, 1000); + if (hr == static_cast<HRESULT>(WAIT_TIMEOUT)) { + // Can't use shared surface right now, try again later + WaitToProcessCurrentFrame = true; + continue; + } + else if (FAILED(hr)) { + // Generic unknown failure + Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error acquiring KeyMutex", L"Error", hr, SystemTransitionsExpectedErrors); + DuplMgr.DoneWithFrame(); + break; + } + + // We can now process the current frame + WaitToProcessCurrentFrame = false; + + // Get mouse info + Ret = DuplMgr.GetMouse(TData->PtrInfo, &(CurrentData.FrameInfo), TData->OffsetX, TData->OffsetY); + if (Ret != DUPL_RETURN_SUCCESS) { + DuplMgr.DoneWithFrame(); + KeyMutex->ReleaseSync(1); + break; + } + + // Process new frame + Ret = DispMgr.ProcessFrame(&CurrentData, SharedSurf, TData->OffsetX, TData->OffsetY, &DesktopDesc); + if (Ret != DUPL_RETURN_SUCCESS) { + DuplMgr.DoneWithFrame(); + KeyMutex->ReleaseSync(1); + break; + } + + // Release acquired keyed mutex + hr = KeyMutex->ReleaseSync(1); + if (FAILED(hr)) { + Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error releasing the keyed mutex", L"Error", hr, SystemTransitionsExpectedErrors); + DuplMgr.DoneWithFrame(); + break; + } + + // Send Frame Over the Network + TimeNow = tsk_time_now(); + if ((TimeNow - TimeLastFrame) > TimeFrameDuration) { + if (!((const plugin_win_dd_producer_t*)TData->Producer)->bMuted) { + hr = DuplMgr.SendData(const_cast<struct tmedia_producer_s*>(TData->Producer), &CurrentData); + } + if (SUCCEEDED(hr)) { + TimeLastFrame = TimeNow; + } + } #if 0 - else - { - DD_DEBUG_INFO("Skip frame"); - } + else { + DD_DEBUG_INFO("Skip frame"); + } #endif - // Release frame back to desktop duplication - Ret = DuplMgr.DoneWithFrame(); - if (Ret != DUPL_RETURN_SUCCESS) - { - break; - } - } + // Release frame back to desktop duplication + Ret = DuplMgr.DoneWithFrame(); + if (Ret != DUPL_RETURN_SUCCESS) { + break; + } + } Exit: - if (Ret != DUPL_RETURN_SUCCESS) - { - if (Ret == DUPL_RETURN_ERROR_EXPECTED) - { - // The system is in a transition state so request the duplication be restarted - SetEvent(TData->ExpectedErrorEvent); - } - else - { - // Unexpected error so exit the application - SetEvent(TData->UnexpectedErrorEvent); - } - } - - if (SharedSurf) - { - SharedSurf->Release(); - SharedSurf = nullptr; - } - - if (KeyMutex) - { - KeyMutex->Release(); - KeyMutex = nullptr; - } - - DD_DEBUG_INFO("DDProc (producer) - EXIT"); - - return 0; + if (Ret != DUPL_RETURN_SUCCESS) { + if (Ret == DUPL_RETURN_ERROR_EXPECTED) { + // The system is in a transition state so request the duplication be restarted + SetEvent(TData->ExpectedErrorEvent); + } + else { + // Unexpected error so exit the application + SetEvent(TData->UnexpectedErrorEvent); + } + } + + if (SharedSurf) { + SharedSurf->Release(); + SharedSurf = nullptr; + } + + if (KeyMutex) { + KeyMutex->Release(); + KeyMutex = nullptr; + } + + DD_DEBUG_INFO("DDProc (producer) - EXIT"); + + return 0; } // Run session async thread static void* TSK_STDCALL DDThread(void *pArg) { - plugin_win_dd_producer_t *pSelf = (plugin_win_dd_producer_t *)pArg; - HRESULT hr = S_OK; - INT SingleOutput = -1; - - RECT DeskBounds = {}; - UINT OutputCount = 1; - - bool FirstTime = true; - bool Occluded = true; - bool PreviewChanged = false; - DYNAMIC_WAIT DynamicWait; - HWND hwndPreview = NULL; - - DD_DEBUG_INFO("DDThread (producer) - ENTER"); - - while (pSelf->bStarted) - { - DUPL_RETURN Ret = DUPL_RETURN_SUCCESS; - - // Check if Preview window changed - PreviewChanged = (hwndPreview != pSelf->hwndPreview); - - if (WaitForSingleObjectEx(pSelf->hlOcclutionEvent, 0, FALSE) == WAIT_OBJECT_0) - { - Occluded = false; - } - if (WaitForSingleObjectEx(pSelf->hlUnexpectedErrorEvent, 0, FALSE) == WAIT_OBJECT_0) - { - // Unexpected error occurred so exit the application - DD_CHECK_HR(hr = E_UNEXPECTED); - } - else if (FirstTime || PreviewChanged || WaitForSingleObjectEx(pSelf->hlExpectedErrorEvent, 0, FALSE) == WAIT_OBJECT_0) - { - if (PreviewChanged) - { - hwndPreview = pSelf->hwndPreview; - } - - if (!FirstTime) - { - // Terminate other threads - SetEvent(pSelf->hlTerminateThreadsEvent); - pSelf->pThreadMgr->WaitForThreadTermination(); - ResetEvent(pSelf->hlTerminateThreadsEvent); - ResetEvent(pSelf->hlExpectedErrorEvent); - - // Clean up - pSelf->pThreadMgr->Clean(); - pSelf->pOutMgr->CleanRefs(); - - // As we have encountered an error due to a system transition we wait before trying again, using this dynamic wait - // the wait periods will get progressively long to avoid wasting too much system resource if this state lasts a long time - DynamicWait.Wait(); - } - else - { - // First time through the loop so nothing to clean up - FirstTime = false; - } - - // Re-initialize - Ret = pSelf->pOutMgr->InitOutput(hwndPreview, SingleOutput, &OutputCount, &DeskBounds); - if (Ret == DUPL_RETURN_SUCCESS) - { - HANDLE SharedHandle = pSelf->pOutMgr->GetSharedHandle(); - if (SharedHandle) - { - Ret = pSelf->pThreadMgr->Initialize(SingleOutput, OutputCount, pSelf->hlUnexpectedErrorEvent, pSelf->hlExpectedErrorEvent, pSelf->hlTerminateThreadsEvent, SharedHandle, TMEDIA_PRODUCER(pSelf), &DeskBounds); - } - else - { - DisplayMsg(L"Failed to get handle of shared surface", L"Error", S_OK); - Ret = DUPL_RETURN_ERROR_UNEXPECTED; - } - } - - - // We start off in occluded state and we should immediate get a occlusion status window message - Occluded = true; - } - else - { - // Nothing else to do, so try to present to write out to window if not occluded - if (!Occluded || !pSelf->bWindowHooked) - { - Ret = pSelf->pOutMgr->UpdateApplicationWindow(pSelf->pThreadMgr->GetPointerInfo(), &Occluded); - } - } - - // Check if for errors - if (Ret != DUPL_RETURN_SUCCESS) - { - if (Ret == DUPL_RETURN_ERROR_EXPECTED) - { - // Some type of system transition is occurring so retry - SetEvent(pSelf->hlExpectedErrorEvent); - } - else - { - // Unexpected error so exit - DD_CHECK_HR(hr = E_UNEXPECTED); - break; - } - } - } + plugin_win_dd_producer_t *pSelf = (plugin_win_dd_producer_t *)pArg; + HRESULT hr = S_OK; + INT SingleOutput = -1; + + RECT DeskBounds = {}; + UINT OutputCount = 1; + + bool FirstTime = true; + bool Occluded = true; + bool PreviewChanged = false; + DYNAMIC_WAIT DynamicWait; + HWND hwndPreview = NULL; + + DD_DEBUG_INFO("DDThread (producer) - ENTER"); + + while (pSelf->bStarted) { + DUPL_RETURN Ret = DUPL_RETURN_SUCCESS; + + // Check if Preview window changed + PreviewChanged = (hwndPreview != pSelf->hwndPreview); + + if (WaitForSingleObjectEx(pSelf->hlOcclutionEvent, 0, FALSE) == WAIT_OBJECT_0) { + Occluded = false; + } + if (WaitForSingleObjectEx(pSelf->hlUnexpectedErrorEvent, 0, FALSE) == WAIT_OBJECT_0) { + // Unexpected error occurred so exit the application + DD_CHECK_HR(hr = E_UNEXPECTED); + } + else if (FirstTime || PreviewChanged || WaitForSingleObjectEx(pSelf->hlExpectedErrorEvent, 0, FALSE) == WAIT_OBJECT_0) { + if (PreviewChanged) { + hwndPreview = pSelf->hwndPreview; + } + + if (!FirstTime) { + // Terminate other threads + SetEvent(pSelf->hlTerminateThreadsEvent); + pSelf->pThreadMgr->WaitForThreadTermination(); + ResetEvent(pSelf->hlTerminateThreadsEvent); + ResetEvent(pSelf->hlExpectedErrorEvent); + + // Clean up + pSelf->pThreadMgr->Clean(); + pSelf->pOutMgr->CleanRefs(); + + // As we have encountered an error due to a system transition we wait before trying again, using this dynamic wait + // the wait periods will get progressively long to avoid wasting too much system resource if this state lasts a long time + DynamicWait.Wait(); + } + else { + // First time through the loop so nothing to clean up + FirstTime = false; + } + + // Re-initialize + Ret = pSelf->pOutMgr->InitOutput(hwndPreview, SingleOutput, &OutputCount, &DeskBounds); + if (Ret == DUPL_RETURN_SUCCESS) { + HANDLE SharedHandle = pSelf->pOutMgr->GetSharedHandle(); + if (SharedHandle) { + Ret = pSelf->pThreadMgr->Initialize(SingleOutput, OutputCount, pSelf->hlUnexpectedErrorEvent, pSelf->hlExpectedErrorEvent, pSelf->hlTerminateThreadsEvent, SharedHandle, TMEDIA_PRODUCER(pSelf), &DeskBounds); + } + else { + DisplayMsg(L"Failed to get handle of shared surface", L"Error", S_OK); + Ret = DUPL_RETURN_ERROR_UNEXPECTED; + } + } + + + // We start off in occluded state and we should immediate get a occlusion status window message + Occluded = true; + } + else { + // Nothing else to do, so try to present to write out to window if not occluded + if (!Occluded || !pSelf->bWindowHooked) { + Ret = pSelf->pOutMgr->UpdateApplicationWindow(pSelf->pThreadMgr->GetPointerInfo(), &Occluded); + } + } + + // Check if for errors + if (Ret != DUPL_RETURN_SUCCESS) { + if (Ret == DUPL_RETURN_ERROR_EXPECTED) { + // Some type of system transition is occurring so retry + SetEvent(pSelf->hlExpectedErrorEvent); + } + else { + // Unexpected error so exit + DD_CHECK_HR(hr = E_UNEXPECTED); + break; + } + } + } bail: - - DD_DEBUG_INFO("DDThread (producer) - BAIL"); + + DD_DEBUG_INFO("DDThread (producer) - BAIL"); #if 0 // Done by unprepare() - // Make sure all other threads have exited - if (SetEvent(pSelf->hlTerminateThreadsEvent)) - { - ThreadMgr.WaitForThreadTermination(); - } - - // Clean up - CloseHandle(pSelf->hlUnexpectedErrorEvent); pSelf->hlUnexpectedErrorEvent = NULL; - CloseHandle(pSelf->hlExpectedErrorEvent); pSelf->hlExpectedErrorEvent = NULL; - CloseHandle(pSelf->hlTerminateThreadsEvent); pSelf->hlTerminateThreadsEvent = NULL; + // Make sure all other threads have exited + if (SetEvent(pSelf->hlTerminateThreadsEvent)) { + ThreadMgr.WaitForThreadTermination(); + } + + // Clean up + CloseHandle(pSelf->hlUnexpectedErrorEvent); + pSelf->hlUnexpectedErrorEvent = NULL; + CloseHandle(pSelf->hlExpectedErrorEvent); + pSelf->hlExpectedErrorEvent = NULL; + CloseHandle(pSelf->hlTerminateThreadsEvent); + pSelf->hlTerminateThreadsEvent = NULL; #endif - DD_DEBUG_INFO("DDThread (producer) - EXIT"); + DD_DEBUG_INFO("DDThread (producer) - EXIT"); - return NULL; + return NULL; } // // Displays a message // - void DisplayMsg(_In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr) +void DisplayMsg(_In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr) { - const UINT StringLen = (UINT)(wcslen(Str) + sizeof(" with HRESULT 0x########.")); - wchar_t* OutStr = new wchar_t[StringLen]; - if (!OutStr) - { - return; - } - - INT LenWritten = swprintf_s(OutStr, StringLen, L"%s with 0x%X.", Str, hr); - if (LenWritten != -1) - { - DD_DEBUG_ERROR("%ls: %ls", Title, OutStr); - } - - delete[] OutStr; + const UINT StringLen = (UINT)(wcslen(Str) + sizeof(" with HRESULT 0x########.")); + wchar_t* OutStr = new wchar_t[StringLen]; + if (!OutStr) { + return; + } + + INT LenWritten = swprintf_s(OutStr, StringLen, L"%s with 0x%X.", Str, hr); + if (LenWritten != -1) { + DD_DEBUG_ERROR("%ls: %ls", Title, OutStr); + } + + delete[] OutStr; } - DYNAMIC_WAIT::DYNAMIC_WAIT() : m_CurrentWaitBandIdx(0), m_WaitCountInCurrentBand(0) - { - m_QPCValid = QueryPerformanceFrequency(&m_QPCFrequency); - m_LastWakeUpTime.QuadPart = 0L; - } - - DYNAMIC_WAIT::~DYNAMIC_WAIT() - { - } - - void DYNAMIC_WAIT::Wait() - { - LARGE_INTEGER CurrentQPC = { 0 }; - - // Is this wait being called with the period that we consider it to be part of the same wait sequence - QueryPerformanceCounter(&CurrentQPC); - if (m_QPCValid && (CurrentQPC.QuadPart <= (m_LastWakeUpTime.QuadPart + (m_QPCFrequency.QuadPart * m_WaitSequenceTimeInSeconds)))) - { - // We are still in the same wait sequence, lets check if we should move to the next band - if ((m_WaitBands[m_CurrentWaitBandIdx].WaitCount != WAIT_BAND_STOP) && (m_WaitCountInCurrentBand > m_WaitBands[m_CurrentWaitBandIdx].WaitCount)) - { - m_CurrentWaitBandIdx++; - m_WaitCountInCurrentBand = 0; - } - } - else - { - // Either we could not get the current time or we are starting a new wait sequence - m_WaitCountInCurrentBand = 0; - m_CurrentWaitBandIdx = 0; - } - - // Sleep for the required period of time - Sleep(m_WaitBands[m_CurrentWaitBandIdx].WaitTime); - - // Record the time we woke up so we can detect wait sequences - QueryPerformanceCounter(&m_LastWakeUpTime); - m_WaitCountInCurrentBand++; - }
\ No newline at end of file +DYNAMIC_WAIT::DYNAMIC_WAIT() : m_CurrentWaitBandIdx(0), m_WaitCountInCurrentBand(0) +{ + m_QPCValid = QueryPerformanceFrequency(&m_QPCFrequency); + m_LastWakeUpTime.QuadPart = 0L; +} + +DYNAMIC_WAIT::~DYNAMIC_WAIT() +{ +} + +void DYNAMIC_WAIT::Wait() +{ + LARGE_INTEGER CurrentQPC = { 0 }; + + // Is this wait being called with the period that we consider it to be part of the same wait sequence + QueryPerformanceCounter(&CurrentQPC); + if (m_QPCValid && (CurrentQPC.QuadPart <= (m_LastWakeUpTime.QuadPart + (m_QPCFrequency.QuadPart * m_WaitSequenceTimeInSeconds)))) { + // We are still in the same wait sequence, lets check if we should move to the next band + if ((m_WaitBands[m_CurrentWaitBandIdx].WaitCount != WAIT_BAND_STOP) && (m_WaitCountInCurrentBand > m_WaitBands[m_CurrentWaitBandIdx].WaitCount)) { + m_CurrentWaitBandIdx++; + m_WaitCountInCurrentBand = 0; + } + } + else { + // Either we could not get the current time or we are starting a new wait sequence + m_WaitCountInCurrentBand = 0; + m_CurrentWaitBandIdx = 0; + } + + // Sleep for the required period of time + Sleep(m_WaitBands[m_CurrentWaitBandIdx].WaitTime); + + // Record the time we woke up so we can detect wait sequences + QueryPerformanceCounter(&m_LastWakeUpTime); + m_WaitCountInCurrentBand++; +}
\ No newline at end of file |