1
//------------------------------------------------------------------------------
4
// Desc: DirectShow base classes - implements helper classes for building
7
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
8
//------------------------------------------------------------------------------
10
#include <pjmedia-videodev/config.h>
12
#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
15
#define STRSAFE_NO_DEPRECATE
19
// --- CAMEvent -----------------------
20
CAMEvent::CAMEvent(BOOL fManualReset, __inout_opt HRESULT *phr)
22
m_hEvent = CreateEvent(NULL, fManualReset, FALSE, NULL);
23
if (NULL == m_hEvent) {
24
if (NULL != phr && SUCCEEDED(*phr)) {
30
CAMEvent::CAMEvent(__inout_opt HRESULT *phr)
32
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
33
if (NULL == m_hEvent) {
34
if (NULL != phr && SUCCEEDED(*phr)) {
43
EXECUTE_ASSERT(CloseHandle(m_hEvent));
48
// --- CAMMsgEvent -----------------------
49
// One routine. The rest is handled in CAMEvent
51
CAMMsgEvent::CAMMsgEvent(__inout_opt HRESULT *phr) : CAMEvent(FALSE, phr)
55
BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout)
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
61
DWORD dwStartTime = 0;
63
// set the waiting period.
64
DWORD dwWaitTime = dwTimeout;
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();
74
dwWait = MsgWaitForMultipleObjects(1,&m_hEvent,FALSE, dwWaitTime, QS_SENDMESSAGE);
75
if (dwWait == WAIT_OBJECT_0 + 1) {
77
PeekMessage(&Message,NULL,0,0,PM_NOREMOVE);
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) {
84
DWORD dwElapsed = timeGetTime()-dwStartTime;
87
(dwElapsed >= dwTimeout)
88
? 0 // wake up with WAIT_TIMEOUT
89
: dwTimeout-dwElapsed;
92
} while (dwWait == WAIT_OBJECT_0 + 1);
94
// return TRUE if we woke on the event handle,
95
// FALSE if we timed out.
96
return (dwWait == WAIT_OBJECT_0);
99
// --- CAMThread ----------------------
102
CAMThread::CAMThread(__inout_opt HRESULT *phr)
103
: m_EventSend(TRUE, phr), // must be manual-reset for CheckRequest()
104
m_EventComplete(FALSE, phr)
109
CAMThread::~CAMThread() {
114
// when the thread starts, it calls this function. We unwrap the 'this'
115
//pointer and call ThreadProc.
117
CAMThread::InitialThreadProc(__inout LPVOID pv)
119
HRESULT hrCoInit = CAMThread::CoInitializeHelper();
120
if(FAILED(hrCoInit)) {
121
DbgLog((LOG_ERROR, 1, TEXT("CoInitializeEx failed.")));
124
CAMThread * pThread = (CAMThread *) pv;
126
HRESULT hr = pThread->ThreadProc();
128
if(SUCCEEDED(hrCoInit)) {
140
CAutoLock lock(&m_AccessLock);
142
if (ThreadExists()) {
146
m_hThread = CreateThread(
149
CAMThread::InitialThreadProc,
162
CAMThread::CallWorker(DWORD dwParam)
164
// lock access to the worker thread for scope of this object
165
CAutoLock lock(&m_AccessLock);
167
if (!ThreadExists()) {
168
return (DWORD) E_FAIL;
174
// signal the worker thread
177
// wait for the completion to be signalled
178
m_EventComplete.Wait();
180
// done - this is the thread's return value
181
return m_dwReturnVal;
184
// Wait for a request from the client
186
CAMThread::GetRequest()
192
// is there a request?
194
CAMThread::CheckRequest(__out_opt DWORD * pParam)
196
if (!m_EventSend.Check()) {
206
// reply to the request
208
CAMThread::Reply(DWORD dw)
212
// The request is now complete so CheckRequest should fail from
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
221
// Tell the client we're finished
223
m_EventComplete.Set();
226
HRESULT CAMThread::CoInitializeHelper()
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).
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
238
// older versions of ole32.dll don't have CoInitializeEx
241
HINSTANCE hOle = GetModuleHandle(TEXT("ole32.dll"));
244
typedef HRESULT (STDAPICALLTYPE *PCoInitializeEx)(
245
LPVOID pvReserved, DWORD dwCoInit);
246
PCoInitializeEx pCoInitializeEx =
247
(PCoInitializeEx)(GetProcAddress(hOle, "CoInitializeEx"));
250
hr = (*pCoInitializeEx)(0, COINIT_DISABLE_OLE1DDE );
255
// caller must load ole32.dll
256
DbgBreak("couldn't locate ole32.dll");
263
// destructor for CMsgThread - cleans up any messages left in the
264
// queue when the thread exited
265
CMsgThread::~CMsgThread()
267
if (m_hThread != NULL) {
268
WaitForSingleObject(m_hThread, INFINITE);
269
EXECUTE_ASSERT(CloseHandle(m_hThread));
272
POSITION pos = m_ThreadQueue.GetHeadPosition();
274
CMsg * pMsg = m_ThreadQueue.GetNext(pos);
277
m_ThreadQueue.RemoveAll();
279
if (m_hSem != NULL) {
280
EXECUTE_ASSERT(CloseHandle(m_hSem));
285
CMsgThread::CreateThread(
288
m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
289
if (m_hSem == NULL) {
293
m_hThread = ::CreateThread(NULL, 0, DefaultThreadProc,
294
(LPVOID)this, 0, &m_ThreadId);
295
return m_hThread != NULL;
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.
305
CMsgThread::DefaultThreadProc(
306
__inout LPVOID lpParam
309
CMsgThread *lpThis = (CMsgThread *)lpParam;
316
// allow a derived class to handle thread startup
317
lpThis->OnThreadInit();
320
lpThis->GetThreadMsg(&msg);
321
lResult = lpThis->ThreadMessageProc(msg.uMsg,msg.dwFlags,
322
msg.lpParam, msg.pEvent);
323
} while (lResult == 0L);
328
return (DWORD)lResult;
332
// Block until the next message is placed on the list m_ThreadQueue.
333
// copies the message to the message pointed to by *pmsg
335
CMsgThread::GetThreadMsg(__out CMsg *msg)
339
// keep trying until a message appears
342
CAutoLock lck(&m_Lock);
343
pmsg = m_ThreadQueue.RemoveHead();
350
// the semaphore will be signalled when it is non-empty
351
WaitForSingleObject(m_hSem, INFINITE);
353
// copy fields to caller's CMsg
356
// this CMsg was allocated by the 'new' in PutThreadMsg
361
// Helper function - convert int to WSTR
362
void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr)
365
if (FAILED(StringCchPrintf(wstr, 12, L"%d", i))) {
370
if (FAILED(StringCchPrintf(temp, NUMELMS(temp), "%d", i))) {
373
MultiByteToWideChar(CP_ACP, 0, temp, -1, wstr, 12);
379
#define MEMORY_ALIGNMENT 4
380
#define MEMORY_ALIGNMENT_LOG2 2
381
#define MEMORY_ALIGNMENT_MASK MEMORY_ALIGNMENT - 1
383
void * __stdcall memmoveInternal(void * dst, const void * src, size_t count)
388
if (dst <= src || (char *)dst >= ((char *)src + count)) {
391
* Non-Overlapping Buffers
392
* copy from lower addresses to higher addresses
400
and edx,MEMORY_ALIGNMENT_MASK
401
shr ecx,MEMORY_ALIGNMENT_LOG2
412
* Overlapping Buffers
413
* copy from higher addresses to lower addresses
429
MoveMemory(dst, src, count);
435
HRESULT 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,
444
// prevent read overruns
445
if( count + cb_src_offset < count || // prevent integer overflow
446
count + cb_src_offset > src_size) // prevent read overrun
451
// prevent write overruns
452
if( count + cb_dst_offset < count || // prevent integer overflow
453
count + cb_dst_offset > dst_size) // prevent write overrun
458
memmoveInternal( (BYTE *)dst+cb_dst_offset, (BYTE *)src+cb_src_offset, count);
464
/******************************Public*Routine******************************\
465
* Debug CCritSec helpers
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.
473
\**************************************************************************/
477
InitializeCriticalSection(&m_CritSec);
478
m_currentOwner = m_lockCount = 0;
482
CCritSec::~CCritSec()
484
DeleteCriticalSection(&m_CritSec);
487
void CCritSec::Lock()
490
DWORD us = GetCurrentThreadId();
491
DWORD currentOwner = m_currentOwner;
492
if (currentOwner && (currentOwner != us)) {
493
// already owned, but not by us
495
DbgLog((LOG_LOCKING, 2, TEXT("Thread %d about to wait for lock %x owned by %d"),
496
GetCurrentThreadId(), &m_CritSec, currentOwner));
498
// if we saw the message about waiting for the critical
499
// section we ensure we see the message when we get the
503
EnterCriticalSection(&m_CritSec);
504
if (0 == m_lockCount++) {
505
// we now own it for the first time. Set owner information
509
DbgLog((LOG_LOCKING, tracelevel, TEXT("Thread %d now owns lock %x"), m_currentOwner, &m_CritSec));
514
void CCritSec::Unlock() {
515
if (0 == --m_lockCount) {
516
// about to be unowned
518
DbgLog((LOG_LOCKING, 3, TEXT("Thread %d releasing lock %x"), m_currentOwner, &m_CritSec));
523
LeaveCriticalSection(&m_CritSec);
526
void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace)
528
pcCrit->m_fTrace = fTrace;
531
BOOL WINAPI CritCheckIn(CCritSec * pcCrit)
533
return (GetCurrentThreadId() == pcCrit->m_currentOwner);
536
BOOL WINAPI CritCheckIn(const CCritSec * pcCrit)
538
return (GetCurrentThreadId() == pcCrit->m_currentOwner);
541
BOOL WINAPI CritCheckOut(CCritSec * pcCrit)
543
return (GetCurrentThreadId() != pcCrit->m_currentOwner);
546
BOOL WINAPI CritCheckOut(const CCritSec * pcCrit)
548
return (GetCurrentThreadId() != pcCrit->m_currentOwner);
553
STDAPI WriteBSTR(__deref_out BSTR *pstrDest, LPCWSTR szSrc)
555
*pstrDest = SysAllocString( szSrc );
556
if( !(*pstrDest) ) return E_OUTOFMEMORY;
561
STDAPI FreeBSTR(__deref_in BSTR* pstr)
563
if( (PVOID)*pstr == NULL ) return S_FALSE;
564
SysFreeString( *pstr );
569
// Return a wide string - allocating memory for it
572
// E_POINTER - ppszReturn == NULL
573
// E_OUTOFMEMORY - can't allocate memory for returned string
574
STDAPI AMGetWideString(LPCWSTR psz, __deref_out LPWSTR *ppszReturn)
576
CheckPointer(ppszReturn, E_POINTER);
577
ValidateReadWritePtr(ppszReturn, sizeof(LPWSTR));
580
HRESULT hr = StringCbLengthW(psz, 100000, &nameLen);
584
*ppszReturn = (LPWSTR)CoTaskMemAlloc(nameLen + sizeof(WCHAR));
585
if (*ppszReturn == NULL) {
586
return E_OUTOFMEMORY;
588
CopyMemory(*ppszReturn, psz, nameLen + sizeof(WCHAR));
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
597
DWORD WINAPI WaitDispatchingMessages(
604
BOOL bPeeked = FALSE;
607
DWORD dwThreadPriority = THREAD_PRIORITY_HIGHEST;
609
static UINT uMsgId = 0;
611
HANDLE hObjects[2] = { hObject, hEvent };
612
if (dwWait != INFINITE && dwWait != 0) {
613
dwStart = GetTickCount();
616
DWORD nCount = NULL != hEvent ? 2 : 1;
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) {
625
DWORD dwTimeOut = dwWait;
626
if (dwTimeOut > 10) {
629
dwResult = MsgWaitForMultipleObjects(
634
hwnd == NULL ? QS_SENDMESSAGE :
635
QS_SENDMESSAGE + QS_POSTMESSAGE);
636
if (dwResult == WAIT_OBJECT_0 + nCount ||
637
dwResult == WAIT_TIMEOUT && dwTimeOut != dwWait) {
640
while (PeekMessage(&msg, hwnd, uMsg, uMsg, PM_REMOVE)) {
641
DispatchMessage(&msg);
644
// Do this anyway - the previous peek doesn't flush out the
646
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
648
if (dwWait != INFINITE && dwWait != 0) {
649
DWORD dwNow = GetTickCount();
651
// Working with differences handles wrap-around
652
DWORD dwDiff = dwNow - dwStart;
653
if (dwDiff > dwWait) {
661
// Raise our priority to prevent our message queue
663
dwThreadPriority = GetThreadPriority(GetCurrentThread());
664
if (dwThreadPriority < THREAD_PRIORITY_HIGHEST) {
665
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
674
SetThreadPriority(GetCurrentThread(), dwThreadPriority);
675
if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) {
677
uMsgId = RegisterWindowMessage(TEXT("AMUnblock"));
682
while (PeekMessage(&msg, (HWND)-1, uMsgId, uMsgId, PM_REMOVE)) {
685
PostThreadMessage(GetCurrentThreadId(), uMsgId, 0, 0);
691
HRESULT AmGetLastErrorToHResult()
693
DWORD dwLastError = GetLastError();
696
return HRESULT_FROM_WIN32(dwLastError);
704
IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp)
714
/******************************************************************************
716
CompatibleTimeSetEvent
718
CompatibleTimeSetEvent() sets the TIME_KILL_SYNCHRONOUS flag before calling
719
timeSetEvent() if the current operating system supports it. TIME_KILL_SYNCHRONOUS
720
is supported on Windows XP and later operating systems.
723
- The same parameters as timeSetEvent(). See timeSetEvent()'s documentation in
724
the Platform SDK for more information.
727
- The same return value as timeSetEvent(). See timeSetEvent()'s documentation in
728
the Platform SDK for more information.
730
******************************************************************************/
731
MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent )
735
static bool fCheckedVersion = false;
736
static bool fTimeKillSynchronousFlagAvailable = false;
738
if( !fCheckedVersion ) {
739
fTimeKillSynchronousFlagAvailable = TimeKillSynchronousFlagAvailable();
740
fCheckedVersion = true;
743
if( fTimeKillSynchronousFlagAvailable ) {
744
fuEvent = fuEvent | TIME_KILL_SYNCHRONOUS;
747
#endif // WINVER >= 0x0501
749
return timeSetEvent( uDelay, uResolution, lpTimeProc, dwUser, fuEvent );
752
bool TimeKillSynchronousFlagAvailable( void )
754
OSVERSIONINFO osverinfo;
756
osverinfo.dwOSVersionInfoSize = sizeof(osverinfo);
758
if( GetVersionEx( &osverinfo ) ) {
760
// Windows XP's major version is 5 and its' minor version is 1.
761
// timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag
763
if( (osverinfo.dwMajorVersion > 5) ||
764
( (osverinfo.dwMajorVersion == 5) && (osverinfo.dwMinorVersion >= 1) ) ) {
773
#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */