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
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;
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 */