blob: 59474ed064fd665de4163cad14034db9e851bde7 [file] [log] [blame]
Alexandre Lision67916dd2014-01-24 13:33:04 -05001//------------------------------------------------------------------------------
2// File: WXUtil.cpp
3//
4// Desc: DirectShow base classes - implements helper classes for building
5// multimedia filters.
6//
7// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
8//------------------------------------------------------------------------------
9
10#include <pjmedia-videodev/config.h>
11
12#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
13
14#include <streams.h>
15#define STRSAFE_NO_DEPRECATE
16#include <strsafe.h>
17
18
19// --- CAMEvent -----------------------
20CAMEvent::CAMEvent(BOOL fManualReset, __inout_opt HRESULT *phr)
21{
22 m_hEvent = CreateEvent(NULL, fManualReset, FALSE, NULL);
23 if (NULL == m_hEvent) {
24 if (NULL != phr && SUCCEEDED(*phr)) {
25 *phr = E_OUTOFMEMORY;
26 }
27 }
28}
29
30CAMEvent::CAMEvent(__inout_opt HRESULT *phr)
31{
32 m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
33 if (NULL == m_hEvent) {
34 if (NULL != phr && SUCCEEDED(*phr)) {
35 *phr = E_OUTOFMEMORY;
36 }
37 }
38}
39
40CAMEvent::~CAMEvent()
41{
42 if (m_hEvent) {
43 EXECUTE_ASSERT(CloseHandle(m_hEvent));
44 }
45}
46
47
48// --- CAMMsgEvent -----------------------
49// One routine. The rest is handled in CAMEvent
50
51CAMMsgEvent::CAMMsgEvent(__inout_opt HRESULT *phr) : CAMEvent(FALSE, phr)
52{
53}
54
55BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout)
56{
57 // wait for the event to be signalled, or for the
58 // timeout (in MS) to expire. allow SENT messages
59 // to be processed while we wait
60 DWORD dwWait;
61 DWORD dwStartTime = 0;
62
63 // set the waiting period.
64 DWORD dwWaitTime = dwTimeout;
65
66 // the timeout will eventually run down as we iterate
67 // processing messages. grab the start time so that
68 // we can calculate elapsed times.
69 if (dwWaitTime != INFINITE) {
70 dwStartTime = timeGetTime();
71 }
72
73 do {
74 dwWait = MsgWaitForMultipleObjects(1,&m_hEvent,FALSE, dwWaitTime, QS_SENDMESSAGE);
75 if (dwWait == WAIT_OBJECT_0 + 1) {
76 MSG Message;
77 PeekMessage(&Message,NULL,0,0,PM_NOREMOVE);
78
79 // If we have an explicit length of time to wait calculate
80 // the next wake up point - which might be now.
81 // If dwTimeout is INFINITE, it stays INFINITE
82 if (dwWaitTime != INFINITE) {
83
84 DWORD dwElapsed = timeGetTime()-dwStartTime;
85
86 dwWaitTime =
87 (dwElapsed >= dwTimeout)
88 ? 0 // wake up with WAIT_TIMEOUT
89 : dwTimeout-dwElapsed;
90 }
91 }
92 } while (dwWait == WAIT_OBJECT_0 + 1);
93
94 // return TRUE if we woke on the event handle,
95 // FALSE if we timed out.
96 return (dwWait == WAIT_OBJECT_0);
97}
98
99// --- CAMThread ----------------------
100
101
102CAMThread::CAMThread(__inout_opt HRESULT *phr)
103 : m_EventSend(TRUE, phr), // must be manual-reset for CheckRequest()
104 m_EventComplete(FALSE, phr)
105{
106 m_hThread = NULL;
107}
108
109CAMThread::~CAMThread() {
110 Close();
111}
112
113
114// when the thread starts, it calls this function. We unwrap the 'this'
115//pointer and call ThreadProc.
116DWORD WINAPI
117CAMThread::InitialThreadProc(__inout LPVOID pv)
118{
119 HRESULT hrCoInit = CAMThread::CoInitializeHelper();
120 if(FAILED(hrCoInit)) {
121 DbgLog((LOG_ERROR, 1, TEXT("CoInitializeEx failed.")));
122 }
123
124 CAMThread * pThread = (CAMThread *) pv;
125
126 HRESULT hr = pThread->ThreadProc();
127
128 if(SUCCEEDED(hrCoInit)) {
129 CoUninitialize();
130 }
131
132 return hr;
133}
134
135BOOL
136CAMThread::Create()
137{
138 DWORD threadid;
139
140 CAutoLock lock(&m_AccessLock);
141
142 if (ThreadExists()) {
143 return FALSE;
144 }
145
146 m_hThread = CreateThread(
147 NULL,
148 0,
149 CAMThread::InitialThreadProc,
150 this,
151 0,
152 &threadid);
153
154 if (!m_hThread) {
155 return FALSE;
156 }
157
158 return TRUE;
159}
160
161DWORD
162CAMThread::CallWorker(DWORD dwParam)
163{
164 // lock access to the worker thread for scope of this object
165 CAutoLock lock(&m_AccessLock);
166
167 if (!ThreadExists()) {
168 return (DWORD) E_FAIL;
169 }
170
171 // set the parameter
172 m_dwParam = dwParam;
173
174 // signal the worker thread
175 m_EventSend.Set();
176
177 // wait for the completion to be signalled
178 m_EventComplete.Wait();
179
180 // done - this is the thread's return value
181 return m_dwReturnVal;
182}
183
184// Wait for a request from the client
185DWORD
186CAMThread::GetRequest()
187{
188 m_EventSend.Wait();
189 return m_dwParam;
190}
191
192// is there a request?
193BOOL
194CAMThread::CheckRequest(__out_opt DWORD * pParam)
195{
196 if (!m_EventSend.Check()) {
197 return FALSE;
198 } else {
199 if (pParam) {
200 *pParam = m_dwParam;
201 }
202 return TRUE;
203 }
204}
205
206// reply to the request
207void
208CAMThread::Reply(DWORD dw)
209{
210 m_dwReturnVal = dw;
211
212 // The request is now complete so CheckRequest should fail from
213 // now on
214 //
215 // This event should be reset BEFORE we signal the client or
216 // the client may Set it before we reset it and we'll then
217 // reset it (!)
218
219 m_EventSend.Reset();
220
221 // Tell the client we're finished
222
223 m_EventComplete.Set();
224}
225
226HRESULT CAMThread::CoInitializeHelper()
227{
228 // call CoInitializeEx and tell OLE not to create a window (this
229 // thread probably won't dispatch messages and will hang on
230 // broadcast msgs o/w).
231 //
232 // If CoInitEx is not available, threads that don't call CoCreate
233 // aren't affected. Threads that do will have to handle the
234 // failure. Perhaps we should fall back to CoInitialize and risk
235 // hanging?
236 //
237
238 // older versions of ole32.dll don't have CoInitializeEx
239
240 HRESULT hr = E_FAIL;
241 HINSTANCE hOle = GetModuleHandle(TEXT("ole32.dll"));
242 if(hOle)
243 {
244 typedef HRESULT (STDAPICALLTYPE *PCoInitializeEx)(
245 LPVOID pvReserved, DWORD dwCoInit);
246 PCoInitializeEx pCoInitializeEx =
247 (PCoInitializeEx)(GetProcAddress(hOle, "CoInitializeEx"));
248 if(pCoInitializeEx)
249 {
250 hr = (*pCoInitializeEx)(0, COINIT_DISABLE_OLE1DDE );
251 }
252 }
253 else
254 {
255 // caller must load ole32.dll
256 DbgBreak("couldn't locate ole32.dll");
257 }
258
259 return hr;
260}
261
262
263// destructor for CMsgThread - cleans up any messages left in the
264// queue when the thread exited
265CMsgThread::~CMsgThread()
266{
267 if (m_hThread != NULL) {
268 WaitForSingleObject(m_hThread, INFINITE);
269 EXECUTE_ASSERT(CloseHandle(m_hThread));
270 }
271
272 POSITION pos = m_ThreadQueue.GetHeadPosition();
273 while (pos) {
274 CMsg * pMsg = m_ThreadQueue.GetNext(pos);
275 delete pMsg;
276 }
277 m_ThreadQueue.RemoveAll();
278
279 if (m_hSem != NULL) {
280 EXECUTE_ASSERT(CloseHandle(m_hSem));
281 }
282}
283
284BOOL
285CMsgThread::CreateThread(
286 )
287{
288 m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
289 if (m_hSem == NULL) {
290 return FALSE;
291 }
292
293 m_hThread = ::CreateThread(NULL, 0, DefaultThreadProc,
294 (LPVOID)this, 0, &m_ThreadId);
295 return m_hThread != NULL;
296}
297
298
299// This is the threads message pump. Here we get and dispatch messages to
300// clients thread proc until the client refuses to process a message.
301// The client returns a non-zero value to stop the message pump, this
302// value becomes the threads exit code.
303
304DWORD WINAPI
305CMsgThread::DefaultThreadProc(
306 __inout LPVOID lpParam
307 )
308{
309 CMsgThread *lpThis = (CMsgThread *)lpParam;
310 CMsg msg;
311 LRESULT lResult;
312
313 // !!!
314 CoInitialize(NULL);
315
316 // allow a derived class to handle thread startup
317 lpThis->OnThreadInit();
318
319 do {
320 lpThis->GetThreadMsg(&msg);
321 lResult = lpThis->ThreadMessageProc(msg.uMsg,msg.dwFlags,
322 msg.lpParam, msg.pEvent);
323 } while (lResult == 0L);
324
325 // !!!
326 CoUninitialize();
327
328 return (DWORD)lResult;
329}
330
331
332// Block until the next message is placed on the list m_ThreadQueue.
333// copies the message to the message pointed to by *pmsg
334void
335CMsgThread::GetThreadMsg(__out CMsg *msg)
336{
337 CMsg * pmsg = NULL;
338
339 // keep trying until a message appears
340 while (TRUE) {
341 {
342 CAutoLock lck(&m_Lock);
343 pmsg = m_ThreadQueue.RemoveHead();
344 if (pmsg == NULL) {
345 m_lWaiting++;
346 } else {
347 break;
348 }
349 }
350 // the semaphore will be signalled when it is non-empty
351 WaitForSingleObject(m_hSem, INFINITE);
352 }
353 // copy fields to caller's CMsg
354 *msg = *pmsg;
355
356 // this CMsg was allocated by the 'new' in PutThreadMsg
357 delete pmsg;
358
359}
360
361// Helper function - convert int to WSTR
362void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr)
363{
364#ifdef UNICODE
365 if (FAILED(StringCchPrintf(wstr, 12, L"%d", i))) {
366 wstr[0] = 0;
367 }
368#else
369 TCHAR temp[12];
370 if (FAILED(StringCchPrintf(temp, NUMELMS(temp), "%d", i))) {
371 wstr[0] = 0;
372 } else {
373 MultiByteToWideChar(CP_ACP, 0, temp, -1, wstr, 12);
374 }
375#endif
376} // IntToWstr
377
378
379#define MEMORY_ALIGNMENT 4
380#define MEMORY_ALIGNMENT_LOG2 2
381#define MEMORY_ALIGNMENT_MASK MEMORY_ALIGNMENT - 1
382
383void * __stdcall memmoveInternal(void * dst, const void * src, size_t count)
384{
385 void * ret = dst;
386
387#ifdef _X86_
388 if (dst <= src || (char *)dst >= ((char *)src + count)) {
389
390 /*
391 * Non-Overlapping Buffers
392 * copy from lower addresses to higher addresses
393 */
394 _asm {
395 mov esi,src
396 mov edi,dst
397 mov ecx,count
398 cld
399 mov edx,ecx
400 and edx,MEMORY_ALIGNMENT_MASK
401 shr ecx,MEMORY_ALIGNMENT_LOG2
402 rep movsd
403 or ecx,edx
404 jz memmove_done
405 rep movsb
406memmove_done:
407 }
408 }
409 else {
410
411 /*
412 * Overlapping Buffers
413 * copy from higher addresses to lower addresses
414 */
415 _asm {
416 mov esi,src
417 mov edi,dst
418 mov ecx,count
419 std
420 add esi,ecx
421 add edi,ecx
422 dec esi
423 dec edi
424 rep movsb
425 cld
426 }
427 }
428#else
429 MoveMemory(dst, src, count);
430#endif
431
432 return ret;
433}
434
435HRESULT AMSafeMemMoveOffset(
436 __in_bcount(dst_size) void * dst,
437 __in size_t dst_size,
438 __in DWORD cb_dst_offset,
439 __in_bcount(src_size) const void * src,
440 __in size_t src_size,
441 __in DWORD cb_src_offset,
442 __in size_t count)
443{
444 // prevent read overruns
445 if( count + cb_src_offset < count || // prevent integer overflow
446 count + cb_src_offset > src_size) // prevent read overrun
447 {
448 return E_INVALIDARG;
449 }
450
451 // prevent write overruns
452 if( count + cb_dst_offset < count || // prevent integer overflow
453 count + cb_dst_offset > dst_size) // prevent write overrun
454 {
455 return E_INVALIDARG;
456 }
457
458 memmoveInternal( (BYTE *)dst+cb_dst_offset, (BYTE *)src+cb_src_offset, count);
459 return S_OK;
460}
461
462
463#ifdef DEBUG
464/******************************Public*Routine******************************\
465* Debug CCritSec helpers
466*
467* We provide debug versions of the Constructor, destructor, Lock and Unlock
468* routines. The debug code tracks who owns each critical section by
469* maintaining a depth count.
470*
471* History:
472*
473\**************************************************************************/
474
475CCritSec::CCritSec()
476{
477 InitializeCriticalSection(&m_CritSec);
478 m_currentOwner = m_lockCount = 0;
479 m_fTrace = FALSE;
480}
481
482CCritSec::~CCritSec()
483{
484 DeleteCriticalSection(&m_CritSec);
485}
486
487void CCritSec::Lock()
488{
489 UINT tracelevel=3;
490 DWORD us = GetCurrentThreadId();
491 DWORD currentOwner = m_currentOwner;
492 if (currentOwner && (currentOwner != us)) {
493 // already owned, but not by us
494 if (m_fTrace) {
495 DbgLog((LOG_LOCKING, 2, TEXT("Thread %d about to wait for lock %x owned by %d"),
496 GetCurrentThreadId(), &m_CritSec, currentOwner));
497 tracelevel=2;
498 // if we saw the message about waiting for the critical
499 // section we ensure we see the message when we get the
500 // critical section
501 }
502 }
503 EnterCriticalSection(&m_CritSec);
504 if (0 == m_lockCount++) {
505 // we now own it for the first time. Set owner information
506 m_currentOwner = us;
507
508 if (m_fTrace) {
509 DbgLog((LOG_LOCKING, tracelevel, TEXT("Thread %d now owns lock %x"), m_currentOwner, &m_CritSec));
510 }
511 }
512}
513
514void CCritSec::Unlock() {
515 if (0 == --m_lockCount) {
516 // about to be unowned
517 if (m_fTrace) {
518 DbgLog((LOG_LOCKING, 3, TEXT("Thread %d releasing lock %x"), m_currentOwner, &m_CritSec));
519 }
520
521 m_currentOwner = 0;
522 }
523 LeaveCriticalSection(&m_CritSec);
524}
525
526void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace)
527{
528 pcCrit->m_fTrace = fTrace;
529}
530
531BOOL WINAPI CritCheckIn(CCritSec * pcCrit)
532{
533 return (GetCurrentThreadId() == pcCrit->m_currentOwner);
534}
535
536BOOL WINAPI CritCheckIn(const CCritSec * pcCrit)
537{
538 return (GetCurrentThreadId() == pcCrit->m_currentOwner);
539}
540
541BOOL WINAPI CritCheckOut(CCritSec * pcCrit)
542{
543 return (GetCurrentThreadId() != pcCrit->m_currentOwner);
544}
545
546BOOL WINAPI CritCheckOut(const CCritSec * pcCrit)
547{
548 return (GetCurrentThreadId() != pcCrit->m_currentOwner);
549}
550#endif
551
552
553STDAPI WriteBSTR(__deref_out BSTR *pstrDest, LPCWSTR szSrc)
554{
555 *pstrDest = SysAllocString( szSrc );
556 if( !(*pstrDest) ) return E_OUTOFMEMORY;
557 return NOERROR;
558}
559
560
561STDAPI FreeBSTR(__deref_in BSTR* pstr)
562{
563 if( (PVOID)*pstr == NULL ) return S_FALSE;
564 SysFreeString( *pstr );
565 return NOERROR;
566}
567
568
569// Return a wide string - allocating memory for it
570// Returns:
571// S_OK - no error
572// E_POINTER - ppszReturn == NULL
573// E_OUTOFMEMORY - can't allocate memory for returned string
574STDAPI AMGetWideString(LPCWSTR psz, __deref_out LPWSTR *ppszReturn)
575{
576 CheckPointer(ppszReturn, E_POINTER);
577 ValidateReadWritePtr(ppszReturn, sizeof(LPWSTR));
578 *ppszReturn = NULL;
579 size_t nameLen;
580 HRESULT hr = StringCbLengthW(psz, 100000, &nameLen);
581 if (FAILED(hr)) {
582 return hr;
583 }
584 *ppszReturn = (LPWSTR)CoTaskMemAlloc(nameLen + sizeof(WCHAR));
585 if (*ppszReturn == NULL) {
586 return E_OUTOFMEMORY;
587 }
588 CopyMemory(*ppszReturn, psz, nameLen + sizeof(WCHAR));
589 return NOERROR;
590}
591
592// Waits for the HANDLE hObject. While waiting messages sent
593// to windows on our thread by SendMessage will be processed.
594// Using this function to do waits and mutual exclusion
595// avoids some deadlocks in objects with windows.
596// Return codes are the same as for WaitForSingleObject
597DWORD WINAPI WaitDispatchingMessages(
598 HANDLE hObject,
599 DWORD dwWait,
600 HWND hwnd,
601 UINT uMsg,
602 HANDLE hEvent)
603{
604 BOOL bPeeked = FALSE;
605 DWORD dwResult;
606 DWORD dwStart = 0;
607 DWORD dwThreadPriority = THREAD_PRIORITY_HIGHEST;
608
609 static UINT uMsgId = 0;
610
611 HANDLE hObjects[2] = { hObject, hEvent };
612 if (dwWait != INFINITE && dwWait != 0) {
613 dwStart = GetTickCount();
614 }
615 for (; ; ) {
616 DWORD nCount = NULL != hEvent ? 2 : 1;
617
618 // Minimize the chance of actually dispatching any messages
619 // by seeing if we can lock immediately.
620 dwResult = WaitForMultipleObjects(nCount, hObjects, FALSE, 0);
621 if (dwResult < WAIT_OBJECT_0 + nCount) {
622 break;
623 }
624
625 DWORD dwTimeOut = dwWait;
626 if (dwTimeOut > 10) {
627 dwTimeOut = 10;
628 }
629 dwResult = MsgWaitForMultipleObjects(
630 nCount,
631 hObjects,
632 FALSE,
633 dwTimeOut,
634 hwnd == NULL ? QS_SENDMESSAGE :
635 QS_SENDMESSAGE + QS_POSTMESSAGE);
636 if (dwResult == WAIT_OBJECT_0 + nCount ||
637 dwResult == WAIT_TIMEOUT && dwTimeOut != dwWait) {
638 MSG msg;
639 if (hwnd != NULL) {
640 while (PeekMessage(&msg, hwnd, uMsg, uMsg, PM_REMOVE)) {
641 DispatchMessage(&msg);
642 }
643 }
644 // Do this anyway - the previous peek doesn't flush out the
645 // messages
646 PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
647
648 if (dwWait != INFINITE && dwWait != 0) {
649 DWORD dwNow = GetTickCount();
650
651 // Working with differences handles wrap-around
652 DWORD dwDiff = dwNow - dwStart;
653 if (dwDiff > dwWait) {
654 dwWait = 0;
655 } else {
656 dwWait -= dwDiff;
657 }
658 dwStart = dwNow;
659 }
660 if (!bPeeked) {
661 // Raise our priority to prevent our message queue
662 // building up
663 dwThreadPriority = GetThreadPriority(GetCurrentThread());
664 if (dwThreadPriority < THREAD_PRIORITY_HIGHEST) {
665 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
666 }
667 bPeeked = TRUE;
668 }
669 } else {
670 break;
671 }
672 }
673 if (bPeeked) {
674 SetThreadPriority(GetCurrentThread(), dwThreadPriority);
675 if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {
676 if (uMsgId == 0) {
677 uMsgId = RegisterWindowMessage(TEXT("AMUnblock"));
678 }
679 if (uMsgId != 0) {
680 MSG msg;
681 // Remove old ones
682 while (PeekMessage(&msg, (HWND)-1, uMsgId, uMsgId, PM_REMOVE)) {
683 }
684 }
685 PostThreadMessage(GetCurrentThreadId(), uMsgId, 0, 0);
686 }
687 }
688 return dwResult;
689}
690
691HRESULT AmGetLastErrorToHResult()
692{
693 DWORD dwLastError = GetLastError();
694 if(dwLastError != 0)
695 {
696 return HRESULT_FROM_WIN32(dwLastError);
697 }
698 else
699 {
700 return E_FAIL;
701 }
702}
703
704IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp)
705{
706 if (lp != NULL)
707 lp->AddRef();
708 if (*pp)
709 (*pp)->Release();
710 *pp = lp;
711 return lp;
712}
713
714/******************************************************************************
715
716CompatibleTimeSetEvent
717
718 CompatibleTimeSetEvent() sets the TIME_KILL_SYNCHRONOUS flag before calling
719timeSetEvent() if the current operating system supports it. TIME_KILL_SYNCHRONOUS
720is supported on Windows XP and later operating systems.
721
722Parameters:
723- The same parameters as timeSetEvent(). See timeSetEvent()'s documentation in
724the Platform SDK for more information.
725
726Return Value:
727- The same return value as timeSetEvent(). See timeSetEvent()'s documentation in
728the Platform SDK for more information.
729
730******************************************************************************/
731MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent )
732{
733 #if WINVER >= 0x0501
734 {
735 static bool fCheckedVersion = false;
736 static bool fTimeKillSynchronousFlagAvailable = false;
737
738 if( !fCheckedVersion ) {
739 fTimeKillSynchronousFlagAvailable = TimeKillSynchronousFlagAvailable();
740 fCheckedVersion = true;
741 }
742
743 if( fTimeKillSynchronousFlagAvailable ) {
744 fuEvent = fuEvent | TIME_KILL_SYNCHRONOUS;
745 }
746 }
747 #endif // WINVER >= 0x0501
748
749 return timeSetEvent( uDelay, uResolution, lpTimeProc, dwUser, fuEvent );
750}
751
752bool TimeKillSynchronousFlagAvailable( void )
753{
754 OSVERSIONINFO osverinfo;
755
756 osverinfo.dwOSVersionInfoSize = sizeof(osverinfo);
757
758 if( GetVersionEx( &osverinfo ) ) {
759
760 // Windows XP's major version is 5 and its' minor version is 1.
761 // timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag
762 // in Windows XP.
763 if( (osverinfo.dwMajorVersion > 5) ||
764 ( (osverinfo.dwMajorVersion == 5) && (osverinfo.dwMinorVersion >= 1) ) ) {
765 return true;
766 }
767 }
768
769 return false;
770}
771
772
773#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */